led.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666
  1. #include "led.h"
  2. #include "esp_log.h"
  3. #include "esp_timer.h"
  4. #include "freertos/FreeRTOS.h"
  5. #include "freertos/timers.h"
  6. #include "rtsp_events.h"
  7. #include "settings.h"
  8. #if CONFIG_LED_STATUS_GPIO >= 0 || CONFIG_LED_ERROR_GPIO >= 0
  9. #include "driver/ledc.h"
  10. #endif
  11. #include <math.h>
  12. // Convert Kconfig boolean values to C macros
  13. #ifdef CONFIG_LED_STATUS_INVERT
  14. #define LED_STATUS_INVERT_VAL 1
  15. #else
  16. #define LED_STATUS_INVERT_VAL 0
  17. #endif
  18. #ifdef CONFIG_LED_ERROR_INVERT
  19. #define LED_ERROR_INVERT_VAL 1
  20. #else
  21. #define LED_ERROR_INVERT_VAL 0
  22. #endif
  23. static const char *TAG = "led";
  24. // Module-level brightness (0–255), shared across all LED types.
  25. // Loaded from NVS in led_init(); updated by led_set_brightness().
  26. static uint8_t s_brightness = CONFIG_LED_STATUS_BRIGHTNESS;
  27. // ============================================================================
  28. // Configuration helpers - map Kconfig to led_mode_t
  29. // ============================================================================
  30. static led_mode_t get_status_mode_playing(void) {
  31. #if defined(CONFIG_LED_STATUS_PLAYING_VU)
  32. return LED_VU;
  33. #elif defined(CONFIG_LED_STATUS_PLAYING_BLINK_FAST)
  34. return LED_BLINK_FAST;
  35. #else
  36. return LED_STEADY;
  37. #endif
  38. }
  39. static led_mode_t get_status_mode_paused(void) {
  40. #if defined(CONFIG_LED_STATUS_PAUSED_OFF)
  41. return LED_OFF;
  42. #elif defined(CONFIG_LED_STATUS_PAUSED_STEADY)
  43. return LED_STEADY;
  44. #elif defined(CONFIG_LED_STATUS_PAUSED_BLINK_SLOW)
  45. return LED_BLINK_SLOW;
  46. #else
  47. return LED_BLINK_MEDIUM;
  48. #endif
  49. }
  50. static led_mode_t get_status_mode_standby(void) {
  51. #if defined(CONFIG_LED_STATUS_STANDBY_OFF)
  52. return LED_OFF;
  53. #elif defined(CONFIG_LED_STATUS_STANDBY_BLINK_MEDIUM)
  54. return LED_BLINK_MEDIUM;
  55. #else
  56. return LED_BLINK_SLOW;
  57. #endif
  58. }
  59. static led_mode_t get_rgb_mode_playing(void) {
  60. #if defined(CONFIG_LED_RGB_PLAYING_OFF)
  61. return LED_OFF;
  62. #elif defined(CONFIG_LED_RGB_PLAYING_STEADY)
  63. return LED_STEADY;
  64. #else
  65. return LED_VU;
  66. #endif
  67. }
  68. static led_mode_t get_rgb_mode_paused(void) {
  69. #if defined(CONFIG_LED_RGB_PAUSED_OFF)
  70. return LED_OFF;
  71. #else
  72. return LED_STEADY;
  73. #endif
  74. }
  75. static led_mode_t get_rgb_mode_standby(void) {
  76. #if defined(CONFIG_LED_RGB_STANDBY_STEADY)
  77. return LED_STEADY;
  78. #else
  79. return LED_OFF;
  80. #endif
  81. }
  82. // ============================================================================
  83. // Status LED (single color via LEDC PWM)
  84. // ============================================================================
  85. #if CONFIG_LED_STATUS_GPIO >= 0
  86. #define STATUS_LED_CHANNEL LEDC_CHANNEL_0
  87. #define STATUS_LED_TIMER LEDC_TIMER_0
  88. static led_mode_t s_status_mode = LED_OFF;
  89. static TimerHandle_t s_status_timer = NULL;
  90. static bool s_status_on = false;
  91. static uint8_t s_status_duty = CONFIG_LED_STATUS_BRIGHTNESS;
  92. static void status_led_set_duty(uint8_t duty) {
  93. ledc_set_duty(LEDC_LOW_SPEED_MODE, STATUS_LED_CHANNEL, duty);
  94. ledc_update_duty(LEDC_LOW_SPEED_MODE, STATUS_LED_CHANNEL);
  95. ESP_LOGV(TAG, "Status LED duty set to %d", duty);
  96. }
  97. static void status_timer_cb(TimerHandle_t xTimer) {
  98. (void)xTimer;
  99. s_status_on = !s_status_on;
  100. status_led_set_duty(s_status_on ? s_status_duty : 0);
  101. ESP_LOGD(TAG, "Status LED timer: mode=%d, state=%s", s_status_mode,
  102. s_status_on ? "ON" : "OFF");
  103. uint32_t period_ms;
  104. switch (s_status_mode) {
  105. case LED_BLINK_SLOW:
  106. period_ms = s_status_on ? 100 : 2500;
  107. break;
  108. case LED_BLINK_MEDIUM:
  109. period_ms = 500;
  110. break;
  111. case LED_BLINK_FAST:
  112. period_ms = 250;
  113. break;
  114. default:
  115. return;
  116. }
  117. TickType_t ticks = pdMS_TO_TICKS(period_ms);
  118. if (ticks == 0) {
  119. ticks = 1;
  120. }
  121. BaseType_t ret = xTimerChangePeriod(s_status_timer, ticks, 10);
  122. if (ret != pdPASS) {
  123. ESP_LOGW(TAG, "Failed to change timer period: %d", ret);
  124. }
  125. }
  126. static void status_led_init(void) {
  127. s_status_duty = s_brightness;
  128. ledc_timer_config_t timer_cfg = {
  129. .speed_mode = LEDC_LOW_SPEED_MODE,
  130. .timer_num = STATUS_LED_TIMER,
  131. .duty_resolution = LEDC_TIMER_8_BIT,
  132. .freq_hz = 1000,
  133. .clk_cfg = LEDC_AUTO_CLK,
  134. };
  135. if (ledc_timer_config(&timer_cfg) != ESP_OK) {
  136. ESP_LOGE(TAG, "Status LED timer init failed");
  137. return;
  138. }
  139. ledc_channel_config_t ch_cfg = {
  140. .speed_mode = LEDC_LOW_SPEED_MODE,
  141. .channel = STATUS_LED_CHANNEL,
  142. .timer_sel = STATUS_LED_TIMER,
  143. .intr_type = LEDC_INTR_DISABLE,
  144. .gpio_num = CONFIG_LED_STATUS_GPIO,
  145. .duty = 0,
  146. .hpoint = 0,
  147. .flags = {.output_invert = LED_STATUS_INVERT_VAL},
  148. };
  149. if (ledc_channel_config(&ch_cfg) != ESP_OK) {
  150. ESP_LOGE(TAG, "Status LED channel init failed");
  151. return;
  152. }
  153. // Explicitly apply initial duty (off)
  154. ledc_set_duty(LEDC_LOW_SPEED_MODE, STATUS_LED_CHANNEL, 0);
  155. ledc_update_duty(LEDC_LOW_SPEED_MODE, STATUS_LED_CHANNEL);
  156. s_status_timer = xTimerCreate("status_led", pdMS_TO_TICKS(500), pdFALSE, NULL,
  157. status_timer_cb);
  158. if (s_status_timer == NULL) {
  159. ESP_LOGE(TAG, "Failed to create status LED timer");
  160. return;
  161. }
  162. ESP_LOGI(TAG, "Status LED initialized on GPIO %d", CONFIG_LED_STATUS_GPIO);
  163. }
  164. static void status_led_set_mode(led_mode_t mode) {
  165. if (mode == s_status_mode) {
  166. return;
  167. }
  168. ESP_LOGD(TAG, "Status LED mode change: %d -> %d", s_status_mode, mode);
  169. s_status_mode = mode;
  170. if (s_status_timer && xTimerIsTimerActive(s_status_timer)) {
  171. BaseType_t ret = xTimerStop(s_status_timer, 10);
  172. if (ret != pdPASS) {
  173. ESP_LOGW(TAG, "Failed to stop status LED timer: %d", ret);
  174. }
  175. }
  176. switch (mode) {
  177. case LED_OFF:
  178. ESP_LOGD(TAG, "Status LED: OFF");
  179. status_led_set_duty(0);
  180. break;
  181. case LED_STEADY:
  182. ESP_LOGD(TAG, "Status LED: STEADY (duty=%d)", s_status_duty);
  183. status_led_set_duty(s_status_duty);
  184. break;
  185. case LED_BLINK_SLOW:
  186. case LED_BLINK_MEDIUM:
  187. case LED_BLINK_FAST:
  188. // Reset state and turn LED on for first blink cycle
  189. s_status_on =
  190. false; // Will be toggled to true immediately in first timer callback
  191. if (!s_status_timer) {
  192. ESP_LOGE(TAG, "Status LED timer not initialized!");
  193. break;
  194. }
  195. BaseType_t ret = xTimerStart(s_status_timer, 10);
  196. if (ret != pdPASS) {
  197. ESP_LOGE(TAG, "Failed to start status LED timer: %d", ret);
  198. } else {
  199. ESP_LOGD(TAG, "Status LED: BLINK mode %d started", mode);
  200. // Immediately trigger first state to avoid initial delay
  201. status_timer_cb(s_status_timer);
  202. }
  203. break;
  204. case LED_VU:
  205. ESP_LOGD(TAG, "Status LED: VU mode (initial OFF)");
  206. // Initialize to OFF, will be updated by led_audio_feed()
  207. status_led_set_duty(0);
  208. break;
  209. }
  210. }
  211. static void status_led_set_vu(float norm) {
  212. if (s_status_mode != LED_VU) {
  213. return;
  214. }
  215. uint8_t duty = (uint8_t)(norm * (float)s_status_duty);
  216. status_led_set_duty(duty);
  217. }
  218. #else
  219. static void status_led_init(void) {
  220. }
  221. static void status_led_set_mode(led_mode_t mode) {
  222. (void)mode;
  223. }
  224. static void status_led_set_vu(float norm) {
  225. (void)norm;
  226. }
  227. #endif
  228. // ============================================================================
  229. // Error LED (simple on/off via LEDC)
  230. // ============================================================================
  231. #if CONFIG_LED_ERROR_GPIO >= 0
  232. #define ERROR_LED_CHANNEL LEDC_CHANNEL_1
  233. #define ERROR_LED_TIMER LEDC_TIMER_1
  234. static void error_led_init(void) {
  235. ledc_timer_config_t timer_cfg = {
  236. .speed_mode = LEDC_LOW_SPEED_MODE,
  237. .timer_num = ERROR_LED_TIMER,
  238. .duty_resolution = LEDC_TIMER_8_BIT,
  239. .freq_hz = 1000,
  240. .clk_cfg = LEDC_AUTO_CLK,
  241. };
  242. if (ledc_timer_config(&timer_cfg) != ESP_OK) {
  243. ESP_LOGE(TAG, "Error LED timer init failed");
  244. return;
  245. }
  246. ledc_channel_config_t ch_cfg = {
  247. .speed_mode = LEDC_LOW_SPEED_MODE,
  248. .channel = ERROR_LED_CHANNEL,
  249. .timer_sel = ERROR_LED_TIMER,
  250. .intr_type = LEDC_INTR_DISABLE,
  251. .gpio_num = CONFIG_LED_ERROR_GPIO,
  252. .duty = 0,
  253. .hpoint = 0,
  254. .flags = {.output_invert = LED_ERROR_INVERT_VAL},
  255. };
  256. if (ledc_channel_config(&ch_cfg) != ESP_OK) {
  257. ESP_LOGE(TAG, "Error LED channel init failed");
  258. return;
  259. }
  260. // Explicitly apply initial duty (off)
  261. ledc_set_duty(LEDC_LOW_SPEED_MODE, ERROR_LED_CHANNEL, 0);
  262. ledc_update_duty(LEDC_LOW_SPEED_MODE, ERROR_LED_CHANNEL);
  263. ESP_LOGI(TAG, "Error LED initialized on GPIO %d", CONFIG_LED_ERROR_GPIO);
  264. }
  265. static void error_led_set(bool on) {
  266. ledc_set_duty(LEDC_LOW_SPEED_MODE, ERROR_LED_CHANNEL, on ? s_brightness : 0);
  267. ledc_update_duty(LEDC_LOW_SPEED_MODE, ERROR_LED_CHANNEL);
  268. }
  269. #else
  270. static void error_led_init(void) {
  271. }
  272. static void error_led_set(bool on) {
  273. (void)on;
  274. }
  275. #endif
  276. // ============================================================================
  277. // RGB LED (WS2812 via led_strip)
  278. // ============================================================================
  279. #if CONFIG_LED_RGB_GPIO >= 0
  280. #include "led_strip.h"
  281. static led_strip_handle_t s_rgb_strip = NULL;
  282. static led_mode_t s_rgb_mode = LED_OFF;
  283. static void rgb_led_init(void) {
  284. led_strip_config_t strip_cfg = {
  285. .strip_gpio_num = CONFIG_LED_RGB_GPIO,
  286. .max_leds = 1,
  287. .led_model = LED_MODEL_WS2812,
  288. .flags.invert_out = false,
  289. };
  290. led_strip_rmt_config_t rmt_cfg = {
  291. .clk_src = RMT_CLK_SRC_DEFAULT,
  292. .resolution_hz = 10 * 1000 * 1000,
  293. .flags.with_dma = false,
  294. };
  295. if (led_strip_new_rmt_device(&strip_cfg, &rmt_cfg, &s_rgb_strip) != ESP_OK) {
  296. ESP_LOGE(TAG, "RGB LED init failed");
  297. s_rgb_strip = NULL;
  298. return;
  299. }
  300. led_strip_clear(s_rgb_strip);
  301. ESP_LOGI(TAG, "RGB LED initialized on GPIO %d", CONFIG_LED_RGB_GPIO);
  302. }
  303. static void rgb_led_set_color(uint8_t r, uint8_t g, uint8_t b) {
  304. if (!s_rgb_strip) {
  305. return;
  306. }
  307. led_strip_set_pixel(s_rgb_strip, 0, r, g, b);
  308. led_strip_refresh(s_rgb_strip);
  309. }
  310. static void rgb_led_clear(void) {
  311. if (!s_rgb_strip) {
  312. return;
  313. }
  314. led_strip_clear(s_rgb_strip);
  315. led_strip_refresh(s_rgb_strip);
  316. }
  317. static void rgb_led_set_mode(led_mode_t mode) {
  318. s_rgb_mode = mode;
  319. switch (mode) {
  320. case LED_OFF:
  321. rgb_led_clear();
  322. break;
  323. case LED_STEADY: {
  324. // Color depends on current state - handled by on_rtsp_event
  325. break;
  326. }
  327. case LED_VU:
  328. // Handled by led_audio_feed
  329. break;
  330. default:
  331. break;
  332. }
  333. }
  334. static void rgb_led_set_vu(float norm, float bass_ratio) {
  335. if (s_rgb_mode != LED_VU || !s_rgb_strip) {
  336. return;
  337. }
  338. if (norm <= 0.0f || s_brightness == 0) {
  339. rgb_led_clear();
  340. return;
  341. }
  342. uint8_t val = (uint8_t)(norm * (float)s_brightness);
  343. if (val < 1) {
  344. val = 1;
  345. }
  346. // Map to HSV hue: 170 (blue, quiet) -> 85 (green, medium) -> 0 (red, loud)
  347. uint16_t hue = (uint16_t)(170.0f * (1.0f - norm));
  348. // Shift towards purple/magenta when bassy
  349. if (bass_ratio > 0.3f) {
  350. hue = (uint16_t)(hue + (uint16_t)(bass_ratio * 60.0f));
  351. if (hue > 255) {
  352. hue = 255;
  353. }
  354. }
  355. // High saturation, reduce slightly at very high energy for warm white
  356. uint8_t sat = 255;
  357. if (norm > 0.85f) {
  358. sat = (uint8_t)(255 - (uint8_t)((norm - 0.85f) / 0.15f * 80.0f));
  359. }
  360. led_strip_set_pixel_hsv(s_rgb_strip, 0, hue, sat, val);
  361. led_strip_refresh(s_rgb_strip);
  362. }
  363. #else
  364. static void rgb_led_init(void) {
  365. }
  366. static void rgb_led_set_mode(led_mode_t mode) {
  367. (void)mode;
  368. }
  369. static void rgb_led_set_color(uint8_t r, uint8_t g, uint8_t b) {
  370. (void)r;
  371. (void)g;
  372. (void)b;
  373. }
  374. static void rgb_led_clear(void) {
  375. }
  376. static void rgb_led_set_vu(float norm, float bass_ratio) {
  377. (void)norm;
  378. (void)bass_ratio;
  379. }
  380. #endif
  381. // ============================================================================
  382. // RTSP Event Handler
  383. // ============================================================================
  384. typedef enum {
  385. STATE_STANDBY,
  386. STATE_PAUSED,
  387. STATE_PLAYING,
  388. STATE_ERROR,
  389. } led_state_t;
  390. static led_state_t s_prev_state = STATE_STANDBY;
  391. static led_state_t s_current_state = STATE_STANDBY;
  392. static uint8_t scale_bright(uint8_t v) {
  393. return (uint8_t)((uint16_t)v * s_brightness / 255);
  394. }
  395. static void render_state(led_state_t state) {
  396. switch (state) {
  397. case STATE_PLAYING:
  398. status_led_set_mode(get_status_mode_playing());
  399. rgb_led_set_mode(get_rgb_mode_playing());
  400. error_led_set(false);
  401. break;
  402. case STATE_PAUSED:
  403. status_led_set_mode(get_status_mode_paused());
  404. rgb_led_set_mode(get_rgb_mode_paused());
  405. if (get_rgb_mode_paused() == LED_STEADY) {
  406. #ifdef CONFIG_LED_RGB_COLOR_PAUSED
  407. uint32_t c = CONFIG_LED_RGB_COLOR_PAUSED;
  408. rgb_led_set_color(scale_bright((c >> 16) & 0xFF),
  409. scale_bright((c >> 8) & 0xFF), scale_bright(c & 0xFF));
  410. #else
  411. rgb_led_set_color(0, 0, scale_bright(0x33));
  412. #endif
  413. }
  414. error_led_set(false);
  415. break;
  416. case STATE_STANDBY:
  417. status_led_set_mode(get_status_mode_standby());
  418. rgb_led_set_mode(get_rgb_mode_standby());
  419. if (get_rgb_mode_standby() == LED_STEADY) {
  420. #ifdef CONFIG_LED_RGB_COLOR_STANDBY
  421. uint32_t c = CONFIG_LED_RGB_COLOR_STANDBY;
  422. rgb_led_set_color(scale_bright((c >> 16) & 0xFF),
  423. scale_bright((c >> 8) & 0xFF), scale_bright(c & 0xFF));
  424. #else
  425. rgb_led_set_color(0, scale_bright(0x11), 0);
  426. #endif
  427. }
  428. error_led_set(false);
  429. break;
  430. case STATE_ERROR:
  431. #if CONFIG_LED_ERROR_GPIO >= 0
  432. // Dedicated error LED - turn off status to avoid mixed signals
  433. status_led_set_mode(LED_OFF);
  434. #else
  435. // No error LED - use status LED to indicate error
  436. status_led_set_mode(LED_BLINK_FAST);
  437. #endif
  438. rgb_led_set_color(scale_bright(0x80), 0, 0);
  439. error_led_set(true);
  440. break;
  441. }
  442. }
  443. static void apply_state(led_state_t state) {
  444. s_prev_state = s_current_state;
  445. s_current_state = state;
  446. ESP_LOGI(TAG, "LED state change: %d -> %d", s_prev_state, state);
  447. render_state(state);
  448. }
  449. static void on_rtsp_event(rtsp_event_t event, const rtsp_event_data_t *data,
  450. void *user_data) {
  451. ESP_LOGD(TAG, "RTSP event: %d", event);
  452. switch (event) {
  453. case RTSP_EVENT_CLIENT_CONNECTED:
  454. apply_state(STATE_PAUSED);
  455. break;
  456. case RTSP_EVENT_PLAYING:
  457. apply_state(STATE_PLAYING);
  458. break;
  459. case RTSP_EVENT_PAUSED:
  460. apply_state(STATE_PAUSED);
  461. break;
  462. case RTSP_EVENT_DISCONNECTED:
  463. apply_state(STATE_STANDBY);
  464. break;
  465. case RTSP_EVENT_METADATA:
  466. break;
  467. }
  468. }
  469. // ============================================================================
  470. // Audio VU Processing
  471. // ============================================================================
  472. #define SILENCE_THRESH 200
  473. #define UPDATE_INTERVAL_US (1000000 / 30) // ~30 Hz
  474. static int64_t s_last_update_us = 0;
  475. void led_audio_feed(const int16_t *pcm, size_t stereo_samples) {
  476. if (stereo_samples == 0 || s_current_state != STATE_PLAYING) {
  477. return;
  478. }
  479. // Rate limit to ~30 Hz
  480. int64_t now = esp_timer_get_time();
  481. if (now - s_last_update_us < UPDATE_INTERVAL_US) {
  482. return;
  483. }
  484. s_last_update_us = now;
  485. size_t total = stereo_samples * 2;
  486. // Compute RMS energy
  487. uint64_t sum_sq = 0;
  488. for (size_t i = 0; i < total; i++) {
  489. int32_t s = pcm[i];
  490. sum_sq += (uint64_t)(s * s);
  491. }
  492. float rms = sqrtf((float)sum_sq / (float)total);
  493. // Simple bass energy estimate
  494. uint64_t diff_sum = 0;
  495. for (size_t i = 2; i < total; i += 2) {
  496. int32_t d = (int32_t)pcm[i] - (int32_t)pcm[i - 2];
  497. diff_sum += (uint64_t)(d < 0 ? -d : d);
  498. }
  499. float high_energy = (float)diff_sum / ((float)total / 2.0f);
  500. float bass_ratio = 0.0f;
  501. if (rms > SILENCE_THRESH) {
  502. bass_ratio = 1.0f - (high_energy / (rms * 2.0f + 1.0f));
  503. if (bass_ratio < 0.0f) {
  504. bass_ratio = 0.0f;
  505. }
  506. if (bass_ratio > 1.0f) {
  507. bass_ratio = 1.0f;
  508. }
  509. }
  510. float norm = 0.0f;
  511. if (rms >= SILENCE_THRESH) {
  512. norm = (rms - SILENCE_THRESH) / (16000.0f - SILENCE_THRESH);
  513. if (norm > 1.0f) {
  514. norm = 1.0f;
  515. }
  516. }
  517. status_led_set_vu(norm);
  518. rgb_led_set_vu(norm, bass_ratio);
  519. }
  520. // ============================================================================
  521. // Public API
  522. // ============================================================================
  523. void led_init(void) {
  524. ESP_LOGI(TAG, "Initializing LED subsystem");
  525. ESP_LOGI(TAG, " Status LED GPIO: %d", CONFIG_LED_STATUS_GPIO);
  526. ESP_LOGI(TAG, " Error LED GPIO: %d", CONFIG_LED_ERROR_GPIO);
  527. ESP_LOGI(TAG, " RGB LED GPIO: %d", CONFIG_LED_RGB_GPIO);
  528. ESP_LOGI(TAG, " LED brightness: %d", CONFIG_LED_STATUS_BRIGHTNESS);
  529. uint8_t saved;
  530. if (settings_get_led_brightness(&saved) == ESP_OK) {
  531. s_brightness = saved;
  532. }
  533. status_led_init();
  534. error_led_init();
  535. rgb_led_init();
  536. rtsp_events_register(on_rtsp_event, NULL);
  537. // Start in standby
  538. ESP_LOGI(TAG, "Starting in STANDBY state");
  539. apply_state(STATE_STANDBY);
  540. ESP_LOGI(TAG, "LED subsystem initialized");
  541. }
  542. void led_set_error(bool error) {
  543. if (error) {
  544. if (s_current_state != STATE_ERROR) {
  545. apply_state(STATE_ERROR);
  546. } else {
  547. render_state(STATE_ERROR);
  548. }
  549. } else if (s_current_state == STATE_ERROR) {
  550. apply_state(s_prev_state);
  551. }
  552. }
  553. esp_err_t led_set_brightness(uint8_t brightness) {
  554. esp_err_t err = settings_set_led_brightness(brightness);
  555. if (err != ESP_OK) {
  556. return err;
  557. }
  558. s_brightness = brightness;
  559. #if CONFIG_LED_STATUS_GPIO >= 0
  560. s_status_duty = brightness;
  561. if (s_status_mode == LED_STEADY ||
  562. (s_status_mode >= LED_BLINK_SLOW && s_status_on)) {
  563. status_led_set_duty(s_status_duty);
  564. }
  565. #endif
  566. // Re-render without changing previous/current state history.
  567. render_state(s_current_state);
  568. return ESP_OK;
  569. }
  570. uint8_t led_get_brightness(void) {
  571. return s_brightness;
  572. }