Intermittent Modbus RTU Receive
Hi All,
I've been using a few D21G for projects over the past few years. However, on the most recent project, Ive been plagued by a Modbus issue that I just ignored but now I cant ignore any longer. The issue is happening on BOTH D21G units. They have seperate slave addresses but in other facets run the same code. The problem I have is that both devices, whether connected at the same time or only one at a time, will only respond to ~50% of modbus messages.
I have attached an external Waveshare RS485 serial analyser and I get the message sent from my FTDI chip every single time, but the D21G only responds maybe 50% of the time.
Ive noticed it will generally fail to respond and then continuously fail to respond to any further messages unless a long delay (maybe 15-20seconds is given) before attempting to re-poll.
I cant find any rhyme or reason but its infuriating! Code below
/*
This sketch controls the Digital Slave in the SETH IO Module. It works by acting as a modbus slave in Remote mode, or a general UART device when operating in Handheld.
Written 24th May 2022 by Josh Crawford
*/
#include <Indio.h>
#include <Wire.h>
#include <UC1701.h>
#include <SimpleModbusSlave.h>
#include <EasyNextionLibrary.h>
#include <trigger.h>
#define baud 9600
#define TxEnablePin 9 // INDUSTRUINO RS485
#define SlaveID 1 //Digi Slave [IOC]
static UC1701 lcd;
const int backlightPin = 26; // PWM output pin that the LED backlight is attached to
const int backlightIntensity = 5; // LCD backlight intensity
const int backlightIntensityDef = 5; // Default LCD backlight intensity
//NEW
#define mode_switch 2 //analogue channel 1 for detecting mode switch status
int mode_adc; //read the mode switch adc value, int for 12bit ADC resolution, float for 14/16/18bit
int mode_selection = 1; //0=handheld, 1=remote. Default to remote
int settingRequest;
int io_w_line = 0; //line for writing input/output state
enum{
READ_STATE_1,
READ_STATE_2,
READ_STATE_3,
READ_STATE_4,
WRITE_STATE_1,
WRITE_STATE_2,
WRITE_STATE_3,
WRITE_STATE_4,
HEALTH_STATUS, //this is the register that is updated with error codes or health status by industruino
HOLDING_REGS_SIZE
};
unsigned int r_cnt = 0;
unsigned int holdingRegs[HOLDING_REGS_SIZE]; //function 3 and 16 register array. 16bit(2byte) values, same as Modbus RTU data word size (Hi & Lo)
unsigned int lastHoldingRegs[HOLDING_REGS_SIZE];
int ctr;
long previousMillis = 0;
long interval = 100;
unsigned long currentMillis;
void setup()
{
// NEW
//Serial.begin(19200);
lcd.begin(); //sets the resolution of the LCD screen
analogWrite(backlightPin, (map(backlightIntensity, 5, 1, 255, 0))); //convert backlight intesity from a value of 0-5 to a value of 0-255 for PWM.
for (int y = 0; y <= 7; y++) {
for (int x = 0; x <= 128; x++) {
lcd.setCursor(x, y);
lcd.print(" ");
}
}
Indio.setADCResolution(12); //sets the ADC resolution to 12bit.
Indio.digitalMode(READ_STATE_1 + 1, OUTPUT);
Indio.digitalWrite(READ_STATE_1 + 1, LOW); //clear D1 before using
Indio.digitalMode(READ_STATE_1 + 1, INPUT);
Indio.digitalMode(READ_STATE_2 + 1, OUTPUT);
Indio.digitalWrite(READ_STATE_2 + 1, LOW); //clear D2 before using
Indio.digitalMode(READ_STATE_2 + 1, INPUT);
Indio.digitalMode(READ_STATE_3 + 1, OUTPUT);
Indio.digitalWrite(READ_STATE_3 + 1, LOW); //clear D3 before using
Indio.digitalMode(READ_STATE_3 + 1, INPUT);
Indio.digitalMode(READ_STATE_4 + 1, OUTPUT);
Indio.digitalWrite(READ_STATE_4 + 1, LOW); //clear D4 before using
Indio.digitalMode(READ_STATE_4 + 1, INPUT);
Indio.digitalMode(WRITE_STATE_1 + 1, OUTPUT);
Indio.digitalWrite(WRITE_STATE_1 + 1, LOW);
Indio.digitalMode(WRITE_STATE_2 + 1, OUTPUT);
Indio.digitalWrite(WRITE_STATE_2 + 1, LOW);
Indio.digitalMode(WRITE_STATE_3 + 1, OUTPUT);
Indio.digitalWrite(WRITE_STATE_3 + 1, LOW);
Indio.digitalMode(WRITE_STATE_4 + 1, OUTPUT);
Indio.digitalWrite(WRITE_STATE_4 + 1, LOW);
Indio.analogReadMode(1, V10_raw); //sets Mode Switch analog in ch1 to 0-10V read mode
lcd.setCursor(2, 0); //sets cursor to the first line.
lcd.print("SETH I/O Module 1");
//mode_adc = Indio.analogRead(1); //Beware! Doesnt seem to work if Serial.begin is not initiated before AnalogRead
lcd.setCursor(2, 1); //sets cursor to the second line.
/*if(mode_adc > 6.0) {
mode_selection = 1;
lcd.print("[REMOTE]");
}
else{
mode_selection = 0;
lcd.print("[HANDHELD]");
}*/
mode_selection = 1;
lcd.setCursor(2, 2); //sets cursor to the second line.
lcd.print("INPUTS");
lcd.setCursor(2, 3);
lcd.print("CH1:");
lcd.setCursor(2, 4);
lcd.print("CH2:");
lcd.setCursor(2, 5);
lcd.print("CH3:");
lcd.setCursor(2, 6);
lcd.print("CH4:");
lcd.setCursor(64 ,2);
lcd.print("OUTPUTS");
lcd.setCursor(64, 3);
lcd.print("CH5:");
lcd.setCursor(64, 4);
lcd.print("CH6:");
lcd.setCursor(64, 5);
lcd.print("CH7:");
lcd.setCursor(64, 6);
lcd.print("CH8:");
//need setting on setup as won't receive a value from SETH immediately
holdingRegs[0] = 0;
holdingRegs[1] = 0;
holdingRegs[2] = 0;
holdingRegs[3] = 0;
holdingRegs[4] = 0;
holdingRegs[5] = 0;
holdingRegs[6] = 0;
holdingRegs[7] = 0;
holdingRegs[8] = 1; //health status
lastHoldingRegs[0] = 0;
lastHoldingRegs[1] = 0;
lastHoldingRegs[2] = 0;
lastHoldingRegs[3] = 0;
lastHoldingRegs[4] = 0;
lastHoldingRegs[5] = 0;
lastHoldingRegs[6] = 0;
lastHoldingRegs[7] = 0;
lastHoldingRegs[8] = 1;
}
void loop()
{
if(mode_selection == 0){
//HANDHELD MODE
EasyNex myNex(Serial);
myNex.begin(baud);
handheld_mode(myNex);
}
else{
//REMOTE MODE
modbus_configure(&Serial, baud, SERIAL_8N2, SlaveID, TxEnablePin, HOLDING_REGS_SIZE, holdingRegs);
modbus_update_comms(baud, SERIAL_8N2, SlaveID);
remote_mode();
}
}
void remote_mode()
{
//REMOTE MODE (CONTROL VIA SETH)
modbus_update(); //updates all register values based on any modbus requests
//Iterate through each of the 'READ' input pins and assign to relevant register (channel minus 1)
//READ BLOCK
for (int i=0; i < WRITE_STATE_1; i++){
holdingRegs[i] = Indio.digitalRead(i + 1); //Holding Regs start at 0, Channels start at 1
}
//WRITE BLOCK
for (int j=WRITE_STATE_1; j < HEALTH_STATUS; j++){
if (holdingRegs[j] != lastHoldingRegs[j]){
lastHoldingRegs[j] = holdingRegs[j];
if (holdingRegs[j] == 0){
Indio.digitalWrite(j + 1, LOW);
}
else{
Indio.digitalWrite(j + 1, HIGH);
}
}
}
ctr = 0;
//Sets the LCD values for each register (read or write), following any modbus register updates
for (int col = 25; col < 89; col += 63) {
for (int row = 3; row < 7; row++) {
lcd.setCursor(col, row);
if (holdingRegs[ctr] == 1){
lcd.print("ON ");
}
else{
lcd.print("OFF");
}
ctr ++;
}
}
}
void handheld_mode(EasyNex &myNex)
{
//HANDHELD MODE
myNex.NextionListen(); //cyclically check for touch events
}
void trigger0()
{
//Trigger 0 is for *****
}
Hi,
There are 2 things i would suggest you to check:
1. in your code, you constantly run 'modbus_configure', while this is typically only done once to initiate the protocol e.g. in setup(). the loop() only needs to call 'modbus_update'. please refer to the example slave code at https://industruino.com/blog/our-news-1/post/modbus-rtu-master-and-slave-14
2. if the above does not fix it, please have a look at the 'termination resistor' section in the same link. your INDIOs come with 3 jumpers to activate termination resistors as usually used on modbus master devices. for slaves, you can remove 2 of those jumpers. this is often not critical, but worth checking out.
Your answer
Please try to give a substantial answer. If you wanted to comment on the question or answer, just use the commenting tool. Please remember that you can always revise your answers - no need to answer the same question twice. Also, please don't forget to vote - it really helps to select the best questions and answers!
Keep Informed
About This Forum
This community is for professionals and enthusiasts of our products and services.
Read GuidelinesQuestion tools
Stats
| Asked: 3/12/23, 5:14 PM |
| Seen: 1587 times |
| Last updated: 3/13/23, 1:20 AM |