Commit 26322d65 authored by Vikrant More's avatar Vikrant More Committed by Johan Hedberg
Browse files

samples: mesh: nrf52: upgrade state binding



Upgraded state binding algorithm & created separated
file for it. Moved light_default_status_init() from
device_composition.c to main.c. Shorten variables names
defined in struct light_lightness_state.

Signed-off-by: default avatarVikrant More <vikrant8051@gmail.com>
parent 14b4084d
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -10,6 +10,7 @@ target_sources(app PRIVATE
	       src/mesh/ble_mesh.c
	       src/mesh/device_composition.c
	       src/mesh/publisher.c
	       src/mesh/state_binding.c
	       )

zephyr_include_directories(
+50 −0
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@
#include "ble_mesh.h"
#include "device_composition.h"
#include "publisher.h"
#include "state_binding.h"

struct device *led_device[4];
struct device *button_device[4];
@@ -91,6 +92,55 @@ static void gpio_init(void)
	gpio_pin_enable_callback(button_device[3], SW3_GPIO_PIN);
}

void light_default_status_init(void)
{
	/* Assume vaules are retrived from Persistence Storage (Start).
	 * These had saved by respective Setup Servers.
	 */
	gen_power_onoff_srv_user_data.onpowerup = STATE_DEFAULT;

	light_lightness_srv_user_data.light_range_min = LIGHTNESS_MIN;
	light_lightness_srv_user_data.light_range_max = LIGHTNESS_MAX;
	light_lightness_srv_user_data.def = LIGHTNESS_MAX;

	/* Following 2 values are as per specification */
	light_ctl_srv_user_data.temp_range_min = TEMP_MIN;
	light_ctl_srv_user_data.temp_range_max = TEMP_MAX;

	light_ctl_srv_user_data.temp_def = TEMP_MIN;
	/* (End) */

	/* Assume following values are retrived from Persistence
	 * Storage (Start).
	 * These values had saved before power down.
	 */
	light_lightness_srv_user_data.last = LIGHTNESS_MAX;
	light_ctl_srv_user_data.temp_last = TEMP_MIN;
	/* (End) */

	light_ctl_srv_user_data.temp = light_ctl_srv_user_data.temp_def;

	if (gen_power_onoff_srv_user_data.onpowerup == STATE_OFF) {
		gen_onoff_srv_root_user_data.onoff = STATE_OFF;
		state_binding(ONOFF, ONOFF_TEMP);
	} else if (gen_power_onoff_srv_user_data.onpowerup == STATE_DEFAULT) {
		gen_onoff_srv_root_user_data.onoff = STATE_ON;
		state_binding(ONOFF, ONOFF_TEMP);
	} else if (gen_power_onoff_srv_user_data.onpowerup == STATE_RESTORE) {
		/* Assume following values is retrived from Persistence
		 * Storage (Start).
		 * This value had saved before power down.
		 */
		gen_onoff_srv_root_user_data.onoff = STATE_ON;
		/* (End) */

		light_ctl_srv_user_data.temp =
			light_ctl_srv_user_data.temp_last;

		state_binding(ONPOWERUP, ONOFF_TEMP);
	}
}

void update_light_state(void)
{
	u8_t power, color;
+12 −311
Original line number Diff line number Diff line
@@ -11,6 +11,7 @@
#include "common.h"
#include "ble_mesh.h"
#include "device_composition.h"
#include "state_binding.h"

static struct bt_mesh_cfg_srv cfg_srv = {
	.relay = BT_MESH_RELAY_DISABLED,
@@ -85,306 +86,6 @@ struct generic_onoff_state gen_onoff_srv_s0_user_data;
struct generic_level_state gen_level_srv_s0_user_data;
/* Definitions of models user data (End) */

#define MINDIFF 2.25e-308

static float sqrt(float square)
{
	float root, last, diff;

	root = square / 3.0;
	diff = 1;

	if (square <= 0) {
		return 0;
	}

	do {
		last = root;
		root = (root + square / root) / 2.0;
		diff = root - last;
	} while (diff > MINDIFF || diff < -MINDIFF);

	return root;
}

static void state_binding(u8_t lightness, u8_t temperature)
{
	u16_t tmp16;
	float tmp;

	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;
		} else if (gen_onoff_srv_root_user_data.onoff == STATE_ON) {
			light_lightness_srv_user_data.actual =
				light_lightness_srv_user_data.last;

			tmp = ((float)
			       light_lightness_srv_user_data.actual / 65535);
			light_lightness_srv_user_data.linear =
				(u16_t) (65535 * tmp * tmp);

			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;
		}
		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;
		} 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;
			}

			tmp = ((float)
			       light_lightness_srv_user_data.actual / 65535);
			light_lightness_srv_user_data.linear =
				(u16_t) (65535 * tmp * tmp);

			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;
		}

		goto update_temp;

		break;
	case LEVEL: /* Lightness update as per Generic 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.lightness_range_min) {
			tmp16 =
			light_lightness_srv_user_data.lightness_range_min;
		} else if (tmp16 >
			   light_lightness_srv_user_data.lightness_range_max) {
			tmp16 =
			light_lightness_srv_user_data.lightness_range_max;
		}

		light_lightness_srv_user_data.actual = tmp16;

		tmp = ((float) light_lightness_srv_user_data.actual / 65535);
		light_lightness_srv_user_data.linear =
			(u16_t) (65535 * tmp * tmp);

		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;
		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.lightness_range_min) {
			if (gen_level_srv_root_user_data.last_delta < 0) {
				tmp16 = 0;
			} else {
				tmp16 =
				light_lightness_srv_user_data.lightness_range_min;
			}
		} else if (tmp16 >
			   light_lightness_srv_user_data.lightness_range_max) {
			tmp16 =
			light_lightness_srv_user_data.lightness_range_max;
		}

		light_lightness_srv_user_data.actual = tmp16;

		tmp = ((float) light_lightness_srv_user_data.actual / 65535);
		light_lightness_srv_user_data.linear =
			(u16_t) (65535 * tmp * tmp);

		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;
		break;
	case ACTUAL: /* Lightness update as per Light Lightness Actual state */
		tmp = ((float) light_lightness_srv_user_data.actual / 65535);
		light_lightness_srv_user_data.linear =
			(u16_t) (65535 * tmp * tmp);

		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;
		break;
	case LINEAR: /* Lightness update as per Light Lightness Linear state */
		tmp16 = (u16_t) 65535 *
			sqrt(((float) light_lightness_srv_user_data.linear /
			      65535));

		if (tmp16 > 0 && tmp16 <
		    light_lightness_srv_user_data.lightness_range_min) {
			tmp16 =
			light_lightness_srv_user_data.lightness_range_min;
		} else if (tmp16 >
			   light_lightness_srv_user_data.lightness_range_max) {
			tmp16 =
			light_lightness_srv_user_data.lightness_range_max;
		}

		light_lightness_srv_user_data.actual = tmp16;

		tmp = ((float) light_lightness_srv_user_data.actual / 65535);
		light_lightness_srv_user_data.linear =
			(u16_t) (65535 * tmp * tmp);

		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;
		break;
	case CTL: /* Lightness update as per Light CTL Lightness state */
		tmp16 = light_ctl_srv_user_data.lightness;

		if (tmp16 > 0 && tmp16 <
		    light_lightness_srv_user_data.lightness_range_min) {
			tmp16 =
			light_lightness_srv_user_data.lightness_range_min;
		} else if (tmp16 >
			   light_lightness_srv_user_data.lightness_range_max) {
			tmp16 =
			light_lightness_srv_user_data.lightness_range_max;
		}

		light_lightness_srv_user_data.actual = tmp16;

		tmp = ((float) light_lightness_srv_user_data.actual / 65535);
		light_lightness_srv_user_data.linear =
			(u16_t) (65535 * tmp * tmp);

		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;
		break;
	default:
		goto update_temp;
		break;
	}

	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;
	}

update_temp:
	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;
	}
}

void light_default_status_init(void)
{
	/* Assume vaules are retrived from Persistence Storage (Start).
	 * These had saved by respective Setup Servers.
	 */
	gen_power_onoff_srv_user_data.onpowerup = STATE_DEFAULT;

	light_lightness_srv_user_data.lightness_range_min = LIGHTNESS_MIN;
	light_lightness_srv_user_data.lightness_range_max = LIGHTNESS_MAX;
	light_lightness_srv_user_data.def = LIGHTNESS_MAX;

	/* Following 2 values are as per specification */
	light_ctl_srv_user_data.temp_range_min = TEMP_MIN;
	light_ctl_srv_user_data.temp_range_max = TEMP_MAX;

	light_ctl_srv_user_data.temp_def = TEMP_MIN;
	/* (End) */

	/* Assume following values are retrived from Persistence
	 * Storage (Start).
	 * These values had saved before power down.
	 */
	light_lightness_srv_user_data.last = LIGHTNESS_MAX;
	light_ctl_srv_user_data.temp_last = TEMP_MIN;
	/* (End) */

	light_ctl_srv_user_data.temp = light_ctl_srv_user_data.temp_def;

	if (gen_power_onoff_srv_user_data.onpowerup == STATE_OFF) {
		gen_onoff_srv_root_user_data.onoff = STATE_OFF;
		state_binding(ONOFF, ONOFF_TEMP);
	} else if (gen_power_onoff_srv_user_data.onpowerup == STATE_DEFAULT) {
		gen_onoff_srv_root_user_data.onoff = STATE_ON;
		state_binding(ONOFF, ONOFF_TEMP);
	} else if (gen_power_onoff_srv_user_data.onpowerup == STATE_RESTORE) {
		/* Assume following values is retrived from Persistence
		 * Storage (Start).
		 * This value had saved before power down.
		 */
		gen_onoff_srv_root_user_data.onoff = STATE_ON;
		/* (End) */

		light_ctl_srv_user_data.temp =
			light_ctl_srv_user_data.temp_last;

		state_binding(ONPOWERUP, ONOFF_TEMP);
	}
}

/* message handlers (Start) */

@@ -828,12 +529,12 @@ static void light_lightness_set_unack(struct bt_mesh_model *model,
	state->last_tid = tid;
	state->last_tx_addr = ctx->addr;

	if (actual < state->lightness_range_min && actual > 0) {
		actual = state->lightness_range_min;
	if (actual < state->light_range_min && actual > 0) {
		actual = state->light_range_min;
	}

	if (actual > state->lightness_range_max) {
		actual = state->lightness_range_max;
	if (actual > state->light_range_max) {
		actual = state->light_range_max;
	}

	state->actual = actual;
@@ -968,8 +669,8 @@ static void light_lightness_range_get(struct bt_mesh_model *model,
	bt_mesh_model_msg_init(msg, BT_MESH_MODEL_OP_2(0x82, 0x58));

	net_buf_simple_add_u8(msg, state->status_code);
	net_buf_simple_add_le16(msg, state->lightness_range_min);
	net_buf_simple_add_le16(msg, state->lightness_range_max);
	net_buf_simple_add_le16(msg, state->light_range_min);
	net_buf_simple_add_le16(msg, state->light_range_max);

	if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
		printk("Unable to send LightLightnessRange Status response\n");
@@ -1033,8 +734,8 @@ static void light_lightness_range_set_unack(struct bt_mesh_model *model,
		if (min <= max && max != 0) {
			state->status_code = RANGE_SUCCESSFULLY_UPDATED;

			state->lightness_range_min = min;
			state->lightness_range_max = max;
			state->light_range_min = min;
			state->light_range_max = max;
		} else {
			/* The provided value for Range Max cannot be set */
			state->status_code = CANNOT_SET_RANGE_MAX;
@@ -1042,7 +743,7 @@ static void light_lightness_range_set_unack(struct bt_mesh_model *model,
	}

	/* Do some work here to save values of
	 * state->lightness_range_min & state->lightness_range_max
	 * state->light_range_min & state->light_range_max
	 * on SoC flash
	 */

@@ -1053,8 +754,8 @@ static void light_lightness_range_set_unack(struct bt_mesh_model *model,
				       BT_MESH_MODEL_OP_2(0x82, 0x58));

		net_buf_simple_add_u8(msg, state->status_code);
		net_buf_simple_add_le16(msg, state->lightness_range_min);
		net_buf_simple_add_le16(msg, state->lightness_range_max);
		net_buf_simple_add_le16(msg, state->light_range_min);
		net_buf_simple_add_le16(msg, state->light_range_max);

		err = bt_mesh_model_publish(model);
		if (err) {
+5 −2
Original line number Diff line number Diff line
@@ -73,8 +73,8 @@ struct light_lightness_state {
	u16_t def;

	u8_t status_code;
	u16_t lightness_range_min;
	u16_t lightness_range_max;
	u16_t light_range_min;
	u16_t light_range_max;

	u8_t last_tid;
	u16_t last_tx_addr;
@@ -100,7 +100,10 @@ struct light_ctl_state {
};

extern struct generic_onoff_state gen_onoff_srv_root_user_data;
extern struct generic_level_state gen_level_srv_root_user_data;
extern struct generic_onpowerup_state gen_power_onoff_srv_user_data;
extern struct light_lightness_state light_lightness_srv_user_data;
extern struct light_ctl_state light_ctl_srv_user_data;
extern struct generic_level_state gen_level_srv_s0_user_data;

extern struct bt_mesh_model root_models[];
+206 −0
Original line number Diff line number Diff line
/* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
 *
 * Copyright (c) 2018 Vikrant More
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#include <board.h>
#include <gpio.h>

#include "common.h"
#include "ble_mesh.h"
#include "device_composition.h"

#define MINDIFF 2.25e-308

static float sqrt(float square)
{
	float root, last, diff;

	root = square / 3.0;
	diff = 1;

	if (square <= 0) {
		return 0;
	}

	do {
		last = root;
		root = (root + square / root) / 2.0;
		diff = root - last;
	} while (diff > MINDIFF || diff < -MINDIFF);

	return root;
}

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;
}
Loading