Thursday, April 2, 2015

Python to plot graph of serial data from Arduino Uno analog input

It's a simple example to read analog input from Arduino Uno, send to PC via USB serial. In the PC side, running Python on Linux, plot the received serial graphically using matplotlib and drawnow library.




Arduino side:


AnalogInSerialOut.ino
const int analogIn = A0;
int analogVal = 0;

void setup() {
  Serial.begin(19200);
}

void loop() {

  analogVal = analogRead(analogIn);
  Serial.println(analogVal);
  delay(1000);
}

PC side running Python 2.7.6, plotArduino.py. It's the example code run in the demo video.
import serial
import matplotlib.pyplot as plt
from drawnow import *

values = []

plt.ion()
cnt=0

serialArduino = serial.Serial('/dev/ttyACM0', 19200)

def plotValues():
    plt.title('Serial value from Arduino')
    plt.grid(True)
    plt.ylabel('Values')
    plt.plot(values, 'rx-', label='values')
    plt.legend(loc='upper right')

#pre-load dummy data
for i in range(0,26):
    values.append(0)
    
while True:
    while (serialArduino.inWaiting()==0):
        pass
    valueRead = serialArduino.readline()

    #check if valid value can be casted
    try:
        valueInInt = int(valueRead)
        print(valueInInt)
        if valueInInt <= 1024:
            if valueInInt >= 0:
                values.append(valueInInt)
                values.pop(0)
                drawnow(plotValues)
            else:
                print "Invalid! negative number"
        else:
            print "Invalid! too large"
    except ValueError:
        print "Invalid! cannot cast"
    



Modified version of plotArduino.py:
- It seem that 19200 baud is not stable in my unit, so change to 9600. (Have to modify Arduino side also)
- Add some line to indicate status.
- Add atexit handling.
- Make it work on both Python 2 and 3.

import serial
import matplotlib.pyplot as plt
from drawnow import *
import atexit

values = []

plt.ion()
cnt=0

serialArduino = serial.Serial('/dev/ttyACM0', 9600)

def plotValues():
    plt.title('Serial value from Arduino')
    plt.grid(True)
    plt.ylabel('Values')
    plt.plot(values, 'rx-', label='values')
    plt.legend(loc='upper right')

def doAtExit():
    serialArduino.close()
    print("Close serial")
    print("serialArduino.isOpen() = " + str(serialArduino.isOpen()))

atexit.register(doAtExit)

print("serialArduino.isOpen() = " + str(serialArduino.isOpen()))

#pre-load dummy data
for i in range(0,26):
    values.append(0)
    
while True:
    while (serialArduino.inWaiting()==0):
        pass
    print("readline()")
    valueRead = serialArduino.readline(500)

    #check if valid value can be casted
    try:
        valueInInt = int(valueRead)
        print(valueInInt)
        if valueInInt <= 1024:
            if valueInInt >= 0:
                values.append(valueInInt)
                values.pop(0)
                drawnow(plotValues)
            else:
                print("Invalid! negative number")
        else:
            print("Invalid! too large")
    except ValueError:
        print("Invalid! cannot cast")




To install matplotlib, drawnow and pyserial:

for Python 2
$ sudo apt-get install python-matplotlib
$ sudo apt-get install python-pip
$ sudo pip install drawnow
$ sudo pip install pyserial

for Python 3
$ sudo apt-get install python3-matplotlib
$ sudo apt-get install python3-pip
$ sudo pip3 install drawnow
$ sudo pip3 install pyserial


Updated@2017-06-17:
It's a similarity example without using drawnow, tested on Raspberry Pi and PC/Ubuntu 17.04/Python 3.6 ~ Python run on Raspberry Pi (and PC running Ubuntu) to plot serial data from ESP8266/NodeMCU


9 comments:

  1. I don't get the ``To install matplotlib, drawnow and pyserial:`` part. Can someon help?

    ReplyDelete
    Replies
    1. The install method is for debian based linux distributions, such as debian, ubuntu and raspbian. You need to use some variation of debian to execute those commands.

      Delete
  2. I use windows, you may need to install pip library for installing these libraries. I also Recommend you Python XY

    ReplyDelete
  3. hi i am getting the following error
    C:\Users\halovivek\AppData\Local\Programs\Python\Python36-32\python.exe C:/Users/halovivek/PycharmProjects/mylearning/plotArduino.py
    Traceback (most recent call last):
    File "C:/Users/halovivek/PycharmProjects/mylearning/plotArduino.py", line 10, in
    serial_arduino = serial.Serial('/dev/ttyACM0', 38400)
    File "C:\Users\halovivek\AppData\Local\Programs\Python\Python36-32\lib\site-packages\serial\serialwin32.py", line 31, in __init__
    super(Serial, self).__init__(*args, **kwargs)
    File "C:\Users\halovivek\AppData\Local\Programs\Python\Python36-32\lib\site-packages\serial\serialutil.py", line 240, in __init__
    self.open()
    File "C:\Users\halovivek\AppData\Local\Programs\Python\Python36-32\lib\site-packages\serial\serialwin32.py", line 62, in open
    raise SerialException("could not open port {!r}: {!r}".format(self.portstr, ctypes.WinError()))
    serial.serialutil.SerialException: could not open port '/dev/ttyACM0': FileNotFoundError(2, 'The system cannot find the path specified.', None, 3)

    Process finished with exit code 1

    ReplyDelete
  4. I want to use this code for three potentiometer. which line of code I have to change?

    ReplyDelete
  5. may I use this code for more than one potentiometer?

    ReplyDelete
  6. Anyone know how the serial can be dumped into LibreOffice Calc - to store and retrieve the values later and to use Calc's functions and chart plotting functions?

    ReplyDelete
  7. develop pyserialusb voltage and curretn plotting ? any suggetions

    ReplyDelete