- Video downloaded using youtube-dl
- Resized to 360p and 30 fps using ffmpeg
- Loop found and resized using MoviePy, with ffmpeg used to create the gif
- gif optimized using gifsicle -O3
Result: 320×180, 22 frames, 128 colours, ~ 600 KB.
Result: 320×180, 22 frames, 128 colours, ~ 600 KB.
Update, 2019-01: raspblocks.com appears to be dead, with an “Account Suspended” error from the host
Raspblocks is a new Blocks-based web programming environment for Raspberry Pi. You don’t even need to write the code on a Raspberry Pi, but the Python 3 code it produces will need to be transferred to a Raspberry Pi to run.
For maximum authenticity (and slowness), I fired up http://www.raspblocks.com/ on a Raspberry Pi Zero over VNC. It took a minute or more to load up the site in Chromium, but creating a simple program was all easy dragging and dropping:
The code it produced was pretty much exactly what you’d write by hand:
import RPi.GPIO as GPIO import time GPIO.setmode(GPIO.BCM) GPIO.setup(26, GPIO.OUT) while True: GPIO.output(26,True) time.sleep(1) GPIO.output(26,False) time.sleep(1)
And, as you might expect, the code make an LED connected to GPIO 26 turn on and off. Science!
Raspblocks isn’t as polished as its more established rival EduBlocks, but Raspblocks doesn’t need any software installed. Edublocks installs its own Node.js-based web service, which would be painfully slow on a Raspberry Pi Zero. Raspblocks’ code needs to be run manually from a terminal, but I’d put up with that any day over having yet another Node server distribution installed under /opt.
NB: this version doesn’t actually do the rebooting or shutting down: it’s a demo. This one does, though …
Here’s how you might have just one button being a reset button (hold down for two seconds) or a shutdown button (hold down for five):
(That’s a Trinket M0, btw)
I’m trying to resist running it on every generation of Raspberry Pi that I have (B, A, 2B, 3B, Zero, Zero W) just for these pretty displays.
(ANSI console colours captured using script, then fed through ansi2html [from the Ubuntu colorized-logs package], printed to PDF from Firefox then mucked about a bit with in Inkscape)
I just picked up a micro:bit, the little educational microprocessor board originally from the BBC. It’s a nice little unit, though like all educational resources, it’s sometimes hard to access resources as a non-edu type.
I landed upon MicroPython, a Python language subset that runs directly on the micro:bit’s ARM chip. I rather like the Mu editor:
To give the old microcontroller grumps something real to complain about, MicroPython includes a bunch of very high-level functions, such as a powerful music and sound module. Getting the sound out is easy: just croc-clip a speaker onto the output pads:
(MicroPython warns against using a piezo buzzer as a speaker, but mine worked fine — loudly and supremely annoyingly — with a large piezo element. Some piezos have a fixed-frequency oscillator attached, but this simple one was great.)
This trivial example plays the Nyan Cat theme forever, but every time it loops it gets faster. The beats variable starts at the default 120 bpm, but is increased by one every time:
# nyan but it gets faster import music beats = 120 while True: music.set_tempo(bpm=beats) music.play(music.NYAN) beats = beats + 1
This starts out as merely irritating, but quite quickly becomes deeply annoying, and in mere hours become vastly vexing. I’m sure you’d only use this power for good …
Well, that was easy!
Since the Verbal Machines VM-CLAP1 sensor is an open collector type — that is, it sinks current when triggered — it behaves like a simple button to gpiozero, the Raspberry Pi Python GPIO library. If you attach a callback function to the sensor’s when_pressed event, your Python script will call that function every time it registers a clap.
VM-CLAP1: Raspberry Pi: ========= ============= GND → GND PWR → 3V3 OUT → GPIO 4
This example code just prints clap! when the board picks up a 👏:
#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Raspberry Pi gpiozero test for # Verbal Machines VM-CLAP1 clap sensor # scruss - 2017-06 # # Wiring: # # VM-CLAP1: Raspberry Pi: # ========= ============= # GND → GND # PWR → 3V3 # OUT → GPIO 4 from gpiozero import Button from signal import pause def clapping(): print("clap!") clap = Button(4) clap.when_pressed = clapping pause()
This is a trivial example, but at least it shows that anything you can do with a button, you can also do with this hand-clap sensor.
As Side Door sign v3 seemed to have fallen off, I needed to make a new one. With access to a laser cutter, I can make really permanent things now, so I designed this:
In order to get the sign to hang correctly, I needed to work out the centroid of the pointy outline. thedatachef/inkscape-centroid: Centroids for Inkscape paths and shapes to the rescue! Well, kinda. First off, the installer had a bug that said a Ruby file was a dependency when the plugin was in Python. So I forked the repo, made the change, tested it, and issued a pull request. So yay, working centroid calculations in Inkscape!
Secondly, the plugin only works well for simple shapes, like these:
Pen plotters were pretty expensive and complex pieces of electromechanical equipment. While they often earned their keep in the CAD office, they also had a function that’s almost forgotten: they could be used as input devices, too.
As a kid, we sometimes used to drive past the office of Ferranti-Cetec in Edinburgh. They specialized in digitizers: great big desk or wall mounted devices for capturing points from maps and drawings. Here’s one of their 1973 models:
While the technology and size have changed a bit, these huge bits of engineering kit are the ancestors of today’s track pads and touch screens.
Realizing that their plotters had very precise X-Y indexing and that they had two-way communications to a computer, HP made a drafting sight that fitted in place of a pen on their plotters:
This is a very pleasing piece of kit, all metal, thick plastic and polished optical glass. They show up on eBay occasionally, and aren’t cheap. With a bit of coercion, it fits into my HP plotter like this:
The image is very bright and clear:
If one has a digitizing sight, one needs to find something to digitize post haste … I’m sure everyone can sense the urgency in that. So I found this, a scan from my undergraduate project writeup (centrifugal pump impeller design ftw, or something), which was probably made on an Amiga or Atari ST:
I printed this as large as I could on Letter paper, as it’s the only size my HP7470A plotter can take. Now all it needed was a small matter of programming to get the data from the plotter. Here’s a minimally-useful digitizer for HP and compatible serial plotters. Although I ran it on my little HP grit wheel plotter attached to a Raspberry Pi, I developed it with my larger Roland plotter. The only fancy module it needs is pySerial.
#!/usr/bin/env python # -*- coding: utf-8 -*- # a really crap HP-GL point digitizer # scruss - 2016 from time import sleep from string import strip import serial ser = serial.Serial(port='/dev/ttyUSB1', baudrate=9600, timeout=0.5) lbl = '' points =  labels =  k = 0 retval = 0 ser.write('DP;') # put in digitizing mode while lbl != 'quit': ser.write('OS;') ret = strip(ser.read(size=5), chr(13)) print ('Retval: ', ret) if ret != '': retval = int(ret) if retval & 4: # bit 2 is set; we have a point! print ('Have Point! Retval: ', retval) retval = 0 ser.write('OD;') pt = strip(ser.read(size=20), chr(13)) print ('OD point: ', pt) lbl = raw_input('Input label [quit to end]: ') points.append(pt) labels.append(lbl) k = k + 1 ser.write('DP;') # put in digitizing mode again sleep(1) ser.close() f = open('digit.dat', 'w') for i in range(k): f.write(points[i]) f.write(',') f.write(labels[i]) f.write('\n') f.close()
In the unlikely event that anyone actually uses this, they’ll need to change the serial port details near the top of the program.
The program works like this:
I didn’t implement any flow control or other buffer management, so it can crash in a variety of hilarious ways. I did manage to get it to work on the lower trace of that graph, and got these data:
9649,2428,1,300,0 357,2428,1,0,0 357,7217,1,0,0.60 733,3112,1,first 826,3167,1, 968,3256,1, 1122,3334,1, 1290,3405,1, 1588,3583,1, 1891,3725,1, 2215,3880,1, 2526,4051,1, 2830,4194,1, 3143,4280,1, 3455,4516,1, 4077,4767,1, 5008,5229,1, 6543,5954,1, 8067,6548,1, 8740,7195,1, 8740,7195,1,last 8740,7195,1,quit
The first two columns are X and Y, in HP-GL units — that’s 1/40 mm, or 1/1016 inches. The third column will always be 1 if you have the sight down. The last columns are the label; if you put commas in them, opening the file as CSV will split the label into columns. I used it to fudge axis points. You’ll also note that the last three lines of data are my valiant attempts to quit the program …
Assuming the axes are not skewed (they are, very slightly, but shhh) some simple linear interpolation gives you the results below:
12.1 0.086 15.1 0.093 19.7 0.104 24.7 0.114 30.1 0.122 39.7 0.145 49.5 0.162 60.0 0.182 70.0 0.203 79.8 0.221 89.9 0.232 100.0 0.262 120.1 0.293 150.2 0.351 199.7 0.442 248.9 0.516 270.7 0.597
Good enough for a demo.
(For prettier things to do with plotter digitizing commands, Ed Nisley KE4ZNU has made some rather lovely Superformula patterns)
If you don’t have a plotter, or even if you do and you don’t have hours to waste mucking about with Python, obsolete optics and serial connections, Ankit Rohatgi’s excellent WebPlotDigitizer (or Engauge, as I found out when this article hit HackerNews in 2021) gets numbers out of graphs quickly. It handles all sorts of graphs rather well.
The cryptically-named q (it also bills itself as being able to “Run SQL directly on CSV files | Text as Data”) is very nifty indeed. It allows you to run SQL queries on delimited text files. It seems to support the full SQLite SQL dialect, too.
I used to frequently query the IESO‘s Hourly Wind Generator Output report (which now hides behind a JS link to obscure the source URL, http://www.ieso.ca//imoweb/pubs/marketReports/download/HourlyWindFarmGen_20160122.csv). Now that the file has nearly 10 years of hourly data and many (but not all) wind projects, it may have outlived its usefulness. But it does allow me to show off some mad SQLite skills …
The first problem is that the file uses nasty date formats. Today would be 23-Jan-16 in the report’s Date field, which is filled with the ugh. You can fix that, though, with a fragment of SQL modified from here:
printf("%4d-%02d-%02d", substr(Date, 8,2)+2000, (instr("---JanFebMarAprMayJunJulAugSepOctNovDec", substr(Date, 4,3))-1)/3, substr(Date, 1, 2)) as isodate
The above data definition sets the isodate column to be in the familiar and useful YYYY-MM-DD ISO format.
A related example would be to query the whole CSV file for monthly mean generation from Kingsbridge and K2 Wind projects (they’re next to one another) for months after K2’s commissioning in March 2015. Here’s what I did in q:
q -T -O -H -d, 'select printf("%4d-%02d", substr(Date, 8,2)+2000, (instr("---JanFebMarAprMayJunJulAugSepOctNovDec", substr(Date, 4,3))-1)/3) as isomonth, avg(KINGSBRIDGE) as kavg, avg(K2WIND) as k2avg from Downloads/HourlyWindFarmGen_20160122.csv where isomonth>"2015-03" group by isomonth'
which gave the results:
isomonth kavg k2avg 2015-04 12.7277777778 37.4569444444 2015-05 8.94623655914 67.6747311828 2015-06 6.05833333333 66.6847222222 2015-07 3.96370967742 45.372311828 2015-08 6.34811827957 67.436827957 2015-09 7.29027777778 79.7194444444 2015-10 14.5658602151 128.037634409 2015-11 15.9944444444 130.729166667 2015-12 17.6075268817 152.422043011 2016-01 19.6408730159 163.013888889
Neat! (or at least, I think so.)
Over the last few weeks, I’ve been playing with a few small thermal printers. Meant as POS or information booth printers, they make a diverting project for the lo-fi printing enthusiast. While they all have common features — 58 mm/2¼” paper width, 8 pixel/mm resolution, 48 mm print width, serial connection — they all have their quirks. You may have seen these sold as the Adafruit Mini Thermal Receipt Printer or Sparkfun’s Thermal Printer, but there are many others. I’m going to write more on interfacing these directly to Raspberry Pi, Arduino, and (if I can navigate the documentation) a CUPS driver.
Update, July 2015: Here’s a CUPS driver: klirichek/zj-58, and my writeup on installing it on a Raspberry Pi — Thermal Printer driver for CUPS, Linux, and Raspberry Pi: zj-58
For now, I’m just leaving you a list of things I’ve found helpful for the DP-EH600 and 701 printers. Note that the similar-looking BTHT-v6 printer uses a completely different command set.
Posted more for historical reference:
Hey! This post is completely ancient. It doesn’t even use Python 3. Advice given here might be well out of date.
… it complains that the oscilloscope is always making waves.
Ahem. Anyway. I have a Rigol DS1102E 100 MHz Digital Oscilloscope. For such a cheap device, it’s remarkable that you can control it using USB Test & Measurement Class commands. I’d been wanting to use a Raspberry Pi as a headless data acquisition box with the oscilloscope for a while, but Raspbian doesn’t ship with the usbtmc kernel module. I thought I was stuck.
Alex Forencich turned up in the forum with an all-Python solution: Python USBTMC (source: alexforencich / python-usbtmc). I got this working quite nicely today on both the Raspberry Pi and my Ubuntu laptop. Here’s how I installed it:
If you get the status line, congratulations! You now have a fully working usbtmc link. I haven’t had much time to play with this, but I know I can make really nice screenshots to an attached USB drive using the command: instr.write(“:HARDcopy”). Many more commands can be found in the DS1000D/E Programming Guide, available on Rigol‘s site.
I had a couple of problems, though:
I’ve still got to work my way through the Rigol’s data format, but other people have done that before:
I’ll post any updates here, along with the Raspberry Pi forum topic: USB Test & Measurement class (usbtmc) driver?
Incidentally, if you’re working with WFM data dumps from the Rigol ‘scopes (and you should, because they make storing data to USB drives quick), mabl/pyRigolWFM is basically magic. Not merely can it describe and decode those binary files, it can do pretty graphics with no thought required:
Update, 2013-12-20: I’ve successfully managed to run most of Ken’s examples with Alex’s code. The major modification you have to do is use ask_raw instead of ask. Example code shown below:
#!/usr/bin/python # -*- coding: utf-8 -*- """ Download data from a Rigol DS1102E oscilloscope and graph with matplotlib using Alex Forencich's python-usbtmc pure python driver https://github.com/alexforencich/python-usbtmc scruss - 2013-12-20 based on Download data from a Rigol DS1052E oscilloscope and graph with matplotlib. By Ken Shirriff, http://righto.com/rigol which in turn was Based on http://www.cibomahto.com/2010/04/controlling-a-rigol-oscilloscope-using-linux-and-python/ by Cibo Mahto. """ import usbtmc import time import numpy import matplotlib.pyplot as plot # initialise device instr = usbtmc.Instrument(0x1ab1, 0x0588) # Rigol DS1102E # read data instr.write(":STOP") instr.write(":WAV:POIN:MODE RAW") # first ten bytes are header, so skip rawdata = instr.ask_raw(":WAV:DATA? CHAN1")[10:] data_size = len(rawdata) # get metadata sample_rate = float(instr.ask_raw(':ACQ:SAMP?')) timescale = float(instr.ask_raw(":TIM:SCAL?")) timeoffset = float(instr.ask_raw(":TIM:OFFS?")) voltscale = float(instr.ask_raw(':CHAN1:SCAL?')) voltoffset = float(instr.ask_raw(":CHAN1:OFFS?")) # show metadata print "Data size: ", data_size print "Sample rate: ", sample_rate print "Time scale: ", timescale print "Time offset: ", timeoffset print "Voltage offset: ", voltoffset print "Voltage scale: ", voltscale # convert data from (inverted) bytes to an array of scaled floats # this magic from Matthew Mets data = numpy.frombuffer(rawdata, 'B') data = data * -1 + 255 data = (data - 130.0 - voltoffset/voltscale*25) / 25 * voltscale # creat array of matching timestamps time = numpy.linspace(timeoffset - 6 * timescale, timeoffset + 6 * timescale, num=len(data)) # scale time series and label accordingly if (time[-1] &lt; 1e-3): time = time * 1e6 tUnit = "µS" elif (time[-1] &lt; 1): time = time * 1e3 tUnit = "mS" else: tUnit = "S" # Plot the data plot.plot(time, data) plot.title("Oscilloscope Channel 1") plot.ylabel("Voltage (V)") plot.xlabel("Time (" + tUnit + ")") plot.xlim(time, time[-1]) plot.show()
Boodler is rather fun. It generates ambient music based on user-defined or downloaded ‘soundscapes’. If you’ve got a modern (HTML5/Opus-capable) browser, you can hear a streaming demo here: http://repeater.xiph.org:8000/clock.opus. It’s using the FM3 Buddha Machine samples in this demo, but it can run lots more: a tree full of crows, a thunderstorm, dripping water, …
It’s pretty easy to run on a Raspberry Pi running a recent version of Raspbian. The only technical glitch I had was that there’s something deeply confused about ALSA sound handling on the Raspberry Pi. I’m sure it’ll get fixed soon, but for now, you have to use PulseAudio. (If you want to read about my ALSA woes, go here.)
The installation prerequisites are simple:
sudo apt-get install pulseaudio pulseaudio-utils libpulse-dev python-dev
Now download and configure Boodler:
wget http://boodler.org/dl/Boodler-2.0.4.tar.gz tar xvzf Boodler-2.0.4.tar.gz cd Boodler-2.0.4 python setup.py build
It takes a while to do this, but make sure it does something useful when it’s building the various sound drivers. You don’t want it to say:
skipping 'boodle.cboodle_pulse' extension
If it says that, you haven’t installed Pulseaudio. Go back and check your apt-get line.
Once it’s built, now install it:
sudo python setup.py install
Now test it:
boodler --hardware --output pulse --testsound
Not merely should you get some pleasant tones from your Raspberry Pi’s audio, but you sound get some informative and non-threatening terminal output. Mine looks like:
Boodler: PulseAudio sound driver. PulseAudio library: 2.0.0. Sample rate is 44100 fps. Samples are 16-bit little-endian. Buffer size is 32768. 21:37:46 (root) Running "Boodler test sound"
If that works, let’s get those crows a-cawin’. Download the soundscapes you need:
boodle-mgr install http://boodler.org/lib/org.boodler.old.crow.1.0.boop boodle-mgr install http://boodler.org/lib/com.eblong.zarf.crows.1.0.boop
and run it:
boodler --output pulse com.eblong.zarf.crows/ParliamentOfCrows
I really like the Buddha Machine samples. It’s quite big (> 80 MB), so this next set will take a while to download:
boodle-mgr install http://boodler.org/lib/com.azulebanana.buddhamachine.1.5.1.boop boodle-mgr install http://boodler.org/lib/com.azulebanana.buddhaagent.1.5.1.boop
It’s worth the wait:
boodler --output pulse com.azulebanana.buddhaagent/ChangingLoops
Boodler has tons of options, prebuilt packages, and instructions to build your own: Boodler Documentation.
One thing I’ve tried to get working, but failed, is streaming from Boodler via icecast. Sure, I can install and run it, it’s just that the results are, um, undesirable. If you want to have a play, here’s how to install icecast:
sudo apt-get install icecast2 ices2 libshout3-dev
Icecast will configure itself, and ask for a couple of passwords. You’ll have to rebuild and reinstall Boodler for it to catch the new configuration. You can then try streaming:
boodler --output shout --define shout-password=mypassword --define shout-mount='/boodler-buddha.ogg' com.azulebanana.buddhaagent/ChangingLoops
If you open a web browser at this address http://raspberrypi:8000/ you should see a config page listing your boodler-buddha.ogg stream. Click on the M3U link next to it, and your streaming music player should start making a joyful noise …
… except in my case, something went very wrong, and it started to produce industrial ultra-glitch nightmare noise: boodler-streaming_test-fail. I’m sure it’s fixable with some tweaking, but I’m not there yet.
Hey! This is a really old article. You should really be using gpiozero these days.
I hadn’t realised it, but the The Quite Rubbish Clock did something that a lot of people seem to have trouble with on the Raspberry Pi: communicating using hardware SPI. Perhaps it’s because everything is moving so fast with Raspberry Pi development, tutorials go out of date really quickly. Thankfully, hardware SPI is much easier to understand than the older way of emulation through bit-banging.
SPI is a synchronous serial protocol, so it needs a clock line as well as a data in and data out line. In addition, it has a Chip Enable (CE, or Chip Select, CS) line that is used to choose which SPI device to talk to. The Raspberry Pi has two CE lines (pins 24 and 26) so can talk to two SPI devices at once. It supports a maximum clock rate of 32 MHz, though in practice you’ll be limited to the rate your device supports.
The device I’m testing here is an MCP3008 10-bit Analogue-to-Digital Converter (ADC). These are simple to use, cheap and quite fast converters with 8 input channels. If you hook them up to a 3.3 V supply they will convert a DC voltage varying from 0-3.3 V to a digital reading of 0-1023 (= 210 – 1). Not quite up there in quality for hi-fi audio or precision sensing, but good enough to read from most simple analogue sensors.
The sensor I’m reading is the astonishingly dull LM35DZ temperature sensor. All the cool kids seem to be using TMP36s (as they can read temperatures below freezing without a negative supply voltage). One day I’ll show them all and use a LM135 direct Kelvin sensor, but not yet.
To run this code, install the SPI libraries as before. Now wire up the MCP3008 to the Raspberry Pi like so:
MCP 3008 Pin Pi GPIO Pin # Pi Pin Name ============== =============== ============= 16 VDD 1 3.3 V 15 VREF 1 3.3 V 14 AGND 6 GND 13 CLK 23 GPIO11 SPI0_SCLK 12 DOUT 21 GPIO09 SPI0_MISO 11 DIN 19 GPIO10 SPI0_MOSI 10 CS 24 GPIO08 CE0 9 DGND 6 GND
The wiring for the LM35 is very simple:
LM35 Pin MCP3008 Pin ========== ============= Vs 16 VDD Vout 1 CH0 GND 9 DGND
The code I’m using is a straight lift of Jeremy Blythe’s Raspberry Pi hardware SPI analog inputs using the MCP3008. The clever bit in Jeremy’s code is the
readadc() function which reads the relevant length of bits (by writing the same number of bits; SPI’s weird that way) from the SPI bus and converting it to a single 10-bit value.
#!/usr/bin/python # -*- coding: utf-8 -*- # mcp3008_lm35.py - read an LM35 on CH0 of an MCP3008 on a Raspberry Pi # mostly nicked from # http://jeremyblythe.blogspot.ca/2012/09/raspberry-pi-hardware-spi-analog-inputs.html import spidev import time spi = spidev.SpiDev() spi.open(0, 0) def readadc(adcnum): # read SPI data from MCP3008 chip, 8 possible adc's (0 thru 7) if adcnum > 7 or adcnum < 0: return -1 r = spi.xfer2([1, 8 + adcnum << 4, 0]) adcout = ((r & 3) << 8) + r return adcout while True: value = readadc(0) volts = (value * 3.3) / 1024 temperature = volts / (10.0 / 1000) print ("%4d/1023 => %5.3f V => %4.1f °C" % (value, volts, temperature)) time.sleep(0.5)
The slightly awkward code
temperature = volts / (10.0 / 1000) is just a simpler way of acknowledging that the LM35DZ puts out 10 mV (= 10/1000, or 0.01) per °C. Well-behaved sensors generally have a linear relationship between what they indicate and what they measure.
If you run the code:
you should get something like:
91/1023 => 0.293 V => 29.3 °C 93/1023 => 0.300 V => 30.0 °C 94/1023 => 0.303 V => 30.3 °C 95/1023 => 0.306 V => 30.6 °C 96/1023 => 0.309 V => 30.9 °C 97/1023 => 0.313 V => 31.3 °C 97/1023 => 0.313 V => 31.3 °C 98/1023 => 0.316 V => 31.6 °C 99/1023 => 0.319 V => 31.9 °C 99/1023 => 0.319 V => 31.9 °C 100/1023 => 0.322 V => 32.2 °C 100/1023 => 0.322 V => 32.2 °C 100/1023 => 0.322 V => 32.2 °C 101/1023 => 0.325 V => 32.5 °C 101/1023 => 0.325 V => 32.5 °C 102/1023 => 0.329 V => 32.9 °C 102/1023 => 0.329 V => 32.9 °C 103/1023 => 0.332 V => 33.2 °C
Note that the sensor had been sitting over the Raspberry Pi’s CPU for a while; I don’t keep my house at 29 °C. I made the temperature go up by holding the LM35.
So, you’ve just (fairly cheaply) given your Raspberry Pi 8 analogue input channels, so it can behave much more like a real microcontroller now. I remember from my datalogging days that analogue inputs can be pretty finicky and almost always return a value even if it’s an incorrect one. Check the chip’s datasheet to see if you’re doing it right, and if in doubt, meter it!
#!/usr/bin/python # -*- coding: utf-8 -*- # qrmovie import time # need to use git://github.com/mozillazg/python-qrcode.git import qrcode from PIL import Image, ImageFont import ImageOps # uses bgreat's SPI code; see # raspberrypi.org/phpBB3/viewtopic.php?f=32&t=9814&p=262274&hilit=nokia#p261925 import nokiaSPI noki = nokiaSPI.NokiaSPI() # create display device qr = qrcode.QRCode(version=1, # V.1 QR Code: 21x21 px error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=2, border=1) bg = Image.new('1', (84, 48)) # blank (black) image background # intro noki.cls() noki.led(0) time.sleep(3) for i in range(0,769,32): noki.led(i) time.sleep(0.04) # display is 14 columns by 8 rows noki.centre_word(1, 'scruss.com') noki.centre_word(3, 'presents') time.sleep(3) noki.cls() noki.centre_word(1, 'qrclock') noki.centre_word(2, 'the') noki.gotorc(3,3) noki.text("[Q]uite") noki.gotorc(4,3) noki.text("[R]ubbish") noki.gotorc(5,3) noki.text(" Clock") time.sleep(3) elapsed=0 start_time = time.time() while (elapsed<12): qr.clear() newbg = bg.copy() # copy blank background s = time.strftime('%Y-%m-%d %H:%M:%S') qr.add_data(s) # make QR Code of YYYY-MM-DD HH:MM:SS qr.make() qrim = qr.make_image() # convert qrcode object to PIL image qrim = qrim.convert('L') # make greyscale qrim = ImageOps.invert(qrim) # invert colours: B->W and W->B qrim = qrim.convert('1') # convert back to 1-bit newbg.paste(qrim, (18, 0)) # paste QR Code into blank background noki.show_image(newbg) # display code on LCD time.sleep(0.4) # pause before next display elapsed = time.time() - start_time noki.cls() noki.centre_word(1, 'for') noki.centre_word(2, 'more') noki.centre_word(3, 'details') time.sleep(3) noki.cls() noki.load_bitmap("blogpost-nokia.bmp", True) time.sleep(7) noki.cls() noki.centre_word(3, 'fin') noki.centre_word(5, 'scruss, 2013') time.sleep(1) for i in range(768,-1,-32): noki.led(i) time.sleep(0.05) time.sleep(1) noki.cls()
(This source, plus nokiaSPI class: qrclock-movie.zip)
Lines 43-58 show off the QR clock for a maximum of 12 seconds. Any more, and you’d get really bored.
The screen handling functions I used are:
The text handling functions I used are:
There are many more functions in the nokiaSPI class; watch the demo, have a dig through the source and see what you can use.
Hey! This article is really old and probably doesn’t work any more: things have changed a lot in Raspberry Pi world since 2013 …
Update 3: code for the demo video is here.
Update 2: In which I actually post working code.
Update: Eep! This post was featured on the Raspberry Pi blog today. Thanks, Liz!
And now for something completely different:
… a clock that isn’t human readable. You’ll need a QR code reader to be able to tell the time.
This, however, is not the prime purpose of the exercise. I was looking for an excuse to try some direct hardware projects with the GPIO, and I remembered I had a couple of Nokia-style surplus LCDs lying about that could be pressed into service. These LCDs aren’t great: 84×48 pixels, 3V3 logic, driven by SPI via an 8-pin header which includes PWM-controllable LED backlighting. They are cheap, and available almost everywhere: DealExtreme ($5.36), SparkFun ($9.95), Adafruit ($10 – but includes a level shifter, which you really need if you’re using a 5V logic Arduino), Solarbotics ($10) and Creatron (about $12; but you can walk right in and buy one). Despite being quite difficult to use, helpful people have written drivers to make these behave like tiny dot-addressable screens.
I’d been following the discussion on the Raspberry Pi forum about driving the Nokia LCD from a Raspberry Pi. Only when user bgreat posted some compact code that was supposed to run really fast did I dig out the LCD board and jumper wires. Building on bgreat’s nokiaSPI.py class and a few other bits of code, here’s what I built to make this singularly pointless clock:
#!/usr/bin/python # -*- coding: utf-8 -*- # qrclock - The Quite Rubbish Clock for Raspberry Pi - scruss, 2013-01-19 import time # need to use git://github.com/mozillazg/python-qrcode.git import qrcode from PIL import Image import ImageOps # uses bgreat's SPI code; see # raspberrypi.org/phpBB3/viewtopic.php?f=32&amp;amp;t=9814&amp;amp;p=262274&amp;amp;hilit=nokia#p261925 import nokiaSPI noki = nokiaSPI.NokiaSPI() # create display device qr = qrcode.QRCode(version=1, # V.1 QR Code: 21x21 px error_correction=qrcode.constants.ERROR_CORRECT_M, box_size=2, border=1) bg = Image.new('1', (84, 48)) # blank (black) image background while 1: qr.clear() newbg = bg.copy() # copy blank background s = time.strftime('%Y-%m-%d %H:%M:%S') qr.add_data(s) # make QR Code of YYYY-MM-DD HH:MM:SS qr.make() qrim = qr.make_image() # convert qrcode object to PIL image qrim = qrim.convert('L') # make greyscale qrim = ImageOps.invert(qrim) # invert colours: B-&amp;gt;W and W-&amp;gt;B qrim = qrim.convert('1') # convert back to 1-bit newbg.paste(qrim, (18, 0)) # paste QR Code into blank background noki.show_image(newbg) # display code on LCD time.sleep(0.4) # pause before next display
(Convenient archive of all the source: qrclock2.zip, really including bgreat’s nokiaSPI class this time …)
To get all this working on your Raspberry Pi, there’s a fair amount of configuration. The best references are bgreat’s own comments in the thread, but I’ve tried to include everything here.
Enabling the SPI kernel module
As root, edit the kernel module blacklist file:sudo vi /etc/modprobe.d/raspi-blacklist.conf
Comment out the spi-bcm2708 line so it looks like this:#blacklist spi-bcm2708
Save the file so that the module will load on future reboots. To enable the module now, enter:sudo modprobe spi-bcm2708
Now, if you run the
lsmodcommand, you should see something like:Module Size Used by spi_bcm2708 4421 0
Installing the WiringPi, SPI and other required packages
WiringPi by Gordon is one of the neater Raspberry Pi-specific modules, as it allows relatively easy access to the Raspberry Pi’s GPIO pins. For Raspbian, there are a few other imaging libraries and package management tools you’ll need to install here:sudo apt-get install python-imaging python-imaging-tk python-pip python-dev git sudo pip install spidev sudo pip install wiringpi
Installing the Python QR code library
Finding a library that provided all the right functions was the hardest part here. I ended up using mozillazg‘s fork of lincolnloop‘s python-qrcode module. mozillazg’s fork lets you use most of the lovely PIL methods, while the original hides most of them. Since I had to do some image compositing and colour remapping to make the image appear correct on the Nokia screen, the new fork was very helpful.
To install it:git clone git://github.com/mozillazg/python-qrcode.git cd python-qrcode/ sudo python ./setup.py install
The tiny 84×48 resolution of the Nokia screen doesn’t give you many options for sizing QR codes. For the time display of the clock, a 21×21 module Version 1 code with two pixels per module and one module margin just fits into 48 pixels. Using a medium level of error correction, you can fit the 19-character message (such as “2013-01-19 18:56:59”) into this tiny screen with a very good chance of it being read by any QR code reader.
(In the video, there’s a much larger QR code that’s a link to this blog post. That’s a Version 7 code [45×45 modules] at one pixel per module and no margin. This doesn’t meet Denso Wave’s readability guidelines, but the Nokia screen has large blank margins which seem to help. It won’t read on every phone, but you’re here at this link now, so you don’t need it …)
Wiring it all up
(Do I really need to say that you’ll be messing around with the inner delicate bits of your Raspberry Pi here, and if you do something wrong, you could end up with a dead Raspberry Pi? No? Okay. Just make sure you take some static precautions and you really should have the thing shut down and powered off.)
You’ll need 8 female-female jumper wires, and also some kind of pin header soldered in (I used right-angled ones). Note that the thick border of the LCD is the top of the screen. These boards are made who-knows-where by who-knows-whom, and there’s a huge variety of labels and layouts on the pins. My one appears to be yet another variant, and is labelled:
This is how I wired it (from comments in bgreat’s code and the GPIO reference):LCD Pin Function Pi GPIO Pin # Pi Pin Name ============= ============= =============== ============= 1 VCC Vcc 1 3.3 V 2 GND Ground 25 GND 3 SCE Chip Enable 24 GPIO08 SPI0_CE0_N 4 RST Reset 11 GPIO17 5 D/C Data/Command 15 GPIO22 6 DNK(MOSI) Data In 19 GPIO10 SPI0_MOSI 7 SCLK Serial Clock 23 GPIO11 SPI0_SCLK 8 LED Backlight 12 GPIO18 PWM0
Wire it up, and fire up the program:sudo ./qrclock.py
Yes, code that accesses GPIO needs to be run as root. Pesky, but helps you avoid running code that accidentally scrams the nuclear power station you’re controlling from your Raspberry Pi …
Whoa! This is so old I don’t even know where to start!
Phil sent me a note last week asking how to turn an LED on or off using Python talking through Firmata to an Arduino. This was harder than it looked.
It turns out the hard part is getting the value from the Tkinter Checkbutton itself. It seems that some widgets don’t return values directly, so you must read the widget’s value with a
get() method. This appears to work:
#!/usr/bin/python # turn an LED on/off with a Tk Checkbutton - scruss 2012/11/13 # Connection: # - small LED connected from D3, through a resistor, to GND import pyfirmata from Tkinter import * # Create a new board, specifying serial port # board = pyfirmata.Arduino('/dev/ttyACM0') # Raspberry Pi board = pyfirmata.Arduino('/dev/tty.usbmodem411') # Mac root = Tk() var = BooleanVar() # set up pins pin3 = board.get_pin('d:3:o') # D3 On/Off Output (LED) def set_led(): # set LED on/off ledval = var.get() print "Toggled", ledval pin3.write(ledval) # now set up GUI b = Checkbutton(root, text = "LED", command = set_led, variable = var) b.pack(anchor = CENTER) root.mainloop()
This is explained quite well here: Tkinter Checkbutton doesn’t change my variable – Stack Overflow. I also learnt a couple of things about my previous programs:
Hey! This article is really old! So old, in fact, that I clearly thought that saying (ahem) “w00t w00t” was a good idea. Information here may be misleading and possibly wrong. You probably want to be using a newer client library and you definitely want to use an Arduino IDE ≥ 1.6 and not the ancient one that comes with Raspbian.
pyFirmata‘s documentation is, to be charitable, sparse. After writing Raspberry Pi, Python & Arduino *and* a GUI (which should be making an appearance in The MagPi soon,
w00t w00t yeet!), I looked at pyFirmata again to see what it could do. That pretty much meant digging through the source.
Firmata can drive hobby servos, and if you’re not driving too many, you can run them straight from the Arduino with no additional power. I used a standard cheapo-but-decent Futaba S3003, which gives you about 180° of motion. The particular one I tried started to make little growly noises past 175°, so in the example below, that’s hardcoded as the limit.
#!/usr/bin/python # -*- coding: utf-8 -*- # move a servo from a Tk slider - scruss 2012-10-28 import pyfirmata from Tkinter import * # don't forget to change the serial port to suit board = pyfirmata.Arduino('/dev/tty.usbmodem26271') # start an iterator thread so # serial buffer doesn't overflow iter8 = pyfirmata.util.Iterator(board) iter8.start() # set up pin D9 as Servo Output pin9 = board.get_pin('d:9:s') def move_servo(a): pin9.write(a) # set up GUI root = Tk() # draw a nice big slider for servo position scale = Scale(root, command = move_servo, to = 175, orient = HORIZONTAL, length = 400, label = 'Angle') scale.pack(anchor = CENTER) # run Tk event loop root.mainloop()
The code above makes a slider (oh, okay, a Tkinter Scale widget) that moves the servo connected to Arduino pin D9 through its whole range. To set the servo position, you just need to write the angle value to the pin.
I haven’t tried this with the Raspberry Pi yet. It wouldn’t surprise me if it needed external power to drive the Arduino and the servo. This might be a good excuse to use my Omega-328U board — it’s Arduino code compatible, runs from an external power supply, and has Signal-Voltage-Ground (SVG) connectors that the servo cable would just plug straight into.
#!/usr/bin/python # -*- coding: utf-8 -*- import sys results= indicator=' ' left=int(sys.argv) right=int(sys.argv) while right >= 1: indicator='X' if right % 2: indicator=' ' # right number is odd, results.append(left) # so add left number to results print (" %s %16d \t %16d %s") % (indicator, left, right, indicator) left *= 2 right /= 2 print("%s × %s = %s = %d")%(sys.argv, sys.argv, ' + '.join(map(str,results)), sum(results))
So to multiply 571 × 293:
$ ./rpmult.py 571 293 571 293 X 1142 146 X 2284 73 X 4568 36 X X 9136 18 X 18272 9 X 36544 4 X X 73088 2 X 146176 1 571 × 293 = 571 + 2284 + 18272 + 146176 = 167303
Python’s still got some weirdness compared to Perl; where I’d join the list of sum terms in Perl with
join(' + ', @results), in Python you have to convert the integer values to strings, then call the join method of the separator string:
' + '.join(map(str,results)). Still, I’ll give Python props for having a built-in list
sum() function, which Perl lacks.
Okay, so maybe I can stop answering the StackExchange question “How to attach an Arduino?” now. While I got the Arduino working with pyFirmata on the Raspberry Pi before, it wasn’t that pretty. With a TkInter front end, it actually looks like some effort was involved. You can happily brighten and dim the LED attached to the Arduino all you want, while the temperature quietly updates on the screen independent of your LED frobbing.
I’d never used TkInter before. For tiny simple things like this, it’s not that hard. Every widget needs a callback; either a subroutine it calls every time it is activated, or a variable that the widget’s value is tied to. In this case, the Scale widget merely calls a function
set_brightness() that sets a PWM value on the Arduino.
Updating the temperature was more difficult, though. After TkInter has set up its GUI, it runs in a loop, waiting for user events to trigger callback events. It doesn’t really allow you to run another loop alongside its main event loop. What you have to do then is set up a routine which is called periodically using TkInter’s
after() function, which calls a subroutine after a set amount of time. If this subroutine ends with another call to
after() to call itself again, it will maintain its own event loop separate from TkInter’s GUI loop. This is what I do in the
get_temp() subroutine, which schedules itself after a ½ second.
#!/usr/bin/python # -*- coding: utf-8 -*- # graphical test of pyfirmata and Arduino; read from an LM35 on A0, # brighten an LED on D3 using PWM # Connections: # - small LED connected from D3, through a 1kΩ resistor to GND; # - LM35: +Vs -> +5V, Vout -> A0, and GND -> GND. # scruss, 2012-08-16 - tested on Raspberry Pi and Arduino Uno import pyfirmata import sys # just for script name and window from Tkinter import * # Create a new board, specifying serial port board = pyfirmata.Arduino('/dev/ttyACM0') # start an iterator thread so that serial buffer doesn't overflow it = pyfirmata.util.Iterator(board) it.start() # set up pins pin0=board.get_pin('a:0:i') # A0 Input (LM35) pin3=board.get_pin('d:3:p') # D3 PWM Output (LED) # IMPORTANT! discard first reads until A0 gets something valid while pin0.read() is None: pass def get_temp(): # LM35 reading in °C to label selection = "Temperature: %6.1f °C" % (pin0.read() * 5 * 100) label.config(text = selection) root.after(500, get_temp) # reschedule after half second def set_brightness(x): # set LED; range 0 .. 100 called by Scale widget y=float(x) pin3.write(y / 100.0) # pyfirmata expects 0 .. 1.0 def cleanup(): # on exit print("Shutting down ...") pin3.write(0) # turn LED back off board.exit() # now set up GUI root = Tk() root.wm_title(sys.argv) # set window title to program name root.wm_protocol("WM_DELETE_WINDOW", cleanup) # cleanup called on exit scale = Scale( root, command=set_brightness, orient=HORIZONTAL, length=400, label='Brightness') # a nice big slider for LED brightness scale.pack(anchor=CENTER) label = Label(root) label.pack(anchor='nw') # place label up against scale widget root.after(500, get_temp) # start temperature read loop root.mainloop()
The program takes a few seconds to start on the Raspberry Pi, mainly because initializing pyFirmata over a serial line and waiting for the duff values to subside takes time. I tried to exit the program gracefully in the
cleanup() subroutine, but sometimes one of the loops (I suspect pyFirmata’s iterator) doesn’t want to quit, so it takes a few clicks to exit.
The program also seems to chew up a fair bit of CPU on the Raspberry Pi; I had it at around 40% usage just sitting idle. I guess those serial ports don’t read themselves, and you have to remember that this computer is basically no more powerful than a phone.
So there you are; a simple demo of how to control an output and read an input on an Arduino, from a Raspberry Pi, written in Python (the Raspberry Pi’s official language) with a simple GUI. Considering I’d never written a line of Python before the beginning of this month, I think I’m doing not too badly.