Arduino - Hacking a Canon DSLR shutter release

The Canon 450D supports external shutter release mechanisms using a simple 2.5mm TRS stereo plug - closing Tip or Ring to Sleeve will cause the camera to either take a picture or autofocus:

I created a quick circuit to test out the shutter (I've left out autofocus for now) and take a picture every few seconds using my Arduino, with the idea that I'd put together a nice sunrise timelapse. More on this later. The circuit itself is pretty simple, but the tough part for me was splicing my cable in a way that I could connect it to the breadboard.

In practice my setup is actually a little more complex as I wanted to use my SSD1306 display to show a little indicator of how many photos I'd taken without having to fiddle with the camera - note the very sketchy wiring :-)

The first thing I did was set it up to create a picture every 30 seconds to create a little timelapse of my puppy - Alfie - sleeping on a pile of my jumpers:


I used ffmpeg to produce a nicely sized mp4 video from the generated images.


Satisfied that I could get a decent result, I prepared a couple of timelapses from my living room window:

Timelapse #1 - overcast/snow

The first timelapse is a little boring - a grey sky turning dark blue then being illuminated by street lights. I pointed the camera roughly SSE/120° let the Arduino trigger a picture every 30 seconds for a few hours leading up to 5.30pm. Each frame was originally taken on the lowest-res setting available on my camera - 2256x1504 - at ISO 800 (since we'd also need to take evening pics) using a Canon 20mm f/2.8 lens. I actually started this one off at 1pm but there was a lot of boring grey sky so I trimmed it.

Timelapse #2 - sun/sunset

The second timelapse is a little more interesting. I used the same basic timing and setup but used a lower ISO since the evening pictures ended up a little darker and blurrier anyway, and this time pointed directly towards the sunset (SSW/220°). The main thing I learned from this video is that my window is filthy

Again I used ffmpeg to produce an h264 video:

Code for the Arduino sketch is available HERE but really it's just writing a 1 to a GPIO every few seconds so not particularly tough. 

Some AVR memory questions

I had a few Arduino itches I wanted to scratch, bits and pieces which aren't particularly complex or interesting enough to warrant a standalone blog post, but which are nonetheless worth spending a few paragrapsh on.

Why does every Arduino build include "-R .eeprom" when producing the ELF binary?

I had previously wondered whether it was necessary to pass in "-R .eeprom" when building the ELF binary, but I didn't really explore it much at the time. Essentially all articles or guides about using the GNU toolchain to compile programs for Arduino/AVR (and indeed the Arduino IDE itself) specify this switch so it's really worth clarifying what it's all about.

The first thing is that some articles are just plain wrong in their documentation about this option, for example the Arduino Playground docs for FreeBSD state that it's required to upload our binary to the Arduino board:

This is pretty wrong, since in avr-objcopy the -R switch actually specifies sections to be removed from the binary - per the manpage below:

So let's investigate what's up - here's an avr-objdump on the binary produced by blink.c:

There's actually no .eeprom section here to be removed - so nothing in binary would end up there anyway. If we define a variable with the necessary attribute "__section__" we can force something to be placed here, like the following string:

char *foo __attribute__((__section__(".eeprom"))) = "bar";

and now if we rebuild our ELF binary and inspect using avr-objdump:

All of a sudden we've got an .eeprom section in our executable, which would be removed if we followed the default instructions.

So why is this removed by default when uploading an Arduino sketch? I can only guess, but presumably it's just a common courtesy to anyone who uses .eeprom as persistent runtime storage - so they don't accidentally hose their data when programming their board. Hopefully if you know enough to place a variable in .eeprom, then you'd have the know-how to be able to alter the build configuration to omit these flags. It could also be possible that the eeprom wears out after fewer writes than internal flash so a bit of extra care is taken to preserve it - though I really cannot say why.

How is Arduino memory laid out and what controls it?

I was curious about how the memory layout is defined in the avr-gcc toolchain used in building programs for the Arduino, and ow it can be controlled. I've played around with LDF files for Analog Devices' Blackfin and SHARC chips (which look like ADSP-BF533.ldf and ADSP-21160.ldf respectively), but have never used GCC's linker scripts before.

On OS X using CrossPack these linker scripts reside in /usr/local/CrossPack-AVR/avr/lib/ldscripts, and there are an astonishing 95 of them in total:

These linker scripts aren't defined per microprocessor (i.e. atmega328, atmega168), but are written per architecture - taking a quick look at the documentation at nongnu.org we can see that the AVR chips used in current Arduino boards are all in the avr5 family (except for the atmega2560, which is avr6) so we can narrow things down slightly, but there are still five linker scripts to choose from:

Which one of these is used depends on the arguments passed to the linker, and each of them should be different and tailored for a certain purpose - although in practice this is not strictly true:

  • avr5.x: default linker script, this is used for linking straight C programs
  • avr5.xn: linker script used when the "-n" flag is passed to the linker. According the the ld manpage this flag is used to turn off page alignment and mark the output as "NMAGIC" however diffing this and avr5.x shows no difference in practice.
  • avr5.xbn: used when the "-N" flag is passed to ld. It's meant to mark the .text and .data sections to be readable & writeable (as well as not page-aligning the data segment apparently) however again this is script is identical to avr5.x.
  • avr5.xr: used when "-r" flag passed to ld. This is to "generate relocatable output" - but essentially it's used to pre-link C++ files I think?
  • avr5.xu: used when "-Ur" is passed to ld. This is used for the final link on C++. It's roughly the same as avr5.xr but includes something about ctor table

To see what the memory layout the basic linker script produces, we can take a pretty boring program like blink.c and take it apart using avr-objdump. The GCC docs have a nice wee visualisation of the SRAM usage of an atmega128, which won't be a million miles away from the 328p:

So the SRAM contains Data, BSS (a segment containing variables initialised to zero) and then some space for the heap (which starts at the address __heap_start and continues to a handful of bytes before the stack base) and the stack begins at RAMEND (address 0x8FF on Atmega328p) and grows down as necessary.

This is why you get that little "low memory" warning in the Arduino IDE - if there's only a couple hundred bytes spare in SRAM it's not too hard to accidentally write a program that uses too much stack and ends up overwriting some of heap variables. There's no memory protection (that I'm aware of) on these chips so there's no way to prevent this other than being careful.

How quickly can we access the Arduino's SRAM, Program Memory and EEPROM?

I wrote a couple of simple benchmarks to see how quickly we can access the different memory types available on the Arduino:

  • read: read and sum a 256 byte array
  • write: write the values 0...255 into a 256 byte array

This is not massively scientific but I just wanted ballpark figures to see how things stacked up. The results I got were:

memory read (μs)
write (μs)
data 148 148
program 196 n/a
eeprom 584 859580

I was pretty surprised by how fast reads from program memory could be, and even more so how quick eeprom reads were - but slow eeprom writes aren't particularly surprising. It's worth noting that for program memory at runtime you can only write to certain sections reserved for the bootloader and I want to risk messing that up so I left it out.

Debugging a TP-Link WR741ND using serial

I bought a little GL.iNet router and flashed it with a fresh OpenWRT image but I've been unable to do anything with it yet since it was unable to connect to the net via my apartment's ethernet connection. After some diagnostics I managed to find out that I needed to find the PPPoE username/password my working router used to connect, which was where my weird adventure began. 

My landlord or previous tenant had (smartly) changed the default password - admin/admin - so I wasn't able to use the web interface or SSH. I realised there was likely some Serial headers on the board I could use, so I dug out the schematics, cracked open my router and hoped to god that there was no login required, so that I could retrieve the settings from /etc/chap-secrets where I think they reside.

My router is a TP-Link WR741ND v4, a router that sadly doesn't already have serial pins which meant I needed to do a little soldering.


Two things need soldering, first is the connection between TX header and the onboard MIPS cpu as it's left open on this version of the router: 


Once this was done I just needed to connect some cables to the TX, RX and GND headers (I also connected up VCC in case I ever needed it in future):

Once these were sorted I was able to hook up my USB-TTL cable, open up terminal and crosss my fingers to see a very reassuring Linux boot in progress...

... followed swiftly by a login prompt!

I genuinely hoped that serial access would not be password protected, but it seems that I need to regroup and figure out how I can maybe retrieve the pppoe information some other way.

There is hope - typing "tpl" and hitting <Enter> during the boot process takes me to the u-boot prompt below 


So it's possible that either:

  1. I could coax uboot to boot into linux in single user mode
  2. Theres a way I could dump the data in the router's flash memory (I think md/mw/cp uboot commands can help here), find a way to access the filesystem this way. 
  3. I could get uboot to load a minimal linux image over TFTP (using tftpboot), then mount the flash file system and browse it.

 The final alternative is to brute force the username (probably still admin) and password, which is a little inelegant and slow.

A tiny font ... Eastern European edition

In a previous post I created a little replacement font for the Adafruit Graphics Library, but left the non-ASCII characters (values > 0x7F, 128 in total) completely blank. This is because this area is used to implement the additional symbols necessary for languages other than English, and I had initially just intended to create an English language font for my own purposes.

Obviously you can't represent all of the necessary symbols for all the world's languages in just 128 slots, and this is where ISO 8859 comes in*. There are 16 ISO/IEC 8859 character sets which implement the symbols for loosely related (and sometimes unrelated) languages in the upper 128 bytes.

The most common one is probably ISO 8859-1 which can represent most of the EU's main languages, so it should be a pretty sensible one to implement. However since I live in the Czech Republic and I love Central\Eastern Europe I decided to implement ISO 8859-2 which covers Bosnian, Polish, Croatian, Czech, Slovak, Slovene, Serbian, and Hungarian and looks like the below

Note: the above rendering I picked up someplace on the internet is actually incorrect - when the letters t and d have a caron/haček it actually looks more like an apostrophe - ď and ť.

I only implemented this for my slimmer 3x8 font, but if there's any interest I can quickly put together something for the Adafruit 5x8 one or attempt a different character set so long as it's based based on a latin script. Sadly I'm not sure if my skills are up to the task of creating something tougher like Chinese, Tamil or Thai.

I'm a novice at font design, but looking at the required diacritics it seemed to make sense to reserve the top two rows for things like Haček (e.g. č, ř and ž) and Čarka (e.g. á, ý and é) and the bottom row for anything below the letters - which leaves a 3x5 space in the middle to implement the root of each letter - which I managed to do with a couple of exceptions. Here's a quick visualisation of this:

So for example if we want to create the letter "č" it would look something like this:

With only three columns available there are a number of characters which will look a little weird - the worst of which were Đ, § and ď - and the Albanian characters with the tail (ç, ţ and the like). Here's what the font looks like:

It's a little cramped - with some characters appearing to be joined to those above/below - which is due to the fact that the top rows were previously blank and served as line spacing.

I've pushed this into my fork of the Adafruit GFX Library repo on github - check it out and copy the whole thing into your Arduino libraries folder to install it. Note that the Arduino IDE saves files using UTF-8 encoding, so you can't just throw string literals in and assume they'll work - the simplest way to output a string using this character set is to manually enter the hex values. 

So if we want to have the string representation of a nice little Czech sentence, we'd encode it as follows:

When we pass this to display.write() - not display.print(), which will just output the numeric values - we'll see this:

* = Yes I know that we have unicode to properly address this problem, but it's quite complex and is mercifully not used by the Adafruit Graphics library for simplicity's sake.

A tiny new font for the SSD1306 128x64 OLED screen

The default font for the Adafruit graphics library is a 5x7 font, it looks a little something like this:

It's readable, but I thought it'd be interesting to use a more compact font like "Tom Thumb" by Robey Pointer:

The font currently bundled with the library is in a file called glcdfont.c and is defined as a big byte array:

Each line of that array is 5 bytes and represents a simple monochrome bitmap of a single ascii character, with each byte representing a column. For example example the letter "a" is represented by the following line:

0x20, 0x54, 0x54, 0x78, 0x40,

In binary each of these is:

You can almost see the shape if you twist your head 90 degrees to the right, but to make things a little clearer here's what this actually represents:

If we were to use the Tom Thumb font we could save ourselves two bytes per character, since it is a little slimmer, meaning that "a" could be represented by the following three bytes:

0x68, 0x58, 0x70

Or, in binary:

Which looks like this:

Since there's a bit of wasted space at the top (the top two bits won't get used at all) we could technically save ourselves a little more space, but for simplicity's sake I'm going to sacrifice those two bits. Currently the font can be easily retrieved and manipulated as each column fits neatly into a single byte, however we'd introduce a good deal more complexity by squeezing those additional bits - which would mean substantial changes to the driver itself to compute the correct address to read the bitmap from, and shift/mask the necessary bits.

However even with this tradeoff we can still save 2 bytes per character, which is a pretty impressive 512 bytes over the whole ascii space which is pretty substantial.

The main challenge is creating a separate .c file we can swap glcdfont.c out for. I had trouble dealing with the BDF file Robey shared, but since there's a little bitmap representation of the whole ascii space it wasn't too tough recreating the font by hand ... just extremely tedious! I hacked together a simple little app (source is here) using Processing that let me paint each character by clicking boxes and which spat out the hex values when I hit a key. I'd then copy and paste this into the glcdfont.c. As I said, extremely tedious.

Once I'd produce my file it was time to test it - the beauty of having the 3x6 font means that the entire ascii space could be displayed on a 128x64 screen:

It's actually pretty surprising how much text you can fit on screen.

And just a little reminder of how small this screen is, with a €2 coin for scale

You can see the amount of memory saved in the .text section (where program code lives) by examining binaries GCC has produced - exactly 512 bytes.

My fork of the Adafruit gfx library with the Tom Thumb font is at https://github.com/smcl/Adafruit-GFX-Library and is a drop-in replacement for the existing library (replace the entire library, though, since I made some changes in Adafruit_GFX.cpp. You can switch between the fonts by toggling the #if ... #else condition in glcdfont.h below:

Apes and Embedded Systems (part two)

In part one I just hooked up a little monkey keychain called Chuggzilla to my Arduino. A day or so prior to my 90 Kč spending spree on Chuggzilla I'd just received a 128x64 Adafruit OLED display and realised this was a nice opportunity to play a bit with it.

The idea was that Chuggzilla would still scream his weird morse code madness, but the screen would display the actual text behind the message, with the current letter highlighted. I was just wanting to quickly get something working, so instead of integrating with the OLED library using C I just ported my morse code stuff to the Arduino IDE so I could easily re-use the existing API. The final result is pretty fun - though I suspect the current drain from the screen is too high because the previously manic ape noises were almost whisper quiet.

I am not sure exactly who would want to reproduce this, but if you're interested hook the OLED screen up as per the Adafruit wiring instructions then connect up whatever animal or thing you want to control per my original schematic (connecting digital 2 instead of digital13 to the transistor's base, since we need that for the screen), create a sketch with the code below and add the Adafruit libraries.

Code is on this gist on github. 

Apes and Embedded Systems (part one)

I woke up today and decided to do some neurosurgery. I set off into the town and sought out a willing test subject - ladies and gentlemen, meet Chuggzilla (credit goes to Miriam for the name):

Chuggzilla is a keychain monkey I bought in Tiger for around 90 Kč. Pressing a little button on the back of his head causes his LED eyes to light up and a little speaker inside to play a monkey noise:

This is what he looks like on the inside

I soldered a couple of wires to either side of the button element, so that I could control him electronically:

Testing him out with a button on a breadboard

Finally being controlled (via a transistor) by an output pin on the Arduino using the morse code program I previously wrote - the message is "hello world" or ".... . .-.. .-.. --- / .-- --- .-. .-.. -.."

The circuit driving this is below. I used a little LED for debugging when I was checking Chuggzilla was connected fine, and just kept it in the circuit - you can omit it if you're certain your animal is wired up fine :)

The code behind this is at https://github.com/smcl/arduino_morse - it's straight C, so if you want to load it to your Arduino you'll need to have the AVR GCC tools installed, and change the AVRDUDE_PROGRAMMING_DEVICE variable in the Makefile, then run make flash.

Arduino primer/refresher - OS X perspective

I've been wanting to get back into some interesting embedded systems and electronics work - so I figured I'd dust off my old Arduino. In the process I ended up having to re-learn how to program it using C without using the Arduino IDE (it's not my favourite thing), which is what I'm going to go into here.

I downloaded CrossPack which contains GCC cross compiler for the AVR architecture, as well as a handful of other useful tools (like avrdude, which we'll use to flash the Arduino). Once that's setup we can start off with a pretty simple program which rapidly blinks the arduino's onboard LED, which is sort of the Hello World of the Arduino community:


To build this using GCC we just need to call avr-gcc, making sure to specify the processor (using -mmcu), define the processor frequency (the F_CPU macro)


This creates an ELF binary. One odd thing is that pretty much everyone building C code for Arduino seems to include the "-R .eeprom" switch which removes the ".eeprom" section from the output binary. However I've not ever seen this section does not exist in my ELF binary - see below:


Anyway, perhaps there's something else at play that I've not understood so I've kept this switch in (I'm making a mental note to explore this later). Again many other places seem to insist on using the avr-objcopy utility to produce intel hex file before uploading to the Arduino - so often you'll see the below:


However again this is not a necessary step it seems, since the utility we use to upload the binary to the Arduino - avrdude - supports ELF so you can skip it. When we want to upload the binary to the Arduino we'll use avrdude, but first we need to know the programming device. For me this is just the USB type A to type B cable that comes with the arduino, which in the example below is /dev/tty.usbmodem1421:


I've found it useful to create a little Makefile to tie these steps all together:


Finally to tie this together in a slightly more existing example than blink.c, I've created a little program that will flash the LED attached to pin 13 with a message encoded in morse - "hello world" or ".... . .-.. .-.. --- / .-- --- .-. .-.. -..":


The code is on github at https://github.com/smcl/arduino_morse and only requires a bare Arduino and USB cable.

BeagleBone Black - loading OpenBSD from an OS X machine

I thought I'd share the process of installing OpenBSD onto a BeagleBone Black from the perspective of someone who's maybe not experienced with either, and is doing so from OS X. Ted Unangst has a good article on this but there's a couple of bits that maybe need to be a little clearer. What I've got is a little guide to install OpenBSD, mount /var and /tmp in memory to spare our SD card, and build and setup the avahi port.

Equipment

Equipment needed:

  • BeagleBone Black
  • MicroSD card >4GB or so
  • A USB -> TTL cable - (try this one if you're in the EU, cheap and worked just fine)
  • Ethernet cable, and Router\Hub\Switch with internet a
  • Power source for the BBB (USB cable is fine)

Sadly the USB-TTL cable is necessary, since the OpenBSD installer doesn't seem to support the HDMI output on the BBB and this is probably the simplest (or only?) way you can interact with it during this process. Also note that the ethernet cable probably not necessary but it'll make your life a little simpler by letting you download and install everything first without having to worry about setting up some packages later.

Preparation

Firstly you'll want to download the OpenBSD image which is currently miniroot-am335x-58.fs (note: it is the am335x one and NOT the "beagle" one, which is actually for the slightly older BeagleBoard) and use it to create a bootable MicroSD card. You have probably done this dozens of times, but it can't hurt to repeat this info:

Then with the BBB powered off, remove the SD card from your Mac and plug it into the Beagle's SD card slot, plug the USB end of the USB->TTL cable into your Mac, and the other end into the BBB. It's a little hard to see but there's an arrow on the TTL plug and a little white dot beside one of the pins on the board - this indicates the orientation you should use when connecting it. It should look a little something like this:

Then open up a terminal on the Mac and connect to the BBB using screen:

Attach your BBB to your network and finally it to a power source. You should see the installer kick in and dump a load of text to the screen in your screen session in the terminal.

Installing OpenBSD

You can probably just hit <return> through the installer, but it is not very complicated so I'd suggest reading everything relatively carefully (disk layout can be skipped, the automatic layout was fine for me). By default OpenBSD will be installed onto your SD card instead of the BBB's internal eMMC chip, if that's fine by you then all good. If not, pay close attention and change it.

Note: this process will be VERY slow, and may appear to be hanging at some points (particularly building the disk partitions on your SD card). Just be really patient.

Once the install is finished you can restart the BBB. Keep the serial cable attached and let the board boot up, log in and use `ifconfig cpsw0` to find your ip address (for OpenBSD newbies: the network interfaces named after the device driver instead of like wlanN/ethN/enN) then log in via SSH. You can now kill your USB-TTL session in screen, disconnect the cable and put it somewhere safe.

Setting /tmp and /var to use mfs

To reduce wear on the SD card we're going to make some changes to /etc/fstab so that /tmp and /var are actually in-memory filesystems using mfs. First we might need to find the path to the device in the filesystem as OpenBSD may have set up /etc/fstab using DUID (take a look at this FAQ, and scroll to the "Disklabel Unique Identifiers" section):

Now that we know /var is /dev/sd0d and /tmp is /dev/sd0e we can open up fstab, comment out the existing lines where they were mounted as locations on the SD card and create new ones mounting them as mfs partitions:

The changes will take effect after a reboot.

Ports

Congrats, you now have a sort-of usable BeagleBone Black running OpenBSD 5.8. It's probably unlikely that everything you need is in the OpenBSD base install, so to install any extra packages we need to turn to the "ports" system - which is a prepared set of makefiles which can be used to build a whole host of software (on non-ARM architectures we'd use pkg_add to install pre-built binaries but these aren't available on ARM).

While connected to your BBB then download and extract the latest ports per the below: 

In the ports directory there are numerous folders containing makefiles which define how many different packages can be built and installed on OpenBSD. There’s a great deal of information on the OpenBSD website and a slightly gentler introduction here.

Installing Avahi

Avahi is a pretty useful piece of software which will allow us to communicate with the BBB on the local network using <hostname>.local - so if you've set your hostname to be pyongyang you can ssh to pyongyang.local. First we need to find the location of the avahi package in the ports directory, which we can do using `make search`

This tells us that the avahi port lives in the folder net/avahi, so we can just go there and install it as follows:

A bit of patience is again required here as this will take a while. 

Setting up Avahi

After the avahi package is installed there's a bit of setup required. Firstly enabling multicast_host in rc.conf.local by adding the following lines:

Then we need to open up /etc/rc.local and ensure that the require daemons are running. If you already have dbus-daemon in rc.local, you can just add the avahi-daemon and avahi-dnsconfd sections after it, otherwise copy-paste all of this at the end of the file:

Finally you'll need ensure that avahi advertises the correct hostname on the network, so open up /etc/avahi/avahi-daemon.conf, locate the [server] section and specify the hostname you'd like to use to connect to the BBB. I've used pyongyang.local:

There will be a lot of other settings in this file, but we can leave these alone for the time being. After you reboot the BBB you should be able to ping the board and connect via ssh using the hostname you selected:

At this point you should have a nice network-connected BeagleBone Black running OpenBSD which you can easily access through a *.local address on your local network and which shouldn't chew through an SD card in months. Now you just need to find something useful to do with your new setup. Have fun!