Python3 Two-Way Serial Communication: Reading In Data - python-3.x

I am trying to establish a two-way communication via Python3. There is a laser range finder plugged into one of my USB ports and I'd like to send/receive commands to that. I have a sheet of commands which can be sent and what they would return, so this part is already there.
What I need is a convenient way to do it in real-time. So far I have the following code:
import serial, time
SERIALPORT = "/dev/ttyUSB0"
BAUDRATE = 115200
ser = serial.Serial(SERIALPORT, BAUDRATE)
ser.bytesize = serial.EIGHTBITS #number of bits per bytes
ser.parity = serial.PARITY_NONE #set parity check: no parity
ser.stopbits = serial.STOPBITS_ONE #number of stop bits
ser.timeout = None #block read
ser.xonxoff = False #disable software flow control
ser.rtscts = False #disable hardware (RTS/CTS) flow control
ser.dsrdtr = False #disable hardware (DSR/DTR) flow control
ser.writeTimeout = 0 #timeout for write
print ("Starting Up Serial Monitor")
try:
ser.open()
except Exception as e:
print ("Exception: Opening serial port: " + str(e))
if ser.isOpen():
try:
ser.flushInput()
ser.flushOutput()
ser.write("1\r\n".encode('ascii'))
print("write data: 1")
time.sleep(0.5)
numberOfLine = 0
while True:
response = ser.readline().decode('ascii')
print("read data: " + response)
numberOfLine = numberOfLine + 1
if (numberOfLine >= 5):
break
ser.close()
except Exception as e:
print ("Error communicating...: " + str(e))
else:
print ("Cannot open serial port.")
So in the above code I am sending "1" which should trigger "getDistance()" function of the laser finder and return the distance in mm. I tried this on Putty and it works, returns distances up to 4 digits. However, when I launch the above Python script, my output is only the following:
Starting Up Serial Monitor
Exception: Opening serial port: Port is already open.
write data: 1
read data:
and it goes forever. There is no read data or whatsoever.
Where am I mistaken?

Apparently much more simpler version of the code solved the issue.
import serial
import time
ser = serial.Serial('/dev/ttyUSB0', 115200, timeout = 1) # ttyACM1 for Arduino board
readOut = 0 #chars waiting from laser range finder
print ("Starting up")
connected = False
commandToSend = 1 # get the distance in mm
while True:
print ("Writing: ", commandToSend)
ser.write(str(commandToSend).encode())
time.sleep(1)
while True:
try:
print ("Attempt to Read")
readOut = ser.readline().decode('ascii')
time.sleep(1)
print ("Reading: ", readOut)
break
except:
pass
print ("Restart")
ser.flush() #flush the buffer
Output, as desired:
Writing: 1
Attempt to Read
Reading: 20
Restart
Writing: 1
Attempt to Read
Reading: 22
Restart
Writing: 1
Attempt to Read
Reading: 24
Restart
Writing: 1
Attempt to Read
Reading: 22
Restart
Writing: 1
Attempt to Read
Reading: 26
Restart
Writing: 1
Attempt to Read
Reading: 35
Restart
Writing: 1
Attempt to Read
Reading: 36

It seems to me that your ser.timeout = None may be causing a problem here. The first cycle of your while loop seems to go through fine, but your program hangs when it tries ser.readline() for the second time.
There are a few ways to solve this. My preferred way would be to specify a non-None timeout, perhaps of one second. This would allow ser.readline() to return a value even when the device does not send an endline character.
Another way would be to change your ser.readline() to be something like ser.read(ser.in_waiting) or ser.read(ser.inWaiting()) in order to return all of the characters available in the buffer.

try this code
try:
ser = serial.Serial("/dev/ttyS0", 9600) #for COM3
ser_bytes = ser.readline()
time.sleep(1)
inp = ser_bytes.decode('utf-8')
print (inp)
except:
pass

Related

unable to write command on serial port on a single attempt

I am currently trying to read the output of writing a command to the serial port where my USB device is connected.
import pyserial
ser = serial.Serial(port=portName,baudrate=19200,parity=serial.PARITY_NONE,stopbits=serial.STOPBITS_ONE,bytesize=serial.EIGHTBITS)
ser.xonxoff = 0
ser.rtscts = 0
ser.set_buffer_size(rx_size=256, tx_size=256)
ser.writeTimeout = 2
ser.timeout = 2
ser.write("\r".encode())
if not ser.is_open:
ser.open()
try:
ser.flushInput()
ser.flushOutput()
ser.write(command.encode())
return ser.readlines()
finally:
ser.close()
I always have to run this code twice in order to execute a command. What parameter should I change here in order to not have this issue?

How to pick out the second to last line from a telnet command

like the many other threads I've opened, I am trying to create a multi-feature instant replay system utilizing the blackmagic hyperdeck which operates over Telnet. The current feature I am trying to implement is an in-out replay which requires storing two timecode variables in the format of hh:mm:ss;ff where h=hours, m=minutes, s=seconds, and f=frames #30fps. the telnet command for this is transport info, and the response returns 9 lines of which I only want the timecode from the 7th. Any idea on how to do this, as it is way out of my league?
status: stopped
speed: 0
slot id: 1
clip id: 1
single clip: false
display timecode: 00:00:09;22
timecode: 00:00:09;22
video format: 1080i5994
loop: false
Here's ideally what I would like it to look like
import telnetlib
host = "192.168.1.13" #changes for each device
port = 9993 #specific for hyperdecks
timeout = 10
session = telnetlib.Telnet(host, port, timeout)
def In():
session.write(b"transport info \n")
line = session.read_until(b";00",.5)
print(line)
#code to take response and store given line as variable IOin
def out():
session.write(b"transport info \n")
line = session.read_until(b";00",.5)
print(line)
#code to take response and store given line as variable IOout
def IOplay():
IOtc = "playrange set: in: " + str(IOin) + " out: " + str(IOout) + " \n"
session.write( IOtc.encode() )
speed = "play: speed: " + str(Pspeed.get() ) + "\n"
session.write(speed.encode() )
For the most part here's what I got to at least partially work
TCi = 1
TCo = 1
def In():
global TCi
session.write(b"transport info \n")
by = session.read_until(b";00",.5)
print(by)
s = by.find(b"00:")
TCi = by[s:s+11]
def Out():
global TCo
session.write(b"transport info \n")
by = session.read_until(b";00",.5)
print(by)
s = by.find(b"00:")
TCo = by[s:s+11]
def IOplay():
IOtc = "playrange set: in: " + str(TCi) + " out: " + str(TCo) + " \n"
print(IOtc.encode() )
session.write(IOtc.encode() )
speed = "play: speed: 2 \n"
session.write(speed.encode() )
except that its encoding as
b"playrange set: in: b'00:00:01;11' out: b'00:00:03;10' \n"
rather than
"playrange set: in: 00:00:01;11 out: 00:00:03;10 \n"
I need to get rid of the apostrophe's and b prefix in front of the variables
Any ideas?
def get_timecode(text):
tc = ''
lines = text.split('\r\n')
for line in lines:
var, val = line.split(': ', maxsplit=1)
if var == 'timecode':
tc = val
return tc
You could choose to go directly to lines[6], without scanning,
but that would be more fragile if client got out of sync with server,
or if server's output formatting changed in a later release.
EDIT:
You wrote:
session.write(b"transport info \n")
#code to take response and store given line as variable IOin
You don't appear to be reading anything from the session.
I don't use telnetlib, but the docs suggest you'll
never obtain those nine lines of text if you don't do something like:
expect = b"foo" # some prompt string returned by server that you never described in your question
session.write(b"transport info\n")
bytes = session.read_until(expect, timeout)
text = bytes.decode()
print(text)
print('Timecode is', get_timecode(text))

UART send no bytes

I'm trying to write test code for sending data via UART on my Raspberry PI 3 b+ , but I cannot receive back the data I've sent . Raspberry is connected via UART module to my laptop , so I can see in Putty results. Anyone can tell me what Am I doing wrong?
I've checked if port isOpen and it returned True , msg=b'Hello' returned Hello showed hello , but no bytes received . Sending single bytes give also no bytes detected . Erasing the timeout showed that it is reached.
Edit: did little testing if port's are working properly
from __future__ import print_function
import serial
test_string = "Testing 1 2 3 4".encode('utf-8')
#test_string = b"Testing 1 2 3 4" ### Will also work
port_list = ["/dev/serial0", "/dev/ttyS0"]
for port in port_list:
try:
serialPort = serial.Serial(port, 9600, timeout = 2)
serialPort.flushInput()
serialPort.flushOutput()
print("Opened port", port, "for testing:")
bytes_sent = serialPort.write(test_string)
print ("Sent", bytes_sent, "bytes")
loopback = serialPort.read(bytes_sent)
if loopback == test_string:
print ("Received", len(loopback), "valid bytes, Serial port", port, "working \n")
else:
print ("Received incorrect data", loopback, "over Serial port", port, "loopback\n")
serialPort.close()
except IOError:
print ("Failed at", port, "\n")
That give me information that tty0 is not working properly but , also got absolutly no answer about correctness on port serial0
import serial
import struct
import time
port = serial.Serial("/dev/ttyS0", baudrate=115200, timeout=2.0)
i = 0
while True:
msg = struct.pack('>HBBB', 3000, 243, 234, 254)
port.write(msg)
time.sleep(0.3)
bytesToRead = port.inWaiting()
print("Found {} bytes in serial".format(bytesToRead))
if bytesToRead == 5:
rcv = port.read(5)
# port.write('\r\nYou sent:' + repr(rcv))
for i in range(5):
print('\r {} - {}'.format(i, bytes(rcv[i])))
idCode = struct.pack('BB', rcv[0], rcv[1])
idCode = struct.unpack('>H', idCode)
idCode = idCode[0]
# value = struct.unpack_from('HBBB', decode)
i += 1
if i == 4:
exit()
Expected Results:
Found 5 bytes in serial
(index) - (byte at that index)
Got:
Found 0 bytes in serial
Solved. It was just matter of wiring .
If someone in future would like to run test like that - they must remember to connect RX with TX line . Or if it connected with laptop there's it should be better to create code responsible for communication on it (but instead of ttyS0 or Serial0 , port should be set to proper COM e.g. "COM3")

Python Can't decode byte : Invalid start byte

So I'm building this socket application, and it works just fine on my computer. But when i start server socket on another laptop, it just crashes with a invalid start byte error:
How do i proper encode the program to work with all laptops
This is the error i get on :
Other laptops.
My laptop.
I have tried to change the encoding, but I'm just not quite sure where i have to change it.
Class Listener:
def __init__(self):
self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self.server_address = (socket.gethostbyname(socket.gethostname()), 10000)
self.sock.bind(self.server_address)
print(f"LISTENER : {str(self.server_address[0])} port {str(self.server_address[1])}")
def listen(self):
self.sock.listen(1)
while True:
print("Connection Open")
print(" Waiting for connections")
self.connection, self.client_address = self.sock.accept()
try:
print(f"Connection from {str(self.client_address)}")
while True:
data = self.connection.recv(1024)
if data:
message = str(data)
if not "print" in message.lower(): # This just checks if the client wants to print system information from the server
Validate(message) # this checks for a command the server have to do
else:
self.connection.sendall(pickle.dumps(self.computerinfomation))
else:
self.listen()
except Exception as e:
print(e)
I want it to work on other laptops as well, and i just cant see why it wont.
Furas came with a solution.
I changed the
message = str(data)
to
message = str(data, encoding="utf-8")
I did the same on the client side
Not going to lie. I just changed the encoding = utf-16.
Example:
df = pd.read_csv(C:/folders path/untitled.csv, encoding = "utf-16")

Simultaneous input and output for network based messaging program

In python, I am creating a message system where a client and server can send messages back and forth simeltaneously. Here is my code for the client:
import threading
import socket
# Global variables
host = input("Server: ")
port = 9000
buff = 1024
# Create socket instance
s = socket.socket()
# Connect to server
s.connect( (host, port) )
print("Connected to server\n")
class Recieve(threading.Thread):
def run(self):
while True: # Recieve loop
r_msg = s.recv(buff).decode()
print("\nServer: " + r_msg)
recieve_thread = Recieve()
recieve_thread.start()
while True: # Send loop
s_msg = input("Send message: ")
if s_msg.lower() == 'q': # Quit option
break
s.send( s_msg.encode() )
s.close()
I have a thread in the background to check for server messages and a looping input to send messages to the server. The problem arises when the server sends a message and the user input is immediately bounced up to make room for the servers message. I want it so that the input stays pinned to the bottom of the shell window, while the output is printed from the 2nd line up, leaving the first line alone. I have been told that you can use curses or Queues to do this, but I am not sure which one would be best in my situation nor how to implement these modules into my project.
Any help would be appreciated. Thank you.
I want it so that the input stays pinned to the bottom of the shell
window, while the output is printed from the 2nd line up, leaving the
first line alone. I have been told that you can use curses
Here's a supplemented version of your client code using curses.
import threading
import socket
# Global variables
host = input("Server: ")
port = 9000
buff = 1024
# Create socket instance
s = socket.socket()
# Connect to server
s.connect( (host, port) )
print("Connected to server\n")
import sys
write = sys.stdout.buffer.raw.write
from curses import *
setupterm()
lines = tigetnum('lines')
change_scroll_region = tigetstr('csr')
cursor_up = tigetstr('cuu1')
restore_cursor = tigetstr('rc')
save_cursor = tigetstr('sc')
def pin(input_lines): # protect input_lines at the bottom from scrolling
write(save_cursor + \
tparm(change_scroll_region, 0, lines-1-input_lines) + \
restore_cursor)
pin(1)
class Recieve(threading.Thread):
def run(self):
while True: # Recieve loop
r_msg = s.recv(buff).decode()
write(save_cursor+cursor_up)
print("\nServer: " + r_msg)
write(restore_cursor)
recieve_thread = Recieve()
recieve_thread.daemon = True
recieve_thread.start()
while True: # Send loop
s_msg = input("Send message: ")
if s_msg.lower() == 'q': # Quit option
break
s.send( s_msg.encode() )
pin(0)
s.close()
It changes the scrolling region to leave out the screen's bottom line, enters the scrolling region temporarily to output the server messages, and changes it back at the end.

Resources