firmware.ino 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. #include "esp_camera.h"
  2. #include "FS.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(camera_fb_t * fb);
  26. void initializeCamera();
  27. void processImage(camera_fb_t * fb);
  28. void ditherImage(camera_fb_t * fb);
  29. void saveFrameBufferToSDCard(camera_fb_t * fb);
  30. // Enumeration to represent the available dithering algorithms.
  31. enum DitheringAlgorithm {
  32. FLOYD_STEINBERG,
  33. JARVIS_JUDICE_NINKE,
  34. STUCKI
  35. };
  36. // Holds the currently selected dithering algorithm.
  37. DitheringAlgorithm ditherAlgorithm = FLOYD_STEINBERG;
  38. // Flag to enable or disable dithering.
  39. bool disableDithering = false;
  40. // Flag to invert pixel colors.
  41. bool invert = false;
  42. // Flag to represent the flash state.
  43. bool isFlashOn = false;
  44. // Flag to represent whether the image is rotated.
  45. bool rotated = false;
  46. // Flag to stop or start the stream.
  47. bool stopStream = false;
  48. // Flag to store jpeg images to sd card.
  49. bool storeJpeg = false;
  50. void setup() {
  51. // Start serial communication at 230400 baud rate.
  52. Serial.begin(230400);
  53. initializeCamera();
  54. }
  55. void loop() {
  56. // Capture and process the frame buffer if streaming is enabled.
  57. camera_fb_t * fb = esp_camera_fb_get();
  58. if (!stopStream) {
  59. if (fb) {
  60. processImage(fb);
  61. // Return the frame buffer back to the camera driver.
  62. esp_camera_fb_return(fb);
  63. }
  64. // Delay for 10ms between each frame.
  65. delay(10);
  66. }
  67. // Handle any available serial input commands.
  68. handleSerialInput(fb);
  69. }
  70. void handleSerialInput(camera_fb_t * fb) {
  71. if (Serial.available() > 0) {
  72. char input = Serial.read();
  73. sensor_t * cameraSensor = esp_camera_sensor_get();
  74. switch (input) {
  75. case '>': // Toggle dithering.
  76. disableDithering = !disableDithering;
  77. break;
  78. case '<': // Toggle invert.
  79. invert = !invert;
  80. break;
  81. case 'B': // Add brightness.
  82. cameraSensor -> set_contrast(
  83. cameraSensor,
  84. cameraSensor -> status.brightness + 1
  85. );
  86. break;
  87. case 'b': // Remove brightness.
  88. cameraSensor -> set_contrast(
  89. cameraSensor,
  90. cameraSensor -> status.brightness - 1
  91. );
  92. break;
  93. case 'C': // Add contrast.
  94. cameraSensor -> set_contrast(
  95. cameraSensor,
  96. cameraSensor -> status.contrast + 1
  97. );
  98. break;
  99. case 'c': // Remove contrast.
  100. cameraSensor -> set_contrast(
  101. cameraSensor,
  102. cameraSensor -> status.contrast - 1
  103. );
  104. break;
  105. case 'j': // Toggle store jpeg to sd card off.
  106. storeJpeg = false;
  107. break;
  108. case 'J': // Toggle store jpeg to sd card on.
  109. storeJpeg = true;
  110. break;
  111. case 'P': // Picture sequence.
  112. if (!isFlashOn) {
  113. isFlashOn = true;
  114. // Set up the flash light control pin (number 4) as an "output"
  115. // so we can turn the torch ON and OFF.
  116. pinMode(FLASH_GPIO_NUM, OUTPUT);
  117. // Turn on torch.
  118. digitalWrite(FLASH_GPIO_NUM, HIGH);
  119. if (storeJpeg) {
  120. // Save jpeg image to sd card.
  121. saveFrameBufferToSDCard(fb);
  122. // Return the frame buffer back to the camera driver.
  123. esp_camera_fb_return(fb);
  124. }
  125. // Give some time for Flipper to save locally with flash on.
  126. delay(15);
  127. // Turn off torch.
  128. digitalWrite(FLASH_GPIO_NUM, LOW);
  129. isFlashOn = false;
  130. }
  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. // Set camera configurations
  158. config.ledc_channel = LEDC_CHANNEL_0;
  159. config.ledc_timer = LEDC_TIMER_0;
  160. config.pin_d0 = Y2_GPIO_NUM;
  161. config.pin_d1 = Y3_GPIO_NUM;
  162. config.pin_d2 = Y4_GPIO_NUM;
  163. config.pin_d3 = Y5_GPIO_NUM;
  164. config.pin_d4 = Y6_GPIO_NUM;
  165. config.pin_d5 = Y7_GPIO_NUM;
  166. config.pin_d6 = Y8_GPIO_NUM;
  167. config.pin_d7 = Y9_GPIO_NUM;
  168. config.pin_xclk = XCLK_GPIO_NUM;
  169. config.pin_pclk = PCLK_GPIO_NUM;
  170. config.pin_vsync = VSYNC_GPIO_NUM;
  171. config.pin_href = HREF_GPIO_NUM;
  172. config.pin_sscb_sda = SIOD_GPIO_NUM;
  173. config.pin_sscb_scl = SIOC_GPIO_NUM;
  174. config.pin_pwdn = PWDN_GPIO_NUM;
  175. config.pin_reset = RESET_GPIO_NUM;
  176. config.xclk_freq_hz = 20000000;
  177. config.pixel_format = PIXFORMAT_GRAYSCALE;
  178. config.frame_size = FRAMESIZE_QQVGA;
  179. config.fb_count = 1;
  180. if (isFlashOn) {
  181. pinMode(FLASH_GPIO_NUM, OUTPUT);
  182. // Turn off torch.
  183. digitalWrite(FLASH_GPIO_NUM, LOW);
  184. isFlashOn = false;
  185. }
  186. // Initialize camera
  187. esp_err_t err = esp_camera_init( & config);
  188. if (err != ESP_OK) {
  189. Serial.printf("Camera init failed with error 0x%x", err);
  190. return;
  191. }
  192. // Set initial contrast.
  193. sensor_t * s = esp_camera_sensor_get();
  194. s -> set_contrast(s, 0);
  195. // Set rotation
  196. s -> set_vflip(s, true); // Vertical flip
  197. s -> set_hmirror(s, true); // Horizontal mirror
  198. }
  199. void processImage(camera_fb_t * frameBuffer) {
  200. // If dithering is not disabled, perform dithering on the image. Dithering is the
  201. // process of approximating the look of a high-resolution grayscale image in a
  202. // lower resolution by binary values (black & white), thereby representing
  203. // different shades of gray.
  204. if (!disableDithering) {
  205. ditherImage(frameBuffer); // 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. Serial.print("Y:"); // Print "Y:" for every new row.
  211. Serial.write(flipper_y); // Send the row identifier as a byte.
  212. // Calculate the actual y index in the frame buffer 1D array by multiplying the
  213. // y value with the width of the frame buffer. This gives the starting index
  214. // of the row in the 1D array.
  215. size_t true_y = y * frameBuffer -> width;
  216. // Iterating over specific columns of each row in the frame buffer.
  217. for (uint8_t x = 16; x < 144; x += 8) { // step by 8 as we're packing 8 pixels per byte
  218. uint8_t packed_pixels = 0;
  219. // Packing 8 pixel values into one byte.
  220. for (uint8_t bit = 0; bit < 8; ++bit) {
  221. // Check the invert flag and pack the pixels accordingly.
  222. if (invert) {
  223. // If invert is true, consider pixel as 1 if it's more than 127.
  224. if (frameBuffer -> buf[true_y + x + bit] > 127) {
  225. packed_pixels |= (1 << (7 - bit));
  226. }
  227. } else {
  228. // If invert is false, consider pixel as 1 if it's less than 127.
  229. if (frameBuffer -> buf[true_y + x + bit] < 127) {
  230. packed_pixels |= (1 << (7 - bit));
  231. }
  232. }
  233. }
  234. Serial.write(packed_pixels); // Sending packed pixel byte.
  235. }
  236. ++flipper_y; // Move to the next row.
  237. Serial.flush(); // Ensure all data in the Serial buffer is sent before moving to the next iteration.
  238. }
  239. }
  240. void ditherImage(camera_fb_t * fb) {
  241. for (uint8_t y = 0; y < fb -> height; ++y) {
  242. for (uint8_t x = 0; x < fb -> width; ++x) {
  243. size_t current = (y * fb -> width) + x;
  244. uint8_t oldpixel = fb -> buf[current];
  245. uint8_t newpixel = oldpixel >= 128 ? 255 : 0;
  246. fb -> buf[current] = newpixel;
  247. int8_t quant_error = oldpixel - newpixel;
  248. // Apply error diffusion based on the selected algorithm
  249. switch (ditherAlgorithm) {
  250. case JARVIS_JUDICE_NINKE:
  251. fb -> buf[(y * fb -> width) + x + 1] += quant_error * 7 / 48;
  252. fb -> buf[(y * fb -> width) + x + 2] += quant_error * 5 / 48;
  253. fb -> buf[(y + 1) * fb -> width + x - 2] += quant_error * 3 / 48;
  254. fb -> buf[(y + 1) * fb -> width + x - 1] += quant_error * 5 / 48;
  255. fb -> buf[(y + 1) * fb -> width + x] += quant_error * 7 / 48;
  256. fb -> buf[(y + 1) * fb -> width + x + 1] += quant_error * 5 / 48;
  257. fb -> buf[(y + 1) * fb -> width + x + 2] += quant_error * 3 / 48;
  258. fb -> buf[(y + 2) * fb -> width + x - 2] += quant_error * 1 / 48;
  259. fb -> buf[(y + 2) * fb -> width + x - 1] += quant_error * 3 / 48;
  260. fb -> buf[(y + 2) * fb -> width + x] += quant_error * 5 / 48;
  261. fb -> buf[(y + 2) * fb -> width + x + 1] += quant_error * 3 / 48;
  262. fb -> buf[(y + 2) * fb -> width + x + 2] += quant_error * 1 / 48;
  263. break;
  264. case STUCKI:
  265. fb -> buf[(y * fb -> width) + x + 1] += quant_error * 8 / 42;
  266. fb -> buf[(y * fb -> width) + x + 2] += quant_error * 4 / 42;
  267. fb -> buf[(y + 1) * fb -> width + x - 2] += quant_error * 2 / 42;
  268. fb -> buf[(y + 1) * fb -> width + x - 1] += quant_error * 4 / 42;
  269. fb -> buf[(y + 1) * fb -> width + x] += quant_error * 8 / 42;
  270. fb -> buf[(y + 1) * fb -> width + x + 1] += quant_error * 4 / 42;
  271. fb -> buf[(y + 1) * fb -> width + x + 2] += quant_error * 2 / 42;
  272. fb -> buf[(y + 2) * fb -> width + x - 2] += quant_error * 1 / 42;
  273. fb -> buf[(y + 2) * fb -> width + x - 1] += quant_error * 2 / 42;
  274. fb -> buf[(y + 2) * fb -> width + x] += quant_error * 4 / 42;
  275. fb -> buf[(y + 2) * fb -> width + x + 1] += quant_error * 2 / 42;
  276. fb -> buf[(y + 2) * fb -> width + x + 2] += quant_error * 1 / 42;
  277. break;
  278. case FLOYD_STEINBERG:
  279. default:
  280. // Default to Floyd-Steinberg dithering if an invalid algorithm is selected
  281. fb -> buf[(y * fb -> width) + x + 1] += quant_error * 7 / 16;
  282. fb -> buf[(y + 1) * fb -> width + x - 1] += quant_error * 3 / 16;
  283. fb -> buf[(y + 1) * fb -> width + x] += quant_error * 5 / 16;
  284. fb -> buf[(y + 1) * fb -> width + x + 1] += quant_error * 1 / 16;
  285. break;
  286. }
  287. }
  288. }
  289. }
  290. void saveFrameBufferToSDCard(camera_fb_t * fb) {
  291. if (!SD_MMC.begin()) {
  292. // Serial.println("SD Card Mount Failed");
  293. return;
  294. }
  295. uint8_t cardType = SD_MMC.cardType();
  296. if (cardType == CARD_NONE) {
  297. // Serial.println("No SD Card attached");
  298. return;
  299. }
  300. // Generate a unique filename
  301. String path = "/picture";
  302. path += String(millis());
  303. path += ".jpg";
  304. File file = SD_MMC.open(path.c_str(), FILE_WRITE);
  305. if (!file) {
  306. // Serial.println("Failed to open file for writing");
  307. return;
  308. }
  309. // Write frame buffer to file
  310. file.write(fb -> buf, fb -> len);
  311. // Serial.println("File written to SD card");
  312. file.close();
  313. }