Add ESP32 alarms system status and debug commands

This commit is contained in:
2026-06-04 00:59:51 -06:00
parent d971afde96
commit 817db651ce
5 changed files with 111 additions and 0 deletions
@@ -0,0 +1,13 @@
#include "alarms.h"
#include "bms.h"
AlarmState alarms;
void updateAlarms() {
alarms.lowSoc = bmsData.valid && bmsData.soc < 20;
alarms.criticalSoc = bmsData.valid && bmsData.soc < 10;
alarms.lowVoltage = bmsData.valid && bmsData.voltage < 12.0;
alarms.highBatteryTemp = bmsData.valid && bmsData.temperatureF > 120.0;
alarms.cellImbalance = bmsData.cellsValid && bmsData.cellDeltaMv > 20;
alarms.bmsDisconnected = !bmsData.connected;
}
+14
View File
@@ -0,0 +1,14 @@
#pragma once
struct AlarmState {
bool lowSoc = false;
bool criticalSoc = false;
bool lowVoltage = false;
bool highBatteryTemp = false;
bool cellImbalance = false;
bool bmsDisconnected = false;
};
extern AlarmState alarms;
void updateAlarms();
+15
View File
@@ -64,9 +64,19 @@ static void handleNotify(
static bool connectBms() { static bool connectBms() {
if (client && client->isConnected()) { if (client && client->isConnected()) {
bmsData.connected = true;
return true; return true;
} }
if (client && !client->isConnected()) {
Serial.println("BMS: Disconnected, resetting client");
NimBLEDevice::deleteClient(client);
client = nullptr;
notifyChar = nullptr;
writeChar = nullptr;
bmsData.connected = false;
}
Serial.println("BMS: Connecting..."); Serial.println("BMS: Connecting...");
NimBLEAddress address(std::string(BMS_ADDRESS), BLE_ADDR_PUBLIC); NimBLEAddress address(std::string(BMS_ADDRESS), BLE_ADDR_PUBLIC);
@@ -76,6 +86,8 @@ static bool connectBms() {
if (!client->connect(address)) { if (!client->connect(address)) {
Serial.println("BMS: Connect failed"); Serial.println("BMS: Connect failed");
bmsData.connected = false; bmsData.connected = false;
NimBLEDevice::deleteClient(client);
client = nullptr;
return false; return false;
} }
@@ -116,6 +128,9 @@ static bool requestPacket(uint8_t* command, size_t commandLength) {
if (!ok) { if (!ok) {
Serial.println("BMS: Write failed"); Serial.println("BMS: Write failed");
bmsData.connected = false; bmsData.connected = false;
if (client) {
client->disconnect();
}
return false; return false;
} }
@@ -0,0 +1,4 @@
#pragma once
#define FIRMWARE_VERSION "0.3.0"
#define FIRMWARE_NAME "xterra-controller"
@@ -7,6 +7,8 @@
#include "relays.h" #include "relays.h"
#include "sensors.h" #include "sensors.h"
#include "bms.h" #include "bms.h"
#include "alarms.h"
#include "system_status.h"
WebServer server(80); WebServer server(80);
HardwareSerial DashboardSerial(2); HardwareSerial DashboardSerial(2);
@@ -56,6 +58,7 @@ void buildStatusDocument(JsonDocument& doc) {
battery["cell_min_voltage"] = bmsData.cellMinVoltage; battery["cell_min_voltage"] = bmsData.cellMinVoltage;
battery["cell_max_voltage"] = bmsData.cellMaxVoltage; battery["cell_max_voltage"] = bmsData.cellMaxVoltage;
battery["cell_delta_mv"] = bmsData.cellDeltaMv; battery["cell_delta_mv"] = bmsData.cellDeltaMv;
battery["cells_valid"] = bmsData.cellsValid;
} else { } else {
battery["source"] = "placeholder"; battery["source"] = "placeholder";
battery["connected"] = false; battery["connected"] = false;
@@ -113,6 +116,21 @@ void buildStatusDocument(JsonDocument& doc) {
JsonObject network = doc.createNestedObject("network"); JsonObject network = doc.createNestedObject("network");
network["wifi_enabled"] = true; network["wifi_enabled"] = true;
network["uart_connected"] = true; network["uart_connected"] = true;
JsonObject alarmObj = doc.createNestedObject("alarms");
alarmObj["low_soc"] = alarms.lowSoc;
alarmObj["critical_soc"] = alarms.criticalSoc;
alarmObj["low_voltage"] = alarms.lowVoltage;
alarmObj["high_battery_temp"] = alarms.highBatteryTemp;
alarmObj["cell_imbalance"] = alarms.cellImbalance;
alarmObj["bms_disconnected"] = alarms.bmsDisconnected;
JsonObject system = doc.createNestedObject("system");
system["firmware_name"] = FIRMWARE_NAME;
system["firmware_version"] = FIRMWARE_VERSION;
system["build_date"] = __DATE__;
system["build_time"] = __TIME__;
system["uptime_seconds"] = millis() / 1000;
} }
void sendStatus(Stream& output, bool pretty = false) { void sendStatus(Stream& output, bool pretty = false) {
@@ -214,6 +232,51 @@ void pollDashboardUart() {
} }
} }
void handleDebugSerial() {
if (!Serial.available()) {
return;
}
String command = Serial.readStringUntil('\n');
command.trim();
if (command == "status") {
sendStatus(Serial, true);
return;
}
if (command == "relay starlink on") {
setRelayByName("starlink", true);
Serial.println("OK starlink on");
return;
}
if (command == "relay starlink off") {
setRelayByName("starlink", false);
Serial.println("OK starlink off");
return;
}
if (command == "relay fridge on") {
setRelayByName("fridge", true);
Serial.println("OK fridge on");
return;
}
if (command == "relay fridge off") {
setRelayByName("fridge", false);
Serial.println("OK fridge off");
return;
}
if (command.length() > 0) {
Serial.print("Unknown command: ");
Serial.println(command);
Serial.println("Commands: status, relay starlink on/off, relay fridge on/off");
}
}
void handleStatus() { void handleStatus() {
DynamicJsonDocument doc(2048); DynamicJsonDocument doc(2048);
buildStatusDocument(doc); buildStatusDocument(doc);
@@ -305,7 +368,9 @@ void setup() {
void loop() { void loop() {
server.handleClient(); server.handleClient();
handleDebugSerial();
updateBms(); updateBms();
updateAlarms();
pollDashboardUart(); pollDashboardUart();
static unsigned long lastSensorUpdate = 0; static unsigned long lastSensorUpdate = 0;