安信可ESP8266系列模组实现PWM平滑调光,丝滑一般的效果,让你体验艺术的照明方案;(附带demo)

一、冷暖光的PWM关系;

     我们在买灯具时候,一般看我们的灯支持怎么样的调节,比如只是单色,冷暖色,还是RGB七彩控制的。

    一般地,家庭也就单色和冷暖光为多,而 RGB 是那些舞台音乐场景为多。所以,智能家居的灯具涉及,以单色和冷暖光为多,以 冷暖光为例,那些灯具的色温是怎么定义的? PWM 又是如何输出这样的效果呢? 这里,我一一为大家阐述:


2.1、色温的定义

    任何一种属性都有单位,比如温度有摄氏度,而色温的单位是 卡尔文 ,简称 K,下面一图说明了:

    色温数值越大,看到的效果越冷白;
    
误解:色温越大,就越暖光;
在这里插入图片描述

    而我们常买的灯具又是如何产生这样的效果呢? 聪明的开发者,采用冷白灯珠和暖光灯具各自产生的亮度不一样,就有了以上的效果!下面是我平时开发中常用的2钟灯珠类型,3.3v即可点亮;

在这里插入图片描述


2.2、PWM与指定的色温亮度的计算

     PWM平滑调光已经在目前最新的 master 分支实现了,例子位置在:

     这里带带大家简单入门这个例子:

  1. LEDC功能来源 ESP32 之鉴,ledc_timer_config_t 的配置分辨率尤其重要,例子是采用13位的LEDC_TIMER_13_BIT,又可以参照结构体设置其他分辨率;
  2. 配置 GPIO 引脚的方法,参考官方例子;
  3. 然后,我们最关心的是设置占空比方法的对应方法入参,ledc_set_fade_with_time(模式,通道,周期,渐变时间); 而设置周期的入参范围:[ 0 , 2的分辨率位数次方],以13位分辨率举个例子,就是 2的13次方,即 2^13=8192。所以,设置是13位分辨率的话,入参范围为:【0,8192】
  4. 还有重要一步,开始设置,类似 pwm_start();采用 ledc_fade_start(模式,通道 是否阻塞);,后面的阻塞和非阻塞可能有些不懂,就是等待这个方法函数执行完毕再下一个方法,而如果是非阻塞,直接返回执行下一个函数;

     而目前 2020.4.30 我本人今天在跑这个例子时候,发现一个bug,现已提交上去,具体的issue链接在这: issues/883,对于这个bug我也是无脑解决,具体的解决方法在上述的链接里面;

     以上就是的带领大家简单入门 LEDC 的例子,下面步入正题;


     既然有2个灯珠同时在亮,各自的亮度不同来调节色温亮度,那么我们好奇的是如何通过 pwm 产生这样的关系呢?

     很多人也许会这样说:色温大小就是暖色灯珠的亮度明暗,而亮度大小就是冷白灯珠的亮度明暗,这样的说话是错误的!

     对于 PWM 和 色温亮度的计算的公式,博主接触到很多公式,这里本人总结的为例,如下:

  • color_temperature :用户预想的灯具色温数值,范围[2700,6500] ,这范围是参 阿里飞燕平台的;
  • brightness :用户预想的灯具的亮度数值,范围[0,100]
  • 下面的 8192 的是从上面 13位定时器
  • 计算过程如下,得到的cold_tmpPWM冷灯珠的占空比,warm_tmpPWM冷灯珠的占空比:
    int part = (APK_MAX_COLORTEMP - APK_MIN_COLORTEMP) / 100;
    int temp = APK_MAX_COLORTEMP - color_temperature;
    int tempWW = ((temp) / part);
    uint8_t outCW = (brightness - (brightness * tempWW / 100));
    uint8_t outWW = (brightness * tempWW / 100);
    int outBrightnessChannle = 8192 * outCW / 100;
    int outColortempChannle = 8192 * outWW / 100;

     看了上面的计算步骤,是否很简单?我们可以假想一下:

  • 效果1 中性光
         入参 color_temperature = 4096 ,brightness = 100 ;
         代入公式后得到:outColortempChannle=4096 ,outBrightnessChannle =50 ;

  • 效果2 最冷光
         入参 color_temperature = 8192 ,brightness = 100 ;
         代入公式后得到:outColortempChannle=8192 ,outBrightnessChannle =0;

  • 效果3 最暖光
         入参 color_temperature = 0,brightness = 100 ;
         代入公式后得到:outColortempChannle=0,outBrightnessChannle =8192 ;

效果预想的各路PWM输出以上公式算出来的PWM输出对比结果
中性光2路一样亮度的输出warm_tmp=4096 ,cold_tmp =4096符合
最冷光效果冷灯珠100亮度,暖灯珠0亮度warm_tmp=8192 ,cold_tmp =0反了
最暖光效果冷灯珠 0亮度,暖灯珠100亮度warm_tmp=0,cold_tmp =8192反了

     以上步骤,为啥结论是反了?因为上上图可以看到:色温数值越大表示越冷效果,意味着色温越大,其暖灯珠的PWM高电平占空比越小! 那么我们如何纠正这个错误呢?

在这里插入图片描述

     这不用我来说吧?把入参的 color_temperature 修改为即可:

color_temperature = 6500 - color_temperature + 2700

二、相关API接口

  • 先看看我们的头文件,思路非常清晰,就是可开发性非常高。
  • 根据自己的需求,修改 GPIO 引脚配置;
/***************  用户可修改 start **********************/
#define LEDC_FADE_TIME (2000)  //渐变时间
#define APK_MAX_COLORTEMP 6500 //用户定义最大的色温数值
#define APK_MID_COLORTEMP 4800 //用户定义中间的色温数值
#define APK_MIN_COLORTEMP 2700 //用户定义最小的色温数值
#define APK_MAX_COLOR 255      //用户定义最大的RGB数值

//pwm gpio口配置
#define CHANNLE_PWM_TOTAL 5 //五路

#define PWM_CW_OUT_IO_NUM 14 //冷灯珠
#define PWM_WW_OUT_IO_NUM 13 //暖灯珠
#define PWM_RED_OUT_IO_NUM 12 //红色灯珠
#define PWM_GREEN_OUT_IO_NUM 15 //绿色灯珠
#define PWM_BLUE_OUT_IO_NUM 5 //蓝色灯珠

//是否带有记忆功能
#define IS_SAVE_PARAMS true

/***************  用户可修改  end**********************/

typedef struct User_dev_status_t
{
    int Power;
    int Mode;
    int Brightness;
    int Colortemp;
    int Red;
    int Green;
    int Blue;
} User_dev_status_t;

/**
 * @brief  设置
 */
esp_err_t pwm_init_data(); //gpio初始化
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_colorTemperature(int colorTemperature);
esp_err_t light_driver_set_ctb(const int brightness, const int color_temperature); //设置色温亮度
esp_err_t light_driver_set_rgb(const uint8_t red, const uint8_t green, const uint8_t blue);
esp_err_t light_driver_set_switch(bool status); //设置开关
esp_err_t light_driver_set_mode(uint8_t mode); //设置模式
esp_err_t light_driver_set_cycle(uint8_t nums); // 设置呼吸效果
esp_err_t light_driver_set_ctb_from_last(); //恢复上次的状态

三、其他