RS485 communication

Hello Coders!

I discovered the Arduino for which I have only a very small experience and I will soon receive measurement equipment whose data is transmitted according to the RS485 protocol. The project is to acquire this data with Arduino and then exploit it.

The sensor manufacturer gives me the following information:

  • Wired connection: RS485 (A +): RS485 communication interface A + / RS485 (B -): RS485 communication interface B-
  • Communication protocol:
    Sensor inquiry: 00 03 00 00 00 01 85 DB
    Sensor response: 00 03 02 02 AE 05 58

As you might expect, my question is how to interrogate the sensor and how to receive its response with an Arduino and an RS485 module.

For some of you it should be easy but I would have trouble without some explanation (how to connect the module to the sensor and the Arduino, which libraries, examples of sketch for the interrogation of the sensor and to receive the reply...).

I'm waiting for your help Thanks in advance.

RS485 is an electrical protocol, so has nothing to do with software. Look at the serial data examples and tutorials. They will get you going.

Paul

Ok but should I send to the sensor a full string like this in my sketch: RS485Serial.write(00030000000185DB); ?

TEDDOL:
Ok but should I send to the sensor a full string like this in my sketch: RS485Serial.write(00030000000185DB); ?

You would need to read the sensor's datasheet for the answer to that. It would surely explain what the sensor expects to receive and will deem a valid request.

Have a look here, where there's a library with an example of how to compose a string containing the recipient address, request code, paramater values and so on.

Hi Ted,
(I suggested he come here with these questions as I am travelling...)

You really need to tell us the manufacturer and type of sensor, and point to a data sheet etc..

Hi, come back with this subject about RS485 modbus and my controller. I did not find the good way yet.
You can fin in attached file data sheet communication and wiring scheama used.

Here the sketch used:

#include SoftwareSerial.h

#define SSerialRX        10  //Serial Receive pin
#define SSerialTX        11  //Serial Transmit pin
#define SSerialTxControl 3   //RS485 Direction control
#define RS485Transmit    HIGH
#define RS485Receive     LOW
#define Pin13LED         13

SoftwareSerial RS485Serial(SSerialRX, SSerialTX); // RX, TX
byte byteSend;

void setup()   
{
  // Start the built-in serial port, probably to Serial Monitor
  Serial.begin(9600);
  Serial.println("SerialRemote");  // Can be ignored

  pinMode(Pin13LED, OUTPUT);   
  pinMode(SSerialTxControl, OUTPUT);  

  digitalWrite(SSerialTxControl, RS485Receive);  // Init Transceiver


  RS485Serial.begin(9600);   
  delay(2000);
  digitalWrite(SSerialTxControl, RS485Transmit);  
   RS485Serial.write((byte)0x00);         
   RS485Serial.write((byte)0x03);                 
   RS485Serial.write((byte)0x00);                 
   RS485Serial.write((byte)0x00);                   
   RS485Serial.write((byte)0x00);                 
   RS485Serial.write((byte)0x01);         
   RS485Serial.write((byte)0x85);                
   RS485Serial.write((byte)0xDB); 
   Serial.println(" "); 

   digitalWrite(Pin13LED, LOW);  
   delay(10);
   digitalWrite(SSerialTxControl, RS485Receive);

}
void loop()   
{

  if (RS485Serial.available()) 
  {
    byteSend = RS485Serial.read();   // Read the byte
    Serial.print(byteSend, HEX);
    Serial.print(" ");
  }
else
  {
    Serial.println(" ");
   delay (1000);
   digitalWrite(SSerialTxControl, RS485Transmit);  // Enable RS485 Transmit
   RS485Serial.write((byte)0x00);          // Send byte to Remote Arduino
   RS485Serial.write((byte)0x03);                  // Send byte to Remote Arduino
   RS485Serial.write((byte)0x00);                  // Send byte to Remote Arduino
   RS485Serial.write((byte)0x00);                    // Send byte to Remote Arduino
   RS485Serial.write((byte)0x00);                  // Send byte to Remote Arduino
   RS485Serial.write((byte)0x01);         // Send byte to Remote Arduino
   RS485Serial.write((byte)0x85);                   // Send byte to Remote Arduino
   RS485Serial.write((byte)0xDB);                   // Send byte to Remote Arduino 
   digitalWrite(Pin13LED, LOW);  // Show activity    
   delay(10);
   digitalWrite(SSerialTxControl, RS485Receive);  // Disable RS485 Transmit
  }
}

My serial monitor displays strange datas as:

SerialRemote
 
E  
C4 B6 E  
C4 B6 E  
C4 B6 E  
C4 B6 1D  
C4 B6 7 C4 B6  
7 C4 B6 E  
C4 B6 E  
C4 B6 1D  
C4 B6 E  
C4 B6 1D  
C4 B6 1D  
C4 B6 E  
C4 B6 1D  
C4 B6 1D  
C4 B6 E

How can i read the good data format ?

pH_controller.pdf (106 KB)

Does this make any difference, from the Modbus RTU documentation:

The difference between MODBUS RTU and MODBUS/ASCII
There are two basic transmission modes found in serial MODBUS connections, ASCII and RTU. These transmission modes determine the way in which the MODBUS messages are coded. In ASCII format, the messages are readable, whereas in RTU the messages are in binary coding and cannot be read while monitoring. The trade-off is that the RTU messages are a smaller size, which allows for more data exchange in the same time span. One should be aware that all nodes within one MODBUS network must be of the same transmission mode, meaning MODBUS ASCII cannot communicate with MODBUS RTU and vice versa.

You are not using the ASCII version, so, as stated, they are not directly readable.
Paul

Not directly readable? What do you mean ?
Is it impossible to get response from the controller with an arduino? I think we can.
How can I write my sketch to send a request and get controller return ?

TEDDOL:
Not directly readable? What do you mean ?
Is it impossible to get response from the controller with an arduino? I think we can.
How can I write my sketch to send a request and get controller return ?

It means the data is binary, not ASCII characters. You must convert them to something readable on your monitor before they will make sense. Or write the binary equivalent out by hand for each character you display.

Paul

OK Paul, but before, i would like to know if my sketch is right ?!
Is it the good way to send request to controller according the manufacturer datasheet ?:

RS485Serial.write((byte)0x00);         
   RS485Serial.write((byte)0x03);                 
   RS485Serial.write((byte)0x00);                 
   RS485Serial.write((byte)0x00);                   
   RS485Serial.write((byte)0x00);                 
   RS485Serial.write((byte)0x01);         
   RS485Serial.write((byte)0x85);                
   RS485Serial.write((byte)0xDB);

TEDDOL:
OK Paul, but before, i would like to know if my sketch is right ?!
Is it the good way to send request to controller according the manufacturer datasheet ?:

RS485Serial.write((byte)0x00);         

RS485Serial.write((byte)0x03);               
  RS485Serial.write((byte)0x00);               
  RS485Serial.write((byte)0x00);                 
  RS485Serial.write((byte)0x00);               
  RS485Serial.write((byte)0x01);       
  RS485Serial.write((byte)0x85);               
  RS485Serial.write((byte)0xDB);

Yes, that seems correct and the message it right out of the document. And notice, you are sending the hex equivalent of binary, not ASCII characters. And you will be getting the same back from the device.

Paul

Paul_KD7HB:
RS485 is an electrical protocol, so has nothing to do with software. Look at the serial data examples and tutorials. They will get you going.

Paul

Other than software has to control the direction of the bus explicitly, and know the possible baud rate(s),
so it is not fully transparent to code.

Here we have a serial packet to send, then a response to listen for, and somewhere the sensor baud
rate and timing (for packet response) will be documented - and hopefully the structure of both
sent and returned packet is also documented.

Find in attached file the communication datasheet. That's all i have from the manufacturer.
Any solution ?

pH_controller.pdf (106 KB)

TEDDOL:
Find in attached file the communication datasheet. That's all i have from the manufacturer.
Any solution ?

Well, have you studied the MODBUS/RTU protocol?

Paul

No, that's why many people like me use arduino and librairies...

Hi everybody,

I found the solution:

#include <SoftwareSerial.h>
/*-----( Declare Constants and Pin Numbers )-----*/
#define SSerialRX        10  //Serial Receive pin
#define SSerialTX        11  //Serial Transmit pin

#define SSerialTxControl 3   //RS485 Direction control

#define RS485Transmit    HIGH
#define RS485Receive     LOW

#define Pin13LED         13

/*-----( Declare objects )-----*/
SoftwareSerial RS485Serial(SSerialRX, SSerialTX); // RX, TX

/*-----( Declare Variables )-----*/
int byteReceived;
String ph_hexa, ph_hexa_hight_byte, ph_hexa_low_byte, ph_hexa_value; 
byte request [] = {
    0x00, 
    0x03,
    0x00,
    0x00,  
    0x00,   
    0x01,   
    0x85,    
    0xDB    
};

void setup()   /****** SETUP: RUNS ONCE ******/
{
  // Start the built-in serial port, probably to Serial Monitor
  Serial.begin(9600);
  Serial.println("YourDuino.com SoftwareSerial remote loop example");
  Serial.println("Use Serial Monitor, type in upper window, ENTER");
  
  pinMode(Pin13LED, OUTPUT);   
  pinMode(SSerialTxControl, OUTPUT);    
  
  digitalWrite(SSerialTxControl, RS485Receive);  // Init Transceiver   
  
  // Start the software serial port, to another device
  RS485Serial.begin(19200);   // set the data rate 

}//--(end setup )---


void loop()   /****** LOOP: RUNS CONSTANTLY ******/
{
   if (RS485Serial.available())  //Look for data from other Arduino
   {
    Serial.print("Response: ");
    for (int i = 0; i <= 6; i++) {
        byteReceived = RS485Serial.read();    // Read received byte
      
        ph_hexa = String(byteReceived, HEX);
          
          // index 3 = high byte
          if(i == 3)  
          {
            ph_hexa_hight_byte =   ph_hexa;  
          }

          // index 4 = low byte
          if(i == 4)  
          {
            ph_hexa_low_byte =   ph_hexa;  

            ph_hexa_value = ph_hexa_hight_byte + ph_hexa_low_byte;

            Serial.print(ph_hexa_value);// Show on Serial Monitor  
          }
        Serial.print(" ");                       
      }
      /*
      Serial.println(ph_hexa);
      ph_hexa = "";
      */
      Serial.println();
   }
   else
   {
      Serial.println();
      Serial.print("Request: ");
      digitalWrite(SSerialTxControl, RS485Transmit);  // Init Transceiver
      for (int i = 0; i < 8; i++) {
        Serial.print(request[i], HEX);          
        RS485Serial.write((byte)request[i]);                
      }
      Serial.println();
      digitalWrite(SSerialTxControl, RS485Receive);  // Init Transmit       
   }
   delay(1000);

}

My serial monitor shows:

YourDuino.com SoftwareSerial remote loop example
Use Serial Monitor, type in upper window, ENTER

Request: 03000185DB
Response:     179   

Request: 03000185DB
Response:     179   

Request: 03000185DB
Response:     179   

Request: 03000185DB
Response:     179   

Request: 03000185DB
Response:     179

179 (HEX) = 377(DEC) => 3.77pH Yes!!!! My controller displays 3.77pH. I tested it with different pH value of the water and it matches with the hexa value.

But i'm not satisfied of my code. I want to transform the 179(HEX) value in 3.77 float value for others math calculations.
How can do that ?

Divide where ever the 177 is stored by 100.0 and print the result.

Paul

179 value is a String in my code corresponding to 0x0179. First i want to convert it in int and after, divide it by 100.
What do you think ? Maybe the code is not appropriated for this...

Thanks to GitHub - benrugg/Arduino-Hex-Decimal-Conversion: Utility functions for converting values between hex strings and decimal numbers on Arduino. (Helpful for color conversion).

Transform an hexa string to dec value

unsigned int hexToDec(String hexString) {
  
  unsigned int decValue = 0;
  int nextInt;
  
  for (int i = 0; i < hexString.length(); i++) {
    
    nextInt = int(hexString.charAt(i));
    if (nextInt >= 48 && nextInt <= 57) nextInt = map(nextInt, 48, 57, 0, 9);
    if (nextInt >= 65 && nextInt <= 70) nextInt = map(nextInt, 65, 70, 10, 15);
    if (nextInt >= 97 && nextInt <= 102) nextInt = map(nextInt, 97, 102, 10, 15);
    nextInt = constrain(nextInt, 0, 15);
    
    decValue = (decValue * 16) + nextInt;
  }
  
  return decValue;
}

my response return empty. do you have nay idea?

23:17:17.663 -> YourDuino.com SoftwareSerial remote loop example
23:17:17.709 -> Use Serial Monitor, type in upper window, ENTER
23:17:17.756 -> 
23:17:17.756 -> Request: 131770016580
23:17:18.691 -> 
23:17:18.691 -> Request: 131770016580
23:17:19.719 -> 
23:17:19.719 -> Request: 131770016580
1 Like