Plotting real-time data from Arduino using Python


Arduino is fantastic as an intermediary between your computer and a raw electronic circuit. Using the serial interface, you can retrieve information from sensors attached to your Arduino. (You can also send information via the serial interface to actuate circuits and devices (LEDs, relays, servos, etc.) connected to your Arduino.) Once you have the data in your computer, you can do all sorts of things with it – analyze it, display it, or share it on the internet, for instance.


In this post, I will be reading and displaying analog data from a pair of LDRs connected to an Arduino. Here is the schematic:



Here is how you hook it up to the Arduino:



The Arduino sketch is very simple – it just reads the values from analog pins A0 and A1 (in the range [0, 1023]) and prints it to the serial port. Here is the code:


// analog-plot
//
// Read analog values from A0 and A1 and print them to serial port.
//
// electronut.in

#include "Arduino.h"

void setup()
{
// initialize serial comms
Serial.begin(9600);
}

void loop()
{
// read A0
int val1 = analogRead(0);
// read A1
int val2 = analogRead(1);
// print to serial
Serial.print(val1);
Serial.print(" ");
Serial.print(val2);
Serial.print("\n");
// wait
delay(50);
}

The serial port sends values in the format:


512 300
513 280
400 200


On the computer side, I need to read these values, and plot them as a function of time. I am using Python and the Matplotlib library for this. I wanted to display this as a scrolling graph that moves to the right as data keeps coming in. For that, I am using the Python deque class to keep and update a fixed number of data points for each time frame.


You can see the full implementation here:


UPDATE: I have upgraded the code below to use the matplotlib animation class. You can still get to the old code here.


"""
ldr.py
Display analog data from Arduino using Python (matplotlib)
Author: Mahesh Venkitachalam
Website: electronut.in
"""

import sys, serial, argparse
import numpy as np
from time import sleep
from collections import deque

import matplotlib.pyplot as plt
import matplotlib.animation as animation

# plot class
class AnalogPlot:
# constr
def __init__(self, strPort, maxLen):
# open serial port
self.ser = serial.Serial(strPort, 9600)

self.ax = deque([0.0]*maxLen)
self.ay = deque([0.0]*maxLen)
self.maxLen = maxLen

# add to buffer
def addToBuf(self, buf, val):
if len(buf) < self.maxLen:
buf.append(val)
else:
buf.pop()
buf.appendleft(val)

# add data
def add(self, data):
assert(len(data) == 2)
self.addToBuf(self.ax, data[0])
self.addToBuf(self.ay, data[1])

# update plot
def update(self, frameNum, a0, a1):
try:
line = self.ser.readline()
data = [float(val) for val in line.split()]
# print data
if(len(data) == 2):
self.add(data)
a0.set_data(range(self.maxLen), self.ax)
a1.set_data(range(self.maxLen), self.ay)
except KeyboardInterrupt:
print('exiting')

return a0,

# clean up
def close(self):
# close serial
self.ser.flush()
self.ser.close()

# main() function
def main():
# create parser
parser = argparse.ArgumentParser(description="LDR serial")
# add expected arguments
parser.add_argument('--port', dest='port', required=True)

# parse args
args = parser.parse_args()

#strPort = '/dev/tty.usbserial-A7006Yqh'
strPort = args.port

print('reading from serial port %s...' % strPort)

# plot parameters
analogPlot = AnalogPlot(strPort, 100)

print('plotting data...')

# set up animation
fig = plt.figure()
ax = plt.axes(xlim=(0, 100), ylim=(0, 1023))
a0, = ax.plot([], [])
a1, = ax.plot([], [])
anim = animation.FuncAnimation(fig, analogPlot.update,
fargs=(a0, a1),
interval=50)

# show plot
plt.show()

# clean up
analogPlot.close()

print('exiting.')

# call main
if __name__ == '__main__':
main()

And here is what the plot looks like. It scrolls to the right as data keeps coming in.