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

First attempt at screen rotation.

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

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+.vscode

+ 57 - 6
src-fap/views/camera_suite_view_style_1.c

@@ -5,11 +5,14 @@
 #include <gui/elements.h>
 #include <gui/elements.h>
 #include <dolphin/dolphin.h>
 #include <dolphin/dolphin.h>
 
 
+static CameraSuiteViewStyle1* current_instance = NULL;
+
 struct CameraSuiteViewStyle1 {
 struct CameraSuiteViewStyle1 {
-    View* view;
     CameraSuiteViewStyle1Callback callback;
     CameraSuiteViewStyle1Callback callback;
-    FuriThread* worker_thread;
     FuriStreamBuffer* rx_stream;
     FuriStreamBuffer* rx_stream;
+    FuriThread* worker_thread;
+    View* view;
+    int rotation_angle;
     void* context;
     void* context;
 };
 };
 
 
@@ -30,18 +33,58 @@ static void camera_suite_view_style_1_draw(Canvas* canvas, UartDumpModel* model)
     // Draw the frame.
     // Draw the frame.
     canvas_draw_frame(canvas, 0, 0, FRAME_WIDTH, FRAME_HEIGHT);
     canvas_draw_frame(canvas, 0, 0, FRAME_WIDTH, FRAME_HEIGHT);
 
 
-    // Draw the pixels.
+    // Draw the pixels with rotation.
     for(size_t p = 0; p < FRAME_BUFFER_LENGTH; ++p) {
     for(size_t p = 0; p < FRAME_BUFFER_LENGTH; ++p) {
         uint8_t x = p % ROW_BUFFER_LENGTH; // 0 .. 15
         uint8_t x = p % ROW_BUFFER_LENGTH; // 0 .. 15
         uint8_t y = p / ROW_BUFFER_LENGTH; // 0 .. 63
         uint8_t y = p / ROW_BUFFER_LENGTH; // 0 .. 63
 
 
+        // Apply rotation
+        int16_t rotated_x, rotated_y;
+        switch(current_instance->rotation_angle) {
+        case 90:
+            rotated_x = y;
+            rotated_y = FRAME_WIDTH - 1 - x;
+            break;
+        case 180:
+            rotated_x = FRAME_WIDTH - 1 - x;
+            rotated_y = FRAME_HEIGHT - 1 - y;
+            break;
+        case 270:
+            rotated_x = FRAME_HEIGHT - 1 - y;
+            rotated_y = x;
+            break;
+        default:
+            rotated_x = x;
+            rotated_y = y;
+            break;
+        }
+
         for(uint8_t i = 0; i < 8; ++i) {
         for(uint8_t i = 0; i < 8; ++i) {
-            if((model->pixels[p] & (1 << (7 - i))) != 0) {
-                canvas_draw_dot(canvas, (x * 8) + i, y);
+            if((model->pixels[p] & (1 << i)) != 0) {
+                // Adjust the coordinates based on the new screen dimensions
+                uint16_t screen_x, screen_y;
+                switch(current_instance->rotation_angle) {
+                case 90:
+                    screen_x = rotated_x;
+                    screen_y = FRAME_HEIGHT - 8 + (rotated_y * 8) + i;
+                    break;
+                case 180:
+                    screen_x = FRAME_WIDTH - 8 + (rotated_x * 8) + i;
+                    screen_y = FRAME_HEIGHT - 1 - rotated_y;
+                    break;
+                case 270:
+                    screen_x = FRAME_WIDTH - 1 - rotated_x;
+                    screen_y = rotated_y * 8 + i;
+                    break;
+                default:
+                    screen_x = rotated_x * 8 + i;
+                    screen_y = rotated_y;
+                    break;
+                }
+                canvas_draw_dot(canvas, screen_x, screen_y);
             }
             }
         }
         }
     }
     }
-
     // Draw the guide if the camera is not initialized.
     // Draw the guide if the camera is not initialized.
     if(!model->initialized) {
     if(!model->initialized) {
         canvas_draw_icon(canvas, 74, 16, &I_DolphinCommon_56x48);
         canvas_draw_icon(canvas, 74, 16, &I_DolphinCommon_56x48);
@@ -128,6 +171,11 @@ static bool camera_suite_view_style_1_input(InputEvent* event, void* context) {
                 true);
                 true);
             break;
             break;
         case InputKeyOk:
         case InputKeyOk:
+            // Rotate the camera image 90 degrees
+            instance->rotation_angle += 90;
+            if(instance->rotation_angle >= 360) {
+                instance->rotation_angle = 0;
+            }
             with_view_model(
             with_view_model(
                 instance->view,
                 instance->view,
                 UartDumpModel * model,
                 UartDumpModel * model,
@@ -157,6 +205,9 @@ static void camera_suite_view_style_1_enter(void* context) {
     // Cast `context` to `CameraSuiteViewStyle1*` and store it in `instance`.
     // Cast `context` to `CameraSuiteViewStyle1*` and store it in `instance`.
     CameraSuiteViewStyle1* instance = (CameraSuiteViewStyle1*)context;
     CameraSuiteViewStyle1* instance = (CameraSuiteViewStyle1*)context;
 
 
+    // Assign the current instance to the global variable
+    current_instance = instance;
+
     with_view_model(
     with_view_model(
         instance->view,
         instance->view,
         UartDumpModel * model,
         UartDumpModel * model,

+ 3 - 4
src-fap/views/camera_suite_view_style_1.h

@@ -53,12 +53,11 @@
 typedef struct UartDumpModel UartDumpModel;
 typedef struct UartDumpModel UartDumpModel;
 
 
 struct UartDumpModel {
 struct UartDumpModel {
-    uint8_t pixels[FRAME_BUFFER_LENGTH];
-
     bool initialized;
     bool initialized;
-
-    uint8_t row_ringbuffer[RING_BUFFER_LENGTH];
+    int rotation_angle;
+    uint8_t pixels[FRAME_BUFFER_LENGTH];
     uint8_t ringbuffer_index;
     uint8_t ringbuffer_index;
+    uint8_t row_ringbuffer[RING_BUFFER_LENGTH];
 };
 };
 
 
 typedef struct CameraSuiteViewStyle1 CameraSuiteViewStyle1;
 typedef struct CameraSuiteViewStyle1 CameraSuiteViewStyle1;

+ 0 - 183
src-firmware/esp32_cam_uart_stream.ino

@@ -1,183 +0,0 @@
-#include "esp_camera.h"
-
-#define PWDN_GPIO_NUM     32
-#define RESET_GPIO_NUM    -1
-#define XCLK_GPIO_NUM      0
-#define SIOD_GPIO_NUM     26
-#define SIOC_GPIO_NUM     27
-
-#define Y9_GPIO_NUM       35
-#define Y8_GPIO_NUM       34
-#define Y7_GPIO_NUM       39
-#define Y6_GPIO_NUM       36
-#define Y5_GPIO_NUM       21
-#define Y4_GPIO_NUM       19
-#define Y3_GPIO_NUM       18
-#define Y2_GPIO_NUM        5
-#define VSYNC_GPIO_NUM    25
-#define HREF_GPIO_NUM     23
-#define PCLK_GPIO_NUM     22
-
-
-void setup() {
-  Serial.begin(230400);
-
-  camera_config_t config;
-  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;
-
-  // We don't need a big frame
-  config.frame_size = FRAMESIZE_QQVGA;
-  config.fb_count = 1;
-
-  // camera init
-  esp_err_t err = esp_camera_init(&config);
-  if (err != ESP_OK) {
-    Serial.printf("Camera init failed with error 0x%x", err);
-    return;
-  }
-
-  // Setting high contrast to make easier to dither
-  sensor_t * s = esp_camera_sensor_get();
-  s->set_contrast(s, 2);
-}
-
-bool stop_stream = false;
-bool disable_dithering = false;
-bool invert = false;
-
-void loop() {
-
-  // Reading serial
-  if (Serial.available() > 0) {
-    char r = Serial.read();
-    sensor_t * s = esp_camera_sensor_get();
-    
-    switch(r) {
-      case 'S':
-        stop_stream = false;
-        break;
-      case 's':
-        stop_stream = true;
-        break;
-      case 'D':
-        disable_dithering = false;
-        break;
-      case 'd':
-        disable_dithering = true;
-        break;
-      case 'C':
-        s->set_contrast(s, s->status.contrast + 1);
-        break;
-      case 'c':
-        s->set_contrast(s, s->status.contrast - 1);
-        break;
-      case 'B':
-        s->set_contrast(s, s->status.brightness + 1);
-        break;
-      case 'b':
-        s->set_contrast(s, s->status.brightness - 1);
-        break;
-
-      // Toggle cases
-      case 'M': // Toggle Mirror
-        s->set_hmirror(s, !s->status.hmirror);
-        break;
-      case '>':
-        disable_dithering = !disable_dithering;
-        break;
-      case '<':
-        invert = !invert;
-      default:
-        break;
-    }
-  }
-
-  if (stop_stream){
-    return;
-  }
-
-  camera_fb_t* fb = esp_camera_fb_get();
-
-  if (!fb) {
-    return;
-  }
-  
-  //Length: 19200
-  //Width: 160
-  //Height: 120
-  //Format: 2
-  //Target: 128x64
-
-  if (!disable_dithering) {
-    DitherImage(fb);
-  }
-
-  uint8_t flipper_y = 0;
-  for(uint8_t y = 28; y < 92; ++y) {
-    Serial.print("Y:");
-    Serial.print((char)flipper_y);
-
-    size_t true_y = y * fb->width;
-    for (uint8_t x = 16; x < 144; x+=8){
-      char c = 0;
-      for(uint8_t j = 0; j < 8; ++j){
-        if (IsDarkBit(fb->buf[true_y + x + (7-j)])){
-          c |= 1 << j;
-        }
-      }
-      Serial.print(c);
-    }
-
-    ++flipper_y;
-    Serial.flush();
-  }
-
-  esp_camera_fb_return(fb);
-  fb = NULL;
-  delay(50);
-}
-
-bool IsDarkBit(uint8_t bit){
-  bool result = bit < 128;
-
-  if (invert){
-    result = !result;
-  }
-
-  return result;
-}
-
-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];
-        uint8_t newpixel = oldpixel >= 128 ? 255 : 0;
-        fb->buf[current] = newpixel;
-        uint8_t quant_error = oldpixel - newpixel;
-        fb->buf[(y*fb->width) + x + 1] = fb->buf[(y*fb->width) + x + 1] + quant_error * 7 / 16;
-        fb->buf[(y+1*fb->width) + x-1] = fb->buf[(y+1*fb->width) + x-1] + quant_error * 3 / 16;
-        fb->buf[(y + 1*fb->width) + x] = fb->buf[(y + 1*fb->width) + x] + quant_error * 5 / 16;
-        fb->buf[(y+1*fb->width) + x+1] = fb->buf[(y+1*fb->width) + x+1] + quant_error * 1 / 16;
-      }
-    }  
-}

+ 215 - 0
src-firmware/esp32_cam_uart_stream/esp32_cam_uart_stream.ino

@@ -0,0 +1,215 @@
+#include "esp_camera.h"
+
+// Pin definitions
+#define PWDN_GPIO_NUM     32
+#define RESET_GPIO_NUM    -1
+#define XCLK_GPIO_NUM      0
+#define SIOD_GPIO_NUM     26
+#define SIOC_GPIO_NUM     27
+
+#define Y9_GPIO_NUM       35
+#define Y8_GPIO_NUM       34
+#define Y7_GPIO_NUM       39
+#define Y6_GPIO_NUM       36
+#define Y5_GPIO_NUM       21
+#define Y4_GPIO_NUM       19
+#define Y3_GPIO_NUM       18
+#define Y2_GPIO_NUM        5
+#define VSYNC_GPIO_NUM    25
+#define HREF_GPIO_NUM     23
+#define PCLK_GPIO_NUM     22
+
+// Camera configuration
+camera_config_t config;
+
+// Function prototypes
+void handleSerialInput();
+void initializeCamera();
+void processImage(camera_fb_t* fb);
+void ditherImage(camera_fb_t* fb);
+bool isDarkBit(uint8_t bit);
+
+// Serial input flags
+bool disableDithering = false;
+bool invert = false;
+bool rotated = false;
+bool stopStream = false;
+
+void setup() {
+  Serial.begin(230400);
+
+  initializeCamera();
+}
+
+void loop() {
+  handleSerialInput();
+
+  if (stopStream) {
+    return;
+  }
+
+  camera_fb_t* fb = esp_camera_fb_get();
+
+  if (!fb) {
+    return;
+  }
+
+  processImage(fb);
+
+  esp_camera_fb_return(fb);
+  fb = NULL;
+  delay(50);
+}
+
+void handleSerialInput() {
+  if (Serial.available() > 0) {
+    char input = Serial.read();
+    sensor_t* cameraSensor = esp_camera_sensor_get();
+
+    switch (input) {
+      case '>': // Toggle dithering.
+        disableDithering = !disableDithering;
+        break;
+      case '<': // Toggle invert.
+        invert = !invert;
+        break;
+      case 'B': // Add brightness.
+        cameraSensor->set_contrast(cameraSensor, cameraSensor->status.brightness + 1);
+        break;
+      case 'b': // Remove brightness.
+        cameraSensor->set_contrast(cameraSensor, cameraSensor->status.brightness - 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);
+        break;
+      case 'D': // Enable dithering.
+        disableDithering = false;
+        break;
+      case 'd': // Disable dithering.
+        disableDithering = true;
+        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;
+      default:
+        break;
+    }
+  }
+}
+
+void initializeCamera() {
+  // Set camera configuration
+  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);
+  if (err != ESP_OK) {
+    Serial.printf("Camera init failed with error 0x%x", err);
+    return;
+  }
+
+  // Set high contrast to make dithering easier
+  sensor_t* s = esp_camera_sensor_get();
+  s->set_contrast(s, 2);
+
+  // Set rotation (added lines)
+  s->set_vflip(s, true);  // Vertical flip
+  s->set_hmirror(s, true);  // Horizontal mirror
+}
+
+void processImage(camera_fb_t* frameBuffer) {
+  if (!disableDithering) {
+    ditherImage(frameBuffer);
+  }
+
+  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.
+    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;
+        }
+      }
+
+      // Output the character.
+      Serial.print(c);
+    }
+
+    // Move to the next line.
+    ++flipper_y;
+    Serial.flush();
+  }
+}
+
+// Dither image.
+void ditherImage(camera_fb_t* fb) {
+  for (int y = 0; y < fb->height - 1; ++y) {
+    for (int x = 1; x < fb->width - 1; ++x) {
+      int current = y * fb->width + x;
+      // Convert to black or white
+      uint8_t oldpixel = fb->buf[current];
+      uint8_t newpixel = oldpixel >= 128 ? 255 : 0;
+      fb->buf[current] = newpixel;
+      // Compute quantization error
+      int quant_error = oldpixel - newpixel;
+      // Propagate the error
+      fb->buf[current + 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 / 16;
+    }
+  }
+}
+
+// Returns true if the bit is "dark".
+bool isDarkBit(uint8_t bit) {
+  if (invert) {
+    return bit >= 128;
+  } else {
+    return bit < 128;
+  }
+}