problemi di overflow con la seriale

Salve a tutti, conosco il forum ma è la prima volta che pubblico un topic spero di non fare errori :slight_smile: :sweat_smile: , uso arduino da un pò di anni, ho una conoscenza base di elettronica e ottima di informatica.
Ho scritto un programma per leggere valori dai misuratori di energia (in particolare gli SDM-230 della eastron)
tramite protocollo modbus con un convertitore rs485-ttl, questi dati vengono presi tramite usb da un raspberry pi e caricati in un database; Utilizzo un arduino mega 2560 ed ho usato due seriali diverse, la seriale1 per i misuratori e la seriale normale per il raspberry.
Il problema è che dopo qualche minuto-ora l'arduino incomincia a restituire zeri oppure va in overflow, non avendo molto tempo il programma l'ho scritto molto velocemente, e visto che sono tre giorni che ci sbatto non sapevo dove altro rivolgermi.
Questo è il codice:

#include <avr/wdt.h>
// RS485 control pin
const int RS485_control = 2;
// Receive buffer
byte Buffer[9];
//----
/*union
  {
  unsigned long a ;
  float b ;
  } u ;*/
//Comandi per richiedere i valori
//C1
byte VoltageReadCommand[] = {0x01, 0x04, 0x00, 0x00, 0x00, 0x02, 0x71, 0xCB };
byte KWhReadCommand[] = {0x01, 0x04, 0x00, 0x048, 0x00, 0x02, 0xF1, 0xDD };
byte FrequencyReadCommand[] = {0x01, 0x04, 0x00, 0x46, 0x00, 0x02, 0x90, 0x1E};
byte CurrentReadCommand[] = {0x01, 0x04, 0x00, 0x06, 0x00, 0x02, 0x91, 0xCA};
byte PowerReadCommand[] = {0x01, 0x04, 0x00, 0x0C, 0x00, 0x02, 0xB1, 0xC8};
//C2
byte VoltageReadCommand2[] = {0x02, 0x04, 0x00, 0x00, 0x00, 0x02, 0x71, 0xF8 };
byte KWhReadCommand2[] = {0x02, 0x04, 0x00, 0x048, 0x00, 0x02, 0xF1, 0xEE };
byte FrequencyReadCommand2[] = {0x02, 0x04, 0x00, 0x46, 0x00, 0x02, 0x90, 0x2D};
byte CurrentReadCommand2[] = {0x02, 0x04, 0x00, 0x06, 0x00, 0x02, 0x91, 0xF9};
byte PowerReadCommand2[] = {0x02, 0x04, 0x00, 0x0C, 0x00, 0x02, 0xB1, 0xFB};
//C3
byte VoltageReadCommand3[] = {0x03, 0x04, 0x00, 0x00, 0x00, 0x02, 0x70, 0x29 };
byte KWhReadCommand3[] = {0x03, 0x04, 0x00, 0x048, 0x00, 0x02, 0xF0, 0x3F };
byte FrequencyReadCommand3[] = {0x03, 0x04, 0x00, 0x46, 0x00, 0x02, 0x91, 0xFC};
byte CurrentReadCommand3[] = {0x03, 0x04, 0x00, 0x06, 0x00, 0x02, 0x90, 0x28};
byte PowerReadCommand3[] = {0x03, 0x04, 0x00, 0x0C, 0x00, 0x02, 0xB0, 0x2A};

int j = 0;
int q = 0;
int k = 0;

void setup() {
  // Init serial communication
  Serial.begin(9600);
  Serial1.begin(2400);

  // Set RS485 Control pin to OUTPUT
  pinMode(RS485_control, OUTPUT);
  // Set pin to send data HIGH
  digitalWrite(RS485_control, HIGH);
  //wdt_enable(WDTO_1S);
}

void loop() {
  wdt_reset();
  for (int p = 0; p <= 3; p++) {
    Conta1();
    delay(1000);
  }
  Serial.flush();
  Serial1.flush();
  for (int t = 0; t <= 3; t++) {
    Conta2();
    delay(1000);
  }
  Serial.flush();
  Serial1.flush();
  for (int l = 0; l <= 3; l++) {
    Conta3();
    delay(1000);
  }
  Serial.flush();
  Serial1.flush();


}
////////////////////////////////

float Conta1() {

  digitalWrite(RS485_control, LOW);
  // Set pin to HIGH > Transmit mode
  digitalWrite(RS485_control, HIGH);
  switch (j) {
    case 0:
      for (int i = 0; i < 8; i++) Serial1.write(VoltageReadCommand[i]);
      delay(1);
      break;
    case 1:
      for (int i = 0; i < 8; i++) Serial1.write(CurrentReadCommand[i]);
      delay(1);
      break;
    case 2:
      for (int i = 0; i < 8; i++) Serial1.write(PowerReadCommand[i]);
      delay(1);
      break;
    case 3:
      for (int i = 0; i < 8; i++) Serial1.write(KWhReadCommand[i]);
      delay(1);
      break;
      /*default:
        for (int i=0; i < 8; i++) Serial1.write(VoltageReadCommand[i]);
        break;*/
  }
  delay(50);
  // Set pin to LOW > Receive mode
  digitalWrite(RS485_control, LOW);
  // Waiting for receive reply
  while ( Serial1.available() < 9);
  //Serial.println("ciao");
  // Read reply in Buffer[];
  for (int i = 0; i < 9; i++) Buffer[i] = Serial1.read();
  float x;
  ((byte*)&x)[3] = Buffer[3];
  ((byte*)&x)[2] = Buffer[4];
  ((byte*)&x)[1] = Buffer[5];
  ((byte*)&x)[0] = Buffer[6];
  // Serial.println(j);
  Serial.print(x, 2);
  switch (j) {
    case 0:
      Serial.println(" V1");
      //z=1;
      break;
    case 1:
      Serial.println(" A1");
      //z=2;
      break;
    case 2:
      Serial.println(" W1");
      //z=3;
      break;
    case 3:
      Serial.println(" KWH1");
      j = -1;

      break;
      /*case 2:
        Serial.print(" F1");
        break;*/
  }
  //delay(100);
  delay(50);
  j++;
  return x;
}
///////////////////////////
float Conta2() {

  digitalWrite(RS485_control, LOW);
  // Set pin to HIGH > Transmit mode
  digitalWrite(RS485_control, HIGH);
  switch (q) {
    case 0:
      for (int i = 0; i < 8; i++) Serial1.write(VoltageReadCommand2[i]);
      delay(1);
      break;
    case 1:
      for (int i = 0; i < 8; i++) Serial1.write(CurrentReadCommand2[i]);
      delay(1);
      break;
    case 2:
      for (int i = 0; i < 8; i++) Serial1.write(PowerReadCommand2[i]);
      delay(1);
      break;
    case 3:
      for (int i = 0; i < 8; i++) Serial1.write(KWhReadCommand2[i]);
      delay(1);
      break;
      /*default:
        for (int i=0; i < 8; i++) Serial1.write(VoltageReadCommand[i]);
        break;*/
  }
  delay(50);
  // Set pin to LOW > Receive mode
  digitalWrite(RS485_control, LOW);
  // Waiting for receive reply
  while ( Serial1.available() < 9);
  //Serial.println("ciao");
  // Read reply in Buffer[];
  for (int i = 0; i < 9; i++) Buffer[i] = Serial1.read();
  float y;
  ((byte*)&y)[3] = Buffer[3];
  ((byte*)&y)[2] = Buffer[4];
  ((byte*)&y)[1] = Buffer[5];
  ((byte*)&y)[0] = Buffer[6];
  //Serial.println(q);
  Serial.print(y, 2);
  switch (q) {
    case 0:
      Serial.println(" V2");
      //z=1;
      break;
    case 1:
      Serial.println(" A2");
      //z=2;
      break;
    case 2:
      Serial.println(" W2");
      //z=3;
      break;
    case 3:
      Serial.println(" KWH2");
      q = -1;
      break;
      /*case 2:
        Serial.print(" F1");
        break;*/


  }
  delay(50);
  q++;
  return y;
}
//////////////////////////////////////
float Conta3() {

  digitalWrite(RS485_control, LOW);
  // Set pin to HIGH > Transmit mode
  digitalWrite(RS485_control, HIGH);
  switch (k) {
    case 0:
      for (int i = 0; i < 8; i++) Serial1.write(VoltageReadCommand3[i]);
      delay(1);
      break;
    case 1:
      for (int i = 0; i < 8; i++) Serial1.write(CurrentReadCommand3[i]);
      delay(1);
      break;
    case 2:
      for (int i = 0; i < 8; i++) Serial1.write(PowerReadCommand3[i]);
      delay(1);
      break;
    case 3:
      for (int i = 0; i < 8; i++) Serial1.write(KWhReadCommand3[i]);
      delay(1);
      break;
      /*default:
        for (int i=0; i < 8; i++) Serial1.write(VoltageReadCommand[i]);
        break;*/
  }
  delay(50);
  // Set pin to LOW > Receive mode
  digitalWrite(RS485_control, LOW);
  // Waiting for receive reply
  while ( Serial1.available() < 9);
  //Serial.println("ciao");
  // Read reply in Buffer[];
  for (int i = 0; i < 9; i++) Buffer[i] = Serial1.read();
  float z;
  ((byte*)&z)[3] = Buffer[3];
  ((byte*)&z)[2] = Buffer[4];
  ((byte*)&z)[1] = Buffer[5];
  ((byte*)&z)[0] = Buffer[6];
  //Serial.println(k);
  Serial.print(z, 2);
  switch (k) {
    case 0:
      Serial.println(" V3");
      //z=1;
      break;
    case 1:
      Serial.println(" A3");
      //z=2;
      break;
    case 2:
      Serial.println(" W3");
      //z=3;
      break;
    case 3:
      Serial.println(" KWH3");
      k = -1;
      break;
      /*case 2:
        Serial.print(" F1");
        break;*/


  }

  delay(50);
  k++;
  return z;
}

Spero possiate aiutarmi.

Buonasera e benvenuto, :slight_smile:
essendo il tuo primo post, nel rispetto del regolamento della sezione Italiana del forum (… punto 13, primo capoverso), ti chiedo cortesemente di presentarti IN QUESTO THREAD (spiegando bene quali conoscenze hai di elettronica e di programmazione ... possibilmente evitando di scrivere solo una riga di saluto) e di leggere con molta attenzione tutto il su citato REGOLAMENTO ... Grazie. :slight_smile:

Guglielmo

P.S.: Ti ricordo che, purtroppo, fino a quando non sarà fatta la presentazione nell’apposito thread, nessuno ti potrà rispondere, quindi ti consiglio di farla al più presto. :wink:

Buonasera, ho appena postato la mia presentazione :slight_smile:

Bene, ma il tuo programma fa venire un poco il mal di testa a leggerlo: per favore, carica il tuo programma nell'IDE e premi Ctrl-T per farlo indentare decentemente dall'IDE stesso, quindi modifica il primo post e mettici il listato così "corretto". :wink:

corretto

A quest'ora non ci vedo nulla di particolare nel programma, se non nel fatto che all'inizio hai una serie di array di byte definiti però come "int" (se sono tutti valori come singoli byte, definiscili "byte" e non "int").

Poi non capisco bene perché tu hai delle variabili globali "vuno", "vdue" e "vtre" che sono "int", poi le passi (quindi per valore) a parametro alle funzioni Conta() che poi cerca di impostare la variabile "int" con il valore float letto, ed infine lo restituisce anche come valore della funzione, valore che poi non usi...
Hai un "mix" di cose che apparentemente non hanno grosso senso: se una variabile è globale, non hai bisogno di passarla a parametro, e comunque devi definirla dello stesso tipo di ciò che ci metti dentro (se sono "int" ci metti "int", se devi metterci un float devono essere "float"...), ed infine per cambiare il valore di una variabile globale non c'è bisogno né di passarla a parametro né di restituirla, ti basta impostarla direttamente...

Ma in genere se il codice si "impalla" in modo apparentemente imprevedibile, trascurando problemi hardware e/o l'uso di variabili "String" (che tu non hai), in genere sono problemi di puntatori che vanno "in vacca" a scrivere dove non dovrebbero, e dentro vedo vari giochini tipo "((byte*)&y)[1] = Buffer[5];".

Per cui per ora, pensaci, io vado a dormire! :slight_smile:

Si scusa, le variabili erano delle prove che ora ho rimosso, ho modificato il tipo degli array ed ho aggiornato il codice

Ok, ora quindi inizia a spiegare meglio cosa intendi con "incomincia a restituire zeri oppure va in overflow", perché una cosa è che restituisca zeri (restituisce a chi, al Raspberry?) un'altra è che vada in "overflow" (cos'è che va in overflow? Parli sempre del Raspberry? Cosa ti va in overflow, quello che ricevi da Arduino?).

Intanto quindi dando per scontato questo, ho l'impressione che tu possa avere problemi di comunicazione che ti fanno "sballare" l'interpretazione dei dati. Intendo questo:

// Set pin to LOW > Receive mode
  digitalWrite(RS485_control, LOW);
  // Waiting for receive reply
  while ( Serial1.available() < 9);
  // Read reply in Buffer[];
  for (int i = 0; i < 9; i++) Buffer[i] = Serial1.read();
  float y;
  ((byte*)&y)[3] = Buffer[3];
  ((byte*)&y)[2] = Buffer[4];
  ((byte*)&y)[1] = Buffer[5];
  ((byte*)&y)[0] = Buffer[6];

Praticamente tu aspetti di ricevere almeno 9 byte quindi leggi sempre "alla cieca" i byte dal 3 al 6 del pacchetto che evidentemente è composto da 9 byte: in caso di problemi di comunicazione, ad esempio se "manca" un byte o se ce n'è uno "anomalo", rischi pesantemente di essere sempre disallineato da un certo momento in poi, e questo spiegherebbe il tuo "overflow". Non so poi se tu hai sullo stesso bus anche altri device che potrebbero ricevere dati...

Insomma, se hai un protocollo con un pacchetto di 9 byte ci sarà un motivo, no? I primi 3 immagino che siano un header (es. STX, poi il codice device destinatario) e gli ultimi due (forse un checksum ed ETX). Tu quindi devi SEMPRE acquisire i byte e cercare il primo STX (poi dopo di questo se i due caratteri successivi sono un ID devi verificare che sia il tuo, ed in caso negativo, ignorare tutto fino all'ETX) quindi controllare il checksum (se fa parte del pacchetto) e devi alla fine trovare ETX: appena completata l'acquisizione di un pacchetto sicuramente "buono" allora lo puoi processare, il resto lo devi ignorare.

Non conoscendo con cosa come stai comunicando, ti consiglio comunque di iniziare ad usare la seriale "base" per inviarti su serial monitor informazioni di debug, e spostare quindi su Serial2 la comunicazione con Raspberry, per poter quindi "vedere" esattamente cosa stai ricevendo e processando.

Sono d'accordo cono @docdoc

Per l'overflow penso sia semplicemente perchè hai l'array buffer di tipo int, questo perchè la Serial.read() ritorna un int, ma solo perchè può ritornare -1 se non ci sono dati, altrimenti sempre 0-255
Quindi se hai nel buffer un bel -1, poi scrivi nel float forzando byte che è unsigned.
((byte*)&z)[3] = Buffer[3];
Prova a forzare l'array buffer a byte, eventualmente la Serial.read() a -1 fa un cast a 255
Non risolvi i problemi in generale, credo solo overflow

@docdoc
Ok, per rispondere alla prima domanda

231.05 V1

3.76 A1

-613.61 W1

50.22 KWH1

questo è quello che vedo sulla seriale dopo un pò di tempo (possono essere minuti o ore) al posto dei valori che leggo adesso c'è scritto

0.00 V1

0.00 A1

0.00 W1

0.00 KWH1

ed ogni tanto al posto degli zeri (per esempio al posto di 0.00 KWH3) mi ritrovo ovf.

Si è come dici tu, ecco i byte che devo inviare sulla seriale per chiedere una informazione a misuratori :
Slave Address
Function Code
Start Address (HI)
Start Address (LO)
Number of Address (Hi)
Number of Address (Lo)
Error Check (Lo)
Error Check (Hi)

.
Ora cercherò di fare come mi hai detto, farò dei controlli sui byte ricevuti è farò sapere.

Grazie dell'aiuto e della disponibilità

guglielmo_:
ed ogni tanto al posto degli zeri (per esempio al posto di 0.00 KWH3) mi ritrovo ovf.

ovf lo da quando il float che hai ricostruito dai 4 byte non ha un valore sensato

nid69ita:
Sono d'accordo cono @docdoc

Per l'overflow penso sia semplicemente perchè hai l'array buffer di tipo int, questo perchè la Serial.read() ritorna un int, ma solo perchè può ritornare -1 se non ci sono dati, altrimenti sempre 0-255
Quindi se hai nel buffer un bel -1, poi scrivi nel float forzando byte che è unsigned.
((byte*)&z)[3] = Buffer[3];
Prova a forzare l'array buffer a byte, eventualmente la Serial.read() a -1 fa un cast a 255
Non risolvi i problemi in generale, credo solo overflow

Ok, proverò anche questo

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.