[RISOLTO] Comunicazione "base" Software Serial tra due Arduini

Ciao a tutti ragazzi, vi pongo una questione su cui mi sto scervellando da giorni. Premetto che ho iniziato da poco, quindi potrei dire qualche (molte) cavolate.

Il problema è il seguente: vorrei far comunicare due arduini 1 tramite la libreria Software Serial; entrambi sono connessi allo stesso pc (testato anche alimentandoli tramite due pc differenti e/o con usb esterno ma la situazione non cambia) ed entrambi utilizzano piedini digitali per comunicare. Purtroppo, sebbene il codice venga compilato correttamente (invia un int tramite SS, ricevi int tramite SS e stampa a video su monitor seriale "HW"). Ho provato anche collegando i due arduini a massa, ma niente.

Di seguito il codice per il "Sender":

#include <SoftwareSerial.h>
SoftwareSerial Seriale(10,11);
int v;

void setup() {  
  Serial.begin(9600);
  Seriale.begin(9600);
 
  delay(1000);

}
void loop() {
  
    Seriale.print(v);
    Serial.println(v);
    delay(500);
    v++;
  }

E questo lato "Receiver":

#include <SoftwareSerial.h>
SoftwareSerial Seriale(10,11);
void setup() {  
  Serial.begin(9600);
  Seriale.begin(9600);
 
  delay(1000);

}
void loop() {
  while (Seriale.available()){
     Serial.println(Seriale.read());
  }  
}

Purtroppo i risultati che ricevo sono una serie di numeri scollegati (anzichè ricevere 0,1,2,3,4...compare sul receiver 130,148,129,150...)

I due arduino sono così collegati:

Qualche suggerimento?
Grazie mille

Il problema è che tu stai inviando un intero scrivendolo come carattere con il metodo print lato trasmissione, lato ricezione invece leggi un solo carattere e lo metti nel serial monitor. Un carattere è composto da un byte mentre un int da due byte, da qui i valori apparentemente errati.
Dovresti cambiare modo di inviare e di ricevere, guarda il reference qui e qui dove c'è anche l'esempio che dovrebbe fare al caso tuo

Concorquoto
Ovvero siccome concordo, allora quoto
Passo propedeutico sarebbe cercare di capire che cosa scrive la print (inteso che tipo dato spara fuori) dal Trasmettore
che cosa riceve la read()
che cosa fa a questo punto la print del ricevitore

Ciao ragazzi...vi confermo che ora funziona correttamente! Ho notato però alcune cose:

  1. Se i due arduini sono connessi contemporaneamente al pc, se faccio un cambio di COM (dall'IDE: strumenti-porta di collegamento) per vedere la seriale di uno o dell'altro, spesso sono costretto a riavviare i moduli, altrimenti è come se si bloccassero. Potrebbe essere dovuto al while? Ovvero, quando cambio la porta COM, l'Arduino è come se si reinizializzasse (lato hw seriale) e quindi il while passa da TRUE a FALSE. Se fosse così esce dal ciclo while e non riparte automaticamente?!?

  2. L'arduino "sender" stampa correttamente i caratteri sulla seriale monitor, da 0 a ... 2147483647 (immagino, non ci sono arrivato... :smiley: ); il receiver invece stampa a video il range 0-255, poi ricomincia. Questo perchè 255 è il massimo numero rappresentabile con 1 byte?

3)Utilizzando, come mi avete proposto, write() per inviare i dati da SW serial, ricevo correttamente i dati...ma se lo uso per stampare i dati a monitor, ovvero lato HW serial, vedo dati a caso. Sono quindi costretto ad usare print() per vedere i dati corretti a monitor?!?

Ad ogni modo, nei prossimi giorni proverò ad indagare maggiormente sulla differenza tra le varie funzioni che mi avete indicato.

Riposto il codice QUASI funzionante per i posteri.
Grazie mille

Sender

#include <SoftwareSerial.h>

SoftwareSerial Sender(10,11);
int v=0;

void setup() {  
  Serial.begin(9600); 
  Sender.begin(9600);
  delay(1000);
}
void loop() {
    while(Sender.available()){
    Sender.write(v);
    Serial.println(v);
    ++v;
    delay(500);
  }
}

Receiver

#include <SoftwareSerial.h>

SoftwareSerial Receiver(10,11);
int x;

void setup() {  
  Serial.begin(9600); 
  Receiver.begin(9600);
  delay(1000);
}
void loop() {
    while(Receiver.available()){
    x=Receiver.read();
    Serial.println(x);
  }
}

hai scritto di aver ripostato il codice funzionante
ma subito prima hailamentato alcuni problemi....
non so tu, ma io segnalerei che il codice NON funziona corretamente, per non dare errate informazioni a eventuali inesperti che capitasserro qui per caso, poi vedi tu....
comunque i problemi che lamenti te li sei creati con le ultime modifiche, che evidentemente NON erano nella direzione corretta
riparti con le domande del mio post 2

Non entro nel merito del codice perché @Standardoil ha già detto il necessario, rispondo ai tuoi questi per quanto possibile:
Il sender stampa correttamente il valore dell'int perché gli stai chiedendo di stampare quello e la print è fatta bene quindi lo stampa corretto, come da te ipotizzato stamperai valori compresi nel range dell'int ma non si fermerà a +2..... ma se continui ad incrementarlo non essendo definito unsigned arrivato all'overflow stamperebbe -2.... fino ad arrivare a zero per poi tornare ai positivi e ripetere il ciclo all'infinito.
Lato receiver ti stampa fino a 255 proprio perché è il limite di un char, non ti segue i valori inviati perché non hai sistemato il codice del receiver (come nelle guide linkate), altrimenti avresti i medesimi valori del sender.
Quando cambi la porta com e apri il serial monitor questo causa il riavvio di Arduino il che mi porta a chiederti, il codcie che stai facendo è a mero scopo di studio? Se si ok, vai pure avanti così per imparare se invece il tutto è volto ad un progetto reale allora ti dico che la strada che hai intrapreso è quantomeno poco corretta, e ti spiego il perché; in una trasmissione tra due o più dispositivi non solo occorre stabilire il collegamento e inviare i dati ma ti serve un "protocollo" che renda sicure le trasmissioni (e non mi sto riferendo a criptarli) in modo tale che quando il ricevente riceve un messaggio può determinare quando questo è terminato (nel tuo caso dovrestri aspettare almeno due byte) se il messaggio stesso è corretto ovvero se ricevuto dall'inizio alla fine, se quello che ho ricevuto è formalmente corretto, ecc.
Come hai fatto tu se il ricevente lo colleghi dopo del trasmittente riceveresti un flusso di dati che potrebbe non essere coerente (Es. parti a ricevere non dal byte 0 ma dal byte 1 e tutto fa a farsi benedire).
Aspetto la tua risposta perché se fosse l'inizio di una comunicazione per un progetto reale (Es. Arduino fuori con sensore tempratura che invia il dato ad un arduino interno con display) allora forse ti converrebbe cambiare strategia, a meno di non voler spendere decine di ore a creare e gestire un tuo protocollo, che ci stà é se è un hobby può essere anche divertente farlo.

fizio:
Ovvero, quando cambio la porta COM, l'Arduino è come se si reinizializzasse (lato hw seriale)

Ma scusa, mi sfugge qualcosa... Ma tu i due Arduino li alimenti separatamente con un alimentatore ciascuno, o usi solamente la USB? Perché se è la seconda, è ovvio che si spenga quando sfili la USB e quindi ricominci da zero quando la colleghi...

Non puoi usare due porte USB sullo stesso PC e quindi tenerle sempre collegate (ovviamene i due IDE devi assegnarli ognuno alla propria porta)?

EDIT: mi associo alle giuste osservazioni di fabpolli, ma per prima cosa verifica il discorso almentazione/USB...

fabpolli:
Lato receiver ti stampa fino a 255 proprio perché è il limite di un char, non ti segue i valori inviati perché non hai sistemato il codice del receiver (come nelle guide linkate), altrimenti avresti i medesimi valori del sender.

Ciao e grazie (a tutti) delle risposte. In merito a questa frase: in realtà CREDO di aver fatto quanto scritto c'è scritto nella guida, in particolare nell'esempio che riporta ovvero:

incomingByte = Serial.read();
                Serial.print("I received: ");
                Serial.println(incomingByte, DEC);

L'unica differenza è che nell'esempio specifica DEC, ma nel mio caso ho notato che, pur specificandolo, non cambia la situazione.

Nel sender ho invece utilizzato la funzione write() per spedire i dati lato SwSerial.

Dato che mi sembra di capire di essere fuori strada, potresti gentilmente postare un codice funzionante?

PS: risponderò in maniera più completa appena possibile.
Grazie ancora

fizio:
L'unica differenza è che nell'esempio specifica DEC, ma nel mio caso ho notato che, pur specificandolo, non cambia la situazione.

Eh si, significa che visualizzi il valore in decimale, che è poi il default.

Nel sender ho invece utilizzato la funzione write() per spedire i dati lato SwSerial.

Io un'idea ce l'ho ma a questo punto posta i nuovi (ultimi) codici, e possibilmente fai anche copia/incolla dell'output seriale (mettili sempre nel tag "code" oppure un file testo in allegato) così possiamo darti indicazioni più precise.

docdoc:
Ma scusa, mi sfugge qualcosa... Ma tu i due Arduino li alimenti separatamente con un alimentatore ciascuno, o usi solamente la USB? Perché se è la seconda, è ovvio che si spenga quando sfili la USB e quindi ricominci da zero quando la colleghi...

Non puoi usare due porte USB sullo stesso PC e quindi tenerle sempre collegate (ovviamene i due IDE devi assegnarli ognuno alla propria porta)?

EDIT: mi associo alle giuste osservazioni di fabpolli, ma per prima cosa verifica il discorso almentazione/USB...

Gli Arduini sono collegati entrambi allo stesso pc tramite due porte USB diverse. Se faccio una qualche modifica al codice e lo carico su uno dei due (o su entrambi) le seriali SW non si reinizializzano automaticamente, ma devo staccare/riattaccare (ovvero spegnere/riaccendere) il receiver per far ricominciare la comunicazione.

Modifica dell'ultimo minuto: dal codice del sender ho tolto la funzione Sender.available()...inteso come: "non badare al collegamento seriale, fai ciò che devi a prescindere". E la situazione è decisamente migliorata!

fabpolli:
Non entro nel merito del codice perché @Standardoil ha già detto il necessario, rispondo ai tuoi questi per quanto possibile:
Il sender stampa correttamente il valore dell'int perché gli stai chiedendo di stampare quello e la print è fatta bene quindi lo stampa corretto, come da te ipotizzato stamperai valori compresi nel range dell'int ma non si fermerà a +2..... ma se continui ad incrementarlo non essendo definito unsigned arrivato all'overflow stamperebbe -2.... fino ad arrivare a zero per poi tornare ai positivi e ripetere il ciclo all'infinito.
Lato receiver ti stampa fino a 255 proprio perché è il limite di un char, non ti segue i valori inviati perché non hai sistemato il codice del receiver (come nelle guide linkate), altrimenti avresti i medesimi valori del sender.
Quando cambi la porta com e apri il serial monitor questo causa il riavvio di Arduino il che mi porta a chiederti, il codcie che stai facendo è a mero scopo di studio? Se si ok, vai pure avanti così per imparare se invece il tutto è volto ad un progetto reale allora ti dico che la strada che hai intrapreso è quantomeno poco corretta, e ti spiego il perché; in una trasmissione tra due o più dispositivi non solo occorre stabilire il collegamento e inviare i dati ma ti serve un "protocollo" che renda sicure le trasmissioni (e non mi sto riferendo a criptarli) in modo tale che quando il ricevente riceve un messaggio può determinare quando questo è terminato (nel tuo caso dovrestri aspettare almeno due byte) se il messaggio stesso è corretto ovvero se ricevuto dall'inizio alla fine, se quello che ho ricevuto è formalmente corretto, ecc.
Come hai fatto tu se il ricevente lo colleghi dopo del trasmittente riceveresti un flusso di dati che potrebbe non essere coerente (Es. parti a ricevere non dal byte 0 ma dal byte 1 e tutto fa a farsi benedire).
Aspetto la tua risposta perché se fosse l'inizio di una comunicazione per un progetto reale (Es. Arduino fuori con sensore tempratura che invia il dato ad un arduino interno con display) allora forse ti converrebbe cambiare strategia, a meno di non voler spendere decine di ore a creare e gestire un tuo protocollo, che ci stà é se è un hobby può essere anche divertente farlo.

In questo momento la cosa principale che voglio fare è capire ed imparare, a piccoli passi (e pensavo che passare un int incrementato fosse più facile :o forse con un carattere si semplifica, ma ancora devo testare). Successivamente si, vorrei dilettarmi a far comunicare più arduini tramite modulo LoRa collegato tramite RS485. L'idea malsana è proprio quella di arrivare a scrivere un miniprotocollo (al momento mi interessa principalmente il discorso comunicazione, sensori e altro saranno una diretta conseguenza)...ma la strada è lunga e difficile!

Comunque, mi pare di aver capito che tramite read() (e quindi tramite comunicazione seriale in generale?) posso leggere 1 solo byte alla volta, di conseguenza il massimo valore dell'intero che posso ricevere è 255. Per poter leggere interi rappresentati da più byte, dovrei mettere su un buffer e sommare i byte quando sono >1, per poi stamparli a monitor?!?

Al momento la soluzione migliore che ho trovato (non si blocca se ricarico il codice) con l'unica pecca che non riceve/stampa a video valori maggiori di 255 è la seguente:

sender

#include <SoftwareSerial.h>

SoftwareSerial Sender(10,11);
unsigned int v=0;

void setup() {  
  Serial.begin(9600); 
  delay(1000);
  Sender.begin(9600);
  delay(1000);
}
void loop() {
  {
    Sender.write(v);
    Serial.println(v);
    ++v;
    delay(500);
  }
  
}

Receiver

#include <SoftwareSerial.h>

SoftwareSerial Receiver(10,11);
unsigned int x;

void setup() {  
  Serial.begin(9600); 
  delay(1000);
  Receiver.begin(9600);
  delay(1000);
}
void loop() {
    while(Receiver.available()){
    x=Receiver.read();
    Serial.println(x);
  }
}

Le ho provate tutte, (parseInt()-write() nel receiver al posto di println...)ma nessuna da risultati così "soddisfacenti". Credo di aver seguito correttamente gli esempi dei link che mi avete gentilmente postato, se mi è sfuggito qualcosa sono pronto ad autoflagellarmi.

Ancora...grazie mille!

non è che devo o non devi seguire gli esempi dei link
devi capire che "tipo dati" viene "maneggiato" dalla write, dalla print, dalla read
e dato che tutte questa "leggono" da una parte e scrivono da un'altra...
devi pensare sia a che tipo dati entra sia a che tipo dati esce, e come viene fatta la conversione
che NON sempre è puramente matematica
ma questo te lo avevo già detto, pur se con parole differenti

Cominciamo dalle basi:

il tipo di dato int è memorizzato in 16 bit, ok.

se inizializzo una variabile int x=0 e la stampo a video: con print(x) leggerò la sua codifica ASCII, ovvero 0 (in quanto di default la funzione mi restituisce il valore decimale), con write() leggerò invece null, il suo valore binario (mmm?).

a questo punto: se dal sender invio, tramite write() l'intero x=0, il receiver riceverà, tramite read(), il valore binario (mmm?) null e sarà stampato tramite print() come 0.

Il "collo di bottiglia" nell'invio di un int è rappresentato dalle funzioni write() e read() in quanto ragionano un byte alla volta. Di conseguenza sul monitor del receiver il massimo valore che posso leggere in ASCII da un int è 2^8=256 meno lo 0 (a meno di tecniche per sommare il secondo byte).

Nel ragionamento che ho fatto però c'è un presupposto sbagliato: ASCII può rappresentare 127 caratteri, al massimo 256 considerando ASCII Extended...perchè allora sul monitor del sender riesco a visualizzare tutti gli int anche maggiori di 256? Sarà l'orario, ma ditemi se la strada che sto percorrendo è quasi giusta.

Grazie.

fizio:
Cominciamo dalle basi:

il tipo di dato int è memorizzato in 16 bit, ok.

se inizializzo una variabile int x=0 e la stampo a video: con print(x) leggerò la sua codifica ASCII, ovvero 0 (in quanto di default la funzione mi restituisce il valore decimale), con write() leggerò invece null, il suo valore binario (mmm?).

Cominciamo con questo:
NO
non avrai zero, ma il carattere '0' che non è la stessa cosa (ti do un aiuto: in ASCII lo zero vale tren...)
con write avrai è vero il suo valore binario
e in ricezione la read() in un caso ti darà.... e nell'altro....
che se stampi con una print...

Standardoil:
devi capire che "tipo dati" viene "maneggiato" dalla write, dalla print, dalla read
e dato che tutte questa "leggono" da una parte e scrivono da un'altra...

Perfetto. Ma per prima cosa deve decidere quale debba essere il "protocollo" (è molto semplice ma in pratica è sempre un protocollo..) di comunicazione, e far "parlare" ai due estremi la stessa "lingua".
O scrive e legge BYTE o scrive e legge STRINGHE.

(ti do un aiuto: in ASCII lo zero vale tren...)

No, "quaran..." :wink:

Vero
La memoria....

Ti consiglio di lasciar perdere il parseint. Sembra la via di uscita più immediata (e lo è), ma lo useresti solo all'inizio per poi passare a sistemi migliori, quindi tanto vale partire direttamente da qualcosa di buono anche se più complicato.

Ti conviene scomporre l'operazione in operazioni semplici.
Nel tuo caso (trasmettere e ricevere un intero)

-1 - sul trasmettitore, scomponi l'intero nei suoi due byte
-2 - trasmetti i due byte
-3 - ricevi i due byte
-4 - sul ricevitore, ricomponi l'intero a partire da suoi due byte

Riesci a fare questi quattro passi?
Hai difficoltà da qualche parte?

Fatto questo, potrai passare a cose più complicate
(trasmettere sequenze di interi con ack, verifica, e tutto il resto)

paulus1969:
Ti consiglio di lasciar perdere il parseint. Sembra la via di uscita più immediata (e lo è), ma lo useresti solo all'inizio per poi passare a sistemi migliori, quindi tanto vale partire direttamente da qualcosa di buono anche se più complicato.

Ti conviene scomporre l'operazione in operazioni semplici.
Nel tuo caso (trasmettere e ricevere un intero)

-1 - sul trasmettitore, scomponi l'intero nei suoi due byte
-2 - trasmetti i due byte
-3 - ricevi i due byte
-4 - sul ricevitore, ricomponi l'intero a partire da suoi due byte

Riesci a fare questi quattro passi?
Hai difficoltà da qualche parte?

Fatto questo, potrai passare a cose più complicate
(trasmettere sequenze di interi con ack, verifica, e tutto il resto)

Lato trasmettitore:

unsigned int x;
byte high;
byte low;
low=x>>8;
high=x & 0xFF;

Lato Ricevitore:

byte high;
byte low;
unsigned int x= high | low << 8;

Palo, lato trasmettitore hai scomposto al contrario ovvero il primo è l'high e il secondo è il low. Di conseguenza anche il ricevente è invertito

Standardoil:
Cominciamo con questo:
NO
non avrai zero, ma il carattere '0' che non è la stessa cosa (ti do un aiuto: in ASCII lo zero vale tren...)
con write avrai è vero il suo valore binario
e in ricezione la read() in un caso ti darà.... e nell'altro....
che se stampi con una print...

in ASCII lo zero vale 48, ok.

ma se è vero che la print stampa caratteri (0-255), perchè se dichiaro ad es unsigned int x=9999 e do il comando Serial.print(x), vedo a monitor correttamente il 9999?

la read legge il primo BYTE in arrivo dalla seriale. il write() scrive il primo BYTE in invio alla seriale.

Se invio int x=0 tramite print(), il ricevitore, tramite read() leggerà 0 e stamperà tramite print() 48.
Se invio int x=0 tramite print(), il ricevitore, tramite read() leggerà 0 e stamperà tramite write() 0.

Se invio int x=0 tramite write(), il ricevitore, tramite read() leggerà null (?) e stamperà tramite print() 0.
Se invio int x=0 tramite write(), il ricevitore, tramite read() leggerà null (?) e stamperà tramite write() null (immagino perchè non vedo niente a monitor).

Ho finito ora di fare questi test, ti torna?

Se tornano a te...
E di questi comportamenti, quale ti serve?