123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- /*
- This example code is in the Public Domain (or CC0 licensed, at your option.)
- Unless required by applicable law or agreed to in writing, this
- software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- CONDITIONS OF ANY KIND, either express or implied.
- */
- #include <stdio.h>
- #include <stdlib.h>
- #include <unistd.h>
- #include <string.h>
- #include <math.h>
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "freertos/timers.h"
- #include "esp_system.h"
- #include "esp_log.h"
- #include "driver/gpio.h"
- #include "driver/ledc.h"
- #include "driver/rmt.h"
- #include "platform_config.h"
- #include "gpio_exp.h"
- #include "led.h"
- #include "globdefs.h"
- #include "accessors.h"
- #include "services.h"
- #define MAX_LED 8
- #define BLOCKTIME 10 // up to portMAX_DELAY
- #ifdef CONFIG_IDF_TARGET_ESP32S3
- #define LEDC_SPEED_MODE LEDC_LOW_SPEED_MODE
- #else
- #define LEDC_SPEED_MODE LEDC_HIGH_SPEED_MODE
- #endif
- static const char *TAG = "led";
- #define RMT_CLK (40/2)
- static int8_t led_rmt_channel = -1;
- static uint32_t scale24(uint32_t bright, uint8_t);
- static const struct rmt_led_param_s {
- led_type_t type;
- uint8_t bits;
- // number of ticks in nanoseconds converted in RMT_CLK ticks
- rmt_item32_t bit_0;
- rmt_item32_t bit_1;
- uint32_t green, red;
- uint32_t (*scale)(uint32_t, uint8_t);
- } rmt_led_param[] = {
- { LED_WS2812, 24, {{{350 / RMT_CLK, 1, 1000 / RMT_CLK, 0}}}, {{{1000 / RMT_CLK, 1, 350 / RMT_CLK, 0}}}, 0xff0000, 0x00ff00, scale24 },
- { .type = -1 } };
- static EXT_RAM_ATTR struct led_s {
- gpio_num_t gpio;
- bool on;
- uint32_t color;
- int ontime, offtime;
- int bright;
- int channel;
- const struct rmt_led_param_s *rmt;
- int pushedon, pushedoff;
- bool pushed;
- TimerHandle_t timer;
- } leds[MAX_LED];
- // can't use EXT_RAM_ATTR for initialized structure
- static struct led_config_s {
- int gpio;
- int color;
- int bright;
- led_type_t type;
- } green = { .gpio = CONFIG_LED_GREEN_GPIO, .color = 0, .bright = -1, .type = LED_GPIO },
- red = { .gpio = CONFIG_LED_RED_GPIO, .color = 0, .bright = -1, .type = LED_GPIO };
- static int led_max = 2;
- /****************************************************************************************
- *
- */
- static uint32_t scale24(uint32_t color, uint8_t scale) {
- uint32_t scaled = (((color & 0xff0000) >> 16) * scale / 100) << 16;
- scaled |= (((color & 0xff00) >> 8) * scale / 100) << 8;
- scaled |= (color & 0xff) * scale / 100;
- return scaled;
- }
- /****************************************************************************************
- *
- */
- static void set_level(struct led_s *led, bool on) {
- if (led->rmt) {
- uint32_t data = on ? led->rmt->scale(led->color, led->bright) : 0;
- uint32_t mask = 1 << (led->rmt->bits - 1);
- rmt_item32_t buffer[led->rmt->bits];
- for (uint32_t bit = 0; bit < led->rmt->bits; bit++) {
- uint32_t set = data & mask;
- buffer[bit] = set ? led->rmt->bit_1 : led->rmt->bit_0;
- mask >>= 1;
- }
- rmt_write_items(led->channel, buffer, led->rmt->bits, false);
- } else if (led->bright < 0 || led->gpio >= GPIO_NUM_MAX) {
- gpio_set_level_x(led->gpio, on ? led->color : !led->color);
- } else {
- ledc_set_duty(LEDC_SPEED_MODE, led->channel, on ? led->bright : (led->color ? 0 : pwm_system.max));
- ledc_update_duty(LEDC_SPEED_MODE, led->channel);
- }
- }
- /****************************************************************************************
- *
- */
- static void vCallbackFunction( TimerHandle_t xTimer ) {
- struct led_s *led = (struct led_s*) pvTimerGetTimerID (xTimer);
- if (!led->timer) return;
- led->on = !led->on;
- ESP_EARLY_LOGD(TAG,"led vCallbackFunction setting gpio %d level %d (bright:%d)", led->gpio, led->on, led->bright);
- set_level(led, led->on);
- // was just on for a while
- if (!led->on && led->offtime == -1) return;
- // regular blinking
- xTimerChangePeriod(xTimer, (led->on ? led->ontime : led->offtime) / portTICK_RATE_MS, BLOCKTIME);
- }
- /****************************************************************************************
- *
- */
- bool led_blink_core(int idx, int ontime, int offtime, bool pushed) {
- if (!leds[idx].gpio || leds[idx].gpio < 0 ) return false;
- ESP_LOGD(TAG,"led_blink_core %d on:%d off:%d, pushed:%u", idx, ontime, offtime, pushed);
- if (leds[idx].timer) {
- // normal requests waits if a pop is pending
- if (!pushed && leds[idx].pushed) {
- leds[idx].pushedon = ontime;
- leds[idx].pushedoff = offtime;
- return true;
- }
- xTimerStop(leds[idx].timer, BLOCKTIME);
- }
- // save current state if not already pushed
- if (!leds[idx].pushed) {
- leds[idx].pushedon = leds[idx].ontime;
- leds[idx].pushedoff = leds[idx].offtime;
- leds[idx].pushed = pushed;
- }
- // then set new one
- leds[idx].ontime = ontime;
- leds[idx].offtime = offtime;
- if (ontime == 0) {
- ESP_LOGD(TAG,"led %d, setting reverse level", idx);
- set_level(leds + idx, false);
- } else if (offtime == 0) {
- ESP_LOGD(TAG,"led %d, setting level", idx);
- set_level(leds + idx, true);
- } else {
- if (!leds[idx].timer) {
- ESP_LOGD(TAG,"led %d, Creating timer", idx);
- leds[idx].timer = xTimerCreate("ledTimer", ontime / portTICK_RATE_MS, pdFALSE, (void *)&leds[idx], vCallbackFunction);
- }
- leds[idx].on = true;
- set_level(leds + idx, true);
- ESP_LOGD(TAG,"led %d, Setting gpio %d and starting timer", idx, leds[idx].gpio);
- if (xTimerStart(leds[idx].timer, BLOCKTIME) == pdFAIL) return false;
- }
- return true;
- }
- /****************************************************************************************
- *
- */
- bool led_brightness(int idx, int bright) {
- if (bright > 100) bright = 100;
- if (leds[idx].rmt) {
- leds[idx].bright = bright;
- } else {
- leds[idx].bright = pwm_system.max * powf(bright / 100.0, 3);
- if (!leds[idx].color) leds[idx].bright = pwm_system.max - leds[idx].bright;
- ledc_set_duty(LEDC_SPEED_MODE, leds[idx].channel, leds[idx].bright);
- ledc_update_duty(LEDC_SPEED_MODE, leds[idx].channel);
- }
- return true;
- }
- /****************************************************************************************
- *
- */
- bool led_unpush(int idx) {
- if (!leds[idx].gpio || leds[idx].gpio<0) return false;
- led_blink_core(idx, leds[idx].pushedon, leds[idx].pushedoff, true);
- leds[idx].pushed = false;
- return true;
- }
- /****************************************************************************************
- *
- */
- int led_allocate(void) {
- if (led_max < MAX_LED) return led_max++;
- return -1;
- }
- /****************************************************************************************
- *
- */
- bool led_config(int idx, gpio_num_t gpio, int color, int bright, led_type_t type) {
- if (gpio < 0) {
- ESP_LOGW(TAG,"LED GPIO -1 ignored");
- return false;
- }
- if (idx >= MAX_LED) return false;
- if (bright > 100) bright = 100;
- leds[idx].gpio = gpio;
- leds[idx].color = color;
- leds[idx].rmt = NULL;
- leds[idx].bright = -1;
- if (type != LED_GPIO) {
- // first make sure we have a known addressable led
- 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;
- if (!leds[idx].rmt) return false;
- if (led_rmt_channel < 0) led_rmt_channel = RMT_NEXT_TX_CHANNEL();
- leds[idx].channel = led_rmt_channel;
- leds[idx].bright = bright > 0 ? bright : 100;
- // set counter clock to 40MHz
- rmt_config_t config = RMT_DEFAULT_CONFIG_TX(gpio, leds[idx].channel);
- config.clk_div = 2;
- rmt_config(&config);
- rmt_driver_install(config.channel, 0, 0);
- } else if (bright < 0 || gpio >= GPIO_NUM_MAX) {
- gpio_pad_select_gpio_x(gpio);
- gpio_set_direction_x(gpio, GPIO_MODE_OUTPUT);
- } else {
- leds[idx].channel = pwm_system.base_channel++;
- leds[idx].bright = pwm_system.max * powf(bright / 100.0, 3);
- if (!color) leds[idx].bright = pwm_system.max - leds[idx].bright;
- ledc_channel_config_t ledc_channel = {
- .channel = leds[idx].channel,
- .duty = leds[idx].bright,
- .gpio_num = gpio,
- .speed_mode = LEDC_SPEED_MODE,
- .hpoint = 0,
- .timer_sel = pwm_system.timer,
- };
- ledc_channel_config(&ledc_channel);
- }
- set_level(leds + idx, false);
- ESP_LOGD(TAG,"Index %d, GPIO %d, color/onstate %d / RMT %d, bright %d%%", idx, gpio, color, type, bright);
- return true;
- }
- /****************************************************************************************
- *
- */
- static void led_suspend(void) {
- led_off(LED_GREEN);
- led_off(LED_RED);
- }
- /****************************************************************************************
- *
- */
- void set_led_gpio(int gpio, char *value) {
- struct led_config_s *config;
- if (strcasestr(value, "green")) config = &green;
- else if (strcasestr(value, "red"))config = &red;
- else return;
- config->gpio = gpio;
- char *p = value;
- while ((p = strchr(p, ':')) != NULL) {
- p++;
- if ((strcasestr(p, "ws2812")) != NULL) config->type = LED_WS2812;
- else config->color = atoi(p);
- }
- if (config->type != LED_GPIO) {
- for (const struct rmt_led_param_s *p = rmt_led_param; p->type >= 0; p++) {
- if (p->type == config->type) {
- if (config == &green) config->color = p->green;
- else config->color = p->red;
- break;
- }
- }
- }
- }
- void led_svc_init(void) {
- #ifdef CONFIG_LED_GREEN_GPIO_LEVEL
- green.color = CONFIG_LED_GREEN_GPIO_LEVEL;
- #endif
- #ifdef CONFIG_LED_RED_GPIO_LEVEL
- red.color = CONFIG_LED_RED_GPIO_LEVEL;
- #endif
- #ifndef CONFIG_LED_LOCKED
- parse_set_GPIO(set_led_gpio);
- #endif
- char *nvs_item = config_alloc_get(NVS_TYPE_STR, "led_brightness");
- if (nvs_item) {
- PARSE_PARAM(nvs_item, "green", '=', green.bright);
- PARSE_PARAM(nvs_item, "red", '=', red.bright);
- free(nvs_item);
- }
- led_config(LED_GREEN, green.gpio, green.color, green.bright, green.type);
- led_config(LED_RED, red.gpio, red.color, red.bright, red.type);
-
- // make sure we switch off all leds (useful for gpio expanders)
- services_sleep_setsuspend(led_suspend);
- ESP_LOGI(TAG,"Configuring LEDs green:%d (on:%d rmt:%d %d%% ), red:%d (on:%d rmt:%d %d%% )",
- green.gpio, green.color, green.type, green.bright,
- red.gpio, red.color, red.type, red.bright);
- }
|