Problema con la seriale

Salve a tutti, ho un problema con la comunicazione seriale e forse mi sta sfuggendo qualcosa...
Devo far comunicare uno standalone provvisto di chip MAX232 con la porta seriale di un vecchio pc con sopra windows xp; sul pc per inviare i dati sulla seriale devo usare per forza uno script eseguito in un file bat che qui sotto riporto:

mode com1:9600,n,8,1
echo 0A& >com1

Premettendo che il MAX232 è collegato bene visto che l'ho usato diverse volte, non so per quale motivo la comunicazione non funziona a dovere, infatti tutto fila liscio soltanto se invio un solo carattere. Come vedete il file bat contiene solo due istruzioni, la prima riga setta i parametri per la comunicazione con la porta seriale e successivamente invia la stringa "0A&".
Qui sotto invece inserisco lo sketch che uso sull'arduino.

// variabili per la gestione del protocollo seriale
char serialBuffer[40]; // buffer per i messaggi che arrivano dalla seriale
int indiceBuffer = 0; // variabile per contenere l'indice dell'array
boolean msgTrasmesso = false;// dice se il messaggio è stato trasmesso

void setup()
{
delay(2000);
Serial.begin(9600);
}

void loop()
{
/* trasmetto un solo carattere e funziona
if(Serial.available())
{
char lettera = Serial.read();
if(lettera == 'A')
{
accensione();
}
else if(lettera == 'B')
{
spegnimento();
}
}
*/
if(Serial.available())
{
char lettera = Serial.read();
if(lettera != '&')// '&' è il carattere di fine messaggio
{ // se quello che arriva è diverso dal carattere di fine messaggio
serialBuffer[indiceBuffer] = lettera;// popola l'array del buffer
indiceBuffer++;//incrementa l'indice dell'array del buffer per dare la posizione al carattere successivo
}
else // se è arrivato il carattere di fine messaggio
{
indiceBuffer = 0; // l'indice ritorna a 0
msgTrasmesso = true; // il messaggio è stato trasmesso
}
}
// analizza il buffer
if(msgTrasmesso == true)
{
switch(serialBuffer[0])
{
case '0':
{
switch(serialBuffer[1])
{
case 'A':
{
accensione();
}
break;
case 'B':
{
spegnimento();
}
break;
}
memset(serialBuffer,0,sizeof(serialBuffer)); // svuota il buffer
msgTrasmesso = false;
}
break;
default: // se arriva qualcosa non previsto svuota il buffer
{
memset(serialBuffer,0,sizeof(serialBuffer));
msgTrasmesso = false;
}
}
}
}

Le istruzioni commentate nel loop le ho inserite per vedere se effettivamente arriva qualche dato e funzionano alla perfezione; in questo modo trasmetto un solo carattere dal file bat.
Lo sketch che non mi funziona invece prevede l'arrivo di più caratteri: il primo '0', il secondo 'A' e infine il carattere '&' che ho utilizzato come carattere di fine messaggio. Ogni volta che arriva un carattere e se questo è diverso da '&', lo vado ad inserire in un array incrementandone l'indice fino a quando non arriva il carattere di fine messaggio (l'ho spiegato da cani, ma credo che si capisca dai....). Giusto per completare il discorso dico che entrambi gli switch che ho messo mi servono perchè devo gestire più messaggi.
Perchè non funziona questo codice? Mi sa che mi sto perdendo per una sciocchezza....
Help

Edit:
So che la faccenda di incrementare l'indice per l'array è proprio una porcheria, avevo provato con la funzione while(Serial.available()) ma non funzionava ugualmente....

L'errore è nel file bat, se lo lanci da finestra dos (CMD) ti viene fornito un errore "sintassi del comando errata".
Quello che da fastidio è il carattere '&' perché è utilizzato dalla sintassi per la redirezione dell'output pertanto non è possibile usarlo come carattere da inviare.
Usa un differente carattere come terminatore d'invio, p.e. la @, ricordati che ti viene sempre aggiunto un 0x0A e un 0x0D (CR e LF) alla fine della stringa e tanti 0x20 (spazio) per ogni spazio compreso tra la fine della stringa e il simbolo di redirezione (>).

ricordati che ti viene sempre aggiunto un 0x0A e un 0x0D (CR e LF) alla fine della stringa

Caspita che bella seccatura... mi pareva strano che dipendesse dal codice sull'arduino.
Mi sa che installerò processing su quel pc per dialogare con l'arduino, in fondo mi permette di fare molte più cose.
Grazie per l'aiuto.

Ecco il tuo esempio è la prima cosa che si avvicina alle domande che mi facevo.
L'ho trovato per caso mettendo COMUNICAZIONE SERIALE STRING, ma in 3 pagina.

Adesso lo provo.
Ciao

Finalmente qualcosa che mi aiuta.
Ho modificato il codice in modo che scriva lo stato e mi va

// variabili per la gestione del protocollo seriale
  char serialBuffer[40]; // buffer per i messaggi che arrivano dalla seriale
  int indiceBuffer = 0; // variabile per contenere l'indice dell'array 
  boolean msgTrasmesso = false;// dice se il messaggio è stato trasmesso
  
void setup()
{
  delay(2000);
  Serial.begin(9600);
}

void loop()
{
  if(Serial.available())
  {
    char lettera = Serial.read();
    if(lettera != '&')// '&' è il carattere di fine messaggio
    { // se quello che arriva è diverso dal carattere di fine messaggio
      serialBuffer[indiceBuffer] = lettera;// popola l'array del buffer
      indiceBuffer++;//incrementa l'indice dell'array del buffer per dare la posizione al carattere successivo
    }
    else // se è arrivato il carattere di fine messaggio
    {
      indiceBuffer = 0; // l'indice ritorna a 0
      msgTrasmesso = true; // il messaggio è stato trasmesso
    }
  }
  // analizza il buffer
  if(msgTrasmesso == true)
  {
    switch(serialBuffer[0])
    {
      case '0': //zero
      {
        switch(serialBuffer[1])
        {
          case 'A':
          {
            Serial.println("Accensione");
          }
          break;
          case 'B':
          {
            Serial.println("Spengo");
          }
          break;
        }
        memset(serialBuffer,0,sizeof(serialBuffer)); // svuota il buffer
        msgTrasmesso = false;
      }
      break;
      default: // se arriva qualcosa non previsto svuota il buffer
      {
        memset(serialBuffer,0,sizeof(serialBuffer));
        msgTrasmesso = false;
      }
    }
  }
}

Grazie Pelletta

Prego :slight_smile:

Pelletta:
Prego :slight_smile:

classico esempio di come il malato diventa medico XD

scusate se mi intrometto con una domanda che c'entra poco con l'argomento trattato, ma ho da tempo questa curiosità: cosa serve il delay di 2 secondi prima del begin della seriale? lo vedo spesso in giro, io non l'ho mai usato (non mi piace molto l'idea di aspettare 2 secondi prima che l'arduino entri in azione :sweat_smile:), ma vorrei capire se e perchè dovrei usarlo, grazie :wink:

Da esperienze personali direi che pulisce eventuali residui o segnali dovuti al boot o allo start di Arduino, ma non so. Sto cercando anche io di capire bene i delay e i flussi. Anzi se qualcuno sa darmi consigli per capire flusso, delay e problematiche legate a questo. Ad esempio se chiamo una funzione nel loop, è sincrona o asincrona? Esiste qualcosa di asincrono a parte gli interrupt? Gli interrupt interrompono anche un delay? La seriale continua a bufferizzare tranquillamente anche se sono in un delay vero?

Chi mi sa dare consigli di dove trovare documentazione ben fatta su questa problematica con esempi?
Grazie

?R:
scusate se mi intrometto con una domanda che c'entra poco con l'argomento trattato, ma ho da tempo questa curiosità: cosa serve il delay di 2 secondi prima del begin della seriale? lo vedo spesso in giro, io non l'ho mai usato (non mi piace molto l'idea di aspettare 2 secondi prima che l'arduino entri in azione :sweat_smile:), ma vorrei capire se e perchè dovrei usarlo, grazie :wink:

Serve se devi collegare l'Arduino al PC, altrimenti puoi toglierlo.
In pratica inserisce un ritardo nell'inizio della trasmissione permettendo all'utente di uploadare una versione aggiornata dello sketch. Se avessi un codice che inizia a trasmettere una grossa mole di dati, non avresti materialmente il tempo di aprire il terminale seriale e ti ritroveresti con il buffer di trasmissione intasato (classico led RX acceso fisso) con l'impossibilità di spedire alla scheda un nuovo sketch perché l'8U2 non riesce a comunicare con il bootloader del 328.

leo72:

?R:
scusate se mi intrometto con una domanda che c'entra poco con l'argomento trattato, ma ho da tempo questa curiosità: cosa serve il delay di 2 secondi prima del begin della seriale? lo vedo spesso in giro, io non l'ho mai usato (non mi piace molto l'idea di aspettare 2 secondi prima che l'arduino entri in azione :sweat_smile:), ma vorrei capire se e perchè dovrei usarlo, grazie :wink:

Serve se devi collegare l'Arduino al PC, altrimenti puoi toglierlo.
In pratica inserisce un ritardo nell'inizio della trasmissione permettendo all'utente di uploadare una versione aggiornata dello sketch. Se avessi un codice che inizia a trasmettere una grossa mole di dati, non avresti materialmente il tempo di aprire il terminale seriale e ti ritroveresti con il buffer di trasmissione intasato (classico led RX acceso fisso) con l'impossibilità di spedire alla scheda un nuovo sketch perché l'8U2 non riesce a comunicare con il bootloader del 328.

ahn ok, grazie per la spiegazione! :wink:

contecavour:
Gli interrupt interrompono anche un delay? La seriale continua a bufferizzare tranquillamente anche se sono in un delay vero?

da quel che so io gli interrupt interropono i delay mentre la seriale continua a bufferizzare durante un delay

Grazie.

Negli test che avevo fatto a giugno mi succedevano cose strane, ma son sempre piu convinto che fosse dovuto alla saturazione della memoria con l'UNO con tutte le librerie. Da quando uso il MEGA mi sembra tutto piu lineare.

io con l'arduino mega avevo avuto problemi sugli interrupt 2,3,4 e 5 che non andavano come previsto (chiarimenti sugli interrupt - #52 by leo72 - Generale - Arduino Forum) però non li ho mai risolti perchè non ho avuto tempo....

Per quanto ne so l'interrupt ha priorità sulle normali attività del processore ma non sul delay che è un "blocco a tmpo" del processore, durante il quale esso non reagisce a nessun tipo di attività esterna. Per dare efficacia assoluta all'interrupt è decisamente preferibile ricorrere a millis piuttosto che a delay.

adesso mi viene il dubbio :blush:, se non ricordo male tempo fa mi era stato detto che un interrupt hardware interrompeva anche la funzione pulseIn oltre che alla funzione delay, forse proprio in quel topic che ho linkato prima, ma penso che leo ne sappia qualcosa di più a riguardo...

contecavour:
apire bene i delay e i flussi.

Nel codice di Arduino esiste un flusso principale del programma, ripetuto ininterrottamente. Il loop(). Tutto quello che è lì dentro viene ripetuto continuamente. Questo per scelta dei progettisti, che hanno strutturato un sketch in 2 blocchi distinti, setup() e loop(). Setup() viene eseguito solo 1 volta, il loop() sempre.
Siccome non c'è attesa tra un'istruzione e l'altra, può capitare di avere la necessità di attendere un certo lasso di tempo tra l'esecuzione di un'azione e la successiva. E' stato perciò pensato al delay(), una funzione che attende x millisecondi.
Il tutto però sempre inserito nel loop().

Anzi se qualcuno sa darmi consigli per capire flusso, delay e problematiche legate a questo. Ad esempio se chiamo una funzione nel loop, è sincrona o asincrona?

Cosa intendi per sincrono/asincrono?
Se intendi eseguito nel flusso principale, tutto quello che è scritto in linguaggio Arduino è sincrono. Ma sarebbe più corretto dire "seriale". Una istruzione dietro l'altra, ecco come viene eseguito il codice Arduino.
Se hai un loop() così strutturato:
loop() {
funzione1();
funzione2();
}
funzione2 sarà eseguito sempre e soltanto dopo funzione1. Dopo funzione2 sarà sempre eseguita funziona1 perché loop() è ciclico.

Esiste qualcosa di asincrono a parte gli interrupt?

Ad esser sinceri a livello di SW non esiste nulla di asincrono, ossia eseguito alternativamente a qualcos'altro. Questo perché l'Atmega non è dotato di multitasking per cui anche per eseguire un interrupt viene bloccata l'esecuzione del codice principale. Il micro infatti salva lo stato dei registri, esegue la routine di interrupt, e poi torna ad eseguire il programma dell'utente dal punto in cui era stato interrotto.
A livello di HW esiste un modulo asincrono, il timer 2, che può funzionare con una sorgente di clock differente da quella del clock di sistema, lavorando quindi anche a frequenze differenti ed anche quando il micro è in sleep. Ma non penso c'entri molto con quello che volevi sapere.

Gli interrupt interrompono anche un delay? La seriale continua a bufferizzare tranquillamente anche se sono in un delay vero?

Gli interrupt bloccano anche un delay. La funzione delay altro non fa che controllare che il contatore interno dei millisecondi (agganciato al timer 0 e letto tramite la funzione millis()) sia avanzato del valore passato come parametro, ossia sia trascorso il tempo richiesto. Ma per far ciò è interrotta continuamente dall'interrupt che incrementa il contatore del timer 0, nonché da altri interrupt. Un altro interrupt è proprio quello che gestisce i dati in arrivo sulla seriale ed in partenza su di essa. Tutte operazioni eseguite in background, quindi ad un livello inferiore rispetto a quello del codice utente. Ma io non mi azzarderei a definire un interrupt un'operazione asincrona perché, come detto, in realtà interrompi l'esecuzione dello sketch e poi la riprendi in un secondo tempo.

Chi mi sa dare consigli di dove trovare documentazione ben fatta su questa problematica con esempi?
Grazie

Cerca su internet, leggi un sacco su timer, interrupt, guarda il datasheet e sperimenta.

ottimo direi, grazie per i chiarimenti! :wink:

Oggi sono nel mondo win32 e DB quindi niente Arduino, ma lunedì faccio un po' di prove.

Che l'interrupt interrompe un delay lo avevo provato ieri. Quindi ne ho la certezza fisica.

Cosa intendi per sincrono/asincrono?

Detto che è chiaro che essendoci un processore (oggi diremmo single core) parallelismi reali sono impossibili.
Per asincrono intendevo qualcosa tipo multithread. O flusso principale e gestione di eventi che interrompono il flusso.
Tipo funzione1 fa qualcosa in attesa di ma funzione2 viene eseguita proprio perchè funzione1 non porta via CPU.
MA dalla tua risposta ho gia capito, non ci sono meccanismio che lo facciano, devi essere tu che fai funzione uno che se non è successo qualcosa...finisca e al limite venga riattivata al prossimo loop.

Leo, anche se faccio un interrupt, la seriale ha un buffer e continua a leggere dati o smette e perde glie eventuali dati?

Altra cosa, se sto eseguendo un SerialEvent, è so solleva un interrupt... viene caricata la routine di interrupt o serialevent ha la priorità?
Ci sono tabelle di priorità?
Col MEGA ho 4 interrupt, c'è una logica di importanza (tipo nei xx86 che avevano un codice di importanza da 0 a 8 e uno di livello inferiore non poteva interrompere uno superiore) o qualunque può interrompere qualunque?

E' solo per capire tutto meglio, per la mia applicazione ho già tutto quello che mi serve.
Ciao

contecavour:
Detto che è chiaro che essendoci un processore (oggi diremmo single core) parallelismi reali sono impossibili.
Per asincrono intendevo qualcosa tipo multithread. O flusso principale e gestione di eventi che interrompono il flusso.
Tipo funzione1 fa qualcosa in attesa di ma funzione2 viene eseguita proprio perchè funzione1 non porta via CPU.
MA dalla tua risposta ho gia capito, non ci sono meccanismio che lo facciano, devi essere tu che fai funzione uno che se non è successo qualcosa...finisca e al limite venga riattivata al prossimo loop.

Esatto. Devi gestire tutto via software. E' ciò che fanno gli RTOS, i SO in tempo reale: assegnano dei tick ad ogni task, terminato il tempo a disposizione congelano il task salvando lo stato di tutti i registri e poi caricano i registri salvati del task successivo. Ma anche qui c'è un interrupt agganciato ad un timer a gestire i vari tick.

Leo, anche se faccio un interrupt, la seriale ha un buffer e continua a leggere dati o smette e perde glie eventuali dati?

Nel micro ci sono diversi interrupt, ognuno con una certa priorità sugli altri. Inoltre per default tutte le ISR sono compilate atomicamente (vale a dire che viene creato un codice non interrompibile da un'altra ISR) per cui se il micro sta eseguendo una ISR ed arriva qualcosa sulla seriale, l'evento viene registrato ma il relativo interrupt non viene eseguito finché non è terminata l'altra ISR. Terminata questa, viene eseguita l'ISR per gestire l'arrivo dei dati sulla seriale, che vengono riversati nel buffer.

Altra cosa, se sto eseguendo un SerialEvent, è so solleva un interrupt... viene caricata la routine di interrupt o serialevent ha la priorità?
Ci sono tabelle di priorità?

Sì, come ti ho detto ci sono delle priorità. Vedi il datasheet del 328 alla pag. 67, cap. 12.4.

Col MEGA ho 4 interrupt, c'è una logica di importanza (tipo nei xx86 che avevano un codice di importanza da 0 a 8 e uno di livello inferiore non poteva interrompere uno superiore) o qualunque può interrompere qualunque?

Diciamo che hai 4 interrupt esterni, non è corretto dire che hai 4 interrupt e basta :wink:
Anche in questo caso ci sono delle priorità: INT0 verrà eseguito prima di INT1, INT1 prima di INT2 ecc...

Parli di questo documento?

Qui direi che parte da pagina 67.

Oppure dimmi tu il link del pdf di cui parli.
Grazie
Nic