blob: bffe38bae93258e48ee4f1200b63fae6bd08692b [file] [log] [blame]
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
*
* Copyright (c) 2018 Vikrant More
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <math.h>
#include "common.h"
#include "ble_mesh.h"
#include "device_composition.h"
static s32_t ceiling(float num)
{
s32_t inum;
inum = (s32_t) num;
if (num == (float) inum) {
return inum;
}
return inum + 1;
}
static bool constrain_light_actual_state(u16_t var)
{
bool is_value_within_range;
is_value_within_range = false;
if (var > 0 &&
var < light_lightness_srv_user_data.light_range_min) {
var = light_lightness_srv_user_data.light_range_min;
} else if (var > light_lightness_srv_user_data.light_range_max) {
var = light_lightness_srv_user_data.light_range_max;
} else {
is_value_within_range = true;
}
light_lightness_srv_user_data.actual = var;
return is_value_within_range;
}
static void update_gen_onoff_state(void)
{
if (light_lightness_srv_user_data.actual == 0) {
gen_onoff_srv_root_user_data.onoff = STATE_OFF;
} else {
gen_onoff_srv_root_user_data.onoff = STATE_ON;
}
}
void state_binding(u8_t lightness, u8_t temperature)
{
bool is_value_within_range;
u16_t tmp16;
float tmp;
switch (temperature) {
case ONOFF_TEMP:/* Temp. update as per Light CTL temp. default state */
case CTL_TEMP: /* Temp. update as per Light CTL temp. state */
/* Mesh Model Specification 6.1.3.1.1 2nd formula start */
tmp = (light_ctl_srv_user_data.temp -
light_ctl_srv_user_data.temp_range_min) * 65535;
tmp = tmp / (light_ctl_srv_user_data.temp_range_max -
light_ctl_srv_user_data.temp_range_min);
gen_level_srv_s0_user_data.level = tmp - 32768;
/* 6.1.3.1.1 2nd formula end */
break;
case LEVEL_TEMP:/* Temp. update as per Generic Level (s0) state */
/* Mesh Model Specification 6.1.3.1.1 1st formula start */
tmp = (float) (light_ctl_srv_user_data.temp_range_max -
light_ctl_srv_user_data.temp_range_min) / 65535;
tmp = (gen_level_srv_s0_user_data.level + 32768) * tmp;
light_ctl_srv_user_data.temp =
light_ctl_srv_user_data.temp_range_min + tmp;
/* 6.1.3.1.1 1st formula end */
break;
default:
break;
}
tmp16 = 0;
switch (lightness) {
case ONPOWERUP: /* Lightness update as per Generic OnPowerUp state */
if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) {
light_lightness_srv_user_data.actual = 0;
light_lightness_srv_user_data.linear = 0;
gen_level_srv_root_user_data.level = -32768;
light_ctl_srv_user_data.lightness = 0;
return;
} else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) {
light_lightness_srv_user_data.actual =
light_lightness_srv_user_data.last;
}
break;
case ONOFF: /* Lightness update as per Generic OnOff (root) state */
if (gen_onoff_srv_root_user_data.onoff == STATE_OFF) {
light_lightness_srv_user_data.actual = 0;
light_lightness_srv_user_data.linear = 0;
gen_level_srv_root_user_data.level = -32768;
light_ctl_srv_user_data.lightness = 0;
return;
} else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) {
if (light_lightness_srv_user_data.def == 0) {
light_lightness_srv_user_data.actual =
light_lightness_srv_user_data.last;
} else {
light_lightness_srv_user_data.actual =
light_lightness_srv_user_data.def;
}
}
break;
case LEVEL: /* Lightness update as per Generic Level (root) state */
tmp16 = gen_level_srv_root_user_data.level + 32768;
constrain_light_actual_state(tmp16);
update_gen_onoff_state();
break;
case DELTA_LEVEL: /* Lightness update as per Gen. Level (root) state */
/* This is as per Mesh Model Specification 3.3.2.2.3 */
tmp16 = gen_level_srv_root_user_data.level + 32768;
if (tmp16 > 0 &&
tmp16 < light_lightness_srv_user_data.light_range_min) {
if (gen_level_srv_root_user_data.last_delta < 0) {
tmp16 = 0;
} else {
tmp16 =
light_lightness_srv_user_data.light_range_min;
}
} else if (tmp16 >
light_lightness_srv_user_data.light_range_max) {
tmp16 =
light_lightness_srv_user_data.light_range_max;
}
light_lightness_srv_user_data.actual = tmp16;
update_gen_onoff_state();
break;
case ACTUAL: /* Lightness update as per Light Lightness Actual state */
update_gen_onoff_state();
break;
case LINEAR: /* Lightness update as per Light Lightness Linear state */
tmp16 = (u16_t) 65535 *
sqrt(((float) light_lightness_srv_user_data.linear /
65535));
is_value_within_range = constrain_light_actual_state(tmp16);
update_gen_onoff_state();
if (is_value_within_range) {
goto ignore_linear_update_others;
}
break;
case CTL: /* Lightness update as per Light CTL Lightness state */
constrain_light_actual_state(light_ctl_srv_user_data.lightness);
update_gen_onoff_state();
break;
default:
return;
}
tmp = ((float) light_lightness_srv_user_data.actual / 65535);
light_lightness_srv_user_data.linear = ceiling(65535 * tmp * tmp);
ignore_linear_update_others:
if (light_lightness_srv_user_data.actual != 0) {
light_lightness_srv_user_data.last =
light_lightness_srv_user_data.actual;
}
gen_level_srv_root_user_data.level =
light_lightness_srv_user_data.actual - 32768;
light_ctl_srv_user_data.lightness =
light_lightness_srv_user_data.actual;
}