Wireless SSDV images from a canon camera using RTTY on CHDK

CHDK (canon hackers development kit) is a temporary firmware hack that allows you to run custom code and commands on canon compact digital cameras. It is often used on high altitude balloon flights to run an intervalometer to take a photo every x number of seconds. The scripts are written in LUA or uBasic, with LUA seeming to be the preferred language. You can take this one step further and use CHDK it to log data such as CCD temperature like Mark Ireland (S_Mark) from Stratodean.


Now the above is pretty neat, but up until starting this project I really underestimated the power of a camera running CHDK! In the article below I will be using a Canon PowerShot A530 to broadcast photographs over a Radiometrix NTX2 FSK radio module, in a similar way Dave Akerman does on his Raspbbery Pi payloads.


SSDV (Slow Scan Digital Video) is a packetised digital form of SSTV (Slow Scan TeleVision). It can be used to transmit small images along with the regular telemetry transmitted by a payload during flight. Any digital mode that can carry text or data can be used, although the current implementation is limited to 8-bit RTTY (Radio TeleTYpe).

This is a vast improvement over simply sending the JPEG data as it supports forward error correction in the (likley) event that some bits are scrambled in the receiving process. It also supports integration into the UKHAS distributed listener platform, a group of enthusiasts who tune their HAM radios and SDR’s into balloons and upload the data to a central server.


RTTY was originally used on teleprinters in the form of Baudot (5 bit). An RTTY transmitter broadcasts a continuous carrier which shifts up and down in frequency depending on the bit being sent. This produces the MARK (upper) and SPACE (lower) tones. You can see these two tones in the waterfall above). This is known as FSK (frequency shift keying). In this case we will not be using Baudot, instead we will be using 8 bit ASCII with one start and one stop bit to send a byte of SSDV data at a time.

On the CHDK wiki I started to read up on how toggling one of the cameras LEDs can be used to dump the firmware of a camera over serial. So I started off trying to produce RTTY by toggling an LED on and off. Whilst the code behind this worked, the timing was very inconsistent and I struggled to get anything above 33 bits per second. Not ideal for sending images! I figured there must be an alternative.

The camera has an ARM processor and runs VxWorks as an operating system, A popular OS for embedded devices ranging from mobile phones to the Mars Curiosity Rover. Using the cameras UART connections it is possible to access the VxWorks event shell and issue basic commands to the camera. The UART connections on the A530 are located under the display and require some fiddly soldering to the unpopulated chip footprint to connect! (Red wire below is the Tx) The UART’s default settings are 115200 baud 8N1.

Image from CHDK wiki

Upon experimenting with the UART connected to PuTTY running on my PC, using a USB to TTL converter. I found that the Printf command seemed suitable for writing text to the port. The Printf command is a native call and must be registered by calling SystemEventInit in the VxWorks shell before using it.

To call a native VxWorks function from LUA you must enable native calls in the CHDK menu (ALT, menu, misc, enable lua native calls [check]). Commands can then be executed using event_proc as below.


call_event_proc(‘Printf’,’Script Started’)

To make the UART suitable for RTTY the baud rate will need to be dropped to something a little more sensible, in my testing both 50 baud and 600 baud worked well, however 300 baud seemed to drop characters. Reyalp on the CHDK wiki helped me out by showing me how ioctl() can be used to change the UART settings, and finding the memory addresses for it along with the function codes for some of the commands from VxWorks.

fd=call_func_ptr(0xffec8b34,”/tyCo/0″,0,0) –fptr_open (Opens the UART on fd)
print(“fd:”,fd) –fd>0 if the UART was opened successfuly
status=call_func_ptr(0xffec8630,fd,4,600) –fptr_ioctl(4=FIOBAUDRATE,600=baud)
status=call_func_ptr(0xffec8630,fd,3,0) –FIOSETOPTIONS,3=OPT_RAW,0=OFF (Stops VxWorks messing with null bytes and line returns)
print(“status:”,status) –0 probably means success
call_func_ptr(0xffec84f0,fd) –Closes down fd

The 0xffec8630 function address points to ioctl on the a530, this will probably vary from camera to camera and can be found in stubs_entry.s for your camera in the CHDK source. The function codes for the various icotl commands in VxWorks can then be looked up here [backup: ioLib].

Initially I had a lot of trouble with VxWorks messing with some bytes I was sending to the UART, such as 0x00, and 0x0D being automatically followed by a 0x0A (need to check this). This can be overcome by setting the OPT_RAW option in  FIOSETOPTIONS as above.

I found that printing a whole binary file in one go as a single string did not work, so I use a for loop to print each byte in a string to the UART, rtty being the string name to print below. (note in LUA strings start at character position 1)

for d=1,string.len(rtty),1 do

Now from this point I could encode a JPEG image to SSDV using Phil Heron’s [fsphil] C based encoder running on a Linux PC. Copyy the files to the SD card,and then read them out to the UART and have dl-fldigi decode them perfectly. The next step is where the real fun started, compiling the C based SSDV encoder into the source of CHDK…


With some more advice from Reyalp at CHDK, Phil and I had a go at setting up the environment required to build CHDK, with myself working on Windows and Phil working on Linux. After a couple of days of Phil installing various bits of Linux software such as GCC, and myself trying to download one of the thousands of CHDK source trunks that would actually build, we managed to succeed at about the same time (For some reason the windows GUI decided to auto-update and started working from that point…). Phil had a go at implementing the C code from his SSDV encoder into the source and we had a semi-working encoder after just one evening! It took a few more revisions of the firmware as we tested different ways of calling the functions and addressing memory.

CHDK build with SSDV for a530 ONLY!

NOTE: I have built the firmware for every platform supported so to download a binary, scroll down…

You can use CHDK-Shell GUI under windows to compile the patched trunk for your camera. Or if you are a Linux super genius you can build it using GCC. For windows head to the page linked above. Read it ALL! Download the latest FULL program, run it, enable online mode, let it update, restart. Keep doing the above and eventually you will be up to date enough for it to automatically download the most up to date trunk for you to use.

Change the trunk in CHDK_Shell GUI to the trunk we just patched:selectingtrunk


CHDK_Shell should restart and you can tick off your cameras firmware on the list then click “compile selected”select camera and build

Unzip the binaries found in chdk\trunk\trunk3040_PATCHED\trunk\bin (or wherever) to your SD card and boot CHDK, its that easy!

I have had a go at building for every platform available so if you are feeling lazy, give one of these a go. TRUNK3039_SSDV BINARIES DOWNLOAD (Thanks to Dom for hosting the files!)

The first step to encode a JPEG image from LUA is to open the file and read it into memory (JPEG string).


We now have to create a new encoder object which we can feed the JPEG file into (specifying the callsign as CALL and imageID as 0).

s = ssdvenc.new(“CALL”, 0)

Now we feed the JPEG file (held in the variable we set earlier) into our SSDV encoder object above.


Now we need to enter a loop to make the object encode each packet, checking if the encoding is complete each time so we can jump out of the loop.

i=1 –packet encoder counter
rtty = “” –initialize output string
repeat –start loop
print(“Packet “,i,” encoding”)

–print encoder status message to screen
r, packet = ssdvenc.read(s)

–Read the output packet from the encoder, r being encoder status and packet being 1 SSDV packet.
if r == SSDV_OK then
–If we have data, then do something with it
rtty = rtty .. packet –concatenate new packet onto the end of output stream
i=i+1 –increment packet counter
until r ~= SSDV_OK –keep trying to get new packets until the encoder reaches EOF
ssdvenc.close(s) –Close the encoder

So now we have SSDV data in the rtty string. All we have to do is loop through each byte in the string and print it out the the UART

for d=1,string.len(rtty),1 do –loop through each byte

–print byte to UART as a byte

You *should* now have 600 baud SSDV flowing out of your hardware uart and into your ntx2. With a suitable voltage divider you can set the shift to 600Hz and have this decoding in dl-fldigi and uploading to the web!

Whilst Phil was compiling the new source, I worked on integrating the transmitter into the camera. The battery bay worked out to be the perfect size, with space for an SMA connector in the battery door!

The now out of action battery bay is not a problem, as I wanted to build a 4 cell battery holder to power the camera anyway. This will probably be a 3d printed addition to snap on to the back of the camera or on to the side. As I wanted to demo this at the UKHAS 2013 conference I have added 2 AA holders to the side of the camera just with hot glue for power.


Demo video of the camera in testing.

People have showed a lot of interest in the project and with a few more tweaks to the code I will be ready to launch it in the next couple of months!

The only thing preventing the camera becoming an entire self contained tracker is the lack of positioning data. I am hoping to be able to get hold of a new canon camera with GPS for geo tagging and send back position reports. I very much doubt canon will have used any specialist GPS chips in the camera to allow it to work above 12km (to comply with US regulations that prevent you using a commercial GPS to build your own cruise missile). I guess the only way to find out is to rip one open! Chances are that whatever GPS module they have used will output serial NMEA data, so a uBlox chip (works to 50km) could quite easily be patched in.

Stay tuned for updates!

CHEAPO7 flight 17/08/2013

I wanted to test the new cheapo4 board at altitude to see how well it worked, but to comply with the <2m rule I could not use a standard 100g balloon (http://randomsolutions.co.uk now stocks a 100g that complies with this rule, which I look forward to testing soon).

After picking up some 36″ party balloons from ebay and testing their burst diameter. I discovered they burst at about 1.2m, well within the limit. The new cheapo4 board was assembled and tested a few days before the flight, and again the night before the flight and worked fine. However once the balloon was filled and the payload attached it refused to get a GPS lock! After hours of moving things around, making sure the battery was out of the way and breaking out the ublox tx pin. I found that it was reporting back 0 satellites with the odd 1 popping up now and again. Not good.

With some more tweaking and still no luck we gave up, but determined to still launch something I pulled out an old cheapo3 board. This too refused to get a fix for some time but eventually came to life. We attached it to the already filled balloon and promptly launched it.

We began the chase , but after what seemed to be no time at all the balloon’s ascent rate started to drop. I thought this could have just been it struggling to break through a layer of cloud but after reaching a grand 813m altitude it started to descend again at about 1.5m/s.

A little disappointing we headed to the landing site and recovered the payload, which was sitting in the middle of a soaking wet corn field (easy recovery none the less).

9592861580_10585d80b3_b 9592864290_8e5f3b975e_b

cheapo7 path cheapo7alt

The cheapo4 tracker seems to have come back to life today, I have no idea what the problem was.. Just hope it doesn’t happen again!

I will certainly try this type of balloon again as I have a few left. It probably just leaked through the neck as I only tied it off with one cable tie. Next time I will consider just sealing the balloon with a conventional knot.

CHEAPO6 foil pico – it floated!

Left this write up a little late… But this was my first successful float! Using a cheapo R3 board powered from a cheap ebay boost regulator. These things are designed for > 0.8v input and 5v regulated output. It turns out by changing a resistor value you can modify them for 3.3v output. These things are not very efficient so only lasted about 8-10 hours.

We filled the 36″ foil balloon carefully using 1.5g of solder to measure the free lift. Which proved to be just about perfect (thanks Leo!). The tracker performed fine (still concerned about the GPS bug before, even though it did not reach 12km) and the balloon floated at a steady 3.7km for 10 hours before the batteries died over the north sea.

cheapo6 path

I have been working on a new tracker PCB which should be smaller, lighter and last longer. Stay tuned for updates!

CHEAPO5 + MONTY1 Charity Flight


This was a charity flight by Matt Downs to raise money for the Cats Protection League and Essex Air Ambulance. I offered to help out with launching and chasing with Matt and flew a cheapo R3 board as backup. Matt’s tracker worked perfectly and got some great stills from the gopro camera. However cheapo hit a bug at about 12km altitude and started giving incorrect GPS coordinates. The current theory is that the 808 camera I (stupidly) attached to the tracker in an effort to catch the burst, caused interference with the GPS. This caused the GPS to reset, taking it out of flight mode and defaulting back into pedestrian mode. 9430521810_2611f6c2b7_o

We were in the paper!


Filling the 1600g balloon.


Letting it up.


Running with the payload to launch.



Easy recovery! Trailed out along the pavement.


CHEAPO4 foil pico flight – 07/07/2013

After the disappointing signal reports from cheapo3 I decided to investigate the cause of this. The most efficient, fast and cost effective way of doing this was obviously to just build up a new board and launch it. This also gave me a chance to play around with foil balloons which seem far more suited than small latex for this sort of payload.

I made the 1/4 wave antenna using cat5 cable instead of telephone cable this time around and it seemed to make all the difference! Had lots of great signal reports and a fair few trackers. The software had also been updated with some power saving code dropping the current down to 50mA, and a distance from launch site calculation added. The tracker complete with batteries and foam box weighed in at about 40g.


I aimed for a fairly low ascent, but by just measuring this by eye in quite a confined space (my garage) it was probably a little on the overfilled side. I cannot imagine having to fill one of these outdoors!

Instead of using a plastic tube for filling I simply opened the valve using a drinking straw then filled the 36″ qualatex balloon using the filler nozzle on a disposable 50 helium canister.

I filled 2 small latex balloons to use as wind indicators and to test for tree clearance. Just as well as the first one got stuck!


Launch video:

Unfortunately the balloon did not enter a float despite its fairly slow ascent rate. At about 5400m the balloon started to leak gas. This looked like it was leveling off for a float. But then started to descend at about 1.1m/s.

cheapo4 path

As the balloon descended I tried to decide if I should go to recover. My inverter went missing and I doubt my old laptop would have lasted long enough to be worthwhile. I sat there and watched as the balloon crossed the River Thames and bounced along between 100-200m altitude as it rode thermals over the town.

cheapo4 land path

CHEAPO4 bobs

We lost contact with the payload at about 60m altitude over Bexleyheath golf club. Just then Steve (G0TDJ) stepped in to save the day! With his mate Ken at the wheel, they drove down to the golf club to take a look around. After a few minutes with nothing seen, one of the golfers pointed them in the direction of the A2 (eek!). Luckily it seems that the balloon was blown to the side of the road by the traffic and up onto the fence.

Steve caught a passer by just in the nick of time to pass the balloon over the fence to him after convincing him that the payload was NOT in fact a bomb and he did NOT need to call the police! A close shave!

As you can see below the payload had landed directly below the tracked position. I can only presume the balloon floated further on and was blown back down the road, or the wind was directed downwards due to the shape of the trees.


So all in all a successful flight. The new antenna is tried and tested. I have some experience with launching foils. The HC49 RFM22B showed slightly less drift than usual, I will be interested to investigate that one further.

Comparison of RFM22B’s (Standard HABsupplies on top, alibaba board on the bottom)


Thanks again to all those who helped track, and an even bigger thanks to Steve and Ken for going out to recover! CHEAPO5 to follow sooner than you may think 🙂


Cheapo3 flight – 14/06/2013

Last week I launched a test flight of the new cheapo boards to test how they performed against the old ones. Sadly the results were disappointing with a very weak RF output.

We aimed for a low ascent rate (1.2m/s, which we came very close to!) and released it out to sea.
After the balloon had traveled a couple of miles I noticed the signal was much weaker than anticipated, after eventually losing the signal altogether after about 25 miles.
Luckilly G8KNN (Cambridge), jijdaar(NL) and a few others jumped in to save the day, and managed to track the thing all the way up, and most of the way down (to about 5km).

Sadly the payload did not make it into Europe, however I am glad to have had the chance to test the new boards before relying on them for a much more expensive flight.

UPDATE: On the 30th June 2013 I had a phone call from a woman on the island of Borkum (Germany) who said she had found the (badly damaged) payload in the sea!

I would have loved to have seen some photos of the payload, but unfortunately she did no have access to the internet. After spending about 15 days in the north sea I expect the tracker would have been damaged beyond repair, so I said not to worry about sending it back.

You can see in the (badly edited) image below quite how far the payload drifted from its tracked landing location.


I believe the problem was down to the antenna being made from cheap single core telephone cable (which I thought would be extremely similar to cat5, which works well). I will launch another board in the next few weeks with either a cat5 or a coax antenna and see if there is an improvement. In hope to be able to source a small step up regulator board to allow future flights to run from just one lithium AA cell


RFM22B temperature cycling tests

I recently ran a test to investigate why the rfm22b often fails at low temperatures, with some interesting results.

8982032990_0c6a7376f1_c 8980841043_2cab9994ae_h

The radio module was set up in a test chamber and cycled from +20 to -50 degrees Celsius and repeated 5 times. The board was powered and the output frequency monitored and logged using an SDR.


The above results were surprising, as I was expecting the rfm22b to drop out around -40, however it carried on transmitting just fine all the way to -50. However as the temperature rose back out to ambient the module stopped transmitting as the temperature passed about -10. This happened consistently 4 out of the 5 times tested. I believe this is caused by dew forming on the crystal and pulling the frequency way out. You can see the frequency clearly shoot off to the side in this screen capture:



You can see in the graph below how the temperature affected the frequency, and where the module dropped out. I have also put together a time lapse of the SDR output to illustrate the drifting.


Test data spreadsheet: rfm22b_test_data.xlsm



CHEAPO R3 boards

The new CHEAPO boards are here! I have soldered one up and all seems to be working perfectly! Here is a quick write up on the results.


New board compared to old, looks much neater!


Main components of CHEAPO3

IMAG1195 IMAG1197

Weight of CHEAPO3 with and without batteries


Micro SD card installed (2GB seems like overkill for text logging!)


Current consumption of board whilst acquiring GPS fix, writing to SD card and transmitting = 110.9mA. This will drop when the tracker gets a GPS fix and if the SD card is removed for long duration flights. I think I will have a go at optimizing the code and look into the power saving modes of the uBlox NEO-6

I am very happy with these boards and look forward to testing one in the air. Currently planning a possible pico launch on the 14/06/2013 (PM)

uBlox UBX ACK code in Python

In order to use the uBlox chip in a high altitude balloon, it must be set to flight mode to enable its use up to 50,000m altitude.

This mode is set by issuing a UBX command over the serial port, then checking its acknowledgement (ACK) packet response. There are great examples on UKHAS of how to do this on an Arduino, However my Pi payload code is all in Python, a language I am still getting to grips with.

It turns out it was relatively straightforward to translate line by line, also improving my understanding of how the code actually worked instead of just blindly copying/pasting blocks of example code. I encountered a slight problem when checking the ACK response packet from the GPS against a expected response as the expected response was stored as an integer value, and the actual reply was stored as a character!

My code can be found on GitHub