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)17static 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)29static 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)38static 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)43uint16_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)54static 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)69uint16_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)80static 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)95uint16_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)111void 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)174int 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)206int 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