SpenceKonde / DxCore

Arduino core for AVR DA, DB, DD, EA and future DU-series parts - Microchip's latest and greatest AVRs. Library maintainers: Porting help and adviccee is available.
Other
180 stars 47 forks source link

Modbus Protocol Not working on AVR128DA48 Curiosity Nano Board #435

Closed rakeshdhanda closed 1 year ago

rakeshdhanda commented 1 year ago

This is my first post so please excuse me if I have done some mistakes or did not follow the rules of a post. My below code is working well on Arduino UNO but it is not working on AVR128DA48 Curiosity Nano Board. I am using Modscan PC software to check the working of the code. Can someone please help me understand what is wrong with my code or why this board/Chip not supporting the working code of UNO ?

Working Code on Arduino UNO:-

/**
 *  Modbus slave example 2:
 *  The purpose of this example is to link the Arduino digital and analog
 *  pins to an external device.
 *
 *  Recommended Modbus Master: QModbus
 *  http://qmodbus.sourceforge.net/
 *
 *  Editado al español por LuxARTS
 */

//Incluye la librería del protocolo Modbus
#include <ModbusRtu.h>
#define ID   1

//Crear instancia
Modbus slave(ID, Serial, 0); //ID of the node. 0 for master, 1-247 for slave
                        //Serial port (0 = TX: 1 - RX: 0)
                        //Serial protocol. 0 for RS-232 + USB (default), any pin greater than 1 for RS-485
boolean led;
int8_t state = 0;
unsigned long tempus;

uint16_t au16data[9]; //La tabla de registros que se desea compartir por la red

/*********************************************************
 Configuración del programa
*********************************************************/
void setup() {
  io_setup(); //configura las entradas y salidas

  //Serial.begin(19200); //Abre la comunicación como esclavo
  Serial.begin(9600); //You have to make it Serial1 in case of curriosity Nano AVR128DA48 Kit

  slave.start();
  tempus = millis() + 100; //Guarda el tiempo actual + 100ms
  digitalWrite(13, HIGH ); //Prende el led del pin 13 (el de la placa)
}

/*********************************************************
 Inicio del programa
*********************************************************/
void loop() {
  //Comprueba el buffer de entrada
  state = slave.poll( au16data, 9 ); //Parámetros: Tabla de registros para el intercambio de info
                                     //            Tamaño de la tabla de registros
                                     //Devuelve 0 si no hay pedido de datos
                                     //Devuelve 1 al 4 si hubo error de comunicación
                                     //Devuelve mas de 4 si se procesó correctamente el pedido

  if (state > 4) { //Si es mayor a 4 = el pedido fué correcto
    tempus = millis() + 50; //Tiempo actual + 50ms
    digitalWrite(13, HIGH);//Prende el led
  }
  if (millis() > tempus) digitalWrite(13, LOW );//Apaga el led 50ms después

  //Actualiza los pines de Arduino con la tabla de Modbus
  io_poll();
} 

/**
 * pin maping:
 * 2 - digital input
 * 3 - digital input
 * 4 - digital input
 * 5 - digital input
 * 6 - digital output
 * 7 - digital output
 * 8 - digital output
 * 9 - digital output
 * 10 - analog output
 * 11 - analog output
 * 14 - analog input
 * 15 - analog input
 *
 * pin 13 reservado para ver el estado de la comunicación
 */
void io_setup() {
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(13, OUTPUT);

  digitalWrite(6, LOW );
  digitalWrite(7, LOW );
  digitalWrite(8, LOW );
  digitalWrite(9, LOW );
  digitalWrite(13, HIGH ); //onboard LED PIN13 in case of UNO PIN20 in case of Curiosity Nano
  analogWrite(10, 0 ); //PWM 0%
  analogWrite(11, 0 ); //PWM 0%
}

/*********************************************************
Enlaza la tabla de registros con los pines
*********************************************************/
void io_poll() {
  // digital inputs -> au16data[0]
  // Lee las entradas digitales y las guarda en bits de la primera variable del vector
  // (es lo mismo que hacer una máscara)
  bitWrite( au16data[0], 0, digitalRead( 2 )); //Lee el pin 2 de Arduino y lo guarda en el bit 0 de la variable au16data[0] 
  bitWrite( au16data[0], 1, digitalRead( 3 ));
  bitWrite( au16data[0], 2, digitalRead( 4 ));
  bitWrite( au16data[0], 3, digitalRead( 5 ));

  // digital outputs -> au16data[1]
  // Lee los bits de la segunda variable y los pone en las salidas digitales
  digitalWrite( 6, bitRead( au16data[1], 0 )); //Lee el bit 0 de la variable au16data[1] y lo pone en el pin 6 de Arduino
  digitalWrite( 7, bitRead( au16data[1], 1 ));
  digitalWrite( 8, bitRead( au16data[1], 2 ));
  digitalWrite( 9, bitRead( au16data[1], 3 ));

  // Cambia el valor del PWM
  analogWrite( 10, au16data[2] ); //El valor de au16data[2] se escribe en la salida de PWM del pin 10 de Arduino. (siendo 0=0% y 255=100%)
  analogWrite( 11, au16data[3] );

  // Lee las entradas analógicas (ADC)
  au16data[4] = analogRead( 0 ); //El valor analógico leido en el pin A0 se guarda en au16data[4]. (siendo 0=0v y 1023=5v)
  au16data[5] = analogRead( 1 );

  // Diagnóstico de la comunicación (para debug)
  au16data[6] = slave.getInCnt();  //Devuelve cuantos mensajes se recibieron
  au16data[7] = slave.getOutCnt(); //Devuelve cuantos mensajes se transmitieron
  au16data[8] = slave.getErrCnt(); //Devuelve cuantos errores hubieron
}
// end of Uno Code

//Changed code to work on AVR128DA48 Curiosity Nano Board
//I just changed the Serial to Serial1 and onboard LED PIN from 13 to 20
//But this code is not working. I have also tried Serial port 0 using USB to TTL Convertor but that also didn't work.

/**
 *  Modbus slave example 2:
 *  The purpose of this example is to link the Arduino digital and analog
 *  pins to an external device.
 *
 *  Recommended Modbus Master: QModbus
 *  http://qmodbus.sourceforge.net/
 *
 *  Editado al español por LuxARTS
 */

//Incluye la librería del protocolo Modbus
#include <ModbusRtu.h>
#define ID   1

//Crear instancia
Modbus slave(ID, Serial1, 0); //ID of the node. 0 for master, 1-247 for slave
                        //Serial port (0 = TX: 1 - RX: 0)
                        //Serial protocol. 0 for RS-232 + USB (default), any pin greater than 1 for RS-485
boolean led;
int8_t state = 0;
unsigned long tempus;

uint16_t au16data[9]; //La tabla de registros que se desea compartir por la red

/*********************************************************
 Configuración del programa
*********************************************************/
void setup() {
  io_setup(); //configura las entradas y salidas

  //Serial.begin(19200); //Abre la comunicación como esclavo
  Serial1.begin(9600); //You have to make it Serial1 in case of curriosity Nano AVR128DA48 Kit

  slave.start();
  tempus = millis() + 100; //Guarda el tiempo actual + 100ms
  digitalWrite(20, HIGH ); //Prende el led del pin 13 (el de la placa)
}

/*********************************************************
 Inicio del programa
*********************************************************/
void loop() {
  //Comprueba el buffer de entrada
  state = slave.poll( au16data, 9 ); //Parámetros: Tabla de registros para el intercambio de info
                                     //            Tamaño de la tabla de registros
                                     //Devuelve 0 si no hay pedido de datos
                                     //Devuelve 1 al 4 si hubo error de comunicación
                                     //Devuelve mas de 4 si se procesó correctamente el pedido

  if (state > 4) { //Si es mayor a 4 = el pedido fué correcto
    tempus = millis() + 50; //Tiempo actual + 50ms
    digitalWrite(20, HIGH);//Prende el led
  }
  if (millis() > tempus) digitalWrite(20, LOW );//Apaga el led 50ms después

  //Actualiza los pines de Arduino con la tabla de Modbus
  io_poll();
} 

/**
 * pin maping:
 * 2 - digital input
 * 3 - digital input
 * 4 - digital input
 * 5 - digital input
 * 6 - digital output
 * 7 - digital output
 * 8 - digital output
 * 9 - digital output
 * 10 - analog output
 * 11 - analog output
 * 14 - analog input
 * 15 - analog input
 *
 * pin 13 reservado para ver el estado de la comunicación
 */
void io_setup() {
  pinMode(2, INPUT);
  pinMode(3, INPUT);
  pinMode(4, INPUT);
  pinMode(5, INPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(20, OUTPUT);

  digitalWrite(6, LOW );
  digitalWrite(7, LOW );
  digitalWrite(8, LOW );
  digitalWrite(9, LOW );
  digitalWrite(20, HIGH ); //onboard LED PIN13 in case of UNO PIN20 in case of Curiosity Nano
  analogWrite(10, 0 ); //PWM 0%
  analogWrite(11, 0 ); //PWM 0%
}

/*********************************************************
Enlaza la tabla de registros con los pines
*********************************************************/
void io_poll() {
  // digital inputs -> au16data[0]
  // Lee las entradas digitales y las guarda en bits de la primera variable del vector
  // (es lo mismo que hacer una máscara)
  bitWrite( au16data[0], 0, digitalRead( 2 )); //Lee el pin 2 de Arduino y lo guarda en el bit 0 de la variable au16data[0] 
  bitWrite( au16data[0], 1, digitalRead( 3 ));
  bitWrite( au16data[0], 2, digitalRead( 4 ));
  bitWrite( au16data[0], 3, digitalRead( 5 ));

  // digital outputs -> au16data[1]
  // Lee los bits de la segunda variable y los pone en las salidas digitales
  digitalWrite( 6, bitRead( au16data[1], 0 )); //Lee el bit 0 de la variable au16data[1] y lo pone en el pin 6 de Arduino
  digitalWrite( 7, bitRead( au16data[1], 1 ));
  digitalWrite( 8, bitRead( au16data[1], 2 ));
  digitalWrite( 9, bitRead( au16data[1], 3 ));

  // Cambia el valor del PWM
  analogWrite( 10, au16data[2] ); //El valor de au16data[2] se escribe en la salida de PWM del pin 10 de Arduino. (siendo 0=0% y 255=100%)
  analogWrite( 11, au16data[3] );

  // Lee las entradas analógicas (ADC)
  au16data[4] = analogRead( 0 ); //El valor analógico leido en el pin A0 se guarda en au16data[4]. (siendo 0=0v y 1023=5v)
  au16data[5] = analogRead( 1 );

  // Diagnóstico de la comunicación (para debug)
  au16data[6] = slave.getInCnt();  //Devuelve cuantos mensajes se recibieron
  au16data[7] = slave.getOutCnt(); //Devuelve cuantos mensajes se transmitieron
  au16data[8] = slave.getErrCnt(); //Devuelve cuantos errores hubieron
}

//END of Code I am using the below Library for Modbus Slave and it is working well on Arduino Uno Modbus-Master-Slave-for-Arduino-master.zip image

I am getting valid response in case of UNO as per above Image but no response in case of AVR128DA48. I will be thankful for your help.

SpenceKonde commented 1 year ago

I don't really know anything about modbus, but where did the library come from? I do not accept names of libraries, because they are meaningless, any popular library has dozens or hundreds of forks, none of which document the changes or whether they're meant for public consumption. URL of the github repo or name of it in library manager if it's there.

What's the actual topology of a modbus network look like and over which type of interface is it implemented? Serial? Something else? I'm trying to get a better idea of what I'm going to be looking at, and whether it seems more likely to be a core issue or a modbus library issue.

SpenceKonde commented 1 year ago

@rakeshdhanda Moved to the DxCore repo as you are using DxCore (presumably, since that's the one that supports AVR128DA48 You're certainly not using one of my other cores, since they're for tinyAVRs and most certainly not ATTinyCore where you created this issue, as that core only covers classic (old peripherals) tinyAVR parts.

coburnw commented 1 year ago

Are you using half-duplex modbus? I have used https://github.com/CMB27/ModbusRTUSlave with earlier releases of DxCore-1.5 with good success on a 32DB32.

When initializing the serial port for use by ModbusRTUSlave in half duplex mode i use the following:

  // configure half duplex modbus                                                                                            
  Serial2.swap(0);
  Serial2.begin(sys.config()->modbus_baudrate(), (SERIAL_8N1 | SERIAL_HALF_DUPLEX | SERIAL_RS485));
rakeshdhanda commented 1 year ago

I don't really know anything about modbus, but where did the library come from? I do not accept names of libraries, because they are meaningless, any popular library has dozens or hundreds of forks, none of which document the changes or whether they're meant for public consumption. URL of the github repo or name of it in library manager if it's there.

What's the actual topology of a modbus network look like and over which type of interface is it implemented? Serial? Something else? I'm trying to get a better idea of what I'm going to be looking at, and whether it seems more likely to be a core issue or a modbus library issue.

Thanks for your reply. The Modbus protocol is very popular among HMI and PLCs, I am making a PLC to communicate with a Standard HMI over RS485 and Modbus protocol. I already have a PLC working since 5 Years now using the same Modbus Library and ATMEGA328P (Arduino UNO) Chip as quoted by me. About the connections first I try the code using the onboard USB to Serial Interface with Modscan as Modbus Master PC Software. Once I get proper response using the USB Interface I just add the circuit of RS485 to TTL Convertor to my project and start using it with a standard HMI with my custom build application. I need more flash in my controller to run the PLC logic so that is why I am trying to migrate to the AVR128DA48 Device from the ATMEGA328P device. I am using the given below image references as my board and the Arduino Pinout of the same:- AVR128DA44 Curosity Nano Pinout

AVR128DA48 Arduino Pinout

I have also tried all UART other available ports using a USB to TTL Converter to check if any other works on any other UART Port but no luck so far. I hope to hear from you soon. Thanks again.

rakeshdhanda commented 1 year ago

Are you using half-duplex modbus? I have used https://github.com/CMB27/ModbusRTUSlave with earlier releases of DxCore-1.5 with good success on a 32DB32.

When initializing the serial port for use by ModbusRTUSlave in half duplex mode i use the following:

  // configure half duplex modbus                                                                                            
  Serial2.swap(0);
  Serial2.begin(sys.config()->modbus_baudrate(), (SERIAL_8N1 | SERIAL_HALF_DUPLEX | SERIAL_RS485));

Thanks for your suggestion, I tried this Modbus library also but the same thing is happening that it is working on Arduino Uno Board and not on AVR128DA48 Board. Given Below is the Board Settings in the Arduino Ver (1.8.19) that I am doing while working:-

Board Settings AVR128DA48

Board type selected in Arduino I Have tried the below code with and without Half Duplex mode as suggested by you : - /* ModbusRTUSlave Library - Coils_HardwareSerial

This sketch demonstrates how to setup ModbusRTUSlave with 2 coils. This sketch requires a Modbus RTU master/client device with an RS-232 or UART/USART port. If the port is a RS-232 port, a RS-232 to UART converter will be needed. If the port is UART but the logic levels of the port are different than the logic levels of your Arduino, a logic level converter will be needed.

The master/client will need to be cofigured using the following settings:

*/

include

const byte id = 1; const unsigned long baud = 9600; const byte config = SERIAL_8E1; // I have tried both ways "This line and Below line HAlf Duplex mode" it doesn't Work //const byte config = (SERIAL_8E1 | SERIAL_HALF_DUPLEX | SERIAL_RS485); const unsigned int bufSize = 256;

const unsigned int numCoils = 2;

const byte tonePin = 22;//8; Default value in the code const byte ledPin = 20;//13 In case of Arduino UNO

byte buf[bufSize]; ModbusRTUSlave modbus(Serial1, buf, bufSize);

boolean toneActive = 0; unsigned int toneFrequency = 2000;

// This is a function that will be passed to ModbusRTUSlave for reading coils. char coilRead(unsigned int address) { switch (address) { case 0: return digitalRead(ledPin); // No "break" is needed because we exited the function with "return". case 1: return toneActive; } // -1 can be returned if there is an error in getting a coil value. // This will cause the library to send the appropriate exception response to the master/client device. }

// This is a function that will be passed to ModbusRTUSlave for writing to coils. boolean coilWrite(unsigned int address, boolean value) { switch (address) { case 0: digitalWrite(ledPin, value); break; case 1: toneActive = value; break; } return true; // false or 0 can be returned if there is an error in setting a coil value. // This will cause the library to send the appropriate exception response to the master/client device. }

void setup() { pinMode(tonePin, OUTPUT); pinMode(ledPin, OUTPUT); // configure half duplex modbus
//Serial1.swap(0); //Serial1.begin(sys.config()->modbus_baudrate(), (SERIAL_8E1 | SERIAL_HALF_DUPLEX | SERIAL_RS485)); Serial1.begin(baud, config);//Serial in case of Arduino UNO modbus.begin(id, baud, config); modbus.configureCoils(numCoils, coilRead, coilWrite); }

void loop() { modbus.poll(); if (toneActive == true and toneFrequency >= 31) { tone(tonePin, toneFrequency); } else { noTone(tonePin); } }

coburnw commented 1 year ago

Give me a day or two to get my rs485 boards running and sort out (hardware and DxCore) versions. Maybe something has slipped.

coburnw commented 1 year ago

Modbus RTU over rs485 half-duplex seems to work fine for me using DxCore versions 1.5.4 and 1.5.6. I suspect the same for 1.5.5 tho i didnt try. I dont have any DA's to try.

Server:

Client:

When you open the serial port with the RS485 flag set, the core will drive the xdir pin associated with that serial port to enable the output drivers of your rs-485 interface chip as appropriate. In this case the core is doing something special related to rs-485 for you. When the RS485 flag is not set, the core does nothing different (to the best of my knowledge) than any other serial operation. In this case, any modbus related stuff is solely in the hands of your library, your hardware, and your code. see here https://github.com/SpenceKonde/DxCore/blob/master/megaavr/extras/Ref_Serial.md#rs485-mode

If it were me, i would go back to basics: is the baudrate between client and server correct? is tx/rx reversed? parity? one of your pins inverted? a broken wire? You know, the ah-shit stuff. Maybe even pull out the scope.

If you think it might help, pare your code down to a bare minimum on your working system, verify it still fails on your new board, post it here, and i can try it.

Good luck!