Uso di udp e seriale per controllo scheda MD49 : latenze di comunicazione

Ciao,
come da buona educazione, dopo essermi presentato sono schizzato qui a postare le mie domande/dubbi/ignoranze! :smiley:

Ho in corso la realizzazione di un piccolo rover casalingo (o più in generale indoor) che avevo motorizzato con una scheda MD49 +motori EMG49 (presi da robotitaly). Il tutto controllato da seriale del pc (vecchio ieeepc asus con windows).
Ultimamente ho voluto provare ad aggiungere arduino come driver della scheda MD49 per 'scaricare' il pc da un po' di lavoro. Ho fatto un po' di prove e devo dire che sono rimasto felicemente colpito dalla sua facilità d'uso e dalla mole di informazioni che si possono trovare in rete.

La configurazione hw con cui sto facendo le prove è fatta da:

  • arduino uno + ethernet shield
  • MD49

Arduino comunica col pc via ethernet-udp e accetta dei comandi di movimentazione (per ora avanti/indietro, ruota); quindi provvede, via SoftwareSerial, a inviare i comandi appropriati alla MD49 e a leggerne lo stato (encoders, condizioni di errore) in modo da chiudere un loop di posizione; la MD49 si pilota in velocità. Ho misurato le latenze di comunicazione arduino/MD49 e in media ho quasi 10ms fra 1)invio comando, 2)richiesta/ricezione encoders e 3)richiesta/ricezione errori.

Per gestire questi ritardi e mantenere una buona reattività alla comunicazione udp avevo provato a 'pollare' la presenza di pacchetti udp con un timer (libreria TimerOne). La routine del timer verifica la presenza di un nuovo pacchetto, invia l'ACK, lo decodifica e lo mette in un buffer (coda dei comandi) che viene successivamente letto dal loop(). Purtroppo col timer1 in funzione non solo la comunicazione udp andava in stallo ma anche la lettura degli encoders (ricevuta su pin7) era errata, quindi ho dovuto rinunciarci (almeno per ora) anche se resto dell'avviso che una gestione asincrona (interrupt) dell'udp sarebbe la cosa migliore.

La soluzione corrente quindi è consistita nel fare tutto nel loop(). Di seguito posto il codice (che contiene qualche reminiscenza del precedente approccio).

void loop(){
byte command = NO_CMD;
boolean mustRead;
float data[2];

//La seguente riga era gestita come ISR del timer.
udpCheckAndManage();
//Se ho nuovi comandi ...
//noInterrupts();
if(packready) {
command = _command;
data[0] = _data[0];
data[1] = _data[1];

mustRead = packready;
packready = false;
_command = NO_CMD;
}
//interrupts();

if(mustRead) {
Serial.print("command: ");
Serial.print(command,HEX);
Serial.println();
}

switch(command) {
case NO_CMD: break;

case MOVE_FWD:
case MOVE_BWD:
Serial.println("setspeeds :");
Serial.println(_data[0]);
Serial.println(_data[1]);
board.set_speeds(_data[0], _data[1]);
break;
case ROTATE:
Serial.println("setspeeds :");
Serial.println(_data[0]);
Serial.println(_data[1]);
board.set_speeds(_data[0], _data[1]);
break;
case INIT_CMD:
board.disable_timeout();
delay(10);
board.set_mode(MODE_SIGNED_SPEED_WHEELS);
delay(10);
board.reset_encoders();
delay(10);
inited = true;

Serial.println("Inited...\tSending URM to");
Serial.print(Udp.remoteIP());
Serial.print(" : ");
Serial.println(UDP_O_PORT);
break;
case STOP:
board.set_speeds(0.0, 0.0);
Serial.println("Stopped...");
break;
}

//MD49 state reading
//1. read encoders and speeds
//2. get error state
if(inited) {
byte encs[8];
long _encs[2];
boolean res = board.get_encoders(encs);
if(!res){
Serial.println("Fail read encoders");
return;
}

Serial.print("Encs ");
for(int i=0;i<4;++i){ Serial.print(encs_, HEX); Serial.print("-");}_
_ Serial.print(" ");_
for(int i=4;i<8;++i){ Serial.print(encs_, HEX); Serial.print("-");}
Serial.println("");
* encs[0] = board.decode_encoder(&encs[0]);
encs[1] = board.decode_encoder(&encs[4]);
_
//*

Serial.print("Encoders ");

Serial.print(encs[0]);
Serial.print(" ");_

Serial.println(_encs[1]);
* //*
* //noInterrupts();*
* sendURM(&Udp, _encs[0], encs[1]);
_
//interrupts();*

* }*
}
[/quote]
E finalmente le domande!!!
Qualcuno ha già avuto a che fare con problemi simili? Cioè vi siete trovati a dover gestire contemporaneamente comunicazioni (o dispositivi) con velocità molto diverse fra loro? Che tipo di soluzioni si possono utilizzare ? E, infine, è possibile che la TimerOne incasini l'udp o la comunicazione seriale sw sui pint 7 e 8?

l'arduino ha 3 timer, di cui il timer0 si stende a lasciare inalterato (funzioni delay e milli), e restanti devi quindi fare molta attenzione perchè spesso sono usati dalle librerie: quindi si tratta di un caso di incompatibilita. Gaurda il looper di leo72, usa il timer2 e FORSE non ti incasina nulla.

Cioè vi siete trovati a dover gestire contemporaneamente comunicazioni (o dispositivi) con velocità molto diverse fra loro?

dipende.
Potresti decidere di leggere i messaggi parziali di ogni comunicazione ogni loop, e quando rilevi che un messaggio è completo lo metti in esecuzione (o in un buffer di cose da fare)

Oppure potresti attendere un messaggio completo sulla comunicazione più lenta, e a quel punto cercare di leggere un messaggio sulla comunicazione veloce.

lesto:
dipende.
Potresti decidere di leggere i messaggi parziali di ogni comunicazione ogni loop, e quando rilevi che un messaggio è completo lo metti in esecuzione (o in un buffer di cose da fare)

Il fatto è che la macchina a stati finale non sarà troppo semplice. Seguendo questa linea la renderei ancora più incasinata

lesto:
Oppure potresti attendere un messaggio completo sulla comunicazione più lenta, e a quel punto cercare di leggere un messaggio sulla comunicazione veloce.

Questo lo condivido molto di più. In effetti alternare alla seriale lenta un check/processamento di nuovo messaggio da udp sarebbe l'ideale (l'efficienza mi ripagherebbe la complicazione della macchina a stati). L'uso di un timer (stasera provo con Looper e acnhe con la FrequencyTimer2) resta abbastanza importante per poter inviare al più presto l'ACK del mio protocollo udp e liberare il pc.

grazie

Il fatto è che la macchina a stati finale non sarà troppo semplice. Seguendo questa linea la renderei ancora più incasinata

se incaspluli il codice in classi non dovrebbe essere complicato, anzi sarebbe altamente riusabile

Questo lo condivido molto di più. In effetti alternare alla seriale lenta un check/processamento di nuovo messaggio da udp sarebbe l'ideale (l'efficienza mi ripagherebbe la complicazione della macchina a stati). L'uso di un timer (stasera provo con Looper e acnhe con la FrequencyTimer2) resta abbastanza importante per poter inviare al più presto l'ACK del mio protocollo udp e liberare il pc.

però ogni messaggio seriale fallo seguire da uno svuotamento del buffer UDP (non conosco il tuo protocollo) altrimenti riempi il buffer UDP e perdi dati per strada

lesto:
se incaspluli il codice in classi non dovrebbe essere complicato, anzi sarebbe altamente riusabile

si, appena avrò preso le misure del c++ arduino e della memoria disponibile inizierò a fare pure quello ... sono 'intimorito' dalle ristrettezze di risorse dei micro (abituato al pc) :grin:

però ogni messaggio seriale fallo seguire da uno svuotamento del buffer UDP (non conosco il tuo protocollo) altrimenti riempi il buffer UDP e perdi dati per strada

mmmh ... ma non si svuota 'automaticamente' quando leggo i pacchetti ... urge ri-studio della eth-library!!!
Comunque il consiglio è ben accetto.

s', il buffer si svuota di quello che leggi, ma se leggi mano velocemente di quanto il buffer si riempie...

yesss tutto 'protocollato' ....

grazie1000 per lo scambio di idee

de nada