esp32_cam_uart_stream.ino 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. #include "esp_camera.h"
  2. // Pin definitions
  3. #define PWDN_GPIO_NUM 32
  4. #define RESET_GPIO_NUM -1
  5. #define XCLK_GPIO_NUM 0
  6. #define SIOD_GPIO_NUM 26
  7. #define SIOC_GPIO_NUM 27
  8. #define Y9_GPIO_NUM 35
  9. #define Y8_GPIO_NUM 34
  10. #define Y7_GPIO_NUM 39
  11. #define Y6_GPIO_NUM 36
  12. #define Y5_GPIO_NUM 21
  13. #define Y4_GPIO_NUM 19
  14. #define Y3_GPIO_NUM 18
  15. #define Y2_GPIO_NUM 5
  16. #define VSYNC_GPIO_NUM 25
  17. #define HREF_GPIO_NUM 23
  18. #define PCLK_GPIO_NUM 22
  19. // Camera configuration
  20. camera_config_t config;
  21. // Function prototypes
  22. void handleSerialInput();
  23. void initializeCamera();
  24. void processImage(camera_fb_t* fb);
  25. void ditherImage(camera_fb_t* fb);
  26. bool isDarkBit(uint8_t bit);
  27. // Dithering algorithm options
  28. enum DitheringAlgorithm {
  29. FLOYD_STEINBERG,
  30. JARVIS_JUDICE_NINKE,
  31. STUCKI
  32. };
  33. // Default dithering algorithm
  34. DitheringAlgorithm ditherAlgorithm = FLOYD_STEINBERG;
  35. // Serial input flags
  36. bool disableDithering = false;
  37. bool invert = false;
  38. bool rotated = false;
  39. bool stopStream = false;
  40. void setup() {
  41. Serial.begin(230400);
  42. initializeCamera();
  43. }
  44. void loop() {
  45. if (!stopStream) {
  46. // Frame buffer capture and processing
  47. camera_fb_t* fb = esp_camera_fb_get();
  48. if (fb) {
  49. processImage(fb);
  50. esp_camera_fb_return(fb);
  51. }
  52. delay(50);
  53. }
  54. handleSerialInput(); // Process serial input commands
  55. }
  56. void handleSerialInput() {
  57. if (Serial.available() > 0) {
  58. char input = Serial.read();
  59. sensor_t* cameraSensor = esp_camera_sensor_get();
  60. switch (input) {
  61. case '>': // Toggle dithering
  62. disableDithering = !disableDithering;
  63. break;
  64. case '<': // Toggle invert
  65. invert = !invert;
  66. break;
  67. case 'B': // Add brightness
  68. cameraSensor->set_contrast(cameraSensor, cameraSensor->status.brightness + 1);
  69. break;
  70. case 'b': // Remove brightness
  71. cameraSensor->set_contrast(cameraSensor, cameraSensor->status.brightness - 1);
  72. break;
  73. case 'C': // Add contrast
  74. cameraSensor->set_contrast(cameraSensor, cameraSensor->status.contrast + 1);
  75. break;
  76. case 'c': // Remove contrast
  77. cameraSensor->set_contrast(cameraSensor, cameraSensor->status.contrast - 1);
  78. break;
  79. case 'P': // TODO: Take a picture
  80. break;
  81. case 'M': // Toggle Mirror
  82. cameraSensor->set_hmirror(cameraSensor, !cameraSensor->status.hmirror);
  83. break;
  84. case 'S': // Start stream
  85. stopStream = false;
  86. break;
  87. case 's': // Stop stream
  88. stopStream = true;
  89. break;
  90. case 'D': // Change dithering algorithm.
  91. ditherAlgorithm = static_cast<DitheringAlgorithm>((ditherAlgorithm + 1) % 3);
  92. break;
  93. }
  94. }
  95. }
  96. void initializeCamera() {
  97. // Set camera configuration
  98. config.ledc_channel = LEDC_CHANNEL_0;
  99. config.ledc_timer = LEDC_TIMER_0;
  100. config.pin_d0 = Y2_GPIO_NUM;
  101. config.pin_d1 = Y3_GPIO_NUM;
  102. config.pin_d2 = Y4_GPIO_NUM;
  103. config.pin_d3 = Y5_GPIO_NUM;
  104. config.pin_d4 = Y6_GPIO_NUM;
  105. config.pin_d5 = Y7_GPIO_NUM;
  106. config.pin_d6 = Y8_GPIO_NUM;
  107. config.pin_d7 = Y9_GPIO_NUM;
  108. config.pin_xclk = XCLK_GPIO_NUM;
  109. config.pin_pclk = PCLK_GPIO_NUM;
  110. config.pin_vsync = VSYNC_GPIO_NUM;
  111. config.pin_href = HREF_GPIO_NUM;
  112. config.pin_sscb_sda = SIOD_GPIO_NUM;
  113. config.pin_sscb_scl = SIOC_GPIO_NUM;
  114. config.pin_pwdn = PWDN_GPIO_NUM;
  115. config.pin_reset = RESET_GPIO_NUM;
  116. config.xclk_freq_hz = 20000000;
  117. config.pixel_format = PIXFORMAT_GRAYSCALE;
  118. config.frame_size = FRAMESIZE_QQVGA;
  119. config.fb_count = 1;
  120. // Initialize camera
  121. esp_err_t err = esp_camera_init(&config);
  122. if (err != ESP_OK) {
  123. Serial.printf("Camera init failed with error 0x%x", err);
  124. return;
  125. }
  126. // Set high contrast to make dithering easier
  127. sensor_t* s = esp_camera_sensor_get();
  128. s->set_contrast(s, 2);
  129. // Set rotation
  130. s->set_vflip(s, true); // Vertical flip
  131. s->set_hmirror(s, true); // Horizontal mirror
  132. }
  133. void processImage(camera_fb_t* frameBuffer) {
  134. if (!disableDithering) {
  135. ditherImage(frameBuffer);
  136. }
  137. uint8_t flipper_y = 0;
  138. for (uint8_t y = 28; y < 92; ++y) {
  139. // Print the Y coordinate.
  140. Serial.print("Y:");
  141. Serial.print((char)flipper_y);
  142. // Print the character.
  143. // The y value to use in the frame buffer array.
  144. size_t true_y = y * frameBuffer->width;
  145. // For each column of 8 pixels in the current row.
  146. for (uint8_t x = 16; x < 144; x += 8) {
  147. // The current character being constructed.
  148. char c = 0;
  149. // For each pixel in the current column of 8.
  150. for (uint8_t j = 0; j < 8; ++j) {
  151. if (isDarkBit(frameBuffer->buf[true_y + x + (7 - j)])) {
  152. // Shift the bit into the right position
  153. c |= (uint8_t)1 << (uint8_t)j;
  154. }
  155. }
  156. // Output the character.
  157. Serial.print(c);
  158. }
  159. // Move to the next line.
  160. ++flipper_y;
  161. Serial.flush();
  162. }
  163. }
  164. void ditherImage(camera_fb_t* fb) {
  165. for (uint8_t y = 0; y < fb->height; ++y) {
  166. for (uint8_t x = 0; x < fb->width; ++x) {
  167. size_t current = (y * fb->width) + x;
  168. uint8_t oldpixel = fb->buf[current];
  169. uint8_t newpixel = oldpixel >= 128 ? 255 : 0;
  170. fb->buf[current] = newpixel;
  171. int8_t quant_error = oldpixel - newpixel;
  172. // Apply error diffusion based on the selected algorithm
  173. switch (ditherAlgorithm) {
  174. case JARVIS_JUDICE_NINKE:
  175. fb->buf[(y * fb->width) + x + 1] += quant_error * 7 / 48;
  176. fb->buf[(y * fb->width) + x + 2] += quant_error * 5 / 48;
  177. fb->buf[(y + 1) * fb->width + x - 2] += quant_error * 3 / 48;
  178. fb->buf[(y + 1) * fb->width + x - 1] += quant_error * 5 / 48;
  179. fb->buf[(y + 1) * fb->width + x] += quant_error * 7 / 48;
  180. fb->buf[(y + 1) * fb->width + x + 1] += quant_error * 5 / 48;
  181. fb->buf[(y + 1) * fb->width + x + 2] += quant_error * 3 / 48;
  182. fb->buf[(y + 2) * fb->width + x - 2] += quant_error * 1 / 48;
  183. fb->buf[(y + 2) * fb->width + x - 1] += quant_error * 3 / 48;
  184. fb->buf[(y + 2) * fb->width + x] += quant_error * 5 / 48;
  185. fb->buf[(y + 2) * fb->width + x + 1] += quant_error * 3 / 48;
  186. fb->buf[(y + 2) * fb->width + x + 2] += quant_error * 1 / 48;
  187. break;
  188. case STUCKI:
  189. fb->buf[(y * fb->width) + x + 1] += quant_error * 8 / 42;
  190. fb->buf[(y * fb->width) + x + 2] += quant_error * 4 / 42;
  191. fb->buf[(y + 1) * fb->width + x - 2] += quant_error * 2 / 42;
  192. fb->buf[(y + 1) * fb->width + x - 1] += quant_error * 4 / 42;
  193. fb->buf[(y + 1) * fb->width + x] += quant_error * 8 / 42;
  194. fb->buf[(y + 1) * fb->width + x + 1] += quant_error * 4 / 42;
  195. fb->buf[(y + 1) * fb->width + x + 2] += quant_error * 2 / 42;
  196. fb->buf[(y + 2) * fb->width + x - 2] += quant_error * 1 / 42;
  197. fb->buf[(y + 2) * fb->width + x - 1] += quant_error * 2 / 42;
  198. fb->buf[(y + 2) * fb->width + x] += quant_error * 4 / 42;
  199. fb->buf[(y + 2) * fb->width + x + 1] += quant_error * 2 / 42;
  200. fb->buf[(y + 2) * fb->width + x + 2] += quant_error * 1 / 42;
  201. break;
  202. case FLOYD_STEINBERG:
  203. default:
  204. // Default to Floyd-Steinberg dithering if an invalid algorithm is selected
  205. fb->buf[(y * fb->width) + x + 1] += quant_error * 7 / 16;
  206. fb->buf[(y + 1) * fb->width + x - 1] += quant_error * 3 / 16;
  207. fb->buf[(y + 1) * fb->width + x] += quant_error * 5 / 16;
  208. fb->buf[(y + 1) * fb->width + x + 1] += quant_error * 1 / 16;
  209. break;
  210. }
  211. }
  212. }
  213. }
  214. // Returns true if the bit is "dark".
  215. bool isDarkBit(uint8_t bit) {
  216. if (invert) {
  217. return bit >= 128;
  218. } else {
  219. return bit < 128;
  220. }
  221. }