Skip to content

Repository files navigation

CAM Hardware Test

Bring-up and bench-test firmware for the CAM-MK3 transmitter board and the EAGLE receiver board.

This repo is the sandbox that feeds CAM-Software (~/iss/CAM-Software). Anything that touches new hardware — a radio chip, a link budget, a packet format, a new peripheral — gets proven here first as a small standalone sketch. Once it works and the numbers look right, the driver and protocol get ported into CAM-Software, which is the real flight firmware (video capture → JPEG encode → radio → ground station).

Rule of thumb: if you are asking "does this hardware even work?" you belong in this repo. If you are asking "how does the flight video pipeline behave?" you belong in CAM-Software.

CAM-Hardware-Test (this repo) CAM-Software
Purpose Hardware bring-up, driver bring-up, RF benchmarks Flight firmware
Scope One test program at a time Full video pipeline (DVP → JPEG → RF → USB)
Framework PlatformIO + Arduino (esp32-p4) PlatformIO, hybrid ESP-IDF + Arduino
components/arduino Committed in-tree Git submodule
Video Synthetic test patterns Real NTSC from Runcam via TVP5151

Hardware

  • CAM-MK3 — ESP32-P4 transmitter board (rocket avionics bay). Built with -DIS_CAM.
  • EAGLE — ESP32-P4 ground-station receiver board. Built with -DIS_EAGLE.
  • LR2021 — sub-GHz transceiver on both boards, driven over SPI (RadioLib 7.6.0, vendored in lib/RadioLib/).

Both boards use the same esp32-p4-evboard PlatformIO board definition and differ only in pin map and role. All pins for both boards live in src/pins.h, guarded by IS_CAM / IS_EAGLE.

Building and flashing

# CAM (transmitter)
pio run -e CAMmk3 -t upload

# EAGLE (receiver)
pio run -e EAGLE -t upload

# Serial monitor (115200)
pio device monitor -e EAGLE

There is no git submodule step in this repo — components/arduino is committed directly, unlike CAM-Software. A plain clone builds.

Serial output goes over USB-CDC (TinyUSB), not USB-serial-JTAG — see sdkconfig.defaults. Every test program does the same dance:

USBCDC USBSerial;
#undef Serial
#define Serial USBSerial

so Serial in these sketches means the CDC port (e.g. /dev/tty.usbmodem01, COM4), which only enumerates after USB.begin().

How the test programs work

src/CMakeLists.txt globs everything in src/, so exactly one file may have live code at a time. Every other test is kept in the tree fully commented out. To switch tests: comment out the currently active file, uncomment the one you want, rebuild.

Yes, this is crude. It is also why each test file is self-contained and duplicates its own setup()/loop().

Currently active: src/fsk_framer_test.cpp.

File What it proves
fsk_framer_test.cpp End-to-end frame test: CAM builds a synthetic YUV422 frame, fragments it, bursts it; EAGLE reassembles it and streams the raw frame out over USB for the Python reader.
fsk_tester.cpp Minimal FSK hello-world — single packet TX/RX, sanity check that the link is alive.
fsk_tester_benchmark_single.cpp Throughput/loss over N single packets, one transmit() per packet.
fsk_tester_benchmark_burst.cpp Same benchmark using transmitBurst() (back-to-back FIFO writes), plus per-packet RSSI/LQI log.
flrc_tester.cpp FLRC mode hello-world. Deprecated — FLRC's 511-byte FIFO refill was never made reliable.
flrc_tester_benchmark.cpp FLRC throughput benchmark. Deprecated with the above.
blank.cpp LED blink + serial print. First thing to flash on a fresh board.
old.cpp Deprecated raw-RadioLib example, kept for reference on the FIFO opcodes.

Radio driver (lib/LR2021/)

  • radio.h — all RF configuration in one place: 434 MHz, 1000 kbps GFSK, 250 kHz deviation, 2222.22 kHz RX bandwidth, 22 dBm, 24-bit preamble, 2-byte IBM CRC, 8-byte sync word, 255-byte fixed packet length. 915 MHz constants are defined alongside. Also defines the LR2021Error struct — every driver call returns one, and .stageStr() tells you which init/TX/RX stage failed instead of a bare RadioLib code.
  • fsk.h / fsk.cpp — the driver actually in use. transmit(), transmitBurst(), receive() (with RSSI/LQI packet status), and transmitCallSign() for FCC ID (KE2CNQ).
  • flrc.h / flrc.cppdeprecated, do not use.
  • fsk_framer.h — splits a buffer into fixed-size fragments.
  • fsk_reassembler.h — puts them back together on EAGLE, with frame timeout and drop/reset statistics.

Fragment format

A 255-byte packet carries a 7-byte header and 248 bytes of payload:

byte  0      frame_id
bytes 1-2    fragment index   (big endian)
bytes 3-6    total frame size (big endian)
bytes 7-254  payload, zero-padded on the last fragment

The last fragment is zero-padded rather than short, because the radio runs in fixed packet-length mode. Frames and fragment pools are allocated in SPIRAM (heap_caps_malloc(..., MALLOC_CAP_SPIRAM)), not internal RAM.

Host tools (reader/)

Python side of the bench, managed with uv.

cd reader
uv run frame_reader.py   # live YUV422 preview + per-frame PNG dump
uv run reader.py         # plain serial logger, appends to log.txt
  • frame_reader.py — reads EAGLE's USB-CDC stream, splits text status lines from the raw binary frame blob, converts YUYV → BGR, shows it with OpenCV and writes frames to yuv_frames/.
  • reader.py — timestamped line logger, for benchmark runs where you only care about the printed stats.

Both scripts hardcode the serial port and frame_reader.py hardcodes the frame dimensions — edit PORT / YUV_WIDTH / YUV_HEIGHT to match your machine and whatever fsk_framer_test.cpp is currently configured for, or the reader will desync from the stream.

Captured runs

Raw serial captures from bench sessions are committed for reference:

Note the framer capture predates the "decreasing image size" commit, so its frame size is larger than what the current fsk_framer_test.cpp sends.

Gotchas

  • Only one active file in src/. Two files with a live setup() will fail to link with duplicate-symbol errors.
  • Flash appears to fail but succeeded. Same known toolchain watchdog bug as in CAM-Software — if the flashing dialog appeared, it almost certainly took.
  • EAGLE prints nothing. It waits on while (!Serial). Open the port; also check the CDC device re-enumerated after reset.
  • Bring both boards up together. CAM transmits shortly after boot and EAGLE ends the run on a 500 ms RX idle timeout, so a late receiver simply misses the burst.
  • SPIRAM allocation failures print FATAL: SPIRAM alloc failed and halt — check the sdkconfig for the environment you built.

About

LR2021 Driver testing and implementation. Can be used for CAM & EAGLE bringup when issues need to be isolated.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages