esp32_cam_uart_stream.ino 6.8 KB

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