Unable to send/receive data via HC-12/UART in Python - python

I've written some code to communicate between two Raspberry Pi's, using identical HC-12 433Mhz transceivers. I was able to successfully echo between the two Pi's using a direct serial connection and echo/cat, however am unable to replicate this using HC-12s, which theoretically work by a similar principal. I'm using the port ttyAMA0 on both for this example, but ttyS0 is also available and have tried every combination of these ports.
The following code is common to both the sending and receiving, just writing once for sake of brevity:
import serial
import time
ser = serial.Serial(
port = "/dev/ttyAMA0",
baudrate = 9600,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE,
bytesize = serial.EIGHTBITS
)
print("Serial status: " + str(ser.isOpen()))
This is the sending program:
while True:
print("Sending...")
ser.write("hello\n".encode())
time.sleep(1)
And the receiving program:
while True:
print("Receiving...")
data = ser.readlines()
print(data.decode())
The sending program simply loops as expected, but the receiver prints "Receiving...", and then nothing.
When I keyboard interrupt the receiving program at that point, it says it is currently up to data = ser.readlines().
Any help would be much appreciated - I've spent the better part of the last week trawling and exhausting forums and READMEs to no avail, and this is literally my last option. Am close to insanity on this one!

The pyserial readlines() function relies on the timeout parameter to know when end-of-file is reached - this is warned about in the doco. So with no timeout, the end never occurs, so it keeps buffering all lines read forever.
So you can just add a timeout to the serial port open, and your existing code will begin to work.
ser = serial.Serial(
port = "/dev/ttyAMA0",
baudrate = 9600,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE,
bytesize = serial.EIGHTBITS,
timeout = 2 # seconds # <-- HERE
)
A better approach might be to use readline() (note singular, no 's'), for each line in turn:
print( "Receiving..." )
while True:
try:
data = ser.readline()
print( data.decode() )
# TODO - something with data
except:
print( "Error reading from port" )
break
As that will allow the code to act on the input line-by-line.

Use Serial.read_until method. The default termination character is \n.
For example,
data = ser.read_until()
print(data)

Related

using PySerial don't work until port is opened with minicom

I developed a plugin for Domoticz on a RPi3B+. This plugin is in Python.
I want to send commands to a Arduino board using a USB serial port.
The plugin opens the serial port, sends a command and closes the serial port. It works well except after a reboot.
After a reboot, the port is open, and the command seems to be sent to the Arduino, but it doesn't understand it, just as if the baud rate were wrong. Arduino's Rx LED is blinking.
If I open the serial in parallel using minicom and exit minicom without resetting the parameters, then the plugin starts working properly.
Here's my code:
serialCmd = "gpio sh" + str( shutterId ) + "_" + order +" on for " + str( PULSE_DURATION_MS ) + "\r"
Domoticz.Debug( "Serial command : " + serialCmd )
# open serial port
try:
Domoticz.Debug( "Opening serial port : " + Parameters["SerialPort"] )
serialPort = serial.Serial( port = Parameters["SerialPort"],
baudrate = 115200,
bytesize = serial.EIGHTBITS,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE,
timeout = 1,
xonxoff = False,
rtscts = False,
dsrdtr = False )
except:
serialPort = None
if serialPort:
serialPort.write( serialCmd.encode( 'utf-8' ) )
serialPort.close()
serialPort = None
The serial port is /dev/ttyUSB0.
If I try to use exclusive = True in serial.Serial(...), it fails to open the port, as if the port were already open.
Another strange thing: when the Arduino board reboots, it sends some information to the serial port.
I can't read it with the plugin in Python with PySerial, but I can read it with minicom.
If I close minicom with resetting the parameters, each time I open minicom this information is read by minicom (without resetting the Arduino board) as if the input buffer has never been read, and the Python plugin still doesn't work.
How do I solve the problem?
This issue was because of Arduino nano schematic where DTR signal is connected to the reset signal which makes the Arduino to reset each time the serial port is open. This post made me search the right thing on the internet. That's why opening minicom and closing without resetting the port makes it work...
When the serial is not used anymore by a process the DTR line is reset so next time a process open the serial port the DTR is driven which makes the Arduino reboot.
A way to fix it is to modify the board or two disable the DTR handling. This can be done by dsiable the HUPCL bit of the terminal lib.
Some people manage to fix it with pySerial but it does not work for me so I had to do as below...found here and here.
import serial
import termios
port = '/dev/ttysUSB0'
f = open(port)
attrs = termios.tcgetattr(f)
attrs[2] = attrs[2] & ~termios.HUPCL
termios.tcsetattr(f, termios.TCSAFLUSH, attrs)
f.close()
se = serial.Serial()
se.baudrate = 115200
se.port = port
se.open()

Python PySerial changing settings on timeout

I have a question about Python and PySerial.
On my Raspberry Pi i want to read a serial port from a device. I got 2 types of devices i want to read from. They both got different settings:
ser = serial.Serial()
ser.baudrate = 9600
ser.bytesize=serial.SEVENBITS
ser.parity=serial.PARITY_EVEN
ser.stopbits=serial.STOPBITS_ONE
ser.xonxoff=0
ser.rtscts=0
ser.timeout=20
ser.port="/dev/ttyUSB0
and:
ser = serial.Serial()
ser.baudrate = 115200
ser.bytesize=serial.EIGHTBITS
ser.parity=serial.PARITY_NONE
ser.stopbits=serial.STOPBITS_ONE
ser.xonxoff=1
ser.rtscts=0
ser.timeout=20
ser.port="/dev/ttyUSB0
This is the code to read the serial port:
try:
ser.open()
except:
sys.exit ("Error opening %s." % ser.name)
t_count = 0
while t_count < 20:
t_line = ''
try:
t_raw = ser.readline()
except serial.SerialException:
sys.exit ("Serial port %s could not be read." % ser.name )
t_str = str(t_raw)
t_line = t_str.strip()
print (t_line)
t_count += 1
try:
ser.close()
except:
sys.exit ("Oops %s. Program aborted. Could not close serial port." % ser.name )
So when i connect to a device with 115200 but the device runs 9600, i get timeouts ofcourse. But in my program it will just time out twenty times (times the for loop will run), and no exception to be thrown. Not even just before the program exits after looping 20 times. No error message nothing.
What i want to achieve is the following, i want to make the python script self detecting what he is connected to. When the readline() times out 20 times it should change settings. (by running another function or something).
I cant check if the readline returns something empty, because there are empty lines in the serial message.
Is there any way to get the right exception? Or any other smart way to solve this?
(By the way, i am sure the settings work. As i tested them both and run fine.)
Thanks in advance.
Cheers!
Easiest way would be to create a handshake function,
send something with one serial handler that you know the device will respond to correctly. If the answer is jibberish then change the handler and try the other one until you succceed.

Raspberry Pi is stuck when no char is received on the serial port

I am using an XBee module connected to my RPI, serial communication is established between both, the problem is my code gets stuck if there is no data present by the XBee, is there a away to solve this, I tried timeout but wasn't successful.
code:
ser = serial.Serial (
port = "/dev/ttyAMAO",
baudrate = 9600,
parity = serial.PARITY_NONE,
stopbits = serial,STOPBITS_ONE,
bytesize = serial.EIGHTBITS,
timeout = 0
)
ser = serial.Serial("/dev/ttyAMAO")
for c in ser.read():
l.append(c)
ser.read()
is a blocking call it is probably better to check if there is anything there to read first
if ser.inWaiting(): #only true if there is data waiting to be read
for c in ser.read():
....
or if you want your serial thread to run in parallel to your main program (ie. dont block user interface ever at all..) you should maybe look into something like twisted or asyncio
although typically with serial you are working with some device that wants 2-way communication, usually initiated with a query to the serial device, and you do want to actually block until you get a response. I will usually make a class to handle this for me
class MySerial:
def __init__(self,port,baudrate):
self.ser = serial.Serial(port,baudrate)
def query(cmd,terminal_char="\r"):
self.ser.write(cmd)
return ''.join(iter(ser.read,terminal_char))
s = MySerial("COM9",11200)
result = s.query("get -temp\r")
print result
this will accumulate an entire response until the specified terminal character is recieved

python pyserial writing more than once to serial port

I am writing and reading from the same serial port. Here is what problem is:
When I write to serial port one after another, like in the code below:
import serial
import array
rec = serial.Serial()
trans = serial.Serial()
rec.port = "/dev/ttyUSB0"
rec.baudrate = 9600
rec.timeout = 0.5
rec.open()
trans.port = "/dev/ttyUSB0"
trans.baudrate = 9600
trans.timeout = 0.5
trans.open()
if trans.isOpen():
trans.write("\xFE\x1D\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE2")
trans.write("\xFE\x1D\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE3")
while rec.isOpen():
response = rec.readline()
etc. etc. etc
As long as while loop turns, I keep reading only the first sent string. Also I tried to send data just after the while loop: (I did not delete the if statement)
while rec.isOpen():
trans.write("\x00\x00")
response = rec.readline()
What I read from the serial port is still:
"\xFE\x1D\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xE2"
I cannot send any other data other than the first one. Why is it happening? What should I do?
note: Also in the while loop I tried to read twice:
response = rec.readline()
response = rec.readline()
This time, when I print the array that I read, it prints [] which means I cannot read anything.
Regards
EDIT: Now I used two different usb ports ttyUSB0 and ttyUSB1 to check if my problem is related to that situation. But seems like nothing is wrong with writing and reading from the same port.
The issue can be port misconfiguration. You have to set proper values
ser = serial.Serial(
port='/dev/ttyUSB0',
baudrate=9600,
parity=???,
stopbits=???,
bytesize=???)
I did not see newlines, so you should try with rec.read(bytes) or finish lines with \0d\0a or something.
See http://pyserial.sourceforge.net/shortintro.html#readline

Serial communication. Sending DTR in the right way?

I'm dealing with a gm29 by Sony Ericsson.
The datasheet says that plugging the power is not sufficient to switch on the modem. It says:
activate the RS232 control line DTR, high for > 0.2s.
I'm writing some tests in python, but:
#!/usr/bin/env python
import serial
from time import sleep
socket = serial.Serial('/dev/ttyS0',
baudrate=9600,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=1,
xonxoff=0,
rtscts=0
)
socket.setDTR(True)
sleep(3)
socket.setDTR(False)
try:
while True:
socket.write('AT'+chr(13));
sleep(1)
print "Reading"
print socket.readlines()
except:
socket.close()
does not works... I there a way to get DTR high in other ways? Let's say minicom or some other stuff? Or, easily, am I missing something?
Thanks in advance.
Ok, that was driving me mad. The clue is that the power supplier was "broken", or better, it works good testing with a tester, but plugging on the modem some wires moves and does not carry voltage...
Thanks anyway for the answer, marked as correct 'couse it was :D
There are several things that occur to me here.
1) the spec says that DTR is active low, so you may need to swap the true and false values to setDTR(), depending on who is confused here.
2) You are setting DTR to false after you wake the modem. This tells the modem to go offline, and ignore all input till it goes true again. Try the following:
import serial
from time import sleep
conn = serial.Serial('/dev/ttyS0',
baudrate=9600,
bytesize=serial.EIGHTBITS,
parity=serial.PARITY_NONE,
stopbits=serial.STOPBITS_ONE,
timeout=1,
xonxoff=0,
rtscts=0
)
# Wake Modem
conn.setDTR(True)
sleep(3)
conn.setDTR(False)
sleep(5)
# Start talking
conn.setDTR(True)
try:
while True:
conn.write('AT'+chr(13));
print conn.readline() # readlines() will probably never return.
finally:
conn.close()
3) socket is probably a bad name for your serial connection.

Categories

Resources