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 *****
}

Joshua Crawford
Joshua Crawford
14
| 0 0 0
Asked on 3/12/23, 5:14 PM
0
vote
361 Views

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.

Tom
Tom
5675
| 1 1 3
Answered on 3/13/23, 1:20 AM
0
vote

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!

Ask a Question

Keep Informed

About This Forum

This community is for professionals and enthusiasts of our products and services.

Read Guidelines

Question tools

68 follower(s)

Stats

Asked: 3/12/23, 5:14 PM
Seen: 361 times
Last updated: 3/13/23, 1:20 AM