PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : UART Kommunikation zwischen Raspi und Arduino hängt sich ständig auf



HaWe
31.05.2019, 18:33
hallo,
ich komme zur Zeit nicht weiter: meine UART Kommunikation zwischen Raspi und Arduino hängt sich ständig auf.
Ich vermute den Fehler eher Raspi-seitig, weil der Arduino-Code in ähnlicher Form bereits mit Borland C++ Builder auf dem PC funktioniert hat (wenn sich nicht ein c+p Fehler eingeschlichen hat, den ich allerdings noch nicht gefunden habe).
Der Arduino (momentan ein Mega2560) ist per USB mit einem Raspi-USB-Port verbunden ("/dev/ttyACM0") .

Hat jemand einen Tipp, das zu beheben?

Raspi code:


/*
UART communication
send/receive string of tokens

Raspberry Pi master
ver 0101
*/



#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <math.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <pthread.h>

#include <wiringPi.h>
#include <wiringSerial.h>

#define byte uint8_t

char uart[256] = "/dev/ttyACM0";
int Serial;

const uint32_t UARTclock = 115200;



int i0;

#define TOKLEN 30
#define MSGLEN 1024
#define iINVALID -29999


std::string inputString="";
char cval[TOKLEN]; // number as cstring
char mbuf[MSGLEN]; // cstring msg buffer





//================================================== ================
// serial TCP
//================================================== ================


void loop() {

while(1) {

static bool stringComplete = false;


//-------------------------------------------------------------
// send

char formatstr[MSGLEN];

// debug, cut-down:
strcpy(formatstr, "§");
strcat(formatstr, "message from Raspi: %d;\n");
sprintf(mbuf, formatstr, i0);

for(uint8_t i=0; i<strlen(mbuf); i++) { //
serialPutchar( Serial, mbuf[i]); // Send values to the slave
}

// strcpy(mbuf, ""); // <~~~~~~~~~~~~~~~~~~~~~~~~editiert!

//delay?
delay(10); // <~~~~~~~~~~~~~~~~~~~~~~~~editiert!




//-------------------------------------------------------------
// receive

inputString="";
char cstr[TOKLEN];

int n=0;
char inChar;


while (serialDataAvail( Serial ) && n<MSGLEN-1) {
if(n==MSGLEN-2) inChar='\n'; // emergency brake
else
inChar = serialGetchar( Serial );

inputString += inChar;

if (inChar == '\n') {
stringComplete = true;
}
n++;
}


if (stringComplete) {

strcpy (mbuf, inputString.c_str() );
// strcat(mbuf, "\0"); // <~~~~~~~~~~~~~~~~~~~~~~~~editiert!
fprintf(stderr,mbuf); fprintf(stderr,"\n");

// process mbuf !


inputString="";
stringComplete = false;

//delay?
delay(1);
}

}
}




//================================================== ================


int main() {

printf("initializing..."); printf("\n");

// UART Serial com port
Serial = serialOpen (uart, UARTclock);
printf("starting UART loop..."); printf("\n");

loop();

serialClose( Serial);
exit(0);
}


//
// eof



Arduino code:


// Arduino COM to Raspi
// (C) 2018 by dsyleixa

// ver 0101


// i2c
#include <Wire.h>


int i0 = 0;

#define TOKLEN 30
#define MSGLEN 1024
#define iINVALID -29999

String inputString="";
char cval[TOKLEN]; // number as cstring
char mbuf[MSGLEN]; // cstring msg buffer


//================================================== ================
// setup
//================================================== ================
void setup() {
Serial.begin(115200);

pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);

}


//================================================== ================
// loop
//================================================== ================

void loop() {
static bool stringComplete = false;

//-------------------------------------------------------------
// receive

int n=0;
char inChar;


while (Serial.available() && n<MSGLEN-1) {
if(n==MSGLEN-2) inChar='\n';
else
inChar = (char)Serial.read();

inputString += inChar;

if (inChar == '\n') {
stringComplete = true;
}
n++;
}

if (stringComplete) {
inputString.toCharArray(mbuf, MSGLEN-1);

// process mbuf !

//

inputString="";
stringComplete = false;

//delay?
delay(1);
}
//-------------------------------------------------------------
// send


//----------------------

char formatstr[MSGLEN];
strcpy(formatstr, "§");
strcat(formatstr, "message from Arduino: %d;\n");
sprintf(mbuf, formatstr, i0);

for (int i=0; i<strlen(mbuf); i++ ) Serial.print(mbuf[i]);

// strcpy(mbuf, ""); // <~~~~~~~~~~~~~~~~~~~~~~~~editiert!

i0++;

//delay?
delay(10); // <~~~~~~~~~~~~~~~~~~~~~~~~editiert!
}




// end of file



console output:


initializing...
starting UART loop...
§message from Arduino: 0;
§message fr211;
§message from Arduino: 2212;
§message from Arduino: 2213;
§message from Arduino: 221�no: 2210;
§message from Arduino: 2211;
§message from Arduino: 2212;
§message from Arduino: 2213;
§message from Arduino: 221�






dann bleibt es stehen...

Restart, neuer output:


initializing...
starting UART loop...
§message from Arduino: 0gno: 618;
§message fr��message from Arduino: 615;
§message from Arduino: 616;
§message from Arduino: 617;
§message from Arduino: 618;
§message fr





dann bleibt es wieder stehen...

Siro
31.05.2019, 18:38
Hallo HaWe,

115200 Baud:
sind 8,68 us pro Bit
10 Bits = 1 Byte wegen Start Stop Bit
also 68,8us pro Byte
* 30 Byte = 2,6 Millisekunden
Du wartest 1 Millisekunde, läuft Dir da evtl. ein Puffer über ?

Siro

HaWe
31.05.2019, 18:43
kann sein, kenne mich da nicht aus -
aber wenn der Arduino sendet, dann nur bis zu einem \n,
dann kann der Raspi empfangen, ebenfalls bis zum \n,
und solange sollte der Arduino ja gar nichts mehr tun, bis der Raspi wieder sendet und der Arduino empfängt.
Erst danach dürfte der Arduino erneut senden - so ist es jedenfalls gedacht, quasi als Handshake.

Wie könnten sich Puffer füllen, wenn jeder immer mit erneutem Senden wartet, bis der andere mit Empfangen fertig ist und dann erst die Rück-Antwort empfangen wurde?

Kann ich evtl. Puffer (welche?) zwischendurch manuell löchen?

Siro
31.05.2019, 18:46
Eigentlich sollte er nach dem Senden ja erstmal etwas empfangen, sehe ich auch so.
Mach doch testweise mal ein delay(10) beim Senden und schau was passiert.

HaWe
31.05.2019, 18:53
kein Unterschied mit 10, mit 100 auch nicht,
einziger Unterschied: keine Sonderzeichen � � mehr zwischendrin

Siro
31.05.2019, 18:56
Wenn ich das richtig sehe,
löschst Du den Buffer strcpy(mbuf, ""); direkt nach dem Senden.
Oder wofür ist das gut, ich bin da jetzt nicht sicher.

Wenn das serielle Ausschieben Interrupt gesteuert ist,
dann muss der Buffer aber noch erhalten bleiben bis wirklich alles ausgesendet wurde.

Bau mal vor dem Löschen also vor dem strcpy(mbuf, ""); zum testen noch eine Delay ein 10 ein.

HaWe
31.05.2019, 19:40
stimmt, das Löschen von mbuf ist überflüssig, habe es überall rausgenommen, danach delay(10) testweise.
Bleibt aber leider immer noch hängen nach ein paar Runden.

- - - Aktualisiert - - -

update:
habe den Code jetzt auf einen Due hochgeladen:

FUNZT! :shock:

danach wieder auf Mega umgesteckt!
HÄNGT! :shock:

was ist DA denn los...?

- - - Aktualisiert - - -

Könnte das bitte jemand mal selber bei sich testen, mit einem Mega2560 am Raspi?

HaWe
01.06.2019, 09:21
kann jemand was dazu sagen, wieso es beim Mega2560 hängt und beim Due einwandfrei klappt?
USB Kabel habe ich auch schon getauscht, ohne dass sich was ändert.

Ist evtl mein Mega kaputt - oder ist das generell so? (habe leider nur den einen zum Testen)

HaWe
01.06.2019, 19:05
kriegs nicht hin mit dem Mega, auch nicht wenn MSGLEN=256, bleibt immer hängen.
Due nach wie vor kein Problem.
Gerade für manche 5V-Uno-Shields wäre das interessant am Raspi gewesen, aber wenn es nicht geht, dann ist man leider auf den Due festgelegt, wenn man ein Arduino-Huckepackboard mit sehr vielen GPIOs braucht.

Oder ist doch ein Spezialist hier, der den Code hinkriegt mit einem Mega?

wkrug
02.06.2019, 08:29
Ich hab jetzt den Code nicht zerpflückt und bin auch kein ARDUINO Mann aber,

könnte es sein das der Raspberry einen Softwareloop auf die serielle Schnittstelle macht und somit der Eingangspuffer mit den empfangenen Daten, die der Arduino selbst gesendet hat überläuft?
Das sollte sich testen lassen, wenn man die RxD Leitung des Arduino mal auftrennt und den Sendecode umschreibt.

Ich lös das immer so, das der Datenempfang im Interrupt läuft und einen Ringpuffer befüllt.
Dann gibt's da noch einen Schreib- und einen Lesezeiger auf den Ringbuffer und in der Hauptroutine wird dann, wenn die beiden unterschiedlich sind eine Abfrageroutine ausgeführt und die empfangenen Bytes ausgewertet.

Die Senderoutine läuft dann ganz normal mit Polling auf das USARTx DRE Bit.
Da die Atmegas einen Doppelpuffer haben gehen da am Anfang immer gleich 2 Bytes rein.
Kriegt man die Hauptroutine unter 86µs pro Durchlauf sollten da auch keine Lücken im Sendedatenstrom entstehen.

HaWe
02.06.2019, 10:21
Ich hab jetzt den Code nicht zerpflückt und bin auch kein ARDUINO Mann aber,

könnte es sein das der Raspberry einen Softwareloop auf die serielle Schnittstelle macht und somit der Eingangspuffer mit den empfangenen Daten, die der Arduino selbst gesendet hat überläuft?
Das sollte sich testen lassen, wenn man die RxD Leitung des Arduino mal auftrennt und den Sendecode umschreibt.

Ich lös das immer so, das der Datenempfang im Interrupt läuft und einen Ringpuffer befüllt.
Dann gibt's da noch einen Schreib- und einen Lesezeiger auf den Ringbuffer und in der Hauptroutine wird dann, wenn die beiden unterschiedlich sind eine Abfrageroutine ausgeführt und die empfangenen Bytes ausgewertet.

Die Senderoutine läuft dann ganz normal mit Polling auf das USARTx DRE Bit.
Da die Atmegas einen Doppelpuffer haben gehen da am Anfang immer gleich 2 Bytes rein.
Kriegt man die Hauptroutine unter 86µs pro Durchlauf sollten da auch keine Lücken im Sendedatenstrom entstehen.

danke, aber ich wüsste jetzt nicht, wie ich das umschreiben soll, und ich kann den Arduino ja nicht per Serial/USB- console auslesen, weil er per USB am Raspi hängt - auf einen genauen (Arduino- oder Raspi C) Code wäre ich da jetzt schon angewiesen.
Kann aber auch sein, dass der Fehler Raspi-seitig ist, nicht Arduino-seitig, s. hier: https://www.raspberrypi.org/forums/viewtopic.php?f=33&t=241691&sid=e03155fa797e120ec2c0f5138709e491#p1475408

In jedem Falle wäre es aber am besten, einen Code zu benutzen, den schon jemand erfolgreich verwendet, und nach so vielen Jahren Raspi und Arduino müsste es das doch geben - ich bin doch da wschl nicht wirklich der allererste, der sich daran versucht?

(Später will ich einen sehr langen String mit an die 50 tokens+args übergeben, mit denen ich schnell Variablen (int, double)- und pin-r/w-Werte (digital, ADC) hin und her übertragen will. Auch serielle Eingaben über die Konsole, abgeschlossen per Enter, sollen später grundsätzlich möglich sein, daher soll der lange String mit '\n' enden.)

- - - Aktualisiert - - -

PS,
auf einen Tipp hin habe ich die Leseroutine beim Pi jetzt abgeändert (versuchsweise optional auch am Arduino entsprechend)


if (serialDataAvail(Serial)) {
while(!stringComplete && n<MSGLEN-1) {
if(n==MSGLEN-2) inChar='\n'; // emergency brake
else
inChar = serialGetchar(Serial);

if(inChar=='\n' || inChar>=' ') inputString += inChar;
if (inChar == '\n') {
stringComplete = true;
}
n++;
}
}

läuft jetzt am Mega mit 2000-3000 loops, aber dann hängt er wieder - am Due läufts ohne Probleme bislang weiter....
Künftig sollen beide Codes aber auch unabhängig vom Arduino-Board laufen, egal ob AVR oder ARM oder ESP.



Update: nach ca. 30min hat es sich dann auch beim Due aufgehängt...

wkrug
03.06.2019, 18:45
Häng doch mal ein OSZI an den TxD Pin des des Prozessors am ARDUINO.
Der müsste dann weiter senden.
Soweit Ich weiss ist auf em ARDUINO nur eine Serial nach USB Bridge.
Dieser Chip muss dann auch vom RASPI aus konfiguriert werden. Eventuell liegt ja da der Hund begraben?

HaWe
03.06.2019, 19:19
sowas wie ein Oszi besitze ich doch gar nicht... ;)
aus irgendeinem Grund hängt sich die Verbindung auf, und vermutlich liegt es am Raspi, denke ich inzwischen (UART-timing, Puffer, USB, Linux kernel, wiringPi-/wiringSerial libs....).
Hilfreich wären wahrscheinlich wirklich nur Codes für beide Boards (Arduino Sketch C++11 und Raspi gcc/gpp), die jemand auch bei sich selber testen kann.
Am besten ntl, wenn jemand eine funktionierende Lösung bereits irgendwo (github?) gefunden haben sollte...

wkrug
04.06.2019, 07:25
Hat zwar jetzt nicht direkt was mit dem Thema zu tun, aber wenn Du dich ernsthaft mit Elektronik beschäftigen willst wäre ein kleines Oszi schon nicht schlecht.
Eventuell passt ja so ein JYE Tech Oszi in dein Budget ?
https://www.elektor.de/jye-tech-dso062-oscilloscope-diy-kit
Ist jetzt zwar nichts besonderes, aber für einfache Messungen sollte es reichen.
Der Hersteller hat auch noch andere Modelle auf Lager.

HaWe
04.06.2019, 07:35
Hat zwar jetzt nicht direkt was mit dem Thema zu tun, aber wenn Du dich ernsthaft mit Elektronik beschäftigen willst wäre ein kleines Oszi schon nicht schlecht.
Eventuell passt ja so ein JYE Tech Oszi in dein Budget ?
https://www.elektor.de/jye-tech-dso062-oscilloscope-diy-kit
Ist jetzt zwar nichts besonderes, aber für einfache Messungen sollte es reichen.
Der Hersteller hat auch noch andere Modelle auf Lager.

ja, stimmt schon, hatte ich auch schon mal überlegt, aber ich programmiere lieber als dass ich elektronische Schaltungen entwickle und analysiere.

Thomas$
04.06.2019, 08:50
seh ich das richtig:
du Prüfst ob Daten Im Eingangspuffer sind und liest diese aber nur unter bestimmten Umständen ein?
Wenn jetzt irgendwas in den Eintreffenden Daten kaputt ist scheitert das Programm auf der Empfängerseite

Allgemein ist Polling für soetwas wie UART einlesen nicht sonderlich geeignet, geht auch Eventgesteuert, siehe UART RX Complete Interrupt

ob die software überhaupt etwas tut, lässt sich auch mit blinkender led visuell überprüfen

HaWe
04.06.2019, 09:03
seh ich das richtig:
du Prüfst ob Daten Im Eingangspuffer sind und liest diese aber nur unter bestimmten Umständen ein?
Wenn jetzt irgendwas in den Eintreffenden Daten kaputt ist scheitert das Programm auf der Empfängerseite

Allgemein ist Polling für soetwas wie UART einlesen nicht sonderlich geeignet, geht auch Eventgesteuert, siehe UART RX Complete Interrupt

ob die software überhaupt etwas tut, lässt sich auch mit blinkender led visuell überprüfen

die Arduino-Libs funktionieren mit ihrer internen Puffer- und Timeout-Verwaltung an sich sehr stabil, der von mir verwendete Code funktioniert sowohl für Arduino<->Arduino als auch für Arduino<->PC (Borland C++ Builder Anwendung): nur nicht mit dem Raspi und wiringPi Libs.
Irgendetwas tun tut sie auch momentan, es hängt nur irgendwann irgendwo, nur weiß keiner, wo genau: https://www.raspberrypi.org/forums/posting.php?mode=quote&f=33&p=1475408
alles trouble-shooting hat noch keinen eindeutig Schuldigen ermittelt.

Wenn ich feste Arrays von max. 64byte Größe (= UART-buffer-Größe auf Arduino oder Raspi) übergeben, funktioniert es ebenfalls sehr stabil.

Nur mit den sehr langen Message-Strings von variabler Länge klappt es nicht, dabei mit dem schnelleren Due allerdings noch besser als mit dem AVR.

Allerdings ist genau das das Ziel:
UART-Kommunikation zwischen AVR Mega2560 per Arduino-Serial über verschieden lange messages, denen nur gemeinsam ist: dass sie mit "§" beginnen und mit "\n" enden.
Dazu werden jetzt Fachleute gesucht, die den Code selber verfeinern und testen können, per Arduino Sketch und Raspi gcc.

Ceos
04.06.2019, 09:18
Dazu werden jetzt Fachleute gesucht

Dann musst du aber auch ein Fachgerechtes Gehalt zahlen :P

Scherz beiseite! Hast du schonmal darüber nachgedacht eine CRC Prüfung einzubauen? https://github.com/dwblair/arduino_crc8/blob/master/examples/crc8test.ino

So könntest du bei einem timeout während dem Empfang einer unvollständigen Nachricht einafch den Puffer löschen und auf die nächste Eingabe warten und ggf. Fehlerhafte Pakete zum Beispiel byteweise ausgeben um herauszufinden an welcher Stelle die Kommunikation unterbrochen wird (in meinem Fall mit einem STM32NUCLEO Board war es der STLink Chip der als Programmer zwischen dem eigentlichen Controller und dem USB steckt der ab dem 212ten Byte angefangen hat einzelne Bytes zu dropppen)

Holomino
04.06.2019, 09:39
Wie ist denn das mit folgender Zeile?
inputString += inChar;

Wird da nicht jedes mal neuer Speicher auf'm heap alloziiert und der ganze Summs dahin kopiert?
(zumindest deutet der Complexity-Absatz unter http://www.cplusplus.com/reference/string/string/append/ darauf hin, dass ein string keine verkettete Liste ist).

Ceos
04.06.2019, 09:47
Jein @Holomino

der "+"-Operator für Strings ist überschrieben mit der concat() Funktion

man bräuchte mehr Details/Code um die Auswirkung bestimmen zu können. Wenn die Variable außerhalb der Methode deklariert ist sollte es kein Problem damit geben.

Aber du hast recht was das Design angeht ... wenn ich nur eine maximale definierte Zahl an Bytes empfange, sollte man IMMER einen statischen Speicher verwenden um solch "volatilen" Daten zu verarbeiten, das ist nicht nur performanter, sondern vermeidet auch Nebeneffekte wie du sie beschreibst

Thomas$
04.06.2019, 10:45
das Design kann man allgemein verbessern
Elementweiser zugriff auf statisches array z.b.


#DEFINE MAXLEN = 1024
...
char inputString[MAXLEN];
...
inputString[n] = serialGetchar(Serial);
if (inputString[n]=="\n")
{
stringComplete = true;
...
}
...
n++;

Interrup vs. Polling
"If abfragen" mit Klammern machen es übersichtlicher und vermeidet Fehler


if(foo == 42) // Leerzeichen zwischen == und dem Rest
{
doBar;
}
else
{
doFoo
}

zum überprüfen ob der Buffer evtl. überläuft kann man das Resultat von serialDataAvail(Serial) ausgeben
Auf dem Raspi kann man sich auch mal dmesg "sudo dmesg" ausgeben lassen, damit sollte man Fehler von USB-Serial adapter+treiber finden können. bzw auch Speicherzugriffsfehler von deinem Programm

HaWe
04.06.2019, 11:26
Jein @Holomino

der "+"-Operator für Strings ist überschrieben mit der concat() Funktion

man bräuchte mehr Details/Code um die Auswirkung bestimmen zu können. Wenn die Variable außerhalb der Methode deklariert ist sollte es kein Problem damit geben.

Aber du hast recht was das Design angeht ... wenn ich nur eine maximale definierte Zahl an Bytes empfange, sollte man IMMER einen statischen Speicher verwenden um solch "volatilen" Daten zu verarbeiten, das ist nicht nur performanter, sondern vermeidet auch Nebeneffekte wie du sie beschreibst

wenn du dir meinen Code genau ansiehst, mache ich das ja schon genau so.
Wirklich weiterhelfen täte aber jetzt nur ein verbesserter, vollständiger, kompilierbarer Code sowohl für Raspi als auch (falls unbedingt nötig) für den Arduino, womit ich die neue Variante dann auch sofort testen könnte.

Ceos
04.06.2019, 11:49
basierend auf deinem Code fetzen (mir war entgangen dass weiter vorne noch mehr code steht :D)




uint8_t stringBuffer[MAXLEN] ;


if (serialDataAvail(Serial)) {
while(!stringComplete && n<MSGLEN-1) {
if(n==MSGLEN-2) inChar='\n'; // emergency brake
else
inChar = serialGetchar(Serial);

if(inChar=='\n' || inChar>=' ') stringBuffer[n] = inChar;
if (inChar == '\n') {
stringComplete = true;
}
n++;
}
}

du musst dann nur den stringBuffer in dein inputString kopieren bevor du ihn weiter verwendest (oder den inputString gleich ganz verwerfen bzw. umdeklarieren)

für MAXLEN wäre natürlich sinnvoll MSGLEN zu wählen

HaWe
04.06.2019, 11:57
basierend auf deinem Code fetzen (mir war entgangen dass weiter vorne noch mehr code steht :D)




uint8_t stringBuffer[MAXLEN] ;


if (serialDataAvail(Serial)) {
while(!stringComplete && n<MSGLEN-1) {
if(n==MSGLEN-2) inChar='\n'; // emergency brake
else
inChar = serialGetchar(Serial);

if(inChar=='\n' || inChar>=' ') stringBuffer[n] = inChar;
if (inChar == '\n') {
stringComplete = true;
}
n++;
}
}

du musst dann nur den stringBuffer in dein inputString kopieren bevor du ihn weiter verwendest (oder den inputString gleich ganz verwerfen bzw. umdeklarieren)

für MAXLEN wäre natürlich sinnvoll MSGLEN zu wählen

klar, genau so mache ich es ja:

strcpy (mbuf, inputString.c_str() );

auch eine Variante mit strncpy habe ich getestet, macht aber keinen Unterschied zur Laufzeit.

int len=min( (int)inputString.length(), (int)MSGLEN-1) );
strncpy (mbuf, inputString.c_str(), len );

HaWe
08.06.2019, 09:38
update,
soweit ich es beurteilen kann ist der Arduino code jetzt fail-safe, weil Serial.readStringUntil() intern alle Puffer und Timeouts selber verwaltet.
(Arduino = Mega2560)

Der Code wurde inzwischen geupdated zum Debuggen:
i0 wird jetzt incrementiert in jeder Arduino loop und dann zurückgesendet,
und i2 wird jetzt incrementiert in jeder Raspi loop und dann zurückgesendet.

Es geht einige Sekunden oder gar Minuten gut, dann hängt sich das System plötzlich aber wieder auf:

Arduino code:


// Arduino Serial to Raspi USB

// history:
// 0705 debug value (i2) from Raspi
// 0704 Serial.readStringUntil(), debug value (i0) from Arduino
// 0703 simple msg str
// 0702 fixes
// 0701 Serial.read delimiters
// 0700
// 0101 ported for RPi USB
//
// 0009
// 0008 analog aX=analogRead(AX) (X=0...11), syntax "&AX=%ld",aX
// 0007 pin-loop, 6x out-pins OUTn(I/O/pwm), &_ALLPINS_=0 when BCB-close
// 0006 output pins DPINn
// 0003 send/receive strings
// 0002 receiving strings, pattern: &varname1=value1;
// 0001 receiving simple Serial char

// ver 0705

const uint32_t UARTclock = 115200;

int32_t i0=0,i1=0,i2=0,i3=0,i4=0,i5=0,i6=0,i7=0,i8=0,i9=0; // int (general)



String inputString="";

#define TOKLEN 30
#define MSGLEN 1024
char mbuf[MSGLEN]; // cstring msg buffer
char cval[TOKLEN]; // number as cstring


//================================================== ================
// string tools
//================================================== ================

int16_t strstrpos(const char * haystack, const char * needle) // find 1st occurance of substr in str
{
const char *p = strstr(haystack, needle);
if (p) return static_cast<int16_t>(p-haystack);
return -1; // Not found = -1.
}


char * cstringarg( const char* haystack, const char* vname, char* sarg ) {
int i=0, pos=-1;
unsigned char ch=0xff;
const char* kini = "&"; // start of varname: '&'
const char* kin2 = "?"; // start of varname: '?'
const char* kequ = "="; // end of varname, start of argument: '='

const int NLEN=30;
char needle[NLEN] = ""; // complete pattern: &varname=abc1234


strcpy(sarg,"");
strcpy(needle, kini);
strcat(needle, vname);
strcat(needle, kequ);
pos = strstrpos(haystack, needle);
if(pos==-1) {
needle[0]=kin2[0];
pos = strstrpos(haystack, needle);
if(pos==-1) return sarg;
}
pos=pos+strlen(vname)+2; // start of value = kini+vname+kequ
while( (ch!='&')&&(ch!='\0') ) {
ch=haystack[pos+i];
if( (ch=='&')||(ch==';')||(ch=='\0') ||(ch=='\n')
||(i+pos>=(int)strlen(haystack))||(i>NLEN-2) ) {
sarg[i]='\0';
return sarg;
}
if( (ch!='&') ) {
sarg[i]=ch;
i++;
}
}
return sarg;
}


//================================================== ================
// setup
//================================================== ================
void setup() {
Serial.begin(UARTclock);

pinMode(LED_BUILTIN, OUTPUT);
digitalWrite(LED_BUILTIN, LOW);

i0=0;
i1=1;
i2=-22;
i3=33;
i4=-444;
i5=5555;
i6= (uint16_t)B10101010*256;
i6+=(uint16_t)B10101010;

}


//================================================== ================
// loop
//================================================== ================

void loop() {

//-------------------------------------------------------------
// receive

int n=0;
char inChar;

if(Serial.available()) {
inputString=Serial.readStringUntil('\n');
inputString.toCharArray(mbuf, min( (int)inputString.length(), MSGLEN-1) );
}

//----------------------
// process mbuf!

// debug: check for changed i2 by Raspi
cstringarg(mbuf, "i2", cval); //
if(strlen(cval)>0) {
i2=(int32_t)atol(cval);
}


//----------------------
inputString="";

//delay
delay(1);



//-------------------------------------------------------------
// send

//Serial.flush(); // debug: not needed

char formatstr[MSGLEN];
strcpy(formatstr, "§");
strcat(formatstr, "&i0=%ld;&i1=%ld;&i2=%ld;&i3=%ld;&i4=%ld;&i5=%ld;&i6=%ld;\n");
sprintf(mbuf, formatstr, i0,i1,i2,i3,i4,i5,i6 );

//for (int i=0; i<strlen(mbuf); i++ ) Serial.print(mbuf[i]);
Serial.print(mbuf);

//delay?
delay(1);

i0++;


}



Raspi code:



/*
UART communication
send/receive string of tokens
*
Raspberry Pi master
ver 0702
*/

/*
* change log
* 0702: debug value (i2) from Raspi
* 0701: Serial Read delimiter, debug value (i0) from Arduino
* 0700: first adjustments
* 0669: UART 115200 baud *
* 0667: Arduino via USB = ACM0
*
*/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string>
#include <math.h>
#include <fcntl.h>
#include <string.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <time.h>
#include <sys/time.h>
#include <errno.h>
#include <pthread.h>


#include <wiringPi.h>
#include <wiringSerial.h>

#define byte uint8_t

char uart[128] = "/dev/ttyACM0";
int Serial;
const uint32_t UARTclock = 115200;

int32_t i0=0,i1=0,i2=0,i3=0,i4=0,i5=0,i6=0,i7=0,i8=0,i9=0; // int (general)
//int32_t i0,i1,i2,i3,i4,i5,i6,i7,i8,i9;


#define TOKLEN 30
#define MSGLEN 1024
#define iINVALID -29999


std::string inputString="";
char cval[TOKLEN]; // number as cstring
char mbuf[MSGLEN]; // cstring msg buffer






//================================================== ================
// string tools
//================================================== ================


int16_t strstrpos(const char * haystack, const char * needle) // find 1st occurance of substr in str
{
const char *p = strstr(haystack, needle);
if (p) return static_cast<int16_t>(p-haystack);
return -1; // Not found = -1.
}


char * cstringarg( const char* haystack, const char* vname, char* sarg ) {
int i=0, pos=-1;
unsigned char ch=0xff;
const char* kini = "&"; // start of varname: '&'
const char* kin2 = "?"; // start of varname: '?'
const char* kequ = "="; // end of varname, start of argument: '='

const int NLEN=30;
char needle[NLEN] = ""; // complete pattern: &varname=abc1234


strcpy(sarg,"");
strcpy(needle, kini);
strcat(needle, vname);
strcat(needle, kequ);
pos = strstrpos(haystack, needle);
if(pos==-1) {
needle[0]=kin2[0];
pos = strstrpos(haystack, needle);
if(pos==-1) return sarg;
}
pos=pos+strlen(vname)+2; // start of value = kini+vname+kequ
while( (ch!='&')&&(ch!='\0') ) {
ch=haystack[pos+i];
if( (ch=='&')||(ch==';')||(ch=='\0') ||(ch=='\n')
||(i+pos>=(int)strlen(haystack))||(i>NLEN-2) ) {
sarg[i]='\0';
return sarg;
}
if( (ch!='&') ) {
sarg[i]=ch;
i++;
}
}
return sarg;
}



//================================================== ================
// serial TCP
//================================================== ================


void loop() {

while(1) {

static bool stringComplete = false;


//-------------------------------------------------------------
// send

// debug
i2++; // change value to send back to Arduino

char formatstr[MSGLEN];

// debug, cut-down:
strcpy(formatstr, "§");
strcat(formatstr, "&i0=%d;&i1=%d;&i2=%d;&i3=%d;\n");
sprintf(mbuf, formatstr, i0,i1,i2,i3);

for(uint8_t i=0; i<strlen(mbuf); i++) { //
serialPutchar( Serial, mbuf[i]); // Send values to the slave
}


//delay?
delay(1);




//-------------------------------------------------------------
// receive

int n=0;
char inChar;
char cstr[TOKLEN];

inputString="";
stringComplete = false;

if (serialDataAvail(Serial)) {
while(!stringComplete && n<MSGLEN-1) {
if(n==MSGLEN-2) inChar='\n'; // emergency brake
else
inChar = serialGetchar(Serial);

if(inChar=='\n' || inChar>=' ') inputString += inChar;
if (inChar == '\n') {
stringComplete = true;
}
n++;
}
}


if (stringComplete) {

//inputString.to_str(mbuf, len-1);
strcpy (mbuf, inputString.c_str() );

fprintf(stderr,"\n"); fprintf(stderr,mbuf); //fprintf(stderr,"\n");


// cstringarg( char* haystack, char* vname, char* carg )
// haystack pattern: &varname=1234abc; delimiters &, \n, \0, EOF


cstringarg(mbuf, "i0", cval); //
if(strlen(cval)>0) {
sprintf (cstr, "i0=%d \n", (int32_t)atol(cval));
fprintf(stderr, cstr);
}
cstringarg(mbuf, "i1", cval); //
if(strlen(cval)>0) {
sprintf (cstr, "i1=%d \n", (int32_t)atol(cval));
fprintf(stderr, cstr);
}
cstringarg(mbuf, "i2", cval); //
if(strlen(cval)>0) {
sprintf (cstr, "i2=%d \n", (int32_t)atol(cval));
fprintf(stderr, cstr);
}
cstringarg(mbuf, "i3", cval); //
if(strlen(cval)>0) {
sprintf (cstr, "i3=%d \n", (int32_t)atol(cval));
fprintf(stderr, cstr);
}
cstringarg(mbuf, "i4", cval); //
if(strlen(cval)>0) {
sprintf (cstr, "i4=%d \n", (int32_t)atol(cval));
fprintf(stderr, cstr);
}

inputString="";
stringComplete = false;

//delay?
delay(1);
}

}
}




//================================================== ================


int main() {

printf("initializing..."); printf("\n");

// UART Serial com port
Serial = serialOpen (uart, UARTclock);
printf("starting UART loop..."); printf("\n");

loop();

serialClose( Serial);
exit(0);
}




output e.g.


§&i0=17628;&i1=1;&i2=17434;&i3=33;&i4=-444;&i5=5555;&i6=43690;
i0=17628
i1=1
i2=17434
i3=33
i4=-444

§&i0=17629;&i1=1;&i2=17435;&i3=33;&i4=-444;&i5=5555;&i6=43690;
i0=17629
i1=1
i2=17435
i3=33
i4=-444

§&i0=17630;&i1=1;&i2=17436;&i3=33;&i4=-444;&i5=5555;&i6=43690;
i0=17630
i1=1
i2=17436
i3=33
i4=-444

§&i0=17631;&i1=1;&i2=17437;&i3=33;&i4=-444;&i5=5555;&i6=43690;
i0=17631
i1=1
i2=17437
i3=33
i4=-444

§&i0=17632;&i1=1;&i2=17438;&i3=33;&i4=-444;&i5=5555;&i6=43690;
i0=17632
i1=1
i2=17438
i3=33
i4=-444

§&i0=17633;&i1=1;&i2=17439;&i3=33;&i4=-444;&i5=5555;&i6=43690;
i0=17633
i1=1
i2=17439
i3=33
i4=-444

§&i0=17634;&i1=1;&i2=17440;&i3=33;&i4=-444;&i5=5555;&i6=43690;
i0=17634
i1=1
i2=17440
i3=33
i4=-444


!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! hangs up !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


hat jemand vielleicht doch die rettende Idee?

HaWe
08.06.2019, 15:20
PS, @Ceos, Holomino:
wegen eurer Anmerkungen von oben:
Grundsätzlich sind "klassische" C cstring Funktionen eher fehleranfällig als C++ std::string bzw. String-Methoden (0-Terminierung, Längenüberschreitung etc.), und noch nicht einmal originale <string.h> u/o <stdio.h> Funktionen verhalten sich hier immer identisch, logisch und/oder vorhersagbar.

Arduino String entspricht aber einem abgespeckten std::string samt vieler seiner Methoden, und für beide sind

String msg, s;
char cstr[30];
msg+= s;
msg+= (String)cstr;

völlig normale und legitime C++ Operationen, und Arduino arbeitet ja mit C++ und nicht mit C.

Da die endgültige Länge von msg bei mir zur Laufzeit aber ständig wechselt und daher nicht bekannt ist, sondern nur: wie das terminierende byte aussieht ('\n'), wird gelesen bis '\n' erreicht ist.
Genau wie std::string verwaltet Arduino String den notwendigen Speicher bei String-Methoden selbstständig, gerade auch bei der "Addition" (Concatenierung).
Zur weiteren Verabeitung von enthaltenen "tokens" über ANSI C - <string.h> Funktionen wird dann der Inhalt von msg in einen Array definierter Maximallänge kopiert und dort weiterverarbeitet.
(Außerdem ist das später in threads auch einfacher über mutexe zu schützen.)

Übrigens kompiliere ich auch den Raspi code mit gpp/C++, nicht mit gcc/C.

Betr. UART:
Im Gegensatz zur Arduino Serial() class (vererbt von der Stream class) inkl. vieler eingebauter Puffer- und Timeout-Methoden arbeitet wiringPi per wiringSerial aber mit "klassischem" C, ohne eingebaute Korrekturmethoden, und daher vermute ich momentan die "Aufhänger" eher ursächlich Raspi-seitig (CMIIW)...

HaWe
12.06.2019, 18:01
ich vermute, ich habe das Problem gelöst!
Ich habe die UART-Kommunikation in einen extra pthread mit prio 60 ausgelagert, jetzt läuft es seit knapp 1 1/2 Stunden stabil ohne Unterbrechungen! 8)

update: schon über 3 Stunden fehlerfrei - ich denke ja, jetzt hab ich's! 8)


hier der aktuell getestete Code:




/*
UART communication
send/receive string of tokens
*
Raspberry Pi master
ver 0704
*/

/*
* change log
* 0704: extra console thread
* 0703: pthread MT
* 0702: debug value (i2) from Raspi
* 0701: Serial Read delimiter, debug value (i0) from Arduino
* 0700: first adjustments
* 0669: UART 115200 baud *
* 0667: Arduino via USB = ACM0
*
*/


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdint.h>

#include <math.h>
#include <fcntl.h>
#include <string.h>
#include <string>
#include <sys/ioctl.h>
#include <sys/time.h>
#include <errno.h>
#include <pthread.h>

#include <linux/input.h>
#include <termios.h>
#include <signal.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/select.h>

#include <wiringPi.h>
#include <wiringSerial.h>

#define byte uint8_t

char uart[128] = "/dev/ttyACM0";
int Serial;
const uint32_t UARTclock = 115200;

int32_t i0=0,i1=0,i2=0,i3=0,i4=0,i5=0,i6=0,i7=0,i8=0,i9=0; // int (general)
//int32_t i0,i1,i2,i3,i4,i5,i6,i7,i8,i9;


#define TOKLEN 30
#define MSGLEN 1024
#define iINVALID -29999


std::string inputString="";
char cval[TOKLEN]; // number as cstring
char mbuf[MSGLEN]; // cstring msg buffer

volatile uint8_t _TASKS_ACTIVE_= 1;






//================================================== ================
// tools
//================================================== ================

// ================================================== ==========
// conio.h

bool kbhit(void)
{
struct termios original;
tcgetattr(STDIN_FILENO, &original);

struct termios term;
memcpy(&term, &original, sizeof(term));

term.c_lflag &= ~ICANON;
tcsetattr(STDIN_FILENO, TCSANOW, &term);

int characters_buffered = 0;
ioctl(STDIN_FILENO, FIONREAD, &characters_buffered);

tcsetattr(STDIN_FILENO, TCSANOW, &original);

bool pressed = (characters_buffered != 0);

return pressed;
}

void echoOff(void)
{
struct termios term;
tcgetattr(STDIN_FILENO, &term);

term.c_lflag &= ~ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}

void echoOn(void)
{
struct termios term;
tcgetattr(STDIN_FILENO, &term);

term.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &term);
}




// ================================================== ==========
// string tokens

int16_t strstrpos(const char * haystack, const char * needle) // find 1st occurance of substr in str
{
const char *p = strstr(haystack, needle);
if (p) return static_cast<int16_t>(p-haystack);
return -1; // Not found = -1.
}


char * cstringarg( const char* haystack, const char* vname, char* sarg ) {
int i=0, pos=-1;
unsigned char ch=0xff;
const char* kini = "&"; // start of varname: '&'
const char* kin2 = "?"; // start of varname: '?'
const char* kequ = "="; // end of varname, start of argument: '='

const int NLEN=30;
char needle[NLEN] = ""; // complete pattern: &varname=abc1234


strcpy(sarg,"");
strcpy(needle, kini);
strcat(needle, vname);
strcat(needle, kequ);
pos = strstrpos(haystack, needle);
if(pos==-1) {
needle[0]=kin2[0];
pos = strstrpos(haystack, needle);
if(pos==-1) return sarg;
}
pos=pos+strlen(vname)+2; // start of value = kini+vname+kequ
while( (ch!='&')&&(ch!='\0') ) {
ch=haystack[pos+i];
if( (ch=='&')||(ch==';')||(ch=='\0') ||(ch=='\n')
||(i+pos>=(int)strlen(haystack))||(i>NLEN-2) ) {
sarg[i]='\0';
return sarg;
}
if( (ch!='&') ) {
sarg[i]=ch;
i++;
}
}
return sarg;
}






//================================================== ================
// threads
//================================================== ================


void* UART_thr( void* ) {

while(_TASKS_ACTIVE_) {

static bool stringComplete = false;

//serialFlush(Serial);

//-------------------------------------------------------------
// send

// debug
i2++; // change value to send back to Arduino

char formatstr[MSGLEN];

// debug, cut-down:
strcpy(formatstr, "§");
strcat(formatstr, "&i0=%d;&i1=%d;&i2=%d;&i3=%d;\n");
sprintf(mbuf, formatstr, i0,i1,i2,i3);

for(uint8_t i=0; i<strlen(mbuf); i++) { //
serialPutchar( Serial, mbuf[i]); // Send values to the slave
}

//delay?
delay(1);


//-------------------------------------------------------------
// receive

inputString="";
char cstr[TOKLEN];

int n=0;
char inChar;

stringComplete=false;

if (serialDataAvail(Serial))
{
while(n<MSGLEN-1) {
if(n==MSGLEN-2) inChar='\n'; // emergency brake
else
inChar = serialGetchar(Serial);

if(inChar=='\n' || inChar>=' ') inputString += inChar;
if (inChar == '\n') {
stringComplete = true;
break;
}
n++;
}
}

if (stringComplete) {

//inputString.to_str(mbuf, len-1);
strcpy (mbuf, inputString.c_str() );

fprintf(stderr,"\n"); fprintf(stderr,mbuf); //fprintf(stderr,"\n");


// cstringarg( char* haystack, char* vname, char* carg )
// haystack pattern: &varname=1234abc; delimiters &, \n, \0, EOF


cstringarg(mbuf, "i0", cval); //
if(strlen(cval)>0) {
sprintf (cstr, "i0=%d \n", (int32_t)atol(cval));
fprintf(stderr, cstr);
}
cstringarg(mbuf, "i1", cval); //
if(strlen(cval)>0) {
sprintf (cstr, "i1=%d \n", (int32_t)atol(cval));
fprintf(stderr, cstr);
}
cstringarg(mbuf, "i2", cval); //
if(strlen(cval)>0) {
sprintf (cstr, "i2=%d \n", (int32_t)atol(cval));
fprintf(stderr, cstr);
}


inputString="";
stringComplete = false;

//delay?
delay(1);
}
}
return NULL;
}


//=================================================


void* KBD_thr(void*) // low priority: keyboard monitoring // low priority: keyboard monitoring
{
int ch; // keyboard scancode, letter char

while(_TASKS_ACTIVE_) {

ch=0;
echoOff();

if (kbhit()) {
ch=getchar();
if(ch>=32 && ch<127) {
printf("%c", ch);
fflush(stdout);
}
else if (ch<32) {
if (kbhit()) ch=getchar();
}
if(ch==27) _TASKS_ACTIVE_ = false;
}
delay(50);
}
return NULL;
}



//================================================== ================
//================================================== ================

int main() {

// threads
pthread_t thread1, thread2;
struct sched_param param;

printf("starting ..."); printf("\n");

// open UART Serial com port
Serial = serialOpen (uart, UARTclock);


// start multithreading

pthread_create(&thread1, NULL, KBD_thr, NULL); // low priority: keyboard monitor
param.sched_priority = 40;
pthread_setschedparam(thread1, SCHED_RR, &param);

pthread_create(&thread2, NULL, UART_thr, NULL); // medium priority: UART
param.sched_priority = 40;
pthread_setschedparam(thread2, SCHED_RR, &param);


while(_TASKS_ACTIVE_) { delay(10); }

// wait for threads to join before exiting
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);

serialClose( Serial);
exit(0);
}