RS485 MODBUS ARDUINO: Print multiple response data frame from soil sensor

Thanks for your answer. I have implemented the code you provided me and the result:


I don't understand... What could be wrong?

Thank for reply @markd833. The circuit is like the image and without the LCD and instead arduino nano I working with Arduino UNO

Initialising the state the output are those:


I don't know... what is missing?

I see the response frame apparently starting after two other characters on your first line there. Perhaps it would be worth emptying the serial input buffer before you send the request frame.

Give this a try (compiles, lightly tested with Uno emulating your sensor...)

Uses CRC checks to verify packets.

#include <Wire.h>
#include <SoftwareSerial.h>

#define RE 8
#define DE 7

#define RX_BUFF_SIZE    30

enum RS485States
{
    ST_IDLE = 0,
    ST_RXDATA,
    ST_CHECKDATA
    
};

const uint16_t crc16_tab[] PROGMEM = 
{
    0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
    0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
    0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
    0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
    0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
    0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
    0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
    0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
    0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
    0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
    0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
    0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
    0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
    0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
    0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
    0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
    0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
    0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
    0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
    0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
    0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
    0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
    0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
    0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
    0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
    0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
    0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
    0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
    0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
    0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
    0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
    0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
};

const byte requestframe[] = { 0x01, 0x03, 0x00, 0x00, 0x00, 0x07 };   //CRC is added in TX logic

uint8_t values[RX_BUFF_SIZE];

SoftwareSerial mod(2,3); // RX, TX ( Creates a new SoftwareSerial object ) (for Uno)
//SoftwareSerial mod(10,3); // RX, TX ( Creates a new SoftwareSerial object ) (for Mega2560)

void setup() 
{
    Serial.begin(4800);
    mod.begin(4800);
    pinMode(RE, OUTPUT);
    pinMode(DE, OUTPUT);

}//setup

void loop() 
{
    ModSM();
    
}//loop


void ModSM( void )
{
    static uint32_t
        tFrame = 0ul,
        tSM = 0ul;        
    static uint8_t
        dataLen,
        rxIdx,
        state = ST_IDLE;
    uint16_t
        crc;
    uint32_t
        tNow = millis();
    uint8_t
        ch;

    switch( state )
    {
        case    ST_IDLE:           
            if( (tNow - tSM) >= 3000ul )
            {                
                //flush any stray characters from RX buffer
                while( mod.available() )
                    mod.read();
                    
                tSM = tNow;
                digitalWrite(DE, HIGH);
                digitalWrite(RE, HIGH);

                delayMicroseconds(100);
                crc = CRC16_Modbus( requestframe, sizeof( requestframe ) );                
                mod.write( requestframe, sizeof(requestframe) );
                mod.write( crc & 0xff );
                mod.write( (crc >> 8) & 0xff );                
                delayMicroseconds(100);
                
                digitalWrite(DE, LOW);
                digitalWrite(RE, LOW);

                rxIdx = 0;  
                dataLen = 0;              
                state = ST_RXDATA;
                    
            }//if
            
        break;

        case    ST_RXDATA:            
            if( mod.available() )            
            {   
                ch = mod.read();                             
                switch( rxIdx )
                {
                    case    0:  //address code
                    case    1:  //function code
                        //do nothing but store these
                        values[rxIdx++] = ch;
                    break;

                    case    2:
                        //number of data + CRC bytes to follow
                        dataLen = ch;
                        values[rxIdx++] = ch;
                    break;

                    default:
                        //RX data and CRC
                        values[rxIdx++] = ch;
                        if( rxIdx == RX_BUFF_SIZE )
                            rxIdx--;
                            
                        dataLen--;
                        if( dataLen == 0 )
                            state = ST_CHECKDATA;
                        
                    break;
                                            
                }//switch rx index

            }//if available
            
        break;

        case    ST_CHECKDATA:
            //data done; check the CRC                
            crc = CRC16_Modbus( values, values[2]+1 );    //-2 for CRC, +2 for header, +1 for value[2] itself
            if( ((uint8_t)(crc & 0x0ff) == values[rxIdx-2]) && ((uint8_t)((crc>>8) & 0x0ff) == values[rxIdx-1]) )
            {
                //msg is good; print results
                PrintData();                                
                    
            }//if

            state = ST_IDLE;
            
        break;
        

    }//switch
    
}//ModSM

uint16_t CRC16_Modbus( uint8_t *pData, uint8_t Len )
{
    //CRC16 calculation
    uint16_t crc = 0xFFFF;

    while( Len-- )
        crc = (crc >> 8) ^ (pgm_read_word(&crc16_tab[(crc ^ *pData++)&0xFF]) );   

    return crc;
    
}//CRC16_Modbus

void PrintData( void )
{
    int16_t
        val;
                          
    //0 index 3     moisture content
    val = (values[3] << 8) + values[4];
    Serial.print( "Humidity....: " ); Serial.print( (float)val/10.0, 1 ); Serial.println("%");
    
    //1 index 5     temperature
    val = (values[5] << 8) + values[6];
    Serial.print( "Temperature.: " ); Serial.print( (float)val/10.0, 1 ); Serial.println("oC");

    //2 index 7     conductivity
    val = (values[7] << 8) + values[8];
    Serial.print( "Conductivity: " ); Serial.print( val ); Serial.println("us/cm");

    //3 index 9     PH
    val = (values[9] << 8) + values[10];
    Serial.print( "pH..........: " ); Serial.print( (float)val/10.0 ); Serial.println("");

    //4 index 11    nitrogen content
    val = (values[11] << 8) + values[12];
    Serial.print( "Nitrogen....: " ); Serial.print( val ); Serial.println("units?");
    
    //5 index 13    phosphorus content
    val = (values[13] << 8) + values[14];
    Serial.print( "Phosphorus..: " ); Serial.print( val ); Serial.println("units?");
    
    //6 index 15    potassium content 
    val = (values[15] << 8) + values[16];
    Serial.print( "Potassium...: " ); Serial.print( val ); Serial.println("units?");
    
}//PrintData

Dear @Blackfin. I appreciate your time and response. It does not print within seconds of execution.


I was worried about that I left it for a while I went back and saw that data:

                                               Parameters

Temperature:

  • Range: -40~80℃
  • Accuracy: ±0.5℃ (25℃)
  • Resolution: 0.1℃
    Moisture
  • Range: 0-100%
  • Accuracy: 0-50%≤ 2%, 50-100%≤ 3%
  • Resolution: 0.1%
    EC
  • Range: 0-20000us/cm
  • Accuracy: 0-10000us/cm ≤±3%FS, 10000-20000us/cm ≤±5%FS
  • Resolution: 1us/cm
    NPK
  • Range: 1-1999 mg/kg(mg/L)
  • Accuracy: ±2%FS
  • Resolution: 1 mg/kg(mg/L)
    PH
  • Range: 3~9PH
  • Accuracy: ±0.3PH
  • Resolution: 0.1

UNITS VALUES:
HUMIDITY: % percentage
TEMPERATURE: ºC Celsius
EC: us/cm
PH: As far as I know pH-value is a dimensionless quantity and therefore has no unit.
Nitrogen: mg/kg
Phosphorus: mg/kg
Potassium: mg/kg

And I need your help. I'll leave this forum when the output values are correct.

Can you add the following lines to print the received data in the ST_CHECKDATA state:

        case    ST_CHECKDATA:
            //data done; check the CRC                
            for( crc=0; crc<rxIdx; crc++ )            //<--- add
                Serial.print( values[crc], HEX );  //<--- add
            Serial.println(); // <-- add

            
            crc = CRC16_Modbus( values, values[2]+1 );    //-2 for CRC, +2 for header, +1 for value[2] itself
            if( ((uint8_t)(crc & 0x0ff) == values[rxIdx-2]) && ((uint8_t)((crc>>8) & 0x0ff) == values[rxIdx-1]) )
            {
                //msg is good; print results
                PrintData();                                
                    
            }//if
            else //<--- add from here...
            {
                Serial.println( "Bad CRC" );
            }//else //<--- to here

The CRC must be okay if it's printing the data but the numbers don't make sense. It should update once every 3 seconds; if it's only updating once in a while the CRC may be bad.

Can you also copy the serial monitor contents and paste it within code tags instead of taking screen pics?

Now the output prints:

Bad CRC
0013

every second

As a follow up to my earlier question regarding the correct wiring of the A & B wires, I believe that you have the sensor wired correctly. In your screenshot in #21, you are receiving a valid message - but only once, and it's out of sync with the query.


The highlighted sequence of bytes is a valid modbus response to the command you are sending, including the correct checksum in the last 2 bytes. There are various other occurrences of the beginning of a correct response with the "01 03 0E" sequence, but they are all out of sync with the command message.

Are those few bytes at the top left of your screenshot the very first bytes printed out when your sketch runs?

Not wanting to make things worse for you, but have you tried another software serial implementation? I've used AltSoftSerial in the past with good results. You will have to adjust your wiring if you want to try this library as it has to use pin 9 for Tx and pin 8 for Rx on an UNO.

Every second?

That was the first line output that seems ok. I'll try with AltSoftSerial...

Yes. Every second...

Interesting. When I run it on my Uno with my 2650 emulating your sensor I see:

138292FF9B03836A3Humidity....: 65.8%
Temperature.: -10.1oC
Conductivity: 56us/cm
pH..........: 1398.70
Nitrogen....: 0units?
Phosphorus..: 0units?
Potassium...: 0units?
138292FF9B03836A3Humidity....: 65.8%
Temperature.: -10.1oC
Conductivity: 56us/cm
pH..........: 1398.70
Nitrogen....: 0units?
Phosphorus..: 0units?
Potassium...: 0units?
138292FF9B03836A3Humidity....: 65.8%
Temperature.: -10.1oC
Conductivity: 56us/cm
.
.
.

The 2560 sends this canned message:

buffTX[] = { 0x01, 0x03, 0x08, 0x02, 0x92, 0xff, 0x9b, 0x00, 0x38, 0x36, 0xa3 };

This is the message from the datasheet (page 16) though with a different CRC (0xa336 vs 0xb657; I couldn't get the CRC16 generator nor an online resource to produce 0xb657 for that message...)

Is there nothing more being printed to your serial monitor than the two lines you showed (i.e.

Bad CRC
0013

?

No. Only in new line every second

Bad CRC
0013 ...

OK. Please try changing the delayMicroseconds() after the last CRC byte is sent to this:

.
.
.               
                crc = CRC16_Modbus( requestframe, sizeof( requestframe ) );                
                mod.write( requestframe, sizeof(requestframe) );                
                mod.write( crc & 0xff );                
                mod.write( (crc >> 8) & 0xff );                
                delay(20); //<--- change to 20mS
.
.
.

Now prints every ms:

Bad CRC
00000C0000000C00C00000000000000C0

Are you still using SoftwareSerial or did you change to AltSoftSerial? Are the serial connections correct?

I tried with AltSoftSerial library:
// RS485 module wired up as:
// RS485 DI signal to pin 9
// RS485 RO signal to pin 8
// RS485 RE signal to pin 7
// RS485 DE signal to pin 6
Output:

0 0 C0 C0 0 C0 C0 C0 C0 0 C0 C0 C0 C0 0 C0 C0 C0  | hum: -1619.20 %, temp: 19.20 º, ec: -1619.20, ph: -1638.40, n: -1619.20, p: -1619.20, k: 19.20
C0 0 C0 C0 C0 C0 0 C0 C0 C0 C0 0 C0 C0 C0 C0 0 C0  | hum: -1619.20 %, temp: -1619.20 º, ec: 19.20, ph: -1619.20, n: -1638.40, p: -1619.20, k: -1619.20
C0 C0 C0 0 C0 C0 C0 C0 0 C0 E0 70 B6 6B 0 4B 83 37  | hum: -1638.40 %, temp: -1619.20 º, ec: -1619.20, ph: 19.20, n: -808.00, p: -1883.70, k: 7.50
4B 6B 4B 0 4B 3 7B 94 6B 6B 3 6B B 6B 4B 6B 6B 3  | hum: 1920.00 %, temp: 1920.30 º, ec: 3163.60, ph: 2749.90, n: 87.50, p: 292.30, k: 1930.70
4B B 8B B4 4B 4B B 4B B 4B B4 4B 4B 3 4B B CB 4B  | hum: -2977.20 %, temp: 1927.50 º, ec: 289.10, ph: 289.10, n: -1938.10, p: 1920.30, k: 1921.10
4B 4B 3 4B B 3B 4B 0 7C FF F0 40 0 4B B B 4B 9B  | hum: 84.30 %, temp: 287.50 º, ec: 1920.00, ph: 3199.90, n: -403.20, p: 7.50, k: 282.70

For the code that I gave you can you revert to SoftwareSerial and pins 2 and 3?

I'll keep checking in on your progress, but for now i'll back off as we're providing you with two alternate solutions which can only lead to confusion.

I tried with AltSoftSerial library:
// RS485 module wired up as:
// RS485 DI signal to pin 9
// RS485 RO signal to pin 8
// RS485 RE signal to pin 7
// RS485 DE signal to pin 6
With your code the provide me prints every 10 ms:

Bad CRC
9280FF8306B6BB6B6B6B696B6B16B6B6996696934BB6BB44B4B0