Expert guidance for Omnimeter ESP32-S2 precision voltmeter firmware development with auto-ranging, web interface, and calibration management using PlatformIO/Arduino.
Specialized skill for developing and maintaining the Omnimeter ESP32-S2 precision voltmeter firmware. This is a production-ready embedded C++ project using PlatformIO with the Arduino framework, featuring 5-range auto-ranging, web interface, USB serial output, and persistent calibration.
The Omnimeter is an ESP32-S2 based precision voltmeter (Lolin S2 Mini board) with:
1. **Voltmeter Module** (`src/Voltmeter.cpp`)
- ADC driver with 5 parallel voltage dividers
- 13-bit resolution with 64-sample oversampling
- Hysteresis-based auto-ranging
- GPIO pins 1-5 for different ranges
2. **WebInterface Module** (`src/WebInterface.cpp`)
- ESPAsyncWebServer implementation
- Dual-mode WiFi (AP/Station)
- JSON REST API at `/api/voltage`
- Embedded HTML using PROGMEM
3. **CalibrationManager** (`src/Calibration.cpp`)
- NVS-persistent storage
- Two-point calibration per range
- Serial command interface
- Magic number validation (0xCA11B001)
4. **Main Loop** (`main.cpp`)
- Non-blocking task scheduler
- millis()-based timing intervals
- Coordinates all modules
1. **Memory Management Rules**
- Always prefer static allocation over dynamic allocation
- Use `static` objects, avoid `new` in `setup()`
- Never use String concatenation in loops
- Use `snprintf()` with pre-allocated `char[]` buffers
- Store large constants (HTML, etc.) in PROGMEM using `PROGMEM` storage
2. **Non-Blocking Pattern (Critical)**
- All timing MUST use `millis()` with interval constants
- NEVER use `delay()` anywhere in `loop()`
- Pattern to follow:
```cpp
constexpr uint32_t INTERVAL_MS = 100;
if (currentTime - lastUpdate >= INTERVAL_MS) {
lastUpdate = currentTime;
// ... task code
}
```
3. **ADC Configuration Standards**
- Resolution: 13-bit (`analogReadResolution(13)`)
- Attenuation: 11dB for ~2.5V max range
- Use factory eFuse calibration when available
- 64-sample oversampling for noise reduction
4. **Calibration Safety Requirements**
- Gain must default to `1.0f` if NVS empty/corrupt
- Offset must default to `0.0f` if NVS empty/corrupt
- Validate all float inputs via `parseVoltage()` function
- Reject NaN, Inf, and multiple decimal points
- Always use magic number validation for stored data
5. **Data Flow Architecture**
```
ADC Pins (GPIO1-5) → Voltmeter.measure() → MeasurementResult struct
↓
Serial output ← main loop (250ms)
Web JSON API ← AsyncWebServer (on request)
```
| Constant | Location | Purpose |
|----------|----------|---------|
| `PIN_RANGE_*` | Voltmeter.h | ADC GPIO pins (1-5) |
| `MULTIPLIER_*` | Voltmeter.h | Voltage divider scaling factors |
| `AP_SSID/AP_PASSWORD` | WebInterface.h | Default WiFi AP credentials |
| `*_INTERVAL_MS` | main.cpp | Task timing intervals |
When providing build/upload instructions, use these PlatformIO commands:
```bash
pio run
pio run --target upload
pio device monitor
pio run --target uploadfs
```
Available commands for calibration and diagnostics:
No automated test framework is used. Validate changes via:
1. **Serial output verification**: Monitor output using `pio device monitor`
2. **Web API response check**: Test with `curl http://192.168.4.1/api/voltage`
3. **Calibration verification**: Use known reference voltages and verify readings
4. **Range switching**: Test hysteresis and auto-ranging with varying input voltages
Before suggesting code changes, verify:
```cpp
// In main.cpp
constexpr uint32_t NEW_TASK_INTERVAL_MS = 1000;
static uint32_t lastNewTask = 0;
void loop() {
uint32_t currentTime = millis();
if (currentTime - lastNewTask >= NEW_TASK_INTERVAL_MS) {
lastNewTask = currentTime;
// Your task code here
}
}
```
```cpp
// In main.cpp or appropriate module
void handleSerialCommand(const String& command) {
if (command.startsWith("NEWCMD:")) {
// Parse and execute
Serial.println("Response");
}
}
```
```cpp
// In WebInterface.cpp
char jsonBuffer[128];
snprintf(jsonBuffer, sizeof(jsonBuffer),
"{\"voltage\":%.3f,\"range\":%d}",
result.voltage, result.rangeIndex);
request->send(200, "application/json", jsonBuffer);
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/esp32-voltmeter-firmware-development/raw