PySerial not able to read LoRa E5 Mini responses - python

When I try to read data off of a microcontroller using PySerial, I get no response with the following code (a LoRa-E5 mini is atttached to my UART COM5 Port)
import serial
import time
print("serial test")
ser = serial.Serial()
ser.baudrate = 9600
ser.port = 'COM4'
ser.open()
print(ser.is_open)
ser.write("test".encode())
print("data received")
time.sleep(1)
numlines = 0
while True:
try:
ser_bytes = ser.readline()
decoded_bytes = float(ser_bytes[0:len(ser_bytes)-2].decode("utf-8"))
print(decoded_bytes)
except:
print("Keyboard Interrupt")
break
ser.close()
I just need to be able to read the data off of the LoRa E5 mini.
What could be done to remedy my probem?

Could be problem be that you read from the serial port line by line? Does your application in LoRa-E5 serial sends a line ending with \n?
You do not send line endings characters, and you open the serial port without timeout, which will result in the readline command blocking forever.
See readline doc.
readline() reads up to one line, including the \n at the end. Be careful when using readline(). Do specify a timeout when opening the serial port otherwise it could block forever if no newline character is received. If the \n is missing in the return value, it returned on timeout.
Default serial port parameters class serial.Serial
init(port=None, baudrate=9600, bytesize=EIGHTBITS, parity=PARITY_NONE, stopbits=STOPBITS_ONE, timeout=None, xonxoff=False, rtscts=False, write_timeout=None, dsrdtr=False, inter_byte_timeout=None, exclusive=None)
timeout = None: wait forever / until requested number of bytes are received

What you're not getting is how the LoRa-E5 is working: its AT firmware doesn't just spit out whatever it receives, but uses AT commands, and outputs hex-encoded strings. In the case of LoRa P2P, aka in the LoRa-E5 AT firmware "Test Mode", when the module receives a string it outputs +TEST: RX , followed by hex-encoded bytes. For instance, if you sent 012345, the incoming string will be +TEST: RX 303132333435.
So you need to take the hex part, decode it and THEN do your decoded_bytes = float(...).
And because it is AT firmware, sending meaningless strings like "test" won't do anything (especially when not terminated by CR/LF). If you want to make sure it is alive, send AT\r\n. You should at least then get a response: AT+OK\r\n.

Related

Establishing communication with device through RS232 using PySerial or PyVisa

Trying to communicate with a McPherson 747 Device Controller for the Filter Wheel on a lab instrument. It is connected to the lab computer through RS232 to usb. I've connected and communicated with other devices through serial before.
It seems that for a read request, I need to send an enquiry and header and then the Device will acknowledge and send data.
The manual for the Device says that all data is exchanged in ASCII format. Though there are also a few tables in the manual that has inputs in Hex ASCII code. The manual says that a three-byte enquiry is Hex 4E2105 or text N!< ENQ >. Acknowledge is Hex 4E2106 or text N!< ACK > . The header is a mix of several Hex ASCII inputs for reading and writing based on what information the user wants.
The issue I am having is when I code the serial information and send the enquire input, it always sends an error due to communication with the device. I am unfamiliar with Hex. Do I need to be sending the entire enquire, headeras one input, or can I send them separately? I can also try to set up communication with PyVisa but I've gotten similar errors.
Through serial:
import codecs
import serial
try:
ser = serial.Serial(port='COM1',
baudrate = 9600,
timeout = None,
xonxoff = False,
parity = serial.PARITY_NONE,
stopbits = serial.STOPBITS_ONE,
bytesize = serial.EIGHTBITS,
)
ser.close()
string = '4E2105'
enquire = codecs.decode(string,'hex')
ser.open()
ser.write(enquire); #ascii intput for pressing enter on keyboard
ser.read_until(size=None) #reads out feedback until no data is left
ser.close()
msg = f"Program communication initialized"
print(msg)
except Exception as ex:
msg = f"Error: {ex}"
print(msg)
Error: could not open port 'COM1': FileNotFoundError(2, 'The system cannot find the file specified.', None, 2)
Through Pyvisa:
import pyvisa
import serial
import codecs
string = '4E2105'
enquire = codecs.decode(string,'hex')
rm = pyvisa.ResourceManager()
rm.list_resources()
# ('ASRL1::INSTR', 'ASRL2::INSTR', 'GPIB0::12::INSTR')
inst = rm.open_resource('asrl1::instr')
inst.write_termination='\r'
inst.read_termination='\r'
inst.baud_rate = 9600
inst.data_bits = 8
inst.parity = visa.constants.Parity.none
inst.flow_control = visa.constants.VI_ASRL_FLOW_NONE
inst.write(enquire)
print(inst.query("N!<ENQ>"))
rm.close()
VisaIOError: VI_ERROR_RSRC_NFOUND (-1073807343): Insufficient location information or the requested device or resource is not present in the system.

pyserial write() writes only the first few bytes

I'm performing reads and writes with pyserial to a serial terminal running on a decawave DWM1001C. The serial connection goes from my Linux laptop to the device over a microUSB cable which also powers the device. The DWM1001C offers the serial connection internally on the chip. The connection is opened at the baudrate of 115200 with 8N1, as specified by the DWN1001C reference manual.
The problem I have is that when I call pyserial's write() on the serial device, I can only write a maximum of 7 characters per write call, sometimes as few as 4. Here's a minimal code to reproduce the issue:
import serial
import sys
import time
dev = "/dev/ttyACM0"
baudrate = 115200
try:
ser = serial.Serial(dev, baudrate, timeout=3)
except Exception as e:
print("Failed opening device: " + dev + ": " + str(e))
sys.exit(1)
try:
# Two CR's are required to open the terminal as the device default to "API" mode
ser.write(b"\r\r")
ser.flush()
time.sleep(2)
# only letters up to f are sent
ser.write(b"abcdefg hijklmn opqrstu vwxyz\n")
ser.flush()
time.sleep(2)
except Exception as e:
print("Failed writing instructions to device: " + str(e))
sys.exit(1)
while True:
try:
line = ser.readline()
if line == None:
continue
except Exception as e:
print("Failed reading from serial: " + str(e))
sys.exit(1)
s = line.decode("utf-8")
print("got data: " + s)
By using the readline() call and by monitoring the serial terminal over minicom, I have verified that only the 4-7 first characters appear in the terminal. Minicom can be used to interact with the serial terminal normally, so I assume minicom does something under the hood that this pyserial code doesn't.
I have a hacky workaround: Every time I perform writes, I split the input data into 4 character chunks and send them one-by-one over write(), then sleep for 0.1 seconds. This seems to work, but is likely about as platform specific as it can be.
The pyserial constructor takes a few different flow control flags and I tried setting each of them on independently. They are:
xonxoff=True
rtscts=True
dsrdtr=True
They didn't seem to do anything. I also tried lowering the baudrate to 57600 to no effect.
What should I read on to understand what's going on?

Python pySerial on Raspberry Pi Zero W: detecting a disconnected serial device

In my project, there's a Raspberry Pi Zero W, linked with Arduino Pro Micro over the serial TX/RX lines (through 5 - 3.3V logic level converter). At power-on, the Arduino starts sending "are you there" command over the serial, until Pi has finished booting and launched a python program that listens on serial port, and it replies with "yes". Then Arduino proceeds with sending regular updates with sensor data.
For some reason not yet understood, the Arduino can disconnect and reboot, while the Pi is still running. And, for some reason, the connection on the Python end is severed in a way that does not raise exception if connection.in_waiting is read.
import serial
import time
ser = serial.Serial('/dev/serial0')
ser.baudrate = 9600
cmd = b''
while True:
time.sleep(1)
print(ser.is_open)
while ser.in_waiting:
ch = ser.read()
if ch == b'\n':
print('New command:', cmd)
cmd = b''
ser.write(b'OK\n')
continue
else:
cmd = cmd + ch
I have tested with this simple code, but in my tests, if I disconnect the Arduino and connect it back, the ser.is_open never is False, and the old connection works just fine also with reconnected Arduino. So, unfortunately I can not exactly replicate the loss of connection scenario when the data stop coming after disconnection. However, in order to investigate further, I'd like to add some monitoring code that would log serial disconnection incidents in file. But is there a way? If connection.is_open is always true, even without Arduino connected, then how one can detect if there is no connection?
The port remains open if there's a disconnection so your example code will loop continuously.
import serial
def connect(port):
return serial.Serial(port, baudrate=9600, timeout=1)
ser = connect('/dev/serial0')
buf = bytearray()
while True:
i = buf.find(b'\n')
if i >= 0:
buf = buf[i + 1:]
ser.write('OK\n')
num_bytes = max(1, min(1024, ser.in_waiting))
data = ser.read(num_bytes)
if data:
buf.extend(data)
else:
# no data to read aka disconnection has occurred
ser = connect('/dev/serial0')
if ser.isOpen():
continue
else:
# could not reconnect
break

How to read the response from serial port

I'm new with the python language
I have a uart bluetooth dongle, I wrote this code below and the write method works fine because I can see the response using the gtkterm software
Code:
import serial
ser = serial.Serial()
ser.baudrate = 115200
ser.port = '/dev/ttyUSB0'
ser.open()
print(ser.is_open)
ser.write(b'info\r\n') # get info command
ser.write(b'scan=00\r\n') # start scan command
The response displayed in gtkterm software:
Device information
firmware: nrf_dongle
firmware_version: 0.2.5-ba519b3
firmware_build: 20180413-104249
device_name: amine
serial_number: a58f2080352ac55bd1850576df54
mac_address: d1850576df54
device_state: 1
adv_state: 0
scan_state: 0
END
#scan:d1850576df54,20fabb03c064,-71,2,30,0201041aff4c00021570996ffaa2c34f00b776a3852c4bbd790cb90006c2
#scan:d1850576df54,20fabb044b2c,-62,2,30,0201041aff4c000215023f3d601143013582ba2e1e1603bcb9ffff02e5c5
#scan:d1850576df54,20fabb044b51,-54,3,30,0201041aff4c000215023f3d601143013582ba2e1e1603bcb9ffff02c8c5
#scan:d1850576df54,20fabb044b2c,-62,2,30,0201041aff4c000215023f3d601143013582ba2e1e1603bcb9ffff02e5c5
.
.
.
So my question is how can I read this data using the pyserial module or any other approach ?
There are many approaches to this problem. First of all the question is - do you want to implement every detail yourself as an exercise? If so then you can implement a function that will read from the serial port one byte at the time, like so:
def readline(port):
message = ""
byte = ""
while True:
byte = port.read()
if byte == "\n":
break
message += byte
return message
It will stop reading from the port when the newline character is encountered and return the message so far. But be aware that there are some problems here (is the end-of-line character always "\n"? What if there is a timeout on the read function?)
Here is the link to the documentation about how the read function behaves. Note, that if the Serial object has not been set with a timeout the function will block, which means that it will wait for the incoming data from the serial port.
The PySerial documentation is a great source of information on the topic - they also provide an example for using the readline function that takes into account problems connected to the newline differences (end-of-line characters). Here is the example from the docs rewritten for your example:
import serial
import io
ser = serial.Serial()
ser.baudrate = 115200
ser.port = '/dev/ttyUSB0'
ser.open()
print(ser.is_open)
sio = io.TextIOWrapper(io.BufferedRWPair(ser, ser))
sio.write(b'info\r\n')
sio.flush() # it is buffering. required to get the data out *now*
response = sio.readline()
print(response)
I strongly suggest to look at the miniterm.py module that is supplied with the PySerial module. Although it might be quite hard at first it is in my opinion a good source of learning material to get accustomed with this library.

PySerial - Full-duplex communication

Is it possible to achieve full-duplex communication using PySerial? Specifically, would it be possible to monitor the port continuously for input and write whenever needed? I imagine it should be possible using threads (and serial interfaces are full duplex no?). If not, what would be the best approach to monitoring a serial port when not transmitting? A timeout?
Edit: Here's my attempt at it. This code is targeting TI's CC2540 Bluetooth LE chip. On sending the GATT init message I expect a reply (detailing the operating parameters of the chip)...I'm getting nothing though
import serial
import threading
from time import sleep
serial_port = serial.Serial()
GAP_DeviceInit = \
"\x01\x00\xfe\x26\x08\x03\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00"
def read():
while True:
data = serial_port.read(9999);
if len(data) > 0:
print 'Got:', data
sleep(0.5)
print 'not blocked'
def main():
serial_port.baudrate = 57600
serial_port.port = '/dev/ttyACM0'
serial_port.timeout = 0
if serial_port.isOpen(): serial_port.close()
serial_port.open()
t1 = threading.Thread(target=read, args=())
while True:
try:
command = raw_input('Enter a command to send to the Keyfob: \n\t')
if (command == "1"):
serial_port.write(message)
except KeyboardInterrupt:
break
serial_port.close()
Yes serial port hardware is full duplex. Yes, you can use threads to do Rx and Tx at the same time. Alternatively, you can use a single thread loop that does reads with a short timeout and alternates between reading and writing.
You didn't specify a timeout, so the read waits for the full number of bytes to receive and only then displays anything.

Categories