/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* Bluetooth: Mesh Generic OnOff, Generic Level, Lighting & Vendor Models * * Copyright (c) 2018 Vikrant More * * SPDX-License-Identifier: Apache-2.0 */ #include "ble_mesh.h" #include "common.h" #include "device_composition.h" #include "state_binding.h" #include "transition.h" struct os_callout onoff_work; struct os_callout level_lightness_work; struct os_callout level_temp_work; struct os_callout light_lightness_actual_work; struct os_callout light_lightness_linear_work; struct os_callout light_ctl_work; struct os_callout light_ctl_temp_work; struct os_callout dummy_timer; uint8_t transition_type, default_tt; uint32_t *ptr_counter; struct os_callout *ptr_timer = &dummy_timer; struct transition lightness_transition, temp_transition; /* Function to calculate Remaining Time (Start) */ void calculate_rt(struct transition *transition) { uint8_t steps, resolution; int32_t duration_remainder; int64_t now; if (transition->just_started) { transition->rt = transition->tt; } else { now = k_uptime_get(); duration_remainder = transition->total_duration - (now - transition->start_timestamp); if (duration_remainder > 620000) { /* > 620 seconds -> resolution = 0b11 [10 minutes] */ resolution = 0x03; steps = duration_remainder / 600000; } else if (duration_remainder > 62000) { /* > 62 seconds -> resolution = 0b10 [10 seconds] */ resolution = 0x02; steps = duration_remainder / 10000; } else if (duration_remainder > 6200) { /* > 6.2 seconds -> resolution = 0b01 [1 seconds] */ resolution = 0x01; steps = duration_remainder / 1000; } else if (duration_remainder > 0) { /* <= 6.2 seconds -> resolution = 0b00 [100 ms] */ resolution = 0x00; steps = duration_remainder / 100; } else { resolution = 0x00; steps = 0x00; } transition->rt = (resolution << 6) | steps; } } /* Function to calculate Remaining Time (End) */ static void bound_states_transition_type_reassignment(uint8_t type) { switch (type) { case ONOFF: case LEVEL: case ACTUAL: case LINEAR: light_ctl_srv_user_data.transition = &lightness_transition; break; case CTL: light_ctl_srv_user_data.transition = &lightness_transition; gen_level_srv_s0_user_data.transition = &lightness_transition; break; case LEVEL_TEMP: case CTL_TEMP: gen_level_srv_s0_user_data.transition = &temp_transition; light_ctl_srv_user_data.transition = &temp_transition; break; default: break; } } static void tt_values_calculator(struct transition *transition) { uint8_t steps_multiplier, resolution; resolution = (transition->tt >> 6); steps_multiplier = (transition->tt & 0x3F); switch (resolution) { case 0: /* 100ms */ transition->total_duration = steps_multiplier * 100; break; case 1: /* 1 second */ transition->total_duration = steps_multiplier * 1000; break; case 2: /* 10 seconds */ transition->total_duration = steps_multiplier * 10000; break; case 3: /* 10 minutes */ transition->total_duration = steps_multiplier * 600000; break; } transition->counter = ((float) transition->total_duration / 100); if (transition->counter > DEVICE_SPECIFIC_RESOLUTION) { transition->counter = DEVICE_SPECIFIC_RESOLUTION; } ptr_counter = &transition->counter; } void onoff_tt_values(struct generic_onoff_state *state, uint8_t tt, uint8_t delay) { bound_states_transition_type_reassignment(ONOFF); calculate_lightness_target_values(ONOFF); state->transition->tt = tt; state->transition->delay = delay; if (tt != 0) { tt_values_calculator(state->transition); } else { return; } state->transition->quo_tt = state->transition->total_duration / state->transition->counter; state->tt_delta = ((float) (lightness - target_lightness) / state->transition->counter); } void level_tt_values(struct generic_level_state *state, uint8_t tt, uint8_t delay) { if (state == &gen_level_srv_root_user_data) { bound_states_transition_type_reassignment(LEVEL); calculate_lightness_target_values(LEVEL); } else if (state == &gen_level_srv_s0_user_data) { bound_states_transition_type_reassignment(LEVEL_TEMP); calculate_temp_target_values(LEVEL_TEMP); } state->transition->tt = tt; state->transition->delay = delay; if (tt != 0) { tt_values_calculator(state->transition); } else { return; } state->transition->quo_tt = state->transition->total_duration / state->transition->counter; state->tt_delta = ((float) (state->level - state->target_level) / state->transition->counter); } void light_lightness_actual_tt_values(struct light_lightness_state *state, uint8_t tt, uint8_t delay) { bound_states_transition_type_reassignment(ACTUAL); calculate_lightness_target_values(ACTUAL); state->transition->tt = tt; state->transition->delay = delay; if (tt != 0) { tt_values_calculator(state->transition); } else { return; } state->transition->quo_tt = state->transition->total_duration / state->transition->counter; state->tt_delta_actual = ((float) (state->actual - state->target_actual) / state->transition->counter); } void light_lightness_linear_tt_values(struct light_lightness_state *state, uint8_t tt, uint8_t delay) { bound_states_transition_type_reassignment(LINEAR); calculate_lightness_target_values(LINEAR); state->transition->tt = tt; state->transition->delay = delay; if (tt != 0) { tt_values_calculator(state->transition); } else { return; } state->transition->quo_tt = state->transition->total_duration / state->transition->counter; state->tt_delta_linear = ((float) (state->linear - state->target_linear) / state->transition->counter); } void light_ctl_tt_values(struct light_ctl_state *state, uint8_t tt, uint8_t delay) { bound_states_transition_type_reassignment(CTL); calculate_lightness_target_values(CTL); state->transition->tt = tt; state->transition->delay = delay; if (tt != 0) { tt_values_calculator(state->transition); } else { return; } state->transition->quo_tt = state->transition->total_duration / state->transition->counter; state->tt_delta_lightness = ((float) (state->lightness - state->target_lightness) / state->transition->counter); state->tt_delta_temp = ((float) (state->temp - state->target_temp) / state->transition->counter); state->tt_delta_duv = ((float) (state->delta_uv - state->target_delta_uv) / state->transition->counter); } void light_ctl_temp_tt_values(struct light_ctl_state *state, uint8_t tt, uint8_t delay) { bound_states_transition_type_reassignment(CTL_TEMP); calculate_temp_target_values(CTL_TEMP); state->transition->tt = tt; state->transition->delay = delay; if (tt != 0) { tt_values_calculator(state->transition); } else { return; } state->transition->quo_tt = state->transition->total_duration / state->transition->counter; state->tt_delta_temp = ((float) (state->temp - state->target_temp) / state->transition->counter); state->tt_delta_duv = ((float) (state->delta_uv - state->target_delta_uv) / state->transition->counter); } /* Timers related handlers & threads (Start) */ static void onoff_work_handler(struct os_event *work) { struct generic_onoff_state *state = &gen_onoff_srv_root_user_data; if (state->transition->just_started) { state->transition->just_started = false; if (state->transition->counter == 0) { state_binding(ONOFF, IGNORE_TEMP); update_light_state(); os_callout_stop(ptr_timer); } else { state->transition->start_timestamp = k_uptime_get(); if (state->target_onoff == STATE_ON) { state->onoff = STATE_ON; } } return; } if (state->transition->counter != 0) { state->transition->counter--; lightness -= state->tt_delta; state_binding(IGNORE, IGNORE_TEMP); update_light_state(); } if (state->transition->counter == 0) { state->onoff = state->target_onoff; lightness = target_lightness; state_binding(IGNORE, IGNORE_TEMP); update_light_state(); os_callout_stop(ptr_timer); } } static void level_lightness_work_handler(struct os_event *work) { uint8_t level; struct generic_level_state *state = &gen_level_srv_root_user_data; switch (transition_type) { case LEVEL_TT: level = LEVEL; break; case LEVEL_TT_DELTA: level = DELTA_LEVEL; break; case LEVEL_TT_MOVE: level = LEVEL; break; default: return; } if (state->transition->just_started) { state->transition->just_started = false; if (state->transition->counter == 0) { state_binding(level, IGNORE_TEMP); update_light_state(); os_callout_stop(ptr_timer); } else { state->transition->start_timestamp = k_uptime_get(); } return; } if (state->transition->counter != 0) { state->transition->counter--; state->level -= state->tt_delta; state_binding(level, IGNORE_TEMP); update_light_state(); } if (state->transition->counter == 0) { state->level = state->target_level; state_binding(level, IGNORE_TEMP); update_light_state(); os_callout_stop(ptr_timer); } } static void level_temp_work_handler(struct os_event *work) { struct generic_level_state *state = &gen_level_srv_s0_user_data; switch (transition_type) { case LEVEL_TEMP_TT: break; case LEVEL_TEMP_TT_DELTA: break; case LEVEL_TEMP_TT_MOVE: break; default: return; } if (state->transition->just_started) { state->transition->just_started = false; if (state->transition->counter == 0) { state_binding(IGNORE, LEVEL_TEMP); update_light_state(); os_callout_stop(ptr_timer); } else { state->transition->start_timestamp = k_uptime_get(); } return; } if (state->transition->counter != 0) { state->transition->counter--; state->level -= state->tt_delta; state_binding(IGNORE, LEVEL_TEMP); update_light_state(); } if (state->transition->counter == 0) { state->level = state->target_level; state_binding(IGNORE, LEVEL_TEMP); update_light_state(); os_callout_stop(ptr_timer); } } static void light_lightness_actual_work_handler(struct os_event *work) { struct light_lightness_state *state = &light_lightness_srv_user_data; if (state->transition->just_started) { state->transition->just_started = false; if (state->transition->counter == 0) { state_binding(ACTUAL, IGNORE_TEMP); update_light_state(); os_callout_stop(ptr_timer); } else { state->transition->start_timestamp = k_uptime_get(); } return; } if (state->transition->counter != 0) { state->transition->counter--; state->actual -= state->tt_delta_actual; state_binding(ACTUAL, IGNORE_TEMP); update_light_state(); } if (state->transition->counter == 0) { state->actual = state->target_actual; state_binding(ACTUAL, IGNORE_TEMP); update_light_state(); os_callout_stop(ptr_timer); } } static void light_lightness_linear_work_handler(struct os_event *work) { struct light_lightness_state *state = &light_lightness_srv_user_data; if (state->transition->just_started) { state->transition->just_started = false; if (state->transition->counter == 0) { state_binding(LINEAR, IGNORE_TEMP); update_light_state(); os_callout_stop(ptr_timer); } else { state->transition->start_timestamp = k_uptime_get(); } return; } if (state->transition->counter != 0) { state->transition->counter--; state->linear -= state->tt_delta_linear; state_binding(LINEAR, IGNORE_TEMP); update_light_state(); } if (state->transition->counter == 0) { state->linear = state->target_linear; state_binding(LINEAR, IGNORE_TEMP); update_light_state(); os_callout_stop(ptr_timer); } } static void light_ctl_work_handler(struct os_event *work) { struct light_ctl_state *state = &light_ctl_srv_user_data; if (state->transition->just_started) { state->transition->just_started = false; if (state->transition->counter == 0) { state_binding(CTL, CTL_TEMP); update_light_state(); os_callout_stop(ptr_timer); } else { state->transition->start_timestamp = k_uptime_get(); } return; } if (state->transition->counter != 0) { state->transition->counter--; /* Lightness */ state->lightness -= state->tt_delta_lightness; /* Temperature */ state->temp -= state->tt_delta_temp; /* Delta_UV */ state->delta_uv -= state->tt_delta_duv; state_binding(CTL, CTL_TEMP); update_light_state(); } if (state->transition->counter == 0) { state->lightness = state->target_lightness; state->temp = state->target_temp; state->delta_uv = state->target_delta_uv; state_binding(CTL, CTL_TEMP); update_light_state(); os_callout_stop(ptr_timer); } } static void light_ctl_temp_work_handler(struct os_event *work) { struct light_ctl_state *state = &light_ctl_srv_user_data; if (state->transition->just_started) { state->transition->just_started = false; if (state->transition->counter == 0) { state_binding(IGNORE, CTL_TEMP); update_light_state(); os_callout_stop(ptr_timer); } else { state->transition->start_timestamp = k_uptime_get(); } return; } if (state->transition->counter != 0) { state->transition->counter--; /* Temperature */ state->temp -= state->tt_delta_temp; /* Delta UV */ state->delta_uv -= state->tt_delta_duv; state_binding(IGNORE, CTL_TEMP); update_light_state(); } if (state->transition->counter == 0) { state->temp = state->target_temp; state->delta_uv = state->target_delta_uv; state_binding(IGNORE, CTL_TEMP); update_light_state(); os_callout_stop(ptr_timer); } } static void dummy_timer_handler(struct os_event *ev) { } static void onoff_tt_handler(struct os_event *ev) { struct generic_onoff_state *state = ev->ev_arg; assert(state != NULL); os_callout_reset(&onoff_work, 0); os_callout_reset(&state->transition->timer, os_time_ms_to_ticks32( K_MSEC(state->transition->quo_tt))); } static void level_lightness_tt_handler(struct os_event *ev) { struct generic_level_state *state = ev->ev_arg; assert(state != NULL); os_callout_reset(&level_lightness_work, 0); os_callout_reset(&state->transition->timer, os_time_ms_to_ticks32( K_MSEC(state->transition->quo_tt))); } static void level_temp_tt_handler(struct os_event *ev) { struct generic_level_state *state = ev->ev_arg; assert(state != NULL); os_callout_reset(&level_temp_work, 0); os_callout_reset(&state->transition->timer, os_time_ms_to_ticks32( K_MSEC(state->transition->quo_tt))); } static void light_lightness_actual_tt_handler(struct os_event *ev) { struct light_lightness_state *state = ev->ev_arg; assert(state != NULL); os_callout_reset(&light_lightness_actual_work, 0); os_callout_reset(&state->transition->timer, os_time_ms_to_ticks32( K_MSEC(state->transition->quo_tt))); } static void light_lightness_linear_tt_handler(struct os_event *ev) { struct light_lightness_state *state = ev->ev_arg; assert(state != NULL); os_callout_reset(&light_lightness_linear_work, 0); os_callout_reset(&state->transition->timer, os_time_ms_to_ticks32( K_MSEC(state->transition->quo_tt))); } static void light_ctl_tt_handler(struct os_event *ev) { struct light_ctl_state *state = ev->ev_arg; assert(state != NULL); os_callout_reset(&light_ctl_work, 0); os_callout_reset(&state->transition->timer, os_time_ms_to_ticks32( K_MSEC(state->transition->quo_tt))); } static void light_ctl_temp_tt_handler(struct os_event *ev) { struct light_ctl_state *state = ev->ev_arg; assert(state != NULL); os_callout_reset(&light_ctl_temp_work, 0); os_callout_reset(&state->transition->timer, os_time_ms_to_ticks32( K_MSEC(state->transition->quo_tt))); } /* Timers related handlers & threads (End) */ /* Messages handlers (Start) */ void onoff_handler(struct generic_onoff_state *state) { ptr_timer = &state->transition->timer; os_callout_init(ptr_timer, os_eventq_dflt_get(), onoff_tt_handler, NULL); ptr_timer->c_ev.ev_arg = state; os_callout_reset(ptr_timer, os_time_ms_to_ticks32( K_MSEC(5 * state->transition->delay))); } void level_lightness_handler(struct generic_level_state *state) { ptr_timer = &state->transition->timer; os_callout_init(ptr_timer, os_eventq_dflt_get(), level_lightness_tt_handler, NULL); ptr_timer->c_ev.ev_arg = state; os_callout_reset(ptr_timer, os_time_ms_to_ticks32( K_MSEC(5 * state->transition->delay))); } void level_temp_handler(struct generic_level_state *state) { ptr_timer = &state->transition->timer; os_callout_init(ptr_timer, os_eventq_dflt_get(), level_temp_tt_handler, NULL); ptr_timer->c_ev.ev_arg = state; os_callout_reset(ptr_timer, os_time_ms_to_ticks32( K_MSEC(5 * state->transition->delay))); } void light_lightness_actual_handler(struct light_lightness_state *state) { ptr_timer = &state->transition->timer; os_callout_init(ptr_timer, os_eventq_dflt_get(), light_lightness_actual_tt_handler, NULL); ptr_timer->c_ev.ev_arg = state; os_callout_reset(ptr_timer, os_time_ms_to_ticks32( K_MSEC(5 * state->transition->delay))); } void light_lightness_linear_handler(struct light_lightness_state *state) { ptr_timer = &state->transition->timer; os_callout_init(ptr_timer, os_eventq_dflt_get(), light_lightness_linear_tt_handler, NULL); ptr_timer->c_ev.ev_arg = state; os_callout_reset(ptr_timer, os_time_ms_to_ticks32( K_MSEC(5 * state->transition->delay))); } void light_ctl_handler(struct light_ctl_state *state) { ptr_timer = &state->transition->timer; os_callout_init(ptr_timer, os_eventq_dflt_get(), light_ctl_tt_handler, NULL); ptr_timer->c_ev.ev_arg = state; os_callout_reset(ptr_timer, os_time_ms_to_ticks32( K_MSEC(5 * state->transition->delay))); } void light_ctl_temp_handler(struct light_ctl_state *state) { ptr_timer = &state->transition->timer; os_callout_init(ptr_timer, os_eventq_dflt_get(), light_ctl_temp_tt_handler, NULL); ptr_timer->c_ev.ev_arg = state; os_callout_reset(ptr_timer, os_time_ms_to_ticks32( K_MSEC(5 * state->transition->delay))); } /* Messages handlers (End) */ void transition_timers_init(void) { os_callout_init(&onoff_work, os_eventq_dflt_get(), onoff_work_handler, NULL); os_callout_init(&level_lightness_work, os_eventq_dflt_get(), level_lightness_work_handler, NULL); os_callout_init(&level_temp_work, os_eventq_dflt_get(), level_temp_work_handler, NULL); os_callout_init(&light_lightness_actual_work, os_eventq_dflt_get(), light_lightness_actual_work_handler, NULL); os_callout_init(&light_lightness_linear_work, os_eventq_dflt_get(), light_lightness_linear_work_handler, NULL); os_callout_init(&light_ctl_work, os_eventq_dflt_get(), light_ctl_work_handler, NULL); os_callout_init(&light_ctl_temp_work, os_eventq_dflt_get(), light_ctl_temp_work_handler, NULL); os_callout_init(&dummy_timer, os_eventq_dflt_get(), dummy_timer_handler, NULL); }