Burak Unlu
← Home

pebl

Firmware and desktop software for a handheld neuromuscular assessment device.

Software & Firmware Engineering Intern · Dec 2025 – present · In progress

Rust · Tauri · C (ESP-IDF) · TypeScript · React Native · Python

~67% of commits across 4 repos; sole author of 3 of the 4

pebl is a handheld device that measures grip strength, tremor, reaction time, and vibration threshold in one short clinical battery. I am the software and firmware intern on the team. My work covers four repositories: ESP32-S3 firmware in C, a Rust and Tauri desktop app, an iOS companion app, and a Python SDK. The thread through all of them is a binary wire protocol I implemented three times, once per language, and verified byte for byte.

The problem

A clinic measures grip strength, tremor, reaction time, and vibration threshold with four separate pieces of equipment, and none of them talk to each other. pebl health is building one palm-sized device that runs the whole battery in about two and a half minutes and produces numbers a researcher can track over months.

That only works if the software is boring in the right way. The device, the clinician's laptop, and the patient's phone all have to agree on every byte that crosses the wire, and the timestamps have to line up well enough that a tremor spectrum means something.

Constraints

Two hardware generations exist at once: a bootstrap board and the clinical unit. The rule we work to is one protocol and one software stack across both boards.

The device is positioned as a clinical-grade assessment tool, not a cleared medical device, so clinical data stays on the clinician's machine instead of in a cloud we would then have to defend. Researchers also expect their own tools to keep working, which ruled out a closed app with a private file format.

What I built

I designed a binary wire protocol with CRC-16/CCITT framing and wrote it three times: in C for the firmware, in Rust for the desktop backend, and in TypeScript for the phone, with tests that prove the three agree byte for byte.

On the desktop side I built a Tauri backend in Rust, about 19,700 lines with roughly 390 tests, that republishes device data over Lab Streaming Layer, gRPC, and a Python SDK, so a lab can record pebl next to its existing equipment. It carries a clock-sync engine with min-RTT filtering and a weighted-least-squares drift model that time-aligns multi-kilohertz streams.

What I owned

I wrote the firmware for both board generations in C on ESP-IDF, the Rust and Tauri desktop app, the React frontend on top of it, the iOS companion app in React Native, and the Python SDK. Across the four repositories I am the primary software contributor, around 67% of commits, and the sole author of three of the four.

What is not mine: an electrical engineer owns the hardware and the board design, the CTO owns the system architecture and shipped the clinical unit's Bluetooth stack, and a clinician designed the six-test battery and the scoring that makes the numbers mean anything. I implemented that battery twice, on desktop and on iOS, but I did not design it.

Results

The protocol has held. Three independent implementations, checked byte for byte in three separate reviews, and a desktop app that discovers devices, streams them live, and hands the same samples to LabRecorder, MATLAB, or a Python script at once. Of 55 pull requests, 52 merged.

We review the codebase end to end before any clinic sees the device, and I would much rather hear about a problem early than late.

What I would change

I would start the firmware acquisition path earlier. The host pipeline and the wire protocol were ready long before the firmware could fill them, and building the fast half first let me put off the harder driver work.

I would also be quicker to delete code the product no longer needs. The sooner that call is made, the less work gets thrown away.

Screens