blob: 9927e490c0d3d4c52f6fe250dabb8eb097e00a3d [file] [log] [blame]
/*
*
* Copyright (c) 2018 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @file LEDWidget.cpp
*
* Implements an LED Widget controller that is usually tied to a GPIO
* It also updates the display widget if it's enabled
*/
#include "LEDWidget.h"
#if CONFIG_HAVE_DISPLAY
#include "ScreenManager.h"
#endif
#include "esp_log.h"
#include "esp_system.h"
#include "esp_timer.h"
#if CONFIG_DEVICE_TYPE_ESP32_C3_DEVKITM || CONFIG_DEVICE_TYPE_ESP32_C6_DEVKITC
#include "driver/rmt.h"
#include "led_strip.h"
#define RMT_TX_DEFAULT_GPIO GPIO_NUM_8
#define RMT_TX_DEFAULT_CHANNEL RMT_CHANNEL_0
static led_strip_t * strip = NULL;
#else
#include "driver/ledc.h"
#include "hal/ledc_types.h"
#endif
void LEDWidget::Init(gpio_num_t gpioNum)
{
mLastChangeTimeUS = 0;
mBlinkOnTimeMS = 0;
mBlinkOffTimeMS = 0;
mGPIONum = gpioNum;
mVLED1 = -1;
mVLED2 = -1;
mState = false;
mError = false;
errorTimer = NULL;
#if CONFIG_DEVICE_TYPE_ESP32_C3_DEVKITM || CONFIG_DEVICE_TYPE_ESP32_C6_DEVKITC
if (gpioNum == RMT_TX_DEFAULT_GPIO)
{
rmt_config_t config = RMT_DEFAULT_CONFIG_TX(RMT_TX_DEFAULT_GPIO, RMT_TX_DEFAULT_CHANNEL);
config.clk_div = 2;
rmt_config(&config);
rmt_driver_install(config.channel, 0, 0);
led_strip_config_t strip_config = LED_STRIP_DEFAULT_CONFIG(1, (led_strip_dev_t) config.channel);
strip = led_strip_new_rmt_ws2812(&strip_config);
mDefaultOnBrightness = UINT8_MAX;
mHue = 0;
mSaturation = 0;
}
#else
if (gpioNum < GPIO_NUM_MAX)
{
ledc_timer_config_t ledc_timer = {
.speed_mode = LEDC_LOW_SPEED_MODE, // timer mode
.duty_resolution = LEDC_TIMER_8_BIT, // resolution of PWM duty
.timer_num = LEDC_TIMER_1, // timer index
.freq_hz = 5000, // frequency of PWM signal
.clk_cfg = LEDC_AUTO_CLK, // Auto select the source clock
};
ledc_timer_config(&ledc_timer);
ledc_channel_config_t ledc_channel = {
.gpio_num = gpioNum,
.speed_mode = LEDC_LOW_SPEED_MODE,
.channel = LEDC_CHANNEL_0,
.intr_type = LEDC_INTR_DISABLE,
.timer_sel = LEDC_TIMER_1,
.duty = 0,
.hpoint = 0,
};
ledc_channel_config(&ledc_channel);
mDefaultOnBrightness = UINT8_MAX;
}
#endif
}
void LEDWidget::Set(bool state)
{
mBlinkOnTimeMS = mBlinkOffTimeMS = 0;
DoSet(state);
}
void LEDWidget::SetBrightness(uint8_t brightness)
{
#if CONFIG_DEVICE_TYPE_ESP32_C3_DEVKITM || CONFIG_DEVICE_TYPE_ESP32_C6_DEVKITC
if (strip)
{
uint8_t red, green, blue;
HSB2rgb(mHue, mSaturation, brightness, red, green, blue);
strip->set_pixel(strip, 0, red, green, blue);
strip->refresh(strip, 100);
}
#else
if (mGPIONum < GPIO_NUM_MAX)
{
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, brightness);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
}
#endif
if (brightness > 0)
{
mDefaultOnBrightness = brightness;
}
#if CONFIG_HAVE_DISPLAY
if (mVLED1 != -1)
{
ScreenManager::SetVLED(mVLED1, mState);
}
#endif // CONFIG_HAVE_DISPLAY
}
void LEDWidget::Blink(uint32_t changeRateMS)
{
Blink(changeRateMS, changeRateMS);
}
void LEDWidget::Blink(uint32_t onTimeMS, uint32_t offTimeMS)
{
mBlinkOnTimeMS = onTimeMS;
mBlinkOffTimeMS = offTimeMS;
Animate();
}
void ClearErrorState(TimerHandle_t handle)
{
#if CONFIG_HAVE_DISPLAY
LEDWidget * pWidget = (LEDWidget *) pvTimerGetTimerID(handle);
pWidget->mError = false;
if (pWidget->mVLED2 != -1)
{
ScreenManager::SetVLED(pWidget->mVLED2, false);
}
#endif
}
void LEDWidget::BlinkOnError()
{
#if CONFIG_HAVE_DISPLAY
mError = true;
if (errorTimer != NULL)
{
xTimerDelete(errorTimer, 0);
}
errorTimer = xTimerCreate("ErrorTimer", pdMS_TO_TICKS(2000), false, this, ClearErrorState);
xTimerStart(errorTimer, 0);
if (mVLED2 != -1)
{
ScreenManager::SetVLED(mVLED2, true);
}
#endif
}
void LEDWidget::Animate()
{
if (mBlinkOnTimeMS != 0 && mBlinkOffTimeMS != 0)
{
int64_t nowUS = ::esp_timer_get_time();
int64_t stateDurUS = ((mState) ? mBlinkOnTimeMS : mBlinkOffTimeMS) * 1000LL;
int64_t nextChangeTimeUS = mLastChangeTimeUS + stateDurUS;
if (nowUS > nextChangeTimeUS)
{
DoSet(!mState);
mLastChangeTimeUS = nowUS;
}
}
}
void LEDWidget::DoSet(bool state)
{
bool stateChange = (mState != state);
mState = state;
#if CONFIG_DEVICE_TYPE_ESP32_C3_DEVKITM || CONFIG_DEVICE_TYPE_ESP32_C6_DEVKITC
if (strip)
{
uint8_t red, green, blue;
uint8_t brightness = state ? mDefaultOnBrightness : 0;
HSB2rgb(mHue, mSaturation, brightness, red, green, blue);
strip->set_pixel(strip, 0, red, green, blue);
strip->refresh(strip, 100);
}
#else
if (mGPIONum < GPIO_NUM_MAX)
{
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, state ? mDefaultOnBrightness : 0);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
}
#endif
if (stateChange)
{
#if CONFIG_HAVE_DISPLAY
if (mVLED1 != -1)
{
ScreenManager::SetVLED(mVLED1, mState);
}
#endif
}
}
#if CONFIG_HAVE_DISPLAY
void LEDWidget::SetVLED(int id1, int id2)
{
mVLED1 = id1;
if (mVLED1 != -1)
{
ScreenManager::SetVLED(mVLED1, mState);
}
mVLED2 = id2;
if (mVLED2 != -1)
{
ScreenManager::SetVLED(mVLED2, mError);
}
}
#endif
#if CONFIG_DEVICE_TYPE_ESP32_C3_DEVKITM || CONFIG_DEVICE_TYPE_ESP32_C6_DEVKITC
void LEDWidget::SetColor(uint8_t Hue, uint8_t Saturation)
{
uint8_t red, green, blue;
uint8_t brightness = mState ? mDefaultOnBrightness : 0;
mHue = static_cast<uint16_t>(Hue) * 360 / 254; // mHue [0, 360]
mSaturation = static_cast<uint16_t>(Saturation) * 100 / 254; // mSaturation [0 , 100]
HSB2rgb(mHue, mSaturation, brightness, red, green, blue);
strip->set_pixel(strip, 0, red, green, blue);
strip->refresh(strip, 100);
}
void LEDWidget::HSB2rgb(uint16_t Hue, uint8_t Saturation, uint8_t brightness, uint8_t & red, uint8_t & green, uint8_t & blue)
{
uint16_t i = Hue / 60;
uint16_t rgb_max = brightness;
uint16_t rgb_min = rgb_max * (100 - Saturation) / 100;
uint16_t diff = Hue % 60;
uint16_t rgb_adj = (rgb_max - rgb_min) * diff / 60;
switch (i)
{
case 0:
red = rgb_max;
green = rgb_min + rgb_adj;
blue = rgb_min;
break;
case 1:
red = rgb_max - rgb_adj;
green = rgb_max;
blue = rgb_min;
break;
case 2:
red = rgb_min;
green = rgb_max;
blue = rgb_min + rgb_adj;
break;
case 3:
red = rgb_min;
green = rgb_max - rgb_adj;
blue = rgb_max;
break;
case 4:
red = rgb_min + rgb_adj;
green = rgb_min;
blue = rgb_max;
break;
default:
red = rgb_max;
green = rgb_min;
blue = rgb_max - rgb_adj;
break;
}
}
#endif