1 /* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models
2  *
3  * Copyright (c) 2018 Vikrant More
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <math.h>
9 #include <zephyr/drivers/gpio.h>
10 
11 #include "ble_mesh.h"
12 #include "device_composition.h"
13 #include "state_binding.h"
14 #include "storage.h"
15 #include "transition.h"
16 
ceiling(float num)17 static int32_t ceiling(float num)
18 {
19 	int32_t inum;
20 
21 	inum = (int32_t) num;
22 	if (num == (float) inum) {
23 		return inum;
24 	}
25 
26 	return inum + 1;
27 }
28 
actual_to_linear(uint16_t val)29 static uint16_t actual_to_linear(uint16_t val)
30 {
31 	float tmp;
32 
33 	tmp = ((float) val / UINT16_MAX);
34 
35 	return (uint16_t) ceiling(UINT16_MAX * tmp * tmp);
36 }
37 
linear_to_actual(uint16_t val)38 static uint16_t linear_to_actual(uint16_t val)
39 {
40 	return (uint16_t) (UINT16_MAX * sqrtf(((float) val / UINT16_MAX)));
41 }
42 
constrain_lightness(uint16_t light)43 uint16_t constrain_lightness(uint16_t light)
44 {
45 	if (light > 0 && light < ctl->light->range_min) {
46 		light = ctl->light->range_min;
47 	} else if (light > ctl->light->range_max) {
48 		light = ctl->light->range_max;
49 	}
50 
51 	return light;
52 }
53 
constrain_target_lightness2(void)54 static void constrain_target_lightness2(void)
55 {
56 	/* This is as per Mesh Model Specification 3.3.2.2.3 */
57 	if (ctl->light->target > 0 &&
58 	    ctl->light->target < ctl->light->range_min) {
59 		if (ctl->light->delta < 0) {
60 			ctl->light->target = 0U;
61 		} else {
62 			ctl->light->target = ctl->light->range_min;
63 		}
64 	} else if (ctl->light->target > ctl->light->range_max) {
65 		ctl->light->target = ctl->light->range_max;
66 	}
67 }
68 
constrain_temperature(uint16_t temp)69 uint16_t constrain_temperature(uint16_t temp)
70 {
71 	if (temp < ctl->temp->range_min) {
72 		temp = ctl->temp->range_min;
73 	} else if (temp > ctl->temp->range_max) {
74 		temp = ctl->temp->range_max;
75 	}
76 
77 	return temp;
78 }
79 
light_ctl_temp_to_level(uint16_t temp)80 static int16_t light_ctl_temp_to_level(uint16_t temp)
81 {
82 	float tmp;
83 
84 	/* Mesh Model Specification 6.1.3.1.1 2nd formula start */
85 
86 	tmp = (temp - ctl->temp->range_min) * UINT16_MAX;
87 
88 	tmp = tmp / (ctl->temp->range_max - ctl->temp->range_min);
89 
90 	return (int16_t) (tmp + INT16_MIN);
91 
92 	/* 6.1.3.1.1 2nd formula end */
93 }
94 
level_to_light_ctl_temp(int16_t level)95 uint16_t level_to_light_ctl_temp(int16_t level)
96 {
97 	uint16_t tmp;
98 	float diff;
99 
100 	/* Mesh Model Specification 6.1.3.1.1 1st formula start */
101 	diff = (float) (ctl->temp->range_max - ctl->temp->range_min) /
102 		       UINT16_MAX;
103 
104 	tmp = (uint16_t) ((level - INT16_MIN) * diff);
105 
106 	return (ctl->temp->range_min + tmp);
107 
108 	/* 6.1.3.1.1 1st formula end */
109 }
110 
set_target(uint8_t type,void * dptr)111 void set_target(uint8_t type, void *dptr)
112 {
113 	switch (type) {
114 	case ONOFF: {
115 		uint8_t onoff;
116 
117 		onoff = *((uint8_t *) dptr);
118 		if (onoff == STATE_OFF) {
119 			ctl->light->target = 0U;
120 		} else if (onoff == STATE_ON) {
121 			if (ctl->light->def == 0U) {
122 				ctl->light->target = ctl->light->last;
123 			} else {
124 				ctl->light->target = ctl->light->def;
125 			}
126 		}
127 	}
128 	break;
129 	case LEVEL_LIGHT:
130 		ctl->light->target = *((int16_t *) dptr) - INT16_MIN;
131 		ctl->light->target = constrain_lightness(ctl->light->target);
132 		break;
133 	case DELTA_LEVEL_LIGHT:
134 		ctl->light->target =  *((int16_t *) dptr) - INT16_MIN;
135 		constrain_target_lightness2();
136 		break;
137 	case MOVE_LIGHT:
138 		ctl->light->target = *((uint16_t *) dptr);
139 		break;
140 	case ACTUAL:
141 		ctl->light->target = *((uint16_t *) dptr);
142 		ctl->light->target = constrain_lightness(ctl->light->target);
143 		break;
144 	case LINEAR:
145 		ctl->light->target = linear_to_actual(*((uint16_t *) dptr));
146 		ctl->light->target = constrain_lightness(ctl->light->target);
147 		break;
148 	case CTL_LIGHT:
149 		ctl->light->target = *((uint16_t *) dptr);
150 		ctl->light->target = constrain_lightness(ctl->light->target);
151 		break;
152 	case LEVEL_TEMP:
153 		ctl->temp->target = level_to_light_ctl_temp(*((int16_t *) dptr));
154 		break;
155 	case MOVE_TEMP:
156 		ctl->temp->target = *((uint16_t *) dptr);
157 		break;
158 	case CTL_TEMP:
159 		ctl->temp->target = *((uint16_t *) dptr);
160 		ctl->temp->target = constrain_temperature(ctl->temp->target);
161 		break;
162 	case CTL_DELTA_UV:
163 		ctl->duv->target = *((int16_t *) dptr);
164 		break;
165 	default:
166 		return;
167 	}
168 
169 	if (ctl->onpowerup == STATE_RESTORE) {
170 		save_on_flash(LAST_TARGET_STATES);
171 	}
172 }
173 
get_current(uint8_t type)174 int get_current(uint8_t type)
175 {
176 	switch (type) {
177 	case ONOFF:
178 		if (ctl->light->current != 0U) {
179 			return STATE_ON;
180 		} else {
181 			if (ctl->light->target) {
182 				return STATE_ON;
183 			} else {
184 				return STATE_OFF;
185 			}
186 		}
187 	case LEVEL_LIGHT:
188 		return (int16_t) (ctl->light->current + INT16_MIN);
189 	case ACTUAL:
190 		return ctl->light->current;
191 	case LINEAR:
192 		return actual_to_linear(ctl->light->current);
193 	case CTL_LIGHT:
194 		return ctl->light->current;
195 	case LEVEL_TEMP:
196 		return light_ctl_temp_to_level(ctl->temp->current);
197 	case CTL_TEMP:
198 		return ctl->temp->current;
199 	case CTL_DELTA_UV:
200 		return ctl->duv->current;
201 	default:
202 		return 0U;
203 	}
204 }
205 
get_target(uint8_t type)206 int get_target(uint8_t type)
207 {
208 	switch (type) {
209 	case ONOFF:
210 		if (ctl->light->target != 0U) {
211 			return STATE_ON;
212 		} else {
213 			return STATE_OFF;
214 		}
215 	case LEVEL_LIGHT:
216 		return (int16_t) (ctl->light->target + INT16_MIN);
217 	case ACTUAL:
218 		return ctl->light->target;
219 	case LINEAR:
220 		return actual_to_linear(ctl->light->target);
221 	case CTL_LIGHT:
222 		return ctl->light->target;
223 	case LEVEL_TEMP:
224 		return light_ctl_temp_to_level(ctl->temp->target);
225 	case CTL_TEMP:
226 		return ctl->temp->target;
227 	case CTL_DELTA_UV:
228 		return ctl->duv->target;
229 	default:
230 		return 0U;
231 	}
232 }
233