2026In Progress
Airspeed PCB
Custom PCB system for capturing real-time air pressure data across aerodynamic components on a Formula SAE race car.
AltiumC++React.js

The Board
A standalone data acquisition board designed for aerodynamic testing on a Formula SAE race car. During testing, 12 Honeywell differential pressure sensors connect to pitot tubes mounted on an aero-rig. The sensors measure differential pressure across aerodynamic surfaces, providing the aero team with real-world data to validate their CFD models.
Data is logged through the ESP32's flash memory and stored long-term as .csv files on an SD card for post-session analysis.
| Component | Role |
|---|---|
| ESP32-S3-DevKitC-1 | Main microcontroller with built-in WiFi and USB CDC |
| Honeywell ABP2 Sensors (×12) | Differential pressure measurement via pitot tubes |
| TCA9548A I2C Mux (×2) | Route 12 sensors on a shared I2C bus |
| PCF8523 RTC | Real-time clock for timestamping logged data |
| CAN Transceiver | Vehicle CAN bus integration |
The Code
▫main.cpp
1#include <Arduino.h>2#include <Wire.h>3#include <virtualTimer.h>4#include "TCA9548A.h"5#include "airSpeed.h"6#include "PCF8523.h"78// GPIO Definitions9#define SDA_PIN 2110#define SCL_PIN 221112// SD card pins13#define SD_MISO 2714#define SD_CLK 1415#define SD_MOSI 1316#define SD_CS 151718// Hardware stuff19TCA9548A muxA(0x70); // MUX A at address 0x7020TCA9548A muxB(0x71); // MUX B at address 0x7121PCF8523_RTC rtc;2223// Airspeed sensors24// MUX A channels 0-7, MUX B channels 0-325static constexpr uint8_t MUX_A_COUNT = 6;26static constexpr uint8_t MUX_B_COUNT = 6;27static constexpr uint8_t SENSOR_COUNT = MUX_A_COUNT + MUX_B_COUNT;2829AirSpeedSensor sensorsA[] = {30 AirSpeedSensor(muxA, 0), AirSpeedSensor(muxA, 1),31 AirSpeedSensor(muxA, 2), AirSpeedSensor(muxA, 3),32 AirSpeedSensor(muxA, 4), AirSpeedSensor(muxA, 5),33};3435AirSpeedSensor sensorsB[] = {36 AirSpeedSensor(muxB, 0), AirSpeedSensor(muxB, 1),37 AirSpeedSensor(muxB, 2), AirSpeedSensor(muxB, 3),38 AirSpeedSensor(muxB, 4), AirSpeedSensor(muxB, 5),39};4041// Readings storage42ABP2Reading readings[SENSOR_COUNT];43RTCTime currentTime;4445// Timer group46VirtualTimerGroup timerGroup;4748// Forward declarations49void pollSensors();50void printData();5152// Callbacks53void pollSensors() {54 // Read RTC55 currentTime = rtc.readTime();5657 // Read all MUX A sensors58 for (uint8_t i = 0; i < MUX_A_COUNT; i++) {59 readings[i] = sensorsA[i].read();60 }6162 // Read all MUX B sensors63 for (uint8_t i = 0; i < MUX_B_COUNT; i++) {64 readings[MUX_A_COUNT + i] = sensorsB[i].read();65 }66}6768void printData() {69 // Print RTC timestamp70 if (currentTime.valid) {71 char ts[24];72 snprintf(ts, sizeof(ts), "20%02u-%02u-%02u %02u:%02u:%02u",73 currentTime.year, currentTime.month, currentTime.day,74 currentTime.hours, currentTime.minutes, currentTime.seconds);75 Serial.print("[");76 Serial.print(ts);77 Serial.print("] ");78 if (currentTime.os_flag) Serial.print("(RTC CLOCK LOST) ");79 } else {80 Serial.print("[RTC ERROR] ");81 }8283 Serial.print("t=");84 Serial.print(millis());85 Serial.println("ms");8687 // Print each sensor reading88 for (uint8_t i = 0; i < SENSOR_COUNT; i++) {89 const char *muxLabel = (i < MUX_A_COUNT) ? "A" : "B";90 uint8_t ch = (i < MUX_A_COUNT) ? i : (i - MUX_A_COUNT);9192 Serial.print(" MUX");93 Serial.print(muxLabel);94 Serial.print(":CH");95 Serial.print(ch);9697 if (readings[i].valid) {98 Serial.print(" P=");99 Serial.print(readings[i].pressure_inH2O, 4);100 Serial.print(" inH2O T=");101 Serial.print(readings[i].temperature_C, 1);102 Serial.println(" C");103 } else {104 Serial.print(" INVALID (status=0x"); // error catching105 Serial.print(readings[i].status, HEX);106 Serial.println(")");107 }108 }109 Serial.println("---");110}111112void setup() {113 Serial.begin(9600);114 Wire.begin(SDA_PIN, SCL_PIN);115116 Serial.println("DAQ Airspeed — Initializing...");117118 // Init MUXes119 if (!muxA.begin()) Serial.println("ERROR: MUX A (0x70) not found!");120 if (!muxB.begin()) Serial.println("ERROR: MUX B (0x71) not found!");121122 // Init RTC123 if (!rtc.begin()) {124 Serial.println("ERROR: PCF8523 RTC not found!");125 } else {126 Serial.println("PCF8523 RTC OK");127 }128129 // Init sensors — report which ones respond130 Serial.print("Sensors on MUX A: ");131 for (uint8_t i = 0; i < MUX_A_COUNT; i++) {132 bool ok = sensorsA[i].begin();133 Serial.print(ok ? "+" : "-");134 }135 Serial.println();136137 Serial.print("Sensors on MUX B: ");138 for (uint8_t i = 0; i < MUX_B_COUNT; i++) {139 bool ok = sensorsB[i].begin();140 Serial.print(ok ? "+" : "-");141 }142 Serial.println();143144 // CSV-style header for serial145 Serial.println();146 Serial.println("timestamp, millis, mux, ch, inH2O, temp_C");147 Serial.println("========================================================");148149 // Schedule repeating timers150 timerGroup.AddTimer(1000, pollSensors); // poll sensors every 1s151 timerGroup.AddTimer(1000, printData); // print to serial every 1s152153 Serial.println("Timers started.");154}155156void loop() {157 timerGroup.Tick(millis());158}