firmware.ino 13 KB

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