Tiny Squares: Camera System
Engineering a camera that shoots, converts image, bitmaps jpg, and prints instantly.
Overview
Tiny Squares is a compact camera system built around the ESP32-Wrover-Kit by Espressif alongside the OV3660 camera module and a thermal printer.
This system combines camera control, constrained image processing, serial communication, and power management in one small, embedded pipeline. Each stage must operate within the ESP32's limited memory while still producing an image that remains recognizable on a thermal print.
In this post, the first part will be more of a personal story of how this came to be. The second part will be the technical aspect of the system.
Films are so expensive
Lately, I've noticed a growing trend of people intentionally disconnecting from their phones. Whether it's building custom cyberdecks, carrying dedicated MP3 players, or using standalone cameras, many people are creating devices that replicate functions already available on a smartphone.
I've always been a fan of physical media, which is a big reason why I enjoy shooting photos with my Fujifilm Instax. There's something satisfying about holding a printed photo instead of scrolling through a camera roll.
The problem is that Instax films are expensive.
So, I decided to build my own instant camera.
I have no idea how image processing works
Before starting this project, my experience was mostly embedded systems and PCB design. I understood microcontrollers, communication protocols, and hardware design, but image processing was almost entirely new territory.
I knew thermal printers could print graphics, and I knew camera modules could capture images. The challenge was figuring out everything in between.
Lots of relearning
Since it was summer and I hadn't worked with some embedded concepts in a while, a large part of the project involved refreshing old knowledge. I found myself revisiting UART communication, power distribution, breadboarding, and debugging techniques. Some of that theory came back surprisingly quickly after spending time experimenting in TinkerCAD.
The first step was getting the camera operational. I configured the ESP32 to connect to a local Wi-Fi network and host a lightweight web server over the TCP/IP stack, allowing me to stream camera frames through a browser. This provided a fast way to verify image capture and debug the camera before integrating the printer.
Once image capture was working, I configured UART communication between the ESP32 and the thermal printer. Before attempting image printing, I tested basic communication by sending simple text and graphics commands.
Next, I added a momentary push button that would act as the camera shutter. Using the Arduino serial monitor, I verified that button presses were being detected correctly by the microcontroller.
To validate the entire capture process, I initially wrote a Python receiver that could accept and save photos transmitted by the ESP32. Once image capture was reliable, I connected everything together: a button press triggered the camera, the ESP32 processed the image, and the thermal printer produced a physical copy.
The biggest challenge came when I realized that the printer couldn't directly understand the JPEG image produced by the camera. The JPEG first had to be decoded, resized, converted into a monochrome bitmap, and optimized for thermal printing. This led me down a rabbit hole of image processing techniques, eventually landing on dithering, which I'll explain in the technical section below.
From capture to print
The system begins by capturing a JPEG frame from the camera. That compressed image is useful for storage and transport, but the thermal printer cannot interpret it directly. The ESP32 therefore has to decode the frame, resize it to the printer width, convert its pixels to grayscale, and prepare a one-bit bitmap.
Treating the project as a pipeline makes each failure easier to isolate. A captured frame can be validated before conversion, the bitmap can be inspected before transmission, and the printer can be tested independently with known patterns.
Converting the image
A thermal printer only produces black or white dots, so the captured color image must lose most of its original information. The important decision is how to preserve edges, faces, and contrast while reducing every pixel to a single bit.
The processing stage converts RGB values into luminance and then applies thresholding or dithering. A fixed threshold is fast, but it can erase detail under uneven lighting. Dithering distributes error between nearby pixels, creating the appearance of additional shades while still producing a valid monochrome bitmap.
Printing instantly
Once the bitmap is packed into bytes, it is sent to the thermal printer one row at a time. The printer's width determines the final image dimensions, and its speed has to be balanced against heat, power draw, and print density.
The printer is the highest-current part of the system, so it cannot be treated like a normal logic peripheral. Stable power, shared grounding, and controlled transmission timing are necessary to prevent resets and incomplete prints while the heating elements are active.
Current stage
Updated: June 11, 2026
The current prototype can capture camera frames and produce recognizable monochrome prints using a process called dithering. The next steps include supplying more current to the printer and creating a camera enclosure with Fusion 360.