diff --git a/Software/src/devboard/mqtt/mqtt.cpp b/Software/src/devboard/mqtt/mqtt.cpp index 921ad6e8..1a8be1dd 100644 --- a/Software/src/devboard/mqtt/mqtt.cpp +++ b/Software/src/devboard/mqtt/mqtt.cpp @@ -60,6 +60,8 @@ SensorConfig sensorConfigs[] = { }; +static std::vector order_events; + static String generateCommonInfoAutoConfigTopic(const char* object_id, const char* hostname) { return String("homeassistant/sensor/battery-emulator_") + String(hostname) + "/" + String(object_id) + "/config"; } @@ -233,7 +235,7 @@ void publish_events() { doc["unique_id"] = "battery-emulator_" + String(hostname) + "_event"; doc["object_id"] = String(hostname) + "_event"; doc["value_template"] = - "{{ value_json.event_type ~ ' (c:' ~ value_json.count ~ ',m:' ~ value_json.milis ~ ') ' ~ value_json.message " + "{{ value_json.event_type ~ ' (c:' ~ value_json.count ~ ',m:' ~ value_json.millis ~ ') ' ~ value_json.message " "}}"; doc["json_attributes_topic"] = state_topic; doc["json_attributes_template"] = "{{ value_json | tojson }}"; @@ -253,36 +255,43 @@ void publish_events() { #endif // HA_AUTODISCOVERY const EVENTS_STRUCT_TYPE* event_pointer; - unsigned long timestamp_now = get_current_event_time_secs(); + //clear the vector + order_events.clear(); + // Collect all events for (int i = 0; i < EVENT_NOF_EVENTS; i++) { event_pointer = get_event_pointer((EVENTS_ENUM_TYPE)i); - EVENTS_ENUM_TYPE event_handle = static_cast(i); - if (event_pointer->occurences > 0 && !event_pointer->MQTTpublished) { - - doc["event_type"] = String(get_event_enum_string(event_handle)); - doc["severity"] = String(get_event_level_string(event_handle)); - - time_t time_difference = timestamp_now - event_pointer->timestamp; - - doc["last_event"] = String(timestamp_now - event_pointer->timestamp); - doc["count"] = String(event_pointer->occurences); - doc["data"] = String(event_pointer->data); - doc["message"] = String(get_event_message_string(event_handle)); - doc["milis"] = String(millis()); - - serializeJson(doc, mqtt_msg); - if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) { -#ifdef DEBUG_VIA_USB - Serial.println("Common info MQTT msg could not be sent"); -#endif // DEBUG_VIA_USB - } else { - set_event_MQTTpublished(event_handle); - } - doc.clear(); + order_events.push_back({static_cast(i), event_pointer}); } } + // Sort events by timestamp + std::sort(order_events.begin(), order_events.end(), compareEventsByTimestamp); + + for (const auto& event : order_events) { + + EVENTS_ENUM_TYPE event_handle = event.event_handle; + event_pointer = event.event_pointer; + + doc["event_type"] = String(get_event_enum_string(event_handle)); + doc["severity"] = String(get_event_level_string(event_handle)); + doc["count"] = String(event_pointer->occurences); + doc["data"] = String(event_pointer->data); + doc["message"] = String(get_event_message_string(event_handle)); + doc["millis"] = String(event_pointer->timestamp); + + serializeJson(doc, mqtt_msg); + if (!mqtt_publish(state_topic.c_str(), mqtt_msg, false)) { +#ifdef DEBUG_VIA_USB + Serial.println("Common info MQTT msg could not be sent"); +#endif // DEBUG_VIA_USB + } else { + set_event_MQTTpublished(event_handle); + } + doc.clear(); + //clear the vector + order_events.clear(); + } #ifdef HA_AUTODISCOVERY } #endif // HA_AUTODISCOVERY diff --git a/Software/src/devboard/mqtt/mqtt.h b/Software/src/devboard/mqtt/mqtt.h index 9a3d872e..b2494c63 100644 --- a/Software/src/devboard/mqtt/mqtt.h +++ b/Software/src/devboard/mqtt/mqtt.h @@ -35,6 +35,7 @@ #define __MQTT_H__ #include +#include #include "../../include.h" #define MQTT_MSG_BUFFER_SIZE (1024) diff --git a/Software/src/devboard/utils/events.cpp b/Software/src/devboard/utils/events.cpp index fa9e4cb4..7dde9ed4 100644 --- a/Software/src/devboard/utils/events.cpp +++ b/Software/src/devboard/utils/events.cpp @@ -44,16 +44,14 @@ typedef struct { EVENTS_ENUM_TYPE event; + uint8_t millisrolloverCount; uint32_t timestamp; uint8_t data; } EVENT_LOG_ENTRY_TYPE; typedef struct { EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS]; - unsigned long time_seconds; - MyTimer second_timer; MyTimer ee_timer; - MyTimer update_timer; EVENTS_LEVEL_TYPE level; uint16_t event_log_head_index; uint16_t event_log_tail_index; @@ -66,21 +64,29 @@ static EVENT_TYPE events; static const char* EVENTS_ENUM_TYPE_STRING[] = {EVENTS_ENUM_TYPE(GENERATE_STRING)}; static const char* EVENTS_LEVEL_TYPE_STRING[] = {EVENTS_LEVEL_TYPE(GENERATE_STRING)}; +static uint32_t lastMillis = millis(); + /* Local function prototypes */ -static void update_event_time(void); static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched); static void update_event_level(void); static void update_bms_status(void); -static void log_event(EVENTS_ENUM_TYPE event, uint8_t data); +static void log_event(EVENTS_ENUM_TYPE event, uint8_t millisrolloverCount, uint32_t timestamp, uint8_t data); static void print_event_log(void); static void check_ee_write(void); +uint8_t millisrolloverCount = 0; + /* Exported functions */ /* Main execution function, should handle various continuous functionality */ void run_event_handling(void) { - update_event_time(); + uint32_t currentMillis = millis(); + if (currentMillis < lastMillis) { // Overflow detected + millisrolloverCount++; + } + lastMillis = currentMillis; + run_sequence_on_target(); //check_ee_write(); update_event_level(); @@ -100,7 +106,7 @@ void init_events(void) { EEPROM.writeUShort(EE_EVENT_LOG_TAIL_INDEX_ADDRESS, 0); // Prepare an empty event block to write - EVENT_LOG_ENTRY_TYPE entry = {.event = EVENT_NOF_EVENTS, .timestamp = 0, .data = 0}; + EVENT_LOG_ENTRY_TYPE entry = {.event = EVENT_NOF_EVENTS, .millisrolloverCount = 0, .timestamp = 0, .data = 0}; // Put the event in (what I guess is) the RAM EEPROM mirror, or write buffer @@ -128,6 +134,7 @@ void init_events(void) { for (uint16_t i = 0; i < EVENT_NOF_EVENTS; i++) { events.entries[i].data = 0; events.entries[i].timestamp = 0; + events.entries[i].millisrolloverCount = 0; events.entries[i].occurences = 0; events.entries[i].log = true; events.entries[i].MQTTpublished = false; // Not published by default @@ -206,10 +213,8 @@ void init_events(void) { events.entries[EVENT_EEPROM_WRITE].log = false; // Don't log the logger... - events.second_timer.set_interval(600); // Write to EEPROM every X minutes (if an event has been set) events.ee_timer.set_interval(EE_WRITE_PERIOD_MINUTES * 60 * 1000); - events.update_timer.set_interval(2000); } void set_event(EVENTS_ENUM_TYPE event, uint8_t data) { @@ -419,12 +424,13 @@ static void set_event(EVENTS_ENUM_TYPE event, uint8_t data, bool latched) { events.entries[event].occurences++; events.entries[event].MQTTpublished = false; if (events.entries[event].log) { - log_event(event, data); + log_event(event, events.entries[event].millisrolloverCount, events.entries[event].timestamp, data); } } // We should set the event, update event info - events.entries[event].timestamp = events.time_seconds; + events.entries[event].timestamp = millis(); + events.entries[event].millisrolloverCount = millisrolloverCount; events.entries[event].data = data; // Check if the event is latching events.entries[event].state = latched ? EVENT_STATE_ACTIVE_LATCHED : EVENT_STATE_ACTIVE; @@ -457,6 +463,14 @@ static void update_bms_status(void) { } } +// Function to compare events by timestamp +bool compareEventsByTimestamp(const EventData& a, const EventData& b) { + if (a.event_pointer->millisrolloverCount != b.event_pointer->millisrolloverCount) { + return a.event_pointer->millisrolloverCount > b.event_pointer->millisrolloverCount; + } + return a.event_pointer->timestamp > b.event_pointer->timestamp; +} + static void update_event_level(void) { EVENTS_LEVEL_TYPE temporary_level = EVENT_LEVEL_INFO; for (uint8_t i = 0u; i < EVENT_NOF_EVENTS; i++) { @@ -467,22 +481,7 @@ static void update_event_level(void) { events.level = temporary_level; } -static void update_event_time(void) { - // This should run roughly 2 times per second - if (events.second_timer.elapsed() == true) { - uptime::calculateUptime(); // millis() overflows every 50 days, so update occasionally to adjust - events.time_seconds = uptime::getDays() * DAYS_TO_SECS; - events.time_seconds += uptime::getHours() * HOURS_TO_SECS; - events.time_seconds += uptime::getMinutes() * MINUTES_TO_SECS; - events.time_seconds += uptime::getSeconds(); - } -} - -unsigned long get_current_event_time_secs(void) { - return events.time_seconds; -} - -static void log_event(EVENTS_ENUM_TYPE event, uint8_t data) { +static void log_event(EVENTS_ENUM_TYPE event, uint8_t millisrolloverCount, uint32_t timestamp, uint8_t data) { // Update head with wrap to 0 if (++events.event_log_head_index == EE_NOF_EVENT_ENTRIES) { events.event_log_head_index = 0; @@ -500,7 +499,8 @@ static void log_event(EVENTS_ENUM_TYPE event, uint8_t data) { int entry_address = EE_EVENT_ENTRY_START_ADDRESS + EE_EVENT_ENTRY_SIZE * events.event_log_head_index; // Prepare an event block to write - EVENT_LOG_ENTRY_TYPE entry = {.event = event, .timestamp = events.time_seconds, .data = data}; + EVENT_LOG_ENTRY_TYPE entry = { + .event = event, .millisrolloverCount = millisrolloverCount, .timestamp = timestamp, .data = data}; // Put the event in (what I guess is) the RAM EEPROM mirror, or write buffer EEPROM.put(entry_address, entry); diff --git a/Software/src/devboard/utils/events.h b/Software/src/devboard/utils/events.h index c4404e79..0b162cf8 100644 --- a/Software/src/devboard/utils/events.h +++ b/Software/src/devboard/utils/events.h @@ -120,20 +120,28 @@ typedef enum { } EVENTS_STATE_TYPE; typedef struct { - uint32_t timestamp; // Time in seconds since startup when the event occurred - uint8_t data; // Custom data passed when setting the event, for example cell number for under voltage - uint8_t occurences; // Number of occurrences since startup - EVENTS_LEVEL_TYPE level; // Event level, i.e. ERROR/WARNING... - EVENTS_STATE_TYPE state; // Event state, i.e. ACTIVE/INACTIVE... + uint32_t timestamp; // Time in seconds since startup when the event occurred + uint8_t millisrolloverCount; // number of times millis rollovers before timestamp + uint8_t data; // Custom data passed when setting the event, for example cell number for under voltage + uint8_t occurences; // Number of occurrences since startup + EVENTS_LEVEL_TYPE level; // Event level, i.e. ERROR/WARNING... + EVENTS_STATE_TYPE state; // Event state, i.e. ACTIVE/INACTIVE... bool log; bool MQTTpublished; } EVENTS_STRUCT_TYPE; +// Define a struct to hold event data +struct EventData { + EVENTS_ENUM_TYPE event_handle; + const EVENTS_STRUCT_TYPE* event_pointer; +}; + +extern uint8_t millisrolloverCount; // number of times millis rollovers + const char* get_event_enum_string(EVENTS_ENUM_TYPE event); const char* get_event_message_string(EVENTS_ENUM_TYPE event); const char* get_event_level_string(EVENTS_ENUM_TYPE event); const char* get_event_type(EVENTS_ENUM_TYPE event); -unsigned long get_current_event_time_secs(void); EVENTS_LEVEL_TYPE get_event_level(void); @@ -149,4 +157,6 @@ void run_event_handling(void); void run_sequence_on_target(void); +bool compareEventsByTimestamp(const EventData& a, const EventData& b); + #endif // __MYTIMER_H__ diff --git a/Software/src/devboard/webserver/events_html.cpp b/Software/src/devboard/webserver/events_html.cpp index dc6ca02b..a8ec776e 100644 --- a/Software/src/devboard/webserver/events_html.cpp +++ b/Software/src/devboard/webserver/events_html.cpp @@ -7,16 +7,11 @@ const char EVENTS_HTML_END[] = R"=====( - + )====="; static std::vector order_events; -// Function to compare events by timestamp -static bool compareEventsByTimestamp(const EventData& a, const EventData& b) { - return a.event_pointer->timestamp > b.event_pointer->timestamp; -} - String events_processor(const String& var) { if (var == "X") { String content = ""; @@ -25,8 +20,6 @@ String events_processor(const String& var) { content.concat(FPSTR(EVENTS_HTML_START)); const EVENTS_STRUCT_TYPE* event_pointer; - unsigned long timestamp_now = get_current_event_time_secs(); - //clear the vector order_events.clear(); // Collect all events @@ -36,9 +29,9 @@ String events_processor(const String& var) { order_events.push_back({static_cast(i), event_pointer}); } } - // Sort events by timestamp std::sort(order_events.begin(), order_events.end(), compareEventsByTimestamp); + unsigned long timestamp_now = millis(); // Generate HTML and debug output for (const auto& event : order_events) { @@ -53,7 +46,8 @@ String events_processor(const String& var) { content.concat("
"); content.concat("
" + String(get_event_enum_string(event_handle)) + "
"); content.concat("
" + String(get_event_level_string(event_handle)) + "
"); - content.concat("
" + String(timestamp_now - event_pointer->timestamp) + "
"); + content.concat("
" + String(millisrolloverCount) + ";" + + String(timestamp_now - event_pointer->timestamp) + "
"); content.concat("
" + String(event_pointer->occurences) + "
"); content.concat("
" + String(event_pointer->data) + "
"); content.concat("
" + String(get_event_message_string(event_handle)) + "
"); diff --git a/Software/src/devboard/webserver/events_html.h b/Software/src/devboard/webserver/events_html.h index debd2d99..3aae340f 100644 --- a/Software/src/devboard/webserver/events_html.h +++ b/Software/src/devboard/webserver/events_html.h @@ -14,10 +14,5 @@ * @return String */ String events_processor(const String& var); -// Define a struct to hold event data -struct EventData { - EVENTS_ENUM_TYPE event_handle; - const EVENTS_STRUCT_TYPE* event_pointer; -}; #endif