AC63_BT_SDK/apps/mesh/examples/AliGenie_light.c
2025-02-18 15:40:42 +08:00

1257 lines
40 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "btstack/bluetooth.h"
#include "system/includes.h"
#include "bt_common.h"
#include "sha256.h"
#include "api/sig_mesh_api.h"
#include "model_api.h"
#include "unix_timestamp.h"
#define LOG_TAG "[Mesh-AliLight]"
#define LOG_ERROR_ENABLE
#define LOG_DEBUG_ENABLE
#define LOG_INFO_ENABLE
/* #define LOG_DUMP_ENABLE */
#define LOG_CLI_ENABLE
#include "debug.h"
#if (CONFIG_MESH_MODEL == SIG_MESH_ALIGENIE_LIGHT)
#if (defined(CONFIG_CPU_BD19))
extern void timer_pwm_init(JL_TIMER_TypeDef *JL_TIMERx, u32 pwm_io, u32 fre, u32 duty);
extern void set_led_duty(u16 duty);
#elif (defined(CONFIG_CPU_BD25))
extern int timer_pwm_init(JL_TIMER_TypeDef *JL_TIMERx, u32 fre, u32 duty, u32 port, int output_ch);
#else
_WEAK_ int timer_pwm_init(JL_TIMER_TypeDef *JL_TIMERx, u32 fre, u32 duty, u32 port, int output_ch)
{
return 0;
}
_WEAK_ void set_led_duty(u16 duty)
{
}
#endif
/*
* @brief Config current node features(Relay/Proxy/Friend/Low Power)
*/
/*-----------------------------------------------------------*/
#define BT_MESH_FEAT_SUPPORTED_TEMP ( \
BT_MESH_FEAT_RELAY | \
BT_MESH_FEAT_PROXY | \
0 \
)
#include "feature_correct.h"
const int config_bt_mesh_features = BT_MESH_FEAT_SUPPORTED;
/*
* @brief Config proxy connectable adv hardware param
*/
/*-----------------------------------------------------------*/
#if BT_MESH_FEATURES_GET(BT_MESH_FEAT_LOW_POWER)
const u16 config_bt_mesh_proxy_node_adv_interval = ADV_SCAN_UNIT(3000); // unit: ms
#else
const u16 config_bt_mesh_proxy_node_adv_interval = ADV_SCAN_UNIT(300); // unit: ms
#endif /* BT_MESH_FEATURES_GET(BT_MESH_FEAT_LOW_POWER) */
/*
* @brief Conifg complete local name
*/
/*-----------------------------------------------------------*/
#define BLE_DEV_NAME 'A', 'G', '-', 'L', 'i', 'g', 'h', 't'
const uint8_t mesh_name[] = {
// Name
BYTE_LEN(BLE_DEV_NAME) + 1, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, BLE_DEV_NAME,
};
void get_mesh_adv_name(u8 *len, u8 **data)
{
*len = sizeof(mesh_name);
*data = mesh_name;
}
/*
* @brief Conifg AliGenie 三元组
*
* detail on https://www.aligenie.com/doc/357554/gtgprq
*/
/*-----------------------------------------------------------*/
#define PID_TO_LITTLE_ENDIAN(x) \
(x & 0xff), \
((x >> 8) & 0xff), \
((x >> 16) & 0xff), \
((x >> 24) & 0xff)
#define PID_TO_BIG_ENDIAN(x) \
((x >> 24) & 0xff), \
((x >> 16) & 0xff), \
((x >> 8) & 0xff), \
(x & 0xff)
#define PRODUCT_ID_STRING_SIZE (sizeof(Product_ID) * 2)
#define MAC_ADDRESS_STRING_SIZE (sizeof(Mac_Address) * 2)
#define SECRET_STRING_SIZE (sizeof(Secret) - 1)
#define CUR_DEVICE_MAC_ADDR 0x18146c110301
#define PRODUCT_ID 11528252
#define DEVICE_SECRET "3f2254fcdaa99a446cd93377444123c9"
#define ALIGENIE_SUB_ADDR_1 0xc000
#define ALIGENIE_SUB_ADDR_2 0xcfff
/*
* @brief Publication Declarations
*
* The publication messages are initialized to the
* the size of the opcode + content
*
* For publication, the message must be in static or global as
* it is re-transmitted several times. This occurs
* after the function that called bt_mesh_model_publish() has
* exited and the stack is no longer valid.
*
* Note that the additional 4 bytes for the AppMIC is not needed
* because it is added to a stack variable at the time a
* transmission occurs.
*
*/
/*-----------------------------------------------------------*/
BT_MESH_MODEL_PUB_DEFINE(gen_onoff_pub_srv, NULL, 2 + 2);
/*
* @brief Generic OnOff Model Operation Codes
*/
/*-----------------------------------------------------------*/
#define BT_MESH_MODEL_OP_GEN_ONOFF_GET BT_MESH_MODEL_OP_2(0x82, 0x01)
#define BT_MESH_MODEL_OP_GEN_ONOFF_SET BT_MESH_MODEL_OP_2(0x82, 0x02)
#define BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x03)
#define BT_MESH_MODEL_OP_GEN_ONOFF_STATUS BT_MESH_MODEL_OP_2(0x82, 0x04)
/*
* @brief Light Lightness server Model Operation Codes
*/
/*-----------------------------------------------------------*/
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_GET BT_MESH_MODEL_OP_2(0x82, 0x4B)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET BT_MESH_MODEL_OP_2(0x82, 0x4C)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x4D)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS BT_MESH_MODEL_OP_2(0x82, 0x4E)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_GET BT_MESH_MODEL_OP_2(0x82, 0x4F)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET BT_MESH_MODEL_OP_2(0x82, 0x50)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_SET_UNACK BT_MESH_MODEL_OP_2(0x82, 0x51)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_LINEAR_STATUS BT_MESH_MODEL_OP_2(0x82, 0x52)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_GET BT_MESH_MODEL_OP_2(0x82, 0x53)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_LAST_STATUS BT_MESH_MODEL_OP_2(0x82, 0x54)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_GET BT_MESH_MODEL_OP_2(0x82, 0x55)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_DEFAULT_STATUS BT_MESH_MODEL_OP_2(0x82, 0x56)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_GET BT_MESH_MODEL_OP_2(0x82, 0x56)
#define BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_RANGE_STATUS BT_MESH_MODEL_OP_2(0x82, 0x58)
/*
* @brief Vendor Model ID
*
* Company Identifiers (see Bluetooth Assigned Numbers)
* detail on Mesh_v1.0 <3.7.2 Model identifier>
* detail on https://www.aligenie.com/doc/357554/iv5it7
*/
/*-----------------------------------------------------------*/
#define BT_COMP_ID_LF 0x01A8 // Alibaba
#define BT_MESH_VENDOR_MODEL_ID_SRV 0x01A80000 //0x0000
#define BT_MESH_VENDOR_MODEL_ID_CLI 0x01A80001 //0x0001
/*
* @brief AliGenie Vendor Model Operation Codes
*
* detail on Mesh_v1.0 <3.7.3.1 Operation codes>
* 扩展消息 detail on https://www.aligenie.com/doc/357554/iv5it7
*/
/*-----------------------------------------------------------*/
#define VENDOR_MSG_ATTR_GET BT_MESH_MODEL_OP_3(0xD0, BT_COMP_ID_LF)
#define VENDOR_MSG_ATTR_SET BT_MESH_MODEL_OP_3(0xD1, BT_COMP_ID_LF)
#define VENDOR_MSG_ATTR_SET_UNACK BT_MESH_MODEL_OP_3(0xD2, BT_COMP_ID_LF)
#define VENDOR_MSG_ATTR_STATUS BT_MESH_MODEL_OP_3(0xD3, BT_COMP_ID_LF)
#define VENDOR_MSG_ATTR_INDICAT BT_MESH_MODEL_OP_3(0xD4, BT_COMP_ID_LF)
#define VENDOR_MSG_ATTR_CONFIRM BT_MESH_MODEL_OP_3(0xD5, BT_COMP_ID_LF)
#define VENDOR_MSG_ATTR_TRANSPARENT BT_MESH_MODEL_OP_3(0xCF, BT_COMP_ID_LF)
/*
* @brief AliGenie Vendor Model Message Struct
*
* 定时功能 detail on https://www.aligenie.com/doc/357554/ovzn6v
*/
/*-----------------------------------------------------------*/
#define ATTR_TYPE_UNIX_TIME 0xF01F
#define ATTR_TYPE_SET_TIMEOUT 0xF010
#define ATTR_TYPE_SET_PERIOD_TIMEOUT 0xF011
#define ATTR_TYPE_DELETE_TIMEOUT 0xF012
struct __unix_time {
u32 Opcode: 24,
TID: 8;
u16 Attr_Type;
u32 time;
} _GNU_PACKED_;
struct __set_timeout {
u32 Opcode: 24,
TID: 8;
u16 Attr_Type;
struct __param {
u8 index;
u32 time;
u16 attr_type;
u8 attr_para;
} _GNU_PACKED_ param[1] ;
} _GNU_PACKED_;
struct __set_period_timeout {
u32 Opcode: 24,
TID: 8;
u16 Attr_Type;
struct __period_param {
u8 index;
u16 _24h_timer;
u8 schedule;
u16 attr_type;
u8 attr_para;
} _GNU_PACKED_ period_param[1] ;
} _GNU_PACKED_;
struct __delete_time {
u32 Opcode: 24,
TID: 8;
u16 Attr_Type;
u8 index;
} _GNU_PACKED_;
struct __timer_success {
u32 Opcode: 24,
TID: 8;
u16 Attr_Type;
u8 event;
u8 index;
} _GNU_PACKED_;
struct __set_bright {
u32 Opcode: 24,
TID: 8;
u16 Attr_Type;
u16 lightness_actual;
} _GNU_PACKED_;
struct __timer_param {
u8 tid;
u8 index;
u8 timer_cnt;
bool onoff;
};
struct light_state {
u16 lightness_actual;
u16 lightness_linear;
u16 lightness_default;
u16 lightness_last;
u16 lightness_range_min;
u16 lightness_range_max;
};
static struct light_state light = {
.lightness_actual = 0,
.lightness_default = 10000,
.lightness_range_min = 0,
.lightness_range_max = 65535,
.lightness_last = 0,
};
struct __onoff_repo {
u32 Opcode: 24,
TID: 8;
u16 Attr_Type;
bool OnOff;
} _GNU_PACKED_;
struct __indicate_msg {
u32 Opcode: 24,
TID: 8;
u16 Attr_Type;
u8 Event;
u8 EventParam;
} _GNU_PACKED_;
struct __comfirm_check_param {
u8 resend_cnt;
u8 timer_cnt;
u8 indicate_tid;
void *buf;
u16 len;
};
static bool led_flag = 0;
static u8 indicate_tid = 0x80;
static u16 timer_index[20];
static struct __timer_param timer_param[20];
static struct __comfirm_check_param comfirm_check_param[20];
static u8 timer_cnt = -1;
struct bt_mesh_model vendor_server_models[];
static bool indicate_flag[256];
u8 root_model_cnt = 3;
/*
* @brief Access Payload Fields
*
* detail on Mesh_v1.0 <3.7.3 Access payload>
*/
/*-----------------------------------------------------------*/
#define TRANSMIC_SIZE 4
#define MAX_USEFUL_ACCESS_PAYLOAD_SIZE 11 // 32 bit TransMIC (unsegmented)
#define ACCESS_OP_SIZE 3
#define ACCESS_PARAM_SIZE (MAX_USEFUL_ACCESS_PAYLOAD_SIZE - ACCESS_OP_SIZE)
/*
* @brief Generic OnOff State Set
*/
/*-----------------------------------------------------------*/
#define LED0_GPIO_PIN 0
#define LED1_GPIO_PIN 1
struct onoff_state {
u8_t current;
u8_t previous;
u8_t led_gpio_pin;
};
struct _switch {
u8_t sw_num;
u8_t onoff_state;
};
static struct onoff_state onoff_state[] = {
{ .led_gpio_pin = LED0_GPIO_PIN },
};
const u8 led_use_port[] = {
IO_PORTA_01,
};
/*
* @brief Generic OnOff Model Server Message Handlers
*
* Mesh Model Specification 3.1.1
*/
/*-----------------------------------------------------------*/
static void respond_messsage_schedule(u16 *delay, u16 *duration, void *cb_data)
{
/* Mesh_v1.0 <3.7.4.1 Transmitting an access message>
*
* If the message is sent in response to a received message
* that was sent to a unicast address, the node should transmit
* the response message with a random delay between 20 and 50 milliseconds.
*
* If the message is sent in response to a received message
* that was sent to a group address or a virtual address,
* the node should transmit the response message with
* a random delay between 20 and 500 milliseconds.
*/
u16 delay_ms;
//struct bt_mesh_msg_ctx *ctx = cb_data;
u16 dst_addr = (u16)cb_data;
pseudo_random_genrate((u8 *)&delay_ms, 2);
if (BT_MESH_ADDR_IS_UNICAST(dst_addr)) {
delay_ms = btctler_get_rand_from_assign_range(delay_ms, 20, 50);
} else {
delay_ms = btctler_get_rand_from_assign_range(delay_ms, 20, 200);
}
*delay = delay_ms;
log_info("respond_messsage delay =%u ms", delay_ms);
}
// static const struct bt_mesh_send_cb rsp_msg_cb = {
// .user_intercept = respond_messsage_schedule,
// /* .user_intercept = NULL, */
// };
/*
* @brief AliGenie Vendor Model Message Handlers
*
* 定时功能 detail on https://www.aligenie.com/doc/357554/ovzn6v
*/
/*-----------------------------------------------------------*/
static void vendor_attr_status_send(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
void *buf, u16 len)
{
log_info("ready to send ATTR_TYPE_SET_TIMEOUT status");
NET_BUF_SIMPLE_DEFINE(msg, len + TRANSMIC_SIZE);
buffer_memcpy(&msg, buf, len);
log_info_hexdump(msg.data, msg.len);
if (bt_mesh_model_send(model, ctx, &msg, NULL, NULL)) {
log_error("Unable to send Status response\n");
}
}
void indicate_tid_get(u8 *indicate_tid)
{
if (*indicate_tid >= 0x80 && *indicate_tid <= 0xbf) {
*indicate_tid += 1;
} else {
*indicate_tid = 0x80;
}
}
void timer_cnt_get(u8 *timer_cnt)
{
if (*timer_cnt >= -1 && *timer_cnt <= 18) {
*timer_cnt += 1;
} else {
*timer_cnt = 0;
}
}
void comfirm_check(struct __comfirm_check_param *param)
{
struct bt_mesh_msg_ctx ctx = {
.addr = 0xf000,
};
if (!indicate_flag[param->indicate_tid]) {
param->resend_cnt += 1;
if (param->resend_cnt >= 75) { //最多重传75次即30秒重传时间
sys_timer_remove(timer_index[param->timer_cnt]);
log_info("resend msg more than 75 times\r\n");
}
log_info("indicate_flag[ %d ] = %d\r\n", param->indicate_tid, indicate_flag[param->indicate_tid]);
log_info("\n param->buf.tid = %d, timer_cnt = %d \n", ((struct __onoff_repo *)(param->buf))->TID, param->timer_cnt);
vendor_attr_status_send(&vendor_server_models[0], &ctx, param->buf, param->len);
} else {
sys_timer_remove(timer_index[param->timer_cnt]);
}
}
static void gen_onoff_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
NET_BUF_SIMPLE_DEFINE(msg, 2 + 1 + 4);
struct onoff_state *onoff_state = model->rt->user_data;
log_info("gen_onoff_get addr 0x%04x onoff 0x%02x",
bt_mesh_model_elem(model)->rt->addr, onoff_state->current);
bt_mesh_model_msg_init(&msg, BT_MESH_MODEL_OP_GEN_ONOFF_STATUS);
buffer_add_u8_at_tail(&msg, onoff_state->current);
/* delay for send msg because adv timer duration is 50ms. */
os_time_dly(5);
if (bt_mesh_model_send(model, ctx, &msg, NULL/*&rsp_msg_cb*/, (void *)ctx->recv_dst)) {
log_error("Unable to send On Off Status response");
}
}
static void gen_onoff_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct net_buf_simple *msg = model->pub->msg;
struct onoff_state *onoff_state = model->rt->user_data;
int err;
onoff_state->current = buffer_pull_u8_from_head(buf);
log_info("gen_onoff_set_unack addr 0x%02x state 0x%02x", bt_mesh_model_elem(model)->rt->addr, onoff_state->current);
/* log_info_hexdump((u8 *)onoff_state, sizeof(*onoff_state)); */
if (onoff_state->current == 1) {
log_info("onoff set lightness = %d", light.lightness_default);
set_led_duty(light.lightness_default);
} else {
set_led_duty(0);
}
led_flag = onoff_state->current;
log_info(" tmall set led to %d ", led_flag);
gpio_pin_write(onoff_state->led_gpio_pin,
onoff_state->current);
indicate_tid_get(&indicate_tid);
timer_cnt_get(&timer_cnt);
indicate_flag[indicate_tid] = 0;
static struct __onoff_repo onoff_repo_set[10];
static u8 onoff_repo_set_cnt = 0;
if (onoff_repo_set_cnt < 9) {
onoff_repo_set_cnt ++;
} else {
onoff_repo_set_cnt = 0;
}
onoff_repo_set[onoff_repo_set_cnt].Opcode = buffer_head_init(VENDOR_MSG_ATTR_INDICAT);
onoff_repo_set[onoff_repo_set_cnt].TID = indicate_tid;
onoff_repo_set[onoff_repo_set_cnt].Attr_Type = 0x0100;
onoff_repo_set[onoff_repo_set_cnt].OnOff = led_flag;
comfirm_check_param[timer_cnt].resend_cnt = 0;
comfirm_check_param[timer_cnt].timer_cnt = timer_cnt;
comfirm_check_param[timer_cnt].indicate_tid = onoff_repo_set[onoff_repo_set_cnt].TID;
comfirm_check_param[timer_cnt].buf = &onoff_repo_set[onoff_repo_set_cnt];
comfirm_check_param[timer_cnt].len = sizeof(onoff_repo_set[onoff_repo_set_cnt]);
log_info(" set unack tid = %d, timer_cnt = %d, param.tid = %d ", onoff_repo_set[onoff_repo_set_cnt].TID, timer_cnt, comfirm_check_param[timer_cnt].indicate_tid);
vendor_attr_status_send(&vendor_server_models[0], ctx, &onoff_repo_set[onoff_repo_set_cnt], sizeof(onoff_repo_set[onoff_repo_set_cnt]));
timer_index[timer_cnt] = sys_timer_add(&comfirm_check_param[timer_cnt], comfirm_check, 400);
}
static void gen_onoff_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
log_info("gen_onoff_set\n");
gen_onoff_set_unack(model, ctx, buf);
gen_onoff_get(model, ctx, buf);
}
void lightness_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
log_info("lightness_get");
NET_BUF_SIMPLE_DEFINE(msg, 2 + 2 + 4);
struct light_state *light_state = model->rt->user_data;
log_info("addr 0x%04x actual lightness 0x%02x", bt_mesh_model_elem(model)->rt->addr, light_state->lightness_actual);
bt_mesh_model_msg_init(&msg, BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_STATUS);
buffer_add_le16_at_tail(&msg, light_state->lightness_actual);
if (bt_mesh_model_send(model, ctx, &msg, NULL/*&rsp_msg_cb*/, (void *)ctx->recv_dst)) {
log_info("Unable to send lightness Status response");
}
}
void lightness_set_unack(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
struct light_state *light_state = model->rt->user_data;
u16 lightness = buffer_pull_le16_from_head(buf);
log_info("light set to %d", lightness);
if (lightness > light_state->lightness_range_min) {
if (lightness < light_state->lightness_range_max) {
light_state->lightness_actual = lightness;
} else {
light_state->lightness_actual = light_state->lightness_range_max;
}
} else {
light_state->lightness_actual = light_state->lightness_range_min;
}
if (light_state->lightness_actual > 0) {
onoff_state[0].current = 1;
} else {
onoff_state[0].current = 0;
}
light_state->lightness_last = light_state->lightness_actual;
u32 bright = (light_state->lightness_actual) * 10000 / 65535;
set_led_duty(bright);
log_info("actual lightness set to %d\n", light_state->lightness_actual);
indicate_tid_get(&indicate_tid);
timer_cnt_get(&timer_cnt);
indicate_flag[indicate_tid] = 0;
static struct __set_bright set_bright[10];
static u8 bright_set_cnt = 0;
if (bright_set_cnt < 9) {
bright_set_cnt ++;
} else {
bright_set_cnt = 0;
}
set_bright[bright_set_cnt].Opcode = buffer_head_init(VENDOR_MSG_ATTR_INDICAT),
set_bright[bright_set_cnt].TID = indicate_tid,
set_bright[bright_set_cnt].Attr_Type = 0x0121,
set_bright[bright_set_cnt].lightness_actual = light_state->lightness_actual,
comfirm_check_param[timer_cnt].resend_cnt = 0;
comfirm_check_param[timer_cnt].timer_cnt = timer_cnt;
comfirm_check_param[timer_cnt].indicate_tid = set_bright[bright_set_cnt].TID;
comfirm_check_param[timer_cnt].buf = &set_bright[bright_set_cnt];
comfirm_check_param[timer_cnt].len = sizeof(set_bright[bright_set_cnt]);
vendor_attr_status_send(&vendor_server_models[0], ctx, &set_bright[bright_set_cnt], sizeof(set_bright[bright_set_cnt]));
timer_index[timer_cnt] = sys_timer_add(&comfirm_check_param[timer_cnt], comfirm_check, 400);
}
void lightness_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
log_info("lightness_set\n");
lightness_set_unack(model, ctx, buf);
lightness_get(model, ctx, buf);
}
static void timer_handler(struct __timer_param *param)
{
indicate_tid_get(&indicate_tid);
static struct __timer_success timer_success;
timer_success.Opcode = buffer_head_init(VENDOR_MSG_ATTR_INDICAT);
timer_success.TID = indicate_tid;
timer_success.Attr_Type = 0xf009;
timer_success.event = 0x11;
timer_success.index = param->index;
indicate_flag[timer_success.TID] = 0;
indicate_tid_get(&indicate_tid);
static struct __onoff_repo onoff_repo_handle;
onoff_repo_handle.Opcode = buffer_head_init(VENDOR_MSG_ATTR_INDICAT);
onoff_repo_handle.TID = indicate_tid;
onoff_repo_handle.Attr_Type = 0x0100;
onoff_repo_handle.OnOff = param->onoff;
indicate_flag[onoff_repo_handle.TID] = 0;
struct bt_mesh_msg_ctx ctx = {
.addr = 0xf000,
};
if (param->onoff == 1) {
log_info("onoff set lightness = %d", light.lightness_default);
set_led_duty(light.lightness_default);
} else {
set_led_duty(0);
}
led_flag = param->onoff;
log_info("timer_cnt = %d, timer_index = 0x%x, onoff = %d", param->timer_cnt, timer_index[param->timer_cnt], param->onoff);
sys_timer_remove(timer_index[param->timer_cnt]);
//timing success msg send and check comfirm
timer_cnt_get(&timer_cnt);
comfirm_check_param[timer_cnt].resend_cnt = 0;
comfirm_check_param[timer_cnt].timer_cnt = timer_cnt;
comfirm_check_param[timer_cnt].indicate_tid = indicate_tid;
comfirm_check_param[timer_cnt].buf = &timer_success;
comfirm_check_param[timer_cnt].len = sizeof(timer_success);
vendor_attr_status_send(&vendor_server_models[0], &ctx, &timer_success, sizeof(timer_success));
timer_index[timer_cnt] = sys_timer_add(&comfirm_check_param[timer_cnt], comfirm_check, 400);
//onoff_state msg send and check comfirm
timer_cnt_get(&timer_cnt);
comfirm_check_param[timer_cnt].resend_cnt = 0;
comfirm_check_param[timer_cnt].timer_cnt = timer_cnt;
comfirm_check_param[timer_cnt].indicate_tid = indicate_tid;
comfirm_check_param[timer_cnt].buf = &onoff_repo_handle;
comfirm_check_param[timer_cnt].len = sizeof(onoff_repo_handle);
vendor_attr_status_send(&vendor_server_models[0], &ctx, &onoff_repo_handle, sizeof(onoff_repo_handle));
timer_index[timer_cnt] = sys_timer_add(&comfirm_check_param[timer_cnt], comfirm_check, 400);
}
static void set_timer_start(u8 tid,
u32 delay_s,
u8 index,
bool onoff)
{
timer_cnt_get(&timer_cnt);
timer_param[timer_cnt].tid = tid;
timer_param[timer_cnt].index = index;
timer_param[timer_cnt].onoff = onoff;
timer_param[timer_cnt].timer_cnt = timer_cnt;
log_info("time_cnt = %d\r\n", timer_cnt);
timer_index[timer_cnt] = sys_timer_add(&timer_param[timer_cnt], timer_handler, delay_s * 1000);
log_info("timer set delay %d second\r\n", delay_s);
}
static void vendor_attr_cfm(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
u8 cfm_tid = buffer_pull_u8_from_head(buf);
indicate_flag[cfm_tid] = 1;
log_info("receice vendor_attr_confirm, indicate_tid = %d\r\n", cfm_tid);
}
static void vendor_attr_get(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
log_info("receive vendor_attr_get, len except opcode =0x%x", buf->len);
log_info_hexdump(buf->data, buf->len);
}
static void vendor_attr_set(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
log_info("receive vendor_attr_set, len except opcode =0x%x", buf->len);
log_info_hexdump(buf->data, buf->len);
static struct UTC_TIME cur_utc;
u8 tid = buffer_pull_u8_from_head(buf);
u16 Attr_Type = buffer_pull_le16_from_head(buf);
switch (Attr_Type) {
case ATTR_TYPE_UNIX_TIME: {
u32 time = buffer_pull_le32_from_head(buf);
cur_utc = unix32_to_UTC_beijing(time);
log_info("\n __unix_time BeiJing time: %d/%d/%d %02d:%02d:%02d, weekday %d \n",
cur_utc.year, cur_utc.month, cur_utc.day,
cur_utc.hour, cur_utc.minute, cur_utc.second,
cur_utc.weekday);
struct __unix_time unix_time = {
.Opcode = buffer_head_init(VENDOR_MSG_ATTR_STATUS),
.TID = tid,
.Attr_Type = Attr_Type,
.time = time,
};
vendor_attr_status_send(model, ctx, &unix_time, sizeof(unix_time));
}
break;
case ATTR_TYPE_SET_TIMEOUT: {
u8 index = buffer_pull_u8_from_head(buf);
u32 time = buffer_pull_le32_from_head(buf);
u16 attr_type = buffer_pull_le16_from_head(buf);
u8 attr_para = buffer_pull_u8_from_head(buf);
struct UTC_TIME set_cur_utc = unix32_to_UTC_beijing(time);
log_info("\n __set_timeout BeiJinG time: %d/%d/%d %02d:%02d:%02d, weekday %d\n",
set_cur_utc.year, set_cur_utc.month, set_cur_utc.day,
set_cur_utc.hour, set_cur_utc.minute, set_cur_utc.second,
set_cur_utc.weekday);
u32 delay_s = ((set_cur_utc.hour - cur_utc.hour) * 3600) + ((set_cur_utc.minute - cur_utc.minute) * 60) + (set_cur_utc.second - cur_utc.second);
log_info("\n delay_s = %d, switch set to %d\n", delay_s, attr_para);
set_timer_start(tid, delay_s, index, attr_para);
struct __set_timeout set_timeout = {
.Opcode = buffer_head_init(VENDOR_MSG_ATTR_STATUS),
.TID = tid,
.Attr_Type = Attr_Type,
.param[0] = {
.index = index,
.time = time,
.attr_type = attr_type,
.attr_para = attr_para,
},
};
vendor_attr_status_send(model, ctx, &set_timeout, sizeof(set_timeout));
}
break;
case ATTR_TYPE_SET_PERIOD_TIMEOUT: {
u8 index = buffer_pull_u8_from_head(buf);
u16 _24h_timer = buffer_pull_le16_from_head(buf);
u8 schedule = buffer_pull_u8_from_head(buf);
u16 attr_type = buffer_pull_le16_from_head(buf);
u8 attr_para = buffer_pull_u8_from_head(buf);
log_info("\n set_period_timeout BeiJinG time: %02d:%02d, weekday %d\n",
(_24h_timer & 0x0fff) / 60, (_24h_timer & 0x0fff) % 60, schedule);
struct __set_period_timeout set_period_timeout = {
.Opcode = buffer_head_init(VENDOR_MSG_ATTR_STATUS),
.TID = tid,
.Attr_Type = Attr_Type,
.period_param[0] = {
.index = index,
._24h_timer = _24h_timer,
.schedule = schedule,
.attr_type = attr_type,
.attr_para = attr_para,
},
};
log_info("\n time = 0x%x\n", _24h_timer);
log_info("\n set_period opcode = 0x%x, Attr_Type = 0x%x, para = 0x%x, index = 0x%x \n", set_period_timeout.Opcode, Attr_Type, attr_para, index);
vendor_attr_status_send(model, ctx, &set_period_timeout, sizeof(set_period_timeout));
}
break;
case ATTR_TYPE_DELETE_TIMEOUT: {
u8 index = buffer_pull_u8_from_head(buf);
struct __delete_time delete_time = {
.Opcode = buffer_head_init(VENDOR_MSG_ATTR_STATUS),
.TID = tid,
.Attr_Type = Attr_Type,
.index = index,
};
log_info("delete timeout for index = 0x%x\r\n", index);
vendor_attr_status_send(model, ctx, &delete_time, sizeof(delete_time));
}
break;
default :
log_info("\n\n\n\n default attr = 0x%x \n\n\n\n", Attr_Type);
break;
}
}
/*
* @brief OnOff Model Server Op Dispatch Table
*/
/*-----------------------------------------------------------*/
static const struct bt_mesh_model_op gen_onoff_srv_op[] = {
{ BT_MESH_MODEL_OP_GEN_ONOFF_GET, 0, gen_onoff_get },
{ BT_MESH_MODEL_OP_GEN_ONOFF_SET, 2, gen_onoff_set },
{ BT_MESH_MODEL_OP_GEN_ONOFF_SET_UNACK, 2, gen_onoff_set_unack },
BT_MESH_MODEL_OP_END,
};
/*
* @brief Vendor Model Server Op Dispatch Table
*/
/*-----------------------------------------------------------*/
static const struct bt_mesh_model_op vendor_srv_op[] = {
{ VENDOR_MSG_ATTR_GET, ACCESS_OP_SIZE, vendor_attr_get },
{ VENDOR_MSG_ATTR_SET, ACCESS_OP_SIZE, vendor_attr_set },
{ VENDOR_MSG_ATTR_CONFIRM, 1, vendor_attr_cfm },
BT_MESH_MODEL_OP_END,
};
const struct bt_mesh_model_op light_lightness_srv_op[] = {
{ BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_GET, 0, lightness_get},
{ BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET, 0, lightness_set},
{ BT_MESH_MODEL_OP_LIGHT_LIGHTNESS_SET_UNACK, 0, lightness_set_unack},
BT_MESH_MODEL_OP_END,
};
/*
* @brief Element Model Declarations
*
* Element 0 Root Models
*/
/*-----------------------------------------------------------*/
struct bt_mesh_model root_models[] = {
BT_MESH_MODEL_CFG_SRV,
BT_MESH_MODEL(BT_MESH_MODEL_ID_GEN_ONOFF_SRV, gen_onoff_srv_op, &gen_onoff_pub_srv, &onoff_state[0]),
BT_MESH_MODEL(BT_MESH_MODEL_ID_LIGHT_LIGHTNESS_SRV, light_lightness_srv_op, &gen_onoff_pub_srv, &light),
};
struct bt_mesh_model vendor_server_models[] = {
BT_MESH_MODEL_VND(BT_COMP_ID_LF, BT_MESH_VENDOR_MODEL_ID_SRV, vendor_srv_op, NULL, &onoff_state[0]),
};
/*
* @brief LED to Server Model Assigmnents
*/
/*-----------------------------------------------------------*/
static struct bt_mesh_model *mod_srv_sw[] = {
&root_models[1],
};
/*
* @brief Root and Secondary Element Declarations
*/
/*-----------------------------------------------------------*/
static struct bt_mesh_elem elements[] = {
BT_MESH_ELEM(0, root_models, vendor_server_models),
};
static const struct bt_mesh_comp composition = {
.cid = BT_COMP_ID_LF,
.elem = elements,
.elem_count = ARRAY_SIZE(elements),
};
/*
* @brief AliGenie UUID格式
*
* detail on https://www.aligenie.com/doc/357554/gtgprq <表1Device UUID格式>
*/
/*-----------------------------------------------------------*/
static const u8 dev_uuid[16] = {
0xA8, 0x01, // CID
0x01 | BIT(4) | BIT(6), // PID
PID_TO_LITTLE_ENDIAN(PRODUCT_ID), // ProductID
MAC_TO_LITTLE_ENDIAN(CUR_DEVICE_MAC_ADDR), // MAC
BIT(1), // FeatureFlag
0x00, 0x00 // RFU
};
/*
* @brief AliGenie 设备配网流程
*
* detail on https://www.aligenie.com/doc/357554/gtgprq
*/
/*-----------------------------------------------------------*/
static u8 auth_data[16];
static void hex_to_string(void *in, int in_len, void *out, int out_len)
{
static const char hex[] = "0123456789abcdef";
u8 *a = in;
u8 *b = out;
int i;
if (in_len > (out_len / 2)) {
log_error("\"error: in_len > (out_len / 2)\"");
return;
}
for (i = 0; i < in_len; i++) {
b[i * 2] = hex[a[i] >> 4];
b[i * 2 + 1] = hex[a[i] & 0xf];
}
}
static void static_auth_value_calculate(void)
{
log_info("--func=%s", __FUNCTION__);
const u8 Product_ID[] = {
PID_TO_BIG_ENDIAN(PRODUCT_ID)
};
const u8 Mac_Address[] = {
MAC_TO_BIG_ENDIAN(CUR_DEVICE_MAC_ADDR)
};
const u8 Secret[] = {
DEVICE_SECRET
};
u8 string_buf[PRODUCT_ID_STRING_SIZE + MAC_ADDRESS_STRING_SIZE + SECRET_STRING_SIZE + 2];
u8 *string_p = string_buf;
u8 digest[SHA256_DIGEST_SIZE];
//< Product ID
hex_to_string(Product_ID, sizeof(Product_ID), string_p, PRODUCT_ID_STRING_SIZE);
string_p += PRODUCT_ID_STRING_SIZE;
*string_p++ = ',';
//< Mac Address
hex_to_string(Mac_Address, sizeof(Mac_Address), string_p, MAC_ADDRESS_STRING_SIZE);
string_p += MAC_ADDRESS_STRING_SIZE;
*string_p++ = ',';
//< Secert
memcpy(string_p, Secret, SECRET_STRING_SIZE);
sha256Compute(string_buf, sizeof(string_buf), digest);
memcpy(auth_data, digest, sizeof(auth_data));
log_info("string_buf : %s", string_buf);
log_info_hexdump(string_buf, sizeof(string_buf));
log_info_hexdump(auth_data, sizeof(auth_data));
}
static const struct bt_mesh_prov prov = {
.uuid = dev_uuid,
.static_val = auth_data,
.static_val_len = sizeof(auth_data),
.output_size = 0,
.output_actions = 0,
.output_number = 0,
.complete = prov_complete,
.reset = prov_reset,
};
/*
* @brief Button Pressed Worker Task
*/
/*-----------------------------------------------------------*/
static bool server_publish(struct _switch *sw)
{
struct bt_mesh_model *mod_srv;
struct bt_mesh_model_pub *pub_srv;
mod_srv = mod_srv_sw[sw->sw_num];
pub_srv = mod_srv->pub;
/* If unprovisioned, just call the set function.
* The intent is to have switch-like behavior
* prior to provisioning. Once provisioned,
* the button and its corresponding led are no longer
* associated and act independently. So, if a button is to
* control its associated led after provisioning, the button
* must be configured to either publish to the led's unicast
* address or a group to which the led is subscribed.
*/
u16 primary_addr = get_primary_addr();
if (primary_addr == BT_MESH_ADDR_UNASSIGNED) {
NET_BUF_SIMPLE_DEFINE(msg, 1);
struct bt_mesh_msg_ctx ctx = {
.addr = sw->sw_num + primary_addr,
};
/* This is a dummy message sufficient
* for the led server
*/
buffer_add_u8_at_tail(&msg, sw->onoff_state);
gen_onoff_set_unack(mod_srv, &ctx, &msg);
return TRUE;
}
return FALSE;
}
static void button_pressed_worker(struct _switch *sw)
{
if (sw->sw_num >= composition.elem_count) {
log_info("sw_num over elem_count");
return;
}
if (server_publish(sw)) {
return;
}
}
void led_set(void)
{
led_flag = !led_flag;
indicate_tid_get(&indicate_tid);
timer_cnt_get(&timer_cnt);
if (led_flag == 1) {
log_info("onoff set lightness = %d", light.lightness_default);
set_led_duty(light.lightness_default);
} else {
set_led_duty(0);
}
log_info("state set to %d, indicate_tid now = %d\r\n", led_flag, indicate_tid);
if (!bt_mesh_is_provisioned()) {
log_info("no provision, not send state indicate\r\n");
return;
}
struct bt_mesh_msg_ctx ctx = {
.addr = 0xf000,
};
static struct __onoff_repo onoff_repo;
onoff_repo.Opcode = buffer_head_init(VENDOR_MSG_ATTR_INDICAT);
onoff_repo.TID = indicate_tid;
onoff_repo.Attr_Type = 0x0100;
onoff_repo.OnOff = led_flag;
indicate_flag[indicate_tid] = 0;
comfirm_check_param[timer_cnt].resend_cnt = 0;
comfirm_check_param[timer_cnt].timer_cnt = timer_cnt;
comfirm_check_param[timer_cnt].indicate_tid = indicate_tid;
comfirm_check_param[timer_cnt].buf = &onoff_repo;
comfirm_check_param[timer_cnt].len = sizeof(onoff_repo);
log_info(" vendor &model = 0x%x, vendor model = 0x%x", &vendor_server_models[0], vendor_server_models[0]);
timer_index[timer_cnt] = sys_timer_add(&comfirm_check_param[timer_cnt], comfirm_check, 400);
}
void iot_reset()
{
bt_mesh_reset();
p33_soft_reset();
}
void input_key_handler(u8 key_status, u8 key_number)
{
struct _switch press_switch;
log_info("key_number=0x%x", key_number);
if ((key_number == 2) && (key_status == KEY_EVENT_CLICK)) {
log_info("\n <bt_mesh_reset> \n");
struct __indicate_msg HardReset_msg = {
.Opcode = buffer_head_init(VENDOR_MSG_ATTR_INDICAT),
.TID = indicate_tid,
.Attr_Type = 0xf009,
.Event = 0x23,
};
struct bt_mesh_msg_ctx ctx = {
.addr = 0xf000,
};
vendor_attr_status_send(&vendor_server_models[0], &ctx, &HardReset_msg, sizeof(HardReset_msg));
sys_timer_add(NULL, iot_reset, 1000 * 3);
return;
}
if ((key_number == 0) && (key_status == KEY_EVENT_CLICK)) {
led_set();
return;
}
switch (key_status) {
case KEY_EVENT_CLICK:
log_info(" [KEY_EVENT_CLICK] ");
press_switch.sw_num = key_number;
press_switch.onoff_state = 1;
button_pressed_worker(&press_switch);
break;
case KEY_EVENT_LONG:
log_info(" [KEY_EVENT_LONG] ");
press_switch.sw_num = key_number;
press_switch.onoff_state = 0;
button_pressed_worker(&press_switch);
break;
case KEY_EVENT_HOLD:
log_info(" [KEY_EVENT_HOLD] ");
break;
default :
return;
}
}
static void period_msg(void *empty)
{
struct bt_mesh_msg_ctx ctx = {
.addr = 0xf000,
};
struct __onoff_repo period_indicat = {
.Opcode = buffer_head_init(VENDOR_MSG_ATTR_INDICAT),
.TID = indicate_tid,
.Attr_Type = 0x0100, //设备开关状态与generic onoff绑定
.OnOff = led_flag,
};
if (bt_mesh_is_provisioned()) {
vendor_attr_status_send(&vendor_server_models[0], &ctx, &period_indicat, sizeof(period_indicat));
}
}
void iot_init()
{
if (bt_mesh_is_provisioned()) {
indicate_tid_get(&indicate_tid);
struct bt_mesh_msg_ctx ctx = {
.addr = 0xf000,
};
struct __onoff_repo state_msg = {
.Opcode = buffer_head_init(VENDOR_MSG_ATTR_INDICAT),
.TID = indicate_tid,
.Attr_Type = 0x0100, //设备开关状态与generic onoff绑定
.OnOff = led_flag,
};
vendor_attr_status_send(&vendor_server_models[0], &ctx, &state_msg, sizeof(state_msg));
indicate_tid_get(&indicate_tid);
struct __indicate_msg indicate_msg = {
.Opcode = buffer_head_init(VENDOR_MSG_ATTR_INDICAT),
.TID = indicate_tid,
.Attr_Type = 0xf009,
.Event = 0x03,
};
vendor_attr_status_send(&vendor_server_models[0], &ctx, &indicate_msg, sizeof(indicate_msg));
}
sys_timer_add(NULL, period_msg, 180 * 1000);
}
static void aligenie_app_key_set(u16_t app_key)
{
log_info("aligenie_app_key_set");
mesh_mod_bind(root_models[1], app_key);
mesh_mod_bind(root_models[2], app_key);
mesh_mod_bind(vendor_server_models[0], app_key);
}
static void aligenie_sub_set(struct bt_mesh_model *mod, u16_t sub_addr)
{
int i = 0;
for (i = 0; i < ARRAY_SIZE(mod->groups); i++) {
if (mod->groups[i] == BT_MESH_ADDR_UNASSIGNED) {
mod->groups[i] = sub_addr;
break;
}
}
if (i != ARRAY_SIZE(mod->groups)) {
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
bt_mesh_model_sub_store(mod);
}
if (IS_ENABLED(CONFIG_BT_MESH_LOW_POWER)) {
bt_mesh_lpn_group_add(sub_addr);
}
}
}
static void aligenie_configuration()
{
aligenie_sub_set(&root_models[1], ALIGENIE_SUB_ADDR_1);
aligenie_sub_set(&root_models[1], ALIGENIE_SUB_ADDR_2);
aligenie_sub_set(&root_models[2], ALIGENIE_SUB_ADDR_1);
aligenie_sub_set(&root_models[2], ALIGENIE_SUB_ADDR_2);
aligenie_sub_set(&vendor_server_models[0], ALIGENIE_SUB_ADDR_1);
aligenie_sub_set(&vendor_server_models[0], ALIGENIE_SUB_ADDR_2);
}
/*
* @brief Mesh Profile Setup
*/
/*-----------------------------------------------------------*/
static void mesh_init(void)
{
log_info("--func=%s", __FUNCTION__);
static_auth_value_calculate();
bt_conn_cb_register(bt_conn_get_callbacks());
int err = bt_mesh_init(&prov, &composition);
if (err) {
log_error("Initializing mesh failed (err %d)\n", err);
return;
}
if (IS_ENABLED(CONFIG_BT_SETTINGS)) {
settings_load();
}
bt_mesh_prov_enable(BT_MESH_PROV_GATT | BT_MESH_PROV_ADV);
#if defined(CONFIG_CPU_BD19)
timer_pwm_init(JL_TIMER0, IO_PORTB_07, 10000, 0);
#elif defined (CONFIG_CPU_BR25)
timer_pwm_init(JL_TIMER5, 10000, 0, IO_PORTB_07, 0);
#else
log_info("\nThis board had not set up timer_pwm\n");
#endif
mesh_app_key_add_callback_register(aligenie_app_key_set);
aligenie_configuration();
iot_init(); //indicate_state
}
void bt_ble_init(void)
{
u8 bt_addr[6] = {MAC_TO_LITTLE_ENDIAN(CUR_DEVICE_MAC_ADDR)};
bt_mac_addr_set(bt_addr);
mesh_setup(mesh_init);
}
#endif /* (CONFIG_MESH_MODEL == SIG_MESH_ALIGENIE_LIGHT) */