Handling time-out for Python-script with RS485 & minimalmodbus - python

Presently have a read-out on an RS485-bus of 1 (one) RS485 kWh-meter, type DDS238-1ZN by means of a Python-script 'assisted' by module minimalmodbus.
Adding more kWh-meters means that (prior or during installation) the Slave-Adress of the added kWh-meter has to be shifted from the initial '1'.
Such shift-action starts with scanning the RS485-bus to determine where kWh-meter(s) are actually located.
First step is the following simple Python-script
import serial
import minimalmodbus
# Check address 00 = broadcast
instrument = minimalmodbus.Instrument('/dev/ttyAMA0',0) # port name, slave address
instrument.serial.baudrate = 9600
instrument.serial.timeout = 0.5
instrument.debug = True
print instrument
print instrument.read_register(21,0) # registernumber, number of decimals
# Check address 01 = slave 01
instrument = minimalmodbus.Instrument('/dev/ttyAMA0',1) # port name, slave address
print instrument
print instrument.read_register(21,0) # registernumber, number of decimals
# Check address 02 = slave02
instrument = minimalmodbus.Instrument('/dev/ttyAMA0',2) # port name, slave address
print instrument
print instrument.read_register(21,0) # registernumber, number of decimals
The check on Slave-addresses 00 and 01 produces the result (257) as expected, but (due to absence of a device) obviously response on Slave-adress 02 is failing with a time-out.
For further problem-description see http://www.domoticz.com/forum/viewtopic.php?f=31&t=13592#p102901
From test runs, I can see that a time-out occurs.
A time-out-signal could be trigger to step-over to check for next slave-address, if I knew the layout of such time-out-signal for a Python-script with minimalmodbus ....
Searching the internet for an alternative, I see all kind of 'wonderful &elaborate' solutions to trap a time-out, but in the perspective of my simple script I am looking for something very basic (preferably a 'one-liner') to enable stepping out of the time-out to check the next slave-address 3, etc.
Looking at those solutions mentioned above, could perhaps the following setup with semi-code be a simple/basic solution? [for which I have been looking in direction of the C-function fread() ]
start of loop
start time-counter
read register from slave address x
N = number of characters received
if time-counter at value t, then
look if N > 0
if N == 0 then
x = x + 1
jump to start of loop
Any hint for a script using Python or MinimalModbus to perform the semi-code, compatible with the first script?

No working solution found/received in this Forum, but a pragmatic, simple remedy has been developed, tested and put in action, as described at http://www.domoticz.com/forum/viewtopic.php?f=14&t=5808&start=20#p113697
Due to the characteristics of it's data-protocol, the remedy is specific for kWh-meter type DDS238-1ZN, but the idea can possibly be applied for other, comparable kWh-meters with RS485-interface.

Related

Error: PySerial Communication with MKS647C Mass Flow Controller on Linux

I'm trying to communicate with the 647C Gas Delivery with PySerial and Linux. The manual is this.
Here's the code that I'm running:
import io
import time
SerialObj=serial.Serial('/dev/ttyUSB0', parity='O') #Odd parity
SerialObj.baudrate = 9600 # set Baud rate to 9600
SerialObj.bytesize = 8 # Number of data bits = 8
SerialObj.stopbits = 1 # Number of Stop bits = 1
SerialObj.timeout=10
if SerialObj.isOpen(): # make sure port is open
print(SerialObj)
print("CONNECTED! :)")
cmd="ID\r\n"
SerialObj.write(cmd.encode())
time.sleep(.2)
msg=SerialObj.read(128)
print(msg.decode("utf-8"))
However, I'm encountering the following error: "E2" which indicates according to the manual that Only one character has been sent instead of the expected 2 byte command.
Any ideas what might be causing this?

How to use a Sainsmart 8 Channel USB relay with 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)

SMBus write_i2c_block_data() command

So I started a project on a microcontroler that uses I2C communication a month ago with no knowledge about anything but python.
I have to talk to a peristaltic pump that uses ASCII strings for communication.
So my setup currently consists of a Raspberry Pi, the I2C bus, Arduino and the peristaltic pump. The Arduino is only used as a powersupply. I thought a good starting point would be just to try and turn the pumps LED on and off. The code for LED on is "L,1" and for LED off is "L,0". (The "" indicates that whats inside it is the absolute code). [a link] https://www.atlas-scientific.com/_files/_datasheets/_peristaltic/EZO_PMP_Datasheet.pdf
By using smbus.SMBus in python I sent the data through the command through write_i2c_block_data. Documentation of smbus gives the following: write_i2c_block_data(int addr,char cmd,long vals[]) however i dont understand what is meant with 'char cmd'. I couldnt put a string command in there and it only worked when I put an integer in there.
Here is the code:
import smbus
import time
bus = smbus.SMBus(1)
slave_address = 0x67
led_on = 'L,1'
led_off = 'L,0'
def string_to_charlist(a_string):
stringlist = list(a_string)
return stringlist
def string_to_intlist(a_string):
lst = string_to_charlist(a_string)
intlist = []
for i in range(len(lst)):
an_int = string_to_charlist(lst[i])
intlist.append(an_int)
return intlist
ledon_intlist = string_to_intlist(led_on)
ledoff_intlist = string_to_intlist(led_off)
# this will result in ledon_intlist = [76,44,49]
# this will result in ledon_int =list [76,44,48]
command1_on = ledon_intlist.pop(0)
command1_off = ledoff_intlist.pop(0)
for i in range(1):
time.sleep(0.5)
bus.write_i2c_block_data(slave_address, command1_on, ledon_intlist)
time.sleep(0.5)
bus.write_i2c_block_data(slave_address, command1_on, ledon_intlist)
After running this code through the raspberry Pi command prompt the pumps LED started blinking on the given time frame. Unfortunately it never stopped blinking and also didt show up when I searched for it using i2ctools command i2cdetect -y 1 I assume the pump's chip is in an infinite loop now.
My questions are:
1. How should one use the write_i2c_block_data() command and what argument does it take. Currently I figured that the 1st argument is the slave address, 2nd is the initial byte of the stream and the 3rd argument is the rest of the stream integer values to be sent.
2. What possibly could have gone wrong that the pump is stuck in an infinite loop and how do I fix it

OSC proper fader control algorithm

I am coding in python 2.7 to accommodate OSC communication from my ipad to my rasp pi using the 'touchOSC' app from the app store. (this is not for music or midi, but simply to access values to control stepper motors etc..) I am using windows to code and then I transfer the .py to my rasp pi (not the issue here). I set up the fader in touch OSC's layout software with the name, IP address that is in the code below. Also, I set the range value of the fader in the software that touchOSC has provided from 0 to 1.
The issue is that I am receiving only zeros when I invoke the fader on my ipad. What I wish to see is the values incrementing from 0 to 1 by floating values. I believe I would need to use some sort range function? This is the issue, How can specify that I want the fader to increment in the code below?
Right now the code is:
from OSC import OSCServer,OSCClient, OSCMessage
server = OSCServer( ("192.168.0.8", 8000) )
client = OSCClient()
def handle_timeout(self):
print ("I'm IDLE")
#This here is just to do something while the script recieves no information....
server.handle_timeout = types.MethodType(handle_timeout, server)
def fader_0(path, tags, args, source):
value=int(args[0]) # Value is the variable that will transform the input from the faders into whole numbers(easier to deal with)
print "Fader Value:", value
server.addMsgHandler("/1/fader1", fader)
My question regards the Def fader_0(path, tags, args, source)
When I input: value = int(args[0]), I receive a zero when I adjust the fader on my ipad via touch OSC. this is obvious; however, I wish to access the floating points in between 0 and 1 as the fader is incremented.
I assume the code would be: value = float(args[someLoop]) maybe something like value = float(args[range(0,1,.125)])
Thank you in advance.
The values coming from TouchOSC are floats between 0 and 1. In the fader_0 function, you currently transform the float from TouchOSC, args[0] to an int which rounds any number less than 1 down to 0, thus your program prints out zero every time.
You can easy see how this happens in the python interpreter:
In [2]: int(0.7)
Out[2]: 0
The solution is just to remove the int function from fader_0 like so:
def fader_0(path, tags, args, source):
value=args[0]
print "Fader Value:", value

Python - multithreading with sockets gives random results

I am really confused about my problem right now. I want to discover an open port over a list of hosts (or subnet).
So first let's show what I've done so far..
from multiprocessing.dummy import Pool as ThreadPool
from netaddr import IPNetwork as getAddrList
import socket, sys
this = sys.modules[__name__]
def threading(ip):
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.settimeout(this.timeout)
failCode = 0
try:
if sock.connect_ex((str(ip), this.port)) == 0:
#port open
this.count += 1
else:
#port closed/filtered
failCode = 1
pass
except Exception:
#host unreachable
failCode = 2
pass
finally:
sock.close()
#set thread num
threads = 64
#set socket timeout
this.timeout = 1
#set ip list
ipList = getAddrList('8.8.8.0/24')
#set port
this.port = 53
#set count
this.count = 0
#threading
Pool = ThreadPool(threads)
Pool.map_async(threading, ipList).get(9999999)
Pool.close()
Pool.join()
#result
print str(this.count)
The Script works fine without any error. But I'm struggling about what it prints out..
So if I want to scan for example the subnet 8.8.8.0/24 and discover port 53. I know the only server that has an open dns port is 8.8.8.8 (google-dns).
But when I run my script serveral times the print str(this.count) will randomly (as it seems to me..) return 0 or 1.
What I also know:
Scan only 8.8.8.8 prints always 1
Use only 1 thread for /24 prints always 1
Use 256 threads for /24 prints randomly 0 and 1
changing the timeout doesn't help
So it seems like it has to do with the threading option that causes lags on my computer. But note that my CPU usage is <10% and the network usage is <50%!
But there is also another thing I don't understand..
If print str(this.count) returns 0 I would normally think it is because the threads are in conflict with each other and the socket doesn't get a connection.. but that isn't true because if this.count equals 0, the failCode is set to 1 (on 8.8.8.8)! Which means the port is closed.. but that must be also a bug of my script. I cannot think that this is caused by a lag of the server.. it's google there are no lags..
So additionally we know:
output of 0 is because the server respond that the port is closed
and we are sure the port is definitely open
So I also think about that if I have many threads that run if sock.connect_ex((str(ip), this.port)) == 0: at the same time maybe the host 8.8.8.8 looks in the wrong answer value. Maybe it struggles and look in the response of 8.8.8.7. ..hope you know what I mean.
**max_socket_connections is set to 512 on my linux distribution
Anybody experienced with socket threading and can explain me the situation or give me an answer how to prevent this?
Solved:
.. look at the answer ..
The first mistake was:
But note that my CPU usage is <10% and the
network usage is <50%!
Actually it was a 400 % network usage. I expected bits instead of bytes.
Very embrassing..
And the second mistake was:
and we are sure the port is definitely open
Google does actually block the port after ~5 attempts in a short period of time. They release the restriction after a few seconds or minutes.

Categories