airmon_pms.c 8.4 KB

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