I guess I’ve had some qualified success in getting the ZX Spectrum going. After finding the right (mono) cable, sticking it in the Spectrum’s EAR socket, converting tape images to audio with tape2wav, and getting a PAL/NTSC converter, I can load stuff. Sort of.
The problem is mostly with the video converter; it’s too slow. The screen refreshes a couple of times a second, which is enough to lose many Hen-House Harrys. I know, I should’ve gone for a more expensive one.
I’ve found that the audio files made by tape2wav are very loud; 0 dB, full deflection, distort-o-matic. Dropping the volume to -3 dB made games load better. Adding a couple of seconds of silence at the beginning of the file wouldn’t hurt, either.
Colour management is good. It means that what I see on the screen is what you meant it to look like, and anything I make with a colour-managed workflow you’ll see in the colours I meant it to have. (Mostly.) You can spend a lot of money to do this professionally, but you can also get most of the benefits for about $125, if you’re prepared to do some fiddly stuff.
The most important part is calibrating your display. Hughski’s ColorHug (which I’ve mentioned before) is as close to plug-and-play as you’ll get: plug it in, and the colour management software pops up with prompts on what to do next. Attach the ColorHug to the screen (with the newly supplied stretchy band), let it burble away for 10–20 minutes, and the next time you log in, colours will be just right.
Calibrating the scanner on my Epson WorkForce WF-7520 was much more work, and the process could use optimization. To calibrate any scanner, you need a physical colour target to scan and compare against reference data. The cheapest place to get these (unless there was one in the box with your scanner) is Wolf Faust’s Affordable IT 8.7 (ISO 12641) Scanner Colour Calibration Targets. If there are a bunch of likeminded folk in your area, it’s definitely worth clubbing together on a group buy to save on shipping. It’s also less work for Wolf, since he doesn’t have to send out so many little packages.
(I’ve known of Wolf Faust since my Amiga days. He produced the most glorious drivers for Canon printers, and Jeff Walker produced the camera-ready copy for JAM using Wolf’s code. While Macs had the high end DTP sewn up back then, you could do amazing things on a budget with an Amiga.)
The target comes packed in a protective sleeve, and along with a CD-R containing the calibration data which matches the print run of the target. Wolf makes a lot of targets for OEMs, and cost savings from his volume clients allow him to sell to individuals cheaply.
Scanning the thing without introducing automatic image corrections was the hard part. I found that my scanner had two drivers (epson2 and epkowa), the latter of which claimed to support 48-bit scanning. Unfortunately, it only supports 24-bit, like the epson2 driver, so whichever I chose was moot. I used the scanimage command line tool to make the scan:
which looks, when reduced down to web resolution, a bit like this:
It looks a lot darker than the physical target, so it’s clear that the scanner needs calibrating. To do this, you need two tools from the Argyll Colour Management System. The first creates a text representation of the scanned target’s colour patches:
I didn’t quite need to add that much metadata, but I could, so I did. The resultant ICC file can be used to apply colour calibrations to scanned images. Here’s the target scan, corrected:
(I’ve made this a mouseover with the original image, so you can see the difference. Also, yes, there is a greasy thumb-print on my scanner glass near the bottom right, thank you so much for noticing.)
I used tifficc from the Little CMS package to apply the colour correction:
There are probably many easier, quicker ways of doing this, but this was the first thing I found that worked.
To show you a real example, here’s an un-retouched scan of the cover of Algrove Publishing‘s book “All the Knots You Needâ€, scanned at 75 dpi. Mouseover to see the corrected version:
(Incidentally, there are two old but well-linked programs that are out there that purport to do scanner calibration:Â Scarse and LPROF. Don’t use them! They’re really hard to build on modern systems, and the Argyll tools work well.)
The last part of my workflow that remains uncalibrated is my printer. I could make a target with Argyll, print it, scan it, colour correct it, then use that as the input to colprof as above. I’m suspecting the results would be mediocre, as my scanner’s bit depth isn’t great, and I’d have to do this process for every paper and print setting combination. I’d also have to work out what magic CUPS does and compensate. Maybe later, but not yet.
I have an Epson WorkForce WF-7520, and I really like it: excellent built-in duplex large format scanner with ADF, CIFS network storage, giant paper bins, photo quality printing up to 330×482 mm, only slightly expensive print cartridges… Under Linux, though, it’s not so well behaved, especially if you want to print on large format paper. This is the workaround I developed:
Put the B-size/11×17″ paper in Tray 1 (the upper one), and the Letter paper in Tray 2. This, unfortunately, matters — the driver that can print large format can only access the upper tray. On the setup menu on the printer console, tell the printer which tray holds what size of paper.
Install the epson-inkjet-printer-escpr driver; it should be in the standard Ubuntu repos. Define a printer (wf7520-lf, for example) that uses this driver. Set the paper size to “US B 11 x 17 in”.
Ensure that the lsb and lsb-printing packages are installed: sudo apt-get install lsb lsb-printing
Download and install the non-free epson-201115w driver from the EPSON Download Center. Define a printer (I used wf-7520 for the name) using this driver, making sure that the correct PPD (epson-inkjet-printer 1.0.0-1lsb3.2 (Seiko Epson Corporation LSB 3.2)) is used. Set it up to use Letter paper, and (important!) set the source to Paper Cassette 2. You might want to make this printer the system default.
To print to large format, use the command:
lp -d wf7520-lf -o PageSize=USB file.pdf
To print regular size, just send the job to the wf-7520 device.
wf7520-lf.ppd — this is the large-format driver I use. Compare it with the epson-inkjet-printer-escpr PPD in the Debian code repo, and you’ll find all references to many paper sizes gone from it. Hmm …
Copying PPDs from one driver to another may not work, but you’ve likely nothing to lose.
Ah, the ZX Spectrum… so many hours of my youth wasted on this book-sized computer. Now anything with a display can emulate one in its spare processor cycles, the 30 year old hardware itself is a bit chunky: That’s a lot of discrete components; all through-hole, too. I brought this one back from the UK earlier this year in the hope of getting it working.
First item that needed attention was the power supply. The original had a 230 V AC to 9 V DC, 1.4 A supply of some extremely dodgy regulation. I replace this with a Circuit-Test AC/DC Adapter – 9 V DC 2.2 A, which will have plenty of current. Since the adapter has a 2.1 mm centre positive DC barrel connector, and the Spectrum uses a centre negative connector, I used the soldering opportunity to wire in an inline switch. The Speccy’s famous lack of a power or reset switch really isn’t part of my retrocomputing experience.
Next up, bypass the UK PAL TV modulator. This required disassembling the computer, and disconnecting the legendarily fragile keyboard membrane edge connectors. It’s a very simple soldering job to re-route the composite video feed (dot crawl and all) from the PCB to the Video Out. Put it all back together, plug it into the TV, and:
… another partial success. My TV doesn’t talk 50 Hz PAL composite well, giving an oversized black and white display. I’ll either need to buy a PAL to NTSC converter box, or spring for a tinymonitor which supports both standards.
Trickles out a few thousand made-with-love organic random numbers per second to the attached Arduino. The circuit is essentially Rob Seward’s True Random Number Generator v1 (after Will Ware, et al) which uses a MAX232 to power two reverse-biased 2N3904s to create avalanche noise. Another 2N3904 amplifies the resulting noise into something an Arduino can sample using AnalogRead(). Many modern processors include hardware RNGs (such as RdRand in recent Intel chipsets) so this circuit is just a toy now.
My interest in random number generators didn’t just arise from yesterday’s post. I’ve had various circuits breadboarded for months gathering dust, so I thought I’d pull out the most successful one and photograph it. Hardware RNGs seem to be a popular hobby electronics obsession, and there are many designs out there in variable states of “working†and/or “documentedâ€. I wanted one that could be powered from the 5V rail of an Arduino, and didn’t use too many expensive components. Rob’s RNG Version 2 circuit and code is the basis, but I replaced the 12V external supply with the MAX232 circuit he used in version 1.
Perhaps the reason that there are so many RNG projects out there in various states of abandonment is that making a good, reliable hardware RNG is hard. Just a few of the things you have to think about are:
Analogue sources of noise can fade over time; power supplies droop as capacitors age, contacts can corrode, … How do you deal with this fade? If the output becomes so small, can you rely on those few bits from your A→D converter to be useful noise?
Could someone try to attack your RNG so they can influence the results of your secure transactions? How would you detect it? How would you signal to the data user that something is amiss securely, such that an attacker couldn’t fake distress behaviour?
What if the generator just stops? How do you flag that in a trusted “no really i mean it and it’s really me saying this not some attacker honest no†way? There may still be a tiny bit of noise that your circuit picks up; are you sure it’s your kind of noise, or some attacker trying to inject noise into your system? Remember, testing for real noise is exceptionally hard, and you can’t guarantee that a hardware RNG that worked today will work properly tomorrow.
(I’d like to thank Peter Todd for providing most of those issues over a pint and a chat during from a keysigning event. Peter saved me from spending too many hours working on this by hinting that — just maybe — I didn’t actually know what I was doing…)
If you want to read more on how to build a proper hardware RNG, the article “Understanding Intel’s Ivy Bridge Random Number Generator†and its references make a good (if very technical in places) introduction. I’m nowhere near paranoid enough to experiment further with RNG design, although I do have all the components to build an LM393-based XR232USB…
Hey! This is a bit old! Things may have changed and I haven’t necessarily fixed them.
Most computers can’t create true random numbers. They use a formula which makes a very long stream of pseudo-random numbers, but real randomness comes from thermal noise in analogue components. The Raspberry Pi has such a circuit in its SoC, as it helps making the seed data for secure transactions. It was only recently that a driver for this circuit was supplied. To enable it (on Raspbian): I think the module is enabled by default now for the different versions of the SoC.
Make sure your system is up to date with sudo apt-get update sudo apt -y upgrade
Install the module: sudo modprobe bcm2708-rng
To make sure it’s always loaded, add the following line to /etc/modules (editing as root): bcm2708-rng
For some RNG-related stuff, install rng-tools: sudo apt-get install rng-tools
The /dev/hwrng device should now be available, but can only be read by the root user.
Yup, sounds like static. It was made with the rndsound.sh script. You’ll need to install sox to run it.
This is not random
If it sounds like static, and even if it sometimes looks like static, it may not actually be true random noise. An infamous case of a pseudo random number generator being not very random at all was RANDU, which at first glance appeared to produce nearly random results, but close study showed it to be very predictable.
I wrote (what I think to be) a C implementation of RANDU: randu.c. While it produces appropriately random-sounding audio data (randu17.wav), if you output it as an image:
Those stripes are a giveaway; there should be no order in the output. (Then again, I have no idea if I’ve implemented RANDU correctly.) Testing random data is hard, then — you really need a barrage of tests, and even some of them might fail even for truly random output. Thankfully, when you installed rngtools, it included rngtest, a simple checker for random data:
sudo cat /dev/hwrng | rngtest -c 1000 rngtest 2-unofficial-mt.14 Copyright (c) 2004 by Henrique de Moraes Holschuh This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
rngtest: starting FIPS tests… rngtest: bits received from input: 20000032 rngtest: FIPS 140-2 successes: 1000 rngtest: FIPS 140-2 failures: 0 rngtest: FIPS 140-2(2001-10-10) Monobit: 0 rngtest: FIPS 140-2(2001-10-10) Poker: 0 rngtest: FIPS 140-2(2001-10-10) Runs: 0 rngtest: FIPS 140-2(2001-10-10) Long run: 0 rngtest: FIPS 140-2(2001-10-10) Continuous run: 0 rngtest: input channel speed: (min=67.969; avg=921.967; max=1953125.000)Kibits/s rngtest: FIPS tests speed: (min=842.881; avg=3208.336; max=6407.890)Kibits/s rngtest: Program run time: 27658884 microseconds
We were lucky that none of the tests failed for that run; sometimes there are a few failures. RANDU, on the other hand fares very badly:
./randu 17 | rngtest -c 1000 rngtest 2-unofficial-mt.14 Copyright (c) 2004 by Henrique de Moraes Holschuh This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
rngtest: starting FIPS tests… rngtest: bits received from input: 20000032 rngtest: FIPS 140-2 successes: 0 rngtest: FIPS 140-2 failures: 1000 rngtest: FIPS 140-2(2001-10-10) Monobit: 730 rngtest: FIPS 140-2(2001-10-10) Poker: 1000 rngtest: FIPS 140-2(2001-10-10) Runs: 289 rngtest: FIPS 140-2(2001-10-10) Long run: 0 rngtest: FIPS 140-2(2001-10-10) Continuous run: 0 rngtest: input channel speed: (min=45.630; avg=14255.221; max=19073.486)Mibits/s rngtest: FIPS tests speed: (min=23.694; avg=154.238; max=176.606)Mibits/s rngtest: Program run time: 141071 microseconds
See? Lots of failures there. It’s hardly random at all. If you really want to get out testing randomness, there are the dieharder tests. They takes ages to run, though.
(Note: newish Intel machines also have a real hardware RNG in the shape of Rdrand.)
I trust you all got the obvious Strictly Ballroom reference in the title?
Update 2015-09: Better yet, install Infinality. It makes font rendering pretty.
Switching back to Linux from Mac is still a process of ironing out minor wrinkles. Take, for example, this abomination (enlarged to show texture):—
… No, I’m not talking about Mr Paul’s antics (or the typo in the TP post, either), but the horrid non-matching ligatures (‘attack’, ‘flubbed’, ‘targeting’) in a sea of blocky text. Almost every programme I was running had this problem. Mouse over the image to see how it could look if you apply this easy fix.
Create (or edit) the file ~/.fonts.conf ~/.config/fontconfig/conf.d, and add the following lines:
Years ago, I worked in multilingual dictionary publishing. I was on the computing team, so we had to support the entry and storage of text in many different languages. Computers could display accented and special characters, but we were stuck with 8-bit character sets. This meant that we could only have a little over 200 distinct characters display in the same font at the same time. We’d be pretty much okay doing French & English together, but French & Norwegian started to get a little trying, and Italian & Greek couldn’t really be together at all.
We were very fortunate to be using Sun workstations in the editorial office. These were quite powerful Unix machines, which means that they were a fraction of the speed and capabilities of a Raspberry Pi. Suns had one particularly neat feature:
That little key marked “Compose” (to the right of the space bar) acted as a semi-smart typewriter backspace key: if you hit Compose, then the right key combination, an accented character or symbol would appear. Some of the straightforward compose key sequences are:
Like every (non-embedded) Linux system I’ve used, the Raspberry Pi running Raspbian can use the compose key method for entering extra characters. I’m annoyed, though, that almost every setup tutorial either says to disable it, or doesn’t explain what it’s for. Let me fix that for you …
Setup
Run raspi-config
sudo raspi-config
and go to the configure_keyboard “4 Internationalisation Options” → “I3 Change Keyboard Layout” section. Your keyboard’s probably mostly set up the way you want it, so hit the Tab key and select <Ok> until you get to the Compose key section:
Choose whatever is convenient. The combined keyboard and trackpad I use (a SolidTek KB-3910) with my Raspberry Pi has a couple of “Windows® Logo” keys, and the one on the right works for me. Keep the rest of the keyboard options the same, and exit raspi-config. After the message
Reloading keymap. This may take a short while
[ ok ] Setting preliminary keymap...done.
appears, you now have a working Compose key.
Using the Compose key
raspi-config hints (‘On the text console the Compose key does not work in Unicode mode …’) that Compose might not work everywhere with every piece of software. I’ve tested it across quite a few pieces of software — both on the text console and under LXDE — and support seems to be almost universal. The only differences I can find are:
Text Console — (a. k. a. the texty bit you see after booting) Despite raspi-config’s warning, accented alphabetical characters do seem to work (é è ñ ö ø å, etc). Most symbols, however, don’t (like ± × ÷ …). The currency symbol for your country is a special case. In Canada, I need to use Compose for € and £, but you’ve probably got a key for that.
LXDE — (a. k. a. the mousey bit you see after typing ‘startx’) All characters and symbols I’ve tried work everywhere, in LXTerminal, Leafpad, Midori, Dillo (browser), IDLE, and FocusWriter (a very minimal word processor).
Special characters in Python’s IDLESome Compose key sequences — Leafpad
To find out which key sequences do what, the Compose key – Wikipedia page is a decent start. I prefer the slightly friendlier Ubuntu references GtkComposeTable and Compose Key, or the almost unreadable but frighteningly comprehensive UTF-8 (Unicode) compose sequence reference (which is essentially mirrored on your Raspberry Pi as the file /usr/share/X11/locale/en_US.UTF-8/Compose). Now go forth and work that Compose key like a boß.
I have too many passwords to remember, so I’ve been using a password manager for years. First there was Keyring for Palm OS, then 1Password on the Mac. 1Password’s a very polished commercial program, but it only has Mac and Windows desktop clients. Sadly, it had to go.
Finding a replacement was tough. It needed to be free, and yet cross-platform. It needed to work on iOS and Android. It also needed to integrate with a cloud service like Dropbox so I could keep my passwords in sync. The only program that met all of these requirements was KeePassX. I’ve stuck with the stable (v 0.4.3) branch rather than the flashy 2.0 version, as the older database format does all I need and is fully portable. MiniKeePass on iOS and KeePassDroid on Android look after my mobile needs. But first, I needed to get my password data out of 1Password.
1Password offers two export formats: a delimited text format (which seemed to drop some of the more obscure fields), and the 1Password Interchange Format (1PIF). The latter is a JSONish format (ಠ_ಠ) containing a dump of all of the internal data structures. There is, of course, no documentation for this file format, because no-one would ever move away from this lovely commercial software, no …
So armed with my favourite swiss army chainsaw, I set about picking the file apart. JSON::XS and Data::Dumper::Simple were invaluable for this process, and pretty soon I had all the fields picked apart that I cared about. I decided to write a converter that wrote KeePassX 1.x XML, since it was readily imported into KeePassX, would could then write a database readable by all of the KeePass variants.
To run this converter you’ll need Perl, the JSON::XS and Data::Dumper::Simple modules, and if your Perl is older than about 5.12, the Time::Piece module (it’s a core module for newer Perls, so you don’t have to install it). Here’s the code:
#!/usr/bin/perl -w
# 1pw2kpxxml.pl - convert 1Password Exchange file to KeePassX XML
# created by scruss on 02013/04/21
use strict;
use JSON::XS;
use HTML::Entities;
use Time::Piece;
# print xml header
print <<HEADER;
<!DOCTYPE KEEPASSX_DATABASE>
<database>
<group>
<title>General</title>
<icon>2</icon>
HEADER
##############################################################
# Field Map
#
# 1Password KeePassX
# ============================ ==============================
# title title
# username username
# password password
# location url
# notesPlain comment
# - icon
# createdAt creation
# - lastaccess (use updatedAt)
# updatedAt lastmod
# - expire ('Never')
# 1PW exchange files are made of single lines of JSON (O_o)
# interleaved with separators that start '**'
while (<>) {
next if (/^\*\*/); # skip separator
my $rec = decode_json($_);
# throw out records we don't want:
# - 'trashed' entries
# - system.sync.Point entries
next if ( exists( $rec->{'trashed'} ) );
next if ( $rec->{'typeName'} eq 'system.sync.Point' );
print ' <entry>', "\n"; # begin entry
################
# title field
print ' <title>', xq( $rec->{'title'} ), '</title>', "\n";
################
# username field - can be in one of two places
my $username = '';
# 1. check secureContents as array
foreach ( @{ $rec->{'secureContents'}->{'fields'} } ) {
if (
(
exists( $_->{'designation'} )
&& ( $_->{'designation'} eq 'username' )
)
)
{
$username = $_->{'value'};
}
}
# 2. check secureContents as scalar
if ( $username eq '' ) {
$username = $rec->{'secureContents'}->{'username'}
if ( exists( $rec->{'secureContents'}->{'username'} ) );
}
print ' <username>', xq($username), '</username>', "\n";
################
# password field - as username
my $password = '';
# 1. check secureContents as array
foreach ( @{ $rec->{'secureContents'}->{'fields'} } ) {
if (
(
exists( $_->{'designation'} )
&& ( $_->{'designation'} eq 'password' )
)
)
{
$password = $_->{'value'};
}
}
# 2. check secureContents as scalar
if ( $password eq '' ) {
$password = $rec->{'secureContents'}->{'password'}
if ( exists( $rec->{'secureContents'}->{'password'} ) );
}
print ' <password>', xq($password), '</password>', "\n";
################
# url field
print ' <url>', xq( $rec->{'location'} ), '</url>', "\n";
################
# comment field
my $comment = '';
$comment = $rec->{'secureContents'}->{'notesPlain'}
if ( exists( $rec->{'secureContents'}->{'notesPlain'} ) );
$comment = xq($comment); # pre-quote
$comment =~ s,\\n,<br/>,g; # replace escaped NL with HTML
$comment =~ s,\n,<br/>,mg; # replace NL with HTML
print ' <comment>', $comment, '</comment>', "\n";
################
# icon field (placeholder)
print ' <icon>2</icon>', "\n";
################
# creation field
my $creation = localtime( $rec->{'createdAt'} );
print ' <creation>', $creation->datetime, '</creation>', "\n";
################
# lastaccess field
my $lastaccess = localtime( $rec->{'updatedAt'} );
print ' <lastaccess>', $lastaccess->datetime, '</lastaccess>', "\n";
################
# lastmod field (= lastaccess)
print ' <lastmod>', $lastaccess->datetime, '</lastmod>', "\n";
################
# expire field (placeholder)
print ' <expire>Never</expire>', "\n";
print ' </entry>', "\n"; # end entry
}
# print xml footer
print <<FOOTER;
</group>
</database>
FOOTER
exit;
sub xq { # encode string for XML
$_ = shift;
return encode_entities( $_, q/<>&"'/ );
}
To run it,
./1pw2kpxxml.pl data.1pif > data.xml
You can then import data.xml into KeePassX.
Please be careful to delete the 1PIF file and the data.xml once you’ve finished the export/import. These files contain all of your passwords in plain text; if they fell into the wrong hands, it would be a disaster for your online identity. Be careful that none of these files accidentally slip onto backups, too. Also note that, while I think I’m quite a trustworthy bloke, to you, I’m Some Random Guy On The Internet. Check this code accordingly; I don’t warrant it for anything save for looking like line noise.
My Late 2008 MacBook was getting a little slow, so I went laptop shopping. I ended up with the Samsung Chronos 7 (NP700Z5CH). Under my budget, but met my spec in every way.
Installing Ubuntu was a minor trial, but it works, and has preserved the Win 8 (blecch!) dual-boot. If it helps anyone, the procedure I followed was:
Updated the BIOS, made a recovery DVD and shrank the Windows partition using the DISKPART app (which reminds me so much of the old VMS admin tools).
In the BIOS (F2 when the Samsung logo shows), I disabled Secure Boot and Fast Boot, but kept EFI on, as Win8 won’t work without it. I also disabled (temporarily, with Shift+1) the HD and Windows Boot Manager from the boot sequence, moving USB boot up to first place.
After trying Ubuntu from the LiveUSB, I installed it. Once it had finished and rebooted, I re-enabled HD and Windows Boot Manager in the BIOS.
Ubuntu would work fine from here, but to restore Win8 to a usable (?) state, I had to reboot with the LiveUSB image and run Boot-Repair as suggested in the UEFI documentation.
The fan maybe runs a little more than it should, but everything I’ve tried works. There’s clearly been a lot of work done on Samsung/UEFI support recently, as any of the web tutorials I found from even 8 months ago recommended really arcane stuff I didn’t actually need.
Markus Hoffmann has been very helpful with getting X11-Basic running on the Raspberry Pi. Remember how I said that the simple Mandelbrot Set test took nearly 1¼ hours to run using the interpreter? How about 2′ 6″ when compiled? That’s a speedup of 35 times! What you need to do is:
xbc -virtualm -o mandel-simple mandel-simple.bas
The “-virtualm” bit is the secret key to speed. Without it, the compiled code is a bit faster than interpreted.
If you’re running from the source code posted to SourceForge yesterday, you might want to replace xb2csol.h with this new xb2csol.h. It’s supposed to help with the compiled code. Just make clean; make; sudo make install to replace the code.
Update: Markus Hoffmann uploaded a new version of X11Basic-1.20.tar.gz to SourceForge that addresses most of these problems. I’ve edited the article to remove the obsolete bits.
More than 20 years ago, I really liked GFA-Basic. It ran blindingly fast on the Atari ST, and when it didn’t crash on the Amiga, it ran blindingly fast there too. I even wrote a review of it for comp.sys.amiga.programmer, which you can read to this day in all its textual glory. One of the e-mail addresses in that article still works, too.
I still sometimes think in BASIC, and there is much wringing of hands (not by me, really) that there isn’t a good interpreter for Raspbian on the Raspberry Pi. So when I found X11-Basic — a cross-platform GFA-Basic-like system — I had to take a look.
While I have managed to get X11-Basic demos to run, I have to say it’s not running super well. I’ll show you how to install X11-Basic 1.20 and get it (mostly) running, but it’s a bit rough on the ARM. Incidentally, these instructions also work on Ubuntu 12.mumble LTS on x86.
First, you need to install some (okay, a lot of) packages:
For X11Basic-1.20, you have to issue an extra command before the standard ‘./configure ; make ; make install‘ sequence:
sudo mkdir -p /usr/local/share/man/man1
./configure
make
sudo make install
This is enough to make a working xbasic interpreter. I made some screenshots of some of the graphics demos —
As you can see, there’s some screen corruption, but most demos just worked. Incidentally, the Mandelbrot one took almost 1¼ hours to run. Took me right back, that did (or it would have, if I hadn’t been outside bombing about in the slush on my bicycle while it churned away).
In order to see just how fast the interpreter is, I ran the formerly fearsome Personal Computer World Benchmark #8 under X11-Basic. PCW#8 used to bring 8-bit home computers to their knees, typically taking more than a minute to run. Here’s the code, indented a bit and with a timing wrapper added:
LET start=TIMER
LET K=0
L30:
LET K=K+1
LET A=K^2
LET B=LN(K)
LET C=SIN(K)
IF K<1000
GOTO L30
ENDIF
PRINT TIMER-start
QUIT
(yeah, GFA-style BASIC isn’t too pretty …)
It takes about ¼s to run. The old BBC B was supposed to take about 50s. By comparison, X11-Basic on a manky old dual-core Atom took 0.04s.
The native compiler xbc seems to work. To make a standalone binary of the above code, you do:
xbc -o PCWBenchmark PCWBenchmark.xbas
The compiled binary runs roughly twice as fast as the interpreted code. Not blazing fast, but a useful increase.
Unfortunately, the bytecode compiler xbbc doesn’t actually do anything on the Raspberry Pi yet. So here I leave it up to you to play with X11-Basic, and see what it can and can’t do.
Here are the complete 1988-vintage Sun manuals “Using NROFF and TROFF†and “Formatting Documents†scanned just for you. I’d scanned these in 2000, and they’d sat on a forgotten archive volume since then.
It is a truth universally acknowledged, that an engineer in possession of a solid-state flammable gas detector, will shortly make a fart detector with it. I’m sorry, but call it childishness, simple-minded curiosity, or the results of a diet high in polysaccharides, but this is something I have to get out of my system. (It’s okay; I’ll waft the door.)
This all started when our carbon monoxide detector decided it was past its best, and started to emit an ear-splitting shriek. Thinking there might be some cool parts inside, I took it apart. Inside, in amongst the other stuff, I found this:
Thankfully, David Cook of Robot Room had once had the same idea as me (well, minus the puerile bits), and he documented the sensor board very well: Explosive Gas Detector Board. Here are the four pins that you really need to get the thing going:
Pins 2 and 3 are active low signals. To be typographically correct, I’d write them as Enable and Gas, but that’s hard to do in fixed-pitch ASCII. I can understand why the Gas signal should be active low (think about it; if the Figaro TGS 2611 sensor fails or shorts, it will likely fail to an alarm state, so you’ll still be alive to curse the bloody noise that woke you at 03h00), but the Enable being active low? Dunno.
I was hoping to have presented a little sketch for the Digispark that would have typed something unhelpful every time that gas was detected, but it was not to be. It seems that Macs and Digispark keyboard emulation is a thing of great wobbliness, so I had to resort to an Arduino and a serial connection.
Here’s the code:
/*
gas_detector - uses board scavenged from CO detector
scruss - 2013-02-18 (unfortunately)
*/
int gas = 2; // /Gas line on pin 2
int val = 0;
int lastval = 0;
void setup() {
pinMode(gas, INPUT);
Serial.begin(115200);
}
void loop() {
val = digitalRead(gas);
if (val != lastval) {
if (val == LOW) { // LOW means gas detected
Serial.println("gas");
Serial.println();
delay(1000); // wait 1s for air to clear
}
}
lastval = val;
}
Before you ask, I tested the circuit by briefly hitting the button on a gas lighter. Honest.
I’ll keep working on the Digispark; it’s such a nifty little device, and this is such a worthy project …
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.)
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:
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:
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.
Now I’ve got my X10 system running and know its limitations, I could have saved a wheen of money not buying stuff I don’t need. Our house appears to have been wired by an, um, spirited amateur, so powerline signalling is of limited use. Thankfully, the tiny and cheap X10 FireCracker CM17A (warning: too many flashing GIFs at this link!) can be driven from heyu [previously]. You can score these on eBay for under $10, and all you need is a serial adapter to drive them.
The really cheap bit in my system was discovered in Active Surplus. I found a case of Leviton “Plug-in Frequency Transceiver Modules” for $4/each. One was out of its case, and wouldn’t you know it, it’s the same as a RR501 module, which typically retails for about $30. Sure, these are old stock and are a nasty beige colour, but they provide a way of switching a two-pin appliance. They can also relay remote commands from RF to wired controls.
The only X10 controller I can’t get to work with the Raspberry Pi is the CM19a USB PC Transceiver. I suspect it draws a bit too much power to run from a Raspberry Pi, as it makes the machine unresponsive if it’s plugged it. Running from my bench setup it works fine with the mochad driver, but no dice with the other machine. The CM19a reads wireless RF X10 commands, and it would be useful if I’d added a motion sensor. As is, I’ll stick to the lights going on and off.
(Update: there’s a good chance that my CM19a problems are down to the ancient dwc_otg* fixes I still run on my Raspberry Pi’s kernel. You probably don’t need them, and this device could work fine. One day I will find time to fix ’em …)
(Incidentally, this is the “North American Edition” because X10 RF controls are completely different in Europe, and none of the above is useful to you. Yeah, I know this article is the equivalent of PC Load Letter to you; sorry.)
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.
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[1] & 3) << 8) + r[2]
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:
sudo ./mcp3008_lm35.py
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!
The video of the Quite Rubbish Clock isn’t running the same code that’s in the listing. Here it is, showing off some of the handy code that’s in bgreat’s nokiaSPI Python class:
#!/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()
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:
cls() — Clears the screen.
led(brightness) — sets the backlight to brightness. For me, full brightness is at 768. A value of zero turns the backlight off. If you don’t have the screen LED connected to one of the Raspberry Pi’s PWM pin, this will either be full on (for any brightness >= 1), or off, for brightness=0. This is used to fade up the screen in lines 24-26, and fade it down far too theatrically in lines 72-74.
show_image(PILImage) — display a single bit depth black and white Python Imaging Library object PILImage. This can be no larger than 84×48 pixels.
load_bitmap(file, Invert) — load a single bit depth black and white BMP file of maximum size 48×84. If Invert is true, keep the colours as they are, otherwise swap black and white to make a negative image. nokiSPI flips images by 90°, so the image I loaded to show the URL of the blog post looks like this:
(I know, I could have generated this in code, but I’d already made the image using qrencode. I couldn’t be bothered working out the image size and offsets.)
The text handling functions I used are:
gotorc(row, column) — move the text cursor to row, column. The screen only has 14 columns by 8 rows if you use the standard 6×6 pixel font, so keep your text short to avoid disappointment.
text(text) — write text at the current cursor position.
centre_word(row, text) — write text centred in row row. Since the text rows are a maximum of 14 columns, text with an odd number of characters will appear slightly off-centre.
There are many more functions in the nokiaSPI class; watch the demo, have a dig through the source and see what you can use.
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;amp;t=9814&amp;amp;amp;p=262274&amp;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;amp;gt;W and W-&amp;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 lsmod command, 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:
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 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 hugevariety of labels and layouts on the pins. My one appears to be yet another variant, and is labelled:
VCC
GND
SCE
RST
D/C
DNK(MOSI)
SCLK
LED
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 …
I have, of late, been rather more attached to QR Codes than might be healthy. I’ve been trying all sorts of sizes and input data, printing them, and seeing what camera phones can scan them. I tried three different devices to scan the codes:
iPhone 4s – 8 MP, running either i-nigma (free) or Denso Wave’s own QRdeCODE ($2). QRdeCODE is better, but then, it should be, since it was created by the developer of the QR Code standard.
Nexus 7 – 1.2 MP, running Google Goggles.
Nokia X2-01 – Catherine‘s new(ish) phone, which I can’t believe only has a 0.3 MP VGA camera on it. Still, it worked for a small range of codes.
QR Code readability is defined by the module size; that is, the number of device pixels (screen or print) that represent a single QR Code pixel. Denso Wave recommends that each module is made up of 4 or more dots. I was amazed that the iPhone could read images with a module size of 1 from the screen, like this one:
On this laptop, one pixel is about 0.24 mm. The other cameras didn’t fare so well on reading from the screen:
iPhone 4s – Min module size: 1-2 pixels (0.24-0.48 mm/module)
Nexus 7 – Min module size: 2-3 pixels (0.48-0.72 mm/module)
Nokia X2-01 – Min module size: 3-4 pixels (0.72-0.96 mm/module)
So I guess for screen scanning, Denso Wave’s recommendation of 4 pixels/module will pretty much work everywhere.
I then generated and printed a bunch of codes on a laser printer, and scanned them. The results were surprisingly similar:
iPhone 4s – Min module size: 3-4 dots (0.25-0.34 mm/module)
Nexus 7 – Min module size: 4-5 dots (0.34-0.42 mm/module)
Nokia X2-01 – Min module size: 8-9 dots (0.68-0.76 mm/module)
A test print on an inkjet resulted in far less impressive results. I reckon you need to make the module size around 25% bigger on an inkjet than a laser, perhaps because the inkjet is less crisp.
I have to admit I went a bit nuts with QR Codes. I made a Vcard:
(and while I was at it, I created a new field for ham radio operators: X-CALLSIGN. Why not?). I even encoded some locations in QR Codes.
Just to show you what qrencode can do, here’s a favourite piece of little prose: