wird es verfälscht und ich sehe im Datalogger die Übertragung
N '179' '188' \n #
und erhalte auf der seriellen µC Ausgabe nur eine N. Der Rest fehlt.
Reine Stringübertragungen sind dagegen fehlerfrei.
Ich lese erstmal nur in einen Buffer ein und lasse diesen ausgeben.
Kann man echte Byte Werte nur im String übertragen? Und muss diese dann wieder herausfiltern?
Testweise habe ich die eingelesenen Zeichen mitzählen lassen, es kommt auch nur eines in den Buffer.
Wo verbleibt der Rest?
bool read_Serial_1()
{
static byte index = 0;
static byte counter = 0;
if (Serial1.available() > 0) {
char c = Serial1.read();
if (c >= 32 && c != '#' && (index < SERIAL_BUFFER_SIZE - 1))
{
serialBuffer_1[index++] = c;
counter++;
}
else if ( c == '\t') {
serialBuffer_1[index] = '\0';
index = 0;
serial1COMMAND = TAB;
return true;
}
else if ( c == '\r') {
serialBuffer_1[index] = '\0';
index = 0;
serial1COMMAND = CR;
return true;
}
else if ( c == '\n') {
serialBuffer_1[index] = '\0';
index = 0;
serial1COMMAND = NL;
return true;
}
else if ( c == '#') { // Übertragungsendezeichen
serialBuffer_1[index] = '\0';
index = 0;
state_UART1 = FREE;
digitalWrite(Led_UartTimeout, LOW); // Debug LED
Serial.print("counter ");
Serial.println(counter);
counter = 0;
return true;
}
}
return false;
}
Wenn du Binär einlesen willst, mache entweder eine Union aus dem struct und einem Array der gleichen Größe. Oder caste die Adresse des structs auf einen Zeiger auf Byte. In beiden Fällen kannst du dann byte-weise einlesen
wird es verfälscht und ich sehe im Datalogger die Übertragung
N '179' '188' \n #
Dein sendendes Programm scheint nur reines ASCII (<128) direkt als Zeichen zu schicken. Darüber hinaus wird der Dezimale Wert als Zeichenkette geschickt.
Probiere mal einen anderen Sender oder sende als HEX-Wert.
ASCII-Buchstaben sind nur 7 bit ( also Werte 0 .. 127 )
Bytes haben 8 Bit und wie die Werte 128 .. 255 dargestellt werden, hängt von vielerlei ab.
char haben auch 8 Bit, sind aber vorzeichenbehaftet, werden also als -128 .. 127 interpretiert.
Wenn du kein Problem mit strings hast, sende doch mal Serial.println("N\xB3\xBC").
Das sind dieselben 3 Zeichen wie bei dir.
Wo verbleibt der Rest?
Zeichen > 127 werden von dir als negativ interpretiert und per[b] if (c >=32)[/b] ausgefiltert
Wenn du uint8_t senden und empfangen willst, dann solltest du das auch tun.
char ist allerdings ein Zeichen.
Und darum macht dir print() auch einen Buchstaben draus.
Serenifly:
Das mit dem c >= 32 z.B. ist um ASCII-Steuerzeichen herauszufiltern. Wenn man Binärwerte übertragt braucht man das natürlich nicht
Außerdem ist da eine böse Falle versteckt, wenn man mal auf ein größeres Board wechselt. Auf ARM ist char nämlich aus irgendwelchen Gründen ein vorzeichenloser Typ. Die Zeile würde also auf Zero, Due usw. was ganz anderes tun.
das mit char c >= 32 ist natürlich eine Falle. Ich habe bisher nur Zeichenketten hin und her geschoben und war happy.
Mit byte c ist es jedoch nicht getan. Ich erhalte damit nun erstmal mehr wie ein Zeichen. ⸮N⸮⸮
if (Serial1.available() > 0) {
byte c = Serial1.read();
if (c != '#' && (index < SERIAL_BUFFER_SIZE - 1))
...
Mit obiger union von Serenifly erhalte ich expected ';' at end of member declaration
für die Zeile uint8_t asArray[3];
das wird fehlerfrei kompiliert, aber dann ohne array. Was tun?
Den Sender habe ich umgestellt, der sendet nur noch die 3 Bytewerte und das # Endezeichen. Also ohne '\n'
Der Sender sendet bytes. Datentyp unsigned char. Sonst würde ich im Datalogger schon Müll sehen. Im Datalogger passt jedoch alles.
void uart0_putc(unsigned char data)
{
Mal was grundlegendes. Ich kann nach dem Umbau keine Strings mehr empfangen? Richtig? Nur noch reine Bytewerte?
Und brauche eine Funktion die mir die reinkommendes Bytes in die struktur bzw. union reinschiebt? Worauf ich dann wieder normal zugreifen kann?
Das mit dem Array passt schon. Der Fehler war nur der fehlende Strichtpunkt nach dem struct. Wie kommst du auf die Idee dann das Array wegzulassen?
Den Sender habe ich umgestellt, der sendet nur noch die 3 Bytewerte und das # Endezeichen.
Wenn du Binär sendest kannst du nicht wirklich mit einem Endzeichen arbeiten. Du kannst als erstes Byte die Länge des Daten-Telegramms senden wenn du eine Kontrolle willst
Wenn du mit Strings arbeiten willst, sende die Werte mit Komma getrennt und zerlege den String danach mit strtok() + atoi(). Oder frage ob ob ein Komma da ist und mache dann gleich atoi()
das Array hatte ich testweise weggelassen zur Fehlersuche, komme mit dem vermischten Syntax von union und struct noch nicht klar. Einzeln schon. Jetzt funktioniert es.
Wenn ich in beiden Fällen weiterhin mit
daten.adresse
daten.funktion
daten.wert
arbeiten kann, was ist dann der genauer Unterschied zu struct alleine nochmal genau?
Wofür dient das asArray[3] ? Das ich mit Zeigern Byte reinschreiben kann?
Bei einer Union teilen sich alle Variablen den gleichen Speicherplatz. Das ist vor allem sinnvoll wenn das struct Multi-Byte Datentypen enthält. Man kann also Byte für Byte einlesen und z.B. 2 * int und 1 * float direkt beschreiben. Ohne irgendwas zu konvertieren.
Das ich mit Zeigern Byte reinschreiben kann?
Du brauchst dabei keine Zeiger. Direkt in das Array schreiben und fertig. Senden geht genauso. Man kann mit Serial.write() das Array auf einmal senden
Wenn du Binär sendest kannst du nicht wirklich mit einem Endzeichen arbeiten. Du kannst als erstes Byte die Länge des Daten-Telegramms senden wenn du eine Kontrolle willst
Da haben sich schon viele Leute viele Ideen ausgedacht.
Erstmal musst du sicher sein, dass Sender und Empfänger synchron sind.
Bidirektional: Halbduplex, Handshake austauschen
nach einer Pause fängt auf jeden Fall eine neue Nachricht ohne größere Pausen im Datenstrom an.
Dann kennst du in deinem Fall die NachrichtenLänge: sizeof(Nachricht)
Ansonsten einen Header drumherum senden: Anfangszeichen, Typ, Länge, Nutzdaten, Checksum
Kannst auch alternativ ein Spezialzeichen definieren: z.B. \xFF Wenn das ein Datenbyte sein soll, folgt ein weiteres \xFF, sonst was anderes, das dann als Steuerzeichen (z.B. \x00 = Ende) interpretiert wird.
Da kannst du dich richtig austoben
Um deine Bytes in eine passende Struct zu füllen, kannst du statt union auch einen Pointer umcasten:
(Keine Angst vor Zeigern )
struct Nachricht recData; // hier wird das empfangene abgelegt
byte * target = (byte*) &recData; // Byte-Zeiger initialisieren
while ( Serial.available() )
*target++ = Serial.read(); // empfangenes Zeichen ablegen und Zeiger erhöhen
okay, ich glaube ich habe es verstanden. Erstmal sacken lassen und dann umsetzen.
Etwas rumgespielt habe ich schon. Das struct im union mit Array ist ja richtig multifunktional.
Danke Serenifly.
Ja das Übertragungsprotokoll muss ich anpassen bzw. umbauen. Da muss ich mir was neues ausdenken. Mit Strings verlasse ich mich bisher auf das Endezeichen und einen eingebauten Timeout. Der Umbau soll alles vereinfachen.
Bisher war das alles ein einziger String zum Slave. Jetzt möchte ich ein einheitliches Datenformat möglichst ohne String. Wenn sich ein Slave mittels Adresse angesprochen fühlt, sendet dieser die gewünschten Daten zurück.
Vor Zeigern habe ich noch scheu ... sonst dreht er selbst am Zeiger.
Eine andere Stelle wo das extrem wichtig ist ist I2C. Weil du da im Receive Event Handler nur einmal write() machen kannst. So kann man auf die Art bequem mehrere Variablen als Antwort senden
das mit der Start und Endekennung ist noch nicht perfekt, wenn sich das mit den Daten-Byte deckt gibts noch ein Problem, aber es geht in die richtige Richtung. Es läuft erstmal.
Danke an alle Helfer!
Eine Frage noch an Micha.
Hat die Schreibweise \xFF und \x00 einen bestimmten Grund? Sind doch auch nur 255 und 0. 'grübel'
habe den Testcode erweitert mit allgemeiner Längenermittlung und einer einfachen Checksummenberechnung.
Ich hoffe das kann man allgemein so stehen lassen.
/*
Doc_Arduino - german Arduino Forum
IDE 1.8.13
Arduino Mega2560
14.09.2017
*/
typedef enum {FREE, WAIT, BUSY, FEHLER} sUART1; // Steuerzustände
sUART1 state_UART1 = FREE;
unsigned long countErrorsUART1; // uart1 Fehlerzähler
unsigned long last_millis;
unsigned long UartTimeoutStart;
unsigned long UartTimeoutEnde;
const byte Led_UartTimeout = 30;
const byte SNT = 255; // Startkennzeichen der Nachricht
const byte ENT = 0; // Endkennzeichen der Nachricht
byte addr_ATtinySlave = 31;
// definieren der Nachricht
union Nachricht
{
struct
{
byte adresse;
byte funktion;
byte wert;
byte checksumme;
};
byte asArray[4];
};
Nachricht sendDaten;
Nachricht empfDaten;
void setup() {
Serial.begin(500000);
Serial1.begin(250000); // Serial1 gekreuzt mit Serial2 verbunden
Serial2.begin(250000);
pinMode(Led_UartTimeout, OUTPUT);
sendDaten.adresse = addr_ATtinySlave;
sendDaten.funktion = 26;
sendDaten.wert = 22;
}
void loop() {
read_Serial_1_to_Serial_0();
check_Uart_Receive_Timeout();
if (millis() - last_millis > 999) {
last_millis = millis();
if (state_UART1 == FREE) {
send_Nachricht();
sendDaten.adresse++;
sendDaten.funktion++;
sendDaten.wert++;
}
}
} // loop Ende
// ****** Funktionen ******* //
bool read_Serial_1()
{
static byte index = 0;
static bool state = false;
if (Serial1.available() > 0) {
byte c = Serial1.read();
if (c == ENT && index > (sizeof(Nachricht)-1) ) { // Ende-Kennung ?
state = false;
}
if (state == true && (index < sizeof(Nachricht))) {
empfDaten.asArray[index++] = c;
}
if (c == SNT && state == false) { // Start-Kennung ?
state = true;
}
if (state == false) { // Übertragungsende
index = 0;
state = false;
// Checksumme korrekt ?
if ( empfDaten.checksumme == calc_Checksumme(empfDaten.asArray, sizeof(Nachricht)) ) {
digitalWrite(Led_UartTimeout, LOW); // Debug LED
state_UART1 = FREE;
return true;
}
}
}
return false;
}
void read_Serial_1_to_Serial_0 ()
{
if ( read_Serial_1() == true) {
Serial.println(F("Serial1 hat empfangen:"));
Serial.println(empfDaten.adresse);
Serial.println(empfDaten.funktion);
Serial.println(empfDaten.wert);
Serial.println(empfDaten.checksumme);
}
}
void check_Uart_Receive_Timeout ()
{
static unsigned long timeout = 0;
if (state_UART1 == WAIT) {
state_UART1 = BUSY;
UartTimeoutStart = millis();
digitalWrite(Led_UartTimeout, HIGH); // Debug LED
}
if (state_UART1 == BUSY) {
UartTimeoutEnde = millis();
}
timeout = UartTimeoutEnde - UartTimeoutStart;
if (timeout > 5) {
state_UART1 = FEHLER;
}
if (state_UART1 == FREE) {
//Serial.print("Debug ms"); Serial.print('\t'); Serial.println(timeout);
}
if (state_UART1 == FEHLER) {
countErrorsUART1++;
Serial.print(F("UART TIMEOUT")); Serial.print('\t'); Serial.println(timeout);
Serial.print(F("UART1 Errors")); Serial.print('\t'); Serial.println(countErrorsUART1);
state_UART1 = FREE;
}
}
void send_Nachricht ()
{
sendDaten.checksumme = calc_Checksumme(sendDaten.asArray, sizeof(Nachricht)) ;
Serial.println(F("Serial2 sendet ..."));
Serial2.write(SNT);
for (byte i=0; i < sizeof(Nachricht); i++) {
Serial2.write(sendDaten.asArray[i]);
}
Serial2.write(ENT);
Serial2.flush();
state_UART1 = WAIT;
}
byte calc_Checksumme (byte feld[], byte length)
{
byte Checksumme = 0;
for (byte i=0; i<length-1; i++) { // nur Nutzdaten aufsummieren
Checksumme = Checksumme + feld[i]; // ohne Rücksicht auf Überlauf
}
return Checksumme;
}