mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 18:29:48 +02:00
Merge branch 'main' into feature/DIY-battery-RJXZS-bms
This commit is contained in:
commit
86b999e643
32 changed files with 733 additions and 479 deletions
2
.github/workflows/compile-all-batteries.yml
vendored
2
.github/workflows/compile-all-batteries.yml
vendored
|
@ -45,7 +45,7 @@ jobs:
|
||||||
- RENAULT_KANGOO_BATTERY
|
- RENAULT_KANGOO_BATTERY
|
||||||
- RENAULT_ZOE_GEN1_BATTERY
|
- RENAULT_ZOE_GEN1_BATTERY
|
||||||
- RENAULT_ZOE_GEN2_BATTERY
|
- RENAULT_ZOE_GEN2_BATTERY
|
||||||
- TESLA_MODEL_3_BATTERY
|
- TESLA_MODEL_3Y_BATTERY
|
||||||
- VOLVO_SPA_BATTERY
|
- VOLVO_SPA_BATTERY
|
||||||
- TEST_FAKE_BATTERY
|
- TEST_FAKE_BATTERY
|
||||||
- SERIAL_LINK_RECEIVER
|
- SERIAL_LINK_RECEIVER
|
||||||
|
|
|
@ -47,7 +47,7 @@ jobs:
|
||||||
- RENAULT_KANGOO_BATTERY
|
- RENAULT_KANGOO_BATTERY
|
||||||
- RENAULT_ZOE_GEN1_BATTERY
|
- RENAULT_ZOE_GEN1_BATTERY
|
||||||
- RENAULT_ZOE_GEN2_BATTERY
|
- RENAULT_ZOE_GEN2_BATTERY
|
||||||
- TESLA_MODEL_3_BATTERY
|
- TESLA_MODEL_3Y_BATTERY
|
||||||
- VOLVO_SPA_BATTERY
|
- VOLVO_SPA_BATTERY
|
||||||
- TEST_FAKE_BATTERY
|
- TEST_FAKE_BATTERY
|
||||||
# These are the emulated inverter communication protocols for which the code will be compiled.
|
# These are the emulated inverter communication protocols for which the code will be compiled.
|
||||||
|
|
2
.github/workflows/compile-all-inverters.yml
vendored
2
.github/workflows/compile-all-inverters.yml
vendored
|
@ -39,7 +39,7 @@ jobs:
|
||||||
# - KIA_HYUNDAI_64_BATTERY
|
# - KIA_HYUNDAI_64_BATTERY
|
||||||
- NISSAN_LEAF_BATTERY
|
- NISSAN_LEAF_BATTERY
|
||||||
# - RENAULT_ZOE_BATTERY
|
# - RENAULT_ZOE_BATTERY
|
||||||
# - TESLA_MODEL_3_BATTERY
|
# - TESLA_MODEL_3Y_BATTERY
|
||||||
# These are the emulated inverter communication protocols for which the code will be compiled.
|
# These are the emulated inverter communication protocols for which the code will be compiled.
|
||||||
inverter:
|
inverter:
|
||||||
- BYD_CAN
|
- BYD_CAN
|
||||||
|
|
|
@ -38,7 +38,7 @@
|
||||||
|
|
||||||
Preferences settings; // Store user settings
|
Preferences settings; // Store user settings
|
||||||
// The current software version, shown on webserver
|
// The current software version, shown on webserver
|
||||||
const char* version_number = "7.2.dev";
|
const char* version_number = "7.3.dev";
|
||||||
|
|
||||||
// Interval settings
|
// Interval settings
|
||||||
uint16_t intervalUpdateValues = INTERVAL_5_S; // Interval at which to update inverter values / Modbus registers
|
uint16_t intervalUpdateValues = INTERVAL_5_S; // Interval at which to update inverter values / Modbus registers
|
||||||
|
@ -94,6 +94,8 @@ MyTimer connectivity_task_timer_10s(INTERVAL_10_S);
|
||||||
|
|
||||||
MyTimer loop_task_timer_10s(INTERVAL_10_S);
|
MyTimer loop_task_timer_10s(INTERVAL_10_S);
|
||||||
|
|
||||||
|
MyTimer check_pause_2s(INTERVAL_2_S);
|
||||||
|
|
||||||
// Contactor parameters
|
// Contactor parameters
|
||||||
#ifdef CONTACTOR_CONTROL
|
#ifdef CONTACTOR_CONTROL
|
||||||
enum State { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
|
enum State { DISCONNECTED, PRECHARGE, NEGATIVE, POSITIVE, PRECHARGE_OFF, COMPLETED, SHUTDOWN_REQUESTED };
|
||||||
|
@ -287,6 +289,10 @@ void core_loop(void* task_time_us) {
|
||||||
datalayer.system.status.time_cantx_us = 0;
|
datalayer.system.status.time_cantx_us = 0;
|
||||||
datalayer.system.status.core_task_10s_max_us = 0;
|
datalayer.system.status.core_task_10s_max_us = 0;
|
||||||
}
|
}
|
||||||
|
if (check_pause_2s.elapsed()) {
|
||||||
|
emulator_pause_state_send_CAN_battery();
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
vTaskDelayUntil(&xLastWakeTime, xFrequency);
|
vTaskDelayUntil(&xLastWakeTime, xFrequency);
|
||||||
}
|
}
|
||||||
|
@ -575,14 +581,16 @@ void receive_can_native() { // This section checks if we have a complete CAN me
|
||||||
|
|
||||||
void send_can() {
|
void send_can() {
|
||||||
|
|
||||||
send_can_battery();
|
if (can_send_CAN)
|
||||||
|
send_can_battery();
|
||||||
|
|
||||||
#ifdef CAN_INVERTER_SELECTED
|
#ifdef CAN_INVERTER_SELECTED
|
||||||
send_can_inverter();
|
send_can_inverter();
|
||||||
#endif // CAN_INVERTER_SELECTED
|
#endif // CAN_INVERTER_SELECTED
|
||||||
|
|
||||||
#ifdef CHARGER_SELECTED
|
#ifdef CHARGER_SELECTED
|
||||||
send_can_charger();
|
if (can_send_CAN)
|
||||||
|
send_can_charger();
|
||||||
#endif // CHARGER_SELECTED
|
#endif // CHARGER_SELECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
//#define RENAULT_ZOE_GEN1_BATTERY
|
//#define RENAULT_ZOE_GEN1_BATTERY
|
||||||
//#define RENAULT_ZOE_GEN2_BATTERY
|
//#define RENAULT_ZOE_GEN2_BATTERY
|
||||||
//#define SANTA_FE_PHEV_BATTERY
|
//#define SANTA_FE_PHEV_BATTERY
|
||||||
//#define TESLA_MODEL_3_BATTERY
|
//#define TESLA_MODEL_SX_BATTERY
|
||||||
|
//#define TESLA_MODEL_3Y_BATTERY
|
||||||
//#define VOLVO_SPA_BATTERY
|
//#define VOLVO_SPA_BATTERY
|
||||||
//#define TEST_FAKE_BATTERY
|
//#define TEST_FAKE_BATTERY
|
||||||
//#define DOUBLE_BATTERY //Enable this line if you use two identical batteries at the same time (requires DUAL_CAN setup)
|
//#define DOUBLE_BATTERY //Enable this line if you use two identical batteries at the same time (requires DUAL_CAN setup)
|
||||||
|
|
|
@ -66,8 +66,9 @@
|
||||||
#include "SANTA-FE-PHEV-BATTERY.h"
|
#include "SANTA-FE-PHEV-BATTERY.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
#if defined(TESLA_MODEL_SX_BATTERY) || defined(TESLA_MODEL_3Y_BATTERY)
|
||||||
#include "TESLA-MODEL-3-BATTERY.h"
|
#define TESLA_BATTERY
|
||||||
|
#include "TESLA-BATTERY.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef TEST_FAKE_BATTERY
|
#ifdef TEST_FAKE_BATTERY
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
#ifdef TESLA_BATTERY
|
||||||
#include "../datalayer/datalayer.h"
|
#include "../datalayer/datalayer.h"
|
||||||
#include "../devboard/utils/events.h"
|
#include "../devboard/utils/events.h"
|
||||||
#include "TESLA-MODEL-3-BATTERY.h"
|
#include "TESLA-BATTERY.h"
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
/* Credits: Most of the code comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */
|
/* Credits: Most of the code comes from Per Carlen's bms_comms_tesla_model3.py (https://gitlab.com/pelle8/batt2gen24/) */
|
||||||
|
@ -327,6 +327,8 @@ void update_values_battery() { //This function maps all the values fetched via
|
||||||
|
|
||||||
battery_cell_deviation_mV = (battery_cell_max_v - battery_cell_min_v);
|
battery_cell_deviation_mV = (battery_cell_max_v - battery_cell_min_v);
|
||||||
|
|
||||||
|
#ifdef TESLA_MODEL_3Y_BATTERY
|
||||||
|
// Autodetect algoritm for chemistry on 3/Y packs.
|
||||||
// NCM/A batteries have 96s, LFP has 102-106s
|
// NCM/A batteries have 96s, LFP has 102-106s
|
||||||
// Drawback with this check is that it takes 3-5minutes before all cells have been counted!
|
// Drawback with this check is that it takes 3-5minutes before all cells have been counted!
|
||||||
if (datalayer.battery.info.number_of_cells > 101) {
|
if (datalayer.battery.info.number_of_cells > 101) {
|
||||||
|
@ -335,12 +337,13 @@ void update_values_battery() { //This function maps all the values fetched via
|
||||||
|
|
||||||
//Once cell chemistry is determined, set maximum and minimum total pack voltage safety limits
|
//Once cell chemistry is determined, set maximum and minimum total pack voltage safety limits
|
||||||
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
|
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
|
||||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_LFP;
|
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP;
|
||||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_LFP;
|
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_LFP;
|
||||||
} else { // NCM/A chemistry
|
} else { // NCM/A chemistry
|
||||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_NCMA;
|
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA;
|
||||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_NCMA;
|
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_NCMA;
|
||||||
}
|
}
|
||||||
|
#endif // TESLA_MODEL_3Y_BATTERY
|
||||||
|
|
||||||
//Check if SOC% is plausible
|
//Check if SOC% is plausible
|
||||||
if (datalayer.battery.status.voltage_dV >
|
if (datalayer.battery.status.voltage_dV >
|
||||||
|
@ -906,6 +909,8 @@ void update_values_battery2() { //This function maps all the values fetched via
|
||||||
|
|
||||||
battery2_cell_deviation_mV = (battery2_cell_max_v - battery2_cell_min_v);
|
battery2_cell_deviation_mV = (battery2_cell_max_v - battery2_cell_min_v);
|
||||||
|
|
||||||
|
#ifdef TESLA_MODEL_3Y_BATTERY
|
||||||
|
// Autodetect algoritm for chemistry on 3/Y packs.
|
||||||
// NCM/A batteries have 96s, LFP has 102-106s
|
// NCM/A batteries have 96s, LFP has 102-106s
|
||||||
// Drawback with this check is that it takes 3-5minutes before all cells have been counted!
|
// Drawback with this check is that it takes 3-5minutes before all cells have been counted!
|
||||||
if (datalayer.battery2.info.number_of_cells > 101) {
|
if (datalayer.battery2.info.number_of_cells > 101) {
|
||||||
|
@ -914,13 +919,15 @@ void update_values_battery2() { //This function maps all the values fetched via
|
||||||
|
|
||||||
//Once cell chemistry is determined, set maximum and minimum total pack voltage safety limits
|
//Once cell chemistry is determined, set maximum and minimum total pack voltage safety limits
|
||||||
if (datalayer.battery2.info.chemistry == battery_chemistry_enum::LFP) {
|
if (datalayer.battery2.info.chemistry == battery_chemistry_enum::LFP) {
|
||||||
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_LFP;
|
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP;
|
||||||
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_LFP;
|
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_LFP;
|
||||||
} else { // NCM/A chemistry
|
} else { // NCM/A chemistry
|
||||||
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_NCMA;
|
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA;
|
||||||
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_NCMA;
|
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_NCMA;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif // TESLA_MODEL_3Y_BATTERY
|
||||||
|
|
||||||
//Check if SOC% is plausible
|
//Check if SOC% is plausible
|
||||||
if (datalayer.battery2.status.voltage_dV >
|
if (datalayer.battery2.status.voltage_dV >
|
||||||
(datalayer.battery2.info.max_design_voltage_dV -
|
(datalayer.battery2.info.max_design_voltage_dV -
|
||||||
|
@ -1239,28 +1246,39 @@ void printDebugIfActive(uint8_t symbol, const char* message) {
|
||||||
|
|
||||||
void setup_battery(void) { // Performs one time setup at startup
|
void setup_battery(void) { // Performs one time setup at startup
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.println("Tesla Model 3 battery selected");
|
Serial.println("Tesla Model S/3/X/Y battery selected");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||||
|
|
||||||
|
#ifdef TESLA_MODEL_SX_BATTERY // Always use NCM/A mode on S/X packs
|
||||||
|
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA;
|
||||||
|
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_SX_NCMA;
|
||||||
|
#ifdef DOUBLE_BATTERY
|
||||||
|
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_SX_NCMA;
|
||||||
|
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_SX_NCMA;
|
||||||
|
#endif // DOUBLE_BATTERY
|
||||||
|
#endif // TESLA_MODEL_SX_BATTERY
|
||||||
|
|
||||||
|
#ifdef TESLA_MODEL_3Y_BATTERY // Model 3/Y can be either LFP or NCM/A
|
||||||
#ifdef LFP_CHEMISTRY
|
#ifdef LFP_CHEMISTRY
|
||||||
datalayer.battery.info.chemistry = battery_chemistry_enum::LFP;
|
datalayer.battery.info.chemistry = battery_chemistry_enum::LFP;
|
||||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_LFP;
|
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP;
|
||||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_LFP;
|
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_LFP;
|
||||||
#ifdef DOUBLE_BATTERY
|
#ifdef DOUBLE_BATTERY
|
||||||
datalayer.battery2.info.chemistry = battery_chemistry_enum::LFP;
|
datalayer.battery2.info.chemistry = battery_chemistry_enum::LFP;
|
||||||
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_LFP;
|
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP;
|
||||||
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_LFP;
|
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_LFP;
|
||||||
#endif
|
#endif // DOUBLE_BATTERY
|
||||||
#else // Startup in NCM/A mode
|
#else // Startup in NCM/A mode
|
||||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_NCMA;
|
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA;
|
||||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_NCMA;
|
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_NCMA;
|
||||||
#ifdef DOUBLE_BATTERY
|
#ifdef DOUBLE_BATTERY
|
||||||
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_NCMA;
|
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA;
|
||||||
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_NCMA;
|
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_NCMA;
|
||||||
#endif
|
#endif // DOUBLE_BATTERY
|
||||||
#endif
|
#endif // !LFP_CHEMISTRY
|
||||||
|
#endif // TESLA_MODEL_3Y_BATTERY
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif // TESLA_BATTERY
|
|
@ -1,5 +1,5 @@
|
||||||
#ifndef TESLA_MODEL_3_BATTERY_H
|
#ifndef TESLA_BATTERY_H
|
||||||
#define TESLA_MODEL_3_BATTERY_H
|
#define TESLA_BATTERY_H
|
||||||
#include "../include.h"
|
#include "../include.h"
|
||||||
|
|
||||||
#define BATTERY_SELECTED
|
#define BATTERY_SELECTED
|
||||||
|
@ -14,11 +14,14 @@
|
||||||
#define RAMPDOWNPOWERALLOWED 15000 // What power we ramp down from towards top balancing
|
#define RAMPDOWNPOWERALLOWED 15000 // What power we ramp down from towards top balancing
|
||||||
#define FLOAT_MAX_POWER_W 200 // W, what power to allow for top balancing battery
|
#define FLOAT_MAX_POWER_W 200 // W, what power to allow for top balancing battery
|
||||||
#define FLOAT_START_MV 20 // mV, how many mV under overvoltage to start float charging
|
#define FLOAT_START_MV 20 // mV, how many mV under overvoltage to start float charging
|
||||||
#define MAX_PACK_VOLTAGE_NCMA 4030 // V+1, if pack voltage goes over this, charge stops
|
|
||||||
#define MIN_PACK_VOLTAGE_NCMA 3100 // V+1, if pack voltage goes below this, discharge stops
|
#define MAX_PACK_VOLTAGE_SX_NCMA 4600 // V+1, if pack voltage goes over this, charge stops
|
||||||
#define MAX_PACK_VOLTAGE_LFP 3880 // V+1, if pack voltage goes over this, charge stops
|
#define MIN_PACK_VOLTAGE_SX_NCMA 3100 // V+1, if pack voltage goes over this, charge stops
|
||||||
#define MIN_PACK_VOLTAGE_LFP 2968 // V+1, if pack voltage goes below this, discharge stops
|
#define MAX_PACK_VOLTAGE_3Y_NCMA 4030 // V+1, if pack voltage goes over this, charge stops
|
||||||
#define MAX_CELL_DEVIATION_MV 9999 // Handled inside the Tesla.cpp file, just for compilation
|
#define MIN_PACK_VOLTAGE_3Y_NCMA 3100 // V+1, if pack voltage goes below this, discharge stops
|
||||||
|
#define MAX_PACK_VOLTAGE_3Y_LFP 3880 // V+1, if pack voltage goes over this, charge stops
|
||||||
|
#define MIN_PACK_VOLTAGE_3Y_LFP 2968 // V+1, if pack voltage goes below this, discharge stops
|
||||||
|
#define MAX_CELL_DEVIATION_MV 9999 // Handled inside the Tesla.cpp file, just for compilation
|
||||||
|
|
||||||
void printFaultCodesIfActive();
|
void printFaultCodesIfActive();
|
||||||
void printDebugIfActive(uint8_t symbol, const char* message);
|
void printDebugIfActive(uint8_t symbol, const char* message);
|
|
@ -48,6 +48,16 @@ SensorConfig sensorConfigs[] = {
|
||||||
{"cell_max_voltage", "Battery Emulator Cell Max Voltage", "{{ value_json.cell_max_voltage }}", "V", "voltage"},
|
{"cell_max_voltage", "Battery Emulator Cell Max Voltage", "{{ value_json.cell_max_voltage }}", "V", "voltage"},
|
||||||
{"cell_min_voltage", "Battery Emulator Cell Min Voltage", "{{ value_json.cell_min_voltage }}", "V", "voltage"},
|
{"cell_min_voltage", "Battery Emulator Cell Min Voltage", "{{ value_json.cell_min_voltage }}", "V", "voltage"},
|
||||||
{"battery_voltage", "Battery Emulator Battery Voltage", "{{ value_json.battery_voltage }}", "V", "voltage"},
|
{"battery_voltage", "Battery Emulator Battery Voltage", "{{ value_json.battery_voltage }}", "V", "voltage"},
|
||||||
|
{"total_capacity", "Battery Emulator Battery Total Capacity", "{{ value_json.total_capacity }}", "Wh", "energy"},
|
||||||
|
{"remaining_capacity", "Battery Emulator Battery Remaining Capacity", "{{ value_json.remaining_capacity }}", "Wh",
|
||||||
|
"energy"},
|
||||||
|
{"max_discharge_power", "Battery Emulator Battery Max Discharge Power", "{{ value_json.max_discharge_power }}", "W",
|
||||||
|
"power"},
|
||||||
|
{"max_charge_power", "Battery Emulator Battery Max Charge Power", "{{ value_json.max_charge_power }}", "W",
|
||||||
|
"power"},
|
||||||
|
{"bms_status", "Battery Emulator BMS Status", "{{ value_json.bms_status }}", "", ""},
|
||||||
|
{"pause_status", "Battery Emulator Pause Status", "{{ value_json.pause_status }}", "", ""},
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static String generateCommonInfoAutoConfigTopic(const char* object_id, const char* hostname) {
|
static String generateCommonInfoAutoConfigTopic(const char* object_id, const char* hostname) {
|
||||||
|
@ -81,10 +91,13 @@ static void publish_common_info(void) {
|
||||||
doc["unique_id"] = "battery-emulator_" + String(hostname) + "_" + String(config.object_id);
|
doc["unique_id"] = "battery-emulator_" + String(hostname) + "_" + String(config.object_id);
|
||||||
doc["object_id"] = String(hostname) + "_" + String(config.object_id);
|
doc["object_id"] = String(hostname) + "_" + String(config.object_id);
|
||||||
doc["value_template"] = config.value_template;
|
doc["value_template"] = config.value_template;
|
||||||
doc["unit_of_measurement"] = config.unit;
|
if (config.unit != nullptr && strlen(config.unit) > 0)
|
||||||
doc["device_class"] = config.device_class;
|
doc["unit_of_measurement"] = config.unit;
|
||||||
|
if (config.device_class != nullptr && strlen(config.device_class) > 0) {
|
||||||
|
doc["device_class"] = config.device_class;
|
||||||
|
doc["state_class"] = "measurement";
|
||||||
|
}
|
||||||
doc["enabled_by_default"] = true;
|
doc["enabled_by_default"] = true;
|
||||||
doc["state_class"] = "measurement";
|
|
||||||
doc["expire_after"] = 240;
|
doc["expire_after"] = 240;
|
||||||
doc["device"]["identifiers"][0] = "battery-emulator";
|
doc["device"]["identifiers"][0] = "battery-emulator";
|
||||||
doc["device"]["manufacturer"] = "DalaTech";
|
doc["device"]["manufacturer"] = "DalaTech";
|
||||||
|
@ -95,24 +108,36 @@ static void publish_common_info(void) {
|
||||||
doc["origin"]["url"] = "https://github.com/dalathegreat/Battery-Emulator";
|
doc["origin"]["url"] = "https://github.com/dalathegreat/Battery-Emulator";
|
||||||
serializeJson(doc, mqtt_msg);
|
serializeJson(doc, mqtt_msg);
|
||||||
mqtt_publish(generateCommonInfoAutoConfigTopic(config.object_id, hostname).c_str(), mqtt_msg, true);
|
mqtt_publish(generateCommonInfoAutoConfigTopic(config.object_id, hostname).c_str(), mqtt_msg, true);
|
||||||
|
doc.clear();
|
||||||
}
|
}
|
||||||
doc.clear();
|
|
||||||
} else {
|
} else {
|
||||||
#endif // HA_AUTODISCOVERY
|
#endif // HA_AUTODISCOVERY
|
||||||
doc["SOC"] = ((float)datalayer.battery.status.reported_soc) / 100.0;
|
doc["bms_status"] = getBMSStatus(datalayer.battery.status.bms_status);
|
||||||
doc["SOC_real"] = ((float)datalayer.battery.status.real_soc) / 100.0;
|
doc["pause_status"] = get_emulator_pause_status();
|
||||||
doc["state_of_health"] = ((float)datalayer.battery.status.soh_pptt) / 100.0;
|
|
||||||
doc["temperature_min"] = ((float)((int16_t)datalayer.battery.status.temperature_min_dC)) / 10.0;
|
//only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery)
|
||||||
doc["temperature_max"] = ((float)((int16_t)datalayer.battery.status.temperature_max_dC)) / 10.0;
|
if (datalayer.battery.status.bms_status == ACTIVE && can_send_CAN && millis() > BOOTUP_TIME) {
|
||||||
doc["stat_batt_power"] = ((float)((int32_t)datalayer.battery.status.active_power_W));
|
doc["SOC"] = ((float)datalayer.battery.status.reported_soc) / 100.0;
|
||||||
doc["battery_current"] = ((float)((int16_t)datalayer.battery.status.current_dA)) / 10.0;
|
doc["SOC_real"] = ((float)datalayer.battery.status.real_soc) / 100.0;
|
||||||
doc["battery_voltage"] = ((float)datalayer.battery.status.voltage_dV) / 10.0;
|
doc["state_of_health"] = ((float)datalayer.battery.status.soh_pptt) / 100.0;
|
||||||
// publish only if cell voltages have been populated...
|
doc["temperature_min"] = ((float)((int16_t)datalayer.battery.status.temperature_min_dC)) / 10.0;
|
||||||
if (datalayer.battery.info.number_of_cells != 0u &&
|
doc["temperature_max"] = ((float)((int16_t)datalayer.battery.status.temperature_max_dC)) / 10.0;
|
||||||
datalayer.battery.status.cell_voltages_mV[datalayer.battery.info.number_of_cells - 1] != 0u) {
|
doc["stat_batt_power"] = ((float)((int32_t)datalayer.battery.status.active_power_W));
|
||||||
doc["cell_max_voltage"] = ((float)datalayer.battery.status.cell_max_voltage_mV) / 1000.0;
|
doc["battery_current"] = ((float)((int16_t)datalayer.battery.status.current_dA)) / 10.0;
|
||||||
doc["cell_min_voltage"] = ((float)datalayer.battery.status.cell_min_voltage_mV) / 1000.0;
|
doc["battery_voltage"] = ((float)datalayer.battery.status.voltage_dV) / 10.0;
|
||||||
|
// publish only if cell voltages have been populated...
|
||||||
|
if (datalayer.battery.info.number_of_cells != 0u &&
|
||||||
|
datalayer.battery.status.cell_voltages_mV[datalayer.battery.info.number_of_cells - 1] != 0u) {
|
||||||
|
doc["cell_max_voltage"] = ((float)datalayer.battery.status.cell_max_voltage_mV) / 1000.0;
|
||||||
|
doc["cell_min_voltage"] = ((float)datalayer.battery.status.cell_min_voltage_mV) / 1000.0;
|
||||||
|
}
|
||||||
|
doc["total_capacity"] = ((float)datalayer.battery.info.total_capacity_Wh);
|
||||||
|
doc["remaining_capacity"] = ((float)datalayer.battery.status.remaining_capacity_Wh);
|
||||||
|
doc["max_discharge_power"] = ((float)datalayer.battery.status.max_discharge_power_W);
|
||||||
|
doc["max_charge_power"] = ((float)datalayer.battery.status.max_charge_power_W);
|
||||||
}
|
}
|
||||||
|
|
||||||
serializeJson(doc, mqtt_msg);
|
serializeJson(doc, mqtt_msg);
|
||||||
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
|
|
|
@ -9,9 +9,23 @@ static bool battery_empty_event_fired = false;
|
||||||
|
|
||||||
#define MAX_SOH_DEVIATION_PPTT 2500
|
#define MAX_SOH_DEVIATION_PPTT 2500
|
||||||
|
|
||||||
|
//battery pause status begin
|
||||||
|
bool emulator_pause_request_ON = false;
|
||||||
|
bool emulator_pause_CAN_send_ON = false;
|
||||||
|
bool can_send_CAN = true;
|
||||||
|
|
||||||
|
battery_pause_status emulator_pause_status = NORMAL;
|
||||||
|
//battery pause status end
|
||||||
|
|
||||||
void update_machineryprotection() {
|
void update_machineryprotection() {
|
||||||
// Start checking that the battery is within reason. Incase we see any funny business, raise an event!
|
// Start checking that the battery is within reason. Incase we see any funny business, raise an event!
|
||||||
|
|
||||||
|
// Pause function is on
|
||||||
|
if (emulator_pause_request_ON) {
|
||||||
|
datalayer.battery.status.max_discharge_power_W = 0;
|
||||||
|
datalayer.battery.status.max_charge_power_W = 0;
|
||||||
|
}
|
||||||
|
|
||||||
// Battery is overheated!
|
// Battery is overheated!
|
||||||
if (datalayer.battery.status.temperature_max_dC > 500) {
|
if (datalayer.battery.status.temperature_max_dC > 500) {
|
||||||
set_event(EVENT_BATTERY_OVERHEAT, datalayer.battery.status.temperature_max_dC);
|
set_event(EVENT_BATTERY_OVERHEAT, datalayer.battery.status.temperature_max_dC);
|
||||||
|
@ -138,6 +152,13 @@ void update_machineryprotection() {
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY // Additional Double-Battery safeties are checked here
|
#ifdef DOUBLE_BATTERY // Additional Double-Battery safeties are checked here
|
||||||
// Check if the Battery 2 BMS is still sending CAN messages. If we go 60s without messages we raise an error
|
// Check if the Battery 2 BMS is still sending CAN messages. If we go 60s without messages we raise an error
|
||||||
|
|
||||||
|
// Pause function is on
|
||||||
|
if (emulator_pause_request_ON) {
|
||||||
|
datalayer.battery2.status.max_discharge_power_W = 0;
|
||||||
|
datalayer.battery2.status.max_charge_power_W = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!datalayer.battery2.status.CAN_battery_still_alive) {
|
if (!datalayer.battery2.status.CAN_battery_still_alive) {
|
||||||
set_event(EVENT_CAN2_RX_FAILURE, 0);
|
set_event(EVENT_CAN2_RX_FAILURE, 0);
|
||||||
} else {
|
} else {
|
||||||
|
@ -171,3 +192,67 @@ void update_machineryprotection() {
|
||||||
|
|
||||||
#endif // DOUBLE_BATTERY
|
#endif // DOUBLE_BATTERY
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//battery pause status begin
|
||||||
|
void setBatteryPause(bool pause_battery, bool pause_CAN) {
|
||||||
|
|
||||||
|
emulator_pause_CAN_send_ON = pause_CAN;
|
||||||
|
|
||||||
|
if (pause_battery) {
|
||||||
|
|
||||||
|
set_event(EVENT_PAUSE_BEGIN, 1);
|
||||||
|
emulator_pause_request_ON = true;
|
||||||
|
emulator_pause_status = PAUSING;
|
||||||
|
datalayer.battery.status.max_discharge_power_W = 0;
|
||||||
|
datalayer.battery.status.max_charge_power_W = 0;
|
||||||
|
#ifdef DOUBLE_BATTERY
|
||||||
|
datalayer.battery2.status.max_discharge_power_W = 0;
|
||||||
|
datalayer.battery2.status.max_charge_power_W = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
} else {
|
||||||
|
clear_event(EVENT_PAUSE_BEGIN);
|
||||||
|
set_event(EVENT_PAUSE_END, 0);
|
||||||
|
emulator_pause_request_ON = false;
|
||||||
|
emulator_pause_CAN_send_ON = false;
|
||||||
|
emulator_pause_status = RESUMING;
|
||||||
|
clear_event(EVENT_PAUSE_END);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @brief handle emulator pause status
|
||||||
|
/// @return true if CAN messages should be sent to battery, false if not
|
||||||
|
void emulator_pause_state_send_CAN_battery() {
|
||||||
|
|
||||||
|
if (emulator_pause_status == NORMAL)
|
||||||
|
can_send_CAN = true;
|
||||||
|
|
||||||
|
// in some inverters this values are not accurate, so we need to check if we are consider 1.8 amps as the limit
|
||||||
|
if (emulator_pause_request_ON && emulator_pause_status == PAUSING && datalayer.battery.status.current_dA < 18 &&
|
||||||
|
datalayer.battery.status.current_dA > -18) {
|
||||||
|
emulator_pause_status = PAUSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!emulator_pause_request_ON && emulator_pause_status == RESUMING) {
|
||||||
|
emulator_pause_status = NORMAL;
|
||||||
|
can_send_CAN = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
can_send_CAN = (!emulator_pause_CAN_send_ON || emulator_pause_status == NORMAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string get_emulator_pause_status() {
|
||||||
|
switch (emulator_pause_status) {
|
||||||
|
case NORMAL:
|
||||||
|
return "RUNNING";
|
||||||
|
case PAUSING:
|
||||||
|
return "PAUSING";
|
||||||
|
case PAUSED:
|
||||||
|
return "PAUSED";
|
||||||
|
case RESUMING:
|
||||||
|
return "RESUMING";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//battery pause status
|
||||||
|
|
|
@ -1,11 +1,26 @@
|
||||||
#ifndef SAFETY_H
|
#ifndef SAFETY_H
|
||||||
#define SAFETY_H
|
#define SAFETY_H
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
#define MAX_CAN_FAILURES 50
|
#define MAX_CAN_FAILURES 50
|
||||||
|
|
||||||
#define MAX_CHARGE_DISCHARGE_LIMIT_FAILURES 1
|
#define MAX_CHARGE_DISCHARGE_LIMIT_FAILURES 1
|
||||||
|
|
||||||
|
//battery pause status begin
|
||||||
|
enum battery_pause_status { NORMAL = 0, PAUSING = 1, PAUSED = 2, RESUMING = 3 };
|
||||||
|
extern bool emulator_pause_request_ON;
|
||||||
|
extern bool emulator_pause_CAN_send_ON;
|
||||||
|
extern battery_pause_status emulator_pause_status;
|
||||||
|
extern bool can_send_CAN;
|
||||||
|
//battery pause status end
|
||||||
|
|
||||||
void update_machineryprotection();
|
void update_machineryprotection();
|
||||||
|
|
||||||
|
//battery pause status begin
|
||||||
|
void setBatteryPause(bool pause_battery, bool pause_CAN);
|
||||||
|
void emulator_pause_state_send_CAN_battery();
|
||||||
|
std::string get_emulator_pause_status();
|
||||||
|
//battery pause status end
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -198,6 +198,8 @@ void init_events(void) {
|
||||||
events.entries[EVENT_RESET_EFUSE].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_RESET_EFUSE].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_RESET_PWR_GLITCH].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_RESET_PWR_GLITCH].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_RESET_CPU_LOCKUP].level = EVENT_LEVEL_WARNING;
|
events.entries[EVENT_RESET_CPU_LOCKUP].level = EVENT_LEVEL_WARNING;
|
||||||
|
events.entries[EVENT_PAUSE_BEGIN].level = EVENT_LEVEL_WARNING;
|
||||||
|
events.entries[EVENT_PAUSE_END].level = EVENT_LEVEL_INFO;
|
||||||
|
|
||||||
events.entries[EVENT_EEPROM_WRITE].log = false; // Don't log the logger...
|
events.entries[EVENT_EEPROM_WRITE].log = false; // Don't log the logger...
|
||||||
|
|
||||||
|
@ -367,6 +369,10 @@ const char* get_event_message_string(EVENTS_ENUM_TYPE event) {
|
||||||
return "Info: The board was reset due to a detected power glitch";
|
return "Info: The board was reset due to a detected power glitch";
|
||||||
case EVENT_RESET_CPU_LOCKUP:
|
case EVENT_RESET_CPU_LOCKUP:
|
||||||
return "Warning: The board was reset due to CPU lockup. Inform developers!";
|
return "Warning: The board was reset due to CPU lockup. Inform developers!";
|
||||||
|
case EVENT_PAUSE_BEGIN:
|
||||||
|
return "Warning: The emulator is trying to pause the battery.";
|
||||||
|
case EVENT_PAUSE_END:
|
||||||
|
return "Info: The emulator is attempting to resume battery operation from pause.";
|
||||||
default:
|
default:
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,6 +93,8 @@
|
||||||
XX(EVENT_RESET_EFUSE) \
|
XX(EVENT_RESET_EFUSE) \
|
||||||
XX(EVENT_RESET_PWR_GLITCH) \
|
XX(EVENT_RESET_PWR_GLITCH) \
|
||||||
XX(EVENT_RESET_CPU_LOCKUP) \
|
XX(EVENT_RESET_CPU_LOCKUP) \
|
||||||
|
XX(EVENT_PAUSE_BEGIN) \
|
||||||
|
XX(EVENT_PAUSE_END) \
|
||||||
XX(EVENT_NOF_EVENTS)
|
XX(EVENT_NOF_EVENTS)
|
||||||
|
|
||||||
typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE;
|
typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE;
|
||||||
|
|
21
Software/src/devboard/utils/types.cpp
Normal file
21
Software/src/devboard/utils/types.cpp
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
// Function to get string representation of bms_status_enum
|
||||||
|
std::string getBMSStatus(bms_status_enum status) {
|
||||||
|
switch (status) {
|
||||||
|
case STANDBY:
|
||||||
|
return "STANDBY";
|
||||||
|
case INACTIVE:
|
||||||
|
return "INACTIVE";
|
||||||
|
case DARKSTART:
|
||||||
|
return "DARKSTART";
|
||||||
|
case ACTIVE:
|
||||||
|
return "ACTIVE";
|
||||||
|
case FAULT:
|
||||||
|
return "FAULT";
|
||||||
|
case UPDATING:
|
||||||
|
return "UPDATING";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef _TYPES_H_
|
#ifndef _TYPES_H_
|
||||||
#define _TYPES_H_
|
#define _TYPES_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
enum bms_status_enum { STANDBY = 0, INACTIVE = 1, DARKSTART = 2, ACTIVE = 3, FAULT = 4, UPDATING = 5 };
|
enum bms_status_enum { STANDBY = 0, INACTIVE = 1, DARKSTART = 2, ACTIVE = 3, FAULT = 4, UPDATING = 5 };
|
||||||
enum battery_chemistry_enum { NCA, NMC, LFP };
|
enum battery_chemistry_enum { NCA, NMC, LFP };
|
||||||
enum led_color { GREEN, YELLOW, RED, BLUE, RGB };
|
enum led_color { GREEN, YELLOW, RED, BLUE, RGB };
|
||||||
|
@ -46,4 +48,6 @@ typedef struct {
|
||||||
} data;
|
} data;
|
||||||
} CAN_frame;
|
} CAN_frame;
|
||||||
|
|
||||||
|
std::string getBMSStatus(bms_status_enum status);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
#include "events_html.h"
|
#include "events_html.h"
|
||||||
#include <Arduino.h>
|
|
||||||
#include "../utils/events.h"
|
|
||||||
|
|
||||||
const char EVENTS_HTML_START[] = R"=====(
|
const char EVENTS_HTML_START[] = R"=====(
|
||||||
<style>body{background-color:#000;color:#fff}.event-log{display:flex;flex-direction:column}.event{display:flex;flex-wrap:wrap;border:1px solid #fff;padding:10px}.event>div{flex:1;min-width:100px;max-width:90%;word-break:break-word}</style><div style="background-color:#303e47;padding:10px;margin-bottom:10px;border-radius:25px"><div class="event-log"><div class="event" style="background-color:#1e2c33;font-weight:700"><div>Event Type</div><div>Severity</div><div>Last Event</div><div>Count</div><div>Data</div><div>Message</div></div>
|
<style>body{background-color:#000;color:#fff}.event-log{display:flex;flex-direction:column}.event{display:flex;flex-wrap:wrap;border:1px solid #fff;padding:10px}.event>div{flex:1;min-width:100px;max-width:90%;word-break:break-word}</style><div style="background-color:#303e47;padding:10px;margin-bottom:10px;border-radius:25px"><div class="event-log"><div class="event" style="background-color:#1e2c33;font-weight:700"><div>Event Type</div><div>Severity</div><div>Last Event</div><div>Count</div><div>Data</div><div>Message</div></div>
|
||||||
|
@ -12,6 +10,13 @@ const char EVENTS_HTML_END[] = R"=====(
|
||||||
<script>function showEvent(){document.querySelectorAll(".event").forEach(function(e){var n=e.querySelector(".sec-ago");n&&(n.innerText=new Date(new Date().getTime()-1e3*parseInt(n.innerText,10)).toLocaleString())})}function home(){window.location.href="/"}window.onload=function(){showEvent()}</script>
|
<script>function showEvent(){document.querySelectorAll(".event").forEach(function(e){var n=e.querySelector(".sec-ago");n&&(n.innerText=new Date(new Date().getTime()-1e3*parseInt(n.innerText,10)).toLocaleString())})}function home(){window.location.href="/"}window.onload=function(){showEvent()}</script>
|
||||||
)=====";
|
)=====";
|
||||||
|
|
||||||
|
static std::vector<EventData> order_events;
|
||||||
|
|
||||||
|
// Function to compare events by timestamp
|
||||||
|
static bool compareEventsByTimestamp(const EventData& a, const EventData& b) {
|
||||||
|
return a.event_pointer->timestamp > b.event_pointer->timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
String events_processor(const String& var) {
|
String events_processor(const String& var) {
|
||||||
if (var == "X") {
|
if (var == "X") {
|
||||||
String content = "";
|
String content = "";
|
||||||
|
@ -22,30 +27,45 @@ String events_processor(const String& var) {
|
||||||
|
|
||||||
unsigned long timestamp_now = get_current_event_time_secs();
|
unsigned long timestamp_now = get_current_event_time_secs();
|
||||||
|
|
||||||
|
//clear the vector
|
||||||
|
order_events.clear();
|
||||||
|
// Collect all events
|
||||||
for (int i = 0; i < EVENT_NOF_EVENTS; i++) {
|
for (int i = 0; i < EVENT_NOF_EVENTS; i++) {
|
||||||
event_pointer = get_event_pointer((EVENTS_ENUM_TYPE)i);
|
event_pointer = get_event_pointer((EVENTS_ENUM_TYPE)i);
|
||||||
EVENTS_ENUM_TYPE event_handle = static_cast<EVENTS_ENUM_TYPE>(i);
|
if (event_pointer->occurences > 0) {
|
||||||
|
order_events.push_back({static_cast<EVENTS_ENUM_TYPE>(i), event_pointer});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sort events by timestamp
|
||||||
|
std::sort(order_events.begin(), order_events.end(), compareEventsByTimestamp);
|
||||||
|
|
||||||
|
// Generate HTML and debug output
|
||||||
|
for (const auto& event : order_events) {
|
||||||
|
EVENTS_ENUM_TYPE event_handle = event.event_handle;
|
||||||
|
event_pointer = event.event_pointer;
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.println("Event: " + String(get_event_enum_string(event_handle)) +
|
Serial.println("Event: " + String(get_event_enum_string(event_handle)) +
|
||||||
" count: " + String(event_pointer->occurences) + " seconds: " + String(event_pointer->timestamp) +
|
" count: " + String(event_pointer->occurences) + " seconds: " + String(event_pointer->timestamp) +
|
||||||
" data: " + String(event_pointer->data) +
|
" data: " + String(event_pointer->data) +
|
||||||
" level: " + String(get_event_level_string(event_handle)));
|
" level: " + String(get_event_level_string(event_handle)));
|
||||||
#endif
|
#endif
|
||||||
if (event_pointer->occurences > 0) {
|
content.concat("<div class='event'>");
|
||||||
content.concat("<div class='event'>");
|
content.concat("<div>" + String(get_event_enum_string(event_handle)) + "</div>");
|
||||||
content.concat("<div>" + String(get_event_enum_string(event_handle)) + "</div>");
|
content.concat("<div>" + String(get_event_level_string(event_handle)) + "</div>");
|
||||||
content.concat("<div>" + String(get_event_level_string(event_handle)) + "</div>");
|
content.concat("<div class='sec-ago'>" + String(timestamp_now - event_pointer->timestamp) + "</div>");
|
||||||
content.concat("<div class='sec-ago'>" + String(timestamp_now - event_pointer->timestamp) + "</div>");
|
content.concat("<div>" + String(event_pointer->occurences) + "</div>");
|
||||||
content.concat("<div>" + String(event_pointer->occurences) + "</div>");
|
content.concat("<div>" + String(event_pointer->data) + "</div>");
|
||||||
content.concat("<div>" + String(event_pointer->data) + "</div>");
|
content.concat("<div>" + String(get_event_message_string(event_handle)) + "</div>");
|
||||||
content.concat("<div>" + String(get_event_message_string(event_handle)) + "</div>");
|
content.concat("</div>"); // End of event row
|
||||||
content.concat("</div>"); // End of event row
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//clear the vector
|
||||||
|
order_events.clear();
|
||||||
content.concat(FPSTR(EVENTS_HTML_END));
|
content.concat(FPSTR(EVENTS_HTML_END));
|
||||||
return content;
|
return content;
|
||||||
|
return String();
|
||||||
}
|
}
|
||||||
return String();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Script for displaying event log before it gets minified
|
/* Script for displaying event log before it gets minified
|
||||||
|
|
|
@ -2,6 +2,9 @@
|
||||||
#define EVENTS_H
|
#define EVENTS_H
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
#include "../utils/events.h"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Replaces placeholder with content section in web page
|
* @brief Replaces placeholder with content section in web page
|
||||||
|
@ -11,5 +14,10 @@
|
||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
String events_processor(const String& var);
|
String events_processor(const String& var);
|
||||||
|
// Define a struct to hold event data
|
||||||
|
struct EventData {
|
||||||
|
EVENTS_ENUM_TYPE event_handle;
|
||||||
|
const EVENTS_STRUCT_TYPE* event_pointer;
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "webserver.h"
|
#include "webserver.h"
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include "../../datalayer/datalayer.h"
|
#include "../../datalayer/datalayer.h"
|
||||||
|
#include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h"
|
||||||
#include "../utils/events.h"
|
#include "../utils/events.h"
|
||||||
#include "../utils/led_handler.h"
|
#include "../utils/led_handler.h"
|
||||||
#include "../utils/timer.h"
|
#include "../utils/timer.h"
|
||||||
|
@ -34,6 +35,7 @@ unsigned const long MAX_WIFI_RETRY_INTERVAL = 90000; // Maximum wifi ret
|
||||||
unsigned long last_wifi_monitor_time = millis(); //init millis so wifi monitor doesn't run immediately
|
unsigned long last_wifi_monitor_time = millis(); //init millis so wifi monitor doesn't run immediately
|
||||||
unsigned long wifi_reconnect_interval = DEFAULT_WIFI_RECONNECT_INTERVAL;
|
unsigned long wifi_reconnect_interval = DEFAULT_WIFI_RECONNECT_INTERVAL;
|
||||||
unsigned long last_wifi_attempt_time = millis(); //init millis so wifi monitor doesn't run immediately
|
unsigned long last_wifi_attempt_time = millis(); //init millis so wifi monitor doesn't run immediately
|
||||||
|
const char get_firmware_info_html[] = R"rawliteral(%X%)rawliteral";
|
||||||
|
|
||||||
void init_webserver() {
|
void init_webserver() {
|
||||||
// Configure WiFi
|
// Configure WiFi
|
||||||
|
@ -53,6 +55,13 @@ void init_webserver() {
|
||||||
|
|
||||||
server.on("/logout", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(401); });
|
server.on("/logout", HTTP_GET, [](AsyncWebServerRequest* request) { request->send(401); });
|
||||||
|
|
||||||
|
// Route for firmware info from ota update page
|
||||||
|
server.on("/GetFirmwareInfo", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
|
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||||
|
return request->requestAuthentication();
|
||||||
|
request->send_P(200, "application/json", get_firmware_info_html, get_firmware_info_processor);
|
||||||
|
});
|
||||||
|
|
||||||
// Route for root / web page
|
// Route for root / web page
|
||||||
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||||
|
@ -159,6 +168,19 @@ void init_webserver() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Route for pause/resume Battery emulator
|
||||||
|
server.on("/pause", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
|
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||||
|
return request->requestAuthentication();
|
||||||
|
if (request->hasParam("p")) {
|
||||||
|
String valueStr = request->getParam("p")->value();
|
||||||
|
setBatteryPause(valueStr == "true" || valueStr == "1", false);
|
||||||
|
request->send(200, "text/plain", "Updated successfully");
|
||||||
|
} else {
|
||||||
|
request->send(400, "text/plain", "Bad Request");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Route for editing SOCMin
|
// Route for editing SOCMin
|
||||||
server.on("/updateSocMin", HTTP_GET, [](AsyncWebServerRequest* request) {
|
server.on("/updateSocMin", HTTP_GET, [](AsyncWebServerRequest* request) {
|
||||||
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
|
||||||
|
@ -414,10 +436,8 @@ void wifi_monitor() {
|
||||||
|
|
||||||
if (ota_active && ota_timeout_timer.elapsed()) {
|
if (ota_active && ota_timeout_timer.elapsed()) {
|
||||||
// OTA timeout, try to restore can and clear the update event
|
// OTA timeout, try to restore can and clear the update event
|
||||||
ESP32Can.CANInit();
|
|
||||||
clear_event(EVENT_OTA_UPDATE);
|
|
||||||
set_event(EVENT_OTA_UPDATE_TIMEOUT, 0);
|
set_event(EVENT_OTA_UPDATE_TIMEOUT, 0);
|
||||||
ota_active = false;
|
onOTAEnd(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -444,6 +464,24 @@ void init_ElegantOTA() {
|
||||||
ElegantOTA.onEnd(onOTAEnd);
|
ElegantOTA.onEnd(onOTAEnd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String get_firmware_info_processor(const String& var) {
|
||||||
|
if (var == "X") {
|
||||||
|
String content = "";
|
||||||
|
static JsonDocument doc;
|
||||||
|
#ifdef HW_LILYGO
|
||||||
|
doc["hardware"] = "LilyGo T-CAN485";
|
||||||
|
#endif // HW_LILYGO
|
||||||
|
#ifdef HW_STARK
|
||||||
|
doc["hardware"] = "Stark CMR Module";
|
||||||
|
#endif // HW_STARK
|
||||||
|
|
||||||
|
doc["firmware"] = String(version_number);
|
||||||
|
serializeJson(doc, content);
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
return String();
|
||||||
|
}
|
||||||
|
|
||||||
String processor(const String& var) {
|
String processor(const String& var) {
|
||||||
if (var == "X") {
|
if (var == "X") {
|
||||||
String content = "";
|
String content = "";
|
||||||
|
@ -576,9 +614,12 @@ String processor(const String& var) {
|
||||||
#ifdef SERIAL_LINK_RECEIVER
|
#ifdef SERIAL_LINK_RECEIVER
|
||||||
content += "Serial link to another LilyGo board";
|
content += "Serial link to another LilyGo board";
|
||||||
#endif // SERIAL_LINK_RECEIVER
|
#endif // SERIAL_LINK_RECEIVER
|
||||||
#ifdef TESLA_MODEL_3_BATTERY
|
#ifdef TESLA_MODEL_SX_BATTERY
|
||||||
content += "Tesla Model S/3/X/Y";
|
content += "Tesla Model S/X";
|
||||||
#endif // TESLA_MODEL_3_BATTERY
|
#endif // TESLA_MODEL_SX_BATTERY
|
||||||
|
#ifdef TESLA_MODEL_3Y_BATTERY
|
||||||
|
content += "Tesla Model 3/Y";
|
||||||
|
#endif // TESLA_MODEL_3Y_BATTERY
|
||||||
#ifdef VOLVO_SPA_BATTERY
|
#ifdef VOLVO_SPA_BATTERY
|
||||||
content += "Volvo / Polestar 78kWh battery";
|
content += "Volvo / Polestar 78kWh battery";
|
||||||
#endif // VOLVO_SPA_BATTERY
|
#endif // VOLVO_SPA_BATTERY
|
||||||
|
@ -587,6 +628,9 @@ String processor(const String& var) {
|
||||||
#endif // TEST_FAKE_BATTERY
|
#endif // TEST_FAKE_BATTERY
|
||||||
#ifdef DOUBLE_BATTERY
|
#ifdef DOUBLE_BATTERY
|
||||||
content += " (Double battery)";
|
content += " (Double battery)";
|
||||||
|
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
|
||||||
|
content += " (LFP)";
|
||||||
|
}
|
||||||
#endif // DOUBLE_BATTERY
|
#endif // DOUBLE_BATTERY
|
||||||
content += "</h4>";
|
content += "</h4>";
|
||||||
|
|
||||||
|
@ -693,6 +737,10 @@ String processor(const String& var) {
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>✕</span></h4>";
|
content += "<span style='color: red;'>✕</span></h4>";
|
||||||
}
|
}
|
||||||
|
if (emulator_pause_status == NORMAL)
|
||||||
|
content += "<h4>Pause status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||||
|
else
|
||||||
|
content += "<h4 style='color: red;'>Pause status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||||
|
|
||||||
// Close the block
|
// Close the block
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
|
@ -769,6 +817,10 @@ String processor(const String& var) {
|
||||||
} else {
|
} else {
|
||||||
content += "<span style='color: red;'>✕</span></h4>";
|
content += "<span style='color: red;'>✕</span></h4>";
|
||||||
}
|
}
|
||||||
|
if (emulator_pause_status == NORMAL)
|
||||||
|
content += "<h4>Pause status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||||
|
else
|
||||||
|
content += "<h4 style='color: red;'>Pause status: " + String(get_emulator_pause_status().c_str()) + " </h4>";
|
||||||
|
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
|
@ -830,6 +882,11 @@ String processor(const String& var) {
|
||||||
content += "</div>";
|
content += "</div>";
|
||||||
#endif // defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
|
#endif // defined CHEVYVOLT_CHARGER || defined NISSANLEAF_CHARGER
|
||||||
|
|
||||||
|
if (emulator_pause_request_ON)
|
||||||
|
content += "<button onclick='PauseBattery(false)'>Resume Battery</button>";
|
||||||
|
else
|
||||||
|
content += "<button onclick='PauseBattery(true)'>Pause Battery</button>";
|
||||||
|
|
||||||
content += "<button onclick='OTA()'>Perform OTA update</button>";
|
content += "<button onclick='OTA()'>Perform OTA update</button>";
|
||||||
content += " ";
|
content += " ";
|
||||||
content += "<button onclick='Settings()'>Change Settings</button>";
|
content += "<button onclick='Settings()'>Change Settings</button>";
|
||||||
|
@ -863,6 +920,12 @@ String processor(const String& var) {
|
||||||
content += " setTimeout(function(){ window.open(\"/\",\"_self\"); }, 1000);";
|
content += " setTimeout(function(){ window.open(\"/\",\"_self\"); }, 1000);";
|
||||||
content += "}";
|
content += "}";
|
||||||
}
|
}
|
||||||
|
content += "function PauseBattery(pause){";
|
||||||
|
content +=
|
||||||
|
"var xhr=new "
|
||||||
|
"XMLHttpRequest();xhr.onload=function() { "
|
||||||
|
"window.location.reload();};xhr.open('GET','/pause?p='+pause,true);xhr.send();";
|
||||||
|
content += "}";
|
||||||
|
|
||||||
content += "</script>";
|
content += "</script>";
|
||||||
|
|
||||||
|
@ -877,8 +940,10 @@ String processor(const String& var) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onOTAStart() {
|
void onOTAStart() {
|
||||||
|
//try to Pause the battery
|
||||||
|
setBatteryPause(true, true);
|
||||||
|
|
||||||
// Log when OTA has started
|
// Log when OTA has started
|
||||||
ESP32Can.CANStop();
|
|
||||||
set_event(EVENT_OTA_UPDATE, 0);
|
set_event(EVENT_OTA_UPDATE, 0);
|
||||||
|
|
||||||
// If already set, make a new attempt
|
// If already set, make a new attempt
|
||||||
|
@ -900,8 +965,13 @@ void onOTAProgress(size_t current, size_t final) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void onOTAEnd(bool success) {
|
void onOTAEnd(bool success) {
|
||||||
|
|
||||||
|
ota_active = false;
|
||||||
|
clear_event(EVENT_OTA_UPDATE);
|
||||||
|
|
||||||
// Log when OTA has finished
|
// Log when OTA has finished
|
||||||
if (success) {
|
if (success) {
|
||||||
|
// a reboot will be done by the OTA library. no need to do anything here
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.println("OTA update finished successfully!");
|
Serial.println("OTA update finished successfully!");
|
||||||
#endif // DEBUG_VIA_USB
|
#endif // DEBUG_VIA_USB
|
||||||
|
@ -909,12 +979,9 @@ void onOTAEnd(bool success) {
|
||||||
#ifdef DEBUG_VIA_USB
|
#ifdef DEBUG_VIA_USB
|
||||||
Serial.println("There was an error during OTA update!");
|
Serial.println("There was an error during OTA update!");
|
||||||
#endif // DEBUG_VIA_USB
|
#endif // DEBUG_VIA_USB
|
||||||
|
//try to Resume the battery pause and CAN communication
|
||||||
// If we fail without a timeout, try to restore CAN
|
setBatteryPause(false, false);
|
||||||
ESP32Can.CANInit();
|
|
||||||
}
|
}
|
||||||
ota_active = false;
|
|
||||||
clear_event(EVENT_OTA_UPDATE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T> // This function makes power values appear as W when under 1000, and kW when over
|
template <typename T> // This function makes power values appear as W when under 1000, and kW when over
|
||||||
|
|
|
@ -103,6 +103,7 @@ void init_ElegantOTA();
|
||||||
* @return String
|
* @return String
|
||||||
*/
|
*/
|
||||||
String processor(const String& var);
|
String processor(const String& var);
|
||||||
|
String get_firmware_info_processor(const String& var);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Executes on OTA start
|
* @brief Executes on OTA start
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
/* TODO: Map error bits in 0x158 */
|
/* TODO: Map error bits in 0x158 */
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
static unsigned long previousMillis60s = 0;
|
static unsigned long previousMillis100ms = 0;
|
||||||
|
|
||||||
//Actual content messages
|
//Actual content messages
|
||||||
CAN_frame SMA_358 = {.FD = false,
|
CAN_frame SMA_358 = {.FD = false,
|
||||||
|
@ -51,7 +51,7 @@ CAN_frame SMA_598 = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x598,
|
.ID = 0x598,
|
||||||
.data = {0x00, 0xD3, 0x00, 0x01, 0x5C, 0x98, 0xB6, 0xEE}};
|
.data = {0x00, 0x01, 0x0F, 0x2C, 0x5C, 0x98, 0xB6, 0xEE}};
|
||||||
CAN_frame SMA_5D8 = {.FD = false,
|
CAN_frame SMA_5D8 = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
|
@ -221,12 +221,14 @@ void receive_can_inverter(CAN_frame rx_frame) {
|
||||||
break;
|
break;
|
||||||
case 0x420: //Message originating from SMA inverter - Timestamp
|
case 0x420: //Message originating from SMA inverter - Timestamp
|
||||||
//Frame0-3 Timestamp
|
//Frame0-3 Timestamp
|
||||||
|
/*
|
||||||
transmit_can(&SMA_158, can_config.inverter);
|
transmit_can(&SMA_158, can_config.inverter);
|
||||||
transmit_can(&SMA_358, can_config.inverter);
|
transmit_can(&SMA_358, can_config.inverter);
|
||||||
transmit_can(&SMA_3D8, can_config.inverter);
|
transmit_can(&SMA_3D8, can_config.inverter);
|
||||||
transmit_can(&SMA_458, can_config.inverter);
|
transmit_can(&SMA_458, can_config.inverter);
|
||||||
transmit_can(&SMA_518, can_config.inverter);
|
transmit_can(&SMA_518, can_config.inverter);
|
||||||
transmit_can(&SMA_4D8, can_config.inverter);
|
transmit_can(&SMA_4D8, can_config.inverter);
|
||||||
|
*/
|
||||||
break;
|
break;
|
||||||
case 0x5E0: //Message originating from SMA inverter - String
|
case 0x5E0: //Message originating from SMA inverter - String
|
||||||
break;
|
break;
|
||||||
|
@ -254,15 +256,17 @@ void receive_can_inverter(CAN_frame rx_frame) {
|
||||||
void send_can_inverter() {
|
void send_can_inverter() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
|
|
||||||
// Send CAN Message every 60s
|
// Send CAN Message every 100ms if we're enabled
|
||||||
if (currentMillis - previousMillis60s >= INTERVAL_60_S) {
|
if (datalayer.system.status.inverter_allows_contactor_closing) {
|
||||||
previousMillis60s = currentMillis;
|
if (currentMillis - previousMillis100ms >= 100) {
|
||||||
transmit_can(&SMA_158, can_config.inverter);
|
previousMillis100ms = currentMillis;
|
||||||
transmit_can(&SMA_358, can_config.inverter);
|
transmit_can(&SMA_158, can_config.inverter);
|
||||||
transmit_can(&SMA_3D8, can_config.inverter);
|
transmit_can(&SMA_358, can_config.inverter);
|
||||||
transmit_can(&SMA_458, can_config.inverter);
|
transmit_can(&SMA_3D8, can_config.inverter);
|
||||||
transmit_can(&SMA_518, can_config.inverter);
|
transmit_can(&SMA_458, can_config.inverter);
|
||||||
transmit_can(&SMA_4D8, can_config.inverter);
|
transmit_can(&SMA_518, can_config.inverter);
|
||||||
|
transmit_can(&SMA_4D8, can_config.inverter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
/* TODO: Map error bits in 0x158 */
|
/* TODO: Map error bits in 0x158 */
|
||||||
|
|
||||||
/* Do not change code below unless you are sure what you are doing */
|
/* Do not change code below unless you are sure what you are doing */
|
||||||
static unsigned long previousMillis60s = 0;
|
static unsigned long previousMillis100ms = 0;
|
||||||
|
|
||||||
//Actual content messages
|
//Actual content messages
|
||||||
CAN_frame SMA_558 = {.FD = false,
|
CAN_frame SMA_558 = {.FD = false,
|
||||||
|
@ -218,12 +218,14 @@ void receive_can_inverter(CAN_frame rx_frame) {
|
||||||
break;
|
break;
|
||||||
case 0x420: //Message originating from SMA inverter - Timestamp
|
case 0x420: //Message originating from SMA inverter - Timestamp
|
||||||
//Frame0-3 Timestamp
|
//Frame0-3 Timestamp
|
||||||
|
/*
|
||||||
transmit_can(&SMA_158, can_config.inverter);
|
transmit_can(&SMA_158, can_config.inverter);
|
||||||
transmit_can(&SMA_358, can_config.inverter);
|
transmit_can(&SMA_358, can_config.inverter);
|
||||||
transmit_can(&SMA_3D8, can_config.inverter);
|
transmit_can(&SMA_3D8, can_config.inverter);
|
||||||
transmit_can(&SMA_458, can_config.inverter);
|
transmit_can(&SMA_458, can_config.inverter);
|
||||||
transmit_can(&SMA_518, can_config.inverter);
|
transmit_can(&SMA_518, can_config.inverter);
|
||||||
transmit_can(&SMA_4D8, can_config.inverter);
|
transmit_can(&SMA_4D8, can_config.inverter);
|
||||||
|
*/
|
||||||
break;
|
break;
|
||||||
case 0x5E0: //Message originating from SMA inverter - String
|
case 0x5E0: //Message originating from SMA inverter - String
|
||||||
break;
|
break;
|
||||||
|
@ -251,16 +253,18 @@ void receive_can_inverter(CAN_frame rx_frame) {
|
||||||
void send_can_inverter() {
|
void send_can_inverter() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
|
|
||||||
// Send CAN Message every 60s
|
// Send CAN Message every 100ms if Enable line is HIGH
|
||||||
if (currentMillis - previousMillis60s >= INTERVAL_60_S) {
|
if (datalayer.system.status.inverter_allows_contactor_closing) {
|
||||||
previousMillis60s = currentMillis;
|
if (currentMillis - previousMillis100ms >= 100) {
|
||||||
|
previousMillis100ms = currentMillis;
|
||||||
|
|
||||||
transmit_can(&SMA_158, can_config.inverter);
|
transmit_can(&SMA_158, can_config.inverter);
|
||||||
transmit_can(&SMA_358, can_config.inverter);
|
transmit_can(&SMA_358, can_config.inverter);
|
||||||
transmit_can(&SMA_3D8, can_config.inverter);
|
transmit_can(&SMA_3D8, can_config.inverter);
|
||||||
transmit_can(&SMA_458, can_config.inverter);
|
transmit_can(&SMA_458, can_config.inverter);
|
||||||
transmit_can(&SMA_518, can_config.inverter);
|
transmit_can(&SMA_518, can_config.inverter);
|
||||||
transmit_can(&SMA_4D8, can_config.inverter);
|
transmit_can(&SMA_4D8, can_config.inverter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -36,4 +36,5 @@ node_modules
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.vscode
|
.vscode
|
||||||
/build
|
/build
|
||||||
/portal
|
/portal
|
||||||
|
.pio
|
||||||
|
|
212
Software/src/lib/ayushsharma82-ElegantOTA/CurrentPlainHTML.txt
Normal file
212
Software/src/lib/ayushsharma82-ElegantOTA/CurrentPlainHTML.txt
Normal file
File diff suppressed because one or more lines are too long
|
@ -1,19 +1,24 @@
|
||||||
<p><br/></p>
|
<p align="center">
|
||||||
<p align="center"><img src="/docs/feature.png?sanitize=true&raw=true" width="700"></p>
|
<a href="https://elegantota.pro?ref=ghfeature" target="_blank">
|
||||||
|
<img src="https://raw.githubusercontent.com/ayushsharma82/ElegantOTA/master/docs/feature.png" width="1200"></p>
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
<p align="center">
|
|
||||||
|
<p>
|
||||||
<img src="https://img.shields.io/github/last-commit/ayushsharma82/ElegantOTA.svg?style=for-the-badge" />
|
<img src="https://img.shields.io/github/last-commit/ayushsharma82/ElegantOTA.svg?style=for-the-badge" />
|
||||||
|
|
||||||
<img src="https://img.shields.io/github/actions/workflow/status/ayushsharma82/ElegantOTA/ci.yml?branch=master&style=for-the-badge" />
|
<img src="https://img.shields.io/github/actions/workflow/status/ayushsharma82/ElegantOTA/ci.yml?branch=master&style=for-the-badge" />
|
||||||
|
|
||||||
<img src="https://img.shields.io/github/license/ayushsharma82/ElegantOTA.svg?style=for-the-badge" />
|
<img src="https://img.shields.io/github/license/ayushsharma82/ElegantOTA.svg?style=for-the-badge" />
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
|
<p>Over-the-Air update library for wireless microcontrollers</p>
|
||||||
|
|
||||||
<p align="center">OTA update library for wireless microcontrollers</p>
|
<p>
|
||||||
<p align="center">
|
|
||||||
ElegantOTA provides a beautiful user interface to upload over-the-air firmware/filesystem updates to your hardware with precise status and progress. ElegantOTA is designed to make the process of OTA updates slick and simple!
|
ElegantOTA provides a beautiful user interface to upload over-the-air firmware/filesystem updates to your hardware with precise status and progress. ElegantOTA is designed to make the process of OTA updates slick and simple!
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
@ -21,6 +26,7 @@ ElegantOTA provides a beautiful user interface to upload over-the-air firmware/f
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- 🔥 Quick & Simple OTA procedure
|
- 🔥 Quick & Simple OTA procedure
|
||||||
- 🏀 Get useful insight on progress and status of your OTA update
|
- 🏀 Get useful insight on progress and status of your OTA update
|
||||||
- 🎷 No need to learn HTML/CSS/JS
|
- 🎷 No need to learn HTML/CSS/JS
|
||||||
|
@ -29,7 +35,9 @@ ElegantOTA provides a beautiful user interface to upload over-the-air firmware/f
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
## Supported MCUs
|
## Supported MCUs
|
||||||
|
|
||||||
ElegantOTA works on the following microcontrollers/boards:
|
ElegantOTA works on the following microcontrollers/boards:
|
||||||
|
|
||||||
- ESP8266
|
- ESP8266
|
||||||
- ESP32
|
- ESP32
|
||||||
- RP2040 ( Pico W )
|
- RP2040 ( Pico W )
|
||||||
|
@ -51,12 +59,12 @@ ElegantOTA works on the following microcontrollers/boards:
|
||||||
*Preview might appear as blurry due to image optimization.*
|
*Preview might appear as blurry due to image optimization.*
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<img src="/docs/demo.gif?raw=true" width="600">
|
<img src="https://raw.githubusercontent.com/ayushsharma82/ElegantOTA/master/docs/demo.gif" width="600">
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## Want More? Upgrade to Pro
|
## Looking for more? Upgrade to Pro.
|
||||||
|
|
||||||
ElegantOTA Pro comes with the following extended functionality:
|
ElegantOTA Pro comes with the following extended functionality:
|
||||||
- New Drag & Drop Zone
|
- New Drag & Drop Zone
|
||||||
|
@ -74,7 +82,7 @@ ElegantOTA Pro comes with the following extended functionality:
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
<a href="https://elegantota.pro" target="_blank">
|
<a href="https://elegantota.pro" target="_blank">
|
||||||
<img src="/docs/pro-preview.jpg" alt="ElegantOTA Pro" width="600">
|
<img src="https://raw.githubusercontent.com/ayushsharma82/ElegantOTA/master/docs/pro-preview.jpg" alt="ElegantOTA Pro" width="600">
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
Modifying HTML for the ElegantOTA Library
|
||||||
|
|
||||||
|
This guide provides the necessary steps to update and modify the HTML used in the ElegantOTA library.
|
||||||
|
Follow these steps carefully to ensure that your changes are properly integrated.
|
||||||
|
|
||||||
|
Steps to Modify the HTML:
|
||||||
|
|
||||||
|
1 - Edit the CurrentPlainHTML.txt:
|
||||||
|
|
||||||
|
Locate the file CurrentPlainHTML.txt in your project directory.
|
||||||
|
Modify the HTML content as needed. This file contains the plain HTML code that will be served through the OTA interface.
|
||||||
|
|
||||||
|
Convert the HTML to GZIP and Decimal Format:
|
||||||
|
|
||||||
|
Copy the content of the updated CurrentPlainHTML.txt.
|
||||||
|
Navigate to the CyberChef tool for encoding and compression.
|
||||||
|
Apply the following recipe:
|
||||||
|
Gzip with the "Dynamic Huffman Coding" option enabled.
|
||||||
|
Convert to Decimal with a comma separator.
|
||||||
|
Use this link for the process: https://gchq.github.io/CyberChef/#recipe=Gzip('Dynamic%20Huffman%20Coding','','',false)To_Decimal('Comma',false)
|
||||||
|
|
||||||
|
2 - Update the ELEGANT_HTML Array:
|
||||||
|
|
||||||
|
Copy the resulting decimal output from CyberChef.
|
||||||
|
Replace the existing content of the ELEGANT_HTML array in elop.cpp with the new decimal data from CyberChef.
|
||||||
|
|
||||||
|
3 - Adjust the ELEGANT_HTML Array Size:
|
||||||
|
|
||||||
|
After updating the ELEGANT_HTML array in both elop.h and elop.cpp, update the array size to match the length of the new output from CyberChef.
|
||||||
|
Ensure that the array size reflects the new length of the compressed HTML to avoid errors during compilation.
|
|
@ -15,7 +15,18 @@
|
||||||
"maintainer": true
|
"maintainer": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"version": "3.1.1",
|
"dependencies": [
|
||||||
|
{
|
||||||
|
"owner": "mathieucarbou",
|
||||||
|
"name": "ESPAsyncWebServer",
|
||||||
|
"version": "^3.1.1",
|
||||||
|
"platforms": ["espressif8266", "espressif32"]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "3.1.5",
|
||||||
"frameworks": "arduino",
|
"frameworks": "arduino",
|
||||||
"platforms": ["espressif8266", "espressif32", "raspberrypi"]
|
"platforms": ["espressif8266", "espressif32", "raspberrypi"],
|
||||||
|
"build": {
|
||||||
|
"libCompatMode": "strict"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name=ElegantOTA
|
name=ElegantOTA
|
||||||
version=3.1.1
|
version=3.1.5
|
||||||
author=Ayush Sharma
|
author=Ayush Sharma
|
||||||
category=Communication
|
category=Communication
|
||||||
maintainer=Ayush Sharma <asrocks5@gmail.com>
|
maintainer=Ayush Sharma <asrocks5@gmail.com>
|
||||||
|
|
|
@ -6,12 +6,13 @@
|
||||||
#
|
#
|
||||||
# extra_scripts = platformio_upload.py
|
# extra_scripts = platformio_upload.py
|
||||||
# upload_protocol = custom
|
# upload_protocol = custom
|
||||||
# upload_url = <your upload URL>
|
# custom_upload_url = <your upload URL>
|
||||||
#
|
#
|
||||||
# An example of an upload URL:
|
# An example of an upload URL:
|
||||||
# upload_url = http://192.168.1.123/update
|
# custom_upload_url = http://192.168.1.123/update
|
||||||
# also possible: upload_url = http://domainname/update
|
# also possible: custom_upload_url = http://domainname/update
|
||||||
|
|
||||||
|
import sys
|
||||||
import requests
|
import requests
|
||||||
import hashlib
|
import hashlib
|
||||||
from urllib.parse import urlparse
|
from urllib.parse import urlparse
|
||||||
|
@ -54,7 +55,10 @@ def on_upload(source, target, env):
|
||||||
'Connection': 'keep-alive'
|
'Connection': 'keep-alive'
|
||||||
}
|
}
|
||||||
|
|
||||||
checkAuthResponse = requests.get(f"{upload_url_compatibility}/update")
|
try:
|
||||||
|
checkAuthResponse = requests.get(f"{upload_url_compatibility}/update")
|
||||||
|
except Exception as e:
|
||||||
|
return 'Error checking auth: ' + repr(e)
|
||||||
|
|
||||||
if checkAuthResponse.status_code == 401:
|
if checkAuthResponse.status_code == 401:
|
||||||
try:
|
try:
|
||||||
|
@ -66,24 +70,27 @@ def on_upload(source, target, env):
|
||||||
print("No authentication values specified.")
|
print("No authentication values specified.")
|
||||||
print('Please, add some Options in your .ini file like: \n\ncustom_username=username\ncustom_password=password\n')
|
print('Please, add some Options in your .ini file like: \n\ncustom_username=username\ncustom_password=password\n')
|
||||||
if username is None or password is None:
|
if username is None or password is None:
|
||||||
print("Authentication required, but no credentials provided.")
|
return "Authentication required, but no credentials provided."
|
||||||
return
|
|
||||||
print("Serverconfiguration: authentication needed.")
|
print("Serverconfiguration: authentication needed.")
|
||||||
auth = HTTPDigestAuth(username, password)
|
auth = HTTPDigestAuth(username, password)
|
||||||
doUpdateAuth = requests.get(start_url, headers=start_headers, auth=auth)
|
try:
|
||||||
|
doUpdateAuth = requests.get(start_url, headers=start_headers, auth=auth)
|
||||||
|
except Exception as e:
|
||||||
|
return 'Error while authenticating: ' + repr(e)
|
||||||
|
|
||||||
if doUpdateAuth.status_code != 200:
|
if doUpdateAuth.status_code != 200:
|
||||||
print("authentication faild " + str(doUpdateAuth.status_code))
|
return "Authentication failed " + str(doUpdateAuth.status_code)
|
||||||
return
|
print("Authentication successful")
|
||||||
print("Authentication successfull")
|
|
||||||
else:
|
else:
|
||||||
auth = None
|
auth = None
|
||||||
print("Serverconfiguration: autentication not needed.")
|
print("Serverconfiguration: authentication not needed.")
|
||||||
doUpdate = requests.get(start_url, headers=start_headers)
|
try:
|
||||||
|
doUpdate = requests.get(start_url, headers=start_headers)
|
||||||
|
except Exception as e:
|
||||||
|
return 'Error while starting upload: ' + repr(e)
|
||||||
|
|
||||||
if doUpdate.status_code != 200:
|
if doUpdate.status_code != 200:
|
||||||
print("start-request faild " + str(doUpdate.status_code))
|
return "Start request failed " + str(doUpdate.status_code)
|
||||||
return
|
|
||||||
|
|
||||||
firmware.seek(0)
|
firmware.seek(0)
|
||||||
encoder = MultipartEncoder(fields={
|
encoder = MultipartEncoder(fields={
|
||||||
|
@ -114,14 +121,16 @@ def on_upload(source, target, env):
|
||||||
'Origin': f'{upload_url}'
|
'Origin': f'{upload_url}'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
response = requests.post(f"{upload_url}/ota/upload", data=monitor, headers=post_headers, auth=auth)
|
response = requests.post(f"{upload_url}/ota/upload", data=monitor, headers=post_headers, auth=auth)
|
||||||
|
except Exception as e:
|
||||||
|
return 'Error while uploading: ' + repr(e)
|
||||||
|
|
||||||
bar.close()
|
bar.close()
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
if response.status_code != 200:
|
if response.status_code != 200:
|
||||||
message = "\nUpload faild.\nServer response: " + response.text
|
message = "\nUpload failed.\nServer response: " + response.text
|
||||||
tqdm.write(message)
|
tqdm.write(message)
|
||||||
else:
|
else:
|
||||||
message = "\nUpload successful.\nServer response: " + response.text
|
message = "\nUpload successful.\nServer response: " + response.text
|
||||||
|
|
|
@ -16,16 +16,20 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
|
|
||||||
#if ELEGANTOTA_USE_ASYNC_WEBSERVER == 1
|
#if ELEGANTOTA_USE_ASYNC_WEBSERVER == 1
|
||||||
_server->on("/update", HTTP_GET, [&](AsyncWebServerRequest *request){
|
_server->on("/update", HTTP_GET, [&](AsyncWebServerRequest *request){
|
||||||
if(_authenticate && !request->authenticate(_username, _password)){
|
if(_authenticate && !request->authenticate(_username.c_str(), _password.c_str())){
|
||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
}
|
}
|
||||||
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", ELEGANT_HTML, sizeof(ELEGANT_HTML));
|
#if defined(ASYNCWEBSERVER_VERSION) && ASYNCWEBSERVER_VERSION_MAJOR > 2 // This means we are using recommended fork of AsyncWebServer
|
||||||
|
AsyncWebServerResponse *response = request->beginResponse(200, "text/html", ELEGANT_HTML, sizeof(ELEGANT_HTML));
|
||||||
|
#else
|
||||||
|
AsyncWebServerResponse *response = request->beginResponse_P(200, "text/html", ELEGANT_HTML, sizeof(ELEGANT_HTML));
|
||||||
|
#endif
|
||||||
response->addHeader("Content-Encoding", "gzip");
|
response->addHeader("Content-Encoding", "gzip");
|
||||||
request->send(response);
|
request->send(response);
|
||||||
});
|
});
|
||||||
#else
|
#else
|
||||||
_server->on("/update", HTTP_GET, [&](){
|
_server->on("/update", HTTP_GET, [&](){
|
||||||
if (_authenticate && !_server->authenticate(_username, _password)) {
|
if (_authenticate && !_server->authenticate(_username.c_str(), _password.c_str())) {
|
||||||
return _server->requestAuthentication();
|
return _server->requestAuthentication();
|
||||||
}
|
}
|
||||||
_server->sendHeader("Content-Encoding", "gzip");
|
_server->sendHeader("Content-Encoding", "gzip");
|
||||||
|
@ -35,10 +39,19 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
|
|
||||||
#if ELEGANTOTA_USE_ASYNC_WEBSERVER == 1
|
#if ELEGANTOTA_USE_ASYNC_WEBSERVER == 1
|
||||||
_server->on("/ota/start", HTTP_GET, [&](AsyncWebServerRequest *request) {
|
_server->on("/ota/start", HTTP_GET, [&](AsyncWebServerRequest *request) {
|
||||||
if (_authenticate && !request->authenticate(_username, _password)) {
|
if (_authenticate && !request->authenticate(_username.c_str(), _password.c_str())) {
|
||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pre-OTA update callback
|
||||||
|
if (preUpdateCallback != NULL) preUpdateCallback();
|
||||||
|
|
||||||
|
// Sleep for 3 seconds to allow asynchronous preUpdateCallback tasks to complete
|
||||||
|
unsigned long sleepStart = millis();
|
||||||
|
while (millis() - sleepStart < 3000) { // Sleep for 3 second
|
||||||
|
delay(1); // Yield to other tasks
|
||||||
|
}
|
||||||
|
|
||||||
// Get header x-ota-mode value, if present
|
// Get header x-ota-mode value, if present
|
||||||
OTA_Mode mode = OTA_MODE_FIRMWARE;
|
OTA_Mode mode = OTA_MODE_FIRMWARE;
|
||||||
// Get mode from arg
|
// Get mode from arg
|
||||||
|
@ -69,7 +82,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Pre-OTA update callback
|
// Pre-OTA update callback
|
||||||
if (preUpdateCallback != NULL) preUpdateCallback();
|
//if (preUpdateCallback != NULL) preUpdateCallback();
|
||||||
|
|
||||||
// Start update process
|
// Start update process
|
||||||
#if defined(ESP8266)
|
#if defined(ESP8266)
|
||||||
|
@ -84,7 +97,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
StreamString str;
|
StreamString str;
|
||||||
Update.printError(str);
|
Update.printError(str);
|
||||||
_update_error_str = str.c_str();
|
_update_error_str = str.c_str();
|
||||||
_update_error_str += "\n";
|
_update_error_str.concat("\n");
|
||||||
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
|
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
|
||||||
}
|
}
|
||||||
#elif defined(ESP32)
|
#elif defined(ESP32)
|
||||||
|
@ -94,7 +107,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
StreamString str;
|
StreamString str;
|
||||||
Update.printError(str);
|
Update.printError(str);
|
||||||
_update_error_str = str.c_str();
|
_update_error_str = str.c_str();
|
||||||
_update_error_str += "\n";
|
_update_error_str.concat("\n");
|
||||||
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
|
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
@ -103,7 +116,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
});
|
});
|
||||||
#else
|
#else
|
||||||
_server->on("/ota/start", HTTP_GET, [&]() {
|
_server->on("/ota/start", HTTP_GET, [&]() {
|
||||||
if (_authenticate && !_server->authenticate(_username, _password)) {
|
if (_authenticate && !_server->authenticate(_username.c_str(), _password.c_str())) {
|
||||||
return _server->requestAuthentication();
|
return _server->requestAuthentication();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,7 +165,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
StreamString str;
|
StreamString str;
|
||||||
Update.printError(str);
|
Update.printError(str);
|
||||||
_update_error_str = str.c_str();
|
_update_error_str = str.c_str();
|
||||||
_update_error_str += "\n";
|
_update_error_str.concat("\n");
|
||||||
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
|
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
|
||||||
}
|
}
|
||||||
#elif defined(ESP32)
|
#elif defined(ESP32)
|
||||||
|
@ -162,7 +175,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
StreamString str;
|
StreamString str;
|
||||||
Update.printError(str);
|
Update.printError(str);
|
||||||
_update_error_str = str.c_str();
|
_update_error_str = str.c_str();
|
||||||
_update_error_str += "\n";
|
_update_error_str.concat("\n");
|
||||||
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
|
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
|
||||||
}
|
}
|
||||||
#elif defined(TARGET_RP2040)
|
#elif defined(TARGET_RP2040)
|
||||||
|
@ -191,7 +204,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
|
|
||||||
#if ELEGANTOTA_USE_ASYNC_WEBSERVER == 1
|
#if ELEGANTOTA_USE_ASYNC_WEBSERVER == 1
|
||||||
_server->on("/ota/upload", HTTP_POST, [&](AsyncWebServerRequest *request) {
|
_server->on("/ota/upload", HTTP_POST, [&](AsyncWebServerRequest *request) {
|
||||||
if(_authenticate && !request->authenticate(_username, _password)){
|
if(_authenticate && !request->authenticate(_username.c_str(), _password.c_str())){
|
||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
}
|
}
|
||||||
// Post-OTA update callback
|
// Post-OTA update callback
|
||||||
|
@ -210,7 +223,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
}, [&](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
}, [&](AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
|
||||||
//Upload handler chunks in data
|
//Upload handler chunks in data
|
||||||
if(_authenticate){
|
if(_authenticate){
|
||||||
if(!request->authenticate(_username, _password)){
|
if(!request->authenticate(_username.c_str(), _password.c_str())){
|
||||||
return request->requestAuthentication();
|
return request->requestAuthentication();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,7 +249,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
StreamString str;
|
StreamString str;
|
||||||
Update.printError(str);
|
Update.printError(str);
|
||||||
_update_error_str = str.c_str();
|
_update_error_str = str.c_str();
|
||||||
_update_error_str += "\n";
|
_update_error_str.concat("\n");
|
||||||
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
|
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
|
@ -245,7 +258,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
});
|
});
|
||||||
#else
|
#else
|
||||||
_server->on("/ota/upload", HTTP_POST, [&](){
|
_server->on("/ota/upload", HTTP_POST, [&](){
|
||||||
if (_authenticate && !_server->authenticate(_username, _password)) {
|
if (_authenticate && !_server->authenticate(_username.c_str(), _password.c_str())) {
|
||||||
return _server->requestAuthentication();
|
return _server->requestAuthentication();
|
||||||
}
|
}
|
||||||
// Post-OTA update callback
|
// Post-OTA update callback
|
||||||
|
@ -264,7 +277,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
HTTPUpload& upload = _server->upload();
|
HTTPUpload& upload = _server->upload();
|
||||||
if (upload.status == UPLOAD_FILE_START) {
|
if (upload.status == UPLOAD_FILE_START) {
|
||||||
// Check authentication
|
// Check authentication
|
||||||
if (_authenticate && !_server->authenticate(_username, _password)) {
|
if (_authenticate && !_server->authenticate(_username.c_str(), _password.c_str())) {
|
||||||
ELEGANTOTA_DEBUG_MSG("Authentication Failed on UPLOAD_FILE_START\n");
|
ELEGANTOTA_DEBUG_MSG("Authentication Failed on UPLOAD_FILE_START\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -289,7 +302,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
StreamString str;
|
StreamString str;
|
||||||
Update.printError(str);
|
Update.printError(str);
|
||||||
_update_error_str = str.c_str();
|
_update_error_str = str.c_str();
|
||||||
_update_error_str += "\n";
|
_update_error_str.concat("\n");
|
||||||
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
|
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -304,11 +317,9 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElegantOTAClass::setAuth(const char * username, const char * password){
|
void ElegantOTAClass::setAuth(const char * username, const char * password){
|
||||||
if (strlen(username) > 0 && strlen(password) > 0) {
|
_username = username;
|
||||||
strlcpy(_username, username, sizeof(_username));
|
_password = password;
|
||||||
strlcpy(_password, password, sizeof(_password));
|
_authenticate = _username.length() && _password.length();
|
||||||
_authenticate = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ElegantOTAClass::clearAuth(){
|
void ElegantOTAClass::clearAuth(){
|
||||||
|
|
|
@ -119,8 +119,8 @@ class ElegantOTAClass{
|
||||||
ELEGANTOTA_WEBSERVER *_server;
|
ELEGANTOTA_WEBSERVER *_server;
|
||||||
|
|
||||||
bool _authenticate;
|
bool _authenticate;
|
||||||
char _username[64];
|
String _username;
|
||||||
char _password[64];
|
String _password;
|
||||||
|
|
||||||
bool _auto_reboot = true;
|
bool _auto_reboot = true;
|
||||||
bool _reboot = false;
|
bool _reboot = false;
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -3,6 +3,6 @@
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
extern const uint8_t ELEGANT_HTML[9590];
|
extern const uint8_t ELEGANT_HTML[40500];
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue