how this happened
The original plan for EDES 301 was an autonomous agricultural drone that flew a field and surveyed crops. Then the supply chain happened: the LiPo we needed wouldn't ship, one of the ESCs we'd ordered was a different revision than the datasheet, and we ran out of weeks before we ran out of dependencies.
So we grounded the drone and rebuilt what we had into something that could still demo. The flight controller became a Linux board on a desk. The gimbal became a servo-driven scan head. The altitude sensor (ultrasonic) became the radar's range finder. Same parts, different problem.
what it does
A PocketBeagle drives two servos that sweep two ultrasonic sensors across a 180° arc. Each ping gets time-of-flight processed in software and turned into a (range, bearing) pair. A small Flask web server hosts an HTML5 canvas dashboard that draws the sweep in real time. There's a manual mode (slider to aim the head), an auto-scan mode (continuous sweep), and a tunable alarm threshold that flashes red when something crosses the range you set.
hardware
the voltage thing
The PocketBeagle's GPIO runs at 3.3 V. The HC-SR04 ultrasonic sensor needs 5.0 V on its supply and won't trigger reliably below that. The drone hardware happens to have a 5 V rail on pin P1_05 (VBUS), so the fix was a split-rail bus: 5 V to the sensors and servos, signal lines pulled down to 3.3 V on the GPIO side. Once that was sorted, the actual time-of-flight loop is the easy part.
time of flight
Fire a 10 µs pulse on TRIG, wait for the rising edge on ECHO, measure how long ECHO stays high. The pulse travels at the speed of sound (340 m/s in dry air, 343 with humidity), bounces off whatever it finds, and comes back. Distance to the object is half the round trip.
distance_cm = (echo_us * 34300) / 2 / 1_000_000
The /2 is because the wave goes out and comes back. The 34300 is just 343 m/s converted to cm/s. The sensor is rated to about 4 m before the return pulse drops below the threshold and you start getting timeouts.
software
Two threads. The first owns the radar: step the servo, fire a ping, wait for echo, calculate range, write to a shared buffer. The second is a Flask server that exposes the buffer over a websocket and serves the dashboard. The canvas takes (angle, range) tuples and draws them on a polar plot with a sweep trail.
The HC-SR04 Python libraries I could find all assumed Raspberry Pi GPIO and didn't work on the PocketBeagle, so the GPIO ended up as a thin userspace shim that reads and writes the sysfs files directly:
echo 1 > /sys/class/gpio/gpio59/value
Crude but works, and writing it from scratch took less time than fighting the libraries.
Drawing the sweep on the canvas is just polar-to-cartesian: each detection has a range r and a bearing θ, and the dashboard projects it onto pixel coordinates.
what's left to fix
- Servo jitter on the sweep. The SG90 is cheap and noisy. A better servo or a small dead-band filter on the angle command would clean up the trace.
- Echo cross-talk. Running both sensors simultaneously occasionally has one's ping confused for the other's echo. Easy fix is alternating pings (we just didn't get to it).
- Range vs. update rate. A full sweep at usable angular resolution is around 1 Hz. Fine for a desk demo, not fine for anything moving.