Comunicazione modbus - arduino opta con nano

Buongiorno.
Sto realizzando una comunicazione tra un Arduino OPTA (Master) e un ARDUINO NANO (slave) tramite RS485 con protocollo MODBUS RTU.
La necessità è quella di comandare l'uscita 12 dell'arduino NANO e trasmettere la lettura di tre sensori ad ultrasuoni che faccio con l'arduino NANO.
Ho utilizzato le librerie <ModbusMaster.h> per l'OPTA e <ModbusSlave.h> per il NANO.
Ho trovato diverse guide per impostare la ricezione/trasmissione per l'Arduino NANO attraverso i PIN RE/DE del modulo MAX 485.
Invece per l'Arduino OPTA, che ha la porta di comunicazione RS485 integrata, ho fatto alcune modifiche al solito codice mettendo nel
void preTransmission(){
RS485.beginTransmission();
RS485.noReceive();
}

e nel
void postTransmission(){
RS485.endTransmission();
RS485.receive();
}

Il sistema sembra funzionare ma sembra instabile. Inoltre noto che per ogni lettura/scrittura MODBUS il sistema impieghi oltre 1500ms.
Qualcuno potrebbe indicarmi se la modifica fatta va bene?
Allego il codice del master e dello slave.
Grazie a tutti in anticipo

per il master

#include <ArduinoRS485.h>
#include <ModbusMaster.h>

unsigned long mymillis = millis();
unsigned long differenza;
unsigned long mymillis1 = millis();
unsigned long differenza1;
uint8_t result;
uint8_t result1;
int ciclo;


ModbusMaster node;

void setup() {
constexpr auto baudrate { 9600 }; 
constexpr auto btime { 1.0f / baudrate };
constexpr auto predl { btime * 9.6f * 3.5f * 1e6 };
constexpr auto postdl { btime * 9.6f * 3.5f * 1e6 };

  Serial.begin(9600); 
  RS485.begin(baudrate);
  RS485.setDelays(predl, postdl);
  node.begin(1, RS485); 
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
  RS485.receive(); 
}

void loop() {
modbus_relay();
modbus_sensore_distanza();
delay(50);
}


void modbus_relay(){   /////attivazione PIN 12 dello slave 1 - ARDUINO NANO
  mymillis = millis();
  Serial.print("ciclo= ");
  Serial.println(ciclo);
  result = node.writeSingleCoil(12, 1); ///scrittura del valore 1 all'indirizzo 12 dello slave 1 
  Serial.println("PRIMO NODO");
  Serial.print("node.ku8MBSuccess= ");
  Serial.println(node.ku8MBSuccess);
  Serial.print("result= ");
  Serial.println(result);

  if (result == node.ku8MBSuccess)
  {
    differenza = millis() - mymillis;
    Serial.print("tempo ciclo ID1= ");
    Serial.print(differenza);
    Serial.println(" ms");
    mymillis = millis();
  }
 
  ciclo++;
}


void modbus_sensore_distanza(){

mymillis1 = millis();
  Serial.print("ciclo= ");
  Serial.println(ciclo);

///leggo i 16 registri dell'indirizzo 30001
result1=node.readInputRegisters(30001, 16);

Serial.print("node.ku8MBSuccess= ");
  Serial.println(node.ku8MBSuccess);
  Serial.print("result= ");
  Serial.println(result1);

  if (result == node.ku8MBSuccess)
  {
    differenza1 = millis() - mymillis1;
    Serial.print("tempo ciclo ID1= ");
    Serial.print(differenza1);
    Serial.println(" ms");
    mymillis1 = millis();

    Serial.print("distanza_1 ");
    Serial.println(node.getResponseBuffer(0));
    Serial.print("distanza_2 ");
    Serial.println(node.getResponseBuffer(1));
    Serial.print("distanza_3 ");
    Serial.println(node.getResponseBuffer(2));
  }
 
  ciclo++;
  

}


void preTransmission(){
  RS485.beginTransmission();
  RS485.noReceive();
}

void postTransmission(){
  RS485.endTransmission();
  RS485.receive();
}

e per lo slave ARDUINO NANO

#include <ModbusSlave.h>
#include <SoftwareSerial.h>

int SLAVE_ID=1; ///1= slave ID
int RS485_CTRL_PIN=3; /// 3 = PIN TX/RX per RS485


#define trigPin1 4////sensore vasca 1
#define echoPin1 5///sensore vasca 1
#define trigPin2 6////sensore vasca 2
#define echoPin2 7///sensore vasca 2
#define trigPin3 8////sensore vasca 3
#define echoPin3 9///sensore vasca 3



long intervallo=1000; ///2secondi intervallo tar una lettuar e la successiva dei sensori di livello ad ultrasuoni

long distanza_1;  /////sensore vasca 1
long distanza_1m;  /////media del sensore vasca 1
long tempo_lettura_1=0; /////sensore vasca 1
int sommaValoriValidi_1;/////sensore vasca 1
int conteggioValoricorretti_1;/////sensore vasca 1

long distanza_2;  /////sensore vasca 2
long distanza_2m;  /////media del sensore vasca 2
long tempo_lettura_2=0; /////sensore vasca 2
int sommaValoriValidi_2;/////sensore vasca 2
int conteggioValoricorretti_2;/////sensore vasca 2

long distanza_3;  /////sensore vasca 3
long distanza_3m;  /////media del sensore vasca 3
long tempo_lettura_3=0; /////sensore vasca 3
int sommaValoriValidi_3;/////sensore vasca 3
int conteggioValoricorretti_3;/////sensore vasca 3

SoftwareSerial RS485Serial(10, 11); // RX, TX
Modbus slave(RS485Serial, SLAVE_ID, RS485_CTRL_PIN); 

void setup() {

    pinMode(3, OUTPUT);     // settaggio PIN di controllo RX/TX per l'RS485
    pinMode(12, OUTPUT);  //LED
    pinMode(trigPin1, OUTPUT);
    pinMode(echoPin1, INPUT);
    
        /* register handler functions.
     * into the modbus slave callback vector.
     */
    slave.cbVector[CB_WRITE_COILS] = writeDigitalOut; ///all'oggetto slave applico il codice della funzione (write coil) per il relè
    slave.cbVector[CB_READ_INPUT_REGISTERS] = readAnalogIn; ///all'oggetto slave applico il codice della funzione (leggi registro) per i sensori di distanza
// set Serial and slave at baud 9600.
    Serial.begin( 9600 );
    RS485Serial.begin(9600);
    slave.begin( 9600 );

}

void loop() {
    /* listen for modbus commands con serial port.
     *
     * on a request, handle the request.
     * if the request has a user handler function registered in cbVector.
     * call the user handler function.
     */ 
    slave.poll();
    sensore1();
    sensore2();
    sensore3();
    delay(200);
}


////////////////SENSORRE ULTRASUONI VASCA 1
void sensore1 (){

  if (millis()<tempo_lettura_1) {
  tempo_lettura_1=millis();
  } 

  if (millis()-tempo_lettura_1>intervallo) {
  digitalWrite(trigPin1, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin1, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin1, LOW);
  int durata1 = pulseIn(echoPin1, HIGH);
  distanza_1 = durata1 / 58; // per i pollici la formula è durata / 148;
  tempo_lettura_1=millis();
  }

  if (distanza_1>0) {  ////se la sonda sta leggendo; quando non ha alimentazione il valore restituito è zero 
                                                      /////quindi non consideriamo il dato 
    sommaValoriValidi_1=sommaValoriValidi_1+distanza_1;////consideriamo il dato valido
    conteggioValoricorretti_1=conteggioValoricorretti_1+1; 
    }

  if (conteggioValoricorretti_1>=10) { ////dopo circa xxx secondi se tutte le letture sono corrette
    distanza_1m= sommaValoriValidi_1/conteggioValoricorretti_1;  ////media
    conteggioValoricorretti_1 = 0; 
    sommaValoriValidi_1 = 0; 
    Serial.print("distanza_1 ");
    Serial.println(distanza_1m);  ///media del sensore della vasca 1
  }

}

////////////////SENSORE ULTRASUONI VASCA 2
void sensore2 (){

  if (millis()<tempo_lettura_2) {
  tempo_lettura_2=millis();
  } 

  if (millis()-tempo_lettura_2>intervallo) {
  digitalWrite(trigPin2, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin2, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin2, LOW);
  int durata2 = pulseIn(echoPin2, HIGH);
  distanza_2 = durata2 / 58; // per i pollici la formula è durata / 148;
  tempo_lettura_2=millis();
  }

  if (distanza_2>0) {  ////se la sonda sta leggendo; quando non ha alimentazione il valore restituito è zero 
                                                      /////quindi non consideriamo il dato 
    sommaValoriValidi_2=sommaValoriValidi_2+distanza_2;////consideriamo il dato valido
    conteggioValoricorretti_2=conteggioValoricorretti_2+1; 
    }

  if (conteggioValoricorretti_2>=10) { ////dopo circa xxx secondi se tutte le letture sono corrette
    distanza_2m= sommaValoriValidi_2/conteggioValoricorretti_2;  ////media
    conteggioValoricorretti_2 = 0; 
    sommaValoriValidi_2 = 0; 
    Serial.print("distanza_2 ");
    Serial.println(distanza_2m);  ///media del sensore della vasca 1
  }

}

void sensore3 (){

  if (millis()<tempo_lettura_3) {
  tempo_lettura_3=millis();
  } 

  if (millis()-tempo_lettura_3>intervallo) {
  digitalWrite(trigPin3, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin3, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin3, LOW);
  int durata3 = pulseIn(echoPin3, HIGH);
  distanza_3 = durata3 / 58; // per i pollici la formula è durata / 148;
  tempo_lettura_3=millis();
  }

  if (distanza_3>0) {  ////se la sonda sta leggendo; quando non ha alimentazione il valore restituito è zero 
                                                      /////quindi non consideriamo il dato 
    sommaValoriValidi_3=sommaValoriValidi_3+distanza_3;////consideriamo il dato valido
    conteggioValoricorretti_3=conteggioValoricorretti_3+1; 
    }

  if (conteggioValoricorretti_3>=10) { ////dopo circa xxx secondi se tutte le letture sono corrette
    distanza_3m= sommaValoriValidi_3/conteggioValoricorretti_3;  ////media
    conteggioValoricorretti_3 = 0; 
    sommaValoriValidi_3 = 0; 
    Serial.print("distanza_3 ");
    Serial.println(distanza_3m);  ///media del sensore della vasca 1
  }

}



//////INVIO DATI SENSORI ULTRASUONI A MASTER TRAMITE RS485-MODBUS

uint8_t readAnalogIn(uint8_t fc, uint16_t address, uint16_t length) {
  Serial.print("FC=04: ");
  Serial.println(fc);
  Serial.print("indirizzo: ");
  Serial.println(address);
  Serial.print("byte: ");
  Serial.println(length);
  
 if (address == 30001) {   
  int valori[3]={distanza_1m, distanza_2m, distanza_3m};

  for (int i=0; i<length; i++){
      if (i<3) {
      slave.writeRegisterToBuffer(i, valori[i]);  //Inserisco nel buffer i 3 valori distanza1, 2 e 3
      }
      else{      
      slave.writeRegisterToBuffer(i, 0);  ///completo le informazioni di ritorno verso il master con uno zero
      }
    }
    return STATUS_OK;
  }
    
}



/**
 * Handle Force Single Coil (FC=05) and Force Multiple Coils (FC=15)
 * set digital output pins (coils).
 */
uint8_t writeDigitalOut(uint8_t fc, uint16_t address, uint16_t length) {
  Serial.print("FC=05: ");
  Serial.println(fc);
  Serial.print("indirizzo: ");
  Serial.println(address);
  Serial.print("byte: ");
  Serial.println(length);
  Serial.print("valore: ");
  Serial.println(slave.readCoilFromBuffer(0));
  if (address == 12) {
  int relay=slave.readCoilFromBuffer(0);   ///prendi il dato dal buffer
  ///digitalWrite(12, slave.readCoilFromBuffer(0));
  digitalWrite(12,relay); ////attiva PIN 12
  Serial.println("accendo led");
  }

    return STATUS_OK;
}

se disabiliti, temporaneamente, le pulsein()...cosa succede?

scusami....ma non saprei come disabilitarle. Mi indicheresti come fare?

queste 2 righe:

  int durata1 = pulseIn(echoPin1, HIGH);
  distanza_1 = durata1 / 58; // per i pollici la formula è durata / 148;

le fai diventare:

  // int durata1 = pulseIn(echoPin1, HIGH);
  // distanza_1 = durata1 / 58; // per i pollici la formula è durata / 148;
distanza_1 = 10;

e queste altre 2:

  int durata2 = pulseIn(echoPin2, HIGH);
  distanza_2 = durata2 / 58; // per i pollici la formula è durata / 148;

le fai diventare:

  // int durata2 = pulseIn(echoPin2, HIGH);
  // distanza_2 = durata2 / 58; // per i pollici la formula è durata / 148;
distanza_2 = 11;

e verifichi come si comporta

Ciao Andrea.
Disabilitando le pulsein, come da te indicato, si riduce drasticamente il tempo di risposta. Siamo nell'ordine di 300 ms contro i 1700 ms.
Quindi?

è normale che per restituire la risposta uno slave/server necessiti di un tot di tempo per elaborare la richiesta ed avere i dati pronti...quanto "lungo" è questo tot dipende da molti fattori tra i quali come è scritto il programma.
per esempio la pulseIn() resta in attesa di un cambio di stato del pin...se avviene ti da il tempo trascorso e passa avanti, se non avviene aspetta il suo timeout...in ogni caso sempre tempo passato ad eseguire un'azione "bloccante"...

per rispondere alla tua domanda..."quindi"...devi definire qual'è il tempo massimo di attesa che ti prefiggi e di conseguenza trovare hardware e scrivere il programma in modo "appropriato".

se condividi le necessità magari si fa qualche ragionamento assieme.

Ciao e grazie per le info. In realtà non ho la necessità di avere tempi di ricezione ridotti. Semplicemente volevo capire se il codice da me modificato andava bene e i tempi di ricezione erano giustificabili.
Comunque come test cerco di provare ad inserire un interrupt sulla richiesta modbus (penso che succeda qualcosa sui PIN 10 e 11 della SoftwareSerial) OPPURE allungare i tempi tra una misura e l'altra in modo che alla chiamata modbus arduino non sia impegnato in altri calcoli; infatti il dato è già disponibile nelle tre variabili distanza_1, distanza_2 e distanza_3, per cui alla chiamata modbus, arduino può rispondere immediatamente avendo il dato disponibile. Cosa ne pensi?

Usare la libreria NewPing ? Usa però il Timer2 (quindi non devi usare quel timer2 per altro, esempio la tone )

Proverò questa libreria che ho visto non usa Pulsein. Quando dici che on devi utilizzarlo per altro a cosa ti riferisci? All'arduino nano? Grazie

Nel codice (del nano) non devi avere altre librerie/comandi che utilizzino quel timer2, ad esempio il comando tone() usa il timer2

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