فهرست منبع

Stage new view/scene for wifi streaming. Various other updates and improvements.

Cody Tolene 2 سال پیش
والد
کامیت
e96b801998

+ 1 - 0
.prettierignore

@@ -0,0 +1 @@
+README.md

+ 9 - 0
camera_suite.c

@@ -71,6 +71,12 @@ CameraSuite* camera_suite_app_alloc() {
         CameraSuiteViewIdCamera,
         camera_suite_view_camera_get_view(app->camera_suite_view_camera));
 
+    app->camera_suite_view_wifi_camera = camera_suite_view_wifi_camera_alloc();
+    view_dispatcher_add_view(
+        app->view_dispatcher,
+        CameraSuiteViewIdWiFiCamera,
+        camera_suite_view_wifi_camera_get_view(app->camera_suite_view_wifi_camera));
+
     app->camera_suite_view_guide = camera_suite_view_guide_alloc();
     view_dispatcher_add_view(
         app->view_dispatcher,
@@ -104,6 +110,7 @@ void camera_suite_app_free(CameraSuite* app) {
     view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdStartscreen);
     view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdMenu);
     view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdCamera);
+    view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdWiFiCamera);
     view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdGuide);
     view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdAppSettings);
     view_dispatcher_remove_view(app->view_dispatcher, CameraSuiteViewIdCamSettings);
@@ -115,7 +122,9 @@ void camera_suite_app_free(CameraSuite* app) {
     // Free remaining resources
     camera_suite_view_start_free(app->camera_suite_view_start);
     camera_suite_view_camera_free(app->camera_suite_view_camera);
+    camera_suite_view_wifi_camera_free(app->camera_suite_view_wifi_camera);
     camera_suite_view_guide_free(app->camera_suite_view_guide);
+
     button_menu_free(app->button_menu);
     variable_item_list_free(app->variable_item_list);
 

+ 3 - 0
camera_suite.h

@@ -16,6 +16,7 @@
 #include "views/camera_suite_view_guide.h"
 #include "views/camera_suite_view_start.h"
 #include "views/camera_suite_view_camera.h"
+#include "views/camera_suite_view_wifi_camera.h"
 #include "helpers/camera_suite_storage.h"
 
 #define TAG "Camera Suite"
@@ -29,6 +30,7 @@ typedef struct {
     VariableItemList* variable_item_list;
     CameraSuiteViewStart* camera_suite_view_start;
     CameraSuiteViewCamera* camera_suite_view_camera;
+    CameraSuiteViewWiFiCamera* camera_suite_view_wifi_camera;
     CameraSuiteViewGuide* camera_suite_view_guide;
     uint32_t orientation;
     uint32_t dither;
@@ -44,6 +46,7 @@ typedef enum {
     CameraSuiteViewIdStartscreen,
     CameraSuiteViewIdMenu,
     CameraSuiteViewIdCamera,
+    CameraSuiteViewIdWiFiCamera,
     CameraSuiteViewIdGuide,
     CameraSuiteViewIdAppSettings,
     CameraSuiteViewIdCamSettings,

+ 5 - 5
docs/README.md

@@ -10,9 +10,9 @@ Firmware is needed for the ESP32-CAM module, see here for more information: http
 
 Button mappings:
 
-**Up** = Contrast Up
+**Up** = Contrast Up.
 
-**Down** = Contrast Down
+**Down** = Contrast Down.
 
 **Left** = Toggle invert.
 
@@ -30,8 +30,8 @@ Settings:
 
 **Dithering Type** Change between the Cycle Floyd–Steinberg, Jarvis-Judice-Ninke, and Stucki dithering types.
 
-**Haptic FX** = Toggle haptic feedback on/off.
+**Haptic Effects** = Toggle haptic feedback on/off.
 
-**Sound FX** = Toggle sound effects on/off.
+**Sound Effects** = Toggle sound effects on/off.
 
-**LED FX** = Toggle LED effects on/off.
+**LED Effects** = Toggle LED effects on/off.

+ 7 - 0
helpers/camera_suite_custom_event.h

@@ -15,6 +15,13 @@ typedef enum {
     CameraSuiteCustomEventSceneCameraRight,
     CameraSuiteCustomEventSceneCameraOk,
     CameraSuiteCustomEventSceneCameraBack,
+    // Scene events: WiFi Camera
+    CameraSuiteCustomEventSceneWiFiCameraUp,
+    CameraSuiteCustomEventSceneWiFiCameraDown,
+    CameraSuiteCustomEventSceneWiFiCameraLeft,
+    CameraSuiteCustomEventSceneWiFiCameraRight,
+    CameraSuiteCustomEventSceneWiFiCameraOk,
+    CameraSuiteCustomEventSceneWiFiCameraBack,
     // Scene events: Guide
     CameraSuiteCustomEventSceneGuideUp,
     CameraSuiteCustomEventSceneGuideDown,

+ 1 - 0
scenes/camera_suite_scene_config.h

@@ -1,6 +1,7 @@
 ADD_SCENE(camera_suite, start, Start)
 ADD_SCENE(camera_suite, menu, Menu)
 ADD_SCENE(camera_suite, camera, Camera)
+ADD_SCENE(camera_suite, wifi_camera, WiFiCamera)
 ADD_SCENE(camera_suite, guide, Guide)
 ADD_SCENE(camera_suite, app_settings, AppSettings)
 ADD_SCENE(camera_suite, cam_settings, CamSettings)

+ 27 - 9
scenes/camera_suite_scene_menu.c

@@ -3,12 +3,14 @@
 enum SubmenuIndex {
     /** Camera. */
     SubmenuIndexSceneCamera = 10,
-    /** Guide/how-to. */
-    SubmenuIndexGuide,
+    /** WiFi Camera */
+    SubmenuIndexSceneWiFiCamera,
     /** Cam settings menu. */
     SubmenuIndexCamSettings,
     /** App settings menu. */
     SubmenuIndexAppSettings,
+    /** Guide/how-to. */
+    SubmenuIndexGuide,
 };
 
 void camera_suite_scene_menu_submenu_callback(void* context, uint32_t index) {
@@ -21,28 +23,39 @@ void camera_suite_scene_menu_on_enter(void* context) {
 
     submenu_add_item(
         app->submenu,
-        "Open Camera",
+        "Stream Camera to Screen",
         SubmenuIndexSceneCamera,
         camera_suite_scene_menu_submenu_callback,
         app);
 
     submenu_add_item(
-        app->submenu, "Guide", SubmenuIndexGuide, camera_suite_scene_menu_submenu_callback, app);
+        app->submenu,
+        "Stream Camera to WiFi",
+        SubmenuIndexSceneWiFiCamera,
+        camera_suite_scene_menu_submenu_callback,
+        app);
 
     submenu_add_item(
         app->submenu,
-        "Cam Settings",
+        "Camera Settings",
         SubmenuIndexCamSettings,
         camera_suite_scene_menu_submenu_callback,
         app);
 
     submenu_add_item(
         app->submenu,
-        "App Settings",
+        "Application Settings",
         SubmenuIndexAppSettings,
         camera_suite_scene_menu_submenu_callback,
         app);
 
+    submenu_add_item(
+        app->submenu,
+        "Camera Suite Guide",
+        SubmenuIndexGuide,
+        camera_suite_scene_menu_submenu_callback,
+        app);
+
     submenu_set_selected_item(
         app->submenu, scene_manager_get_scene_state(app->scene_manager, CameraSuiteSceneMenu));
 
@@ -63,10 +76,10 @@ bool camera_suite_scene_menu_on_event(void* context, SceneManagerEvent event) {
                 app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSceneCamera);
             scene_manager_next_scene(app->scene_manager, CameraSuiteSceneCamera);
             return true;
-        } else if(event.event == SubmenuIndexGuide) {
+        } else if(event.event == SubmenuIndexSceneWiFiCamera) {
             scene_manager_set_scene_state(
-                app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexGuide);
-            scene_manager_next_scene(app->scene_manager, CameraSuiteSceneGuide);
+                app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexSceneWiFiCamera);
+            scene_manager_next_scene(app->scene_manager, CameraSuiteSceneWiFiCamera);
             return true;
         } else if(event.event == SubmenuIndexAppSettings) {
             scene_manager_set_scene_state(
@@ -78,6 +91,11 @@ bool camera_suite_scene_menu_on_event(void* context, SceneManagerEvent event) {
                 app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexCamSettings);
             scene_manager_next_scene(app->scene_manager, CameraSuiteSceneCamSettings);
             return true;
+        } else if(event.event == SubmenuIndexGuide) {
+            scene_manager_set_scene_state(
+                app->scene_manager, CameraSuiteSceneMenu, SubmenuIndexGuide);
+            scene_manager_next_scene(app->scene_manager, CameraSuiteSceneGuide);
+            return true;
         }
     }
     return false;

+ 51 - 0
scenes/camera_suite_scene_wifi_camera.c

@@ -0,0 +1,51 @@
+#include "../camera_suite.h"
+#include "../helpers/camera_suite_custom_event.h"
+#include "../views/camera_suite_view_wifi_camera.h"
+
+void camera_suite_view_wifi_camera_callback(CameraSuiteCustomEvent event, void* context) {
+    furi_assert(context);
+    CameraSuite* app = context;
+    view_dispatcher_send_custom_event(app->view_dispatcher, event);
+}
+
+void camera_suite_scene_wifi_camera_on_enter(void* context) {
+    furi_assert(context);
+    CameraSuite* app = context;
+    camera_suite_view_wifi_camera_set_callback(
+        app->camera_suite_view_wifi_camera, camera_suite_view_wifi_camera_callback, app);
+    view_dispatcher_switch_to_view(app->view_dispatcher, CameraSuiteViewIdWiFiCamera);
+}
+
+bool camera_suite_scene_wifi_camera_on_event(void* context, SceneManagerEvent event) {
+    CameraSuite* app = context;
+    bool consumed = false;
+
+    if(event.type == SceneManagerEventTypeCustom) {
+        switch(event.event) {
+        case CameraSuiteCustomEventSceneWiFiCameraLeft:
+        case CameraSuiteCustomEventSceneWiFiCameraRight:
+        case CameraSuiteCustomEventSceneWiFiCameraUp:
+        case CameraSuiteCustomEventSceneWiFiCameraDown:
+            // Do nothing.
+            break;
+        case CameraSuiteCustomEventSceneWiFiCameraBack:
+            notification_message(app->notification, &sequence_reset_red);
+            notification_message(app->notification, &sequence_reset_green);
+            notification_message(app->notification, &sequence_reset_blue);
+            if(!scene_manager_search_and_switch_to_previous_scene(
+                   app->scene_manager, CameraSuiteSceneMenu)) {
+                scene_manager_stop(app->scene_manager);
+                view_dispatcher_stop(app->view_dispatcher);
+            }
+            consumed = true;
+            break;
+        }
+    }
+
+    return consumed;
+}
+
+void camera_suite_scene_wifi_camera_on_exit(void* context) {
+    CameraSuite* app = context;
+    UNUSED(app);
+}

+ 1 - 1
views/camera_suite_view_camera.c

@@ -598,7 +598,7 @@ CameraSuiteViewCamera* camera_suite_view_camera_alloc() {
     instance->worker_thread = thread;
     furi_thread_start(instance->worker_thread);
 
-    // Enable uart listener
+    // Disable console.
     furi_hal_console_disable();
 
     // 115200 is the default baud rate for the ESP32-CAM.

+ 1 - 1
views/camera_suite_view_guide.c

@@ -30,7 +30,7 @@ void camera_suite_view_guide_draw(Canvas* canvas, CameraSuiteViewGuideModel* mod
     canvas_clear(canvas);
     canvas_set_color(canvas, ColorBlack);
     canvas_set_font(canvas, FontPrimary);
-    canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Guide");
+    canvas_draw_str_aligned(canvas, 0, 0, AlignLeft, AlignTop, "Camera Suite Guide");
     canvas_set_font(canvas, FontSecondary);
     canvas_draw_str_aligned(canvas, 0, 12, AlignLeft, AlignTop, "Left = Toggle invert");
     canvas_draw_str_aligned(canvas, 0, 22, AlignLeft, AlignTop, "Right = Toggle dithering");

+ 232 - 0
views/camera_suite_view_wifi_camera.c

@@ -0,0 +1,232 @@
+#include "../camera_suite.h"
+#include <furi.h>
+#include <furi_hal.h>
+#include <input/input.h>
+#include <gui/elements.h>
+#include <dolphin/dolphin.h>
+#include "../helpers/camera_suite_haptic.h"
+#include "../helpers/camera_suite_speaker.h"
+#include "../helpers/camera_suite_led.h"
+
+static void camera_suite_view_wifi_camera_draw(Canvas* canvas, void* model) {
+    furi_assert(canvas);
+    furi_assert(model);
+
+    // Clear the screen.
+    canvas_set_color(canvas, ColorBlack);
+
+    // Draw a frame.
+    canvas_draw_frame(canvas, 0, 0, FRAME_WIDTH, FRAME_HEIGHT);
+}
+
+static bool camera_suite_view_wifi_camera_input(InputEvent* event, void* context) {
+    furi_assert(context);
+    furi_assert(event);
+
+    CameraSuiteViewWiFiCamera* instance = context;
+
+    if(event->type == InputTypeRelease) {
+        switch(event->key) {
+        default: // Stop all sounds, reset the LED.
+            with_view_model(
+                instance->view,
+                CameraSuiteViewWiFiCameraModel * model,
+                {
+                    UNUSED(model);
+                    camera_suite_play_bad_bump(instance->context);
+                    camera_suite_stop_all_sound(instance->context);
+                    camera_suite_led_set_rgb(instance->context, 0, 0, 0);
+                },
+                true);
+            break;
+        }
+    } else if(event->type == InputTypePress) {
+        switch(event->key) {
+        case InputKeyBack: {
+            with_view_model(
+                instance->view,
+                CameraSuiteViewWiFiCameraModel * model,
+                {
+                    UNUSED(model);
+
+                    // Stop camera stream.
+                    furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t[]){'w'}, 1);
+                    furi_delay_ms(50);
+
+                    // Go back to the main menu.
+                    instance->callback(CameraSuiteCustomEventSceneCameraBack, instance->context);
+                },
+                true);
+            break;
+        }
+        case InputKeyLeft: {
+            with_view_model(
+                instance->view,
+                CameraSuiteViewWiFiCameraModel * model,
+                {
+                    UNUSED(model);
+
+                    // Play sound.
+                    camera_suite_play_happy_bump(instance->context);
+                    camera_suite_play_input_sound(instance->context);
+                    camera_suite_led_set_rgb(instance->context, 0, 0, 255);
+
+                    instance->callback(CameraSuiteCustomEventSceneCameraLeft, instance->context);
+                },
+                true);
+            break;
+        }
+        case InputKeyRight: {
+            with_view_model(
+                instance->view,
+                CameraSuiteViewWiFiCameraModel * model,
+                {
+                    UNUSED(model);
+
+                    // Play sound.
+                    camera_suite_play_happy_bump(instance->context);
+                    camera_suite_play_input_sound(instance->context);
+                    camera_suite_led_set_rgb(instance->context, 0, 0, 255);
+
+                    instance->callback(CameraSuiteCustomEventSceneCameraRight, instance->context);
+                },
+                true);
+            break;
+        }
+        case InputKeyUp: {
+            with_view_model(
+                instance->view,
+                CameraSuiteViewWiFiCameraModel * model,
+                {
+                    UNUSED(model);
+
+                    // Play sound.
+                    camera_suite_play_happy_bump(instance->context);
+                    camera_suite_play_input_sound(instance->context);
+                    camera_suite_led_set_rgb(instance->context, 0, 0, 255);
+
+                    instance->callback(CameraSuiteCustomEventSceneCameraUp, instance->context);
+                },
+                true);
+            break;
+        }
+        case InputKeyDown: {
+            with_view_model(
+                instance->view,
+                CameraSuiteViewWiFiCameraModel * model,
+                {
+                    UNUSED(model);
+
+                    // Play sound.
+                    camera_suite_play_happy_bump(instance->context);
+                    camera_suite_play_input_sound(instance->context);
+                    camera_suite_led_set_rgb(instance->context, 0, 0, 255);
+
+                    instance->callback(CameraSuiteCustomEventSceneCameraDown, instance->context);
+                },
+                true);
+            break;
+        }
+        case InputKeyOk: {
+            with_view_model(
+                instance->view,
+                CameraSuiteViewWiFiCameraModel * model,
+                {
+                    UNUSED(model);
+
+                    // Play sound.
+                    camera_suite_play_long_bump(instance->context);
+                    camera_suite_play_input_sound(instance->context);
+                    camera_suite_led_set_rgb(instance->context, 0, 0, 255);
+
+                    instance->callback(CameraSuiteCustomEventSceneCameraOk, instance->context);
+                },
+                true);
+            break;
+        }
+        case InputKeyMAX:
+        default: {
+            break;
+        }
+        }
+    }
+
+    return false;
+}
+
+static void camera_suite_view_wifi_camera_exit(void* context) {
+    furi_assert(context);
+}
+
+static void camera_suite_view_wifi_camera_enter(void* context) {
+    UNUSED(context);
+
+    // Start wifi camera stream.
+    furi_hal_uart_tx(FuriHalUartIdUSART1, (uint8_t[]){'W'}, 1);
+}
+
+CameraSuiteViewWiFiCamera* camera_suite_view_wifi_camera_alloc() {
+    // Allocate memory for the instance
+    CameraSuiteViewWiFiCamera* instance = malloc(sizeof(CameraSuiteViewWiFiCamera));
+
+    // Allocate the view object
+    instance->view = view_alloc();
+
+    // Allocate model
+    view_allocate_model(
+        instance->view, ViewModelTypeLocking, sizeof(CameraSuiteViewWiFiCameraModel));
+
+    // Set context for the view
+    view_set_context(instance->view, instance);
+
+    // Set draw callback
+    view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_wifi_camera_draw);
+
+    // Set input callback
+    view_set_input_callback(instance->view, camera_suite_view_wifi_camera_input);
+
+    // Set enter callback
+    view_set_enter_callback(instance->view, camera_suite_view_wifi_camera_enter);
+
+    // Set exit callback
+    view_set_exit_callback(instance->view, camera_suite_view_wifi_camera_exit);
+
+    // Disable console.
+    furi_hal_console_disable();
+
+    // 115200 is the default baud rate for the ESP32-CAM.
+    furi_hal_uart_set_br(FuriHalUartIdUSART1, 230400);
+
+    return instance;
+}
+
+void camera_suite_view_wifi_camera_free(CameraSuiteViewWiFiCamera* instance) {
+    furi_assert(instance);
+
+    // Remove the IRQ callback.
+    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL);
+
+    // Re-enable the console.
+    furi_hal_console_enable();
+
+    with_view_model(
+        instance->view, CameraSuiteViewWiFiCameraModel * model, { UNUSED(model); }, true);
+
+    view_free(instance->view);
+    free(instance);
+}
+
+View* camera_suite_view_wifi_camera_get_view(CameraSuiteViewWiFiCamera* instance) {
+    furi_assert(instance);
+    return instance->view;
+}
+
+void camera_suite_view_wifi_camera_set_callback(
+    CameraSuiteViewWiFiCamera* instance,
+    CameraSuiteViewWiFiCameraCallback callback,
+    void* context) {
+    furi_assert(instance);
+    furi_assert(callback);
+    instance->callback = callback;
+    instance->context = context;
+}

+ 41 - 0
views/camera_suite_view_wifi_camera.h

@@ -0,0 +1,41 @@
+#pragma once
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <furi_hal_console.h>
+#include <furi_hal_uart.h>
+#include <gui/elements.h>
+#include <gui/gui.h>
+#include <gui/icon_i.h>
+#include <gui/modules/dialog_ex.h>
+#include <gui/view.h>
+#include <gui/view_dispatcher.h>
+#include <notification/notification.h>
+#include <notification/notification_messages.h>
+#include <storage/filesystem_api_defines.h>
+#include <storage/storage.h>
+
+#include "../helpers/camera_suite_custom_event.h"
+
+typedef struct CameraSuiteViewWiFiCamera {
+    CameraSuiteViewCameraCallback callback;
+    View* view;
+    void* context;
+} CameraSuiteViewWiFiCamera;
+
+typedef struct {
+    int some_value;
+} CameraSuiteViewWiFiCameraModel;
+
+typedef void (*CameraSuiteViewWiFiCameraCallback)(CameraSuiteCustomEvent event, void* context);
+
+CameraSuiteViewWiFiCamera* camera_suite_view_wifi_camera_alloc();
+
+View* camera_suite_view_wifi_camera_get_view(CameraSuiteViewWiFiCamera* camera_suite_static);
+
+void camera_suite_view_wifi_camera_free(CameraSuiteViewWiFiCamera* camera_suite_static);
+
+void camera_suite_view_wifi_camera_set_callback(
+    CameraSuiteViewWiFiCamera* camera_suite_view_wifi_camera,
+    CameraSuiteViewWiFiCameraCallback callback,
+    void* context);