panel_uart.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596
  1. #include "panel_uart.h"
  2. #include "dac_njw1195.h"
  3. #include "board_common.h"
  4. #include "playback_control.h"
  5. #include "settings.h"
  6. #include "dacp_client.h"
  7. #include "rtsp_events.h"
  8. #include "audio_prompt.h"
  9. #ifdef CONFIG_BT_A2DP_ENABLE
  10. #include "a2dp_sink.h"
  11. #endif
  12. #include "driver/uart.h"
  13. #include "driver/gpio.h"
  14. #include "esp_log.h"
  15. #include "esp_timer.h"
  16. #include "esp_wifi.h"
  17. #include "freertos/FreeRTOS.h"
  18. #include "freertos/task.h"
  19. #include "freertos/queue.h"
  20. static const char TAG[] = "PanelUart";
  21. #define UART_PORT UART_NUM_2
  22. #define UART_TX_GPIO 4
  23. #define UART_RX_GPIO 5
  24. #define UART_BAUD_RATE 115200
  25. #define UART_BUF_SIZE 256
  26. // 协议定义
  27. #define FRAME_HEAD_1 0x55
  28. #define FRAME_HEAD_2 0xAA
  29. #define CMD_READ 0x01
  30. #define CMD_WRITE 0x02
  31. #define CMD_REPORT 0x81
  32. // 功能监控项定义
  33. #define ITEM_SOURCE 0x01
  34. #define ITEM_BT_CONN 0x02
  35. #define ITEM_BT_AUDIO 0x03
  36. #define ITEM_DAC_MUTE 0x04
  37. #define ITEM_AUTO_RECONN 0x05
  38. #define ITEM_BATTERY 0x09
  39. #define ITEM_CHG_FULL 0x0A
  40. #define ITEM_CHG_ING 0x0B
  41. #define ITEM_VOL_LR 0x0C
  42. #define ITEM_VOL_BASS 0x0D
  43. #define ITEM_RESET 0x12
  44. #define ITEM_BASS_MUTE 0x13
  45. // 协议状态值定义 (归一化为 0 和 1)
  46. #define PROTO_VAL_OFF 0x00
  47. #define PROTO_VAL_ON 0x01
  48. #define CHG_FULL_GPIO 34
  49. #define CHG_ING_GPIO 35
  50. static TaskHandle_t s_charge_task_handle = NULL;
  51. static volatile bool s_bt_connected = false;
  52. static volatile bool s_bt_streaming = false;
  53. static volatile uint8_t s_battery_percent = 0;
  54. static volatile bool s_chg_full = false;
  55. static volatile bool s_chg_ing = false;
  56. static volatile bool s_chg_error = false;
  57. static uint8_t s_bass_before_mute = 100;
  58. extern bool s_in_panel_update; // 定义在 dac_njw1195.c 中
  59. // ----------------------------------------------------------------------------
  60. // 内部辅助函数
  61. // ----------------------------------------------------------------------------
  62. static const char* get_item_name(uint8_t item) {
  63. switch (item) {
  64. case ITEM_SOURCE: return "SOURCE";
  65. case ITEM_BT_CONN: return "BT_CONN";
  66. case ITEM_BT_AUDIO: return "BT_AUDIO";
  67. case ITEM_DAC_MUTE: return "DAC_MUTE";
  68. case ITEM_AUTO_RECONN: return "AUTO_RECONN";
  69. case ITEM_BATTERY: return "BATTERY";
  70. case ITEM_CHG_FULL: return "CHG_FULL";
  71. case ITEM_CHG_ING: return "CHG_ING";
  72. case ITEM_VOL_LR: return "VOL_LR";
  73. case ITEM_VOL_BASS: return "VOL_BASS";
  74. case ITEM_RESET: return "RESET";
  75. case ITEM_BASS_MUTE: return "BASS_MUTE";
  76. default: return "UNKNOWN";
  77. }
  78. }
  79. static void send_frame(uint8_t cmd, uint8_t item, uint8_t value) {
  80. uint8_t len = 2;
  81. uint8_t crc = cmd ^ len ^ item ^ value;
  82. uint8_t buf[7] = { FRAME_HEAD_1, FRAME_HEAD_2, cmd, len, item, value, crc };
  83. uart_write_bytes(UART_PORT, buf, 7);
  84. ESP_LOGI(TAG, "TX [%s]: %02X %02X %02X %02X %02X %02X %02X",
  85. get_item_name(item), buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
  86. }
  87. static void send_ack(void) {
  88. uint8_t ack = 0xA0; // 成功响应应答,区别于帧头 0xAA
  89. uart_write_bytes(UART_PORT, &ack, 1);
  90. ESP_LOGI(TAG, "RX ACK: %02X", ack);
  91. }
  92. static void send_report_frame(uint8_t *items, uint8_t *values, uint8_t count) {
  93. uint8_t len = 1 + count * 2;
  94. uint8_t buf[5 + len]; // Header(2) + Cmd(1) + Len(1) + Payload(len) + CRC(1)
  95. int idx = 0;
  96. buf[idx++] = FRAME_HEAD_1;
  97. buf[idx++] = FRAME_HEAD_2;
  98. buf[idx++] = CMD_REPORT;
  99. buf[idx++] = len;
  100. uint8_t crc = CMD_REPORT ^ len ^ count;
  101. buf[idx++] = count;
  102. for (int i = 0; i < count; i++) {
  103. buf[idx++] = items[i];
  104. buf[idx++] = values[i];
  105. crc ^= items[i] ^ values[i];
  106. }
  107. buf[idx++] = crc;
  108. uart_write_bytes(UART_PORT, buf, idx);
  109. char hex_str[128] = {0};
  110. int offset = 0;
  111. for (int i = 0; i < idx; i++) {
  112. offset += snprintf(hex_str + offset, sizeof(hex_str) - offset, "%02X ", buf[i]);
  113. }
  114. ESP_LOGI(TAG, "TX_RAW: %s", hex_str);
  115. }
  116. void panel_uart_report(uint8_t item, uint8_t value) {
  117. send_frame(CMD_REPORT, item, value);
  118. }
  119. // ----------------------------------------------------------------------------
  120. // GPIO 充电状态中断处理与控制任务
  121. // ----------------------------------------------------------------------------
  122. static void IRAM_ATTR charge_isr_handler(void *arg) {
  123. BaseType_t need_yield = pdFALSE;
  124. if (s_charge_task_handle != NULL) {
  125. vTaskNotifyGiveFromISR(s_charge_task_handle, &need_yield);
  126. }
  127. if (need_yield) {
  128. portYIELD_FROM_ISR();
  129. }
  130. }
  131. static void read_charge_status(void) {
  132. int full = gpio_get_level(CHG_FULL_GPIO);
  133. int ing = gpio_get_level(CHG_ING_GPIO);
  134. // 新三极管电路逻辑 (平时上拉为1,触发有效为0):
  135. // 充满: full=0, ing=1
  136. // 充电中: full=1, ing=0
  137. // 异常: full=0, ing=0
  138. // 未接充电器: full=1, ing=1
  139. s_chg_full = (full == 0 && ing == 1);
  140. s_chg_ing = (full == 1 && ing == 0);
  141. s_chg_error = (full == 0 && ing == 0);
  142. }
  143. static void charge_status_task(void *arg) {
  144. vTaskDelay(pdMS_TO_TICKS(100));
  145. read_charge_status();
  146. // 面板在上报完整数据前会舍弃单项指令。
  147. // 因此开机不在这里单独上报,由 report_status_task 延迟 2s 后的 report_all_status 统一下发初始状态。
  148. int64_t last_edge_time = 0;
  149. int toggle_count = 0;
  150. while (1) {
  151. // 600ms 超时:若 600ms 内无引脚跳变,说明电平稳定,重置翻转计数器
  152. uint32_t notified = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(600));
  153. if (notified > 0) {
  154. vTaskDelay(pdMS_TO_TICKS(30)); // 消抖
  155. int64_t now = esp_timer_get_time();
  156. int64_t delta_ms = (now - last_edge_time) / 1000;
  157. last_edge_time = now;
  158. // 芯片异常时引脚会以 250ms 周期反复翻转 (周期约 150ms ~ 400ms)
  159. if (delta_ms > 120 && delta_ms < 450) {
  160. toggle_count++;
  161. } else if (delta_ms > 600) {
  162. toggle_count = 0;
  163. }
  164. bool old_full = s_chg_full;
  165. bool old_ing = s_chg_ing;
  166. bool old_err = s_chg_error;
  167. if (toggle_count >= 2) {
  168. // 连续监测到 250ms 快速翻转,判定为充电异常
  169. s_chg_error = true;
  170. s_chg_full = false;
  171. s_chg_ing = false;
  172. } else if (toggle_count == 0) {
  173. read_charge_status();
  174. }
  175. if (s_chg_full != old_full || s_chg_ing != old_ing || s_chg_error != old_err) {
  176. if (s_chg_error) {
  177. send_frame(CMD_REPORT, ITEM_CHG_ING, 0xFE); // 充电异常错误码
  178. ESP_LOGE(TAG, "Chg: Error (250ms Toggling Detected)");
  179. } else if (s_chg_full) {
  180. send_frame(CMD_REPORT, ITEM_CHG_FULL, 1);
  181. ESP_LOGI(TAG, "Chg: Full");
  182. } else if (s_chg_ing) {
  183. send_frame(CMD_REPORT, ITEM_CHG_ING, 1);
  184. ESP_LOGI(TAG, "Chg: Charging");
  185. } else {
  186. send_frame(CMD_REPORT, ITEM_CHG_ING, 0);
  187. ESP_LOGI(TAG, "Chg: Discharging");
  188. }
  189. }
  190. } else {
  191. // 600ms 超时无新中断,判定电平已经稳定
  192. if (toggle_count > 0 || s_chg_error) {
  193. toggle_count = 0;
  194. bool old_full = s_chg_full;
  195. bool old_ing = s_chg_ing;
  196. bool old_err = s_chg_error;
  197. read_charge_status();
  198. if (s_chg_full != old_full || s_chg_ing != old_ing || s_chg_error != old_err) {
  199. if (s_chg_full) {
  200. send_frame(CMD_REPORT, ITEM_CHG_FULL, 1);
  201. ESP_LOGI(TAG, "Chg: Full (Recovered)");
  202. } else if (s_chg_ing) {
  203. send_frame(CMD_REPORT, ITEM_CHG_ING, 1);
  204. ESP_LOGI(TAG, "Chg: Charging (Recovered)");
  205. } else {
  206. send_frame(CMD_REPORT, ITEM_CHG_ING, 0);
  207. ESP_LOGI(TAG, "Chg: Discharging (Recovered)");
  208. }
  209. }
  210. }
  211. }
  212. }
  213. }
  214. // ----------------------------------------------------------------------------
  215. // 全部状态主动上报
  216. // ----------------------------------------------------------------------------
  217. static void report_all_status(void) {
  218. // 上报全部 10 项面板状态参数
  219. uint8_t rpt_items[] = {
  220. ITEM_SOURCE, ITEM_BT_CONN, ITEM_BT_AUDIO, ITEM_DAC_MUTE,
  221. ITEM_AUTO_RECONN, ITEM_VOL_LR, ITEM_VOL_BASS,
  222. ITEM_BATTERY, ITEM_CHG_FULL, ITEM_CHG_ING
  223. };
  224. uint8_t rpt_values[] = {
  225. (dac_njw1195_get_source() == NJW_SOURCE_BT) ? PROTO_VAL_ON : PROTO_VAL_OFF,
  226. s_bt_connected ? PROTO_VAL_ON : PROTO_VAL_OFF,
  227. s_bt_streaming ? PROTO_VAL_ON : PROTO_VAL_OFF,
  228. PROTO_VAL_OFF, // DAC 硬件静音状态(默认不静音,为 0)
  229. PROTO_VAL_ON, // 自动连接默认开启
  230. dac_njw1195_get_vol_main(),
  231. dac_njw1195_get_vol_bass(),
  232. s_battery_percent,
  233. s_chg_full ? 1 : 0,
  234. s_chg_ing ? 1 : 0
  235. };
  236. send_report_frame(rpt_items, rpt_values, 10);
  237. ESP_LOGI(TAG, "主动上报 (10 项)");
  238. }
  239. // ----------------------------------------------------------------------------
  240. // 电池电量定期自动上报任务
  241. // ----------------------------------------------------------------------------
  242. static void report_status_task(void *arg) {
  243. // 开机等待 2 秒(等待各模块初始化稳定后),发送包含电量、连接等 10 项的完整状态报告
  244. vTaskDelay(pdMS_TO_TICKS(2000));
  245. int pct = 0;
  246. bool chg = false;
  247. // 初始化并读取真实电量
  248. if (board_battery_read(&pct, &chg)) {
  249. s_battery_percent = pct;
  250. }
  251. // 开机 2s 后主动推送全局初始化状态(包含正确的初始电量)
  252. report_all_status();
  253. uint8_t last_sent_pct = s_battery_percent;
  254. bool low_battery_played = false;
  255. while (1) {
  256. if (board_battery_read(&pct, &chg)) {
  257. s_chg_ing = chg;
  258. // 充电中只增不减,放电中只减不增
  259. if (chg) {
  260. if (pct > s_battery_percent) {
  261. s_battery_percent = pct;
  262. }
  263. } else { // 放电中:只允许容量值单调减少
  264. if (pct < s_battery_percent) {
  265. s_battery_percent = pct;
  266. }
  267. }
  268. }
  269. if (s_battery_percent != last_sent_pct) {
  270. panel_uart_report(ITEM_BATTERY, s_battery_percent);
  271. #ifdef CONFIG_BT_A2DP_ENABLE
  272. bt_a2dp_send_battery_level(s_battery_percent);
  273. #endif
  274. last_sent_pct = s_battery_percent;
  275. ESP_LOGI(TAG, "Bat: %d%%", s_battery_percent);
  276. }
  277. // 低电量报警逻辑
  278. if (s_battery_percent <= 10 && !low_battery_played && !chg) {
  279. ESP_LOGW(TAG, "触发低电量警告!");
  280. audio_prompt_play(PROMPT_LOW_BATTERY);
  281. low_battery_played = true;
  282. } else if (s_battery_percent > 15 || chg) {
  283. low_battery_played = false;
  284. }
  285. vTaskDelay(pdMS_TO_TICKS(2000));
  286. }
  287. }
  288. // ----------------------------------------------------------------------------
  289. // 协议接收和处理逻辑
  290. // ----------------------------------------------------------------------------
  291. static void protocol_handler(uint8_t *data, int len) {
  292. for (int i = 0; i < len; i++) {
  293. if (data[i] != FRAME_HEAD_1) continue;
  294. if (i + 1 < len && data[i+1] != FRAME_HEAD_2) continue;
  295. if (i + 4 > len) break; // 至少包含 55 AA CMD LEN
  296. uint8_t cmd = data[i+2];
  297. uint8_t pkt_len = data[i+3];
  298. if (i + 4 + pkt_len + 1 > len) break; // 等待完整包 (含 CRC)
  299. uint8_t *payload = &data[i+4];
  300. uint8_t expected_crc = cmd ^ pkt_len;
  301. for (int j = 0; j < pkt_len; j++) {
  302. expected_crc ^= payload[j];
  303. }
  304. uint8_t actual_crc = data[i + 4 + pkt_len];
  305. if (actual_crc != expected_crc) {
  306. ESP_LOGW(TAG, "CRC 校验错误: 预期 %02X, 实际 %02X", expected_crc, actual_crc);
  307. continue;
  308. }
  309. if (cmd == CMD_READ) {
  310. // Read expect LEN=2: [ITEM] [VALUE]
  311. if (pkt_len == 2) {
  312. uint8_t item = payload[0];
  313. if (item == 0x00) {
  314. report_all_status();
  315. } else {
  316. switch (item) {
  317. case ITEM_SOURCE:
  318. send_frame(CMD_READ, ITEM_SOURCE, (dac_njw1195_get_source() == NJW_SOURCE_BT) ? PROTO_VAL_ON : PROTO_VAL_OFF);
  319. break;
  320. case ITEM_BT_CONN:
  321. send_frame(CMD_READ, ITEM_BT_CONN, s_bt_connected ? PROTO_VAL_ON : PROTO_VAL_OFF);
  322. break;
  323. case ITEM_BT_AUDIO:
  324. send_frame(CMD_READ, ITEM_BT_AUDIO, s_bt_streaming ? PROTO_VAL_ON : PROTO_VAL_OFF);
  325. break;
  326. case ITEM_VOL_LR:
  327. send_frame(CMD_READ, ITEM_VOL_LR, dac_njw1195_get_vol_main());
  328. break;
  329. case ITEM_VOL_BASS:
  330. send_frame(CMD_READ, ITEM_VOL_BASS, dac_njw1195_get_vol_bass());
  331. break;
  332. case ITEM_DAC_MUTE:
  333. send_frame(CMD_READ, ITEM_DAC_MUTE, dac_njw1195_get_mute() ? PROTO_VAL_OFF : PROTO_VAL_ON);
  334. break;
  335. case ITEM_AUTO_RECONN:
  336. send_frame(CMD_READ, ITEM_AUTO_RECONN, PROTO_VAL_ON);
  337. break;
  338. case ITEM_BATTERY:
  339. send_frame(CMD_READ, ITEM_BATTERY, s_battery_percent);
  340. break;
  341. case ITEM_CHG_FULL:
  342. send_frame(CMD_READ, ITEM_CHG_FULL, s_chg_full ? 1 : 0);
  343. break;
  344. case ITEM_CHG_ING:
  345. send_frame(CMD_READ, ITEM_CHG_ING, s_chg_ing ? 1 : 0);
  346. break;
  347. default:
  348. send_frame(CMD_READ, item, 0xFF);
  349. break;
  350. }
  351. }
  352. }
  353. } else if (cmd == CMD_WRITE) {
  354. if (pkt_len == 2) {
  355. uint8_t item = payload[0];
  356. uint8_t value = payload[1];
  357. switch (item) {
  358. case ITEM_SOURCE:
  359. if (value == PROTO_VAL_ON) {
  360. dac_njw1195_set_source(NJW_SOURCE_BT);
  361. send_ack();
  362. } else if (value == PROTO_VAL_OFF) {
  363. dac_njw1195_set_source(NJW_SOURCE_AUX);
  364. send_ack();
  365. } else {
  366. send_frame(CMD_WRITE, ITEM_SOURCE, 0xFF);
  367. }
  368. break;
  369. case ITEM_BT_CONN:
  370. if (value == PROTO_VAL_OFF) {
  371. ESP_LOGI(TAG, "RX: Reset");
  372. #ifdef CONFIG_BT_A2DP_ENABLE
  373. settings_clear_last_bt_bda();
  374. bt_a2dp_sink_disconnect_current();
  375. #endif
  376. send_ack();
  377. } else {
  378. send_frame(CMD_WRITE, ITEM_BT_CONN, 0xFF);
  379. }
  380. break;
  381. case ITEM_DAC_MUTE:
  382. if (value == PROTO_VAL_ON || value == PROTO_VAL_OFF) {
  383. dac_njw1195_set_mute(value == PROTO_VAL_OFF);
  384. send_ack();
  385. } else {
  386. send_frame(CMD_WRITE, ITEM_DAC_MUTE, 0xFF);
  387. }
  388. break;
  389. case ITEM_AUTO_RECONN:
  390. if (value == PROTO_VAL_ON || value == PROTO_VAL_OFF) {
  391. // 如果有开启/关闭自动回连的需求,在这里处理
  392. send_ack();
  393. } else {
  394. send_frame(CMD_WRITE, ITEM_AUTO_RECONN, 0xFF);
  395. }
  396. break;
  397. case ITEM_VOL_LR:
  398. ESP_LOGD(TAG, "面板音量设置为: %d", value);
  399. s_in_panel_update = true;
  400. dac_njw1195_set_vol_main(value);
  401. ESP_LOGD(TAG, "同步音量到播放器...");
  402. playback_control_set_volume_percent(value);
  403. s_in_panel_update = false;
  404. ESP_LOGD(TAG, "音量已同步,发送 ACK 应答...");
  405. send_ack();
  406. break;
  407. case ITEM_VOL_BASS:
  408. dac_njw1195_set_vol_bass(value);
  409. send_ack();
  410. break;
  411. case ITEM_RESET:
  412. send_ack();
  413. vTaskDelay(pdMS_TO_TICKS(50));
  414. settings_clear_wifi_credentials();
  415. esp_wifi_restore(); // 清除 WiFi 信息
  416. esp_restart();
  417. break;
  418. case ITEM_BASS_MUTE:
  419. if (value == PROTO_VAL_ON) {
  420. dac_njw1195_set_vol_bass(s_bass_before_mute);
  421. send_ack();
  422. } else if (value == PROTO_VAL_OFF) {
  423. s_bass_before_mute = dac_njw1195_get_vol_bass();
  424. dac_njw1195_set_vol_bass(0);
  425. send_ack();
  426. } else {
  427. send_frame(CMD_WRITE, ITEM_BASS_MUTE, 0xFF);
  428. }
  429. break;
  430. default:
  431. send_frame(CMD_WRITE, item, 0xFF);
  432. break;
  433. }
  434. }
  435. }
  436. i += 4 + pkt_len; // 跳过已处理的字节帧
  437. }
  438. }
  439. // ----------------------------------------------------------------------------
  440. // 串口接收读取任务
  441. // ----------------------------------------------------------------------------
  442. static void uart_task(void *arg) {
  443. uint8_t rx_buf[UART_BUF_SIZE];
  444. while (1) {
  445. int len = uart_read_bytes(UART_PORT, rx_buf, sizeof(rx_buf), pdMS_TO_TICKS(50));
  446. if (len > 0) {
  447. char hex_str[256] = {0};
  448. int offset = 0;
  449. for (int i = 0; i < len && offset < sizeof(hex_str) - 4; i++) {
  450. offset += snprintf(hex_str + offset, sizeof(hex_str) - offset, "%02X ", rx_buf[i]);
  451. }
  452. ESP_LOGI(TAG, "RX_RAW: %s", hex_str);
  453. protocol_handler(rx_buf, len);
  454. }
  455. }
  456. }
  457. // RTSP 事件回调,统一处理蓝牙和 AirPlay 的连接与播放状态
  458. static void on_rtsp_event(rtsp_event_t event, const rtsp_event_data_t *data, void *user_data) {
  459. (void)data;
  460. (void)user_data;
  461. switch (event) {
  462. case RTSP_EVENT_CLIENT_CONNECTED:
  463. panel_uart_set_bt_state(true, s_bt_streaming);
  464. break;
  465. case RTSP_EVENT_DISCONNECTED:
  466. panel_uart_set_bt_state(false, false);
  467. break;
  468. case RTSP_EVENT_PLAYING:
  469. panel_uart_set_bt_state(s_bt_connected, true);
  470. break;
  471. case RTSP_EVENT_PAUSED:
  472. panel_uart_set_bt_state(s_bt_connected, false);
  473. break;
  474. default:
  475. break;
  476. }
  477. }
  478. // ----------------------------------------------------------------------------
  479. // 公共对外 API 接口
  480. // ----------------------------------------------------------------------------
  481. esp_err_t panel_uart_init(void) {
  482. // 1. 初始化串口端口 2
  483. uart_config_t uart_config = {
  484. .baud_rate = UART_BAUD_RATE,
  485. .data_bits = UART_DATA_8_BITS,
  486. .parity = UART_PARITY_DISABLE,
  487. .stop_bits = UART_STOP_BITS_1,
  488. .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
  489. .source_clk = UART_SCLK_DEFAULT,
  490. };
  491. ESP_ERROR_CHECK(uart_param_config(UART_PORT, &uart_config));
  492. ESP_ERROR_CHECK(uart_set_pin(UART_PORT, UART_TX_GPIO, UART_RX_GPIO, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
  493. ESP_ERROR_CHECK(uart_driver_install(UART_PORT, UART_BUF_SIZE * 2, UART_BUF_SIZE * 2, 0, NULL, 0));
  494. // 2. 初始化充电状态检测中断 GPIO
  495. gpio_config_t io_conf = {
  496. .pin_bit_mask = (1ULL << CHG_FULL_GPIO) | (1ULL << CHG_ING_GPIO),
  497. .mode = GPIO_MODE_INPUT,
  498. .pull_up_en = GPIO_PULLUP_DISABLE,
  499. .pull_down_en = GPIO_PULLDOWN_DISABLE,
  500. .intr_type = GPIO_INTR_ANYEDGE,
  501. };
  502. ESP_ERROR_CHECK(gpio_config(&io_conf));
  503. // 全局安装中断服务,如果已安装则安全跳过 (兼容 IDF)
  504. gpio_install_isr_service(0);
  505. ESP_ERROR_CHECK(gpio_isr_handler_add(CHG_FULL_GPIO, charge_isr_handler, NULL));
  506. ESP_ERROR_CHECK(gpio_isr_handler_add(CHG_ING_GPIO, charge_isr_handler, NULL));
  507. // 注册 RTSP 事件监听器 (自动上报蓝牙和 AirPlay 的连接及播放状态)
  508. rtsp_events_register(on_rtsp_event, NULL);
  509. // 3. 创建执行任务
  510. xTaskCreate(uart_task, "uart_task", 4096, NULL, 5, NULL);
  511. xTaskCreate(charge_status_task, "chg_stat", 4096, NULL, 3, &s_charge_task_handle);
  512. xTaskCreate(report_status_task, "rpt_stat", 4096, NULL, 2, NULL);
  513. ESP_LOGI(TAG, "UART init OK, TX=%d RX=%d", UART_TX_GPIO, UART_RX_GPIO);
  514. return ESP_OK;
  515. }
  516. void panel_uart_set_bt_state(bool connected, bool streaming) {
  517. if (s_bt_connected != connected) {
  518. s_bt_connected = connected;
  519. panel_uart_report(ITEM_BT_CONN, s_bt_connected ? PROTO_VAL_ON : PROTO_VAL_OFF);
  520. }
  521. if (s_bt_streaming != streaming) {
  522. s_bt_streaming = streaming;
  523. panel_uart_report(ITEM_BT_AUDIO, s_bt_streaming ? PROTO_VAL_ON : PROTO_VAL_OFF);
  524. }
  525. }
  526. uint8_t panel_uart_get_battery_percent(void) {
  527. return s_battery_percent;
  528. }
  529. bool panel_uart_get_charging_status(void) {
  530. return s_chg_ing;
  531. }