Introduction: 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.
PySSTV
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 transmitImage(img.copy())
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).

Participated in the
Makerlympics Contest

Participated in the
Raspberry Pi Contest
34 Comments
Question 1 year ago
As an amatuer radio person, i wonder if it would be possible to do this set up to where someone could request the image to be sent out over the air?
Answer 1 year ago
interesting idea, you need communication from outside to the Pi, so either a WiFi or other network connection, or by radio, using a rtf-sdr dongle and some decoding software.
Reply 1 year ago
I was thinking like a way to request current pic from a ham radio. Like on aprs you can request wx. I am currently working on a packet bbs and thought this might be a cool addition. Haha. Still trying to get wx to populate when requested though.
Reply 1 year ago
of course, packet radio is a good option, but in that case I would prefer to send the picture with packet rather than with sstv.
4 years ago
Unfortunately pisstv is not complied in rpitx. But just pisstv can be found in github can be installed that can convert file to rgb file. But pşsstv in rpitx is not avle to convert other file name. It is just able to stream.
I just can send jpeg file to another PC via FM with using compalable pisstv that can convert jpeg to other file type and piam. Oike in this video: https://youtu.be/VVq1nzfgc4I
4 years ago
I tried to compile the pifm_sstv program and these are the errors I got:
pi@raspberrypi:~/pisstv $ gcc pifm_sstv.c -o pifm_sstv
pifm_sstv.c:180:1: error: unknown type name ‘class’
class SampleSink{
^
pifm_sstv.c:180:17: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘{’ token
class SampleSink{
^
pifm_sstv.c:186:1: error: unknown type name ‘class’
class Outputter : public SampleSink {
^
pifm_sstv.c:186:17: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘:’ token
class Outputter : public SampleSink {
^
pifm_sstv.c:267:1: error: unknown type name ‘class’
class PreEmp : public SampleSink {
^
pifm_sstv.c:267:14: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘:’ token
class PreEmp : public SampleSink {
^
pifm_sstv.c:295:1: error: unknown type name ‘class’
class Resamp : public SampleSink {
^
pifm_sstv.c:295:14: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘:’ token
class Resamp : public SampleSink {
^
pifm_sstv.c:358:1: error: unknown type name ‘class’
class NullSink: public SampleSink {
^
pifm_sstv.c:358:15: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘:’ token
class NullSink: public SampleSink {
^
pifm_sstv.c:367:1: error: unknown type name ‘class’
class Mono: public SampleSink {
^
pifm_sstv.c:367:11: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘:’ token
class Mono: public SampleSink {
^
pifm_sstv.c:381:1: error: unknown type name ‘class’
class StereoSplitter: public SampleSink {
^
pifm_sstv.c:381:21: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘:’ token
class StereoSplitter: public SampleSink {
^
pifm_sstv.c:426:1: error: unknown type name ‘class’
class RDSEncoder: public SampleSink {
^
pifm_sstv.c:426:17: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘:’ token
class RDSEncoder: public SampleSink {
^
pifm_sstv.c:464:1: error: unknown type name ‘class’
class StereoModulator: public SampleSink {
^
pifm_sstv.c:464:22: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘:’ token
class StereoModulator: public SampleSink {
^
pifm_sstv.c:536:90: error: unknown type name ‘bool’
void playWav(char* filename, float samplerate, float bandwidth, float timing_correction, bool stereo)
^
pifm_sstv.c: In function ‘setupDMA’:
pifm_sstv.c:594:4: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
for (int i=0; i<1024; i++)
^
pifm_sstv.c:594:4: note: use option -std=c99, -std=gnu99, -std=c11 or -std=gnu11 to compile your code
pifm_sstv.c:606:6: error: ‘for’ loop initial declarations are only allowed in C99 or C11 mode
for (int i=0; i<4096/sizeof(struct CB); i++) {
^
I don't know anything about coding in c, so any help would be appreciated!
73
Reply 4 years ago
you need to compile with:
g++ -O3 -o pifm_sstv pifm_sstv.c
Reply 4 years ago
Thanks! That worked!
7 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
Reply 7 years ago
sstv bin :
https://drive.google.com/file/d/0B-fg490HfaS6b3dhbktrVWhlNVE/view?usp=sharing
7 years ago
gcc -o sstv sstv.c -lm -lgd -lmagic
7 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
Reply 7 years ago
fix for debian x86:
compile whit: gcc -o sstv sstv.c -lm -lgd -lmagic
7 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 ?
73
Reply 7 years ago
fix:
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
7 years ago
any one can confirm tis is working nowdays ? or it is obsolete
7 years ago
Would this work with the A+ or not?
KD2DVF
Reply 7 years ago
Since the A+ only differs in pin number and memory, I expect yes.
8 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.
8 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?