I believe I misunderstand the communication protocol for the 4-channel SPDT Relay. The relays are connected to +5; SDA on pin 3; SCL on pin 5, and ground. So that is fine.
gives me the address
</s>i2cdetect -y 1<e>
</s>0x11<e>
I had anticipated, that I could simply send data binary to switch on and off the channels one by one.
</s><i>
</i>from smbus2 import SMBus
with SMBus(1) as bus:
# Write a byte to address 80, offset 0
data = 0x01
bus.write_byte_data(0x11, 1, data)
<e>
the above - in my mind would switch on channel 1, if I had chosen data to be F, all channels should be switched.
The code executed, but none of the relais reacts (measured with multimeter)
Can anybody help? Many thanks!
ah sorry. Reverse engineered it from the cpp example linked. Almost as I thought, but we need to send 0x10 to first stell that we want to control channels. So this does the job for me.
</s>i2cset -y 1 0x11 0x10 0x0f
<e>
closed.
Hi fal414, did you solve the issue? im trying to use the same relay dev board and find the address to be 0x11. do you have any sample code or library i could take a look at?
Thanks
I have one of these and will clarify additional information for controlling this 4-channel relay in python.
The Quick Answer
In python, we can do this using the smbus2
module and the write_byte_data()
function in the SMBus
class.
from smbus2 import SMBus
with SMBus(1) as bus:
bus.write_byte_data(0x11, 0x10, 0x0b)
bus.write_byte_data(0x11, 0x10, 0x0f)
The Long Answer
I experimented with writing data using the i2cset
command:
i2cset -y 1 0x11 0x10 <DATA>
When looking at the side with the I2C connector, with the left-most relay as #1 (COM1), here is the relay state when sent the hex DATA value to the device. Note that this is the coil state (relay coil ON and red LED ON).
1 2 3 4
---------------
0x00: OFF OFF OFF OFF
0x01: ON OFF OFF OFF
0x02: OFF ON OFF OFF
0x03: ON ON OFF OFF
0x04: OFF OFF ON OFF
0x05: ON OFF ON OFF
0x06: OFF ON ON OFF
0x07: ON ON ON OFF
0x08: OFF OFF OFF ON
0x09: ON OFF OFF ON
0x0a: OFF ON OFF ON
0x0b: ON ON OFF ON
0x0c: OFF OFF ON ON
0x0d: ON OFF ON ON
0x0e: OFF ON ON ON
0x0f: ON ON ON ON
Unfortunately I could not find a way to get the state of a relay. Reading 0x11 0x10
always locked up my whole I2C bus requiring a cold power cycle of my GrovePi+.
Let us say everything ON is our default state which is 0x0f
and I want to power cycle relay 3. I would set state 0x0b
then set 0x0f
. Using i2cset
:
i2cset -y 1 0x11 0x10 0x0b
i2cset -y 1 0x11 0x10 0x0f
In python, we can do this using the smbus2
module and the write_byte_data()
function in the SMBus
class.
from smbus2 import SMBus
with SMBus(1) as bus:
bus.write_byte_data(0x11, 0x10, 0x0b)
bus.write_byte_data(0x11, 0x10, 0x0f)
SMBus(1)
is I2C bus 1 (/dev/i2c-1
)
0x11
is the I2C address of the relay
0x10
is (I think but not sure) the register we’re sending data to
0x0b
is the data from the table above that defines the state of all relays
I’m hitting the complete lock up problem when trying to use the 4-Channel SPDP Relay. Happened upon this post. I’ll give my feedback when done.
First, that tale will make a bit more sense if you reverse the registers
4 3 2 1
---------------
0x00: OFF OFF OFF OFF
0x01: OFF OFF OFF ON
0x02: OFF OFF ON OFF
0x03: OFF OFF ON ON
0x04: OFF ON OFF OFF
0x05: OFF ON OFF ON
0x06: OFF ON ON OFF
0x07: OFF ON ON OFF ON
0x08: ON OFF OFF OFF
0x09: ON OFF OFF ON
0x0a: ON OFF ON OFF
0x0b: ON OFF ON ON
0x0c: ON ON OFF OFF
0x0d: ON ON OFF ON
0x0e: ON ON ON OFF
0x0f: ON ON ON ON
Thanks for the hints.
I put together a quick python script to to some bitwise or’ing with arguments.
This is like the second python program I ever wrote so it was
google python arguments
google python bitwise or
google python byte type
etc
There is a lot of printf() debugging in here
#!/usr/bin/python
import sys
# I tried to mess with the byte objext but quit
Regs_to_turn_on = 0
# we should make sure that the arguments are numbers between 1 and 4.
print ('Number of arguments:', len(sys.argv), 'arguments.')
print ('Argument List:', str(sys.argv))
print("\nArguments passed:\n", end = " ")
for i in range(1, len(sys.argv)):
print(sys.argv[i], end = " ")
Regs_to_turn_on = Regs_to_turn_on | ( 1 << int(sys.argv[i])-1 )
print("\n")
print("Interim Byte to write is = (in hex)" + hex(Regs_to_turn_on))
print("Interim Byte to write is = (in binary)" + bin(Regs_to_turn_on))
print("\n")
print("\n")
print("Final Byte to write is = (in hex)" + hex(Regs_to_turn_on))
print("Final Byte to write is = (in binary)" + bin(Regs_to_turn_on))
print("\n")
from smbus2 import SMBus
with SMBus(1) as bus:
# 4321
bus.write_byte_data(0x11, 0x10, Regs_to_turn_on)
Thnx, this was very helpfull for me!
I just got one of these boards and came across this so figured I would share. See below for a python example where I have replicated the Adruino C++ example so that you can leverage it more easily.
Hope this helps.
import time
import smbus
class GroveRelay(object):
def __init__(self):
self.channel_state = 0
self.cmd_channel_ctrl = None
self.save_i2c_addr = None
self.read_i2c_addr = None
self.read_firmware_ver = None
def begin(self, i2c_port, cmd_channel_ctrl, save_i2c_addr, read_i2c_addr, read_firmware_ver):
self.channel_state = 0
self.cmd_channel_ctrl = cmd_channel_ctrl
self.save_i2c_addr = save_i2c_addr
self.read_i2c_addr = read_i2c_addr
self.read_firmware_ver = read_firmware_ver
self.bus = smbus.SMBus(i2c_port)
self.bus.write_byte_data(self.save_i2c_addr, self.cmd_channel_ctrl, self.channel_state)
def change_i2c_address(self, new_addr):
self.bus.write_byte_data(self.save_i2c_addr, self.save_i2c_addr, new_addr)
self.save_i2c_addr = new_addr;
def get_channel_state(self):
return self.channel_state
def get_firmware_version(self):
self.bus.write_byte(self.save_i2c_addr, self.read_firmware_ver)
return self.bus.read_byte(self.save_i2c_addr)
def channel_ctrl(self, state):
self.channel_state = state
self.bus.write_byte_data(self.save_i2c_addr, self.cmd_channel_ctrl, self.channel_state)
def turn_on_channel(self, channel):
self.channel_state |= (1 << (channel - 1))
self.bus.write_byte_data(self.save_i2c_addr, self.cmd_channel_ctrl, self.channel_state)
def turn_off_channel(self, channel):
self.channel_state &= ~(1 << (channel - 1))
self.bus.write_byte_data(self.save_i2c_addr, self.cmd_channel_ctrl, self.channel_state)
if __name__ == "__main__":
I2C_PORT = 1
CMD_CHANNEL_CTRL = 0x10
CMD_SAVE_I2C_ADDR = 0x11
CMD_READ_I2C_ADDR = 0x12
CMD_READ_FIRMWARE_VER = 0x13
CHANNLE1_BIT = 0x01
CHANNLE2_BIT = 0x02
CHANNLE3_BIT = 0x04
CHANNLE4_BIT = 0x08
CHANNLE5_BIT = 0x10
CHANNLE6_BIT = 0x20
CHANNLE7_BIT = 0x40
CHANNLE8_BIT = 0x80
relay = GroveRelay()
relay.begin(1, 0x10, 0x11, 0x12, 0x13)
print ("Firmware version:" + str(relay.get_firmware_version()))
#/* Begin Controlling Relay */
print("Channel 1 on");
relay.turn_on_channel(1);
time.sleep(1);
print("Channel 2 on");
relay.turn_off_channel(1);
relay.turn_on_channel(2);
time.sleep(1);
print("Channel 3 on");
relay.turn_off_channel(2);
relay.turn_on_channel(3);
time.sleep(1);
print("Channel 4 on");
relay.turn_off_channel(3);
relay.turn_on_channel(4);
time.sleep(1);
relay.turn_off_channel(4);
relay.channel_ctrl(CHANNLE1_BIT |
CHANNLE2_BIT |
CHANNLE3_BIT |
CHANNLE4_BIT);
print("Turn all channels on, State: ");
print(relay.get_channel_state());
time.sleep(1);
relay.channel_ctrl(CHANNLE1_BIT |
CHANNLE3_BIT);
print("Turn 1 3 channels on, State: ");
print(relay.get_channel_state());
time.sleep(1);
relay.channel_ctrl(CHANNLE2_BIT |
CHANNLE4_BIT);
print("Turn 2 4 channels on, State: ");
print(relay.get_channel_state());
time.sleep(1);
relay.channel_ctrl(0);
print("Turn off all channels, State: ");
print(relay.get_channel_state());
time.sleep(1);