bleSDK_expansion_board/mesh/model/light/lights/mm_lights_ln.c

1271 lines
43 KiB
C

/**
****************************************************************************************
* @file mm_lights_ln.c
*
* @brief Mesh Model Light Lightness Server Module
*
****************************************************************************************
*/
/**
****************************************************************************************
* @addtogroup MM_LIGHTS_LN
* @{
****************************************************************************************
*/
/*
* INCLUDE FILES
****************************************************************************************
*/
#include "mm_itf.h"
#include "mm_gens.h"
#include "mm_lights.h"
/*
* DEFINES
****************************************************************************************
*/
/// Validity of information provided to the Replay Manager
#define MM_LIGHTS_LN_REPLAY_MS (6000)
/*
* MACROS
****************************************************************************************
*/
/// Convert Light Lightness Linear value into Light Lightness Actual value
/// LA = 65535 * sqrt(LL / 65535)
/// = sqrt(65535 * 65535) * sqrt(LL / 65535)
/// = sqrt(65535 * LL)
/// = sqrt(65536 * LL - LL)
/// = sqrt(2^16 * LL - LL)
#define MM_LIGHTS_LN_ACTUAL(linear) \
(mm_lights_isqrt(((uint32_t)linear << 16) - linear))
/// Convert Light Lightness Actual value into Light Lightness Linear value
/// LL = Ceil(65535 * (LA / 65535)^2)
/// = Ceil(LA^2 / 65535)
/// = Floor((LA^2 + 65534) + 65535)
#define MM_LIGHTS_LN_LINEAR(actual) \
((((uint32_t)actual * actual) + 65534) / 65535)
/*
* TYPE DEFINITIONS
****************************************************************************************
*/
/// Structure for Lighting Lightness Server model environment
typedef struct mm_lights_ln_env
{
/// Basic model environment - Must be first element in the structure - DO NOT MOVE
mm_mdl_env_t env;
/// Timer for sending of publications
mesh_timer_t tmr_publi;
/// Publication period in milliseconds
uint32_t publi_period_ms;
/// Environment for replay protection mechanism
mm_replay_env_t replay_env;
/// Delta value in case of move transition
int16_t move_delta;
/// Light Lightness Actual state value
uint16_t ln;
/// Light Lightness Linear
uint16_t ln_linear;
/// Target value of Light Lightness Actual state value
uint16_t ln_tgt;
/// Target value of Light Lightness Linear state value
uint16_t ln_tgt_linear;
/// Light Lightness Last state value
uint16_t ln_last;
/// Light Lightness Default state value
uint16_t ln_dflt;
/// Light Lightness Range Min state value
uint16_t ln_min;
/// Light Lightness Range Max state value
uint16_t ln_max;
/// Source address of set message that has triggered last or current transition
uint8_t status_dst_addr;
/// Application key local index to be used for transmission of Light Lightness Status
/// or Light Lightness Linear Status message
m_lid_t status_app_key_lid;
/// Relaying of sent Light Lightness Status or Light Lightness Linear Status message
/// authorized
bool status_relay;
/// Send a Light Lightness Status or Light Lightness Linear Status message
bool status_linear;
} mm_lights_ln_env_t;
/// Structure for Light Lightness Setup Server model environment
typedef struct mm_lights_lns_env
{
/// Basic model environment - Must be first element in the structure - DO NOT MOVE
mm_mdl_env_t env;
/// Pointer to environment of associated Light Lightness Server model
mm_lights_ln_env_t *p_env_ln;
} mm_lights_lns_env_t;
/*
* LOCAL FUNCTIONS
****************************************************************************************
*/
/**
****************************************************************************************
* @brief Compure integer square root
*
* @param[in] n Value
*
* @return Integer square root of n
****************************************************************************************
*/
__STATIC uint32_t mm_lights_isqrt(uint32_t n)
{
uint64_t isqrt;
uint64_t min = 0;
uint64_t max = ((uint64_t)1) << 32;
while (1)
{
uint64_t sq;
if (max <= (1 + min))
{
isqrt = min;
break;
}
isqrt = min + ((max - min) >> 1);
sq = isqrt * isqrt;
if (sq == n)
{
break;
}
if (sq > n)
{
max = isqrt;
}
else
{
min = isqrt;
}
}
return (isqrt);
}
/**
****************************************************************************************
* @brief Prepare and send a Light Lightness Status or a Light Lightness Linear Status message.
* Note that both messages have the same content.
*
* @param[in] p_env_ln Pointer to Light Lightness Server model environment
* @param[in] p_route_env Pointer to structure containing reception information provided
* by Mesh Profile for received request message
* @param[in] publish Indicate if message is sent as a publication (true) or as
* a response to a received request (false)
* @param[in] linear True if Light Lightness Status must be sent, else False
****************************************************************************************
*/
__STATIC void mm_lights_ln_send_status(mm_lights_ln_env_t *p_env_ln,
mm_route_env_t *p_route_env, bool publish, bool linear)
{
// Pointer to the buffer that will contain the message
mesh_buf_t *p_buf_status;
// Remaining time
uint8_t rem_time;
// Transition type
uint8_t trans_type;
// Data length
uint8_t data_length;
// Check if a transition has been started
mm_bind_get_trans_info(p_env_ln->env.grp_lid, &trans_type, &rem_time);
// Deduce deduce data length
data_length = (trans_type != MM_TRANS_TYPE_NONE)
? MM_LIGHT_LN_STATUS_LEN : MM_LIGHT_LN_STATUS_MIN_LEN;
p_buf_status = mm_route_buf_alloc(data_length);
if (p_buf_status)
{
// Get pointer to data
uint8_t *p_data = MESH_BUF_DATA(p_buf_status);
// Get pointer to environment
mm_route_env_t *p_buf_env = (mm_route_env_t *)&p_buf_status->env;
// Prepare environment
if (p_route_env)
{
memcpy(p_buf_env, p_route_env, sizeof(mm_route_env_t));
}
else if (!publish)
{
p_buf_env->app_key_lid = p_env_ln->status_app_key_lid;
p_buf_env->u_addr.dst = p_env_ln->status_dst_addr;
SETB(p_buf_env->info, MM_ROUTE_INFO_RELAY, p_env_ln->status_relay);
}
SETB(p_buf_env->info, MM_ROUTE_INFO_RX, 0);
SETB(p_buf_env->info, MM_ROUTE_INFO_PUBLISH, publish);
p_buf_env->mdl_lid = p_env_ln->env.mdl_lid;
p_buf_env->opcode = (linear) ? MM_MSG_LIGHT_LN_LINEAR_STATUS : MM_MSG_LIGHT_LN_STATUS;
// Fill the message
write16p(p_data + MM_LIGHT_LN_STATUS_LIGHTNESS_POS,
(linear) ? p_env_ln->ln_linear : p_env_ln->ln);
if (data_length == MM_LIGHT_LN_STATUS_LEN)
{
// Target state value
uint16_t tgt;
if (trans_type == MM_TRANS_TYPE_MOVE)
{
tgt = (p_env_ln->move_delta > 0) ? p_env_ln->ln_max
: p_env_ln->ln_min;
if (linear)
{
tgt = MM_LIGHTS_LN_LINEAR(tgt);
}
rem_time = MM_TRANS_TIME_UNKNOWN;
}
else
{
tgt = (linear) ? p_env_ln->ln_tgt_linear : p_env_ln->ln_tgt;
}
write16p(p_data + MM_LIGHT_LN_STATUS_TGT_LIGHTNESS_POS, tgt);
*(p_data + MM_LIGHT_LN_STATUS_REM_TIME_POS) = rem_time;
}
// Send the message
mm_route_send(p_buf_status);
}
}
/**
****************************************************************************************
* @brief Prepare and send a Light Lightness Last Status message
*
* @param[in] p_env_ln Pointer to Light Lightness Server model environment
* @param[in] p_route_env Pointer to structure containing reception information provided
* by Mesh Profile for received request message
****************************************************************************************
*/
__STATIC void mm_lights_ln_send_status_last(mm_lights_ln_env_t *p_env_ln,
mm_route_env_t *p_route_env)
{
// Pointer to the buffer that will contain the message
mesh_buf_t *p_buf_status = mm_route_buf_alloc_status(MM_LIGHT_LN_LAST_STATUS_LEN, p_route_env);
if (p_buf_status)
{
// Get pointer to data
uint8_t *p_data = MESH_BUF_DATA(p_buf_status);
// Get pointer to environment
mm_route_env_t *p_buf_env = (mm_route_env_t *)&p_buf_status->env;
// Complete environment
p_buf_env->mdl_lid = p_env_ln->env.mdl_lid;
p_buf_env->opcode = MM_MSG_LIGHT_LN_LAST_STATUS;
// Fill the message
write16p(p_data + MM_LIGHT_LN_LAST_STATUS_LIGHTNESS_POS, p_env_ln->ln_last);
// Send the message
mm_route_send(p_buf_status);
}
}
/**
****************************************************************************************
* @brief Prepare and send a Light Lightness Default Status message
*
* @param[in] p_env_ln Pointer to Light Lightness Server model environment
* @param[in] p_route_env Pointer to structure containing reception information provided
* by Mesh Profile for received request message
****************************************************************************************
*/
__STATIC void mm_lights_ln_send_status_dflt(mm_lights_ln_env_t *p_env_ln,
mm_route_env_t *p_route_env)
{
// Pointer to the buffer that will contain the message
mesh_buf_t *p_buf_status = mm_route_buf_alloc_status(MM_LIGHT_LN_DFLT_STATUS_LEN, p_route_env);
if (p_buf_status)
{
// Get pointer to data
uint8_t *p_data = MESH_BUF_DATA(p_buf_status);
// Get pointer to environment
mm_route_env_t *p_buf_env = (mm_route_env_t *)&p_buf_status->env;
// Complete environment
p_buf_env->mdl_lid = p_env_ln->env.mdl_lid;
p_buf_env->opcode = MM_MSG_LIGHT_LN_DFLT_STATUS;
// Fill the message
write16p(p_data + MM_LIGHT_LN_DFLT_STATUS_LIGHTNESS_POS, p_env_ln->ln_dflt);
// Send the message
mm_route_send(p_buf_status);
}
}
/**
****************************************************************************************
* @brief Prepare and send a Light Lightness Range Status message
*
* @param[in] p_env_ln Pointer to Light Lightness Level Server model environment
* @param[in] p_route_env Pointer to structure containing reception information provided
* by Mesh Profile for received request message
* @param[in] status Status sent in the Light Lightness Range Status message
* (@see enum mm_status)
****************************************************************************************
*/
__STATIC void mm_lights_ln_send_status_range(mm_lights_ln_env_t *p_env_ln,
mm_route_env_t *p_route_env,
uint8_t status)
{
// Pointer to the buffer that will contain the message
mesh_buf_t *p_buf_status = mm_route_buf_alloc_status(MM_LIGHT_LN_RANGE_STATUS_LEN, p_route_env);
if (p_buf_status)
{
// Get pointer to data
uint8_t *p_data = MESH_BUF_DATA(p_buf_status);
// Get pointer to environment
mm_route_env_t *p_buf_env = (mm_route_env_t *)&p_buf_status->env;
// Prepare environment
p_buf_env->mdl_lid = p_env_ln->env.mdl_lid;
p_buf_env->opcode = MM_MSG_LIGHT_LN_RANGE_STATUS;
// Fill the message
*(p_data + MM_LIGHT_LN_RANGE_STATUS_CODE_POS) = status;
write16p(p_data + MM_LIGHT_LN_RANGE_STATUS_MIN_POS, p_env_ln->ln_min);
write16p(p_data + MM_LIGHT_LN_RANGE_STATUS_MAX_POS, p_env_ln->ln_max);
// Send the message
mm_route_send(p_buf_status);
}
}
/**
****************************************************************************************
* @brief Publish Light Lightness Actual state value if sending of publications is enabled
*
* @param[in] p_env_ln Pointer to Light Lightness Server model environment
****************************************************************************************
*/
__STATIC void mm_lights_ln_publish(mm_lights_ln_env_t *p_env_ln)
{
// Check if sending of publication is enabled
if (GETB(p_env_ln->env.info, MM_INFO_PUBLI))
{
mm_lights_ln_send_status(p_env_ln, NULL, true, false);
}
}
/**
****************************************************************************************
* @brief Check if a Light Lightnes Status message must be sent and send it if it
* is the case
*
* @param[in] p_env_ln Pointer to Light Lightness Server model environment
****************************************************************************************
*/
__STATIC void mm_lights_ln_check_status_rsp(mm_lights_ln_env_t *p_env_ln)
{
if (p_env_ln->status_dst_addr != MESH_UNASSIGNED_ADDR)
{
// Send a response to the node that has required the transition
mm_lights_ln_send_status(p_env_ln, NULL, false, p_env_ln->status_linear);
p_env_ln->status_dst_addr = MESH_UNASSIGNED_ADDR;
}
}
/*
* MESSAGE HANDLERS
****************************************************************************************
*/
/**
****************************************************************************************
* @brief Handler for Light Lightness Set/Set Unacknowledged and Light Lightness Linear
* Set/Set Unacknowledged message.
* Note that all messages have the same content.
*
* @param[in] p_env Pointer to environment of model for which message has been received
* @param[in] p_buf Pointer to buffer containing the received message
* @param[in] p_route_env Pointer to routing information for the received buffer
****************************************************************************************
*/
__STATIC void mm_lights_ln_handler_set(mm_lights_ln_env_t *p_env_ln, mesh_buf_t *p_buf,
mm_route_env_t *p_route_env)
{
do
{
// Set Light Lightness Actual or Light Lightness Linear staet value
bool linear = ((p_route_env->opcode == MM_MSG_LIGHT_LN_LINEAR_SET)
|| (p_route_env->opcode == MM_MSG_LIGHT_LN_LINEAR_SET_UNACK));
// Check if a status message must be sent
bool send_status = ((p_route_env->opcode == MM_MSG_LIGHT_LN_LINEAR_SET)
|| (p_route_env->opcode == MM_MSG_LIGHT_LN_SET));
// Get pointer to data
uint8_t *p_data = MESH_BUF_DATA(p_buf);
// Lightness Actual and Lightness Linear values
uint16_t ln, ln_linear;
// Extract TID value
uint8_t tid = *(p_data + MM_LIGHT_LN_SET_TID_POS);
// Transition time
uint8_t trans_time = MM_TRANS_TIME_UNKNOWN;
// Delay
uint8_t delay = 0;
// Extract and check optional parameters if present
if (p_buf->data_len == MM_LIGHT_LN_SET_LEN)
{
trans_time = (uint16_t)(*(p_data + MM_LIGHT_LN_SET_TRANS_TIME_POS));
// Check received value
if (GETF(trans_time, MM_TRANS_TIME_STEP_NB) > MM_TRANS_TIME_STEPS_MAX)
{
// Drop the message
break;
}
delay = *(p_data + MM_LIGHT_LN_SET_DELAY_POS);
}
if (linear)
{
ln_linear = read16p(p_data + MM_LIGHT_LN_SET_LIGHTNESS_POS);
ln = MM_LIGHTS_LN_ACTUAL(ln_linear);
}
else
{
ln = read16p(p_data + MM_LIGHT_LN_SET_LIGHTNESS_POS);
}
if (ln != 0)
{
// Ensure that Light Lightness Actual state value is between Light Lightness Range
// Min and Max values
if (ln > p_env_ln->ln_max)
{
ln = p_env_ln->ln_max;
}
else if (ln < p_env_ln->ln_min)
{
ln = p_env_ln->ln_min;
}
}
// Check if Light Lightness Actual state is modified
if ((p_env_ln->status_dst_addr != MESH_UNASSIGNED_ADDR)
|| mm_replay_is_retx(&p_env_ln->replay_env, p_route_env->u_addr.src, tid)
|| (ln == p_env_ln->ln))
{
// Send a Light Lightness Status or a Light Lightness Linear Status message
if (send_status)
{
mm_lights_ln_send_status(p_env_ln, p_route_env, false, linear);
}
break;
};
if (send_status)
{
// Keep information for transmission of status
p_env_ln->status_dst_addr = p_route_env->u_addr.src;
p_env_ln->status_app_key_lid = p_route_env->app_key_lid;
p_env_ln->status_relay = GETB(p_route_env->info, MM_ROUTE_INFO_RELAY);
p_env_ln->status_linear = linear;
}
if (GETB(p_env_ln->env.info, MM_INFO_MAIN))
{
// Update target state
p_env_ln->ln_tgt = ln;
p_env_ln->ln_tgt_linear = MM_LIGHTS_LN_LINEAR(ln);
// Inform the Binding Manager about new transition
mm_bind_trans_new(p_env_ln->env.grp_lid, MM_TRANS_TYPE_CLASSIC, trans_time, delay);
}
else
{
// Inform the Binding Manager
mm_bind_trans_req(p_env_ln->env.grp_lid, p_env_ln->env.mdl_lid,
MM_TRANS_TYPE_CLASSIC, ln, trans_time, delay);
}
} while (0);
}
/**
****************************************************************************************
* @brief Handler for Light Lightness Default Set/Set Unacknowledged message
*
* @param[in] p_env Pointer to environment of model for which message has been received
* @param[in] p_buf Pointer to buffer containing the received message
* @param[in] p_route_env Pointer to routing information for the received buffer
****************************************************************************************
*/
__STATIC void mm_lights_ln_handler_set_dflt(mm_lights_ln_env_t *p_env_ln, mesh_buf_t *p_buf,
mm_route_env_t *p_route_env)
{
// Get pointer to data
uint8_t *p_data = MESH_BUF_DATA(p_buf);
// Extract received state value
uint16_t ln_dflt = read16p(p_data + MM_LIGHT_LN_DFLT_SET_LIGHTNESS_POS);
if (ln_dflt != p_env_ln->ln_dflt)
{
// Keep received value
p_env_ln->ln_dflt = ln_dflt;
// Inform application about received value
mm_srv_state_upd_ind_send(MM_STATE_LIGHT_LN_DFLT, p_env_ln->env.elmt_idx,
p_env_ln->ln_dflt, 0);
}
// If needed, send a Light Lightness Default Status message to the requester
if (p_route_env->opcode == MM_MSG_LIGHT_LN_DFLT_SET)
{
mm_lights_ln_send_status_dflt(p_env_ln, p_route_env);
}
}
/**
****************************************************************************************
* @brief Handler for Light Lightness Range Set/Set Unacknowledged message
*
* @param[in] p_env Pointer to environment of model for which message has been received
* @param[in] p_buf Pointer to buffer containing the received message
* @param[in] p_route_env Pointer to routing information for the received buffer
****************************************************************************************
*/
__STATIC void mm_lights_ln_handler_set_range(mm_lights_ln_env_t *p_env_ln, mesh_buf_t *p_buf,
mm_route_env_t *p_route_env)
{
do
{
// Get pointer to data
uint8_t *p_data = MESH_BUF_DATA(p_buf);
// Status
uint8_t status = MM_STATUS_SUCCESS;
// Extract Light Lightness Range state value
uint16_t ln_min = read16p(p_data + MM_LIGHT_LN_RANGE_SET_MIN_POS);
// Extract Light Lightness Range state value
uint16_t ln_max = read16p(p_data + MM_LIGHT_LN_RANGE_SET_MAX_POS);
// Check provided values
if (ln_min == 0)
{
status = MM_STATUS_ERROR_RANGE_MIN;
}
else if (ln_max == 0)
{
status = MM_STATUS_ERROR_RANGE_MAX;
}
else if (ln_min > ln_max)
{
// Drop the message
break;
}
if ((status == MM_STATUS_SUCCESS)
&& ((p_env_ln->ln_min != ln_min)
|| (p_env_ln->ln_max != ln_max)))
{
p_env_ln->ln_min = ln_min;
p_env_ln->ln_max = ln_max;
// Inform application about received value
mm_srv_state_upd_ind_send(MM_STATE_LIGHT_LN_RANGE, p_env_ln->env.elmt_idx,
(uint32_t)ln_min | ((uint32_t)ln_max << 16), 0);
}
// If needed, send a Light Lightness Range Status message to the requester
if (p_route_env->opcode == MM_MSG_LIGHT_LN_RANGE_SET)
{
mm_lights_ln_send_status_range(p_env_ln, p_route_env, status);
}
} while (0);
}
/*
* INTERNAL CALLBACK FUNCTIONS
****************************************************************************************
*/
/**
****************************************************************************************
* @brief Callback function called when timer monitoring publication duration for
* Light Lightness Server model expires
*
* @param[in] p_env Pointer to model environment for Light Lightness Server model
****************************************************************************************
*/
__STATIC void mm_lights_ln_cb_tmr_publi(void *p_tmr)
{
// Get allocated environment
mm_lights_ln_env_t *p_env_ln = MESH_TMR2ENV(p_tmr, mm_lights_ln_env_t, tmr_publi);
if (p_env_ln->publi_period_ms)
{
// Publish a Light Lightness Status message
mm_lights_ln_publish(p_env_ln);
// Restart the timer
mesh_timer_set(&p_env_ln->tmr_publi, p_env_ln->publi_period_ms);
}
}
/**
****************************************************************************************
* @brief Inform Light Lightness Server model about a received message
*
* @param[in] p_env Pointer to the environment allocated for the Light Lightness
* Server model
* @param[in] p_buf Pointer to the buffer containing the received message
* @param[in] p_route_env Pointer to structure containing reception parameters provided
* by the Mesh Profile block
****************************************************************************************
*/
__STATIC void mm_lights_ln_cb_rx(mm_mdl_env_t *p_env, mesh_buf_t *p_buf,
mm_route_env_t *p_route_env)
{
do
{
// Environment for Light Lightness Server model
mm_lights_ln_env_t *p_env_ln;
if (p_env->model_id == MM_ID_LIGHTS_LN)
{
p_env_ln = (mm_lights_ln_env_t *)p_env;
switch (p_route_env->opcode)
{
case (MM_MSG_LIGHT_LN_GET):
case (MM_MSG_LIGHT_LN_LINEAR_GET):
{
// Send a Light Lightness Status or a Light Lightness Linear Status message
mm_lights_ln_send_status(p_env_ln, p_route_env, false,
(p_route_env->opcode == MM_MSG_LIGHT_LN_LINEAR_GET));
} break;
case (MM_MSG_LIGHT_LN_SET):
case (MM_MSG_LIGHT_LN_SET_UNACK):
case (MM_MSG_LIGHT_LN_LINEAR_SET):
case (MM_MSG_LIGHT_LN_LINEAR_SET_UNACK):
{
// Handle the message
mm_lights_ln_handler_set(p_env_ln, p_buf, p_route_env);
} break;
case (MM_MSG_LIGHT_LN_LAST_GET):
{
// Send a Light Lightness Last Status message
mm_lights_ln_send_status_last(p_env_ln, p_route_env);
} break;
case (MM_MSG_LIGHT_LN_DFLT_GET):
{
// Send a Light Lightness Default Status message
mm_lights_ln_send_status_dflt(p_env_ln, p_route_env);
} break;
case (MM_MSG_LIGHT_LN_RANGE_GET):
{
// Send a Light Lightness Range Status message
mm_lights_ln_send_status_range(p_env_ln, p_route_env, MM_STATUS_SUCCESS);
} break;
default:
{
} break;
}
}
else // (p_env->model_id == MM_ID_LIGHTS_LNS)
{
// Environment for Light Lightness Setup Server model
mm_lights_lns_env_t *p_env_lns = (mm_lights_lns_env_t *)p_env;
p_env_ln = p_env_lns->p_env_ln;
switch (p_route_env->opcode)
{
case (MM_MSG_LIGHT_LN_DFLT_SET):
case (MM_MSG_LIGHT_LN_DFLT_SET_UNACK):
{
// Handle the message
mm_lights_ln_handler_set_dflt(p_env_ln, p_buf, p_route_env);
} break;
case (MM_MSG_LIGHT_LN_RANGE_SET):
case (MM_MSG_LIGHT_LN_RANGE_SET_UNACK):
{
// Handle the message
mm_lights_ln_handler_set_range(p_env_ln, p_buf, p_route_env);
} break;
default:
{
} break;
}
}
} while (0);
}
/**
****************************************************************************************
* @brief Check if a given opcode is handled by the Light Lightness Server model
*
* @param[in] p_env Pointer to the environment allocated for the Light Lightness
* Server model
* @param[in] opcode Opcode to check
*
* @return An error status (@see enum mesh_err)
****************************************************************************************
*/
__STATIC uint8_t mm_lights_ln_cb_opcode_check(mm_mdl_env_t *p_env, uint32_t opcode)
{
uint8_t status = MESH_ERR_MDL_INVALID_OPCODE;
if (p_env->model_id == MM_ID_LIGHTS_LN)
{
if ((opcode == MM_MSG_LIGHT_LN_GET)
|| (opcode == MM_MSG_LIGHT_LN_SET)
|| (opcode == MM_MSG_LIGHT_LN_SET_UNACK)
|| (opcode == MM_MSG_LIGHT_LN_LINEAR_GET)
|| (opcode == MM_MSG_LIGHT_LN_LINEAR_SET)
|| (opcode == MM_MSG_LIGHT_LN_LINEAR_SET_UNACK)
|| (opcode == MM_MSG_LIGHT_LN_LAST_GET)
|| (opcode == MM_MSG_LIGHT_LN_DFLT_GET)
|| (opcode == MM_MSG_LIGHT_LN_RANGE_GET))
{
status = MESH_ERR_NO_ERROR;
}
}
else if (p_env->model_id == MM_ID_LIGHTS_LNS)
{
if ((opcode == MM_MSG_LIGHT_LN_DFLT_SET)
|| (opcode == MM_MSG_LIGHT_LN_DFLT_SET_UNACK)
|| (opcode == MM_MSG_LIGHT_LN_RANGE_SET)
|| (opcode == MM_MSG_LIGHT_LN_RANGE_SET_UNACK))
{
status = MESH_ERR_NO_ERROR;
}
}
return (status);
}
/**
****************************************************************************************
* @brief Set Light Lightness Actual or Light Lightness Default or Light Lightness Range
* state value
*
* @param[in] p_env Pointer the the environment allocated for the Generic OnOff
* Server model
* @param[in] state_id State identifier
* @param[in] state Light Lightness Actual or Light Lightness Default or Light
* Lightness Range state value
*
* @return An error status (@see enum mesh_err)
****************************************************************************************
*/
__STATIC uint8_t mm_lights_ln_cb_set(mm_mdl_env_t *p_env, uint16_t state_id, uint32_t state)
{
// Returned status
uint8_t status = MESH_ERR_NO_ERROR;
// Get environment for the Light Lightness Server model
mm_lights_ln_env_t *p_env_ln = (mm_lights_ln_env_t *)p_env;
switch (state_id)
{
case (MM_STATE_LIGHT_LN):
{
uint16_t ln = state;
p_env_ln->ln = ln;
p_env_ln->ln_linear = MM_LIGHTS_LN_LINEAR(ln);
if (ln != 0)
{
p_env_ln->ln_last = ln;
}
// Set the targeted Generic OnOff state value
mm_bind_set_state(p_env_ln->env.grp_lid, MM_STATE_TYPE_TARGET, MM_ID_GENS_OO,
(p_env_ln->ln == 0) ? 0 : 1);
// Set the targeted Generic Level state value
mm_bind_set_state(p_env_ln->env.grp_lid, MM_STATE_TYPE_TARGET, MM_ID_GENS_LVL,
p_env_ln->ln - 32768);
} break;
case (MM_STATE_LIGHT_LN_DFLT):
{
p_env_ln->ln_dflt = state;
} break;
case (MM_STATE_LIGHT_LN_RANGE):
{
p_env_ln->ln_min = state;
p_env_ln->ln_max = (state >> 16);
} break;
default:
{
status = MESH_ERR_INVALID_PARAM;
} break;
}
return (status);
}
/*
* CALLBACK FUNCTIONS FOR BINDING MANAGER
****************************************************************************************
*/
/**
****************************************************************************************
* @brief Callback function provided to the Binding Manager so that Light Lighting Server
* model can be informed about group event
*
* @param[in] mdl_lid Model Local Index
* @param[in] event Event
* @param[in] info Information (depends on the event type)
****************************************************************************************
*/
__STATIC void mm_lights_ln_cb_grp_event(m_lid_t mdl_lid, uint8_t event, uint8_t info)
{
// Get environment for Light Lightness Server model
mm_lights_ln_env_t *p_env_ln = (mm_lights_ln_env_t *)mm_state_get_env(mdl_lid);
switch (event)
{
case (MM_GRP_EVENT_TRANS_DELAY_EXPIRED):
{
// Set the targeted Generic OnOff state value
mm_bind_set_state(p_env_ln->env.grp_lid, MM_STATE_TYPE_TARGET, MM_ID_GENS_OO,
(p_env_ln->ln_tgt == 0) ? 0 : 1);
// Set the targeted Generic Level state value
mm_bind_set_state(p_env_ln->env.grp_lid, MM_STATE_TYPE_TARGET, MM_ID_GENS_LVL,
(int32_t)p_env_ln->ln_tgt - 32768);
// Start the transition
mm_bind_trans_start(p_env_ln->env.grp_lid);
} break;
case (MM_GRP_EVENT_TRANS_IMMEDIATE):
{
p_env_ln->ln = p_env_ln->ln_tgt;
p_env_ln->ln_linear = p_env_ln->ln_tgt_linear;
if (p_env_ln->ln != 0)
{
p_env_ln->ln_last = p_env_ln->ln;
}
} // no break;
case (MM_GRP_EVENT_TRANS_STARTED):
{
uint8_t trans_time = info;
if (GETB(p_env_ln->env.info, MM_INFO_MAIN))
{
// Inform application about state update
mm_srv_state_upd_ind_send(MM_STATE_LIGHT_LN, p_env_ln->env.elmt_idx,
p_env_ln->ln_tgt,
(trans_time) ? mm_get_trans_time_ms(trans_time) : 0);
}
// Check if a status message must be sent
mm_lights_ln_check_status_rsp(p_env_ln);
// Send a publication
mm_lights_ln_publish(p_env_ln);
} break;
case (MM_GRP_EVENT_TRANS_END):
{
p_env_ln->ln = p_env_ln->ln_tgt;
p_env_ln->ln_linear = p_env_ln->ln_tgt_linear;
if (GETB(p_env_ln->env.info, MM_INFO_MAIN))
{
// Inform application about state update
mm_srv_state_upd_ind_send(MM_STATE_LIGHT_LN, p_env_ln->env.elmt_idx, p_env_ln->ln, 0);
}
if (p_env_ln->ln != 0)
{
p_env_ln->ln_last = p_env_ln->ln;
}
// Check if a status message must be sent
mm_lights_ln_check_status_rsp(p_env_ln);
// Send a publication
mm_lights_ln_publish(p_env_ln);
} break;
case (MM_GRP_EVENT_GROUP_FULL):
{
// Set Generic Level state value
mm_bind_set_state(p_env_ln->env.grp_lid, MM_STATE_TYPE_CURRENT,
MM_ID_GENS_LVL, 0x8000);
} break;
//case (MM_GRP_EVENT_TRANS_ABORTED):
default:
{
} break;
}
}
/**
****************************************************************************************
* @brief Callback function provided to the Binding Manager so that Light Lighting state
* value can be set by main model of the group
*
* @param[in] mdl_lid Model Local Index
* @param[in] type Type
* @param[in] state State value
****************************************************************************************
*/
__STATIC void mm_lights_ln_cb_trans_req(m_lid_t main_mdl_lid, uint32_t req_model_id, uint8_t trans_type,
uint32_t state_delta)
{
// Get environment for Light Lightness Server model
mm_lights_ln_env_t *p_env_ln = (mm_lights_ln_env_t *)mm_state_get_env(main_mdl_lid);
// Targeted Light Lightness Actual state value
uint16_t ln_tgt;
if (req_model_id == MM_ID_GENS_OO)
{
// Requested Generic OnOff state value
uint8_t onoff = (uint8_t)state_delta;
if (onoff == 0)
{
ln_tgt = 0;
}
else
{
ln_tgt = (p_env_ln->ln_dflt == 0) ? p_env_ln->ln_last
: p_env_ln->ln_dflt;
}
}
else // req_model_id == MM_ID_GENS_LVL
{
if (trans_type == MM_TRANS_TYPE_CLASSIC)
{
// Requested Generic Level state value
int16_t level = (int16_t)state_delta;
// Light Lightness Actual = Generic Level + 32768
ln_tgt = 32768 + level;
}
else // ((trans_type == MM_TRANS_TYPE_DELTA) || trans_type == MM_TRANS_TYPE_MOVE))
{
// Delta value
int32_t delta;
if (trans_type == MM_TRANS_TYPE_MOVE)
{
delta = (int16_t)state_delta;
// Keep the provided delta value
p_env_ln->move_delta = (int16_t)state_delta;
}
else
{
delta = (int32_t)state_delta;
}
// Add the Light Lightness Actual state value to the received delta value
delta += p_env_ln->ln;
// The Light Lightness Actual state value cannot wrap
if (delta < 0)
{
ln_tgt = 0;
}
else
{
ln_tgt = (delta > 0xFFFF) ? 0xFFFF : (uint16_t)delta;
}
}
}
if (ln_tgt != 0)
{
// Ensure that Light Lightness Actual state value is between Light Lightness Range
// Min and Max values
if (ln_tgt > p_env_ln->ln_max)
{
ln_tgt = p_env_ln->ln_max;
}
else if (ln_tgt < p_env_ln->ln_min)
{
ln_tgt = p_env_ln->ln_min;
}
}
// Check if Light Lightness Actual state value is modified
if (ln_tgt != p_env_ln->ln)
{
p_env_ln->ln_tgt = ln_tgt;
p_env_ln->ln_tgt_linear = MM_LIGHTS_LN_LINEAR(ln_tgt);
// Start a new transition
mm_bind_trans_new(p_env_ln->env.grp_lid, trans_type, 0, 0);
}
else
{
// Reject the transition
mm_bind_trans_reject(p_env_ln->env.grp_lid);
}
}
/**
****************************************************************************************
* @brief Callback function provided to the Binding Manager so that Light Lightness state
* value can be set by main model of the group
*
* @param[in] mdl_lid Model Local Index
* @param[in] type Type
* @param[in] state State value
****************************************************************************************
*/
__STATIC void mm_lights_ln_cb_set_state(m_lid_t mdl_lid, uint8_t type, uint32_t state)
{
// Get environment for Light Lightness Server model
mm_lights_ln_env_t *p_env_ln = (mm_lights_ln_env_t *)mm_state_get_env(mdl_lid);
// Light Lightness state value
uint16_t ln = state;
// Generic Level = Light Lightness - 32768
int16_t lvl = (int32_t)ln - 32768;
// Sanity check
ASSERT_INFO(!GETB(p_env_ln->env.info, MM_INFO_MAIN), mdl_lid, 0);
if (type == MM_STATE_TYPE_CURRENT)
{
p_env_ln->ln = ln;
p_env_ln->ln_linear = MM_LIGHTS_LN_LINEAR(ln);
}
else // (type == MM_STATE_TYPE_TARGET)
{
p_env_ln->ln_tgt = ln;
p_env_ln->ln_tgt_linear = MM_LIGHTS_LN_LINEAR(ln);
}
// Set the Generic Level state (current or target) value
mm_bind_set_state(p_env_ln->env.grp_lid, type, MM_ID_GENS_LVL, lvl);
}
/**
****************************************************************************************
* @brief Register Light Lightness Server model for a given local element
*
* @param[in] elmt_idx Element Index
* @param[in] p_mdl_lid Pointer to the variable in which allocated model local index
* will be written
*
* @return An error status (@see mesh_err)
****************************************************************************************
*/
__STATIC uint8_t mm_lights_lns_register(uint8_t elmt_idx)
{
// Register Light Lightness Server model
m_lid_t mdl_lid = ms_register_model(MM_ID_LIGHTS_LN, elmt_idx, MM_CFG_PUBLI_AUTH_BIT);
if (mdl_lid != MESH_INVALID_LID)
{
// Inform the Model State Manager about registered model
mm_lights_ln_env_t *p_env_ln = (mm_lights_ln_env_t *)mm_state_register(elmt_idx, MM_ID_LIGHTS_LN, mdl_lid,
MM_ROLE_SRV_PUBLI, sizeof(mm_lights_ln_env_t));
if (p_env_ln)
{
// Initiate states
p_env_ln->ln_min = 1;
p_env_ln->ln_max = 0xFFFF;
// Prepare environment for Replay Manager
p_env_ln->replay_env.delay_ms = MM_LIGHTS_LN_REPLAY_MS;
// Prepare timer for publications
p_env_ln->tmr_publi.cb = mm_lights_ln_cb_tmr_publi;
//p_env_ln->tmr_publi.p_env = (void *)p_env_ln;
// Set internal callback functions
p_env_ln->env.mdl_cb.cb_rx = mm_lights_ln_cb_rx;
p_env_ln->env.mdl_cb.cb_opcode_check = mm_lights_ln_cb_opcode_check;
//p_env_ln->env.mdl_cb.cb_publish_param = mm_lights_ln_cb_publish_param;
//p_cb_srv->cb_set = mm_lights_ln_cb_set;
p_env_ln->env.mdl_cb.cb_srv_set = mm_lights_ln_cb_set;
// Inform application about registered model
mm_register_ind_send(MM_ID_LIGHTS_LN, elmt_idx, mdl_lid);
}
// Register Light Lightness Setup Server model
m_lid_t lns_lid = ms_register_model(MM_ID_LIGHTS_LNS, elmt_idx, 0);
if (lns_lid != MESH_INVALID_LID)
{
// Inform the Model State Manager about registered model
mm_lights_lns_env_t *p_env_lns = (mm_lights_lns_env_t *)mm_state_register(elmt_idx, MM_ID_LIGHTS_LNS, lns_lid,
MM_ROLE_SRV, sizeof(mm_lights_lns_env_t));
if (p_env_lns)
{
// Set internal callback functions
p_env_lns->env.mdl_cb.cb_rx = mm_lights_ln_cb_rx;
p_env_lns->env.mdl_cb.cb_opcode_check = mm_lights_ln_cb_opcode_check;
// Link environment
p_env_lns->p_env_ln = p_env_ln;
// Inform application about registered model
mm_register_ind_send(MM_ID_LIGHTS_LNS, elmt_idx, lns_lid);
}
}
}
return (mdl_lid);
}
/*
* GLOBAL FUNCTIONS
****************************************************************************************
*/
uint8_t mm_lights_ln_register(uint8_t elmt_idx, bool main)
{
m_lid_t mdl_lid = MESH_INVALID_LID;
do
{
m_lid_t oo_lid, lvl_lid;
// Register the Generic OnOff Server, the Generic Power OnOff Server and Setup models
oo_lid = mm_gens_poo_register(elmt_idx, false);
if (oo_lid == MESH_INVALID_LID)
{
break;
}
// Register the Generic Level Server model
lvl_lid = mm_gens_lvl_register(elmt_idx, false);
if (lvl_lid == MESH_INVALID_LID)
{
break;
}
// Register the Light Lightness Server and the Light Lightness Setup models
mdl_lid = mm_lights_lns_register(elmt_idx);
if (main && (mdl_lid != MESH_INVALID_LID))
{
// Create group and set Light Lightness Server model as main model
m_lid_t grp_lid = mm_bind_add_group(2, elmt_idx, mdl_lid,
mm_lights_ln_cb_grp_event, mm_lights_ln_cb_trans_req);
// Add Generic Level Server model to the group
mm_gens_lvl_bind_group(grp_lid, lvl_lid);
// Add Generic OnOff Server model to the group
mm_gens_oo_bind_group(grp_lid, oo_lid);
}
} while (0);
return (mdl_lid);
}
uint8_t mm_lights_ln_bind_group(m_lid_t grp_lid, m_lid_t ln_lid)
{
return mm_bind_group_add_mdl(grp_lid, ln_lid, MM_ID_LIGHTS_LN,
mm_lights_ln_cb_grp_event,
mm_lights_ln_cb_set_state);
}
uint16_t mm_lights_ln_get(uint8_t elmt_idx, uint32_t state_id)
{
// Get model local index of Light Lightness Server model on given element
m_lid_t mdl_lid = mm_state_get_lid(elmt_idx, MM_ID_LIGHTS_LN);
// State
uint16_t state = 0;
if (mdl_lid != MESH_INVALID_LID)
{
// Get environment for the model
mm_lights_ln_env_t *p_env_ln = (mm_lights_ln_env_t *)mm_state_get_env(mdl_lid);
switch (state_id)
{
case (MM_STATE_LIGHT_LN_DFLT):
{
state = p_env_ln->ln_dflt;
} break;
case (MM_STATE_LIGHT_LN_RANGE_MIN):
{
state = p_env_ln->ln_min;
} break;
case (MM_STATE_LIGHT_LN_RANGE_MAX):
{
state = p_env_ln->ln_max;
} break;
case (MM_STATE_LIGHT_LN_LAST):
{
state = p_env_ln->ln_last;
} break;
default:
{
} break;
}
}
return (state);
}
void mm_lights_ln_set_dflt(uint8_t elmt_idx, uint16_t ln_dflt)
{
// Get model local index of Light Lightness Server model on given element
m_lid_t mdl_lid = mm_state_get_lid(elmt_idx, MM_ID_LIGHTS_LN);
if (mdl_lid != MESH_INVALID_LID)
{
// Get environment for the model
mm_lights_ln_env_t *p_env_ln = (mm_lights_ln_env_t *)mm_state_get_env(mdl_lid);
// Keep the provided value
p_env_ln->ln_dflt = ln_dflt;
// Inform application about received value
mm_srv_state_upd_ind_send(MM_STATE_LIGHT_LN_DFLT, elmt_idx, ln_dflt, 0);
}
}
/// @} end of group