airmon_pms.c 8.4 KB

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