Sensor Systems & Data Acquisition¶
Overview¶
The regulator uses multiple sensor systems to monitor electrical parameters, temperature, and engine status. The system implements redundant measurements with automatic fallback and data validation to ensure reliable operation.
ADS1115 4-Channel Analog Input¶
Purpose¶
The ADS1115 provides four 16-bit analog-to-digital conversion channels for primary system measurements: - Channel 0: Battery voltage (via voltage divider) - Channel 1: Alternator current (Hall effect sensor) - Channel 2: Engine RPM (magnetic pickup) - Channel 3: Thermistor temperature
Hardware Configuration¶
// ADS1115 initialization
ADS1115_lite adc(ADS1115_DEFAULT_ADDRESS);
adc.setGain(ADS1115_REG_CONFIG_PGA_6_144V); // ±6.144V range
adc.setSampleRate(ADS1115_REG_CONFIG_DR_64SPS); // 64 samples/second (15.6ms)
Gain Selection: ±6.144V range provides maximum resolution for 0-5V sensor inputs Sample Rate: 64 SPS balances conversion time (15.6ms) with noise rejection
State Machine Implementation¶
The ADS1115 uses a non-blocking state machine to cycle through all channels:
enum ADS1115_State {
ADS_IDLE,
ADS_WAITING_FOR_CONVERSION
};
void ReadAnalogInputs() {
switch (adsState) {
case ADS_IDLE:
// Set channel and trigger conversion
adc.setMux(channelConfig[adsCurrentChannel]);
adc.triggerConversion();
adsStartTime = millis();
adsState = ADS_WAITING_FOR_CONVERSION;
break;
case ADS_WAITING_FOR_CONVERSION:
if (millis() - adsStartTime >= ADSConversionDelay) {
Raw = adc.getConversion();
processChannelData(adsCurrentChannel, Raw);
adsCurrentChannel = (adsCurrentChannel + 1) % 4;
adsState = ADS_IDLE;
}
break;
}
}
Benefits: - Non-blocking operation prevents main loop delays - Complete 4-channel cycle every 80ms (20ms per channel) - Automatic channel rotation ensures fresh data
Channel Processing¶
Channel 0: Battery Voltage¶
Channel0V = Raw / 32767.0 * 6.144 / 0.0697674419; // Voltage divider compensation
BatteryV = Channel0V;
if (BatteryV > 5.0 && BatteryV < 70.0) { // Sanity check
MARK_FRESH(IDX_BATTERY_V);
}
Channel 1: Alternator Current¶
Channel1V = Raw / 32767.0 * 6.144 * 2; // 2:1 voltage divider
MeasuredAmps = (Channel1V - 2.5) * 100; // Hall sensor: 2.5V = 0A
if (InvertAltAmps == 1) MeasuredAmps *= -1; // Polarity correction
MeasuredAmps -= AlternatorCOffset; // User calibration offset
if (AutoAltCurrentZero == 1) {
MeasuredAmps -= DynamicAltCurrentZero; // Auto-zero correction
}
Channel 2: Engine RPM¶
Channel2V = Raw / 32767.0 * 2 * 6.144 * RPMScalingFactor;
RPM = Channel2V;
if (RPM < 100) RPM = 0; // Eliminate noise at idle
Channel 3: Thermistor Temperature¶
Channel3V = Raw / 32767.0 * 6.144 * 833 * 2; // Signal conditioning
temperatureThermistor = thermistorTempC(Channel3V);
if (temperatureThermistor > 500) temperatureThermistor = -99; // Error indication
Thermistor Calculation¶
int thermistorTempC(float V_thermistor) {
float R_thermistor = R_fixed * (V_thermistor / (5.0 - V_thermistor));
float T0_K = T0_C + 273.15;
float tempK = 1.0 / ((1.0 / T0_K) + (1.0 / Beta) * log(R_thermistor / R0));
return (int)(tempK - 273.15);
}
INA228 High-Precision Battery Monitor¶
Purpose¶
The INA228 provides high-accuracy battery voltage and current measurement with hardware overvoltage protection: - Shunt voltage: ±163.84mV range with 40nV resolution - Bus voltage: 0-85V range with 3.125mV resolution - Hardware protection: Configurable overvoltage threshold
Configuration¶
INA.setMode(11); // Continuous shunt and bus voltage
INA.setAverage(4); // 4-sample averaging
INA.setBusVoltageConversionTime(7); // 4120µs conversion time
INA.setShuntVoltageConversionTime(7); // 4120µs conversion time
// Hardware overvoltage protection
uint16_t thresholdLSB = (uint16_t)(VoltageHardwareLimit / 0.003125);
INA.setBusOvervoltageTH(thresholdLSB);
INA.setDiagnoseAlertBit(INA228_DIAG_BUS_OVER_LIMIT);
Update Rate: ~529ms total conversion time with averaging and conversion settings
Data Processing¶
void ReadINA228() {
if (INADisconnected == 0) {
IBV = INA.getBusVoltage(); // Battery voltage
ShuntVoltage_mV = INA.getShuntVoltage() / 1000; // Shunt voltage
if (!isnan(IBV) && IBV > 5.0 && IBV < 70.0) {
Bcur = ShuntVoltage_mV * 1000.0f / ShuntResistanceMicroOhm; // Current calculation
Bcur = Bcur + BatteryCOffset; // User calibration
if (InvertBattAmps == 1) Bcur = -Bcur; // Polarity correction
// Dynamic gain correction (Learning mode)
if (AutoShuntGainCorrection == 1 && AmpSrc == 1) {
Bcur = Bcur * DynamicShuntGainFactor;
}
MARK_FRESH(IDX_IBV);
MARK_FRESH(IDX_BCUR);
}
}
}
Hardware Overvoltage Protection¶
// Check hardware alert status
uint16_t alertStatus = readINA228AlertRegister(INA.getAddress());
if (!inaOvervoltageLatched && (alertStatus & 0x0080)) {
inaOvervoltageLatched = true;
inaOvervoltageTime = millis();
queueConsoleMessage("INA228 hardware overvoltage detected! Field disabled until corrected");
}
// Auto-clear after 10 seconds if condition resolved
if (inaOvervoltageLatched && millis() - inaOvervoltageTime >= 10000) {
clearINA228AlertLatch(INA.getAddress());
if (!(readINA228AlertRegister(INA.getAddress()) & 0x0080)) {
inaOvervoltageLatched = false;
}
}
Benefits: - Hardware-level protection independent of software - Automatic recovery when overvoltage condition clears - Prevents alternator runaway scenarios
OneWire Temperature Sensors¶
Purpose¶
DS18B20 digital temperature sensors provide alternator temperature monitoring with high accuracy and immunity to electrical noise.
Hardware Configuration¶
#define ONE_WIRE_BUS 13
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
void initializeOneWire() {
sensors.begin();
sensors.setResolution(12); // 12-bit resolution (0.0625°C)
sensors.getAddress(tempDeviceAddress, 0); // Get first sensor address
}
FreeRTOS Task Implementation¶
Temperature reading runs in a separate task to prevent main loop blocking:
void TempTask(void *parameter) {
esp_task_wdt_add(NULL); // Add to watchdog monitoring
for (;;) {
if (millis() - lastTempRead < 10000) { // 10-second update interval
vTaskDelay(pdMS_TO_TICKS(1000));
esp_task_wdt_reset();
continue;
}
// Trigger conversion
sensors.requestTemperaturesByAddress(tempDeviceAddress);
// Wait for conversion (5 seconds with watchdog feeding)
for (int i = 0; i < 25; i++) {
vTaskDelay(pdMS_TO_TICKS(200));
esp_task_wdt_reset();
}
// Read result
if (sensors.readScratchPad(tempDeviceAddress, scratchPad)) {
int16_t raw = (scratchPad[1] << 8) | scratchPad[0];
float tempF = (raw / 16.0) * 1.8 + 32.0;
if (tempF > -50 && tempF < 300) { // Sanity check
AlternatorTemperatureF = tempF;
if (AlternatorTemperatureF > MaxAlternatorTemperatureF) {
MaxAlternatorTemperatureF = AlternatorTemperatureF;
}
MARK_FRESH(IDX_ALTERNATOR_TEMP);
}
}
lastTempRead = millis();
}
}
Task Benefits: - Non-blocking main loop operation - Independent watchdog monitoring - Automatic error handling and recovery
Data Validation and Quality Control¶
Sanity Checking¶
Each sensor reading undergoes validation before use:
// Example: Battery voltage validation
if (BatteryV > 5.0 && BatteryV < 70.0 && !isnan(BatteryV)) {
// Valid reading - mark fresh and use
MARK_FRESH(IDX_BATTERY_V);
} else {
// Invalid reading - do not mark fresh, data goes stale
Serial.println("Invalid battery voltage reading: " + String(BatteryV));
}
Validation Criteria: - Range limits: Physical limits for each measurement type - NaN detection: Arithmetic error checking - Rate limits: Maximum change rate between readings - Cross-validation: Compare redundant sensors when available
Data Freshness Tracking¶
enum DataIndex {
IDX_ALTERNATOR_TEMP = 0, // OneWire temperature
IDX_BATTERY_V, // ADS1115 battery voltage
IDX_MEASURED_AMPS, // ADS1115 alternator current
IDX_IBV, // INA228 battery voltage
IDX_BCUR, // INA228 battery current
IDX_RPM, // ADS1115 engine RPM
IDX_VICTRON_VOLTAGE, // VE.Direct voltage
IDX_VICTRON_CURRENT, // VE.Direct current
// ... additional indices
MAX_DATA_INDICES = 17
};
unsigned long dataTimestamps[MAX_DATA_INDICES];
const unsigned long DATA_TIMEOUT = 10000; // 10 seconds
#define MARK_FRESH(index) dataTimestamps[index] = millis()
#define IS_STALE(index) (millis() - dataTimestamps[index] > DATA_TIMEOUT)
Benefits: - Web interface shows stale data indicators - Prevents control decisions based on old readings - Enables automatic sensor fallback strategies - Facilitates troubleshooting sensor failures
Sensor Fallback Strategy¶
float getBatteryVoltage() {
switch (BatteryVoltageSource) {
case 0: // INA228 (preferred)
if (!IS_STALE(IDX_IBV) && IBV > 8.0 && IBV < 70.0) {
return IBV;
}
// Fall through to ADS1115
case 1: // ADS1115 (backup)
if (!IS_STALE(IDX_BATTERY_V) && BatteryV > 8.0 && BatteryV < 70.0) {
return BatteryV;
}
// Fall through to VE.Direct
case 2: // VE.Direct (tertiary)
if (!IS_STALE(IDX_VICTRON_VOLTAGE) && VictronVoltage > 8.0) {
return VictronVoltage;
}
break;
}
queueConsoleMessage("No valid battery voltage source available");
return 999; // Error indication
}
Fallback Hierarchy: 1. Primary source: User-configured preference 2. Secondary sources: Automatic fallback to validated alternatives 3. Error handling: Graceful degradation with user notification
Performance Characteristics¶
Update Rates¶
Sensor | Update Rate | Notes |
---|---|---|
ADS1115 | 80ms (4 channels) | Complete cycle every 80ms |
INA228 | 529ms | With averaging and high precision |
OneWire | 10 seconds | Separate task, temperature changes slowly |
NMEA2K | 100ms-2s | Variable based on message type |
VE.Direct | 2 seconds | Victron device dependent |
Accuracy Specifications¶
Parameter | Sensor | Resolution | Accuracy |
---|---|---|---|
Battery Voltage | INA228 | 3.125mV | ±0.1% |
Battery Current | INA228 | 40nV (shunt) | ±0.5% |
Alternator Current | Hall Sensor | ~100mA | ±2% |
Temperature | DS18B20 | 0.0625°C | ±0.5°C |
Engine RPM | Magnetic Pickup | ~1 RPM | ±1% |
Resource Usage¶
- CPU time: ~15% of main loop for sensor reading
- Memory: 68 bytes for data timestamps, ~200 bytes for sensor buffers
- I2C bandwidth: ~2kHz for ADS1115 + INA228 continuous operation
- Task stack: 4KB for temperature task
Error Handling and Recovery¶
Sensor Disconnection Detection¶
// ADS1115 connection check
if (!adc.testConnection()) {
ADS1115Disconnected = 1;
queueConsoleMessage("ADS1115 connection failed");
}
// INA228 connection check
if (!INA.begin()) {
INADisconnected = 1;
queueConsoleMessage("INA228 connection failed");
}
Automatic Recovery¶
- I2C bus reset: Automatic recovery from bus lockup conditions
- Sensor reinitialization: Periodic connection testing and recovery
- Graceful degradation: Continue operation with available sensors
- User notification: Console messages for failed sensors
Data Integrity¶
- Checksums: Protocol-level validation for digital communications
- Range checking: Physical limit validation for all measurements
- Trend analysis: Rate-of-change validation to detect sensor glitches
- Cross-validation: Compare redundant measurements when available
This sensor system provides the reliable, accurate data foundation required for safe alternator control and comprehensive system monitoring.