Просмотр исходного кода

Update the new firmware update, add camera model. Stabilize Flipper Zero software.

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

+ 54 - 58
fap/views/camera_suite_view_camera.c

@@ -20,6 +20,11 @@ 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) {
+    furi_assert(canvas);
+    furi_assert(x);
+    furi_assert(y);
+    furi_assert(orientation);
+
     switch(orientation) {
     default:
     case 0: { // Camera rotated 0 degrees (right side up, default)
@@ -42,8 +47,11 @@ static void draw_pixel_by_orientation(Canvas* canvas, uint8_t x, uint8_t y, uint
     }
 }
 
-static void camera_suite_view_camera_draw(Canvas* canvas, void* _model) {
-    UartDumpModel* model = _model;
+static void camera_suite_view_camera_draw(Canvas* canvas, void* model) {
+    furi_assert(canvas);
+    furi_assert(model);
+
+    UartDumpModel* uartDumpModel = model;
 
     // Clear the screen.
     canvas_set_color(canvas, ColorBlack);
@@ -56,14 +64,14 @@ static void camera_suite_view_camera_draw(Canvas* canvas, void* _model) {
         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, model->orientation);
+            if((uartDumpModel->pixels[p] & (1 << (7 - i))) != 0) {
+                draw_pixel_by_orientation(canvas, (x * 8) + i, y, uartDumpModel->orientation);
             }
         }
     }
 
     // Draw the guide if the camera is not initialized.
-    if(!model->initialized) {
+    if(!uartDumpModel->initialized) {
         canvas_draw_icon(canvas, 74, 16, &I_DolphinCommon_56x48);
         canvas_set_font(canvas, FontSecondary);
         canvas_draw_str(canvas, 8, 12, "Connect the ESP32-CAM");
@@ -74,8 +82,10 @@ static void camera_suite_view_camera_draw(Canvas* canvas, void* _model) {
     }
 }
 
-static void save_image(void* _model) {
-    UartDumpModel* model = _model;
+static void save_image(void* model) {
+    furi_assert(model);
+
+    UartDumpModel* uartDumpModel = model;
 
     // This pointer is used to access the storage.
     Storage* storage = furi_record_open(RECORD_STORAGE);
@@ -122,7 +132,7 @@ static void save_image(void* _model) {
     // if(!model->inverted) { }
 
     for(size_t i = 0; i < FRAME_BUFFER_LENGTH; ++i) {
-        model->pixels[i] = ~model->pixels[i];
+        uartDumpModel->pixels[i] = ~uartDumpModel->pixels[i];
     }
 
     // If the file was opened successfully, write the bitmap header and the
@@ -140,7 +150,7 @@ static void save_image(void* _model) {
         // @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];
+                row_buffer[j] = uartDumpModel->pixels[((i - 1) * ROW_BUFFER_LENGTH) + j];
             }
             storage_file_write(file, row_buffer, ROW_BUFFER_LENGTH);
         }
@@ -154,6 +164,8 @@ static void save_image(void* _model) {
 }
 
 static void camera_suite_view_camera_model_init(UartDumpModel* const model) {
+    furi_assert(model);
+
     for(size_t i = 0; i < FRAME_BUFFER_LENGTH; i++) {
         model->pixels[i] = 0;
     }
@@ -161,6 +173,7 @@ 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);
+    furi_assert(event);
 
     CameraSuiteViewCamera* instance = context;
 
@@ -184,7 +197,6 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
         switch(event->key) {
         // Camera: Stop stream.
         case InputKeyBack: {
-            data[0] = 's';
             // Go back to the main menu.
             with_view_model(
                 instance->view,
@@ -262,10 +274,10 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
         }
         // Camera: Take picture.
         case InputKeyOk: {
-            // 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);
+            // Save picture directly to ESP32-CAM.
+            // @todo - Add this functionality.
+            // data[0] = 'P';
+            // furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
             with_view_model(
                 instance->view,
                 UartDumpModel * model,
@@ -278,7 +290,6 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
                     instance->callback(CameraSuiteCustomEventSceneCameraOk, instance->context);
                 },
                 true);
-            return true;
         }
         // Camera: Do nothing.
         case InputKeyMAX:
@@ -294,58 +305,34 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
 }
 
 static void camera_suite_view_camera_exit(void* context) {
-    furi_assert(context);
+    UNUSED(context);
+
+    // Stop camera stream.
+    uint8_t stop_camera = 's';
+    furi_hal_uart_tx(FuriHalUartIdUSART1, &stop_camera, 1);
+    furi_delay_ms(50);
 }
 
 static void camera_suite_view_camera_enter(void* context) {
-    // Check `context` for null. If it is null, abort program, else continue.
     furi_assert(context);
 
-    // Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`.
+    // Get the camera suite instance context.
     CameraSuiteViewCamera* instance = (CameraSuiteViewCamera*)context;
 
-    uint8_t data[1];
-    data[0] = 'S'; // Uppercase `S` to start the camera
-
-    // Send `data` to the ESP32-CAM
-    furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
+    // Get the camera suite instance context.
+    CameraSuite* instance_context = instance->context;
 
-    // Delay for 50ms to make sure the camera is started before sending any other commands.
+    // Start camera stream.
+    uint8_t start_camera = 'S';
+    furi_hal_uart_tx(FuriHalUartIdUSART1, &start_camera, 1);
     furi_delay_ms(50);
 
-    // Initialize the camera with the selected dithering option from options.
-    CameraSuite* instanceContext = instance->context;
-    switch(instanceContext->dither) {
-    case 0: // Floyd Steinberg
-        data[0] = '0';
-        break;
-    case 1: // Stucki
-        data[0] = '1';
-        break;
-    case 2: // Jarvis Judice Ninke
-        data[0] = '2';
-        break;
-    }
-
-    // Send `data` as the dither type to the ESP32-CAM
-    furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
-
-    // Wait for 50ms to make sure dither is set before sending any other commands.
+    // Get/set dither type.
+    uint8_t dither_type = instance_context->dither;
+    furi_hal_uart_tx(FuriHalUartIdUSART1, &dither_type, 1);
     furi_delay_ms(50);
 
-    // Initialize the camera with the selected flash option from options.
-    switch(instanceContext->flash) {
-    case 0: // Flash OFF
-        data[0] = 'f';
-        break;
-    case 1: // Flash ON
-        data[0] = 'F';
-        break;
-    }
-
-    // Send `data` as the flash bool to the ESP32-CAM
-    furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
-    uint32_t orientation = instanceContext->orientation;
+    uint32_t orientation = instance_context->orientation;
     with_view_model(
         instance->view,
         UartDumpModel * model,
@@ -358,7 +345,8 @@ static void camera_suite_view_camera_enter(void* context) {
 }
 
 static void camera_on_irq_cb(UartIrqEvent uartIrqEvent, uint8_t data, void* context) {
-    // Check `context` for null. If it is null, abort program, else continue.
+    furi_assert(uartIrqEvent);
+    furi_assert(data);
     furi_assert(context);
 
     // Cast `context` to `CameraSuiteViewCamera*` and store it in `instance`.
@@ -372,7 +360,10 @@ static void camera_on_irq_cb(UartIrqEvent uartIrqEvent, uint8_t data, void* cont
     }
 }
 
-static void process_ringbuffer(UartDumpModel* model, uint8_t byte) {
+static void process_ringbuffer(UartDumpModel* model, uint8_t const byte) {
+    furi_assert(model);
+    furi_assert(byte);
+
     // The first HEADER_LENGTH bytes are reserved for header information.
     if(model->ringbuffer_index < HEADER_LENGTH) {
         // Validate the start of row characters 'Y' and ':'.
@@ -419,6 +410,7 @@ static void process_ringbuffer(UartDumpModel* model, uint8_t byte) {
 
 static int32_t camera_worker(void* context) {
     furi_assert(context);
+
     CameraSuiteViewCamera* instance = context;
 
     while(1) {
@@ -499,7 +491,11 @@ CameraSuiteViewCamera* camera_suite_view_camera_alloc() {
 
     // Enable uart listener
     furi_hal_console_disable();
+
+    // 115200 is the default baud rate for the ESP32-CAM.
     furi_hal_uart_set_br(FuriHalUartIdUSART1, 230400);
+
+    // Enable UART1 and set the IRQ callback.
     furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, camera_on_irq_cb, instance);
 
     return instance;
@@ -529,4 +525,4 @@ void camera_suite_view_camera_free(CameraSuiteViewCamera* instance) {
 View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* instance) {
     furi_assert(instance);
     return instance->view;
-}
+}

+ 15 - 5
firmware-flash.bat

@@ -1,6 +1,8 @@
 @echo off
 setlocal EnableDelayedExpansion
 
+rem λ
+
 set CLI_FOUND_FOLLOW_UP=0
 set CLI_TEMP=%TEMP%\arduino-cli
 set COMPILE_FLAG=firmware\compile.flag
@@ -10,9 +12,9 @@ set FIRMWARE_SRC=firmware\firmware.ino
 set SELECTED_BOARD=%DEFAULT_BOARD_FQBN%
 
 chcp 65001 > nul
-echo ┏┓   ┓    ┏┳┓  ┓      
+echo ┏┓   ┓    ┏┳┓  ┓
 echo ┃ ┏┓┏┫┓┏   ┃ ┏┓┃┏┓┏┓┏┓
-echo ┗┛┗┛┗┻┗┫   ┻ ┗┛┗┗ ┛┗┗ 
+echo ┗┛┗┛┗┻┗┫   ┻ ┗┛┗┗ ┛┗┗
 echo        ┛  https://github.com/CodyTolene
 echo.
 echo Flipper Zero - ESP32-CAM Firmware Flasher - Windows 10+
@@ -110,12 +112,12 @@ arduino-cli %CONFIG_FILE% upload -p %PORT_NUMBER% --fqbn !SELECTED_BOARD! %FIRMW
 if !ERRORLEVEL! EQU 0 (
     goto :uploadSuccess
 ) else (
-    if !RETRY_COUNT! lss 3 (
+    if !RETRY_COUNT! lss 5 (
         set /a RETRY_COUNT+=1
         goto :uploadLoop
     ) else (
         echo.
-        set /p UPLOAD_TRY_AGAIN="Upload failed after 3 attempts, dont give up friend. Would you like to try again? (Y/N): "
+        set /p UPLOAD_TRY_AGAIN="Upload failed after 5 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
@@ -123,6 +125,14 @@ if !ERRORLEVEL! EQU 0 (
             echo.
             echo If you're still having issues, feel free to open a ticket at the following link:
             echo https://github.com/CodyTolene/Flipper-Zero-Camera-Suite/issues
+            echo.
+            set /p DELETE_TEMP="Would you like to delete the temporary files? (Y/N): "
+            if /i "!DELETE_TEMP!"=="Y" (
+                rmdir /s /q %CLI_TEMP%
+            )
+            echo.
+            pause
+            exit /b
         )
     )
 )
@@ -140,7 +150,7 @@ if /i "!DELETE_TEMP!"=="Y" (
     rmdir /s /q %CLI_TEMP%
 )
 echo.
-echo Fin. Happy programming.
+echo Fin. Happy programming friend.
 echo.
 pause
 exit /b

+ 61 - 0
firmware-update/camera_model.h

@@ -0,0 +1,61 @@
+#ifndef CAMERA_MODEL_H
+#define CAMERA_MODEL_H
+
+#include <stdint.h>
+
+/**
+ * The dithering algorithms available.
+ */
+enum DitheringAlgorithm : uint8_t
+{
+    FLOYD_STEINBERG,
+    JARVIS_JUDICE_NINKE,
+    STUCKI
+};
+
+class CameraModel
+{
+private:
+    static CameraModel *instance;
+    // Private constructor to prevent instantiation.
+    CameraModel();
+    /**
+     * Flag to enable or disable dithering.
+     */
+    bool isDitheringDisabled;
+    /**
+     * Flag to represent the flash state when saving pictures to the Flipper.
+     */
+    bool isFlashEnabled;
+    /**
+     * Flag to invert pixel colors.
+     */
+    bool isInverted;
+    /**
+     * Flag to stop or start the stream.
+     */
+    bool isStreamEnabled;
+    /**
+     * Holds the currently selected dithering algorithm.
+     */
+    DitheringAlgorithm ditherAlgorithm;
+
+public:
+    static CameraModel *getInstance();
+
+    // Getter functions
+    bool getIsDitheringDisabled();
+    bool getIsFlashEnabled();
+    bool getIsInverted();
+    bool getIsStreamEnabled();
+    DitheringAlgorithm getDitherAlgorithm();
+
+    // Setter functions
+    void setIsDitheringDisabled(bool value);
+    void setIsFlashEnabled(bool value);
+    void setIsInverted(bool value);
+    void setIsStreamEnabled(bool value);
+    void setDitherAlgorithm(DitheringAlgorithm value);
+};
+
+#endif

+ 74 - 0
firmware-update/camera_model.ino

@@ -0,0 +1,74 @@
+#include "camera_model.h"
+
+CameraModel *CameraModel::instance = nullptr;
+
+CameraModel::CameraModel()
+{
+    // Set up defaults.
+    isDitheringDisabled = false;
+    isFlashEnabled = false;
+    isInverted = false;
+    isStreamEnabled = true;
+    ditherAlgorithm = FLOYD_STEINBERG;
+}
+
+CameraModel *CameraModel::getInstance()
+{
+    if (instance == nullptr)
+    {
+        instance = new CameraModel();
+    }
+    return instance;
+}
+
+// Getter implementations
+bool CameraModel::getIsDitheringDisabled()
+{
+    return isDitheringDisabled;
+}
+
+bool CameraModel::getIsFlashEnabled()
+{
+    return isFlashEnabled;
+}
+
+bool CameraModel::getIsInverted()
+{
+    return isInverted;
+}
+
+bool CameraModel::getIsStreamEnabled()
+{
+    return isStreamEnabled;
+}
+
+DitheringAlgorithm CameraModel::getDitherAlgorithm()
+{
+    return ditherAlgorithm;
+}
+
+// Setter implementations
+void CameraModel::setIsDitheringDisabled(bool value)
+{
+    isDitheringDisabled = value;
+}
+
+void CameraModel::setIsFlashEnabled(bool value)
+{
+    isFlashEnabled = value;
+}
+
+void CameraModel::setIsInverted(bool value)
+{
+    isInverted = value;
+}
+
+void CameraModel::setIsStreamEnabled(bool value)
+{
+    isStreamEnabled = value;
+}
+
+void CameraModel::setDitherAlgorithm(DitheringAlgorithm algorithm)
+{
+    ditherAlgorithm = algorithm;
+}

+ 2 - 2
firmware-update/dithering.h

@@ -3,8 +3,8 @@
 
 #include <esp_camera.h>
 
-#include "globals.h"
+#include "camera_model.h"
 
-void dither_image(camera_fb_t *frame_buffer, CameraModel *model);
+void dither_image(camera_fb_t *frame_buffer);
 
 #endif

+ 5 - 2
firmware-update/dithering.ino

@@ -1,7 +1,10 @@
 #include "dithering.h"
 
-void dither_image(camera_fb_t *frame_buffer, CameraModel *model)
+void dither_image(camera_fb_t *frame_buffer)
 {
+    // Get the camera model reference.
+    CameraModel *model = CameraModel::getInstance();
+
     for (uint8_t y = 0; y < frame_buffer->height; ++y)
     {
         for (uint8_t x = 0; x < frame_buffer->width; ++x)
@@ -13,7 +16,7 @@ void dither_image(camera_fb_t *frame_buffer, CameraModel *model)
             int8_t quant_error = oldpixel - newpixel;
 
             // Apply error diffusion based on the selected algorithm
-            switch (model->ditherAlgorithm)
+            switch (model->getDitherAlgorithm())
             {
             case JARVIS_JUDICE_NINKE:
                 frame_buffer->buf[(y * frame_buffer->width) + x + 1] += quant_error * 7 / 48;

+ 0 - 10
firmware-update/firmware.h

@@ -1,10 +0,0 @@
-#ifndef FIRMWARE_H
-#define FIRMWARE_H
-
-#include <esp_camera.h>
-
-#include "initialize.h"
-#include "process_image.h"
-#include "serial_commands.h"
-
-#endif

+ 51 - 7
firmware-update/firmware.ino

@@ -1,27 +1,71 @@
-#include "firmware.h"
+#include <esp_camera.h>
 
-CameraModel model;
+#include "camera_model.h"
+#include "initialize.h"
+#include "process_image.h"
+#include "serial_commands.h"
+
+camera_config_t config;
 
 // Entry point of the program.
 void setup()
 {
-    Serial.begin(115200); // Previously 230400, 115200 seems more stable.
-    initialize(&model);
+    // Set up the camera model.
+    CameraModel *model = CameraModel::getInstance();
+
+    // Set up the model defaults.
+    model->setIsDitheringDisabled(false);
+    model->setIsInverted(false);
+    model->setIsFlashEnabled(false);
+    model->setIsStreamEnabled(true);
+    model->setDitherAlgorithm(FLOYD_STEINBERG);
+
+    // Set initial camera configurations for grayscale.
+    config.ledc_channel = LEDC_CHANNEL_0;
+    config.ledc_timer = LEDC_TIMER_0;
+    config.pin_d0 = Y2_GPIO_NUM;
+    config.pin_d1 = Y3_GPIO_NUM;
+    config.pin_d2 = Y4_GPIO_NUM;
+    config.pin_d3 = Y5_GPIO_NUM;
+    config.pin_d4 = Y6_GPIO_NUM;
+    config.pin_d5 = Y7_GPIO_NUM;
+    config.pin_d6 = Y8_GPIO_NUM;
+    config.pin_d7 = Y9_GPIO_NUM;
+    config.pin_xclk = XCLK_GPIO_NUM;
+    config.pin_pclk = PCLK_GPIO_NUM;
+    config.pin_vsync = VSYNC_GPIO_NUM;
+    config.pin_href = HREF_GPIO_NUM;
+    config.pin_sscb_sda = SIOD_GPIO_NUM;
+    config.pin_sscb_scl = SIOC_GPIO_NUM;
+    config.pin_pwdn = PWDN_GPIO_NUM;
+    config.pin_reset = RESET_GPIO_NUM;
+    config.xclk_freq_hz = 20000000;
+    config.pixel_format = PIXFORMAT_GRAYSCALE;
+    config.frame_size = FRAMESIZE_QQVGA;
+    config.fb_count = 1;
+
+    // Begin serial communication.
+    Serial.begin(230400); // 115200
+
+    // Initialize the camera.
+    initialize(&config);
 }
 
 // Main loop of the program.
 void loop()
 {
-    if (model.isStreamEnabled)
+    // Get the camera model reference.
+    CameraModel *model = CameraModel::getInstance();
+    if (model->getIsStreamEnabled())
     {
         camera_fb_t *frame_buffer = esp_camera_fb_get();
         if (frame_buffer)
         {
-            process_image(frame_buffer, &model);
+            process_image(frame_buffer);
             // Return the frame buffer back to the camera driver.
             esp_camera_fb_return(frame_buffer);
         }
         delay(25);
     }
-    serial_commands(&model);
+    serial_commands();
 }

+ 0 - 59
firmware-update/globals.h

@@ -1,59 +0,0 @@
-#ifndef GLOBALS_H
-#define GLOBALS_H
-
-#include <stdint.h>
-
-// Define Pin numbers used by the camera.
-#define FLASH_GPIO_NUM 4
-#define HREF_GPIO_NUM 23
-#define PCLK_GPIO_NUM 22
-#define PWDN_GPIO_NUM 32
-#define RESET_GPIO_NUM -1
-#define SIOC_GPIO_NUM 27
-#define SIOD_GPIO_NUM 26
-#define VSYNC_GPIO_NUM 25
-#define XCLK_GPIO_NUM 0
-#define Y2_GPIO_NUM 5
-#define Y3_GPIO_NUM 18
-#define Y4_GPIO_NUM 19
-#define Y5_GPIO_NUM 21
-#define Y6_GPIO_NUM 36
-#define Y7_GPIO_NUM 39
-#define Y8_GPIO_NUM 34
-#define Y9_GPIO_NUM 35
-
-/**
- * The dithering algorithms available.
- */
-enum DitheringAlgorithm : uint8_t
-{
-    FLOYD_STEINBERG,
-    JARVIS_JUDICE_NINKE,
-    STUCKI
-};
-
-typedef struct CameraModel
-{
-    /**
-     * Flag to enable or disable dithering.
-     */
-    bool isDitheringDisabled;
-    /**
-     * Flag to represent the flash state when saving pictures to the Flipper.
-     */
-    bool isFlashEnabled;
-    /**
-     * Flag to invert pixel colors.
-     */
-    bool isInverted;
-    /**
-     * Flag to stop or start the stream.
-     */
-    bool isStreamEnabled;
-    /**
-     * Holds the currently selected dithering algorithm.
-     */
-    DitheringAlgorithm ditherAlgorithm;
-} CameraModel;
-
-#endif

+ 3 - 4
firmware-update/initialize.h

@@ -4,10 +4,9 @@
 #include <esp_camera.h>
 #include <FS.h>
 
-#include "globals.h"
+#include "camera_model.h"
+#include "pins.h"
 
-camera_config_t config;
-
-void initialize(CameraModel *model);
+void initialize(camera_config_t *config);
 
 #endif

+ 8 - 36
firmware-update/initialize.ino

@@ -1,54 +1,26 @@
 #include "initialize.h"
 
-void initialize(CameraModel *model)
+void initialize(camera_config_t *config)
 {
-    // Set up the model defaults.
-    model->isDitheringDisabled = false;
-    model->isInverted = false;
-    model->isFlashEnabled = false;
-    model->isStreamEnabled = true;
-    model->ditherAlgorithm = FLOYD_STEINBERG;
-
-    // Set initial camera configurations for grayscale.
-    config.ledc_channel = LEDC_CHANNEL_0;
-    config.ledc_timer = LEDC_TIMER_0;
-    config.pin_d0 = Y2_GPIO_NUM;
-    config.pin_d1 = Y3_GPIO_NUM;
-    config.pin_d2 = Y4_GPIO_NUM;
-    config.pin_d3 = Y5_GPIO_NUM;
-    config.pin_d4 = Y6_GPIO_NUM;
-    config.pin_d5 = Y7_GPIO_NUM;
-    config.pin_d6 = Y8_GPIO_NUM;
-    config.pin_d7 = Y9_GPIO_NUM;
-    config.pin_xclk = XCLK_GPIO_NUM;
-    config.pin_pclk = PCLK_GPIO_NUM;
-    config.pin_vsync = VSYNC_GPIO_NUM;
-    config.pin_href = HREF_GPIO_NUM;
-    config.pin_sscb_sda = SIOD_GPIO_NUM;
-    config.pin_sscb_scl = SIOC_GPIO_NUM;
-    config.pin_pwdn = PWDN_GPIO_NUM;
-    config.pin_reset = RESET_GPIO_NUM;
-    config.xclk_freq_hz = 20000000;
-    config.pixel_format = PIXFORMAT_GRAYSCALE;
-    config.frame_size = FRAMESIZE_QQVGA;
-    config.fb_count = 1;
-
     // Initialize camera.
-    esp_err_t err = esp_camera_init(&config);
+    esp_err_t err = esp_camera_init(config);
     if (err != ESP_OK)
     {
         return;
     }
 
+    // Get the camera model reference.
+    CameraModel *model = CameraModel::getInstance();
+
     // Check if the flash is already on, if it is turn it off.
-    if (digitalRead(FLASH_GPIO_NUM) == HIGH)
+    if (model->getIsFlashEnabled())
     {
         pinMode(FLASH_GPIO_NUM, OUTPUT);
         digitalWrite(FLASH_GPIO_NUM, LOW);
-        model->isFlashEnabled = false;
+        model->setIsFlashEnabled(false);
     }
 
-    // Set global reference to the camera.
+    // Get the camera sensor reference.
     sensor_t *cam = esp_camera_sensor_get();
 
     // Set up the frame buffer reference.

+ 23 - 0
firmware-update/pins.h

@@ -0,0 +1,23 @@
+#ifndef GLOBALS_H
+#define GLOBALS_H
+
+// Define Pin numbers used by the camera.
+#define FLASH_GPIO_NUM 4
+#define HREF_GPIO_NUM 23
+#define PCLK_GPIO_NUM 22
+#define PWDN_GPIO_NUM 32
+#define RESET_GPIO_NUM -1
+#define SIOC_GPIO_NUM 27
+#define SIOD_GPIO_NUM 26
+#define VSYNC_GPIO_NUM 25
+#define XCLK_GPIO_NUM 0
+#define Y2_GPIO_NUM 5
+#define Y3_GPIO_NUM 18
+#define Y4_GPIO_NUM 19
+#define Y5_GPIO_NUM 21
+#define Y6_GPIO_NUM 36
+#define Y7_GPIO_NUM 39
+#define Y8_GPIO_NUM 34
+#define Y9_GPIO_NUM 35
+
+#endif

+ 2 - 1
firmware-update/process_image.h

@@ -4,9 +4,10 @@
 #include <esp_camera.h>
 #include <FS.h>
 
+#include "camera_model.h"
 #include "dithering.h"
 
 /** Process and send grayscale images back to the Flipper Zero. */
-void process_image(camera_fb_t *frame_buffer, CameraModel *model);
+void process_image(camera_fb_t *frame_buffer);
 
 #endif

+ 7 - 4
firmware-update/process_image.ino

@@ -1,14 +1,17 @@
 #include "process_image.h"
 
-void process_image(camera_fb_t *frame_buffer, CameraModel *model)
+void process_image(camera_fb_t *frame_buffer)
 {
+    // Get the camera model reference.
+    CameraModel *model = CameraModel::getInstance();
+
     // If dithering is not disabled, perform dithering on the image. Dithering is the
     // process of approximating the look of a high-resolution grayscale image in a
     // lower resolution by binary values (black & white), thereby representing
     // different shades of gray.
-    if (!model->isDitheringDisabled)
+    if (!model->getIsDitheringDisabled())
     {
-        dither_image(frame_buffer, model); // Invokes the dithering process on the frame buffer.
+        dither_image(frame_buffer); // Invokes the dithering process on the frame buffer.
     }
 
     uint8_t flipper_y = 0;
@@ -32,7 +35,7 @@ void process_image(camera_fb_t *frame_buffer, CameraModel *model)
             for (uint8_t bit = 0; bit < 8; ++bit)
             {
                 // Check the invert flag and pack the pixels accordingly.
-                if (model->isInverted)
+                if (model->getIsInverted())
                 {
                     // If invert is true, consider pixel as 1 if it's more than 127.
                     if (frame_buffer->buf[true_y + x + bit] > 127)

+ 0 - 2
firmware-update/save_picture.h

@@ -4,8 +4,6 @@
 #include <esp_camera.h>
 #include <SD_MMC.h>
 
-#include "globals.h"
-
 /**
  * Save the current picture to the onboard SD card.
  * @todo - Future feature.

+ 3 - 4
firmware-update/save_picture.ino

@@ -28,14 +28,13 @@ void save_picture()
     camera_fb_t *frame_buffer = esp_camera_fb_get();
     if (!frame_buffer)
     {
-        Serial.println("Camera capture failed");
+        // Camera capture failed
         return;
     }
 
     if (!SD_MMC.begin())
     {
         // SD Card Mount Failed.
-        Serial.println("SD Card Mount Failed");
         esp_camera_fb_return(frame_buffer);
         return;
     }
@@ -50,13 +49,13 @@ void save_picture()
 
     if (!file)
     {
-        Serial.println("Failed to open file in writing mode");
+        // Failed to open file in writing mode
     }
     else
     {
         if (file.write(frame_buffer->buf, frame_buffer->len) != frame_buffer->len)
         {
-            Serial.println("Failed to write the image to the file");
+            // Failed to write the image to the file
         }
         file.close(); // Close the file in any case.
     }

+ 3 - 2
firmware-update/serial_commands.h

@@ -4,9 +4,10 @@
 #include <esp_camera.h>
 #include <FS.h>
 
-#include "globals.h"
+#include "camera_model.h"
+#include "pins.h"
 
 /** Handle the serial input commands coming from the Flipper Zero. */
-void serial_commands(CameraModel *model);
+void serial_commands();
 
 #endif

+ 15 - 10
firmware-update/serial_commands.ino

@@ -1,19 +1,24 @@
 #include "serial_commands.h"
 
-void serial_commands(CameraModel *model)
+void serial_commands()
 {
     if (Serial.available() > 0)
     {
+        // Get the camera model reference.
+        CameraModel *model = CameraModel::getInstance();
+
         char input = Serial.read();
         sensor_t *cam = esp_camera_sensor_get();
 
         switch (input)
         {
         case '>': // Toggle dithering.
-            model->isDitheringDisabled = !model->isDitheringDisabled;
+            model->setIsDitheringDisabled(
+                !model->getIsDitheringDisabled());
             break;
         case '<': // Toggle invert.
-            model->isInverted = !model->isInverted;
+            model->setIsInverted(
+                !model->getIsInverted());
             break;
         case 'b': // Remove brightness.
             cam->set_contrast(cam, cam->status.brightness - 1);
@@ -28,14 +33,14 @@ void serial_commands(CameraModel *model)
             cam->set_contrast(cam, cam->status.contrast + 1);
             break;
         case 'f': // Turn the flash off.
-            model->isFlashEnabled = false;
             pinMode(FLASH_GPIO_NUM, OUTPUT);
             digitalWrite(FLASH_GPIO_NUM, LOW);
+            model->setIsFlashEnabled(false);
             break;
         case 'F': // Turn the flash on.
-            model->isFlashEnabled = true;
             pinMode(FLASH_GPIO_NUM, OUTPUT);
             digitalWrite(FLASH_GPIO_NUM, HIGH);
+            model->setIsFlashEnabled(true);
             break;
         case 'P': // Save image to the onboard SD card.
             // @todo - Future feature.
@@ -45,19 +50,19 @@ void serial_commands(CameraModel *model)
             cam->set_hmirror(cam, !cam->status.hmirror);
             break;
         case 's': // Stop stream.
-            model->isStreamEnabled = false;
+            model->setIsStreamEnabled(false);
             break;
         case 'S': // Start stream.
-            model->isStreamEnabled = true;
+            model->setIsStreamEnabled(true);
             break;
         case '0': // Use Floyd Steinberg dithering.
-            model->ditherAlgorithm = FLOYD_STEINBERG;
+            model->setDitherAlgorithm(FLOYD_STEINBERG);
             break;
         case '1': // Use Jarvis Judice dithering.
-            model->ditherAlgorithm = JARVIS_JUDICE_NINKE;
+            model->setDitherAlgorithm(JARVIS_JUDICE_NINKE);
             break;
         case '2': // Use Stucki dithering.
-            model->ditherAlgorithm = STUCKI;
+            model->setDitherAlgorithm(STUCKI);
             break;
         default:
             // Do nothing.