TinyPhoto is a small rotating photobook embedded graphics project that uses the low-power ATtiny85 microcontroller (3mA) and a 128×64 pixel OLED display (c.5-10mA typical, 15mA max). This combination can deliver at least 20 hrs of continuous play on a 3V coin cell battery (225mAh capacity). TinyPhoto can be readily built from a handful of through-hole electronic components (12 parts, £5) organized to fit onto a 3cm x 7cm single-sided prototype PCB. The embedded software is c.150 lines of C code and uses less than 1,300 bytes of on-chip memory. TinyPhoto rotates through five user-selectable images using a total of 4,900 bytes (yes, bytes!) stored in the on-chip flash RAM. The setup produces crisp photos on the OLED display with a real-time display rate that is instantaneous to the human eye with the Tiny85 boosted to run at 8MHz. A custom device driver (200 bytes) sets up the OLED screen and enables pixel-by-pixel display. Custom Forth code converts a 0-1 color depth image into a byte-stream that can be written to the onboard flash for rapid display. It is a reminder of what can be accomplished with low-fat computing…
The magic, of course, is in the software. This article describes how this was done, and the software that enables it. Checkout the TinyPhoto review on Hackaday!
*New!* (24 Jul 2021) – TinyPhoto V3 is available! Includes fixed-width font driver for OLED (700 bytes), one-time startup screen, and SSD1306 command constants.
Simple Hardware
The electronics are quite simple as you can see from the image, schematic, and PCB layout. Total of 12 components are used, with a bit of hookup wire, which allows it all to fit handily onto a 3cm x 7cm PCB, at a total parts cost of c.£5 including microcontroller (£1.50) and display (£2.50). I used an 8-pin DIL socket to hold the ATtiny85 controller to allow the controller chip to be easily removed, reprogrammed and then re-inserted to display a new set of 5 images, or a different display order for the images. It took 30 minutes to draw the schematics / PCB layout, 90 minutes to solder and test the board, and 60 minutes to prepare the 5 images, including touch-up, for the rotating display.
The Magic is in the Software.
The first algorithm resizes e.g. a 1.7 megapixel color image (225kB JPG, 24-bit color) into a crisp 128×64 pixel color image (25kB BMP, 24-bit color, 90% shrinkage, IrfanView).
A second algorithm converts the color image to grayscale (Rec 601 luma formula Y=0.2989R+0.5870G+0.1140B, i.e. 30% red, 60% green, 10% blue) and applies a user selectable threshold (say 25) to produce a 0-1 matrix (binary image) with 1=black, 0=white, Image Binarizer).
128×64 OLED displays are built for easy display of text. The smallest font size is 8 pixels high, so the OLED display RAM is segmented into 8 text rows, each 8 pixels high, with 128 columns. Image data is displayed by sending a byte stream, with each vertical byte code containing the 8 pixels from a given text row, with least significant bit at the top.
The last step is post-processing this 0-1 matrix and extracting the vertical byte codes (1 byte holding 8 pixels from a single row) needed to display the image onto the OLED display. As an example in the image above, the three vertical byte codes in the red boxes are 0x00, 0x03, and 0x30 respectively. A macro gets the text 0-1 matrix into the right format to load directly into Forth as an image-map in memory ready for extraction of byte codes. The vertical byte code calculation is done in a few lines of Forth code (see Code section below — 3iArt.fs)
The vertical byte codes are then burned to the onboard Flash memory of the ATtiny85 microcontroller (4,900 bytes for 5 images). I use a 4 field datastructure per image to hold the image parameters (metadata) to ensure the images are displayed correctly (image size, packet transmission size over I2C, number of packets before adding a newline, etc.).
The ATtiny85 microcontroller runs the 150 lines of C code (c.1,300 bytes) in a tight loop that generates one by one the five images and sends the image data via I2C to the 128×64 pixel OLED display.
Size Matters on the Tiny85
In order to get all this to fit on the ATtiny85 (512 bytes SRAM, 8kB Flash), I wrote my own simple display driver code (c.200 bytes Flash) for the SSD1306 display driver IC integrated into the OLED display. It removes all the frills and preserves only what is needed to initialize the OLED display, set its contrast (high brightness) and orientation, and directly send image data for display. This removes the dependency on the 35x larger (7kB Flash) software drivers provided by e.g. Adafruit which would have left no room on the Tiny85 for the images. It also removes the need for a 1kB SRAM software buffer duplicating the OLED’s display RAM, but which is double the 512 bytes that the Tiny85 has. The result is a low-fat graphics driver for the OLED SSD1306 chip.
Communicating to the Display
Since the ATtiny85 does not come with built in I2C capability, I used the TinyWireM drivers from BroHogan that use Atmel’s USI (universal serial interface) in hardware to emulate I2C. This reduced the max packet size that worked to 8 bytes (from 32 bytes with Atmega328P controller that does have I2C in hardware), but even so, the display was smooth, and able to generate all 8k pixels (128×64) within the 60Hz framerate that allows the human eye to see it as instantaneously generated.
Important: To get snappy display performance, the ATtiny85 must be running at 8MHz or faster. The default fuses for the chip are likely set at 1MHz, which is not fast enough to refresh the screen within the 60Hz the human eye can detect. You’ll need to select 8MHz (internal) clock and burn the bootloader using an ISP programmer. This will set the fuses on the chip correctly to run at the faster setting.
Programming the Chip
The final step is programming the chip using the Arduino IDE. You will need to have ATtiny board definitions loaded and the TinyWireM I2C drivers for ATtiny (get both here).
To do this, you can use a commercially available ISP programmer (less than £20), or if you have a spare Arduino Nano or Uno lying around, you can use this (see Programming Microcontrollers for details).
Low Power
The components of TinyPhoto are selected for low power. The ATtiny85 sips power, at 3mA. The OLED display draws 5mA while displaying a 64×64 pixel photo, and 10mA displaying a 128×64 photo (full screen). With an average total draw of 10mA, TinyPhoto can provide 22 hrs of operation on the 3V CR2032 coin cell battery (225mAh capacity).
Result!
You can see the result in the 10-second video clip below.
Hackaday’s review of TinyPhoto, by Tom Nardi (July 19, 2021)
As noted by one of the readers, the two-color yellow/blue OLEDs that I had in my parts box at the time are not ideal, and the final vibe would have been better with the all-white or all-blue OLED displays.
Details
Specifications:
- Microcontroller – Tiny85, 8-pin DIP, 8-bit microcontroller (1kB SRAM, 8kB Flash), running at 8Mhz
- Small Hardware – PCB is 3cm x 7cm
- Low Power – less than 10mA power consumption
- Long battery life – powered by single 3V 2032 coin cell battery (225mAh capacity), expect 20+ hrs of operation
- Low Memory usage: 300 bytes SRAM, 1.3kB core program. Max 1kB per image.
- Display – OLED 128×64 pixel display with integrated SSD1306 controller chip and I2C communication
- I2C driver – BroHogan 2011: TinyWireM.avr
Fun facts:
- Tiny85 uses a meagre 6kB of memory (1kB code, 5kB data), 1 million times less memory than the 8GB RAM used by a typical PC.
- TinyPhoto powers up and displays first picture instantaneously (within 200ms) after applying power.
- The refresh rate is fast enough (within 60Hz) that delays are imperceptible by the human eye.
Summary of Steps (see Code below):
- Select any photo image to display, no limitations on size or color-depth.
- Use IrfanView to resize to a 128×64 pixel color bitmap
- Use Image to Binary to convert to hex codes intended for SSD1306 driver
- Test the image display using 3iForth
- If correct, store hex codes into Flash and read from Flash
- Initialize OLED by sending right commands to SSD1306 driver chip (no need to use the bloated AdaFruit SSD1306/GFX drivers)
- Setup I2C for communication between the uC and the SSD1306 driver IC integrated into the OLED display. Use
for Attiny85/84 processors. Can use the built-in library for Atmega328P-based processors. - Burn images and code to the Tiny85 controller using an ISP programmer.
- Run the hardware at 8MHz to display the graphics (pixels) in a tight loop.
Parts List
TOTAL: £5.09 for 12 parts count
(prices given as at purchase, current prices continually fluctuate, further searches can find lower prices)
- ATtiny85 microcontroller (£1.50)
- OLED display 128×64 pixels (£2.50, all-white or all-blue)
- 2x 0.1uF caps to power smooth the Tiny85 and the OLED display (1p)
- 8-pin DIL IC socket for holding Tiny85 (6p)
- 3V CR2032 coin cell battery (22p)
- CR2032 coin cell battery holder (5p)
- Green LED 3mm standard (2p)
- 1k ohm current-limiting resistor for the LED (1p)
- Slide switch SPDT (2p)
- Proto PCB board 3cm x 7cm (30p)
- 4x M2 standoffs M/F 8mm/6mm (5p ea, 20p)
- 4x M2 nuts F/F 6mm (5p ea, 20p)
Code / Instructions
More details can be added based on reader interest (feel free to use comments to request).
-
Image Examples: butterfly and jaguar images to test with
- butterfly_sm.bmp (this is the resized butterfly color file, 114×64 pixels)
- jaguar.bmp (this is the resized butterfly color file, 114×64 pixels)
Forth code:
Instructions: This GForth code loads the 0-1 pixel data from the butterfly image and converts to vertical byte codes which are stored in TinyPhoto_data.avr which Tiny85 controller sends to the OLED display.
include 3iArt_rel.fs
include 3iArt-butterfly_rel.fs
butterfly - 3iArt_rel.fs (GForth) (this file has the Forth code to extra image bytecode data from 0-1 pixel matrix)
- 3iArt-butterfly_rel.fs (this file has the 0-1 pixel matrix input data for butterfly.bmp)
C code for Tiny85 microcontroller (Arduino):
Instructions: Compile TinyPhoto.avr in Arduino IDE using ATtiny board files and BroHogan I2C TinyWireM driver. Upload to Tiny85 microcontroller using ISP Programmer. - TinyPhoto.avr (compile this in Arduino, change path to include *_data.avr)
- *New!* (24 Jul 2021) 3iFonts.avr (this file has fixed width font definitions 8hx5w for display to OLED)
- TinyPhoto_data.avr (this file has the image bytecode data, must be included)
- ATtiny_ArduinoBoardFiles.zip
- ATtiny_I2C_driver_BroHogan2011.zip
ISP Programmer:
NEW (July 2021) – For programming the chips using the Arduino IDE, I have built a simple three-chip Atmel programmer that has sockets to receive the ATtiny85 (DIP-8), ATtiny84 (DIP-14), or Atmega328P (DIP-28) microcontrollers, and which uses an Arduino Nano running the ArduinoISP code to act as the in-system programmer (ISP) to write the compiled code to the target chips. It is easy to assemble on a 4cm x 6cm PCB and less than a dozen through-hole components. Total parts cost, including the Nano, is c.£5. If interested, ask in the comments.
> Microcontroller – Tiny85, 8-pin DIP, 8-bit microcontroller (1kB SRAM, 8kB RAM), running at 8Mhz
8k Flash?
@Mind Reader – Yes, 8k Flash, corrected, thanks!
Nice experience!
Unfortunately you can get no benefit from 60 Hz frame rate having 5 frames stored in this chip… unless you want to show a 12 cycles/sec animation…
But one thing that can be improved is the quality of your images. Use dithering when converting images from grayscale to 1-bit and get better visual result. (don’t forget the gamma correction, 7F is not a half of brightness of FF, it is about 1/5 of FF)
Best regards
Thank you for posting this. I was able to adapt this into some existing code I had. I’m using Peter Fleury’s I2C code for the AtTiny rather than Tiny/WireM and Gnu C compiler with avr-libc.
Cheers.
@Mike – glad to hear the approach works for other I2C libraries. Feel free to share a link to your code. Also, would be great to see some images of what you’re doing with graphics on the ATtiny, of course if it’s not confidential.
Assad-
[…] be built, (if you wanted to program bare metal, chances are you would need to drop into assembly to roll your own minimal Forth, albeit a 3-instruction Forth tethered to your native computer via a ser…); and […]
[…] TinyPhoto: Embedded Graphics and Low-Fat Computing – using the Atmel ATtiny85 and an early version of the tiny toolchain above. […]