Digital Level
Two-axis digital level built from scratch on an ATmega328
Overview
A two-axis digital level I built from scratch around an ATmega328 microcontroller, an ADXL343 accelerometer, and an SSD1306 OLED display. The device runs on four AA batteries through a linear regulator, reads the accelerometer on a fixed interval, converts raw XYZ values into a tilt angle, and draws a dot on the OLED that moves away from center as the unit tilts. Past a threshold on either axis it stops drawing the dot and lights up the corresponding side of the screen instead. Everything lives inside a 3D-printed case with a custom PCB. Built as a course project alongside my capstone.
Technology Stack
Electronics
- ATmega328PB @ 8 MHz
- ADXL343 accelerometer
- SSD1306 OLED
- LD1117V33 regulator
- 4×AA battery pack
Firmware
- Embedded C
- I²C bus
- Fixed-interval sampling
Mechanical
- Custom PCB
- 3D-printed PLA case
- Replaceable AA battery door
Architecture & Design Choices
The screen updates around five times a second, which makes the level feel sluggish next to a commercial unit. I'd assumed the bottleneck was I²C transport, but the bus is already at 400 kHz and the renderer only flushes dirty banks, not the full framebuffer — both of those are fine. The real cost is the circle drawing routine. It walks the perimeter in fine angular steps and calls cos() and sin() on every step, four circles per frame between the two clear-and-redraw passes. The ATmega328 has no FPU, so each soft-float trig call is roughly a thousand cycles, and at 8 MHz the trig alone burns about 125 ms per frame — almost exactly the observed 5–7 Hz. The fix is a precomputed unit-circle lookup table in PROGMEM (or a midpoint Bresenham circle), which would drop per-frame cost by two orders of magnitude and push the refresh well past 50 Hz.
The 3D-printed case was sized to fit a 4×AA battery holder alongside the PCB, OLED, and buttons. I forgot to put a channel in it for the battery wires to reach the board. Rather than reprint the whole case I jerry-rigged the wiring after the fact.
This project ran in parallel with my capstone, and a few things got cut when time ran out. Tap-to-wake via the ADXL343 interrupt pin was in the design but never implemented. A tilt-controlled game and a beeper that would chirp as you approached level were both stretch goals that didn't survive. Sleep and low-power modes got disabled entirely rather than tuned, trading battery life for one less thing to debug under deadline.
What Works Today
Powers on, shows a dot that tracks the current tilt in real time, and lights up the corresponding side of the OLED when the tilt goes past threshold on either axis. That's the demo. No "approaching level" feedback — color, blink, beeper — shipped.
Project Media
Photos coming soon