Start Here¶
This page covers what the software stack is, where the code lives, how the repository is laid out, and how to navigate a codebase this size. It's written for open-source contributors — anyone from a hobbyist who has run the Arduino Blink example up through experienced developers.
What the software stack is¶
The project has three layers:
- Firmware — Arduino C++ running on an ESP32-S3 microcontroller. It reads the sensors, runs the charging control loop, enforces the safety protections, and acts as a small web server.
- Embedded web dashboard — plain HTML, JavaScript, and CSS (no framework) stored in the device's flash and served over WiFi to any browser on the same network. The same web bundle is also wrapped as an iOS app (using Capacitor), so keep phones in mind when touching the interface.
- Optional cloud features — the device can upload anonymized history and statistics to the project's servers for long-term trend plots and fleet leaderboards. Everything safety-critical runs entirely on the device; the regulator operates fully without internet.
Where the code lives¶
The public source repository is github.com/markliquid1/Regulator2026-public.
It contains the firmware (.ino files) and the web dashboard source (web_src/ — index.html, script.js, styles.css, plus the uPlot charting library files).
A tour of the repository¶
The starting point is Xregulator.ino. It holds all the global variables, setup(), and loop() — all primary program flow and state live here, the same layout as a basic Arduino sketch.
The remaining numbered files hold supporting functions. The numeric prefixes (2_, 3_, ...) exist only because the Arduino IDE presents files as alphabetized tabs — the numbers fix the display order, nothing more. Broad roles:
| File | Broad role |
|---|---|
Xregulator.ino |
All globals, setup(), loop() — the program's spine |
2_functions.ino |
Storage and persistence layer (the key-value settings helpers settingExists() / settingRead() / settingWrite() and their NK_* key names, plus flash-filesystem helpers), the two background tasks (TempTask, httpsTask), and the sensor-history ring buffers and cloud upload payload builders |
3_functions.ino |
Networking and the web server — WiFi mode selection (setupWiFi()), the first-time-setup captive portal, the HTTP endpoint handlers (setupServer()), and assembly of the five live-telemetry payloads (SendWifiData()) |
4_functions.ino |
Hardware bring-up (initializeHardware()), boot-time settings loading (InitSystemSettings()), motion metrics from the inertial sensor (IMU), weather mode, and the over-the-air firmware update machinery |
5_functions.ino |
External data sources (NMEA2000 message handlers, Victron VE.Direct via ReadVEData()), sensor acquisition (ReadAnalogInputs()), alarms (CheckAlarms()), battery state-of-charge and engine/travel statistics, and the persistence of runtime counters (saveNVSDataFull() / loadNVSData()) |
6_functions.ino |
The core control code — the field control loop (AdjustFieldLearnMode()), mode and safety-reason selection (selectFieldControlMode()), the ramp-rate governor and shutdown paths, charging stages, RPM tables, and tuning logs/scoring |
7_functions.ino |
Analytics layers — alternator health tracking, sailing and motoring performance, high-rate current statistics, and serving of the pre-compressed web files (cacheGzFiles()) |
8_functions.ino |
Fast alternator-current failure detector (the high-rate diode/winding-fault detector) and the config-sharing manifest (CONFIG_MANIFEST[]) that drives both config export/import and the daily fleet config snapshot |
The web_src/ folder is the dashboard: index.html for structure, script.js for behavior, styles.css for styling. Workflow there is much the same as the firmware — you'll often touch all three in parallel.
Design philosophy¶
The codebase is an intentional monolith, and the large majority of variables are global. The layout is flat and predictable: state in one file, functions grouped loosely by purpose in the rest.
This goes against conventional software engineering practice, and that trade is deliberate:
- Beginner navigation. No class hierarchies, no dependency injection, no build system beyond the Arduino IDE. If you can read one function, you can read them all.
- AI-assisted development. A flat global-state layout means an AI assistant (or a new human) can reason about any function from
Xregulator.inoplus one supporting file, without chasing abstractions across a dozen modules. This keeps token spend — and human reading time — low.
The original goal was fast solo iteration; the goal now is clarity, as the open community starts to help.
How to navigate a codebase this large¶
The codebase is far too large to read end-to-end, and you never need to. Two approaches work well:
Search-driven. Use your editor's project-wide search (or grep on the command line) for the function or variable name you care about. Names are long and descriptive on purpose — searching BulkVoltage or AdjustFieldLearnMode lands directly on the relevant code. The file table above tells you which file to expect a hit in.
AI-assisted. A workflow that has proven effective:
- Paste
Xregulator.inointo your AI assistant along with your question or change request. - Ask it what other files or functions it needs to see.
- Paste those in as requested.
Because the code is organized in a predictable, mostly flat way, this converges quickly — the assistant rarely needs more than one or two supporting files.
Suggested reading order¶
- Architecture first. Boot and the main loop — how the program starts and what runs every tick. Then memory and persistence — where state lives and survives reboots. Then the telemetry pipeline — how live data reaches the dashboard.
- Then the subsystem you care about: charging control, safeties and protections, sensors, external interfaces, networking and the web server, alternator health, sailing performance, or over-the-air updates.
- The client side, if you're touching the dashboard: dashboard architecture and styling.
- When you're ready to modify: the Extending the System recipes — adding telemetry, adding a setting, adding a dashboard control — and the contributing guide.
To build and flash the firmware yourself, see Building the Firmware. For the optional cloud account side, see account management.
Questions, bugs, ideas: email joe@xengineering.net or open an issue on the public repo.