It's not terribly easy, but yes, it can be done. The design I've been exploring is to:
1. Prep Phase:
a. Download two rows of image data from a digital camera, using a serial connection.
b. Down-convert those rows to a single row of a 320x256 pixel, 12-bit color image.
c. Write down-converted image row to external flash storage (SRAM would be better).
d. Repeat steps a-c for the rest of the image.
2. Transmit Phase:
a. Read 320x256 image back in from flash one row at a time.
b. Generate SSTV audio based on image data.
c. While one row of image data is being transmitted, download next row from flash.
d. Repeat b and c for the rest of the image.
The big whopper of a problem with this approach is timing. SSTV requires VERY accurate timing of image elements. In some uC implementations, it is possible to send a read request to serial flash, and let the returning data stream flow into a buffer, to be processed in between pixels. Each pixel lasts less than half a millisecond in Martin1, so it can be tricky to keep the serial reads from affecting the generation of the audio.
To eliminate this timing contention, I tried using a second uC as an audio frequency oscillator. This has worked out very well. I use a 16-element lookup table of 4-bit sine values in the oscillator, and check six pins to read the frequency to be generated. This allows me to check for a new frequency 16 times per cycle (good for quick changes of frequency), and also yields phase-correct, glitch-free audio. With a simple low-pass filter, the 4-bit output makes fairly clean output.
Some code samples are attached below.
sstv_osc_4 is the external oscillator code for an ATtiny2313. It uses pin 17 (PB5) as the enable (pull low to mute), and pins 12-16 (PB0 - PB4) as the frequency select lines. Pins 6-9 (PD2 - PD5) feed an R2R ladder to generate the audio. The program uses the 2313's 16-bit Timer1 to count the duration of each 1/16 of a sine wave for the selected frequency. Note that the timing values I have used require a fuse to be set to select the internal 8 MHz system clock. Further, the values are not do not generate the audio frequency with 100% accuracy. Thankfully most SSTV software is not very picky about frequency precision, so being within a couple of percent is fine.
sstv_test_8 is a Martin1 test pattern generator for an ATmega32. It uses an ATtiny2313 external oscillator (with the code above) to produce an entire Martin1 image, including preamble, VIS codes, and sign-off. This version does not attempt to insert any text in the image, but otherwise should work well enough to transmit on the air (you'll want to use a low-pass filter on the oscillator output, as mentioned). This code uses the Mega32's 16-bit Timer1 to precisely measure each element of the image. Since the visible parts of the image are nice and wide (8 bars, 40 pixels wide), we do not need to time each individual pixel.
Here are the results of these programs running together:
The image above was sent from a breadboard to my desktop PC, over a 1/8" stereo cable (i.e., not over the air). The receiving program was QSSTV on Linux, which required some fddling with slant settings before it would receive correctly. The color transitions are mostly clean, except for the green-magenta transition, where all three channels make a state transition. More fine-tuning of the timing settings should fix that.
sstv_test_9 is the next version in the series, circa December 22, 2008. This version sends each pixel individually for each line. Timing errors accumulate very quickly in this code, so I had to use a timer prescale of 8 (instead of 64) and adjust all of the macros accordingly. After some trial and error, I was able to send the test image below with =no= slant tinkering on the Linux PC. The pattern itself is a set of 1-pixel-wide pinstripes on a black background. There's no extreme tinting or smudging to the lines, so the timing seems to be pretty consistent from line to line.
The next set of tests will be the hard part - trying to interleave serial port IO in between pixel duration interrupts. Should be interesting.
sstv4.pl is a new PC-based experiment. Instead of generating an audio waveform in real time, it creates a WAV file that can be transmitted separately (via any sound-playing program). This program requires the Image::BMP and Audio::WAV libraries, which are readily available for most Perl distros. By separating the runtime logic from the sub-millisecond timing required at transmit time, I was able to create effective Martin1 WAV files almost immediately. I also began playing with some code for a character generator. Here is a sample image, "transmitted" by Sound Recorder on my wife's PC, passed through a 1/8" stereo cable, and "received" by MMSSTV on my laptop.
I'm still trying to figure out the rippling at the left edge, and adjacent to high-contrast color transitions. The distortion around the vertical bar to the left of my son seems to point to some lingering timing problems. I was hoping for better, but this isn't too bad for two evenings' work.
sstv_midi_2.pl is another radical departure. Here, I thought perhaps I could use the MIDI synthesizer in a PC as a tone oscillator. This experiment turned out poorly, but only because I could not find a General MIDI voice with an attack time fast enough to produce notes just 0.4576 milliseconds long. Interestingly, MMSSTV had no trouble decoding the VIS codes at the beginning of the "song", but it could not make sense of the image data, since so few of the notes are actually audible. It is pretty funny to hear the computer trying to render sound this quickly - as though a musician's fingers were flying across a keyboard at super-human speed.