From 098d4c3db774c5806ead1adaa6e4686bd1cc0e1d Mon Sep 17 00:00:00 2001 From: brandon Date: Fri, 1 Dec 2023 16:52:02 -0800 Subject: [PATCH 1/2] Re-organizing enums + state definitions --- lib/app_states/src/app_states.h | 54 +++++++++++++++++++++ src/deployable_example.cpp | 83 ++++----------------------------- src/stacklight.h | 35 +++++++++++--- 3 files changed, 90 insertions(+), 82 deletions(-) create mode 100644 lib/app_states/src/app_states.h diff --git a/lib/app_states/src/app_states.h b/lib/app_states/src/app_states.h new file mode 100644 index 0000000..0caaaf2 --- /dev/null +++ b/lib/app_states/src/app_states.h @@ -0,0 +1,54 @@ + +#pragma once + +#include + +enum QueryState { + WAITING_TO_QUERY, + DNS_NOT_FOUND, + SSL_CONNECTION_FAILURE, + LAST_RESPONSE_PASS, + LAST_RESPONSE_FAIL, + LAST_RESPONSE_UNSURE, + NOT_AUTHENTICATED, +}; + +enum NotificationState { + NOTIFICATION_NOT_ATTEMPTED, + NOTIFICATIONS_SENT, + NOTIFICATION_ATTEMPT_FAILED, +}; + +String queryStateToString (QueryState state) { + switch (state) { + case WAITING_TO_QUERY: + return "WAITING_TO_QUERY"; + case SSL_CONNECTION_FAILURE: + return "CONNECTION_FAILURE"; + case DNS_NOT_FOUND: + return "DNS_NOT_FOUND"; + case LAST_RESPONSE_PASS: + return "LAST_RESPONSE_PASS"; + case LAST_RESPONSE_FAIL: + return "LAST_RESPONSE_FAIL"; + case LAST_RESPONSE_UNSURE: + return "LAST_RESPONSE_UNSURE"; + case NOT_AUTHENTICATED: + return "NOT_AUTHENTICATED"; + default: + return "UNKNOWN"; + } +} + +String notificationStateToString (NotificationState state) { + switch (state) { + case NOTIFICATION_NOT_ATTEMPTED: + return "NOTIFICATION_NOT_ATTEMPTED"; + case NOTIFICATIONS_SENT: + return "NOTIFICATIONS_SENT"; + case NOTIFICATION_ATTEMPT_FAILED: + return "NOTIFICATION_ATTEMPT_FAILED"; + default: + return "UNKNOWN"; + } +} \ No newline at end of file diff --git a/src/deployable_example.cpp b/src/deployable_example.cpp index 5cdb323..0a4289e 100644 --- a/src/deployable_example.cpp +++ b/src/deployable_example.cpp @@ -24,14 +24,15 @@ SOFTWARE. */ -#include + #include -#include "WiFi.h" +#include #include #include -#include "ArduinoJson.h" -#include "groundlight.h" +#include +#include "groundlight.h" +#include "app_states.h" #include "camera_pins.h" // thank you seeedstudio for this file #include "integrations.h" #include "stacklight.h" @@ -56,75 +57,6 @@ uint8_t *frame_565_old; #define ALPHA FRAME_ARR_LEN / ALPHA_DIVISOR #define BETA 20 // out of 31 -enum QueryState { - WAITING_TO_QUERY, - DNS_NOT_FOUND, - SSL_CONNECTION_FAILURE, - LAST_RESPONSE_PASS, - LAST_RESPONSE_FAIL, - LAST_RESPONSE_UNSURE, - NOT_AUTHENTICATED, -}; - -String queryStateToString (QueryState state) { - switch (state) { - case WAITING_TO_QUERY: - return "WAITING_TO_QUERY"; - case SSL_CONNECTION_FAILURE: - return "CONNECTION_FAILURE"; - case DNS_NOT_FOUND: - return "DNS_NOT_FOUND"; - case LAST_RESPONSE_PASS: - return "LAST_RESPONSE_PASS"; - case LAST_RESPONSE_FAIL: - return "LAST_RESPONSE_FAIL"; - case LAST_RESPONSE_UNSURE: - return "LAST_RESPONSE_UNSURE"; - case NOT_AUTHENTICATED: - return "NOT_AUTHENTICATED"; - default: - return "UNKNOWN"; - } -} - -enum NotificationState { - NOTIFICATION_NOT_ATTEMPTED, - NOTIFICATIONS_SENT, - NOTIFICATION_ATTEMPT_FAILED, -}; - -String notificationStateToString (NotificationState state) { - switch (state) { - case NOTIFICATION_NOT_ATTEMPTED: - return "NOTIFICATION_NOT_ATTEMPTED"; - case NOTIFICATIONS_SENT: - return "NOTIFICATIONS_SENT"; - case NOTIFICATION_ATTEMPT_FAILED: - return "NOTIFICATION_ATTEMPT_FAILED"; - default: - return "UNKNOWN"; - } -} - -enum StacklightState { - STACKLIGHT_NOT_FOUND, - STACKLIGHT_ONLINE, - STACKLIGHT_PAIRED, -}; - -String stacklightStateToString (StacklightState state) { - switch (state) { - case STACKLIGHT_NOT_FOUND: - return "STACKLIGHT_NOT_FOUND"; - case STACKLIGHT_ONLINE: - return "STACKLIGHT_ONLINE"; - case STACKLIGHT_PAIRED: - return "STACKLIGHT_PAIRED"; - default: - return "UNKNOWN"; - } -} - QueryState queryState = WAITING_TO_QUERY; NotificationState notificationState = NOTIFICATION_NOT_ATTEMPTED; StacklightState stacklightState = STACKLIGHT_NOT_FOUND; @@ -248,7 +180,7 @@ bool decodeWorkingHoursString(String working_hours); bool should_deep_sleep() { return (query_delay > 29) && !disable_deep_sleep_for_notifications && !disable_deep_sleep_until_reset; -} +} void deep_sleep() { int time_elapsed = millis() - last_upload_time; @@ -532,7 +464,7 @@ void setup() { preferences.getString("api_key", groundlight_API_key, 75); preferences.getString("det_id", groundlight_det_id, 100); query_delay = preferences.getInt("query_delay", query_delay); - + WiFi.begin(ssid, password); wifi_configured = true; } @@ -732,6 +664,7 @@ void loop () { vTaskDelay(1000 / portTICK_PERIOD_MS); #endif + // TODO: figure out why we need to capture twice to get the latest image? frame = esp_camera_fb_get(); // Testing how to get the latest image esp_camera_fb_return(frame); diff --git a/src/stacklight.h b/src/stacklight.h index 6bc8ea7..9795212 100644 --- a/src/stacklight.h +++ b/src/stacklight.h @@ -1,3 +1,5 @@ +#pragma once + #include #include "WiFi.h" #include @@ -117,12 +119,12 @@ namespace Stacklight /** * @brief Try to connect to Stacklight AP and push WiFi credentials - * + * * This happens over time, and not for initialization. Must be connected to the stacklight first. - * + * * @param ssid WiFi SSID * @param password WiFi password - * + * * @return Stacklight IP address (empty string if not connected) */ String tryConnectToStacklight(const char * ssid, const char * password) { @@ -133,18 +135,18 @@ namespace Stacklight vTaskDelay(1000 / portTICK_PERIOD_MS); pushWiFiCredToStacklight(ssid, password); } - + return ip; } /** * @brief Try to connect to Stacklight AP and push WiFi credentials - * + * * This is for initialization. Must be connected to the stacklight first. - * + * * @param ssid WiFi SSID * @param password WiFi password - * + * * @return Stacklight IP address (empty string if not connected) */ String initStacklight(const char * ssid, const char * password, int retries = 10) { @@ -184,3 +186,22 @@ namespace Stacklight return ip; } } + +enum StacklightState { + STACKLIGHT_NOT_FOUND, + STACKLIGHT_ONLINE, + STACKLIGHT_PAIRED, +}; + +String stacklightStateToString (StacklightState state) { + switch (state) { + case STACKLIGHT_NOT_FOUND: + return "STACKLIGHT_NOT_FOUND"; + case STACKLIGHT_ONLINE: + return "STACKLIGHT_ONLINE"; + case STACKLIGHT_PAIRED: + return "STACKLIGHT_PAIRED"; + default: + return "UNKNOWN"; + } +} \ No newline at end of file From cbdc0d2fab41c0150a400a780c516f3fcaac65cf Mon Sep 17 00:00:00 2001 From: brandon Date: Thu, 14 Dec 2023 15:12:05 -0800 Subject: [PATCH 2/2] add class to ensure esp frame buffer is freed between cycles --- lib/app_states/src/app_states.h | 26 +++++++++++++++++++++++++- src/deployable_example.cpp | 21 ++++++--------------- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/lib/app_states/src/app_states.h b/lib/app_states/src/app_states.h index 0caaaf2..6805cdd 100644 --- a/lib/app_states/src/app_states.h +++ b/lib/app_states/src/app_states.h @@ -51,4 +51,28 @@ String notificationStateToString (NotificationState state) { default: return "UNKNOWN"; } -} \ No newline at end of file +} + +/** + * @brief A class to lock the ESP32 frame buffer + * + * This class is used to lock the ESP32 frame buffer. It is used mainly for RAII, so that the frame buffer is properly released when the object goes out of scope. + * It's not a true lock, the pointer to the frame is freely accessbile + * + * @todo Implement this class + */ +class EspFrameLock { + public: + camera_fb_t* frame = NULL; + + EspFrameLock() { + frame = esp_camera_fb_get(); + } + + ~EspFrameLock() { + esp_camera_fb_return(frame); + delete frame; + } + EspFrameLock& operator=(const EspFrameLock&) = delete; // disallow assignment + EspFrameLock(const EspFrameLock&) = delete; // disallow copying +}; \ No newline at end of file diff --git a/src/deployable_example.cpp b/src/deployable_example.cpp index 0a4289e..aa2a80d 100644 --- a/src/deployable_example.cpp +++ b/src/deployable_example.cpp @@ -61,7 +61,6 @@ QueryState queryState = WAITING_TO_QUERY; NotificationState notificationState = NOTIFICATION_NOT_ATTEMPTED; StacklightState stacklightState = STACKLIGHT_NOT_FOUND; -camera_fb_t *frame = NULL; int *last_frame_buffer = NULL; char groundlight_endpoint[60] = "api.groundlight.ai"; @@ -664,33 +663,28 @@ void loop () { vTaskDelay(1000 / portTICK_PERIOD_MS); #endif - // TODO: figure out why we need to capture twice to get the latest image? - frame = esp_camera_fb_get(); - // Testing how to get the latest image - esp_camera_fb_return(frame); - frame = esp_camera_fb_get(); + EspFrameLock frameLock; #if defined(GPIO_LED_FLASH) digitalWrite(GPIO_LED_FLASH, LOW); #endif - if (!frame) + if (!frameLock.frame) { debug_printf("Camera capture failed! Restarting system in 3 seconds!\n"); delay(3000); ESP.restart(); // some boards are less reliable for camera captures and will everntually just start working } - debug_printf("encoded size is %d bytes\n", frame->len); + debug_printf("encoded size is %d bytes\n", frameLock.frame->len); preferences.begin("config"); if (preferences.isKey("motion") && preferences.getBool("motion") && preferences.isKey("mot_a") && preferences.isKey("mot_b")) { int alpha = round(preferences.getString("mot_a", "0.0").toFloat() * (float) FRAME_ARR_LEN); int beta = round(preferences.getString("mot_b", "0.0").toFloat() * (float) COLOR_VAL_MAX); - if (is_motion_detected(frame, alpha, beta)) { + if (is_motion_detected(frameLock.frame, alpha, beta)) { debug_println("Motion detected!"); } else { - esp_camera_fb_return(frame); if (should_deep_sleep()) { vTaskDelay(500 / portTICK_PERIOD_MS); deep_sleep(); @@ -717,14 +711,13 @@ void loop () { debug_printf("Submitting image query to Groundlight..."); - queryResults = submit_image_query(frame, groundlight_endpoint, groundlight_det_id, groundlight_API_key); + queryResults = submit_image_query(frameLock.frame, groundlight_endpoint, groundlight_det_id, groundlight_API_key); queryID = get_query_id(queryResults); debug_printf("Query ID: %s\n", queryID.c_str()); if (queryID == "NONE" || queryID == "") { debug_println("Failed to get query ID"); - esp_camera_fb_return(frame); return; } @@ -778,7 +771,7 @@ void loop () { } } if (shouldDoNotification(queryResults)) { - if (sendNotifications(last_label, frame)) { + if (sendNotifications(last_label, frameLock.frame)) { notificationState = NOTIFICATIONS_SENT; } else { notificationState = NOTIFICATION_ATTEMPT_FAILED; @@ -803,8 +796,6 @@ void loop () { debug_println(error.c_str()); } - esp_camera_fb_return(frame); - if (should_deep_sleep()) { vTaskDelay(500 / portTICK_PERIOD_MS); deep_sleep();