reverse engineered

Arduino Modbus RTU ADC

Modbus is an industry standard communications protocol for electronic devices. Given that most industrial sensors and meters provide their output by varying the voltage of the output pin between 0-10 Volts, Modbus compatible analog to digital converters are popular devices. Modbus is surprisingly resilient to interference. This is because it is based around the RS-485 standard. RS-485 is essentially a multi-point, balanced, version of RS-232 (which people might recognise as the serial port protocol), which is point-to-point, and unbalanced.

Given that the cheapest industrial unit I found retails for €75 ex.VAT, it is interesting to know that an Arduino UNO + MAX485 can do the same thing for €25 inc.VAT, ⅓ of the price.

The Arduino UNO has 5 analog inputs, which means it is capable of sampling 5 analog values and storing them storing them in 5 sequential registers. The Modbus master device can then query Arduino slave, and retrieve these values from the registers. If you find that you don’t have enough device inputs with 5 analog inputs, consider using an Arduino Mega, which has 16 analog inputs.

Most sensors output a range between 0-10 V, while the Arduino accepts a maximum of 5 V. By passing the sensor output through a pair of resistors in parallel, we will reduce the voltage range from 0-10 V to 0-5 V, which is suitable for the Arduino.

The Arduino only has full duplex serial lines, so a MAX485 IC chip must be used to convert them into the half-duplex differential lines for RS-485. Due to the the fact that the MAX485 is half duplex, Pin2 is used as an output enable pin.

The code is based on this library by jpmzometa which must be downloaded and placed in the Arduino/Libraries directory.

#include <ModbusSlave.h>

/* create new mbs instance */
ModbusSlave mbs;

/* slave registers */
enum {        
        MB_A1,        /* analogIn 1 */
        MB_A2,        /* analogIn 2 */
        MB_A3,        /* analogIn 3 */
        MB_A4,        /* analogIn 4 */
        MB_A5,        /* analogIn 5 */
        MB_REGS       /* dummy register. using 0 offset to keep size of array */

int regs[MB_REGS];
unsigned long wdog = 0;         /* watchdog */

void setup(){
  /* Modbus slave configuration parameters */
  const unsigned char SLAVE = 10;      /* slaveId */
  const long BAUD = 19200;             /* baud rate */
  const char PARITY = 'n';             /* n=none; e=even; o=odd */
  const char TXENPIN = 2;              /* output driver enable pin */
  /* configure msb with config settings */


void loop()
/* pass current register values to mbs */
  if(mbs.update(regs, MB_REGS))
  wdog = millis();

  /* ADC reads are slow. sample every 5 seconds */  
  if ((millis() - wdog) > 5000)  {      
    regs[MB_A1] = analogRead(A1); /* read input A1 */
    regs[MB_A2] = analogRead(A2); /* read input A2 */
    regs[MB_A3] = analogRead(A3); /* read input A3 */
    regs[MB_A4] = analogRead(A4); /* read input A4 */
    regs[MB_A5] = analogRead(A5); /* read input A5 */

comments powered by Disqus