Salve ragazzi, mi sto incamminando in questa nuova avventura. Mi sapreste consigliare una libreria "funzionante" per quanto riguarda la comunicazione RS485 via modbus-RTU ?
Grazie e buona giornata
Ciao fabio22,
dopo qualche prova io ho scelto questa:
Grazie Orso per la risposta.
Hai utilizzato l'RS485 come slave ? Nel mio caso arduino farà da MASTER...
Quale esempio hai utilizzato ?
ciao fabio22,
intanto scusa...ti ho indicato un link sbagliato...la libreria che ho utilizzato alla fine è al link:
giusto per cercare di fare un po' di chiarezza: ModBus-RTU è un protocollo di scambio dati; RS485 è il "mezzo/modo" con cui scambi questi dati (comunemente detta "porta seriale")...arduino non ha una porta RS485 nativa ma devi sfruttare moduli aggiuntivi tipo:
https://www.amazon.it/Consiglio-MAX485-Converter-Module-Arduino/dp/B00W1BURBI
...ho preso il primo della ricerca...che "traduce" il TTL di arduino in RS485.
Nella configurazione più semplice c'è un master ed uno slave...in quelle un po'più complesse ci possono essere più master e più slave (la loro gestione dipende da come scrivi il codice)...e da che devices hai nella linea.
la libreria che ti ho indicato può far lavorare arduino sia da Master che da slave.
come esempi avevo dato un occhio a quelli presenti in libreria...ma come spesso gli esempi non illustrano tutta la potenzialità della libreria...
alla fine ho scelto questa, che comunque è quella indicata nel sito, in quanto mi permette di inizializzare anche Serial diverse dalla 0.
Grazie Orso per la spiegazione data. Io il modulino RS485 lo possiedo già... Ora proverò la libreria da te consigliata.
Bisogna fare una configurazione particolare ? Purtroppo non penso che funzioni. Nella seriale mi compaiono cose strane
Foto seriale:
https://we.tl/CjVkXcSdMr
Codice:
#include <ModbusMaster.h>
/*!
We're using a MAX485-compatible RS485 Transceiver.
Rx/Tx is hooked up to the hardware serial port at 'Serial'.
The Data Enable and Receiver Enable pins are hooked up as follows:
*/
#define MAX485_DE 3
#define MAX485_RE_NEG 2
// instantiate ModbusMaster object
ModbusMaster node;
void preTransmission()
{
digitalWrite(MAX485_RE_NEG, 1);
digitalWrite(MAX485_DE, 1);
}
void postTransmission()
{
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
}
void setup()
{
pinMode(MAX485_RE_NEG, OUTPUT);
pinMode(MAX485_DE, OUTPUT);
// Init in receive mode
digitalWrite(MAX485_RE_NEG, 0);
digitalWrite(MAX485_DE, 0);
// Modbus communication runs at 115200 baud
Serial.begin(9600);
// Modbus slave ID 1
node.begin(1, Serial);
// Callbacks allow us to configure the RS485 transceiver correctly
node.preTransmission(preTransmission);
node.postTransmission(postTransmission);
}
bool state = true;
void loop()
{
uint8_t result;
uint16_t data[6];
// Toggle the coil at address 0x0002 (Manual Load Control)
result = node.writeSingleCoil(0x0002, state);
state = !state;
// Read 16 registers starting at 0x3100)
result = node.readInputRegisters(0x168, 16);
if (result == node.ku8MBSuccess)
{
Serial.print("Vbatt: ");
Serial.println(node.getResponseBuffer(0x04)/100.0f);
Serial.print("Vload: ");
Serial.println(node.getResponseBuffer(0xC0)/100.0f);
Serial.print("Pload: ");
Serial.println((node.getResponseBuffer(0x0D) +
node.getResponseBuffer(0x0E) << 16)/100.0f);
}
delay(1000);
}
ciao fabio22,
un paio di cose:
vedo che hai una MEGA2560 (come me)...ma vedo anche che hai inizializzato "solo" la seriale "0" che è quella che ti permette di scrivere sul monitor seriale...io ho lasciato libera la "0" per i comandi da video ed ho inizializzato la 3 per la comunicazione verso l'esterno.
Vedo che hai utilizzato quasi pari pari l'esempio della libreria....dove :
// Toggle the coil at address 0x0002 (Manual Load Control)
result = node.writeSingleCoil(0x0002, state);
state = !state;
cambi di stato un "coil" con indirizzo 2 dello slave 1.
con:
// Read 16 registers starting at 0x3100)
result = node.readInputRegisters(0x168, 16);
leggi 16 registri contigui a partire dal registro 0x168 , che in decimale è 360 passando la verifica della riuscita a "result"
poi tramite:
if (result == node.ku8MBSuccess)
come puoi leggere dalla documentazione se "result == 0" e "node.ku8MBSuccess == 0" la trasmissione è riuscita ed il device esterno ha capito cosa fare e ti ha dato risposta positiva...
quindi, per capire meglio, ti consiglio di dividere la seriale verso l'esterno lasciando libera quella a monitor.
interroga un solo registro alla volta e stampa a video "result" e "node.ku8MBSuccess"...capisci se c'è qualche errore...c'è la lista nel manuale incluso nella libreria...e se vuoi/riesci stampa a video il valore di quel registro.
fammi sapere
Grazie Orso, sto cominciando a capire come funziona in modo pratico questa libreria. Non ho capito solo il passaggio del Coil, principalmente a cosa serve ? Mi cambia il valore di quell'indirizzo ?
un "coil" lo puoi pensare come ad un'uscita fisica tipo un relè o ad un ingresso digitale...di solito "0" spento, "1" acceso...puoi verificare/leggere il suo stato o forzarlo...dipende se il tuo slave te lo lascia fare...di solito per ogni registro c'è scritto se è R/W/RW.
Con questo, che non è altro che la lettura (R) del registro di un'indirizzo, lui mi ritorna "0" e mi entra all'interno della condizione.
result = node.readInputRegisters(0x0101,1);
Ora sto cercando di capire quello che fa all'interno della condizione.
Tu mi confermi che entra solo quando la comunicazione tra i 2 dispositivi è corretta ?
ciao...tuttto è andato a buon fine se :
result == node.ku8MBSuccess
e cioè result == 0 & node.ku8MBSuccess == 0
invio e risposta positivi...
Allora Orso, grazie per la pazienza che possiedi, result = node.writeSingleCoil(0x0101, state);
serve per scrivere un parametro, invece per la lettura si utilizza node.getResponseBuffer(0x0106)/100.0f )
.
È corretto oppure mi sbaglio ?
ciao,
per la pazienza...figurati.
cerco di chiarire meglio cosa vuol dire:
result = node.writeSingleCoil(0x0101, state);
"result" è una variabile nella quale salvi il valore che ritorna la funzione dopo il segno =.
per "node" se guardi all'inizio dell'esempio vedi che c'è scritto "ModbusMaster node;" ...in pratica "node" è un'istanza della classe ModbusMaster che acquisirà i sui attributi e metodi...infatti scrivi "node.writeSingleCoil(0x0101, state)" dove "node" è l'oggetto della classe e writeSingleCoil() è un metodo...e ce ne sono ovviamente altri. Quindi all'inizio puoi anche scrivere ModbusMater ciccio e quindi di conseguenza ciccio.writeSingleCoil().
"writeSingleCoil()" è la funzione o meglio il metodo della classe che invierà al tuo slave la funzione "5" del protocollo ModBus..e cioè forzare (scrivere) lo stato di un coil (che come abbiamo detto puoi pensarlo come ad un relè a cui puoi dire di essere acceso 1 o spento 0)...ci sono anche le altre funzioni del modbus: leggi singolo registro, scrivi singolo registro, leggi multi registro etc.
tra i vari metodi della classe ModbusMaster c'è anche "getResponseBuffer()"...che serve a catturare e contenere la risposta dallo slave ed è un'array...Se non ricordo male ti ritorna una uint16_t per ogni posizione...questo puoi subito scriverlo a video oppure salvartelo in una tua array...il buffer si svuota ogni volta che esegui una richiesta.
Se vuoi interrogare uno o più registri dal tuo slave devi usare i metodi che iniziano con "read"...sono tutti descritti nel manuale della libreria...poi lo slave ti ritornerà la risposta che catturi con "getResponseBuffer()"
spero di non aver fatto troppa confusione.
Ok, funziona tutto... GRAZIE
Ho notato che modificando un bel pò di parametri tutto in una volta si perde del tempo prezioso...
node.writeSingleRegister(0xA100, 50);
node.writeSingleRegister(0xA101, 50);
node.writeSingleRegister(0xA214, 5000);
Come posso eliminare questa perdita di tempo prezioso ?
in che senso si perde tempo!?
Mi rallenta il sistema, quando vado a scrivere... sono all'incirca 24 parametri da scrivere
ciao...ho fatto una prova con un device che ho...un tempo ciclo varia dai 180 ai 300 mS... se i registri che devi scrivere sono contigui, cioè uno di seguito all'altro, potresti usare writeMultipleRegisters()...che se non ho capito male questa libreria gestisce fino a 123 registri per questa funzione (ma dipende ovviamente dai tuoi devices).
però non ho capito se rallenta l'arduino, il PC o lo slave ...e da cosa lo intuisci che è rallentato!?
Ciao, avevo pensato la stessa cosa tua... mi rallenta arduino e lo intuisci mettendo un contatore che aumenta di uno per ogni ciclo e il valore lo stampi nella seriale. Ogni volta che passo i valori dal Master (arduino) allo slave ho un ritardo di un secondo.
ciao...per ciclo intendo "invio comando/ricezione risposta" ...se vuoi verificare un tempo ciclo usa millis()...hai lasciato il delay(1000) o l'avevi rimosso?
nel caso, per test, usane uno più piccolo tipo 100 o 50 mS.
Poi tutto dipende da tante cose, boudrate, i vari devices sono pronti... ?
Guardando nella libreria vedo che come tempo di timeout mettono 2000 mS...giusto per dire quanta latenza ci può essere.