I need to communicate and pass values to a serial connected device using RS232 Protocol. I need to pass commands through the 8 bytes of data and then be able to receive the response afterwards.. Im not sure how to write this in PySerial so if anyone can help out it would be great (9600 Baud, 8 data bits, No parity, and 1 stop bit.)
import serial
ser = serial.Serial('/dev/ttyUSB0') # open serial port
print(ser.name) # check which port was really used
ser.write(b'hello') # write a string
ser.close() # close port
The Timer Manager Command structure consists of one start byte, one command byte, five bytes of data, and a one byte checksum. Each message packet is formatted as follows:
BYTE 0 BYTE 1 BYTE 2 BYTE 3 BYTE 4 BYTE 5 BYTE 6 BYTE 7
200 COMMAND DATA1 DATA2 DATA3 DATA4 DATA5 CK SUM
Im looking to receive the following back from the machine:
If command was successfully received, the Timer Manager will respond with:
BYTE 0 BYTE 1 BYTE 2
6 0 6
The actual data that I want to send is this
Data i need to pass to the timer is structured this way:
BYTE 0 BYTE 1 BYTE 2 BYTE 3 BYTE 4 BYTE 5 BYTE 6 BYTE 7
200 31 4 0 0 0 0 235
Is this passed via bytearray ?
ser.write( bytearray(200,31,4,0,0,0,0,235) );
I generally have something like this to do binary IO over a serial port:
from timeit import default_timer as clk
from serial import Serial, SerialException
class TimeManager(object):
def __init__(self, port, baudrate=9600):
self.ser = Serial(port, baudrate=baudrate)
self.ser.open()
self.ser.flushInput()
self.ser.flushOutput()
def send(self, tx):
tx = bytearray(tx)
try:
self.ser.write(tx)
self.ser.flush()
except SerialException as e:
if e.args == (5, "WriteFile", "Access is denied."):
# This occurs on win32 when a USB serial port is
# unplugged and replugged. It should be fixed by
# closing and reopening the port, which should happen
# in the error handling of our caller.
raise IOError(errno.ENOENT, "Serial port disappeared.",
self.ser.portstr)
else:
raise
def receive(self):
rx = bytearray()
delay = 10e-3 # s
timeout = 1 # s
end_time = clk() + timeout
while True:
time_remaining = end_time - clk()
if time_remaining < 0:
break
rx += self.ser.read(self.ser.inWaiting())
if 0 in rx:
break
time.sleep(delay)
if time_remaining <= 0:
raise IOError(errno.ETIMEDOUT, "Communication timed out.")
return rx
tm = TimeManager("/dev/ttyS0")
My device sends null terminated messages (the if 0 in rx: line). You'd have to figure out a similar condition for your messages.
First of all, due to the fact that you use RS232, you must set the ASCII characters you wanna send in variables. And then, when you got in a variable all the sentence you want to send, sent it decoding it into bytes.
It would be something like this.
def sendserial(sendstring):
ser.port(yourport)
try:
ser.open()
except Exception as e:
flag=1
if ser.isOpen():
try:
ser.flushInput()
ser.flushOutput()
ser.write(bytes(sendstring,'iso-8859-1'))
#iso 8859-1 is the only encode that works for me
time.sleep(0.5)
numOfLines = 0
while True:
resp = bytes.decode(ser.readline())
result = ord(str(response))
if result == ord(ACK)
#I set previously the ACK var to the ASCII symbol that the machine returns
response = 'Y'
else:
response = 'N'
numOfLines = numOfLines +1
if (numOfLines>=1):
break
ser.close()
except Exception as e1:
print('Communication error...:' + str(e1))
else:
pass
return(response)
Related
Posting this here out of desperation. Any help is appreciated. Thank you.
Backstory:
I am helping my friend with a device that he got from China. The device supposedly sends a audio file to my server using UDP.
assuming you want some Python code to do this automatically, here's how I'd validate and decode the packet:
import struct
def decode_packet(packet):
framehead, version, command, datalen = struct.unpack_from('!HBBH', packet)
valid = (
framehead == 0x55aa and
version == 0x00 and
command == 0x1e and
len(packet) <= datalen + 11
)
if not valid:
# ignore other protocols using this address/port
print(
' header invalid',
f'{framehead:04x} {version:02x} {command:02x} {datalen:04x}'
)
return
if len(packet) < datalen + 11:
print(' warning: packet was truncated')
offset, = struct.unpack_from('!I', packet, 6)
if datalen == 4:
print(f' end of data: file size={offset}')
return
data = packet[10:10+datalen]
print(f' got data: offset={offset} len={len(data)} hex(data)={data.hex()}')
if len(packet) == datalen + 11:
print(f' hex(checksum)={packet[datalen + 10:].hex()}')
it obviously prints out a lot of stuff, but this is good to seeing if the device is actually following the documented protocol. it doesn't seem to be, as the +4 on the data length doesn't seem to be being applied. you can test this with:
decode_packet(bytes.fromhex('55aa001e038400000000a9b6ad98d2923...'))
assuming you can get this to function correctly, you can put this into some code that listens for packets on the correct port:
import socket
def server(portnum):
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock:
sock.bind(('', portnum))
while True:
packet, addr = sock.recvfrom(10240)
print(f'received {len(packet)} bytes from {addr[0]}')
decode_packet(packet)
again, doesn't do much. you'd want to write the data to a file rather than printing it out, but you can pull the offset out and you get a signal for when the data has finished transferring
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
I'm recently studiying sockets trying to make them work inside a Python script (Python3), inside Windows.
Here the Python script of the server side.
import socket
import time
MSGLEN = 2048
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind(('localhost', 8000))
server.listen(1)
while 1:
#accept connections from outside
(clientsocket, address) = server.accept()
chunks = []
bytes_recd = 0
while bytes_recd < MSGLEN:
chunk = clientsocket.recv(min(MSGLEN - bytes_recd, 2048)) #should enough this row without checks if transmission guaranteed inside buffer dimension
#print(chunk)
#i=0
chunk = chunk.decode()
bytes_recd = bytes_recd + len(chunk)
chunks.append(chunk)
for i in range(bytes_recd):
if(chunk[i] == "_"):
print("Breaking(_Bad?)")
break
buff_str = chunk[:i]
print(buff_str)
if chunk == '':
print("Server notification: connection broken")
break
mex = ''.join(chunks)
print("Server notification: \n\tIncoming data: " + mex)
i=1;
while i==1:
chunk = clientsocket.recv(128)
chunk = chunk.decode()
if chunk == '':
i = 0
totalsent = 0
msg = "Server notification: data received"
while totalsent < MSGLEN:
sent = clientsocket.send(bytes(msg[totalsent:], 'UTF-8'))
if sent == 0 :
print ("Server notification: end transmitting")
break
totalsent = totalsent + sent
I'm checking when a "_" is received and make some decision in it. This because I'm using blocking sockets. You should forget the very last part and the whole program functionality since I'm working on it and the incriminated part is here:
for i in range(bytes_recd):
if(chunk[i] == "_"):
print("Breaking(_Bad?)")
break
buff_str = chunk[:i]
Something weird happens: the check works fine and break the loop by printing the rest at the right index value. BUT! This wild and apparently non-sense error appears:
>>>
Breaking(_Bad?), i: 2
13
Traceback (most recent call last):
File "C:\Users\TheXeno\Dropbox\Firmwares\Altri\server.py", line 24, in <module>
if(chunk[i] == "_"):
IndexError: string index out of range
As you can see from the console output, it finds the number before the "_", in this case is the string "13" and is located at i = 2, which is compliant with the receiving string format form the socket: "charNumber_String". But seems to keep counting until exit from bounds.
EDIT: I will not rename the variables, but next time, better use improved names, and not "chunk" and "chunks".
Let's look at this block of code:
while bytes_recd < MSGLEN:
chunk = clientsocket.recv(min(MSGLEN - bytes_recd, 2048))
chunk = chunk.decode()
bytes_recd = bytes_recd + len(chunk)
chunks.append(chunk)
for i in range(bytes_recd):
if(chunk[i] == "_"):
print("Breaking(_Bad?)")
break
Lets say you read 100 bytes, and lets assume that the decoded chunk is the same length as the encoded chunk. bytes_recd will be 100, and your for loop goes from zero to 99, and all is well.
Now you read another 100 bytes. chunk is again 100 bytes long, and chunks (with an "s") is 200 bytes. bytes_recd is now 200. Your for loop now goes from 0 to 199, and you're checking chunk[i]. chunk is only 100 bytes long, so when i gets past 99, you get the error.
Maybe you meant to compare chunks[i] (with an "s")?
try:
for i, chunk in enumerate(chunks):
if(chunk == "_"):
print("Breaking(_Bad?)")
break
This way you never go out of bounds. So one error less :)
I'm using PySerial to read from serial port like in the code below.
CheckReadUntil() read output of the command that I send to serial port until the sequence of symbols readUntil are in the serial output.
...
self.ser = serial.Serial(comDev, 115200, timeout=10)
...
#Function that continue to read from Serial port until 'readUntil'
#sequence of symbols appears
def CheckReadUntil(self, readUntil):
outputCharacters = []
while 1:
ch = self.ser.read()
outputCharacters += ch
if outputCharacters[-len(readUntil):]==readUntil:
break
outputLines = ''.join(outputCharacters)
return outputLines
However, if there is no sequence readUntil (for any reason), I'm just stuck in the function CheckReadUntil() forever. The setting timeout=10 sets up timeout so I'm stuck in a loop that iterates every 10 seconds and does nothing, just waiting.
How it is possible to understand that there was a timeout event so I may exit the infinite loop? Output length may be different.
UPDATE (previous answer was not correct, this is the working code from #konstantin):
...
self.ser = serial.Serial(comDev, 115200, timeout=10)
...
#Function that continue to read from Serial port until 'readUntil'
#sequence of symbols appears
def CheckReadUntil(self, readUntil):
outputCharacters = []
while 1:
ch = self.ser.read()
if len(ch) == 0:
break
outputCharacters += ch
if outputCharacters[-len(readUntil):]==readUntil:
break
outputLines = ''.join(outputCharacters)
return outputLines
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()