led.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /*
  2. This example code is in the Public Domain (or CC0 licensed, at your option.)
  3. Unless required by applicable law or agreed to in writing, this
  4. software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  5. CONDITIONS OF ANY KIND, either express or implied.
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <unistd.h>
  10. #include <string.h>
  11. #include <math.h>
  12. #include "freertos/FreeRTOS.h"
  13. #include "freertos/task.h"
  14. #include "freertos/timers.h"
  15. #include "esp_system.h"
  16. #include "esp_log.h"
  17. #include "driver/gpio.h"
  18. #include "driver/ledc.h"
  19. #include "driver/rmt.h"
  20. #include "platform_config.h"
  21. #include "gpio_exp.h"
  22. #include "led.h"
  23. #include "globdefs.h"
  24. #include "accessors.h"
  25. #include "services.h"
  26. #define MAX_LED 8
  27. #define BLOCKTIME 10 // up to portMAX_DELAY
  28. #ifdef CONFIG_IDF_TARGET_ESP32S3
  29. #define LEDC_SPEED_MODE LEDC_LOW_SPEED_MODE
  30. #else
  31. #define LEDC_SPEED_MODE LEDC_HIGH_SPEED_MODE
  32. #endif
  33. static const char *TAG = "led";
  34. #define RMT_CLK (40/2)
  35. static int8_t led_rmt_channel = -1;
  36. static uint32_t scale24(uint32_t bright, uint8_t);
  37. static const struct rmt_led_param_s {
  38. led_type_t type;
  39. uint8_t bits;
  40. // number of ticks in nanoseconds converted in RMT_CLK ticks
  41. rmt_item32_t bit_0;
  42. rmt_item32_t bit_1;
  43. uint32_t green, red;
  44. uint32_t (*scale)(uint32_t, uint8_t);
  45. } rmt_led_param[] = {
  46. { LED_WS2812, 24, {{{350 / RMT_CLK, 1, 1000 / RMT_CLK, 0}}}, {{{1000 / RMT_CLK, 1, 350 / RMT_CLK, 0}}}, 0xff0000, 0x00ff00, scale24 },
  47. { .type = -1 } };
  48. static EXT_RAM_ATTR struct led_s {
  49. gpio_num_t gpio;
  50. bool on;
  51. uint32_t color;
  52. int ontime, offtime;
  53. int bright;
  54. int channel;
  55. const struct rmt_led_param_s *rmt;
  56. int pushedon, pushedoff;
  57. bool pushed;
  58. TimerHandle_t timer;
  59. } leds[MAX_LED];
  60. // can't use EXT_RAM_ATTR for initialized structure
  61. static struct led_config_s {
  62. int gpio;
  63. int color;
  64. int bright;
  65. led_type_t type;
  66. } green = { .gpio = CONFIG_LED_GREEN_GPIO, .color = 0, .bright = -1, .type = LED_GPIO },
  67. red = { .gpio = CONFIG_LED_RED_GPIO, .color = 0, .bright = -1, .type = LED_GPIO };
  68. static int led_max = 2;
  69. /****************************************************************************************
  70. *
  71. */
  72. static uint32_t scale24(uint32_t color, uint8_t scale) {
  73. uint32_t scaled = (((color & 0xff0000) >> 16) * scale / 100) << 16;
  74. scaled |= (((color & 0xff00) >> 8) * scale / 100) << 8;
  75. scaled |= (color & 0xff) * scale / 100;
  76. return scaled;
  77. }
  78. /****************************************************************************************
  79. *
  80. */
  81. static void set_level(struct led_s *led, bool on) {
  82. if (led->rmt) {
  83. uint32_t data = on ? led->rmt->scale(led->color, led->bright) : 0;
  84. uint32_t mask = 1 << (led->rmt->bits - 1);
  85. rmt_item32_t buffer[led->rmt->bits];
  86. for (uint32_t bit = 0; bit < led->rmt->bits; bit++) {
  87. uint32_t set = data & mask;
  88. buffer[bit] = set ? led->rmt->bit_1 : led->rmt->bit_0;
  89. mask >>= 1;
  90. }
  91. rmt_write_items(led->channel, buffer, led->rmt->bits, false);
  92. } else if (led->bright < 0 || led->gpio >= GPIO_NUM_MAX) {
  93. gpio_set_level_x(led->gpio, on ? led->color : !led->color);
  94. } else {
  95. ledc_set_duty(LEDC_SPEED_MODE, led->channel, on ? led->bright : (led->color ? 0 : pwm_system.max));
  96. ledc_update_duty(LEDC_SPEED_MODE, led->channel);
  97. }
  98. }
  99. /****************************************************************************************
  100. *
  101. */
  102. static void vCallbackFunction( TimerHandle_t xTimer ) {
  103. struct led_s *led = (struct led_s*) pvTimerGetTimerID (xTimer);
  104. if (!led->timer) return;
  105. led->on = !led->on;
  106. ESP_EARLY_LOGD(TAG,"led vCallbackFunction setting gpio %d level %d (bright:%d)", led->gpio, led->on, led->bright);
  107. set_level(led, led->on);
  108. // was just on for a while
  109. if (!led->on && led->offtime == -1) return;
  110. // regular blinking
  111. xTimerChangePeriod(xTimer, (led->on ? led->ontime : led->offtime) / portTICK_RATE_MS, BLOCKTIME);
  112. }
  113. /****************************************************************************************
  114. *
  115. */
  116. bool led_blink_core(int idx, int ontime, int offtime, bool pushed) {
  117. if (!leds[idx].gpio || leds[idx].gpio < 0 ) return false;
  118. ESP_LOGD(TAG,"led_blink_core %d on:%d off:%d, pushed:%u", idx, ontime, offtime, pushed);
  119. if (leds[idx].timer) {
  120. // normal requests waits if a pop is pending
  121. if (!pushed && leds[idx].pushed) {
  122. leds[idx].pushedon = ontime;
  123. leds[idx].pushedoff = offtime;
  124. return true;
  125. }
  126. xTimerStop(leds[idx].timer, BLOCKTIME);
  127. }
  128. // save current state if not already pushed
  129. if (!leds[idx].pushed) {
  130. leds[idx].pushedon = leds[idx].ontime;
  131. leds[idx].pushedoff = leds[idx].offtime;
  132. leds[idx].pushed = pushed;
  133. }
  134. // then set new one
  135. leds[idx].ontime = ontime;
  136. leds[idx].offtime = offtime;
  137. if (ontime == 0) {
  138. ESP_LOGD(TAG,"led %d, setting reverse level", idx);
  139. set_level(leds + idx, false);
  140. } else if (offtime == 0) {
  141. ESP_LOGD(TAG,"led %d, setting level", idx);
  142. set_level(leds + idx, true);
  143. } else {
  144. if (!leds[idx].timer) {
  145. ESP_LOGD(TAG,"led %d, Creating timer", idx);
  146. leds[idx].timer = xTimerCreate("ledTimer", ontime / portTICK_RATE_MS, pdFALSE, (void *)&leds[idx], vCallbackFunction);
  147. }
  148. leds[idx].on = true;
  149. set_level(leds + idx, true);
  150. ESP_LOGD(TAG,"led %d, Setting gpio %d and starting timer", idx, leds[idx].gpio);
  151. if (xTimerStart(leds[idx].timer, BLOCKTIME) == pdFAIL) return false;
  152. }
  153. return true;
  154. }
  155. /****************************************************************************************
  156. *
  157. */
  158. bool led_brightness(int idx, int bright) {
  159. if (bright > 100) bright = 100;
  160. if (leds[idx].rmt) {
  161. leds[idx].bright = bright;
  162. } else {
  163. leds[idx].bright = pwm_system.max * powf(bright / 100.0, 3);
  164. if (!leds[idx].color) leds[idx].bright = pwm_system.max - leds[idx].bright;
  165. ledc_set_duty(LEDC_SPEED_MODE, leds[idx].channel, leds[idx].bright);
  166. ledc_update_duty(LEDC_SPEED_MODE, leds[idx].channel);
  167. }
  168. return true;
  169. }
  170. /****************************************************************************************
  171. *
  172. */
  173. bool led_unpush(int idx) {
  174. if (!leds[idx].gpio || leds[idx].gpio<0) return false;
  175. led_blink_core(idx, leds[idx].pushedon, leds[idx].pushedoff, true);
  176. leds[idx].pushed = false;
  177. return true;
  178. }
  179. /****************************************************************************************
  180. *
  181. */
  182. int led_allocate(void) {
  183. if (led_max < MAX_LED) return led_max++;
  184. return -1;
  185. }
  186. /****************************************************************************************
  187. *
  188. */
  189. bool led_config(int idx, gpio_num_t gpio, int color, int bright, led_type_t type) {
  190. if (gpio < 0) {
  191. ESP_LOGW(TAG,"LED GPIO -1 ignored");
  192. return false;
  193. }
  194. if (idx >= MAX_LED) return false;
  195. if (bright > 100) bright = 100;
  196. leds[idx].gpio = gpio;
  197. leds[idx].color = color;
  198. leds[idx].rmt = NULL;
  199. leds[idx].bright = -1;
  200. if (type != LED_GPIO) {
  201. // first make sure we have a known addressable led
  202. for (const struct rmt_led_param_s *p = rmt_led_param; !leds[idx].rmt && p->type >= 0; p++) if (p->type == type) leds[idx].rmt = p;
  203. if (!leds[idx].rmt) return false;
  204. if (led_rmt_channel < 0) led_rmt_channel = RMT_NEXT_TX_CHANNEL();
  205. leds[idx].channel = led_rmt_channel;
  206. leds[idx].bright = bright > 0 ? bright : 100;
  207. // set counter clock to 40MHz
  208. rmt_config_t config = RMT_DEFAULT_CONFIG_TX(gpio, leds[idx].channel);
  209. config.clk_div = 2;
  210. rmt_config(&config);
  211. rmt_driver_install(config.channel, 0, 0);
  212. } else if (bright < 0 || gpio >= GPIO_NUM_MAX) {
  213. gpio_pad_select_gpio_x(gpio);
  214. gpio_set_direction_x(gpio, GPIO_MODE_OUTPUT);
  215. } else {
  216. leds[idx].channel = pwm_system.base_channel++;
  217. leds[idx].bright = pwm_system.max * powf(bright / 100.0, 3);
  218. if (!color) leds[idx].bright = pwm_system.max - leds[idx].bright;
  219. ledc_channel_config_t ledc_channel = {
  220. .channel = leds[idx].channel,
  221. .duty = leds[idx].bright,
  222. .gpio_num = gpio,
  223. .speed_mode = LEDC_SPEED_MODE,
  224. .hpoint = 0,
  225. .timer_sel = pwm_system.timer,
  226. };
  227. ledc_channel_config(&ledc_channel);
  228. }
  229. set_level(leds + idx, false);
  230. ESP_LOGD(TAG,"Index %d, GPIO %d, color/onstate %d / RMT %d, bright %d%%", idx, gpio, color, type, bright);
  231. return true;
  232. }
  233. /****************************************************************************************
  234. *
  235. */
  236. static void led_suspend(void) {
  237. led_off(LED_GREEN);
  238. led_off(LED_RED);
  239. }
  240. /****************************************************************************************
  241. *
  242. */
  243. void set_led_gpio(int gpio, char *value) {
  244. struct led_config_s *config;
  245. if (strcasestr(value, "green")) config = &green;
  246. else if (strcasestr(value, "red"))config = &red;
  247. else return;
  248. config->gpio = gpio;
  249. char *p = value;
  250. while ((p = strchr(p, ':')) != NULL) {
  251. p++;
  252. if ((strcasestr(p, "ws2812")) != NULL) config->type = LED_WS2812;
  253. else config->color = atoi(p);
  254. }
  255. if (config->type != LED_GPIO) {
  256. for (const struct rmt_led_param_s *p = rmt_led_param; p->type >= 0; p++) {
  257. if (p->type == config->type) {
  258. if (config == &green) config->color = p->green;
  259. else config->color = p->red;
  260. break;
  261. }
  262. }
  263. }
  264. }
  265. void led_svc_init(void) {
  266. #ifdef CONFIG_LED_GREEN_GPIO_LEVEL
  267. green.color = CONFIG_LED_GREEN_GPIO_LEVEL;
  268. #endif
  269. #ifdef CONFIG_LED_RED_GPIO_LEVEL
  270. red.color = CONFIG_LED_RED_GPIO_LEVEL;
  271. #endif
  272. #ifndef CONFIG_LED_LOCKED
  273. parse_set_GPIO(set_led_gpio);
  274. #endif
  275. char *nvs_item = config_alloc_get(NVS_TYPE_STR, "led_brightness");
  276. if (nvs_item) {
  277. PARSE_PARAM(nvs_item, "green", '=', green.bright);
  278. PARSE_PARAM(nvs_item, "red", '=', red.bright);
  279. free(nvs_item);
  280. }
  281. led_config(LED_GREEN, green.gpio, green.color, green.bright, green.type);
  282. led_config(LED_RED, red.gpio, red.color, red.bright, red.type);
  283. // make sure we switch off all leds (useful for gpio expanders)
  284. services_sleep_setsuspend(led_suspend);
  285. ESP_LOGI(TAG,"Configuring LEDs green:%d (on:%d rmt:%d %d%% ), red:%d (on:%d rmt:%d %d%% )",
  286. green.gpio, green.color, green.type, green.bright,
  287. red.gpio, red.color, red.type, red.bright);
  288. }