Cody Tolene 2 лет назад
Родитель
Сommit
42a1ab9e22

+ 2 - 2
arduino-cli.yaml

@@ -1,6 +1,6 @@
 board_manager:
   additional_urls:
-    - https://dl.espressif.com/dl/package_esp32_index.json
+  - https://dl.espressif.com/dl/package_esp32_index.json
 build_cache:
   compilations_before_purge: 10
   ttl: 720h0m0s
@@ -8,7 +8,7 @@ daemon:
   port: "50051"
 directories:
   data: C:\temp\arduino-cli\data
-  downloads: C:\temp\arduino-cli\downloads
+  downloads: C:\temp\arduino-cli\staging
   user: C:\temp\arduino-cli\user
 library:
   enable_unsafe_install: false

+ 74 - 57
fap/views/camera_suite_view_camera.c

@@ -8,18 +8,6 @@
 #include "../helpers/camera_suite_speaker.h"
 #include "../helpers/camera_suite_led.h"
 
-static CameraSuiteViewCamera* current_instance = NULL;
-
-bool is_inverted = false;
-
-struct CameraSuiteViewCamera {
-    CameraSuiteViewCameraCallback callback;
-    FuriStreamBuffer* rx_stream;
-    FuriThread* worker_thread;
-    View* view;
-    void* context;
-};
-
 void camera_suite_view_camera_set_callback(
     CameraSuiteViewCamera* instance,
     CameraSuiteViewCameraCallback callback,
@@ -33,20 +21,24 @@ void camera_suite_view_camera_set_callback(
 // Function to draw pixels on the canvas based on camera orientation
 static void draw_pixel_by_orientation(Canvas* canvas, uint8_t x, uint8_t y, uint8_t orientation) {
     switch(orientation) {
-    case 0: // Camera rotated 0 degrees (right side up, default)
+    default:
+    case 0: { // Camera rotated 0 degrees (right side up, default)
         canvas_draw_dot(canvas, x, y);
         break;
-    case 1: // Camera rotated 90 degrees
+    }
+    case 1: { // Camera rotated 90 degrees
+
         canvas_draw_dot(canvas, y, FRAME_WIDTH - 1 - x);
         break;
-    case 2: // Camera rotated 180 degrees (upside down)
+    }
+    case 2: { // Camera rotated 180 degrees (upside down)
         canvas_draw_dot(canvas, FRAME_WIDTH - 1 - x, FRAME_HEIGHT - 1 - y);
         break;
-    case 3: // Camera rotated 270 degrees
+    }
+    case 3: { // Camera rotated 270 degrees
         canvas_draw_dot(canvas, FRAME_HEIGHT - 1 - y, x);
         break;
-    default:
-        break;
+    }
     }
 }
 
@@ -59,15 +51,13 @@ static void camera_suite_view_camera_draw(Canvas* canvas, void* _model) {
     // Draw the frame.
     canvas_draw_frame(canvas, 0, 0, FRAME_WIDTH, FRAME_HEIGHT);
 
-    CameraSuite* app = current_instance->context;
-
     for(size_t p = 0; p < FRAME_BUFFER_LENGTH; ++p) {
         uint8_t x = p % ROW_BUFFER_LENGTH; // 0 .. 15
         uint8_t y = p / ROW_BUFFER_LENGTH; // 0 .. 63
 
         for(uint8_t i = 0; i < 8; ++i) {
             if((model->pixels[p] & (1 << (7 - i))) != 0) {
-                draw_pixel_by_orientation(canvas, (x * 8) + i, y, app->orientation);
+                draw_pixel_by_orientation(canvas, (x * 8) + i, y, model->orientation);
             }
         }
     }
@@ -127,6 +117,14 @@ static void save_image(void* _model) {
     // Free the file name after use.
     furi_string_free(file_name);
 
+    // @todo - Add functionaly for saving images inverted if necessary.
+    // Invert pixel values if necessary.
+    // if(!model->inverted) { }
+
+    for(size_t i = 0; i < FRAME_BUFFER_LENGTH; ++i) {
+        model->pixels[i] = ~model->pixels[i];
+    }
+
     // If the file was opened successfully, write the bitmap header and the
     // image data.
     if(result) {
@@ -138,20 +136,13 @@ static void save_image(void* _model) {
 
         // Write locally to the Flipper Zero SD card in the DCIM folder.
         int8_t row_buffer[ROW_BUFFER_LENGTH];
-        if(is_inverted) {
-            for(size_t i = 0; i < 64; ++i) {
-                for(size_t j = 0; j < ROW_BUFFER_LENGTH; ++j) {
-                    row_buffer[j] = model->pixels[i * ROW_BUFFER_LENGTH + j];
-                }
-                storage_file_write(file, row_buffer, ROW_BUFFER_LENGTH);
-            }
-        } else {
-            for(size_t i = 64; i > 0; --i) {
-                for(size_t j = 0; j < ROW_BUFFER_LENGTH; ++j) {
-                    row_buffer[j] = model->pixels[((i - 1) * ROW_BUFFER_LENGTH) + j];
-                }
-                storage_file_write(file, row_buffer, ROW_BUFFER_LENGTH);
+
+        // @todo - Save image based on orientation.
+        for(size_t i = 64; i > 0; --i) {
+            for(size_t j = 0; j < ROW_BUFFER_LENGTH; ++j) {
+                row_buffer[j] = model->pixels[((i - 1) * ROW_BUFFER_LENGTH) + j];
             }
+            storage_file_write(file, row_buffer, ROW_BUFFER_LENGTH);
         }
     }
 
@@ -170,7 +161,9 @@ static void camera_suite_view_camera_model_init(UartDumpModel* const model) {
 
 static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
     furi_assert(context);
+
     CameraSuiteViewCamera* instance = context;
+
     if(event->type == InputTypeRelease) {
         switch(event->key) {
         default: // Stop all sounds, reset the LED.
@@ -189,8 +182,8 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
     } else if(event->type == InputTypePress) {
         uint8_t data[1];
         switch(event->key) {
-        case InputKeyBack:
-            // Stop the camera stream.
+        // Camera: Stop stream.
+        case InputKeyBack: {
             data[0] = 's';
             // Go back to the main menu.
             with_view_model(
@@ -202,25 +195,25 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
                 },
                 true);
             break;
-        case InputKeyLeft:
-            // Camera: Toggle invert on the ESP32-CAM.
+        }
+        // Camera: Toggle invert on the ESP32-CAM.
+        case InputKeyLeft: {
             data[0] = '<';
-            // Toggle invert state locally.
-            is_inverted = !is_inverted;
             with_view_model(
                 instance->view,
                 UartDumpModel * model,
                 {
-                    UNUSED(model);
                     camera_suite_play_happy_bump(instance->context);
                     camera_suite_play_input_sound(instance->context);
                     camera_suite_led_set_rgb(instance->context, 0, 0, 255);
+                    model->inverted = !model->inverted;
                     instance->callback(CameraSuiteCustomEventSceneCameraLeft, instance->context);
                 },
                 true);
             break;
-        case InputKeyRight:
-            // Camera: Enable/disable dithering.
+        }
+        // Camera: Enable/disable dithering.
+        case InputKeyRight: {
             data[0] = '>';
             with_view_model(
                 instance->view,
@@ -234,8 +227,9 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
                 },
                 true);
             break;
-        case InputKeyUp:
-            // Camera: Increase contrast.
+        }
+        // Camera: Increase contrast.
+        case InputKeyUp: {
             data[0] = 'C';
             with_view_model(
                 instance->view,
@@ -249,8 +243,9 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
                 },
                 true);
             break;
-        case InputKeyDown:
-            // Camera: Reduce contrast.
+        }
+        // Camera: Reduce contrast.
+        case InputKeyDown: {
             data[0] = 'c';
             with_view_model(
                 instance->view,
@@ -264,26 +259,35 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
                 },
                 true);
             break;
+        }
+        // Camera: Take picture.
         case InputKeyOk: {
-            // Take picture.
+            // Set up data to trigger picture sequence on the ESP32-CAM.
+            data[0] = 'P';
+            // Send `data` to the ESP32-CAM.
+            furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
             with_view_model(
                 instance->view,
                 UartDumpModel * model,
                 {
-                    // If flash is enabled, flash the onboard ESP32-CAM LED.
-                    camera_suite_play_happy_bump(instance->context);
+                    camera_suite_play_long_bump(instance->context);
                     camera_suite_play_input_sound(instance->context);
                     camera_suite_led_set_rgb(instance->context, 0, 0, 255);
+                    // Take a picture if the data is 'P'.
                     save_image(model);
                     instance->callback(CameraSuiteCustomEventSceneCameraOk, instance->context);
                 },
                 true);
             return true;
         }
+        // Camera: Do nothing.
         case InputKeyMAX:
+        default: {
             break;
         }
-        // Send `data` to the ESP32-CAM
+        }
+
+        // Send `data` to the ESP32-CAM.
         furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
     }
     return true;
@@ -300,9 +304,6 @@ static void camera_suite_view_camera_enter(void* context) {
     // Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`.
     CameraSuiteViewCamera* instance = (CameraSuiteViewCamera*)context;
 
-    // Assign the current instance to the global variable
-    current_instance = instance;
-
     uint8_t data[1];
     data[0] = 'S'; // Uppercase `S` to start the camera
 
@@ -344,11 +345,15 @@ static void camera_suite_view_camera_enter(void* context) {
 
     // Send `data` as the flash bool to the ESP32-CAM
     furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
-
+    uint32_t orientation = instanceContext->orientation;
     with_view_model(
         instance->view,
         UartDumpModel * model,
-        { camera_suite_view_camera_model_init(model); },
+        {
+            model->inverted = false;
+            model->orientation = orientation;
+            camera_suite_view_camera_model_init(model);
+        },
         true);
 }
 
@@ -465,7 +470,7 @@ CameraSuiteViewCamera* camera_suite_view_camera_alloc() {
     // Allocate model
     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(UartDumpModel));
 
-    // Set context
+    // Set context for the view
     view_set_context(instance->view, instance);
 
     // Set draw callback
@@ -503,6 +508,18 @@ CameraSuiteViewCamera* camera_suite_view_camera_alloc() {
 void camera_suite_view_camera_free(CameraSuiteViewCamera* instance) {
     furi_assert(instance);
 
+    // Remove the IRQ callback.
+    furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL);
+
+    // Free the worker thread.
+    furi_thread_free(instance->worker_thread);
+
+    // Free the allocated stream buffer.
+    furi_stream_buffer_free(instance->rx_stream);
+
+    // Re-enable the console.
+    furi_hal_console_enable();
+
     with_view_model(
         instance->view, UartDumpModel * model, { UNUSED(model); }, true);
     view_free(instance->view);

+ 14 - 6
fap/views/camera_suite_view_camera.h

@@ -41,20 +41,28 @@ typedef enum {
 
 #define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx)
 
+// Forward declaration
+typedef void (*CameraSuiteViewCameraCallback)(CameraSuiteCustomEvent event, void* context);
+
+typedef struct CameraSuiteViewCamera {
+    CameraSuiteViewCameraCallback callback;
+    FuriStreamBuffer* rx_stream;
+    FuriThread* worker_thread;
+    View* view;
+    void* context;
+} CameraSuiteViewCamera;
+
 typedef struct UartDumpModel {
     bool initialized;
+    bool inverted;
     int rotation_angle;
+    uint32_t orientation;
     uint8_t pixels[FRAME_BUFFER_LENGTH];
     uint8_t ringbuffer_index;
-    uint8_t row_ringbuffer[RING_BUFFER_LENGTH];
     uint8_t row_identifier;
+    uint8_t row_ringbuffer[RING_BUFFER_LENGTH];
 } UartDumpModel;
 
-typedef struct CameraSuiteViewCamera CameraSuiteViewCamera;
-
-// Callback
-typedef void (*CameraSuiteViewCameraCallback)(CameraSuiteCustomEvent event, void* context);
-
 // Function Prototypes
 CameraSuiteViewCamera* camera_suite_view_camera_alloc();
 void camera_suite_view_camera_free(CameraSuiteViewCamera* camera_suite_static);

+ 19 - 21
firmware/firmware.ino

@@ -100,25 +100,31 @@ void handleSerialInput()
     case '<': // Toggle invert.
       invert = !invert;
       break;
+    case 'b': // Remove brightness.
+      cameraSensor->set_contrast(
+          cameraSensor,
+          cameraSensor->status.brightness - 1);
+      break;
     case 'B': // Add brightness.
       cameraSensor->set_contrast(
           cameraSensor,
           cameraSensor->status.brightness + 1);
       break;
-    case 'b': // Remove brightness.
+    case 'c': // Remove contrast.
       cameraSensor->set_contrast(
           cameraSensor,
-          cameraSensor->status.brightness - 1);
+          cameraSensor->status.contrast - 1);
       break;
     case 'C': // Add contrast.
       cameraSensor->set_contrast(
           cameraSensor,
           cameraSensor->status.contrast + 1);
       break;
-    case 'c': // Remove contrast.
-      cameraSensor->set_contrast(
-          cameraSensor,
-          cameraSensor->status.contrast - 1);
+    case 'f': // Toggle flash off.
+      isFlashEnabled = false;
+      break;
+    case 'F': // Toggle flash on.
+      isFlashEnabled = true;
       break;
     case 'j': // Toggle store jpeg to sd card off.
       storeJpeg = false;
@@ -127,37 +133,29 @@ void handleSerialInput()
       storeJpeg = true;
       break;
     case 'P': // Picture sequence.
-      // Stop the IO stream before taking a picture.
-      stopStream = true;
       if (isFlashEnabled)
       {
         // Turn on torch.
         pinMode(FLASH_GPIO_NUM, OUTPUT);
         digitalWrite(FLASH_GPIO_NUM, HIGH);
+        // Give some time (500ms) for Flipper to save locally with flash on.
+        delay(500);
+        // Turn off torch.
+        digitalWrite(FLASH_GPIO_NUM, LOW);
       }
 
       // @todo - Future feature.
       // if (storeJpeg) { saveJpegPictureToSDCard(); }
-
-      // Give some time (100ms) for Flipper to save locally with flash on.
-      delay(100);
-
-      // Turn off torch.
-      pinMode(FLASH_GPIO_NUM, OUTPUT);
-      digitalWrite(FLASH_GPIO_NUM, LOW);
-
-      // Restart the stream after the picture is taken.
-      stopStream = false;
       break;
     case 'M': // Toggle Mirror.
       cameraSensor->set_hmirror(cameraSensor, !cameraSensor->status.hmirror);
       break;
-    case 'S': // Start stream.
-      stopStream = false;
-      break;
     case 's': // Stop stream.
       stopStream = true;
       break;
+    case 'S': // Start stream.
+      stopStream = false;
+      break;
     case '0': // Use Floyd Steinberg dithering.
       ditherAlgorithm = FLOYD_STEINBERG;
       break;

+ 5 - 5
firware-flash.bat

@@ -93,10 +93,10 @@ echo Your ESP32-CAM is ready to be flashed. Please follow the instructions below
 :uploadFirmware
 echo.
 echo 1. Ensure IO0 pin on ESP32-CAM is grounded to the proper GND pin.
-echo 2. Hold reset, and plug in your ESP32-CAM; release reset a few seconds later.
-echo 3. As you proceed, release the reset button simultaneously with the next step.
+echo 2. Hold reset, and plug in your ESP32-CAM; hold for a few seconds and release.
+echo 3. Try to time your release simultaneously with continuing to the next step.
 echo 4. ESP32-CAM should now be in flash mode; allow some time for firmware upload.
-echo 5. Failure is common; persist and verify all connections if errors persist.
+echo 5. Failure is common; verify all connections if errors persist and try again.
 echo 6. Disconnecting and reconnecting USB between attempts may sometimes work.
 echo.
 pause
@@ -115,7 +115,7 @@ if !ERRORLEVEL! EQU 0 (
         goto :uploadLoop
     ) else (
         echo.
-        set /p UPLOAD_TRY_AGAIN="Upload failed after 3 attempts, don't give up! Would you like to try again? (Y/N): "
+        set /p UPLOAD_TRY_AGAIN="Upload failed after 3 attempts, dont give up friend. Would you like to try again? (Y/N): "
         if /i "!UPLOAD_TRY_AGAIN!"=="Y" (
             set RETRY_COUNT=1
             goto :uploadFirmware
@@ -140,7 +140,7 @@ if /i "!DELETE_TEMP!"=="Y" (
     rmdir /s /q %CLI_TEMP%
 )
 echo.
-echo Fin. Happy programming friend.
+echo Fin. Happy programming.
 echo.
 pause
 exit /b