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: 688 times |
Last updated: 3/13/23, 1:20 AM |