Arduino reading Modbus data/RS485.

Hi,

I have been trying for a while now to get this code working but now really stuck… any helping hand much appreciated :slight_smile:

The sensor is sending information automatically every 3 seconds via Modbus - I am trying to use the shield below to recieve the data and print it out via an LCD. At the moment I am getting 0.00 values for all three outputs: CO2, temp & RH. I have had help with the code and it is quite well documented by I’m no programmer and there apears to be a risk of damaging the sensor if I do something wrong… so far it is still working :slight_smile:

The shield is: http://www.dfrobot.com/index.php?route=product/product&path=35_39&product_id=54

The rest of the code is on the next post as it wouldn’t fit here.

//-----------------------$2010.06.10$-2010-06-10-15:44 code add-----------------------------------------
// i have initialize the timer2 interrupt for you ,but i suppose that your CLKcpu is 8M.
//so you should recalculate the timer2 register value and the value of TIMING_CYCLE_4S.
//you must guarantee that the timer2 interrupt cycle is at least 4 second.
//you can find the code of initializing the timer2 by finding the string  "$2010.06.10$-2010-06-10-15:44"

// ------------------------$2010.06.10$ is the flag of changed code.--------------------------------------
//At First,read the following context,please.
// Pay attention to the variable "Time_Is_4Sec_After_Last_Read_From_Device"  in the process "loop".
//the process "loop" will not run to read the device ,until "Time_Is_4Sec_After_Last_Read_From_Device" is set to 1.
// !!!!NOTE!!!!: the interval time between twice reading from device must be at least 4 seconds
//So "Time_Is_4Sec_After_Last_Read_From_Device" can not be always setting to 1;

//you should do:
//1.setup a timer,use the timer to generate a 4 second timing cycle.
//2.only when 4 second timing cycle is reached ,you set the "Time_Is_4Sec_After_Last_Read_From_Device" to 1.

//in the process "loop",mcu will find the "Time_Is_4Sec_After_Last_Read_From_Device"==1,
//then mcu set "Time_Is_4Sec_After_Last_Read_From_Device" to  0, and run to read the device.
//the next 4 second timing cycle will lead to the next read from device.
//---------------------------------------------------------------------------------------------------------------------

//-------------------------$2010.04.27$   is a flag of changed code. you can search the flag to find the changed code. -----------------------
//-------------------------------------------------------------------------------------------------------------------------------------------------------
/*
  Basic.pde - example using ModbusMaster library
  This file is part of ModbusMaster.
  ModbusMaster is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.
  
  ModbusMaster is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.
  
  You should have received a copy of the GNU General Public License
  along with ModbusMaster.  If not, see <http://www.gnu.org/licenses/>.
  
  Written by Doc Walker (Rx)
  Copyright ?2009, 2010 Doc Walker <dfwmountaineers at gmail dot com>
  $Id: Basic.pde 39 2010-02-10 02:12:21Z dfwmountaineers $
  
*/

Thanks.

#include <LiquidCrystal.h>
LiquidCrystal lcd(12, 11, 5, 4, 3, 2);

#include <ModbusMaster.h>

// instantiate ModbusMaster object as slave ID 1
// defaults to serial port 0 since no port was specified

//  $2010.04.27$ change 2=>1  because device default address is 1; you can change it to your slave address.
ModbusMaster node(1);   

//  $2010.04.27$  code add {{{{{
union uint16_float_convert{
  uint16_t uint16_data[2];
  float    float_data;
}uint_to_float;

unsigned char Time_Is_4Sec_After_Last_Read_From_Device;
float CO2_Measurement;
float Temperature_Measurement; 
float Humiture_Measurement;
//  $2010.04.27$  code add }}}}}}}

//-----------------------$2010.06.10$ code add---------------------------
uint32_t counter_at_least_4s;
//---------------------------------------------------------------------------

void setup()
{
 // initialize serial communications at 9600 bps:
  Serial.begin(9600);

  //  $2010.04.27$  code add {{{{{
  Time_Is_4Sec_After_Last_Read_From_Device=0;  
  CO2_Measurement=0;
  Temperature_Measurement=0; 
  Humiture_Measurement=0;
  //  $2010.04.27$  code add }}}}}}}
  
  //-----------------------$2010.06.10$ code add---------------------------
  counter_at_least_4s=0;
  //---------------------------------------------------------------------------
  
   // initialize Modbus communication baud rate
  node.begin(19200);
  
  //-------------------------$2010.06.10$ code add{{{{{{-----------------------------------
  //TODO:initialize a timer at here ,use the timer to generate a 4 second timing cycle in the timer's interrupt handle function.
  //initialize timer's every control and mode register at here
  //....... 

  //$2010.06.10$-2010-06-10-15:44 code add
  //I suppose that your CLKcpu=8MHZ,you should recalculate the timer2 register value according to your real CLKcpu.
  OCR2A=70;                        //set timer2 interrupt cycle to 70*4US=280US 
  TCCR2A=0x0b;                     //Timer mode 8M  Clock Division Factor=32��CS21=CS20=1�� CTC       
  TIMSK2=2;                       //Enable timer2 CTC interrupt

  sei();
  //-------------------------$2010.06.10$ code add}}}}}} ------------------------------------ 
  
}

//-------------------------------$2010.06.10$ code add{{{{{{-------------------------------------
 //use the timer to generate a 4 second timing cycle in the timer's interrupt handle function.
 //i have no idea on which timer you may use. so i suppose timer2 is used.
 //i don't know how long each timer interrupt you will set. so you should calculate the value of TIMING_CYCLE_4S by yourself.
 //the method of calculating the value of TIMING_CYCLE_4S : ((4Second)/(each timer interrupt cycle)+1)
#define TIMING_CYCLE_4S    (28000)     //$2010.06.10$-2010-06-10-15:44 code add

ISR(TIMER2_COMPA_vect) {
  //at here, you may count to generate  at least 4 second timing cycle.
  counter_at_least_4s++;
  if(counter_at_least_4s>=TIMING_CYCLE_4S)
  {
      counter_at_least_4s=0;
      Time_Is_4Sec_After_Last_Read_From_Device=1;
  }
};
//---------------------------------$2010.06.10$ code add}}}}}}  ------------------------------------------

void loop()
{
  static uint32_t i;
  uint8_t j, result;
  uint16_t data[6];
  
  i++;
  /*   //$2010.04.27$  deleted. i suppose you will only read the device data.
  // set word 0 of TX buffer to least-significant word of counter (bits 15..0)
  node.setTransmitBuffer(0, lowWord(i));
  
  // set word 1 of TX buffer to most-significant word of counter (bits 31..16)
  node.setTransmitBuffer(1, highWord(i));
  
  // slave: write TX buffer to (2) 16-bit registers starting at register 0
  result = node.writeMultipleRegisters(0, 2);
  
  // slave: read (6) 16-bit registers starting at register 2 to RX buffer
  result = node.readHoldingRegisters(2, 6);
  */
  
 // $2010.04.27$  added. for read the CO2 , Temperature and Humiture measurement data from device.
 // !!!!NOTE!!!!: the interval time between twice reading from device must be at least 4 seconds, 
 //               Too little interval time between twice reading will probably do damage to device!!! 
 //Time_Is_4Sec_After_Last_Read_From_Device  is a flag ,mcu can set it to 1 in a timer when 4 seconds has reached.  
 if(1==Time_Is_4Sec_After_Last_Read_From_Device)    
 {   
     Time_Is_4Sec_After_Last_Read_From_Device=0;  //only after 4 seconds , mcu can again run to here and read device .
     //slave: read (6) 16-bit registers starting at register 0 to RX buffer
     result=node.readInputRegisters(0,6);
     if (result == node.ku8MBSuccess)
     {
        for (j = 0; j < 6; j++)
        {
            data[j] = node.getResponseBuffer(j);
        }
        //for example: compiler is gcc ,Big-endian
        //the device sends datas :0x41 0x7C 0x33 0x30 ....., the 0x41 is sent at first.
        //the ModbusMaster Class will return datas to variable data[], then data[0]=0x417C ,data[1]=0x3330  ,they are CO2 measurement=24.899994
        //in union uint16_float_convert , gcc  sets uint16_data[1] at higher address and uint16_data[0] at lower address .
        //float value 24.899994 ,in memory, gcc saves it as 0x417C 0x3330 ,0x417C is at higher address , 0x3330 is at lower address.  
        //so the code will be like this:
        //uint_to_float.uint16_data[0]=data[1];
        //uint_to_float.uint16_data[1]=data[0];
        
        //data[0] data[1]   :CO2 measurement, float type.
        //data[2] data[3]   :Temperature measurement, float type.
        //data[4] data[5]   :Humiture measurement, float type.   
        
        //decode the readed data,and get CO2 measurement
        uint_to_float.uint16_data[0]=data[1];
        uint_to_float.uint16_data[1]=data[0];
        //The readed CO2 measurement from device is put into CO2_Measurement . 
        CO2_Measurement=uint_to_float.float_data;
        
        //decode the readed data,and get Temperature measurement
        uint_to_float.uint16_data[0]=data[3];
        uint_to_float.uint16_data[1]=data[2];
        //The readed Temperature measurement from device is put into Temperature_Measurement . 
        Temperature_Measurement=uint_to_float.float_data;
        
        //decode the readed data,and get Humiture measurement
        uint_to_float.uint16_data[0]=data[5];
        uint_to_float.uint16_data[1]=data[4];
        //The readed Humiture measurement from device is put into Humiture_Measurement . 
        Humiture_Measurement=uint_to_float.float_data;
        
        //now you can use CO2_Measurement, Temperature_Measurement and Humiture_Measurement to deal with the other task.
        
     
     lcd.setCursor(0, 0);
     lcd.print("Temp     : ");
     lcd.print(Temperature_Measurement);
     lcd.setCursor(0, 1);
     lcd.print("Humidity : ");
     lcd.print(Humiture_Measurement);
     lcd.setCursor(0, 0); 

  }
}