Go Down

Topic: 6 Arduini UNO che comunicano un float a un MEGA 2560 con SoftwareSerial (Read 15367 times) previous topic - next topic

Marcobz

Guglieeeeeeeelmooooo !!!

Sto per piangere !!!!
Nell'attesa che mi rispondessi proseguivo imperterrito nelle mie ricerche e prove e ho messo insieme questo
Code: [Select]
#include <SoftwareSerial.h>                    //includo libreria
SoftwareSerial mySerial(10,11);                //assegno pin tx e rx

float valore;                                  //valore finale float

void setup() {
  Serial.begin(9600);                          //inizializzo la seriale
 
  mySerial.begin(9600);                        // inizializzo la seriale software
}

void loop() {
  while (mySerial.available() > 0) {           //finchè la mySerial è in trasmissione

  valore =  mySerial.parseFloat();
    Serial.println(valore);
    }
  }



E funzionaaaaaaaa !!!!!!! Sarà una scorciatoia diseducativa  XD ma funzionaaaaa !
Ciò non toglie che il tuo sistema serve comunque in tutti gli altri casi...
Infatti adesso vado ad apportare le correzioni che hai riportato.

Marcobz

Ho apportato le modifiche al tuo sketch ma non cambia niente... anzi si accorcia la stringa overflow.

gpb01

... mi dai due notizie interessanti :

1. che la parseFloat() funziona ... cosa che a me in precedenza non aveva funzionato, quindi dovevo avere qualche problema io nel mio sorgente.  :smiley-roll:

2. che comunque invece le modifiche sul codice non ti hanno risolto il problema ... e questo è veramente incomprensibile ...  :smiley-eek:

Domani faccio delle prove pratiche e ti saprò dire ... ;)

Guglielmo
Search is Your friend ... or I am Your enemy !

gpb01


... anzi si accorcia la stringa overflow.


Si, quello è quello che mi aspettavo ... ora dovrebbero essere corrette e non dovrebbe più sparare delle raffiche di caratteri leggendoli dalla memoria. Il problema è ... perché perde il sincronismo e perché va in overflow ???  :smiley-eek: :smiley-eek: :smiley-eek:

Guglielmo
Search is Your friend ... or I am Your enemy !

Marcobz

A sto punto sono curioso anche io perchè in realtà il parseFloat non mi risolve al 100%. Infatti lo step successivo sarebbe ricevere 2 valori float da ogni arduino UNO, uno relativo alla temperatura rilevata e uno relativo alla temperatura impostata..
Siccome il parseFloat si ferma al primo numero con virgola che trova come faccio a mandarne due ? Ho letto 30 volte le reference ma non mi pare di aver capito che si possa fare...
Quote
list : the stream to check for floats (char)

Cosa viene inteso qui ?

gpb01

Non capisco .. la sto provando da me e ... non sbaglia un colpo ...

C'è effettivamente un problema con l'overflow ...
... quando va in overflow, bisogna infatti aggiungere un piccolo loop che pulisce il buffer in ingresso, altrimenti, quando ricomincia a leggere, legge i vecchi caratteri che erano rimasti nel buffer ! ;)

Se hai pazienza, domani mattina sistemo il codice, faccio un altro piccolo test e te lo metto qui.

Per l'altra tua domanda relativa all'uso del parseFloat...
... suppongo tu debba fare delle println() separate, una per ogni float e delle parseFloat separate una per ogni float che devi ricevere ... ma non ne sono certo, non l'ho mai usata  :D :D :D

Continuo a dire che è meglio avere sotto il proprio controllo la situazione e saper gestire ogni possibile caso (... capendo come fare) che affidarsi a dei blocchetti già fatti che oggi qui trovi, domani, in un altro ambiente, non trovi più ;)

Guglielmo
Search is Your friend ... or I am Your enemy !


gpb01

Difficile andare a letto con il tarlo nella mente ...  :smiley-mr-green: :smiley-mr-green: :smiley-mr-green:

Versione aggiornata e collaudata del mio codice :

Code: [Select]

#define MAXCHAR  10
#define TERCHAR  0x0D

char inputString[MAXCHAR + 2];
char inChar;
byte strIndex;


void setup() {
 
 strIndex = 0;
 inputString[strIndex] = 0x00;
 
 Serial.begin(9600);
 
}

void loop() {
 
 if (Serial.available()) {
   inChar = Serial.read();
   // se si vogliono vedere in HEX i caratteri che si ricevono
   // togliere il commento alla riga seguente
   // Serial.println(inChar, HEX);
   if (inChar == (char) TERCHAR) {
     // è arrivato il carattere terminatore, si puo' elaborare la stringa
     // In questo esempio semplicemente la si stampa ...
     Serial.print(F("Input string : "));
     Serial.println(inputString);
     //
     // finito il suo uso la si ripulisce per un uso successivo
     strIndex = 0;
     inputString[strIndex] = 0x00;
   }
   // NON è arrivato il carattere terminatore, si memorizza il carattere ricevuto
   // e si sposta il fine stringa ...
   inputString[strIndex] = inChar;
   strIndex++;
   inputString[strIndex] = 0x00;
   if (strIndex > MAXCHAR + 1) {
     // ... NON c'è pi` spazio nella stringa ... bisogna elaborarla e svuotarla
     // In questo esempio semplicemente la si stampa ...
     Serial.print(F("Overflow string : "));
     Serial.println(inputString);
     delay(5);
     Serial.print(F("More chars : "));
     Serial.println(Serial.available());
     // Butta via i caratteri che erano rimasti nel buffer della seriale ...
     while (Serial.available()) {
         delay(5);
         Serial.read();
     }
     //
     // e ripulisce la stringa per un uso successivo
     strIndex = 0;
     inputString[strIndex] = 0x00;
   }
 }
}


... ho ampliato di un carattere il buffer e inserito, in caso di overflow, un ciclo di svuotamento del buffer. Come vedi ci sono dei piccolissimi ritardi (dei delay(5)) che servono a dare il tempo ai vari caratteri di arrivare anche a basse velocità ;)

Devi riadattarlo alla tua SoftwareSerial ... visto che io le prove le sto facendo sulla Serial connessa alla USB ;)

Buona notte  XD

Guglielmo
Search is Your friend ... or I am Your enemy !

Maurotec

Quote


Posts: 17
View Profile
   
   
Re: 6 Arduini UNO che comunicano un float a un MEGA 2560 con SoftwareSerial
« Reply #49 on: Today at 06:21:00 pm »
   Bigger Bigger Smaller Smaller Reset Reset
A sto punto sono curioso anche io perchè in realtà il parseFloat non mi risolve al 100%. Infatti lo step successivo sarebbe ricevere 2 valori float da ogni arduino UNO, uno relativo alla temperatura rilevata e uno relativo alla temperatura impostata..
Siccome il parseFloat si ferma al primo numero con virgola che trova come faccio a mandarne due ? Ho letto 30 volte le reference ma non mi pare di aver capito che si possa fare...


@Marcobz
Ho letto tutti i post, mi sembra evidente che l'approccio è sbagliato, e questo ti porta in confusione, perché in effetti se fosse così complicato spedire e ricevere dati in pochi saprebbero farlo.

Studiare il reference non è risolutivo, ciò non vuol dire che non va studiato, per cui devi integrare altre conoscenze.
Studia come è composto un float. Un float in Arduino è un tipo che occupa in memoria 4 byte.
I byte sono spediti nudi e crudi e ricevuti alla stessa maniera.

Più dati spediti in sequenza devono essere ricevuti nella stessa sequenza. Il protocollo software serve proprio a spedire e ricevere dati in modo che il ricevente sappia che i 4 byte spediti per primo sono la temperatura ambiente
gli altri 4 byte ricevuti per secondi sono la temperatura richiesta o viceversa.

C'è un modo semplice per spedire dati in modo ordinato e riceverli nello stesso ordine ed è quello di creare un pacchetto dati. Il pacchetto nel tuo caso è formato da 8 byte che se spediti 2 volte rappresentano per il ricevente due pacchetti. Stivare i dati in una struttura (keyword struct) semplifica il lavoro, la stessa struct l'avrai nel trasmittente e nel ricevente. Un struct composta da due dati di tipo float puo essere scandita byte per byte e inviata via seriale. Il ricevente riceve byte per byte e allora basta scrivere nella variabile di tipo struct 8 byte per il primo pacchetto. Questo dovrebbe farti capire anche che devi trovare il modo di capire quando inizia il pacchetto e quando finisce. La struct da inviare allora protrebbe essere composta da un byte, due float, e un byte, totale 10 byte.


Quando ricevi da seriale testi se il primo byte vale es 10 (decimale) se lo è i byte seguenti li scrivi byte per byte fino a che non ricevi un byte che es vale 20.

Es:
spedisci:
Startpack (10), 8 byte (i due float) e per ultimo il byte di EndPack(20)

Ho dei nomi al valore 10 e 20, cioè StartPack e EndPack, solo per intenderci, ma non è escluso si possano usare nel codice.
struct data {
    float tAmb;
    float tSet;
}

struct data dataSending;  // crea una variabile di tipo struct
dataSending.tAmb = 22.5;
dataSending.tSet   = 25.0;
for (byte i=0; i<sizeof(data); i++) { // clicla n volte in base a quanto è grande la struct data
    // qui spedisci i dati byte per byte
    // come scomporre la variabile dataSending in byte ora non mi viene (è tardi e sono cotto)
}

Vedi se riesci a capirci qualcosa, perché di tempo per scrivere tutto funzionante non nè ho.
In alternativa puoi usare il metodo parse... aggiungendo la gestione dei pacchetti, con start pack e end pack. Capisci? quando trasmetti chi riceve non è a conoscenza di quando hai iniziato a trasmettere.

PS: l'organizzazione dei dati in struct o contenitori come le classi o array ecc è solo una rappresentazione C/C++,
rappresentazioni create per aiutare il programmatore ad organizzare i dati e nel caso della classi unisce dati e metodi legati ai dati all'interno appunto di classi, ma tutto alla fine è fatto di byte o bit.

Ciao.

gpb01


@Marcobz
Ho letto tutti i post, mi sembra evidente che l'approccio è sbagliato, e questo ti porta in confusione, perché in effetti se fosse così complicato spedire e ricevere dati in pochi saprebbero farlo.


Ciao Mauro,
non è così complicato, ma non è nemmeno così semplice e ...
... il problema al momento NON è come trasferire uno o più float, cosa che affronterà in seguito, il problema (... se così si può chiamare ;) ) è mettere su una comunicazione seriale che preveda la gestione dell'overflow, la futura gestione del timeout, ecc. senza ricorre a ... scorciatioie dell'IDE (parseXXXX).

Una volta che ha messo a punto un bel sistema per trasmettere/ricevere, in modo controllato, una qualsiasi sequenza di byte ... usarlo per spedire ciò che si vuole non è un problema. :)

E, come vedi dai vari post, l'intento di questo thread è proprio questo ...

Guglielmo
Search is Your friend ... or I am Your enemy !

gpb01


E funzionaaaaaaaa !!!!!!! Sarà una scorciatoia diseducativa  XD ma funzionaaaaa !


Sembra, ma ... ecco un'altro esempio in cui un blocchetto chiuso ... ti nasconde quello che realmente accade ...

Leggendo carattere a carattere, stiamo evidenziando che, comunque, un qualche cosa di sporco sulla seriale c'è ... altrimenti non andrebbe mai in overflow e ti darebbe sempre la stinga pulita, mente, tutto il lavoro che stiamo facendo è per "intercettare" condizioni particolari e per gestirle (caratteri di troppo, mancanza del CR, ...)

Sai perché non vedi nulla di tutto questo con la parseXXXX ? ... E' scritto nel reference :

Quote
Serial.parseFloat() returns the first valid floating point number from the Serial buffer. Characters that are not digits (or the minus sign) are skipped. parseFloat() is terminated by the first character that is not a floating point number.


Quindi ... qualsiasi porcheria accada sulla linea, tu non la vedrai MAI ... e se dei dati arrivano corrotti, semplicemente li perderai senza nessun avvertimento.

Come vedi c'è una bella differenza, nella realtà pratica, tra usare quella e ... controllarsi da soli quello che succede ;)

Guglielmo
Search is Your friend ... or I am Your enemy !

PaoloP

La cosa più semplice per capire come funziona è guardare il listato.
Le funzioni sono in Stream.cpp
Code: (Stream.cpp) [Select]
// returns the first valid (long) integer value from the current position.
// initial characters that are not digits (or the minus sign) are skipped
// function is terminated by the first character that is not a digit.
long Stream::parseInt()
{
 return parseInt(NO_SKIP_CHAR); // terminate on first non-digit character (or timeout)
}

// as above but a given skipChar is ignored
// this allows format characters (typically commas) in values to be ignored
long Stream::parseInt(char skipChar)
{
 boolean isNegative = false;
 long value = 0;
 int c;

 c = peekNextDigit();
 // ignore non numeric leading characters
 if(c < 0)
   return 0; // zero returned if timeout

 do{
   if(c == skipChar)
     ; // ignore this charactor
   else if(c == '-')
     isNegative = true;
   else if(c >= '0' && c <= '9')        // is c a digit?
     value = value * 10 + c - '0';
   read();  // consume the character we got with peek
   c = timedPeek();
 }
 while( (c >= '0' && c <= '9') || c == skipChar );

 if(isNegative)
   value = -value;
 return value;
}


// as parseInt but returns a floating point value
float Stream::parseFloat()
{
 return parseFloat(NO_SKIP_CHAR);
}

// as above but the given skipChar is ignored
// this allows format characters (typically commas) in values to be ignored
float Stream::parseFloat(char skipChar){
 boolean isNegative = false;
 boolean isFraction = false;
 long value = 0;
 char c;
 float fraction = 1.0;

 c = peekNextDigit();
   // ignore non numeric leading characters
 if(c < 0)
   return 0; // zero returned if timeout

 do{
   if(c == skipChar)
     ; // ignore
   else if(c == '-')
     isNegative = true;
   else if (c == '.')
     isFraction = true;
   else if(c >= '0' && c <= '9')  {      // is c a digit?
     value = value * 10 + c - '0';
     if(isFraction)
        fraction *= 0.1;
   }
   read();  // consume the character we got with peek
   c = timedPeek();
 }
 while( (c >= '0' && c <= '9')  || c == '.' || c == skipChar );

 if(isNegative)
   value = -value;
 if(isFraction)
   return value * fraction;
 else
   return value;
}


Le funzioni usano due metodi privati della classe per leggere il carattere successivo.
Code: [Select]
// private method to peek stream with timeout
int Stream::timedPeek()
{
 int c;
 _startMillis = millis();
 do {
   c = peek();
   if (c >= 0) return c;
 } while(millis() - _startMillis < _timeout);
 return -1;     // -1 indicates timeout
}

// returns peek of the next digit in the stream or -1 if timeout
// discards non-numeric characters
int Stream::peekNextDigit()
{
 int c;
 while (1) {
   c = timedPeek();
   if (c < 0) return c;  // timeout
   if (c == '-') return c;
   if (c >= '0' && c <= '9') return c;
   read();  // discard non-numeric
 }
}


Read, Peek e Flush sono dichiarate virtuali e si riferiscono alla classe chiamante la classe Stream, ad esempio Serial.
Code: [Select]
virtual int read() = 0;
virtual int peek() = 0;
virtual void flush() = 0;

Quindi il read() può essere interpretato come Serial.read(), cioè legge un carattere dalla Seriale.

Etemenanki

#57
Dec 16, 2013, 10:01 am Last Edit: Dec 16, 2013, 10:04 am by Etemenanki Reason: 1
Scusa, forse sto dicendo una ca**ata, ma non potrebbe essere la Mega ad interrogare a turno i vari slave, invece di passare tutto il tempo ad aspettare la loro trasmissione ? ... tipo:

(master>slave1) "inizia trasmissione" (e poi si mette in ricezione, se entro, esempio, 5 secondi, non riceve nulla, riprova, dopo 3 tentativi senza ricevere nulla segnala allarme mancanza comunicazione con slave 1, cosi c'e' pure una segnalazione di possibile malfunzionamento)

(slave1) (dopo un secondo di pausa, per consentire al master di passare in ricezione senza problemi) trasmette una stringa composita con tutti i valori in sequenza, ad esempio, carattere inizio stringa (puo essere *), primo valore, separatore (puo essere |), secondo valore, fine stringa (puo essere #), per due volte di seguito (come controllo) ... se la stringa fosse di lunghezza fissa, sarebbe meglio (ad esempio, spedendo sempre i valori della temperatura come quantita' fisse di bytes, potrebbero bastare 5 caratteri per dato, positivo/negativo, prima cifra, seconda cifra, punto decimale, cifra decimale ... in questo modo una stringa completa sarebbe sempre di 13 bytes, e una trasmissione sempre di 26), perche' cosi il master non dovrebbe controllarle in tempo reale, basterebbe che le infilasse in ordine in un buffer da 26 caratteri (o in due da 13, per semplificare il controllo comparandoli), e che le controllasse dopo averle ricevute tutte ...

(master) mentre riceve piazza tutto in un buffer, al termine della ricezione controlla che le due meta' della stringa siano uguali, se no, la trasmissione e' errata e ripete il tutto (max 3 volte, poi segnala errore), se si, trasforma le parti "numeriche" della stringa in valori numerici, li elabora, ci fa quello che vuoi, poi svuota il buffer e passa a "slave 2" ... e cosi via ...

In questo modo saprebbe sempre quale slave sta rispondendo, e cosa gli sta mandando, controllerebbe per errori di comunicazione, e non dovrebbe tenere sempre una linea aperta in ricezione mentre fa il resto (l'interrogazione degli slave potrebbe farla anche ogni 5 secondi, tanto la temperatura ambiente non cambia piu di tanto velocemente) ... inoltre in questo modo gli slave potrebbero trasmettere "codici di errore" se necessario quando interrogati, al posto del valore di temperatura, ed il master potrebbe discriminarli (magari perche' cominciano con un carattere diverso) ed eseguire le opportune azioni  ... o sbaglio e l'idea e' stupida ?
"Sopravvivere" e' attualmente l'unico lusso che la maggior parte dei Cittadini italiani,
sia pure a costo di enormi sacrifici, riesce ancora a permettersi.

gpb01


Scusa, forse sto dicendo una ca**ata, ma non potrebbe essere la Mega ad interrogare a turno i vari slave, invece di passare tutto il tempo ad aspettare la loro trasmissione ? ...


Ahahahah ... SI, ma è ovvio che poi farà così, anzi ...
... la lettura dalla seriale dovrà diventare proprio una funzione che o gli ritorna l'array di char (se ha ricevuto qualche cosa di valido) o gli ritorna NULL in caso di errore (overflow, timeout, ...) e lui la chiamerà quando gli servirà di leggere una data porta ;)


Ragazzi, sappiamo che siete tutti bravissimi i materia e che problemini del genere sono banalità (tu, Paolo, ecc.) ... ma, scusate ... vi manca la parte "didattica" ...

... ce li volete far arrivare per gradi e da soli o gli dovete sempre dire tutto e fare voi il lavoro ???  XD XD XD

Guglielmo
Search is Your friend ... or I am Your enemy !


Go Up