PoRelay8 board with 8 relay outputs can be connected to any device, which can communicate over I2C protocol (see example for Raspberry Pi and PoKeys). If 8 relay outputs do not satisfy your demands, additional 9 boards can be daisy-chained over CAN bus, even with long cables between boards.

Here we present an example of connecting PoRelay8 board to Arduino Mega2560. Any other Arduino device can be used, as long as it has support for communication over I2C protocol.

Connecting PoRelay8 board to Arduino Mega2560

Connecting PoRelay8 to your Arduino Mega2560 is simple. Just connect together I2C pins and ground as is shown on pictures 1 and 2. On Arduino Mega pins 20 (SDA) and 21 (SCL) are used for I2C communication. For other devices, look at the following list:

Board I2C / TWI pins
Uno, Ethernet A4 (SDA), A5 (SCL)
Mega2560 20 (SDA), 21 (SCL)
Leonardo 2 (SDA), 3 (SCL)
Due 20 (SDA), 21 (SCL), SDA1, SCL1

 

I2C bus require pull-up resistor on both lines. Mega2560 has pull-ups already integrated and do not require external resistors. Other I2C capable Arduino devices do not have integrated pull-ups and external resistors should be added. Pull-up resistor value depends on bus capacitance and communication speed. 4,7 kΩ resistors connected between SDA, SCL and power supply should be appropriate for most cases. For other allowed resistors values, please consider I2C bus specifications.

Picture 1: PoExtBusIn connector as is on PoRelay8 board with marked pins SCL, SDA and GND.

Picture 1: PoExtBusIn connector as is on PoRelay8 board with marked pins SCL, SDA and GND.

 

Picture 2: SDA and SCL connectors on Arduino Mega2560, GND is not displayed

Picture 2: SDA and SCL connectors on Arduino Mega2560, GND is not displayed

Before powering up your boards, do not forget that PoRelay8 board require separate 12V or 24V power supply (depends on relay type on your board, required power supply is marked on PCB next to screw terminals).

Picture 3: PoRelay8 board connected to Arduino Mega2560

Picture 3: PoRelay8 board connected to Arduino Mega2560

Connecting more PoRelay8 boards with CAN bus

Up to 10 boards can be connected to your Arduino board with use of a CAN bus, extending number of relay outputs to 80. Another great feature of CAN bus is possibility to use long cables if needed.

When connecting multiple relay board to your device, first board is connected directly to device on I2C bus and functions as a bridge between I2C and CAN bus. All other boards are connected to bridge board over CAN bus in daisy-chain fashion.

CAN bus connection can be made with flat cable with Micro-MaTch connectors (red connector on the relay board) or with use of screw terminal connectors as our case. All connections (picture 1) marked CAN-L and CAN-H must be connected together in parallel (connect all CAN-L terminals together with one wire, use a separate wire for all CAN-H terminals).  If you are going to use long cables, use of shielded twisted pair cables is recommended to decrease possible interferences.

Both ends of CAN bus must be terminated with resistors, which are already integrated on all PoRelay boards. They can be enabled or disabled with resistors, as is shown on picture 4. For correct jumper orientation please check PoRelay manual or see picture 4.

Picture 4: CAN connections and termination jumpers

Picture 4: CAN connections and termination jumpers

Communicating with PoRelay8 relay boards

In this chapter communication with 3 PoRelay8 boards is presented with chaser code example for Arduino Mega2560. Same code should work well with any Arduino device with support for I2C protocol. If you not using Mega2560, do not forget to add pull-up resistors to both I2C lines.

From Arduino side, all communication with PoRelay8 boards is executed over I2C bus with PoRelay8 acting as a bridge. Bridge relays all communication to (or from) other boards over CAN bus.

Any PoRelay8 board can act as a bridge board.

Picture 5: Arduino Mega2560 and 3 PoRelay8 boards

Picture 5: Arduino Mega2560 and 3 PoRelay8 boards

Communication with bridge board is simple with use of Arduino wire library, which is included at beginning of our program. If you want to find out more about this library, you can check official documentation. I2C address  of the PoRelay8 bridge board is 0x7B. All PoRelays boards have this I2C address preprogramed. To set the outputs of bridge, 3 bytes must be send over I2C. First is set outputs command, followed by byte with outputs values and checksum. Checksum is sum of all sent bytes, masked with 0xFF, as sum must not exceed 255. To turn on relay A on bridge board following bytes must be sent over I2C:

Set ouputs Output values Checksum
0x10 0x01 0x11

 

Setting of bridge PoRelay8 outputs is implemented in bridge_set_outputs function of our code example.

Sending data to other boards (connected to bridge over CAN bus) is little more complicated. To send data over CAN bus we must start with sending 0x40 value over I2C. This is interpreted as send over CAN command. Follows CAN message ID, which consist of two bytes: 0x01 and 0x08. Next byte is number of data bytes we want to send, followed by all data bytes.  Last byte is checksum of all sent bytes.

Reading data that was received from CAN bus is simple. It starts with writing 0x41 byte to I2C bus and reading from I2C expected number of received bytes from CAN bus. First read byte should have value 0x1A, as marker that data was received and is valid.

In our example, we have 2 PoRelay8 boards connected to bridge over CAN. Now we have to find out addresses of two boards connected to bridge. This can be done by issuing CAN identify command. To do this, we write to I2C bus next bytes:

Send over CAN CAN message ID Data length Data Checksum
0x40 0x01 0x08 0x01 0x10 0x5A

 

In this case, data part with value 0x10 is interpreted as request for identification. Each board connected to bridge will respond with 12 bytes long CAN message containing information about it. Received bytes from 8 to 12 represent boards unique address, that must be used when setting outputs on desired board.

To set the outputs of desired board connected over CAN bus, following sequence must be written to I2C:

Send over CAN CAN message ID Data lenght Data Checksum
Set outputs Board address Outputs
0x40 0x01 0x08 0x06 0x20 0xXX 0xXX 0xXX 0xXX 0x01 0xXX

 

This sequence of bytes will turn on relay A on board with selected address.

Our example is a chaser, which will turn on all relays on all boards in series, beginning with relay A on bridge board. It is important to know, that in this case, we are always scanning for connected boards at the start of program. The order of PoRelays board connected on the CAN bus will vary, as boards are numbered in order as they answer. When identifying boards, it is impossible to know which board will reply first. For full control over your boards, their CAN addresses should be hardcoded in your program.

Arduino code example

#include 

#define CAN_SEND    0x40
#define CAN_READ    0x41
#define IDENTIFY    0x10
#define SET_OUTPUTS 0x20
#define CAN_ID_L    0x01
#define CAN_ID_H    0x08

#define BRIDGE_ADDRESS 0x7B

byte porelays[10][4];
byte found_boards;

void setup() {
  Wire.begin(); // Initialize wire library for I2C communication
  Serial.begin(9600); // Configure serial port

  found_boards = CAN_identify(); // Identify devices connected to CAN bus
}

void loop() {
  byte chaser = 0x01;
  int i = 0;

  while( chaser )
  {
    bridge_set_outputs(chaser);
    delay(300);
    chaser = chaser  << 1;
  }
  bridge_set_outputs(0x00);

  while (i<found_boards)
  {
    chaser = 0x01;
    while( chaser )
    {    
      CAN_set_outputs(chaser, porelays[i]);
      delay(300);
      chaser = chaser  << 1;
    }
    CAN_set_outputs(0x00, porelays[i]);
    i++;
  }
}

void bridge_set_outputs(byte outputs)
{
  byte checksum = (SET_OUTPUTS + outputs) & 0xFF;

  byte data[] = { SET_OUTPUTS, outputs, checksum };
  Wire.beginTransmission(BRIDGE_ADDRESS); // transmit
  Wire.write(data, 3);
  Wire.endTransmission();    // stop transmitting
}

void CAN_set_outputs(byte outputs, byte address[])
{
  byte checksum = CAN_SEND + 0x06 + CAN_ID_H + CAN_ID_L + SET_OUTPUTS + address[0] + address[1] + address[2] + address[3] + outputs;
  byte CAN_set_outputs[] = {CAN_SEND, 0x06, CAN_ID_H, CAN_ID_L, SET_OUTPUTS, address[0], address[1], address[2], address[3], outputs, checksum };
  
  Wire.beginTransmission(BRIDGE_ADDRESS); // transmit
  Wire.write( CAN_set_outputs, 11 );
  Wire.endTransmission();    // stop transmitting
}

int CAN_identify()
{
  byte response[13];
  byte checksum = CAN_SEND + 0x01 + CAN_ID_H + CAN_ID_L + IDENTIFY;
  int i,k;
  byte CAN_dentify[] = {CAN_SEND, 0x01, CAN_ID_H, CAN_ID_L, IDENTIFY, checksum};
  
  Wire.beginTransmission(BRIDGE_ADDRESS); // transmit
  Wire.write( CAN_dentify, 6 );
  Wire.endTransmission();    // stop transmitting

  delay(200); // Wait 200ms for all responses

  k = 0;
  do
  {
    Wire.beginTransmission(BRIDGE_ADDRESS); // transmit
    Wire.write( CAN_READ );
    Wire.endTransmission();    // stop transmitting
  
    Wire.requestFrom(BRIDGE_ADDRESS, 13);    // request 13 bytes from slave device

    // Read received bytes

    i = 0;
    while(Wire.available())
    { 
      response[i] = Wire.read();
      
      if ( response[i] != 0x1A && i == 0 )
      {
        break;
      }
      
      if ( i >= 8 && i <= 11 )
      {
        porelays[k][i-8] = response[i];
      }
      
      i++;
    }
    k++;
  } while ( response[0] == 0x1A );

  // Print found boards addresses to serial port, output is in HEX format
  for (i = 0; i < k-1 ; i++)
  {
    Serial.print("CAN board ");
    Serial.print(i);
    Serial.print(" address: ");
    
    for (int j = 0; j < 4 ; j++)
    {
      Serial.print(porelays[i][j], HEX);
      Serial.print(" ");
    }
    Serial.print("\n"); // New line
  }
  
  return k-1;
}