Main Code (File Layout)¶
The Arduino IDE concatenates every .ino file in alphabetical order into one translation unit. The numeric prefixes in this project are how that ordering is forced — Xregulator.ino first (it's the sketch and matches the folder name), then 2_functions.ino through 7_functions.ino. 99_obsolete.ino is kept private (not part of the public mirror) for archival code that hasn't been deleted yet.
This page is the map of what lives where, not a guide to the functions themselves — those are covered in the topic-specific pages.
File-by-File¶
Xregulator.ino (~4340 lines)¶
The sketch itself. Contains:
- All
#includedirectives and library global objects (ADS1115_lite adc,INA228 INA,tNMEA2000_esp32 NMEA2000,BMP388_DEV bmp388,OneWire oneWire,DallasTemperature sensors,PID currentPID,PID tempPID,VeDirectFrameHandler myve,AsyncWebServer server,AsyncEventSource events,DNSServer dnsServer,LSM6DSOXSensor imu). - Build-time constants (
FIRMWARE_VERSION, partition keys, bucket boundaries, buffer sizes, time intervals). - All major enum declarations (
Csv1Index,Csv2Index,Csv3Index,TsIndex,HttpsRequestType,DeviceMode,ChargeStageDisplayCode,TimeSource,GovernorMode,SystemMode,FieldControlMode,FieldEventReason,ShutdownPhase,DataIndex,ADS1115_State). - Most global state (system config, learning tables, PID state, IMU state, sensor extrema, NMEA caches, log structures).
- The certificate strings (
server_root_cafor Let's Encrypt,OTA_PUBLIC_KEYfor OTA signature verification). - The captive portal HTML (
WIFI_CONFIG_HTMLPROGMEM string). - The Arduino entry points:
setup()(~350 lines) andloop()(~600 lines).
If a global doesn't grep here it's probably one of the few static locals inside a function.
2_functions.ino (~2250 lines)¶
LittleFS helpers, TempTask, httpsTask and HTTPS execution paths, sensor window aggregation, sensor-history upload pipeline, config-payload builder, NTP time sync.
Notable functions: readFile(), writeFile(), fsExists(), fsRemove(), fsMkdir(), fsTakeLock(), fsReleaseLock(), performDeepFactoryReset(), TempTask(), httpsTask(), executeUploadPayload(), pushSensorSnapshot(), peekTailSnapshot(), popTailSnapshot(), dumpSensorRingToLittleFS(), restoreSensorRingFromLittleFS(), uploadSensorHistory(), clearSensorBuffer(), buildConfigPayload(), executeUploadConfig(), syncTimeFromNTP(), canUploadNow(), initSensorBuffer(), resetSensorWindow(), resetAccelWindow(), updateSensorWindow(), uploadBufferedRecords().
3_functions.ino (~6300 lines)¶
CSV payload enums and SafeInt(). The CSV index enums (Csv1Index, Csv2Index, Csv3Index, TsIndex) are declared at the top. All four _FIELD_COUNT sentinels live here.
WiFi mode selection (setupWiFi, connectToWiFi, setupAccessPoint, setupWiFiConfigServer, setupCaptivePortalLanding, loadAPCredentials). HTTP server (setupServer) with the entire /get?<name>=<value> settings dispatcher, log-download endpoints, action endpoints, factory reset, password handling, registration.
The SSE dispatcher SendWifiData() lives here — the most-edited function in the codebase because every new CSV1/2/3 field gets plumbed through it.
checkWiFiConnection() (intelligent reconnection logic). Authentication token handling (loadAuthToken, saveAuthToken, clearAuthToken). Vessel-info I/O (updateVesselInfoField, saveVesselInfoToFile, executeClearVesselInfo). debugStackBeforeHTTPS() (stack-overflow guard for the long-running TLS code path).
4_functions.ino (~4290 lines)¶
System health, weather mode, IMU init and analysis, OTA streaming + signature verification, time-sync state machine, partition tools, device-ID handling.
Notable: updateCpuLoad(), updateSystemHealthStats(), executeFetchWeatherData(), analyzeWeatherMode(), updateWeatherMode(), initWeatherModeSettings(), triggerWeatherUpdate(), checkTempTaskHealth(), writeINA228Register(), i2cProbe8bit(), imuInit(), initIMUStructures(), resetAccelStats(), complementaryFilter(), updateAccelMetrics(), updateWavePeriod(), updateVibrationEnergy(), initializeDeviceId(), getDeviceId(), checkDeviceUIDChange(), base64Decode(), verifyPackageSignature() (mbedTLS RSA), initStreamingExtractor(), parseTarHeader(), processDataChunk(), prepareForOTA(), validateHeapIntegrity(), performStreamingOTAUpdate(), performOTAUpdate(), performOTAUpdateToVersion(), checkForPendingUpdateNonBlocking(), executeUpdateFirmwareVersion(), executeCheckForcedUpdate(), executeClearForcedUpdate(), otaRestoreNormalOperation(), loadTimeSyncState(), saveTimeSyncState(), syncTimeFromGPS(), checkTimeSync(), executeClearToken(), printPartitionInfo(), testInternetSpeed(), initializeHardware().
5_functions.ino (~3990 lines)¶
NMEA2000 PGN handlers, VE.Direct reader, sensor data acquisition (ReadAnalogInputs and its inner _ReadAnalogInputs_inner), thermistor math, alarm-checking, persistent runtime updates (engine fuel/runtime, battery SoC, travel statistics, sailing metrics), thermal stress accumulator, system-time stuff.
Notable: SystemTime(), Rudder(), Heading(), COGSOG(), GNSS(), GNSSSatsInView(), BatteryConfigurationStatus(), DCStatus(), Speed(), WindSpeed(), WaterDepth(), Attitude(), HandleNMEA2000Msg() (PGN dispatcher), ReadVEData(), checkAndRestart(), captureResetReason(), UpdateEngineFuel(), UpdateBatterySOC(), UpdateTravelStatistics(), UpdateEngineRuntime(), getSmoothedGPS(), updateGPSBuffer(), UpdateSailingMetrics(), UpdateDistanceThisInterval(), UpdateWindMaximums(), UpdateBoardTempPressureMaximums(), getBatteryCurrent(), getBatteryVoltage(), getTargetAmps(), getFiltI(), getFiltV(), thermistorTempC(), CheckAlarms(), calculateThermalStress(), applySocGainCorrection(), handleSocGainReset(), checkAutoZeroTriggers(), processAutoZero(), handleAltZeroReset(), calculateChargeTimes(), readINA228AlertRegister(), clearINA228AlertLatch(), updateINA228OvervoltageThreshold(), checkWebFilesExist(), ReadAnalogInputs(), _ReadAnalogInputs_inner() (where the ADS state machine and INA228 + BMP388 reads live), drainIMUFifo(), ReadAnalogInputs_Fake() (dev-mode fake data), validateWebFile(), validateWebFilesystem(), ensureWebFS(), switchToFactoryWebFiles(), performFactoryReset(), calculateDerivedMetrics(), saveFuelTableToNVS() / loadFuelTableFromNVS(), ensureLittleFS(), ensurePreferredBootPartition(), loadPasswordHash(), savePasswordHash(), savePasswordPlaintext(), validatePassword(), queueConsoleMessage*(), popConsoleMessages(), trySendConsoleSSE(), initializeNVS(), saveNVSDataFull(), inCriticalZone(), safeToFlushIO(), fieldOffSettled(), loadNVSData(), initNVSCache().
6_functions.ino (~4220 lines)¶
Field control. Forward declarations at the top (the auto-prototype generator doesn't handle default-argument functions or struct parameters cleanly), then helpers, then the main control function AdjustFieldLearnMode(), then selectFieldControlMode() / selectFieldEventReason() (pure decision functions), updateProtectionCounters(), the shutdown path state machine runShutdownPath(), the governor governor_apply(), mode-entry bumpless-transfer functions enter_sys_off/manual/auto/fault, applyImmediateCut(), handleLimpHome(), RPM-table helpers (interpolateRPMTable, getMinimumFieldForRPM, getCapCurrentForRPM, updateCurrentRPMTableIndex), the temperature PID (tempPID_init, tempPID_tick), the charging-stage state machine (updateChargingStage, getChargeStageDisplayCode), voltage sensor checks (isVoltageSensorPlausible, isVoltageDisagreementCritical, isVoltageDisagreementWarning), updateFieldTelemetry(), reportFieldModeEvent(), the system-ID step test (systemID_tick, g_sysIDResume), the GPIO4 cut decision helpers (shouldImmediatelyCutGPIO4, shouldCutGPIO4AfterSettle), the buildTickSnapshot() factory, and the NVS-resident learning-table I/O (loadLearningTableFromNVS, resetLearningTableToDefaults, loadCapTablesForMode, saveUserTableEdits, saveHistoricalDataImmediate).
The PWM helper apply_pwm_float() and the slew limiter slew_limit_f() are here too.
7_functions.ino (~1940 lines)¶
Diagnostics and analytics: the efficiency tracker (initEfficiencyTracker, updateEfficiencyRedDot, updateEfficiencyMatrix, resetEfficiencyMatrix, sendEfficiencyData, sendEfficiencyRedDot, efficiencyTracker_tick, sendEfficiencyHistory, plus the matrix-merge / reference-selection / anomaly-scoring internals). The CV log writer (cvLog_init, cvLog_tick). The CH1 ring buffer stats (ch1_record, ch1_compute_stats). The INA228 cadence stats (resetINA228IntervalWindows, resetINA228AllStats, recordINA228Interval). The cached gz-file server (cacheGzFiles, serveCachedGz). The system-ID step-test core (systemID_tick).
99_obsolete.ino¶
Private-only. Holds code paths that have been removed from the active hot loop but not yet deleted (typically because of pending re-evaluation, e.g. paths that were live during a refactor and might come back). Not part of the public mirror. Should be empty in steady-state but is allowed to contain anything during a refactor window.
Forward Declarations and Auto-Prototypes¶
Arduino's auto-prototype generator runs over the concatenated .inos before compile. Two failure modes appear here:
- Default-argument functions — auto-prototype emits the declaration without the default, so callers later in the file see the no-default signature. Fix: hand-declare them in
Xregulator.inoafter the includes. Example:bool fieldOffSettled(uint32_t extraMs = 0); - Functions that take struct parameters whose definition appears later — auto-prototype runs before struct declarations are visible. Fix: forward-declare the struct in
Xregulator.inobefore the includes section ends, then hand-declare the function. Example:struct UpdateInfo;ahead ofvoid performOTAUpdate(const UpdateInfo &updateInfo);.
Both are documented in Xregulator.ino around line 81.
Top of 6_functions.ino has its own forward-declaration block specifically for the field-control helpers — that file is so cross-referential internally that the auto-prototyper sometimes places things in the wrong order otherwise.
Build Configuration¶
| Setting | Value |
|---|---|
| Board | esp32:esp32:esp32s3 |
| Flash Size | 16M |
| Partition Scheme | custom — uses partitions.csv in the sketch folder |
| CPU Freq | 240 MHz at boot (lowered to 80 MHz dynamically in low-power state) |
| PSRAM Mode | enabled (OPI / octal — required) |
| Loop Task Stack | SET_LOOP_TASK_STACK_SIZE(20 * 1024) — bumped from default 8 KB for TLS |
The build/deploy aliases live in ~/.zshrc:
flashFactory—./compress_web.sh && mklittlefs && esptool.pywriting to factory web partition0x510000flashOTA— same but writes to production web partition0x610000build_and_deploy.sh <version> "<notes>"— full OTA pipeline: compress web → arduino-cli compile → tar bundle → SHA256 + RSA sign → upload to the public Supabase Storage bucket (ota) via the Supabase CLI → updateversions.json
compress_web.sh regenerates the data/ directory from web_src/. Never edit data/ directly. See HTML Structure for the web-file source-of-truth conventions.
Memory Allocation Convention¶
Anything larger than ~1 KB is allocated to PSRAM via ps_malloc() in setup(). The full list is at System Overview → Boot Sequence step 2. Internal RAM is reserved for FreeRTOS stacks and TLS handshake buffers — see System Overview → Hardware Target.
Cross-references¶
- Adding a new variable to a CSV stream → Important Functions
- Adding a timed function (
TIMED_CALLmacro,ft_*structs) → Important Functions → Adding a Timed Function - Function-by-function reading of the control loop → Field Control
- Build aliases, OTA pipeline, deploy commands → CLAUDE.md (project-local conventions)