diff --git a/helpers.h b/helpers.h new file mode 100644 index 0000000..1f2692e --- /dev/null +++ b/helpers.h @@ -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 diff --git a/sampleLightCtrl.c b/sampleLightCtrl.c index ad78bdb..782db77 100644 --- a/sampleLightCtrl.c +++ b/sampleLightCtrl.c @@ -30,6 +30,7 @@ #include "zcl_include.h" #include "sampleLight.h" #include "sampleLightCtrl.h" +#include "helpers.h" /********************************************************************** * LOCAL CONSTANTS @@ -50,11 +51,9 @@ * FUNCTIONS */ extern void sampleLight_updateOnOff(void); -extern void sampleLight_updateLevel(void); extern void sampleLight_updateColor(void); extern void sampleLight_onOffInit(void); -extern void sampleLight_levelInit(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 * @@ -187,6 +165,25 @@ void temperatureToCW(u16 temperatureMireds, u8 level, u8 *C, u8 *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 * @@ -202,7 +199,7 @@ void hwLight_colorUpdate_colorTemperature(u16 colorTemperatureMireds, u8 level) u8 C = 0; u8 W = 0; - level = (level < 0x10) ? 0x10 : level; + level = getZBLightLevelPercentage(level) * ZCL_LEVEL_ATTR_MAX_LEVEL; 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); u8 rS = saturation; - u8 rV = level; + u8 rV = getZBLightLevelPercentage(level) * 255; if (saturation == 0) { @@ -347,35 +344,6 @@ void hwLight_colorUpdate_RGB(u8 R, u8 G, u8 B) 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) { // 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; } -#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) { 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 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 Z = yI == 0 ? 0.0f : (z * Y) / y; // 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 g = MAX(X * -0.9692660f + Y * 1.8760108f + Z * 0.0415560f,0); - float b = MAX(X * 0.0556434f + Y * -0.2040259f + Z * 1.0572252f,0); + float r = max2(X * 3.2404542f + Y * -1.5371385f + Z * -0.4985314f,0); + float g = max2(X * -0.9692660f + Y * 1.8760108f + Z * 0.0415560f,0); + float b = max2(X * 0.0556434f + Y * -0.2040259f + Z * 1.0572252f,0); // Apply LINEAR => SRGB Gamma correction r = LINEAR_TO_SRGB_GAMMA_CORRECTION(r); g = LINEAR_TO_SRGB_GAMMA_CORRECTION(g); b = LINEAR_TO_SRGB_GAMMA_CORRECTION(b); - float maxComponent = MAX(MAX(r, g), b); + float maxComponent = max3(r, g, b); if (maxComponent > 1.0f) { r /= maxComponent; g /= maxComponent; @@ -430,13 +394,7 @@ void hwLight_colorUpdate_XY2RGB(u16 xI, u16 yI, u8 level) */ void light_adjust(void) { -#ifdef ZCL_LIGHT_COLOR_CONTROL sampleLight_colorInit(); -#else -#ifdef ZCL_LEVEL_CTRL - sampleLight_levelInit(); -#endif -#endif sampleLight_onOffInit(); } @@ -451,17 +409,8 @@ void light_adjust(void) */ void light_fresh(void) { -#ifdef ZCL_LIGHT_COLOR_CONTROL 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(); - gLightCtx.lightAttrsChanged = TRUE; } diff --git a/sampleLightCtrl.h b/sampleLightCtrl.h index 4ac0b73..b34ae69 100644 --- a/sampleLightCtrl.h +++ b/sampleLightCtrl.h @@ -33,7 +33,6 @@ */ void hwLight_init(void); void hwLight_onOffUpdate(u8 onOff); -void hwLight_levelUpdate(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_RGB(u8 R, u8 G, u8 B); diff --git a/zcl_colorCtrlCb.c b/zcl_colorCtrlCb.c index 6a99e20..9b5b5fc 100644 --- a/zcl_colorCtrlCb.c +++ b/zcl_colorCtrlCb.c @@ -679,13 +679,15 @@ static void sampleLight_moveToColorProcess(zcl_colorCtrlMoveToColorCmd_t *cmd) pColor->currentY = cmd->colorY; sampleLight_colorTimerStop(); light_fresh(); - + /* colorInfo.currentX256 = (u32)(pColor->currentX) << 8; colorInfo.currentY256 = (u32)(pColor->currentY) << 8; - u16 remTime = (cmd->transitionTime == 0) ? 1 : INTERP_STEPS_FROM_ONE_TENTH(cmd->transitionTime, ZCL_COLOR_CHANGE_INTERVAL); - colorInfo.xyRemainingTime = remTime; + u16 remTime = INTERP_STEPS_FROM_ONE_TENTH(cmd->transitionTime, ZCL_COLOR_CHANGE_INTERVAL); + + // 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)remTime; diff --git a/zcl_levelCb.c b/zcl_levelCb.c index 0da88e1..9879b83 100644 --- a/zcl_levelCb.c +++ b/zcl_levelCb.c @@ -54,47 +54,6 @@ static zcl_levelInfo_t levelInfo = { 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 *