Serial connection problem with Pyserial after reboot - python

I have a Python script to communicate with a measuring instrument over a RS232 serial port.
Everything works fine, but every time I turn on the PC (Windows 10) the communication doesn't work in the beginning. I have to open a serial terminal (for example hterm) press the "connect" and "disconnect" button. After that the Python script works as expected, reading and writing to and from the instrument is no problem.
Here is a short example of the code:
import serial, time
ser = serial.Serial(port='COM6', baudrate=19200, bytesize=8, parity=serial.PARITY_NONE, stopbits=1, timeout=0, xonxoff=False, rtscts=False, dsrdtr=False)
time.sleep(1)
print(ser.isOpen()) #output: true
ser.write(b'READ:CH1\r\n')
time.sleep(1)
print("read:" + ser.read(18).decode('utf-8'))
ser.close()
print(ser.isOpen()) #output: false
The instrument doesn't receive the data "READ:CH1" or any other command. Because of this there isn't any transmitted data to the PC via ser.read().
I tried every possibility with hardware handshakes and very long sleep times. I guess there's a problem between Windows and Pyserial. In Python the port is open, but Windows doesn't send the data. Do you have any ideas what I could do?
Thanks for your help.
Best regards
Edit with solution:
Instead of or additional to "Serial.flushInput()" and "Serial.flushOutput()" you need "Serial.reset_input_buffer()" and "Serial.reset_output_buffer()".

If you are using a third-party tool and then the script works fine, then I think there is some garbage data present in the buffers of either side, flushing the serial port on the hardware device and on the python script too might work and verify the data being received on the hardware device it is possible garbage is being appended on the commands, also try to use some header bits which keep errors at bay in this kind of communication.
Use some serial port sniffer to verify what is being sent, like this

Look at https://github.com/pyserial/pyserial/issues/329
https://github.com/pyserial/pyserial/issues/329#issuecomment-400852426
https://github.com/pyserial/pyserial/issues/329#issuecomment-503059537
Do you see this?
Another issue that might be related:
https://github.com/pyserial/pyserial/issues/485
Another thing you could try is to open and close the port first.
That's the same thing you are doing with hterm for
ser = serial.Serial()
time.sleep(1)
print(ser.isOpen()) #output: true
ser.close()
ser = serial.Serial()
time.sleep(1)
print(ser.isOpen()) #output: true
...
Does this work?

Related

How can I avoid flipping between ttyUSB0 and ttyUSB1 when reconnecting to USB port under Python using pySerial?

I have a serial Python program, Linux environment (Raspbian / Raspberry Pi), that uses a serial port via a USB-to-serial adapter. I need to handle a situation when the user unplugs the USB adapter and then reinsert it.
The problem is that, on reconnect, the ttyUSB0 becomes ttyUSB1 and so the port is no longer found. However, if I stop the Python program (keyboard interrupt) and again unplug and reinsert the USB adapter, then the port goes back to ttyUSB0 (and so I can start over again). This can happen only when the Python program is stopped.
I tested the program in a flip-flop mode (and it seems to be working) in order to use ttyUSB1 when ttyUSB0 is no longer found and then vice versa, use ttyUSB0 back in case ttyUSB1 is no longer found, etc., but this looks like a weird solution to me.
Is there a better way to force pySerial to "forget" it has ever been connected to ttyUSB0 in case of error and release the current port to the system while the program is still running?
Here is a working flip-flop test program:
import serial
import time
p = "/dev/ttyUSB0"
while True:
error_flag = False
try:
s = serial.Serial(port=p, baudrate=9600, bytesize=8, parity="N", stopbits=1, timeout=None, xonxoff=False, rtscts=False, write_timeout=None, dsrdtr=False, inter_byte_timeout=None)
except Exception as e:
error_flag = True
if "ttyUSB0" in str(e):
p = "/dev/ttyUSB1"
print ("port is now", p)
elif "ttyUSB1" in str(e):
p = "/dev/ttyUSB0"
print ("port is now", p)
else:
print (e) # none of the above
# if not error_flag, do whatever, etc.
time.sleep(1)
You could try creating a udev rule that would create a symbolic link to that USB device and then you would be able to use something like /dev/myUSB that would always stay the same for that specific USB device.
First, you will need to find some identifying information for the USB drive. Typing in lsusb should display some information that looks like:
Bus 001 Device 004: ID 0403:6001 Future Technology Devices International
In this example 0403 is the Vendor Id and 6001 is the Product Id.
Create a file named 99_usbdevice.rules (I don't think the name matters, just the directory):
sudo nano /etc/udev/rules.d/99_usbdevices.rules
Note that the directory above may be specific to Raspbian.
Copy/paste the line below into the file and save it:
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", SYMLINK+="myUSB"
Restart your Raspberry Pi or unplug the USB and reinsert it. There should now be a /dev/myUSB entry that you can use the same way you would the ttyUSB# entry.
If the opened port file is closed when unplugged (or on error handler), then the port name will not change on subsequent connection of the USB device. If left open then it will create a different name each time.
Do not forget to close the file descriptor of /dev/ttyUSB0 as soon as you detect that the user unplugged the USB adapter (read or write with error), and before the reinsertion.
If you properly close the device, the ttyUSB1 device will never appear. On the other hand, you can see in some cases also ttyUSB2, ttyUSB3, and so on, if all the previous ttyUSBx's are blocked because not closed.

Modbus Slave don`t respond

I am trying to use a Raspberry Pi 3B (run Ubuntu Mate 16.04 operating system)as a Master to read values from an electric energy meter which supports Modbus-RTU protocol.
I used a RS232/USB adapter and a RS485/RS232 adapter to link the meter and the USB port on the Raspberry Pi. I have tried the modbus_tk 0.5.7 and MinimalModbus to implement the communication under Modbus-RTU protocol.
When I use modbus_tk 0.5.7 and run the following code:
import sys import serial
#add logging capability import logging import modbus_tk import modbus_tk.defines as cst import modbus_tk.modbus_rtu as modbus_rtu
logger = modbus_tk.utils.create_logger("console")
if __name__ == "__main__":
try:
#Connect to the slave
master = modbus_rtu.RtuMaster(serial.Serial(port="/dev/ttyUSB0", baudrate=9600, bytesize=8, parity='N', stopbits=1, xonxoff=0))
master.set_timeout(5.0) #Change the timeout value/Defines a timeout on the MAC layer
master.set_verbose(True) #print some more log prints for debug purpose
logger.info("connected")
logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 49))
except modbus_tk.modbus.ModbusError, e:
logger.error("%s- Code=%d" % (e, e.get_exception_code()))
The parameters such as port, baudrate, bytesize,parity,and stopbits were set correctly, but it always returns this:
2017-08-10 19:24:34,282 INFO modbus_rtu.__init__ MainThread RtuMaster /dev/ttyUSB0 is opened
2017-08-10 19:24:34,283 INFO rtumaster_example.<module> MainThread connected
2017-08-10 19:24:34,284 DEBUG modbus.execute MainThread -> 1-3-0-0-0-49-132-30
2017-08-10 19:24:39,291 DEBUG modbus.execute MainThread <-
Traceback (most recent call last):
File "rtumaster_example.py", line 34, in <module>
logger.info(master.execute(1, cst.READ_HOLDING_REGISTERS, 0, 49))
File "build/bdist.linux-x86_64/egg/modbus_tk/utils.py", line 39, in new
modbus_tk.exceptions.ModbusInvalidResponseError: Response length is invalid 0
When I use MinimalModbus and run the following code:
#!/usr/bin/env python
import minimalmodbus
instrument.serial.port='/dev/ttyUSB0' # this is the serial port name
instrument.serial.baudrate = 9600 # Baud
instrument.serial.bytesize = 8
instrument.serial.parity = serial.PARITY_NONE
instrument.serial.stopbits = 1
instrument.serial.timeout = 0.05 # seconds
#instrument.address # this is the slave address number
instrument.mode = minimalmodbus.MODE_RTU # rtu or ascii mode
instrument = minimalmodbus.Instrument('/dev/ttyUSB0', 1) # port name, slave address (in decimal)
energy = instrument.read_register(10, 1) # Registernumber, number of decimals
print energy
It always returns this:
raise IOError('No communication with the instrument (no answer)')
IOError: No communication with the instrument (no answer)
And then I use the same serial transmission line to link the meter and the laptop, and use a debugging tool running on Windows XP, which was developed by the manufacturer of the meter. The debugging tool sends the same Request (1-3-0-0-0-49-132-30) as before, but the debugging tool can get correct Responses. (Maybe it's because it ignored some incorrect Responses and keep on send Requests regularly) And it can represent that the Request message is correct and the connection of serial transmission has no problem.
I also used the CuteCom(a graphical serial terminal) and the RS232/USB adapter to confirm that the USB port can send and receive correctly. It is also useless to add a resistor between two RS485 lines
I have tried many times, but the Raspberry Pi never gets a Response, and always returns the same error information. I also try to run the same code on a Ubuntu virtual machine, it returned the same message as above and never get a Respond.
I am new to Modbus and serial communication, so any help would be appreciated.
I have solved my problem by using a more expensive USBtoRS485 connector.
The problem took me a lot of time to try different library and different code.
It turns out that the adapter "QinHeng Electronics HL-340 usb-serial" that I bought works well on windows but does not work with Linux. It can achieve send and receive message on Linux, but it just can`t support Modbus communication.
So I suggest you buy a more expensive connector, and it may save your a lot of time and energy.
That`s all, thank you!
I was having the same problem. After use mbpoll tool I have realized the parity was wrong. Just updated to parity.EVEN and it is ok now.
Try connecting RS232-end of a cable into PC with serial port (if you have it ofc) to ensure usb/rs232 works
If you have oscilloscope (like fluke), you can observe RS485 line (it is not difficult in fact) to understand where is the issue.
It happened to me to find out that serial parameters (like baud rate) can be configured it 2 places for Windows - when you open the port and in driver configuration.
Try equalize the potential for all devices. I know rs485 is supposed to be immune to it but rs232 not
You have a too short time out on your Pi, try increasing it to 100 ms and it should work.
EDIT: As requested in the comment below I'm giving some more details on my suggestion. Based on the details given on the question and subsequent answers and comments the hardware setup seemed to be correct. I've seen this happen from time to time: when you set a very short timeout on one side the link suddenly stops working (it makes sense too, a timeout error occurs when you have not received an answer within the timespan you determine with the timeout parameter), and it goes back alive as soon as you increase it again. I've also noticed this effect is hardware dependent, some devices are able to answer faster than others

Inconsistent Xbee LED blinking while controlling remotely

I am trying to blink an LED using a router AT XBee which is controlled by a coordinator API Xbee. The coordinator Xbee is connected to a Raspberry Pi which runs the following program. The LED blinks consistently for exactly 4 cycles but after that, it blinks inconsistently (gets stuck and doesnt light and then blinks super fast, again gets stuck). Sometimes, even after the program is stopped, the LED blinks after few seconds. What is causing the delay and inconsistency, I failed to figure out. I'd appreciate any pointers. Thanks.
Baud Rate: 9600
from xbee import XBee, ZigBee
import serial
import time
ser = serial.Serial('/dev/ttyUSB0', 9600)
xbee = ZigBee(ser)
while True:
try:
xbee.send('remote_at',
frame_id='A',
dest_addr_long='\x00\x00\x00\x00\x00\x00\xFF\xFF',
dest_addr='\xFF\xFE',
options='\x02',
command='P2',
parameter='\x05')
time.sleep(1)
xbee.send('remote_at',
frame_id='A',
dest_addr_long='\x00\x00\x00\x00\x00\x00\xFF\xFF',
dest_addr='\xFF\xFE',
options='\x02',
command='P2',
parameter='\x04')
time.sleep(1)
except KeyboardInterrupt:
break
xbee.send('remote_at',
frame_id='A',
dest_addr_long='\x00\x00\x00\x00\x00\x00\xFF\xFF',
dest_addr='\xFF\xFE',
options='\x02',
command='P2',
parameter='\x04')
ser.close()
To start with, try switching from broadcast to unicast packets by setting dest_addr_long (and not setting dest_addr at all). My recollection is that broadcast packets are sent a total of three times to ensure they're received on the remote device. There probably isn't much traffic on your network, but I think you'll see more reliable timing with unicast.
Second, try switching to 115200 bps. It will streamline communication between the Raspberry Pi and coordinator, and eliminate possible serial buffering delays.

Echo Program in between Arduino and Python

I want to send some data to an Arduino through pyserial in Python. All I want to the Arduino to do is read the variable length string data from the serial port, and write it back so that Python can read it. Since I've been unable to do that, the code below only has Python sending on character. Here's the Python code:
import serial
import sys
import pywapi
import time
def main():
ser = serial.Serial(3, 9600, timeout=1)
print "Conn established"
print "Sending: %s" % "z".__repr__()
print ser.write('z'.encode("ascii"))
time.sleep(2)
print "Received: %s" % ser.read(10).__repr__()
ser.close()
Here's the Arduino code:
void setup(){
analogReference(DEFAULT);
Serial.begin(9600);
}
void loop(){
if(Serial.available() > 0)
Serial.println("x");
while(Serial.available() > 0){
Serial.print(Serial.read(), BYTE);
}
}
The output:
Conn established
Sending: 'z'
1
Received: ''
I know the code for the Arduino works because it works when data is being sent from the Arduino terminal. However, the moment I try to send anything from Python it fails. I've been struggling with this all day. Any help would be greatly appreciated.
Try increasing or removing the timeout, and set read's size to 1. You may also want to increase the sleep delay, or even implement a simple read loop.
Something like:
try:
while True:
data = ser.read(1).__repr__()
if data:
print "Received: %s." % data
else:
print "Looping."
except KeyboardInterrupt:
print "Done."
except:
raise
finally:
ser.close()
print "Closed port."
Then just use ctrl-c to stop it.
I would recommend verifying the two parts independently, using a separate serial port and serial comms software on the PC.
E.g. if your PC has two serial ports, then use a null-modem (loopback) cable to connect them. Or use com0com to make a pair of linked virtual serial ports. Run your Python software on one serial port, and a terminal program (Hyperterminal or RealTerm) on the other serial port. Manually verify the Python program's operation that way.
Then, connect your PC directly to the Arduino as usual, and use the terminal software to manually verify the Arduino software operation.
That process will allow you to narrow down the problem. Once you've verified them both, they should work well together.
Serial Port Monitor
Another method you can use is software that hooks into the PC's serial port driver, and allows you to monitor traffic on the serial port. I've used the Free Serial Port Monitor software from HHD Software in the past, and it worked well for our purposes. It allows you to monitor any of the PC's serial ports, and shows you a log (hex and text) of the serial data going over the port in both directions.
Do you need to flush the sent character out of any held serial buffer?
It may be that your character is not actually leaving the COM port and arriving at the Arduino. When you test this with the Arduino Terminal (I assume you mean the UI terminal in the development environment) you are actually sending your string + a carriage return I think, not just the character. (i.e. do you hit return after you type 'z' in your test?)
Try ser.flush() or perhaps also send a \r character. From your testing the Arduino works just fine, it's the python program that doesn't seem to be sending anything.
The reason you may have to send twice is that, if you're connecting via the USB, the first serial connection will reset the Arduino.

Pyserial problem with Arduino - works with the Python shell but not in a program

All right, so I am positive my Arduino circuit is correct and the code for it. I know this because when I use the serial monitor built into the Arduino IDE and send 'H' an LED lights up, when I send 'L' that LED turns off.
Now I made a Python program
import serial
ser = serial.Serial("COM4",9600)
ser.write("H")
When I run the code the LED blinks on for a second then goes back off.
However when I do each of these lines separately in the shell it works just like it is supposed to.
Any ideas?
When you open the serial port, this causes the Arduino to reset. Since the Arduino takes some time to bootup, all the input goes to the bitbucket (or probably to the bootloader which does god knows what with it). If you insert a sleep, you wait for the Arduino to come up so your serial code. This is why it works interactively; you were waiting the 1.5 seconds needed for the software to come up.
I confirmed that opening the serial port resets my Arduino Uno; I flashed a program which will blink the LED from the setup() routine -- calling open("/dev/ttyACM0") was sufficient to trigger the reset. This is IMHO a confusing and undocumented wrinkle in the serial support.
I had the same problem and it works if I add a delay of about 2 seconds from opening the serial connection to writing on it, 1 second was not enough.
Just to make it a bit more clear I'll modify the code so everyone can see what needs to be added!
import serial
import time
ser = serial.Serial("COM4",9600)
time.sleep(3)
ser.write("H")
Adding in a sleep statment helps to let the serial open up without any problems!
Assuming you are using an Arduino Uno
The USB port and the Uno serial bus exposed on pins 1 and 0 share the same RX/TX lines. I suggest getting a USB to TTL adapter like the one here so that you can communicate to the Arduino without using the USB port. The Arduino IDE has its own method for disengaging from the USB driver such that a virtual serial port can be created. Have your Ardunio use SoftwareSerial instead.
Here is an example I found on the internet where somebody had clashing bus issues.

Categories