Timeout after X second in Python - python

I am writing a program in python, which works like this:
Take input string through Serial port, when press enter (Carriage
Return)
Check if $ sign is present as the first character of input string then continue
Problem
It can make a trouble if I didn't get the Carriage Return CR and got another string at the same time or after specific interval of time.
In order to avoid this problem I want to add timeout session which makes the previous buffer Null after specific interval of time if Carriage Return not received.
kindly review my codes below and guide me on how do I add timeout option in it?
CODE
import serial
x = [0,0,0]
ser = serial.Serial('/dev/ttyAMA0', 9600)
buffer = ''
while True:
buffer += ser.read(ser.inWaiting())
if '\n' in buffer:
if buffer[0] == '$':
x1 = buffer.rstrip()
x2= x1.split(",")
print((x2[0]),(x2[1]),(x2[2]))
buffer = ""

I thought you just need to add this line at the end of program.
This line will add more 60sec every time you input correct string
clear_buffer = time.time() + 60
Check out below coding
import serial
import time
x = [0,0,0]
ser = serial.Serial('/dev/ttyAMA0', 9600)
buffer = ''
clear_buffer = time.time() + 60
while True:
if time.time() >= clear_buffer:
buffer = ''
clear_buffer = time.time() + 60
buffer += ser.read(ser.inWaiting())
if '\n' in buffer:
if buffer[0] == '$':
x1 = buffer.rstrip()
x2= x1.split(",")
print((x2[0]),(x2[1]),(x2[2]))
buffer = ""
clear_buffer = time.time() + 60

I may be reading your question wrong, but it appears as though you just want to clear the buffer string after a set amount of time? This will clear it every 60 seconds:
import serial
import time
x = [0,0,0]
ser = serial.Serial('/dev/ttyAMA0', 9600)
buffer = ''
clear_buffer = time.time() + 60
while True:
if time.time() >= clear_buffer:
buffer = ''
clear_buffer = time.time() + 60
buffer += ser.read(ser.inWaiting())
if '\n' in buffer:
if buffer[0] == '$':
x1 = buffer.rstrip()
x2= x1.split(",")
print((x2[0]),(x2[1]),(x2[2]))
buffer = ""

Related

Writing to serial port but only receiving first character?

I'm reading data in from a machine to windows 7. Using python, I read the serial port, process the data then write the data to a different serial port. Using com0com null modem emulator, the data is sent to another program. Here is the code I'm using:
import serial
import time
ser = serial.Serial(port='COM7', baudrate=9600)
ser2 = serial.Serial(port='COM8', baudrate=9600)
value_one = None
while (True):
# Check if incoming bytes are waiting to be read from the serial input
# buffer.
# NB: for PySerial v3.0 or later, use property `in_waiting` instead of
# function `inWaiting()` below!
if (ser.in_waiting > 16):
# read the bytes and convert from binary array to ASCII
data_str = ser.read(ser.in_waiting).decode('ascii')
if (value_one == None):
time.sleep(1)
print(data_str)
value_one_parse = data_str[7:9]
print(value_one_parse)
value_one = float(value_one_parse)
print(value_one)
else:
time.sleep(1)
print(data_str)
value_two_parse = data_str[7:9]
print(value_two_parse)
value_two = float(value_two_parse)
print(value_two)
avg = ((value_one + value_two)/2)
print(avg)
avgprep = str(avg) + '\r\n'
print(avgprep)
ser2.write(avgprep.encode('utf-8'))
value_one = None
value_two = None
time.sleep(0.01)
So if avgprep = 71.1, why am I only receiving the first digit 7 to the program?
I changed ser.in_waiting > 16 to ser.in_waiting > 0 and put a time.sleep(5) after that.

Python program not writing anything to CSV file

Got thrown on this project with little python experience. Trying to write from an encoder to a csv. Weird thing is that the program wont even write a string into the file. Any advice is appreciated!
while not file_passed:
try:
file_name = input("Enter log file name: ")
# Add extension if not already given
if "." not in file_name:
file_name += ".csv"
log_file = open(file_name, "a")
print("log file open debug passed")
# Add header row to log file
if os.stat(log_file.name).st_size == 0:
log_file.write("time (ms), pressure, angle(rad) \n")
print("Logged header to file")
file_passed = True
except:
print("Invalid file, or could not open the file specified.\n-----")
Here is a snippet of what I have narrowed it down to I believe. You can see my little debug testing statements to see if its breaking into the correct if statements. It does print all of the debug statements so it seems to be breaking into the if with log_file.write("time(ms), pressure, angle(rad) \n")
EDIT: Tried adding a with before log_file.write("") line, program crashes on start when added. Here is the code in its entirety as I should have added to begin with my bad
import serial
from datetime import datetime
import os
pressure_passed = False
arduino_passed = False
file_passed = False
BAUD_RATE = 115200
GARBAGE_CYCLES = 3 # how many cycles to ignore before logging data
garbage_cycle = 0
# Save data to log file
def LogData(startTime, pressureData, arduinoData, file):
global garbage_cycle
if garbage_cycle < GARBAGE_CYCLES:
garbage_cycle += 1
else:
delta = datetime.now() - startTime
ms = delta.total_seconds() * 1000
dataString = "{:0.2f}, {}, {}\n".format(ms, pressureData, arduinoData)
file.write(dataString)
print(dataString, end = "")
# Get the COM port for the Mark-10 Series 5
while not pressure_passed:
try:
pressure_com = input("Enter Mark-10 Series 5 COM Port #: ")
pressure_ser = serial.Serial("COM" + str(pressure_com), BAUD_RATE)
pressure_passed = True
except:
print("Invalid COM Port, please enter a valid port.\n-----")
# Get the COM port for the Arduino
while not arduino_passed:
try:
arduino_com = input("Enter Ardunio COM Port #: ")
arduino_ser = serial.Serial("COM" + str(arduino_com), BAUD_RATE)
arduino_passed = True
except:
print("Invalid COM Port, please enter a valid port.\n-----")
# Get the name for the log file
while not file_passed:
try:
file_name = input("Enter log file name: ")
# Add extension if not already given
if "." not in file_name:
file_name += ".csv"
log_file = open(file_name, "a")
# Add header row to log file
if os.stat(log_file.name).st_size == 0:
log_file.write("time (ms), pressure, angle (deg)\n")
file_passed = True
except:
print("Invalid file, or could not open the file specified.\n-----")
start = datetime.now()
# Variables to read serial input
pressure_data = ""
last_pressure = ""
arduino_data = ""
last_arduino = ""
# Main program loop
# Serial is read from byte by byte to better sync the two devices
while True:
try:
x_changed = False
y_changed = False
# Read from Mark-10 serial if available
# x is a byte read from the serial line, converted to ascii
if pressure_ser.in_waiting > 0:
x = pressure_ser.read().decode('ascii')
x_changed = True
# Read from Arduino serial if available
# y is a byte read from the serial line, converted to ascii
if arduino_ser.in_waiting > 0:
y = arduino_ser.read().decode('ascii')
y_changed = True
# If new data received, check if we should log it
if x_changed:
if x == '\n': # New line detected, log the accumulated data
if last_pressure != pressure_data:
LogData(start, last_pressure, last_arduino, log_file)
last_pressure = pressure_data
pressure_data = ""
elif x != '\r': # Otherwise, add the read character to the string
pressure_data += x
if y_changed:
if y == '\n': # New line detected, log the accumulated data
if last_arduino != arduino_data:
LogData(start, last_pressure, last_arduino, log_file)
last_arduino = arduino_data
arduino_data = ""
elif y != '\r': # Otherwise, add the read character to the string
arduino_data += y
except Exception as e:
print(e)
if arduino_ser.isOpen():
arduino_ser.close()
if pressure_ser.isOpen():
pressure_ser.close()
log_file.close()
break

How to read the thread buffer only when a new sample is available?

The following code should read the serial interface into log file. The code can log with significantly high sampling rates (yes, serial synchronization is missing). But since the code is faster than the data rate of the serial device (which should be the case), it reads duplicate points.
Clarification edit: As we can see from the source code, thread is reading the serial port and writing it to the buffer rawData. Another function getSerialData() should unpack the rawData into 4 variables and write to the log file. But as it is faster than data rate of serial device, it reads rawData even when no new reading available. I would like to make the function getSerialData() unpack rawData only when thread writes new data to rawData.
I want to make sure that the code only gets last sample from serial device. This could be done by comparing the new sample with the previous sample, but it is too slow.
There are many examples of how it is done, but not for a raw connection.
#!/usr/bin/env python
from threading import Thread
import serial
import time
import collections
import struct
import copy
import pandas as pd
import numpy as np
import sys
import os
import h5py
class serialPlot:
def __init__(self, serialPort, serialBaud, bufferLength, dataNumBytes, numVal):
self.port = serialPort
self.baud = serialBaud
#self.plotMaxLength = bufferLength
self.dataNumBytes = dataNumBytes # number of bytes for single value
self.numVal = numVal
self.rawData = bytearray(numVal * dataNumBytes)
self.data = []
for i in range(numVal): # give an array for each type of data and store them in a list
self.data.append(collections.deque(
[0] * bufferLength, maxlen=bufferLength))
self.isRun = True
self.isReceiving = False
self.thread = None
self.plotTimer = 0
self.previousTimer = 0
print('Trying to connect to: ' + str(serialPort) +
' at ' + str(serialBaud) + ' BAUD.')
try:
self.serialConnection = serial.Serial(
serialPort, serialBaud, timeout=4)
print('Connected to ' + str(serialPort) +
' at ' + str(serialBaud) + ' BAUD.')
except:
print("Failed to connect with " + str(serialPort) +
' at ' + str(serialBaud) + ' BAUD.')
def readSerialStart(self):
# start thread if not started yet
if self.thread == None:
self.thread = Thread(target=self.retrieveData)
self.thread.start()
# Block till we start receiving values
while self.isReceiving != True:
time.sleep(0.01)
def getSerialData(self, bufferLength):
hdf5Buffer = [[0 for x in range(self.numVal)] for y in range(
bufferLength)] # Create array to hold data
# Calculate time delta between data points
currentTimer = time.perf_counter_ns()
# the first reading will be erroneous
self.plotTimer = int((currentTimer - self.previousTimer) / 1000)
self.previousTimer = currentTimer
for e in range(bufferLength):
privateCopy = copy.deepcopy(self.rawData[:])
for i in range(self.numVal):
bytedata = privateCopy[(i * self.dataNumBytes):
(self.dataNumBytes + i * self.dataNumBytes)]
value, = struct.unpack('f', bytedata)
# get the latest data point and append it to our array
self.data[i] = value
for f in range(self.numVal):
hdf5Buffer[e][f] = self.data[f]
hdf5Buffer[e].insert(0, self.plotTimer)
return hdf5Buffer
def retrieveData(self): # retrieve data
time.sleep(0.1) # give some buffer time for retrieving data
self.serialConnection.reset_input_buffer() # flush input buffer
while (self.isRun):
# read n bytes into array (rawData) and return num of bytes read
self.serialConnection.readinto(self.rawData)
self.isReceiving = True
def close(self):
self.isRun = False
self.thread.join()
self.serialConnection.close()
print('Disconnected...')
def main():
time.sleep(0.1)
portName = 'COM15'
baudRate = 230400
bufferLength = 10 # number of samples buffered before saving it to HDF5
dataNumBytes = 4 # number of bytes for single data point
numData = 4 # number of data points in single sample
rawData = bytearray(numData * dataNumBytes)
s = serialPlot(portName, baudRate, bufferLength,
dataNumBytes, numData) # initializes all required variables
# Starts background thread
s.readSerialStart()
dataArray = s.getSerialData(bufferLength)
while(True):
# Prepare data to write
dataArray = s.getSerialData(bufferLength)
'''
PROCEDURE FOR WRITING LATEST DATA INTO HDF5
'''
# TESTING
print(str(dataArray[-1]), end=' \r')
s.close()
if __name__ == '__main__':
try:
main()
except KeyboardInterrupt:
print('Interrupted')
try:
sys.exit(0)
except SystemExit:
os._exit(0)

Data from serial port comes in a vertical format

I've a python script that is reading from a serial port (TXRX) using a barcode scanner, everything works well, but my issue when text comes in from the serial port comes in a vertical format, something like this:
if the barcode that I am reading has 123456, it comes in my python script as:
1
2
3
4
5
6
I've tried changing the print() options, but seems not have any luck.
import sys
import serial
ser = serial.Serial("/dev/ttyAMA0",115200,timeout=0.8)
print('serial test start ...')
if ser != None:
print('serial ready...')
else:
print('serial not ready')
sys.exit()
ser.timerout=0.8 #read time out
ser.writeTimeout = 0.8 #write time out.
try:
x = ""
while True:
t = ser.read()
if t != b'':
ser.write(t)
x = x + t
print(str(x)) #<--this one shows what it reads,line by line.
except KeyboardInterrupt:
#print(str(x)) #<--this work fine when I I terminate the loop.
if ser != None:
ser.close()
I would like the text that I am capturing to look like:
123456
After updating my code, if I add the:
try:
x = ""
while True:
t = ser.read()
if t != b'':
ser.write(t)
x = x + t
print(str(x))
except KeyboardInterrupt:
if ser != None:
ser.close()
print(str(x))
I get this result: (I am reading is X001PB45ZF from a barcode)
X
X0
X00
X001
X001P
X001PB
X001PB4
X001PB45
X001PB45Z
X001PB45ZF
If I add it outside the loop:
try:
x = ""
while True:
t = ser.read()
if t != b'':
ser.write(t)
x = x + t
except KeyboardInterrupt:
print(str(x))
if ser != None:
ser.close()
I get this result, but only when I terminate the program.
X001PB45ZF
I added this to my code inside the loop:
try:
while True:
t = ser.read()
if t != b'':
ser.write(t)
print(repr(t))
and the output now looks like this:
'X'
'0'
'0'
'1'
'P'
'B'
'4'
'5'
'Z'
'F'
'\r'
now that I see the \r at the end, I can terminate my loop, right? and capture the text as needed? I am still trying to figure out how to terminate the loop when \r is giving by the scanner...
It worked now!!
try:
x = ""
# look for \r\n. if this doesn't work
# try \x00
while '\r' not in x:
t = ser.read()
if t != b'':
ser.write(t)
x = x + t
x = x.strip() # removes whitepace from beginning and end of string
# including \r \n
print(str(x))
except KeyboardInterrupt:
#print(str(x)) #<--this work fine when I I terminate the loop.
if ser != None:
ser.close()
Now that I can capture the input on a single line, how can I add it to an infinite loop?
My goal is to read the barcode, store it in a txt or DB. The barcode is in motion mode, meaning, as soon as the camera detects movement, the barcode will activate and try to read.
I believe your code is working as expected. while True: is an infinite loop. Usually you replace True with a condition you test for True.
The trick now is to figure out how to break out of your loop. What condition can we test for? Many barcode scanners will send a carriage return (\r = b"\x0d") or line feed (\n = b"\x0a") at the end of the message. Or some other character maybe.
You might have to dig out the manual for your barcode scanner and see if you can configure it to send a termination character.
Assuming you get '\r' at the end of transmission, you could modify while loop to become:
import sys
import serial
ser = serial.Serial("/dev/ttyAMA0",115200,timeout=0.8)
fid = open('output.txt', 'w')
print('serial test start ...')
if ser != None:
print('serial ready...')
else:
print('serial not ready')
sys.exit()
ser.timerout=0.8 #read time out
ser.writeTimeout = 0.8 #write time out.
try:
x = b""
# look for \r\n. if this doesn't work
# try \x00
while b"\r" not in x:
t = ser.read()
if t != b'':
ser.write(t)
x = x + t
x = x.strip() # removes whitepace from beginning and end of string
# including \r \n
x = str(x)
fid.write(x + '\n') # write barcode to output file, one per line
except KeyboardInterrupt:
#print(str(x)) #<--this work fine when I I terminate the loop.
fid.close()
if ser != None:
ser.close()
I've redone the indents in your code to 4 spaces per indent. Like you, I prefer 2 spaces. But the official style guide says 4 and sooner or later you may have to work with someone else and everyone else uses 4.
The code above should work once through. If you want to keep it running until a keyboard interrupt occurs, you need a second while loop.
import sys
import serial
ser = serial.Serial("/dev/ttyAMA0",115200,timeout=0.8)
fid = open('output.txt', 'w')
print('serial test start ...')
if ser != None:
print('serial ready...')
else:
print('serial not ready')
sys.exit()
ser.timerout=0.8 #read time out
ser.writeTimeout = 0.8 #write time out.
try:
x = b""
# look for \r\n. if this doesn't work
# try \x00
while True: # outer while loop. keeps writing barcodes to file until
# keyboard interrupt occurs
while b"\r" not in x:
t = ser.read()
if t != b'':
ser.write(t)
x = x + t
x = x.strip() # removes whitepace from beginning and end of string
# including \r \n
x = str(x)
fid.write(x + '\n') # write barcode to output file, one per line
except KeyboardInterrupt:
#print(str(x)) #<--this work fine when I I terminate the loop.
fid.close()
if ser != None:
ser.close()
Try not printing until after the while loop is finished.
try:
x = ""
while True:
t = ser.read()
if t != b'':
ser.write(t)
x = x + t
#print(str(t))
except KeyboardInterrupt:
print(str(x))
if ser != None:
ser.close()
Just trying to give you another solution.
Instead of printing, you can try to write on stdout.
Here is a snippet for you:
try:
import sys
while True:
t = ser.read()
if t != b'':
ser.write(t)
sys.stdout.write(str(t))
If this doesn't works, probably you are receiving a '\n' in your input.
You can use str(t).replace('\n', '') if that's the case.

Why does this python program not work in IDLE, when using a raspberry pi?

The following code communicates with multiple pH modules, via the serial port. The pH modules are selected via a multiplexer via the i2c bus using an 8574N chip. When the raspberry pi has booted up the program works correctly in the terminal however, if the program was to be stop and be restarted it fails to initialize correctly. Also, this program does not work correctly in the python IDLE (selecting random devices on the multiplexer). It seems like the i2c or serial comms on the pi is not initializing correctly. For example, in IDLE when selecting a module, It brings up the incorrect module to the one that you have chosen spits out incorrect data. However, this all works fine in the terminal.
Does any one have any ideas??
Any help would be much appreciated!
from smbus import SMBus
# from itertools import cycle
import time
import serial
usbport = '/dev/ttyAMA0'
ser = serial.Serial(usbport, 9600, timeout = 2)#
line = ""
data = ""
bus = SMBus(1) # Port 1 used on REV2
print "Electronics Workshop PH Reader."
print "=========================="
global count
count = 0 #init count to 0
probevalue = ""
def inputsel(count):
bus.write_byte(0x38, 0x00)
# count = input(" Select ph unit '0 to 23'") # count now incremented during program
count = input(" Select ph unit '0 to 23'")
if (count< 16):
data_sel = count
data_sel = data_sel | 32 # or the 2 numbers together
else:
data_sel = count - 16
data_sel = data_sel | 16 # or the 2 numbers together
bus.write_byte(0x38, data_sel) # send "count"Varable out to I2c Bus
print str(data_sel)
time.sleep (1)
data_sel = 0
print "Reading Channel:" + str(count)
def write_serial(serial_data):
global ser
data = ""
line = ""
status = ""
ser.write(serial_data) # set ph unit to take one reading at a time
ser.write("\r") # set ph unit to take one reading at a time
time.sleep (1)
ser.write(serial_data) # set ph unit to take one reading at a time
ser.write("\r")
time.sleep (1)
while status != "done":
data = ser.read(1) # gets data from PH module '
if(data == "\r"): # check each bite of data to see if its a carriage return expecting "XX.XXr"
#carriage return sent diplay message and data
print "Received from sensor:" + line
status = "done"
probevalue = line
line =" "
ser.flushInput()
ser.flushOutput()
else:
# all 5 bytes of data have not been received
line = line + data # add one to the varable line
#if(data == " "):
# write_serial()
return probevalue
def main():
global serial_data
serial_data = " "
global count
probevalue = " "
count = 0
bus.write_byte(0x38, 0)
while 1:
inputsel(count) #select the input based off count variable
loop = range(0,7)
for loopcount in loop:
if ((loopcount == 0) | (loopcount == 1)): #set command to #?
serial_data = "#?\r" #set command to request id
if (loopcount == 0): #if buffer needs clearing
print "Clearing Buffer....."
else: print "Requesting ID....."
probevalue = write_serial(serial_data) #call write_serial with #? command put value into probevalue
elif (loopcount >= 2): #set r command once buffer clear and ID obtained
serial_data = "r\r" #set command to read value
if (loopcount == 2): print "Reading pH:" #output reaidng pH when loopcounter at 2
probevalue = write_serial(serial_data) #call write_serial with r command put value into probevalue
print "=========================="

Categories