firmware.ino 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. #include "FS.h"
  2. #include "esp_camera.h"
  3. #include "SD_MMC.h"
  4. // Define Pin numbers used by the camera.
  5. #define FLASH_GPIO_NUM 4
  6. #define HREF_GPIO_NUM 23
  7. #define PCLK_GPIO_NUM 22
  8. #define PWDN_GPIO_NUM 32
  9. #define RESET_GPIO_NUM -1
  10. #define SIOC_GPIO_NUM 27
  11. #define SIOD_GPIO_NUM 26
  12. #define XCLK_GPIO_NUM 0
  13. #define VSYNC_GPIO_NUM 25
  14. #define Y2_GPIO_NUM 5
  15. #define Y3_GPIO_NUM 18
  16. #define Y4_GPIO_NUM 19
  17. #define Y5_GPIO_NUM 21
  18. #define Y6_GPIO_NUM 36
  19. #define Y7_GPIO_NUM 39
  20. #define Y8_GPIO_NUM 34
  21. #define Y9_GPIO_NUM 35
  22. // Structure to hold the camera configuration parameters.
  23. camera_config_t config;
  24. // Function prototypes.
  25. void handleSerialInput();
  26. void initializeCamera();
  27. void processImage(camera_fb_t *frame_buffer);
  28. void ditherImage(camera_fb_t *frame_buffer);
  29. void saveJpegPictureToSDCard();
  30. // Enumeration to represent the available dithering algorithms.
  31. enum DitheringAlgorithm : uint8_t
  32. {
  33. FLOYD_STEINBERG,
  34. JARVIS_JUDICE_NINKE,
  35. STUCKI
  36. };
  37. // Holds the currently selected dithering algorithm.
  38. DitheringAlgorithm ditherAlgorithm = FLOYD_STEINBERG;
  39. // Flag to enable or disable dithering.
  40. bool disableDithering = false;
  41. // Flag to invert pixel colors.
  42. bool invert = false;
  43. // Flag to represent the flash state when saving pictures to the Flipper.
  44. bool isFlashEnabled = false;
  45. // Flag to represent whether the image is rotated.
  46. bool rotated = false;
  47. // Flag to stop or start the stream.
  48. bool stopStream = false;
  49. // Flag to store jpeg images to sd card.
  50. bool storeJpeg = false;
  51. void setup()
  52. {
  53. Serial.begin(230400); // 115200
  54. initializeCamera();
  55. }
  56. void loop()
  57. {
  58. if (!stopStream)
  59. {
  60. camera_fb_t *frame_buffer = esp_camera_fb_get();
  61. if (frame_buffer)
  62. {
  63. // Process and Send Grayscale image.
  64. processImage(frame_buffer);
  65. // Return the frame buffer back to the camera driver.
  66. esp_camera_fb_return(frame_buffer);
  67. }
  68. delay(50);
  69. }
  70. // Handle any available serial input commands.
  71. handleSerialInput();
  72. }
  73. void handleSerialInput()
  74. {
  75. if (Serial.available() > 0)
  76. {
  77. char input = Serial.read();
  78. sensor_t *cameraSensor = esp_camera_sensor_get();
  79. switch (input)
  80. {
  81. case '>': // Toggle dithering.
  82. disableDithering = !disableDithering;
  83. break;
  84. case '<': // Toggle invert.
  85. invert = !invert;
  86. break;
  87. case 'B': // Add brightness.
  88. cameraSensor->set_contrast(
  89. cameraSensor,
  90. cameraSensor->status.brightness + 1);
  91. break;
  92. case 'b': // Remove brightness.
  93. cameraSensor->set_contrast(
  94. cameraSensor,
  95. cameraSensor->status.brightness - 1);
  96. break;
  97. case 'C': // Add contrast.
  98. cameraSensor->set_contrast(
  99. cameraSensor,
  100. cameraSensor->status.contrast + 1);
  101. break;
  102. case 'c': // Remove contrast.
  103. cameraSensor->set_contrast(
  104. cameraSensor,
  105. cameraSensor->status.contrast - 1);
  106. break;
  107. case 'j': // Toggle store jpeg to sd card off.
  108. storeJpeg = false;
  109. break;
  110. case 'J': // Toggle store jpeg to sd card on.
  111. storeJpeg = true;
  112. break;
  113. case 'P': // Picture sequence.
  114. // Stop the IO stream before taking a picture.
  115. stopStream = true;
  116. if (isFlashEnabled)
  117. {
  118. // Turn on torch.
  119. pinMode(FLASH_GPIO_NUM, OUTPUT);
  120. digitalWrite(FLASH_GPIO_NUM, HIGH);
  121. }
  122. // @todo - Future feature.
  123. // if (storeJpeg) { saveJpegPictureToSDCard(); }
  124. // Give some time (100ms) for Flipper to save locally with flash on.
  125. delay(100);
  126. // Turn off torch.
  127. pinMode(FLASH_GPIO_NUM, OUTPUT);
  128. digitalWrite(FLASH_GPIO_NUM, LOW);
  129. // Restart the stream after the picture is taken.
  130. stopStream = false;
  131. break;
  132. case 'M': // Toggle Mirror.
  133. cameraSensor->set_hmirror(cameraSensor, !cameraSensor->status.hmirror);
  134. break;
  135. case 'S': // Start stream.
  136. stopStream = false;
  137. break;
  138. case 's': // Stop stream.
  139. stopStream = true;
  140. break;
  141. case '0': // Use Floyd Steinberg dithering.
  142. ditherAlgorithm = FLOYD_STEINBERG;
  143. break;
  144. case '1': // Use Jarvis Judice dithering.
  145. ditherAlgorithm = JARVIS_JUDICE_NINKE;
  146. break;
  147. case '2': // Use Stucki dithering.
  148. ditherAlgorithm = STUCKI;
  149. break;
  150. default:
  151. // Do nothing.
  152. break;
  153. }
  154. }
  155. }
  156. void initializeCamera()
  157. {
  158. // Set initial camera configurations for grayscale.
  159. config.ledc_channel = LEDC_CHANNEL_0;
  160. config.ledc_timer = LEDC_TIMER_0;
  161. config.pin_d0 = Y2_GPIO_NUM;
  162. config.pin_d1 = Y3_GPIO_NUM;
  163. config.pin_d2 = Y4_GPIO_NUM;
  164. config.pin_d3 = Y5_GPIO_NUM;
  165. config.pin_d4 = Y6_GPIO_NUM;
  166. config.pin_d5 = Y7_GPIO_NUM;
  167. config.pin_d6 = Y8_GPIO_NUM;
  168. config.pin_d7 = Y9_GPIO_NUM;
  169. config.pin_xclk = XCLK_GPIO_NUM;
  170. config.pin_pclk = PCLK_GPIO_NUM;
  171. config.pin_vsync = VSYNC_GPIO_NUM;
  172. config.pin_href = HREF_GPIO_NUM;
  173. config.pin_sscb_sda = SIOD_GPIO_NUM;
  174. config.pin_sscb_scl = SIOC_GPIO_NUM;
  175. config.pin_pwdn = PWDN_GPIO_NUM;
  176. config.pin_reset = RESET_GPIO_NUM;
  177. config.xclk_freq_hz = 20000000;
  178. config.pixel_format = PIXFORMAT_GRAYSCALE;
  179. config.frame_size = FRAMESIZE_QQVGA;
  180. config.fb_count = 1;
  181. // Initialize camera.
  182. esp_err_t err = esp_camera_init(&config);
  183. if (err != ESP_OK)
  184. {
  185. return;
  186. }
  187. // Make sure torch starts as off.
  188. pinMode(FLASH_GPIO_NUM, OUTPUT);
  189. digitalWrite(FLASH_GPIO_NUM, LOW);
  190. // Set initial contrast.
  191. sensor_t *s = esp_camera_sensor_get();
  192. s->set_contrast(s, 0);
  193. // Set rotation.
  194. s->set_vflip(s, true); // Vertical flip.
  195. s->set_hmirror(s, true); // Horizontal mirror.
  196. }
  197. void processImage(camera_fb_t *frame_buffer)
  198. {
  199. // If dithering is not disabled, perform dithering on the image. Dithering is the
  200. // process of approximating the look of a high-resolution grayscale image in a
  201. // lower resolution by binary values (black & white), thereby representing
  202. // different shades of gray.
  203. if (!disableDithering)
  204. {
  205. ditherImage(frame_buffer); // Invokes the dithering process on the frame buffer.
  206. }
  207. uint8_t flipper_y = 0;
  208. // Iterating over specific rows of the frame buffer.
  209. for (uint8_t y = 28; y < 92; ++y)
  210. {
  211. Serial.print("Y:"); // Print "Y:" for every new row.
  212. Serial.write(flipper_y); // Send the row identifier as a byte.
  213. // Calculate the actual y index in the frame buffer 1D array by multiplying the
  214. // y value with the width of the frame buffer. This gives the starting index of
  215. // the row in the 1D array.
  216. size_t true_y = y * frame_buffer->width;
  217. // Iterating over specific columns of each row in the frame buffer.
  218. for (uint8_t x = 16; x < 144; x += 8)
  219. { // step by 8 as we're packing 8 pixels per byte.
  220. uint8_t packed_pixels = 0;
  221. // Packing 8 pixel values into one byte.
  222. for (uint8_t bit = 0; bit < 8; ++bit)
  223. {
  224. // Check the invert flag and pack the pixels accordingly.
  225. if (invert)
  226. {
  227. // If invert is true, consider pixel as 1 if it's more than 127.
  228. if (frame_buffer->buf[true_y + x + bit] > 127)
  229. {
  230. packed_pixels |= (1 << (7 - bit));
  231. }
  232. }
  233. else
  234. {
  235. // If invert is false, consider pixel as 1 if it's less than 127.
  236. if (frame_buffer->buf[true_y + x + bit] < 127)
  237. {
  238. packed_pixels |= (1 << (7 - bit));
  239. }
  240. }
  241. }
  242. Serial.write(packed_pixels); // Sending packed pixel byte.
  243. }
  244. ++flipper_y; // Move to the next row.
  245. Serial.flush(); // Ensure all data in the Serial buffer is sent before moving to the next iteration.
  246. }
  247. }
  248. void ditherImage(camera_fb_t *frame_buffer)
  249. {
  250. for (uint8_t y = 0; y < frame_buffer->height; ++y)
  251. {
  252. for (uint8_t x = 0; x < frame_buffer->width; ++x)
  253. {
  254. size_t current = (y * frame_buffer->width) + x;
  255. uint8_t oldpixel = frame_buffer->buf[current];
  256. uint8_t newpixel = oldpixel >= 128 ? 255 : 0;
  257. frame_buffer->buf[current] = newpixel;
  258. int8_t quant_error = oldpixel - newpixel;
  259. // Apply error diffusion based on the selected algorithm
  260. switch (ditherAlgorithm)
  261. {
  262. case JARVIS_JUDICE_NINKE:
  263. frame_buffer->buf[(y * frame_buffer->width) + x + 1] += quant_error * 7 / 48;
  264. frame_buffer->buf[(y * frame_buffer->width) + x + 2] += quant_error * 5 / 48;
  265. frame_buffer->buf[(y + 1) * frame_buffer->width + x - 2] += quant_error * 3 / 48;
  266. frame_buffer->buf[(y + 1) * frame_buffer->width + x - 1] += quant_error * 5 / 48;
  267. frame_buffer->buf[(y + 1) * frame_buffer->width + x] += quant_error * 7 / 48;
  268. frame_buffer->buf[(y + 1) * frame_buffer->width + x + 1] += quant_error * 5 / 48;
  269. frame_buffer->buf[(y + 1) * frame_buffer->width + x + 2] += quant_error * 3 / 48;
  270. frame_buffer->buf[(y + 2) * frame_buffer->width + x - 2] += quant_error * 1 / 48;
  271. frame_buffer->buf[(y + 2) * frame_buffer->width + x - 1] += quant_error * 3 / 48;
  272. frame_buffer->buf[(y + 2) * frame_buffer->width + x] += quant_error * 5 / 48;
  273. frame_buffer->buf[(y + 2) * frame_buffer->width + x + 1] += quant_error * 3 / 48;
  274. frame_buffer->buf[(y + 2) * frame_buffer->width + x + 2] += quant_error * 1 / 48;
  275. break;
  276. case STUCKI:
  277. frame_buffer->buf[(y * frame_buffer->width) + x + 1] += quant_error * 8 / 42;
  278. frame_buffer->buf[(y * frame_buffer->width) + x + 2] += quant_error * 4 / 42;
  279. frame_buffer->buf[(y + 1) * frame_buffer->width + x - 2] += quant_error * 2 / 42;
  280. frame_buffer->buf[(y + 1) * frame_buffer->width + x - 1] += quant_error * 4 / 42;
  281. frame_buffer->buf[(y + 1) * frame_buffer->width + x] += quant_error * 8 / 42;
  282. frame_buffer->buf[(y + 1) * frame_buffer->width + x + 1] += quant_error * 4 / 42;
  283. frame_buffer->buf[(y + 1) * frame_buffer->width + x + 2] += quant_error * 2 / 42;
  284. frame_buffer->buf[(y + 2) * frame_buffer->width + x - 2] += quant_error * 1 / 42;
  285. frame_buffer->buf[(y + 2) * frame_buffer->width + x - 1] += quant_error * 2 / 42;
  286. frame_buffer->buf[(y + 2) * frame_buffer->width + x] += quant_error * 4 / 42;
  287. frame_buffer->buf[(y + 2) * frame_buffer->width + x + 1] += quant_error * 2 / 42;
  288. frame_buffer->buf[(y + 2) * frame_buffer->width + x + 2] += quant_error * 1 / 42;
  289. break;
  290. case FLOYD_STEINBERG:
  291. default:
  292. // Default to Floyd-Steinberg dithering if an invalid algorithm is selected
  293. frame_buffer->buf[(y * frame_buffer->width) + x + 1] += quant_error * 7 / 16;
  294. frame_buffer->buf[(y + 1) * frame_buffer->width + x - 1] += quant_error * 3 / 16;
  295. frame_buffer->buf[(y + 1) * frame_buffer->width + x] += quant_error * 5 / 16;
  296. frame_buffer->buf[(y + 1) * frame_buffer->width + x + 1] += quant_error * 1 / 16;
  297. break;
  298. }
  299. }
  300. }
  301. }
  302. // @todo - Future feature.
  303. void saveJpegPictureToSDCard()
  304. {
  305. // Get camera sensor.
  306. sensor_t *s = esp_camera_sensor_get();
  307. // Check if the sensor is valid.
  308. if (!s)
  309. {
  310. Serial.println("Failed to acquire camera sensor");
  311. return;
  312. }
  313. // Set pixel format to JPEG for saving picture.
  314. s->set_pixformat(s, PIXFORMAT_JPEG);
  315. // Set frame size based on available PSRAM.
  316. if (psramFound())
  317. {
  318. s->set_framesize(s, FRAMESIZE_UXGA);
  319. }
  320. else
  321. {
  322. s->set_framesize(s, FRAMESIZE_SVGA);
  323. }
  324. // Get a frame buffer from camera.
  325. camera_fb_t *frame_buffer = esp_camera_fb_get();
  326. if (!frame_buffer)
  327. {
  328. Serial.println("Camera capture failed");
  329. return;
  330. }
  331. if (!SD_MMC.begin())
  332. {
  333. // SD Card Mount Failed.
  334. Serial.println("SD Card Mount Failed");
  335. esp_camera_fb_return(frame_buffer);
  336. return;
  337. }
  338. // Generate a unique filename.
  339. String path = "/picture";
  340. path += String(millis());
  341. path += ".jpg";
  342. fs::FS &fs = SD_MMC;
  343. File file = fs.open(path.c_str(), FILE_WRITE);
  344. if (!file)
  345. {
  346. Serial.println("Failed to open file in writing mode");
  347. }
  348. else
  349. {
  350. if (file.write(frame_buffer->buf, frame_buffer->len) != frame_buffer->len)
  351. {
  352. Serial.println("Failed to write the image to the file");
  353. }
  354. file.close(); // Close the file in any case.
  355. }
  356. // Update framesize back to the default.
  357. s->set_framesize(s, FRAMESIZE_QQVGA);
  358. // Return the frame buffer back to the camera driver.
  359. esp_camera_fb_return(frame_buffer);
  360. }