esp32_cam_uart_stream.ino 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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 '0': // Use Floyd Steinberg dithering.
  104. ditherAlgorithm = FLOYD_STEINBERG;
  105. break;
  106. case '1': // Use Jarvis Judice dithering.
  107. ditherAlgorithm = JARVIS_JUDICE_NINKE;
  108. break;
  109. case '2': // Use Stucki dithering.
  110. ditherAlgorithm = STUCKI;
  111. break;
  112. default:
  113. // Do nothing.
  114. break;
  115. }
  116. }
  117. }
  118. void initializeCamera() {
  119. // Set camera configuration
  120. config.ledc_channel = LEDC_CHANNEL_0;
  121. config.ledc_timer = LEDC_TIMER_0;
  122. config.pin_d0 = Y2_GPIO_NUM;
  123. config.pin_d1 = Y3_GPIO_NUM;
  124. config.pin_d2 = Y4_GPIO_NUM;
  125. config.pin_d3 = Y5_GPIO_NUM;
  126. config.pin_d4 = Y6_GPIO_NUM;
  127. config.pin_d5 = Y7_GPIO_NUM;
  128. config.pin_d6 = Y8_GPIO_NUM;
  129. config.pin_d7 = Y9_GPIO_NUM;
  130. config.pin_xclk = XCLK_GPIO_NUM;
  131. config.pin_pclk = PCLK_GPIO_NUM;
  132. config.pin_vsync = VSYNC_GPIO_NUM;
  133. config.pin_href = HREF_GPIO_NUM;
  134. config.pin_sscb_sda = SIOD_GPIO_NUM;
  135. config.pin_sscb_scl = SIOC_GPIO_NUM;
  136. config.pin_pwdn = PWDN_GPIO_NUM;
  137. config.pin_reset = RESET_GPIO_NUM;
  138. config.xclk_freq_hz = 20000000;
  139. config.pixel_format = PIXFORMAT_GRAYSCALE;
  140. config.frame_size = FRAMESIZE_QQVGA;
  141. config.fb_count = 1;
  142. if (isFlashOn) {
  143. pinMode(FLASH_GPIO_NUM, OUTPUT);
  144. // Turn off torch.
  145. digitalWrite(FLASH_GPIO_NUM, LOW);
  146. isFlashOn = false;
  147. }
  148. // Initialize camera
  149. esp_err_t err = esp_camera_init(&config);
  150. if (err != ESP_OK) {
  151. Serial.printf("Camera init failed with error 0x%x", err);
  152. return;
  153. }
  154. // Set high contrast to make dithering easier
  155. sensor_t* s = esp_camera_sensor_get();
  156. s->set_contrast(s, 2);
  157. // Set rotation
  158. s->set_vflip(s, true); // Vertical flip
  159. s->set_hmirror(s, true); // Horizontal mirror
  160. }
  161. void processImage(camera_fb_t* frameBuffer) {
  162. if (!disableDithering) {
  163. ditherImage(frameBuffer);
  164. }
  165. uint8_t flipper_y = 0;
  166. for (uint8_t y = 28; y < 92; ++y) {
  167. // Print the Y coordinate.
  168. Serial.print("Y:");
  169. Serial.print((char)flipper_y);
  170. // Print the character.
  171. // The y value to use in the frame buffer array.
  172. size_t true_y = y * frameBuffer->width;
  173. // For each column of 8 pixels in the current row.
  174. for (uint8_t x = 16; x < 144; x += 8) {
  175. // The current character being constructed.
  176. char c = 0;
  177. // For each pixel in the current column of 8.
  178. for (uint8_t j = 0; j < 8; ++j) {
  179. if (isDarkBit(frameBuffer->buf[true_y + x + (7 - j)])) {
  180. // Shift the bit into the right position
  181. c |= (uint8_t)1 << (uint8_t)j;
  182. }
  183. }
  184. // Output the character.
  185. Serial.print(c);
  186. }
  187. // Move to the next line.
  188. ++flipper_y;
  189. Serial.flush();
  190. }
  191. }
  192. void ditherImage(camera_fb_t* fb) {
  193. for (uint8_t y = 0; y < fb->height; ++y) {
  194. for (uint8_t x = 0; x < fb->width; ++x) {
  195. size_t 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. int8_t quant_error = oldpixel - newpixel;
  200. // Apply error diffusion based on the selected algorithm
  201. switch (ditherAlgorithm) {
  202. case JARVIS_JUDICE_NINKE:
  203. fb->buf[(y * fb->width) + x + 1] += quant_error * 7 / 48;
  204. fb->buf[(y * fb->width) + x + 2] += quant_error * 5 / 48;
  205. fb->buf[(y + 1) * fb->width + x - 2] += quant_error * 3 / 48;
  206. fb->buf[(y + 1) * fb->width + x - 1] += quant_error * 5 / 48;
  207. fb->buf[(y + 1) * fb->width + x] += quant_error * 7 / 48;
  208. fb->buf[(y + 1) * fb->width + x + 1] += quant_error * 5 / 48;
  209. fb->buf[(y + 1) * fb->width + x + 2] += quant_error * 3 / 48;
  210. fb->buf[(y + 2) * fb->width + x - 2] += quant_error * 1 / 48;
  211. fb->buf[(y + 2) * fb->width + x - 1] += quant_error * 3 / 48;
  212. fb->buf[(y + 2) * fb->width + x] += quant_error * 5 / 48;
  213. fb->buf[(y + 2) * fb->width + x + 1] += quant_error * 3 / 48;
  214. fb->buf[(y + 2) * fb->width + x + 2] += quant_error * 1 / 48;
  215. break;
  216. case STUCKI:
  217. fb->buf[(y * fb->width) + x + 1] += quant_error * 8 / 42;
  218. fb->buf[(y * fb->width) + x + 2] += quant_error * 4 / 42;
  219. fb->buf[(y + 1) * fb->width + x - 2] += quant_error * 2 / 42;
  220. fb->buf[(y + 1) * fb->width + x - 1] += quant_error * 4 / 42;
  221. fb->buf[(y + 1) * fb->width + x] += quant_error * 8 / 42;
  222. fb->buf[(y + 1) * fb->width + x + 1] += quant_error * 4 / 42;
  223. fb->buf[(y + 1) * fb->width + x + 2] += quant_error * 2 / 42;
  224. fb->buf[(y + 2) * fb->width + x - 2] += quant_error * 1 / 42;
  225. fb->buf[(y + 2) * fb->width + x - 1] += quant_error * 2 / 42;
  226. fb->buf[(y + 2) * fb->width + x] += quant_error * 4 / 42;
  227. fb->buf[(y + 2) * fb->width + x + 1] += quant_error * 2 / 42;
  228. fb->buf[(y + 2) * fb->width + x + 2] += quant_error * 1 / 42;
  229. break;
  230. case FLOYD_STEINBERG:
  231. default:
  232. // Default to Floyd-Steinberg dithering if an invalid algorithm is selected
  233. fb->buf[(y * fb->width) + x + 1] += quant_error * 7 / 16;
  234. fb->buf[(y + 1) * fb->width + x - 1] += quant_error * 3 / 16;
  235. fb->buf[(y + 1) * fb->width + x] += quant_error * 5 / 16;
  236. fb->buf[(y + 1) * fb->width + x + 1] += quant_error * 1 / 16;
  237. break;
  238. }
  239. }
  240. }
  241. }
  242. // Returns true if the bit is "dark".
  243. bool isDarkBit(uint8_t bit) {
  244. if (invert) {
  245. return bit >= 128;
  246. } else {
  247. return bit < 128;
  248. }
  249. }