Raspberry Pi (security) Slow Scan Television Camera

Intro: Raspberry Pi (security) Slow Scan Television Camera

For project updates, visit: AgriVision - Raspberry Pi SSTV Camera.

The goal of this project is to use the Raspberry Pi with the PiCam as a wireless camera which can transmit images over long distances, usually tenths of kilometers. Images will be transmitted by amateur radio (ham-radio) using slow scan television (SSTV) on the 2 meter band (144.5 MHz).

Since the Pi can generate the HF FM signal itself, no additional electronics are needed for low power transmissions. For a little bit more power a one or two transistor amplifier will be suitable. Furthermore a low pass filter is recommended to filter out higher harmonics of the signal.

This project also contains a python script which detects movement. Using this script the Raspberry Pi can be used as a wireless security cam at distances far outside the range of normal WiFi networks.

Be aware that you need a ham-radio license to use this application!

Here is a video of the camera in action:

Step 1: Capturing the Image

First thing to do is capturing the image we want to transmit. This can easily be done with raspistill:

raspistill -t 1 --width 320 --height 256 -e png -o /tmp/image.png   

For sstv we need a small image, of 320 x 256, it is saved into the /tmp directory as png.

Step 2: Converting the Image to a SSTV Sound File

Next we need to convert the image to a sound file which can be transmitted over the air. There are several SSTV implementations available for the Raspberry Pi.


First I had a look at PySSTV, a Python implementation which can be installed using pip:

pi@rpicamera ~/sstv $ sudo apt-get install python-setuptools
pi@rpicamera ~/sstv $ sudo apt-get install python-imaging
pi@rpicamera ~/sstv $ sudo easy_install pip  
pi@rpicamera ~/sstv $ sudo pip install setuptools --no-use-wheel --upgrade  
pi@rpicamera ~/sstv $ sudo pip install PySSTV  

This works, but it is very slooooooooooooow, it takes many minutes to convert a single image. So I continued to search for another option.

C implementation

Next I found a plain C implementation here: https://sites.google.com/site/ki4mcw/Home/sstv-via-uc Unfortunately there were some errors in the preamble tones, but those were easy to fix. I also made it a little bit more flexible so that you can set the audio sample rate from the commandline line. Source of my implementation can be found on GitHub. To compile the source code:

pi@rpicamera ~/sstv $ sudo apt-get install libgd2-xpm-dev  
pi@rpicamera ~/sstv $ sudo apt-get install libmagic-dev  
pi@rpicamera ~/sstv $ gcc -lm -lgd -lmagic -o pisstv pisstv.c

To run the program:

pi@rpicamera ~/pisstv $ ./pisstv /tmp/image.png 22050  
Constants check:
        rate = 22050
        BITS = 16
      VOLPCT = 20
       scale = 6553
     us/samp = 45.351474
     2p/rate = 0.000285
 Checking filetype for file [/tmp/image.png]
 File is a PNG image.
 Input  file is [/tmp/image.png].
 Output file is [/tmp/image.png.wav].  
 Writing audio data to file.
 Got a total of [2589556] samples.
 Done writing to audio file.
 Created soundfile in 4 seconds.

As you can see the SSTV sound file is created in just 4 seconds. So far so good, next step, how to transmit the audio over the air.

Step 3: Transmitting the Sound File With PiFm

You can add a radio transmitter, like a portable radio transceiver, but its much more fun to let the Pi itself generate the high frequency signal. Thanks to Oliver Mattos and Oskar Weigl this is possible. You can find their code here: Turning the Raspberry Pi Into an FM Transmitter - Imperial College Robotics Society Wiki.

Their code has evolved considerably. The first version was very simple, but used all cpu cycles, and the signal was hampered by glitches when other processes were active. The last version uses dma and works pretty good, without eating up all cpu cycles. Nevertheless the code is much more complex now. Oliver and Oskar did a very good job, but out of the box the software is not suitable for ham-radio and SSTV. There are mainly two problems. First the bandwidth is to high and secondly the timing which is very important for SSTV was a little bit off.

Step 4: Reducing the Bandwidth

Reducing the bandwidth appeared to be very simple. As every ham knows, for frequency modulation the bandwidth can be set with the modulation index, which is equal to the volume of the audio signal which modulates the hf carrier. In the source code it is just one value it can be found in the consume function of the Outputter class.

Here is the original code:

void consume(float* data, int num) {
for (int i=0; i<num;i++){
float value = data[i]*8; // modulation index (AKA volume!)

I made a command line parameter of this value, the new code looks like:

void consume(float* data, int num) {
for (int i=0; i<num;i++){
float value = data[i]*modulation_index; // modulation index (AKA volume!) (original 8)

Unfortunately this does not work very well, very strong sidebands persists, so this needs some focus in future versions of the software.

The figure shows a spectral plot of the full bandwidth FM signal. The second spectrum is the reduced bandwidth, tuning on the peak in the middle shows a nice and clean signal, but we need to get rid of the sidebands. The last one is the reduced bandwidth signal of the first version of PiFm, nice bandwidth, but the signal is hampered by clicks due to cpu activity in other processes.

Step 5: Fixing the Timing

When the sample rate of audio transmitted by PiFm is slightly larger or smaller, a listener hardly notice any difference. For SSTV this is not the case, SSTV timing is very precise. A slightly off sample rate results in slanted images, as can be seen below on the left. The second image is the same sound file properly sampled.

Fixing the timing appeared to be straight forward.

//clocksPerSample = 22500.0 / rate * 1373.5;  // for timing, determined by experiment
clocksPerSample = 22050.0 / rate * timing_correction;  // for timing, determined by experiment  

As you can see I replaced the timing constant (1373.5) in the code with the variable 'timing_correction' which can be set from the command line. I expect a different value for each individual Rpi. In my case the value is 1414.0. I'm just curious which is the proper value for you, please comment your value on this blog post. For all other adaptions to the code, see the source file at GitHub.

Step 6: Adding Call-sign

When you start transmitting SSTV signals using your ham-radio license, you are required to transmit your call-sign in every transmission, so we need to add this information to the image. This can easily be done either from the command line using imagick, or from python using the python image library (PIL). Both are used in this project.

In sstvcam.sh mogrify which is part of imagick is used. sstvcam.sh is a simple shell script to just capture and transmit an image. In sstvcatch.py I used PIL.

Step 7: Catching Movement

Now we are able to grab an image and send it properly over the air using PiFm. We now need to focus on triggering the image capture when something interesting happens in front of the camera. I have implemented this in python, using PIL. The code can be found in sstvcatch.py. It works quite straight forward, it just compares the pixels of the previous image with the current image. When the difference is to large, the current image is transmitted. Here is a code snippet:

# loop forever  while (True):
          # grab comparison image
          imgnew, bufnew = captureImage()
          # Count changed pixel
          changedPixels = 0
          for x in xrange(0, 320):
                  for y in xrange(0, 256):
                          # Just check red channel as it's dominant for PiCam NoIR
                          pixdiff = abs(buf[x,y][0] - bufnew[x,y][0])
                          if pixdiff > threshold:
                                  changedPixels += 1
          # Transmit an image if pixels changed
          if changedPixels > sensitivity:
                  # Swap comparison buffers
                  img = imgnew
                  buf = bufnew

The full code can be found on GitHub.

Step 8: ​GitHub

Sources can be found on github: https://github.com/AgriVision/pisstv.

Step 9: Credits

Credits to KI4MCW (sstv), Oliver Mattos and Oskar Weigl (pifm).



    • Plastics Contest

      Plastics Contest
    • Optics Contest

      Optics Contest
    • Halloween Contest 2018

      Halloween Contest 2018

    28 Discussions


    2 years ago

    Hi, i try to compile the program but have a error:

    pisstv.c:(.text+0x485): referencia a `magic_open' sin definir

    pisstv.c:(.text+0x4a5): referencia a `magic_load' sin definir

    pisstv.c:(.text+0x4bf): referencia a `magic_file' sin definir

    pisstv.c:(.text+0x561): referencia a `magic_close' sin definir

    /tmp/cckZd77k.o: En la función `playtone':

    pisstv.c:(.text+0x622): referencia a `sin' sin definir

    /tmp/cckZd77k.o: En la función `buildaudio':

    pisstv.c:(.text+0x9c7): referencia a `gdImageGetTrueColorPixel' sin definir

    I run ubuntu 14.04, some idea? THX for share

    2 replies

    Reply 2 years ago

    gcc -o sstv sstv.c -lm -lgd -lmagic


    2 years ago

    Port to openwrt 15.05 AR71XX / AR91XX / AR93XX :

    link to binary exe: sstv

    before start install dependencies:

    opkg install libgd

    opkg install file

    // ENJOY sstv for 120mA on Wifi router :

    Created soundfile in 53 seconds. /11kHz

    1 reply

    Reply 2 years ago

    sstv bin :



    2 years ago

    sadly the same here:

    :~# gcc -lgd -lmagic -o sstv sstv.c
    /tmp/cc8WnnPs.o: In function `main':
    sstv.c:(.text+0x3f5): undefined reference to `gdImageCreateFromJpeg'
    sstv.c:(.text+0x416): undefined reference to `gdImageCreateFromPng'
    /tmp/cc8WnnPs.o: In function `filetype':
    sstv.c:(.text+0x505): undefined reference to `magic_open'
    sstv.c:(.text+0x522): undefined reference to `magic_load'
    sstv.c:(.text+0x53b): undefined reference to `magic_file'
    sstv.c:(.text+0x5e1): undefined reference to `magic_close'
    /tmp/cc8WnnPs.o: In function `playtone':
    sstv.c:(.text+0x6ac): undefined reference to `sin'
    /tmp/cc8WnnPs.o: In function `buildaudio':
    sstv.c:(.text+0xb1f): undefined reference to `gdImageGetTrueColorPixel'
    collect2: error: ld returned 1 exit status

    1 reply

    Reply 2 years ago

    fix for debian x86:

    compile whit: gcc -o sstv sstv.c -lm -lgd -lmagic


    2 years ago

    on raspi i get :

    root@raspberrypi:/home/pi# gcc -lgd -lmagic -o sstv sstv.c
    /usr/bin/ld: /tmp/ccBLc9lA.o: undefined reference to symbol 'sin@@GLIBC_2.4'
    //lib/arm-linux-gnueabihf/libm.so.6: error adding symbols: DSO missing from command line
    collect2: error: ld returned 1 exit status

    any ideas from author ?


    1 reply

    Reply 2 years ago


    root@raspberrypi:/home/pi# gcc -lgd -lmagic -o sstv sstv.c -lm
    root@raspberrypi:/home/pi# ./sstv
    Usage: ./sstv wavfile.wav [sample rate]
    default sample rate = 11025
    maximum samplerate = 22050


    2 years ago

    any one can confirm tis is working nowdays ? or it is obsolete


    3 years ago on Introduction

    How does one go about figuring out the timing for ones pi? And where would I make that change in the python config? I have it transmitting but the timing is off and I cannot seem to figure it out.


    3 years ago on Introduction

    What kind of distance can you expect out of this rig with just a small antenna and no signal boosting from say a HAB?

    2 replies

    Amazing! Thanks for the reply. Any idea how high the signal was being sent from in order to achieve those types of results?


    3 years ago on Introduction

    Hi, PySSTV dev here. Yes, the Pi is really slow for pure Python PySSTV, so we've developed UNIXSSTV that performs the most CPU intensive part in native code. This way you get the best of both worlds, since PySSTV is versatile and can handle many modes, but converting images on a RPi model A is under 30 seconds. See https://github.com/dnet/unixsstv and http://hsbp.org/rpi-sstv

    1 reply

    Reply 3 years ago on Introduction

    Thanks, this is indeed much better than the minutes needed for PySSTV, although still 30s, where the c implementation I'm using now is just 4 s.
    But indeed that one is less versatile as it only does Martin 1.
    I certainly will give it a try.


    4 years ago on Introduction

    Does the image get screwed over if there is any noise on the frequency? Some of my friends were playing with tcp/ip over ax.25 and some jerk was broadcasting over us and all the packets would become currupted.

    1 reply

    Reply 4 years ago on Introduction

    depends on the signal strength, strongest signal wins, the same as with WiFi or other wireless techniques. You can increase your signal strength by using more power, or better use directional antenna's with high gain on both the receive and transmit side.