Difference between revisions of "RelayDuino-Modbus"

From i3Detroit
Jump to: navigation, search
(Created page with 'The [http://www.sparkfun.com/products/9526 RelayDuino] is a swell little device made by some [http://www.oceancontrols.com.au/home.php dudes in Australia]. It's based on an Ardui...')
 
(Register Map)
Line 23: Line 23:
  
 
{|
 
{|
| 0 || Relay 1 || R/W || Any ''True'' value activates the relay
+
! Register !! Name !! read/write !! Description
 
|-
 
|-
| 1 || Relay 2
+
 +
| 0 || Relay 0 || rowspan="8"| R/W || rowspan="8"|  Any ''true'' (>0) value activates the relay.
 
|-
 
|-
| 2 || Relay 3
+
| 1 || Relay 1
 
|-
 
|-
| 3 || Relay 4
+
| 2 || Relay 2
 
|-
 
|-
| 4 || Relay 5
+
| 3 || Relay 3
 
|-
 
|-
| 5 || Relay 6
+
| 4 || Relay 4
 
|-
 
|-
| 6 || Relay 7
+
| 5 || Relay 5
 +
|-
 +
6 || Relay 6
 +
|-
 +
7 || Relay 7
 +
|-
 +
 
 +
|  8 || Opto-input 0 || rowspan="4" | R || rowspan="4" |
 +
|-
 +
|  9 || Opto-input 1
 +
|-
 +
| 10 || Opto-input 2
 +
|-
 +
| 11 || Opto-input 3
 +
|-
 +
 
 +
| 12 - 15 || reserved || n/a ||
 +
|-
 +
 
 +
| 16 || Analog-input 0 || rowspan="3" | R || rowspan="3" |
 +
|-
 +
| 17 || Analog-input 1
 +
|-
 +
| 18 || Analog-input 2
 +
|-
 +
 
 +
| 19 - 23 || reserved || n/a ||
 +
|-
 +
 
 +
| 24 || soft reset || W || Writing the magic number 0xDEAD to this location causes a reset. Baudrate and Slave ID will be re-read from EEPROM
 +
|-
 +
 
 +
| 25 || software version || R || Version of this Arduino application
 +
|-
 +
 
 +
| 26 || free running counter || R || the lower two bytes returned by millis()
 +
|-
 +
 
 +
| 27 - 31 || reserved || n/a ||
 +
|-
 +
 
 +
| 32 || Slave ID
 +
| rowspan="4" | R/W
 +
The lower byte of each of these registers is kept in EEPROM.
 +
| The Modbus Slave ID is read from this location at boot time.
 +
|-
 +
 
 +
| 33 || baud rate || The Modbus baud rate as defined in the table below.
 +
|-
 +
 
 +
| 34 || watchdog  || Value for the watchdog timer. The box will reset after N * 256ms unless it receives a valid read or write.
 +
N==0 disables the watchdog.
 +
|-
 +
 
 +
| 35 - 63 || more EEPROM || Store your phone number in here, see if I care.
 +
|-
 +
 
 +
 
 +
|}
 +
 
 +
 
 +
==Baud Rate Table==
 +
{|
 +
| 0 || 1200
 +
|-
 +
 
 +
| 1 || 2400
 +
|-
 +
 
 +
| 2 || 4800
 +
|-
 +
 
 +
| 3 || 9600
 +
|-
 +
 
 +
| 4 || 14400
 +
|-
 +
 
 +
| 5 || 1200
 +
|-
 +
 
 +
| 6 || 19200
 +
|-
 +
 
 +
| 7 || 28800
 +
|-
 +
 
 +
| 8 || 38400
 +
|-
 +
 
 +
| 9 || 57600
 +
|-
 +
 
 +
| 10 || 115200
 
|-
 
|-
| 7 || Relay 8
 
 
|}
 
|}
  

Revision as of 18:41, 17 January 2011

The RelayDuino is a swell little device made by some dudes in Australia. It's based on an Arduino and it's got some nice relays, a few analog inputs and even some opto-isolated discrete inputs. It even has an RS485 port on it.

What more could you want?

I'll tell you what more I want, Sally! I wants Modbus!

Now they do make a Modbus version of this thing, but it's a few bucks more and I'll be damned if I'm giving them any more money for the exact same hardware. The Aussies are all criminals, every last one of them. You know it, I know it. Let's just try to put this ugliness behind us, ok?

Good.

Now, I wouldn't have gone to all this trouble if Mango, would support adding your own serial device with a custom protocol. Seems like a no-brainer, right? There's a jillion oddball serial devices out there and you'd think an open-source SCADA system would be easy to hack your own device into right? I mean, come on!

Wait, do you think these Mango guys are from Australia? It would be just like those crafty bastards.

Regardless, Mango + Modbus seemed like the easiest way to go.

I took the Arduino Modbus Slave library from here: http://sites.google.com/site/jpmzometa/arduino-mbrt/arduino-modbus-slave

Register Map

The modbus slave library is pretty minimal, it only supports register reads and writes. So everything is mapped to a regsiter.


Register Name read/write Description
0 Relay 0 R/W Any true (>0) value activates the relay.
1 Relay 1
2 Relay 2
3 Relay 3
4 Relay 4
5 Relay 5
6 Relay 6
7 Relay 7
8 Opto-input 0 R
9 Opto-input 1
10 Opto-input 2
11 Opto-input 3
12 - 15 reserved n/a
16 Analog-input 0 R
17 Analog-input 1
18 Analog-input 2
19 - 23 reserved n/a
24 soft reset W Writing the magic number 0xDEAD to this location causes a reset. Baudrate and Slave ID will be re-read from EEPROM
25 software version R Version of this Arduino application
26 free running counter R the lower two bytes returned by millis()
27 - 31 reserved n/a
32 Slave ID R/W

The lower byte of each of these registers is kept in EEPROM.

The Modbus Slave ID is read from this location at boot time.
33 baud rate The Modbus baud rate as defined in the table below.
34 watchdog Value for the watchdog timer. The box will reset after N * 256ms unless it receives a valid read or write.

N==0 disables the watchdog.

35 - 63 more EEPROM Store your phone number in here, see if I care.


Baud Rate Table

0 1200
1 2400
2 4800
3 9600
4 14400
5 1200
6 19200
7 28800
8 38400
9 57600
10 115200

Code

Version 0.1


#include <EEPROM.h>
#include <ModbusSlave.h>

typedef unsigned int uint;

#define SOFTWARE_VERSION 0x0001

#define SOFT_RESET_COMMAND 0xDEAD
void softReset(void);


/* First step MBS: create an instance */
ModbusSlave mbs;

#define ANIN1 6   // Analog 1 is connected to Arduino Analog In 6
#define ANIN2 7   // Analog 2 is connected to Arduino Analog In 7
#define ANIN3 0   // Analog 3 is connected to Arduino Analog In 0
int Analogs[3] = {ANIN1, ANIN2, ANIN3};

#define REL1 2  // Relay 1 is connected to Arduino Digital 2
#define REL2 3  // Relay 2 is connected to Arduino Digital 3 PWM
#define REL3 4  // Relay 3 is connected to Arduino Digital 4
#define REL4 5  // Relay 4 is connected to Arduino Digital 5 PWM
#define REL5 6  // Relay 5 is connected to Arduino Digital 6 PWM
#define REL6 7  // Relay 6 is connected to Arduino Digital 7
#define REL7 8  // Relay 7 is connected to Arduino Digital 8
#define REL8 9  // Relay 8 is connected to Arduino Digital 9 PWM
int Relays[8] = {REL1, REL2, REL3, REL4, REL5, REL6, REL7, REL8};
int PWMAble[4] = {2, 4, 5, 8};
#define OI1 15 // Opto-Isolated Input 1 is connected to Arduino Analog 1 which is Digital 15
#define OI2 16 // Opto-Isolated Input 2 is connected to Arduino Analog 2 which is Digital 16
#define OI3 17 // Opto-Isolated Input 3 is connected to Arduino Analog 3 which is Digital 17
#define OI4 18 // Opto-Isolated Input 4 is connected to Arduino Analog 4 which is Digital 18
int Optos[4] = {OI1, OI2, OI3, OI4};

#define TXENPIN 19 // RS-485 Transmit Enable is connected to Arduino Analog 5 which is Digital 19

long baudRateTable[10] = {1200,2400,4800,9600,14400,19200,28800,38400,57600,115200};

/* EEPROM map */
#define EE_SLAVEID     0
#define EE_BAUD        1
#define EE_WATCHDOG    2



/* slave registers */
union reg_struct_t  { 
                         int reg[64];
                         struct {
                           uint relay[8];      //  0 -  7
                           uint opto[4];       //  8 - 11
                           uint reserved0[4];
                           uint analog[3];     // 16 - 18
                           uint reserved1[5];
                           uint soft_reset;    // 24
                           uint software_vers; // 25
                           uint counter;       // 26
                           uint reserved2[5];
                           uint eeprom[32];    // 32 - 64
                         };
                    } regs;
#define REG_COUNT (sizeof(regs) / sizeof(int))
#define EEPROM_COUNT (sizeof(regs.eeprom) / sizeof(int))

unsigned long wdog = 0;         /* watchdog */
unsigned long wdog_limit = 0;   /* previous time*/

void setup() 
{
  int i;
  
  for (i=0;i<8;i++)
  {
    pinMode(Relays[i], OUTPUT);  // declare the relay pin as an output
  }  
  for (i=0;i<4;i++)
  {
    pinMode(Optos[i], INPUT);  // declare the relay pin as an output
  } 

  softReset();
}


void softReset() 
{
  int i;
  unsigned char ee_value;
  
  for (i=0;i<8;i++)
  {
    regs.relay[i] = 0;
  }  

  for(i=0; i<REG_COUNT; i++)
  {
    regs.reg[i] = 0;
  }
  
  for(i=0; i<EEPROM_COUNT; i++)
  {
     regs.eeprom[i] = EEPROM.read(i);
  }
       
    /* the Modbus slave configuration parameters */
    unsigned char slave_id = EEPROM.read(EE_SLAVEID);
    long baudrate = baudRateTable[EEPROM.read(EE_BAUD)];
    const char PARITY = 'n';

    /* Second step MBS: configure */
    mbs.configure(slave_id,baudrate,PARITY,TXENPIN);
    
    /* initialize the watchdog timer */
    wdog_limit = EEPROM.read(EE_WATCHDOG);
    wdog_limit <<= 8;
}

void loop()
{
  int i;
  unsigned char ee_value;

  for (i=0 ; i<4 ; i++)
  {
    if (digitalRead(Optos[i])==LOW)
    {
      regs.opto[i] = 1;
    }
    else
    {
      regs.opto[i] = 0;
    }
  }


  for (i=0 ; i<8 ; i++)
  {
    if (regs.relay[i])
    {
      digitalWrite(Relays[i],HIGH);
    }
    else
    {
      digitalWrite(Relays[i],LOW);
    }
  }
  
  for (i=0 ; i<3 ; i++)
  {
      regs.analog[i] = analogRead(Analogs[i]);
  }
  
  for(i=0; i<EEPROM_COUNT; i++)
  {
     ee_value = regs.eeprom[i] & 0xFF;
     if(EEPROM.read(i) != ee_value)
     {
       EEPROM.write(i, ee_value);
     }
  }
  
  if(regs.soft_reset == SOFT_RESET_COMMAND)
  {
    softReset();
  }

  if(wdog_limit != 0)
  {
    if(millis() - wdog > wdog_limit)
    {
       softReset(); 
    }
  }
  

  regs.software_vers = SOFTWARE_VERSION;
  regs.counter = millis();

  /* lastly, we update the modbus registers from the master */
  if(mbs.update((int*)&regs, REG_COUNT))
          wdog = millis();


}