A fully modular, open-source Pinewood Derby race management system that works in every environment — from full cloud to completely offline Raspberry Pi.
git clone https://github.com/fynnob/PineWoodDerby-OpenSource
cd PineWoodDerby-OpenSource
pip3 install -r requirements.txt
python3 setup.py # open http://localhost:8787 and configure
python3 run.py # start the race serverThe system is built from 7 interchangeable modules:
| # | Module | Description |
|---|---|---|
| 1 | Supabase backend | Cloud database, realtime, storage, edge functions |
| 2 | SQLite + FastAPI backend | Fully local, no internet required |
| 3 | GitHub Pages frontend | Serve UI from GitHub Pages CDN |
| 4 | Local frontend | Serve UI from the local FastAPI server |
| 5 | Heat/round scoring | Software-only: officials tap finish order manually |
| 6 | ESP32 sensor bridge | Separate hardware device POSTs times via HTTP |
| 7 | Raspberry Pi GPIO sensors | Sensor wires connected directly to Pi GPIO pins |
| Backend | Frontend | Scoring | Use Case |
|---|---|---|---|
| Supabase | GitHub Pages | Heat | Typical club event with internet |
| Local (Pi) | Local | Heat | Home use, no internet |
| Local (Pi) | Local | GPIO | Automated timing with wire sensors |
| Local (Pi) | Local | ESP32 | Automated timing via wireless ESP32 |
PineWoodDerby-OpenSource/
├── setup.py # Visual setup wizard (runs on port 8787)
├── run.py # Race server launcher
├── config.example.json # Configuration template
├── requirements.txt
│
├── backend/ # Module 2: Local FastAPI server
│ ├── server.py # REST API + WebSocket
│ ├── db.py # SQLite data layer
│ ├── scoring_heat.py # Module 5: Manual heat scoring
│ ├── scoring_sensor.py # Module 6: Remote sensor scoring
│ ├── scoring_gpio.py # Module 7: Raspberry Pi GPIO scoring
│ └── email_sender.py # Local SMTP email
│
├── frontend/ # Modules 3 + 4: Web UI
│ ├── config.js # Generated by setup.py
│ ├── styles.css
│ ├── index.html # Parent registration
│ ├── lib/
│ │ └── api.js # Universal API shim (Supabase OR local)
│ ├── admin/ # Admin station pages
│ │ ├── index.html # Admin hub
│ │ ├── results.html # Race officials + heat control
│ │ ├── inspection.html # QR-code car inspection
│ │ ├── track.html # Lane board
│ │ ├── cars.html # Car management
│ │ ├── announcer.html # Screen controller
│ │ └── settings.html # Email + deployment settings
│ └── display/
│ ├── index.html # Public display screen
│ └── screen.html # Announcer-controlled display
│
├── sensor-bridge/ # Module 6: ESP32 firmware
│ └── sensor_bridge.ino
│
├── wizard/
│ └── index.html # Setup wizard UI
│
└── cloud/ # Module 1: Supabase cloud files
├── DEPLOY.md
└── supabase/
├── schema.sql
└── functions/
└── send-registration-email/
└── index.ts
Run python3 setup.py and open http://localhost:8787 in a browser.
The wizard walks you through 5 steps:
- Backend — Supabase cloud or local FastAPI
- Frontend — GitHub Pages or local server
- Scoring — Manual, remote sensor (ESP32), or GPIO (Pi)
- Network — Hotspot, mDNS, captive portal
- Details — Track name, PINs, SMTP settings
When done, it writes config.json and generates frontend/config.js.
When running locally on a Raspberry Pi, run.py can:
- Create a Wi-Fi hotspot (
hostapd+dnsmasq) so all race devices connect to the Pi - Set up a captive portal so all HTTP requests are redirected to the race app
- Advertise
derby.localvia mDNS (avahi) for easy hostname access - Launch GPIO scoring in a background thread (Module 7)
The sensor-bridge/sensor_bridge.ino firmware runs on an ESP32 connected to
IR break-beam sensors (one per lane). When a car triggers a sensor:
- The elapsed time since gate-open is calculated
- A POST request is sent to
/api/sensor/hitwith{ "lane": N, "time_ms": T } - The server records the time and broadcasts results when all lanes report
See sensor_bridge.ino for pin configuration and wiring notes.
Wire IR sensors directly to Raspberry Pi GPIO pins. Configure the pin numbers
in the setup wizard. run.py starts the GPIO scoring thread automatically.
Fynn Source License v1.0 — see LICENSE for full terms.
Summary: free to use and adapt for personal/non-commercial use with attribution. Commercial use and public redistribution require permission.