How to use a Sainsmart 8 Channel USB relay with python - python

I've been looking for a couple hours now and I would like to get my Sainsmart 8 channel usb relay working using python. If anyone can help me out with this that would be fabulous. Thanks.

So, there are two methods here, and it depends on where you want to go. A) you could get a command line version working like usbrelay and then issue commands from python, and B) you could open up a serial connection from inside python and use usb.core or usb.util to send serial commands to the board.
A) Darrylb's work on github is nice overview here to get a command line tool working for HID Compatible Relays. https://github.com/darrylb123/usbrelay
If you are on debian:
sudo apt-get install usbrelay
Then you update your rules.d file so you don't have to use 'sudo' all the time and you can call the command line within python. This has certain security implications of course, which you should google around for if you plan to use this longer term.
Anyway, once you get it installed, you can just call it from the command line and it will list your relay_ids in some format with suffixes that look like _1 through _8.
$ ./usbrelay
The output will show you info on the board you have attached, and give you the relay ids and the status. A "1" is on and a "0" is off.
then from within python:
import os
os.system("usbrelay 5291D_1=1 5291D_3=1")
To turn those off you just change the ones in the assignments to zeros, and you can do all eight relay numbers simultaneously. So you could go on from there to write all kinds of functions to fire your relays in unique combinations, delays, etc. You can also use subprocess from within python:
import subprocess
subprocess.run(['usbrelay', '5291D_1=1', '5291D_3=1'], stdout=subprocess.PIPE).stdout.decode('utf-8')
B) To take the serial connection approach, you have to start with knowing what the proper serial commands to your board is. For example, I have a sainsmart 16-channel relay that requires me to send the serial commands because it isn't HID compatible.
Find your model number here on the Sainsmart Wiki:
http://wiki.sainsmart.com/index.php/SKU
Then download the zip file and take a look at the relay instructions. For me, I used a modification of RJ's work to send a serial command.
https://gist.github.com/RJ/7acba5b06a03c9b521601e08d0327d56
This is from the instructions for my SKU number (Likely diff than yours!)
CH-6 ON 3A 46 45 30 35 30 30 30 35 46 46 30 30 46 39 0D 0A
You need to add a 0x prefix to the serial messages:
c6_on=[0x3A, 0x46, 0x45, 0x30, 0x35, 0x30, 0x30, 0x30, 0x35, 0x46, 0x46, 0x30, 0x30, 0x46, 0x39, 0x0D, 0x0A]
Then you can send those commands to the usb.core.Endpoint using an approach how RJ approached it in the git gist link above, with 'ep' here being the usb.core.Endpoint:
ep.write(c6_on)
Either way, as long as you can either A) control the board from the command line or B) know the right serial commands to send, then you can figure out how to do it with python. The only other thing to watch out for is making sure you have the proper dependencies in place and watching permissions.

after you have installed the USB driver (the executable from http://wiki.sainsmart.com/index.php/101-70-116) the following code will work to turn on first three relays. Each bit in the third number corresponds to an individual relay and needs to be turned on or off. My test was done on a windows 10 machine and is a result of trial and error. the documentation was not helpful.
e.g. If I passed [255,1,2], it will turn on second relay but turn off the first since second bit is on but all other bits are off.
import serial
import time
serialPort = serial.Serial(port="COM6", baudrate=9600, bytesize=8, timeout=2, stopbits=serial.STOPBITS_ONE)
elements=[255,1,1]
result = serialPort.write(elements)
time.sleep(2)
elements=[255,1,3]
result = serialPort.write(elements)
time.sleep(2)
elements=[255,1,7]
result = serialPort.write(elements)
serialPort.close()

As this module uses FT245RL USB FIFO integrated circuit, you can use this repo
https://github.com/vpatron/relay_ft245r from Vince Patron.
the test.py source is quite easy and understandable :
import relay_ft245r
import sys
import time
rb = relay_ft245r.FT245R()
dev_list = rb.list_dev()
# list of FT245R devices are returned
if len(dev_list) == 0:
print('No FT245R devices found')
sys.exit()
# Show their serial numbers
for dev in dev_list:
print(dev.serial_number)
# Pick the first one for simplicity
dev = dev_list[0]
print('Using device with serial number ' + str(dev.serial_number))
# Connect and turn on relay 2 and 4, and turn off
rb.connect(dev)
rb.switchon(2)
rb.switchon(4)
time.sleep(1.0)
rb.switchoff(2)
time.sleep(1.0)
rb.switchoff(4)

Related

PySerial Attributes - Issues, Documentation Current Reference?

I am very new, learning Python specifically geared toward hardware (serial port and TCP/IP device) testing.
I have been trying to get PySerial based code to work and keep hitting roadblocks. Running Python 3.10.8 on Windows 10.
I worked through the 'import serial' problem (uninstalled and reinstalled Python); the serial.Serial problem (needed to add 'from serial import *). Now, it seems like all of the read syntax does not work. All I want to do at this point is open the port, read and print data - from here I will start working on which data I want).
Here is the code I am working with (this was found in a couple of places on the internet):
#test_sport
import serial
from serial import *
s = serial.Serial(port='COM9', baudrate=9600)
serial_string = ""
while(1):
# Wait until there is data waiting in the serial buffer
if(serialPort.in_waiting > 0):
# Read data out of the buffer until a carraige return / new line is found
serial_string = serial.readline()
# Print the contents of the serial data
print(serial_string.decode('Ascii'))
# Tell the device connected over the serial port that we recevied the data!
# The b at the beginning is used to indicate bytes!
#serialPort.write(b"Thank you for sending data \r\n")
Running this results in an error on serialPort.in_waiting (says serialPort not defined) if I change that to serial.in_waiting (says serial has no attribute 'in_waiting' (PySerial API site says this is correct(?). I've also tried simple commands like serial.read(), serial.readline(), ser.read(), etc. All fail for attributes.
Is the PySerial documentation online current? Does anyone know where to find basic serial port examples?
Thank you!

Problem with MultiPort use in python mido MIDI package

I am working in a program that autosaves MIDI messages as they are played, and everything is working really well when using a single input port.
I want to extend to use 2 or 3 simultaneous ports (the app is aimed at VST piano players, and some use the piano as one MIDI device, and the sustain pedal as a second device in another USB slot). I was able to get it to work using two separate input ports, and then using 2 iter_pending() loops. The basic code is something like this:
ports = mido.get_input_names()
port_A = mido.open_input(ports[0])
port_B = mido.open_input(ports[1])
for msg_A in port_A.iter_pending():
...
for msg_B in port_B.iter_pending():
...
The problem with this is that the loops are sequential. When I test by sending the same midi message through the 2 ports, the event when enters through port_B is processed with a few milliseconds of delay.
The MIDO package includes a different type of port exactly for this: mido.ports.MultiPort()
My problem is that I cannot make it to work.
If I try:
multi = mido.ports.MultiPort([port_A, port_B])
for msg in multi:
print (msg)
as suggested in the MIDO documentation, no msg is printed...
I have done all sorts of syntax combinations for the ports in multi, but nothing seems to work.
What am I doing wrong here?
Have you tried:
open_ports = [portA, portB]
while True:
for port in open_ports:
do_whatever(port.poll())
or to make it asynchronous:
portA = mido.open_input(ports[0], callback=do_whatever)
portB = mido.open_input(ports[1], callback=do_whatever)
while True:
pass
In both cases, do_whatever is a function that takes a single message as an argument and does with that message whatever it is you want to do.

Crystal-lang Accessing Serial port

I want to access the serial port using Crystal lang.
I have following code in python. I want to write the equivalent Crystal-lang code for a pet project.
import serial
def readSerData():
s = ser.readline()
if s:
print(s)
result = something(s) #do other stuff
return result
if __name__ == '__main__':
ser = serial.Serial("/dev/ttyUSB0", 9600)
while True:
data = readSerData()
#do something with data
I couldn't find any library for accessing the serial port.
What is the proper way for accessing serial port in crystal-lang?
Thanks in advance.
It is easier to answer this question in multiple parts to really cover it all:
Q: How do I access a serial port on linux/bsd?
A: Open it as a file. On linux/bsd a serial connection is established the moment a device is plugged in, and is then listed somewhere under /dev/ (these days, usually as /dev/ttyUSB0). In order to access this connection you simply open it like you would a regular file. Sometimes this is actually good enough to start communicating with the device as modern hardware typically works with all baud rates and default flags.
Q: How do I configure a serial/tty device on linux/bsd?
A: Set termios flags on the file. If you do need to configure your connection to set things like baud rate, IXON/IXOFF etc, you can do it before even running your program using stty if it is available. Eg. to set the baud rate you could run: stty -F /dev/ttyUSB0 9600. And after this is set up you can just open it as a file and start using it.
You can spawn stty from crystal using Process.run if you wanted an easy way to configure the device from your app. I would probably recommend this approach over the next solution..
Q: How do I set termios flags from crystal, without using stty?
A: Use the termios posix functions directly.
Crystal actually provides FileDescriptor handles with a few common termios settings such as cooked, which means it has minimal termios bindings already. We can start by using the existing code for our inspiration:
require "termios" # See above link for contents
#Open the file
serial_file = File.open("/dev/ttyACM0")
raise "Oh no, not a TTY" unless serial_file.tty?
# Fetch the unix FD. It's just a number.
fd = serial_file.fd
# Fetch the file's existing TTY flags
raise "Can't access TTY?" unless LibC.tcgetattr(fd, out mode) == 0
# `mode` now contains a termios struct. Let's enable, umm.. ISTRIP and IXON
mode.c_iflag |= (Termios::InputMode::ISTRIP | Termios::InputMode::IXON).value
# Let's turn off IXOFF too.
mode.c_iflag &= ~Termios::InputMode::IXOFF.value
# Unfun discovery: Termios doesn't have cfset[io]speed available
# Let's add them so changing baud isn't so difficult.
lib LibC
fun cfsetispeed(termios_p : Termios*, speed : SpeedT) : Int
fun cfsetospeed(termios_p : Termios*, speed : SpeedT) : Int
end
# Use the above funcs to set the ispeed and ospeed to your nominated baud rate.
LibC.cfsetispeed(pointerof(mode), Termios::BaudRate::B9600)
LibC.cfsetospeed(pointerof(mode), Termios::BaudRate::B9600)
# Write your changes to the FD.
LibC.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode))
# Done! Your serial_file handle is ready to use.
To set any other flags, refer to the termios manual, or this nice serial guide I just found.
Q: Is there a library to do all this for me?
A: No :( . Not that I can see, but it would be great if someone made it. It's probably not much work for someone to make one if they had a vested interest :)

Repeat command / collect result for reuse in another command

I have a python script that was created by another set of people which sends a command to a layer 1 device that gives a feedback based on a connection if it's active or not. However the script can only be executed one port at a time which makes it labor intensive to go through 24/48 ports.
Example:
python portquery.py -status (name of device)-(port)
which it gives a feedback of "Connected" or "Not Connected" or "Port not found" (since this port does not exist on a switch for example).
So I got thinking was what if it were possible to just repeat that script with no port specified and it starts at Port 1 and stops when it gets the feedback "Port not found" which it saves the result into a file or some form of array and then so I can use that set for adding or modifying a vlan on the same switch with those "Not connected" ports.
For example:
$ python repeatquery.py (name of device)
Port 1 - Connected
Port 2 - Not Connected
etc etc
Port 49 - Port not found
I honestly have no idea how to start on this or what this would be called since my work revolves around the hardware aspect of things. I'm just trying to get ideas of where to look. I've looked at repeat commands and the "Similar Questions" sidebar to the right. I guess it's a bit of a convoluted concept that I'm looking at here perhaps?
Without the code, it is hard to show you exactly how to do it. But probably in the code there is a function which does the query, and a place the function is called. You could change the call to something like:
for x in range(0, 47):
do_the_function(x)
Or, you can write a small bash script which calls the python script 48 times.
Something along the lines of (I am not sure if you need to number the ports from 0 to 47 or 1 to 8):
#!/bin/bash
for number in {1..48}
do
python checkport.py number
done
Then you make the bash script executable. Say you called the script "checkallports.sh":
> chmod +x checkallports.sh
Then you can call it:
> ./checkallports.sh

Connecting via USB/Serial port to Newport CONEX-PP Motion Controller in Python

I'm having trouble getting my Windows 7 laptop to talk to a Newport CONEX-PP motion controller. I've tried python (Spyder/Anaconda) and a serial port streaming program called Termite and in either case the results are the same: no response from the device. The end goal is to communicate with the controller using python.
The controller connects to my computer via a USB cable they sold me that is explicitly for use with this device. The connector has a pair of lights that blink when the device receives data (red) or sends data (green). There is also a packaged GUI program that comes with the device that seems to work fine. I haven't tried every button, the ones I have tried have the expected result.
The documentation for accessing this device is next to non-existant. The CD in the box has one way to connect to it and the webpage linked above has a different way. The first way (CD from the box) creates a hierarchy of modules that ends in a module it does not recognize (this is a code snippet provided by Newport):
import sys
sys.path.append(r'C:\Newport\MotionControl\CONEX-PP\Bin')
import clr
clr.AddReference("Newport.CONEXPP.CommandInterface")
from CommandInterfaceConexPP import *
import System
instrument="COM5"
print 'Instrument Key=>', instrument
myPP = ConexPP()
ret = myPP.OpenInstrument(instrument)
print 'OpenInstrument => ', ret
result, response, errString = myPP.SR_Get(1)
That last line returns:
Traceback (most recent call last):
File "< ipython-input-2-5d824f156d8f >", line 2, in
result, response, errString = myPP.SR_Get(1)
TypeError: No method matches given arguments
I'm guessing this is because the various module references are screwy in some way. But I don't know, I'm relatively new to python and the only time I have used it for serial communication the example files provided by the vendor simply worked.
The second way to communicate with the controller is via the visa module (the CONEX_SMC_common module imports the visa module):
import sys
sys.path.append(r'C:\Newport\NewportPython')
class CONEX(CONEXSMC): def __init__(self):
super(CONEX,self).__init__() device_key = 'com5'
self.connect=self.rm.open_resource(device_key, baud_rate=57600, timeout=2000, data_bits=8, write_termination='\r\n',read_termination='\r\n')
mine.connect.read()
That last mine.connect.read() command returns:
VisaIOError: VI_ERROR_TMO (-1073807339): Timeout expired before operation completed.
If, instead, I write to the port mine.connect.write('VE') the light on the connector flashes red as if it received some data and returns:
(4L, < StatusCode.success: 0 >)
If I ask for the dictionary of the "mine" object mine.__dict__, I get:
{'connect': <'SerialInstrument'(u'ASRL5::INSTR')>,
'device_key': u'ASRL5::INSTR',
'list_of_devices': (u'ASRL5::INSTR',),
'rm': )>}
The ASRL5::INSTR resource for VISA is at least related to the controller, because when I unplug the device from the laptop it disappears and the GUI program will stop working.
Maybe there is something simple I'm missing here. I have NI VISA installed and I'm not just running with the DLL that comes from the website. Oh, I found a Github question / answer with this exact problem but the end result makes no sense, the thread is closed after hgrecco tells him to use "open_resource" which is precisely what I am using.
Results with Termite are the same, I can apparently connect to the controller and get the light to flash red, but it never responds, either through Termite or by performing the requested action.
I've tried pySerial too:
import serial
ser = serial.Serial('com5')
ser.write('VE\r\n')
ser.read()
Python just waits there forever, I assume because I haven't set a timeout limit.
So, if anyone has any experience with this particular motion controller, Newport devices or with serial port communication in general and can shed some light on this problem I'd much appreciate it. After about 7 hours on this I'm out of ideas.
After coming back at this with fresh eyes and finding this GitHub discussion I decided to give pySerial another shot because neither of the other methods in my question are yet working. The following code works:
import serial
ser = serial.Serial('com5',baudrate=115200,timeout=1.0,parity=serial.PARITY_NONE, stopbits=serial.STOPBITS_ONE, bytesize=serial.EIGHTBITS)
ser.write('1TS?\r\n')
ser.read(10)
and returns
'1TS000033\r'
The string is 9 characters long, so my arbitrarily chosen 10 character read ended up picking up one of the termination characters.
The problem is that python files that come with the device, or available on the website are at best incomplete and shouldn't be trusted for anything. The GUI manual has the baud rate required. I used Termite to figure out the stop bit settings - or at least one that works.
3.5 years later...
Here is a gist with a class that supports Conex-CC
It took me hours to solve this!
My device is Conex-CC, not PP, but it's seem to be the same idea.
For me, the serial solution didn't work because there was absolutely no response from the serial port, either through the code nor by direct TeraTerm access.
So I was trying to adapt your code to my device (because for Conex-CC, even the code you were trying was not given!).
It is important to say that import clr is based on pip install pythonnet and not pip install clr which will bring something related to colors.
After getting your error, I was looking for this Pythonnet error and have found this answer, which led me to the final solution:
import clr
# We assume Newport.CONEXCC.CommandInterface.dll is copied to our folder
clr.AddReference("Newport.CONEXCC.CommandInterface")
from CommandInterfaceConexCC import *
instrument="COM4"
print('Instrument Key=>', instrument)
myCC = ConexCC()
ret = myCC.OpenInstrument(instrument)
print('OpenInstrument => ', ret)
response = 0
errString = ''
result, response, errString = myCC.SR_Get(1, response, errString)
print('Positive SW Limit: result=%d,response=%.2f,errString=\'%s\''%(result,response,errString))
myCC.CloseInstrument()
And here is the result I've got:
Instrument Key=> COM4
OpenInstrument => 0
Positive SW Limit: result=0,response=25.00,errString=''�
For Conex-CC serial connections are possible using both pyvisa
import pyvisa
rm = pyvisa.ResourceManager()
inst = rm.open_resource('ASRL6::INSTR',baud_rate=921600, write_termination='\r\n',read_termination='\r\n')
pos = inst.query('01PA?').strip()
and serial
import serial
serial = serial.Serial(port='com6',baudrate=921600,bytesize=8,parity='N',stopbits=1,xonxoff=True)
serial.write('01PA?'.encode('ascii'))
serial.read_until(b'\r\n')
All the commands are according to the manual

Categories