A simple LED controlled by a photocell. adafruit.com |
Arduino
The arduino setup is fairly simple and mostly follows the tutorial supplied by adafruit.com listed under 'Simple Demonstration of Use'. Interfacing properly with the serial port, or, rather, getting python to correctly read the data from the serial port, did require altering a few things in the arduino code. Full source here.
The first thing to notice is our
byte val = 0;
at the top of the file. This is the variable that we are going to use to store the raw analog value off the arduino and I couldn't get it to work without it reading into a byte
directly (as opposed to an int
). Other than that, there is not much different from the adafruit tutorial except for the fact that I split the bulk of the code into a separate function adjust_led
.void loop(void) { val = analogRead(photocellPin); Serial.println(val,DEC); adjust_led(val); delay(100); } void adjust_led(int photocellReading){ // LED gets brighter the darker it is at the sensor // that means we have to -invert- the reading from 0-1023 back to 1023-0 photocellReading = 1023 - int(photocellReading); if(photocellReading < 0){ photocellReading = 0; } if(photocellReading > 250){ //now we have to map 0-1023 to 0-255 since thats the range analogWrite uses // for our purposes we only map 250-600 to get a better light range. // feel free to experiment here. LEDbrightness = map(photocellReading, 250, 600, 0, 255); } else { LEDbrightness = 0; } analogWrite(LEDpin, LEDbrightness); }
Pretty standard stuff. In the
loop
we read the analog pin and write it out to the serial port with a newline and using a DEC
format. This is all that is needed in order to plot from python and the rest of the arduino code deals with adjusting the lighting on the LED. Two things to note: if the photocellReading
is less than zero we bump it to zero; if less than 250 we don't turn on the light. So, ideally, the LED should stay off until it is "sufficiently" dark and then it should turn on and proceed to get brighter as it gets darker. The code is not perfect. For some reason I see some flickering and spiking going on and the overall transition isn't as smooth as I would like in terms of the visible effect of the LED. However, since our purpose is to get a graph going, and because looking at the graph might help us debug what is going on with our arduino, we just ignore all that for now and go on.Python
There are two python files that we deal with. The first,
SerialData.py
, is fairly generic and should be able to use any serial data from an arduino. The second, light_sensor_plot.py
, creates our PyQt gui, deals with our data, and creates the graph.SerialData.py
The code is lifted almost directly from this tutorial (on his github this file is called Arduino_Monitor.py). A few differences:buffer = buffer + ser.read(ser.inWaiting()).decode()
I had to add
.decode()
while reading the buffer to get anything to work. I think this is a result of the difference in how python3 handles byte values versus python2.if not self.ser: return 0
I return a zero (0) instead of 100 if our serial port is not read. Ideally shouldn't even get here. I also remove the line that prints "bogus" on the
ValueError
exception. See my full code.light_sensor_plot.py
Most of basics of this file are lifted from Chapter 6 of Sandro Tosi's Matplotlib for Python Developers. In that chapter you will find a section called 'Real-time update of a Matplotlib graph' which does what it says on the tin. In this example Tosi is graphing some cpu values from
This will give us an update every 100 ms, which should be smooth enough. The scrolling graph turns out to be just as easy:
Other than that there is not actually much to the example. I think ideally I would like to split it up so that we have a file for reading from the arduino (currently SerialData.py), one for generating the gui, and one for dealing with the context specific data. You can see that our gui is really simple. Tosi and Eli go on to add fancy naivgation bars and all that so you can build from here.
psutil
and all I really do is strip all of that out, get our data from SerialData.py
instead, and graph away. A lot of Tosi's code deals with the cpu values whereas for our data we don't need to do any additional processing. Perhaps the biggest difference is that Tosi is reading values once per second for a maximum of 30 seconds and sets up his graph accordingly. Our graph, however, wants updates more than once per second but also wants to scroll the graph accordingly. The update value is easy enough:self.timer = self.startTimer(100)
This will give us an update every 100 ms, which should be smooth enough. The scrolling graph turns out to be just as easy:
# force a redraw of the Figure - we start with an initial # horizontal axes but 'scroll' as time goes by if(self.cnt >= self.window_size): self.ax.set_xlim(self.cnt - self.window_size, self.cnt + 15)
So all we do here is check to see if our iteration count is greater than our
window_size
(30) and if so we set the x limit to be a 'window' that follows the data, with window_size
space behind and 15 spaces ahead.Other than that there is not actually much to the example. I think ideally I would like to split it up so that we have a file for reading from the arduino (currently SerialData.py), one for generating the gui, and one for dealing with the context specific data. You can see that our gui is really simple. Tosi and Eli go on to add fancy naivgation bars and all that so you can build from here.
Real-time graph of serial data values read from arduino. |
Resources
- http://www.arduino.cc/playground/interfacing/python
- http://www.packtpub.com/sites/default/files/sample_chapters/7900-matplotlib-for-python-developers-sample-chapter-6-embedding-matplotlib-in-qt-4.pdf
- http://www.blendedtechnologies.com/realtime-plot-of-arduino-serial-data-using-python
- http://eli.thegreenplace.net/2008/08/01/matplotlib-with-wxpython-guis/
- http://learn.adafruit.com/photocells/using-a-photocell
No comments:
Post a Comment