Overview
Princeton Racing Electric (PRE) builds an electric race car to compete at Formula Hybrid + Electric. The high-voltage side makes the car go. The low-voltage side is how it knows what's happening: pedal and steering inputs, battery status, vehicle state, what the driver sees, and what gets logged. If a sensor reads wrong or a message goes missing, the car doesn't drive correctly.
I joined as an embedded software engineer in fall 2024, was embedded software lead through 2025–26, and have led the low-voltage system since May 2026. For the current car, MK3, that means firmware on the individual nodes, the CAN network that connects them, and making sure everything works together once it's actually on the car.
My role
- Write C++ firmware for ESP32 nodes that read sensors over ADC, SPI, I²C, and CAN (TWAI).
- Define the CAN interface: message IDs, data packing, and which node is responsible for what.
- Decide the low-voltage architecture and break it into pieces other members can own.
- Prioritize blockers, review and debug teammates' work, and coordinate with the electrical and mechanical subteams.
- Bring boards up on the bench, then validate them on the car.
System
MK3 is built as a set of CAN-connected nodes. A Jetson Orin Nano acts as the main vehicle computer. It is the CAN gateway, handles safety and vehicle-state logic, logs data, and runs higher-level algorithms. Around it, ESP32-based nodes each own one part of the car: accumulator electronics, HAM/DRS, pedals, the steering wheel, and the dashboard. Third-party CAN devices like GPS, TPMS, and an IMU share the same bus.
The firmware on each node covers pedal sensing, steering-angle sensing, battery and accumulator monitoring, vehicle-state sensing, and data acquisition, plus sensor drivers and bring-up code for each board.
Dashboard and telemetry
The driver needs a small amount of the right information, and the team needs everything logged. Dashboard firmware was a big part of my first year on the team, and I've since redesigned it. It runs C++ and Python on Raspberry Pi and ESP32 based setups that parse live CAN traffic and show useful telemetry to the driver. The same data is logged so we can debug the car after a run.
Engineering decisions
Defining the interface first. Most integration problems on a car like this are really interface problems: two nodes disagreeing about an ID, a byte order, a scale factor, or who sends what. Writing down message IDs, packing, and node responsibilities before writing firmware gives every node a contract to test against.
Splitting work by node. Giving each subsystem its own microcontroller makes the pieces small enough for one person to own, test on the bench, and hand off. It also means a problem in one node can be isolated without taking the whole car apart.
A central computer for the higher-level work. Putting gateway, logging, and vehicle-state logic on the Jetson keeps the ESP32 nodes simple and focused on their sensors.
Debugging and validation
A lot of the work isn't writing firmware. It's proving it works: first on the bench, then on the car. When something doesn't behave, I work from the physical layer up:
- Check the wiring and connectors.
- Watch the CAN traffic. Is the message on the bus at all?
- Verify the message ID and data packing against the spec.
- Test the sensor output directly, independent of the firmware.
- Check timing.
- Compare expected and actual values to decide whether it's a software or hardware problem.
Testing each device on its own before full vehicle integration catches most issues where they are cheapest to fix. The repetitive part of this workflow, sending and watching CAN frames from a laptop, is what led me to build CANbench.
Leading the subteam
Most newer members haven't done embedded work before. When I explain a subsystem, I start from the whole path, sensor → microcontroller → CAN message → vehicle network → consuming node, and only then go into the code. Once someone can see where their piece fits, they can debug it themselves.
What I learned
Systems that have to work on a real car make you care about interfaces, validation, and your own assumptions far more than code that only has to pass a test.
The team's goal is a competitive MK3 at Formula Hybrid + Electric. My part is making sure that when the car rolls out, every node is talking.