esp32_cam_uart_stream.ino 8.1 KB

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