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