Skip to content

Instantly share code, notes, and snippets.

@RJ
Created May 11, 2018 15:29
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save RJ/7acba5b06a03c9b521601e08d0327d56 to your computer and use it in GitHub Desktop.
Save RJ/7acba5b06a03c9b521601e08d0327d56 to your computer and use it in GitHub Desktop.
Python to control cheap USB relay from amazon: QinHeng Electronics HL-340 USB-Serial adapter
# To run with pyusb debugging:
#
# PYUSB_DEBUG=debug python relay.py
#
# Grab the vendor and product codes from syslog when plugging in the relay:
#
# usb 3-1: New USB device found, idVendor=1a86, idProduct=7523
#
import time
import usb.core
import usb.util
dev = usb.core.find(idVendor=0x1a86, idProduct=0x7523)
if dev is None:
raise ValueError("Device not found")
if dev.is_kernel_driver_active(0):
dev.detach_kernel_driver(0)
cfg = dev.get_active_configuration()
intf = cfg[(0,0)]
ep = usb.util.find_descriptor(
intf,
# match the first OUT endpoint
custom_match = \
lambda e: \
usb.util.endpoint_direction(e.bEndpointAddress) == \
usb.util.ENDPOINT_OUT)
assert ep is not None
close_relay_cmd = [0xA0, 0x01, 0x01, 0xA2]
open_relay_cmd = [0xA0, 0x01, 0x00, 0xA1]
# Since I'm using this to send a signal to a gate controller, I'm simulating just
# pressing a button to make the circuit for 2 seconds, then releasing:
ep.write(close_relay_cmd)
time.sleep(2)
ep.write(open_relay_cmd)
@nx-99
Copy link

nx-99 commented Jun 28, 2018

Hi, I have same relay but non working this script in kubuntu 16.04

sudo PYUSB_DEBUG=debug python relay.module.py
2018-06-28 19:06:29,064 DEBUG:usb.backend.libusb1:_LibUSB.init(<CDLL 'libusb-1.0.so.0', handle 10c9ac0 at 7f22705c8350>)
2018-06-28 19:06:29,075 INFO:usb.core:find(): using backend "usb.backend.libusb1"
2018-06-28 19:06:29,075 DEBUG:usb.backend.libusb1:_LibUSB.enumerate_devices()
2018-06-28 19:06:29,076 DEBUG:usb.backend.libusb1:_LibUSB.get_device_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8ad0>)
2018-06-28 19:06:29,076 DEBUG:usb.backend.libusb1:_LibUSB.get_device_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>)
2018-06-28 19:06:29,077 DEBUG:usb.backend.libusb1:_LibUSB.open_device(<usb.backend.libusb1._Device object at 0x7f22705c8c10>)
2018-06-28 19:06:29,077 DEBUG:usb.backend.libusb1:_LibUSB.is_kernel_driver_active(<usb.backend.libusb1._DeviceHandle object at 0x7f2271dfe7d0>, 0)
2018-06-28 19:06:29,077 DEBUG:usb.backend.libusb1:_LibUSB.get_configuration(<usb.backend.libusb1._DeviceHandle object at 0x7f2271dfe7d0>)
2018-06-28 19:06:29,078 DEBUG:usb.backend.libusb1:_LibUSB.get_configuration_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0)
2018-06-28 19:06:29,078 DEBUG:usb.backend.libusb1:_LibUSB.get_interface_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0, 0, 0)
2018-06-28 19:06:29,078 DEBUG:usb.backend.libusb1:_LibUSB.get_configuration_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0)
2018-06-28 19:06:29,078 DEBUG:usb.backend.libusb1:_LibUSB.get_endpoint_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0, 0, 0, 0)
2018-06-28 19:06:29,078 DEBUG:usb.backend.libusb1:_LibUSB.get_interface_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0, 0, 0)
2018-06-28 19:06:29,078 DEBUG:usb.backend.libusb1:_LibUSB.get_configuration_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0)
2018-06-28 19:06:29,079 DEBUG:usb.backend.libusb1:_LibUSB.get_endpoint_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 1, 0, 0, 0)
2018-06-28 19:06:29,079 DEBUG:usb.backend.libusb1:_LibUSB.get_interface_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0, 0, 0)
2018-06-28 19:06:29,079 DEBUG:usb.backend.libusb1:_LibUSB.get_configuration_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0)
2018-06-28 19:06:29,079 DEBUG:usb.backend.libusb1:_LibUSB.get_configuration_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0)
2018-06-28 19:06:29,079 DEBUG:usb.backend.libusb1:_LibUSB.get_interface_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0, 0, 0)
2018-06-28 19:06:29,079 DEBUG:usb.backend.libusb1:_LibUSB.get_configuration_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0)
2018-06-28 19:06:29,080 DEBUG:usb.backend.libusb1:_LibUSB.get_endpoint_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0, 0, 0, 0)
2018-06-28 19:06:29,080 DEBUG:usb.backend.libusb1:_LibUSB.get_interface_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0, 0, 0)
2018-06-28 19:06:29,080 DEBUG:usb.backend.libusb1:_LibUSB.get_configuration_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0)
2018-06-28 19:06:29,080 DEBUG:usb.backend.libusb1:_LibUSB.get_endpoint_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 1, 0, 0, 0)
2018-06-28 19:06:29,080 DEBUG:usb.backend.libusb1:_LibUSB.get_interface_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0, 0, 0)
2018-06-28 19:06:29,080 DEBUG:usb.backend.libusb1:_LibUSB.get_configuration_descriptor(<usb.backend.libusb1._Device object at 0x7f22705c8c10>, 0)
2018-06-28 19:06:29,080 DEBUG:usb.backend.libusb1:_LibUSB.claim_interface(<usb.backend.libusb1._DeviceHandle object at 0x7f2271dfe7d0>, 0)
2018-06-28 19:06:29,081 DEBUG:usb.backend.libusb1:_LibUSB.bulk_write(<usb.backend.libusb1._DeviceHandle object at 0x7f2271dfe7d0>, 2, 0, array('B', [160, 1, 1, 162]), 1000)
2018-06-28 19:06:39,088 DEBUG:usb.backend.libusb1:_LibUSB.bulk_write(<usb.backend.libusb1._DeviceHandle object at 0x7f2271dfe7d0>, 2, 0, array('B', [160, 1, 0, 161]), 1000)
Red led flashing but relay non switch.

@ldnelso2
Copy link

Hi RJ - I have a 16-channel relay with the same chip (QinHeng Electronics HL-340 USB-Serial adapter), but the board has no real documentation, so I am able to use your script to write serial commands. However, I am struggling to figure out which commands might open and close the relays. I am wondering how I figure out what to send... any ideas? I think I am not sending the checksum properly.

The below hex info is I assume in the following order: write command, message length, relay state, and then checksum?

close_relay_cmd = [0xA0, 0x01, 0x01, 0xA2]

Let me know if you have any ideas.

@ldnelso2
Copy link

So converting to decimal:

close: [160, 1, 1, 162]
open: [160, 1, 0, 161]

The last one is a checksum. I don't think my board works the same way... I'm going to dig a bit more.

@ldnelso2
Copy link

I figured my relay out.. turns in I couldn't back into it... I ended up finding the serial commands from a sainsmart product wiki (which is tough to find on their website).

For example to turn on relay 6

c6_on=[0x3A, 0x46, 0x45, 0x30, 0x35, 0x30, 0x30, 0x30, 0x35, 0x46, 0x46, 0x30, 0x30, 0x46, 0x39, 0x0D, 0x0A]
ep.write(c6_on)

Thanks for posting the script! Really helped me get on the right track for sure.

@HerrMuellerluedenscheid
Copy link

Hey, thanks for this snippet. For the sake of completion: You can throw this into e.g. /etc/udev/rules.d/10-usb-relay.rules to allow accessing that device without root permission:

SUBSYSTEM=="usb", ATTR{idVendor}=="1a86", ATTR{idProduct}=="7523", MODE="0666"

@StarTestTM
Copy link

Hello. I try to run your code, but get following error message:

Traceback (most recent call last):
File "D:\Projects\Python\Phytec\WBRD300-REV5\Python Scripts\Modules\RelayControl.py", line 20, in
if dev.is_kernel_driver_active(0):
File "C:\Python\Python39-32\lib\site-packages\usb\core.py", line 989, in is_kernel_driver_active
self._ctx.managed_open()
File "C:\Python\Python39-32\lib\site-packages\usb\core.py", line 105, in managed_open
self.handle = self.backend.open_device(self.dev)
File "C:\Python\Python39-32\lib\site-packages\usb\backend\libusb1.py", line 722, in open_device
return _DeviceHandle(dev)
File "C:\Python\Python39-32\lib\site-packages\usb\backend\libusb1.py", line 600, in init
_check(_lib.libusb_open(self.devid, byref(self.handle)))
File "C:\Python\Python39-32\lib\site-packages\usb\backend\libusb1.py", line 550, in _check
raise NotImplementedError(_strerror(ret))
NotImplementedError: Operation not supported or unimplemented on this platform

What wrong?

@RJ
Copy link
Author

RJ commented May 16, 2022

for future-me, here's how it's wired up to mqtt:

#!/usr/bin/python
# To run with pyusb debugging:
#
#   PYUSB_DEBUG=debug python relay.py
#
# Grab the vendor and product codes from syslog when plugging in the relay:
#
#   usb 3-1: New USB device found, idVendor=1a86, idProduct=7523
#
import time
import usb.core
import usb.util
import paho.mqtt.subscribe as subscribe

dev = usb.core.find(idVendor=0x1a86, idProduct=0x7523)

if dev is None:
	raise ValueError("Device not found")

if dev.is_kernel_driver_active(0):
	dev.detach_kernel_driver(0)

cfg = dev.get_active_configuration()
intf = cfg[(0,0)]

ep = usb.util.find_descriptor(
    intf,
    # match the first OUT endpoint
    custom_match = \
    lambda e: \
        usb.util.endpoint_direction(e.bEndpointAddress) == \
        usb.util.ENDPOINT_OUT)

assert ep is not None

close_relay_cmd = [0xA0, 0x01, 0x01, 0xA2]
open_relay_cmd = [0xA0, 0x01, 0x00, 0xA1]

def handle_mqtt(client, userdata, message):
	print("topic='%s' payload='%s'" % (message.topic, message.payload))
	if message.payload == "ON":
		print "Closing relay (gate -> open)"
		ep.write(close_relay_cmd)
		client.publish("hass/front_gate/state", payload="ON", retain=True)
		return

	if message.payload == "OFF":
		print "Opening relay (gate -> close)"
		ep.write(open_relay_cmd)
		client.publish("hass/front_gate/state", payload="OFF", retain=True)
		return

	print "Unhandled msg"

auth_data = {'username': "gaterelay", 'password': "xxxxxxxxxxx"}

subscribe.callback(handle_mqtt, "hass/front_gate", hostname="zorro", port=1883, auth=auth_data, client_id="gates")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment