First part of this blog (you can find it here) was about connecting and controlling single PoRelay8 board with Raspberry Pi. Relay board and Raspberry Pi were communicating over I2C bus. Setup in this example has two limitations: First, I2C bus was developed for communication between devices on same PCB and can be used only by devices position close together. Second, single PoRelay8 board features 8 relay outputs. But what to do if you need more relay outputs?
Answer to both limitations is simple: connect together up to 10 PoRelay8 boards over CAN bus in daisy-chain. In this blog we will present example hot to connect 3 PoRelay8 boards to Raspberry Pi and how to communicate with them. Up to 10 boards can be connected to a single master device (Raspberry Pi in our case, but this can be done with any device that supports I2C protocol).
Connecting PoRelay8 boards with CAN bus
For detailed information how to connect first PoRelay board to Raspberry Pi, please look at first part of this blog, which can be accessed here.
In our setup, first relay board is connected to Raspberry Pi over I2C bus and functions as a bridge. Additional PoRelay boards should be connected to bridge relay board in daisy-chain fashion with use of a CAN bus.
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 serial fashion. If you want to use long cables, shielded twisted pair cables are recommended.
CAN bus line must be terminated with resistor at both ends. PoRelay boards have termination resistors already integrated. They can be enabled with use of jumper as is shown on picture 1. Watch out for correct jumper orientation. Note that jumpers must be removed on relay boards that are not at the end of the CAN bus (only second board in our csae).
Setting the outputs on PoRelay8 boards
Primarily, the PoRelay8 devices were designed to receive data sent over PoExtBus (serial communication protocol used by PoKeys devices for external outputs). PoRelay8 devices also enable the communications over I2C or CAN buses. Communication with bridge board is quite simple as is presented in previous blog. This board can be controlled directly over I2C. Communication to other boards connected to bridge board with CAN bus is a little bit trickier.
In following python example 3 boards are connected to Raspberry Pi. All communication between Pi and bridge board happens over I2C bus. Bridge board forwards all communication with other boards over CAN bus.
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 case and python example, we have 2 PoRelay boards connected to bridge board over CAN. At beginning I2C address of the bridge board is known and we can set its outputs simply as was described in previous blog. 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 | 0x55 | 0xXX |
This will set outputs targeted board to pattern 01010101. Checksum is sum of all sent bytes masked with 0xFF, as its value must not be greater than 255.
Following python example will do next:
- Identify bridge board and write out boards address and firmware version
- Set outputs of bridge board to 0x55
- Identify all boards connected to can bus and print out their addresses
- Set outputs of every board to lowest byte of its address
Do not forget, for this example to work, you must have pigpio library installed on your Raspberry Pi and daemon running.
Python example
import struct import time import pigpio import random ############################ # Read data from CAN ############################ def CAN_read(): request = [ 0x41 ] # Read from CAN command pi.i2c_write_device(h, request) (count, response) = pi.i2c_read_device(h, 13) # Check if received data is OK if ( response[0] == 0x1A ): return response else: return 0 ############################# # CAN identify ############################# def CAN_identify(): command = [ 0x40 ] # Send over CAN CAN_ID = [ 0x08, 0x01 ] # CAN message ID data = [ 0x10 ] # Identify command data_length = [ len(data) ] request = command + data_length + CAN_ID + data request.append( sum(request) & 0xFF ) # append checksum pi.i2c_write_device(h, request) (count, response) = pi.i2c_read_device(h, 1) print("Response after Identify command:") print(response[0]) # Wait for 400 ms for replays from all boards time.sleep(.400) device_num = 1 found_devices = [] while True: response = CAN_read() if response: found_devices.append( list(response[8:12]) ) (device_id,) = struct.unpack_from("I", response[8:12], 0) print( 'Device {} ID: {:02x}'.format(device_num, device_id) ) else: break device_num += 1 return found_devices ############################# # Set outputs on CAN board ############################# def CAN_set_outputs(board_id, output): command1 = [ 0x40 ] # Send over CAN command2 = [ 0x20 ] # Set outputs command CAN_ID = [ 0x08, 0x01 ] data = [ output ] # Identify command data_length = [ len(command2) + len(data) + len(board_id) ] request = command1 + data_length + CAN_ID + command2 + board_id + data request.append( sum(request) & 0xFF ) # append checksum pi.i2c_write_device(h, request) return ############################# # Main program ############################# pi = pigpio.pi() DEVICE_ADDRESS = 0x7b # 7 bit I2C address of relay board h = pi.i2c_open(1, DEVICE_ADDRESS) request = [ 0x10 ] # Identify board command, to which bridge beard will respond pi.i2c_write_device(h, request) # Send first command (count, response) = pi.i2c_read_device(h, 9) # Read bridge board response (9 bytes) # Print bridge board address (in hex format) and version print( 'Version: {}.{}'.format(response[3], response[4]) ) # (device_id,) = struct.unpack_from("I", response, 5) print( 'Bridge Device ID: {:02x}'.format(device_id) ) # Set outputs on Bridge relay board outputs = 0x22 request = [ 0x20, outputs, (outputs + 0x20) & 0xFF] pi.i2c_write_device(h, request) # Identify devices connected to CAN bus can_boards = CAN_identify() # CAN Set outputs for device in can_boards: CAN_set_outputs(device, device[0]); pi.i2c_close(h)
The post Raspberry Pi 3 and PoRelay8 relay boards – Part 2 (CAN bus) appeared first on PoBlog™.