mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 10:19:29 +02:00
merge with main branch
This commit is contained in:
commit
e84f73709d
53 changed files with 1126 additions and 308 deletions
|
@ -94,7 +94,7 @@ This code uses the following excellent libraries:
|
||||||
- [eModbus/eModbus](https://github.com/eModbus/eModbus) MIT-License
|
- [eModbus/eModbus](https://github.com/eModbus/eModbus) MIT-License
|
||||||
- [mackelec/SerialDataLink](https://github.com/mackelec/SerialDataLink)
|
- [mackelec/SerialDataLink](https://github.com/mackelec/SerialDataLink)
|
||||||
- [mathieucarbou/AsyncTCPsock](https://github.com/mathieucarbou/AsyncTCPSock) LGPL-3.0 license
|
- [mathieucarbou/AsyncTCPsock](https://github.com/mathieucarbou/AsyncTCPSock) LGPL-3.0 license
|
||||||
- [mathieucarbou/ESPAsyncWebServer](https://github.com/mathieucarbou/ESPAsyncWebServer) LGPL-3.0 license
|
- [ESP32Async/ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer) LGPL-3.0 license
|
||||||
- [miwagner/ESP32-Arduino-CAN](https://github.com/miwagner/ESP32-Arduino-CAN/) MIT-License
|
- [miwagner/ESP32-Arduino-CAN](https://github.com/miwagner/ESP32-Arduino-CAN/) MIT-License
|
||||||
- [pierremolinaro/acan2515](https://github.com/pierremolinaro/acan2515) MIT-License
|
- [pierremolinaro/acan2515](https://github.com/pierremolinaro/acan2515) MIT-License
|
||||||
- [pierremolinaro/acan2517FD](https://github.com/pierremolinaro/acan2517FD) MIT-License
|
- [pierremolinaro/acan2517FD](https://github.com/pierremolinaro/acan2517FD) MIT-License
|
||||||
|
|
|
@ -50,7 +50,7 @@
|
||||||
volatile unsigned long long bmsResetTimeOffset = 0;
|
volatile unsigned long long bmsResetTimeOffset = 0;
|
||||||
|
|
||||||
// The current software version, shown on webserver
|
// The current software version, shown on webserver
|
||||||
const char* version_number = "8.4.0";
|
const char* version_number = "8.5.dev";
|
||||||
|
|
||||||
// Interval settings
|
// Interval settings
|
||||||
uint16_t intervalUpdateValues = INTERVAL_1_S; // Interval at which to update inverter values / Modbus registers
|
uint16_t intervalUpdateValues = INTERVAL_1_S; // Interval at which to update inverter values / Modbus registers
|
||||||
|
|
|
@ -76,7 +76,6 @@
|
||||||
//#define NC_CONTACTORS //Enable this line to control normally closed contactors. CONTACTOR_CONTROL must be enabled for this option. Extremely rare setting!
|
//#define NC_CONTACTORS //Enable this line to control normally closed contactors. CONTACTOR_CONTROL must be enabled for this option. Extremely rare setting!
|
||||||
//#define PERIODIC_BMS_RESET //Enable to have the emulator powercycle the connected battery every 24hours via GPIO. Useful for some batteries like Nissan LEAF
|
//#define PERIODIC_BMS_RESET //Enable to have the emulator powercycle the connected battery every 24hours via GPIO. Useful for some batteries like Nissan LEAF
|
||||||
//#define REMOTE_BMS_RESET //Enable to allow the emulator to remotely trigger a powercycle of the battery via MQTT. Useful for some batteries like Nissan LEAF
|
//#define REMOTE_BMS_RESET //Enable to allow the emulator to remotely trigger a powercycle of the battery via MQTT. Useful for some batteries like Nissan LEAF
|
||||||
//#define REMOTE_BMS_RESET //Enable to allow the emulator to remotely trigger a powercycle of the battery via MQTT. Useful for some batteries like Nissan LEAF
|
|
||||||
#define PERIODIC_BMS_RESET_AT 525 // In 24 Hour format WITHOUT leading 0. e.g 0230 should be 230.
|
#define PERIODIC_BMS_RESET_AT 525 // In 24 Hour format WITHOUT leading 0. e.g 0230 should be 230.
|
||||||
|
|
||||||
/* Shunt/Contactor settings (Optional) */
|
/* Shunt/Contactor settings (Optional) */
|
||||||
|
|
|
@ -790,6 +790,9 @@ void setup_battery(void) { // Performs one time setup at startup
|
||||||
strncpy(datalayer.system.info.battery_protocol, "BMW iX and i4-7 platform", 63);
|
strncpy(datalayer.system.info.battery_protocol, "BMW iX and i4-7 platform", 63);
|
||||||
datalayer.system.info.battery_protocol[63] = '\0';
|
datalayer.system.info.battery_protocol[63] = '\0';
|
||||||
|
|
||||||
|
//Reset Battery at bootup
|
||||||
|
transmit_can_frame(&BMWiX_6F4_REQUEST_HARD_RESET, can_config.battery);
|
||||||
|
|
||||||
//Before we have started up and detected which battery is in use, use 108S values
|
//Before we have started up and detected which battery is in use, use 108S values
|
||||||
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
datalayer.battery.info.max_design_voltage_dV = MAX_PACK_VOLTAGE_DV;
|
||||||
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
datalayer.battery.info.min_design_voltage_dV = MIN_PACK_VOLTAGE_DV;
|
||||||
|
|
|
@ -15,7 +15,7 @@
|
||||||
#define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500
|
#define MAX_CHARGE_POWER_WHEN_TOPBALANCING_W 500
|
||||||
#define RAMPDOWN_SOC 9000 // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
|
#define RAMPDOWN_SOC 9000 // (90.00) SOC% to start ramping down from max charge power towards 0 at 100.00%
|
||||||
#define STALE_PERIOD_CONFIG \
|
#define STALE_PERIOD_CONFIG \
|
||||||
300000; //Number of milliseconds before critical values are classed as stale/stuck 300000 = 300 seconds
|
400000; //Number of milliseconds before critical values are classed as stale/stuck 300000 = 400 seconds
|
||||||
void setup_battery(void);
|
void setup_battery(void);
|
||||||
void transmit_can_frame(CAN_frame* tx_frame, int interface);
|
void transmit_can_frame(CAN_frame* tx_frame, int interface);
|
||||||
|
|
||||||
|
|
|
@ -40,6 +40,7 @@ static int8_t powerRelayTemperature = 0;
|
||||||
static bool startedUp = false;
|
static bool startedUp = false;
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
#ifdef DOUBLE_BATTERY
|
||||||
|
static uint8_t counter_200_2 = 0;
|
||||||
static uint16_t battery2_soc_calculated = 0;
|
static uint16_t battery2_soc_calculated = 0;
|
||||||
static uint16_t battery2_SOC_BMS = 0;
|
static uint16_t battery2_SOC_BMS = 0;
|
||||||
static uint16_t battery2_SOC_Display = 0;
|
static uint16_t battery2_SOC_Display = 0;
|
||||||
|
@ -69,23 +70,28 @@ static int8_t battery2_temperature_water_inlet = 0;
|
||||||
static int8_t battery2_heatertemp = 0;
|
static int8_t battery2_heatertemp = 0;
|
||||||
static int8_t battery2_powerRelayTemperature = 0;
|
static int8_t battery2_powerRelayTemperature = 0;
|
||||||
static bool battery2_startedUp = false;
|
static bool battery2_startedUp = false;
|
||||||
|
CAN_frame KIA_HYUNDAI_200_2 = {.FD = false,
|
||||||
|
.ext_ID = false,
|
||||||
|
.DLC = 8,
|
||||||
|
.ID = 0x200,
|
||||||
|
.data = {0x00, 0x80, 0xD8, 0x04, 0x00, 0x17, 0xD0, 0x00}}; //2nd battery
|
||||||
#endif //DOUBLE_BATTERY
|
#endif //DOUBLE_BATTERY
|
||||||
|
|
||||||
CAN_frame KIA_HYUNDAI_200 = {.FD = false,
|
CAN_frame KIA_HYUNDAI_200 = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x200,
|
.ID = 0x200,
|
||||||
.data = {0x00, 0x80, 0xD8, 0x04, 0x00, 0x17, 0xD0, 0x00}}; //Mid log value
|
.data = {0x00, 0x80, 0xD8, 0x04, 0x00, 0x17, 0xD0, 0x00}};
|
||||||
CAN_frame KIA_HYUNDAI_523 = {.FD = false,
|
CAN_frame KIA_HYUNDAI_523 = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x523,
|
.ID = 0x523,
|
||||||
.data = {0x08, 0x38, 0x36, 0x36, 0x33, 0x34, 0x00, 0x01}}; //Mid log value
|
.data = {0x08, 0x38, 0x36, 0x36, 0x33, 0x34, 0x00, 0x01}};
|
||||||
CAN_frame KIA_HYUNDAI_524 = {.FD = false,
|
CAN_frame KIA_HYUNDAI_524 = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
.DLC = 8,
|
.DLC = 8,
|
||||||
.ID = 0x524,
|
.ID = 0x524,
|
||||||
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}; //Initial value
|
.data = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};
|
||||||
//553 Needed frame 200ms
|
//553 Needed frame 200ms
|
||||||
CAN_frame KIA64_553 = {.FD = false,
|
CAN_frame KIA64_553 = {.FD = false,
|
||||||
.ext_ID = false,
|
.ext_ID = false,
|
||||||
|
@ -724,7 +730,7 @@ void handle_incoming_can_frame_battery2(CAN_frame rx_frame) {
|
||||||
case 0x10: //"PID Header"
|
case 0x10: //"PID Header"
|
||||||
if (rx_frame.data.u8[4] == battery2_poll_data_pid) {
|
if (rx_frame.data.u8[4] == battery2_poll_data_pid) {
|
||||||
transmit_can_frame(&KIA64_7E4_ack,
|
transmit_can_frame(&KIA64_7E4_ack,
|
||||||
can_config.battery); //Send ack to BMS if the same frame is sent as polled
|
can_config.battery_double); //Send ack to BMS if the same frame is sent as polled
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x21: //First frame in PID group
|
case 0x21: //First frame in PID group
|
||||||
|
@ -997,8 +1003,49 @@ void transmit_can_battery() {
|
||||||
transmit_can_frame(&KIA_HYUNDAI_524, can_config.battery);
|
transmit_can_frame(&KIA_HYUNDAI_524, can_config.battery);
|
||||||
|
|
||||||
#ifdef DOUBLE_BATTERY
|
#ifdef DOUBLE_BATTERY
|
||||||
|
|
||||||
if (battery2_startedUp && datalayer.system.status.battery2_allows_contactor_closing) {
|
if (battery2_startedUp && datalayer.system.status.battery2_allows_contactor_closing) {
|
||||||
transmit_can_frame(&KIA_HYUNDAI_200, can_config.battery_double);
|
switch (counter_200_2) {
|
||||||
|
case 0:
|
||||||
|
KIA_HYUNDAI_200_2.data.u8[5] = 0x17;
|
||||||
|
++counter_200_2;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
KIA_HYUNDAI_200_2.data.u8[5] = 0x57;
|
||||||
|
++counter_200_2;
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
KIA_HYUNDAI_200_2.data.u8[5] = 0x97;
|
||||||
|
++counter_200_2;
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
KIA_HYUNDAI_200_2.data.u8[5] = 0xD7;
|
||||||
|
++counter_200_2;
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
KIA_HYUNDAI_200_2.data.u8[3] = 0x10;
|
||||||
|
KIA_HYUNDAI_200_2.data.u8[5] = 0xFF;
|
||||||
|
++counter_200_2;
|
||||||
|
break;
|
||||||
|
case 5:
|
||||||
|
KIA_HYUNDAI_200_2.data.u8[5] = 0x3B;
|
||||||
|
++counter_200_2;
|
||||||
|
break;
|
||||||
|
case 6:
|
||||||
|
KIA_HYUNDAI_200_2.data.u8[5] = 0x7B;
|
||||||
|
++counter_200_2;
|
||||||
|
break;
|
||||||
|
case 7:
|
||||||
|
KIA_HYUNDAI_200_2.data.u8[5] = 0xBB;
|
||||||
|
++counter_200_2;
|
||||||
|
break;
|
||||||
|
case 8:
|
||||||
|
KIA_HYUNDAI_200_2.data.u8[5] = 0xFB;
|
||||||
|
counter_200_2 = 5;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
transmit_can_frame(&KIA_HYUNDAI_200_2, can_config.battery_double);
|
||||||
|
|
||||||
transmit_can_frame(&KIA_HYUNDAI_523, can_config.battery_double);
|
transmit_can_frame(&KIA_HYUNDAI_523, can_config.battery_double);
|
||||||
|
|
||||||
|
|
|
@ -10,15 +10,9 @@
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO list
|
TODO list
|
||||||
- Check value mappings on the PID polls
|
|
||||||
- Check all TODO:s in the code
|
- Check all TODO:s in the code
|
||||||
- 0x1B000044 & 1B00008F seems to be missing from logs? (Classic CAN)
|
|
||||||
- Scaled remaining capacity, should take already scaled total capacity into account, or we
|
|
||||||
should undo the scaling on the total capacity (which is calculated from the ah value now,
|
|
||||||
which is scaled already).
|
|
||||||
- Investigate why opening and then closing contactors from webpage does not always work
|
- Investigate why opening and then closing contactors from webpage does not always work
|
||||||
- Invertigate why contactors don't close when lilygo and battery are powered on simultaneously -> timeout on can msgs triggers to late, reset when open contactors is executed
|
- remaining_capacity_Wh is based on a lower limit of 5% soc. This means that at 5% soc, remaining_capacity_Wh returns 0.
|
||||||
- Find out how to get the battery in balancing mode
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* 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 */
|
||||||
|
@ -57,11 +51,6 @@ static uint16_t battery_allowed_charge_power = 0;
|
||||||
static uint16_t battery_allowed_discharge_power = 0;
|
static uint16_t battery_allowed_discharge_power = 0;
|
||||||
static uint16_t cellvoltages_polled[108];
|
static uint16_t cellvoltages_polled[108];
|
||||||
static uint16_t tempval = 0;
|
static uint16_t tempval = 0;
|
||||||
static uint8_t BMS_5A2_CRC = 0;
|
|
||||||
static uint8_t BMS_5CA_CRC = 0;
|
|
||||||
static uint8_t BMS_0CF_CRC = 0;
|
|
||||||
static uint8_t BMS_578_CRC = 0;
|
|
||||||
static uint8_t BMS_0C0_CRC = 0;
|
|
||||||
static uint8_t BMS_16A954A6_CRC = 0;
|
static uint8_t BMS_16A954A6_CRC = 0;
|
||||||
static uint8_t BMS_5A2_counter = 0;
|
static uint8_t BMS_5A2_counter = 0;
|
||||||
static uint8_t BMS_5CA_counter = 0;
|
static uint8_t BMS_5CA_counter = 0;
|
||||||
|
@ -144,7 +133,6 @@ static uint8_t target_flow_temperature_C = 0; //*0,5 -40
|
||||||
static uint8_t return_temperature_C = 0; //*0,5 -40
|
static uint8_t return_temperature_C = 0; //*0,5 -40
|
||||||
static uint8_t status_valve_1 = 0; //0 not active, 1 active, 5 not installed, 6 init, 7 fault
|
static uint8_t status_valve_1 = 0; //0 not active, 1 active, 5 not installed, 6 init, 7 fault
|
||||||
static uint8_t status_valve_2 = 0; //0 not active, 1 active, 5 not installed, 6 init, 7 fault
|
static uint8_t status_valve_2 = 0; //0 not active, 1 active, 5 not installed, 6 init, 7 fault
|
||||||
static uint8_t battery_temperature = 0;
|
|
||||||
static uint8_t temperature_request =
|
static uint8_t temperature_request =
|
||||||
0; //0 high cooling, 1 medium cooling, 2 low cooling, 3 no temp requirement init, 4 low heating , 5 medium heating, 6 high heating, 7 circulation
|
0; //0 high cooling, 1 medium cooling, 2 low cooling, 3 no temp requirement init, 4 low heating , 5 medium heating, 6 high heating, 7 circulation
|
||||||
static uint16_t performance_index_discharge_peak_temperature_percentage = 0;
|
static uint16_t performance_index_discharge_peak_temperature_percentage = 0;
|
||||||
|
@ -164,7 +152,6 @@ static uint16_t actual_cellvoltage_lowest_mV = 0; //bias 1000
|
||||||
static uint16_t predicted_power_dyn_standard_watt = 0;
|
static uint16_t predicted_power_dyn_standard_watt = 0;
|
||||||
static uint8_t predicted_time_dyn_standard_minutes = 0;
|
static uint8_t predicted_time_dyn_standard_minutes = 0;
|
||||||
static uint8_t mux = 0;
|
static uint8_t mux = 0;
|
||||||
static int8_t celltemperature[56] = {0}; //Temperatures 1-56. Value is 0xFD if sensor not present
|
|
||||||
static uint16_t cellvoltages[160] = {0};
|
static uint16_t cellvoltages[160] = {0};
|
||||||
static uint16_t duration_discharge_power_watt = 0;
|
static uint16_t duration_discharge_power_watt = 0;
|
||||||
static uint16_t duration_charge_power_watt = 0;
|
static uint16_t duration_charge_power_watt = 0;
|
||||||
|
@ -197,6 +184,7 @@ static bool instrumentation_cluster_request = false;
|
||||||
static uint8_t seconds = 0;
|
static uint8_t seconds = 0;
|
||||||
static uint32_t first_can_msg = 0;
|
static uint32_t first_can_msg = 0;
|
||||||
static uint32_t last_can_msg_timestamp = 0;
|
static uint32_t last_can_msg_timestamp = 0;
|
||||||
|
static bool hv_requested = false;
|
||||||
|
|
||||||
#define TIME_YEAR 2024
|
#define TIME_YEAR 2024
|
||||||
#define TIME_MONTH 8
|
#define TIME_MONTH 8
|
||||||
|
@ -393,7 +381,7 @@ uint32_t can_msg_received = 0;
|
||||||
* @see https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf
|
* @see https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf
|
||||||
* @see https://web.archive.org/web/20221105210302/https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf
|
* @see https://web.archive.org/web/20221105210302/https://www.autosar.org/fileadmin/user_upload/standards/classic/4-3/AUTOSAR_SWS_CRCLibrary.pdf
|
||||||
*/
|
*/
|
||||||
uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint16_t address) {
|
uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint32_t address) {
|
||||||
|
|
||||||
const uint8_t poly = 0x2F;
|
const uint8_t poly = 0x2F;
|
||||||
const uint8_t xor_output = 0xFF;
|
const uint8_t xor_output = 0xFF;
|
||||||
|
@ -402,6 +390,8 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint16_t address) {
|
||||||
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40};
|
0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40};
|
||||||
const uint8_t MB00C0[16] = {0x2f, 0x44, 0x72, 0xd3, 0x07, 0xf2, 0x39, 0x09,
|
const uint8_t MB00C0[16] = {0x2f, 0x44, 0x72, 0xd3, 0x07, 0xf2, 0x39, 0x09,
|
||||||
0x8d, 0x6f, 0x57, 0x20, 0x37, 0xf9, 0x9b, 0xfa};
|
0x8d, 0x6f, 0x57, 0x20, 0x37, 0xf9, 0x9b, 0xfa};
|
||||||
|
const uint8_t MB00CF[16] = {0xee, 0x80, 0x6e, 0x4e, 0x29, 0xc6, 0x92, 0xc0,
|
||||||
|
0x65, 0xaa, 0x3a, 0xa1, 0x8f, 0xcd, 0xe6, 0x90};
|
||||||
const uint8_t MB00FC[16] = {0x77, 0x5c, 0xa0, 0x89, 0x4b, 0x7c, 0xbb, 0xd6,
|
const uint8_t MB00FC[16] = {0x77, 0x5c, 0xa0, 0x89, 0x4b, 0x7c, 0xbb, 0xd6,
|
||||||
0x1f, 0x6c, 0x4f, 0xf6, 0x20, 0x2b, 0x43, 0xdd};
|
0x1f, 0x6c, 0x4f, 0xf6, 0x20, 0x2b, 0x43, 0xdd};
|
||||||
const uint8_t MB00FD[16] = {0xb4, 0xef, 0xf8, 0x49, 0x1e, 0xe5, 0xc2, 0xc0,
|
const uint8_t MB00FD[16] = {0xb4, 0xef, 0xf8, 0x49, 0x1e, 0xe5, 0xc2, 0xc0,
|
||||||
|
@ -430,6 +420,8 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint16_t address) {
|
||||||
0x1e, 0x0d, 0x24, 0xcd, 0x8c, 0xa6, 0x2f, 0x41};
|
0x1e, 0x0d, 0x24, 0xcd, 0x8c, 0xa6, 0x2f, 0x41};
|
||||||
const uint8_t MB0578[16] = {0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
|
const uint8_t MB0578[16] = {0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
|
||||||
0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48};
|
0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48};
|
||||||
|
const uint8_t MB05A2[16] = {0xeb, 0x4c, 0x44, 0xaf, 0x21, 0x8d, 0x01, 0x58,
|
||||||
|
0xfa, 0x93, 0xdb, 0x89, 0x15, 0x10, 0x4a, 0x61};
|
||||||
const uint8_t MB05CA[16] = {0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
|
const uint8_t MB05CA[16] = {0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43,
|
||||||
0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43};
|
0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43, 0x43};
|
||||||
const uint8_t MB0641[16] = {0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47,
|
const uint8_t MB0641[16] = {0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47, 0x47,
|
||||||
|
@ -452,6 +444,9 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint16_t address) {
|
||||||
case 0x00C0: //
|
case 0x00C0: //
|
||||||
magicByte = MB00C0[counter];
|
magicByte = MB00C0[counter];
|
||||||
break;
|
break;
|
||||||
|
case 0x00CF: //BMS
|
||||||
|
magicByte = MB00CF[counter];
|
||||||
|
break;
|
||||||
case 0x00FC:
|
case 0x00FC:
|
||||||
magicByte = MB00FC[counter];
|
magicByte = MB00FC[counter];
|
||||||
break;
|
break;
|
||||||
|
@ -494,6 +489,9 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint16_t address) {
|
||||||
case 0x0578: // BMS DC
|
case 0x0578: // BMS DC
|
||||||
magicByte = MB0578[counter];
|
magicByte = MB0578[counter];
|
||||||
break;
|
break;
|
||||||
|
case 0x05A2: // BMS
|
||||||
|
magicByte = MB05A2[counter];
|
||||||
|
break;
|
||||||
case 0x05CA: // BMS
|
case 0x05CA: // BMS
|
||||||
magicByte = MB05CA[counter];
|
magicByte = MB05CA[counter];
|
||||||
break;
|
break;
|
||||||
|
@ -510,7 +508,9 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint16_t address) {
|
||||||
magicByte = MB16A954A6[counter];
|
magicByte = MB16A954A6[counter];
|
||||||
break;
|
break;
|
||||||
default: // this won't lead to correct CRC checksums
|
default: // this won't lead to correct CRC checksums
|
||||||
logging.println("Checksum request uknown");
|
#ifdef DEBUG_LOG
|
||||||
|
logging.println("Checksum request unknown");
|
||||||
|
#endif
|
||||||
magicByte = 0x00;
|
magicByte = 0x00;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -538,8 +538,7 @@ uint8_t vw_crc_calc(uint8_t* inputBytes, uint8_t length, uint16_t address) {
|
||||||
|
|
||||||
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
void update_values_battery() { //This function maps all the values fetched via CAN to the correct parameters used for modbus
|
||||||
|
|
||||||
datalayer.battery.status.real_soc = battery_SOC * 5; //*0.05*100 battery_soc_polled * 10;
|
datalayer.battery.status.real_soc = battery_SOC * 5; //*0.05*100
|
||||||
//Alternatively use battery_SOC for more precision
|
|
||||||
|
|
||||||
datalayer.battery.status.soh_pptt;
|
datalayer.battery.status.soh_pptt;
|
||||||
|
|
||||||
|
@ -548,10 +547,9 @@ void update_values_battery() { //This function maps all the values fetched via
|
||||||
datalayer.battery.status.current_dA = (BMS_current - 16300); // 0.1 * 10
|
datalayer.battery.status.current_dA = (BMS_current - 16300); // 0.1 * 10
|
||||||
|
|
||||||
datalayer.battery.info.total_capacity_Wh =
|
datalayer.battery.info.total_capacity_Wh =
|
||||||
((float)datalayer.battery.info.number_of_cells) * 3.6458 * ((float)BMS_capacity_ah) * 0.2;
|
((float)datalayer.battery.info.number_of_cells) * 3.6458 * ((float)BMS_capacity_ah) * 0.2 * 1.13;
|
||||||
|
|
||||||
datalayer.battery.status.remaining_capacity_Wh = usable_energy_amount_Wh * 5;
|
datalayer.battery.status.remaining_capacity_Wh = usable_energy_amount_Wh * 5;
|
||||||
//Alternatively use battery_Wh_left
|
|
||||||
|
|
||||||
datalayer.battery.status.max_charge_power_W = (max_charge_power_watt * 100);
|
datalayer.battery.status.max_charge_power_W = (max_charge_power_watt * 100);
|
||||||
|
|
||||||
|
@ -561,34 +559,15 @@ void update_values_battery() { //This function maps all the values fetched via
|
||||||
datalayer.battery.status.active_power_W =
|
datalayer.battery.status.active_power_W =
|
||||||
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
|
((datalayer.battery.status.voltage_dV * datalayer.battery.status.current_dA) / 100);
|
||||||
|
|
||||||
datalayer.battery.status.temperature_min_dC = (battery_min_temp - 350) / 2;
|
// datalayer.battery.status.temperature_min_dC = actual_temperature_lowest_C*5 -400; // We use the value below, because it has better accuracy
|
||||||
|
datalayer.battery.status.temperature_min_dC = (battery_min_temp * 10) / 64;
|
||||||
|
|
||||||
datalayer.battery.status.temperature_max_dC = (battery_max_temp - 350) / 2;
|
// datalayer.battery.status.temperature_max_dC = actual_temperature_highest_C*5 -400; // We use the value below, because it has better accuracy
|
||||||
|
datalayer.battery.status.temperature_max_dC = (battery_max_temp * 10) / 64;
|
||||||
|
|
||||||
//Map all cell voltages to the global array
|
//Map all cell voltages to the global array
|
||||||
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_polled, 108 * sizeof(uint16_t));
|
memcpy(datalayer.battery.status.cell_voltages_mV, cellvoltages_polled, 108 * sizeof(uint16_t));
|
||||||
|
|
||||||
// Initialize min and max, lets find which cells are min and max!
|
|
||||||
uint16_t min_cell_mv_value = std::numeric_limits<uint16_t>::max();
|
|
||||||
uint16_t max_cell_mv_value = 0;
|
|
||||||
// Loop to find the min and max while ignoring zero values
|
|
||||||
for (uint8_t i = 0; i < 108; ++i) {
|
|
||||||
uint16_t voltage_mV = datalayer.battery.status.cell_voltages_mV[i];
|
|
||||||
if (voltage_mV != 0) { // Skip unread values (0)
|
|
||||||
min_cell_mv_value = std::min(min_cell_mv_value, voltage_mV);
|
|
||||||
max_cell_mv_value = std::max(max_cell_mv_value, voltage_mV);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If all array values are 0, reset min/max to 3700
|
|
||||||
if (min_cell_mv_value == std::numeric_limits<uint16_t>::max()) {
|
|
||||||
min_cell_mv_value = 3700;
|
|
||||||
max_cell_mv_value = 3700;
|
|
||||||
}
|
|
||||||
|
|
||||||
datalayer.battery.status.cell_min_voltage_mV = min_cell_mv_value;
|
|
||||||
datalayer.battery.status.cell_max_voltage_mV = max_cell_mv_value;
|
|
||||||
//TODO, use actual_cellvoltage_lowest_mV instead to save performance
|
|
||||||
|
|
||||||
if (service_disconnect_switch_missing) {
|
if (service_disconnect_switch_missing) {
|
||||||
set_event(EVENT_HVIL_FAILURE, 1);
|
set_event(EVENT_HVIL_FAILURE, 1);
|
||||||
} else {
|
} else {
|
||||||
|
@ -636,6 +615,13 @@ void update_values_battery() { //This function maps all the values fetched via
|
||||||
datalayer_extended.meb.rt_cell_undervol = realtime_cell_undervoltage_warning;
|
datalayer_extended.meb.rt_cell_undervol = realtime_cell_undervoltage_warning;
|
||||||
datalayer_extended.meb.rt_cell_imbalance = realtime_cell_imbalance_warning;
|
datalayer_extended.meb.rt_cell_imbalance = realtime_cell_imbalance_warning;
|
||||||
datalayer_extended.meb.rt_battery_unathorized = realtime_warning_battery_unathorized;
|
datalayer_extended.meb.rt_battery_unathorized = realtime_warning_battery_unathorized;
|
||||||
|
if (balancing_active == 1 && datalayer_extended.meb.balancing_active != 1)
|
||||||
|
set_event_latched(EVENT_BALANCING_START, 0);
|
||||||
|
if (balancing_active == 2 && datalayer_extended.meb.balancing_active == 1)
|
||||||
|
set_event(EVENT_BALANCING_END, 0);
|
||||||
|
datalayer_extended.meb.balancing_active = balancing_active;
|
||||||
|
datalayer_extended.meb.balancing_request = balancing_request;
|
||||||
|
datalayer_extended.meb.charging_active = charging_active;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
|
@ -646,6 +632,26 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
#endif
|
#endif
|
||||||
first_can_msg = last_can_msg_timestamp;
|
first_can_msg = last_can_msg_timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* CRC check on messages with CRC */
|
||||||
|
switch (rx_frame.ID) {
|
||||||
|
case 0x0CF:
|
||||||
|
case 0x578:
|
||||||
|
case 0x5A2:
|
||||||
|
case 0x5CA:
|
||||||
|
case 0x16A954A6:
|
||||||
|
if (rx_frame.data.u8[0] !=
|
||||||
|
vw_crc_calc(rx_frame.data.u8, rx_frame.DLC, rx_frame.ID)) { //If CRC does not match calc
|
||||||
|
datalayer.battery.status.CAN_error_counter++;
|
||||||
|
#ifdef DEBUG_LOG
|
||||||
|
logging.printf("MEB: Msg 0x%04X CRC error\n", rx_frame.ID);
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
switch (rx_frame.ID) {
|
switch (rx_frame.ID) {
|
||||||
case 0x17F0007B: // BMS 500ms
|
case 0x17F0007B: // BMS 500ms
|
||||||
can_msg_received |= RX_0x17F0007B;
|
can_msg_received |= RX_0x17F0007B;
|
||||||
|
@ -713,7 +719,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
status_valve_1 = (rx_frame.data.u8[3] & 0x1C) >> 2;
|
status_valve_1 = (rx_frame.data.u8[3] & 0x1C) >> 2;
|
||||||
status_valve_2 = (rx_frame.data.u8[3] & 0xE0) >> 5;
|
status_valve_2 = (rx_frame.data.u8[3] & 0xE0) >> 5;
|
||||||
temperature_request = (((rx_frame.data.u8[2] & 0x03) << 1) | rx_frame.data.u8[1] >> 7);
|
temperature_request = (((rx_frame.data.u8[2] & 0x03) << 1) | rx_frame.data.u8[1] >> 7);
|
||||||
battery_temperature = rx_frame.data.u8[5]; //*0,5 -40
|
datalayer_extended.meb.battery_temperature_dC = rx_frame.data.u8[5] * 5 - 400; //*0,5 -40
|
||||||
target_flow_temperature_C = rx_frame.data.u8[6]; //*0,5 -40
|
target_flow_temperature_C = rx_frame.data.u8[6]; //*0,5 -40
|
||||||
return_temperature_C = rx_frame.data.u8[7]; //*0,5 -40
|
return_temperature_C = rx_frame.data.u8[7]; //*0,5 -40
|
||||||
break;
|
break;
|
||||||
|
@ -728,7 +734,6 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
break;
|
break;
|
||||||
case 0x16A954A6: // BMS
|
case 0x16A954A6: // BMS
|
||||||
can_msg_received |= RX_0x16A954A6;
|
can_msg_received |= RX_0x16A954A6;
|
||||||
BMS_16A954A6_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
|
|
||||||
BMS_16A954A6_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
|
BMS_16A954A6_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
|
||||||
isolation_fault = (rx_frame.data.u8[2] & 0xE0) >> 5;
|
isolation_fault = (rx_frame.data.u8[2] & 0xE0) >> 5;
|
||||||
isolation_status = (rx_frame.data.u8[2] & 0x1E) >> 1;
|
isolation_status = (rx_frame.data.u8[2] & 0x1E) >> 1;
|
||||||
|
@ -737,6 +742,8 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
actual_temperature_lowest_C = rx_frame.data.u8[4]; //*0,5 -40
|
actual_temperature_lowest_C = rx_frame.data.u8[4]; //*0,5 -40
|
||||||
actual_cellvoltage_highest_mV = (((rx_frame.data.u8[6] & 0x0F) << 8) | rx_frame.data.u8[5]);
|
actual_cellvoltage_highest_mV = (((rx_frame.data.u8[6] & 0x0F) << 8) | rx_frame.data.u8[5]);
|
||||||
actual_cellvoltage_lowest_mV = ((rx_frame.data.u8[7] << 4) | rx_frame.data.u8[6] >> 4);
|
actual_cellvoltage_lowest_mV = ((rx_frame.data.u8[7] << 4) | rx_frame.data.u8[6] >> 4);
|
||||||
|
datalayer.battery.status.cell_min_voltage_mV = actual_cellvoltage_lowest_mV + 1000;
|
||||||
|
datalayer.battery.status.cell_max_voltage_mV = actual_cellvoltage_highest_mV + 1000;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x16A954F8: // BMS
|
case 0x16A954F8: // BMS
|
||||||
|
@ -748,7 +755,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
switch (mux) {
|
switch (mux) {
|
||||||
case 0: // Temperatures 1-56. Value is 0xFD if sensor not present
|
case 0: // Temperatures 1-56. Value is 0xFD if sensor not present
|
||||||
for (uint8_t i = 0; i < 56; i++) {
|
for (uint8_t i = 0; i < 56; i++) {
|
||||||
celltemperature[i] = (rx_frame.data.u8[i + 1] / 2) - 40;
|
datalayer_extended.meb.celltemperature_dC[i] = (rx_frame.data.u8[i + 1] * 5) - 400;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 1: // Cellvoltages 1-42
|
case 1: // Cellvoltages 1-42
|
||||||
|
@ -963,7 +970,6 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
energy_extracted_from_battery = ((rx_frame.data.u8[7] & 0x7F) << 8) | rx_frame.data.u8[6];
|
energy_extracted_from_battery = ((rx_frame.data.u8[7] & 0x7F) << 8) | rx_frame.data.u8[6];
|
||||||
break;
|
break;
|
||||||
case 0x578: // BMS 100ms
|
case 0x578: // BMS 100ms
|
||||||
BMS_578_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
|
|
||||||
BMS_578_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
|
BMS_578_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
|
||||||
BMS_Status_DCLS = ((rx_frame.data.u8[1] & 0x30) >> 4);
|
BMS_Status_DCLS = ((rx_frame.data.u8[1] & 0x30) >> 4);
|
||||||
DC_voltage_DCLS = (rx_frame.data.u8[2] << 6) | (rx_frame.data.u8[1] >> 6);
|
DC_voltage_DCLS = (rx_frame.data.u8[2] << 6) | (rx_frame.data.u8[1] >> 6);
|
||||||
|
@ -972,7 +978,6 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
break;
|
break;
|
||||||
case 0x5A2: // BMS 500ms normal, 100ms fast
|
case 0x5A2: // BMS 500ms normal, 100ms fast
|
||||||
can_msg_received |= RX_0x5A2;
|
can_msg_received |= RX_0x5A2;
|
||||||
BMS_5A2_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
|
|
||||||
BMS_5A2_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
|
BMS_5A2_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
|
||||||
service_disconnect_switch_missing = (rx_frame.data.u8[1] & 0x20) >> 5;
|
service_disconnect_switch_missing = (rx_frame.data.u8[1] & 0x20) >> 5;
|
||||||
pilotline_open = (rx_frame.data.u8[1] & 0x10) >> 4;
|
pilotline_open = (rx_frame.data.u8[1] & 0x10) >> 4;
|
||||||
|
@ -988,9 +993,9 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
break;
|
break;
|
||||||
case 0x5CA: // BMS 500ms
|
case 0x5CA: // BMS 500ms
|
||||||
can_msg_received |= RX_0x5CA;
|
can_msg_received |= RX_0x5CA;
|
||||||
BMS_5CA_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
|
BMS_5CA_counter = (rx_frame.data.u8[1] & 0x0F);
|
||||||
BMS_5CA_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
|
balancing_request = (rx_frame.data.u8[5] & 0x08) >> 3;
|
||||||
balancing_request = (rx_frame.data.u8[5] & 0x08) >> 3; //True/False
|
// balancing_request: BMS requests a low current end charge to support balancing, maybe unused.
|
||||||
battery_diagnostic = (rx_frame.data.u8[3] & 0x07);
|
battery_diagnostic = (rx_frame.data.u8[3] & 0x07);
|
||||||
battery_Wh_left =
|
battery_Wh_left =
|
||||||
(rx_frame.data.u8[2] << 4) | (rx_frame.data.u8[1] >> 4); //*50 ! Not usable, seems to always contain 0x7F0
|
(rx_frame.data.u8[2] << 4) | (rx_frame.data.u8[1] >> 4); //*50 ! Not usable, seems to always contain 0x7F0
|
||||||
|
@ -1003,8 +1008,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
break;
|
break;
|
||||||
case 0x0CF: //BMS 10ms
|
case 0x0CF: //BMS 10ms
|
||||||
can_msg_received |= RX_0x0CF;
|
can_msg_received |= RX_0x0CF;
|
||||||
BMS_0CF_CRC = rx_frame.data.u8[0]; // Can be used to check CAN signal integrity later on
|
BMS_0CF_counter = (rx_frame.data.u8[1] & 0x0F);
|
||||||
BMS_0CF_counter = (rx_frame.data.u8[1] & 0x0F); // Can be used to check CAN signal integrity later on
|
|
||||||
BMS_welded_contactors_status = (rx_frame.data.u8[1] & 0x60) >> 5;
|
BMS_welded_contactors_status = (rx_frame.data.u8[1] & 0x60) >> 5;
|
||||||
BMS_ext_limits_active = (rx_frame.data.u8[1] & 0x80) >> 7;
|
BMS_ext_limits_active = (rx_frame.data.u8[1] & 0x80) >> 7;
|
||||||
BMS_mode = (rx_frame.data.u8[2] & 0x07);
|
BMS_mode = (rx_frame.data.u8[2] & 0x07);
|
||||||
|
@ -1015,34 +1019,37 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
case 6: // DC_CHARGING
|
case 6: // DC_CHARGING
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
if (!datalayer.system.status.battery_allows_contactor_closing)
|
if (!datalayer.system.status.battery_allows_contactor_closing)
|
||||||
logging.printf("MEB Contactors closed\n");
|
logging.printf("MEB: Contactors closed\n");
|
||||||
#endif
|
#endif
|
||||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
||||||
datalayer.battery.status.real_bms_status = BMS_ACTIVE;
|
datalayer.battery.status.real_bms_status = BMS_ACTIVE;
|
||||||
datalayer.system.status.battery_allows_contactor_closing = true;
|
datalayer.system.status.battery_allows_contactor_closing = true;
|
||||||
|
hv_requested = false;
|
||||||
break;
|
break;
|
||||||
case 5: // Error
|
case 5: // Error
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
if (datalayer.system.status.battery_allows_contactor_closing)
|
if (datalayer.system.status.battery_allows_contactor_closing)
|
||||||
logging.printf("MEB Contactors opened\n");
|
logging.printf("MEB: Contactors opened\n");
|
||||||
#endif
|
#endif
|
||||||
datalayer.battery.status.real_bms_status = BMS_FAULT;
|
datalayer.battery.status.real_bms_status = BMS_FAULT;
|
||||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||||
|
hv_requested = false;
|
||||||
break;
|
break;
|
||||||
case 7: // Init
|
case 7: // Init
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
if (datalayer.system.status.battery_allows_contactor_closing)
|
if (datalayer.system.status.battery_allows_contactor_closing)
|
||||||
logging.printf("MEB Contactors opened\n");
|
logging.printf("MEB: Contactors opened\n");
|
||||||
#endif
|
#endif
|
||||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
||||||
datalayer.battery.status.real_bms_status = BMS_STANDBY;
|
datalayer.battery.status.real_bms_status = BMS_STANDBY;
|
||||||
datalayer.system.status.battery_allows_contactor_closing = false;
|
datalayer.system.status.battery_allows_contactor_closing = false;
|
||||||
|
hv_requested = false;
|
||||||
break;
|
break;
|
||||||
case 2: // BALANCING
|
case 2: // BALANCING
|
||||||
default:
|
default:
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
if (datalayer.system.status.battery_allows_contactor_closing)
|
if (datalayer.system.status.battery_allows_contactor_closing)
|
||||||
logging.printf("MEB Contactors opened\n");
|
logging.printf("MEB: Contactors opened\n");
|
||||||
#endif
|
#endif
|
||||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
if (datalayer.battery.status.real_bms_status != BMS_FAULT)
|
||||||
datalayer.battery.status.real_bms_status = BMS_STANDBY;
|
datalayer.battery.status.real_bms_status = BMS_STANDBY;
|
||||||
|
@ -1084,6 +1091,60 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
case PID_MIN_TEMP:
|
case PID_MIN_TEMP:
|
||||||
battery_min_temp = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
battery_min_temp = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||||
break;
|
break;
|
||||||
|
case PID_TEMP_POINT_1:
|
||||||
|
datalayer_extended.meb.temp_points[0] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_2:
|
||||||
|
datalayer_extended.meb.temp_points[1] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_3:
|
||||||
|
datalayer_extended.meb.temp_points[2] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_4:
|
||||||
|
datalayer_extended.meb.temp_points[3] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_5:
|
||||||
|
datalayer_extended.meb.temp_points[4] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_6:
|
||||||
|
datalayer_extended.meb.temp_points[5] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_7:
|
||||||
|
datalayer_extended.meb.temp_points[6] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_8:
|
||||||
|
datalayer_extended.meb.temp_points[7] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_9:
|
||||||
|
datalayer_extended.meb.temp_points[8] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_10:
|
||||||
|
datalayer_extended.meb.temp_points[9] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_11:
|
||||||
|
datalayer_extended.meb.temp_points[10] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_12:
|
||||||
|
datalayer_extended.meb.temp_points[11] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_13:
|
||||||
|
datalayer_extended.meb.temp_points[12] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_14:
|
||||||
|
datalayer_extended.meb.temp_points[13] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_15:
|
||||||
|
datalayer_extended.meb.temp_points[14] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_16:
|
||||||
|
datalayer_extended.meb.temp_points[15] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_17:
|
||||||
|
datalayer_extended.meb.temp_points[16] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_18:
|
||||||
|
datalayer_extended.meb.temp_points[17] = (((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]) / 8.f) - 40;
|
||||||
|
break;
|
||||||
case PID_MAX_CHARGE_VOLTAGE:
|
case PID_MAX_CHARGE_VOLTAGE:
|
||||||
battery_max_charge_voltage = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
battery_max_charge_voltage = ((rx_frame.data.u8[4] << 8) | rx_frame.data.u8[5]);
|
||||||
break;
|
break;
|
||||||
|
@ -1535,7 +1596,7 @@ void handle_incoming_can_frame_battery(CAN_frame rx_frame) {
|
||||||
void transmit_can_battery() {
|
void transmit_can_battery() {
|
||||||
unsigned long currentMillis = millis();
|
unsigned long currentMillis = millis();
|
||||||
// Send 10ms CAN Message
|
// Send 10ms CAN Message
|
||||||
if (datalayer.system.settings.equipment_stop_active || currentMillis > last_can_msg_timestamp + 500) {
|
if (currentMillis > last_can_msg_timestamp + 500) {
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
if (first_can_msg)
|
if (first_can_msg)
|
||||||
logging.printf("MEB: No CAN msg received for 500ms\n");
|
logging.printf("MEB: No CAN msg received for 500ms\n");
|
||||||
|
@ -1615,31 +1676,63 @@ void transmit_can_battery() {
|
||||||
|
|
||||||
//HV request and DC/DC control lies in 0x503
|
//HV request and DC/DC control lies in 0x503
|
||||||
|
|
||||||
if (datalayer.battery.status.real_bms_status != BMS_FAULT &&
|
if ((!datalayer.system.settings.equipment_stop_active) && datalayer.battery.status.real_bms_status != BMS_FAULT &&
|
||||||
(datalayer.battery.status.real_bms_status == BMS_STANDBY ||
|
(datalayer.battery.status.real_bms_status == BMS_ACTIVE ||
|
||||||
datalayer.battery.status.real_bms_status == BMS_ACTIVE) &&
|
(datalayer.battery.status.real_bms_status == BMS_STANDBY &&
|
||||||
(labs(((int32_t)datalayer.battery.status.voltage_dV) -
|
(hv_requested ||
|
||||||
((int32_t)datalayer_extended.meb.BMS_voltage_intermediate_dV)) < 200)) {
|
(datalayer.battery.status.voltage_dV > 200 && datalayer_extended.meb.BMS_voltage_intermediate_dV > 0 &&
|
||||||
|
labs(((int32_t)datalayer.battery.status.voltage_dV) -
|
||||||
|
((int32_t)datalayer_extended.meb.BMS_voltage_intermediate_dV)) < 200))))) {
|
||||||
|
hv_requested = true;
|
||||||
|
datalayer.system.settings.start_precharging = false;
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
if (MEB_503.data.u8[3] == BMS_TARGET_HV_OFF) {
|
if (MEB_503.data.u8[3] == BMS_TARGET_HV_OFF) {
|
||||||
logging.printf("MEB Requesting HV\n");
|
logging.printf("MEB: Requesting HV\n");
|
||||||
|
}
|
||||||
|
if ((MEB_503.data.u8[1] & 0x80) !=
|
||||||
|
(datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00)) {
|
||||||
|
if (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING) {
|
||||||
|
logging.printf("MEB: Precharge bit set to active\n");
|
||||||
|
} else {
|
||||||
|
logging.printf("MEB: Precharge bit set to inactive\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
MEB_503.data.u8[1] =
|
MEB_503.data.u8[1] =
|
||||||
0x30 |
|
0x30 | (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00);
|
||||||
(datalayer.battery.status.real_bms_status == BMS_ACTIVE ? 0x00 : 0x80); // Disable precharing if ACTIVE
|
MEB_503.data.u8[3] = BMS_TARGET_AC_CHARGING;
|
||||||
MEB_503.data.u8[3] = BMS_TARGET_HV_ON; //TODO, should we try AC_2 or DC charging?
|
|
||||||
MEB_503.data.u8[5] = 0x82; // Bordnetz Active
|
MEB_503.data.u8[5] = 0x82; // Bordnetz Active
|
||||||
MEB_503.data.u8[6] = 0xE0; // Request emergency shutdown HV system == 0, false
|
MEB_503.data.u8[6] = 0xE0; // Request emergency shutdown HV system == 0, false
|
||||||
} else if (first_can_msg > 0 && currentMillis > first_can_msg + 2000 && BMS_mode != 0 &&
|
} else if ((first_can_msg > 0 && currentMillis > first_can_msg + 1000 && BMS_mode != 7) ||
|
||||||
BMS_mode != 7) { //FAULT STATE, open contactors
|
datalayer.system.settings.equipment_stop_active) { //FAULT STATE, open contactors
|
||||||
MEB_503.data.u8[1] = 0x90;
|
|
||||||
|
if (datalayer.battery.status.bms_status != FAULT && datalayer.battery.status.real_bms_status == BMS_STANDBY &&
|
||||||
|
!datalayer.system.settings.equipment_stop_active) {
|
||||||
|
datalayer.system.settings.start_precharging = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_LOG
|
||||||
|
if (MEB_503.data.u8[3] != BMS_TARGET_HV_OFF) {
|
||||||
|
logging.printf("MEB: Requesting HV_OFF\n");
|
||||||
|
}
|
||||||
|
if ((MEB_503.data.u8[1] & 0x80) !=
|
||||||
|
(datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00)) {
|
||||||
|
if (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING) {
|
||||||
|
logging.printf("MEB: Precharge bit set to active\n");
|
||||||
|
} else {
|
||||||
|
logging.printf("MEB: Precharge bit set to inactive\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
MEB_503.data.u8[1] =
|
||||||
|
0x10 | (datalayer.system.status.precharge_status == AUTO_PRECHARGE_PRECHARGING ? 0x80 : 0x00);
|
||||||
MEB_503.data.u8[3] = BMS_TARGET_HV_OFF;
|
MEB_503.data.u8[3] = BMS_TARGET_HV_OFF;
|
||||||
MEB_503.data.u8[5] = 0x80; // Bordnetz Inactive
|
MEB_503.data.u8[5] = 0x80; // Bordnetz Inactive
|
||||||
MEB_503.data.u8[6] =
|
MEB_503.data.u8[6] =
|
||||||
0xE3; // Request emergency shutdown HV system == init (3) (not sure if we dare activate this, this is done with 0xE1)
|
0xE3; // Request emergency shutdown HV system == init (3) (not sure if we dare activate this, this is done with 0xE1)
|
||||||
} else {
|
} else {
|
||||||
MEB_503.data.u8[3] = 0;
|
MEB_503.data.u8[3] = 0;
|
||||||
|
MEB_503.data.u8[5] = 0x80; // Bordnetz Inactive
|
||||||
}
|
}
|
||||||
MEB_503.data.u8[1] = ((MEB_503.data.u8[1] & 0xF0) | counter_100ms);
|
MEB_503.data.u8[1] = ((MEB_503.data.u8[1] & 0xF0) | counter_100ms);
|
||||||
MEB_503.data.u8[0] = vw_crc_calc(MEB_503.data.u8, MEB_503.DLC, MEB_503.ID);
|
MEB_503.data.u8[0] = vw_crc_calc(MEB_503.data.u8, MEB_503.DLC, MEB_503.ID);
|
||||||
|
@ -1673,9 +1766,9 @@ void transmit_can_battery() {
|
||||||
if (currentMillis - previousMillis200ms >= INTERVAL_200_MS) {
|
if (currentMillis - previousMillis200ms >= INTERVAL_200_MS) {
|
||||||
previousMillis200ms = currentMillis;
|
previousMillis200ms = currentMillis;
|
||||||
|
|
||||||
//TODO: 153 does not seem to need CRC even though it has it? Empty in some logs and still works
|
// MEB_153 does not need CRC even though it has it. Empty in some logs as well.
|
||||||
|
|
||||||
//TODO: MEB_1B0000B9 & MEB_1B000010 & MEB_1B000046 has CAN sleep commands, static OK?
|
//TODO: MEB_1B0000B9 & MEB_1B000010 & MEB_1B000046 has CAN sleep commands. May be removed?
|
||||||
|
|
||||||
transmit_can_frame(&MEB_5E1, can_config.battery);
|
transmit_can_frame(&MEB_5E1, can_config.battery);
|
||||||
transmit_can_frame(&MEB_153, can_config.battery);
|
transmit_can_frame(&MEB_153, can_config.battery);
|
||||||
|
@ -1707,6 +1800,32 @@ void transmit_can_battery() {
|
||||||
case PID_MIN_TEMP:
|
case PID_MIN_TEMP:
|
||||||
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_MIN_TEMP >> 8);
|
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(PID_MIN_TEMP >> 8);
|
||||||
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_MIN_TEMP;
|
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)PID_MIN_TEMP;
|
||||||
|
poll_pid = PID_TEMP_POINT_1;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_1:
|
||||||
|
case PID_TEMP_POINT_2:
|
||||||
|
case PID_TEMP_POINT_3:
|
||||||
|
case PID_TEMP_POINT_4:
|
||||||
|
case PID_TEMP_POINT_5:
|
||||||
|
case PID_TEMP_POINT_6:
|
||||||
|
case PID_TEMP_POINT_7:
|
||||||
|
case PID_TEMP_POINT_8:
|
||||||
|
case PID_TEMP_POINT_9:
|
||||||
|
case PID_TEMP_POINT_10:
|
||||||
|
case PID_TEMP_POINT_11:
|
||||||
|
case PID_TEMP_POINT_12:
|
||||||
|
case PID_TEMP_POINT_13:
|
||||||
|
case PID_TEMP_POINT_14:
|
||||||
|
case PID_TEMP_POINT_15:
|
||||||
|
case PID_TEMP_POINT_16:
|
||||||
|
case PID_TEMP_POINT_17:
|
||||||
|
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(poll_pid >> 8);
|
||||||
|
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)poll_pid;
|
||||||
|
poll_pid = poll_pid + 1;
|
||||||
|
break;
|
||||||
|
case PID_TEMP_POINT_18:
|
||||||
|
MEB_POLLING_FRAME.data.u8[2] = (uint8_t)(poll_pid >> 8);
|
||||||
|
MEB_POLLING_FRAME.data.u8[3] = (uint8_t)poll_pid;
|
||||||
poll_pid = PID_MAX_CHARGE_VOLTAGE;
|
poll_pid = PID_MAX_CHARGE_VOLTAGE;
|
||||||
break;
|
break;
|
||||||
case PID_MAX_CHARGE_VOLTAGE:
|
case PID_MAX_CHARGE_VOLTAGE:
|
||||||
|
|
|
@ -131,6 +131,24 @@
|
||||||
#define PID_CELLVOLTAGE_CELL_106 0x1EA9
|
#define PID_CELLVOLTAGE_CELL_106 0x1EA9
|
||||||
#define PID_CELLVOLTAGE_CELL_107 0x1EAA
|
#define PID_CELLVOLTAGE_CELL_107 0x1EAA
|
||||||
#define PID_CELLVOLTAGE_CELL_108 0x1EAB
|
#define PID_CELLVOLTAGE_CELL_108 0x1EAB
|
||||||
|
#define PID_TEMP_POINT_1 0x1EAE
|
||||||
|
#define PID_TEMP_POINT_2 0x1EAF
|
||||||
|
#define PID_TEMP_POINT_3 0x1EB0
|
||||||
|
#define PID_TEMP_POINT_4 0x1EB1
|
||||||
|
#define PID_TEMP_POINT_5 0x1EB2
|
||||||
|
#define PID_TEMP_POINT_6 0x1EB3
|
||||||
|
#define PID_TEMP_POINT_7 0x1EB4
|
||||||
|
#define PID_TEMP_POINT_8 0x1EB5
|
||||||
|
#define PID_TEMP_POINT_9 0x1EB6
|
||||||
|
#define PID_TEMP_POINT_10 0x1EB7
|
||||||
|
#define PID_TEMP_POINT_11 0x1EB8
|
||||||
|
#define PID_TEMP_POINT_12 0x1EB9
|
||||||
|
#define PID_TEMP_POINT_13 0x1EBA
|
||||||
|
#define PID_TEMP_POINT_14 0x1EBB
|
||||||
|
#define PID_TEMP_POINT_15 0x1EBC
|
||||||
|
#define PID_TEMP_POINT_16 0x1EBD
|
||||||
|
#define PID_TEMP_POINT_17 0x1EBE
|
||||||
|
#define PID_TEMP_POINT_18 0x1EBF
|
||||||
|
|
||||||
void setup_battery(void);
|
void setup_battery(void);
|
||||||
void transmit_can_frame(CAN_frame* tx_frame, int interface);
|
void transmit_can_frame(CAN_frame* tx_frame, int interface);
|
||||||
|
|
|
@ -10,8 +10,7 @@
|
||||||
|
|
||||||
/* Do not change the defines below */
|
/* Do not change the defines below */
|
||||||
#define RAMPDOWN_SOC 900 // 90.0 SOC% to start ramping down from max charge power towards 0 at 100.00%
|
#define RAMPDOWN_SOC 900 // 90.0 SOC% to start ramping down from max charge power towards 0 at 100.00%
|
||||||
#define RAMPDOWNPOWERALLOWED \
|
#define RAMPDOWNPOWERALLOWED 10000 // What power we ramp down from towards top balancing
|
||||||
15000 // What power we ramp down from towards top balancing (usually same as MAXCHARGEPOWERALLOWED)
|
|
||||||
#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
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
#include "../../datalayer/datalayer.h"
|
#include "../../datalayer/datalayer.h"
|
||||||
#include "../../devboard/utils/events.h"
|
#include "../../devboard/utils/events.h"
|
||||||
#include "../../devboard/utils/value_mapping.h"
|
#include "../../devboard/utils/value_mapping.h"
|
||||||
#include "../../lib/mathieucarbou-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
|
#include "../../lib/ESP32Async-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
|
||||||
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
#include "../../lib/miwagner-ESP32-Arduino-CAN/ESP32CAN.h"
|
||||||
#ifdef CAN_ADDON
|
#ifdef CAN_ADDON
|
||||||
#include "../../lib/pierremolinaro-acan2515/ACAN2515.h"
|
#include "../../lib/pierremolinaro-acan2515/ACAN2515.h"
|
||||||
|
|
|
@ -6,8 +6,6 @@
|
||||||
// Parameters
|
// Parameters
|
||||||
|
|
||||||
#ifdef PRECHARGE_CONTROL
|
#ifdef PRECHARGE_CONTROL
|
||||||
enum State { PRECHARGE_IDLE, START_PRECHARGE, PRECHARGE, PRECHARGE_OFF, COMPLETED };
|
|
||||||
State prechargeStatus = PRECHARGE_IDLE;
|
|
||||||
|
|
||||||
#define MAX_PRECHARGE_TIME_MS 15000 // Maximum time precharge may be enabled
|
#define MAX_PRECHARGE_TIME_MS 15000 // Maximum time precharge may be enabled
|
||||||
|
|
||||||
|
@ -32,6 +30,7 @@ void init_precharge_control() {
|
||||||
#endif
|
#endif
|
||||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
digitalWrite(PRECHARGE_PIN, LOW);
|
||||||
|
pinMode(POSITIVE_CONTACTOR_PIN, OUTPUT);
|
||||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,22 +43,28 @@ void handle_precharge_control() {
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge
|
// Handle actual state machine. This first turns on Negative, then Precharge, then Positive, and finally turns OFF precharge
|
||||||
switch (prechargeStatus) {
|
switch (datalayer.system.status.precharge_status) {
|
||||||
case PRECHARGE_IDLE:
|
case AUTO_PRECHARGE_IDLE:
|
||||||
|
|
||||||
|
#if 0
|
||||||
if (datalayer.battery.status.bms_status != FAULT && datalayer.battery.status.real_bms_status == BMS_STANDBY &&
|
if (datalayer.battery.status.bms_status != FAULT && datalayer.battery.status.real_bms_status == BMS_STANDBY &&
|
||||||
datalayer.system.status.inverter_allows_contactor_closing &&
|
/*datalayer.system.status.inverter_allows_contactor_closing &&*/
|
||||||
!datalayer.system.settings.equipment_stop_active) {
|
!datalayer.system.settings.equipment_stop_active) {
|
||||||
prechargeStatus = START_PRECHARGE;
|
datalayer.system.status.precharge_status = AUTO_PRECHARGE_START;
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
if (datalayer.system.settings.start_precharging) {
|
||||||
|
datalayer.system.status.precharge_status = AUTO_PRECHARGE_START;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case START_PRECHARGE:
|
case AUTO_PRECHARGE_START:
|
||||||
freq = Precharge_default_PWM_Freq;
|
freq = Precharge_default_PWM_Freq;
|
||||||
ledcAttachChannel(PRECHARGE_PIN, freq, PWM_Res, PWM_Precharge_Channel);
|
ledcAttachChannel(PRECHARGE_PIN, freq, PWM_Res, PWM_Precharge_Channel);
|
||||||
ledcWriteTone(PRECHARGE_PIN, freq); // Set frequency and set dutycycle to 50%
|
ledcWriteTone(PRECHARGE_PIN, freq); // Set frequency and set dutycycle to 50%
|
||||||
prechargeStartTime = currentTime;
|
prechargeStartTime = currentTime;
|
||||||
prechargeStatus = PRECHARGE;
|
datalayer.system.status.precharge_status = AUTO_PRECHARGE_PRECHARGING;
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
logging.printf("Precharge: Starting sequence\n");
|
logging.printf("Precharge: Starting sequence\n");
|
||||||
#endif
|
#endif
|
||||||
|
@ -67,9 +72,9 @@ void handle_precharge_control() {
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PRECHARGE:
|
case AUTO_PRECHARGE_PRECHARGING:
|
||||||
// Check if external voltage measurement changed, for instance with the MEB batteries, the external voltage is only updated every 100ms.
|
// Check if external voltage measurement changed, for instance with the MEB batteries, the external voltage is only updated every 100ms.
|
||||||
if (prev_external_voltage != external_voltage) {
|
if (prev_external_voltage != external_voltage && external_voltage != 0) {
|
||||||
prev_external_voltage = external_voltage;
|
prev_external_voltage = external_voltage;
|
||||||
|
|
||||||
if (labs(target_voltage - external_voltage) > 150) {
|
if (labs(target_voltage - external_voltage) > 150) {
|
||||||
|
@ -101,17 +106,18 @@ void handle_precharge_control() {
|
||||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
digitalWrite(PRECHARGE_PIN, LOW);
|
||||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
||||||
prechargeStatus = PRECHARGE_IDLE;
|
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
logging.printf("Precharge: Disabling Precharge bms not standby/active or equipment stop\n");
|
logging.printf("Precharge: Disabling Precharge bms not standby/active or equipment stop\n");
|
||||||
#endif
|
#endif
|
||||||
} else if (currentTime - prechargeStartTime >= MAX_PRECHARGE_TIME_MS) {
|
} else if (currentTime - prechargeStartTime >= MAX_PRECHARGE_TIME_MS ||
|
||||||
|
datalayer.battery.status.real_bms_status == BMS_FAULT) {
|
||||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
digitalWrite(PRECHARGE_PIN, LOW);
|
||||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
||||||
prechargeStatus = PRECHARGE_OFF;
|
datalayer.system.status.precharge_status = AUTO_PRECHARGE_OFF;
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
logging.printf("Precharge: Disabled (timeout reached) -> PRECHARGE_OFF\n");
|
logging.printf("Precharge: Disabled (timeout reached / BMS fault) -> AUTO_PRECHARGE_OFF\n");
|
||||||
#endif
|
#endif
|
||||||
set_event(EVENT_AUTOMATIC_PRECHARGE_FAILURE, 0);
|
set_event(EVENT_AUTOMATIC_PRECHARGE_FAILURE, 0);
|
||||||
|
|
||||||
|
@ -120,27 +126,27 @@ void handle_precharge_control() {
|
||||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
digitalWrite(PRECHARGE_PIN, LOW);
|
||||||
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
digitalWrite(POSITIVE_CONTACTOR_PIN, LOW);
|
||||||
prechargeStatus = COMPLETED;
|
datalayer.system.status.precharge_status = AUTO_PRECHARGE_COMPLETED;
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
logging.printf("Precharge: Disabled (contacts closed) -> COMPLETED\n");
|
logging.printf("Precharge: Disabled (contacts closed) -> COMPLETED\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COMPLETED:
|
case AUTO_PRECHARGE_COMPLETED:
|
||||||
if (datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != ACTIVE) {
|
if (datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != ACTIVE) {
|
||||||
prechargeStatus = PRECHARGE_IDLE;
|
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
logging.printf("Precharge: equipment stop activated -> IDLE\n");
|
logging.printf("Precharge: equipment stop activated -> IDLE\n");
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PRECHARGE_OFF:
|
case AUTO_PRECHARGE_OFF:
|
||||||
if (!datalayer.system.status.battery_allows_contactor_closing ||
|
if (!datalayer.system.status.battery_allows_contactor_closing ||
|
||||||
!datalayer.system.status.inverter_allows_contactor_closing ||
|
!datalayer.system.status.inverter_allows_contactor_closing ||
|
||||||
datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != FAULT) {
|
datalayer.system.settings.equipment_stop_active || datalayer.battery.status.bms_status != FAULT) {
|
||||||
prechargeStatus = PRECHARGE_IDLE;
|
datalayer.system.status.precharge_status = AUTO_PRECHARGE_IDLE;
|
||||||
pinMode(PRECHARGE_PIN, OUTPUT);
|
pinMode(PRECHARGE_PIN, OUTPUT);
|
||||||
digitalWrite(PRECHARGE_PIN, LOW);
|
digitalWrite(PRECHARGE_PIN, LOW);
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
|
@ -153,4 +159,4 @@ void handle_precharge_control() {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif // PRECHARGE_CONTROL
|
#endif // AUTO_PRECHARGE_CONTROL
|
||||||
|
|
|
@ -282,10 +282,17 @@ typedef struct {
|
||||||
bool BMS_reset_in_progress = false;
|
bool BMS_reset_in_progress = false;
|
||||||
/** True if the BMS is starting up */
|
/** True if the BMS is starting up */
|
||||||
bool BMS_startup_in_progress = false;
|
bool BMS_startup_in_progress = false;
|
||||||
|
#ifdef PRECHARGE_CONTROL
|
||||||
|
/** State of automatic precharge sequence */
|
||||||
|
PrechargeState precharge_status = AUTO_PRECHARGE_IDLE;
|
||||||
|
#endif
|
||||||
} DATALAYER_SYSTEM_STATUS_TYPE;
|
} DATALAYER_SYSTEM_STATUS_TYPE;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
bool equipment_stop_active = false;
|
bool equipment_stop_active = false;
|
||||||
|
#ifdef PRECHARGE_CONTROL
|
||||||
|
bool start_precharging = false;
|
||||||
|
#endif
|
||||||
} DATALAYER_SYSTEM_SETTINGS_TYPE;
|
} DATALAYER_SYSTEM_SETTINGS_TYPE;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -603,6 +603,12 @@ typedef struct {
|
||||||
bool BMS_warning_lamp_req = 0;
|
bool BMS_warning_lamp_req = 0;
|
||||||
int32_t BMS_voltage_intermediate_dV = 0;
|
int32_t BMS_voltage_intermediate_dV = 0;
|
||||||
int32_t BMS_voltage_dV = 0;
|
int32_t BMS_voltage_dV = 0;
|
||||||
|
uint8_t balancing_active = 0;
|
||||||
|
bool balancing_request = 0;
|
||||||
|
bool charging_active = 0;
|
||||||
|
float temp_points[18] = {0};
|
||||||
|
uint16_t celltemperature_dC[56] = {0};
|
||||||
|
uint16_t battery_temperature_dC = 0;
|
||||||
} DATALAYER_INFO_MEB;
|
} DATALAYER_INFO_MEB;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
|
|
@ -41,6 +41,14 @@ void update_machineryprotection() {
|
||||||
clear_event(EVENT_BATTERY_FROZEN);
|
clear_event(EVENT_BATTERY_FROZEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (labs(datalayer.battery.status.temperature_max_dC - datalayer.battery.status.temperature_min_dC) >
|
||||||
|
BATTERY_MAX_TEMPERATURE_DEVIATION) {
|
||||||
|
set_event_latched(EVENT_BATTERY_TEMP_DEVIATION_HIGH,
|
||||||
|
datalayer.battery.status.temperature_max_dC - datalayer.battery.status.temperature_min_dC);
|
||||||
|
} else {
|
||||||
|
clear_event(EVENT_BATTERY_TEMP_DEVIATION_HIGH);
|
||||||
|
}
|
||||||
|
|
||||||
// Battery voltage is over designed max voltage!
|
// Battery voltage is over designed max voltage!
|
||||||
if (datalayer.battery.status.voltage_dV > datalayer.battery.info.max_design_voltage_dV) {
|
if (datalayer.battery.status.voltage_dV > datalayer.battery.info.max_design_voltage_dV) {
|
||||||
set_event(EVENT_BATTERY_OVERVOLTAGE, datalayer.battery.status.voltage_dV);
|
set_event(EVENT_BATTERY_OVERVOLTAGE, datalayer.battery.status.voltage_dV);
|
||||||
|
@ -354,14 +362,14 @@ void emulator_pause_state_transmit_can_battery() {
|
||||||
|
|
||||||
if (previous_allowed_to_send_CAN && !allowed_to_send_CAN) {
|
if (previous_allowed_to_send_CAN && !allowed_to_send_CAN) {
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
logging.printf("Safety: Pausing CAN sending");
|
logging.printf("Safety: Pausing CAN sending\n");
|
||||||
#endif
|
#endif
|
||||||
//completely force stop the CAN communication
|
//completely force stop the CAN communication
|
||||||
ESP32Can.CANStop();
|
ESP32Can.CANStop();
|
||||||
} else if (!previous_allowed_to_send_CAN && allowed_to_send_CAN) {
|
} else if (!previous_allowed_to_send_CAN && allowed_to_send_CAN) {
|
||||||
//resume CAN communication
|
//resume CAN communication
|
||||||
#ifdef DEBUG_LOG
|
#ifdef DEBUG_LOG
|
||||||
logging.printf("Safety: Resuming CAN sending");
|
logging.printf("Safety: Resuming CAN sending\n");
|
||||||
#endif
|
#endif
|
||||||
ESP32Can.CANInit();
|
ESP32Can.CANInit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,6 +221,7 @@ void init_events(void) {
|
||||||
events.entries[EVENT_PERIODIC_BMS_RESET].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_PERIODIC_BMS_RESET].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS].level = EVENT_LEVEL_INFO;
|
events.entries[EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS].level = EVENT_LEVEL_INFO;
|
||||||
events.entries[EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED].level = EVENT_LEVEL_WARNING;
|
events.entries[EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED].level = EVENT_LEVEL_WARNING;
|
||||||
|
events.entries[EVENT_BATTERY_TEMP_DEVIATION_HIGH].level = EVENT_LEVEL_WARNING;
|
||||||
|
|
||||||
events.entries[EVENT_EEPROM_WRITE].log = false; // Don't log the logger...
|
events.entries[EVENT_EEPROM_WRITE].log = false; // Don't log the logger...
|
||||||
|
|
||||||
|
@ -257,6 +258,9 @@ void reset_all_events() {
|
||||||
}
|
}
|
||||||
events.level = EVENT_LEVEL_INFO;
|
events.level = EVENT_LEVEL_INFO;
|
||||||
update_bms_status();
|
update_bms_status();
|
||||||
|
#ifdef DEBUG_LOG
|
||||||
|
logging.println("All events have been cleared.");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_event_MQTTpublished(EVENTS_ENUM_TYPE event) {
|
void set_event_MQTTpublished(EVENTS_ENUM_TYPE event) {
|
||||||
|
|
|
@ -119,6 +119,7 @@
|
||||||
XX(EVENT_PERIODIC_BMS_RESET) \
|
XX(EVENT_PERIODIC_BMS_RESET) \
|
||||||
XX(EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS) \
|
XX(EVENT_PERIODIC_BMS_RESET_AT_INIT_SUCCESS) \
|
||||||
XX(EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED) \
|
XX(EVENT_PERIODIC_BMS_RESET_AT_INIT_FAILED) \
|
||||||
|
XX(EVENT_BATTERY_TEMP_DEVIATION_HIGH) \
|
||||||
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;
|
||||||
|
|
|
@ -7,6 +7,13 @@ enum bms_status_enum { STANDBY = 0, INACTIVE = 1, DARKSTART = 2, ACTIVE = 3, FAU
|
||||||
enum real_bms_status_enum { BMS_DISCONNECTED = 0, BMS_STANDBY = 1, BMS_ACTIVE = 2, BMS_FAULT = 3 };
|
enum real_bms_status_enum { BMS_DISCONNECTED = 0, BMS_STANDBY = 1, BMS_ACTIVE = 2, BMS_FAULT = 3 };
|
||||||
enum battery_chemistry_enum { NCA, NMC, LFP };
|
enum battery_chemistry_enum { NCA, NMC, LFP };
|
||||||
enum led_color { GREEN, YELLOW, RED, BLUE };
|
enum led_color { GREEN, YELLOW, RED, BLUE };
|
||||||
|
enum PrechargeState {
|
||||||
|
AUTO_PRECHARGE_IDLE,
|
||||||
|
AUTO_PRECHARGE_START,
|
||||||
|
AUTO_PRECHARGE_PRECHARGING,
|
||||||
|
AUTO_PRECHARGE_OFF,
|
||||||
|
AUTO_PRECHARGE_COMPLETED
|
||||||
|
};
|
||||||
|
|
||||||
#define DISCHARGING 1
|
#define DISCHARGING 1
|
||||||
#define CHARGING 2
|
#define CHARGING 2
|
||||||
|
|
|
@ -998,6 +998,23 @@ String advanced_battery_processor(const String& var) {
|
||||||
default:
|
default:
|
||||||
content += String("?");
|
content += String("?");
|
||||||
}
|
}
|
||||||
|
content += String("</h4><h4>Charging: ") + (datalayer_extended.meb.charging_active ? "active" : "not active");
|
||||||
|
content += String("</h4><h4>Balancing: ");
|
||||||
|
switch (datalayer_extended.meb.balancing_active) {
|
||||||
|
case 0:
|
||||||
|
content += String("init");
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
content += String("active");
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
content += String("inactive");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
content += String("?");
|
||||||
|
}
|
||||||
|
content +=
|
||||||
|
String("</h4><h4>Slow charging: ") + (datalayer_extended.meb.balancing_request ? "requested" : "not requested");
|
||||||
content += "</h4><h4>Diagnostic: ";
|
content += "</h4><h4>Diagnostic: ";
|
||||||
switch (datalayer_extended.meb.battery_diagnostic) {
|
switch (datalayer_extended.meb.battery_diagnostic) {
|
||||||
case 0:
|
case 0:
|
||||||
|
@ -1127,6 +1144,27 @@ String advanced_battery_processor(const String& var) {
|
||||||
content += "<h4>Cell imbalance: " + String(rt_enum[datalayer_extended.meb.rt_cell_imbalance & 0x03]) + "</h4>";
|
content += "<h4>Cell imbalance: " + String(rt_enum[datalayer_extended.meb.rt_cell_imbalance & 0x03]) + "</h4>";
|
||||||
content +=
|
content +=
|
||||||
"<h4>Battery unathorized: " + String(rt_enum[datalayer_extended.meb.rt_battery_unathorized & 0x03]) + "</h4>";
|
"<h4>Battery unathorized: " + String(rt_enum[datalayer_extended.meb.rt_battery_unathorized & 0x03]) + "</h4>";
|
||||||
|
content +=
|
||||||
|
"<h4>Battery temperature: " + String(datalayer_extended.meb.battery_temperature_dC / 10.f, 1) + " °C</h4>";
|
||||||
|
for (int i = 0; i < 3; i++) {
|
||||||
|
content += "<h4>Temperature points " + String(i * 6 + 1) + "-" + String(i * 6 + 6) + " :";
|
||||||
|
for (int j = 0; j < 6; j++)
|
||||||
|
content += " " + String(datalayer_extended.meb.temp_points[i * 6 + j], 1);
|
||||||
|
content += " °C</h4>";
|
||||||
|
}
|
||||||
|
bool temps_done = false;
|
||||||
|
for (int i = 0; i < 7 && !temps_done; i++) {
|
||||||
|
content += "<h4>Cell temperatures " + String(i * 8 + 1) + "-" + String(i * 8 + 8) + " :";
|
||||||
|
for (int j = 0; j < 8; j++) {
|
||||||
|
if (datalayer_extended.meb.celltemperature_dC[i * 8 + j] == 865) {
|
||||||
|
temps_done = true;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
content += " " + String(datalayer_extended.meb.celltemperature_dC[i * 8 + j] / 10.f, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
content += " °C</h4>";
|
||||||
|
}
|
||||||
#endif //MEB_BATTERY
|
#endif //MEB_BATTERY
|
||||||
|
|
||||||
#ifdef RENAULT_ZOE_GEN2_BATTERY
|
#ifdef RENAULT_ZOE_GEN2_BATTERY
|
||||||
|
|
|
@ -896,8 +896,8 @@ String processor(const String& var) {
|
||||||
} else {
|
} else {
|
||||||
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||||
}
|
}
|
||||||
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " C</h4>";
|
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " °C</h4>";
|
||||||
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " C</h4>";
|
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " °C</h4>";
|
||||||
|
|
||||||
content += "<h4>System status: ";
|
content += "<h4>System status: ";
|
||||||
switch (datalayer.battery.status.bms_status) {
|
switch (datalayer.battery.status.bms_status) {
|
||||||
|
@ -1074,8 +1074,8 @@ String processor(const String& var) {
|
||||||
} else {
|
} else {
|
||||||
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
content += "<h4>Cell delta: " + String(cell_delta_mv) + " mV</h4>";
|
||||||
}
|
}
|
||||||
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " C</h4>";
|
content += "<h4>Temperature max: " + String(tempMaxFloat, 1) + " °C</h4>";
|
||||||
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " C</h4>";
|
content += "<h4>Temperature min: " + String(tempMinFloat, 1) + " °C</h4>";
|
||||||
if (datalayer.battery.status.bms_status == ACTIVE) {
|
if (datalayer.battery.status.bms_status == ACTIVE) {
|
||||||
content += "<h4>System status: OK </h4>";
|
content += "<h4>System status: OK </h4>";
|
||||||
} else if (datalayer.battery.status.bms_status == UPDATING) {
|
} else if (datalayer.battery.status.bms_status == UPDATING) {
|
||||||
|
|
|
@ -4,10 +4,10 @@
|
||||||
#include <Preferences.h>
|
#include <Preferences.h>
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#include "../../include.h"
|
#include "../../include.h"
|
||||||
|
#include "../../lib/ESP32Async-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
|
||||||
#include "../../lib/YiannisBourkelis-Uptime-Library/src/uptime_formatter.h"
|
#include "../../lib/YiannisBourkelis-Uptime-Library/src/uptime_formatter.h"
|
||||||
#include "../../lib/ayushsharma82-ElegantOTA/src/ElegantOTA.h"
|
#include "../../lib/ayushsharma82-ElegantOTA/src/ElegantOTA.h"
|
||||||
#include "../../lib/mathieucarbou-AsyncTCPSock/src/AsyncTCP.h"
|
#include "../../lib/mathieucarbou-AsyncTCPSock/src/AsyncTCP.h"
|
||||||
#include "../../lib/mathieucarbou-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
|
|
||||||
|
|
||||||
extern const char* version_number; // The current software version, shown on webserver
|
extern const char* version_number; // The current software version, shown on webserver
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,8 @@ union f32b {
|
||||||
byte b[4];
|
byte b[4];
|
||||||
};
|
};
|
||||||
|
|
||||||
uint8_t frame1[40] = {0x06, 0xE2, 0xFF, 0x02, 0xFF, 0x29, // Frame header
|
uint8_t BATTERY_INFO[40] = {
|
||||||
|
0x06, 0xE2, 0xFF, 0x02, 0xFF, 0x29, // Frame header
|
||||||
0x01, 0x08, 0x80, 0x43, // 256.063 Nominal voltage / 5*51.2=256 first byte 0x01 or 0x04
|
0x01, 0x08, 0x80, 0x43, // 256.063 Nominal voltage / 5*51.2=256 first byte 0x01 or 0x04
|
||||||
0xE4, 0x70, 0x8A, 0x5C, // These might be Umin & Unax, Uint16
|
0xE4, 0x70, 0x8A, 0x5C, // These might be Umin & Unax, Uint16
|
||||||
0xB5, 0x02, 0xD3, 0x01, // Battery Serial number? Modbus register 527
|
0xB5, 0x02, 0xD3, 0x01, // Battery Serial number? Modbus register 527
|
||||||
|
@ -38,46 +39,30 @@ uint8_t frame1[40] = {0x06, 0xE2, 0xFF, 0x02, 0xFF, 0x29, // Frame header
|
||||||
0x4D, // CRC
|
0x4D, // CRC
|
||||||
0x00}; //
|
0x00}; //
|
||||||
|
|
||||||
// values in frame2 will be overwritten at update_modbus_registers_inverter()
|
// values in CyclicData will be overwritten at update_modbus_registers_inverter()
|
||||||
|
|
||||||
uint8_t frame2[64] = {
|
uint8_t CyclicData[64] = {
|
||||||
0x0A, // This may also been 0x06, seen at startup when live values not valid, but also occasionally single frames.
|
0x00, // First zero byte pointer
|
||||||
0xE2, 0xFF, 0x02, 0xFF, 0x29, // frame Header
|
0xE2, 0xFF, 0x02, 0xFF, 0x29, // frame Header
|
||||||
|
|
||||||
0x1D, 0x5A, 0x85, 0x43, // Current Voltage (float) Modbus register 216, Bytes 6-9
|
0x1D, 0x5A, 0x85, 0x43, // Current Voltage (float) Modbus register 216, Bytes 6-9
|
||||||
0x01, 0x03, // Unknown, 0x03 seen also 0x0F, 0x07, might hava something to do with current
|
0x00, 0x00, 0x8D, 0x43, // Max Voltage (2 byte float), Bytes 12-13
|
||||||
0x8D, 0x43, // Max Voltage (2 byte float), Bytes 12-13
|
0x00, 0x00, 0xAC, 0x41, // BAttery Temperature (2 byte float) Modbus register 214, Bytes 16-17
|
||||||
0x01, 0x03, 0xAC, 0x41, // BAttery Temperature (2 byte float) Modbus register 214, Bytes 16-17
|
0x00, 0x00, 0x00, 0x00, // Peak Current (1s period?), Bytes 18-21
|
||||||
0x01, 0x01, 0x01,
|
0x00, 0x00, 0x00, 0x00, // Avg current (1s period?), Bytes 22-25
|
||||||
0x01, // Peak Current (1s period?), Bytes 18-21 - Communication fault seen with some values (>10A?)
|
0x00, 0x00, 0x48, 0x42, // Max discharge current (2 byte float), Bit 26-29, // Sunspec: ADisChaMax
|
||||||
0x01, 0x01, 0x01,
|
0x00, 0x00, 0xC8, 0x41, // Battery gross capacity, Ah (2 byte float) , Bytes 30-33, Modbus 512
|
||||||
0x01, // Avg current (1s period?), Bytes 22-25 - Communication fault seen with some values (>10A?)
|
0x00, 0x00, 0xA0, 0x41, // Max charge current (2 byte float) Bit 36-37, ZERO WHEN SOC=100 // Sunspec: AChaMax
|
||||||
|
|
||||||
0x01, 0x03, 0x48, 0x42, // Max discharge current (2 byte float), Bit 26-29,
|
|
||||||
// Sunspec: ADisChaMax
|
|
||||||
|
|
||||||
0x01, 0x03, // Unknown
|
|
||||||
0xC8, 0x41, // Battery gross capacity, Ah (2 byte float) , Bytes 30-33, Modbus 512
|
|
||||||
|
|
||||||
0x01, // Unknown
|
|
||||||
0x16, // This seems to have something to do with cell temperatures
|
|
||||||
|
|
||||||
0xA0, 0x41, // Max charge current (2 byte float) Bit 36-37, ZERO WHEN SOC=100
|
|
||||||
// Sunspec: AChaMax
|
|
||||||
|
|
||||||
0xCD, 0xCC, 0xB4, 0x41, // MaxCellTemp (4 byte float) Bit 38-41
|
0xCD, 0xCC, 0xB4, 0x41, // MaxCellTemp (4 byte float) Bit 38-41
|
||||||
0x01, 0x0C, 0xA4, 0x41, // MinCellTemp (4 byte float) Bit 42-45
|
0x00, 0x00, 0xA4, 0x41, // MinCellTemp (4 byte float) Bit 42-45
|
||||||
|
|
||||||
0xA4, 0x70, 0x55, 0x40, // MaxCellVolt (float), Bit 46-49
|
0xA4, 0x70, 0x55, 0x40, // MaxCellVolt (float), Bit 46-49
|
||||||
0x7D, 0x3F, 0x55, 0x40, // MinCellVolt (float), Bit 50-53
|
0x7D, 0x3F, 0x55, 0x40, // MinCellVolt (float), Bit 50-53
|
||||||
|
|
||||||
0xFE, // Cylce count , Bit 54
|
0xFE, 0x04, // Cycle count,
|
||||||
0x04, // Cycle count? , Bit 55
|
0x00, // Byte 56
|
||||||
0x01, // Byte 56
|
|
||||||
0x40, // When SOC=100 Byte57=0x40, at startup 0x03 (about 7 times), otherwise 0x02
|
0x40, // When SOC=100 Byte57=0x40, at startup 0x03 (about 7 times), otherwise 0x02
|
||||||
0x64, // SOC , Bit 58
|
0x64, // SOC , Bit 58
|
||||||
0x01, // Unknown, when byte 57 = 0x03, this 0x02, otherwise 0x01
|
0x00, // Unknown,
|
||||||
0x01, // Unknown, Seen only 0x01
|
0x00, // Unknown,
|
||||||
0x02, // Unknown, Mostly 0x02. seen also 0x01
|
0x02, // Unknown, Mostly 0x02. seen also 0x01
|
||||||
0x00, // CRC (inverted sum of bytes 1-62 + 0xC0), Bit 62
|
0x00, // CRC (inverted sum of bytes 1-62 + 0xC0), Bit 62
|
||||||
0x00};
|
0x00};
|
||||||
|
@ -97,7 +82,7 @@ uint8_t frame4[8] = {0x07, 0xE3, 0xFF, 0x02, 0xFF, 0x29, 0xF4, 0x00};
|
||||||
uint8_t frameB1[10] = {0x07, 0x63, 0xFF, 0x02, 0xFF, 0x29, 0x5E, 0x02, 0x16, 0x00};
|
uint8_t frameB1[10] = {0x07, 0x63, 0xFF, 0x02, 0xFF, 0x29, 0x5E, 0x02, 0x16, 0x00};
|
||||||
uint8_t frameB1b[8] = {0x07, 0xE3, 0xFF, 0x02, 0xFF, 0x29, 0xF4, 0x00};
|
uint8_t frameB1b[8] = {0x07, 0xE3, 0xFF, 0x02, 0xFF, 0x29, 0xF4, 0x00};
|
||||||
|
|
||||||
uint8_t RS485_RXFRAME[10];
|
uint8_t RS485_RXFRAME[300];
|
||||||
|
|
||||||
bool register_content_ok = false;
|
bool register_content_ok = false;
|
||||||
|
|
||||||
|
@ -110,13 +95,6 @@ void float2frame(byte* arr, float value, byte framepointer) {
|
||||||
arr[framepointer + 3] = g.b[3];
|
arr[framepointer + 3] = g.b[3];
|
||||||
}
|
}
|
||||||
|
|
||||||
void float2frameMSB(byte* arr, float value, byte framepointer) {
|
|
||||||
f32b g;
|
|
||||||
g.f = value;
|
|
||||||
arr[framepointer + 0] = g.b[2];
|
|
||||||
arr[framepointer + 1] = g.b[3];
|
|
||||||
}
|
|
||||||
|
|
||||||
static void dbg_timestamp(void) {
|
static void dbg_timestamp(void) {
|
||||||
#ifdef DEBUG_KOSTAL_RS485_DATA
|
#ifdef DEBUG_KOSTAL_RS485_DATA
|
||||||
logging.print("[");
|
logging.print("[");
|
||||||
|
@ -148,17 +126,32 @@ static void dbg_message(const char* msg) {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* https://en.wikipedia.org/wiki/Consistent_Overhead_Byte_Stuffing#Encoding_examples */
|
||||||
|
|
||||||
|
void null_stuffer(byte* lfc, int len) {
|
||||||
|
int last_null_byte = 0;
|
||||||
|
for (int i = 0; i < len; i++) {
|
||||||
|
if (lfc[i] == '\0') {
|
||||||
|
lfc[last_null_byte] = (byte)(i - last_null_byte);
|
||||||
|
last_null_byte = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void send_kostal(byte* frame, int len) {
|
static void send_kostal(byte* frame, int len) {
|
||||||
dbg_frame(frame, len, "TX");
|
dbg_frame(frame, len, "TX");
|
||||||
Serial2.write(frame, len);
|
Serial2.write(frame, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte calculate_longframe_crc(byte* lfc, int lastbyte) {
|
byte calculate_kostal_crc(byte* lfc, int len) {
|
||||||
unsigned int sum = 0;
|
unsigned int sum = 0;
|
||||||
for (int i = 0; i < lastbyte; ++i) {
|
if (lfc[0] != 0) {
|
||||||
|
logging.printf("WARNING: first byte should be 0, but is 0x%02x\n", lfc[0]);
|
||||||
|
}
|
||||||
|
for (int i = 1; i < len; i++) {
|
||||||
sum += lfc[i];
|
sum += lfc[i];
|
||||||
}
|
}
|
||||||
return ((byte) ~(sum + 0xc0) & 0xff);
|
return (byte)(-sum & 0xff);
|
||||||
}
|
}
|
||||||
|
|
||||||
byte calculate_frame1_crc(byte* lfc, int lastbyte) {
|
byte calculate_frame1_crc(byte* lfc, int lastbyte) {
|
||||||
|
@ -191,60 +184,65 @@ void update_RS485_registers_inverter() {
|
||||||
|
|
||||||
if (datalayer.system.status.battery_allows_contactor_closing &
|
if (datalayer.system.status.battery_allows_contactor_closing &
|
||||||
datalayer.system.status.inverter_allows_contactor_closing) {
|
datalayer.system.status.inverter_allows_contactor_closing) {
|
||||||
float2frame(frame2, (float)datalayer.battery.status.voltage_dV / 10, 6); // Confirmed OK mapping
|
float2frame(CyclicData, (float)datalayer.battery.status.voltage_dV / 10, 6); // Confirmed OK mapping
|
||||||
frame2[0] = 0x0A;
|
CyclicData[0] = 0x0A;
|
||||||
} else {
|
} else {
|
||||||
frame2[0] = 0x06;
|
CyclicData[0] = 0x06;
|
||||||
float2frame(frame2, 0.0, 6);
|
float2frame(CyclicData, 0.0, 6);
|
||||||
}
|
}
|
||||||
// Set nominal voltage to value between min and max voltage set by battery (Example 400 and 300 results in 350V)
|
// Set nominal voltage to value between min and max voltage set by battery (Example 400 and 300 results in 350V)
|
||||||
nominal_voltage_dV =
|
nominal_voltage_dV =
|
||||||
(((datalayer.battery.info.max_design_voltage_dV - datalayer.battery.info.min_design_voltage_dV) / 2) +
|
(((datalayer.battery.info.max_design_voltage_dV - datalayer.battery.info.min_design_voltage_dV) / 2) +
|
||||||
datalayer.battery.info.min_design_voltage_dV);
|
datalayer.battery.info.min_design_voltage_dV);
|
||||||
float2frameMSB(frame1, (float)nominal_voltage_dV / 10, 8);
|
float2frame(BATTERY_INFO, (float)nominal_voltage_dV / 10, 6);
|
||||||
|
|
||||||
float2frameMSB(frame2, (float)datalayer.battery.info.max_design_voltage_dV / 10, 12);
|
float2frame(CyclicData, (float)datalayer.battery.info.max_design_voltage_dV / 10, 10);
|
||||||
|
|
||||||
float2frameMSB(frame2, (float)average_temperature_dC / 10, 16);
|
float2frame(CyclicData, (float)average_temperature_dC / 10, 14);
|
||||||
|
|
||||||
// Some current values causes communication error, must be resolved, why.
|
// Some current values causes communication error, must be resolved, why.
|
||||||
// float2frameMSB(frame2, (float)datalayer.battery.status.current_dA / 10, 20); // Peak discharge? current (2 byte float)
|
// float2frame(CyclicData, (float)datalayer.battery.status.current_dA / 10, 18); // Peak discharge? current (2 byte float)
|
||||||
// float2frameMSB(frame2, (float)datalayer.battery.status.current_dA / 10, 24);
|
// float2frame(CyclicData, (float)datalayer.battery.status.current_dA / 10, 22);
|
||||||
|
|
||||||
float2frameMSB(frame2, (float)datalayer.battery.status.max_discharge_current_dA / 10, 28); // BAttery capacity Ah
|
float2frame(CyclicData, (float)datalayer.battery.status.max_discharge_current_dA / 10, 26);
|
||||||
|
|
||||||
float2frameMSB(frame2, (float)datalayer.battery.status.max_discharge_current_dA / 10, 32);
|
|
||||||
|
|
||||||
// When SOC = 100%, drop down allowed charge current down.
|
// When SOC = 100%, drop down allowed charge current down.
|
||||||
|
|
||||||
if ((datalayer.battery.status.reported_soc / 100) < 100) {
|
if ((datalayer.battery.status.reported_soc / 100) < 100) {
|
||||||
float2frameMSB(frame2, (float)datalayer.battery.status.max_charge_current_dA / 10, 36);
|
float2frame(CyclicData, (float)datalayer.battery.status.max_charge_current_dA / 10, 34);
|
||||||
frame2[57] = 0x02;
|
|
||||||
frame2[59] = 0x01;
|
|
||||||
} else {
|
} else {
|
||||||
float2frameMSB(frame2, 0.0, 36);
|
float2frame(CyclicData, 0.0, 34);
|
||||||
//frame2[57]=0x40;
|
|
||||||
frame2[57] = 0x02;
|
|
||||||
frame2[59] = 0x01;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// On startup, byte 57 seems to be always 0x03 couple of frames,.
|
// On startup, byte 56 seems to be always 0x00 couple of frames,.
|
||||||
|
if (f2_startup_count < 9) {
|
||||||
|
CyclicData[56] = 0x00;
|
||||||
|
} else {
|
||||||
|
CyclicData[56] = 0x01;
|
||||||
|
}
|
||||||
|
|
||||||
|
// On startup, byte 59 seems to be always 0x02 couple of frames,.
|
||||||
if (f2_startup_count < 14) {
|
if (f2_startup_count < 14) {
|
||||||
frame2[57] = 0x03;
|
CyclicData[59] = 0x02;
|
||||||
frame2[59] = 0x02;
|
} else {
|
||||||
|
CyclicData[59] = 0x00;
|
||||||
}
|
}
|
||||||
|
|
||||||
float2frame(frame2, (float)datalayer.battery.status.temperature_max_dC / 10, 38);
|
if (nominal_voltage_dV > 0) {
|
||||||
float2frame(frame2, (float)datalayer.battery.status.temperature_min_dC / 10, 42);
|
float2frame(CyclicData, (float)(datalayer.battery.info.total_capacity_Wh / nominal_voltage_dV * 10),
|
||||||
|
30); // BAttery capacity Ah
|
||||||
|
}
|
||||||
|
float2frame(CyclicData, (float)datalayer.battery.status.temperature_max_dC / 10, 38);
|
||||||
|
float2frame(CyclicData, (float)datalayer.battery.status.temperature_min_dC / 10, 42);
|
||||||
|
|
||||||
float2frame(frame2, (float)datalayer.battery.status.cell_max_voltage_mV / 1000, 46);
|
float2frame(CyclicData, (float)datalayer.battery.status.cell_max_voltage_mV / 1000, 46);
|
||||||
float2frame(frame2, (float)datalayer.battery.status.cell_min_voltage_mV / 1000, 50);
|
float2frame(CyclicData, (float)datalayer.battery.status.cell_min_voltage_mV / 1000, 50);
|
||||||
|
|
||||||
frame2[58] = (byte)(datalayer.battery.status.reported_soc / 100); // Confirmed OK mapping
|
CyclicData[58] = (byte)(datalayer.battery.status.reported_soc / 100); // Confirmed OK mapping
|
||||||
|
|
||||||
register_content_ok = true;
|
register_content_ok = true;
|
||||||
|
|
||||||
frame1[38] = calculate_frame1_crc(frame1, 38);
|
BATTERY_INFO[38] = calculate_frame1_crc(BATTERY_INFO, 38);
|
||||||
|
|
||||||
if (incoming_message_counter > 0) {
|
if (incoming_message_counter > 0) {
|
||||||
incoming_message_counter--;
|
incoming_message_counter--;
|
||||||
|
@ -328,7 +326,7 @@ void receive_RS485() // Runs as fast as possible to handle the serial stream
|
||||||
}
|
}
|
||||||
|
|
||||||
if (headerA && (RS485_RXFRAME[6] == 0x4A) && (RS485_RXFRAME[7] == 0x08)) { // "frame 1"
|
if (headerA && (RS485_RXFRAME[6] == 0x4A) && (RS485_RXFRAME[7] == 0x08)) { // "frame 1"
|
||||||
send_kostal(frame1, 40);
|
send_kostal(BATTERY_INFO, 40);
|
||||||
if (!startupMillis) {
|
if (!startupMillis) {
|
||||||
startupMillis = currentMillis;
|
startupMillis = currentMillis;
|
||||||
}
|
}
|
||||||
|
@ -340,13 +338,10 @@ void receive_RS485() // Runs as fast as possible to handle the serial stream
|
||||||
f2_startup_count++;
|
f2_startup_count++;
|
||||||
}
|
}
|
||||||
byte tmpframe[64]; //copy values to prevent data manipulation during rewrite/crc calculation
|
byte tmpframe[64]; //copy values to prevent data manipulation during rewrite/crc calculation
|
||||||
memcpy(tmpframe, frame2, 64);
|
memcpy(tmpframe, CyclicData, 64);
|
||||||
for (int i = 1; i < 63; i++) {
|
|
||||||
if (tmpframe[i] == 0x00) {
|
tmpframe[62] = calculate_kostal_crc(tmpframe, 62);
|
||||||
tmpframe[i] = 0x01;
|
null_stuffer(tmpframe, 64);
|
||||||
}
|
|
||||||
}
|
|
||||||
tmpframe[62] = calculate_longframe_crc(tmpframe, 62);
|
|
||||||
send_kostal(tmpframe, 64);
|
send_kostal(tmpframe, 64);
|
||||||
}
|
}
|
||||||
if (headerA && (RS485_RXFRAME[6] == 0x53) && (RS485_RXFRAME[7] == 0x03)) { // "frame 3"
|
if (headerA && (RS485_RXFRAME[6] == 0x53) && (RS485_RXFRAME[7] == 0x03)) { // "frame 3"
|
||||||
|
|
|
@ -310,7 +310,7 @@ void update_values_can_inverter() { //This function maps all the values fetched
|
||||||
PYLON_4271.data.u8[3] = (datalayer.battery.status.temperature_min_dC >> 8);
|
PYLON_4271.data.u8[3] = (datalayer.battery.status.temperature_min_dC >> 8);
|
||||||
#else // Not INVERT_LOW_HIGH_BYTES
|
#else // Not INVERT_LOW_HIGH_BYTES
|
||||||
//Voltage (370.0)
|
//Voltage (370.0)
|
||||||
PYLON_4210.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8;
|
PYLON_4210.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
|
||||||
PYLON_4210.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
|
PYLON_4210.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
|
||||||
PYLON_4211.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
|
PYLON_4211.data.u8[0] = (datalayer.battery.status.voltage_dV >> 8);
|
||||||
PYLON_4211.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
|
PYLON_4211.data.u8[1] = (datalayer.battery.status.voltage_dV & 0x00FF);
|
||||||
|
@ -348,28 +348,28 @@ void update_values_can_inverter() { //This function maps all the values fetched
|
||||||
|
|
||||||
#ifdef SET_30K_OFFSET
|
#ifdef SET_30K_OFFSET
|
||||||
//Max ChargeCurrent
|
//Max ChargeCurrent
|
||||||
PYLON_4220.data.u8[4] = ((max_charge_current + 30000) >> 8);
|
PYLON_4220.data.u8[4] = ((datalayer.battery.status.max_charge_current_dA + 30000) >> 8);
|
||||||
PYLON_4220.data.u8[5] = ((max_charge_current + 30000) & 0x00FF);
|
PYLON_4220.data.u8[5] = ((datalayer.battery.status.max_charge_current_dA + 30000) & 0x00FF);
|
||||||
PYLON_4221.data.u8[4] = ((max_charge_current + 30000) >> 8);
|
PYLON_4221.data.u8[4] = ((datalayer.battery.status.max_charge_current_dA + 30000) >> 8);
|
||||||
PYLON_4221.data.u8[5] = ((max_charge_current + 30000) & 0x00FF);
|
PYLON_4221.data.u8[5] = ((datalayer.battery.status.max_charge_current_dA + 30000) & 0x00FF);
|
||||||
|
|
||||||
//Max DischargeCurrent
|
//Max DischargeCurrent
|
||||||
PYLON_4220.data.u8[6] = ((30000 - max_discharge_current) >> 8);
|
PYLON_4220.data.u8[6] = ((30000 - datalayer.battery.status.max_discharge_current_dA) >> 8);
|
||||||
PYLON_4220.data.u8[7] = ((30000 - max_discharge_current) & 0x00FF);
|
PYLON_4220.data.u8[7] = ((30000 - datalayer.battery.status.max_discharge_current_dA) & 0x00FF);
|
||||||
PYLON_4221.data.u8[6] = ((30000 - max_discharge_current) >> 8);
|
PYLON_4221.data.u8[6] = ((30000 - datalayer.battery.status.max_discharge_current_dA) >> 8);
|
||||||
PYLON_4221.data.u8[7] = ((30000 - max_discharge_current) & 0x00FF);
|
PYLON_4221.data.u8[7] = ((30000 - datalayer.battery.status.max_discharge_current_dA) & 0x00FF);
|
||||||
#else // Not SET_30K_OFFSET
|
#else // Not SET_30K_OFFSET
|
||||||
//Max ChargeCurrent
|
//Max ChargeCurrent
|
||||||
PYLON_4220.data.u8[4] = (max_charge_current >> 8);
|
PYLON_4220.data.u8[4] = (datalayer.battery.status.max_charge_current_dA >> 8);
|
||||||
PYLON_4220.data.u8[5] = (max_charge_current & 0x00FF);
|
PYLON_4220.data.u8[5] = (datalayer.battery.status.max_charge_current_dA & 0x00FF);
|
||||||
PYLON_4221.data.u8[4] = (max_charge_current >> 8);
|
PYLON_4221.data.u8[4] = (datalayer.battery.status.max_charge_current_dA >> 8);
|
||||||
PYLON_4221.data.u8[5] = (max_charge_current & 0x00FF);
|
PYLON_4221.data.u8[5] = (datalayer.battery.status.max_charge_current_dA & 0x00FF);
|
||||||
|
|
||||||
//Max DishargeCurrent
|
//Max DishargeCurrent
|
||||||
PYLON_4220.data.u8[6] = (max_discharge_current >> 8);
|
PYLON_4220.data.u8[6] = (datalayer.battery.status.max_discharge_current_dA >> 8);
|
||||||
PYLON_4220.data.u8[7] = (max_discharge_current & 0x00FF);
|
PYLON_4220.data.u8[7] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF);
|
||||||
PYLON_4221.data.u8[6] = (max_discharge_current >> 8);
|
PYLON_4221.data.u8[6] = (datalayer.battery.status.max_discharge_current >> 8);
|
||||||
PYLON_4221.data.u8[7] = (max_discharge_current & 0x00FF);
|
PYLON_4221.data.u8[7] = (datalayer.battery.status.max_discharge_current_dA & 0x00FF);
|
||||||
#endif //SET_30K_OFFSET
|
#endif //SET_30K_OFFSET
|
||||||
|
|
||||||
//Max cell voltage
|
//Max cell voltage
|
||||||
|
|
129
Software/src/lib/ESP32Async-ESPAsyncWebServer/CODE_OF_CONDUCT.md
Normal file
129
Software/src/lib/ESP32Async-ESPAsyncWebServer/CODE_OF_CONDUCT.md
Normal file
|
@ -0,0 +1,129 @@
|
||||||
|
|
||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
https://sidweb.nl/cms3/en/contact.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
|
@ -1,21 +1,33 @@
|
||||||
|

|
||||||
|
|
||||||
# ESPAsyncWebServer
|
# ESPAsyncWebServer
|
||||||
|
|
||||||
[](https://GitHub.com/mathieucarbou/ESPAsyncWebServer/releases/)
|
[](https://GitHub.com/ESP32Async/ESPAsyncWebServer/releases/)
|
||||||
[](https://registry.platformio.org/libraries/mathieucarbou/ESPAsyncWebServer)
|
[](https://registry.platformio.org/libraries/ESP32Async/ESPAsyncWebServer)
|
||||||
|
|
||||||
[](https://opensource.org/license/lgpl-3-0/)
|
[](https://opensource.org/license/lgpl-3-0/)
|
||||||
[](code_of_conduct.md)
|
[](code_of_conduct.md)
|
||||||
|
|
||||||
[](https://github.com/mathieucarbou/ESPAsyncWebServer/actions/workflows/ci.yml)
|
[](https://github.com/ESP32Async/ESPAsyncWebServer/actions/workflows/ci.yml)
|
||||||
[](https://GitHub.com/mathieucarbou/ESPAsyncWebServer/commit/)
|
[](https://GitHub.com/ESP32Async/ESPAsyncWebServer/commit/)
|
||||||
[](https://gitpod.io/#https://github.com/mathieucarbou/ESPAsyncWebServer)
|
[](https://gitpod.io/#https://github.com/ESP32Async/ESPAsyncWebServer)
|
||||||
|
|
||||||
|
Project moved to [ESP32Async](https://github.com/ESP32Async) organization at [https://github.com/ESP32Async/ESPAsyncWebServer](https://github.com/ESP32Async/ESPAsyncWebServer)
|
||||||
|
|
||||||
|
Discord Server: [https://discord.gg/X7zpGdyUcY](https://discord.gg/X7zpGdyUcY)
|
||||||
|
|
||||||
|
Please see the new links:
|
||||||
|
|
||||||
|
- `ESP32Async/ESPAsyncWebServer @ 3.6.2` (ESP32, ESP8266, RP2040)
|
||||||
|
- `ESP32Async/AsyncTCP @ 3.3.2` (ESP32)
|
||||||
|
- `ESP32Async/ESPAsyncTCP @ 2.0.0` (ESP8266)
|
||||||
|
- `https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip` (AsyncTCP alternative for ESP32)
|
||||||
|
- `khoih-prog/AsyncTCP_RP2040W @ 1.2.0` (RP2040)
|
||||||
|
|
||||||
Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040
|
Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040
|
||||||
Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.
|
Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.
|
||||||
|
|
||||||
This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubox-node-org/ESPAsyncWebServer) and includes all the concurrency fixes.
|
- [Changes in this repository](#changes-in-this-repository)
|
||||||
|
|
||||||
- [Changes in this fork](#changes-in-this-fork)
|
|
||||||
- [Dependencies](#dependencies)
|
- [Dependencies](#dependencies)
|
||||||
- [Performance](#performance)
|
- [Performance](#performance)
|
||||||
- [Important recommendations for build options](#important-recommendations-for-build-options)
|
- [Important recommendations for build options](#important-recommendations-for-build-options)
|
||||||
|
@ -26,7 +38,7 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo
|
||||||
- [Migration to Middleware to improve performance and memory usage](#migration-to-middleware-to-improve-performance-and-memory-usage)
|
- [Migration to Middleware to improve performance and memory usage](#migration-to-middleware-to-improve-performance-and-memory-usage)
|
||||||
- [Original Documentation](#original-documentation)
|
- [Original Documentation](#original-documentation)
|
||||||
|
|
||||||
## Changes in this fork
|
## Changes in this repository
|
||||||
|
|
||||||
- (bug) A lot of bug fixes
|
- (bug) A lot of bug fixes
|
||||||
- (ci) Better CI with a complete matrix of Arduino versions and boards
|
- (ci) Better CI with a complete matrix of Arduino versions and boards
|
||||||
|
@ -42,7 +54,7 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo
|
||||||
- (feat) **Resumable download** support using HEAD and bytes range
|
- (feat) **Resumable download** support using HEAD and bytes range
|
||||||
- (feat) `StreamConcat` example to show how to stream multiple files in one response
|
- (feat) `StreamConcat` example to show how to stream multiple files in one response
|
||||||
- (feat) Removed ESPIDF Editor (this is not the role of a web server library to do that - get the source files from the original repos if required)
|
- (feat) Removed ESPIDF Editor (this is not the role of a web server library to do that - get the source files from the original repos if required)
|
||||||
- (perf) [AsyncTCPSock](https://github.com/mathieucarbou/AsyncTCPSock) support: AsyncTCP can be ignored and AsyncTCPSock used instead
|
- (perf) [AsyncTCPSock](https://github.com/ESP32Async/AsyncTCPSock) support: AsyncTCP can be ignored and AsyncTCPSock used instead
|
||||||
- (perf) `char*` overloads to avoid using `String`
|
- (perf) `char*` overloads to avoid using `String`
|
||||||
- (perf) `DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients
|
- (perf) `DEFAULT_MAX_WS_CLIENTS` to change the number of allows WebSocket clients and use `cleanupClients()` to help cleanup resources about dead clients
|
||||||
- (perf) `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full
|
- (perf) `setCloseClientOnQueueFull(bool)` which can be set on a client to either close the connection or discard messages but not close the connection when the queue is full
|
||||||
|
@ -59,13 +71,19 @@ This fork is based on [yubox-node-org/ESPAsyncWebServer](https://github.com/yubo
|
||||||
|
|
||||||
This ESPAsyncWebServer fork is now at version 3.x, where we try to keep the API compatibility with original project as much as possible.
|
This ESPAsyncWebServer fork is now at version 3.x, where we try to keep the API compatibility with original project as much as possible.
|
||||||
|
|
||||||
Next version 4.x will:
|
We plan on creating a next major 4.x version that will:
|
||||||
|
|
||||||
1. Drop support for ESP8266, which goes EOL in a few years. All ESP8266 boards can be replaced by equivalent ESP32 boards.
|
1. Drop support for ESP8266, which goes EOL in a few years
|
||||||
2. Drop support for Arduino 2.x and ESP-IDF 4.x. The library will be compatible with Arduino 3.x and ESP-IDF 5.x.
|
2. Drop support for Arduino 2.x and ESP-IDF 4.x. The library will be compatible with latest Arduino and ESP-IDF
|
||||||
3. Drop support for ArduinoJson 5.x and 6.x. The library will be compatible with ArduinoJson 7.x.
|
3. Drop support for ArduinoJson 5.x and 6.x. The library will be compatible with latest ArduinoJson
|
||||||
|
|
||||||
So if you need one of these feature, you will have to stick with 3.x or another fork.
|
So if you need one of these feature, you will have to stick with the current 3.x.
|
||||||
|
All releases we do will not cease to exist: all 3.x releases will stay in the release page.
|
||||||
|
That is why we have tags and a release cycle.
|
||||||
|
|
||||||
|
Maintaining a library for ESP8266 and RP2040 has a real cost and clearly what we see is that most users helping are on ESP32.
|
||||||
|
|
||||||
|
If you are an ESP8266 user and want to help improve current 3.x, you are more than welcomed to contribute to this community effort.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
|
@ -77,18 +95,18 @@ So if you need one of these feature, you will have to stick with 3.x or another
|
||||||
```ini
|
```ini
|
||||||
lib_compat_mode = strict
|
lib_compat_mode = strict
|
||||||
lib_ldf_mode = chain
|
lib_ldf_mode = chain
|
||||||
lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.6.0
|
lib_deps = ESP32Async/ESPAsyncWebServer @ 3.6.2
|
||||||
```
|
```
|
||||||
|
|
||||||
**Dependencies:**
|
**Dependencies:**
|
||||||
|
|
||||||
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.3.2`
|
- **ESP32 with AsyncTCP**: `ESP32Async/AsyncTCP @ 3.3.2`
|
||||||
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.3.2](https://github.com/mathieucarbou/AsyncTCP/releases)
|
Arduino IDE: [https://github.com/ESP32Async/AsyncTCP#v3.3.2](https://github.com/ESP32Async/AsyncTCP/releases)
|
||||||
|
|
||||||
- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
|
- **ESP32 with AsyncTCPSock**: `https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
|
||||||
|
|
||||||
- **ESP8266**: `esphome/ESPAsyncTCP-esphome @ 2.0.0`
|
- **ESP8266**: `ESP32Async/ESPAsyncTCP @ 2.0.0`
|
||||||
Arduino IDE: [https://github.com/mathieucarbou/esphome-ESPAsyncTCP#v2.0.0](https://github.com/mathieucarbou/esphome-ESPAsyncTCP/releases/tag/v2.0.0)
|
Arduino IDE: [https://github.com/ESP32Async/ESPAsyncTCP#v2.0.0](https://github.com/ESP32Async/ESPAsyncTCP/releases/tag/v2.0.0)
|
||||||
|
|
||||||
- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.0`
|
- **RP2040**: `khoih-prog/AsyncTCP_RP2040W @ 1.2.0`
|
||||||
Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0)
|
Arduino IDE: [https://github.com/khoih-prog/AsyncTCP_RP2040W#v1.2.0](https://github.com/khoih-prog/AsyncTCP_RP2040W/releases/tag/v1.2.0)
|
||||||
|
@ -101,30 +119,30 @@ AsyncTCPSock can be used instead of AsyncTCP by excluding AsyncTCP from the libr
|
||||||
lib_compat_mode = strict
|
lib_compat_mode = strict
|
||||||
lib_ldf_mode = chain
|
lib_ldf_mode = chain
|
||||||
lib_deps =
|
lib_deps =
|
||||||
; mathieucarbou/AsyncTCP @ 3.3.2
|
; ESP32Async/AsyncTCP @ 3.3.2
|
||||||
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
|
https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
|
||||||
mathieucarbou/ESPAsyncWebServer @ 3.6.0
|
ESP32Async/ESPAsyncWebServer @ 3.6.2
|
||||||
lib_ignore =
|
lib_ignore =
|
||||||
AsyncTCP
|
AsyncTCP
|
||||||
mathieucarbou/AsyncTCP
|
ESP32Async/AsyncTCP
|
||||||
```
|
```
|
||||||
|
|
||||||
## Performance
|
## Performance
|
||||||
|
|
||||||
Performance of `mathieucarbou/ESPAsyncWebServer @ 3.6.0`:
|
Performance of `ESP32Async/ESPAsyncWebServer @ 3.6.2`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
> brew install autocannon
|
> brew install autocannon
|
||||||
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
|
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
|
||||||
```
|
```
|
||||||
|
|
||||||
With `mathieucarbou/AsyncTCP @ 3.3.2`
|
With `ESP32Async/AsyncTCP @ 3.3.2`
|
||||||
|
|
||||||
[](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)
|
<img width="629" alt="perf-c10" src="https://github.com/user-attachments/assets/b4b7f953-c24d-4e04-8d87-ba3f26805737" />
|
||||||
|
|
||||||
With `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`:
|
With `https://github.com/ESP32Async/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`:
|
||||||
|
|
||||||
[](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10-asynctcpsock.png)
|
<img width="654" alt="perf-c10-asynctcpsock" src="https://github.com/user-attachments/assets/0dacf133-ca47-40be-939b-e6f60fc95b81" />
|
||||||
|
|
||||||
**SSE performance**
|
**SSE performance**
|
||||||
|
|
||||||
|
@ -190,6 +208,8 @@ I personally use the following configuration in my projects:
|
||||||
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096 // reduce the stack size (default is 16K)
|
-D CONFIG_ASYNC_TCP_STACK_SIZE=4096 // reduce the stack size (default is 16K)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
If you need to serve chunk requests with a really low buffer (which should be avoided), you can set `-D ASYNCWEBSERVER_USE_CHUNK_INFLIGHT=0` to disable the in-flight control.
|
||||||
|
|
||||||
## `AsyncWebSocketMessageBuffer` and `makeBuffer()`
|
## `AsyncWebSocketMessageBuffer` and `makeBuffer()`
|
||||||
|
|
||||||
The fork from [yubox-node-org](https://github.com/yubox-node-org/ESPAsyncWebServer) introduces some breaking API changes compared to the original library, especially regarding the use of `std::shared_ptr<std::vector<uint8_t>>` for WebSocket.
|
The fork from [yubox-node-org](https://github.com/yubox-node-org/ESPAsyncWebServer) introduces some breaking API changes compared to the original library, especially regarding the use of `std::shared_ptr<std::vector<uint8_t>>` for WebSocket.
|
||||||
|
@ -247,7 +267,7 @@ Middleware is a way to intercept requests to perform some operations on them, li
|
||||||
Middleware can either be attached to individual handlers, attached at the server level (thus applied to all handlers), or both.
|
Middleware can either be attached to individual handlers, attached at the server level (thus applied to all handlers), or both.
|
||||||
They will be executed in the order they are attached, and they can stop the request processing by sending a response themselves.
|
They will be executed in the order they are attached, and they can stop the request processing by sending a response themselves.
|
||||||
|
|
||||||
You can have a look at the [SimpleServer.ino](https://github.com/mathieucarbou/ESPAsyncWebServer/blob/main/examples/SimpleServer/SimpleServer.ino) example for some use cases.
|
You can have a look at the [SimpleServer.ino](https://github.com/ESP32Async/ESPAsyncWebServer/blob/main/examples/SimpleServer/SimpleServer.ino) example for some use cases.
|
||||||
|
|
||||||
For example, such middleware would handle authentication and set some attributes on the request to make them available for the next middleware and for the handler which will process the request.
|
For example, such middleware would handle authentication and set some attributes on the request to make them available for the next middleware and for the handler which will process the request.
|
||||||
|
|
||||||
|
@ -315,15 +335,6 @@ myHandler.addMiddleware(&authMiddleware); // add authentication to a specific ha
|
||||||
These callbacks can be called multiple times during request parsing, so this is up to the user to now call the `AsyncAuthenticationMiddleware.allowed(request)` if needed and ideally when the method is called for the first time.
|
These callbacks can be called multiple times during request parsing, so this is up to the user to now call the `AsyncAuthenticationMiddleware.allowed(request)` if needed and ideally when the method is called for the first time.
|
||||||
These callbacks are also not triggering the whole middleware chain since they are not part of the request processing workflow (they are not the final handler).
|
These callbacks are also not triggering the whole middleware chain since they are not part of the request processing workflow (they are not the final handler).
|
||||||
|
|
||||||
|
|
||||||
## Maintainers
|
|
||||||
This fork of ESPAsyncWebServer and dependend libs are maintained as an opensource project at best effort level.
|
|
||||||
- [Mathieu Carbou](https://github.com/mathieucarbou)
|
|
||||||
- [Emil Muratov](https://github.com/vortigont)
|
|
||||||
|
|
||||||
Thanks to all who contributed by providing PRs, testing and reporting issues.
|
|
||||||
|
|
||||||
|
|
||||||
## Original Documentation
|
## Original Documentation
|
||||||
<!-- no toc -->
|
<!-- no toc -->
|
||||||
- [Why should you care](#why-should-you-care)
|
- [Why should you care](#why-should-you-care)
|
|
@ -1,26 +1,18 @@
|
||||||
{
|
{
|
||||||
"name": "ESPAsyncWebServer",
|
"name": "ESPAsyncWebServer",
|
||||||
"version": "3.6.0",
|
"version": "3.6.2",
|
||||||
"description": "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.",
|
"description": "Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040. Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc.",
|
||||||
"keywords": "http,async,websocket,webserver",
|
"keywords": "http,async,websocket,webserver",
|
||||||
"homepage": "https://github.com/mathieucarbou/ESPAsyncWebServer",
|
"homepage": "https://github.com/ESP32Async/ESPAsyncWebServer",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/mathieucarbou/ESPAsyncWebServer.git"
|
"url": "https://github.com/ESP32Async/ESPAsyncWebServer.git"
|
||||||
},
|
},
|
||||||
"authors": [
|
"authors":
|
||||||
{
|
{
|
||||||
"name": "Hristo Gochkov"
|
"name": "ESP32Async",
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Mathieu Carbou",
|
|
||||||
"maintainer": true
|
"maintainer": true
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "Emil Muratov",
|
|
||||||
"maintainer": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"license": "LGPL-3.0",
|
"license": "LGPL-3.0",
|
||||||
"frameworks": "arduino",
|
"frameworks": "arduino",
|
||||||
"platforms": [
|
"platforms": [
|
||||||
|
@ -30,14 +22,14 @@
|
||||||
],
|
],
|
||||||
"dependencies": [
|
"dependencies": [
|
||||||
{
|
{
|
||||||
"owner": "mathieucarbou",
|
"owner": "ESP32Async",
|
||||||
"name": "AsyncTCP",
|
"name": "AsyncTCP",
|
||||||
"version": "^3.3.2",
|
"version": "^3.3.2",
|
||||||
"platforms": "espressif32"
|
"platforms": "espressif32"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"owner": "esphome",
|
"owner": "ESP32Async",
|
||||||
"name": "ESPAsyncTCP-esphome",
|
"name": "ESPAsyncTCP",
|
||||||
"version": "^2.0.0",
|
"version": "^2.0.0",
|
||||||
"platforms": "espressif8266"
|
"platforms": "espressif8266"
|
||||||
},
|
},
|
|
@ -1,11 +1,11 @@
|
||||||
name=ESP Async WebServer
|
name=ESP Async WebServer
|
||||||
includes=ESPAsyncWebServer.h
|
includes=ESPAsyncWebServer.h
|
||||||
version=3.6.0
|
version=3.6.2
|
||||||
author=Me-No-Dev
|
author=ESP32Async
|
||||||
maintainer=Mathieu Carbou <mathieu.carbou@gmail.com>
|
maintainer=ESP32Async
|
||||||
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040
|
sentence=Asynchronous HTTP and WebSocket Server Library for ESP32, ESP8266 and RP2040
|
||||||
paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc
|
paragraph=Supports: WebSocket, SSE, Authentication, Arduino Json 7, File Upload, Static File serving, URL Rewrite, URL Redirect, etc
|
||||||
category=Other
|
category=Other
|
||||||
url=https://github.com/mathieucarbou/ESPAsyncWebServer
|
url=https://github.com/ESP32Async/ESPAsyncWebServer
|
||||||
architectures=*
|
architectures=*
|
||||||
license=LGPL-3.0
|
license=LGPL-3.0
|
|
@ -18,7 +18,9 @@
|
||||||
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
#include "Arduino.h"
|
#include "Arduino.h"
|
||||||
#include <rom/ets_sys.h>
|
#if defined(ESP32)
|
||||||
|
#include <rom/ets_sys.h>
|
||||||
|
#endif
|
||||||
#include "AsyncEventSource.h"
|
#include "AsyncEventSource.h"
|
||||||
|
|
||||||
#define ASYNC_SSE_NEW_LINE_CHAR (char)0xa
|
#define ASYNC_SSE_NEW_LINE_CHAR (char)0xa
|
||||||
|
@ -174,19 +176,27 @@ AsyncEventSourceClient::AsyncEventSourceClient(AsyncWebServerRequest* request, A
|
||||||
}
|
}
|
||||||
|
|
||||||
AsyncEventSourceClient::~AsyncEventSourceClient() {
|
AsyncEventSourceClient::~AsyncEventSourceClient() {
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_lockmq);
|
std::lock_guard<std::mutex> lock(_lockmq);
|
||||||
|
#endif
|
||||||
_messageQueue.clear();
|
_messageQueue.clear();
|
||||||
close();
|
close();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncEventSourceClient::_queueMessage(const char* message, size_t len) {
|
bool AsyncEventSourceClient::_queueMessage(const char* message, size_t len) {
|
||||||
if (_messageQueue.size() >= SSE_MAX_QUEUED_MESSAGES) {
|
if (_messageQueue.size() >= SSE_MAX_QUEUED_MESSAGES) {
|
||||||
|
#ifdef ESP8266
|
||||||
|
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
|
||||||
|
#elif defined(ESP32)
|
||||||
log_e("Event message queue overflow: discard message");
|
log_e("Event message queue overflow: discard message");
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
// length() is not thread-safe, thus acquiring the lock before this call..
|
// length() is not thread-safe, thus acquiring the lock before this call..
|
||||||
std::lock_guard<std::mutex> lock(_lockmq);
|
std::lock_guard<std::mutex> lock(_lockmq);
|
||||||
|
#endif
|
||||||
|
|
||||||
_messageQueue.emplace_back(message, len);
|
_messageQueue.emplace_back(message, len);
|
||||||
|
|
||||||
|
@ -205,12 +215,18 @@ bool AsyncEventSourceClient::_queueMessage(const char* message, size_t len) {
|
||||||
|
|
||||||
bool AsyncEventSourceClient::_queueMessage(AsyncEvent_SharedData_t&& msg) {
|
bool AsyncEventSourceClient::_queueMessage(AsyncEvent_SharedData_t&& msg) {
|
||||||
if (_messageQueue.size() >= SSE_MAX_QUEUED_MESSAGES) {
|
if (_messageQueue.size() >= SSE_MAX_QUEUED_MESSAGES) {
|
||||||
|
#ifdef ESP8266
|
||||||
|
ets_printf(String(F("ERROR: Too many messages queued\n")).c_str());
|
||||||
|
#elif defined(ESP32)
|
||||||
log_e("Event message queue overflow: discard message");
|
log_e("Event message queue overflow: discard message");
|
||||||
|
#endif
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
// length() is not thread-safe, thus acquiring the lock before this call..
|
// length() is not thread-safe, thus acquiring the lock before this call..
|
||||||
std::lock_guard<std::mutex> lock(_lockmq);
|
std::lock_guard<std::mutex> lock(_lockmq);
|
||||||
|
#endif
|
||||||
|
|
||||||
_messageQueue.emplace_back(std::move(msg));
|
_messageQueue.emplace_back(std::move(msg));
|
||||||
|
|
||||||
|
@ -227,8 +243,10 @@ bool AsyncEventSourceClient::_queueMessage(AsyncEvent_SharedData_t&& msg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AsyncEventSourceClient::_onAck(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))) {
|
void AsyncEventSourceClient::_onAck(size_t len __attribute__((unused)), uint32_t time __attribute__((unused))) {
|
||||||
|
#ifdef ESP32
|
||||||
// Same here, acquiring the lock early
|
// Same here, acquiring the lock early
|
||||||
std::lock_guard<std::mutex> lock(_lockmq);
|
std::lock_guard<std::mutex> lock(_lockmq);
|
||||||
|
#endif
|
||||||
|
|
||||||
// adjust in-flight len
|
// adjust in-flight len
|
||||||
if (len < _inflight)
|
if (len < _inflight)
|
||||||
|
@ -252,8 +270,10 @@ void AsyncEventSourceClient::_onAck(size_t len __attribute__((unused)), uint32_t
|
||||||
|
|
||||||
void AsyncEventSourceClient::_onPoll() {
|
void AsyncEventSourceClient::_onPoll() {
|
||||||
if (_messageQueue.size()) {
|
if (_messageQueue.size()) {
|
||||||
|
#ifdef ESP32
|
||||||
// Same here, acquiring the lock early
|
// Same here, acquiring the lock early
|
||||||
std::lock_guard<std::mutex> lock(_lockmq);
|
std::lock_guard<std::mutex> lock(_lockmq);
|
||||||
|
#endif
|
||||||
_runQueue();
|
_runQueue();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -320,7 +340,9 @@ void AsyncEventSource::authorizeConnect(ArAuthorizeConnectHandler cb) {
|
||||||
void AsyncEventSource::_addClient(AsyncEventSourceClient* client) {
|
void AsyncEventSource::_addClient(AsyncEventSourceClient* client) {
|
||||||
if (!client)
|
if (!client)
|
||||||
return;
|
return;
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||||
|
#endif
|
||||||
_clients.emplace_back(client);
|
_clients.emplace_back(client);
|
||||||
if (_connectcb)
|
if (_connectcb)
|
||||||
_connectcb(client);
|
_connectcb(client);
|
||||||
|
@ -331,7 +353,9 @@ void AsyncEventSource::_addClient(AsyncEventSourceClient* client) {
|
||||||
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient* client) {
|
void AsyncEventSource::_handleDisconnect(AsyncEventSourceClient* client) {
|
||||||
if (_disconnectcb)
|
if (_disconnectcb)
|
||||||
_disconnectcb(client);
|
_disconnectcb(client);
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||||
|
#endif
|
||||||
for (auto i = _clients.begin(); i != _clients.end(); ++i) {
|
for (auto i = _clients.begin(); i != _clients.end(); ++i) {
|
||||||
if (i->get() == client)
|
if (i->get() == client)
|
||||||
_clients.erase(i);
|
_clients.erase(i);
|
||||||
|
@ -343,7 +367,9 @@ void AsyncEventSource::close() {
|
||||||
// While the whole loop is not done, the linked list is locked and so the
|
// While the whole loop is not done, the linked list is locked and so the
|
||||||
// iterator should remain valid even when AsyncEventSource::_handleDisconnect()
|
// iterator should remain valid even when AsyncEventSource::_handleDisconnect()
|
||||||
// is called very early
|
// is called very early
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||||
|
#endif
|
||||||
for (const auto& c : _clients) {
|
for (const auto& c : _clients) {
|
||||||
if (c->connected())
|
if (c->connected())
|
||||||
c->close();
|
c->close();
|
||||||
|
@ -354,7 +380,9 @@ void AsyncEventSource::close() {
|
||||||
size_t AsyncEventSource::avgPacketsWaiting() const {
|
size_t AsyncEventSource::avgPacketsWaiting() const {
|
||||||
size_t aql = 0;
|
size_t aql = 0;
|
||||||
uint32_t nConnectedClients = 0;
|
uint32_t nConnectedClients = 0;
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||||
|
#endif
|
||||||
if (!_clients.size())
|
if (!_clients.size())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
@ -370,7 +398,9 @@ size_t AsyncEventSource::avgPacketsWaiting() const {
|
||||||
AsyncEventSource::SendStatus AsyncEventSource::send(
|
AsyncEventSource::SendStatus AsyncEventSource::send(
|
||||||
const char* message, const char* event, uint32_t id, uint32_t reconnect) {
|
const char* message, const char* event, uint32_t id, uint32_t reconnect) {
|
||||||
AsyncEvent_SharedData_t shared_msg = std::make_shared<String>(generateEventMessage(message, event, id, reconnect));
|
AsyncEvent_SharedData_t shared_msg = std::make_shared<String>(generateEventMessage(message, event, id, reconnect));
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||||
|
#endif
|
||||||
size_t hits = 0;
|
size_t hits = 0;
|
||||||
size_t miss = 0;
|
size_t miss = 0;
|
||||||
for (const auto& c : _clients) {
|
for (const auto& c : _clients) {
|
||||||
|
@ -383,7 +413,9 @@ AsyncEventSource::SendStatus AsyncEventSource::send(
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncEventSource::count() const {
|
size_t AsyncEventSource::count() const {
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
std::lock_guard<std::mutex> lock(_client_queue_lock);
|
||||||
|
#endif
|
||||||
size_t n_clients{0};
|
size_t n_clients{0};
|
||||||
for (const auto& i : _clients)
|
for (const auto& i : _clients)
|
||||||
if (i->connected())
|
if (i->connected())
|
|
@ -22,17 +22,39 @@
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
#include "../../mathieucarbou-AsyncTCPSock/src/AsyncTCP.h"
|
#include "../../mathieucarbou-AsyncTCPSock/src/AsyncTCP.h"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#ifndef SSE_MAX_QUEUED_MESSAGES
|
#ifndef SSE_MAX_QUEUED_MESSAGES
|
||||||
#define SSE_MAX_QUEUED_MESSAGES 32
|
#define SSE_MAX_QUEUED_MESSAGES 32
|
||||||
#endif
|
#endif
|
||||||
#define SSE_MIN_INFLIGH 2 * 1460 // allow 2 MSS packets
|
#define SSE_MIN_INFLIGH 2 * 1460 // allow 2 MSS packets
|
||||||
#define SSE_MAX_INFLIGH 16 * 1024 // but no more than 16k, no need to blow it, since same data is kept in local Q
|
#define SSE_MAX_INFLIGH 16 * 1024 // but no more than 16k, no need to blow it, since same data is kept in local Q
|
||||||
|
#elif defined(ESP8266)
|
||||||
|
#include <ESPAsyncTCP.h>
|
||||||
|
#ifndef SSE_MAX_QUEUED_MESSAGES
|
||||||
|
#define SSE_MAX_QUEUED_MESSAGES 8
|
||||||
|
#endif
|
||||||
|
#define SSE_MIN_INFLIGH 2 * 1460 // allow 2 MSS packets
|
||||||
|
#define SSE_MAX_INFLIGH 8 * 1024 // but no more than 8k, no need to blow it, since same data is kept in local Q
|
||||||
|
#elif defined(TARGET_RP2040)
|
||||||
|
#include <AsyncTCP_RP2040W.h>
|
||||||
|
#ifndef SSE_MAX_QUEUED_MESSAGES
|
||||||
|
#define SSE_MAX_QUEUED_MESSAGES 32
|
||||||
|
#endif
|
||||||
|
#define SSE_MIN_INFLIGH 2 * 1460 // allow 2 MSS packets
|
||||||
|
#define SSE_MAX_INFLIGH 16 * 1024 // but no more than 16k, no need to blow it, since same data is kept in local Q
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "ESPAsyncWebServer.h"
|
#include "ESPAsyncWebServer.h"
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
#include <Hash.h>
|
||||||
|
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
|
||||||
|
#include <../src/Hash.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
class AsyncEventSource;
|
class AsyncEventSource;
|
||||||
class AsyncEventSourceResponse;
|
class AsyncEventSourceResponse;
|
||||||
class AsyncEventSourceClient;
|
class AsyncEventSourceClient;
|
||||||
|
@ -54,7 +76,12 @@ class AsyncEventSourceMessage {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AsyncEventSourceMessage(AsyncEvent_SharedData_t data) : _data(data) {};
|
AsyncEventSourceMessage(AsyncEvent_SharedData_t data) : _data(data) {};
|
||||||
|
#ifdef ESP32
|
||||||
AsyncEventSourceMessage(const char* data, size_t len) : _data(std::make_shared<String>(data, len)) {};
|
AsyncEventSourceMessage(const char* data, size_t len) : _data(std::make_shared<String>(data, len)) {};
|
||||||
|
#else
|
||||||
|
// esp8266's String does not have constructor with data/length arguments. Use a concat method here
|
||||||
|
AsyncEventSourceMessage(const char* data, size_t len) { _data->concat(data, len); };
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief acknowledge sending len bytes of data
|
* @brief acknowledge sending len bytes of data
|
||||||
|
@ -105,7 +132,9 @@ class AsyncEventSourceClient {
|
||||||
size_t _inflight{0}; // num of unacknowledged bytes that has been written to socket buffer
|
size_t _inflight{0}; // num of unacknowledged bytes that has been written to socket buffer
|
||||||
size_t _max_inflight{SSE_MAX_INFLIGH}; // max num of unacknowledged bytes that could be written to socket buffer
|
size_t _max_inflight{SSE_MAX_INFLIGH}; // max num of unacknowledged bytes that could be written to socket buffer
|
||||||
std::list<AsyncEventSourceMessage> _messageQueue;
|
std::list<AsyncEventSourceMessage> _messageQueue;
|
||||||
|
#ifdef ESP32
|
||||||
mutable std::mutex _lockmq;
|
mutable std::mutex _lockmq;
|
||||||
|
#endif
|
||||||
bool _queueMessage(const char* message, size_t len);
|
bool _queueMessage(const char* message, size_t len);
|
||||||
bool _queueMessage(AsyncEvent_SharedData_t&& msg);
|
bool _queueMessage(AsyncEvent_SharedData_t&& msg);
|
||||||
void _runQueue();
|
void _runQueue();
|
||||||
|
@ -184,9 +213,11 @@ class AsyncEventSource : public AsyncWebHandler {
|
||||||
private:
|
private:
|
||||||
String _url;
|
String _url;
|
||||||
std::list<std::unique_ptr<AsyncEventSourceClient>> _clients;
|
std::list<std::unique_ptr<AsyncEventSourceClient>> _clients;
|
||||||
|
#ifdef ESP32
|
||||||
// Same as for individual messages, protect mutations of _clients list
|
// Same as for individual messages, protect mutations of _clients list
|
||||||
// since simultaneous access from different tasks is possible
|
// since simultaneous access from different tasks is possible
|
||||||
mutable std::mutex _client_queue_lock;
|
mutable std::mutex _client_queue_lock;
|
||||||
|
#endif
|
||||||
ArEventHandlerFunction _connectcb = nullptr;
|
ArEventHandlerFunction _connectcb = nullptr;
|
||||||
ArEventHandlerFunction _disconnectcb = nullptr;
|
ArEventHandlerFunction _disconnectcb = nullptr;
|
||||||
|
|
|
@ -1,5 +1,26 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
server.on("/msg_pack", HTTP_ANY, [](AsyncWebServerRequest * request) {
|
||||||
|
AsyncMessagePackResponse * response = new AsyncMessagePackResponse();
|
||||||
|
JsonObject& root = response->getRoot();
|
||||||
|
root["key1"] = "key number one";
|
||||||
|
JsonObject& nested = root.createNestedObject("nested");
|
||||||
|
nested["key1"] = "key number one";
|
||||||
|
response->setLength();
|
||||||
|
request->send(response);
|
||||||
|
});
|
||||||
|
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
AsyncCallbackMessagePackWebHandler* handler = new AsyncCallbackMessagePackWebHandler("/msg_pack/endpoint");
|
||||||
|
handler->onRequest([](AsyncWebServerRequest *request, JsonVariant &json) {
|
||||||
|
JsonObject jsonObj = json.as<JsonObject>();
|
||||||
|
// ...
|
||||||
|
});
|
||||||
|
server.addHandler(handler);
|
||||||
|
*/
|
||||||
|
|
||||||
#if __has_include("ArduinoJson.h")
|
#if __has_include("ArduinoJson.h")
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#if ARDUINOJSON_VERSION_MAJOR >= 6
|
#if ARDUINOJSON_VERSION_MAJOR >= 6
|
|
@ -293,7 +293,9 @@ AsyncWebSocketClient::AsyncWebSocketClient(AsyncWebServerRequest* request, Async
|
||||||
|
|
||||||
AsyncWebSocketClient::~AsyncWebSocketClient() {
|
AsyncWebSocketClient::~AsyncWebSocketClient() {
|
||||||
{
|
{
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_lock);
|
std::lock_guard<std::mutex> lock(_lock);
|
||||||
|
#endif
|
||||||
_messageQueue.clear();
|
_messageQueue.clear();
|
||||||
_controlQueue.clear();
|
_controlQueue.clear();
|
||||||
}
|
}
|
||||||
|
@ -308,7 +310,9 @@ void AsyncWebSocketClient::_clearQueue() {
|
||||||
void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) {
|
void AsyncWebSocketClient::_onAck(size_t len, uint32_t time) {
|
||||||
_lastMessageTime = millis();
|
_lastMessageTime = millis();
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_lock);
|
std::lock_guard<std::mutex> lock(_lock);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (!_controlQueue.empty()) {
|
if (!_controlQueue.empty()) {
|
||||||
auto& head = _controlQueue.front();
|
auto& head = _controlQueue.front();
|
||||||
|
@ -338,11 +342,15 @@ void AsyncWebSocketClient::_onPoll() {
|
||||||
if (!_client)
|
if (!_client)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
std::unique_lock<std::mutex> lock(_lock);
|
std::unique_lock<std::mutex> lock(_lock);
|
||||||
|
#endif
|
||||||
if (_client && _client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) {
|
if (_client && _client->canSend() && (!_controlQueue.empty() || !_messageQueue.empty())) {
|
||||||
_runQueue();
|
_runQueue();
|
||||||
} else if (_keepAlivePeriod > 0 && (millis() - _lastMessageTime) >= _keepAlivePeriod && (_controlQueue.empty() && _messageQueue.empty())) {
|
} else if (_keepAlivePeriod > 0 && (millis() - _lastMessageTime) >= _keepAlivePeriod && (_controlQueue.empty() && _messageQueue.empty())) {
|
||||||
|
#ifdef ESP32
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
|
#endif
|
||||||
ping((uint8_t*)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN);
|
ping((uint8_t*)AWSC_PING_PAYLOAD, AWSC_PING_PAYLOAD_LEN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -362,17 +370,23 @@ void AsyncWebSocketClient::_runQueue() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncWebSocketClient::queueIsFull() const {
|
bool AsyncWebSocketClient::queueIsFull() const {
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_lock);
|
std::lock_guard<std::mutex> lock(_lock);
|
||||||
|
#endif
|
||||||
return (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED);
|
return (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) || (_status != WS_CONNECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t AsyncWebSocketClient::queueLen() const {
|
size_t AsyncWebSocketClient::queueLen() const {
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_lock);
|
std::lock_guard<std::mutex> lock(_lock);
|
||||||
|
#endif
|
||||||
return _messageQueue.size();
|
return _messageQueue.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AsyncWebSocketClient::canSend() const {
|
bool AsyncWebSocketClient::canSend() const {
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_lock);
|
std::lock_guard<std::mutex> lock(_lock);
|
||||||
|
#endif
|
||||||
return _messageQueue.size() < WS_MAX_QUEUED_MESSAGES;
|
return _messageQueue.size() < WS_MAX_QUEUED_MESSAGES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +394,9 @@ bool AsyncWebSocketClient::_queueControl(uint8_t opcode, const uint8_t* data, si
|
||||||
if (!_client)
|
if (!_client)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_lock);
|
std::lock_guard<std::mutex> lock(_lock);
|
||||||
|
#endif
|
||||||
|
|
||||||
_controlQueue.emplace_back(opcode, data, len, mask);
|
_controlQueue.emplace_back(opcode, data, len, mask);
|
||||||
|
|
||||||
|
@ -394,7 +410,9 @@ bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint
|
||||||
if (!_client || buffer->size() == 0 || _status != WS_CONNECTED)
|
if (!_client || buffer->size() == 0 || _status != WS_CONNECTED)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
std::lock_guard<std::mutex> lock(_lock);
|
std::lock_guard<std::mutex> lock(_lock);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) {
|
if (_messageQueue.size() >= WS_MAX_QUEUED_MESSAGES) {
|
||||||
if (closeWhenFull) {
|
if (closeWhenFull) {
|
||||||
|
@ -402,10 +420,19 @@ bool AsyncWebSocketClient::_queueMessage(AsyncWebSocketSharedBuffer buffer, uint
|
||||||
|
|
||||||
if (_client)
|
if (_client)
|
||||||
_client->close(true);
|
_client->close(true);
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: closing connection\n");
|
||||||
|
#elif defined(ESP32)
|
||||||
log_e("Too many messages queued: closing connection");
|
log_e("Too many messages queued: closing connection");
|
||||||
|
#endif
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
#ifdef ESP8266
|
||||||
|
ets_printf("AsyncWebSocketClient::_queueMessage: Too many messages queued: discarding new message\n");
|
||||||
|
#elif defined(ESP32)
|
||||||
log_e("Too many messages queued: discarding new message");
|
log_e("Too many messages queued: discarding new message");
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
|
@ -599,6 +626,30 @@ size_t AsyncWebSocketClient::printf(const char* format, ...) {
|
||||||
return enqueued ? len : 0;
|
return enqueued ? len : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
size_t AsyncWebSocketClient::printf_P(PGM_P formatP, ...) {
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, formatP);
|
||||||
|
size_t len = vsnprintf_P(nullptr, 0, formatP, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
char* buffer = new char[len + 1];
|
||||||
|
|
||||||
|
if (!buffer)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
va_start(arg, formatP);
|
||||||
|
len = vsnprintf_P(buffer, len + 1, formatP, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
bool enqueued = text(buffer, len);
|
||||||
|
delete[] buffer;
|
||||||
|
return enqueued ? len : 0;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
AsyncWebSocketSharedBuffer makeSharedBuffer(const uint8_t* message, size_t len) {
|
AsyncWebSocketSharedBuffer makeSharedBuffer(const uint8_t* message, size_t len) {
|
||||||
|
@ -637,6 +688,28 @@ bool AsyncWebSocketClient::text(const String& message) {
|
||||||
return text(message.c_str(), message.length());
|
return text(message.c_str(), message.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
bool AsyncWebSocketClient::text(const __FlashStringHelper* data) {
|
||||||
|
PGM_P p = reinterpret_cast<PGM_P>(data);
|
||||||
|
|
||||||
|
size_t n = 0;
|
||||||
|
while (1) {
|
||||||
|
if (pgm_read_byte(p + n) == 0)
|
||||||
|
break;
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* message = (char*)malloc(n + 1);
|
||||||
|
bool enqueued = false;
|
||||||
|
if (message) {
|
||||||
|
memcpy_P(message, p, n);
|
||||||
|
message[n] = 0;
|
||||||
|
enqueued = text(message, n);
|
||||||
|
free(message);
|
||||||
|
}
|
||||||
|
return enqueued;
|
||||||
|
}
|
||||||
|
#endif // ESP8266
|
||||||
|
|
||||||
bool AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer* buffer) {
|
bool AsyncWebSocketClient::binary(AsyncWebSocketMessageBuffer* buffer) {
|
||||||
bool enqueued = false;
|
bool enqueued = false;
|
||||||
|
@ -667,6 +740,19 @@ bool AsyncWebSocketClient::binary(const String& message) {
|
||||||
return binary(message.c_str(), message.length());
|
return binary(message.c_str(), message.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
bool AsyncWebSocketClient::binary(const __FlashStringHelper* data, size_t len) {
|
||||||
|
PGM_P p = reinterpret_cast<PGM_P>(data);
|
||||||
|
char* message = (char*)malloc(len);
|
||||||
|
bool enqueued = false;
|
||||||
|
if (message) {
|
||||||
|
memcpy_P(message, p, len);
|
||||||
|
enqueued = binary(message, len);
|
||||||
|
free(message);
|
||||||
|
}
|
||||||
|
return enqueued;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
IPAddress AsyncWebSocketClient::remoteIP() const {
|
IPAddress AsyncWebSocketClient::remoteIP() const {
|
||||||
if (!_client)
|
if (!_client)
|
||||||
|
@ -774,6 +860,29 @@ bool AsyncWebSocket::text(uint32_t id, const String& message) {
|
||||||
return text(id, message.c_str(), message.length());
|
return text(id, message.c_str(), message.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
bool AsyncWebSocket::text(uint32_t id, const __FlashStringHelper* data) {
|
||||||
|
PGM_P p = reinterpret_cast<PGM_P>(data);
|
||||||
|
|
||||||
|
size_t n = 0;
|
||||||
|
while (true) {
|
||||||
|
if (pgm_read_byte(p + n) == 0)
|
||||||
|
break;
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* message = (char*)malloc(n + 1);
|
||||||
|
bool enqueued = false;
|
||||||
|
if (message) {
|
||||||
|
memcpy_P(message, p, n);
|
||||||
|
message[n] = 0;
|
||||||
|
enqueued = text(id, message, n);
|
||||||
|
free(message);
|
||||||
|
}
|
||||||
|
return enqueued;
|
||||||
|
}
|
||||||
|
#endif // ESP8266
|
||||||
|
|
||||||
bool AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer* buffer) {
|
bool AsyncWebSocket::text(uint32_t id, AsyncWebSocketMessageBuffer* buffer) {
|
||||||
bool enqueued = false;
|
bool enqueued = false;
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
|
@ -799,6 +908,28 @@ AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const char* message) {
|
||||||
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const String& message) {
|
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const String& message) {
|
||||||
return textAll(message.c_str(), message.length());
|
return textAll(message.c_str(), message.length());
|
||||||
}
|
}
|
||||||
|
#ifdef ESP8266
|
||||||
|
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(const __FlashStringHelper* data) {
|
||||||
|
PGM_P p = reinterpret_cast<PGM_P>(data);
|
||||||
|
|
||||||
|
size_t n = 0;
|
||||||
|
while (1) {
|
||||||
|
if (pgm_read_byte(p + n) == 0)
|
||||||
|
break;
|
||||||
|
n += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
char* message = (char*)malloc(n + 1);
|
||||||
|
AsyncWebSocket::SendStatus status = DISCARDED;
|
||||||
|
if (message) {
|
||||||
|
memcpy_P(message, p, n);
|
||||||
|
message[n] = 0;
|
||||||
|
status = textAll(message, n);
|
||||||
|
free(message);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
#endif // ESP8266
|
||||||
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer* buffer) {
|
AsyncWebSocket::SendStatus AsyncWebSocket::textAll(AsyncWebSocketMessageBuffer* buffer) {
|
||||||
AsyncWebSocket::SendStatus status = DISCARDED;
|
AsyncWebSocket::SendStatus status = DISCARDED;
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
|
@ -833,6 +964,19 @@ bool AsyncWebSocket::binary(uint32_t id, const String& message) {
|
||||||
return binary(id, message.c_str(), message.length());
|
return binary(id, message.c_str(), message.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
bool AsyncWebSocket::binary(uint32_t id, const __FlashStringHelper* data, size_t len) {
|
||||||
|
PGM_P p = reinterpret_cast<PGM_P>(data);
|
||||||
|
char* message = (char*)malloc(len);
|
||||||
|
bool enqueued = false;
|
||||||
|
if (message) {
|
||||||
|
memcpy_P(message, p, len);
|
||||||
|
enqueued = binary(id, message, len);
|
||||||
|
free(message);
|
||||||
|
}
|
||||||
|
return enqueued;
|
||||||
|
}
|
||||||
|
#endif // ESP8266
|
||||||
|
|
||||||
bool AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer) {
|
bool AsyncWebSocket::binary(uint32_t id, AsyncWebSocketMessageBuffer* buffer) {
|
||||||
bool enqueued = false;
|
bool enqueued = false;
|
||||||
|
@ -860,6 +1004,20 @@ AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const String& message) {
|
||||||
return binaryAll(message.c_str(), message.length());
|
return binaryAll(message.c_str(), message.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(const __FlashStringHelper* data, size_t len) {
|
||||||
|
PGM_P p = reinterpret_cast<PGM_P>(data);
|
||||||
|
char* message = (char*)malloc(len);
|
||||||
|
AsyncWebSocket::SendStatus status = DISCARDED;
|
||||||
|
if (message) {
|
||||||
|
memcpy_P(message, p, len);
|
||||||
|
status = binaryAll(message, len);
|
||||||
|
free(message);
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
#endif // ESP8266
|
||||||
|
|
||||||
AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer* buffer) {
|
AsyncWebSocket::SendStatus AsyncWebSocket::binaryAll(AsyncWebSocketMessageBuffer* buffer) {
|
||||||
AsyncWebSocket::SendStatus status = DISCARDED;
|
AsyncWebSocket::SendStatus status = DISCARDED;
|
||||||
if (buffer) {
|
if (buffer) {
|
||||||
|
@ -914,6 +1072,43 @@ size_t AsyncWebSocket::printfAll(const char* format, ...) {
|
||||||
return status == DISCARDED ? 0 : len;
|
return status == DISCARDED ? 0 : len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
size_t AsyncWebSocket::printf_P(uint32_t id, PGM_P formatP, ...) {
|
||||||
|
AsyncWebSocketClient* c = client(id);
|
||||||
|
if (c != NULL) {
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, formatP);
|
||||||
|
size_t len = c->printf_P(formatP, arg);
|
||||||
|
va_end(arg);
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t AsyncWebSocket::printfAll_P(PGM_P formatP, ...) {
|
||||||
|
va_list arg;
|
||||||
|
va_start(arg, formatP);
|
||||||
|
size_t len = vsnprintf_P(nullptr, 0, formatP, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
if (len == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
char* buffer = new char[len + 1];
|
||||||
|
|
||||||
|
if (!buffer)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
va_start(arg, formatP);
|
||||||
|
len = vsnprintf_P(buffer, len + 1, formatP, arg);
|
||||||
|
va_end(arg);
|
||||||
|
|
||||||
|
AsyncWebSocket::SendStatus status = textAll(buffer, len);
|
||||||
|
delete[] buffer;
|
||||||
|
return status == DISCARDED ? 0 : len;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const char __WS_STR_CONNECTION[] PROGMEM = {"Connection"};
|
const char __WS_STR_CONNECTION[] PROGMEM = {"Connection"};
|
||||||
const char __WS_STR_UPGRADE[] PROGMEM = {"Upgrade"};
|
const char __WS_STR_UPGRADE[] PROGMEM = {"Upgrade"};
|
||||||
const char __WS_STR_ORIGIN[] PROGMEM = {"Origin"};
|
const char __WS_STR_ORIGIN[] PROGMEM = {"Origin"};
|
|
@ -22,18 +22,41 @@
|
||||||
#define ASYNCWEBSOCKET_H_
|
#define ASYNCWEBSOCKET_H_
|
||||||
|
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
#include "../../mathieucarbou-AsyncTCPSock/src/AsyncTCP.h"
|
#ifdef ESP32
|
||||||
|
#include "../../mathieucarbou-AsyncTCPSock/src/AsyncTCP.h"
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#ifndef WS_MAX_QUEUED_MESSAGES
|
#ifndef WS_MAX_QUEUED_MESSAGES
|
||||||
#define WS_MAX_QUEUED_MESSAGES 32
|
#define WS_MAX_QUEUED_MESSAGES 32
|
||||||
#endif
|
#endif
|
||||||
|
#elif defined(ESP8266)
|
||||||
|
#include <ESPAsyncTCP.h>
|
||||||
|
#ifndef WS_MAX_QUEUED_MESSAGES
|
||||||
|
#define WS_MAX_QUEUED_MESSAGES 8
|
||||||
|
#endif
|
||||||
|
#elif defined(TARGET_RP2040)
|
||||||
|
#include <AsyncTCP_RP2040W.h>
|
||||||
|
#ifndef WS_MAX_QUEUED_MESSAGES
|
||||||
|
#define WS_MAX_QUEUED_MESSAGES 32
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#include "ESPAsyncWebServer.h"
|
#include "ESPAsyncWebServer.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
#include <Hash.h>
|
||||||
|
#ifdef CRYPTO_HASH_h // include Hash.h from espressif framework if the first include was from the crypto library
|
||||||
|
#include <../src/Hash.h>
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef DEFAULT_MAX_WS_CLIENTS
|
#ifndef DEFAULT_MAX_WS_CLIENTS
|
||||||
|
#ifdef ESP32
|
||||||
#define DEFAULT_MAX_WS_CLIENTS 8
|
#define DEFAULT_MAX_WS_CLIENTS 8
|
||||||
|
#else
|
||||||
|
#define DEFAULT_MAX_WS_CLIENTS 4
|
||||||
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
using AsyncWebSocketSharedBuffer = std::shared_ptr<std::vector<uint8_t>>;
|
using AsyncWebSocketSharedBuffer = std::shared_ptr<std::vector<uint8_t>>;
|
||||||
|
@ -129,7 +152,9 @@ class AsyncWebSocketClient {
|
||||||
AsyncWebSocket* _server;
|
AsyncWebSocket* _server;
|
||||||
uint32_t _clientId;
|
uint32_t _clientId;
|
||||||
AwsClientStatus _status;
|
AwsClientStatus _status;
|
||||||
|
#ifdef ESP32
|
||||||
mutable std::mutex _lock;
|
mutable std::mutex _lock;
|
||||||
|
#endif
|
||||||
std::deque<AsyncWebSocketControl> _controlQueue;
|
std::deque<AsyncWebSocketControl> _controlQueue;
|
||||||
std::deque<AsyncWebSocketMessage> _messageQueue;
|
std::deque<AsyncWebSocketMessage> _messageQueue;
|
||||||
bool closeWhenFull = true;
|
bool closeWhenFull = true;
|
||||||
|
@ -229,6 +254,11 @@ class AsyncWebSocketClient {
|
||||||
void _onDisconnect();
|
void _onDisconnect();
|
||||||
void _onData(void* pbuf, size_t plen);
|
void _onData(void* pbuf, size_t plen);
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
size_t printf_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
|
||||||
|
bool text(const __FlashStringHelper* message);
|
||||||
|
bool binary(const __FlashStringHelper* message, size_t len);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
using AwsHandshakeHandler = std::function<bool(AsyncWebServerRequest* request)>;
|
using AwsHandshakeHandler = std::function<bool(AsyncWebServerRequest* request)>;
|
||||||
|
@ -243,8 +273,9 @@ class AsyncWebSocket : public AsyncWebHandler {
|
||||||
AwsEventHandler _eventHandler{nullptr};
|
AwsEventHandler _eventHandler{nullptr};
|
||||||
AwsHandshakeHandler _handshakeHandler;
|
AwsHandshakeHandler _handshakeHandler;
|
||||||
bool _enabled;
|
bool _enabled;
|
||||||
|
#ifdef ESP32
|
||||||
mutable std::mutex _lock;
|
mutable std::mutex _lock;
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
@ -304,6 +335,15 @@ class AsyncWebSocket : public AsyncWebHandler {
|
||||||
size_t printf(uint32_t id, const char* format, ...) __attribute__((format(printf, 3, 4)));
|
size_t printf(uint32_t id, const char* format, ...) __attribute__((format(printf, 3, 4)));
|
||||||
size_t printfAll(const char* format, ...) __attribute__((format(printf, 2, 3)));
|
size_t printfAll(const char* format, ...) __attribute__((format(printf, 2, 3)));
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
bool text(uint32_t id, const __FlashStringHelper* message);
|
||||||
|
SendStatus textAll(const __FlashStringHelper* message);
|
||||||
|
bool binary(uint32_t id, const __FlashStringHelper* message, size_t len);
|
||||||
|
SendStatus binaryAll(const __FlashStringHelper* message, size_t len);
|
||||||
|
size_t printf_P(uint32_t id, PGM_P formatP, ...) __attribute__((format(printf, 3, 4)));
|
||||||
|
size_t printfAll_P(PGM_P formatP, ...) __attribute__((format(printf, 2, 3)));
|
||||||
|
#endif
|
||||||
|
|
||||||
void onEvent(AwsEventHandler handler) { _eventHandler = handler; }
|
void onEvent(AwsEventHandler handler) { _eventHandler = handler; }
|
||||||
void handleHandshake(AwsHandshakeHandler handler) { _handshakeHandler = handler; }
|
void handleHandshake(AwsHandshakeHandler handler) { _handshakeHandler = handler; }
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#ifdef ESP32
|
#ifdef ESP32
|
||||||
#include "../../mathieucarbou-AsyncTCPSock/src/AsyncTCP.h"
|
#include "../../mathieucarbou-AsyncTCPSock/src/AsyncTCP.h"
|
||||||
#include <WiFi.h>
|
#include <WiFi.h>
|
||||||
#elif defined(ESP8266)
|
#elif defined(ESP8266)
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
|
@ -48,11 +48,11 @@
|
||||||
|
|
||||||
#include "literals.h"
|
#include "literals.h"
|
||||||
|
|
||||||
#define ASYNCWEBSERVER_VERSION "3.6.0"
|
#define ASYNCWEBSERVER_VERSION "3.6.2"
|
||||||
#define ASYNCWEBSERVER_VERSION_MAJOR 3
|
#define ASYNCWEBSERVER_VERSION_MAJOR 3
|
||||||
#define ASYNCWEBSERVER_VERSION_MINOR 6
|
#define ASYNCWEBSERVER_VERSION_MINOR 6
|
||||||
#define ASYNCWEBSERVER_VERSION_REVISION 0
|
#define ASYNCWEBSERVER_VERSION_REVISION 2
|
||||||
#define ASYNCWEBSERVER_FORK_mathieucarbou
|
#define ASYNCWEBSERVER_FORK_ESP32Async
|
||||||
|
|
||||||
#ifdef ASYNCWEBSERVER_REGEX
|
#ifdef ASYNCWEBSERVER_REGEX
|
||||||
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE
|
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE
|
||||||
|
@ -60,6 +60,12 @@
|
||||||
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined")))
|
#define ASYNCWEBSERVER_REGEX_ATTRIBUTE __attribute__((warning("ASYNCWEBSERVER_REGEX not defined")))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// See https://github.com/ESP32Async/ESPAsyncWebServer/commit/3d3456e9e81502a477f6498c44d0691499dda8f9#diff-646b25b11691c11dce25529e3abce843f0ba4bd07ab75ec9eee7e72b06dbf13fR388-R392
|
||||||
|
// This setting slowdown chunk serving but avoids crashing or deadlocks in the case where slow chunk responses are created, like file serving form SD Card
|
||||||
|
#ifndef ASYNCWEBSERVER_USE_CHUNK_INFLIGHT
|
||||||
|
#define ASYNCWEBSERVER_USE_CHUNK_INFLIGHT 1
|
||||||
|
#endif
|
||||||
|
|
||||||
class AsyncWebServer;
|
class AsyncWebServer;
|
||||||
class AsyncWebServerRequest;
|
class AsyncWebServerRequest;
|
||||||
class AsyncWebServerResponse;
|
class AsyncWebServerResponse;
|
||||||
|
@ -83,7 +89,6 @@ typedef enum {
|
||||||
HTTP_ANY = 0b01111111,
|
HTTP_ANY = 0b01111111,
|
||||||
} WebRequestMethod;
|
} WebRequestMethod;
|
||||||
|
|
||||||
|
|
||||||
#ifndef HAVE_FS_FILE_OPEN_MODE
|
#ifndef HAVE_FS_FILE_OPEN_MODE
|
||||||
namespace fs {
|
namespace fs {
|
||||||
class FileOpenMode {
|
class FileOpenMode {
|
||||||
|
@ -398,6 +403,9 @@ class AsyncWebServerRequest {
|
||||||
const AsyncWebParameter* getParam(const char* name, bool post = false, bool file = false) const;
|
const AsyncWebParameter* getParam(const char* name, bool post = false, bool file = false) const;
|
||||||
|
|
||||||
const AsyncWebParameter* getParam(const String& name, bool post = false, bool file = false) const { return getParam(name.c_str(), post, file); };
|
const AsyncWebParameter* getParam(const String& name, bool post = false, bool file = false) const { return getParam(name.c_str(), post, file); };
|
||||||
|
#ifdef ESP8266
|
||||||
|
const AsyncWebParameter* getParam(const __FlashStringHelper* data, bool post, bool file) const;
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get request parameter by number
|
* @brief Get request parameter by number
|
||||||
|
@ -413,10 +421,16 @@ class AsyncWebServerRequest {
|
||||||
const String& arg(const char* name) const;
|
const String& arg(const char* name) const;
|
||||||
// get request argument value by name
|
// get request argument value by name
|
||||||
const String& arg(const String& name) const { return arg(name.c_str()); };
|
const String& arg(const String& name) const { return arg(name.c_str()); };
|
||||||
|
#ifdef ESP8266
|
||||||
|
const String& arg(const __FlashStringHelper* data) const; // get request argument value by F(name)
|
||||||
|
#endif
|
||||||
const String& arg(size_t i) const; // get request argument value by number
|
const String& arg(size_t i) const; // get request argument value by number
|
||||||
const String& argName(size_t i) const; // get request argument name by number
|
const String& argName(size_t i) const; // get request argument name by number
|
||||||
bool hasArg(const char* name) const; // check if argument exists
|
bool hasArg(const char* name) const; // check if argument exists
|
||||||
bool hasArg(const String& name) const { return hasArg(name.c_str()); };
|
bool hasArg(const String& name) const { return hasArg(name.c_str()); };
|
||||||
|
#ifdef ESP8266
|
||||||
|
bool hasArg(const __FlashStringHelper* data) const; // check if F(argument) exists
|
||||||
|
#endif
|
||||||
|
|
||||||
const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const;
|
const String& ASYNCWEBSERVER_REGEX_ATTRIBUTE pathArg(size_t i) const;
|
||||||
|
|
||||||
|
@ -424,6 +438,9 @@ class AsyncWebServerRequest {
|
||||||
const String& header(const char* name) const;
|
const String& header(const char* name) const;
|
||||||
const String& header(const String& name) const { return header(name.c_str()); };
|
const String& header(const String& name) const { return header(name.c_str()); };
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
const String& header(const __FlashStringHelper* data) const; // get request header value by F(name)
|
||||||
|
#endif
|
||||||
|
|
||||||
const String& header(size_t i) const; // get request header value by number
|
const String& header(size_t i) const; // get request header value by number
|
||||||
const String& headerName(size_t i) const; // get request header name by number
|
const String& headerName(size_t i) const; // get request header name by number
|
||||||
|
@ -433,9 +450,15 @@ class AsyncWebServerRequest {
|
||||||
// check if header exists
|
// check if header exists
|
||||||
bool hasHeader(const char* name) const;
|
bool hasHeader(const char* name) const;
|
||||||
bool hasHeader(const String& name) const { return hasHeader(name.c_str()); };
|
bool hasHeader(const String& name) const { return hasHeader(name.c_str()); };
|
||||||
|
#ifdef ESP8266
|
||||||
|
bool hasHeader(const __FlashStringHelper* data) const; // check if header exists
|
||||||
|
#endif
|
||||||
|
|
||||||
const AsyncWebHeader* getHeader(const char* name) const;
|
const AsyncWebHeader* getHeader(const char* name) const;
|
||||||
const AsyncWebHeader* getHeader(const String& name) const { return getHeader(name.c_str()); };
|
const AsyncWebHeader* getHeader(const String& name) const { return getHeader(name.c_str()); };
|
||||||
|
#ifdef ESP8266
|
||||||
|
const AsyncWebHeader* getHeader(const __FlashStringHelper* data) const;
|
||||||
|
#endif
|
||||||
|
|
||||||
const AsyncWebHeader* getHeader(size_t num) const;
|
const AsyncWebHeader* getHeader(size_t num) const;
|
||||||
|
|
||||||
|
@ -452,6 +475,9 @@ class AsyncWebServerRequest {
|
||||||
size_t params() const; // get arguments count
|
size_t params() const; // get arguments count
|
||||||
bool hasParam(const char* name, bool post = false, bool file = false) const;
|
bool hasParam(const char* name, bool post = false, bool file = false) const;
|
||||||
bool hasParam(const String& name, bool post = false, bool file = false) const { return hasParam(name.c_str(), post, file); };
|
bool hasParam(const String& name, bool post = false, bool file = false) const { return hasParam(name.c_str(), post, file); };
|
||||||
|
#ifdef ESP8266
|
||||||
|
bool hasParam(const __FlashStringHelper* data, bool post = false, bool file = false) const { return hasParam(String(data).c_str(), post, file); };
|
||||||
|
#endif
|
||||||
|
|
||||||
// REQUEST ATTRIBUTES
|
// REQUEST ATTRIBUTES
|
||||||
|
|
|
@ -93,7 +93,11 @@ static bool getMD5(uint8_t* data, uint16_t len, char* output) { // 33 bytes or m
|
||||||
}
|
}
|
||||||
|
|
||||||
String genRandomMD5() {
|
String genRandomMD5() {
|
||||||
|
#ifdef ESP8266
|
||||||
|
uint32_t r = RANDOM_REG32;
|
||||||
|
#else
|
||||||
uint32_t r = rand();
|
uint32_t r = rand();
|
||||||
|
#endif
|
||||||
char* out = (char*)malloc(33);
|
char* out = (char*)malloc(33);
|
||||||
if (out == NULL || !getMD5((uint8_t*)(&r), 4, out))
|
if (out == NULL || !getMD5((uint8_t*)(&r), 4, out))
|
||||||
return emptyString;
|
return emptyString;
|
|
@ -86,7 +86,13 @@ AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(const char* last_m
|
||||||
|
|
||||||
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified) {
|
AsyncStaticWebHandler& AsyncStaticWebHandler::setLastModified(struct tm* last_modified) {
|
||||||
char result[30];
|
char result[30];
|
||||||
|
#ifdef ESP8266
|
||||||
|
auto formatP = PSTR("%a, %d %b %Y %H:%M:%S GMT");
|
||||||
|
char format[strlen_P(formatP) + 1];
|
||||||
|
strcpy_P(format, formatP);
|
||||||
|
#else
|
||||||
static constexpr const char* format = "%a, %d %b %Y %H:%M:%S GMT";
|
static constexpr const char* format = "%a, %d %b %Y %H:%M:%S GMT";
|
||||||
|
#endif
|
||||||
|
|
||||||
strftime(result, sizeof(result), format, last_modified);
|
strftime(result, sizeof(result), format, last_modified);
|
||||||
_last_modified = result;
|
_last_modified = result;
|
||||||
|
@ -133,7 +139,11 @@ bool AsyncStaticWebHandler::_getFile(AsyncWebServerRequest* request) const {
|
||||||
return const_cast<AsyncStaticWebHandler*>(this)->_searchFile(request, path);
|
return const_cast<AsyncStaticWebHandler*>(this)->_searchFile(request, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP32
|
||||||
#define FILE_IS_REAL(f) (f == true && !f.isDirectory())
|
#define FILE_IS_REAL(f) (f == true && !f.isDirectory())
|
||||||
|
#else
|
||||||
|
#define FILE_IS_REAL(f) (f == true)
|
||||||
|
#endif
|
||||||
|
|
||||||
bool AsyncStaticWebHandler::_searchFile(AsyncWebServerRequest* request, const String& path) {
|
bool AsyncStaticWebHandler::_searchFile(AsyncWebServerRequest* request, const String& path) {
|
||||||
bool fileFound = false;
|
bool fileFound = false;
|
||||||
|
@ -298,6 +308,7 @@ void AsyncCallbackWebHandler::handleUpload(AsyncWebServerRequest* request, const
|
||||||
_onUpload(request, filename, index, data, len, final);
|
_onUpload(request, filename, index, data, len, final);
|
||||||
}
|
}
|
||||||
void AsyncCallbackWebHandler::handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) {
|
void AsyncCallbackWebHandler::handleBody(AsyncWebServerRequest* request, uint8_t* data, size_t len, size_t index, size_t total) {
|
||||||
|
// ESP_LOGD("AsyncWebServer", "AsyncCallbackWebHandler::handleBody");
|
||||||
if (_onBody)
|
if (_onBody)
|
||||||
_onBody(request, data, len, index, total);
|
_onBody(request, data, len, index, total);
|
||||||
}
|
}
|
|
@ -70,7 +70,9 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) {
|
||||||
// SSL/TLS handshake detection
|
// SSL/TLS handshake detection
|
||||||
#ifndef ASYNC_TCP_SSL_ENABLED
|
#ifndef ASYNC_TCP_SSL_ENABLED
|
||||||
if (_parseState == PARSE_REQ_START && len && ((uint8_t*)buf)[0] == 0x16) { // 0x16 indicates a Handshake message (SSL/TLS).
|
if (_parseState == PARSE_REQ_START && len && ((uint8_t*)buf)[0] == 0x16) { // 0x16 indicates a Handshake message (SSL/TLS).
|
||||||
|
#ifdef ESP32
|
||||||
log_d("SSL/TLS handshake detected: resetting connection");
|
log_d("SSL/TLS handshake detected: resetting connection");
|
||||||
|
#endif
|
||||||
_parseState = PARSE_REQ_FAIL;
|
_parseState = PARSE_REQ_FAIL;
|
||||||
_client->abort();
|
_client->abort();
|
||||||
return;
|
return;
|
||||||
|
@ -139,6 +141,7 @@ void AsyncWebServerRequest::_onData(void* buf, size_t len) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!_isPlainPost) {
|
if (!_isPlainPost) {
|
||||||
|
// ESP_LOGD("AsyncWebServer", "_isPlainPost: %d, _handler: %p", _isPlainPost, _handler);
|
||||||
if (_handler)
|
if (_handler)
|
||||||
_handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength);
|
_handler->handleBody(this, (uint8_t*)buf, len, _parsedLength, _contentLength);
|
||||||
_parsedLength += len;
|
_parsedLength += len;
|
||||||
|
@ -641,11 +644,32 @@ bool AsyncWebServerRequest::hasHeader(const char* name) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
bool AsyncWebServerRequest::hasHeader(const __FlashStringHelper* data) const {
|
||||||
|
return hasHeader(String(data));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const char* name) const {
|
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const char* name) const {
|
||||||
auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); });
|
auto iter = std::find_if(std::begin(_headers), std::end(_headers), [&name](const AsyncWebHeader& header) { return header.name().equalsIgnoreCase(name); });
|
||||||
return (iter == std::end(_headers)) ? nullptr : &(*iter);
|
return (iter == std::end(_headers)) ? nullptr : &(*iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
const AsyncWebHeader* AsyncWebServerRequest::getHeader(const __FlashStringHelper* data) const {
|
||||||
|
PGM_P p = reinterpret_cast<PGM_P>(data);
|
||||||
|
size_t n = strlen_P(p);
|
||||||
|
char* name = (char*)malloc(n + 1);
|
||||||
|
if (name) {
|
||||||
|
strcpy_P(name, p);
|
||||||
|
const AsyncWebHeader* result = getHeader(String(name));
|
||||||
|
free(name);
|
||||||
|
return result;
|
||||||
|
} else {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const {
|
const AsyncWebHeader* AsyncWebServerRequest::getHeader(size_t num) const {
|
||||||
if (num >= _headers.size())
|
if (num >= _headers.size())
|
||||||
|
@ -690,6 +714,11 @@ const AsyncWebParameter* AsyncWebServerRequest::getParam(const char* name, bool
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
const AsyncWebParameter* AsyncWebServerRequest::getParam(const __FlashStringHelper* data, bool post, bool file) const {
|
||||||
|
return getParam(String(data), post, file);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const {
|
const AsyncWebParameter* AsyncWebServerRequest::getParam(size_t num) const {
|
||||||
if (num >= _params.size())
|
if (num >= _params.size())
|
||||||
|
@ -858,6 +887,12 @@ bool AsyncWebServerRequest::hasArg(const char* name) const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
bool AsyncWebServerRequest::hasArg(const __FlashStringHelper* data) const {
|
||||||
|
return hasArg(String(data).c_str());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const String& AsyncWebServerRequest::arg(const char* name) const {
|
const String& AsyncWebServerRequest::arg(const char* name) const {
|
||||||
for (const auto& arg : _params) {
|
for (const auto& arg : _params) {
|
||||||
if (arg.name() == name) {
|
if (arg.name() == name) {
|
||||||
|
@ -867,6 +902,12 @@ const String& AsyncWebServerRequest::arg(const char* name) const {
|
||||||
return emptyString;
|
return emptyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
const String& AsyncWebServerRequest::arg(const __FlashStringHelper* data) const {
|
||||||
|
return arg(String(data).c_str());
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
const String& AsyncWebServerRequest::arg(size_t i) const {
|
const String& AsyncWebServerRequest::arg(size_t i) const {
|
||||||
return getParam(i)->value();
|
return getParam(i)->value();
|
||||||
}
|
}
|
||||||
|
@ -884,6 +925,12 @@ const String& AsyncWebServerRequest::header(const char* name) const {
|
||||||
return h ? h->value() : emptyString;
|
return h ? h->value() : emptyString;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef ESP8266
|
||||||
|
const String& AsyncWebServerRequest::header(const __FlashStringHelper* data) const {
|
||||||
|
return header(String(data).c_str());
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
const String& AsyncWebServerRequest::header(size_t i) const {
|
const String& AsyncWebServerRequest::header(size_t i) const {
|
||||||
const AsyncWebHeader* h = getHeader(i);
|
const AsyncWebHeader* h = getHeader(i);
|
||||||
return h ? h->value() : emptyString;
|
return h ? h->value() : emptyString;
|
|
@ -47,10 +47,12 @@ class AsyncBasicResponse : public AsyncWebServerResponse {
|
||||||
|
|
||||||
class AsyncAbstractResponse : public AsyncWebServerResponse {
|
class AsyncAbstractResponse : public AsyncWebServerResponse {
|
||||||
private:
|
private:
|
||||||
|
#if ASYNCWEBSERVER_USE_CHUNK_INFLIGHT
|
||||||
// amount of responce data in-flight, i.e. sent, but not acked yet
|
// amount of responce data in-flight, i.e. sent, but not acked yet
|
||||||
size_t _in_flight{0};
|
size_t _in_flight{0};
|
||||||
// in-flight queue credits
|
// in-flight queue credits
|
||||||
size_t _in_flight_credit{2};
|
size_t _in_flight_credit{2};
|
||||||
|
#endif
|
||||||
String _head;
|
String _head;
|
||||||
// Data is inserted into cache at begin().
|
// Data is inserted into cache at begin().
|
||||||
// This is inefficient with vector, but if we use some other container,
|
// This is inefficient with vector, but if we use some other container,
|
|
@ -205,7 +205,11 @@ void AsyncWebServerResponse::_assembleHead(String& buffer, uint8_t version) {
|
||||||
buffer.reserve(len);
|
buffer.reserve(len);
|
||||||
|
|
||||||
// HTTP header
|
// HTTP header
|
||||||
|
#ifdef ESP8266
|
||||||
|
buffer.concat(PSTR("HTTP/1."));
|
||||||
|
#else
|
||||||
buffer.concat("HTTP/1.");
|
buffer.concat("HTTP/1.");
|
||||||
|
#endif
|
||||||
buffer.concat(version);
|
buffer.concat(version);
|
||||||
buffer.concat(' ');
|
buffer.concat(' ');
|
||||||
buffer.concat(_code);
|
buffer.concat(_code);
|
||||||
|
@ -216,7 +220,11 @@ void AsyncWebServerResponse::_assembleHead(String& buffer, uint8_t version) {
|
||||||
// Add headers
|
// Add headers
|
||||||
for (const auto& header : _headers) {
|
for (const auto& header : _headers) {
|
||||||
buffer.concat(header.name());
|
buffer.concat(header.name());
|
||||||
|
#ifdef ESP8266
|
||||||
|
buffer.concat(PSTR(": "));
|
||||||
|
#else
|
||||||
buffer.concat(": ");
|
buffer.concat(": ");
|
||||||
|
#endif
|
||||||
buffer.concat(header.value());
|
buffer.concat(header.value());
|
||||||
buffer.concat(T_rn);
|
buffer.concat(T_rn);
|
||||||
}
|
}
|
||||||
|
@ -344,19 +352,25 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
|
||||||
request->client()->close();
|
request->client()->close();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if ASYNCWEBSERVER_USE_CHUNK_INFLIGHT
|
||||||
// return a credit for each chunk of acked data (polls does not give any credits)
|
// return a credit for each chunk of acked data (polls does not give any credits)
|
||||||
if (len)
|
if (len)
|
||||||
++_in_flight_credit;
|
++_in_flight_credit;
|
||||||
|
|
||||||
// for chunked responses ignore acks if there are no _in_flight_credits left
|
// for chunked responses ignore acks if there are no _in_flight_credits left
|
||||||
if (_chunked && !_in_flight_credit) {
|
if (_chunked && !_in_flight_credit) {
|
||||||
|
#ifdef ESP32
|
||||||
log_d("(chunk) out of in-flight credits");
|
log_d("(chunk) out of in-flight credits");
|
||||||
|
#endif
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
_ackedLength += len;
|
|
||||||
_in_flight -= (_in_flight > len) ? len : _in_flight;
|
_in_flight -= (_in_flight > len) ? len : _in_flight;
|
||||||
// get the size of available sock space
|
// get the size of available sock space
|
||||||
|
#endif
|
||||||
|
|
||||||
|
_ackedLength += len;
|
||||||
size_t space = request->client()->space();
|
size_t space = request->client()->space();
|
||||||
|
|
||||||
size_t headLen = _head.length();
|
size_t headLen = _head.length();
|
||||||
|
@ -368,13 +382,16 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
|
||||||
String out = _head.substring(0, space);
|
String out = _head.substring(0, space);
|
||||||
_head = _head.substring(space);
|
_head = _head.substring(space);
|
||||||
_writtenLength += request->client()->write(out.c_str(), out.length());
|
_writtenLength += request->client()->write(out.c_str(), out.length());
|
||||||
|
#if ASYNCWEBSERVER_USE_CHUNK_INFLIGHT
|
||||||
_in_flight += out.length();
|
_in_flight += out.length();
|
||||||
--_in_flight_credit; // take a credit
|
--_in_flight_credit; // take a credit
|
||||||
|
#endif
|
||||||
return out.length();
|
return out.length();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_state == RESPONSE_CONTENT) {
|
if (_state == RESPONSE_CONTENT) {
|
||||||
|
#if ASYNCWEBSERVER_USE_CHUNK_INFLIGHT
|
||||||
// for response data we need to control the queue and in-flight fragmentation. Sending small chunks could give low latency,
|
// for response data we need to control the queue and in-flight fragmentation. Sending small chunks could give low latency,
|
||||||
// but flood asynctcp's queue and fragment socket buffer space for large responses.
|
// but flood asynctcp's queue and fragment socket buffer space for large responses.
|
||||||
// Let's ignore polled acks and acks in case when we have more in-flight data then the available socket buff space.
|
// Let's ignore polled acks and acks in case when we have more in-flight data then the available socket buff space.
|
||||||
|
@ -386,6 +403,7 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
|
||||||
--_in_flight_credit;
|
--_in_flight_credit;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
size_t outLen;
|
size_t outLen;
|
||||||
if (_chunked) {
|
if (_chunked) {
|
||||||
|
@ -441,8 +459,10 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
|
||||||
|
|
||||||
if (outLen) {
|
if (outLen) {
|
||||||
_writtenLength += request->client()->write((const char*)buf, outLen);
|
_writtenLength += request->client()->write((const char*)buf, outLen);
|
||||||
|
#if ASYNCWEBSERVER_USE_CHUNK_INFLIGHT
|
||||||
_in_flight += outLen;
|
_in_flight += outLen;
|
||||||
--_in_flight_credit; // take a credit
|
--_in_flight_credit; // take a credit
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_chunked) {
|
if (_chunked) {
|
|
@ -153,7 +153,7 @@ void AsyncWebServer::_attachHandler(AsyncWebServerRequest* request) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// ESP_LOGD("AsyncWebServer", "No handler found for %s, using _catchAllHandler pointer: %p", request->url().c_str(), _catchAllHandler);
|
||||||
request->setHandler(_catchAllHandler);
|
request->setHandler(_catchAllHandler);
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ _____ _ _ ___ _____ _
|
||||||
#include "StreamString.h"
|
#include "StreamString.h"
|
||||||
#if ELEGANTOTA_USE_ASYNC_WEBSERVER == 1
|
#if ELEGANTOTA_USE_ASYNC_WEBSERVER == 1
|
||||||
#include "../../mathieucarbou-AsyncTCPSock/src/AsyncTCP.h"
|
#include "../../mathieucarbou-AsyncTCPSock/src/AsyncTCP.h"
|
||||||
#include "../../mathieucarbou-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
|
#include "../../ESP32Async-ESPAsyncWebServer/src/ESPAsyncWebServer.h"
|
||||||
#define ELEGANTOTA_WEBSERVER AsyncWebServer
|
#define ELEGANTOTA_WEBSERVER AsyncWebServer
|
||||||
#else
|
#else
|
||||||
#include "WiFi.h"
|
#include "WiFi.h"
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue