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

Improve camera I/O, code, comments, and draw.

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

BIN
.github/images/firmware-build-success.png


+ 73 - 38
src-fap/views/camera_suite_view_camera.c

@@ -10,6 +10,8 @@
 
 static CameraSuiteViewCamera* current_instance = NULL;
 
+bool is_inverted = false;
+
 struct CameraSuiteViewCamera {
     CameraSuiteViewCameraCallback callback;
     FuriStreamBuffer* rx_stream;
@@ -130,11 +132,20 @@ static void save_image(void* _model) {
     if(result) {
         storage_file_write(file, bitmap_header, BITMAP_HEADER_LENGTH);
         int8_t row_buffer[ROW_BUFFER_LENGTH];
-        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];
+        if(is_inverted) {
+            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);
+            }
+        } else {
+            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);
             }
-            storage_file_write(file, row_buffer, ROW_BUFFER_LENGTH);
         }
     }
 
@@ -186,8 +197,10 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
                 true);
             break;
         case InputKeyLeft:
-            // Camera: Invert.
+            // Camera: Toggle invert on the ESP32-CAM.
             data[0] = '<';
+            // Toggle invert state locally.
+            is_inverted = !is_inverted;
             with_view_model(
                 instance->view,
                 UartDumpModel * model,
@@ -252,8 +265,8 @@ static bool camera_suite_view_camera_input(InputEvent* event, void* context) {
                 data[0] = 'P';
                 // Initialize the ESP32-CAM onboard torch immediately.
                 furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
-                // Delay for 500ms to make sure flash is on before taking picture.
-                furi_delay_ms(500);
+                // Delay for 25ms to make sure flash is on before taking picture.
+                furi_delay_ms(25);
             }
             // Take picture.
             with_view_model(
@@ -341,41 +354,47 @@ static void camera_on_irq_cb(UartIrqEvent uartIrqEvent, uint8_t data, void* cont
 }
 
 static void process_ringbuffer(UartDumpModel* model, uint8_t byte) {
-    // First char has to be 'Y' in the buffer.
-    if(model->ringbuffer_index == 0 && byte != 'Y') {
-        return;
-    }
-
-    // Second char has to be ':' in the buffer or reset.
-    if(model->ringbuffer_index == 1 && byte != ':') {
-        model->ringbuffer_index = 0;
-        process_ringbuffer(model, 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 ':'.
+        if(model->ringbuffer_index == 0 && byte != 'Y') {
+            // Incorrect start of frame; reset.
+            return;
+        }
+        if(model->ringbuffer_index == 1 && byte != ':') {
+            // Incorrect start of frame; reset.
+            model->ringbuffer_index = 0;
+            return;
+        }
+        if(model->ringbuffer_index == 2) {
+            // Assign the third byte as the row identifier.
+            model->row_identifier = byte;
+        }
+        model->ringbuffer_index++; // Increment index for the next byte.
         return;
     }
 
-    // Assign current byte to the ringbuffer.
-    model->row_ringbuffer[model->ringbuffer_index] = byte;
-    // Increment the ringbuffer index.
-    ++model->ringbuffer_index;
+    // Store pixel value directly after the header.
+    model->row_ringbuffer[model->ringbuffer_index - HEADER_LENGTH] = byte;
+    model->ringbuffer_index++; // Increment index for the next byte.
 
-    // Let's wait 'till the buffer fills.
-    if(model->ringbuffer_index < RING_BUFFER_LENGTH) {
-        return;
-    }
+    // Check whether the ring buffer is filled.
+    if(model->ringbuffer_index >= RING_BUFFER_LENGTH) {
+        model->ringbuffer_index = 0; // Reset the ring buffer index.
+        model->initialized = true; // Set the connection as successfully established.
 
-    // Flush the ringbuffer to the framebuffer.
-    model->ringbuffer_index = 0; // Reset the ringbuffer
-    model->initialized = true; // Established the connection successfully.
-    size_t row_start_index =
-        model->row_ringbuffer[2] * ROW_BUFFER_LENGTH; // Third char will determine the row number
+        // Compute the starting index for the row in the pixel buffer.
+        size_t row_start_index = model->row_identifier * ROW_BUFFER_LENGTH;
 
-    if(row_start_index > LAST_ROW_INDEX) { // Failsafe
-        row_start_index = 0;
-    }
+        // Ensure the row start index is within the valid range.
+        if(row_start_index > LAST_ROW_INDEX) {
+            row_start_index = 0; // Reset to a safe value in case of an overflow.
+        }
 
-    for(size_t i = 0; i < ROW_BUFFER_LENGTH; ++i) {
-        model->pixels[row_start_index + i] =
-            model->row_ringbuffer[i + 3]; // Writing the remaining 16 bytes into the frame buffer
+        // Flush the contents of the ring buffer to the pixel buffer.
+        for(size_t i = 0; i < ROW_BUFFER_LENGTH; ++i) {
+            model->pixels[row_start_index + i] = model->row_ringbuffer[i];
+        }
     }
 }
 
@@ -420,27 +439,43 @@ static int32_t camera_worker(void* context) {
 }
 
 CameraSuiteViewCamera* camera_suite_view_camera_alloc() {
+    // Allocate memory for the instance
     CameraSuiteViewCamera* instance = malloc(sizeof(CameraSuiteViewCamera));
 
+    // Allocate the view object
     instance->view = view_alloc();
 
+    // Allocate a stream buffer
     instance->rx_stream = furi_stream_buffer_alloc(2048, 1);
 
-    // Set up views
+    // Allocate model
     view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(UartDumpModel));
-    view_set_context(instance->view, instance); // furi_assert crashes in events without this
+
+    // Set context
+    view_set_context(instance->view, instance);
+
+    // Set draw callback
     view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_camera_draw);
+
+    // Set input callback
     view_set_input_callback(instance->view, camera_suite_view_camera_input);
+
+    // Set enter callback
     view_set_enter_callback(instance->view, camera_suite_view_camera_enter);
+
+    // Set exit callback
     view_set_exit_callback(instance->view, camera_suite_view_camera_exit);
 
+    // Initialize camera model
     with_view_model(
         instance->view,
         UartDumpModel * model,
         { camera_suite_view_camera_model_init(model); },
         true);
 
-    instance->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 2048, camera_worker, instance);
+    // Allocate a thread for this camera to run on.
+    FuriThread* thread = furi_thread_alloc_ex("UsbUartWorker", 2048, camera_worker, instance);
+    instance->worker_thread = thread;
     furi_thread_start(instance->worker_thread);
 
     // Enable uart listener

+ 22 - 25
src-fap/views/camera_suite_view_camera.h

@@ -1,3 +1,5 @@
+#pragma once
+
 #include "../helpers/camera_suite_custom_event.h"
 #include <furi.h>
 #include <furi_hal.h>
@@ -14,16 +16,15 @@
 #include <storage/filesystem_api_defines.h>
 #include <storage/storage.h>
 
-#pragma once
-
-#define FRAME_WIDTH 128
-#define FRAME_HEIGHT 64
+#define BITMAP_HEADER_LENGTH 62
 #define FRAME_BIT_DEPTH 1
 #define FRAME_BUFFER_LENGTH 1024
-#define ROW_BUFFER_LENGTH 16
-#define RING_BUFFER_LENGTH 19
+#define FRAME_HEIGHT 64
+#define FRAME_WIDTH 128
+#define HEADER_LENGTH 3 // 'Y', ':', and row identifier
 #define LAST_ROW_INDEX 1008
-#define BITMAP_HEADER_LENGTH 62
+#define RING_BUFFER_LENGTH 19
+#define ROW_BUFFER_LENGTH 16
 
 static const unsigned char bitmap_header[BITMAP_HEADER_LENGTH] = {
     0x42, 0x4D, 0x3E, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3E, 0x00, 0x00, 0x00, 0x28, 0x00,
@@ -32,37 +33,33 @@ static const unsigned char bitmap_header[BITMAP_HEADER_LENGTH] = {
     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x00};
 
 extern const Icon I_DolphinCommon_56x48;
+typedef enum {
+    WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
+    WorkerEventStop = (1 << 1),
+    WorkerEventRx = (1 << 2),
+} WorkerEventFlags;
 
-typedef struct UartDumpModel UartDumpModel;
+#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx)
 
-struct UartDumpModel {
+typedef struct UartDumpModel {
     bool initialized;
     int rotation_angle;
     uint8_t pixels[FRAME_BUFFER_LENGTH];
     uint8_t ringbuffer_index;
     uint8_t row_ringbuffer[RING_BUFFER_LENGTH];
-};
+    uint8_t row_identifier;
+} 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);
+View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* camera_suite_static);
 void camera_suite_view_camera_set_callback(
     CameraSuiteViewCamera* camera_suite_view_camera,
     CameraSuiteViewCameraCallback callback,
     void* context);
-
-CameraSuiteViewCamera* camera_suite_view_camera_alloc();
-
-void camera_suite_view_camera_free(CameraSuiteViewCamera* camera_suite_static);
-
-View* camera_suite_view_camera_get_view(CameraSuiteViewCamera* camera_suite_static);
-
-typedef enum {
-    // Reserved for StreamBuffer internal event
-    WorkerEventReserved = (1 << 0),
-    WorkerEventStop = (1 << 1),
-    WorkerEventRx = (1 << 2),
-} WorkerEventFlags;
-
-#define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx)

BIN
src-firmware/esp32_cam_uart_stream/build/esp32.esp32.esp32cam/esp32_cam_uart_stream.ino.bin


BIN
src-firmware/esp32_cam_uart_stream/build/esp32.esp32.esp32cam/esp32_cam_uart_stream.ino.bootloader.bin


BIN
src-firmware/esp32_cam_uart_stream/build/esp32.esp32.esp32cam/esp32_cam_uart_stream.ino.elf


Разница между файлами не показана из-за своего большого размера
+ 44322 - 0
src-firmware/esp32_cam_uart_stream/build/esp32.esp32.esp32cam/esp32_cam_uart_stream.ino.map


BIN
src-firmware/esp32_cam_uart_stream/build/esp32.esp32.esp32cam/esp32_cam_uart_stream.ino.partitions.bin


+ 54 - 54
src-firmware/esp32_cam_uart_stream/esp32_cam_uart_stream.ino

@@ -1,6 +1,6 @@
 #include "esp_camera.h"
 
-// Pin definitions
+// Define Pin numbers used by the camera
 #define FLASH_GPIO_NUM    4
 #define HREF_GPIO_NUM     23
 #define PCLK_GPIO_NUM     22
@@ -20,7 +20,7 @@
 #define Y8_GPIO_NUM       34
 #define Y9_GPIO_NUM       35
 
-// Camera configuration
+// Structure to hold the camera configuration parameters
 camera_config_t config;
 
 // Function prototypes
@@ -28,44 +28,44 @@ void handleSerialInput();
 void initializeCamera();
 void processImage(camera_fb_t* fb);
 void ditherImage(camera_fb_t* fb);
-bool isDarkBit(uint8_t bit);
 
-// Dithering algorithm options
+// Enumeration to represent the available dithering algorithms
 enum DitheringAlgorithm {
   FLOYD_STEINBERG,
   JARVIS_JUDICE_NINKE,
   STUCKI
 };
 
-// Default dithering algorithm
-DitheringAlgorithm ditherAlgorithm = FLOYD_STEINBERG;
-
-// Serial input flags
-bool disableDithering = false;
-bool invert = false;
-bool isFlashOn = false;
-bool rotated = false;
-bool stopStream = false;
+// Variables to hold state and configurations
+DitheringAlgorithm ditherAlgorithm = FLOYD_STEINBERG; // Holds the currently selected dithering algorithm
+bool disableDithering = false; // Flag to enable or disable dithering
+bool invert = false; // Flag to invert pixel colors
+bool isFlashOn = false; // Flag to represent the flash state
+bool rotated = false; // Flag to represent whether the image is rotated
+bool stopStream = false; // Flag to stop or start the stream
 
 void setup() {
+  // Start serial communication at 230400 baud rate
   Serial.begin(230400);
   initializeCamera();
 }
 
 void loop() {
   if (!stopStream) {
-    // Frame buffer capture and processing
-    camera_fb_t* fb = esp_camera_fb_get();
+    // Capture and process the frame buffer if streaming is enabled
+    camera_fb_t* fb = esp_camera_fb_get(); // Get the frame buffer from the camera
     if (fb) {
       processImage(fb);
+      // Return the frame buffer back to the camera driver
       esp_camera_fb_return(fb);
     }
-    delay(50);
+    delay(50); // Delay for 50ms between each frame
   }
 
-  handleSerialInput(); // Process serial input commands
+  handleSerialInput(); // Handle any available serial input commands
 }
 
+// Function to handle the serial input commands and perform the associated actions
 void handleSerialInput() {
   if (Serial.available() > 0) {
     char input = Serial.read();
@@ -129,7 +129,7 @@ void handleSerialInput() {
 }
 
 void initializeCamera() {
-  // Set camera configuration
+  // Set camera configurations
   config.ledc_channel = LEDC_CHANNEL_0;
   config.ledc_timer = LEDC_TIMER_0;
   config.pin_d0 = Y2_GPIO_NUM;
@@ -167,9 +167,9 @@ void initializeCamera() {
     return;
   }
 
-  // Set high contrast to make dithering easier
+  // Set initial contrast.
   sensor_t* s = esp_camera_sensor_get();
-  s->set_contrast(s, 2);
+  s->set_contrast(s, 0);
 
   // Set rotation
   s->set_vflip(s, true);  // Vertical flip
@@ -177,40 +177,49 @@ void initializeCamera() {
 }
 
 void processImage(camera_fb_t* frameBuffer) {
+  // 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 (!disableDithering) {
-    ditherImage(frameBuffer);
+    ditherImage(frameBuffer); // Invokes the dithering process on the frame buffer
   }
 
   uint8_t flipper_y = 0;
-  for (uint8_t y = 28; y < 92; ++y) {
-    // Print the Y coordinate.
-    Serial.print("Y:");
-    Serial.print((char)flipper_y);
 
-    // Print the character.
-    // The y value to use in the frame buffer array.
+  // Iterating over specific rows of the frame buffer.
+  for (uint8_t y = 28; y < 92; ++y) {
+    Serial.print("Y:"); // Print "Y:" for every new row.
+    Serial.write(flipper_y); // Send the row identifier as a byte.
+    
+    // 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 
+    // of the row in the 1D array.
     size_t true_y = y * frameBuffer->width;
-
-    // For each column of 8 pixels in the current row.
-    for (uint8_t x = 16; x < 144; x += 8) {
-      // The current character being constructed.
-      char c = 0;
-
-      // For each pixel in the current column of 8.
-      for (uint8_t j = 0; j < 8; ++j) {
-        if (isDarkBit(frameBuffer->buf[true_y + x + (7 - j)])) {
-          // Shift the bit into the right position
-          c |= (uint8_t)1 << (uint8_t)j;
+    
+    // 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
+        uint8_t packed_pixels = 0;
+        // Packing 8 pixel values into one byte.
+        for(uint8_t bit = 0; bit < 8; ++bit) {
+             // Check the invert flag and pack the pixels accordingly.
+            if(invert) {
+                // If invert is true, consider pixel as 1 if it's less than 127.
+                if(frameBuffer->buf[true_y + x + bit] < 127) {
+                    packed_pixels |= (1 << (7 - bit));
+                }
+            } else {
+                // If invert is false, consider pixel as 1 if it's more than 127.
+                if(frameBuffer->buf[true_y + x + bit] > 127) {
+                    packed_pixels |= (1 << (7 - bit));
+                }
+            }
         }
-      }
-
-      // Output the character.
-      Serial.print(c);
+        Serial.write(packed_pixels); // Sending packed pixel byte.
     }
 
-    // Move to the next line.
-    ++flipper_y;
-    Serial.flush();
+    ++flipper_y; // Move to the next row.
+    Serial.flush(); // Ensure all data in the Serial buffer is sent before moving to the next iteration.
   }
 }
 
@@ -265,12 +274,3 @@ void ditherImage(camera_fb_t* fb) {
     }
   }
 }
-
-// Returns true if the bit is "dark".
-bool isDarkBit(uint8_t bit) {
-  if (invert) {
-    return bit >= 128;
-  } else {
-    return bit < 128;
-  }
-}

Некоторые файлы не были показаны из-за большого количества измененных файлов