ModBus : lettura function code 0x10

Esattamente questo!

Vero, l'ho confuso col read(), sistemato

Sistemato grazie

Va visto a blocchi, il primo è l'attesa di un buffer od un timeout (definito al massimo in una macro).
L'unica cosa è che potrei invertire il primo controllo col secondo evitandomi un controllo. Lo faccio per la lettura ma non dovrebbe inficiare nel codice.

Mai un dato rinvenuto? :smiley: male insomma...

Però credo di sapere in realtà cosa sta causando il problema del "unexpected response".

#include <SoftwareSerial.h>
#include <EasyModbusRTU.h>

#define RTS_PIN       14  // Transmission set pin
#define RX_PIN        26  // Serial Receive pin      R0
#define TX_PIN        27  // Serial Transmit pin     D1

unsigned const int address = 0x01;  // indirizzo del dispositivo
unsigned const int baudrate = 9600;  // baudrate per la comunicaizone

SoftwareSerial rsSerial(RX_PIN, TX_PIN); // RX, TX

EasyModbusRTU device(address, rsSerial, RTS_PIN);

void setup() {
  Serial.begin(115200);
  
  rsSerial.begin(baudrate);
  device.begin(baudrate);
}

void loop() 
{
  uint16_t return_data[0x45] = {0};

  ModbusError comm = device.readHoldingRegister(0x0200, 0x20, return_data);
  if(comm == SUCCESS) 
  {
      float BatteryVolt = return_data[0x0E];
      Serial.print("risposta ");
      Serial.println ( (BatteryVolt/10), 1);
  } 
  else 
  {
      Serial.print(device.lastErrorToString());
  }
  
  Serial.println();
  delay(5000);
}

Prova così.

Inizio a sentirmi veramente stupido stasera :speak_no_evil: :speak_no_evil:
(La cosa "bella" è che il processo di comunicazione vero e proprio l'ho copiato da un'altra libreria che ho scritto per controllare un inverter tramite modbus... e quello va alla perfezione. Domani meglio se lo collego e provo la libreria nuova su quello prima di pubblicare altro :smiley: )

ma ... non noto differerenze col codice precedente (se non solo 0x20 al posto di 0x45) e infatti dà lo stesso risultato
attendo domani, tranquillo
come sempre, almeno per me, è una questione di principio !
a tuo comodo attendo ulteriori sviluppi

domanda : che inverter è il tuo ?

si l'avevo capito...tralasciando quel -1...è il concetto della sequeza di verifiche che non mi convince.

solo per discuterne...la libreria che stai facendo, come le altre che ho visto, aspetta che la risposta sia arrivata, o ci sia il timeout, per poter proseguire nell'analisi dei dati...questo rende di fatto l'esecuzione dello sketch poco fluido...pensa se dovessi interrogare 10 slave e per ogni uno avessi il time out, che secondo me 100ms sono anche pochi, terresti il programma bloccato per 1 secondo...quindi una novità potrebbe essere rendere la gestione delle varie queries, risposte incluse, "asincrona".
Non so se ho sparato una vaccata, perchè magari mi sfugge qualche cosa di base, o se è fattibile!?

Anche volendo non si può, è comunque uno standard seriale, ed anche se c'è un minimo di "framing" non c'è un controllo anti collisione, o meglio, si può implementare ma non è detto che gli slave siano al 100% modbus standard (piccola nota, l'OP oltre ad avere un device che ha funzioni che sovrascrivono le standard riesce anche a usarlo tramite software serial, questo non dovrebbe essere fattibile perché tutte le comunicazioni dovrebbero essere allineati su 10 bit, anche quelle 8N1 dovrebbero mandare 2 stop bit).

Comunque a 9.600 baud 9bit/byte parliamo di 107 Byte in 100ms, si potrebbe aumentare la velocita del bus seriale (a 115.200 con siamo sopra i 1200B in 100ms).

Comunque se devi dialogare con tanti slave metti tanti master :wink:

Non è mio. Progetto di una piegatubi, quello è il motore dell'inverter, con Arduino lo controllo insieme ad un encoder per contare i giri/l'angolo.
È un cinesissimo inverter per motori YL620.

Però avrei il fotovoltaico col sunnyboy, potrei provare anche quello...

non credo serva ... purtroppo nella mia "vita" fotovoltaica ho notato che tutti i produttori utilizzano un protocollo differente, diciamo proprietario, usando seriale, usb e 485, tutti con tracciato record - ribadisco - assolutamente personalizzato.

Credo sia meglio andare per tentativi con la tua libreria, facendo in modo che possa prima di tutto funzionare su routine che danno risultati certi, confrontabili con quelle che ho già funzionanti.

Poi passeremo alla cosa per ora impossibile : cercare di leggere data-ora esistente sul display che tra l'altro (a mio parere sono scemi, dirò dopo perchè) è assolutamente imprecisa; sballa oltre 3 minuti al mese. Ho già messo nel circuito che sto realizzando un DS3231, preziosamente suggerito da guglielmo qualche mese fa (al posto di altri orologi del genere).
Perchè scemi (i produttori dell'inverter) ? Semplice : l'inverter ha un cloud dedicato per dati e grafici. Quindi connessione ad internet h24 : ebbene ... non c'è una chiamata ntp per aggiornare l'orologio.... Incredibile ma vero.

Allora ho aggiornato la libreria perchè avevo commesso un errore epocale (inviavo il CRC in ordine sbagliato), il che spiegherebbe il perchè della risposta sempre uguale.

Ho comunque fatto varie prove a leggere i registri dell'inverter che ho qui tramite arduino mega + convertitore basato su max485 (abbastanza comune).

Ho collegato anche il mio bellissimo ed altrettanto cinesissimo convertitore usb-rs485 per sniffare il bus e capire cosa girava.

codice utilizzato:

#include <EasyModbusRTU.h>

uint8_t address = 0x0A;
uint8_t comm_switch_pin = 15;
int baudrate = 38400;

EasyModbusRTU device(address, Serial2, comm_switch_pin);

void setup() {
  Serial2.begin(38400, SERIAL_8O1);
  device.begin(baudrate);

  Serial.begin(115200);

  
}

void loop() {
  uint16_t return_data[0x45] = {0};
  uint16_t single = 0;
/*
  ModbusError comm = device.readHoldingRegister(0x2001, 1, &single);
  if(comm == SUCCESS) 
  {
      Serial.println(single);
  } 
  else 
  {
      Serial.println(device.lastErrorToString());
  }
*/
  delay(100);

  ModbusError comm = device.readHoldingRegister(0x2000, 0x10, return_data);
  if(comm == SUCCESS) 
  {
      for(int i = 0; i < 0x10; i++) {
        Serial.print(return_data[i]); Serial.print(" ");
      }
  } 
  else 
  {
      Serial.println(device.lastErrorToString());
  }
  
  Serial.println();
  delay(5000);

}

(SERIAL_8O1 nel Serial2.begin() è messo solo perchè l'inverter ha il parity check)

PIC or didn't happen?!



Comunque così facendo la libreria, col mio inverter, funziona.

Sarebbe da verificare con anche il tuo device se a questo punto la lettura dei registri avviene in maniera corretta (ora che ho sistemato l'ordine del CRC)

In sostanza hai modificato la libreria, perchè lo sketch che a te funziona è sostanzialemente paro paro al precedente ... in ogni caso, modificando il tuo sketch secondo le esigenze del mio hardware ho visto che la funzione readHoldingRegister lavora, anche se salta purtroppo abbastanza spesso il risultato.

In pratica ho notato che più di 10 bytes non riesce a leggerli mentre se ne chiedi 2 o 4 non ci sono troppi problemi.
Dico troppi perchè tra ModbusError comm = ..... e success ho dovuto mettere un bel delay (50).
Da quel momento i risultati appaiono al 95%; e comunque ogni tanto appare
Unexpected response
Device did not respond
In pratica bisogna dare tempo alla funzione di sputare il risultato.

Ora, visto che a me nello specifico serve la chiamata 0x10, ho provato ad usare lo stesso comando di chiamata ai volts (funzionante con readHoldingRegister) con customFunction ma ... non va.

Quindi ti chiedo di provare il tuo sketch funzionante ma con device.customFunction ...
fammi sapere se è ok

ciao

secondo me il problema sta qua:

	if(comm_stream->available() == 0) {  // if nothing in buffer timeout happened
    last_error = ModbusError::NO_RESPONSE;
    return last_error; // nothing in buffer...
  }
	
  if(comm_stream->available() < expected_response_size) { // data size mismatch
    last_error = ModbusError::UNEXPECTED_RESPONSE;
		// even if we have unexpected response could be because of exception handling
  }

nel primo if dovresti metter anche un riferimento al time out
nel secondo if verificherei solo se > e non se < ...col minore già dovrebbe esserci il CRC che fa il controllo.

EDIT: forse volevi "includerli" in questo while:

while(comm_stream->available() < expected_response_size && millis()-started < COMM_TIMEOUT_TIME); // wait response

ma così com'è scritto sono fuori.