Live plotting data from the serial port using Python, Matplotlib and an Arduino

Yesterday I set out to figure out how to live plot data measured by my Arduino using Python and Matplotlib. While there is plenty information and code how to achieve this, I missed some easy and understandable code. So how to do this:

First we need some Arduino sketch that uses the serial port to send data. I’ve used some temperature setup from the Arduino Starter Kit found here:
http://www.oomlout.com/oom.php/products/ardx/circ-10

Now let’s take a look at the python code. You need pySerial, numpy and matplotlib of course.

[python]import serial
import numpy as np
from matplotlib import pyplot as plt
[/python]

Then you open the connection to the serial port:

[python firstline=“4”]
ser = serial.Serial(‘/dev/ttyACM0’, 9600)
[/python]

Now we need to activate matplotlib’s interactive mode:

[python firstline=“6”]
plt.ion() # set plot to animated
[/python]

After this we select the range of the x-axis and fill those 50 values with zeros:

[python firstline=“8”]
ydata = [0] * 50
[/python]

Now we start the plot. To make matplotlib aware of more incoming data don’t forget the comma after ‘line’:

[python firstline=“9”]
ax1=plt.axes()

# make plot
line, = plt.plot(ydata)
plt.ylim([10,40]) # set the y-range to 10 to 40
[/python]

Now comes the tricky part. First i read data from the serial port and check if it’s in the right format. The Arduino returns values in the form “x.y” where x and y are numbers. Sometimes the script reads two values at once which would lead to an error while plotting so I split the incoming value at the dot and check if there’s only one dot in there and the length of the split is 2.

To understand the following part I need to explain what I did to get the plot working. At the moment we have the x-axis with 50 zeros. Now I read one value at a time from the serial port. append it to the y-data array and afterwards I delete the first element from the y-data array so that the total number of elements in my array is 50 at all times.

Now I automatically adjust the y-limits by using the min and max values from the y-data array and give it a +-10 range.

And finally I tell matplotlib that there is new data to plot by using line.set_xdata and line.set_ydata. To refresh the plot you just have to use plt.draw():

[python firstline=“15”]
# start data collection
while True:
data = ser.readline().rstrip() # read data from serial
# port and strip line endings
if len(data.split(“.”)) == 2:
ymin = float(min(ydata))-10
ymax = float(max(ydata))+10
plt.ylim([ymin,ymax])
ydata.append(data)
del ydata[0]
line.set_xdata(np.arange(len(ydata)))
line.set_ydata(ydata) # update the data
plt.draw() # update the plot[/python]

And the whole script altogether:

[python]
import serial
import numpy as np
from matplotlib import pyplot as plt
ser = serial.Serial(‘/dev/ttyACM0’, 9600)

plt.ion() # set plot to animated

ydata = [0] * 50
ax1=plt.axes()

# make plot
line, = plt.plot(ydata)
plt.ylim([10,40])

# start data collection
while True:
data = ser.readline().rstrip() # read data from serial
# port and strip line endings
if len(data.split(“.”)) == 2:
ymin = float(min(ydata))-10
ymax = float(max(ydata))+10
plt.ylim([ymin,ymax])
ydata.append(data)
del ydata[0]
line.set_xdata(np.arange(len(ydata)))
line.set_ydata(ydata) # update the data
plt.draw() # update the plot[/python]