led.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. /*
  2. * led.c - 系统状态指示灯驱动(事件查询方式,100ms检查周期)
  3. * 硬件:PC0(红), PC1(绿), PC2(蓝) - 低电平点亮
  4. */
  5. #include <rtthread.h>
  6. #include <rtdevice.h>
  7. #include <board.h>
  8. /* 使用RT-Thread标准日志系统 */
  9. #define DBG_TAG "led"
  10. #define DBG_LVL DBG_WARNING
  11. #include <rtdbg.h>
  12. /**
  13. * @brief 外部引入的状态
  14. */
  15. #include "ym310_protecl.h"
  16. extern rt_bool_t ym310_mqtt_is_connected(rt_uint8_t client_idx);
  17. extern SYS_STATUS sys_sta;
  18. extern uint8_t calculate_hours_diff(uint8_t start, uint8_t end);
  19. #define PIN_LED_RED GET_PIN(B, 7)
  20. #define PIN_LED_GREEN GET_PIN(B, 8)
  21. #define PIN_LED_BLUE GET_PIN(C, 13)
  22. /* 时间常量定义(单位:100ms)*/
  23. #define LED_BLINK_ON_TIME 3 // 亮的时间:300ms
  24. #define LED_BLINK_OFF_TIME 2 // 灭的时间:200ms
  25. /* 闪烁次数(使用 light_work_duration)*/
  26. #define BLINK_COUNT (dev_info.light_work_duration) // 光控时长 = 闪烁次数
  27. #define SK_BLINK_COUNT (calculate_hours_diff(dev_info.time_start, dev_info.time_end)) // 时控时长 = 闪烁次数
  28. #define PAUSE_TIME 20 // 一组闪烁完成后暂停:2000ms
  29. /* 长工作状态(light_work_duration == 0):亮2秒,灭2秒 */
  30. #define LONG_WORK_ON_TIME 20 // 亮2秒
  31. #define LONG_WORK_OFF_TIME 20 // 灭2秒
  32. #define SET_EFFECT_BLINK_COUNT 3 // 设置生效闪烁次数
  33. /* LED 状态定义 */
  34. typedef enum
  35. {
  36. LED_STATE_NORMAL = 0,
  37. LED_STATE_ERROR,
  38. LED_STATE_POWER_OFF,
  39. } led_state_t;
  40. /* 异常类型定义 */
  41. typedef enum
  42. {
  43. ERROR_NONE = 0,
  44. ERROR_RAIN, // 雨控 - 黄色闪烁(带次数/联网指示)
  45. ERROR_TEMP, // 温控 - 黄色常亮
  46. ERROR_UNDER_VOLTAGE, // 欠压 - 红色闪烁(带次数/联网指示)
  47. ERROR_TILT, // 倾倒 - 红色常亮
  48. } error_type_t;
  49. /* LED 工作模式 */
  50. typedef enum
  51. {
  52. LED_MODE_NORMAL = 0,
  53. LED_MODE_SET_EFFECT,
  54. } led_mode_t;
  55. /* 工作状态变量 */
  56. static led_state_t current_state = LED_STATE_NORMAL;
  57. static error_type_t current_error = ERROR_NONE;
  58. static led_mode_t current_mode = LED_MODE_NORMAL;
  59. /* 设置生效事件标志 */
  60. static volatile uint8_t set_effect_pending = 0;
  61. /* 定时器控制块 */
  62. static rt_timer_t led_timer = RT_NULL;
  63. /* ========== 统一状态机变量 ========== */
  64. static uint32_t tick = 0; // 通用计时器
  65. static uint8_t phase = 0; // 0:亮状态, 1:灭状态, (长工作模式: 0亮1灭)
  66. static uint8_t blink_done = 0; // 当前已完成的闪烁次数
  67. static uint8_t in_pause = 0; // 是否处于长暂停状态
  68. static uint8_t need_reset = 1; // 是否需要重置状态机
  69. static uint8_t is_long_work = 0; // 是否处于长工作模式(duration==0)
  70. /* 设置生效事件变量 */
  71. static uint32_t set_effect_tick = 0;
  72. static uint8_t set_effect_phase = 0;
  73. static uint8_t set_effect_count = 0;
  74. /* ========== 颜色控制函数 ========== */
  75. static void set_led_color(uint8_t red, uint8_t green, uint8_t blue)
  76. {
  77. rt_pin_write(PIN_LED_RED, red ? PIN_HIGH : PIN_LOW);
  78. rt_pin_write(PIN_LED_GREEN, green ? PIN_HIGH : PIN_LOW);
  79. rt_pin_write(PIN_LED_BLUE, blue ? PIN_HIGH : PIN_LOW);
  80. }
  81. #define COLOR_OFF() set_led_color(0, 0, 0)
  82. #define COLOR_RED() set_led_color(1, 0, 0)
  83. #define COLOR_GREEN() set_led_color(0, 1, 0)
  84. #define COLOR_BLUE() set_led_color(0, 0, 1)
  85. #define COLOR_YELLOW() set_led_color(1, 1, 0)
  86. #define COLOR_CYAN() set_led_color(0, 1, 1)
  87. #define COLOR_WHITE() set_led_color(1, 1, 1)
  88. #define COLOR_PINK() set_led_color(1, 0, 1)
  89. /* ========== NTP时间同步判断 ========== */
  90. /**
  91. * @brief 判断NTP时间是否已同步成功
  92. * @return 1=已同步, 0=未同步
  93. *
  94. * 判断逻辑:当前系统时间若为2026年5月及以前,认为未同步
  95. * 因为设备实际运行时间在2026年6月之后
  96. */
  97. static uint8_t is_ntp_synced(void)
  98. {
  99. extern struct rtc_time time_now;
  100. /* 计算当前时间的 (年*12 + 月) 值 */
  101. uint32_t current_value = (uint32_t)(time_now.year - 2000) * 12 + time_now.month;
  102. /* 2026年5月的参考值:26*12 + 5 = 317 */
  103. uint32_t threshold = 26 * 12 + 5;
  104. // rt_kprintf("current_value %d threshold %d res = %d" , current_value ,threshold, (current_value > threshold));
  105. /* 如果当前时间 <= 2026年5月,认为NTP未同步 */
  106. if (current_value <= threshold)
  107. {
  108. return 0; /* 未同步 */
  109. }
  110. return 1; /* 已同步 */
  111. }
  112. /* ========== 辅助函数 ========== */
  113. static uint8_t is_power_off(void)
  114. {
  115. return (dev_info.power_state == 0);
  116. }
  117. /**
  118. * @brief 获取正常状态(无异常)下的基础颜色
  119. * @param charging 充电状态 (0:未充电, 2:充电中)
  120. */
  121. static void set_normal_color(uint8_t charging)
  122. {
  123. if (charging == 2)
  124. {
  125. COLOR_BLUE();
  126. }
  127. else if (dev_info.time_ctrl_enable == 0)
  128. {
  129. COLOR_GREEN(); // 光控
  130. }
  131. else
  132. {
  133. COLOR_PINK(); // 时控
  134. }
  135. }
  136. /**
  137. * @brief 获取当前应该显示的颜色(用于闪烁时的亮状态)
  138. * @param charging 充电状态 (0:未充电, 2:充电中)
  139. * @param network_ok 网络状态
  140. * @param is_last 是否为本次闪烁周期的最后一次
  141. */
  142. static void set_on_color(uint8_t charging, uint8_t network_ok, uint8_t is_last)
  143. {
  144. if (!network_ok && is_last)
  145. {
  146. COLOR_CYAN(); // 断网时显示青色
  147. return;
  148. }
  149. if (is_last && !is_ntp_synced()) // ntp失效 显示白色
  150. {
  151. COLOR_WHITE();
  152. return;
  153. }
  154. switch (current_error)
  155. {
  156. case ERROR_UNDER_VOLTAGE:
  157. COLOR_RED();
  158. break;
  159. case ERROR_RAIN:
  160. COLOR_YELLOW();
  161. break;
  162. default:
  163. set_normal_color(charging);
  164. break;
  165. }
  166. }
  167. /**
  168. * @brief 长工作模式处理(duration == 0):2秒亮,2秒灭
  169. * @param charging 充电状态
  170. * @param network_ok 网络状态(长工作模式下不使用断网指示)
  171. */
  172. static void long_work_mode_process(uint8_t charging, uint8_t network_ok)
  173. {
  174. tick++;
  175. if (phase == 0) /* 亮状态 */
  176. {
  177. if (tick >= LONG_WORK_ON_TIME)
  178. {
  179. COLOR_OFF();
  180. phase = 1;
  181. tick = 0;
  182. }
  183. }
  184. else /* 灭状态 */
  185. {
  186. if (tick >= LONG_WORK_OFF_TIME)
  187. {
  188. switch (current_error)
  189. {
  190. case ERROR_UNDER_VOLTAGE:
  191. COLOR_RED();
  192. break;
  193. case ERROR_RAIN:
  194. COLOR_YELLOW();
  195. break;
  196. default:
  197. set_normal_color(charging);
  198. break;
  199. }
  200. phase = 0;
  201. tick = 0;
  202. }
  203. }
  204. }
  205. /**
  206. * @brief 快速闪烁模式处理(duration >= 1):支持次数控制和断网指示
  207. * @param charging 充电状态
  208. * @param network_ok 网络状态
  209. * @param blink_count 闪烁次数
  210. */
  211. static void fast_blink_mode_process(uint8_t charging, uint8_t network_ok, uint8_t blink_count)
  212. {
  213. if (need_reset)
  214. {
  215. need_reset = 0;
  216. phase = 0;
  217. blink_done = 0;
  218. in_pause = 0;
  219. tick = 0;
  220. uint8_t is_last = (blink_count == 1);
  221. set_on_color(charging, network_ok, is_last);
  222. return;
  223. }
  224. tick++;
  225. if (in_pause)
  226. {
  227. if (tick >= PAUSE_TIME)
  228. {
  229. in_pause = 0;
  230. blink_done = 0;
  231. phase = 0;
  232. tick = 0;
  233. uint8_t is_last = (blink_count == 1);
  234. set_on_color(charging, network_ok, is_last);
  235. }
  236. return;
  237. }
  238. if (phase == 0)
  239. {
  240. if (tick >= LED_BLINK_ON_TIME)
  241. {
  242. COLOR_OFF();
  243. phase = 1;
  244. tick = 0;
  245. }
  246. }
  247. else
  248. {
  249. if (tick >= LED_BLINK_OFF_TIME)
  250. {
  251. blink_done++;
  252. if (blink_done >= blink_count)
  253. {
  254. in_pause = 1;
  255. tick = 0;
  256. return;
  257. }
  258. phase = 0;
  259. tick = 0;
  260. uint8_t is_last = (blink_done + 1 >= blink_count);
  261. set_on_color(charging, network_ok, is_last);
  262. }
  263. }
  264. }
  265. /**
  266. * @brief 统一状态处理(正常/闪烁类异常共用)
  267. */
  268. static void unified_state_process(void)
  269. {
  270. static uint8_t last_effective_count = 0xff;
  271. uint8_t charging = sys_sta.work_state;
  272. uint8_t network_ok = ym310_mqtt_is_connected(0);
  273. uint8_t effective_count;
  274. if (is_power_off())
  275. {
  276. COLOR_OFF();
  277. return;
  278. }
  279. uint8_t is_pink_mode = (current_state == LED_STATE_NORMAL && current_error == ERROR_NONE &&
  280. charging != 2 && dev_info.time_ctrl_enable == 1);
  281. if (is_pink_mode)
  282. {
  283. effective_count = SK_BLINK_COUNT;
  284. }
  285. else
  286. {
  287. effective_count = BLINK_COUNT;
  288. }
  289. if (last_effective_count != effective_count)
  290. {
  291. last_effective_count = effective_count;
  292. need_reset = 1;
  293. is_long_work = 0;
  294. tick = 0;
  295. phase = 0;
  296. blink_done = 0;
  297. in_pause = 0;
  298. }
  299. if (effective_count == 0)
  300. {
  301. if (!is_long_work)
  302. {
  303. is_long_work = 1;
  304. need_reset = 1;
  305. phase = 0;
  306. tick = 0;
  307. }
  308. long_work_mode_process(charging, network_ok);
  309. }
  310. else
  311. {
  312. if (is_long_work)
  313. {
  314. is_long_work = 0;
  315. need_reset = 1;
  316. }
  317. fast_blink_mode_process(charging, network_ok, effective_count);
  318. }
  319. }
  320. /**
  321. * @brief 常亮状态处理(温控、倾倒)
  322. */
  323. static void solid_state_process(void)
  324. {
  325. if (is_power_off())
  326. {
  327. COLOR_OFF();
  328. return;
  329. }
  330. switch (current_error)
  331. {
  332. case ERROR_TEMP:
  333. COLOR_YELLOW();
  334. break;
  335. case ERROR_TILT:
  336. COLOR_RED();
  337. break;
  338. default:
  339. COLOR_OFF();
  340. break;
  341. }
  342. }
  343. /**
  344. * @brief 设置生效事件处理
  345. */
  346. static void set_effect_process(void)
  347. {
  348. set_effect_tick++;
  349. if (set_effect_phase == 0)
  350. {
  351. if (set_effect_tick >= LED_BLINK_ON_TIME)
  352. {
  353. COLOR_OFF();
  354. set_effect_phase = 1;
  355. set_effect_tick = 0;
  356. }
  357. }
  358. else
  359. {
  360. if (set_effect_tick >= LED_BLINK_OFF_TIME)
  361. {
  362. set_effect_count++;
  363. if (set_effect_count >= SET_EFFECT_BLINK_COUNT)
  364. {
  365. LOG_I("Set effect completed\n");
  366. current_mode = LED_MODE_NORMAL;
  367. need_reset = 1;
  368. is_long_work = 0;
  369. return;
  370. }
  371. set_effect_phase = 0;
  372. COLOR_WHITE();
  373. set_effect_tick = 0;
  374. }
  375. }
  376. }
  377. /**
  378. * @brief 检查设置生效事件
  379. */
  380. static void check_set_effect_event(void)
  381. {
  382. if (set_effect_pending)
  383. {
  384. LOG_I("Set effect event triggered\n");
  385. set_effect_pending = 0;
  386. current_mode = LED_MODE_SET_EFFECT;
  387. set_effect_tick = 0;
  388. set_effect_phase = 0;
  389. set_effect_count = 0;
  390. COLOR_WHITE();
  391. }
  392. }
  393. /**
  394. * @brief 主定时器回调
  395. */
  396. static void led_timer_callback(void *parameter)
  397. {
  398. check_set_effect_event();
  399. if (current_mode == LED_MODE_SET_EFFECT)
  400. {
  401. set_effect_process();
  402. return;
  403. }
  404. if (is_power_off())
  405. {
  406. COLOR_OFF();
  407. return;
  408. }
  409. led_update_status();
  410. switch (current_state)
  411. {
  412. case LED_STATE_NORMAL:
  413. unified_state_process();
  414. break;
  415. case LED_STATE_ERROR:
  416. if (current_error == ERROR_RAIN || current_error == ERROR_UNDER_VOLTAGE)
  417. {
  418. unified_state_process(); // 闪烁,复用正常逻辑
  419. }
  420. else
  421. {
  422. solid_state_process(); // 常亮
  423. }
  424. break;
  425. default:
  426. COLOR_OFF();
  427. break;
  428. }
  429. }
  430. /* ========== 外部API ========== */
  431. void led_trigger_set_effect(void)
  432. {
  433. set_effect_pending = 1;
  434. LOG_I("Set effect event pending\n");
  435. }
  436. void led_update_status(void)
  437. {
  438. if (current_mode != LED_MODE_NORMAL)
  439. return;
  440. led_state_t old_state = current_state;
  441. error_type_t old_error = current_error;
  442. if (is_power_off())
  443. {
  444. current_state = LED_STATE_POWER_OFF;
  445. current_error = ERROR_NONE;
  446. COLOR_OFF();
  447. return;
  448. }
  449. if (sys_sta.tilt_protect == VALID)
  450. {
  451. current_error = ERROR_TILT;
  452. current_state = LED_STATE_ERROR;
  453. }
  454. else if (sys_sta.temp_protect == VALID)
  455. {
  456. current_error = ERROR_TEMP;
  457. current_state = LED_STATE_ERROR;
  458. }
  459. else if (sys_sta.undervolt_protect == VALID)
  460. {
  461. current_error = ERROR_UNDER_VOLTAGE;
  462. current_state = LED_STATE_ERROR;
  463. }
  464. else if (sys_sta.rain_protect == VALID)
  465. {
  466. current_error = ERROR_RAIN;
  467. current_state = LED_STATE_ERROR;
  468. }
  469. else
  470. {
  471. current_state = LED_STATE_NORMAL;
  472. current_error = ERROR_NONE;
  473. }
  474. if (old_state != current_state || old_error != current_error)
  475. {
  476. LOG_I("State: %s, error=%d\n",
  477. current_state == LED_STATE_NORMAL ? "NORMAL" : "ERROR", current_error);
  478. need_reset = 1;
  479. is_long_work = 0;
  480. tick = 0;
  481. }
  482. }
  483. /**
  484. * @brief LED初始化
  485. */
  486. int led_init(void)
  487. {
  488. rt_pin_mode(PIN_LED_RED, PIN_MODE_OUTPUT);
  489. rt_pin_mode(PIN_LED_GREEN, PIN_MODE_OUTPUT);
  490. rt_pin_mode(PIN_LED_BLUE, PIN_MODE_OUTPUT);
  491. COLOR_OFF();
  492. led_timer = rt_timer_create("led_timer",
  493. led_timer_callback,
  494. RT_NULL,
  495. RT_TICK_PER_SECOND / 10,
  496. RT_TIMER_FLAG_PERIODIC | RT_TIMER_FLAG_SOFT_TIMER);
  497. if (led_timer == RT_NULL)
  498. {
  499. LOG_E("Failed to create led timer\n");
  500. return -RT_ERROR;
  501. }
  502. rt_timer_start(led_timer);
  503. LOG_I("LED driver initialized\n");
  504. LOG_I(" Light work duration: %d hours\n", BLINK_COUNT);
  505. LOG_I(" Duration=0: 2s ON/2s OFF (long work mode)\n");
  506. LOG_I(" Duration>=1: blink times depends on mode (pink uses time-control duration)\n");
  507. LOG_I(" Rain/Under-voltage: blink with color (Yellow/Red)\n");
  508. LOG_I(" Temp/Tilt: solid color (Yellow/Red)\n");
  509. LOG_I(" Last blink of each group: Cyan if disconnected\n");
  510. return RT_EOK;
  511. }
  512. INIT_APP_EXPORT(led_init);