Merge branch 'main' into feature/DIY-battery-RJXZS-bms

This commit is contained in:
Daniel Öster 2024-09-16 14:58:51 +03:00
commit 86b999e643
32 changed files with 733 additions and 479 deletions

View file

@ -45,7 +45,7 @@ jobs:
- RENAULT_KANGOO_BATTERY
- RENAULT_ZOE_GEN1_BATTERY
- RENAULT_ZOE_GEN2_BATTERY
- TESLA_MODEL_3_BATTERY
- TESLA_MODEL_3Y_BATTERY
- VOLVO_SPA_BATTERY
- TEST_FAKE_BATTERY
- SERIAL_LINK_RECEIVER

View file

@ -47,7 +47,7 @@ jobs:
- RENAULT_KANGOO_BATTERY
- RENAULT_ZOE_GEN1_BATTERY
- RENAULT_ZOE_GEN2_BATTERY
- TESLA_MODEL_3_BATTERY
- TESLA_MODEL_3Y_BATTERY
- VOLVO_SPA_BATTERY
- TEST_FAKE_BATTERY
# These are the emulated inverter communication protocols for which the code will be compiled.

View file

@ -39,7 +39,7 @@ jobs:
# - KIA_HYUNDAI_64_BATTERY
- NISSAN_LEAF_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.
inverter:
- BYD_CAN

View file

@ -38,7 +38,7 @@
Preferences settings; // Store user settings
// The current software version, shown on webserver
const char* version_number = "7.2.dev";
const char* version_number = "7.3.dev";
// Interval settings
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 check_pause_2s(INTERVAL_2_S);
// Contactor parameters
#ifdef CONTACTOR_CONTROL
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.core_task_10s_max_us = 0;
}
if (check_pause_2s.elapsed()) {
emulator_pause_state_send_CAN_battery();
}
#endif
vTaskDelayUntil(&xLastWakeTime, xFrequency);
}
@ -575,6 +581,7 @@ void receive_can_native() { // This section checks if we have a complete CAN me
void send_can() {
if (can_send_CAN)
send_can_battery();
#ifdef CAN_INVERTER_SELECTED
@ -582,6 +589,7 @@ void send_can() {
#endif // CAN_INVERTER_SELECTED
#ifdef CHARGER_SELECTED
if (can_send_CAN)
send_can_charger();
#endif // CHARGER_SELECTED
}

View file

@ -24,7 +24,8 @@
//#define RENAULT_ZOE_GEN1_BATTERY
//#define RENAULT_ZOE_GEN2_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 TEST_FAKE_BATTERY
//#define DOUBLE_BATTERY //Enable this line if you use two identical batteries at the same time (requires DUAL_CAN setup)

View file

@ -66,8 +66,9 @@
#include "SANTA-FE-PHEV-BATTERY.h"
#endif
#ifdef TESLA_MODEL_3_BATTERY
#include "TESLA-MODEL-3-BATTERY.h"
#if defined(TESLA_MODEL_SX_BATTERY) || defined(TESLA_MODEL_3Y_BATTERY)
#define TESLA_BATTERY
#include "TESLA-BATTERY.h"
#endif
#ifdef TEST_FAKE_BATTERY

View file

@ -1,8 +1,8 @@
#include "../include.h"
#ifdef TESLA_MODEL_3_BATTERY
#ifdef TESLA_BATTERY
#include "../datalayer/datalayer.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 */
/* 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);
#ifdef TESLA_MODEL_3Y_BATTERY
// Autodetect algoritm for chemistry on 3/Y packs.
// 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!
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
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_LFP;
datalayer.battery.info.min_design_voltage_dV = MIN_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_3Y_LFP;
} else { // NCM/A chemistry
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_NCMA;
datalayer.battery.info.min_design_voltage_dV = MIN_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_3Y_NCMA;
}
#endif // TESLA_MODEL_3Y_BATTERY
//Check if SOC% is plausible
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);
#ifdef TESLA_MODEL_3Y_BATTERY
// Autodetect algoritm for chemistry on 3/Y packs.
// 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!
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
if (datalayer.battery2.info.chemistry == battery_chemistry_enum::LFP) {
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_LFP;
datalayer.battery2.info.min_design_voltage_dV = MIN_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_3Y_LFP;
} else { // NCM/A chemistry
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_NCMA;
datalayer.battery2.info.min_design_voltage_dV = MIN_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_3Y_NCMA;
}
#endif // TESLA_MODEL_3Y_BATTERY
//Check if SOC% is plausible
if (datalayer.battery2.status.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
#ifdef DEBUG_VIA_USB
Serial.println("Tesla Model 3 battery selected");
Serial.println("Tesla Model S/3/X/Y battery selected");
#endif
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
datalayer.battery.info.chemistry = battery_chemistry_enum::LFP;
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_LFP;
datalayer.battery.info.min_design_voltage_dV = MIN_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_3Y_LFP;
#ifdef DOUBLE_BATTERY
datalayer.battery2.info.chemistry = battery_chemistry_enum::LFP;
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_LFP;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_LFP;
#endif
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_LFP;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_LFP;
#endif // DOUBLE_BATTERY
#else // Startup in NCM/A mode
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_NCMA;
datalayer.battery.info.min_design_voltage_dV = MIN_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_3Y_NCMA;
#ifdef DOUBLE_BATTERY
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_NCMA;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_NCMA;
#endif
#endif
datalayer.battery2.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_3Y_NCMA;
datalayer.battery2.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_3Y_NCMA;
#endif // DOUBLE_BATTERY
#endif // !LFP_CHEMISTRY
#endif // TESLA_MODEL_3Y_BATTERY
}
#endif
#endif // TESLA_BATTERY

View file

@ -1,5 +1,5 @@
#ifndef TESLA_MODEL_3_BATTERY_H
#define TESLA_MODEL_3_BATTERY_H
#ifndef TESLA_BATTERY_H
#define TESLA_BATTERY_H
#include "../include.h"
#define BATTERY_SELECTED
@ -14,10 +14,13 @@
#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_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_LFP 3880 // 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_SX_NCMA 4600 // 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 MAX_PACK_VOLTAGE_3Y_NCMA 4030 // V+1, if pack voltage goes over this, charge stops
#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();

View file

@ -48,6 +48,16 @@ SensorConfig sensorConfigs[] = {
{"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"},
{"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) {
@ -81,10 +91,13 @@ static void publish_common_info(void) {
doc["unique_id"] = "battery-emulator_" + String(hostname) + "_" + String(config.object_id);
doc["object_id"] = String(hostname) + "_" + String(config.object_id);
doc["value_template"] = config.value_template;
if (config.unit != nullptr && strlen(config.unit) > 0)
doc["unit_of_measurement"] = config.unit;
if (config.device_class != nullptr && strlen(config.device_class) > 0) {
doc["device_class"] = config.device_class;
doc["enabled_by_default"] = true;
doc["state_class"] = "measurement";
}
doc["enabled_by_default"] = true;
doc["expire_after"] = 240;
doc["device"]["identifiers"][0] = "battery-emulator";
doc["device"]["manufacturer"] = "DalaTech";
@ -95,10 +108,16 @@ static void publish_common_info(void) {
doc["origin"]["url"] = "https://github.com/dalathegreat/Battery-Emulator";
serializeJson(doc, mqtt_msg);
mqtt_publish(generateCommonInfoAutoConfigTopic(config.object_id, hostname).c_str(), mqtt_msg, true);
}
doc.clear();
}
} else {
#endif // HA_AUTODISCOVERY
doc["bms_status"] = getBMSStatus(datalayer.battery.status.bms_status);
doc["pause_status"] = get_emulator_pause_status();
//only publish these values if BMS is active and we are comunication with the battery (can send CAN messages to the battery)
if (datalayer.battery.status.bms_status == ACTIVE && can_send_CAN && millis() > BOOTUP_TIME) {
doc["SOC"] = ((float)datalayer.battery.status.reported_soc) / 100.0;
doc["SOC_real"] = ((float)datalayer.battery.status.real_soc) / 100.0;
doc["state_of_health"] = ((float)datalayer.battery.status.soh_pptt) / 100.0;
@ -113,6 +132,12 @@ static void publish_common_info(void) {
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);
if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) {
#ifdef DEBUG_VIA_USB

View file

@ -9,9 +9,23 @@ static bool battery_empty_event_fired = false;
#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() {
// 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!
if (datalayer.battery.status.temperature_max_dC > 500) {
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
// 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) {
set_event(EVENT_CAN2_RX_FAILURE, 0);
} else {
@ -171,3 +192,67 @@ void update_machineryprotection() {
#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

View file

@ -1,11 +1,26 @@
#ifndef SAFETY_H
#define SAFETY_H
#include <Arduino.h>
#include <string>
#define MAX_CAN_FAILURES 50
#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();
//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

View file

@ -198,6 +198,8 @@ void init_events(void) {
events.entries[EVENT_RESET_EFUSE].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_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...
@ -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";
case EVENT_RESET_CPU_LOCKUP:
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:
return "";
}

View file

@ -93,6 +93,8 @@
XX(EVENT_RESET_EFUSE) \
XX(EVENT_RESET_PWR_GLITCH) \
XX(EVENT_RESET_CPU_LOCKUP) \
XX(EVENT_PAUSE_BEGIN) \
XX(EVENT_PAUSE_END) \
XX(EVENT_NOF_EVENTS)
typedef enum { EVENTS_ENUM_TYPE(GENERATE_ENUM) } EVENTS_ENUM_TYPE;

View 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";
}
}

View file

@ -1,6 +1,8 @@
#ifndef _TYPES_H_
#define _TYPES_H_
#include <string>
enum bms_status_enum { STANDBY = 0, INACTIVE = 1, DARKSTART = 2, ACTIVE = 3, FAULT = 4, UPDATING = 5 };
enum battery_chemistry_enum { NCA, NMC, LFP };
enum led_color { GREEN, YELLOW, RED, BLUE, RGB };
@ -46,4 +48,6 @@ typedef struct {
} data;
} CAN_frame;
std::string getBMSStatus(bms_status_enum status);
#endif

View file

@ -1,6 +1,4 @@
#include "events_html.h"
#include <Arduino.h>
#include "../utils/events.h"
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>
@ -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>
)=====";
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) {
if (var == "X") {
String content = "";
@ -22,16 +27,29 @@ String events_processor(const String& var) {
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++) {
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
Serial.println("Event: " + String(get_event_enum_string(event_handle)) +
" count: " + String(event_pointer->occurences) + " seconds: " + String(event_pointer->timestamp) +
" data: " + String(event_pointer->data) +
" level: " + String(get_event_level_string(event_handle)));
#endif
if (event_pointer->occurences > 0) {
content.concat("<div class='event'>");
content.concat("<div>" + String(get_event_enum_string(event_handle)) + "</div>");
content.concat("<div>" + String(get_event_level_string(event_handle)) + "</div>");
@ -41,11 +59,13 @@ String events_processor(const String& var) {
content.concat("<div>" + String(get_event_message_string(event_handle)) + "</div>");
content.concat("</div>"); // End of event row
}
}
//clear the vector
order_events.clear();
content.concat(FPSTR(EVENTS_HTML_END));
return content;
}
return String();
}
}
/* Script for displaying event log before it gets minified

View file

@ -2,6 +2,9 @@
#define EVENTS_H
#include <Arduino.h>
#include <algorithm>
#include <vector>
#include "../utils/events.h"
/**
* @brief Replaces placeholder with content section in web page
@ -11,5 +14,10 @@
* @return String
*/
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

View file

@ -1,6 +1,7 @@
#include "webserver.h"
#include <Preferences.h>
#include "../../datalayer/datalayer.h"
#include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h"
#include "../utils/events.h"
#include "../utils/led_handler.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 wifi_reconnect_interval = DEFAULT_WIFI_RECONNECT_INTERVAL;
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() {
// Configure WiFi
@ -53,6 +55,13 @@ void init_webserver() {
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
server.on("/", HTTP_GET, [](AsyncWebServerRequest* request) {
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
server.on("/updateSocMin", HTTP_GET, [](AsyncWebServerRequest* request) {
if (WEBSERVER_AUTH_REQUIRED && !request->authenticate(http_username, http_password))
@ -414,10 +436,8 @@ void wifi_monitor() {
if (ota_active && ota_timeout_timer.elapsed()) {
// 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);
ota_active = false;
onOTAEnd(false);
}
}
@ -444,6 +464,24 @@ void init_ElegantOTA() {
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) {
if (var == "X") {
String content = "";
@ -576,9 +614,12 @@ String processor(const String& var) {
#ifdef SERIAL_LINK_RECEIVER
content += "Serial link to another LilyGo board";
#endif // SERIAL_LINK_RECEIVER
#ifdef TESLA_MODEL_3_BATTERY
content += "Tesla Model S/3/X/Y";
#endif // TESLA_MODEL_3_BATTERY
#ifdef TESLA_MODEL_SX_BATTERY
content += "Tesla Model S/X";
#endif // TESLA_MODEL_SX_BATTERY
#ifdef TESLA_MODEL_3Y_BATTERY
content += "Tesla Model 3/Y";
#endif // TESLA_MODEL_3Y_BATTERY
#ifdef VOLVO_SPA_BATTERY
content += "Volvo / Polestar 78kWh battery";
#endif // VOLVO_SPA_BATTERY
@ -587,6 +628,9 @@ String processor(const String& var) {
#endif // TEST_FAKE_BATTERY
#ifdef DOUBLE_BATTERY
content += " (Double battery)";
if (datalayer.battery.info.chemistry == battery_chemistry_enum::LFP) {
content += " (LFP)";
}
#endif // DOUBLE_BATTERY
content += "</h4>";
@ -693,6 +737,10 @@ String processor(const String& var) {
} else {
content += "<span style='color: red;'>&#10005;</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
content += "</div>";
@ -769,6 +817,10 @@ String processor(const String& var) {
} else {
content += "<span style='color: red;'>&#10005;</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>";
@ -830,6 +882,11 @@ String processor(const String& var) {
content += "</div>";
#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 += " ";
content += "<button onclick='Settings()'>Change Settings</button>";
@ -863,6 +920,12 @@ String processor(const String& var) {
content += " setTimeout(function(){ window.open(\"/\",\"_self\"); }, 1000);";
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>";
@ -877,8 +940,10 @@ String processor(const String& var) {
}
void onOTAStart() {
//try to Pause the battery
setBatteryPause(true, true);
// Log when OTA has started
ESP32Can.CANStop();
set_event(EVENT_OTA_UPDATE, 0);
// If already set, make a new attempt
@ -900,8 +965,13 @@ void onOTAProgress(size_t current, size_t final) {
}
void onOTAEnd(bool success) {
ota_active = false;
clear_event(EVENT_OTA_UPDATE);
// Log when OTA has finished
if (success) {
// a reboot will be done by the OTA library. no need to do anything here
#ifdef DEBUG_VIA_USB
Serial.println("OTA update finished successfully!");
#endif // DEBUG_VIA_USB
@ -909,12 +979,9 @@ void onOTAEnd(bool success) {
#ifdef DEBUG_VIA_USB
Serial.println("There was an error during OTA update!");
#endif // DEBUG_VIA_USB
// If we fail without a timeout, try to restore CAN
ESP32Can.CANInit();
//try to Resume the battery pause and CAN communication
setBatteryPause(false, false);
}
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

View file

@ -103,6 +103,7 @@ void init_ElegantOTA();
* @return String
*/
String processor(const String& var);
String get_firmware_info_processor(const String& var);
/**
* @brief Executes on OTA start

View file

@ -6,7 +6,7 @@
/* TODO: Map error bits in 0x158 */
/* 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
CAN_frame SMA_358 = {.FD = false,
@ -51,7 +51,7 @@ CAN_frame SMA_598 = {.FD = false,
.ext_ID = false,
.DLC = 8,
.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,
.ext_ID = false,
.DLC = 8,
@ -221,12 +221,14 @@ void receive_can_inverter(CAN_frame rx_frame) {
break;
case 0x420: //Message originating from SMA inverter - Timestamp
//Frame0-3 Timestamp
/*
transmit_can(&SMA_158, can_config.inverter);
transmit_can(&SMA_358, can_config.inverter);
transmit_can(&SMA_3D8, can_config.inverter);
transmit_can(&SMA_458, can_config.inverter);
transmit_can(&SMA_518, can_config.inverter);
transmit_can(&SMA_4D8, can_config.inverter);
*/
break;
case 0x5E0: //Message originating from SMA inverter - String
break;
@ -254,9 +256,10 @@ void receive_can_inverter(CAN_frame rx_frame) {
void send_can_inverter() {
unsigned long currentMillis = millis();
// Send CAN Message every 60s
if (currentMillis - previousMillis60s >= INTERVAL_60_S) {
previousMillis60s = currentMillis;
// Send CAN Message every 100ms if we're enabled
if (datalayer.system.status.inverter_allows_contactor_closing) {
if (currentMillis - previousMillis100ms >= 100) {
previousMillis100ms = currentMillis;
transmit_can(&SMA_158, can_config.inverter);
transmit_can(&SMA_358, can_config.inverter);
transmit_can(&SMA_3D8, can_config.inverter);
@ -264,5 +267,6 @@ void send_can_inverter() {
transmit_can(&SMA_518, can_config.inverter);
transmit_can(&SMA_4D8, can_config.inverter);
}
}
}
#endif

View file

@ -6,7 +6,7 @@
/* TODO: Map error bits in 0x158 */
/* 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
CAN_frame SMA_558 = {.FD = false,
@ -218,12 +218,14 @@ void receive_can_inverter(CAN_frame rx_frame) {
break;
case 0x420: //Message originating from SMA inverter - Timestamp
//Frame0-3 Timestamp
/*
transmit_can(&SMA_158, can_config.inverter);
transmit_can(&SMA_358, can_config.inverter);
transmit_can(&SMA_3D8, can_config.inverter);
transmit_can(&SMA_458, can_config.inverter);
transmit_can(&SMA_518, can_config.inverter);
transmit_can(&SMA_4D8, can_config.inverter);
*/
break;
case 0x5E0: //Message originating from SMA inverter - String
break;
@ -251,9 +253,10 @@ void receive_can_inverter(CAN_frame rx_frame) {
void send_can_inverter() {
unsigned long currentMillis = millis();
// Send CAN Message every 60s
if (currentMillis - previousMillis60s >= INTERVAL_60_S) {
previousMillis60s = currentMillis;
// Send CAN Message every 100ms if Enable line is HIGH
if (datalayer.system.status.inverter_allows_contactor_closing) {
if (currentMillis - previousMillis100ms >= 100) {
previousMillis100ms = currentMillis;
transmit_can(&SMA_158, can_config.inverter);
transmit_can(&SMA_358, can_config.inverter);
@ -262,5 +265,6 @@ void send_can_inverter() {
transmit_can(&SMA_518, can_config.inverter);
transmit_can(&SMA_4D8, can_config.inverter);
}
}
}
#endif

View file

@ -37,3 +37,4 @@ node_modules
.vscode
/build
/portal
.pio

File diff suppressed because one or more lines are too long

View file

@ -1,19 +1,24 @@
<p><br/></p>
<p align="center"><img src="/docs/feature.png?sanitize=true&raw=true" width="700"></p>
<p align="center">
<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/>
<p align="center">
<p>
<img src="https://img.shields.io/github/last-commit/ayushsharma82/ElegantOTA.svg?style=for-the-badge" />
&nbsp;
<img src="https://img.shields.io/github/actions/workflow/status/ayushsharma82/ElegantOTA/ci.yml?branch=master&style=for-the-badge" />
&nbsp;
<img src="https://img.shields.io/github/license/ayushsharma82/ElegantOTA.svg?style=for-the-badge" />
</p>
<br/>
<p>Over-the-Air update library for wireless microcontrollers</p>
<p align="center">OTA update library for wireless microcontrollers</p>
<p align="center">
<p>
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>
@ -21,6 +26,7 @@ ElegantOTA provides a beautiful user interface to upload over-the-air firmware/f
<br/>
## Features
- 🔥 Quick & Simple OTA procedure
- 🏀 Get useful insight on progress and status of your OTA update
- 🎷 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/>
## Supported MCUs
ElegantOTA works on the following microcontrollers/boards:
- ESP8266
- ESP32
- RP2040 ( Pico W )
@ -51,12 +59,12 @@ ElegantOTA works on the following microcontrollers/boards:
*Preview might appear as blurry due to image optimization.*
<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>
## Want More? Upgrade to Pro
## Looking for more? Upgrade to Pro.
ElegantOTA Pro comes with the following extended functionality:
- New Drag & Drop Zone
@ -74,7 +82,7 @@ ElegantOTA Pro comes with the following extended functionality:
<br/>
<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>
<br>

View file

@ -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.

View file

@ -15,7 +15,18 @@
"maintainer": true
}
],
"version": "3.1.1",
"dependencies": [
{
"owner": "mathieucarbou",
"name": "ESPAsyncWebServer",
"version": "^3.1.1",
"platforms": ["espressif8266", "espressif32"]
}
],
"version": "3.1.5",
"frameworks": "arduino",
"platforms": ["espressif8266", "espressif32", "raspberrypi"]
"platforms": ["espressif8266", "espressif32", "raspberrypi"],
"build": {
"libCompatMode": "strict"
}
}

View file

@ -1,5 +1,5 @@
name=ElegantOTA
version=3.1.1
version=3.1.5
author=Ayush Sharma
category=Communication
maintainer=Ayush Sharma <asrocks5@gmail.com>

View file

@ -6,12 +6,13 @@
#
# extra_scripts = platformio_upload.py
# upload_protocol = custom
# upload_url = <your upload URL>
# custom_upload_url = <your upload URL>
#
# An example of an upload URL:
# upload_url = http://192.168.1.123/update
# also possible: upload_url = http://domainname/update
# custom_upload_url = http://192.168.1.123/update
# also possible: custom_upload_url = http://domainname/update
import sys
import requests
import hashlib
from urllib.parse import urlparse
@ -54,7 +55,10 @@ def on_upload(source, target, env):
'Connection': 'keep-alive'
}
try:
checkAuthResponse = requests.get(f"{upload_url_compatibility}/update")
except Exception as e:
return 'Error checking auth: ' + repr(e)
if checkAuthResponse.status_code == 401:
try:
@ -66,24 +70,27 @@ def on_upload(source, target, env):
print("No authentication values specified.")
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:
print("Authentication required, but no credentials provided.")
return
return "Authentication required, but no credentials provided."
print("Serverconfiguration: authentication needed.")
auth = HTTPDigestAuth(username, password)
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:
print("authentication faild " + str(doUpdateAuth.status_code))
return
print("Authentication successfull")
return "Authentication failed " + str(doUpdateAuth.status_code)
print("Authentication successful")
else:
auth = None
print("Serverconfiguration: autentication not needed.")
print("Serverconfiguration: authentication not needed.")
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:
print("start-request faild " + str(doUpdate.status_code))
return
return "Start request failed " + str(doUpdate.status_code)
firmware.seek(0)
encoder = MultipartEncoder(fields={
@ -114,14 +121,16 @@ def on_upload(source, target, env):
'Origin': f'{upload_url}'
}
try:
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()
time.sleep(0.1)
if response.status_code != 200:
message = "\nUpload faild.\nServer response: " + response.text
message = "\nUpload failed.\nServer response: " + response.text
tqdm.write(message)
else:
message = "\nUpload successful.\nServer response: " + response.text

View file

@ -16,16 +16,20 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
#if ELEGANTOTA_USE_ASYNC_WEBSERVER == 1
_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();
}
#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");
request->send(response);
});
#else
_server->on("/update", HTTP_GET, [&](){
if (_authenticate && !_server->authenticate(_username, _password)) {
if (_authenticate && !_server->authenticate(_username.c_str(), _password.c_str())) {
return _server->requestAuthentication();
}
_server->sendHeader("Content-Encoding", "gzip");
@ -35,10 +39,19 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
#if ELEGANTOTA_USE_ASYNC_WEBSERVER == 1
_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();
}
// 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
OTA_Mode mode = OTA_MODE_FIRMWARE;
// Get mode from arg
@ -69,7 +82,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
#endif
// Pre-OTA update callback
if (preUpdateCallback != NULL) preUpdateCallback();
//if (preUpdateCallback != NULL) preUpdateCallback();
// Start update process
#if defined(ESP8266)
@ -84,7 +97,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
StreamString str;
Update.printError(str);
_update_error_str = str.c_str();
_update_error_str += "\n";
_update_error_str.concat("\n");
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
}
#elif defined(ESP32)
@ -94,7 +107,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
StreamString str;
Update.printError(str);
_update_error_str = str.c_str();
_update_error_str += "\n";
_update_error_str.concat("\n");
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
}
#endif
@ -103,7 +116,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
});
#else
_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();
}
@ -152,7 +165,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
StreamString str;
Update.printError(str);
_update_error_str = str.c_str();
_update_error_str += "\n";
_update_error_str.concat("\n");
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
}
#elif defined(ESP32)
@ -162,7 +175,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
StreamString str;
Update.printError(str);
_update_error_str = str.c_str();
_update_error_str += "\n";
_update_error_str.concat("\n");
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
}
#elif defined(TARGET_RP2040)
@ -191,7 +204,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
#if ELEGANTOTA_USE_ASYNC_WEBSERVER == 1
_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();
}
// 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) {
//Upload handler chunks in data
if(_authenticate){
if(!request->authenticate(_username, _password)){
if(!request->authenticate(_username.c_str(), _password.c_str())){
return request->requestAuthentication();
}
}
@ -236,7 +249,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
StreamString str;
Update.printError(str);
_update_error_str = str.c_str();
_update_error_str += "\n";
_update_error_str.concat("\n");
ELEGANTOTA_DEBUG_MSG(_update_error_str.c_str());
}
}else{
@ -245,7 +258,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
});
#else
_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();
}
// Post-OTA update callback
@ -264,7 +277,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
HTTPUpload& upload = _server->upload();
if (upload.status == UPLOAD_FILE_START) {
// 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");
return;
}
@ -289,7 +302,7 @@ void ElegantOTAClass::begin(ELEGANTOTA_WEBSERVER *server, const char * username,
StreamString str;
Update.printError(str);
_update_error_str = str.c_str();
_update_error_str += "\n";
_update_error_str.concat("\n");
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){
if (strlen(username) > 0 && strlen(password) > 0) {
strlcpy(_username, username, sizeof(_username));
strlcpy(_password, password, sizeof(_password));
_authenticate = true;
}
_username = username;
_password = password;
_authenticate = _username.length() && _password.length();
}
void ElegantOTAClass::clearAuth(){

View file

@ -119,8 +119,8 @@ class ElegantOTAClass{
ELEGANTOTA_WEBSERVER *_server;
bool _authenticate;
char _username[64];
char _password[64];
String _username;
String _password;
bool _auto_reboot = true;
bool _reboot = false;

File diff suppressed because one or more lines are too long

View file

@ -3,6 +3,6 @@
#include <Arduino.h>
extern const uint8_t ELEGANT_HTML[9590];
extern const uint8_t ELEGANT_HTML[40500];
#endif