Python UART between 2 Raspberry Pis - python

I'm trying to send strings between two Raspberry Pis via UART. So far, one Pi is able to transmit a string and the second pi is only able to receive. I'm trying send a "command" from one Pi to the other, then the second Pi will receive the "command" and then send "sensor data" back to the first Pi. However, this doesn't seem to work. I'm unable to both send and receive data.
Any help would be greatly appreciated.
The Raspberry Pi that is sending the command:
import serial
ser = serial.Serial(
port = '/dev/ttyAMA0',\
baudrate = 115200,\
bytesize = serial.EIGHTBITS,\
timeout = 0)
dat = ''
while True:
#asks user for input
command = raw_input()
#terminates command with null
ser.write(command + '\0')
#reads data per char
for c in ser.read():
#appends string
dat += c
#terminates at null char
if c == '\0':
print(dat)
dat = ''
break
ser.close()
This RPi that receives command and then sends sensor data:
import serial
ser = serial.Serial(
'/dev/ttyAMA0' ,\
baudrate = 115200 ,\
bytesize = serial.EIGHTBITS ,\
timeout = 0)
dat = ''
sen1 = 'sen1\x00'
sen2 = 'sen2\x00'
com1 = 'hello'
com2 = 'this thing works!'
com3 = 'error!'
while True:
#reads data per char
for c in ser.read():
#appends string
dat += c
#terminates at null char
if c == '\0':
#decides which sensor to choose
if dat == sen1:
print(com1)
ser.write(com1 + '\0')
dat = ''
break
elif dat == sen2:
print(com2)
ser.write(com2 + '\0')
dat = ''
break
else:
print(com3)
ser.write(com3 + '\0')
dat = ''
ser.close()

The cause is most likely your zero timeout. You've set read() to non-blocking as a result, so your sender Pi's for loops will most likely read nothing (or only a byte) and will terminate before everything's arrived. Also iterating over ser.read() doesn't really make sense since it returns only a byte anyway by default.
Increase timeout to some non-zero value.
ser = serial.Serial(
'/dev/ttyAMA0' ,\
baudrate = 115200 ,\
bytesize = serial.EIGHTBITS ,\
timeout = 0.1)
Assuming you want the response to a command received, before another command is sent, this should work for the sender:
while True:
#asks user for input
command = raw_input()
#terminates command with null
ser.write(command + '\0')
dat = []
lastRead = time.time()
while time.time() - lastRead < 0.1:
while ser.inWaiting() > 0:
dat.append(ser.read())
lastRead = time.time()
if dat and (dat[-1] == '\0'):
print(''.join(dat[:-1]))
If 100ms or more passes before another byte is available, the while loop breaks and the last received byte is checked to know if its a null byte. Something similar should work for your receiver:
while True:
dat = []
lastRead = time.time()
while time.time() - lastRead < 0.1:
while ser.inWaiting() > 0:
dat.append(ser.read())
lastRead = time.time()
dat = ''.join(dat)
# begin your comparisons here

Related

Unable to read data from serial port

I tried to read data from USB port on OSX using pyserial. When I tried to read it using CoolTerm, everything worked fine. This is the configuration:
Then I tried to write a short script which doesn't continuously output the data (it outputs the data only once and the even if I restart the script, nothing comes out):
import serial
import time
ser = serial.Serial("/dev/cu.SLAB_USBtoUART", 115200, timeout=5, bytesize=8, stopbits=serial.STOPBITS_ONE, parity=serial.PARITY_NONE)
def getTFminiData():
while True:
# time.sleep(0.1)
count = ser.in_waiting
if count > 8:
recv = ser.read(9)
ser.reset_input_buffer()
if recv[0] == 0x59 and recv[1] == 0x59: # python3
distance = recv[2] + recv[3] * 256
strength = recv[4] + recv[5] * 256
print('(', distance, ',', strength, ')')
ser.reset_input_buffer()
if __name__ == '__main__':
try:
if ser.is_open == False:
ser.open()
getTFminiData()
except KeyboardInterrupt: # Ctrl+C
if ser != None:
ser.close()
Does anyone know what is the right way to get the same data as CoolTerm in this case does?
Thanks to Joe's comment, I fuggered out how to modify the code. The working example is the following:
import serial
import time
ser = serial.Serial("/dev/cu.SLAB_USBtoUART", 115200, bytesize=8, stopbits=serial.STOPBITS_ONE, parity=serial.PARITY_NONE)
def getTFminiData():
bytes_read = 0
data = None
while True:
# time.sleep(0.1)
count = max(1, ser.in_waiting)
if count < 8:
if data is None:
data = ser.read(count)
else:
data.append(ser.read(count))
bytes_read += count
else:
recv = ser.read(9 - bytes_read)
bytes_read = 0
recv = data + recv
data = None
ser.reset_input_buffer()
if recv[0] == 0x59 and recv[1] == 0x59: # python3
distance = recv[2] + recv[3] * 256
strength = recv[4] + recv[5] * 256
print('(', distance, ',', strength, ')')
ser.reset_input_buffer()
if __name__ == '__main__':
try:
if ser.is_open == False:
ser.open()
getTFminiData()
except KeyboardInterrupt: # Ctrl+C
if ser != None:
ser.close()

Python Serial: having trouble reading the port

I'm attempting to read the values of some GPIO. Here's the code:
import serial
import codecs
import time
ser = serial.Serial(port = 'COM4', baudrate = 9600, \
parity = serial.PARITY_NONE, \
stopbits = serial.STOPBITS_ONE, \
bytesize = serial.EIGHTBITS, \
timeout = 0, \
)
print('connected to: ',ser.name)
ser.close()
def SSend(input):
ser.write(codecs.decode(input, "hex_codec")) #send as ASCII
print('sent: ', input)
def ReadIO():
#open the port
try:
ser.open()
except:
print('error opening serial port')
exit()
#flush the buffers
ser.flushInput()
ser.flushOutput()
#write data to read from GPIO
#causes SC18IM to return a byte containing each of the 8 I/O values
SSend(b'4950')
time.sleep(0.1) #allow time for the data to be received
#read the data
serialData = False
serialData = ser.readline()
ser.close()
return serialData
while 1:
print(ReadIO())
time.sleep(0.5)
This prints the following:
sent:
b'4950'
b''
(I am expecting back either 0x00 or 0x20 instead of an empty byte)
I know my hardware is good as is what I'm sending because it get back what I expect when using Realterm and have successful write commands in my script elsewhere.
I had some luck using this
#read the data
serialData = False
for c in ser.readline():
print('in loop')
print(c)
serialData = c
ser.close()
However, I don't really understand why it worked and it only appears to work intermittently.
Thanks for reading.
readline() assumes that there is some end-of-line symbol, like \n or \r. You should read data byte-wise:
serialData = ''
while ser.inWaiting() > 0:
c=ser.read(1)
# or c=ser.read(1).decode('latin1')
serialData += c

Python serial port returing null string

Reading data from the serial port:
readline() in the below code return the null vector, the reading data from the serial port is hexadecimal number like AABB00EF the putty gives me the output means the communication is working but nothing works via python
here is the code:
#!/usr/bin/python
import serial, time
ser = serial.Serial()
ser.port = "/dev/ttyUSB0"
ser.baudrate = 115200
#ser.bytesize = serial.EIGHTBITS
#ser.parity = serial.PARITY_NONE
#ser.stopbits = serial.STOPBITS_ONE
#ser.timeout = None
ser.timeout = 1
#ser.xonxoff = False
#ser.rtscts = False
#ser.dsrdtr = False
#ser.writeTimeout = 2
try:
ser.open()
except Exception, e:
print "error open serial port: " + str(e)
exit()
if ser.isOpen():
try:
#ser.flushInput()
#ser.flushOutput()
#time.sleep(0.5)
# numOfLines = 0
# f=open('signature.txt','w+')
while True:
response = ser.readline()
print len(response)
#f=ser.write(response)
print response
# numOfLines = numOfLines + 1
f.close()
ser.close()
except Exception, e1:
print "error communicating...: " + str(e1)
else:
print "cannot open serial port "
readline will try to read until the end of the line is reached, if there is no \r or \n then it will wait forever (if you have a timeout it might work...) instead try something like this
ser.setTimeout(1)
result = ser.read(1000) # read 1000 characters or until our timeout occures, whichever comes first
print repr(result)
just use this code
ser = serial.Serial("/dev/ttyUSB0",115200,timeout=1)
print "OK OPENED SERIAL:",ser
time.sleep(1)# if this is arduino ... wait longer time.sleep(5)
ser.write("\r") # send newline
time.sleep(0.1)
print "READ:",repr(ser.read(8))
you can create a readuntil method
def read_until(ser,terminator="\n"):
resp = ""
while not resp.endswith(terminator):
tmp = ser.read(1)
if not tmp: return resp # timeout occured
resp += tmp
return resp
then just use it like
read_until(ser,"\r")

Opening 2 serial ports simultaneously in python (one tx one for rx)

I am making a throughput test for a bluetooth link, and I need to send data through a serial port to one bluetooth device which will then transport that data wirelessly to another bluetooth device. The other device will then complete the circuit by sending the data back to the host PC via a different serial port.
The problem seems to be when I attempt to open up 2 different instances of PySerial, the program simply hangs. I have isolated it down to running vs. hanging when I comment out one of the two serial port instantiations. Anyone see a problem with how I'm doing this? If so, what is the proper way to do this? See code below:
#/usr/bin/python
import serial
import time
import sys
DEFAULT_BAUD = 115200
SEND_SIZE = 100
def addPath(file):
pth, fl = os.path.split(__file__)
return os.path.join(pth, file)
def is_number(s):
try:
int(s, 16)
return True
except:
return False
class SerialReader():
def __init__(self, portRx, portTx):
self.portTx = portTx
self.portRx = portRx
self.start_time__sec = time.time()
self.interval__sec = 0
self.buffer = []
self.sendtext = ''.join([str(i) for i in range(SEND_SIZE)])
# send first batch of data
self.portTx.write(self.sendtext)
def didDataArrive(self):
# Read port
self.buffer.extend(list(self.portRx.read(1024)))
# Step through the buffer byte and byte and see if the tick text
# is at the front.
while len(self.buffer) >= len(self.sendtext):
if self.buffer[:len(self.sendtext)] == self.sendtext:
# Discard the tick text
self.buffer = self.buffer[len(self.sendtext):]
# Record time
snapshot__sec = time.time()
self.interval__sec = snapshot__sec - self.start_time__sec
self.start_time__sec = snapshot__sec
# send more data
self.portTx.write(self.sendtext)
return True
else:
self.buffer.pop(0)
return False
def main(port1, port2, baudrate1 = DEFAULT_BAUD, baudrate2 = DEFAULT_BAUD):
try:
import serial
except:
traceback.print_exc()
print "="*60
print "You need to install PySerial"
print "Windows: easy_install pyserial"
print "Mac/Linux: sudo easy_install pyserial"
try:
s1 = serial.Serial(port1, baudrate1, timeout = 0.1)
s2 = serial.Serial(port2, baudrate2, timeout = 0.1)
print "Loading serial ports"
except:
print "Serial port error"
exit()
plot_stop = False
dataread = SerialReader(s2, s1)
try:
while plot_stop == False:
if dataread.didDataArrive():
print dataread.interval__sec
except KeyboardInterrupt:
print "Keyboard Interrupt"
plot_stop = True
finally:
print "Closing"
s1.close()
s2.close()
if __name__ == '__main__':
if (len(sys.argv) < 3):
print "Usage: python extract_data.py phonelink_serialport phonelinkclient_serialport [baudrate1] [baudrate2]"
else:
main(*sys.argv[1:])
If I remove one of the following lines (doesn't matter which one), the python script runs (although it eventually crashes because in the code it eventually tries to reference both ports). If I leave these lines in, the program seems to just hang (it just seems to sit there and run indefinitely):
s1 = serial.Serial(port1, baudrate1, timeout = 0.1)
s2 = serial.Serial(port2, baudrate2, timeout = 0.1)

Python Serial: How to use the read or readline function to read more than 1 character at a time

I'm having trouble to read more than one character using my program, I can't seem to figure out what went wrong with my program.
import serial
ser = serial.Serial(
port='COM5',\
baudrate=9600,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=0)
print("connected to: " + ser.portstr)
count=1
while True:
for line in ser.read():
print(str(count) + str(': ') + chr(line) )
count = count+1
ser.close()
here are the results I get
connected to: COM5
1: 1
2: 2
3: 4
4: 3
5: 1
actually I was expecting this
connected to: COM5
1:12431
2:12431
something like the above mentioned which is able read multiple characters at the same time not one by one.
I see a couple of issues.
First:
ser.read() is only going to return 1 byte at a time.
If you specify a count
ser.read(5)
it will read 5 bytes (less if timeout occurrs before 5 bytes arrive.)
If you know that your input is always properly terminated with EOL characters,
better way is to use
ser.readline()
That will continue to read characters until an EOL is received.
Second:
Even if you get ser.read() or ser.readline() to return multiple bytes,
since you are iterating over the return value, you will
still be handling it one byte at a time.
Get rid of the
for line in ser.read():
and just say:
line = ser.readline()
I use this small method to read Arduino serial monitor with Python
import serial
ser = serial.Serial("COM11", 9600)
while True:
cc=str(ser.readline())
print(cc[2:][:-5])
Serial sends data 8 bits at a time, that translates to 1 byte and 1 byte means 1 character.
You need to implement your own method that can read characters into a buffer until some sentinel is reached. The convention is to send a message like 12431\n indicating one line.
So what you need to do is to implement a buffer that will store X number of characters and as soon as you reach that \n, perform your operation on the line and proceed to read the next line into the buffer.
Note you will have to take care of buffer overflow cases i.e. when a line is received that is longer than your buffer etc...
EDIT
import serial
ser = serial.Serial(
port='COM5',\
baudrate=9600,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=0)
print("connected to: " + ser.portstr)
#this will store the line
line = []
while True:
for c in ser.read():
line.append(c)
if c == '\n':
print("Line: " + ''.join(line))
line = []
break
ser.close()
I was reciving some date from my arduino uno (0-1023 numbers).
Using code from 1337holiday, jwygralak67 and some tips from other sources:
import serial
import time
ser = serial.Serial(
port='COM4',\
baudrate=9600,\
parity=serial.PARITY_NONE,\
stopbits=serial.STOPBITS_ONE,\
bytesize=serial.EIGHTBITS,\
timeout=0)
print("connected to: " + ser.portstr)
#this will store the line
seq = []
count = 1
while True:
for c in ser.read():
seq.append(chr(c)) #convert from ANSII
joined_seq = ''.join(str(v) for v in seq) #Make a string from array
if chr(c) == '\n':
print("Line " + str(count) + ': ' + joined_seq)
seq = []
count += 1
break
ser.close()

Categories

Resources