Error in communication: Modbus RTU and Arduino

Hello. I would really appreciate your help with this problem i am facing.

I want to communicate an Arduino Uno with a FY400 PID temperature controller through RS485 using the protocol Modbus RTU.

The controller info:
Manual: https://mrclab.com/data/products/CATALOG//FY-400-OPR.pdf
Communication Guide: https://www.fa-taie.com.tw/admin/download_en/file/2015-02-04/54d1b66ec08a5.pdf

i am using TTL to Rs485 module: https://core-electronics.com.au/ttl-uart-to-rs485-converter-module.html

Where channel A goes to Dx+ and B goes to Dx- in the controller (as is shown in the image)

The thing is when a run the program based on ModbusMaster library by Doc Walker, I find in the serial port a 226, according to what i had read that is a error message. But why?

The controller ID is 1 as in Arduino
The Baudrate in the controller is 9600 as in Arduino
However, i don´t know how to set the parity in the library, because in the controller is 0_81 (Odd parity , Data bits = 8 , Stop Bit = 1 ), how can i do it? or should i use other Library?

This is the code i am using (The idea is to read PV: 24.9; SV: 22.0 and Baudrate: 9600, but i get 655.35 for PV and Baudrate, and 0 for SV ):

#include <ModbusMaster.h>

/*!
  We're using a MAX485-compatible RS485 Transceiver.
  Rx/Tx is hooked up to the hardware serial port at 'Serial'.
  The Data Enable and Receiver Enable pins are hooked up as follows:
*/

int MAX485_RE_NEG = 3;
int MAX485_DE = 2;

//#define MAX485_RE_NEG  3
//#define MAX485_DE      2


// instantiate ModbusMaster object
ModbusMaster node; // Se llamará ModbusMaster como node

void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1); // Es el modo de transmisión
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0); // Es el modo de recepción
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);
  // Init in receive mode
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  Serial.begin(9600);

  // Modbus slave ID 1
  node.begin(1, Serial);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop()
{
  uint8_t ResultadoLectura;

  ResultadoLectura = node.readHoldingRegisters(0x0000,3); //HERE IS 226

    Serial.print("\nQue es (debe dar 0): ");
    Serial.print(ResultadoLectura);  // Debe dar 0
    Serial.print("\n");
    Serial.print("Proceso Exitoso: ");
    Serial.print(node.ku8MBSuccess); // Debe dar 0
    Serial.print("\n");
    Serial.print("\nPV: ");
    Serial.println(node.getResponseBuffer(0x8A) / 100.0f); // HERE 655.35
    Serial.print("\nSV: ");
    Serial.println(node.getResponseBuffer(0x00) / 100.0f); // HERE 0
    Serial.print("\nBaud: ");
    Serial.println(node.getResponseBuffer(0x63) / 100.0f); // HERE 655.35

  if (ResultadoLectura == node.ku8MBSuccess)
  {
    Serial.print("\nPV: ");
    Serial.println(node.getResponseBuffer(0x8A) / 100.0f);
    Serial.print("\nSV: ");
    Serial.println(node.getResponseBuffer(0x00) / 100.0f);
    Serial.print("\nBaud: ");
    Serial.println(node.getResponseBuffer(0x63) / 100.0f);
  }

  delay(2000);
}

Thank you very much, i would really know how to solve the parity problem, why the 226 is generated and the 655.35. :slight_smile:

  // Modbus communication runs at 115200 baud

You shouldn't keep comments that are apparently wrong.

Serial.begin(9600);

change that line to

Serial.begin(9600, SERIAL_8O1);

to activate odd parity.

Thank you very much, i would really know how to solve the parity problem, why the 226 is generated and the 655.35.

The 655.35 is simply a constant serial read error (-1) converted into a fixed point decimal number.

Thank you very much. :slight_smile:

I had changed to Serial.begin(9600, SERIAL_8O1) but it gives the same result, as it is shown in the image.

And there is something happening, I don´t know why, when I upload the program both TX and RX twinkle, but when the program finishes uploading only TX is ON until I open the Serial port and it starts to twinkle (when i close it, TX is ON permanently until I open again the Serial port) and RX is ALWAYS off.

To do some test I changed some parameters in both the Arduino and the controller: the baudrate to 9600, 19200 and 38400, the ID to 3, 2 and 1, the parity 0_8O1 and 0_8E1 (always keeping in mind that both should have the same values) I also changed channel A and B, but I had no good results.

Also, I tried to write something using node.writeSingleRegister(0x0000,34); but it gives 226, using different baud rates.

As it can be seen I have tried different ways to make this communicate. However, the same output data is there (As is shown in the image) the same 226 and 655.35.

Is there any recommendation? What should i do? Am i doing something wrong? or Should I use any other Library, which one do you recommend to use (the idea is the Arduino to be a Master, to WRITE values in the controller, SV, and to receive, PV) or should i continue with the same library?

Thank you very much.

Here i put the changed code:

#include <ModbusMaster.h>

/*!
  We're using a MAX485-compatible RS485 Transceiver.
  Rx/Tx is hooked up to the hardware serial port at 'Serial'.
  The Data Enable and Receiver Enable pins are hooked up as follows:
*/

//int MAX485_RE_NEG = 8;
//int MAX485_DE = 7;

#define MAX485_RE_NEG  8
#define MAX485_DE      7


// instantiate ModbusMaster object
ModbusMaster node; // Se llamará ModbusMaster como node

void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1); // Es el modo de transmisión
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0); // Es el modo de recepción
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);

  // Empieza en modo de recepción
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 115200 baud
  Serial.begin(9600, SERIAL_8O1); // Acá se define la paridad: 8bits, odd parity, 1 bit de parada

  // Modbus slave ID 1
  node.begin(1, Serial);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop()
{
  uint8_t ResultadoLectura;
    uint8_t ResultadoLectura1;
  // Read 3 registers starting at 0x0000)
  ResultadoLectura = node.readHoldingRegisters(0x008A,3); //HERE IS 226
  ResultadoLectura1= node.writeSingleRegister(0x0000,34);
  Serial.print("Resultado de writeSingleRegister: ");
  Serial.println(ResultadoLectura1);

 
  Serial.print("\nQue es (debe dar 0): ");
  Serial.print(ResultadoLectura);  // Debe dar 0
  Serial.print("\n");
  Serial.print("Proceso Exitoso: ");
  Serial.print(node.ku8MBSuccess); // Debe dar 0
  Serial.print("\n");
  Serial.print("\nPV: ");
  Serial.println(node.getResponseBuffer(0x8A) / 100.0f); // HERE 655.35
  Serial.print("\nSV: ");
  Serial.println(node.getResponseBuffer(0x00) / 100.0f); // HERE 0
  Serial.print("\nBaud: ");
  Serial.println(node.getResponseBuffer(0x63) / 100.0f); // HERE 655.35

  if (ResultadoLectura == node.ku8MBSuccess)
  {
    Serial.print("\nPV: ");
    Serial.println(node.getResponseBuffer(0x8A) / 100.0f);
    Serial.print("\nSV: ");
    Serial.println(node.getResponseBuffer(0x00) / 100.0f);
    Serial.print("\nBaud: ");
    Serial.println(node.getResponseBuffer(0x63) / 100.0f);
  }

  delay(2000);
}

ResultsInSerialPort.PNG

And there is something happening, I don´t know why, when I upload the program both TX and RX twinkle, but when the program finishes uploading only TX is ON until I open the Serial port and it starts to twinkle (when i close it, TX is ON permanently until I open again the Serial port) and RX is ALWAYS off.

That's not strange, the idle state of an UART signal is HIGH, a transition to LOW signals the start bit.

Also, I tried to write something using node.writeSingleRegister(0x0000,34); but it gives 226, using different baud rates.

226 means the request timed out. This usually means you have a wiring problem. Can you post a complete wiring diagram (may be hand drawn)?

  Serial.print("Resultado de writeSingleRegister: ");
  Serial.println(ResultadoLectura1);

You must not use Serial to do debugging. That interface is used for the Modbus, so any character sent to it is sent to the Modbus device and will make it refuse the next message.

Thank you.

I will delete the lines used for debugging.

I made this connection scheme (in the attachments), I hope to be useful.

I will check the conections again.

Thank you very much :slight_smile:

Thanks to all your help I had communicated the Arduino with the FY400. :slight_smile:

I had to correct the connection like this: D1 goes to Tx and RO goes to Rx, and it worked!!!

It works almost great. I can write the setpoint, the SV from the Arduino, I can read the SV. BUT when i want to read the the PV or the baudrate or any other value it return a 65535 or a 0.

In the image in the attachments this can be seen.

Could you please tell me why it happens, you mentioned that: "The 655.35 is simply a constant serial read error (-1) converted into a fixed point decimal number." and have seen the 65535 is the max value of uint8 variables. May be I have to make a conversion? or should I use other type of variable? What do you recommend? Why can i read without problems the SV, but other values like PV gives such number?

This is the code that is working: writing and reading SV, but giving a wrong value of PV which should be 23, and baudrate should be 96.

#include <ModbusMaster.h>

/*!
  We're using a MAX485-compatible RS485 Transceiver.
  Rx/Tx is hooked up to the hardware serial port at 'Serial'.
  The Data Enable and Receiver Enable pins are hooked up as follows:
*/

//int MAX485_RE_NEG = 8;
//int MAX485_DE = 7;

#define MAX485_RE_NEG  8
#define MAX485_DE      7


// instantiate ModbusMaster object
ModbusMaster node; // Se llamará ModbusMaster como node

void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1); // Es el modo de transmisión
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0); // Es el modo de recepción
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);

  // Empieza en modo de recepción
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 115200 baud
  Serial.begin(9600, SERIAL_8O1); // Acá se define la paridad: 8bits, odd parity, 1 bit de parada

  // Modbus slave ID 1
  node.begin(1, Serial);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop()
{
  uint8_t ResultadoLectura;
  uint8_t ResultadoEscritura;

  ResultadoEscritura = node.writeSingleRegister(0x0000, 80); //HERE IS 226, Lee un registro

  ResultadoLectura = node.readHoldingRegisters(0x0000, 1); //HERE IS 226, Lee un registro


  if (ResultadoLectura == node.ku8MBSuccess)
  {
    Serial.print("Funcionó!");
    Serial.print("\nPV: ");
    Serial.println(node.getResponseBuffer(0x8A)); // Divide by 100.0f
    Serial.print("\nSV: ");
    Serial.println(node.getResponseBuffer(0x00));
    Serial.print("\nBaud: ");
    Serial.println(node.getResponseBuffer(0x63));
  }
  else {
    Serial.print("\nError en conexión: ");
    Serial.println(ResultadoLectura);  // Debe dar 0
  }

  delay(1000);
}

Valores.PNG

You have to read the corresponding registers first, you read only register 0x0000:

  ResultadoLectura = node.readHoldingRegisters(0x0000, 1); //HERE IS 226, Lee un registro

So only the index 0x00 of the response buffer will have a result.

Thank you.

I can read and write info to my FY400 temperature controller. I can do it with an Arduino Uno and Mega, but i can not with an Arduino Due.

For Uno and Mega i used the library mentioned before (MosbusMaster), I tried to used the same library in the Arduino Due, but i can only write and in a incorrect way because it writes to the controller, but in serial port it says 226.

I searched on internet about modbus and Due and there is a library called SimpleMosbus, and there is a version for Due, here:

-simple-modbus/Modbus RTU libraries for Arduino/SimpleModbusMasterV2rev2_DUE at master · jecrespo/simple-modbus · GitHub

(I am using SimpleModbusMaster_DUE.h)

However, i have been using the example SimpleModbus provided, but it does nothing, (i can not read nor write).

This is the code i have been using, with no results:

#include <SimpleModbusMaster_DUE.h>

/*
   The example will use packet1 to read a register from address 0 (the adc ch0 value)
   from the arduino slave (id=1). It will then use this value to adjust the brightness
   of an led on pin 9 using PWM.
   It will then use packet2 to write a register (its own adc ch0 value) to address 1 
   on the arduino slave (id=1) adjusting the brightness of an led on pin 9 using PWM.
*/

long previousMillis = 0;
long interval = 1000;

//////////////////// Port information ///////////////////
#define baud 9600
#define timeout 1000
#define polling 200 // the scan rate
#define retry_count 10

// used to toggle the receive/transmit pin on the driver
#define TxEnablePin 7


// The total amount of available memory on the master to store data
#define TOTAL_NO_OF_REGISTERS 3

// This is the easiest way to create new packets
// Add as many as you want. TOTAL_NO_OF_PACKETS
// is automatically updated.
enum
{
  PACKET1,
  PACKET2,
  //PACKET3,
  TOTAL_NO_OF_PACKETS // leave this last entry
};

// Create an array of Packets to be configured
Packet packets[TOTAL_NO_OF_PACKETS];

// Masters register array
unsigned int regs[TOTAL_NO_OF_REGISTERS];

void setup()
{
  // Initialize each packet
   Serial.begin(9600,SERIAL_8O1); // tried to set a parity required, it could alse be 8, even, 0 or 1
  //modbus_construct(&packets[PACKET1], 1, READ_HOLDING_REGISTERS, 80, 1, 0);
  modbus_construct(&packets[PACKET2], 1, PRESET_SINGLE_REGISTER, 0, 41, 0);
  
  // Initialize the Modbus Finite State Machine
  modbus_configure(&Serial, baud, timeout, polling, retry_count, TxEnablePin, packets, TOTAL_NO_OF_PACKETS, regs);
  
}

void loop()
{
  modbus_update();

 // Serial.println(regs[0]);
  //Serial.println(regs[1]);
 // Serial.println(TOTAL_NO_OF_REGISTERS);
 // delay(1000);

  unsigned long SV = (unsigned long)regs[0] << 16 | regs[1];

  //Serial.print("A: ");
  Serial.println(SV);

  //Serial.print("Exception errors: ");
  Serial.println(packets[PACKET1].exception_errors);


  //Serial.print("Failed requests: ");
  //Serial.println(packets[PACKET1].failed_requests);


  //Serial.print("Successful requests: ");
//  Serial.println(packets[PACKET1].successful_requests);
//  Serial.print("Low byte: ");
//  Serial.println(regs[0]);
//  Serial.print("High byte: ");
//  Serial.println(regs[1]);
//  Serial.println("----------");
}

I left some comments to show i have been trying to know how the communication behaves.

Note1: The connections are the same as in the UNO and MEGA.
Note2: I am in problems with the library because i have to stablish a parity in the controller, i chose 8O1, a baud rate, 9600, and the id:1. BUT in the library i find no way to say the parity with the Due, i tried Serial.begin(9600, Serial_8o1), but it does not work.

Note3: I tried everything with SimpleModbusMaster_Due.h, but it doesn't work, it doesn't send any data through rx and tx. I am working with ModbusMaster.h, the one which work with Uno and Mega, because it at least set a data in the FY400, but i can not read because it says 226 error. The entire response was not received within the timeout period,
ModbusMaster::ku8MBResponseTimeout. (And all the connections are the same like with Arduino Uno and Mega)

I will be really thank if you could give me any recommendation or advice. I don´t know what to do in this case. Thanks.

I searched on internet about modbus and Due and there is a library called SimpleMosbus, and there is a version for Due, here:

I can see no reason why ModbusMaster shouldn't work on the Due. It doesn't use specifics of the AVR platform.

Note1: The connections are the same as in the UNO and MEGA.

That cannot work. The UNO and Mega use 5V level while the Due runs with 3.3V levels. You might destroy your Due if you connect it directly to 5V hardware.

Use Serial1 on the Due to avoid conflicts with your debugging channel.

Thank you very much. Muchas gracias :slight_smile:

I can communicate the Arduino Due with my FY400 temperature controller.

The problems I had:

-> The RS485 module was not being powered with 5V from the Arduino and its ground was not common directly to the Arduino. In my electronics all the lands are united, however, it must have been a mistake not to have initially fed the Due with 5V and its land that came directly from the Arduino.

-> Maybe having been using the Serial (Rx0 and Tx1) for debugging and communication generated conflicts, thanks to your recommendation, I assigned the Serial (Rx0 and Tx1) for debugging and Serial1 (Rx19 and Tx18) for communication.

The final connections are:

DI to Tx18
RO to Rx19
DE and RE to digital pins 7 and 9, respectively.
A goes to pin 12 (DX +) of the FY400
B goes to pin 11 (DX-) of FY400
And Vcc-GND come from the arduino directly.

This is the code i used, with the library ModbusMaster by Doc Walker.

/*

  RS485_HalfDuplex.pde - example using ModbusMaster library to communicate
  with EPSolar LS2024B controller using a half-duplex RS485 transceiver.

  This example is tested against an EPSolar LS2024B solar charge controller.
  See here for protocol specs:
  http://www.solar-elektro.cz/data/dokumenty/1733_modbus_protocol.pdf

  Library:: ModbusMaster
  Author:: Marius Kintel <marius at kintel dot net>

  Copyright:: 2009-2016 Doc Walker

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.

*/

#include <ModbusMaster.h>

/*!
  We're using a MAX485-compatible RS485 Transceiver.
  Rx/Tx is hooked up to the hardware serial port at 'Serial'.
  The Data Enable and Receiver Enable pins are hooked up as follows:
*/

//int MAX485_RE_NEG = 9;
//int MAX485_DE = 7;

#define MAX485_RE_NEG  9
#define MAX485_DE      7

int myInts[6];

// instantiate ModbusMaster object
ModbusMaster node; // Se llamará ModbusMaster como node

void preTransmission()
{
  digitalWrite(MAX485_RE_NEG, 1); // Es el modo de transmisión
  digitalWrite(MAX485_DE, 1);
}

void postTransmission()
{
  digitalWrite(MAX485_RE_NEG, 0); // Es el modo de recepción
  digitalWrite(MAX485_DE, 0);
}

void setup()
{
  pinMode(MAX485_RE_NEG, OUTPUT);
  pinMode(MAX485_DE, OUTPUT);

  // Empieza en modo de recepción
  digitalWrite(MAX485_RE_NEG, 0);
  digitalWrite(MAX485_DE, 0);

  // Modbus communication runs at 115200 baud
  Serial.begin(9600);
  Serial1.begin(9600, SERIAL_8E1); // Acá se define la paridad: 8bits, odd parity, 1 bit de parada [Due no soporta bit de parada 2]

  // Modbus slave ID 1
  node.begin(1, Serial1);
  // Callbacks allow us to configure the RS485 transceiver correctly
  node.preTransmission(preTransmission);
  node.postTransmission(postTransmission);
}

void loop()
{
  uint8_t ResultadoLectura;
  uint8_t ResultadoEscritura;


  //  for (int j = 15; j <= 100; j = j + 10 ) {
  //    ResultadoEscritura = node.writeSingleRegister(0x0000, j); //HERE IS 226, Lee un registro
  //    delay(1000);
  //  }

  ResultadoLectura = node.readHoldingRegisters(0x8A, 1); //HERE IS 226, Lee un registro
  ResultadoEscritura = node.writeSingleRegister(0x0000, 20); //HERE IS 226, Lee un registro


  if (ResultadoLectura == node.ku8MBSuccess)
  {
    Serial.print("\nPV2: ");
    Serial.println(node.getResponseBuffer(0));
  }
  else {
    Serial.print("\nError en conexión: ");
    Serial.println(ResultadoLectura, HEX);  // Debe dar 0
    Serial.println(ResultadoEscritura, HEX);  // Debe dar 0

  }

  delay(1000);
}

Thank you very much from Colombia.

I hope everything achieved through the discussion in this forum will be helpful for someone else. :slight_smile:

Thank you bro! Solved my problem of reading holding resistor.
If your modbus query is correct.. or sending is successful then ku8MBSuccess =0.
And to read data we use get responses buffer. getResponseBuffers(0) reads values

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