firmware.ino 13 KB

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