Faster method for clearing serial port on pyserial - python

I have a serial port communication between Windows and an Arduino Due board. A camera is sending data over the serial port. Once an image is captured, analog values from potentiometers are read. The application is in real-time, so it happens continuously.
'COM1' is the serial port that reads data from the camera and the potentiometers. The code uses Serial.close and Serial.open to stop the data stream after retrieving the image. When I try to use reset_input_buffer() without closing and opening the serial port, I still get a random sequence of bad data coming in after the image is captured. Does anyone know why? Currently, I use time.sleep() method so Serial.close() does not get hung up. Is there a faster way to do this?
python code
def readData(port_dev, width, height, regId=0xD0, regVal=0xD0):
ser = serial.Serial(port_dev, 5000000)
ser.write(bytearray([regId, regVal])) # tells Arduino to begin capturing
if regId == 0xD0 and regVal == 0xD0:
dataY = ser.read(size=width * height)
index = 0
bitmap = list()
for y in range(height):
for x in range(width):
Y = dataY[index]
bitmap.append(Y)
index += 1
ser.write(bytearray([0x99, 0x99])) # Stop reading data
npimage = np.array(bitmap)
arrImage = np.resize(npimage, [height, width])
time.sleep(1)
ser.reset_input_buffer()
time.sleep(.5)
ser.reset_output_buffer()
time.sleep(.5)
ser.close()
ser.open()
data = getValues(ser)
ser.close()
return arrImage, data
def getValues(ser):
numPoints = 3 # number of datapoints to read
dataList = [0] * numPoints # initialize the list we will write to
ser.write(bytearray([0x09, 0x09])) # signal arduino to read pot values
for i in range(0, numPoints):
data = ser.readline()
dataList[i] = data
return dataList
Arduino code
void loop() {
char buffer_USB[2];
if(SerialUSB.available() > 0) {
SerialUSB.readBytes(buffer_USB,2);
if(buffer_USB[0] == 0xD0 && buffer_USB[1] == 0xD0) {
captureImg(WIDTH,HEIGHT,0);
captureImg(WIDTH,HEIGHT,1);
}
if(buffer_USB[0] == 0x09 && buffer_USB[1] == 0x09) {
data1 = analogRead(analogPin1);
SerialUSB.println(data1);
data2 = analogRead(analogPin2);
SerialUSB.println(data2);
data3 = analogRead(analogPin3);
SerialUSB.println(data3);
}
}
}

Related

I have an Arduino Rs485 network, and am trying to add a in Raspberry Pi. The Pi is able to send messages to the Arduino's, but is unable to receive

My network consists of multiple Arduino's connected to the Max485. These Arduino's can talk perfectly between each other.
I am currently attempting to wire a Raspberry Pi into the network. I have been following this tutorial.
I have enabled the UART pins, and disabled shell over serial.
For testing, I have wired TX(GPIO 14/pin 8) to DI on the MAX485, RX(GPIO 15/pin 10) to RO, GPIO 4 (pin 7) to DE & RE. It is also powering both MAX485 chips, and both chips are grounded to it.
On the arduino side, I am currently using a Mega. It has TX3 to DI, RX3 to RO, and pin 2 to DE/RE. These two devices are the only ones on this network.
Raspi Python:
import time
import serial
import RPi.GPIO as GPIO
from time import sleep
GPIO.setwarnings(False)
GPIO.setmode(GPIO.BOARD)
#sets pin 7 on the GPIO as DE/RE
GPIO.setup(7, GPIO.OUT, initial=GPIO.LOW)
rs = serial.Serial(port='/dev/serial0', timeout=5, baudrate=9600)
data = bytearray()
msgIn = bytearray()
addr = 1
# Splits each byte into two, then unfold each half-byte to make a full byte.
# The slave will take this data, and fold it back to readable form
# This is to ensure anything being read by the slave is actual data, not noise.
def foldOpen(where, what):
hb = what >> 4
where.append((hb << 4) | (hb ^ 0x0F))
lb = what & 0x0F
where.append((lb << 4) | (lb ^ 0x0F))
# Unfolds the folded data
def unFold():
sByte, cByte = False, 0
timeout = time.perf_counter()
while((time.perf_counter() - timeout) < 1):
inByte = rs.read()
if((inByte >> 4) != ((inByte & 0x0F) ^ 0x0F)):
return 0
inByte >>= 4
if(sByte):
cByte <<= 4
cByte |= inByte
return cByte
else:
cByte = inByte
sByte = True
timeout = time.perf_counter()
return 0
# add's each piece of data into the crc
def AddCrc(crc, n):
for i in range(0, 8):
mix = (n ^ crc) & 0x01
crc >>= 1
if(mix):
crc ^= 0x8C
n >>= 1
return crc & 0xFF
#Receives a start bit, then address, then data length, then data, and finally crc.
#If everything is formatted correctly, the right amound of data is passed and crc correct
#it will return true
def recvMsg(msg):
msgState = crc = msgL = 0
timeout = time.perf_counter()
while(msgState <= 4):
if(rs.in_waiting > 0):
if(msgState < 1):
inByte = rs.read()
sleep(1)
else:
inByte = unFold()
if(msgState == 4):
for x in msg:
crc = AddCrc(crc, x)
if(crc == inByte):
return 1
elif(msgState == 3):
msg.append = inByte
if(len(msg) == msgL):
msgState = 4
elif(msgState == 2):
msgL = inByte
msgState = 3
elif(msgState == 1):
if(inByte == addr):
msgState = 2
else:
msgState = 5
elif(msgState == 0):
print('Start bit is ')
print(inByte)
if(inByte == 2):
print('accepted')
msgState = 1
if((time.perf_counter() - timeout) >= 5):
msgState = 5
#Sends a message, starting with start bit (2), addr, msg length, data, and crc
def sendMsg(where, size, what):
GPIO.output(7, GPIO.HIGH)
msg = bytearray()
crc = 0
msg.append(2)
foldOpen(msg, where)
foldOpen(msg, size)
for x in what:
foldOpen(msg, x)
for x in what:
crc = AddCrc(crc, x)
foldOpen(msg, crc)
rs.write(msg)
rs.flush()
GPIO.output(7, GPIO.LOW)
#creating random data to send to slave for testing
data = bytearray()
info = ord('A')
info2 = 45
data.append(info)
data.append(info2)
sendMsg(2, len(data), data)
#reads 1 byte, just so I know I made a connection
timer = time.perf_counter()
while((time.perf_counter() - timer) < 10):
if(rs.in_waiting):
inByte = rs.read(1)
print(inByte)
Arduino Code:
#include <RS485_Comm.h>
byte enablePin = 2;
byte check = 0;
size_t rsWrite (const byte what) {
Serial3.write (what);
Serial3.flush();
}
bool rsAvailable () {
return Serial3.available ();
}
int rsRead () {
return Serial3.read ();
}
RS485 myChannel (rsWrite, rsAvailable, rsRead, 20, 2, 2, 1);
//name(Write CB, AvailableCB, ReadCB, buffer, Epin, Addr, Debug)
void setup() {
Serial.begin(9600);
Serial3.begin(9600);
myChannel.begin();
Serial.print("A-OK");
}
void loop() {
if (myChannel.recvMsg()) {
if (myChannel.getMsg()[0] == 'A') {
Serial.print("A-OK");
byte msgOut[] = "A";
myChannel.sendMsg(msgOut, sizeof(msgOut), 1);
}
}
}
Again, I can send messages from the Raspi to the Arduino's. The same Arduino, wired in the same configuration, can talk back and forth with other Arduino's on the network.
I just can't get any info from an Arduino to the raspberry pi. rs.read(1) returns nothing, or some random noise. Where am I going wrong?
Reading your question it looks like you are feeding the GPIO pins on you RPi with 5V logic.
That might be OK for a quick test but in the long run, something will probably be damaged. You are better off using 3.3V as the supply voltage for the MAX485 if your converter supports it (some of these boards work only with 5V and others seem to be dual voltage).
What seems to be wrong is the UART RX on your RPi, maybe you should verify it's still good. You can follow these steps:
1)Remove all wires on your RPi 40 pin connector.
2)Short RX to TX on the RPi's UART (pins 8 shorted with pin 10).
3)Run minicom: sudo minicom -D /dev/serial0
4)Type anything on the screen. If you can see what you are typing it means your UART is working.
5)Hit CTRL+A and then E to activate echo. Type something else on the screen and now you should see each keystroke appear twice.
6)Hit CTRL+A and then X to quit minicom.
There are many tutorials with more details if you need to troubleshoot your UART further. See, for instance, here.
If these UART tests are successful you can proceed to connect the RPi to the Arduino and run minicom again, this time you can choose the baud rate with:
sudo minicom -D /dev/serial0 -b 9600
Then switch on your Arduino to see if you receive.
If you do but your Python code still reports nothing you can be sure the problem is in there (your code looks fine if a bit overly complicated for such a simple task).

Problem with capturing data from serial via jupyter notebook

This is my arduino code
I closed my arduino ide while running the python code, learned from past mistakes that the com port can only be accessed by one application at a time
int analogPin = A0;
int ledPin = 13;
int val = 0;
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
int ledPin = 13;
}
void loop() {
// put your main code here, to run repeatedly:
if( millis() % 20 != 0 ) // so a reading is taken every 2 milliseconds
return;
// turn the ledPin on
digitalWrite(ledPin, HIGH);
val = analogRead(analogPin); // read the input pin
Serial.println(val); // debug value
// turn the ledPin off:
digitalWrite(ledPin, LOW);
}
it works correctly and give me readings every fixed interval
and could be graphed or viewed in the arduino ide integrated serial monitor
This is my python code
import time
import logging
import threading
import numpy as np
import cv2
import serial
# configure serial for reading from arduino
# set baudrate and port name accordingly
COM = 'COM6'
baudRate = 9600
sensorDataFileName = r"C:\Users\cy316\Documents\FYP\SensorData.txt"
videoCaptureName = "CameraCapture.mp4"
captureTime = 10
fourcc = cv2.VideoWriter_fourcc(*'MP4V')
ser = serial.Serial(COM, baudRate)
cap = cv2.VideoCapture(0) # 0 for video camera
time.sleep(10) #allow time for arduino to reset
# function for writing the arduino readings into a file
def sensorreading(ser, stoptime):
sensordatafile = open(sensorDataFileName,"w+")
starttime = time.time()
data = []
while ((starttime-time.time())%0.02 == 0): # run every 20ms, better than delay which might or might not be on time
b = ser.readline()
string_n = b.decode()
string = string_n.rstrip()
flt = float(string)
print(flt)
data.append(flt) # add to the end of data list
time.sleep(0.02) # wait for arduino to take new reading
if (starttime-time.time()>stoptime): # if i reach stoptime, return
sensordatafile.close()
return
# function for video capture
def videocapture(cap, stoptime):
starttime = time.time()
# check if camera is available
if (cap.isOpened() == False):
print("Unable to read camera feed")
return
frame_width = int(cap.get(3))
frame_height = int(cap.get(4))
out = cv2.VideoWriter(videoCaptureName,fourcc, 30, (frame_height,frame_width))
cv2.imshow('Frame',frame)
while(starttime-time.time()<stoptime):
ret, frame = cap.read()
out.write(frame)
out.release()
return
# testcell
sensorreading(ser,captureTime)
returned only one value, once, and not my specified timing
also failed to write it into the txt file
# The cell i want to run for simultaneous sensor and camera values
SensorThread = threading.Thread(target=sensorreading(ser,captureTime), args=(1,))
VideoThread = threading.Thread(videocapture(cap,captureTime), args=(1,))
SensorThread.start()
VideoThread.start()
is it because i am running this off jupyter notebook and not something like pycharm?
or is it something to do with the serial code i am missing

Python to Arduino Serial Communication Yields Semi-random Results

I am trying to get an Arduino to give data to Python form a sensor. Then when this data is received into Python, Python needs to send a signal back to the Arduino to get a motor to react to the incoming data. Presently my code results in this, however if I look at the data I will have random spikes of data that do not coincide with the scenario. For example I will place a 5 newton load on the sensor and it will read 5 newtons for several iterations, then it will spike to something that is random. I have looked at just the Arduino output in the Arduino IDE and it is a steady and constant stream of data. I have looked at what Python receives without having the out going signal and that gives the same thing as the Arduino. I don't know why when I have to two send information back and forth I get this issue. If anyone has a better solution on how to get good readings I would appreciate it.
Python
import serial
import csv
import time
import sys
from time import localtime, strftime
import warnings
import serial.tools.list_ports
__author__ = 'Matt Munn'
arduino_ports = [
p.device
for p in serial.tools.list_ports.comports()
if 'Arduino' in p.description
]
if not arduino_ports:
raise IOError("No Arduino found - is it plugged in? If so, restart computer.")
if len(arduino_ports) > 1:
warnings.warn('Multiple Arduinos found - using the first')
Arduino = serial.Serial(arduino_ports[0],9600,timeout=1)
time.sleep(2)
start_time=time.time()
Force = []
Actuator_Signal=[]
Cycle_Count=[]
state = True
Up = True
numPoints = 10
ForceList = [0]*numPoints
AvgForce = 0
#This allows the user to input test conditions.
Force_Input = input("What is the force you want to test with in Newtons?")
Cycles = input("How many cycles would you like to run?")
Material = input("What kind of material is to be tested?")
print("The test will be conducted with", Force_Input, " Newtons of force and for a total of" , Cycles, " cycles.", " on", Material)
print ("REMOVE HANDS AND OTHER OBJECTS AWAY FROM AREA OF OPERATION.")
print("Testing will begin.")
time.sleep(5)
start_time = time.time()
#This creates the unique file for saving test result data.
outputFileName = " Cycle_Pull_Test_#.csv"
outputFileName = outputFileName.replace("#", strftime("%Y-%m-%d_%H %M %S", localtime()))
with open(outputFileName, 'w',newline='') as outfile:
HeaderNames = ['Material','Newtons','Time']
outfileWrite = csv.DictWriter(outfile, fieldnames = HeaderNames)
outfileWrite.writeheader()
#This takes the data from the arduino and interprits it.
while True:
while (Arduino.inWaiting()==0):
pass
try:
data = Arduino.readline()
dataarray = data.decode().rstrip().split(',')
for i in range(0,numPoints):
Force = abs(round(float(dataarray[0]),3))
ForceList[i] = Force
AvgForce = round((sum(ForceList)/numPoints),3)
elapsed_time = round(time.time()-start_time,3)
print (AvgForce, elapsed_time)
#This Controls the actuators direction based on the force input on the loadcell.
if AvgForce > float(Force_Input):
Up = False
state = True
Arduino.write(b'd')
else:
Arduino.write(b'u')
Up = True
if state == True and Up == True:
state = False
Cycles = float(Cycles) + 1
if Cycles >= float(Cycle_Count[0]):
Arduino.write(b'o')
print("Testing is done.")
time.sleep(1)
sys.exit("All the data will be saved now")
except (KeyboardInterrupt, SystemExit,IndexError,ValueError):
pass
#This writes the data from the loadcell to a csv file for future use.
outfileWrite.writerow({'Material' : Material, 'Newtons' : AvgForce, 'Time' :elapsed_time })
Arduino
#include <HX711_ADC.h> // HX711 Library
#include "CytronMotorDriver.h"
// Configure the motor driver.
CytronMD motor(PWM_DIR, 3, 2); // PWM = Pin 3, DIR = Pin 2.
int up = HIGH;
int down = LOW;
int dstate = up;
int python_direction = 0;
float interval = 12000;
float pretime= 0;
float curtime = 0;
HX711_ADC LoadCell(11, 12);
float force;
float calforce;
float newtons;
void setup() {
Serial.begin(9600);
LoadCell.begin();
LoadCell.start(2000); // tare preciscion can be improved by adding a few seconds of stabilising time
LoadCell.setCalFactor(100.0); // user set calibration factor (float)
}
void loop() {
LoadCell.update();
forceRead();
actuatorControl();
}void forceRead()
{
force = LoadCell.getData();
force = (force/285); // Force in (N) // 285 is conversion factor
calforce = (-1.0389*force)+0.0181, // This is in lbs
newtons = 4.45*calforce;
Serial.println(newtons);
//receive from serial terminal for tare
if (Serial.available() > 0) {
char inByte = Serial.read();
if (inByte == 't') LoadCell.tareNoDelay();
}
}
void actuatorControl(){
if (Serial.available()>0){
python_direction = Serial.read();
Serial.print (python_direction);
Serial.print (",");
}
if (python_direction == 'd'){
motor.setSpeed(255); // Run forward at full speed.
}
if (python_direction == 0){
motor.setSpeed(0); // Stop motor.
}
if (python_direction == 'u'){
motor.setSpeed(-255); // Run backward at full speed.
}
}

Reducing Lag between Arduino and Python

I have 3 Arduino-sensor nodes connected to a PC running Python, with XBee Series 1 radios as tools for wireless communications. The baud rate is set at 9600, and all address (ATDL, ATDH, ATMY) are set correctly as all the Arduino-sensor nodes are able to send data correctly to the XBee coordinator connected to my PC running on Python. The Xbee radios connected to the respective Arduinos (there are 2 Arduino Unos and 1 Arduino Nano) are configured as End Devices.
I recently found a problem in that any changes effected at the Arduino will lag by 5 seconds when it reaches the PC, and written to a CSV file. For example, I am reading the state of a pin on one of the Arduinos, and writing this state to a CSV file after it has been transmitted via XBee to my computer. However, I realized that when I effected the state change at 08:30:30 (HH:MM:SS), the change was only reflected in the CSV file at 08:30:35 (HH:MM:SS).
May I ask why is this the case and how should I resolve it? I have the following codes for Arduino and Python respectively.
Arduino (these codes are roughly the same across the 3 Arduino nodes):
#include <SoftwareSerial.h>
#define IR 10 // IR sensor at D10 position
#define pirPin 9 // Input for HC-S501
#define LEDPinPIR 12 // LED at Pin 12 (PIR)
#define lightLED 11 // LED at Pin 11 (Relay, Neg.Logic - ON = Relay off)
SoftwareSerial xbee(2, 3); // RX, TX
int pirValue; // Place to store read PIR Value
int pirNum = 0;
int pirNumyes = 0;
int pirNumno = 0;
int sw_door = 0; //sw_door has been updated to "sw_relay" w.e.f 27-Feb-2018
int IR_val = 0;
char incomingByte;
unsigned long prevMillis = 0;
void setup() {
Serial.begin(9600);
xbee.begin(9600);
pinMode(pirPin, INPUT); // PIR sensor
pinMode(LEDPinPIR, OUTPUT); // Ultrasound sensor indicator
pinMode(lightLED, OUTPUT); // LED at Pin 11 (Relay, Neg.Logic - ON = Relay off)
pinMode(SW, INPUT); // Switch
digitalWrite(SW, LOW);
digitalWrite(LEDPinPIR, LOW);
}
void loop() {
unsigned long currentMillis = millis();
if((unsigned long)currentMillis - prevMillis == 1000){
//IR sensor "d" refers to door
if (digitalRead(IR) == LOW){
IR_val = 1;
String ID = "d";
String IRID = ID + IR_val;
Serial.print(IRID);
Serial.print(',');
xbee.print(IRID);
xbee.print(',');
}
else{
IR_val = 0;
String ID = "d";
String IRID = ID + IR_val;
Serial.print(IRID);
Serial.print(',');
xbee.print(IRID);
xbee.print(',');
}
// Motion sensor
pirValue = digitalRead(pirPin);
if (pirValue == HIGH) {
pirNumyes = 1;
Serial.print(pirNumyes);
Serial.print(',');
xbee.print(pirNumyes);
xbee.print(',');
digitalWrite(LEDPinPIR, HIGH);
}
else {
pirNumno = 0;
Serial.print(pirNumno);
Serial.print(',');
xbee.print(pirNumno);
xbee.print(',');
digitalWrite(LEDPinPIR, LOW);
}
// Switch
if(digitalRead(lightLED)== HIGH){
sw_door = 0;
Serial.print(sw_door);
Serial.println(',');
xbee.print(sw_door);
xbee.println(',');
}
else{
sw_door = 1;
Serial.print(sw_door);
Serial.println(',');
xbee.print(sw_door);
xbee.println(',');
}
prevMillis = currentMillis;
}
// Xbee to Arduino Added: 18-Feb-2018
if (xbee.available()){
incomingByte = xbee.read();
if(incomingByte == '1'){
digitalWrite(lightLED, HIGH);
//xbee.println("OK");
}
else if(incomingByte == '0'){
digitalWrite(lightLED, LOW);
//xbee.println("OK");
}
}
}
Python:
import threading
import time
import serial
import csv
# Arduino; Arduino is now replaced by XBee modules
arduino = serial.Serial('COM18', 9600, timeout=1) # Open serial port.
def acquire_data():
while True:
try:
data_in = arduino.readline() # read serial data from Arduino
except:
pass
data_stripped = data_in.strip() # Removes spaces and \n
for data_stripped in arduino:
if data_stripped.startswith('b') and data_stripped.count(
',') == 3: # first char identifies where data is coming from; count commas to double-check incoming string
field = data_stripped.split(',') # split data to be put into 'boxes'
bed_sen = field[0] + ',' + field[1] + ',' + field[2] # We have 3 data sensor fields
bed_sen_fill = True # A flag to show that this if-statement has been completed
if data_stripped.startswith('t') and data_stripped.count(',') == 3:
field = data_stripped.split(',')
table_sen = field[0] + ',' + field[1] + ',' + field[2]
table_sen_fill = True
if data_stripped.startswith('d') and data_stripped.count(',') == 3:
field = data_stripped.split(',')
door_sen = field[0] + ',' + field[1] + ',' + field[2]
door_sen_fill = True
try:
if bed_sen_fill == True and table_sen_fill == True and door_sen_fill == True:
data_combi = bed_sen + ',' + table_sen + ',' + door_sen
break
except:
pass
if data_combi:
datasplit = data_combi.split(",")
field1 = datasplit[0]
field2 = datasplit[1]
field3 = datasplit[2]
field4 = datasplit[3]
field5 = datasplit[4]
field6 = datasplit[5]
field7 = datasplit[6]
field8 = datasplit[7]
field9 = datasplit[8]
with open('abs_testing.csv', 'ab') as csvfile: # 'ab' to remove newline char after each print
writer = csv.writer(csvfile)
sensor_fields = [field1, field2, field3, field4, field5, field6, field7, field8, field9,
time.strftime("%H%M%S")]
writer.writerow(sensor_fields)
time.sleep(1)
def counting():
while True:
sum = 3 + 2
sum2 = sum*8
print sum2
time.sleep(0.2)
def on_light():
strin = '1'
arduino.write(strin.encode())
print "Confirm ON"
def off_light():
strin = '0'
arduino.write(strin.encode())
print "Confirm OFF"
# now threading1 runs regardless of user input
threading1 = threading.Thread(target = acquire_data)
threading2 = threading.Thread(target = counting)
threading1.daemon = False # May remove later. Unsure at the moment.
threading2.daemon = False # May remove later. Unsure at the moment.
threading1.start()
threading2.start()
while True:
if raw_input() == 't':
on_light()
print "ON"
if raw_input() == 'r':
off_light()
print "OFF"
time.sleep(1)
Multithreading is implemented here by a silly operation that finds what is 8*5, because later, this will be expanded to a real-time machine learning function that determines when the lights should turn on/off. The raw_input() functions are proofs that data can be relayed back to the Arduino-sensor node.
Thank you so much for your help! :)
A few thoughts on possible improvements:
Increase Serial() baud rate to 115200 on Arduino.
Increase XBee baud rate to 115200 on Arduino (or as high as possible with software serial).
Increase baud rate to 115200 on PC running Python (it can definitely handle that rate, and the rates on each device don't need to match).
Remove or reduce the sleep(1) in acquire_data().
Check the sleep configuration on the end devices. Is it possible they're sleeping for seconds at a time? Can you configure them as routers to eliminate that as a possible cause of delays?
Change your sending code to create a single string (see below) and then send it. Then you could easily send it to the XBee first and the Serial output separately. If the Serial() interface isn't buffered, you could be timing your XBee packet out (see ATRO setting) and having your response sent as multiple packets (which is less efficient). Try increasing ATRO from its default of 3 to 20 and see if that helps.
Right now you send every second. What if you checked I/O more frequently and only sent responses when an input changed (or it had been 5 seconds since the last transmission)?
Your sending every second, so on average you'll send 0.5 seconds after an I/O change. You could add some timing code on the Arduino to print out how many milliseconds it takes to assemble and send your data (which could account for some of the delay). Or try this replacement code:
char buffer[16];
pirValue = digitalRead(pirPin);
digitalWrite(LEDPirPIN, pinValue);
sprintf(buffer, "d%u,%u,%u,\n",
digitalRead(IR) == LOW,
pirValue == HIGH,
digitalRead(lightLED) == LOW);
xbee.print(buffer);
Serial.print(buffer);

Use only the most recent value read from an Arduino in a python script, not the values stored in the buffer?

I have been working on a project using Python to read values from an arduino and then control video cameras. The Arduino controls two ultrasonic sensors and reports distance in cm. The python script then reads the distances from the Arduino using ser.readline(). When the script reads values outside the range everything works fine. However if it goes into the loop for the distance inside the required range it works correctly once and then reads old values from the Arduino instead of current "live" values which causes it to continue the record loop instead of exiting the loop. What can I do to get rid of the old values in the buffer and only read the most current value? I have found several methods and tested them but so far no luck.
Here is the code I am using (i know its not well written but its my first try using python and writing code outside of matlab)
import sys, time
import serial
import cv
import os
from time import strftime
#Create window for Camera 0
cv.NamedWindow("Camera 0", cv.CV_WINDOW_AUTOSIZE)
capture0 = cv.CreateCameraCapture(2)
cv.ResizeWindow("Camera 1", 640, 480)
cv.MoveWindow("Camera 0", 0, 0)
#Create window for Camera 1
cv.NamedWindow("Camera 1", cv.CV_WINDOW_AUTOSIZE)
capture1 = cv.CreateCameraCapture(1)
cv.MoveWindow("Camera 1", 150, 150)
#Initialize connection to Arduino
arduino = serial.Serial('COM12', 9600)
connected = False
#Confirm that Arduino is connected and software is able to read inputs
while not connected:
serin = arduino.readline()
connected = True
f = 'Sensor Connected'
print f
'''#Dummy variables for testing
value1 = 145
value2 = 30'''
#Initialize video record on as false (0)
vid = 0
#Initialize counter
counter_vid = 0
counter = 0
Accel = 1
def Camera0():
frame0=cv.QueryFrame(capture0)
cv.WriteFrame(writer0,frame0)
cv.ShowImage("Camera 0", frame0)
def Camera1():
frame1=cv.QueryFrame(capture1)
cv.WriteFrame(writer1,frame1)
cv.ShowImage("Camera 1", frame1)
while True:
status = arduino.readline()
value1=int((status[6:10]))-1000
value2=int((status[17:21]))-1000
print(value1)
print(value2)
if value1>180 and value2>180 and vid==0:
vid = 0
elif value1>180 and value2>180 and vid==1:
vid = 0
elif value1<180 and vid==0 or value2<180 and vid==0:
filename0 = strftime("OUTPUT\%Y_%m_%d %H_%M_%S") + " camera0.avi"
writer0=cv.CreateVideoWriter(filename0, 1, 15.0, (640,480), is_color=1)
filename1 = strftime("OUTPUT\%Y_%m_%d %H_%M_%S") + " camera1.avi"
writer1=cv.CreateVideoWriter(filename1, 1, 15.0, (640,480), is_color=1)
vid=1
while counter_vid<25 and vid==1:
Camera0()
Camera1()
counter_vid += 1
print(counter_vid)
cv.WaitKey(10)
else:
while counter_vid<25 and vid==1:
Camera0()
Camera1()
counter_vid += 1
print(counter_vid)
cv.WaitKey(10)
cv.WaitKey(25)
counter_vid = 0
counter += 1
print('End of Loop Counter')
print(counter)
You're right about the buffer filling up. You need a way to always get the most recent value out of the buffer.
I would suggest replacing this:
status = arduino.readline()
with this:
status = getLatestStatus()
and then further up towards the top, by your camera functions:
def getLatestStatus():
while arduino.inWaiting() > 0:
status = arduino.readline()
return status
This function getLatestStatus will go through the entire buffer every time it is called and only return the latest status, disregarding all the statuses returned in the meantime.
Your other option is to modify the "firmware" for your arduino to return a distance sensor value every time it receives a command, (say "M\n") so that way you don't have to worry about buffer problems. That's what I did for an arduino-powered ultrasonic distance device and I felt it was cleaner than the "read through the entire buffer" solution. It will introduce a bit more latency into your distance measurement though.

Categories