diff --git a/Software/Software.ino b/Software/Software.ino index 68715676..142c7f5b 100644 --- a/Software/Software.ino +++ b/Software/Software.ino @@ -2,9 +2,11 @@ /* Only change battery specific settings in "USER_SETTINGS.h" */ #include +#include #include "HardwareSerial.h" #include "USER_SETTINGS.h" #include "src/battery/BATTERIES.h" +#include "src/charger/CHARGERS.h" #include "src/devboard/config.h" #include "src/inverter/INVERTERS.h" #include "src/lib/adafruit-Adafruit_NeoPixel/Adafruit_NeoPixel.h" @@ -18,6 +20,8 @@ #include "src/devboard/webserver/webserver.h" #endif +Preferences settings; // Store user settings + // Interval settings int intervalUpdateValues = 4800; // Interval at which to update inverter values / Modbus registers const int interval10 = 10; // Interval for 10ms tasks @@ -68,6 +72,21 @@ uint16_t cell_max_voltage = 3700; // Stores the highest cell voltage value uint16_t cell_min_voltage = 3700; // Stores the minimum cell voltage value in the system bool LFP_Chemistry = false; +// Common charger parameters +volatile float charger_setpoint_HV_VDC = 0.0f; +volatile float charger_setpoint_HV_IDC = 0.0f; +volatile float charger_setpoint_HV_IDC_END = 0.0f; +bool charger_HV_enabled = false; +bool charger_aux12V_enabled = false; + +// Common charger statistics, instantaneous values +float charger_stat_HVcur = 0; +float charger_stat_HVvol = 0; +float charger_stat_ACcur = 0; +float charger_stat_ACvol = 0; +float charger_stat_LVcur = 0; +float charger_stat_LVvol = 0; + // LED parameters Adafruit_NeoPixel pixels(1, WS2812_PIN, NEO_GRB + NEO_KHZ800); static uint8_t brightness = 0; @@ -102,6 +121,8 @@ bool inverterAllowsContactorClosing = true; void setup() { init_serial(); + init_stored_settings(); + #ifdef WEBSERVER init_webserver(); #endif @@ -172,6 +193,37 @@ void init_serial() { Serial.println("__ OK __"); } +void init_stored_settings() { + settings.begin("batterySettings", false); + +#ifndef LOAD_SAVED_SETTINGS_ON_BOOT + settings.clear(); // If this clear function is executed, no settings will be read from storage +#endif + + static uint16_t temp = 0; + temp = settings.getUInt("BATTERY_WH_MAX", false); + if (temp != 0) { + BATTERY_WH_MAX = temp; + } + temp = settings.getUInt("MAXPERCENTAGE", false); + if (temp != 0) { + MAXPERCENTAGE = temp; + } + temp = settings.getUInt("MINPERCENTAGE", false); + if (temp != 0) { + MINPERCENTAGE = temp; + } + temp = settings.getUInt("MAXCHARGEAMP", false); + if (temp != 0) { + MAXCHARGEAMP = temp; + } + temp = settings.getUInt("MAXDISCHARGEAMP", false); + if (temp != 0) { + MAXDISCHARGEAMP = temp; + } + settings.end(); +} + void init_CAN() { // CAN pins pinMode(CAN_SE_PIN, OUTPUT); @@ -363,6 +415,9 @@ void receive_can() { // This section checks if we have a complete CAN message i #endif #ifdef SMA_CAN receive_can_sma(rx_frame); +#endif +#ifdef CHEVYVOLT_CHARGER + receive_can_chevyvolt_charger(rx_frame); #endif } else { //printf("New extended frame"); @@ -422,6 +477,9 @@ void send_can() { #ifdef TEST_FAKE_BATTERY send_can_test_battery(); #endif +#ifdef CHEVYVOLT_CHARGER + send_can_chevyvolt_charger(); +#endif } #ifdef DUAL_CAN @@ -672,3 +730,13 @@ void init_serialDataLink() { Serial2.begin(9600, SERIAL_8N1, RS485_RX_PIN, RS485_TX_PIN); #endif } + +void storeSettings() { + settings.begin("batterySettings", false); + settings.putUInt("BATTERY_WH_MAX", BATTERY_WH_MAX); + settings.putUInt("MAXPERCENTAGE", MAXPERCENTAGE); + settings.putUInt("MINPERCENTAGE", MINPERCENTAGE); + settings.putUInt("MAXCHARGEAMP", MAXCHARGEAMP); + settings.putUInt("MAXDISCHARGEAMP", MAXDISCHARGEAMP); + settings.end(); +} diff --git a/Software/USER_SETTINGS.cpp b/Software/USER_SETTINGS.cpp index df69f486..00bd4731 100644 --- a/Software/USER_SETTINGS.cpp +++ b/Software/USER_SETTINGS.cpp @@ -21,6 +21,13 @@ volatile uint16_t MAXDISCHARGEAMP = const char* mqtt_user = "REDACTED"; const char* mqtt_password = "REDACTED"; #endif // USE_MQTT +/* Charger settings */ +volatile float CHARGER_SET_HV = 384; // Reasonably appropriate 4.0v per cell charging of a 96s pack +volatile float CHARGER_MAX_HV = 420; // Max permissible output (VDC) of charger +volatile float CHARGER_MIN_HV = 200; // Min permissible output (VDC) of charger +volatile float CHARGER_MAX_POWER = 3300; // Max power capable of charger, as a ceiling for validating config +volatile float CHARGER_MAX_A = 11.5; // Max current output (amps) of charger +volatile float CHARGER_END_A = 1.0; // Current at which charging is considered complete #ifdef WEBSERVER volatile uint8_t AccessPointEnabled = @@ -29,5 +36,5 @@ const char* ssid = "REPLACE_WITH_YOUR_SSID"; // Maximum of 63 character const char* password = "REPLACE_WITH_YOUR_PASSWORD"; // Minimum of 8 characters; const char* ssidAP = "Battery Emulator"; // Maximum of 63 characters; const char* passwordAP = "123456789"; // Minimum of 8 characters; set to NULL if you want the access point to be open -const char* versionNumber = "4.4.0"; // The current software version, shown on webserver +const char* versionNumber = "4.5.0"; // The current software version, shown on webserver #endif diff --git a/Software/USER_SETTINGS.h b/Software/USER_SETTINGS.h index 5428d7ad..d24e5001 100644 --- a/Software/USER_SETTINGS.h +++ b/Software/USER_SETTINGS.h @@ -38,6 +38,10 @@ //#define SERIAL_LINK_TRANSMITTER //Enable this line to send battery data over RS485 pins to another Lilygo (This LilyGo interfaces with battery) //#define WEBSERVER //Enable this line to enable WiFi, and to run the webserver. See USER_SETTINGS.cpp for the Wifi settings. //#define MQTT // Enable this line to enable MQTT +//#define LOAD_SAVED_SETTINGS_ON_BOOT //Enable this line to read settings stored via the webserver on boot + +/* Select charger used (Optional) */ +//#define CHEVYVOLT_CHARGER //Enable this line to control a Chevrolet Volt charger connected to battery - for example, when generator charging or using an inverter without a charging function. /* Battery limits: These are set in the USER_SETTINGS.cpp file, or later on via the Webserver */ extern volatile uint16_t BATTERY_WH_MAX; @@ -46,4 +50,18 @@ extern volatile uint16_t MINPERCENTAGE; extern volatile uint16_t MAXCHARGEAMP; extern volatile uint16_t MAXDISCHARGEAMP; extern volatile uint8_t AccessPointEnabled; + +/* Charger limits: Set in the USER_SETTINGS.cpp or later in the webserver */ +extern volatile float charger_setpoint_HV_VDC; +extern volatile float charger_setpoint_HV_IDC; +extern volatile float charger_setpoint_HV_IDC_END; +extern volatile float CHARGER_SET_HV; +extern volatile float CHARGER_MAX_HV; +extern volatile float CHARGER_MIN_HV; +extern volatile float CHARGER_MAX_POWER; +extern volatile float CHARGER_MAX_A; +extern volatile float CHARGER_END_A; +extern bool charger_HV_enabled; +extern bool charger_aux12V_enabled; + #endif diff --git a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp index a6d620d6..35d54535 100644 --- a/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp +++ b/Software/src/battery/TESLA-MODEL-3-BATTERY.cpp @@ -32,8 +32,8 @@ static uint16_t volts = 0; // V static int16_t amps = 0; // A static int16_t power = 0; // W static uint16_t raw_amps = 0; // A -static int16_t max_temp = 6; // C* -static int16_t min_temp = 5; // C* +static int16_t max_temp = 0; // C* +static int16_t min_temp = 0; // C* static uint16_t energy_buffer = 0; static uint16_t energy_to_charge_complete = 0; static uint16_t expected_energy_remaining = 0; @@ -209,10 +209,8 @@ void update_values_tesla_model_3_battery() { //This function maps all the value power = ((volts / 10) * amps); stat_batt_power = convert2unsignedInt16(power); - min_temp = (min_temp * 10); temperature_min = convert2unsignedInt16(min_temp); - max_temp = (max_temp * 10); temperature_max = convert2unsignedInt16(max_temp); cell_max_voltage = cell_max_v; @@ -443,11 +441,8 @@ void receive_can_tesla_model_3_battery(CAN_frame_t rx_frame) { } if (mux == 0) //Temperature sensors { - temp = rx_frame.data.u8[2]; - max_temp = (temp * 0.5) - 40; //in celcius, Example 24 - - temp = rx_frame.data.u8[3]; - min_temp = (temp * 0.5) - 40; //in celcius , Example 24 + max_temp = (rx_frame.data.u8[2] * 5) - 400; //Temperature values have 40.0*C offset, 0.5*C /bit + min_temp = (rx_frame.data.u8[3] * 5) - 400; //Multiply by 5 and remove offset to get C+1 (0x61*5=485-400=8.5*C) } break; case 0x2d2: diff --git a/Software/src/charger/CHARGERS.h b/Software/src/charger/CHARGERS.h new file mode 100644 index 00000000..73bae3a1 --- /dev/null +++ b/Software/src/charger/CHARGERS.h @@ -0,0 +1,8 @@ +#ifndef CHARGERS_H +#define CHARGERS_H + +#ifdef CHEVYVOLT_CHARGER +#include "chevyvolt.h" +#endif + +#endif diff --git a/Software/src/charger/chevyvolt.cpp b/Software/src/charger/chevyvolt.cpp new file mode 100644 index 00000000..a44e59b2 --- /dev/null +++ b/Software/src/charger/chevyvolt.cpp @@ -0,0 +1,198 @@ +#include "chevyvolt.h" +#include "../lib/miwagner-ESP32-Arduino-CAN/CAN_config.h" +#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" + +/* This implements Chevy Volt / Ampera charger support (2011-2015 model years). + * + * This code is intended to facilitate battery charging while repurposing inverters + * that lack embedded charging features, to facilitate standalone charging, etc. + * + * Key influence and inspiration informed by prior work by those such as: + * Damien Maguire (evmbw.org, github.com/damienmaguire/AmperaCharger) + * Tom deBree, Arber Kramer, Colin Kidder, EVTV, etc + * Various implementation details aided by openinverter forum discussion/CAN logs + * + * It is very likely that Lear charger support could be added to this with minimal effort + * (similar hardware, different firmware and CAN messages). + * + * 2024 smaresca + */ + +/* CAN cycles and timers */ +static const int interval30ms = 30; // 30ms cycle for keepalive frames +static const int interval200ms = 200; // 200ms cycle for commanding I/V targets +static const int interval5000ms = 5000; // 5s status printout to serial +static unsigned long previousMillis30ms = 0; +static unsigned long previousMillis200ms = 0; +static unsigned long previousMillis5000ms = 0; + +/* voltage and current settings. Validation performed to set ceiling of 3300w vol*cur */ +extern volatile float charger_setpoint_HV_VDC; +extern volatile float charger_setpoint_HV_IDC; +extern volatile float charger_setpoint_HV_IDC_END; +extern bool charger_HV_enabled; +extern bool charger_aux12V_enabled; + +extern float charger_stat_HVcur; +extern float charger_stat_HVvol; +extern float charger_stat_ACcur; +extern float charger_stat_ACvol; +extern float charger_stat_LVcur; +extern float charger_stat_LVvol; + +enum CHARGER_MODES : uint8_t { MODE_DISABLED = 0, MODE_LV, MODE_HV, MODE_HVLV }; + +//Actual content messages +static CAN_frame_t charger_keepalive_frame = {.FIR = {.B = + { + //one byte only, indicating enabled or disabled + .DLC = 1, + .FF = CAN_frame_std, + }}, + .MsgID = 0x30E, + .data = {MODE_DISABLED}}; + +static CAN_frame_t charger_set_targets = {.FIR = {.B = + { + .DLC = 4, + .FF = CAN_frame_std, + }}, + .MsgID = 0x304, + + // data[0] is a static value, meaning unknown + .data = {0x40, 0x00, 0x00, 0x00}}; + +/* We are mostly sending out not receiving */ +void receive_can_chevyvolt_charger(CAN_frame_t rx_frame) { + uint16_t charger_stat_HVcur_temp = 0; + uint16_t charger_stat_HVvol_temp = 0; + uint16_t charger_stat_LVcur_temp = 0; + uint16_t charger_stat_LVvol_temp = 0; + uint16_t charger_stat_ACcur_temp = 0; + uint16_t charger_stat_ACvol_temp = 0; + + switch (rx_frame.MsgID) { + //ID 0x212 conveys instantaneous DC charger stats + case 0x212: + charger_stat_HVcur_temp = (uint16_t)(rx_frame.data.u8[0] << 8 | rx_frame.data.u8[1]); + charger_stat_HVcur = (float)(charger_stat_HVcur_temp >> 3) * 0.05; + + charger_stat_HVvol_temp = (uint16_t)((((rx_frame.data.u8[1] << 8 | rx_frame.data.u8[2])) >> 1) & 0x3ff); + charger_stat_HVvol = (float)(charger_stat_HVvol_temp) * .5; + + charger_stat_LVcur_temp = (uint16_t)(((rx_frame.data.u8[2] << 8 | rx_frame.data.u8[3]) >> 1) & 0x00ff); + charger_stat_LVcur = (float)(charger_stat_LVcur_temp) * .2; + + charger_stat_LVvol_temp = (uint16_t)(((rx_frame.data.u8[3] << 8 | rx_frame.data.u8[4]) >> 1) & 0x00ff); + charger_stat_LVvol = (float)(charger_stat_LVvol_temp) * .1; + + break; + + //ID 0x30A conveys instantaneous AC charger stats + case 0x30A: + charger_stat_ACcur_temp = (uint16_t)((rx_frame.data.u8[0] << 8 | rx_frame.data.u8[1]) >> 4); + charger_stat_ACcur = (float)(charger_stat_ACcur_temp) * 0.2; + + charger_stat_ACvol_temp = (uint16_t)(((rx_frame.data.u8[1] << 8 | rx_frame.data.u8[2]) >> 4) & 0x00ff); + charger_stat_ACvol = (float)(charger_stat_ACvol_temp) * 2; + + break; + + //ID 0x266, 0x268, and 0x308 are regularly emitted by the charger but content is unknown + // 0x266 and 0x308 are len 5 + // 0x268 may be temperature data (len 8). Could resemble the Lear charger equivalent TODO + case 0x266: + break; + case 0x268: + break; + case 0x308: + break; + default: +#ifdef DEBUG_VIA_USB + Serial.printf("CAN Rcv unknown frame MsgID=%x\n", rx_frame.MsgID); +#endif + break; + } +} + +void send_can_chevyvolt_charger() { + unsigned long currentMillis = millis(); + uint16_t Vol_temp = 0; + + uint16_t setpoint_HV_VDC = floor(charger_setpoint_HV_VDC); + uint16_t setpoint_HV_IDC = floor(charger_setpoint_HV_IDC); + uint16_t setpoint_HV_IDC_END = floor(charger_setpoint_HV_IDC_END); + uint8_t charger_mode = MODE_DISABLED; + + /* Send keepalive with mode every 30ms */ + if (currentMillis - previousMillis30ms >= interval30ms) { + previousMillis30ms = currentMillis; + + if (charger_HV_enabled) { + charger_mode += MODE_HV; + + /* disable HV if end amperage reached + * TODO - integration opportunity with battery/inverter code + if (setpoint_HV_IDC_END > 0 && charger_stat_HVcur > setpoint_HV_IDC_END) { + charger_mode -= MODE_HV; + } + */ + } + + if (charger_aux12V_enabled) + charger_mode += MODE_LV; + + charger_keepalive_frame.data.u8[0] = charger_mode; + + ESP32Can.CANWriteFrame(&charger_keepalive_frame); + } + + /* Send current targets every 200ms */ + if (currentMillis - previousMillis200ms >= interval200ms) { + previousMillis200ms = currentMillis; + + /* These values should be and are validated elsewhere, but adjust if needed + * to stay within limits of hardware and user-supplied settings + */ + if (setpoint_HV_VDC > CHARGER_MAX_HV) { + setpoint_HV_VDC = CHEVYVOLT_MAX_HVDC; + } + + if (setpoint_HV_VDC < CHARGER_MIN_HV && setpoint_HV_VDC > 0) { + setpoint_HV_VDC = CHEVYVOLT_MIN_HVDC; + } + + if (setpoint_HV_IDC > CHARGER_MAX_A) { + setpoint_HV_VDC = CHEVYVOLT_MAX_AMP; + } + + /* if power overcommitted, back down to just below while maintaining voltage target */ + if (setpoint_HV_IDC * setpoint_HV_VDC > CHARGER_MAX_POWER) { + setpoint_HV_IDC = floor(CHARGER_MAX_POWER / setpoint_HV_VDC); + } + + /* current setting */ + charger_set_targets.data.u8[1] = setpoint_HV_IDC * 20; + Vol_temp = setpoint_HV_VDC * 2; + + /* first 2 bits are MSB of the voltage command */ + charger_set_targets.data.u8[2] = highByte(Vol_temp); + + /* LSB of the voltage command. Then MSB LSB is divided by 2 */ + charger_set_targets.data.u8[3] = lowByte(Vol_temp); + + ESP32Can.CANWriteFrame(&charger_set_targets); + } + +#ifdef DEBUG_VIA_USB + /* Serial echo every 5s of charger stats */ + if (currentMillis - previousMillis5000ms >= interval5000ms) { + previousMillis5000ms = currentMillis; + Serial.printf("Charger AC in IAC=%fA VAC=%fV\n", charger_stat_ACcur, charger_stat_ACvol); + Serial.printf("Charger HV out IDC=%fA VDC=%fV\n", charger_stat_HVcur, charger_stat_HVvol); + Serial.printf("Charger LV out IDC=%fA VDC=%fV\n", charger_stat_LVcur, charger_stat_LVvol); + Serial.printf("Charger mode=%s\n", (charger_mode > MODE_DISABLED) ? "Enabled" : "Disabled"); + Serial.printf("Charger HVset=%uV,%uA finishCurrent=%uA\n", setpoint_HV_VDC, setpoint_HV_IDC, setpoint_HV_IDC_END); + } +#endif +} diff --git a/Software/src/charger/chevyvolt.h b/Software/src/charger/chevyvolt.h new file mode 100644 index 00000000..522397e6 --- /dev/null +++ b/Software/src/charger/chevyvolt.h @@ -0,0 +1,21 @@ +#ifndef CHEVYVOLT_H +#define CHEVYVOLT_H +#include +#include "../../USER_SETTINGS.h" +#include "../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h" + +/* Charger hardware limits + * + * Relative to runtime settings, expectations are: + * hw minimum <= setting minimum <= setting maximum <= hw max + */ +#define CHEVYVOLT_MAX_HVDC 420.0 +#define CHEVYVOLT_MIN_HVDC 200.0 +#define CHEVYVOLT_MAX_AMP 11.5 +#define CHEVYVOLT_MAX_POWER 3300 + +void update_values_can_chevyvolt_charger(); +void send_can_chevyvolt_charger(); +void receive_can_chevyvolt_charger(CAN_frame_t rx_frame); + +#endif diff --git a/Software/src/devboard/webserver/webserver.cpp b/Software/src/devboard/webserver/webserver.cpp index 7dbfdc32..fdff8e0f 100644 --- a/Software/src/devboard/webserver/webserver.cpp +++ b/Software/src/devboard/webserver/webserver.cpp @@ -1,4 +1,7 @@ #include "webserver.h" +#include + +Preferences preferences3; // Create AsyncWebServer object on port 80 AsyncWebServer server(80); @@ -65,6 +68,7 @@ void init_webserver() { if (request->hasParam("value")) { String value = request->getParam("value")->value(); BATTERY_WH_MAX = value.toInt(); + storeSettings(); request->send(200, "text/plain", "Updated successfully"); } else { request->send(400, "text/plain", "Bad Request"); @@ -76,6 +80,7 @@ void init_webserver() { if (request->hasParam("value")) { String value = request->getParam("value")->value(); MAXPERCENTAGE = value.toInt() * 10; + storeSettings(); request->send(200, "text/plain", "Updated successfully"); } else { request->send(400, "text/plain", "Bad Request"); @@ -87,6 +92,7 @@ void init_webserver() { if (request->hasParam("value")) { String value = request->getParam("value")->value(); MINPERCENTAGE = value.toInt() * 10; + storeSettings(); request->send(200, "text/plain", "Updated successfully"); } else { request->send(400, "text/plain", "Bad Request"); @@ -98,6 +104,7 @@ void init_webserver() { if (request->hasParam("value")) { String value = request->getParam("value")->value(); MAXCHARGEAMP = value.toInt() * 10; + storeSettings(); request->send(200, "text/plain", "Updated successfully"); } else { request->send(400, "text/plain", "Bad Request"); @@ -109,12 +116,92 @@ void init_webserver() { if (request->hasParam("value")) { String value = request->getParam("value")->value(); MAXDISCHARGEAMP = value.toInt() * 10; + storeSettings(); request->send(200, "text/plain", "Updated successfully"); } else { request->send(400, "text/plain", "Bad Request"); } }); +#ifdef CHEVYVOLT_CHARGER + // Route for editing ChargerTargetV + server.on("/updateChargeSetpointV", HTTP_GET, [](AsyncWebServerRequest* request) { + if (!request->hasParam("value")) { + request->send(400, "text/plain", "Bad Request"); + } + + String value = request->getParam("value")->value(); + float val = value.toFloat(); + + if (!(val <= CHARGER_MAX_HV && val >= CHARGER_MIN_HV)) { + request->send(400, "text/plain", "Bad Request"); + } + + if (!(val * charger_setpoint_HV_IDC <= CHARGER_MAX_POWER)) { + request->send(400, "text/plain", "Bad Request"); + } + + charger_setpoint_HV_VDC = val; + + request->send(200, "text/plain", "Updated successfully"); + }); + + // Route for editing ChargerTargetA + server.on("/updateChargeSetpointA", HTTP_GET, [](AsyncWebServerRequest* request) { + if (!request->hasParam("value")) { + request->send(400, "text/plain", "Bad Request"); + } + + String value = request->getParam("value")->value(); + float val = value.toFloat(); + + if (!(val <= MAXCHARGEAMP && val <= CHARGER_MAX_A)) { + request->send(400, "text/plain", "Bad Request"); + } + + if (!(val * charger_setpoint_HV_VDC <= CHARGER_MAX_POWER)) { + request->send(400, "text/plain", "Bad Request"); + } + + charger_setpoint_HV_IDC = value.toFloat(); + + request->send(200, "text/plain", "Updated successfully"); + }); + + // Route for editing ChargerEndA + server.on("/updateChargeEndA", HTTP_GET, [](AsyncWebServerRequest* request) { + if (request->hasParam("value")) { + String value = request->getParam("value")->value(); + charger_setpoint_HV_IDC_END = value.toFloat(); + request->send(200, "text/plain", "Updated successfully"); + } else { + request->send(400, "text/plain", "Bad Request"); + } + }); + + // Route for enabling/disabling HV charger + server.on("/updateChargerHvEnabled", HTTP_GET, [](AsyncWebServerRequest* request) { + if (request->hasParam("value")) { + String value = request->getParam("value")->value(); + charger_HV_enabled = (bool)value.toInt(); + request->send(200, "text/plain", "Updated successfully"); + } else { + request->send(400, "text/plain", "Bad Request"); + } + }); + + // Route for enabling/disabling aux12v charger + server.on("/updateChargerAux12vEnabled", HTTP_GET, [](AsyncWebServerRequest* request) { + if (request->hasParam("value")) { + String value = request->getParam("value")->value(); + charger_aux12V_enabled = (bool)value.toInt(); + request->send(200, "text/plain", "Updated successfully"); + } else { + request->send(400, "text/plain", "Bad Request"); + } + }); +#endif + // Send a GET request to /update server.on("/debug", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(200, "text/plain", "Debug: all OK."); }); @@ -303,6 +390,15 @@ String processor(const String& var) { content += "Fake battery for testing purposes"; #endif content += ""; + +#ifdef CHEVYVOLT_CHARGER + content += "

Charger protocol: "; +#ifdef CHEVYVOLT_CHARGER + content += "Chevy Volt Gen1 Charger"; +#endif + content += "

"; +#endif + // Close the block content += ""; @@ -396,6 +492,42 @@ String processor(const String& var) { content += ""; } +#ifdef CHEVYVOLT_CHARGER + content += "

Charger HV Enabled: "; + if (charger_HV_enabled) { + content += ""; + } else { + content += ""; + } + content += "

"; + + content += "

Charger Aux12v Enabled: "; + if (charger_aux12V_enabled) { + content += ""; + } else { + content += ""; + } + content += "

"; + float chgPwrDC = static_cast(charger_stat_HVcur * charger_stat_HVvol); + float chgPwrAC = static_cast(charger_stat_ACcur * charger_stat_ACvol); + float chgEff = chgPwrDC / chgPwrAC * 100; + float ACcur = charger_stat_ACcur; + float ACvol = charger_stat_ACvol; + float HVvol = charger_stat_HVvol; + float HVcur = charger_stat_HVcur; + float LVvol = charger_stat_LVvol; + float LVcur = charger_stat_LVcur; + + content += formatPowerValue("Charger Output Power", chgPwrDC, "", 1); + content += "

Charger Efficiency: " + String(chgEff) + "%

"; + content += "

Charger HVDC Output V: " + String(HVvol, 2) + "

"; + content += "

Charger HVDC Output I: " + String(HVcur, 2) + "

"; + content += "

Charger LVDC Output I: " + String(LVcur, 2) + "

"; + content += "

Charger LVDC Output V: " + String(LVvol, 2) + "

"; + content += "

Charger AC Input V: " + String(ACvol, 2) + "VAC

"; + content += "

Charger AC Input I: " + String(ACvol, 2) + "VAC

"; +#endif + // Close the block content += ""; @@ -450,6 +582,28 @@ String settings_processor(const String& var) { " A "; content += "

Max discharge speed: " + String(MAXDISCHARGEAMP / 10.0, 1) + " A

"; +#ifdef CHEVYVOLT_CHARGER + content += "

Charger HVDC Enabled: "; + if (charger_HV_enabled) { + content += ""; + } else { + content += ""; + } + content += "

"; + + content += "

Charger Aux12VDC Enabled: "; + if (charger_aux12V_enabled) { + content += ""; + } else { + content += ""; + } + content += "

"; + + content += "

Charger Voltage Setpoint: " + String(charger_setpoint_HV_VDC, 1) + + " V

"; + content += "

Charger Current Setpoint: " + String(charger_setpoint_HV_IDC, 1) + + " A

"; +#endif content += ""; // Close the block diff --git a/Software/src/devboard/webserver/webserver.h b/Software/src/devboard/webserver/webserver.h index a6f11122..a68974d9 100644 --- a/Software/src/devboard/webserver/webserver.h +++ b/Software/src/devboard/webserver/webserver.h @@ -1,7 +1,7 @@ #ifndef WEBSERVER_H #define WEBSERVER_H -// Load Wi-Fi library +#include #include #include "../../../USER_SETTINGS.h" // Needed for WiFi ssid and password #include "../../lib/ayushsharma82-ElegantOTA/src/ElegantOTA.h" @@ -41,6 +41,14 @@ extern const char* ssidAP; extern const char* passwordAP; extern const char* versionNumber; +// Common charger parameters +extern float charger_stat_HVcur; +extern float charger_stat_HVvol; +extern float charger_stat_ACcur; +extern float charger_stat_ACvol; +extern float charger_stat_LVcur; +extern float charger_stat_LVvol; + /** * @brief Initialization function for the webserver. * @@ -134,4 +142,6 @@ void onOTAEnd(bool success); template String formatPowerValue(String label, T value, String unit, int precision); +extern void storeSettings(); + #endif