camera_suite_view_style_1.c 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. #include "../camera-suite.h"
  2. #include <furi.h>
  3. #include <furi_hal.h>
  4. #include <input/input.h>
  5. #include <gui/elements.h>
  6. #include <dolphin/dolphin.h>
  7. struct CameraSuiteViewStyle1 {
  8. View* view;
  9. CameraSuiteViewStyle1Callback callback;
  10. FuriThread* worker_thread;
  11. FuriStreamBuffer* rx_stream;
  12. void* context;
  13. };
  14. void camera_suite_view_style_1_set_callback(
  15. CameraSuiteViewStyle1* instance,
  16. CameraSuiteViewStyle1Callback callback,
  17. void* context) {
  18. furi_assert(instance);
  19. furi_assert(callback);
  20. instance->callback = callback;
  21. instance->context = context;
  22. }
  23. static void camera_suite_view_style_1_draw(Canvas* canvas, UartDumpModel* model) {
  24. // Clear the screen.
  25. canvas_set_color(canvas, ColorBlack);
  26. // Draw the frame.
  27. canvas_draw_frame(canvas, 0, 0, FRAME_WIDTH, FRAME_HEIGHT);
  28. // Draw the pixels.
  29. for(size_t p = 0; p < FRAME_BUFFER_LENGTH; ++p) {
  30. uint8_t x = p % ROW_BUFFER_LENGTH; // 0 .. 15
  31. uint8_t y = p / ROW_BUFFER_LENGTH; // 0 .. 63
  32. for(uint8_t i = 0; i < 8; ++i) {
  33. if((model->pixels[p] & (1 << (7 - i))) != 0) {
  34. canvas_draw_dot(canvas, (x * 8) + i, y);
  35. }
  36. }
  37. }
  38. // Draw the guide if the camera is not initialized.
  39. if(!model->initialized) {
  40. canvas_draw_icon(canvas, 74, 16, &I_DolphinCommon_56x48);
  41. canvas_set_font(canvas, FontSecondary);
  42. canvas_draw_str(canvas, 8, 12, "Connect the ESP32-CAM");
  43. canvas_draw_str(canvas, 20, 24, "VCC - 3V3");
  44. canvas_draw_str(canvas, 20, 34, "GND - GND");
  45. canvas_draw_str(canvas, 20, 44, "U0R - TX");
  46. canvas_draw_str(canvas, 20, 54, "U0T - RX");
  47. }
  48. }
  49. static void camera_suite_view_style_1_model_init(UartDumpModel* const model) {
  50. for(size_t i = 0; i < FRAME_BUFFER_LENGTH; i++) {
  51. model->pixels[i] = 0;
  52. }
  53. }
  54. static bool camera_suite_view_style_1_input(InputEvent* event, void* context) {
  55. furi_assert(context);
  56. CameraSuiteViewStyle1* instance = context;
  57. if(event->type == InputTypeRelease) {
  58. uint8_t data[1];
  59. switch(event->key) {
  60. case InputKeyBack:
  61. // Stop the camera stream.
  62. data[0] = 'S';
  63. // Go back to the main menu.
  64. with_view_model(
  65. instance->view,
  66. UartDumpModel * model,
  67. {
  68. UNUSED(model);
  69. instance->callback(CameraSuiteCustomEventSceneStyle1Back, instance->context);
  70. },
  71. true);
  72. break;
  73. case InputKeyLeft:
  74. // Camera: Invert.
  75. data[0] = '<';
  76. with_view_model(
  77. instance->view,
  78. UartDumpModel * model,
  79. {
  80. UNUSED(model);
  81. instance->callback(CameraSuiteCustomEventSceneStyle1Left, instance->context);
  82. },
  83. true);
  84. break;
  85. case InputKeyRight:
  86. // Camera: Enable/disable dithering.
  87. data[0] = '>';
  88. with_view_model(
  89. instance->view,
  90. UartDumpModel * model,
  91. {
  92. UNUSED(model);
  93. instance->callback(CameraSuiteCustomEventSceneStyle1Right, instance->context);
  94. },
  95. true);
  96. break;
  97. case InputKeyUp:
  98. // Camera: Increase contrast.
  99. data[0] = 'C';
  100. with_view_model(
  101. instance->view,
  102. UartDumpModel * model,
  103. {
  104. UNUSED(model);
  105. instance->callback(CameraSuiteCustomEventSceneStyle1Up, instance->context);
  106. },
  107. true);
  108. break;
  109. case InputKeyDown:
  110. // Camera: Reduce contrast.
  111. data[0] = 'c';
  112. with_view_model(
  113. instance->view,
  114. UartDumpModel * model,
  115. {
  116. UNUSED(model);
  117. instance->callback(CameraSuiteCustomEventSceneStyle1Down, instance->context);
  118. },
  119. true);
  120. break;
  121. case InputKeyOk:
  122. with_view_model(
  123. instance->view,
  124. UartDumpModel * model,
  125. {
  126. UNUSED(model);
  127. instance->callback(CameraSuiteCustomEventSceneStyle1Ok, instance->context);
  128. },
  129. true);
  130. break;
  131. case InputKeyMAX:
  132. break;
  133. }
  134. // Send `data` to the ESP32-CAM
  135. furi_hal_uart_tx(FuriHalUartIdUSART1, data, 1);
  136. }
  137. return true;
  138. }
  139. static void camera_suite_view_style_1_exit(void* context) {
  140. furi_assert(context);
  141. }
  142. static void camera_suite_view_style_1_enter(void* context) {
  143. // Check `context` for null. If it is null, abort program, else continue.
  144. furi_assert(context);
  145. // Cast `context` to `CameraSuiteViewStyle1*` and store it in `instance`.
  146. CameraSuiteViewStyle1* instance = (CameraSuiteViewStyle1*)context;
  147. with_view_model(
  148. instance->view,
  149. UartDumpModel * model,
  150. { camera_suite_view_style_1_model_init(model); },
  151. true);
  152. }
  153. static void camera_on_irq_cb(UartIrqEvent uartIrqEvent, uint8_t data, void* context) {
  154. // Check `context` for null. If it is null, abort program, else continue.
  155. furi_assert(context);
  156. // Cast `context` to `CameraSuiteViewStyle1*` and store it in `instance`.
  157. CameraSuiteViewStyle1* instance = context;
  158. // If `uartIrqEvent` is `UartIrqEventRXNE`, send the data to the
  159. // `rx_stream` and set the `WorkerEventRx` flag.
  160. if(uartIrqEvent == UartIrqEventRXNE) {
  161. furi_stream_buffer_send(instance->rx_stream, &data, 1, 0);
  162. furi_thread_flags_set(furi_thread_get_id(instance->worker_thread), WorkerEventRx);
  163. }
  164. }
  165. static void process_ringbuffer(UartDumpModel* model, uint8_t byte) {
  166. // First char has to be 'Y' in the buffer.
  167. if(model->ringbuffer_index == 0 && byte != 'Y') {
  168. return;
  169. }
  170. // Second char has to be ':' in the buffer or reset.
  171. if(model->ringbuffer_index == 1 && byte != ':') {
  172. model->ringbuffer_index = 0;
  173. process_ringbuffer(model, byte);
  174. return;
  175. }
  176. // Assign current byte to the ringbuffer.
  177. model->row_ringbuffer[model->ringbuffer_index] = byte;
  178. // Increment the ringbuffer index.
  179. ++model->ringbuffer_index;
  180. // Let's wait 'till the buffer fills.
  181. if(model->ringbuffer_index < RING_BUFFER_LENGTH) {
  182. return;
  183. }
  184. // Flush the ringbuffer to the framebuffer.
  185. model->ringbuffer_index = 0; // Reset the ringbuffer
  186. model->initialized = true; // Established the connection successfully.
  187. size_t row_start_index =
  188. model->row_ringbuffer[2] * ROW_BUFFER_LENGTH; // Third char will determine the row number
  189. if(row_start_index > LAST_ROW_INDEX) { // Failsafe
  190. row_start_index = 0;
  191. }
  192. for(size_t i = 0; i < ROW_BUFFER_LENGTH; ++i) {
  193. model->pixels[row_start_index + i] =
  194. model->row_ringbuffer[i + 3]; // Writing the remaining 16 bytes into the frame buffer
  195. }
  196. }
  197. static int32_t camera_worker(void* context) {
  198. furi_assert(context);
  199. CameraSuiteViewStyle1* instance = context;
  200. while(1) {
  201. uint32_t events =
  202. furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever);
  203. furi_check((events & FuriFlagError) == 0);
  204. if(events & WorkerEventStop) {
  205. break;
  206. } else if(events & WorkerEventRx) {
  207. size_t length = 0;
  208. do {
  209. size_t intended_data_size = 64;
  210. uint8_t data[intended_data_size];
  211. length =
  212. furi_stream_buffer_receive(instance->rx_stream, data, intended_data_size, 0);
  213. if(length > 0) {
  214. with_view_model(
  215. instance->view,
  216. UartDumpModel * model,
  217. {
  218. for(size_t i = 0; i < length; i++) {
  219. process_ringbuffer(model, data[i]);
  220. }
  221. },
  222. false);
  223. }
  224. } while(length > 0);
  225. }
  226. }
  227. return 0;
  228. }
  229. CameraSuiteViewStyle1* camera_suite_view_style_1_alloc() {
  230. CameraSuiteViewStyle1* instance = malloc(sizeof(CameraSuiteViewStyle1));
  231. instance->view = view_alloc();
  232. instance->rx_stream = furi_stream_buffer_alloc(2048, 1);
  233. // Set up views
  234. view_allocate_model(instance->view, ViewModelTypeLocking, sizeof(UartDumpModel));
  235. view_set_context(instance->view, instance); // furi_assert crashes in events without this
  236. view_set_draw_callback(instance->view, (ViewDrawCallback)camera_suite_view_style_1_draw);
  237. view_set_input_callback(instance->view, camera_suite_view_style_1_input);
  238. view_set_enter_callback(instance->view, camera_suite_view_style_1_enter);
  239. view_set_exit_callback(instance->view, camera_suite_view_style_1_exit);
  240. with_view_model(
  241. instance->view,
  242. UartDumpModel * model,
  243. { camera_suite_view_style_1_model_init(model); },
  244. true);
  245. instance->worker_thread = furi_thread_alloc_ex("UsbUartWorker", 2048, camera_worker, instance);
  246. furi_thread_start(instance->worker_thread);
  247. // Enable uart listener
  248. furi_hal_console_disable();
  249. furi_hal_uart_set_br(FuriHalUartIdUSART1, 230400);
  250. furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, camera_on_irq_cb, instance);
  251. return instance;
  252. }
  253. void camera_suite_view_style_1_free(CameraSuiteViewStyle1* instance) {
  254. furi_assert(instance);
  255. with_view_model(
  256. instance->view, UartDumpModel * model, { UNUSED(model); }, true);
  257. view_free(instance->view);
  258. free(instance);
  259. }
  260. View* camera_suite_view_style_1_get_view(CameraSuiteViewStyle1* instance) {
  261. furi_assert(instance);
  262. return instance->view;
  263. }