Cody Tolene 2 лет назад
Родитель
Сommit
79628f3217
3 измененных файлов с 230 добавлено и 161 удалено
  1. 9 6
      fap/views/camera_suite_view_camera.c
  2. 58 30
      firmware/cli/cli-install.bat
  3. 163 125
      firmware/firmware.ino

+ 9 - 6
fap/views/camera_suite_view_camera.c

@@ -136,19 +136,22 @@ static void save_image(void* _model) {
                 // Turn on local jpeg save. When this is enabled the ESP32-CAM
                 // Turn on local jpeg save. When this is enabled the ESP32-CAM
                 // will save the image to the SD card and saving the image to
                 // will save the image to the SD card and saving the image to
                 // the Flipper SD card will be disabled/skipped.
                 // the Flipper SD card will be disabled/skipped.
-                furi_hal_uart_tx(FuriHalUartIdUSART1, 'J', 1);
+                unsigned char jpeg_on = 'J';
+                furi_hal_uart_tx(FuriHalUartIdUSART1, &jpeg_on, 1);
             } else {
             } else {
+                unsigned char jpeg_off = 'j';
                 // Turn off local jpeg save.
                 // Turn off local jpeg save.
-                furi_hal_uart_tx(FuriHalUartIdUSART1, 'j', 1);
+                furi_hal_uart_tx(FuriHalUartIdUSART1, &jpeg_off, 1);
             }
             }
             // Initiate the onboard ESP32-CAM picture sequence. So far this
             // Initiate the onboard ESP32-CAM picture sequence. So far this
-            // includes turning on the flash and potentially saving jpeg
-            // locally to the ESP32-CAM SD card.
-            furi_hal_uart_tx(FuriHalUartIdUSART1, 'P', 1);
+            // includes turning on the flash and alternatively saving jpeg
+            // locally to the ESP32-CAM SD card if enabled.
+            unsigned char take_picture = 'P';
+            furi_hal_uart_tx(FuriHalUartIdUSART1, &take_picture, 1);
         }
         }
         // If saving jpeg is enabled locally to the ESP32-CAM SD card, skip
         // If saving jpeg is enabled locally to the ESP32-CAM SD card, skip
         // writing the image data to the Flipper Zero SD card.
         // writing the image data to the Flipper Zero SD card.
-        if(!app->saveJpeg) {
+        if(!app->jpeg) {
             // Write locally to the Flipper Zero SD card in the DCIM folder.
             // Write locally to the Flipper Zero SD card in the DCIM folder.
             int8_t row_buffer[ROW_BUFFER_LENGTH];
             int8_t row_buffer[ROW_BUFFER_LENGTH];
             if(is_inverted) {
             if(is_inverted) {

+ 58 - 30
firmware/cli/cli-install.bat

@@ -8,7 +8,7 @@ set SELECTED_BOARD=%DEFAULT_BOARD_FQBN%
 set CLI_FOUND_FOLLOW_UP=0
 set CLI_FOUND_FOLLOW_UP=0
 set COMPILE_FLAG=..\compile.flag
 set COMPILE_FLAG=..\compile.flag
 
 
-echo.
+echo Initializing...
 
 
 :checkCLI
 :checkCLI
 if not exist "arduino-cli.exe" (
 if not exist "arduino-cli.exe" (
@@ -39,16 +39,23 @@ if not exist "%CLI_TEMP%" (
     echo Assets already installed. Skipping...
     echo Assets already installed. Skipping...
 )
 )
 
 
+echo Ensure your Flipper Zero is plugged in via USB before continuing.
+pause
+
 echo Ready for installation...
 echo Ready for installation...
 
 
-set /p USE_DEFAULT_BOARD="Install to default AI-Thinker ESP32-CAM board with FQBN '%DEFAULT_BOARD_FQBN%'? (Y/N): "
-if /i "%USE_DEFAULT_BOARD%"=="N" (
-    set /p SHOW_BOARDS="Display all possible ESP32 board names and FQBN's? (Y/N): "
-    if /i "!SHOW_BOARDS!"=="Y" (
-        echo.
-        arduino-cli board listall
+if not exist "%COMPILE_FLAG%" (
+    set /p USE_DEFAULT_BOARD="Install to default AI-Thinker ESP32-CAM board with FQBN '%DEFAULT_BOARD_FQBN%'? (Y/N): "
+    if /i "%USE_DEFAULT_BOARD%"=="N" (
+        echo Warning - This script has not been tested with other boards. Please use at your own risk.
+        set /p SHOW_BOARDS="Display all possible ESP32 board names and FQBN's? (Y/N): "
+        if /i "!SHOW_BOARDS!"=="Y" (
+            echo.
+            arduino-cli board listall
+        )
+        set /p SELECTED_BOARD="Please enter your board FQBN. For example '%DEFAULT_BOARD_FQBN%' with no quotes: "
     )
     )
-    set /p SELECTED_BOARD="Please enter your board FQBN. For example '%DEFAULT_BOARD_FQBN%' with no quotes: "
+    goto :compileFirmware
 )
 )
 
 
 if exist "%COMPILE_FLAG%" (
 if exist "%COMPILE_FLAG%" (
@@ -56,19 +63,9 @@ if exist "%COMPILE_FLAG%" (
     if /i "!RECOMPILE!"=="N" (
     if /i "!RECOMPILE!"=="N" (
         goto :compileFirmware
         goto :compileFirmware
     )
     )
-) else (
-    :compileFirmware
-    echo Compiling firmware, this will take a moment...
-    arduino-cli %CONFIG_FILE% compile --fqbn !SELECTED_BOARD! ..\firmware.ino
-    if %ERRORLEVEL% EQU 0 (
-        echo Compile complete. Ready to upload.
-        type nul > %COMPILE_FLAG%
-    ) else (
-        echo Compilation failed. Please correct the errors and try again.
-        exit /b
-    )
 )
 )
 
 
+:continue
 echo.
 echo.
 arduino-cli board list
 arduino-cli board list
 echo Please find your Flipper Zero USB port from the list above (may show as unknown).
 echo Please find your Flipper Zero USB port from the list above (may show as unknown).
@@ -79,27 +76,58 @@ echo Your ESP32-CAM is ready to be flashed. Please follow the instructions below
 
 
 :uploadFirmware
 :uploadFirmware
 echo.
 echo.
-echo 1. Make sure your ESP32-CAM module is unplugged from your Flipper Zero.
-echo 2. Make sure you've grounded your IO0 pin on your ESP32-CAM module to the correct GND pin.
-echo 3. Plug your ESP32-CAM module with the reset button pressed right after going to the next step.
-echo 4. When "Connecting..." is displayed unpress the reset button.
-echo 5. It is common for this to fail a few times, keep trying and double check your connections.
+echo 1. Make sure you've grounded your IO0 pin on your ESP32-CAM module to the correct GND pin.
+echo 2. Plug in your ESP32-CAM module with the reset button pressed a few seconds before continuing to the next step.
+echo 3. When continuing to the next step, simultaneously release the reset button.
+echo 4. Your ESP32-CAM should now be in flash mode. Allow the firmware to upload, this will take a moment.
+echo 5. It's not uncommon for this to fail many times, keep trying and double check your connections.
 echo.
 echo.
 pause
 pause
 
 
+set RETRY_COUNT=1
+
+:uploadLoop
 echo.
 echo.
-echo Preparing firmware upload...
+echo Preparing firmware upload... Attempt number !RETRY_COUNT!...
 arduino-cli %CONFIG_FILE% upload -p %PORT_NUMBER% --fqbn !SELECTED_BOARD! ..\firmware.ino
 arduino-cli %CONFIG_FILE% upload -p %PORT_NUMBER% --fqbn !SELECTED_BOARD! ..\firmware.ino
-if %ERRORLEVEL% NEQ 0 (
-    echo.
-    set /p UPLOAD_TRY_AGAIN="Upload failed, would you like to retry? (Y/N): "
-    if /i "!UPLOAD_TRY_AGAIN!"=="Y" (
-        goto :uploadFirmware
+if !ERRORLEVEL! EQU 0 (
+    goto :uploadSuccess
+) else (
+    if !RETRY_COUNT! lss 3 (
+        set /a RETRY_COUNT+=1
+        goto :uploadLoop
+    ) else (
+        echo.
+        set /p UPLOAD_TRY_AGAIN="Upload failed after 3 attempts, would you like to retry? (Y/N): "
+        if /i "!UPLOAD_TRY_AGAIN!"=="Y" (
+            set RETRY_COUNT=1
+            goto :uploadFirmware
+        ) else (
+            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
+        )
     )
     )
 )
 )
 
 
+:uploadSuccess
+echo.
+echo Firmware upload was successful.
 echo.
 echo.
 echo Fin. Happy programming friend.
 echo Fin. Happy programming friend.
 echo.
 echo.
 pause
 pause
 exit /b
 exit /b
+
+:compileFirmware
+echo Compiling firmware, this will take a moment...
+arduino-cli %CONFIG_FILE% compile --fqbn !SELECTED_BOARD! ..\firmware.ino
+if %ERRORLEVEL% EQU 0 (
+    echo Compile complete. Ready to upload.
+    type nul > %COMPILE_FLAG%
+) else (
+    echo Compilation failed. Please correct the errors and try again.
+    exit /b
+)
+
+goto :continue

+ 163 - 125
firmware/firmware.ino

@@ -1,6 +1,13 @@
 #include "esp_camera.h"
 #include "esp_camera.h"
+#include "Arduino.h"
 #include "FS.h"
 #include "FS.h"
 #include "SD_MMC.h"
 #include "SD_MMC.h"
+#include "soc/soc.h"
+#include "soc/rtc_cntl_reg.h"
+#include "driver/rtc_io.h"
+#include <vector>
+#include <tuple>
+#include <cstdint>
 
 
 // Define Pin numbers used by the camera.
 // Define Pin numbers used by the camera.
 #define FLASH_GPIO_NUM 4
 #define FLASH_GPIO_NUM 4
@@ -26,14 +33,15 @@
 camera_config_t config;
 camera_config_t config;
 
 
 // Function prototypes.
 // Function prototypes.
-void handleSerialInput(camera_fb_t * fb);
+void handleSerialInput();
 void initializeCamera();
 void initializeCamera();
-void processImage(camera_fb_t * fb);
-void ditherImage(camera_fb_t * fb);
-void saveFrameBufferToSDCard(camera_fb_t * fb);
+void processImage(camera_fb_t * frame_buffer);
+void ditherImage(camera_fb_t * frame_buffer);
+void savePictureToSDCard(camera_fb_t * frame_buffer);
+void takePicture();
 
 
 // Enumeration to represent the available dithering algorithms.
 // Enumeration to represent the available dithering algorithms.
-enum DitheringAlgorithm {
+enum DitheringAlgorithm: uint8_t {
   FLOYD_STEINBERG,
   FLOYD_STEINBERG,
   JARVIS_JUDICE_NINKE,
   JARVIS_JUDICE_NINKE,
   STUCKI
   STUCKI
@@ -48,8 +56,8 @@ bool disableDithering = false;
 // Flag to invert pixel colors.
 // Flag to invert pixel colors.
 bool invert = false;
 bool invert = false;
 
 
-// Flag to represent the flash state.
-bool isFlashOn = false;
+// Flag to represent the flash state when saving pictures to the Flipper.
+bool isFlashEnabled = false;
 
 
 // Flag to represent whether the image is rotated.
 // Flag to represent whether the image is rotated.
 bool rotated = false;
 bool rotated = false;
@@ -61,31 +69,35 @@ bool stopStream = false;
 bool storeJpeg = false;
 bool storeJpeg = false;
 
 
 void setup() {
 void setup() {
-  // Start serial communication at 230400 baud rate.
-  Serial.begin(230400);
+  // Disable the brownout detector.
+  WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);
+  // Start serial communication at 115200 baud rate.
+  Serial.begin(115200); // Prev 230400
   initializeCamera();
   initializeCamera();
 }
 }
 
 
 void loop() {
 void loop() {
-  // Capture and process the frame buffer if streaming is enabled.
-  camera_fb_t * fb = esp_camera_fb_get();
-  
   if (!stopStream) {
   if (!stopStream) {
-    
-    if (fb) {
-      processImage(fb);
-      // Return the frame buffer back to the camera driver.
-      esp_camera_fb_return(fb);
+    // Set pixel format to GRAYSCALE for streaming
+    sensor_t * s = esp_camera_sensor_get();
+    s -> set_framesize(s, FRAMESIZE_QVGA); // Or your preferred resolution
+    s -> set_pixformat(s, PIXFORMAT_GRAYSCALE); // For streaming
+
+    camera_fb_t * frame_buffer = esp_camera_fb_get();
+    if (frame_buffer) {
+      // Process and Send Grayscale image
+      processImage(frame_buffer);
+      // Return the frame buffer back to the camera driver
+      esp_camera_fb_return(frame_buffer);
     }
     }
-    // Delay for 10ms between each frame.
-    delay(10); 
+    delay(25); // Adjust delay as necessary
+  } else {
+    // Handle any available serial input commands
+    handleSerialInput();
   }
   }
-  
-  // Handle any available serial input commands.
-  handleSerialInput(fb); 
 }
 }
 
 
-void handleSerialInput(camera_fb_t * fb) {
+void handleSerialInput() {
   if (Serial.available() > 0) {
   if (Serial.available() > 0) {
     char input = Serial.read();
     char input = Serial.read();
     sensor_t * cameraSensor = esp_camera_sensor_get();
     sensor_t * cameraSensor = esp_camera_sensor_get();
@@ -99,25 +111,25 @@ void handleSerialInput(camera_fb_t * fb) {
       break;
       break;
     case 'B': // Add brightness.
     case 'B': // Add brightness.
       cameraSensor -> set_contrast(
       cameraSensor -> set_contrast(
-        cameraSensor, 
+        cameraSensor,
         cameraSensor -> status.brightness + 1
         cameraSensor -> status.brightness + 1
       );
       );
       break;
       break;
     case 'b': // Remove brightness.
     case 'b': // Remove brightness.
       cameraSensor -> set_contrast(
       cameraSensor -> set_contrast(
-        cameraSensor, 
+        cameraSensor,
         cameraSensor -> status.brightness - 1
         cameraSensor -> status.brightness - 1
       );
       );
       break;
       break;
     case 'C': // Add contrast.
     case 'C': // Add contrast.
       cameraSensor -> set_contrast(
       cameraSensor -> set_contrast(
-        cameraSensor, 
+        cameraSensor,
         cameraSensor -> status.contrast + 1
         cameraSensor -> status.contrast + 1
       );
       );
       break;
       break;
     case 'c': // Remove contrast.
     case 'c': // Remove contrast.
       cameraSensor -> set_contrast(
       cameraSensor -> set_contrast(
-        cameraSensor, 
+        cameraSensor,
         cameraSensor -> status.contrast - 1
         cameraSensor -> status.contrast - 1
       );
       );
       break;
       break;
@@ -128,33 +140,33 @@ void handleSerialInput(camera_fb_t * fb) {
       storeJpeg = true;
       storeJpeg = true;
       break;
       break;
     case 'P': // Picture sequence.
     case 'P': // Picture sequence.
-      if (!isFlashOn) {
-        isFlashOn = true;
-        // Set up the flash light control pin (number 4) as an "output"
-        // so we can  turn the torch ON and OFF.
-        pinMode(FLASH_GPIO_NUM, OUTPUT);
-        // Turn on torch.
-        digitalWrite(FLASH_GPIO_NUM, HIGH);
+      // Stop the IO stream before taking a picture.
+      stopStream = true;
+      if (!isFlashEnabled) {
         if (storeJpeg) {
         if (storeJpeg) {
           // Save jpeg image to sd card.
           // Save jpeg image to sd card.
-          saveFrameBufferToSDCard(fb);
-          // Return the frame buffer back to the camera driver.
-          esp_camera_fb_return(fb);
+          takePicture();
+        } else {
+          // Turn on torch.
+          pinMode(FLASH_GPIO_NUM, OUTPUT);
+          digitalWrite(FLASH_GPIO_NUM, HIGH);
+          // Give some time for Flipper to save locally with flash on.
+          delay(50);
         }
         }
-        // Give some time for Flipper to save locally with flash on.
-        delay(15); 
         // Turn off torch.
         // Turn off torch.
+        pinMode(FLASH_GPIO_NUM, OUTPUT);
         digitalWrite(FLASH_GPIO_NUM, LOW);
         digitalWrite(FLASH_GPIO_NUM, LOW);
-        isFlashOn = false;
       }
       }
+      // Restart the stream after the picture is taken.
+      stopStream = false;
       break;
       break;
-    case 'M': // Toggle Mirror
+    case 'M': // Toggle Mirror.
       cameraSensor -> set_hmirror(cameraSensor, !cameraSensor -> status.hmirror);
       cameraSensor -> set_hmirror(cameraSensor, !cameraSensor -> status.hmirror);
       break;
       break;
-    case 'S': // Start stream
+    case 'S': // Start stream.
       stopStream = false;
       stopStream = false;
       break;
       break;
-    case 's': // Stop stream
+    case 's': // Stop stream.
       stopStream = true;
       stopStream = true;
       break;
       break;
     case '0': // Use Floyd Steinberg dithering.
     case '0': // Use Floyd Steinberg dithering.
@@ -174,7 +186,7 @@ void handleSerialInput(camera_fb_t * fb) {
 }
 }
 
 
 void initializeCamera() {
 void initializeCamera() {
-  // Set camera configurations
+  // Set initial camera configurations for grayscale.
   config.ledc_channel = LEDC_CHANNEL_0;
   config.ledc_channel = LEDC_CHANNEL_0;
   config.ledc_timer = LEDC_TIMER_0;
   config.ledc_timer = LEDC_TIMER_0;
   config.pin_d0 = Y2_GPIO_NUM;
   config.pin_d0 = Y2_GPIO_NUM;
@@ -198,20 +210,16 @@ void initializeCamera() {
   config.frame_size = FRAMESIZE_QQVGA;
   config.frame_size = FRAMESIZE_QQVGA;
   config.fb_count = 1;
   config.fb_count = 1;
 
 
-  if (isFlashOn) {
-    pinMode(FLASH_GPIO_NUM, OUTPUT);
-    // Turn off torch.
-    digitalWrite(FLASH_GPIO_NUM, LOW);
-    isFlashOn = false;
-  }
-
-  // Initialize camera
-  esp_err_t err = esp_camera_init( & config);
+  // Initialize camera.
+  esp_err_t err = esp_camera_init(&config);
   if (err != ESP_OK) {
   if (err != ESP_OK) {
-    Serial.printf("Camera init failed with error 0x%x", err);
     return;
     return;
   }
   }
 
 
+  // Make sure torch starts as off.
+  pinMode(FLASH_GPIO_NUM, OUTPUT);
+  digitalWrite(FLASH_GPIO_NUM, LOW);
+
   // Set initial contrast.
   // Set initial contrast.
   sensor_t * s = esp_camera_sensor_get();
   sensor_t * s = esp_camera_sensor_get();
   s -> set_contrast(s, 0);
   s -> set_contrast(s, 0);
@@ -221,13 +229,13 @@ void initializeCamera() {
   s -> set_hmirror(s, true); // Horizontal mirror
   s -> set_hmirror(s, true); // Horizontal mirror
 }
 }
 
 
-void processImage(camera_fb_t * frameBuffer) {
+void processImage(camera_fb_t * frame_buffer) {
   // If dithering is not disabled, perform dithering on the image. Dithering is the 
   // 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 
   // process of approximating the look of a high-resolution grayscale image in a 
   // lower resolution by binary values (black & white), thereby representing
   // lower resolution by binary values (black & white), thereby representing
   // different shades of gray.
   // different shades of gray.
   if (!disableDithering) {
   if (!disableDithering) {
-    ditherImage(frameBuffer); // Invokes the dithering process on the frame buffer
+    ditherImage(frame_buffer); // Invokes the dithering process on the frame buffer
   }
   }
 
 
   uint8_t flipper_y = 0;
   uint8_t flipper_y = 0;
@@ -240,7 +248,7 @@ void processImage(camera_fb_t * frameBuffer) {
     // Calculate the actual y index in the frame buffer 1D array by multiplying the 
     // Calculate the actual y index in the frame buffer 1D array by multiplying the 
     // y value with the width of the frame buffer. This gives the starting index 
     // y value with the width of the frame buffer. This gives the starting index 
     // of the row in the 1D array.
     // of the row in the 1D array.
-    size_t true_y = y * frameBuffer -> width;
+    size_t true_y = y * frame_buffer -> width;
 
 
     // Iterating over specific columns of each row in the frame buffer.
     // Iterating over specific columns of each row in the frame buffer.
     for (uint8_t x = 16; x < 144; x += 8) { // step by 8 as we're packing 8 pixels per byte
     for (uint8_t x = 16; x < 144; x += 8) { // step by 8 as we're packing 8 pixels per byte
@@ -250,12 +258,12 @@ void processImage(camera_fb_t * frameBuffer) {
         // Check the invert flag and pack the pixels accordingly.
         // Check the invert flag and pack the pixels accordingly.
         if (invert) {
         if (invert) {
           // If invert is true, consider pixel as 1 if it's more than 127.
           // If invert is true, consider pixel as 1 if it's more than 127.
-          if (frameBuffer -> buf[true_y + x + bit] > 127) {
+          if (frame_buffer -> buf[true_y + x + bit] > 127) {
             packed_pixels |= (1 << (7 - bit));
             packed_pixels |= (1 << (7 - bit));
           }
           }
         } else {
         } else {
           // If invert is false, consider pixel as 1 if it's less than 127.
           // If invert is false, consider pixel as 1 if it's less than 127.
-          if (frameBuffer -> buf[true_y + x + bit] < 127) {
+          if (frame_buffer -> buf[true_y + x + bit] < 127) {
             packed_pixels |= (1 << (7 - bit));
             packed_pixels |= (1 << (7 - bit));
           }
           }
         }
         }
@@ -268,84 +276,114 @@ void processImage(camera_fb_t * frameBuffer) {
   }
   }
 }
 }
 
 
-void ditherImage(camera_fb_t * fb) {
-  for (uint8_t y = 0; y < fb -> height; ++y) {
-    for (uint8_t x = 0; x < fb -> width; ++x) {
-      size_t current = (y * fb -> width) + x;
-      uint8_t oldpixel = fb -> buf[current];
+void ditherImage(camera_fb_t* frame_buffer) {
+  for (uint8_t y = 0; y < frame_buffer->height; ++y) {
+    for (uint8_t x = 0; x < frame_buffer->width; ++x) {
+      size_t current = (y * frame_buffer->width) + x;
+      uint8_t oldpixel = frame_buffer->buf[current];
       uint8_t newpixel = oldpixel >= 128 ? 255 : 0;
       uint8_t newpixel = oldpixel >= 128 ? 255 : 0;
-      fb -> buf[current] = newpixel;
+      frame_buffer->buf[current] = newpixel;
       int8_t quant_error = oldpixel - newpixel;
       int8_t quant_error = oldpixel - newpixel;
 
 
       // Apply error diffusion based on the selected algorithm
       // Apply error diffusion based on the selected algorithm
       switch (ditherAlgorithm) {
       switch (ditherAlgorithm) {
-      case JARVIS_JUDICE_NINKE:
-        fb -> buf[(y * fb -> width) + x + 1] += quant_error * 7 / 48;
-        fb -> buf[(y * fb -> width) + x + 2] += quant_error * 5 / 48;
-        fb -> buf[(y + 1) * fb -> width + x - 2] += quant_error * 3 / 48;
-        fb -> buf[(y + 1) * fb -> width + x - 1] += quant_error * 5 / 48;
-        fb -> buf[(y + 1) * fb -> width + x] += quant_error * 7 / 48;
-        fb -> buf[(y + 1) * fb -> width + x + 1] += quant_error * 5 / 48;
-        fb -> buf[(y + 1) * fb -> width + x + 2] += quant_error * 3 / 48;
-        fb -> buf[(y + 2) * fb -> width + x - 2] += quant_error * 1 / 48;
-        fb -> buf[(y + 2) * fb -> width + x - 1] += quant_error * 3 / 48;
-        fb -> buf[(y + 2) * fb -> width + x] += quant_error * 5 / 48;
-        fb -> buf[(y + 2) * fb -> width + x + 1] += quant_error * 3 / 48;
-        fb -> buf[(y + 2) * fb -> width + x + 2] += quant_error * 1 / 48;
-        break;
-      case STUCKI:
-        fb -> buf[(y * fb -> width) + x + 1] += quant_error * 8 / 42;
-        fb -> buf[(y * fb -> width) + x + 2] += quant_error * 4 / 42;
-        fb -> buf[(y + 1) * fb -> width + x - 2] += quant_error * 2 / 42;
-        fb -> buf[(y + 1) * fb -> width + x - 1] += quant_error * 4 / 42;
-        fb -> buf[(y + 1) * fb -> width + x] += quant_error * 8 / 42;
-        fb -> buf[(y + 1) * fb -> width + x + 1] += quant_error * 4 / 42;
-        fb -> buf[(y + 1) * fb -> width + x + 2] += quant_error * 2 / 42;
-        fb -> buf[(y + 2) * fb -> width + x - 2] += quant_error * 1 / 42;
-        fb -> buf[(y + 2) * fb -> width + x - 1] += quant_error * 2 / 42;
-        fb -> buf[(y + 2) * fb -> width + x] += quant_error * 4 / 42;
-        fb -> buf[(y + 2) * fb -> width + x + 1] += quant_error * 2 / 42;
-        fb -> buf[(y + 2) * fb -> width + x + 2] += quant_error * 1 / 42;
-        break;
-      case FLOYD_STEINBERG:
-      default:
-        // Default to Floyd-Steinberg dithering if an invalid algorithm is selected
-        fb -> buf[(y * fb -> width) + x + 1] += quant_error * 7 / 16;
-        fb -> buf[(y + 1) * fb -> width + x - 1] += quant_error * 3 / 16;
-        fb -> buf[(y + 1) * fb -> width + x] += quant_error * 5 / 16;
-        fb -> buf[(y + 1) * fb -> width + x + 1] += quant_error * 1 / 16;
-        break;
+        case JARVIS_JUDICE_NINKE:
+          frame_buffer->buf[(y * frame_buffer->width) + x + 1] += quant_error * 7 / 48;
+          frame_buffer->buf[(y * frame_buffer->width) + x + 2] += quant_error * 5 / 48;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x - 2] += quant_error * 3 / 48;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x - 1] += quant_error * 5 / 48;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x] += quant_error * 7 / 48;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x + 1] += quant_error * 5 / 48;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x + 2] += quant_error * 3 / 48;
+          frame_buffer->buf[(y + 2) * frame_buffer->width + x - 2] += quant_error * 1 / 48;
+          frame_buffer->buf[(y + 2) * frame_buffer->width + x - 1] += quant_error * 3 / 48;
+          frame_buffer->buf[(y + 2) * frame_buffer->width + x] += quant_error * 5 / 48;
+          frame_buffer->buf[(y + 2) * frame_buffer->width + x + 1] += quant_error * 3 / 48;
+          frame_buffer->buf[(y + 2) * frame_buffer->width + x + 2] += quant_error * 1 / 48;
+          break;
+        case STUCKI:
+          frame_buffer->buf[(y * frame_buffer->width) + x + 1] += quant_error * 8 / 42;
+          frame_buffer->buf[(y * frame_buffer->width) + x + 2] += quant_error * 4 / 42;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x - 2] += quant_error * 2 / 42;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x - 1] += quant_error * 4 / 42;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x] += quant_error * 8 / 42;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x + 1] += quant_error * 4 / 42;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x + 2] += quant_error * 2 / 42;
+          frame_buffer->buf[(y + 2) * frame_buffer->width + x - 2] += quant_error * 1 / 42;
+          frame_buffer->buf[(y + 2) * frame_buffer->width + x - 1] += quant_error * 2 / 42;
+          frame_buffer->buf[(y + 2) * frame_buffer->width + x] += quant_error * 4 / 42;
+          frame_buffer->buf[(y + 2) * frame_buffer->width + x + 1] += quant_error * 2 / 42;
+          frame_buffer->buf[(y + 2) * frame_buffer->width + x + 2] += quant_error * 1 / 42;
+          break;
+        case FLOYD_STEINBERG:
+        default:
+          // Default to Floyd-Steinberg dithering if an invalid algorithm is selected
+          frame_buffer->buf[(y * frame_buffer->width) + x + 1] += quant_error * 7 / 16;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x - 1] += quant_error * 3 / 16;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x] += quant_error * 5 / 16;
+          frame_buffer->buf[(y + 1) * frame_buffer->width + x + 1] += quant_error * 1 / 16;
+          break;
       }
       }
     }
     }
   }
   }
 }
 }
 
 
-void saveFrameBufferToSDCard(camera_fb_t * fb) {
-  if (!SD_MMC.begin()) {
-    // Serial.println("SD Card Mount Failed");
-    return;
-  }
+void takePicture() {
+    // Get camera sensor
+    sensor_t * s = esp_camera_sensor_get();
+    
+    // Check if the sensor is valid
+    if(!s) {
+        Serial.println("Failed to acquire camera sensor");
+        return;
+    }
 
 
-  uint8_t cardType = SD_MMC.cardType();
-  if (cardType == CARD_NONE) {
-    // Serial.println("No SD Card attached");
-    return;
-  }
+    // Set pixel format to JPEG for saving picture
+    s->set_pixformat(s, PIXFORMAT_JPEG);
 
 
-  // Generate a unique filename
-  String path = "/picture";
-  path += String(millis());
-  path += ".jpg";
+    // Set frame size based on available PSRAM
+    if (psramFound()) {
+        s->set_framesize(s, FRAMESIZE_UXGA);
+    } else {
+        s->set_framesize(s, FRAMESIZE_SVGA);
+    }
 
 
-  File file = SD_MMC.open(path.c_str(), FILE_WRITE);
-  if (!file) {
-    // Serial.println("Failed to open file for writing");
-    return;
-  }
+    // Get a frame buffer from camera
+    camera_fb_t * frame_buffer = esp_camera_fb_get();
+    if (!frame_buffer) {
+        Serial.println("Camera capture failed");
+        return;
+    }
+
+    // Save the picture to SD card
+    savePictureToSDCard(frame_buffer);
+}
 
 
-  // Write frame buffer to file
-  file.write(fb -> buf, fb -> len);
+void savePictureToSDCard(camera_fb_t * frame_buffer) {
+    if (!SD_MMC.begin()) {
+        // SD Card Mount Failed.
+        Serial.println("SD Card Mount Failed");
+        esp_camera_fb_return(frame_buffer);
+        return;
+    }
+
+    // Generate a unique filename
+    String path = "/picture";
+    path += String(millis());
+    path += ".jpg";
 
 
-  // Serial.println("File written to SD card");
-  file.close();
-}
+    fs::FS & fs = SD_MMC;
+    File file = fs.open(path.c_str(), FILE_WRITE);
+
+    if (!file) {
+        Serial.println("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");
+        }
+        file.close(); // Close the file in any case.
+    }
+
+    // Return the frame buffer back to the camera driver.
+    esp_camera_fb_return(frame_buffer);
+}