2020-08-27 18:52:06 +02:00
|
|
|
/*
|
|
|
|
* NimBLEMeshModel.cpp
|
|
|
|
*
|
|
|
|
* Created: on Aug 25 2020
|
|
|
|
* Author H2zero
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "NimBLEMeshModel.h"
|
2020-08-30 15:48:24 +02:00
|
|
|
#include "NimBLEUtils.h"
|
2020-08-27 18:52:06 +02:00
|
|
|
#include "NimBLELog.h"
|
|
|
|
|
2020-08-30 15:48:24 +02:00
|
|
|
#include "nimble/nimble_port.h"
|
|
|
|
|
2020-10-25 15:29:54 +01:00
|
|
|
#include "NimBLEDevice.h"
|
|
|
|
|
|
|
|
#define CID_VENDOR 0x05C3
|
|
|
|
#define STANDARD_TEST_ID 0x00
|
|
|
|
|
2020-08-27 18:52:06 +02:00
|
|
|
static const char* LOG_TAG = "NimBLEMeshModel";
|
|
|
|
|
|
|
|
static NimBLEMeshModelCallbacks defaultCallbacks;
|
|
|
|
|
2020-09-16 06:11:42 +02:00
|
|
|
static const struct bt_mesh_health_srv_cb health_srv_cb = {
|
|
|
|
NimBLEHealthSrvCallbacks::faultGetCurrent,
|
|
|
|
NimBLEHealthSrvCallbacks::faultGetRegistered,
|
|
|
|
NimBLEHealthSrvCallbacks::faultClear,
|
|
|
|
NimBLEHealthSrvCallbacks::faultTest,
|
|
|
|
NimBLEHealthSrvCallbacks::attentionOn,
|
|
|
|
NimBLEHealthSrvCallbacks::attentionOff
|
|
|
|
};
|
|
|
|
|
2020-08-27 18:52:06 +02:00
|
|
|
/**
|
|
|
|
* @brief base model constructor
|
|
|
|
* @param [in] pCallbacks, a pointer to a callback instance for model operations
|
|
|
|
*/
|
2020-09-07 18:21:44 +02:00
|
|
|
NimBLEMeshModel::NimBLEMeshModel(NimBLEMeshModelCallbacks *pCallbacks) {
|
2020-08-27 18:52:06 +02:00
|
|
|
if(pCallbacks == nullptr) {
|
|
|
|
m_callbacks = &defaultCallbacks;
|
|
|
|
} else {
|
|
|
|
m_callbacks = pCallbacks;
|
|
|
|
}
|
|
|
|
|
2020-09-01 16:43:58 +02:00
|
|
|
m_opList = nullptr;
|
|
|
|
m_lastTid = 0;
|
2020-08-30 15:48:24 +02:00
|
|
|
m_lastSrcAddr = 0;
|
|
|
|
m_lastDstAddr = 0;
|
|
|
|
m_lastMsgTime = 0;
|
|
|
|
m_transTime = 0;
|
|
|
|
m_delayTime = 0;
|
|
|
|
m_transStep = 0;
|
2020-09-01 16:43:58 +02:00
|
|
|
|
|
|
|
memset(&m_opPub, 0, sizeof(m_opPub));
|
2020-08-27 18:52:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief destructor
|
|
|
|
*/
|
2020-08-30 15:48:24 +02:00
|
|
|
NimBLEMeshModel::~NimBLEMeshModel() {
|
2020-09-01 16:43:58 +02:00
|
|
|
if(m_opList != nullptr) {
|
|
|
|
delete[] m_opList;
|
2020-08-27 18:52:06 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-30 15:48:24 +02:00
|
|
|
int NimBLEMeshModel::extractTransTimeDelay(os_mbuf *buf)
|
|
|
|
{
|
|
|
|
switch(buf->om_len) {
|
|
|
|
case 0x00:
|
|
|
|
m_transTime = 0;
|
|
|
|
m_delayTime = 0;
|
|
|
|
return 0;
|
|
|
|
case 0x02:
|
|
|
|
m_transTime = buf->om_data[0];
|
|
|
|
if((m_transTime & 0x3F) == 0x3F) {
|
|
|
|
// unknown transition time
|
|
|
|
m_transTime = 0;
|
|
|
|
m_delayTime = 0;
|
|
|
|
return BLE_HS_EINVAL;
|
|
|
|
}
|
|
|
|
m_delayTime = buf->om_data[1];
|
|
|
|
return 0;
|
|
|
|
default:
|
|
|
|
return BLE_HS_EMSGSIZE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool NimBLEMeshModel::checkRetransmit(uint8_t tid, bt_mesh_msg_ctx *ctx) {
|
|
|
|
time_t now = time(nullptr);
|
|
|
|
|
|
|
|
if(m_lastTid == tid &&
|
|
|
|
m_lastSrcAddr == ctx->addr &&
|
|
|
|
m_lastDstAddr == ctx->recv_dst &&
|
|
|
|
(now - m_lastMsgTime <= 6)) {
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "Ignoring retransmit");
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_lastTid = tid;
|
|
|
|
m_lastSrcAddr = ctx->addr;
|
|
|
|
m_lastDstAddr = ctx->recv_dst;
|
|
|
|
m_lastMsgTime = now;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NimBLEMeshModel::sendMessage(bt_mesh_model *model, bt_mesh_msg_ctx *ctx, os_mbuf *msg) {
|
|
|
|
if (bt_mesh_model_send(model, ctx, msg, NULL, NULL)) {
|
|
|
|
NIMBLE_LOGE(LOG_TAG, "Send status failed");
|
|
|
|
}
|
|
|
|
|
|
|
|
os_mbuf_free_chain(msg);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NimBLEMeshModel::startTdTimer(ble_npl_time_t timerMs) {
|
2020-09-01 16:43:58 +02:00
|
|
|
ble_npl_time_t ticks;
|
|
|
|
ble_npl_time_ms_to_ticks(timerMs, &ticks);
|
|
|
|
ble_npl_callout_reset(&m_tdTimer, ticks);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NimBLEMeshModel::publish() {
|
|
|
|
ble_npl_callout_reset(&m_pubTimer, 1);
|
|
|
|
}
|
|
|
|
|
2020-09-16 06:11:42 +02:00
|
|
|
|
2020-09-07 18:21:44 +02:00
|
|
|
uint32_t NimBLEMeshModel::getTransTime() {
|
|
|
|
return (m_transTime & 0x3F) * NimBLEUtils::meshTransTimeMs(m_transTime);
|
2020-08-30 15:48:24 +02:00
|
|
|
}
|
|
|
|
|
2020-09-16 06:11:42 +02:00
|
|
|
|
2020-09-07 18:21:44 +02:00
|
|
|
uint16_t NimBLEMeshModel::getDelayTime() {
|
|
|
|
return m_delayTime * 5;
|
|
|
|
}
|
|
|
|
|
2020-08-30 15:48:24 +02:00
|
|
|
|
2020-08-27 18:52:06 +02:00
|
|
|
/**
|
|
|
|
* @brief Generic on/off server model constructor
|
|
|
|
* @param [in] pCallbacks, a pointer to a callback instance for model operations
|
|
|
|
*/
|
2020-09-07 18:21:44 +02:00
|
|
|
NimBLEGenOnOffSrvModel::NimBLEGenOnOffSrvModel(NimBLEMeshModelCallbacks *pCallbacks)
|
2020-08-27 18:52:06 +02:00
|
|
|
:NimBLEMeshModel(pCallbacks)
|
|
|
|
{
|
|
|
|
// Register the opcodes for this model with the required callbacks
|
2020-09-01 16:43:58 +02:00
|
|
|
m_opList = new bt_mesh_model_op[4]{
|
2020-08-27 18:52:06 +02:00
|
|
|
{ BT_MESH_MODEL_OP_2(0x82, 0x01), 0, NimBLEGenOnOffSrvModel::getOnOff },
|
|
|
|
{ BT_MESH_MODEL_OP_2(0x82, 0x02), 2, NimBLEGenOnOffSrvModel::setOnOff },
|
|
|
|
{ BT_MESH_MODEL_OP_2(0x82, 0x03), 2, NimBLEGenOnOffSrvModel::setOnOffUnack },
|
|
|
|
BT_MESH_MODEL_OP_END};
|
2020-08-30 15:48:24 +02:00
|
|
|
|
|
|
|
ble_npl_callout_init(&m_tdTimer, nimble_port_get_dflt_eventq(),
|
|
|
|
NimBLEGenOnOffSrvModel::tdTimerCb, this);
|
2020-09-01 16:43:58 +02:00
|
|
|
ble_npl_callout_init(&m_pubTimer, nimble_port_get_dflt_eventq(),
|
|
|
|
NimBLEGenOnOffSrvModel::pubTimerCb, this);
|
|
|
|
|
|
|
|
m_opPub.msg = NET_BUF_SIMPLE(2 + 3);
|
2020-09-16 06:11:42 +02:00
|
|
|
|
2020-09-07 18:23:17 +02:00
|
|
|
m_value.push_back(0);
|
|
|
|
m_targetValue.push_back(0);
|
2020-08-27 18:52:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Called by the NimBLE stack to get the on/off status of the model
|
|
|
|
*/
|
|
|
|
void NimBLEGenOnOffSrvModel::getOnOff(bt_mesh_model *model,
|
|
|
|
bt_mesh_msg_ctx *ctx,
|
|
|
|
os_mbuf *buf)
|
|
|
|
{
|
2020-08-30 15:48:24 +02:00
|
|
|
NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data;
|
2020-09-07 18:21:44 +02:00
|
|
|
if(pModel->m_callbacks != &defaultCallbacks) {
|
|
|
|
pModel->setValue(pModel->m_callbacks->getOnOff(pModel));
|
|
|
|
}
|
2020-09-01 16:43:58 +02:00
|
|
|
pModel->setPubMsg();
|
|
|
|
|
|
|
|
if (bt_mesh_model_send(model, ctx, pModel->m_opPub.msg, NULL, NULL)) {
|
|
|
|
NIMBLE_LOGE(LOG_TAG, "Send status failed");
|
|
|
|
}
|
2020-08-27 18:52:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Called by the NimBLE stack to set the status of the model with acknowledgement.
|
|
|
|
*/
|
|
|
|
void NimBLEGenOnOffSrvModel::setOnOff(bt_mesh_model *model,
|
|
|
|
bt_mesh_msg_ctx *ctx,
|
|
|
|
os_mbuf *buf)
|
|
|
|
{
|
2020-08-30 15:48:24 +02:00
|
|
|
// Rather than duplicate code just call the unack function then send the status
|
|
|
|
NimBLEGenOnOffSrvModel::setOnOffUnack(model,ctx,buf);
|
2020-08-27 18:52:06 +02:00
|
|
|
NimBLEGenOnOffSrvModel::getOnOff(model,ctx,buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Called by the NimBLE stack to set the status of the model without acknowledgement.
|
|
|
|
*/
|
|
|
|
void NimBLEGenOnOffSrvModel::setOnOffUnack(bt_mesh_model *model,
|
|
|
|
bt_mesh_msg_ctx *ctx,
|
|
|
|
os_mbuf *buf)
|
|
|
|
{
|
2020-08-30 15:48:24 +02:00
|
|
|
NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data;
|
2020-09-07 18:17:58 +02:00
|
|
|
uint8_t newval = net_buf_simple_pull_u8(buf);
|
2020-08-30 15:48:24 +02:00
|
|
|
uint8_t tid = net_buf_simple_pull_u8(buf);
|
|
|
|
|
|
|
|
if(pModel->checkRetransmit(tid, ctx)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pModel->extractTransTimeDelay(buf) != 0) {
|
|
|
|
NIMBLE_LOGI(LOG_TAG, "Transition time / delay data error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// stop the transition timer to handle the new input
|
|
|
|
ble_npl_callout_stop(&pModel->m_tdTimer);
|
|
|
|
|
|
|
|
// Mesh spec says transition to "ON state" happens immediately
|
|
|
|
// after delay, so ignore the transition time.
|
|
|
|
if(newval == 1) {
|
|
|
|
pModel->m_transTime = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
ble_npl_time_t timerMs = 0;
|
|
|
|
|
2020-09-07 18:23:17 +02:00
|
|
|
if(newval != pModel->m_value[0]) {
|
|
|
|
pModel->m_targetValue[0] = newval;
|
2020-08-30 15:48:24 +02:00
|
|
|
|
|
|
|
if(pModel->m_delayTime > 0) {
|
|
|
|
timerMs = 5 * pModel->m_delayTime;
|
|
|
|
} else if(pModel->m_transTime & 0x3F) {
|
|
|
|
timerMs = NimBLEUtils::meshTransTimeMs(pModel->m_transTime);
|
|
|
|
pModel->m_transTime -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(timerMs > 0) {
|
|
|
|
pModel->startTdTimer(timerMs);
|
|
|
|
} else {
|
2020-09-07 18:17:58 +02:00
|
|
|
pModel->m_value = pModel->m_targetValue;
|
2020-09-07 18:23:17 +02:00
|
|
|
pModel->m_callbacks->setOnOff(pModel, pModel->m_value[0]);
|
2020-08-30 15:48:24 +02:00
|
|
|
}
|
2020-08-27 18:52:06 +02:00
|
|
|
}
|
|
|
|
|
2020-10-25 15:29:54 +01:00
|
|
|
|
2020-08-30 15:48:24 +02:00
|
|
|
void NimBLEGenOnOffSrvModel::tdTimerCb(ble_npl_event *event) {
|
|
|
|
NimBLEMeshModel *pModel = (NimBLEMeshModel*)event->arg;
|
|
|
|
if(pModel->m_delayTime > 0) {
|
|
|
|
pModel->m_delayTime = 0;
|
|
|
|
}
|
|
|
|
|
2020-09-07 18:23:17 +02:00
|
|
|
if((pModel->m_transTime & 0x3F) && pModel->m_targetValue[0]/*m_onOffTarget*/ == 0) {
|
2020-09-01 16:43:58 +02:00
|
|
|
pModel->startTdTimer(NimBLEUtils::meshTransTimeMs(pModel->m_transTime));
|
2020-08-30 15:48:24 +02:00
|
|
|
pModel->m_transTime -= 1;
|
2020-09-01 16:43:58 +02:00
|
|
|
pModel->publish();
|
2020-08-30 15:48:24 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pModel->m_transTime = 0;
|
2020-09-07 18:17:58 +02:00
|
|
|
pModel->m_value = pModel->m_targetValue;
|
2020-09-07 18:23:17 +02:00
|
|
|
pModel->m_callbacks->setOnOff(pModel, pModel->m_value[0]);
|
2020-09-01 16:43:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NimBLEGenOnOffSrvModel::pubTimerCb(ble_npl_event *event) {
|
|
|
|
NimBLEMeshModel *pModel = (NimBLEMeshModel*)event->arg;
|
|
|
|
pModel->setPubMsg();
|
|
|
|
|
|
|
|
int err = bt_mesh_model_publish(pModel->m_opPub.mod);
|
|
|
|
if(err != 0) {
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "Publish rc: %d",err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NimBLEGenOnOffSrvModel::setPubMsg() {
|
|
|
|
bt_mesh_model_msg_init(m_opPub.msg, BT_MESH_MODEL_OP_2(0x82, 0x04));
|
2020-09-07 18:23:17 +02:00
|
|
|
net_buf_simple_add_u8(m_opPub.msg, m_value[0]);
|
2020-09-01 16:43:58 +02:00
|
|
|
if(m_transTime > 0) {
|
2020-09-07 18:23:17 +02:00
|
|
|
net_buf_simple_add_u8(m_opPub.msg, m_targetValue[0]);
|
2020-09-01 16:43:58 +02:00
|
|
|
// If we started the transition timer in setOnOff we need to correct the reported remaining time.
|
|
|
|
net_buf_simple_add_u8(m_opPub.msg, (m_delayTime > 0) ?
|
|
|
|
m_transTime : m_transTime + 1);
|
|
|
|
}
|
2020-08-30 15:48:24 +02:00
|
|
|
}
|
2020-08-27 18:52:06 +02:00
|
|
|
|
2020-10-25 15:29:54 +01:00
|
|
|
|
2020-09-07 18:21:44 +02:00
|
|
|
void NimBLEGenOnOffSrvModel::setValue(uint8_t *val, size_t len) {
|
|
|
|
if(len != sizeof(uint8_t)) {
|
|
|
|
NIMBLE_LOGE(LOG_TAG, "NimBLEGenOnOffSrvModel: Incorrect value length");
|
|
|
|
return;
|
|
|
|
}
|
2020-09-07 18:23:17 +02:00
|
|
|
m_value[0] = *val;
|
2020-09-07 18:21:44 +02:00
|
|
|
}
|
|
|
|
|
2020-10-25 15:29:54 +01:00
|
|
|
|
2020-09-07 18:21:44 +02:00
|
|
|
void NimBLEGenOnOffSrvModel::setTargetValue(uint8_t *val, size_t len) {
|
|
|
|
if(len != sizeof(uint8_t)) {
|
|
|
|
NIMBLE_LOGE(LOG_TAG, "NimBLEGenOnOffSrvModel: Incorrect target value length");
|
|
|
|
return;
|
|
|
|
}
|
2020-09-07 18:23:17 +02:00
|
|
|
m_targetValue[0] = *val;
|
2020-09-07 18:21:44 +02:00
|
|
|
}
|
|
|
|
|
2020-10-25 15:29:54 +01:00
|
|
|
|
2020-08-27 18:52:06 +02:00
|
|
|
/**
|
|
|
|
* @brief Generic level server model constructor
|
|
|
|
* @param [in] pCallbacks, a pointer to a callback instance for model operations
|
|
|
|
*/
|
2020-09-07 18:21:44 +02:00
|
|
|
NimBLEGenLevelSrvModel::NimBLEGenLevelSrvModel(NimBLEMeshModelCallbacks *pCallbacks)
|
2020-08-27 18:52:06 +02:00
|
|
|
:NimBLEMeshModel(pCallbacks)
|
|
|
|
{
|
|
|
|
// Register the opcodes for this model with the required callbacks
|
2020-09-01 16:43:58 +02:00
|
|
|
m_opList = new bt_mesh_model_op[8]{
|
2020-08-27 18:52:06 +02:00
|
|
|
{ BT_MESH_MODEL_OP_2(0x82, 0x05), 0, NimBLEGenLevelSrvModel::getLevel },
|
|
|
|
{ BT_MESH_MODEL_OP_2(0x82, 0x06), 3, NimBLEGenLevelSrvModel::setLevel },
|
|
|
|
{ BT_MESH_MODEL_OP_2(0x82, 0x07), 3, NimBLEGenLevelSrvModel::setLevelUnack },
|
|
|
|
{ BT_MESH_MODEL_OP_2(0x82, 0x09), 5, NimBLEGenLevelSrvModel::setDelta },
|
|
|
|
{ BT_MESH_MODEL_OP_2(0x82, 0x0a), 5, NimBLEGenLevelSrvModel::setDeltaUnack },
|
|
|
|
{ BT_MESH_MODEL_OP_2(0x82, 0x0b), 3, NimBLEGenLevelSrvModel::setMove },
|
|
|
|
{ BT_MESH_MODEL_OP_2(0x82, 0x0c), 3, NimBLEGenLevelSrvModel::setMoveUnack },
|
|
|
|
BT_MESH_MODEL_OP_END};
|
2020-08-30 15:48:24 +02:00
|
|
|
|
|
|
|
ble_npl_callout_init(&m_tdTimer, nimble_port_get_dflt_eventq(),
|
|
|
|
NimBLEGenLevelSrvModel::tdTimerCb, this);
|
2020-09-01 16:43:58 +02:00
|
|
|
ble_npl_callout_init(&m_pubTimer, nimble_port_get_dflt_eventq(),
|
|
|
|
NimBLEGenLevelSrvModel::pubTimerCb, this);
|
|
|
|
m_opPub.msg = NET_BUF_SIMPLE(2 + 5);
|
2020-09-16 06:11:42 +02:00
|
|
|
|
2020-09-07 18:23:17 +02:00
|
|
|
m_value.assign(2, 0);
|
|
|
|
m_targetValue.assign(2, 0);
|
2020-08-27 18:52:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Called by the NimBLE stack to get the level value of the model.
|
|
|
|
*/
|
|
|
|
void NimBLEGenLevelSrvModel::getLevel(bt_mesh_model *model,
|
|
|
|
bt_mesh_msg_ctx *ctx,
|
|
|
|
os_mbuf *buf)
|
|
|
|
{
|
|
|
|
NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data;
|
2020-09-07 18:23:17 +02:00
|
|
|
if(pModel->m_callbacks != &defaultCallbacks) {
|
|
|
|
pModel->setValue(pModel->m_callbacks->getLevel(pModel));
|
2020-08-27 18:52:06 +02:00
|
|
|
}
|
2020-09-07 18:23:17 +02:00
|
|
|
pModel->setPubMsg();
|
2020-08-27 18:52:06 +02:00
|
|
|
|
2020-09-01 16:43:58 +02:00
|
|
|
if (bt_mesh_model_send(model, ctx, pModel->m_opPub.msg, NULL, NULL)) {
|
|
|
|
NIMBLE_LOGE(LOG_TAG, "Send status failed");
|
|
|
|
}
|
2020-08-27 18:52:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Called by the NimBLE stack to set the level value of the model.
|
|
|
|
*/
|
|
|
|
void NimBLEGenLevelSrvModel::setLevel(bt_mesh_model *model,
|
|
|
|
bt_mesh_msg_ctx *ctx,
|
|
|
|
os_mbuf *buf)
|
|
|
|
{
|
2020-08-30 15:48:24 +02:00
|
|
|
NimBLEGenLevelSrvModel::setLevelUnack(model, ctx, buf);
|
2020-08-27 18:52:06 +02:00
|
|
|
NimBLEGenLevelSrvModel::getLevel(model, ctx, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Called by the NimBLE stack to set the level value of the model without acknowledgement.
|
|
|
|
*/
|
|
|
|
void NimBLEGenLevelSrvModel::setLevelUnack(bt_mesh_model *model,
|
|
|
|
bt_mesh_msg_ctx *ctx,
|
|
|
|
os_mbuf *buf)
|
|
|
|
{
|
|
|
|
NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data;
|
2020-08-30 15:48:24 +02:00
|
|
|
int16_t newval = (int16_t) net_buf_simple_pull_le16(buf);
|
|
|
|
uint8_t tid = net_buf_simple_pull_u8(buf);
|
|
|
|
|
|
|
|
if(pModel->checkRetransmit(tid, ctx)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pModel->extractTransTimeDelay(buf) != 0) {
|
|
|
|
NIMBLE_LOGI(LOG_TAG, "Transition time / delay data error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// stop the transition timer to handle the new input
|
|
|
|
ble_npl_callout_stop(&pModel->m_tdTimer);
|
|
|
|
|
|
|
|
ble_npl_time_t timerMs = 0;
|
2020-09-16 06:11:42 +02:00
|
|
|
|
2020-09-07 18:23:17 +02:00
|
|
|
int16_t curval = *(int16_t*)&pModel->m_value[0];
|
2020-09-16 06:11:42 +02:00
|
|
|
|
2020-09-07 18:23:17 +02:00
|
|
|
if(newval != curval) {
|
|
|
|
pModel->m_targetValue.assign({(uint8_t)newval, (uint8_t)(newval >> 8)});
|
2020-08-30 15:48:24 +02:00
|
|
|
|
|
|
|
if(pModel->m_delayTime > 0) {
|
|
|
|
timerMs = 5 * pModel->m_delayTime;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pModel->m_transTime & 0x3F) {
|
2020-09-07 18:23:17 +02:00
|
|
|
pModel->m_transStep = -1 * ((curval - newval) / (pModel->m_transTime & 0x3F));
|
2020-08-30 15:48:24 +02:00
|
|
|
if(timerMs == 0) {
|
|
|
|
timerMs = NimBLEUtils::meshTransTimeMs(pModel->m_transTime);
|
|
|
|
pModel->m_transTime -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(timerMs > 0) {
|
|
|
|
pModel->startTdTimer(timerMs);
|
2020-09-07 18:23:17 +02:00
|
|
|
} else {
|
2020-09-07 18:17:58 +02:00
|
|
|
pModel->m_value = pModel->m_targetValue;
|
2020-09-07 18:23:17 +02:00
|
|
|
pModel->m_callbacks->setLevel(pModel, newval);
|
2020-08-30 15:48:24 +02:00
|
|
|
}
|
2020-08-27 18:52:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Called by the NimBLE stack to set the level value by delta of the model.
|
|
|
|
*/
|
|
|
|
void NimBLEGenLevelSrvModel::setDelta(bt_mesh_model *model,
|
|
|
|
bt_mesh_msg_ctx *ctx,
|
|
|
|
os_mbuf *buf)
|
|
|
|
{
|
2020-08-30 15:48:24 +02:00
|
|
|
NimBLEGenLevelSrvModel::setDeltaUnack(model, ctx, buf);
|
2020-08-27 18:52:06 +02:00
|
|
|
NimBLEGenLevelSrvModel::getLevel(model, ctx, buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Called by the NimBLE stack to set the level value by delta without acknowledgement.
|
|
|
|
*/
|
|
|
|
void NimBLEGenLevelSrvModel::setDeltaUnack(bt_mesh_model *model,
|
|
|
|
bt_mesh_msg_ctx *ctx,
|
|
|
|
os_mbuf *buf)
|
|
|
|
{
|
|
|
|
NimBLEMeshModel *pModel = (NimBLEMeshModel*)model->user_data;
|
2020-08-30 15:48:24 +02:00
|
|
|
int32_t delta = (int32_t) net_buf_simple_pull_le32(buf);
|
|
|
|
|
2020-09-07 18:23:17 +02:00
|
|
|
int32_t temp32 = *(int16_t*)&pModel->m_value[0] + delta;
|
2020-08-30 15:48:24 +02:00
|
|
|
if (temp32 < INT16_MIN) {
|
|
|
|
temp32 = INT16_MIN;
|
|
|
|
} else if (temp32 > INT16_MAX) {
|
|
|
|
temp32 = INT16_MAX;
|
|
|
|
}
|
|
|
|
|
|
|
|
net_buf_simple_push_le16(buf, (uint16_t)temp32);
|
|
|
|
NimBLEGenLevelSrvModel::setLevelUnack(model, ctx, buf);
|
2020-08-27 18:52:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NimBLEGenLevelSrvModel::setMove(bt_mesh_model *model,
|
|
|
|
bt_mesh_msg_ctx *ctx,
|
|
|
|
os_mbuf *buf)
|
|
|
|
{
|
2020-08-30 15:48:24 +02:00
|
|
|
NimBLEGenLevelSrvModel::setMoveUnack(model, ctx, buf);
|
|
|
|
NimBLEGenLevelSrvModel::getLevel(model, ctx, buf);
|
2020-08-27 18:52:06 +02:00
|
|
|
}
|
|
|
|
|
2020-08-30 15:48:24 +02:00
|
|
|
|
2020-08-27 18:52:06 +02:00
|
|
|
void NimBLEGenLevelSrvModel::setMoveUnack(bt_mesh_model *model,
|
|
|
|
bt_mesh_msg_ctx *ctx,
|
|
|
|
os_mbuf *buf)
|
|
|
|
{
|
2020-08-30 15:48:24 +02:00
|
|
|
int16_t delta = (int16_t) net_buf_simple_pull_le16(buf);
|
|
|
|
// Check if a transition time is present, if not then ignore this message.
|
|
|
|
// See: bluetooth mesh specifcation
|
|
|
|
if(buf->om_len < 3) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
put_le32(net_buf_simple_push(buf, 4), (int32_t)delta);
|
|
|
|
NimBLEGenLevelSrvModel::setDeltaUnack(model, ctx, buf);
|
2020-08-27 18:52:06 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-30 15:48:24 +02:00
|
|
|
void NimBLEGenLevelSrvModel::tdTimerCb(ble_npl_event *event) {
|
|
|
|
NimBLEMeshModel *pModel = (NimBLEMeshModel*)event->arg;
|
|
|
|
if(pModel->m_delayTime > 0) {
|
|
|
|
pModel->m_delayTime = 0;
|
|
|
|
}
|
|
|
|
|
2020-09-07 18:17:58 +02:00
|
|
|
if(pModel->m_transTime & 0x3F) {
|
2020-09-07 18:23:17 +02:00
|
|
|
int16_t newval = *(int16_t*)&pModel->m_value[0] + pModel->m_transStep;
|
|
|
|
pModel->m_value.assign({(uint8_t)newval, (uint8_t)(newval >> 8)});
|
2020-09-07 18:21:44 +02:00
|
|
|
pModel->m_callbacks->setLevel(pModel, newval);
|
2020-09-01 16:43:58 +02:00
|
|
|
pModel->startTdTimer(NimBLEUtils::meshTransTimeMs(pModel->m_transTime));
|
2020-08-30 15:48:24 +02:00
|
|
|
pModel->m_transTime -= 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
pModel->m_transTime = 0;
|
2020-09-07 18:17:58 +02:00
|
|
|
pModel->m_value = pModel->m_targetValue;
|
2020-09-07 18:23:17 +02:00
|
|
|
pModel->m_callbacks->setLevel(pModel, *(int16_t*)&pModel->m_value[0]);
|
2020-09-01 16:43:58 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NimBLEGenLevelSrvModel::pubTimerCb(ble_npl_event *event) {
|
|
|
|
NimBLEMeshModel *pModel = (NimBLEMeshModel*)event->arg;
|
|
|
|
pModel->setPubMsg();
|
|
|
|
|
|
|
|
int err = bt_mesh_model_publish(pModel->m_opPub.mod);
|
|
|
|
if(err != 0) {
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "Publish rc: %d",err);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-16 06:11:42 +02:00
|
|
|
|
2020-09-01 16:43:58 +02:00
|
|
|
void NimBLEGenLevelSrvModel::setPubMsg() {
|
|
|
|
bt_mesh_model_msg_init(m_opPub.msg, BT_MESH_MODEL_OP_2(0x82, 0x08));
|
2020-09-07 18:23:17 +02:00
|
|
|
net_buf_simple_add_le16(m_opPub.msg, *(int16_t*)&m_value[0]);
|
2020-09-01 16:43:58 +02:00
|
|
|
if(m_transTime > 0) {
|
2020-09-07 18:23:17 +02:00
|
|
|
net_buf_simple_add_le16(m_opPub.msg, *(int16_t*)&m_targetValue[0]);
|
2020-09-01 16:43:58 +02:00
|
|
|
// If we started the transition timer in setOnOff we need to correct the reported remaining time.
|
|
|
|
net_buf_simple_add_u8(m_opPub.msg, (m_delayTime > 0) ?
|
|
|
|
m_transTime : m_transTime + 1);
|
|
|
|
}
|
2020-08-30 15:48:24 +02:00
|
|
|
}
|
|
|
|
|
2020-09-16 06:11:42 +02:00
|
|
|
|
2020-09-07 18:21:44 +02:00
|
|
|
void NimBLEGenLevelSrvModel::setValue(uint8_t *val, size_t len) {
|
|
|
|
if(len != sizeof(int16_t)) {
|
|
|
|
NIMBLE_LOGE(LOG_TAG, "NimBLEGenLevelSrvModel: Incorrect value length");
|
|
|
|
return;
|
|
|
|
}
|
2020-09-07 18:23:17 +02:00
|
|
|
m_value.assign({*val, val[1]});
|
2020-09-07 18:21:44 +02:00
|
|
|
}
|
|
|
|
|
2020-09-16 06:11:42 +02:00
|
|
|
|
2020-09-07 18:21:44 +02:00
|
|
|
void NimBLEGenLevelSrvModel::setTargetValue(uint8_t *val, size_t len) {
|
|
|
|
if(len != sizeof(int16_t)) {
|
|
|
|
NIMBLE_LOGE(LOG_TAG, "NimBLEGenLevelSrvModel: Incorrect target value length");
|
|
|
|
return;
|
|
|
|
}
|
2020-09-07 18:23:17 +02:00
|
|
|
m_targetValue.assign({*val, val[1]});
|
2020-09-07 18:21:44 +02:00
|
|
|
}
|
|
|
|
|
2020-09-16 06:11:42 +02:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @brief Health server model constructor
|
|
|
|
* @param [in] pCallbacks, a pointer to a callback instance for model operations
|
|
|
|
*/
|
|
|
|
NimBLEHealthSrvModel::NimBLEHealthSrvModel(NimBLEMeshModelCallbacks *pCallbacks)
|
|
|
|
:NimBLEMeshModel(pCallbacks)
|
|
|
|
{
|
|
|
|
memset(&m_healthSrv, 0, sizeof(m_healthSrv));
|
|
|
|
m_healthSrv.cb = &health_srv_cb;
|
2020-10-25 15:29:54 +01:00
|
|
|
m_opPub.msg = NET_BUF_SIMPLE(1 + 3);
|
|
|
|
m_hasFault = false;
|
|
|
|
m_testId = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void NimBLEHealthSrvModel::setFault(uint8_t fault) {
|
|
|
|
m_faults.push_back(fault);
|
|
|
|
m_hasFault = true;
|
2020-09-16 06:11:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-25 15:29:54 +01:00
|
|
|
void NimBLEHealthSrvModel::clearFaults() {
|
|
|
|
m_faults.clear();
|
|
|
|
m_hasFault = false;
|
2020-09-16 06:11:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-08-27 18:52:06 +02:00
|
|
|
/**
|
|
|
|
* Default model callbacks
|
|
|
|
*/
|
|
|
|
NimBLEMeshModelCallbacks::~NimBLEMeshModelCallbacks() {}
|
|
|
|
|
2020-09-07 18:21:44 +02:00
|
|
|
void NimBLEMeshModelCallbacks::setOnOff(NimBLEMeshModel *pModel, uint8_t val) {
|
2020-08-27 18:52:06 +02:00
|
|
|
NIMBLE_LOGD(LOG_TAG, "Gen On/Off set val: %d", val);
|
|
|
|
}
|
|
|
|
|
2020-09-07 18:21:44 +02:00
|
|
|
uint8_t NimBLEMeshModelCallbacks::getOnOff(NimBLEMeshModel *pModel) {
|
2020-08-27 18:52:06 +02:00
|
|
|
NIMBLE_LOGD(LOG_TAG, "Gen On/Off get");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2020-09-07 18:21:44 +02:00
|
|
|
void NimBLEMeshModelCallbacks::setLevel(NimBLEMeshModel *pModel, int16_t val) {
|
2020-08-27 18:52:06 +02:00
|
|
|
NIMBLE_LOGD(LOG_TAG, "Gen Level set val: %d", val);
|
|
|
|
}
|
|
|
|
|
2020-09-07 18:21:44 +02:00
|
|
|
int16_t NimBLEMeshModelCallbacks::getLevel(NimBLEMeshModel *pModel) {
|
2020-08-27 18:52:06 +02:00
|
|
|
NIMBLE_LOGD(LOG_TAG, "Gen Level get");
|
|
|
|
return 0;
|
|
|
|
}
|
2020-09-16 06:11:42 +02:00
|
|
|
|
2020-10-25 15:29:54 +01:00
|
|
|
void NimBLEMeshModelCallbacks::attentionOn(NimBLEMeshModel *pModel) {
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "Attention On Default");
|
|
|
|
}
|
|
|
|
|
|
|
|
void NimBLEMeshModelCallbacks::attentionOff(NimBLEMeshModel *pModel) {
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "Attention Off Default");
|
|
|
|
}
|
|
|
|
|
|
|
|
void NimBLEMeshModelCallbacks::faultTest(NimBLEMeshModel *pModel) {
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "Fault Test");
|
|
|
|
}
|
|
|
|
|
|
|
|
void NimBLEMeshModelCallbacks::faultClear(NimBLEMeshModel *pModel) {
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "Fault Clear");
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-09-16 06:11:42 +02:00
|
|
|
/**
|
|
|
|
* @brief Health server callbacks
|
|
|
|
*/
|
|
|
|
int NimBLEHealthSrvCallbacks::faultGetCurrent(bt_mesh_model *model, uint8_t *test_id,
|
|
|
|
uint16_t *company_id, uint8_t *faults,
|
|
|
|
uint8_t *fault_count)
|
|
|
|
{
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "faultGetCurrent - default");
|
2020-10-25 15:29:54 +01:00
|
|
|
|
|
|
|
NimBLEHealthSrvModel* pModel = (NimBLEHealthSrvModel*)NimBLEDevice::getMeshNode()->getHealthModel(model);
|
|
|
|
*test_id = pModel->m_testId;
|
|
|
|
*company_id = CID_VENDOR;
|
|
|
|
faults = &pModel->m_faults[0];
|
|
|
|
*fault_count = pModel->m_faults.size();
|
2020-09-16 06:11:42 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int NimBLEHealthSrvCallbacks::faultGetRegistered(bt_mesh_model *model, uint16_t company_id,
|
|
|
|
uint8_t *test_id, uint8_t *faults,
|
|
|
|
uint8_t *fault_count)
|
|
|
|
{
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "faultGetRegistered - default");
|
2020-10-25 15:29:54 +01:00
|
|
|
|
|
|
|
if (company_id != CID_VENDOR) {
|
|
|
|
return -BLE_HS_EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
NimBLEHealthSrvModel* pModel = (NimBLEHealthSrvModel*)NimBLEDevice::getMeshNode()->getHealthModel(model);
|
|
|
|
*test_id = pModel->m_testId;
|
|
|
|
faults = &pModel->m_faults[0];
|
|
|
|
*fault_count = pModel->m_faults.size();
|
|
|
|
|
2020-09-16 06:11:42 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int NimBLEHealthSrvCallbacks::faultClear(bt_mesh_model *model, uint16_t company_id)
|
|
|
|
{
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "faultClear - default");
|
2020-10-25 15:29:54 +01:00
|
|
|
|
|
|
|
if (company_id != CID_VENDOR) {
|
|
|
|
return -BLE_HS_EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
NimBLEHealthSrvModel* pModel = (NimBLEHealthSrvModel*)NimBLEDevice::getMeshNode()->getHealthModel(model);
|
|
|
|
pModel->m_callbacks->faultClear(pModel);
|
|
|
|
pModel->clearFaults();
|
|
|
|
|
2020-09-16 06:11:42 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int NimBLEHealthSrvCallbacks::faultTest(bt_mesh_model *model, uint8_t test_id, uint16_t company_id)
|
|
|
|
{
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "faultTest - default");
|
2020-10-25 15:29:54 +01:00
|
|
|
|
|
|
|
if (company_id != CID_VENDOR) {
|
|
|
|
return -BLE_HS_EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (test_id != STANDARD_TEST_ID) {
|
|
|
|
return -BLE_HS_EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
NimBLEHealthSrvModel* pModel = (NimBLEHealthSrvModel*)NimBLEDevice::getMeshNode()->getHealthModel(model);
|
|
|
|
pModel->setFault(0);
|
|
|
|
pModel->m_testId = test_id;
|
|
|
|
pModel->m_callbacks->faultTest(pModel);
|
|
|
|
|
2020-09-16 06:11:42 +02:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void NimBLEHealthSrvCallbacks::attentionOn(bt_mesh_model *model)
|
|
|
|
{
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "attentionOn - default");
|
2020-10-25 15:29:54 +01:00
|
|
|
NimBLEMeshModel* pModel = NimBLEDevice::getMeshNode()->getHealthModel(model);
|
|
|
|
pModel->m_callbacks->attentionOn(pModel);
|
2020-09-16 06:11:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void NimBLEHealthSrvCallbacks::attentionOff(bt_mesh_model *model)
|
|
|
|
{
|
|
|
|
NIMBLE_LOGD(LOG_TAG, "attentionOff - default");
|
2020-10-25 15:29:54 +01:00
|
|
|
NimBLEMeshModel* pModel = NimBLEDevice::getMeshNode()->getHealthModel(model);
|
|
|
|
pModel->m_callbacks->attentionOff(pModel);
|
2020-09-16 06:11:42 +02:00
|
|
|
}
|