airmon_pms.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. #include "airmon_pms.h"
  2. #include <string.h>
  3. #define TAG "AirmonPms"
  4. #define UINT16BE(p) (((p)[0] << 8) | (p)[1])
  5. #define PMS_BAUDRATE 9600
  6. #define PMS_CTRL_FRAME_LEN 4
  7. #define PMS_DATA_FRAME_LEN 28
  8. #define PMS_START_CHAR_1 0x42
  9. #define PMS_START_CHAR_2 0x4d
  10. typedef enum {
  11. WorkerEvtStop = (1 << 0),
  12. WorkerEvtRxDone = (1 << 1),
  13. } WorkerEvtFlags;
  14. typedef enum {
  15. RxStateStartChar1,
  16. RxStateStartChar2,
  17. RxStateFrameLength,
  18. RxStateFrameData
  19. } RxState;
  20. #define WORKER_ALL_RX_EVENTS (WorkerEvtStop | WorkerEvtRxDone)
  21. static void airmon_pms_uart_on_irq_cb(UartIrqEvent ev, uint8_t data, void* context) {
  22. AirmonPmsContext* pms_context = context;
  23. if(ev == UartIrqEventRXNE) {
  24. furi_stream_buffer_send(pms_context->rx_stream, &data, 1, 0);
  25. furi_thread_flags_set(furi_thread_get_id(pms_context->thread), WorkerEvtRxDone);
  26. }
  27. }
  28. static void airmon_pms_serial_init(AirmonPmsContext* pms_context) {
  29. furi_hal_console_disable();
  30. furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, airmon_pms_uart_on_irq_cb, pms_context);
  31. furi_hal_uart_set_br(FuriHalUartIdUSART1, PMS_BAUDRATE);
  32. }
  33. static void airmon_pms_serial_deinit(AirmonPmsContext* pms_context) {
  34. UNUSED(pms_context);
  35. furi_hal_uart_set_irq_cb(FuriHalUartIdUSART1, NULL, NULL);
  36. furi_hal_console_enable();
  37. }
  38. bool airmon_pms_frame_valid(AirmonPmsContext* pms_context, size_t frame_len) {
  39. uint16_t checksum =
  40. PMS_START_CHAR_1 + PMS_START_CHAR_2 + (frame_len >> 8) + (frame_len & 0xff);
  41. uint8_t* p = pms_context->rx_buf;
  42. while(p < pms_context->rx_buf + frame_len - 2) {
  43. checksum += *p++ & 0xff;
  44. }
  45. return checksum == UINT16BE(p);
  46. }
  47. void airmon_pms_process_data_frame(AirmonPmsContext* pms_context) {
  48. furi_mutex_acquire(pms_context->mutex, FuriWaitForever);
  49. AirmonPmsData* d = &pms_context->pms_data;
  50. uint8_t* p = pms_context->rx_buf;
  51. d->pm1_0cf = UINT16BE(p);
  52. d->pm2_5cf = UINT16BE(p + 2);
  53. d->pm10cf = UINT16BE(p + 4);
  54. d->pm1_0at = UINT16BE(p + 6);
  55. d->pm2_5at = UINT16BE(p + 8);
  56. d->pm10at = UINT16BE(p + 10);
  57. d->ct0_3 = UINT16BE(p + 12);
  58. d->ct0_5 = UINT16BE(p + 14);
  59. d->ct1_0 = UINT16BE(p + 16);
  60. d->ct2_5 = UINT16BE(p + 18);
  61. d->ct5_0 = UINT16BE(p + 20);
  62. d->ct10 = UINT16BE(p + 22);
  63. furi_mutex_release(pms_context->mutex);
  64. }
  65. void airmon_pms_process_frame(AirmonPmsContext* pms_context, size_t frame_len) {
  66. if(!airmon_pms_frame_valid(pms_context, frame_len)) {
  67. notification_message_block(pms_context->notifications, &sequence_blink_red_10);
  68. return;
  69. }
  70. if(frame_len == PMS_DATA_FRAME_LEN) {
  71. airmon_pms_process_data_frame(pms_context);
  72. notification_message_block(pms_context->notifications, &sequence_blink_green_10);
  73. } else if(frame_len == PMS_CTRL_FRAME_LEN) {
  74. notification_message_block(pms_context->notifications, &sequence_blink_blue_10);
  75. } else {
  76. notification_message_block(pms_context->notifications, &sequence_blink_magenta_10);
  77. }
  78. }
  79. static int32_t airmon_pms_worker(void* context) {
  80. FURI_LOG_D(TAG, "Worker started");
  81. AirmonPmsContext* pms_context = context;
  82. pms_context->rx_stream = furi_stream_buffer_alloc(RX_BUF_SIZE, 1);
  83. size_t rx_offset = 0;
  84. airmon_pms_serial_init(pms_context);
  85. FURI_LOG_D(TAG, "Entering event loop");
  86. RxState rx_state = RxStateStartChar1;
  87. size_t rx_frame_len = 0;
  88. while(1) {
  89. uint32_t events =
  90. furi_thread_flags_wait(WORKER_ALL_RX_EVENTS, FuriFlagWaitAny, FuriWaitForever);
  91. furi_check((events & FuriFlagError) == 0);
  92. if(events & WorkerEvtStop) {
  93. break;
  94. }
  95. if(events & WorkerEvtRxDone) {
  96. size_t rx_len = 0;
  97. while(
  98. (rx_len = furi_stream_buffer_receive(
  99. pms_context->rx_stream,
  100. pms_context->rx_buf + rx_offset,
  101. RX_BUF_SIZE - rx_offset - 1,
  102. 0))) {
  103. FURI_LOG_D(TAG, "rx_len: %d", rx_len);
  104. rx_offset += rx_len;
  105. bool rx_available = true;
  106. size_t rx_processed;
  107. while(rx_available) {
  108. rx_processed = 0;
  109. switch(rx_state) {
  110. case RxStateStartChar1:
  111. if(*pms_context->rx_buf == PMS_START_CHAR_1) {
  112. rx_state = RxStateStartChar2;
  113. }
  114. rx_processed = 1;
  115. break;
  116. case RxStateStartChar2:
  117. if(*pms_context->rx_buf == PMS_START_CHAR_2) {
  118. rx_state = RxStateFrameLength;
  119. rx_processed = 1;
  120. } else {
  121. rx_state = RxStateStartChar1;
  122. }
  123. break;
  124. case RxStateFrameLength:
  125. if(rx_offset > 2) {
  126. rx_frame_len = UINT16BE(pms_context->rx_buf);
  127. FURI_LOG_D(TAG, "rx_frame_len: %d", rx_frame_len);
  128. rx_processed = 2;
  129. rx_state = (rx_frame_len >= PMS_CTRL_FRAME_LEN &&
  130. rx_frame_len <= PMS_DATA_FRAME_LEN) ?
  131. RxStateFrameData :
  132. RxStateStartChar1;
  133. } else {
  134. rx_available = false;
  135. }
  136. break;
  137. case RxStateFrameData:
  138. if(rx_offset >= rx_frame_len) {
  139. airmon_pms_process_frame(pms_context, rx_frame_len);
  140. rx_processed = rx_frame_len;
  141. rx_state = RxStateStartChar1;
  142. } else {
  143. rx_available = false;
  144. }
  145. break;
  146. default:
  147. rx_state = RxStateStartChar1;
  148. break;
  149. }
  150. FURI_LOG_D(TAG, "RxState: %d", rx_state);
  151. if(rx_processed) {
  152. rx_offset -= rx_processed;
  153. rx_available = !!rx_offset;
  154. if(rx_available) {
  155. memmove(
  156. pms_context->rx_buf,
  157. pms_context->rx_buf + rx_processed,
  158. rx_offset);
  159. }
  160. }
  161. }
  162. };
  163. }
  164. }
  165. airmon_pms_serial_deinit(pms_context);
  166. furi_stream_buffer_free(pms_context->rx_stream);
  167. FURI_LOG_D(TAG, "Worker stopped");
  168. return 0;
  169. }
  170. AirmonPmsContext* airmon_pms_context_alloc() {
  171. AirmonPmsContext* pms_context = malloc(sizeof(AirmonPmsContext));
  172. pms_context->mutex = furi_mutex_alloc(FuriMutexTypeNormal);
  173. if(!pms_context->mutex) {
  174. FURI_LOG_E("AirmonPms", "cannot create mutex\r\n");
  175. free(pms_context);
  176. return NULL;
  177. }
  178. memset(&pms_context->pms_data, 0, sizeof(AirmonPmsData));
  179. pms_context->notifications = furi_record_open(RECORD_NOTIFICATION);
  180. pms_context->thread = furi_thread_alloc();
  181. furi_thread_set_name(pms_context->thread, "AirmonPmsWorker");
  182. furi_thread_set_stack_size(pms_context->thread, 1024);
  183. furi_thread_set_context(pms_context->thread, pms_context);
  184. furi_thread_set_callback(pms_context->thread, airmon_pms_worker);
  185. return pms_context;
  186. }
  187. void airmon_pms_context_free(AirmonPmsContext* pms_context) {
  188. furi_assert(pms_context);
  189. furi_thread_free(pms_context->thread);
  190. furi_record_close(RECORD_NOTIFICATION);
  191. free(pms_context);
  192. }
  193. void airmon_pms_init(AirmonPmsContext* pms_context) {
  194. furi_assert(pms_context);
  195. furi_hal_power_enable_otg();
  196. furi_thread_start(pms_context->thread);
  197. }
  198. void airmon_pms_deinit(AirmonPmsContext* pms_context) {
  199. furi_assert(pms_context);
  200. furi_thread_flags_set(furi_thread_get_id(pms_context->thread), WorkerEvtStop);
  201. furi_thread_join(pms_context->thread);
  202. furi_hal_power_disable_otg();
  203. }