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:

Teacher Notes

Teachers! Did you use this instructable in your classroom?
Add a Teacher Note to share how you incorporated it into your lesson.

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).

Raspberry Pi Contest

Participated in the
Raspberry Pi Contest

Makerlympics Contest

Participated in the
Makerlympics Contest

Share

    Recommendations

    • Indoor Lighting Contest

      Indoor Lighting Contest
    • Make It Fly Challenge

      Make It Fly Challenge
    • Growing Beyond Earth Maker Contest

      Growing Beyond Earth Maker Contest

    32 Discussions

    0
    None
    abdullahi26

    5 months 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

    0
    None
    AlexK293

    7 months 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

    2 replies
    0
    None
    soldeerridderAlexK293

    Reply 7 months ago

    you need to compile with:
    g++ -O3 -o pifm_sstv pifm_sstv.c

    0
    None
    llaven

    3 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
    0
    None
    JohnS521llaven

    Reply 3 years ago

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

    0
    None
    JohnS521

    3 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
    0
    None
    JohnS521JohnS521

    Reply 3 years ago

    sstv bin :

    https://drive.google.com/file/d/0B-fg490HfaS6b3dhbktrVWhlNVE/view?usp=sharing

    0
    None
    JohnS521

    3 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
    0
    None
    JohnS521JohnS521

    Reply 3 years ago

    fix for debian x86:

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

    0
    None
    JohnS521

    3 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

    1 reply
    0
    None
    JohnS521JohnS521

    Reply 3 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

    0
    None
    JohnS521

    3 years ago

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

    0
    None
    Xiiren

    4 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.

    0
    None
    cpoplawski

    4 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?