Saturday, September 14, 2013

Arduino Round Two: Trying to Talk to the Computer

The next thing I wanted to do with the Arduino is: get some real-time data, and plot it in a nice looking graph on screen. For this, I pieced together a simple example of reading an analog voltage from a photo-resistor and pushing it to the serial port. As another testament to Arduino's simplicity, this was a 3 minute job. As a testament to my ineptness, plotting the data was a 3 hour job. The Arduino code and circuit are shown below: The code is fairly straightforward and taken from an example I found online:
const int PHOTO = A0;  // Analog input pin
int voltage = 0;        

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

void loop()
{
  voltage = analogRead(PHOTO); // 0-5v <-> 0-1023
  Serial.println(voltage);     // Plain ASCII
  delay(250);                  // update at 4 Hz
}

 The circuit is even more simpleminded: the photoresistor's resistance varies from about 10k when dark, to just under 1k when I have my phone's flashlight on it. Placing the analog in port between this and a 10K resistor makes a nice voltage divider which goes from about 50% to 95% of whatever you feed it, which in this case, is 5V:



Actually, if you just want to see data sent from an Arduino chip over the serial port, the built in serial monitor works right away.

However, if you want a nice plot, you have to deal with that yourself. I know Java pretty well, so that was my first instinct. I keep hearing about Python though and how amazing it is. I decided to give it a shot and downloaded Python 3.x for windows. Then I read up on connecting to a COM port and apparently, the best bet is to install a module known as pySerial. I went an got ahold of that, and ... it didn't work. I was having trouble with loading the python module and after a brief search, concluded that I'd better downgrade to Python 2.x. I got that and after dome fiddling, could read from the COM port, albeit with some lag. To fix the lag, it turns out that you have to poll the arduino faster than the arduino is writing out, otherwise you get a backbuffer. I believe you can also solve this with a flush of the serial port.

OK, but I still haven't plotted anything. To get going on that , I read about MatPlotLib for Python for which I needed numPy. I went to download that, and as it turns out, I should have installed a Scientific Python distribution instead of plain old Python. Again, I uninstalled Python and installed EndThought Canopy - a gargantuan download/install which in the end, had major trouble with pySerial. At this point, what was another uninstall, so I dumped it and went for Continuum Anaconda. Using this, I could get a plot up and running - well up, but not running.
Long story short, if you want to plot things in real time with MatPlotLib, you need to give the graphics library to sort itself out, or the window will hang. This can be done via a simple 'sleep' command. The program that eventually worked is shown below:
# Poll a COM port for Serial data and plot real time
# Thanks to http://www.lebsanft.org/?p=48 for the code idea

import sys
sys.path.append('C:\\Python27\\lib\\site-packages')
import serial
import time
import numpy as nm
from matplotlib import pyplot as plt

ser = serial.Serial('COM3', 9600, timeout=0)

yData = [0]*50

ax1=plt.axes()

line, = plt.plot(yData)

plt.ylim([800,1100]) 
while 1:
# First, read from the device
    try:
        currVal = ser.readline()
        print(currVal)
        time.sleep(.1);
    except ser.SerialTimeoutException:
        print('Error acquiring data')
# Check if the data was a valid number ...
# may be blank in asynchronous mode
    try:
        cV=float(currVal)
        runnit = True
    except:
        runnit = False
# If the value was a legit number, plot it.
    if runnit:
        ymin = float(min(yData))-10 # Set the y-scaling
        ymax = float(max(yData))+10
        plt.ylim([ymin,ymax])
        yData.append(currVal) # tag on newest point
        del yData[0] # and bump oldest point
        line.set_xdata(np.arange(len(yData)))
        line.set_ydata(yData)  # update the data
        plt.show() # update the plot
        plt.pause(.01)
# Doesnt exit nicely here ... need to fix

 Finally, real time monitoring of, well ... of my shadow here. But I see it as a next step to bigger and better projects:)

No comments:

Post a Comment