There’s a bit of a thing going on with people crunching the Bad Apple!! shadow art video and playing it back on various 8 bits and 16 bits computers and I couldn’t find one for ATmega2560 / Arduino that didn’t stream the data off some external SD card or right off a PC through the serial port.
Something had to be done!
The entire 3m39s video is crunched down to 221KB of video data all contained inside the ATmega2560 there is no SD card and no data streamed off the serial port. Download and see for yourself 🙂 The only extra hardware needed is two resistors, a power source, and a TV (which can even be the power source if it’s got a USB port). Wiring diagram below.
Playback is 128×176 60fps (50fps for the PAL version) 1bpp x 13122 frames. The average data rate is 8Kbps. That’s an effective compression ratio of 167:1. And yes, it is lossy!
Download the hex file to play on your Arduino Mega 2560 (contains both NTSC and PAL hex files).
You will need 2 resistors (1K and 470 Ohm) to connect to the TV’s composite input (see diagram below).
The Arduino mega 2560 (ATmega2560 MCU) is a 8-bit 16Mhz SoC with 8KB of RAM, 256KB of Flash ROM (minus 8KB for the Arduino boot loader), and 4 KB of EEPROM.
The first thing with getting a video to play on an ATmega2560 microcontroller / Arduino mega 2560 is to create a video signal in software as the micro-controller doesn’t have any video output circuitry. We also need two resistors to limit the current and create a voltage divider so we can generate a sync signal.
After this it’s only a question of flipping those ports with the right timing to create an NTSC or PAL signal.
As the CPU was going to be generating the video output, and we’re limited in memory, and the video is mostly large areas of solid black or white I decided to not use a bit frame buffer and instead encode continuous extents. This also greatly sped up decoding and motion compensation.
The video is first converted to 1bpp then scanned for continuous horizontal extents. The encoder then limits the number of extents on every line to a configurable limit by eliminating the smallest ones. This setting affects both the decoder memory usage and the encoded movie size.
After this the video is encoded by flagging which scanlines changed and then whether the change is a full scanline (raw) or a motion scanline (copy + motion), and the final stream is compressed using LZ77 as more advanced compression algorithms such as LZ78 and LZW would have been cost-prohibitive in terms of RAM (we only got 8KB and 3KB is needed just for the video double-buffer). The video data stream is NOT kept in memory once it’s used; it is discarded as the frames are updated. And all the memory-saving tricks for LZ78 and LZW use the decompressed memory to store the dictionary.
Then it’s up to the decoder to recreate the frames and interpolate the motion.
Flashing:
Use XLoader on windows to flash BadDuinoNTSCVideoOutput.ino.hex to your ArduinoMega2650
For other OSes use avrdude, for example:
~/arduino-1.8.5/hardware/tools/avr/bin/avrdude -C~/arduino-1.8.5/hardware/tools/avr/etc/avrdude.conf -q -q -patmega2560 -cwiring -P/dev/ttyACM0 -b115200 -D -Uflash:w:BadDuinoNTSCVideoOutput.ino.hex:i
replace /dev/ttyACM0 with your serial port
replace ~/arduino-1.8.5 with your arduino IDE path