"Robusten"-Stream aufbauen

Hallo,
für meine WS2812B Leds möchte ich in einer Interruptroutine einen String einlesen, der aus einem C# Programm alle 10ms gesendet wird (sofern eine Änderung erfolgte).

Der Stream sieht wie folgt aus
"<ff,ff,ff,ff>"

Beginn des Streams ist ein '<', das Ende ein '>'
Die Werte dazwischen werden als Hex-Char gesendet. 00 - ff.
Feld 1 ist für das Setzen interessant. Zum einen, welche Led angesprochen wird, und ob sich in den Felder 2 - 4 RGB oder HSV Werte befinden.

Wo ihr nun rüberschauen dürft ist zum einen ISR(USART_RX_vect) im ersten Quellcode Teil, sowie den dritten Quellcode Teil.

Was ich damit vor habe,
es soll mit einem '<' signalisiert werden, dass ein neuer Stream angefangen hat. Wurde der letzte Stream nicht abgeschlossen, startet hier ein neuer.
Werden mehr Werte als nötig eingelesen, gibt es ebenfalls einen Abbruch. Hierbei sind 5 Felder vorgesehen mit je 2 Zeichen (getrennt von einem ','

/*
 * uart.h
 *
 * Created: 08.12.2015 21:00:19
 *  Author: sschultewolter
 */ 


#ifndef UART_H_
#define UART_H_

#define BAUD				250000

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <util/setbaud.h>
#include <stdlib.h>

#define UART_STRING_MAX		14
extern volatile char uart_string[UART_STRING_MAX + 1];
extern volatile uint8_t uart_strComplete;

extern void uart_init(void);

extern void uart_putc(char c);
extern void uart_puts(char *s);
extern void uart_puts_p(const char *s);

#define uart_puts_P(__S)	uart_puts_p(PSTR(__S))
#define UART_CLEAR			uart_puts_P("\E[H\E[J")
#define UART_CR				uart_puts_P("\r")
#define UART_NL				uart_puts_P("\n")
#define UART_CRNL			uart_puts_P("\r\n")

#endif /* UART_H_ */
/*
* uart.c
*
* Created: 08.12.2015 21:00:10
*  Author: sschultewolter
*/

#include "uart.h"

volatile char uart_string[UART_STRING_MAX + 1];
volatile uint8_t uart_strComplete = 0;
volatile uint8_t uart_strCount = 0;

extern void uart_init(void)
{
	UBRR0 = UBRR_VALUE;
	
	#if USE_2X
	UCSR0A |= (1 << U2X0);
	#else
	UCSR0A &= ~(1 << U2X0);
	#endif
	
	// UART Tx, Rx Interrupt, Rx einschalten
	UCSR0B |= (1 << TXEN0) | (1 << RXCIE0) | (1 << RXEN0);
	// Asynchron 8N1
	UCSR0C = (1 << UCSZ01) | (1 << UCSZ00);
}

extern void uart_putc(char c)
{
	while(!(UCSR0A & (1 << UDRE0))) ;
	UDR0 = c;
}

extern void uart_puts(char *s)
{
	while(*s)
	{
		uart_putc(*s);
		s++;
	}
}

extern void uart_puts_p(const char *s)
{
	register char c;
	while((c = pgm_read_byte(s++)))
	uart_putc(c);
}

ISR(USART_RX_vect)
{
	// Zeichen aus Puffer lesen
	char c = UDR0;
	
	// wenn uart_strComplete gesetzt ist, werden neue Zeichen verworfen
	if(!uart_strComplete)
	{
		// Erstes zu empfangende Zeichen
		// Sollten Zeichen vor '<' empfangen worden sein, wird der Zaehler (uart_strCount)
		// zurueckgesetzt und die Zeichenkette (uart_string) mit '\0' terminiert
		if(c == '<')
		{
			uart_strCount = 0;
			uart_string[uart_strCount] = '\0';
		}
		// Das letzte zu empfangende Zeichen erhalten
		else if(c == '>')
		{
				uart_string[uart_strCount] = '\0';
				uart_strCount = 0;
				uart_strComplete = 1;
		}
		// Zeichen einlesen und Zaehler (uart_strCount) inkrementieren
		else if(c != '\r' && '\n' && uart_strCount < UART_STRING_MAX)
		{
			uart_string[uart_strCount++] = c;
		}
		else
		{
			uart_strCount = 0;
			uart_string[uart_strCount] = '\0';
		}
	}
}
/*
* m328p_bottlelightv2.c
*
* Created: 08.12.2015 20:55:04
*  Author: sschultewolter
*/

#include <avr/io.h>
#include <avr/interrupt.h>
#include <stdio.h>
#include <string.h>
#include "uart.h"

//#define DEBUG_UART

#ifdef DEBUG_UART
char buf_debug[32];
#endif


uint8_t getValues(char *s, uint8_t *values, uint8_t maxValues)
{
	#ifdef DEBUG_UART
	
	// Empfangen String ausgeben
	sprintf(buf_debug, "Eingabe\t\t: <%s>", (char*)uart_string);
	uart_puts(buf_debug);
	#endif

	const char delimter[] = ",;";
	char *ptr = (char*)strtok((char*)uart_string, delimter);
	uint8_t ct = 0;

	while(ptr != NULL)
	{
		if(ct < maxValues - 1)
		{
			// Speichere Hex-Zeichen als Zahl
			values[ct] = strtol(ptr, NULL, 16);
			
			#ifdef DEBUG_UART
			sprintf(buf_debug, "\r\n\tWert[%d]\t: %d", ct, values[ct]);
			uart_puts(buf_debug);
			#endif
		}
		// Zaehler ct solange einlesen, bis ptr == NULL (Fehlererkennung)
		ct++;
		ptr = strtok(NULL, delimter);
	}
	
	if(ct == (maxValues - 1))
	{
		#ifdef DEBUG_UART
		sprintf(buf_debug, "\r\n! %d Feld(er) empfangen", (maxValues - 1));
		uart_puts(buf_debug);
		#endif
		return 1;
	}
	else if (ct < (maxValues - 1))
	{
		#ifdef DEBUG_UART
		sprintf(buf_debug, "\r\n! %d Feld(er) nicht empfangen", (maxValues - 1) - ct);
		uart_puts(buf_debug);
		#endif
		return 0;
	}
	else
	{
		#ifdef DEBUG_UART
		sprintf(buf_debug, "\r\n! %d Feld(er) zu viel empfangen", ct - (maxValues - 1));
		uart_puts(buf_debug);
		#endif
		return 0;
	}
}



int main(void)
{
	uart_init();
	sei();
		
	const uint8_t numValues = 5;
	uint8_t values[5];
	
	while (1)
	{
		if(uart_strComplete)
		{
			uint8_t tmpValues[numValues];
			// Neue Werte uebernehmen
			if(getValues((char*)uart_string, tmpValues, numValues))
			{
				strncpy((char*)values, (char*)tmpValues, numValues);
				
				#ifdef DEBUG_UART
				sprintf(buf_debug, "\r\n%3d, %3d, %3d, %3d\r\n", values[0], values[1], values[2], values[3]);
				uart_puts(buf_debug);
			#endif
			}
			uart_strComplete = 0;
		}
	}
}

Einen Bug habe ich gerade durch Zufall schon bemerkt :confused:
strncpy kopiert die Werte nur solange, wie kein Feld 0 ist.

Eingabe             : <0,ff,ff,ff>

        Wert[0] : 0

        Wert[1] : 255

        Wert[2] : 255

        Wert[3] : 255

! 4 Feld(er) empfangen

strncpy   0,   0,   0,   0

<ff,ff,0,ff>



Eingabe             : <ff,ff,0,ff>

        Wert[0] : 255

        Wert[1] : 255

        Wert[2] : 0

        Wert[3] : 255

! 4 Feld(er) empfangen

strncpy 255, 255,   0,   0

Edit: Das ist das verflixte '\0'. Muss, so wie ich das dann sehe, eine eigene Funktion basteln.
Edit2: Einfache Schleife tut es hier ja schon :wink:
Werde dann das ganze mal testen. Alternative hatte ich davor noch mit einem 5. Feld für eine Checksumme gearbeitet. Da ich dort aber mehr oder weniger keinen Fehler hatte, wollte ich mir die 3 Bytes sparen. Die Checksumme bestand lediglich aus der Addition der Quersumme jedes Feldes.

				for(uint8_t i = 0; i < numValues; i++)
				{
					values[i] = tmpValues[i];
				}

Es gibt auch memcpy() um Speicher allgemein zu kopieren