firmware: scaffold optional cargo OLED display

This commit is contained in:
2026-06-08 08:45:24 -06:00
parent bc56514489
commit 46d52052e3
4 changed files with 315 additions and 0 deletions
@@ -3,6 +3,15 @@
#include <Preferences.h>
#include <esp_wifi.h>
#include <esp_system.h>
#define OLED_ENABLED 0
#if OLED_ENABLED
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#endif
#include <ArduinoJson.h>
#include "config.h"
@@ -1163,6 +1172,231 @@ void handleUpdateApConfig() {
server.send(200, "application/json", output);
}
#if OLED_ENABLED
const int OLED_SCREEN_WIDTH = 128;
const int OLED_SCREEN_HEIGHT = 64;
const int OLED_RESET_PIN = -1;
const uint8_t OLED_I2C_ADDR = 0x3C;
// Default ESP32 I2C pins. Change these if the cargo controller wiring uses a different I2C bus.
const int OLED_I2C_SDA = 21;
const int OLED_I2C_SCL = 22;
// Optional future setup/status button. Leave disabled until hardware is wired.
const int OLED_BUTTON_PIN = -1;
Adafruit_SSD1306 oled(OLED_SCREEN_WIDTH, OLED_SCREEN_HEIGHT, &Wire, OLED_RESET_PIN);
bool oledReady = false;
unsigned long lastOledUpdateMs = 0;
unsigned long lastOledPageChangeMs = 0;
uint8_t oledPage = 0;
void oledClear(const char* title) {
oled.clearDisplay();
oled.setTextSize(1);
oled.setTextColor(SSD1306_WHITE);
oled.setCursor(0, 0);
oled.println(title);
oled.drawLine(0, 10, OLED_SCREEN_WIDTH - 1, 10, SSD1306_WHITE);
}
void oledPrintTrimmed(const String& value, int maxChars = 21) {
if (value.length() <= maxChars) {
oled.println(value);
} else {
oled.println(value.substring(0, maxChars));
}
}
void oledShowBoot() {
if (!oledReady) return;
oledClear("Overland Controller");
oled.setCursor(0, 18);
oled.println("Booting...");
oled.println();
oled.println("Cargo Controller");
oled.display();
}
void oledShowSetupPage() {
if (!oledReady) return;
oledClear("Setup / AP");
oled.setCursor(0, 14);
oled.print("SSID: ");
oledPrintTrimmed(apSsid, 15);
oled.print("PASS: ");
oledPrintTrimmed(apPassword, 15);
oled.print("IP: ");
oled.println(WiFi.softAPIP());
oled.display();
}
void oledShowBatteryPage() {
if (!oledReady) return;
oledClear("Battery");
oled.setCursor(0, 16);
if (bmsData.valid) {
oled.print("SOC: ");
oled.print(bmsData.soc);
oled.println("%");
oled.print("V: ");
oled.print(bmsData.voltage, 2);
oled.println("V");
oled.print("A: ");
oled.print(bmsData.current, 1);
oled.println("A");
oled.print("Temp: ");
oled.print(bmsData.temperatureF, 1);
oled.println("F");
} else {
oled.println("BMS unavailable");
oled.println();
oled.println("Check BLE/config");
}
oled.display();
}
void oledShowRelayPage() {
if (!oledReady) return;
oledClear("Relays");
oled.setCursor(0, 16);
oled.print(appConfig.relays[0].name);
oled.print(": ");
oled.println(relays.relay1 ? "ON" : "OFF");
oled.print(appConfig.relays[1].name);
oled.print(": ");
oled.println(relays.relay2 ? "ON" : "OFF");
oled.display();
}
void oledShowNetworkPage() {
if (!oledReady) return;
oledClear("Network");
oled.setCursor(0, 14);
oled.print("AP: ");
oledPrintTrimmed(apSsid, 17);
oled.print("AP IP: ");
oled.println(WiFi.softAPIP());
oled.print("STA: ");
if (WiFi.status() == WL_CONNECTED) {
oled.println(WiFi.localIP());
} else {
oled.println("not connected");
}
oled.display();
}
void oledShowAlarmPage() {
if (!oledReady) return;
oledClear("Alarms");
oled.setCursor(0, 14);
bool any = false;
if (alarms.lowSoc) {
oled.println("LOW SOC");
any = true;
}
if (alarms.criticalSoc) {
oled.println("CRITICAL SOC");
any = true;
}
if (alarms.bmsDisconnected) {
oled.println("BMS DISCONNECTED");
any = true;
}
if (alarms.highBatteryTemp) {
oled.println("HIGH BATT TEMP");
any = true;
}
if (alarms.cellImbalance) {
oled.println("CELL IMBALANCE");
any = true;
}
if (!any) {
oled.println("No active alarms");
}
oled.display();
}
void oledRenderPage() {
switch (oledPage) {
case 0:
oledShowBatteryPage();
break;
case 1:
oledShowRelayPage();
break;
case 2:
oledShowNetworkPage();
break;
case 3:
oledShowAlarmPage();
break;
default:
oledPage = 0;
oledShowBatteryPage();
break;
}
}
void oledBegin() {
Wire.begin(OLED_I2C_SDA, OLED_I2C_SCL);
oledReady = oled.begin(SSD1306_SWITCHCAPVCC, OLED_I2C_ADDR);
if (!oledReady) {
Serial.println("OLED not detected");
return;
}
Serial.println("OLED initialized");
oledShowBoot();
}
void oledLoop() {
if (!oledReady) return;
unsigned long now = millis();
if (now - lastOledPageChangeMs > 5000) {
oledPage = (oledPage + 1) % 4;
lastOledPageChangeMs = now;
}
if (now - lastOledUpdateMs > 1000) {
oledRenderPage();
lastOledUpdateMs = now;
}
}
#else
void oledBegin() {}
void oledShowSetupPage() {}
void oledLoop() {}
#endif
int findNextWifiIndexByPriority(bool tried[]) {
int bestIndex = -1;
int bestPriority = 1000000;
@@ -3139,6 +3373,8 @@ void setup() {
Serial.println("Overland Controller Booting");
Serial.println("==================================");
oledBegin();
loadConfig();
printConfig();
@@ -3153,6 +3389,7 @@ void setup() {
WiFi.mode(WIFI_AP_STA);
startAccessPoint();
oledShowSetupPage();
connectStaWifi();
@@ -3235,6 +3472,7 @@ void loop() {
updateBms();
updateAlarms();
pollDashboardUart();
oledLoop();
static unsigned long lastSensorUpdate = 0;