Introduce zigbee dimming curve, remove some useless stuff

main
Martin Böh 2022-04-07 23:49:19 +02:00
parent c7039655c6
commit f01256f553
5 changed files with 66 additions and 123 deletions

34
helpers.h Normal file
View File

@ -0,0 +1,34 @@
#ifndef _HELPERS_H_
#define _HELPERS_H
/*
iLog, pow and root functions, taken from
http://rosettacode.org/wiki/Nth_root#C
*/
float _fpow(float x, int e) {
int i;
float r = 1;
for (i = 0; i < e; i++) {
r *= x;
}
return r;
}
float _fsqrt(float x, int n) {
float d, r = 1;
if (!x) {
return 0;
}
if (n < 1 || (x < 0 && !(n&1))) {
return 0.0 / 0.0; /* NaN */
}
do {
d = (x / _fpow(r, n - 1) - r) / n;
r += d;
}
while (d >= 0.000010f * 10 || d <= -0.000010f * 10);
return r;
}
#define round(a) (s16) (a+0.5)
#endif

View File

@ -30,6 +30,7 @@
#include "zcl_include.h" #include "zcl_include.h"
#include "sampleLight.h" #include "sampleLight.h"
#include "sampleLightCtrl.h" #include "sampleLightCtrl.h"
#include "helpers.h"
/********************************************************************** /**********************************************************************
* LOCAL CONSTANTS * LOCAL CONSTANTS
@ -50,11 +51,9 @@
* FUNCTIONS * FUNCTIONS
*/ */
extern void sampleLight_updateOnOff(void); extern void sampleLight_updateOnOff(void);
extern void sampleLight_updateLevel(void);
extern void sampleLight_updateColor(void); extern void sampleLight_updateColor(void);
extern void sampleLight_onOffInit(void); extern void sampleLight_onOffInit(void);
extern void sampleLight_levelInit(void);
extern void sampleLight_colorInit(void); extern void sampleLight_colorInit(void);
/********************************************************************* /*********************************************************************
@ -146,27 +145,6 @@ void hwLight_onOffUpdate(u8 onOff)
} }
} }
/*********************************************************************
* @fn hwLight_levelUpdate
*
* @brief
*
* @param level - level attribute value
*
* @return None
*/
void hwLight_levelUpdate(u8 level)
{
/* Use this if no rgb support
#if !defined COLOR_RGB_SUPPORT || (COLOR_RGB_SUPPORT == 0)
level = (level < 0x10) ? 0x10 : level;
u16 gammaCorrectLevel = ((u16)level * level) / ZCL_LEVEL_ATTR_MAX_LEVEL;
pwmSetDuty(COOL_LIGHT_PWM_CHANNEL, gammaCorrectLevel * PWM_FULL_DUTYCYCLE);
#endif*/
}
/********************************************************************* /*********************************************************************
* @fn temperatureToCW * @fn temperatureToCW
* *
@ -187,6 +165,25 @@ void temperatureToCW(u16 temperatureMireds, u8 level, u8 *C, u8 *W)
*C = level - (*W); *C = level - (*W);
} }
/**
* @brief This function returns the light level percentage according to the zigbee dimming curve
* Original formula in zigbee spec: powf(10, ((level-1)/(253.f/3.f)) - 1) / 100.f;
* @param level the level value
* @return float the percentage of light output between 0.f and 1.f
*/
float getZBLightLevelPercentage(u8 level) {
bool negativeRoot = ((3*level) - 256) < 0;
float fLevelOpt;
if (negativeRoot) {
fLevelOpt = (_fpow(1/_fsqrt(10, 253), abs((3*level) - 256)));
} else {
fLevelOpt = (_fpow(_fsqrt(10, 253), (3*level) - 256));
}
return fLevelOpt / 99.998390f;
}
/********************************************************************* /*********************************************************************
* @fn hwLight_colorUpdate_colorTemperature * @fn hwLight_colorUpdate_colorTemperature
* *
@ -202,7 +199,7 @@ void hwLight_colorUpdate_colorTemperature(u16 colorTemperatureMireds, u8 level)
u8 C = 0; u8 C = 0;
u8 W = 0; u8 W = 0;
level = (level < 0x10) ? 0x10 : level; level = getZBLightLevelPercentage(level) * ZCL_LEVEL_ATTR_MAX_LEVEL;
temperatureToCW(colorTemperatureMireds, level, &C, &W); temperatureToCW(colorTemperatureMireds, level, &C, &W);
@ -238,7 +235,7 @@ void hsvToRGB(u16 hue, u8 saturation, u8 level, u8 *R, u8 *G, u8 *B, bool enhanc
u16 rHue = (u32)hue * 360 / (enhanced ? ZCL_COLOR_ATTR_ENHANCED_HUE_MAX : ZCL_COLOR_ATTR_HUE_MAX); u16 rHue = (u32)hue * 360 / (enhanced ? ZCL_COLOR_ATTR_ENHANCED_HUE_MAX : ZCL_COLOR_ATTR_HUE_MAX);
u8 rS = saturation; u8 rS = saturation;
u8 rV = level; u8 rV = getZBLightLevelPercentage(level) * 255;
if (saturation == 0) if (saturation == 0)
{ {
@ -347,35 +344,6 @@ void hwLight_colorUpdate_RGB(u8 R, u8 G, u8 B)
pwmSetDuty(WARM_LIGHT_PWM_CHANNEL, 0); pwmSetDuty(WARM_LIGHT_PWM_CHANNEL, 0);
} }
/*
iLog, pow and root functions, taken from
http://rosettacode.org/wiki/Nth_root#C
*/
float _fpow(float x, int e) {
int i;
float r = 1;
for (i = 0; i < e; i++) {
r *= x;
}
return r;
}
float _fsqrt(float x, int n) {
float d, r = 1;
if (!x) {
return 0;
}
if (n < 1 || (x < 0 && !(n&1))) {
return 0.0 / 0.0; /* NaN */
}
do {
d = (x / _fpow(r, n - 1) - r) / n;
r += d;
}
while (d >= 0.000010f * 10 || d <= -0.000010f * 10);
return r;
}
float LINEAR_TO_SRGB_GAMMA_CORRECTION(float v) float LINEAR_TO_SRGB_GAMMA_CORRECTION(float v)
{ {
// Optimization for embedded devices without math libraries: a ^ (m / n) == nth_root(a) ^ m // Optimization for embedded devices without math libraries: a ^ (m / n) == nth_root(a) ^ m
@ -383,10 +351,6 @@ float LINEAR_TO_SRGB_GAMMA_CORRECTION(float v)
return v <= 0.0031308f ? 12.92f * v : 1.055f * _fpow(_fsqrt(v, 11), 5) - 0.055f; return v <= 0.0031308f ? 12.92f * v : 1.055f * _fpow(_fsqrt(v, 11), 5) - 0.055f;
} }
#define MAX(x,y) ((x) > (y) ? (x) : (y))
#define MIN(x,y) ((x) < (y) ? (x) : (y))
#define round(a) (s16) (a+0.5)
void hwLight_colorUpdate_XY2RGB(u16 xI, u16 yI, u8 level) void hwLight_colorUpdate_XY2RGB(u16 xI, u16 yI, u8 level)
{ {
float x = xI / 65536.0f; float x = xI / 65536.0f;
@ -395,21 +359,21 @@ void hwLight_colorUpdate_XY2RGB(u16 xI, u16 yI, u8 level)
// This does not locate the closest point in the gamma spectrum of the lamps. possible #todo // This does not locate the closest point in the gamma spectrum of the lamps. possible #todo
const float z = 1.f - x - y; const float z = 1.f - x - y;
float Y = yI == 0 ? 0.0f : (level / (float)ZCL_LEVEL_ATTR_MAX_LEVEL); // This is luminance, but used as brightness float Y = yI == 0 ? 0.0f : getZBLightLevelPercentage(level); // This is luminance, but used as brightness
float X = yI == 0 ? 0.0f : (x * Y) / y; float X = yI == 0 ? 0.0f : (x * Y) / y;
float Z = yI == 0 ? 0.0f : (z * Y) / y; float Z = yI == 0 ? 0.0f : (z * Y) / y;
// SRGB http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html // SRGB http://www.brucelindbloom.com/index.html?Eqn_RGB_XYZ_Matrix.html
float r = MAX(X * 3.2404542f + Y * -1.5371385f + Z * -0.4985314f,0); float r = max2(X * 3.2404542f + Y * -1.5371385f + Z * -0.4985314f,0);
float g = MAX(X * -0.9692660f + Y * 1.8760108f + Z * 0.0415560f,0); float g = max2(X * -0.9692660f + Y * 1.8760108f + Z * 0.0415560f,0);
float b = MAX(X * 0.0556434f + Y * -0.2040259f + Z * 1.0572252f,0); float b = max2(X * 0.0556434f + Y * -0.2040259f + Z * 1.0572252f,0);
// Apply LINEAR => SRGB Gamma correction // Apply LINEAR => SRGB Gamma correction
r = LINEAR_TO_SRGB_GAMMA_CORRECTION(r); r = LINEAR_TO_SRGB_GAMMA_CORRECTION(r);
g = LINEAR_TO_SRGB_GAMMA_CORRECTION(g); g = LINEAR_TO_SRGB_GAMMA_CORRECTION(g);
b = LINEAR_TO_SRGB_GAMMA_CORRECTION(b); b = LINEAR_TO_SRGB_GAMMA_CORRECTION(b);
float maxComponent = MAX(MAX(r, g), b); float maxComponent = max3(r, g, b);
if (maxComponent > 1.0f) { if (maxComponent > 1.0f) {
r /= maxComponent; r /= maxComponent;
g /= maxComponent; g /= maxComponent;
@ -430,13 +394,7 @@ void hwLight_colorUpdate_XY2RGB(u16 xI, u16 yI, u8 level)
*/ */
void light_adjust(void) void light_adjust(void)
{ {
#ifdef ZCL_LIGHT_COLOR_CONTROL
sampleLight_colorInit(); sampleLight_colorInit();
#else
#ifdef ZCL_LEVEL_CTRL
sampleLight_levelInit();
#endif
#endif
sampleLight_onOffInit(); sampleLight_onOffInit();
} }
@ -451,17 +409,8 @@ void light_adjust(void)
*/ */
void light_fresh(void) void light_fresh(void)
{ {
#ifdef ZCL_LIGHT_COLOR_CONTROL
sampleLight_updateColor(); sampleLight_updateColor();
#else
#ifdef ZCL_LEVEL_CTRL
sampleLight_updateLevel();
#else
pwmSetDuty(COOL_LIGHT_PWM_CHANNEL, ZCL_LEVEL_ATTR_MAX_LEVEL * PWM_FULL_DUTYCYCLE);
#endif
#endif
sampleLight_updateOnOff(); sampleLight_updateOnOff();
gLightCtx.lightAttrsChanged = TRUE; gLightCtx.lightAttrsChanged = TRUE;
} }

View File

@ -33,7 +33,6 @@
*/ */
void hwLight_init(void); void hwLight_init(void);
void hwLight_onOffUpdate(u8 onOff); void hwLight_onOffUpdate(u8 onOff);
void hwLight_levelUpdate(u8 level);
void hwLight_colorUpdate_colorTemperature(u16 colorTemperatureMireds, u8 level); void hwLight_colorUpdate_colorTemperature(u16 colorTemperatureMireds, u8 level);
void hwLight_colorUpdate_HSV2RGB(u16 hue, u8 saturation, u8 level, bool enhanced); void hwLight_colorUpdate_HSV2RGB(u16 hue, u8 saturation, u8 level, bool enhanced);
void hwLight_colorUpdate_RGB(u8 R, u8 G, u8 B); void hwLight_colorUpdate_RGB(u8 R, u8 G, u8 B);

View File

@ -684,8 +684,10 @@ static void sampleLight_moveToColorProcess(zcl_colorCtrlMoveToColorCmd_t *cmd)
colorInfo.currentX256 = (u32)(pColor->currentX) << 8; colorInfo.currentX256 = (u32)(pColor->currentX) << 8;
colorInfo.currentY256 = (u32)(pColor->currentY) << 8; colorInfo.currentY256 = (u32)(pColor->currentY) << 8;
u16 remTime = (cmd->transitionTime == 0) ? 1 : INTERP_STEPS_FROM_ONE_TENTH(cmd->transitionTime, ZCL_COLOR_CHANGE_INTERVAL); u16 remTime = INTERP_STEPS_FROM_ONE_TENTH(cmd->transitionTime, ZCL_COLOR_CHANGE_INTERVAL);
colorInfo.xyRemainingTime = remTime;
// Instantaneous update or follow the time-steps calculated above.
colorInfo.xyRemainingTime = remTime == 0 ? 1 : remTime;
colorInfo.stepX256 = ((s32)(cmd->colorX - pColor->currentX)) << 8; colorInfo.stepX256 = ((s32)(cmd->colorX - pColor->currentX)) << 8;
colorInfo.stepX256 /= (s32)remTime; colorInfo.stepX256 /= (s32)remTime;

View File

@ -54,47 +54,6 @@ static zcl_levelInfo_t levelInfo = {
static ev_timer_event_t *levelTimerEvt = NULL; static ev_timer_event_t *levelTimerEvt = NULL;
/**********************************************************************
* FUNCTIONS
*/
/*********************************************************************
* @fn sampleLight_levelInit
*
* @brief
*
* @param None
*
* @return None
*/
void sampleLight_levelInit(void)
{
zcl_levelAttr_t *pLevel = zcl_levelAttrGet();
pLevel->remainingTime = 0;
levelInfo.currentLevel256 = (u16)(pLevel->curLevel) << 8;
light_applyUpdate(&pLevel->curLevel, &levelInfo.currentLevel256, &levelInfo.stepLevel256, &pLevel->remainingTime,
ZCL_LEVEL_ATTR_MIN_LEVEL, ZCL_LEVEL_ATTR_MAX_LEVEL, FALSE);
}
/*********************************************************************
* @fn sampleLight_updateLevel
*
* @brief
*
* @param None
*
* @return None
*/
void sampleLight_updateLevel(void)
{
zcl_levelAttr_t *pLevel = zcl_levelAttrGet();
hwLight_levelUpdate(pLevel->curLevel);
}
/********************************************************************* /*********************************************************************
* @fn sampleLight_levelTimerEvtCb * @fn sampleLight_levelTimerEvtCb
* *