A minimal Raspberry Pi 3 OS image and C++ camera application for microscopy. The system boots directly into a full-screen live camera viewfinder with a Sony Alpha–style OSD. No desktop environment is installed — the microscopi user autologins and the app starts automatically via systemd.
Pre-built SD card images and binaries are available on the Releases page.
- Live viewfinder — YUV420 camera feed displayed full-screen via SDL2 (kmsdrm backend, no X11 or Wayland)
- Sony Alpha Styled OSD — translucent bottom bar showing exposure mode, shutter speed, ISO, focus mode, and still count; all elements scale proportionally to display resolution
- Exposure modes — Program (P), Aperture priority (A), Shutter priority (S), Manual (M)
- ISO control — AUTO or manual steps (100–6400); adjustable in any mode
- Automatic display resolution — queries EDID at startup and selects the best supported mode up to 1080p (4:3 / 16:9 / 16:10, minimum 480p)
- Hardware H.264 encoding — VideoCore IV GPU encoder via
h264_v4l2m2m; output in crash-recoverable MKV format - Configurable video pipeline — choose between the built-in libavformat muxer or a fully custom
ffmpegsubprocess command - Timelapse mode — 9 configurable interval schedule functions (linear, exponential, logarithmic, power-law, Michaelis-Menten, logistic, stretched-exponential, hyperbolic); per-session floor/ceil clamping; named session directories; RTC-optional naming
- Network streaming — HTTP-MJPEG stream + browser-based control UI; REST API; Unix socket for scripted control; optional HTTPS
- Rebindable key map — all key bindings configurable in
microscopi.conf - Runtime SVG icon rendering — icons rendered from SVG to PNG at the correct size for the display via
rsvg-convert, cached per resolution in~/.cache/microscopi/ - Minimal OS — pi-gen Stage 2 (Raspberry Pi OS Lite) + custom Stage 3; nothing else runs on boot
All bindings are rebindable in the [keys] section of microscopi.conf. Defaults:
| Key | Action |
|---|---|
C / Shift+C |
Cycle exposure mode forward / backward (P→A→S→M) |
P / A / S / M |
Jump directly to that exposure mode |
↑ / ↓ |
Focus step (switches to manual focus) |
Shift+↑ / Shift+↓ |
Shutter speed +/− one stop (S and M modes) |
← / → |
Aperture +/− (A and M modes; cosmetic on fixed-aperture lenses) |
I / Shift+I |
ISO up / down; wraps cyclically through AUTO |
F |
Toggle autofocus on/off |
Space |
Capture still (saved to ~/stills/) |
Shift+R (hold 500 ms) |
Start / stop video recording (saved to ~/videos/) |
T (tap) |
Open timelapse dialog — enter interval + frame cap, Enter to start |
T (hold 3 s, while running) |
Stop timelapse |
X |
Toggle center guide overlay (circle + crosshair) |
H |
Show key binding help overlay (auto-dismisses after 4 s) |
Esc or Q (hold 5 s) |
Quit (warning shown after 2.5 s) |
While recording: a red ● HH:MM:SS'AS timestamp appears in the top bar (arc-seconds = 1/60 s, divides evenly at 30 and 60 fps with integer arithmetic only). A hold-arc grows in the top-left corner of the top bar while Shift+R is held before triggering.
While a timelapse is running: TL NNN (frame count) appears in the top bar in amber. Hold T for 3 s to stop — an amber arc grows in the top-left of the top bar.
See docs/controls.md for a detailed reference covering all controls, timelapse functions, and remote commands.
sudo pacman -S docker qemu-user-static binfmt-qemu-static
sudo systemctl restart systemd-binfmt
sudo systemctl start dockerCompiles inside an ARM64 Debian Bookworm Docker container (QEMU-emulated on x86_64):
./scripts/build-app.sh
# Output: deploy/install/usr/local/Packages the compiled binary, assets, config, and systemd service into an
ARM64 .deb for direct installation on a Pi that already runs Raspberry Pi OS
Bookworm. Requires only ar, tar, and gzip — no dpkg needed on the host.
./scripts/build-deb.sh
# Output: deploy/microscopi_<version>_arm64.debInstall on the Pi:
scp deploy/microscopi_*_arm64.deb microscopi@192.168.1.220:~/
ssh microscopi@192.168.1.220 'sudo dpkg -i ~/microscopi_*_arm64.deb'./scripts/build-image.sh
# Output: deploy/pi-gen/deploy/*.img.xz./scripts/flash.sh /dev/sdXcmake -S . -B build -DCMAKE_EXPORT_COMPILE_COMMANDS=ON
ln -sf build/compile_commands.json compile_commands.json/etc/microscopi.conf on the Pi (or config/microscopi.conf in this repo for the build-time default):
[video]
video_backend = builtin # builtin or ffmpeg
builtin_bitrate = 5000000 # bits/s for builtin backend
ffmpeg_command = ffmpeg -f rawvideo -pix_fmt yuv420p -s {width}x{height} -r {fps} -i pipe:0 -c:v h264_v4l2m2m -b:v 5M -f matroska {output}
video_dir = /home/microscopi/videos
stills_dir = /home/microscopi/stills
[camera]
camera_index = 0
fps = 30
initial_ae_enabled = true
initial_af_enabled = true
[display]
fallback_width = 1280
fallback_height = 720
show_crosshair = false
[keys]
# All values: lowercase letter/digit, or: up down left right space escape return
# Prefix with "shift+" for shift-modified bindings (e.g. "shift+t")
mode_cycle_fwd = c
mode_cycle_back = shift+c
mode_p = p
mode_a = a
mode_s = s
mode_m = m
iso_up = i
iso_down = shift+i
shutter_up = shift+up
shutter_down = shift+down
focus_up = up
focus_down = down
aperture_up = right
aperture_down = left
toggle_af = f
still = space
record = shift+r
timelapse = t
crosshair = x
quit = escape
help = hsrc/
main.cpp Entry point, main loop, exposure mode state machine
camera/
camera.h / camera.cpp libcamera C++ wrapper; controls: AE, AF, shutter, ISO
encoder.h / encoder.cpp V4L2 h264_v4l2m2m + libavformat MKV muxer
ui/
renderer.h / cpp SDL2 window, IYUV texture, frame presentation
osd.h / cpp Sony Alpha OSD; proportional sizing, SVG icons, overlays
input.h / cpp Table-driven key dispatch; hold timers for record/quit/help
config/
config.h / cpp INI parser; KeyMap loaded into InputHandler at startup
util/
resolution.h / cpp EDID display mode selection via SDL2
timelapse.h Interval schedule functions (header-only, no deps)
mjpeg_server.h / cpp HTTP-MJPEG stream server + embedded web UI
socket_server.h / cpp Unix domain socket command server
assets/
icons/ Lucide SVG icons (ISC license)
fonts/ Roboto Condensed (Apache 2.0)
config/
microscopi.conf Default runtime configuration (installed to /etc/)
pi-gen/ pi-gen build config and Stage 3 scripts
scripts/
build-app.sh Build binary in ARM64 Docker container
build-deb.sh Package deploy/install/ as an ARM64 .deb
build-image.sh Build full Pi OS image with pi-gen
build-image-debug.sh Debug image with gdb, debug symbols, crash tooling
flash.sh Flash image to SD card
docker/
Dockerfile.build ARM64 Debian Bookworm build environment
| Component | Choice |
|---|---|
| Camera API | libcamera C++ (not Python picamera2) |
| Display | SDL2 kmsdrm backend — no X11 or Wayland |
| Video encode | h264_v4l2m2m (VideoCore IV) into MKV |
| OSD font | Roboto Condensed via SDL2_ttf |
| Icons | Lucide SVGs → PNG via rsvg-convert at runtime, cached per display height |
| Service user | microscopi (no password, autologin on tty1) |
Security note: change the root password before any network-connected deployment.
See LICENSE.
Asset licenses: assets/icons/ — ISC (Lucide); assets/fonts/ — Apache 2.0 (Roboto Condensed).