uart_echo.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. #include <furi.h>
  2. #include <m-string.h>
  3. #include <gui/gui.h>
  4. #include <notification/notification.h>
  5. #include <notification/notification_messages.h>
  6. #include <gui/elements.h>
  7. #include <stream_buffer.h>
  8. #include <furi_hal_uart.h>
  9. #include <furi_hal_console.h>
  10. #include <gui/view_dispatcher.h>
  11. #include <gui/modules/dialog_ex.h>
  12. #define LINES_ON_SCREEN 6
  13. #define COLUMNS_ON_SCREEN 21
  14. typedef struct UartDumpModel UartDumpModel;
  15. typedef struct {
  16. Gui* gui;
  17. NotificationApp* notification;
  18. ViewDispatcher* view_dispatcher;
  19. View* view;
  20. FuriThread* worker_thread;
  21. StreamBufferHandle_t rx_stream;
  22. } UartEchoApp;
  23. typedef struct {
  24. string_t text;
  25. } ListElement;
  26. struct UartDumpModel {
  27. ListElement* list[LINES_ON_SCREEN];
  28. uint8_t line;
  29. char last_char;
  30. bool escape;
  31. };
  32. typedef enum {
  33. WorkerEventReserved = (1 << 0), // Reserved for StreamBuffer internal event
  34. WorkerEventStop = (1 << 1),
  35. WorkerEventRx = (1 << 2),
  36. } WorkerEventFlags;
  37. #define WORKER_EVENTS_MASK (WorkerEventStop | WorkerEventRx)
  38. const NotificationSequence sequence_notification = {
  39. &message_display_backlight_on,
  40. &message_green_255,
  41. &message_delay_10,
  42. NULL,
  43. };
  44. static void uart_echo_view_draw_callback(Canvas* canvas, void* _model) {
  45. UartDumpModel* model = _model;
  46. // Prepare canvas
  47. canvas_clear(canvas);
  48. canvas_set_color(canvas, ColorBlack);
  49. canvas_set_font(canvas, FontKeyboard);
  50. for(size_t i = 0; i < LINES_ON_SCREEN; i++) {
  51. canvas_draw_str(
  52. canvas,
  53. 0,
  54. (i + 1) * (canvas_current_font_height(canvas) - 1),
  55. string_get_cstr(model->list[i]->text));
  56. if(i == model->line) {
  57. uint8_t width = canvas_string_width(canvas, string_get_cstr(model->list[i]->text));
  58. canvas_draw_box(
  59. canvas,
  60. width,
  61. (i) * (canvas_current_font_height(canvas) - 1) + 2,
  62. 2,
  63. canvas_current_font_height(canvas) - 2);
  64. }
  65. }
  66. }
  67. static bool uart_echo_view_input_callback(InputEvent* event, void* context) {
  68. UNUSED(event);
  69. UNUSED(context);
  70. return false;
  71. }
  72. static uint32_t uart_echo_exit(void* context) {
  73. UNUSED(context);
  74. return VIEW_NONE;
  75. }
  76. static void uart_echo_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
  77. furi_assert(context);
  78. BaseType_t xHigherPriorityTaskWoken = pdFALSE;
  79. UartEchoApp* app = context;
  80. if(ev == UartIrqEventRXNE) {
  81. xStreamBufferSendFromISR(app->rx_stream, &data, 1, &xHigherPriorityTaskWoken);
  82. furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventRx);
  83. portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
  84. }
  85. }
  86. static void uart_echo_push_to_list(UartDumpModel* model, const char data) {
  87. if(model->escape) {
  88. // escape code end with letter
  89. if((data >= 'a' && data <= 'z') || (data >= 'A' && data <= 'Z')) {
  90. model->escape = false;
  91. }
  92. } else if(data == '[' && model->last_char == '\e') {
  93. // "Esc[" is a escape code
  94. model->escape = true;
  95. } else if((data >= ' ' && data <= '~') || (data == '\n' || data == '\r')) {
  96. bool new_string_needed = false;
  97. if(string_size(model->list[model->line]->text) >= COLUMNS_ON_SCREEN) {
  98. new_string_needed = true;
  99. } else if((data == '\n' || data == '\r')) {
  100. // pack line breaks
  101. if(model->last_char != '\n' && model->last_char != '\r') {
  102. new_string_needed = true;
  103. }
  104. }
  105. if(new_string_needed) {
  106. if((model->line + 1) < LINES_ON_SCREEN) {
  107. model->line += 1;
  108. } else {
  109. ListElement* first = model->list[0];
  110. for(size_t i = 1; i < LINES_ON_SCREEN; i++) {
  111. model->list[i - 1] = model->list[i];
  112. }
  113. string_reset(first->text);
  114. model->list[model->line] = first;
  115. }
  116. }
  117. if(data != '\n' && data != '\r') {
  118. string_push_back(model->list[model->line]->text, data);
  119. }
  120. }
  121. model->last_char = data;
  122. }
  123. static int32_t uart_echo_worker(void* context) {
  124. furi_assert(context);
  125. UartEchoApp* app = context;
  126. while(1) {
  127. uint32_t events =
  128. furi_thread_flags_wait(WORKER_EVENTS_MASK, FuriFlagWaitAny, FuriWaitForever);
  129. furi_check((events & FuriFlagError) == 0);
  130. if(events & WorkerEventStop) break;
  131. if(events & WorkerEventRx) {
  132. size_t length = 0;
  133. do {
  134. uint8_t data[64];
  135. length = xStreamBufferReceive(app->rx_stream, data, 64, 0);
  136. if(length > 0) {
  137. furi_hal_uart_tx(FuriHalUartIdUSART1, data, length);
  138. with_view_model(
  139. app->view, (UartDumpModel * model) {
  140. for(size_t i = 0; i < length; i++) {
  141. uart_echo_push_to_list(model, data[i]);
  142. }
  143. return false;
  144. });
  145. }
  146. } while(length > 0);
  147. notification_message(app->notification, &sequence_notification);
  148. with_view_model(
  149. app->view, (UartDumpModel * model) {
  150. UNUSED(model);
  151. return true;
  152. });
  153. }
  154. }
  155. return 0;
  156. }
  157. static UartEchoApp* uart_echo_app_alloc() {
  158. UartEchoApp* app = malloc(sizeof(UartEchoApp));
  159. app->rx_stream = xStreamBufferCreate(2048, 1);
  160. // Gui
  161. app->gui = furi_record_open(RECORD_GUI);
  162. app->notification = furi_record_open(RECORD_NOTIFICATION);
  163. // View dispatcher
  164. app->view_dispatcher = view_dispatcher_alloc();
  165. view_dispatcher_enable_queue(app->view_dispatcher);
  166. view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen);
  167. // Views
  168. app->view = view_alloc();
  169. view_set_draw_callback(app->view, uart_echo_view_draw_callback);
  170. view_set_input_callback(app->view, uart_echo_view_input_callback);
  171. view_allocate_model(app->view, ViewModelTypeLocking, sizeof(UartDumpModel));
  172. with_view_model(
  173. app->view, (UartDumpModel * model) {
  174. for(size_t i = 0; i < LINES_ON_SCREEN; i++) {
  175. model->line = 0;
  176. model->escape = false;
  177. model->list[i] = malloc(sizeof(ListElement));
  178. string_init(model->list[i]->text);
  179. }
  180. return true;
  181. });
  182. view_set_previous_callback(app->view, uart_echo_exit);
  183. view_dispatcher_add_view(app->view_dispatcher, 0, app->view);
  184. view_dispatcher_switch_to_view(app->view_dispatcher, 0);
  185. // Enable uart listener
  186. furi_hal_console_disable();
  187. furi_hal_uart_set_br(FuriHalUartIdUSART1, 115200);
  188. furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, uart_echo_on_irq_cb, app);
  189. app->worker_thread = furi_thread_alloc();
  190. furi_thread_set_name(app->worker_thread, "UsbUartWorker");
  191. furi_thread_set_stack_size(app->worker_thread, 1024);
  192. furi_thread_set_context(app->worker_thread, app);
  193. furi_thread_set_callback(app->worker_thread, uart_echo_worker);
  194. furi_thread_start(app->worker_thread);
  195. return app;
  196. }
  197. static void uart_echo_app_free(UartEchoApp* app) {
  198. furi_assert(app);
  199. furi_thread_flags_set(furi_thread_get_id(app->worker_thread), WorkerEventStop);
  200. furi_thread_join(app->worker_thread);
  201. furi_thread_free(app->worker_thread);
  202. furi_hal_console_enable();
  203. // Free views
  204. view_dispatcher_remove_view(app->view_dispatcher, 0);
  205. with_view_model(
  206. app->view, (UartDumpModel * model) {
  207. for(size_t i = 0; i < LINES_ON_SCREEN; i++) {
  208. string_clear(model->list[i]->text);
  209. free(model->list[i]);
  210. }
  211. return true;
  212. });
  213. view_free(app->view);
  214. view_dispatcher_free(app->view_dispatcher);
  215. // Close gui record
  216. furi_record_close(RECORD_GUI);
  217. furi_record_close(RECORD_NOTIFICATION);
  218. app->gui = NULL;
  219. vStreamBufferDelete(app->rx_stream);
  220. // Free rest
  221. free(app);
  222. }
  223. int32_t uart_echo_app(void* p) {
  224. UNUSED(p);
  225. UartEchoApp* app = uart_echo_app_alloc();
  226. view_dispatcher_run(app->view_dispatcher);
  227. uart_echo_app_free(app);
  228. return 0;
  229. }