diff --git a/17_light_utils_pwm/CMakeLists.txt b/17_light_utils_pwm/CMakeLists.txt new file mode 100644 index 0000000..ee6ca0a --- /dev/null +++ b/17_light_utils_pwm/CMakeLists.txt @@ -0,0 +1,7 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +set(SUPPORTED_TARGETS esp32) +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(mcpwm_brushed_dc_control) diff --git a/17_light_utils_pwm/Makefile b/17_light_utils_pwm/Makefile new file mode 100644 index 0000000..9dd14ff --- /dev/null +++ b/17_light_utils_pwm/Makefile @@ -0,0 +1,9 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := mcpwm_brushed_dc_control + +include $(IDF_PATH)/make/project.mk + diff --git a/17_light_utils_pwm/components/light_driver/CMakeLists.txt b/17_light_utils_pwm/components/light_driver/CMakeLists.txt new file mode 100644 index 0000000..94ea03f --- /dev/null +++ b/17_light_utils_pwm/components/light_driver/CMakeLists.txt @@ -0,0 +1,10 @@ + +set(COMPONENT_SRCS "light_driver.c" + "iot_led.c") + +set(COMPONENT_ADD_INCLUDEDIRS ". include") + +# requirements can't depend on config +set(COMPONENT_REQUIRES mcommon) + +register_component() diff --git a/17_light_utils_pwm/components/light_driver/README.md b/17_light_utils_pwm/components/light_driver/README.md new file mode 100644 index 0000000..ada1266 --- /dev/null +++ b/17_light_utils_pwm/components/light_driver/README.md @@ -0,0 +1,22 @@ +# Component: Light + +* This component defines a light as a well encapsulated object. +* A light device is defined by: + * ledc timer which is used to control the pwm channels of light + * mode of the ledc timer + * frequency of the ledc timer + * pwm channel number of the light + * bit number of the ledc timer +* A light device can provide: + * iot_light_channel_regist function to add channel to corresponding channel id + * iot_light_duty_write function to set the duty of corresponding channel and it support setting duty directly or gradually + * iot_light_breath_write function to set the corresponding channel to breath mode and breath period can be set + * iot_light_blink_starte and iot_light_blink_stop function to make some of channels to blink in appointed period. Note that if any channel works in blink mode, all the other channels would be turned off. + +* To use the light device, you need to: + * create a light object returned by iot_light_create() + * regist the light channels according the channel number by iot_light_channel_regist() + * To free the object, you can call iot_light_delete to delete the button object and free the memory. + +### NOTE: +> If any channel(s) work(s) in blink mode, all the other channels would be turned off. iot_light_blink_stop() must be called before setting any channel to other mode(write duty or breath). \ No newline at end of file diff --git a/17_light_utils_pwm/components/light_driver/component.mk b/17_light_utils_pwm/components/light_driver/component.mk new file mode 100644 index 0000000..a98f634 --- /dev/null +++ b/17_light_utils_pwm/components/light_driver/component.mk @@ -0,0 +1,4 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) diff --git a/17_light_utils_pwm/components/light_driver/include/iot_led.h b/17_light_utils_pwm/components/light_driver/include/iot_led.h new file mode 100644 index 0000000..8faf021 --- /dev/null +++ b/17_light_utils_pwm/components/light_driver/include/iot_led.h @@ -0,0 +1,194 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef __IOT_LED_H__ +#define __IOT_LED_H__ + +#ifdef __cplusplus +extern "C" { +#endif + + +#include +#include +#include +#include "errno.h" + +#include "esp32/rom/rtc.h" +#include "esp32/rom/crc.h" + + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "freertos/event_groups.h" + +#include "esp_system.h" +#include "esp_partition.h" +#include "esp_event.h" +#include "esp_http_client.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" +#include "lwip/sockets.h" +#include "driver/ledc.h" + +#include "nvs.h" +#include "nvs_flash.h" +#include "cJSON.h" +#include "driver/i2c.h" +#include "sys/param.h" +#include "driver/gpio.h" + +#define HW_TIMER_GROUP (0) /**< Hardware timer group */ +#define HW_TIMER_ID (0) /**< Hardware timer number */ +#define HW_TIMER_DIVIDER (16) /**< Hardware timer clock divider */ +#define HW_TIMER_SCALE (TIMER_BASE_CLK / HW_TIMER_DIVIDER) /**< Convert counter value to seconds */ +#define GAMMA_CORRECTION 0.8 /**< Gamma curve parameter */ +#define GAMMA_TABLE_SIZE 256 /**< Gamma table size, used for led fade*/ +#define DUTY_SET_CYCLE (20) /**< Set duty cycle */ + +/** + * @brief Initialize and set the ledc timer for the iot led + * + * @param timer_num The timer index of ledc timer group used for iot led + * This parameter can be one of LEDC_TIMER_x where x can be (0 .. 3) + * + * @param speed_mode speed mode of ledc timer + * This parameter can be one of LEDC_x_SPEED_MODE where x can be (LOW, HIGH) + * + * @param freq_hz frequency of ledc timer + * This parameter must be less than 5000 + * + * @return + * - MDF_OK if sucess + * - MDF_ERR_INVALID_ARG Parameter error + * - MDF_FAIL Can not find a proper pre-divider number base on the given frequency + * and the current duty_resolution. +*/ +esp_err_t iot_led_init(ledc_timer_t timer_num, ledc_mode_t speed_mode, uint32_t freq_hz); + +/** + * @brief DeInitializes the iot led and free resource + * + * @return + * - MDF_OK if sucess +*/ +esp_err_t iot_led_deinit(); + +/** + * @brief Set the ledc channel used by iot led and associate the gpio port used + * for output + * + * @param channel The ledc channel + * This parameter can be LEDC_CHANNEL_x where x can be (0 .. 15) + * @param gpio_num the ledc output gpio_num + * This parameter can be GPIO_NUM_x where x can be (0, 33) + * + * @note If the operation of esp32 depends on SPI FLASH or PSRAM, then these related + * pins should not be set to output. + * + * @return + * - MDF_OK if sucess + * - MDF_ERR_INVALID_ARG Parameter error + * - MDF_ERR_NOT_INIT if lot_led_init() is not called yet +*/ +esp_err_t iot_led_regist_channel(ledc_channel_t channel, gpio_num_t gpio_num); + +/** + * @brief Returns the channel value + * @note before calling this function, you need to call iot_led_regist_channel() to + * set the channel + * + * @param channel The ledc channel + * This parameter can be LEDC_CHANNEL_x where x can be (0 .. 15) + * @param dst The address where the channel value is stored + * @return + * - MDF_OK if sucess + * - MDF_ERR_INVALID_ARG if dst is NULL + * - MDF_ERR_NOT_INIT if lot_led_init() is not called yet +*/ +esp_err_t iot_led_get_channel(ledc_channel_t channel, uint8_t* dst); + +/** + * @brief Set the fade state for the specified channel + * @note before calling this function, you need to call iot_led_regist_channel() to + * set the channel + * + * @param channel The ledc channel + * This parameter can be LEDC_CHANNEL_x where x can be (0 .. 15) + * @param value The target output brightness of iot led + * This parameter can be (0 .. 255) + * @param fade_ms The time from the current value to the target value + * @return + * - MDF_OK if sucess + * - MDF_ERR_NOT_INIT if lot_led_init() is not called yet +*/ +esp_err_t iot_led_set_channel(ledc_channel_t channel, uint8_t value, uint32_t fade_ms); + +/** + * @brief Set the blink state or loop fade for the specified channel + * @note before calling this function, you need to call iot_led_regist_channel() to + * set the channel + * + * @param channel The ledc channel + * This parameter can be LEDC_CHANNEL_x where x can be (0 .. 15) + * @param value The output brightness of iot led + * This parameter can be (0 .. 255) + * @param period_ms Blink cycle + * @param fade_flag select loop fade or blink + * 1 for loop fade + * 0 for blink + * @return + * - MDF_OK if sucess + * - MDF_ERR_NOT_INIT if lot_led_init() is not called yet +*/ +esp_err_t iot_led_start_blink(ledc_channel_t channel, uint8_t value, uint32_t period_ms, bool fade_flag); + +/** + * @brief Stop the blink state or loop fade for the specified channel + * + * @param channel The ledc channel + * This parameter can be LEDC_CHANNEL_x where x can be (0 .. 15) + * @return + * - MDF_OK if sucess + * - MDF_ERR_NOT_INIT if lot_led_init() is not called yet +*/ +esp_err_t iot_led_stop_blink(ledc_channel_t channel); + +/** + * @brief Set the specified gamma_table to control the fade effect, usually + * no need to set + * + * @param gamma_table[GAMMA_TABLE_SIZE] Expected gamma table value + * + * @note Gamma_table is the dimming curve used by the iot_led driver. + * The element type is uint16_t. Each element is treated as a binary + * fixed-point number. The decimal point is before the eighth bit + * and after the ninth bit, so the range of expressions can be + * 0x00.00 ~ 0xff.ff. + * @note default gamma_table is created in iot_led_init() + * + * @return + * - MDF_OK if sucess + * - MDF_ERR_NOT_INIT if lot_led_init() is not called yet +*/ +esp_err_t iot_led_set_gamma_table(const uint16_t gamma_table[GAMMA_TABLE_SIZE]); + +#ifdef __cplusplus +} +#endif + +#endif /**< __IOT_LED_H__ */ diff --git a/17_light_utils_pwm/components/light_driver/include/iot_light.h b/17_light_utils_pwm/components/light_driver/include/iot_light.h new file mode 100644 index 0000000..2cbfb57 --- /dev/null +++ b/17_light_utils_pwm/components/light_driver/include/iot_light.h @@ -0,0 +1,162 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef __IOT_LIGHT_H__ +#define __IOT_LIGHT_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "driver/ledc.h" + +/********************************** NOTE *********************************/ +/* When we create a light object, a hardware timer will be enabled, this */ +/* timer is used to realize fade and blink operation. The default timer */ +/* occupied is timer 0 of timer group 0, user can change this config in */ +/* menuconfig. */ +/*************************************************************************/ + +typedef void *light_handle_t; + +#define HW_TIMER_GROUP (0) /**< Hardware timer group */ +#define HW_TIMER_ID (0) /**< Hardware timer number */ +#define HW_TIMER_DIVIDER (16) /**< Hardware timer clock divider */ +#define HW_TIMER_SCALE (TIMER_BASE_CLK / HW_TIMER_DIVIDER) /**< Convert counter value to seconds */ + +#define DUTY_SET_CYCLE (20) /**< Set duty cycle */ +#define DUTY_SET_GAMMA (0.6) /**< Set the Gamma value for the fade curve, default value is 0.6 */ + +#define LIGHT_MAX_CHANNEL_NUM (5) + +/** + * @brief light initialize + * + * @param timer the LEDC timer used by light + * @param speed_mode speed mode of LEDC timer + * @param freq_hz frequency of LEDC timer + * @param channel_num decide how many channels the light contains + * @param timer_bit LEDC PWM duty resolution + * + * @return the handle of light + */ +light_handle_t iot_light_create(ledc_timer_t timer, ledc_mode_t speed_mode, uint32_t freq_hz, uint8_t channel_num, ledc_timer_bit_t timer_bit); + +/** + * @brief add an output channel to light + * + * @param light_handle light handle + * @param channel_idx the id of channel (0 ~ channel_num-1) + * @param io_num the IO number use to output LEDC PWM + * @param channel the ledc channel you want to use + * + * @return + * - ESP_OK: succeed + * - others: fail + */ +esp_err_t iot_light_channel_regist(light_handle_t light_handle, uint8_t channel_idx, gpio_num_t io_num, ledc_channel_t channel); + +/** + * @brief free the memory of light + * + * @param light_handle light handle + * + * @return + * - ESP_OK: succeed + * - others: fail + */ +esp_err_t iot_light_delete(light_handle_t light_handle); + +/** + * @brief get channel duty + * + * @param light_handle light handle + * @param channel_id the id of channel (0 ~ channel_num-1) + * + * @return + * - LEDC_ERR_DUTY if parameter error + * - Others Current LEDC duty + */ +uint32_t iot_light_duty_get(light_handle_t light_handle, uint8_t channel_id); + +/** + * @brief set light fade with time. if set fade_period_ms as 0, set the duty directly. + * + * @param light_handle light handle + * @param channel_id the id of channel (0 ~ channel_num-1) + * @param duty target duty + * @param fade_period_ms fade time (uint: ms) + * + * @return + * - ESP_OK: succeed + * - others: fail + */ +esp_err_t iot_light_fade_with_time(light_handle_t light_handle, uint8_t channel_id, uint32_t duty, uint32_t fade_period_ms); + +/** + * @brief set breath config of a light channel, call `iot_light_operate_start` to start breath operation + * + * @param light_handle light handle + * @param channel_id the id of channel (0 ~ channel_num-1) + * @param duty the maximum duty when breath + * @param breath_period_ms breath period (uint: ms) + * + * @return + * - ESP_OK: succeed + * - others: fail + */ +esp_err_t iot_light_breath_config(light_handle_t light_handle, uint8_t channel_id, uint32_t duty, uint32_t breath_period_ms); + +/** + * @brief set blink config of a light channel, call `iot_light_operate_start` to start blink operation + * + * @param light_handle light handle + * @param channel_id the id of channel (0 ~ channel_num-1) + * @param blink_period_ms blink period (uint: ms) + * + * @return + * - ESP_OK: succeed + * - others: fail + */ +esp_err_t iot_light_blink_config(light_handle_t light_handle, uint8_t channel_id, uint32_t blink_period_ms); + +/** + * @brief start breath or blink operation, user need to set breath or blink config before call this API + * + * @param light_handle light handle + * @param channel_id the id of channel (0 ~ channel_num-1) + * + * @return + * - ESP_OK: succeed + * - others: fail + */ +esp_err_t iot_light_operate_start(light_handle_t light_handle, uint8_t channel_id); + +/** + * @brief stop breath or blink operation + * + * @param light_handle light handle + * @param channel_id the id of channel (0 ~ channel_num-1) + * + * @return + * - ESP_OK: succeed + * - others: fail + */ +esp_err_t iot_light_operate_stop(light_handle_t light_handle, uint8_t channel_id); + +#ifdef __cplusplus +} +#endif + +#endif /**< __IOT_LIGHT_H__ */ \ No newline at end of file diff --git a/17_light_utils_pwm/components/light_driver/include/light_driver.h b/17_light_utils_pwm/components/light_driver/include/light_driver.h new file mode 100644 index 0000000..b94bbf8 --- /dev/null +++ b/17_light_utils_pwm/components/light_driver/include/light_driver.h @@ -0,0 +1,188 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef __MDF_LIGHT_H__ +#define __MDF_LIGHT_H__ + + +#include +#include +#include +#include "errno.h" +#include "iot_led.h" +#include "esp32/rom/rtc.h" +#include "esp32/rom/crc.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "freertos/event_groups.h" + +#include "esp_system.h" +#include "esp_partition.h" +#include "esp_event.h" +#include "esp_http_client.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" +#include "lwip/sockets.h" +#include "driver/ledc.h" + +#include "nvs.h" +#include "nvs_flash.h" +#include "cJSON.h" +#include "driver/i2c.h" +#include "sys/param.h" +#include "driver/gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The mode of the five-color light + */ +enum light_mode { + MODE_NONE = 0, + MODE_RGB = 1, + MODE_HSV = 2, + MODE_CTB = 3, + MODE_ON = 4, + MODE_OFF = 5, + MODE_HUE_INCREASE = 4, + MODE_HUE_DECREASE = 5, + MODE_WARM_INCREASE = 6, + MODE_WARM_DECREASE = 7, + MODE_BRIGHTNESS_INCREASE = 8, + MODE_BRIGHTNESS_DECREASE = 9, +}; + +/** + * @brief Light driven configuration + */ +typedef struct { + gpio_num_t gpio_red; /**< Red corresponds to GPIO */ + gpio_num_t gpio_green; /**< Green corresponds to GPIO */ + gpio_num_t gpio_blue; /**< Blue corresponds to GPIO */ + gpio_num_t gpio_cold; /**< Cool corresponds to GPIO */ + gpio_num_t gpio_warm; /**< Warm corresponds to GPIO */ + uint32_t fade_period_ms; /**< The time from the current color to the next color */ + uint32_t blink_period_ms; /**< Period of flashing lights */ +} light_driver_config_t; + +/** + * @brief Light initialize + * + * @param config [description] + * + * @return + * - MDF_OK + * - MDF_ERR_INVALID_ARG + */ +esp_err_t light_driver_init(light_driver_config_t *config); + +/** + * @brief Light deinitialize + * + * @return + * - MDF_OK + * - MDF_ERR_INVALID_ARG + */ +esp_err_t light_driver_deinit(); + + +/** + * @brief Set the fade time of the light + * + * @param fade_period_ms The time from the current color to the next color + * @param blink_period_ms Light flashing frequency + * + * @return + * - MDF_OK + * - MDF_FAIL + */ +esp_err_t light_driver_config(uint32_t fade_period_ms, uint32_t blink_period_ms); + +/**@{*/ +/** + * @brief Set the status of the light + * + * + * @return + * - MDF_OK + * - MDF_ERR_INVALID_ARG + */ +esp_err_t light_driver_set_hue(uint16_t hue); +esp_err_t light_driver_set_saturation(uint8_t saturation); +esp_err_t light_driver_set_value(uint8_t value); +esp_err_t light_driver_set_color_temperature(uint8_t color_temperature); +esp_err_t light_driver_set_brightness(uint8_t brightness); +esp_err_t light_driver_set_hsv(uint16_t hue, uint8_t saturation, uint8_t value); +esp_err_t light_driver_set_ctb(uint8_t color_temperature, uint8_t brightness); +esp_err_t light_driver_set_switch(bool status); + +/**@}*/ + +/**@{*/ +/** + * @brief Set the status of the light + */ +uint16_t light_driver_get_hue(); +uint8_t light_driver_get_saturation(); +uint8_t light_driver_get_value(); +esp_err_t light_driver_get_hsv(uint16_t *hue, uint8_t *saturation, uint8_t *value); +uint8_t light_driver_get_color_temperature(); +uint8_t light_driver_get_brightness(); +esp_err_t light_driver_get_ctb(uint8_t *color_temperature, uint8_t *brightness); +bool light_driver_get_switch(); +uint8_t light_driver_get_mode(); +/**@}*/ + +/**@{*/ +/** + * @brief Used to indicate the operating mode, such as configuring the network mode, upgrading mode + * + * @note The state of the light is not saved in nvs + * + * @return + * - MDF_OK + * - MDF_ERR_INVALID_ARG + */ +esp_err_t light_driver_set_rgb(uint8_t red, uint8_t green, uint8_t blue); +esp_err_t light_driver_breath_start(uint8_t red, uint8_t green, uint8_t blue); +esp_err_t light_driver_breath_stop(); +esp_err_t light_driver_blink_start(uint8_t red, uint8_t green, uint8_t blue); +esp_err_t light_driver_blink_stop(); +/**@}*/ + +/**@{*/ +/** + * @brief Color gradient + * + * @return + * - MDF_OK + * - MDF_ERR_INVALID_ARG + */ +esp_err_t light_driver_fade_brightness(uint8_t brightness); +esp_err_t light_driver_fade_hue(uint16_t hue); +esp_err_t light_driver_fade_warm(uint8_t color_temperature); +esp_err_t light_driver_fade_stop(); +/**@}*/ + +#ifdef __cplusplus +} +#endif + +#endif/**< __MDF_LIGHT_H__ */ diff --git a/17_light_utils_pwm/components/light_driver/include/light_err.h b/17_light_utils_pwm/components/light_driver/include/light_err.h new file mode 100644 index 0000000..42335ee --- /dev/null +++ b/17_light_utils_pwm/components/light_driver/include/light_err.h @@ -0,0 +1,191 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#ifndef __MDF_ERR_H__ +#define __MDF_ERR_H__ + +#include "esp_err.h" +#include "esp_log.h" + +#ifdef __cplusplus +extern "C" { +#endif /**< _cplusplus */ + +typedef int32_t mdf_err_t; + +/* Definitions for error constants. */ + +/* Definitions for error constants. */ +#define MDF_OK 0 /**< mdf_err_t value indicating success (no error) */ +#define MDF_FAIL -1 /**< Generic mdf_err_t code indicating failure */ + +#define MDF_ERR_NO_MEM 0x100001 /**< Out of memory */ +#define MDF_ERR_INVALID_ARG 0x100002 /**< Invalid argument */ +#define MDF_ERR_INVALID_STATE 0x100003 /**< Invalid state */ +#define MDF_ERR_INVALID_SIZE 0x100004 /**< Invalid size */ +#define MDF_ERR_NOT_FOUND 0x100005 /**< Requested resource not found */ +#define MDF_ERR_NOT_SUPPORTED 0x100006 /**< Operation or feature not supported */ +#define MDF_ERR_TIMEOUT 0x100007 /**< Operation timed out */ +#define MDF_ERR_INVALID_RESPONSE 0x100008 /**< Received response was invalid */ +#define MDF_ERR_INVALID_CRC 0x100009 /**< CRC or checksum was invalid */ +#define MDF_ERR_INVALID_VERSION 0x10000A /**< Version was invalid */ +#define MDF_ERR_INVALID_MAC 0x10000B /**< MAC address was invalid */ +#define MDF_ERR_NOT_INIT 0x10000C /**< MAC address was invalid */ +#define MDF_ERR_BUF 0x10000D /**< The buffer is too small */ + +#define MDF_ERR_MWIFI_BASE 0x200000 /**< Starting number of MWIFI error codes */ +#define MDF_ERR_MESPNOW_BASE 0x300000 /**< Starting number of MESPNOW error codes */ +#define MDF_ERR_MCONFIG_BASE 0x400000 /**< Starting number of MCONFIG error codes */ +#define MDF_ERR_MUPGRADE_BASE 0x500000 /**< Starting number of MUPGRADE error codes */ +#define MDF_ERR_MDEBUG_BASE 0x600000 /**< Starting number of MDEBUG error codes */ +#define MDF_ERR_MLINK_BASE 0x700000 /**< Starting number of MLINK error codes */ +#define MDF_ERR_CUSTOM_BASE 0x800000 /**< Starting number of COUSTOM error codes */ + +/** + * @brief Returns string for mdf_err_t error codes + * + * This function finds the error code in a pre-generated lookup-table and + * returns its string representation. + * + * The function is generated by the Python script + * tools/gen_mdf_err_to_name.py which should be run each time an mdf_err_t + * error is modified, created or removed from the IDF project. + * + * @param code mdf_err_t error code + * @return string error message + */ +//const char *mdf_err_to_name(mdf_err_t code); + +#define mdf_err_to_name esp_err_to_name + +#ifndef CONFIG_MDF_LOG_LEVEL +#define CONFIG_MDF_LOG_LEVEL ESP_LOG_DEBUG +#endif /**< CONFIG_MDF_LOG_LEVEL */ +#define MDF_LOG_LEVEL CONFIG_MDF_LOG_LEVEL + +#define MDF_LOG_FORMAT(letter, format) LOG_COLOR_ ## letter #letter " (%u) [%s, %d]: " format LOG_RESET_COLOR "\n" + +#define MDF_LOGE( format, ... ) do { \ + if (MDF_LOG_LEVEL >= ESP_LOG_ERROR) { \ + esp_log_write(ESP_LOG_ERROR, TAG, MDF_LOG_FORMAT(E, format), esp_log_timestamp(), TAG, __LINE__, ##__VA_ARGS__); \ + } \ + } while(0) + +#define MDF_LOGW( format, ... ) do { \ + if (MDF_LOG_LEVEL >= ESP_LOG_WARN) { \ + esp_log_write(ESP_LOG_WARN, TAG, MDF_LOG_FORMAT(W, format), esp_log_timestamp(), TAG, __LINE__, ##__VA_ARGS__); \ + } \ + } while(0) + +#define MDF_LOGI( format, ... ) do { \ + if (MDF_LOG_LEVEL >= ESP_LOG_INFO) { \ + esp_log_write(ESP_LOG_INFO, TAG, MDF_LOG_FORMAT(I, format), esp_log_timestamp(), TAG, __LINE__, ##__VA_ARGS__); \ + } \ + } while(0) + +#define MDF_LOGD( format, ... ) do { \ + if (MDF_LOG_LEVEL >= ESP_LOG_DEBUG) { \ + esp_log_write(ESP_LOG_DEBUG, TAG, MDF_LOG_FORMAT(D, format), esp_log_timestamp(), TAG, __LINE__, ##__VA_ARGS__); \ + } \ + } while(0) + +#define MDF_LOGV( format, ... ) do { \ + if (MDF_LOG_LEVEL >= ESP_LOG_VERBOSE) { \ + esp_log_write(ESP_LOG_VERBOSE, TAG, MDF_LOG_FORMAT(V, format), esp_log_timestamp(), TAG, __LINE__, ##__VA_ARGS__); \ + } \ + } while(0) + + +/** + * Macro which can be used to check the error code, + * and terminate the program in case the code is not MDF_OK. + * Prints the error code, error location, and the failed statement to serial output. + * + * Disabled if assertions are disabled. + */ +#define MDF_ERROR_CHECK(con, err, format, ...) do { \ + if (con) { \ + if(*format != '\0') \ + MDF_LOGW("<%s> " format, mdf_err_to_name(err), ##__VA_ARGS__); \ + return err; \ + } \ + } while(0) + +/** + * @brief Macro serves similar purpose as ``assert``, except that it checks `esp_err_t` + * value rather than a `bool` condition. If the argument of `MDF_ERROR_ASSERT` + * is not equal `MDF_OK`, then an error message is printed on the console, + * and `abort()` is called. + * + * @note If `IDF monitor` is used, addresses in the backtrace will be converted + * to file names and line numbers. + * + * @param err [description] + * @return [description] + */ +#define MDF_ERROR_ASSERT(err) do { \ + mdf_err_t __err_rc = (err); \ + if (__err_rc != MDF_OK) { \ + MDF_LOGW("<%s> MDF_ERROR_ASSERT failed, at 0x%08x, expression: %s", \ + mdf_err_to_name(__err_rc), (intptr_t)__builtin_return_address(0) - 3, __ASSERT_FUNC); \ + assert(0 && #err); \ + } \ + } while(0) + +#define MDF_ERROR_GOTO(con, lable, format, ...) do { \ + if (con) { \ + if(*format != '\0') \ + MDF_LOGW(format, ##__VA_ARGS__); \ + goto lable; \ + } \ + } while(0) + +#define MDF_ERROR_CONTINUE(con, format, ...) { \ + if (con) { \ + if(*format != '\0') \ + MDF_LOGW(format, ##__VA_ARGS__); \ + continue; \ + } \ + } + +#define MDF_ERROR_BREAK(con, format, ...) { \ + if (con) { \ + if(*format != '\0') \ + MDF_LOGW(format, ##__VA_ARGS__); \ + break; \ + } \ + } + +#define MDF_PARAM_CHECK(con) do { \ + if (!(con)) { \ + MDF_LOGE(" !(%s)", #con); \ + return MDF_ERR_INVALID_ARG; \ + } \ + } while(0) + +#define MDF_CALLOC(n, size) ({ \ + void *ptr = calloc(n, size); \ + if (0) { \ + if(!ptr) { \ + MDF_LOGW(" Calloc size: %d, ptr: %p, heap free: %d", (int)(n) * (size), ptr, esp_get_free_heap_size()); \ + }\ + } \ + ptr; \ + }) + + +#ifdef __cplusplus +} +#endif /**< _cplusplus */ +#endif /**< __MDF_ERR_H__ */ diff --git a/17_light_utils_pwm/components/light_driver/iot_led.c b/17_light_utils_pwm/components/light_driver/iot_led.c new file mode 100644 index 0000000..46ba6ac --- /dev/null +++ b/17_light_utils_pwm/components/light_driver/iot_led.c @@ -0,0 +1,579 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#include "math.h" +#include "soc/ledc_reg.h" +#include "soc/timer_group_struct.h" +#include "soc/ledc_struct.h" +#include "driver/timer.h" +#include "driver/ledc.h" +#include "iot_led.h" +#include "esp_log.h" + +#include +#include +#include +#include "errno.h" +#include "esp_err.h" +#include "esp32/rom/rtc.h" +#include "esp32/rom/crc.h" + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "freertos/event_groups.h" + +#include "esp_system.h" +#include "esp_partition.h" +#include "esp_event.h" +#include "esp_http_client.h" +#include "esp_err.h" +#include "lwip/sockets.h" +#include "lwip/netdb.h" +#include "lwip/sockets.h" +#include "light_err.h" + +#include "nvs.h" +#include "nvs_flash.h" +#include "cJSON.h" +#include "driver/i2c.h" +#include "sys/param.h" +#include "driver/gpio.h" + + + +#define LEDC_FADE_MARGIN (10) +#define LEDC_VALUE_TO_DUTY(value) (value * ((1 << LEDC_TIMER_13_BIT) - 1) / UINT16_MAX) +#define LEDC_DUTY_TO_VALUE(value) (value * UINT16_MAX / ((1 << LEDC_TIMER_13_BIT) - 1)) +#define LEDC_FIXED_Q (8) +#define FLOATINT_2_FIXED(X, Q) ((int)((X) * (0x1U << Q))) +#define FIXED_2_FLOATING(X, Q) ((int)((X) / (0x1U << Q))) +#define GET_FIXED_INTEGER_PART(X, Q) (X >> Q) +#define GET_FIXED_DECIMAL_PART(X, Q) (X & ((0x1U << Q) - 1)) + + +typedef struct +{ + int cur; + int final; + int step; + int cycle; + size_t num; +} ledc_fade_data_t; + +typedef struct +{ + timer_group_t timer_group; + timer_idx_t timer_id; +} hw_timer_idx_t; + +typedef struct +{ + ledc_fade_data_t fade_data[LEDC_CHANNEL_MAX]; + ledc_mode_t speed_mode; + ledc_timer_t timer_num; + hw_timer_idx_t timer_id; +} iot_light_t; + +static const char *TAG = "iot_light"; +static DRAM_ATTR iot_light_t *g_light_config = NULL; +static DRAM_ATTR uint16_t *g_gamma_table = NULL; +static DRAM_ATTR bool g_hw_timer_started = false; +static DRAM_ATTR timg_dev_t *TG[2] = {&TIMERG0, &TIMERG1}; + +static IRAM_ATTR esp_err_t _timer_pause(timer_group_t group_num, timer_idx_t timer_num) +{ + TG[group_num]->hw_timer[timer_num].config.enable = 0; + return ESP_OK; +} + +static void iot_timer_create(hw_timer_idx_t *timer_id, bool auto_reload, + uint32_t timer_interval_ms, void *isr_handle) +{ + /* Select and initialize basic parameters of the timer */ + timer_config_t config; + config.divider = HW_TIMER_DIVIDER; + config.counter_dir = TIMER_COUNT_UP; + config.counter_en = TIMER_PAUSE; + config.alarm_en = TIMER_ALARM_EN; + config.intr_type = TIMER_INTR_LEVEL; + config.auto_reload = auto_reload; + timer_init(timer_id->timer_group, timer_id->timer_id, &config); + + /* Timer's counter will initially start from value below. + Also, if auto_reload is set, this value will be automatically reload on alarm */ + timer_set_counter_value(timer_id->timer_group, timer_id->timer_id, 0x00000000ULL); + + /* Configure the alarm value and the interrupt on alarm. */ + timer_set_alarm_value(timer_id->timer_group, timer_id->timer_id, timer_interval_ms * HW_TIMER_SCALE / 1000); + timer_enable_intr(timer_id->timer_group, timer_id->timer_id); + timer_isr_register(timer_id->timer_group, timer_id->timer_id, isr_handle, + (void *)timer_id->timer_id, ESP_INTR_FLAG_IRAM, NULL); +} + +static void iot_timer_start(hw_timer_idx_t *timer_id) +{ + timer_start(timer_id->timer_group, timer_id->timer_id); + g_hw_timer_started = true; +} + +static IRAM_ATTR void iot_timer_stop(hw_timer_idx_t *timer_id) +{ + _timer_pause(timer_id->timer_group, timer_id->timer_id); + g_hw_timer_started = false; +} + +static IRAM_ATTR esp_err_t iot_ledc_duty_config(ledc_mode_t speed_mode, ledc_channel_t channel, int hpoint_val, int duty_val, + uint32_t duty_direction, uint32_t duty_num, uint32_t duty_cycle, uint32_t duty_scale) +{ + if (hpoint_val >= 0) + { + LEDC.channel_group[speed_mode].channel[channel].hpoint.hpoint = hpoint_val & LEDC_HPOINT_HSCH1_V; + } + + if (duty_val >= 0) + { + LEDC.channel_group[speed_mode].channel[channel].duty.duty = duty_val; + } + + LEDC.channel_group[speed_mode].channel[channel].conf1.val = ((duty_direction & LEDC_DUTY_INC_HSCH0_V) << LEDC_DUTY_INC_HSCH0_S) | + ((duty_num & LEDC_DUTY_NUM_HSCH0_V) << LEDC_DUTY_NUM_HSCH0_S) | + ((duty_cycle & LEDC_DUTY_CYCLE_HSCH0_V) << LEDC_DUTY_CYCLE_HSCH0_S) | + ((duty_scale & LEDC_DUTY_SCALE_HSCH0_V) << LEDC_DUTY_SCALE_HSCH0_S); + + LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1; + LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1; + + if (speed_mode == LEDC_LOW_SPEED_MODE) + { + LEDC.channel_group[speed_mode].channel[channel].conf0.low_speed_update = 1; + } + + return ESP_OK; +} + +static IRAM_ATTR esp_err_t _iot_set_fade_with_step(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t target_duty, int scale, int cycle_num) +{ + uint32_t duty_cur = LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> 4; + int step_num = 0; + int dir = LEDC_DUTY_DIR_DECREASE; + + if (scale > 0) + { + if (duty_cur > target_duty) + { + step_num = (duty_cur - target_duty) / scale; + step_num = step_num > 1023 ? 1023 : step_num; + scale = (step_num == 1023) ? (duty_cur - target_duty) / step_num : scale; + } + else + { + dir = LEDC_DUTY_DIR_INCREASE; + step_num = (target_duty - duty_cur) / scale; + step_num = step_num > 1023 ? 1023 : step_num; + scale = (step_num == 1023) ? (target_duty - duty_cur) / step_num : scale; + } + } + + if (scale > 0 && step_num > 0) + { + iot_ledc_duty_config(speed_mode, channel, -1, duty_cur << 4, dir, step_num, cycle_num, scale); + } + else + { + iot_ledc_duty_config(speed_mode, channel, -1, target_duty << 4, dir, 0, 1, 0); + } + + return ESP_OK; +} + +static IRAM_ATTR esp_err_t _iot_set_fade_with_time(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t target_duty, int max_fade_time_ms) +{ + uint32_t freq = 0; + uint32_t duty_cur = LEDC.channel_group[speed_mode].channel[channel].duty_rd.duty_read >> 4; + uint32_t duty_delta = target_duty > duty_cur ? target_duty - duty_cur : duty_cur - target_duty; + + uint32_t timer_source_clk = LEDC.timer_group[speed_mode].timer[g_light_config->timer_num].conf.tick_sel; + uint32_t duty_resolution = LEDC.timer_group[speed_mode].timer[g_light_config->timer_num].conf.duty_resolution; + uint32_t clock_divider = LEDC.timer_group[speed_mode].timer[g_light_config->timer_num].conf.clock_divider; + uint32_t precision = (0x1U << duty_resolution); + + if (timer_source_clk == LEDC_APB_CLK) + { + freq = ((uint64_t)LEDC_APB_CLK_HZ << 8) / precision / clock_divider; + } + else + { + freq = ((uint64_t)LEDC_REF_CLK_HZ << 8) / precision / clock_divider; + } + + if (duty_delta == 0) + { + return _iot_set_fade_with_step(speed_mode, channel, target_duty, 0, 0); + } + + int total_cycles = max_fade_time_ms * freq / 1000; + + if (total_cycles == 0) + { + return _iot_set_fade_with_step(speed_mode, channel, target_duty, 0, 0); + } + + int scale, cycle_num; + + if (total_cycles > duty_delta) + { + scale = 1; + cycle_num = total_cycles / duty_delta; + + if (cycle_num > LEDC_DUTY_NUM_HSCH0_V) + { + cycle_num = LEDC_DUTY_NUM_HSCH0_V; + } + } + else + { + cycle_num = 1; + scale = duty_delta / total_cycles; + + if (scale > LEDC_DUTY_SCALE_HSCH0_V) + { + scale = LEDC_DUTY_SCALE_HSCH0_V; + } + } + + return _iot_set_fade_with_step(speed_mode, channel, target_duty, scale, cycle_num); +} + +static IRAM_ATTR esp_err_t _iot_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) +{ + LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1; + LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1; + + if (speed_mode == LEDC_LOW_SPEED_MODE) + { + LEDC.channel_group[speed_mode].channel[channel].conf0.low_speed_update = 1; + } + + return ESP_OK; +} + +static IRAM_ATTR esp_err_t iot_ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty) +{ + return iot_ledc_duty_config(speed_mode, + channel, // uint32_t chan_num, + -1, + duty << 4, // uint32_t duty_val,the least 4 bits are decimal part + 1, // uint32_t increase, + 1, // uint32_t duty_num, + 1, // uint32_t duty_cycle, + 0 // uint32_t duty_scale + ); +} + +static void gamma_table_create(uint16_t *gamma_table, float correction) +{ + float value_tmp = 0; + + /** + * @brief gamma curve formula: y=a*x^(1/gm) + * x ∈ (0,(GAMMA_TABLE_SIZE-1)/GAMMA_TABLE_SIZE) + * a = GAMMA_TABLE_SIZE + */ + for (int i = 0; i < GAMMA_TABLE_SIZE; i++) + { + value_tmp = (float)(i) / GAMMA_TABLE_SIZE; + value_tmp = powf(value_tmp, 1.0f / correction); + gamma_table[i] = (uint16_t)FLOATINT_2_FIXED((value_tmp * GAMMA_TABLE_SIZE), LEDC_FIXED_Q); + } +} + +static IRAM_ATTR uint32_t gamma_value_to_duty(int value) +{ + uint32_t tmp_q = GET_FIXED_INTEGER_PART(value, LEDC_FIXED_Q); + uint32_t tmp_r = GET_FIXED_DECIMAL_PART(value, LEDC_FIXED_Q); + + uint16_t cur = LEDC_VALUE_TO_DUTY(g_gamma_table[tmp_q]); + uint16_t next = LEDC_VALUE_TO_DUTY(g_gamma_table[tmp_q + 1]); + return (cur + (next - cur) * tmp_r / (0x1U << LEDC_FIXED_Q)); +} + +static IRAM_ATTR void fade_timercb(void *para) +{ + int timer_idx = (int)para; + int idle_channel_num = 0; + + if (HW_TIMER_GROUP == TIMER_GROUP_0) + { + /* Retrieve the interrupt status */ + uint32_t intr_status = TIMERG0.int_st_timers.val; + TIMERG0.hw_timer[timer_idx].update = 1; + + /* Clear the interrupt */ + if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) + { + TIMERG0.int_clr_timers.t0 = 1; + } + else if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_1) + { + TIMERG0.int_clr_timers.t1 = 1; + } + + /* After the alarm has been triggered + we need enable it again, so it is triggered the next time */ + TIMERG0.hw_timer[timer_idx].config.alarm_en = TIMER_ALARM_EN; + } + else if (HW_TIMER_GROUP == TIMER_GROUP_1) + { + uint32_t intr_status = TIMERG1.int_st_timers.val; + TIMERG1.hw_timer[timer_idx].update = 1; + + if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) + { + TIMERG1.int_clr_timers.t0 = 1; + } + else if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_1) + { + TIMERG1.int_clr_timers.t1 = 1; + } + + TIMERG1.hw_timer[timer_idx].config.alarm_en = TIMER_ALARM_EN; + } + + for (int channel = 0; channel < LEDC_CHANNEL_MAX; channel++) + { + ledc_fade_data_t *fade_data = g_light_config->fade_data + channel; + + if (fade_data->num > 0) + { + fade_data->num--; + + if (fade_data->step) + { + fade_data->cur += fade_data->step; + + if (fade_data->num != 0) + { + _iot_set_fade_with_time(g_light_config->speed_mode, channel, + gamma_value_to_duty(fade_data->cur), + DUTY_SET_CYCLE - LEDC_FADE_MARGIN); + } + else + { + iot_ledc_set_duty(g_light_config->speed_mode, channel, gamma_value_to_duty(fade_data->cur)); + } + + _iot_update_duty(g_light_config->speed_mode, channel); + } + else + { + iot_ledc_set_duty(g_light_config->speed_mode, channel, gamma_value_to_duty(fade_data->cur)); + _iot_update_duty(g_light_config->speed_mode, channel); + } + } + else if (fade_data->cycle) + { + fade_data->num = fade_data->cycle - 1; + + if (fade_data->step) + { + fade_data->step *= -1; + fade_data->cur += fade_data->step; + } + else + { + fade_data->cur = (fade_data->cur == fade_data->final) ? 0 : fade_data->final; + } + + _iot_set_fade_with_time(g_light_config->speed_mode, channel, + gamma_value_to_duty(fade_data->cur), + DUTY_SET_CYCLE - LEDC_FADE_MARGIN); + _iot_update_duty(g_light_config->speed_mode, channel); + } + else + { + idle_channel_num++; + } + } + + if (idle_channel_num >= LEDC_CHANNEL_MAX) + { + iot_timer_stop(&g_light_config->timer_id); + } +} + +esp_err_t iot_led_init(ledc_timer_t timer_num, ledc_mode_t speed_mode, uint32_t freq_hz) +{ + esp_err_t ret = ESP_OK; + const ledc_timer_config_t ledc_time_config = { + .speed_mode = speed_mode, + .timer_num = timer_num, + .freq_hz = freq_hz, + .duty_resolution = LEDC_TIMER_13_BIT, + }; + + ret = ledc_timer_config(&ledc_time_config); + ESP_ERROR_CHECK(ret); + + + if (g_gamma_table == NULL) + { + g_gamma_table = MDF_CALLOC(GAMMA_TABLE_SIZE, sizeof(uint16_t)); + gamma_table_create(g_gamma_table, GAMMA_CORRECTION); + } + else + { + ESP_LOGE(TAG, "gamma_table has been initialized"); + } + + if (g_light_config == NULL) + { + g_light_config = MDF_CALLOC(1, sizeof(iot_light_t)); + g_light_config->timer_num = timer_num; + g_light_config->speed_mode = speed_mode; + + hw_timer_idx_t hw_timer = { + .timer_group = HW_TIMER_GROUP, + .timer_id = HW_TIMER_ID, + }; + g_light_config->timer_id = hw_timer; + iot_timer_create(&hw_timer, 1, DUTY_SET_CYCLE, fade_timercb); + } + else + { + ESP_LOGE(TAG, "g_light_config has been initialized"); + } + + return ESP_OK; +} + +esp_err_t iot_led_deinit() +{ + if (g_gamma_table) + { + free(g_gamma_table); + } + + if (g_light_config) + { + free(g_light_config); + + } + + timer_disable_intr(g_light_config->timer_id.timer_group, g_light_config->timer_id.timer_id); + + return ESP_OK; +} + +esp_err_t iot_led_regist_channel(ledc_channel_t channel, gpio_num_t gpio_num) +{ + esp_err_t ret = ESP_OK; + MDF_ERROR_CHECK(g_light_config == NULL, ESP_FAIL, "iot_led_init() must be called first"); +#ifdef CONFIG_SPIRAM_SUPPORT + MDF_ERROR_CHECK(gpio_num != GPIO_NUM_16 || gpio_num != GPIO_NUM_17, ESP_FAIL, + "gpio_num must not conflict to PSRAM(IO16 && IO17)"); +#endif + const ledc_channel_config_t ledc_ch_config = { + .gpio_num = gpio_num, + .channel = channel, + .intr_type = LEDC_INTR_DISABLE, + .speed_mode = g_light_config->speed_mode, + .timer_sel = g_light_config->timer_num, + }; + + ret = ledc_channel_config(&ledc_ch_config); + MDF_ERROR_CHECK(ret != ESP_OK, ret, "LEDC channel configuration"); + + return ESP_OK; +} + +esp_err_t iot_led_get_channel(ledc_channel_t channel, uint8_t *dst) +{ + MDF_ERROR_CHECK(g_light_config == NULL, ESP_FAIL, "iot_led_init() must be called first"); + MDF_ERROR_CHECK(dst == NULL, ESP_FAIL, "dst should not be NULL"); + int cur = g_light_config->fade_data[channel].cur; + *dst = FIXED_2_FLOATING(cur, LEDC_FIXED_Q); + return ESP_OK; +} + +esp_err_t iot_led_set_channel(ledc_channel_t channel, uint8_t value, uint32_t fade_ms) +{ + MDF_ERROR_CHECK(g_light_config == NULL, ESP_FAIL, "iot_led_init() must be called first"); + ledc_fade_data_t *fade_data = g_light_config->fade_data + channel; + + fade_data->final = FLOATINT_2_FIXED(value, LEDC_FIXED_Q); + + if (fade_ms < DUTY_SET_CYCLE) + { + fade_data->num = 1; + } + else + { + fade_data->num = fade_ms / DUTY_SET_CYCLE; + } + + fade_data->step = abs(fade_data->cur - fade_data->final) / fade_data->num; + + if (fade_data->cur > fade_data->final) + { + fade_data->step *= -1; + } + + if (fade_data->cycle != 0) + { + fade_data->cycle = 0; + } + + if (g_hw_timer_started != true) + { + iot_timer_start(&g_light_config->timer_id); + } + + return ESP_OK; +} + +esp_err_t iot_led_start_blink(ledc_channel_t channel, uint8_t value, uint32_t period_ms, bool fade_flag) +{ + MDF_ERROR_CHECK(g_light_config == NULL, ESP_FAIL, "iot_led_init() must be called first"); + ledc_fade_data_t *fade_data = g_light_config->fade_data + channel; + + fade_data->final = fade_data->cur = FLOATINT_2_FIXED(value, LEDC_FIXED_Q); + fade_data->cycle = period_ms / 2 / DUTY_SET_CYCLE; + fade_data->num = (fade_flag) ? period_ms / 2 / DUTY_SET_CYCLE : 0; + fade_data->step = (fade_flag) ? fade_data->cur / fade_data->num * -1 : 0; + + if (g_hw_timer_started != true) + { + iot_timer_start(&g_light_config->timer_id); + } + + return ESP_OK; +} + +esp_err_t iot_led_stop_blink(ledc_channel_t channel) +{ + MDF_ERROR_CHECK(g_light_config == NULL, ESP_FAIL, "iot_led_init() must be called first"); + ledc_fade_data_t *fade_data = g_light_config->fade_data + channel; + fade_data->cycle = fade_data->num = 0; + + return ESP_OK; +} + +esp_err_t iot_led_set_gamma_table(const uint16_t gamma_table[GAMMA_TABLE_SIZE]) +{ + MDF_ERROR_CHECK(g_gamma_table == NULL, ESP_FAIL, "iot_led_init() must be called first"); + memcpy(g_gamma_table, gamma_table, GAMMA_TABLE_SIZE * sizeof(uint16_t)); + return ESP_OK; +} diff --git a/17_light_utils_pwm/components/light_driver/iot_light.c b/17_light_utils_pwm/components/light_driver/iot_light.c new file mode 100644 index 0000000..1a88716 --- /dev/null +++ b/17_light_utils_pwm/components/light_driver/iot_light.c @@ -0,0 +1,570 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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. + +#include +#include "math.h" +#include "sys/time.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "esp_log.h" +#include "soc/ledc_reg.h" +#include "soc/timer_group_struct.h" +#include "soc/ledc_struct.h" +#include "driver/periph_ctrl.h" +#include "driver/timer.h" +#include "driver/ledc.h" +#include "iot_light.h" + +static const char *TAG = "light"; + +#define IOT_CHECK(tag, a, ret) \ + if (!(a)) \ + { \ + return (ret); \ + } +#define ERR_ASSERT(tag, param, ret) IOT_CHECK(tag, (param) == ESP_OK, ret) +#define POINT_ASSERT(tag, param) IOT_CHECK(tag, (param) != NULL, ESP_FAIL) +#define LIGHT_NUM_MAX 4 + +typedef enum +{ + LIGHT_CH_NUM_1 = 1, /*!< Light channel number */ + LIGHT_CH_NUM_2 = 2, /*!< Light channel number */ + LIGHT_CH_NUM_3 = 3, /*!< Light channel number */ + LIGHT_CH_NUM_4 = 4, /*!< Light channel number */ + LIGHT_CH_NUM_5 = 5, /*!< Light channel number */ + LIGHT_CH_NUM_MAX, /*!< user shouldn't use this */ +} light_channel_num_t; + +typedef struct +{ + timer_group_t timer_group; + timer_idx_t timer_id; +} hw_timer_idx_t; + +typedef struct +{ + gpio_num_t io_num; + ledc_mode_t mode; + ledc_channel_t channel; + int breath_period; + int blink_period; + uint32_t fade_step_num; + uint32_t *fade_duty_step; + int fade_duty_counter; + bool fade_step_up; + bool fade_once; + bool fade_start; +} light_channel_t; + +typedef struct +{ + uint8_t channel_num; + ledc_mode_t mode; + ledc_timer_t ledc_timer; + uint32_t full_duty; + uint32_t freq_hz; + ledc_timer_bit_t timer_bit; + hw_timer_idx_t hw_timer; + light_channel_t *channel_group[0]; +} light_t; + +static bool g_fade_installed = false; +static bool g_hw_timer_started = false; +static light_t *g_light_group[LIGHT_NUM_MAX] = {NULL}; +static esp_err_t iot_light_duty_set(light_handle_t light_handle, uint8_t channel_id, uint32_t duty); + +static void iot_timer_create(hw_timer_idx_t *timer_id, bool auto_reload, double timer_interval_sec, timer_isr_handle_t *isr_handle) +{ + /* Select and initialize basic parameters of the timer */ + timer_config_t config; + config.divider = HW_TIMER_DIVIDER; + config.counter_dir = TIMER_COUNT_UP; + config.counter_en = TIMER_PAUSE; + config.alarm_en = TIMER_ALARM_EN; + config.intr_type = TIMER_INTR_LEVEL; + config.auto_reload = auto_reload; + timer_init(timer_id->timer_group, timer_id->timer_id, &config); + + /* Timer's counter will initially start from value below. + Also, if auto_reload is set, this value will be automatically reload on alarm */ + timer_set_counter_value(timer_id->timer_group, timer_id->timer_id, 0x00000000ULL); + + /* Configure the alarm value and the interrupt on alarm. */ + timer_set_alarm_value(timer_id->timer_group, timer_id->timer_id, timer_interval_sec * HW_TIMER_SCALE); + timer_enable_intr(timer_id->timer_group, timer_id->timer_id); + timer_isr_register(timer_id->timer_group, timer_id->timer_id, (void *)isr_handle, + (void *)timer_id->timer_id, ESP_INTR_FLAG_IRAM, NULL); +} + +static void iot_timer_start(hw_timer_idx_t *timer_id) +{ + timer_start(timer_id->timer_group, timer_id->timer_id); + g_hw_timer_started = true; +} + +static void iot_timer_stop(hw_timer_idx_t *timer_id) +{ + timer_disable_intr(timer_id->timer_group, timer_id->timer_id); + timer_pause(timer_id->timer_group, timer_id->timer_id); + g_hw_timer_started = false; +} + +static IRAM_ATTR void iot_ledc_ls_channel_update(ledc_mode_t speed_mode, ledc_channel_t channel_num) +{ + if (speed_mode == LEDC_LOW_SPEED_MODE) + { + LEDC.channel_group[speed_mode].channel[channel_num].conf0.low_speed_update = 1; + } +} + +static IRAM_ATTR esp_err_t iot_ledc_duty_config(ledc_mode_t speed_mode, ledc_channel_t channel_num, int hpoint_val, int duty_val, + uint32_t duty_direction, uint32_t duty_num, uint32_t duty_cycle, uint32_t duty_scale) +{ + if (hpoint_val >= 0) + { + LEDC.channel_group[speed_mode].channel[channel_num].hpoint.hpoint = hpoint_val & LEDC_HPOINT_HSCH1_V; + } + + if (duty_val >= 0) + { + LEDC.channel_group[speed_mode].channel[channel_num].duty.duty = duty_val; + } + + LEDC.channel_group[speed_mode].channel[channel_num].conf1.val = ((duty_direction & LEDC_DUTY_INC_HSCH0_V) << LEDC_DUTY_INC_HSCH0_S) | + ((duty_num & LEDC_DUTY_NUM_HSCH0_V) << LEDC_DUTY_NUM_HSCH0_S) | + ((duty_cycle & LEDC_DUTY_CYCLE_HSCH0_V) << LEDC_DUTY_CYCLE_HSCH0_S) | + ((duty_scale & LEDC_DUTY_SCALE_HSCH0_V) << LEDC_DUTY_SCALE_HSCH0_S); + iot_ledc_ls_channel_update(speed_mode, channel_num); + return ESP_OK; +} + +static IRAM_ATTR esp_err_t iot_ledc_set_duty(ledc_mode_t speed_mode, ledc_channel_t channel, uint32_t duty) +{ + return iot_ledc_duty_config(speed_mode, + channel, //uint32_t chan_num, + -1, + duty << 4, //uint32_t duty_val,the least 4 bits are decimal part + 1, //uint32_t increase, + 1, //uint32_t duty_num, + 1, //uint32_t duty_cycle, + 0 //uint32_t duty_scale + ); +} + +static IRAM_ATTR esp_err_t iot_ledc_update_duty(ledc_mode_t speed_mode, ledc_channel_t channel) +{ + LEDC.channel_group[speed_mode].channel[channel].conf0.sig_out_en = 1; + LEDC.channel_group[speed_mode].channel[channel].conf1.duty_start = 1; + iot_ledc_ls_channel_update(speed_mode, channel); + return ESP_OK; +} + +static IRAM_ATTR void breath_timer_callback(void *para) +{ + int timer_idx = (int)para; + + if (HW_TIMER_GROUP == TIMER_GROUP_0) + { + /* Retrieve the interrupt status */ + uint32_t intr_status = TIMERG0.int_st_timers.val; + TIMERG0.hw_timer[timer_idx].update = 1; + + /* Clear the interrupt */ + if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) + { + TIMERG0.int_clr_timers.t0 = 1; + } + else if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_1) + { + TIMERG0.int_clr_timers.t1 = 1; + } + + /* After the alarm has been triggered + we need enable it again, so it is triggered the next time */ + TIMERG0.hw_timer[timer_idx].config.alarm_en = TIMER_ALARM_EN; + } + else if (HW_TIMER_GROUP == TIMER_GROUP_1) + { + uint32_t intr_status = TIMERG1.int_st_timers.val; + TIMERG1.hw_timer[timer_idx].update = 1; + + if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_0) + { + TIMERG1.int_clr_timers.t0 = 1; + } + else if ((intr_status & BIT(timer_idx)) && timer_idx == TIMER_1) + { + TIMERG1.int_clr_timers.t1 = 1; + } + + TIMERG1.hw_timer[timer_idx].config.alarm_en = TIMER_ALARM_EN; + } + + for (int i = 0; i < LIGHT_NUM_MAX; i++) + { + if (g_light_group[i] != NULL) + { + light_t *light = g_light_group[i]; + + for (int j = 0; j < light->channel_num; j++) + { + light_channel_t *l_chn = light->channel_group[j]; + + if (l_chn->fade_start == true) + { + if (l_chn->fade_step_up == true) + { + l_chn->fade_duty_counter++; + } + else + { + l_chn->fade_duty_counter--; + } + + if ((l_chn->fade_duty_counter >= 0) && (l_chn->fade_duty_counter < l_chn->fade_step_num)) + { + iot_ledc_set_duty(l_chn->mode, l_chn->channel, l_chn->fade_duty_step[l_chn->fade_duty_counter]); + iot_ledc_update_duty(l_chn->mode, l_chn->channel); + } + else + { + if (l_chn->fade_once != true) + { + l_chn->fade_step_up = 1 - l_chn->fade_step_up; + } + else + { + l_chn->fade_start = false; + } + } + } + } + } + } +} + +static light_channel_t *light_channel_create(gpio_num_t io_num, ledc_channel_t channel, ledc_mode_t mode, ledc_timer_t timer) +{ + ledc_channel_config_t ledc_channel = { + .channel = channel, + .duty = 0, + .gpio_num = io_num, + .intr_type = LEDC_INTR_FADE_END, + .speed_mode = mode, + .timer_sel = timer}; + ERR_ASSERT(TAG, ledc_channel_config(&ledc_channel), NULL); + light_channel_t *pwm = (light_channel_t *)calloc(1, sizeof(light_channel_t)); + pwm->io_num = io_num; + pwm->channel = channel; + pwm->mode = mode; + pwm->breath_period = 0; + pwm->blink_period = 0; + pwm->fade_step_num = 0; + pwm->fade_duty_step = NULL; + pwm->fade_duty_counter = 0; + pwm->fade_step_up = true; + pwm->fade_once = false; + pwm->fade_start = false; + return pwm; +} + +static esp_err_t light_channel_delete(light_channel_t *light_channel) +{ + POINT_ASSERT(TAG, light_channel); + + if (light_channel->fade_duty_step != NULL) + { + heap_caps_free(light_channel->fade_duty_step); + light_channel->fade_duty_step = NULL; + } + + free(light_channel); + return ESP_OK; +} + +static esp_err_t light_channel_fade_clear(light_channel_t *light_channel) +{ + light_channel->fade_start = false; + light_channel->fade_step_num = 0; + light_channel->fade_duty_counter = 0; + light_channel->fade_step_up = true; + light_channel->fade_once = false; + + if (light_channel->fade_duty_step != NULL) + { + heap_caps_free(light_channel->fade_duty_step); + light_channel->fade_duty_step = NULL; + } + + return ESP_OK; +} + +light_handle_t iot_light_create(ledc_timer_t timer, ledc_mode_t speed_mode, uint32_t freq_hz, uint8_t channel_num, ledc_timer_bit_t timer_bit) +{ + IOT_CHECK(TAG, channel_num != 0, NULL); + ledc_timer_config_t timer_conf = { + .timer_num = timer, + .speed_mode = speed_mode, + .freq_hz = freq_hz, + .bit_num = timer_bit}; + ERR_ASSERT(TAG, ledc_timer_config(&timer_conf), NULL); + light_t *light_ptr = (light_t *)calloc(1, sizeof(light_t) + sizeof(light_channel_t *) * channel_num); + light_ptr->channel_num = channel_num; + light_ptr->ledc_timer = timer; + light_ptr->full_duty = (1 << timer_bit) - 1; + light_ptr->freq_hz = freq_hz; + light_ptr->mode = speed_mode; + light_ptr->timer_bit = timer_bit; + light_ptr->hw_timer.timer_group = HW_TIMER_GROUP; + light_ptr->hw_timer.timer_id = HW_TIMER_ID; + + if (g_hw_timer_started == false) + { + iot_timer_create(&(light_ptr->hw_timer), 1, (double)DUTY_SET_CYCLE / 1000, (void *)breath_timer_callback); + iot_timer_start(&(light_ptr->hw_timer)); + } + + for (int i = 0; i < channel_num; i++) + { + light_ptr->channel_group[i] = NULL; + } + + for (int i = 0; i < LIGHT_NUM_MAX; i++) + { + if (g_light_group[i] == NULL) + { + g_light_group[i] = light_ptr; + break; + } + } + + return (light_handle_t)light_ptr; +} + +esp_err_t iot_light_delete(light_handle_t light_handle) +{ + light_t *light = (light_t *)light_handle; + POINT_ASSERT(TAG, light_handle); + + for (int i = 0; i < light->channel_num; i++) + { + if (light->channel_group[i] != NULL) + { + iot_light_duty_set(light, i, 0); + light_channel_delete(light->channel_group[i]); + } + } + + for (int i = 0; i < LIGHT_NUM_MAX; i++) + { + if (g_light_group[i] == light) + { + g_light_group[i] = NULL; + break; + } + } + + for (int i = 0; i < LIGHT_NUM_MAX; i++) + { + if (g_light_group[i] != NULL) + { + goto FREE_MEM; + } + } + + ledc_fade_func_uninstall(); + g_fade_installed = false; + iot_timer_stop(&(light->hw_timer)); +FREE_MEM: + free(light_handle); + return ESP_OK; +} + +esp_err_t iot_light_channel_regist(light_handle_t light_handle, uint8_t channel_idx, gpio_num_t io_num, ledc_channel_t channel) +{ + light_t *light = (light_t *)light_handle; + POINT_ASSERT(TAG, light_handle); + IOT_CHECK(TAG, channel_idx < light->channel_num, FAIL); + + if (light->channel_group[channel_idx] != NULL) + { + ESP_LOGE(TAG, "this channel index has been registered"); + return ESP_FAIL; + } + + light->channel_group[channel_idx] = light_channel_create(io_num, channel, light->mode, light->ledc_timer); + + if (g_fade_installed == false) + { + ledc_fade_func_install(0); + g_fade_installed = true; + } + + return ESP_OK; +} + +static esp_err_t iot_light_duty_set(light_handle_t light_handle, uint8_t channel_id, uint32_t duty) +{ + light_t *light = (light_t *)light_handle; + POINT_ASSERT(TAG, light_handle); + IOT_CHECK(TAG, channel_id < light->channel_num, ESP_FAIL); + POINT_ASSERT(TAG, light->channel_group[channel_id]); + light_channel_t *l_chn = light->channel_group[channel_id]; + + light_channel_fade_clear(l_chn); + + iot_ledc_set_duty(l_chn->mode, l_chn->channel, duty); + iot_ledc_update_duty(l_chn->mode, l_chn->channel); + return ESP_OK; +} + +uint32_t iot_light_duty_get(light_handle_t light_handle, uint8_t channel_id) +{ + light_t *light = (light_t *)light_handle; + POINT_ASSERT(TAG, light_handle); + IOT_CHECK(TAG, channel_id < light->channel_num, ESP_FAIL); + POINT_ASSERT(TAG, light->channel_group[channel_id]); + light_channel_t *l_chn = light->channel_group[channel_id]; + + return ledc_get_duty(l_chn->mode, l_chn->channel); +} + +esp_err_t iot_light_fade_with_time(light_handle_t light_handle, uint8_t channel_id, uint32_t duty, uint32_t fade_period_ms) +{ + light_t *light = (light_t *)light_handle; + POINT_ASSERT(TAG, light_handle); + IOT_CHECK(TAG, channel_id < light->channel_num, ESP_FAIL); + POINT_ASSERT(TAG, light->channel_group[channel_id]); + light_channel_t *l_chn = light->channel_group[channel_id]; + + if (fade_period_ms == 0) + { + return iot_light_duty_set(light, channel_id, duty); + } + + light_channel_fade_clear(l_chn); + + l_chn->fade_step_num = fade_period_ms / DUTY_SET_CYCLE + 1; + l_chn->fade_duty_step = (uint32_t *)heap_caps_malloc(l_chn->fade_step_num * sizeof(uint32_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + + uint32_t duty_cur = iot_light_duty_get(light, channel_id); + int duty_delta = duty - duty_cur; + + if (duty_delta > 0) + { + float coe = (duty_delta / pow((double)l_chn->fade_step_num, (double)1 / DUTY_SET_GAMMA)); + + for (int i = 0; i < l_chn->fade_step_num; i++) + { + l_chn->fade_duty_step[i] = duty_cur + (int)(coe * pow((double)i, (double)1 / DUTY_SET_GAMMA)); + } + + l_chn->fade_step_up = true; + } + else + { + duty_delta = 0 - duty_delta; + float coe = (duty_delta / pow((double)l_chn->fade_step_num, (double)1 / DUTY_SET_GAMMA)); + + for (int i = 0; i < l_chn->fade_step_num; i++) + { + l_chn->fade_duty_step[i] = duty + (int)(coe * pow((double)i, (double)1 / DUTY_SET_GAMMA)); + } + + l_chn->fade_duty_counter = l_chn->fade_step_num; + l_chn->fade_step_up = false; + } + + l_chn->fade_once = true; + l_chn->fade_start = true; + + return ESP_OK; +} + +esp_err_t iot_light_breath_config(light_handle_t light_handle, uint8_t channel_id, uint32_t duty, uint32_t breath_period_ms) +{ + light_t *light = (light_t *)light_handle; + POINT_ASSERT(TAG, light_handle); + IOT_CHECK(TAG, channel_id < light->channel_num, ESP_FAIL); + POINT_ASSERT(TAG, light->channel_group[channel_id]); + light_channel_t *l_chn = light->channel_group[channel_id]; + + light_channel_fade_clear(l_chn); + + /** + * @brief control light with nonlinearity fade, the duty and the fade step + * conform to gamma curve. gamma curve formula: y=a*x^(1/gm) firstly, + * use the target duty and step number to calculate coefficient `a`, + * secondly, calculate the duty for every step + */ + + l_chn->fade_step_num = (breath_period_ms / 2) / DUTY_SET_CYCLE + 1; + float coe = (duty / pow((double)l_chn->fade_step_num, (double)1 / DUTY_SET_GAMMA)); + + l_chn->fade_duty_step = (uint32_t *)heap_caps_malloc(l_chn->fade_step_num * sizeof(uint32_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + + for (int i = 0; i < l_chn->fade_step_num; i++) + { + l_chn->fade_duty_step[i] = (int)(coe * pow((double)i, (double)1 / DUTY_SET_GAMMA)); + } + + return ESP_OK; +} + +esp_err_t iot_light_blink_config(light_handle_t light_handle, uint8_t channel_id, uint32_t blink_period_ms) +{ + light_t *light = (light_t *)light_handle; + POINT_ASSERT(TAG, light_handle); + IOT_CHECK(TAG, channel_id < light->channel_num, ESP_FAIL); + light_channel_t *l_chn = light->channel_group[channel_id]; + + light_channel_fade_clear(l_chn); + + l_chn->fade_step_num = (blink_period_ms / 2) / DUTY_SET_CYCLE; + l_chn->fade_duty_step = (uint32_t *)heap_caps_malloc(l_chn->fade_step_num * sizeof(uint32_t), MALLOC_CAP_8BIT | MALLOC_CAP_INTERNAL); + int i = 0; + + for (i = 0; i < (l_chn->fade_step_num / 2); i++) + { + l_chn->fade_duty_step[i] = 0; + } + + for (; i < l_chn->fade_step_num; i++) + { + l_chn->fade_duty_step[i] = light->full_duty; + } + + return ESP_OK; +} + +esp_err_t iot_light_operate_start(light_handle_t light_handle, uint8_t channel_id) +{ + light_t *light = (light_t *)light_handle; + POINT_ASSERT(TAG, light_handle); + IOT_CHECK(TAG, channel_id < light->channel_num, ESP_FAIL); + light->channel_group[channel_id]->fade_start = true; + return ESP_OK; +} + +esp_err_t iot_light_operate_stop(light_handle_t light_handle, uint8_t channel_id) +{ + light_t *light = (light_t *)light_handle; + POINT_ASSERT(TAG, light_handle); + IOT_CHECK(TAG, channel_id < light->channel_num, ESP_FAIL); + light->channel_group[channel_id]->fade_start = false; + // iot_light_duty_set(light, channel_id, 0); + return ESP_OK; +} diff --git a/17_light_utils_pwm/components/light_driver/light_driver.c b/17_light_utils_pwm/components/light_driver/light_driver.c new file mode 100644 index 0000000..2839d55 --- /dev/null +++ b/17_light_utils_pwm/components/light_driver/light_driver.c @@ -0,0 +1,750 @@ +// Copyright 2017 Espressif Systems (Shanghai) PTE LTD +// +// 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. + + + +#include +#include +#include +#include "errno.h" +#include "iot_led.h" +#include "light_driver.h" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "freertos/event_groups.h" + + + +#include "light_err.h" +#include "esp_log.h" +#include "esp_system.h" +#include "esp_partition.h" +#include "esp_event.h" +#include "esp_http_client.h" + +#include "lwip/sockets.h" +#include "lwip/netdb.h" +#include "lwip/sockets.h" +#include "driver/ledc.h" + +#include "light_err.h" +#include "nvs.h" +#include "nvs_flash.h" +#include "cJSON.h" +#include "driver/i2c.h" +#include "sys/param.h" +#include "driver/gpio.h" + +/** + * @brief The state of the five-color light + */ +typedef struct { + uint8_t mode; + uint8_t on; + uint16_t hue; + uint8_t saturation; + uint8_t value; + uint8_t color_temperature; + uint8_t brightness; + uint32_t fade_period_ms; + uint32_t blink_period_ms; +} light_status_t; + +/** + * @brief The channel of the five-color light + */ +enum light_channel { + CHANNEL_ID_RED = 0, + CHANNEL_ID_GREEN, + CHANNEL_ID_BLUE, + CHANNEL_ID_WARM, + CHANNEL_ID_COLD, +}; + + + + +#define LIGHT_STATUS_STORE_KEY "light_status" +#define LIGHT_FADE_PERIOD_MAX_MS (3 * 1000) + +static const char *TAG = "light_driver"; +static light_status_t g_light_status = {0}; +static bool g_light_blink_flag = false; +static TimerHandle_t g_fade_timer = NULL; +static int g_fade_mode = MODE_NONE; +static uint16_t g_fade_hue = 0; + +esp_err_t light_driver_init(light_driver_config_t *config) +{ + MDF_PARAM_CHECK(config); + + // if (mdf_info_load(LIGHT_STATUS_STORE_KEY, &g_light_status, sizeof(light_status_t)) != ESP_OK) { + // memset(&g_light_status, 0, sizeof(light_status_t)); + // g_light_status.mode = MODE_HSV; + // g_light_status.on = 1; + // g_light_status.hue = 360; + // g_light_status.saturation = 0; + // g_light_status.value = 100; + // g_light_status.color_temperature = 0; + // g_light_status.brightness = 30; + // } + + iot_led_init(LEDC_TIMER_0, LEDC_HIGH_SPEED_MODE, 1000); + + g_light_status.fade_period_ms = config->fade_period_ms; + g_light_status.blink_period_ms = config->blink_period_ms; + + iot_led_regist_channel(CHANNEL_ID_RED, config->gpio_red); + iot_led_regist_channel(CHANNEL_ID_GREEN, config->gpio_green); + iot_led_regist_channel(CHANNEL_ID_BLUE, config->gpio_blue); + iot_led_regist_channel(CHANNEL_ID_WARM, config->gpio_warm); + iot_led_regist_channel(CHANNEL_ID_COLD, config->gpio_cold); + + //ESP_LOGD(TAG, "hue: %d, saturation: %d, value: %d", g_light_status.hue, g_light_status.saturation, g_light_status.value); + //ESP_LOGD(TAG, "brightness: %d, color_temperature: %d", g_light_status.brightness, g_light_status.color_temperature); + + return ESP_OK; +} + +esp_err_t light_driver_deinit() +{ + esp_err_t ret = ESP_OK; + + iot_led_deinit(); + + return ret; +} + +esp_err_t light_driver_config(uint32_t fade_period_ms, uint32_t blink_period_ms) +{ + g_light_status.fade_period_ms = fade_period_ms; + g_light_status.blink_period_ms = blink_period_ms; + + return ESP_OK; +} + +esp_err_t light_driver_set_rgb(uint8_t red, uint8_t green, uint8_t blue) +{ + esp_err_t ret = ESP_FAIL; + + ret = iot_led_set_channel(CHANNEL_ID_RED, red, 0); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_GREEN, green, 0); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_BLUE, blue, 0); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_WARM, 0, 0); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_COLD, 0, 0); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + return ESP_OK; +} + +static esp_err_t light_driver_hsv2rgb(uint16_t hue, uint8_t saturation, uint8_t value, + uint8_t *red, uint8_t *green, uint8_t *blue) +{ + uint16_t hi = (hue / 60) % 6; + uint16_t F = 100 * hue / 60 - 100 * hi; + uint16_t P = value * (100 - saturation) / 100; + uint16_t Q = value * (10000 - F * saturation) / 10000; + uint16_t T = value * (10000 - saturation * (100 - F)) / 10000; + + switch (hi) { + case 0: + *red = value; + *green = T; + *blue = P; + break; + + case 1: + *red = Q; + *green = value; + *blue = P; + break; + + case 2: + *red = P; + *green = value; + *blue = T; + break; + + case 3: + *red = P; + *green = Q; + *blue = value; + break; + + case 4: + *red = T; + *green = P; + *blue = value; + break; + + case 5: + *red = value; + *green = P; + *blue = Q; + break; + + default: + return ESP_FAIL; + } + + *red = *red * 255 / 100; + *green = *green * 255 / 100; + *blue = *blue * 255 / 100; + + return ESP_OK; +} + +static void light_driver_rgb2hsv(uint16_t red, uint16_t green, uint16_t blue, + uint16_t *h, uint8_t *s, uint8_t *v) +{ + double hue, saturation, value; + double m_max = MAX(red, MAX(green, blue)); + double m_min = MIN(red, MIN(green, blue)); + double m_delta = m_max - m_min; + + value = m_max / 255.0; + + if (m_delta == 0) { + hue = 0; + saturation = 0; + } else { + saturation = m_delta / m_max; + + if (red == m_max) { + hue = (green - blue) / m_delta; + } else if (green == m_max) { + hue = 2 + (blue - red) / m_delta; + } else { + hue = 4 + (red - green) / m_delta; + } + + hue = hue * 60; + + if (hue < 0) { + hue = hue + 360; + } + } + + *h = (int)(hue + 0.5); + *s = (int)(saturation * 100 + 0.5); + *v = (int)(value * 100 + 0.5); +} + +esp_err_t light_driver_set_hsv(uint16_t hue, uint8_t saturation, uint8_t value) +{ + MDF_PARAM_CHECK(hue <= 360); + MDF_PARAM_CHECK(saturation <= 100); + MDF_PARAM_CHECK(value <= 100); + + esp_err_t ret = ESP_OK; + uint8_t red = 0; + uint8_t green = 0; + uint8_t blue = 0; + + ret = light_driver_hsv2rgb(hue, saturation, value, &red, &green, &blue); + MDF_ERROR_CHECK(ret < 0, ret, "light_driver_hsv2rgb, ret: %d", ret); + + //ESP_LOGV(TAG,"red: %d, green: %d, blue: %d", red, green, blue); + + ret = iot_led_set_channel(CHANNEL_ID_RED, red, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_GREEN, green, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_BLUE, blue, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + if (g_light_status.mode != MODE_HSV) { + ret = iot_led_set_channel(CHANNEL_ID_WARM, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_COLD, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + } + + g_light_status.mode = MODE_HSV; + g_light_status.on = 1; + g_light_status.hue = hue; + g_light_status.value = value; + g_light_status.saturation = saturation; + + //ret = mdf_info_save(LIGHT_STATUS_STORE_KEY, &g_light_status, sizeof(light_status_t)); + //MDF_ERROR_CHECK(ret < 0, ret, "mdf_info_save, ret: %d", ret); + + return ESP_OK; +} + +esp_err_t light_driver_set_hue(uint16_t hue) +{ + return light_driver_set_hsv(hue, g_light_status.saturation, g_light_status.value); +} + +esp_err_t light_driver_set_saturation(uint8_t saturation) +{ + return light_driver_set_hsv(g_light_status.hue, saturation, g_light_status.value); +} + +esp_err_t light_driver_set_value(uint8_t value) +{ + return light_driver_set_hsv(g_light_status.hue, g_light_status.saturation, value); +} + +esp_err_t light_driver_get_hsv(uint16_t *hue, uint8_t *saturation, uint8_t *value) +{ + MDF_PARAM_CHECK(hue); + MDF_PARAM_CHECK(saturation); + MDF_PARAM_CHECK(value); + + *hue = g_light_status.hue; + *saturation = g_light_status.saturation; + *value = g_light_status.value; + + return ESP_OK; +} + +uint16_t light_driver_get_hue() +{ + return g_light_status.hue; +} + +uint8_t light_driver_get_saturation() +{ + return g_light_status.saturation; +} + +uint8_t light_driver_get_value() +{ + return g_light_status.value; +} + +uint8_t light_driver_get_mode() +{ + return g_light_status.mode; +} + +esp_err_t light_driver_set_ctb(uint8_t color_temperature, uint8_t brightness) +{ + MDF_PARAM_CHECK(brightness <= 100); + MDF_PARAM_CHECK(color_temperature <= 100); + + esp_err_t ret = ESP_OK; + uint8_t warm_tmp = color_temperature * brightness / 100; + uint8_t cold_tmp = (100 - color_temperature) * brightness / 100; + warm_tmp = warm_tmp < 15 ? warm_tmp : 14 + warm_tmp * 86 / 100; + cold_tmp = cold_tmp < 15 ? cold_tmp : 14 + cold_tmp * 86 / 100; + + ret = iot_led_set_channel(CHANNEL_ID_COLD, + cold_tmp * 255 / 100, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_WARM, + warm_tmp * 255 / 100, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + if (g_light_status.mode != MODE_CTB) { + ret = iot_led_set_channel(CHANNEL_ID_RED, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_GREEN, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_BLUE, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + } + + g_light_status.mode = MODE_CTB; + g_light_status.on = 1; + g_light_status.brightness = brightness; + g_light_status.color_temperature = color_temperature; + + //ret = mdf_info_save(LIGHT_STATUS_STORE_KEY, &g_light_status, sizeof(light_status_t)); + //MDF_ERROR_CHECK(ret < 0, ret, "mdf_info_save, ret: %d", ret); + + return ESP_OK; +} + +esp_err_t light_driver_set_color_temperature(uint8_t color_temperature) +{ + return light_driver_set_ctb(color_temperature, g_light_status.brightness); +} + +esp_err_t light_driver_set_brightness(uint8_t brightness) +{ + return light_driver_set_ctb(g_light_status.color_temperature, brightness); +} + +esp_err_t light_driver_get_ctb(uint8_t *color_temperature, uint8_t *brightness) +{ + MDF_PARAM_CHECK(color_temperature); + MDF_PARAM_CHECK(brightness); + + *brightness = g_light_status.brightness; + *color_temperature = g_light_status.color_temperature; + + return ESP_OK; +} + +uint8_t light_driver_get_color_temperature() +{ + return g_light_status.color_temperature; +} + +uint8_t light_driver_get_brightness() +{ + return g_light_status.brightness; +} + +esp_err_t light_driver_set_switch(bool on) +{ + esp_err_t ret = ESP_OK; + g_light_status.on = on; + + if (!g_light_status.on) { + ret = iot_led_set_channel(CHANNEL_ID_RED, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_GREEN, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_BLUE, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_COLD, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_WARM, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_set_channel, ret: %d", ret); + + } else { + switch (g_light_status.mode) { + case MODE_HSV: + g_light_status.value = (g_light_status.value) ? g_light_status.value : 100; + ret = light_driver_set_hsv(g_light_status.hue, g_light_status.saturation, g_light_status.value); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "light_driver_set_hsv, ret: %d", ret); + break; + + case MODE_CTB: + g_light_status.brightness = (g_light_status.brightness) ? g_light_status.brightness : 100; + ret = light_driver_set_ctb(g_light_status.color_temperature, g_light_status.brightness); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "light_driver_set_ctb, ret: %d", ret); + break; + + default: + //ESP_LOGD(TAG, ("This operation is not supported"); + break; + } + } + + //ret = mdf_info_save(LIGHT_STATUS_STORE_KEY, &g_light_status, sizeof(light_status_t)); + // MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "mdf_info_save, ret: %d", ret); + + return ESP_OK; +} + +bool light_driver_get_switch() +{ + return g_light_status.on; +} + +esp_err_t light_driver_breath_start(uint8_t red, uint8_t green, uint8_t blue) +{ + esp_err_t ret = ESP_OK; + + ret = iot_led_start_blink(CHANNEL_ID_RED, + red, g_light_status.blink_period_ms, true); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_start_blink, ret: %d", ret); + ret = iot_led_start_blink(CHANNEL_ID_GREEN, + green, g_light_status.blink_period_ms, true); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_start_blink, ret: %d", ret); + ret = iot_led_start_blink(CHANNEL_ID_BLUE, + blue, g_light_status.blink_period_ms, true); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_start_blink, ret: %d", ret); + + g_light_blink_flag = true; + + return ESP_OK; +} + +esp_err_t light_driver_breath_stop() +{ + esp_err_t ret = ESP_OK; + + if (g_light_blink_flag == false) { + return ESP_OK; + } + + ret = iot_led_stop_blink(CHANNEL_ID_RED); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_stop_blink, ret: %d", ret); + + ret = iot_led_stop_blink(CHANNEL_ID_GREEN); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_stop_blink, ret: %d", ret); + + ret = iot_led_stop_blink(CHANNEL_ID_BLUE); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_stop_blink, ret: %d", ret); + + light_driver_set_switch(true); + + return ESP_OK; +} + +esp_err_t light_driver_fade_brightness(uint8_t brightness) +{ + esp_err_t ret = ESP_OK; + g_fade_mode = MODE_ON; + uint32_t fade_period_ms = 0; + + if (g_light_status.mode == MODE_HSV) { + uint8_t red = 0; + uint8_t green = 0; + uint8_t blue = 0; + + ret = light_driver_hsv2rgb(g_light_status.hue, g_light_status.saturation, g_light_status.value, &red, &green, &blue); + MDF_ERROR_CHECK(ret < 0, ret, "light_driver_hsv2rgb, ret: %d", ret); + + if (brightness != 0) { + ret = iot_led_get_channel((ledc_channel_t)CHANNEL_ID_RED, &red); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_get_channel, ret: %d", ret); + ret = iot_led_get_channel((ledc_channel_t)CHANNEL_ID_GREEN, &green); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_get_channel, ret: %d", ret); + ret = iot_led_get_channel((ledc_channel_t)CHANNEL_ID_BLUE, &blue); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_get_channel, ret: %d", ret); + + uint8_t max_color = MAX(MAX(red, green), blue); + uint8_t change_value = brightness * 255 / 100 - max_color; + fade_period_ms = LIGHT_FADE_PERIOD_MAX_MS * change_value / 255; + } else { + fade_period_ms = LIGHT_FADE_PERIOD_MAX_MS * MAX(MAX(red, green), blue) / 255; + red = 0; + } + + g_light_status.value = brightness; + light_driver_hsv2rgb(g_light_status.hue, g_light_status.saturation, g_light_status.value, &red, &green, &blue); + + ret = iot_led_set_channel(CHANNEL_ID_RED, red, fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_GREEN, green, fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_BLUE, blue, fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + } else if (g_light_status.mode == MODE_CTB) { + uint8_t warm_tmp = 0; + uint8_t cold_tmp = 0; + fade_period_ms = LIGHT_FADE_PERIOD_MAX_MS * g_light_status.brightness / 100; + + if (brightness != 0) { + uint8_t change_value = brightness - g_light_status.brightness; + warm_tmp = g_light_status.color_temperature; + cold_tmp = (brightness - g_light_status.color_temperature); + fade_period_ms = LIGHT_FADE_PERIOD_MAX_MS * change_value / 100; + } + + ret = iot_led_set_channel(CHANNEL_ID_COLD, + cold_tmp * 255 / 100, fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_WARM, + warm_tmp * 255 / 100, fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + g_light_status.brightness = brightness; + } + + //ret = mdf_info_save(LIGHT_STATUS_STORE_KEY, &g_light_status, sizeof(light_status_t)); + // MDF_ERROR_CHECK(ret < 0, ret, "mdf_info_save, ret: %d", ret); + + return ESP_OK; +} + +static void light_fade_timer_stop() +{ + if (!g_fade_timer) { + return ; + } + + if (!xTimerStop(g_fade_timer, portMAX_DELAY)) { + //ESP_LOGD(TAG, ("xTimerStop timer: %p", g_fade_timer); + } + + if (!xTimerDelete(g_fade_timer, portMAX_DELAY)) { + //ESP_LOGD(TAG, ("xTimerDelete timer: %p", g_fade_timer); + } + + g_fade_timer = NULL; +} + +static void light_fade_timer_cb(void *timer) +{ + uint8_t red = 0; + uint8_t green = 0; + uint8_t blue = 0; + uint32_t fade_period_ms = LIGHT_FADE_PERIOD_MAX_MS * 2 / 6; + int variety = (g_fade_hue > 180) ? 60 : -60; + + if (g_light_status.hue >= 360 || g_light_status.hue <= 0) { + light_fade_timer_stop(); + } + + g_light_status.hue = g_light_status.hue >= 360 ? 360 : g_light_status.hue + variety; + g_light_status.hue = g_light_status.hue <= 60 ? 0 : g_light_status.hue + variety; + + light_driver_hsv2rgb(g_light_status.hue, g_light_status.saturation, g_light_status.value, &red, &green, &blue); + + iot_led_set_channel(CHANNEL_ID_RED, red, fade_period_ms); + iot_led_set_channel(CHANNEL_ID_GREEN, green, fade_period_ms); + iot_led_set_channel(CHANNEL_ID_BLUE, blue, fade_period_ms); +} + +esp_err_t light_driver_fade_hue(uint16_t hue) +{ + esp_err_t ret = ESP_OK; + g_fade_mode = MODE_HSV; + g_fade_hue = hue; + + light_fade_timer_stop(); + + if (g_light_status.mode != MODE_HSV) { + ret = iot_led_set_channel(CHANNEL_ID_WARM, 0, 0); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_COLD, 0, 0); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + } + + g_light_status.mode = MODE_HSV; + g_light_status.value = (g_light_status.value == 0) ? 100 : g_light_status.value; + uint32_t fade_period_ms = LIGHT_FADE_PERIOD_MAX_MS * 2 / 6; + + light_fade_timer_cb(NULL); + + g_fade_timer = xTimerCreate("light_timer", fade_period_ms, + true, NULL, light_fade_timer_cb); + xTimerStart(g_fade_timer, 0); + + return ESP_OK; +} + +esp_err_t light_driver_fade_warm(uint8_t color_temperature) +{ + esp_err_t ret = ESP_OK; + g_fade_mode = MODE_CTB; + + if (g_light_status.mode != MODE_CTB) { + ret = iot_led_set_channel(CHANNEL_ID_RED, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_GREEN, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_BLUE, 0, g_light_status.fade_period_ms); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + } + + uint8_t warm_tmp = color_temperature * g_light_status.brightness / 100; + uint8_t cold_tmp = (100 - color_temperature) * g_light_status.brightness / 100; + + ret = iot_led_set_channel(CHANNEL_ID_COLD, cold_tmp * 255 / 100, LIGHT_FADE_PERIOD_MAX_MS); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + ret = iot_led_set_channel(CHANNEL_ID_WARM, warm_tmp * 255 / 100, LIGHT_FADE_PERIOD_MAX_MS); + MDF_ERROR_CHECK(ret < 0, ret, "iot_led_set_channel, ret: %d", ret); + + g_light_status.mode = MODE_CTB; + g_light_status.color_temperature = color_temperature; + // ret = mdf_info_save(LIGHT_STATUS_STORE_KEY, &g_light_status, sizeof(light_status_t)); + // MDF_ERROR_CHECK(ret < 0, ret, "mdf_info_save, ret: %d", ret); + + return ESP_OK; +} + +esp_err_t light_driver_fade_stop() +{ + esp_err_t ret = ESP_OK; + + light_fade_timer_stop(); + + if (g_light_status.mode != MODE_CTB) { + uint16_t hue = 0; + uint8_t saturation = 0; + uint8_t value = 0; + + ret = iot_led_stop_blink(CHANNEL_ID_RED); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_stop_blink, ret: %d", ret); + + ret = iot_led_stop_blink(CHANNEL_ID_GREEN); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_stop_blink, ret: %d", ret); + + ret = iot_led_stop_blink(CHANNEL_ID_BLUE); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_stop_blink, ret: %d", ret); + + uint8_t red, green, blue; + + ret = iot_led_get_channel(CHANNEL_ID_RED, &red); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_get_channel, ret: %d", ret); + ret = iot_led_get_channel(CHANNEL_ID_GREEN, &green); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_get_channel, ret: %d", ret); + ret = iot_led_get_channel(CHANNEL_ID_BLUE, &blue); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_get_channel, ret: %d", ret); + + light_driver_rgb2hsv(red, green, blue, &hue, &saturation, &value); + + g_light_status.hue = (g_fade_mode == MODE_HSV) ? hue : g_light_status.hue; + g_light_status.value = (g_fade_mode == MODE_OFF || g_fade_mode == MODE_ON) ? value : g_light_status.value; + } else { + uint8_t color_temperature = 0; + uint8_t brightness = 0; + + ret = iot_led_stop_blink(CHANNEL_ID_COLD); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_stop_blink, ret: %d", ret); + + ret = iot_led_stop_blink(CHANNEL_ID_WARM); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_stop_blink, ret: %d", ret); + + uint8_t warm_tmp, cold_tmp; + uint8_t tmp; + + ret = iot_led_get_channel(CHANNEL_ID_WARM, &tmp); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_get_channel, ret: %d", ret); + warm_tmp = (int32_t)tmp * 100 / 255; + + ret = iot_led_get_channel(CHANNEL_ID_COLD, &tmp); + MDF_ERROR_CHECK(ret < 0, ESP_FAIL, "iot_led_get_channel, ret: %d", ret); + cold_tmp = (int32_t)tmp * 100 / 255; + + color_temperature = (!warm_tmp) ? 0 : 100 / (cold_tmp / warm_tmp + 1); + brightness = (!color_temperature) ? cold_tmp : warm_tmp * 100 / color_temperature; + + g_light_status.brightness = (g_fade_mode == MODE_OFF || g_fade_mode == MODE_ON) ? brightness : g_light_status.brightness; + g_light_status.color_temperature = (g_fade_mode == MODE_CTB) ? color_temperature : g_light_status.color_temperature; + } + + //ret = mdf_info_save(LIGHT_STATUS_STORE_KEY, &g_light_status, sizeof(light_status_t)); + //MDF_ERROR_CHECK(ret < 0, ret, "mdf_info_save, ret: %d", ret); + + g_fade_mode = MODE_NONE; + return ESP_OK; +} diff --git a/17_light_utils_pwm/main/CMakeLists.txt b/17_light_utils_pwm/main/CMakeLists.txt new file mode 100644 index 0000000..81ecd07 --- /dev/null +++ b/17_light_utils_pwm/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "mcpwm_brushed_dc_control_example.c" + INCLUDE_DIRS ".") \ No newline at end of file diff --git a/17_light_utils_pwm/main/Kconfig.projbuild b/17_light_utils_pwm/main/Kconfig.projbuild new file mode 100644 index 0000000..5692084 --- /dev/null +++ b/17_light_utils_pwm/main/Kconfig.projbuild @@ -0,0 +1,108 @@ +menu "Example Configuration" + +menu "light driver config" +config LIGHT_GPIO_RED + int "Light red pin GPIO number" + range 0 33 + default 4 + help + There are more enumerations like that + up to GPIO39, excluding GPIO20, GPIO24 and GPIO28..31. + They are not shown here to reduce redundant information. + @note GPIO34..39 are input mode only. + +config LIGHT_GPIO_GREEN + int "Light green pin GPIO number" + range 0 33 + default 16 + help + There are more enumerations like that + up to GPIO39, excluding GPIO20, GPIO24 and GPIO28..31. + They are not shown here to reduce redundant information. + @note GPIO34..39 are input mode only. + +config LIGHT_GPIO_BLUE + int "Light blue pin GPIO number" + range 0 33 + default 5 + help + There are more enumerations like that + up to GPIO39, excluding GPIO20, GPIO24 and GPIO28..31. + They are not shown here to reduce redundant information. + @note GPIO34..39 are input mode only. + +config LIGHT_GPIO_COLD + int "Light cold colors pin GPIO number" + range 0 33 + default 23 + help + There are more enumerations like that + up to GPIO39, excluding GPIO20, GPIO24 and GPIO28..31. + They are not shown here to reduce redundant information. + @note GPIO34..39 are input mode only. + +config LIGHT_GPIO_WARM + int "Light warm color pin GPIO number" + range 0 33 + default 19 + help + There are more enumerations like that + up to GPIO39, excluding GPIO20, GPIO24 and GPIO28..31. + They are not shown here to reduce redundant information. + @note GPIO34..39 are input mode only. + +config LIGHT_FADE_PERIOD_MS + int "The time from the current color to the next color" + default 500 + help + The time from the current color to the next color. + +config LIGHT_BLINK_PERIOD_MS + int "Period of blinking lights" + default 2000 + help + Period of blinking lights. +endmenu + +config LIGHT_VERSION + string "Light version" + default "1.3.1" + help + Version of the five-color light + +config LIGHT_MEMORY_DEBUG + bool "Enable memory debugging" + default n + help + Enable memory debugging. + +config LIGHT_BLE_GATEWAY + bool "Enable the BLE gateway on the non-root node" + default n + help + Enabling the BLE gateway on a non-root node, + enabling the BLE gateway will consume a lot of memory. + +config LIGHT_NETWORKING_TIME_OPTIMIZE_ENABLE + bool "Optimize networking time" + default n + help + 1. Set the Bluetooth configuration network device to a fixed root node, eliminating the time of competing for the root node + 2. Once the networking is complete, select again whether there is a better root node. + 3. When power is turned on again, the device that was the root node last time is selected as the root node by default. + +config LIGHT_RESTART_COUNT_RESET + int "Reset the number of times the device has been powered off continuously to enter the configured network mode" + range 3 10 + default 3 + help + Reset the number of times the device has been powered off continuously to enter the configured network mode. + +config LIGHT_RESTART_COUNT_FALLBACK + int "Number of times the device restarted abnormally, entered, and exited from the previous version" + range 15 32 + default 15 + help + Number of times the device restarted abnormally, entered, and exited from the previous version. + +endmenu diff --git a/17_light_utils_pwm/main/component.mk b/17_light_utils_pwm/main/component.mk new file mode 100644 index 0000000..44bd2b5 --- /dev/null +++ b/17_light_utils_pwm/main/component.mk @@ -0,0 +1,3 @@ +# +# Main Makefile. This is basically the same as a component makefile. +# diff --git a/17_light_utils_pwm/main/light_example.c b/17_light_utils_pwm/main/light_example.c new file mode 100644 index 0000000..c78f1f8 --- /dev/null +++ b/17_light_utils_pwm/main/light_example.c @@ -0,0 +1,47 @@ +#include +#include +#include +#include "errno.h" +#include "iot_led.h" +#include "nvs.h" +#include "nvs_flash.h" +#include + +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "freertos/task.h" +#include "freertos/timers.h" +#include "freertos/event_groups.h" + +#include "light_driver.h" + + +static const char *TAG = "light_example"; + + +void app_main() +{ + + /** + * NOTE: + * If the module has SPI flash, GPIOs 6-11 are connected to the module’s integrated SPI flash and PSRAM. + * If the module has PSRAM, GPIOs 16 and 17 are connected to the module’s integrated PSRAM. + */ + light_driver_config_t driver_config = { + .gpio_red = CONFIG_LIGHT_GPIO_RED, + .gpio_green = CONFIG_LIGHT_GPIO_GREEN, + .gpio_blue = CONFIG_LIGHT_GPIO_BLUE, + .gpio_cold = CONFIG_LIGHT_GPIO_COLD, + .gpio_warm = CONFIG_LIGHT_GPIO_WARM, + .fade_period_ms = CONFIG_LIGHT_FADE_PERIOD_MS, + .blink_period_ms = CONFIG_LIGHT_BLINK_PERIOD_MS, + }; + + /** + * @brief Light driver initialization + */ + (light_driver_init(&driver_config)); + light_driver_set_switch(true); + + ESP_LOGE(TAG,"hello world"); +} diff --git a/17_light_utils_pwm/partitions.csv b/17_light_utils_pwm/partitions.csv new file mode 100644 index 0000000..b623497 --- /dev/null +++ b/17_light_utils_pwm/partitions.csv @@ -0,0 +1,8 @@ +# Name, Type, SubType, Offset, Size, Flags +nvs, data, nvs, 0x9000, 16k +otadata, data, ota, 0xd000, 8k +phy_init, data, phy, 0xf000, 4k +ota_0, app, ota_0, 0x10000, 1920k +ota_1, app, ota_1, , 1920k +coredump, data, coredump, , 64K +reserved, data, 0xfe, , 128K \ No newline at end of file diff --git a/17_light_utils_pwm/sdkconfig.defaults b/17_light_utils_pwm/sdkconfig.defaults new file mode 100644 index 0000000..800b5af --- /dev/null +++ b/17_light_utils_pwm/sdkconfig.defaults @@ -0,0 +1,11 @@ +# Override some defaults in mesh application project. + +# +# Serial flasher config +# +CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y