import sys
import serial
import numpy as np
import matplotlib.pyplot as plt
from collections import deque
port = "COM11"
baud = 9600
timeout=1
ser = serial.Serial()
ser.port = port
ser.baudrate = baud
ser.timeout = timeout
a1 = deque([0.0]*100)
#ax = plt.axes(xlim=(0, 100), ylim=(0, 1000))
line, = plt.plot(a1)
plt.ion()
plt.ylim([0,1000])
try:
ser.open()
except:
sys.stderr.write("Error opening serial port %s\n" % (ser.portstr) )
sys.exit(1)
#ser.setRtsCts(0)
while 1:
# Read from serial port, blocking
data = ser.read(1)
# If there is more than 1 byte, read the rest
n = ser.inWaiting()
data = data + ser.read(n)
#sys.stdout.write(data)
print(a1)
a1.appendleft((data))
datatoplot = a1.pop()
line.set_ydata(a1)
plt.draw()
I am using msp430f5438a board.If I send the data with a new line between each data then I am not able to plot the data because in python the sometimes data gets printed as 78_9, 7_89,_789 where _ means space so python gives me a error cannot convert string to float. But If I say send the data from uart without any new line between them then I get a nice plot but in the plot after some irregular short intervals the plot goes to zero and then becomes fine again although I checked in hyperterminal I am not receiving any zero values
My question is:
Are the two cases I described are related to each other?What can be done to rectify this problem of plot going to zero in between?Because of this I am not getting a smooth wave.
Thanks
The problem might be in the way you handle the serial interface. As you are not parsing the serial input when it comes, it is possible that you receive two messages as follows:
1.23
4.56
7.
and
89
10.11
etc. This is because your code may split the input at any point. It may be so fast that you get one digit at a time, which is probably not what you wanted.
I suggest that if you pad your data with newlines and if the data is good in a terminal program, you use the readline method.
while 1:
# read a line from the input
line = ser.readline()
# try to make a float out of it
try:
a1.appendleft(float(line))
except ValueError:
# in case we got bad input, print it and go to the next line
print "Received an invalid line '{0}'".format(line)
continue
# do the plotting
This most probably fixes your problem.
Reading asynchronous serial line is surprisingly complicated, you usually need to parse the input in-the-fly with timeouts. Fortunately, this is done by pyserial when using readline.
Related
I have connected an Arduino UNO to my raspberry pi and want to read from a connected Sensor with a Python Script.
When I try to read the sensor data from the Arduino IDE, it works perfectly fast, but with Python it is really slow.
This is my code:
import serial
from subprocess import call
from time import sleep
ser = serial.Serial('/dev/ttyACM0')
ser.baudrate = 9600
a = 0
stop = False
file = open("PulseData/MasterArrayData.txt","w")
if(ser.isOpen() == False):
ser.open()
print("Start scanning")
while stop == False:
test = ser.readline()
try:
testInt = int(test)
if testInt > 100 and testInt < 800:
print test
file.write(str(testInt))
file.write("\n")
a = a+1
except ValueError:
print "Not an integer"
if(a == 400):
stop = True
sleep(0.1)
file.close()
call(["./main", "PulseData/MasterArrayData.txt"])
I already tried to use a higher baud rate, or a shorter sleeping time, without success.
I've read that the speed can be improved with PyTTY, but unfortunately I didn't find any documentation about that.
I appreciate any help.
From comment by jasonharper:
Sleeping for a tenth of a second between readings is an absolute guarantee that you will get less than 10 readings per second. The occasional garbage value you're getting happens when the serial port buffer inevitably overflows and characters get lost.
I am having 7 sensors which is connected to a micro controller , the controller a sends data to a pc using serial port , i am trying to plot the sensors values in real-time using python drawnow function , can anybody help me in giving the correct syntax for the same to plot the all the sensors in the same figure
How about this for 4 sensors:
import time
import matplotlib.pyplot as plt
from drawnow import *
sensors = 4
x = dict([(s,[]) for s in range(0,sensors)]) # initialize dictionary of sensor stream values
def makePlot():
plt.subplot(411)
plt.plot(x[0],'r')
plt.subplot(412)
plt.plot(x[1],'g')
plt.subplot(413)
plt.plot(x[2],'b')
plt.subplot(414)
plt.plot(x[3],'c')
for i in range(0,100): # simulate passage of time
time.sleep(1) # 1-sec delay for each loop
for s in range(0,sensors):
x[s].append(i*s)
drawnow(makePlot)
Hi I am trying to make a scatter plot and annotate data points with real value of each point
but just the yLabel value
it mean take real value of the point and plot it near to the point
python code
import serial # import Serial Library
import time #import time
import numpy # Import numpy
import matplotlib.pyplot as plt #import matplotlib library
from drawnow import *
temperature= []
vitesse= []
charge= []
current= []
arduinoData = serial.Serial('com5', 9600) #Creating our serial object named arduinoData
plt.ion() #Tell matplotlib you want interactive mode to plot live data
cnt=0
def makeFig(): #Create a function that makes our desired plot
plt.subplot(2,2,1)
plt.title('Live Streaming Temperature Sensor Data')
plt.ylabel('Temperature C')
plt.grid(True)
plt.plot(temperature, 'ro-')
plt.subplot(2,2,2)
plt.title('Live Streaming Speed Sensor Data')
plt.ylabel('Speed KM/H')
plt.grid(True)
plt.plot(vitesse, 'bo-')
plt.subplot(2,2,3)
plt.title('Live Streaming SOC Sensor Data')
plt.ylabel('Battery Charge %')
plt.grid(True)
plt.plot(charge, 'go-')
plt.subplot(2,2,4)
plt.title('Live Streaming Current Sensor Data')
plt.ylabel('Current A')
plt.grid(True)
plt.plot(current, 'yo-')
while True: # While loop that loops forever
while (arduinoData.inWaiting()==0): #Wait here until there is data
pass #do nothing
arduinoString = arduinoData.readline() #read the line of text from the serial port
dataArray = arduinoString.split(';') #Split it into an array called dataArray
temp = float (dataArray[0])
vite = float (dataArray[1])
char = float (dataArray[2])
curr = float (dataArray[3])
temperature.append(temp) #Build our temperature array by appending temp readings
vitesse.append(vite) #Build our vitesse array by appending temp readings
charge.append(char) #Build our charge array by appending temp readings
current.append(curr) #Build our current array by appending temp readings
drawnow(makeFig) #Call drawnow to update our live graph
plt.pause(0.00001)
cnt=cnt+1
if(cnt>50):
temperature.pop(0)
vitesse.pop(0)
charge.pop(0)
current.pop(0)
any ideas ?????
use the annotate() function to create labels attached to specific points in your plot.
see http://matplotlib.org/users/annotations_intro.html
and http://matplotlib.org/examples/pylab_examples/annotation_demo2.html
If I understood what you were looking for, you should use plt.ticklabel_format(useOffset=False) at the end of every subplot. So that, you are going to fix the y scale. Use plt.ylim(m,n) to set the limits of the y axe, where "m" is the beggining of the axe and "n" is the end.
I am sending serial data from uart to pc and trying to plot sine wave(using Python) sent from function generator through ADC12 of MSP430F5438A.
I am able to plot the wave for lower sampling frequencies(<120Hz) but when I increase the sampling frequency the digits get concatenated i.e. if two values 2563 , 2879 are sent through uart then python reads them as 25632879. So, I am not able to plot the graph as values are not correct.
I am sending the values without new line between them, if I send with new line then the values are not read correctly - python reads them with space in between so then again I get another error: could not convert string to float.
I tried data = ser.readline() as well but no luck
I am attaching the code below.Please see if anything can be done to solve this problem.
import sys
import serial
import numpy as np
import matplotlib.pyplot as plt
from collections import deque
port = "COM11"
baud = 9600
timeout=1
ser = serial.Serial()
ser.port = port
ser.baudrate = baud
ser.timeout = timeout
a1 = deque([0.0]*100)
#ax = plt.axes(xlim=(0, 100), ylim=(0, 1000))
line, = plt.plot(a1)
plt.ion()
plt.ylim([0,1000])
try:
ser.open()
except:
sys.stderr.write("Error opening serial port %s\n" % (ser.portstr) )
sys.exit(1)
#ser.setRtsCts(0)
while 1:
# Read from serial port, blocking
data = ser.read(1)
# If there is more than 1 byte, read the rest
n = ser.inWaiting()
data = data + ser.read(n)
#sys.stdout.write(data)
print(a1)
a1.appendleft((data))
datatoplot = a1.pop()
line.set_ydata(a1)
plt.draw()
Thanks
I can think of two ways for doing this reliably:
Use a delimiter . All you need is to parse the value correctly:
line = serial.readline()
reading = int(line)
If you don't want to use a delimiter then send a formatted reading from the msp:
uint8_t buffer[5];
snprintf(buffer, 4, "%04d", reading);
uart_print(buffer);
this way you will always get 4 characters per reading, so you can do this in the python code:
line = serial.read(4)
reading = int(line)
I'd still go for the first alternative, though.
I have written a python 2.7 script to retrieve all my historical data from Xively.
Originally I wrote it in C#, and it works perfectly.
I am limiting the request to 6 hour blocks, to retrieve all stored data.
My version in Python is as follows:
requestString = 'http://api.xively.com/v2/feeds/41189/datastreams/0001.csv?key=YcfzZVxtXxxxxxxxxxxORnVu_dMQ&start=' + requestDate + '&duration=6hours&interval=0&per_page=1000' response = urllib2.urlopen(requestString).read()
The request date is in the correct format, I compared the full c# requestString version and the python one.
Using the above request, I only get 101 lines of data, which equates to a few minutes of results.
My suspicion is that it is the .read() function, it returns about 34k of characters which is far less than the c# version. I tried adding 100000 as an argument to the ad function, but no change in result.
Left another solution wrote in Python 2.7 too.
In my case, got data each 30 minutes because many sensors sent values every minute and Xively API has limited half hour of data to this sent frequency.
It's general module:
for day in datespan(start_datetime, end_datetime, deltatime): # loop increasing deltatime to star_datetime until finish
while(True): # assurance correct retrieval data
try:
response = urllib2.urlopen('https://api.xively.com/v2/feeds/'+str(feed)+'.csv?key='+apikey_xively+'&start='+ day.strftime("%Y-%m-%dT%H:%M:%SZ")+'&interval='+str(interval)+'&duration='+duration) # get data
break
except:
time.sleep(0.3)
raise # try again
cr = csv.reader(response) # return data in columns
print '.'
for row in cr:
if row[0] in id: # choose desired data
f.write(row[0]+","+row[1]+","+row[2]+"\n") # write "id,timestamp,value"
The full script you can find it here: https://github.com/CarlosRufo/scripts/blob/master/python/retrievalDataXively.py
Hope you might help, delighted to answer any questions :)