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

572 lines
18 KiB
C

#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-TUYA]"
#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_TUYA_LIGHT)
static int tuya_update(struct bt_mesh_model *mod);
/*
* @brief Config current node features(Relay/Proxy/Friend/Low Power)
*/
/*-----------------------------------------------------------*/
#define BT_MESH_FEAT_SUPPORTED_TEMP ( \
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 'T', 'Y', '-', '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 CATEGORY_TO_LITTLE_ENDIAN(x) \
((x >> 8) & 0xff), \
(x & 0xff)
#define CUR_DEVICE_MAC_ADDR 0x112233445566
#define TUYA_CATEGORY 0x1011
#define PRODUCT_ID 'y', 'j', 'f', 's', '5', '0', '6', 'f'
/*
* @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, tuya_update, 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 0xD007 // Alibaba
#define BT_PRODUCT_ID_LF 0x0000 // PID
#define BT_VERSION_ID_LF 0x3533 // VERSION
#define BT_MESH_VENDOR_MODEL_ID_SRV 0x07D00004 // Tuya vendor model
/*
* @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_CMD_ATTR_WRITE BT_MESH_MODEL_OP_3(0xC9, BT_COMP_ID_LF)
#define VENDOR_CMD_ATTR_WRITE_UNACK BT_MESH_MODEL_OP_3(0xCA, BT_COMP_ID_LF)
#define VENDOR_CMD_ATTR_SET_READ BT_MESH_MODEL_OP_3(0xCC, BT_COMP_ID_LF)
#define VENDOR_CMD_ATTR_STATUS BT_MESH_MODEL_OP_3(0xCB, BT_COMP_ID_LF)
#define VENDOR_CMD_ATTR_DATA BT_MESH_MODEL_OP_3(0xCD, BT_COMP_ID_LF)
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,
};
static bool led_switch = 0;
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,
};
static int tuya_update(struct bt_mesh_model *mod)
{
log_info("tuya_update, mod = 0x%x", mod);
}
/*
* @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, */
// };
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\n",
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);
if (bt_mesh_model_send(model, ctx, &msg, NULL/*&rsp_msg_cb*/, (void *)ctx->recv_dst)) {
log_info("Unable to send On Off Status response\n");
}
}
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\n", bt_mesh_model_elem(model)->rt->addr, onoff_state->current);
/* log_info_hexdump((u8 *)onoff_state, sizeof(*onoff_state)); */
gpio_pin_write(onoff_state->led_gpio_pin,
onoff_state->current);
led_switch = onoff_state->current;
}
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\n");
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\n", 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\n");
}
}
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;
light_state->lightness_actual = buffer_pull_le16_from_head(buf);
log_info("light set to %d", light_state->lightness_actual);
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) * 100 / 65535;
//need to make a func to set bright here
log_info("actual lightness set to %d%%\n", bright);
}
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);
}
void vendor_cmd_test(struct bt_mesh_model *model,
struct bt_mesh_msg_ctx *ctx,
struct net_buf_simple *buf)
{
log_info("receive tuya_vendor_msg, len except opcode =0x%x", buf->len);
printf_buf(buf->data, buf->len);
}
/*
* @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_CMD_ATTR_WRITE, ACCESS_OP_SIZE, vendor_cmd_test},
{ VENDOR_CMD_ATTR_WRITE_UNACK, ACCESS_OP_SIZE, vendor_cmd_test},
{ VENDOR_CMD_ATTR_SET_READ, ACCESS_OP_SIZE, vendor_cmd_test},
{ VENDOR_CMD_ATTR_STATUS, ACCESS_OP_SIZE, vendor_cmd_test},
{ VENDOR_CMD_ATTR_DATA, ACCESS_OP_SIZE, vendor_cmd_test},
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,
.pid = BT_PRODUCT_ID_LF,
.vid = BT_VERSION_ID_LF,
.elem = elements,
.elem_count = ARRAY_SIZE(elements),
};
static const u8 dev_uuid[16] = {
MAC_TO_BIG_ENDIAN(CUR_DEVICE_MAC_ADDR), // MAC
CATEGORY_TO_LITTLE_ENDIAN(TUYA_CATEGORY), // mesh category
PRODUCT_ID, // ProductID
};
static const struct bt_mesh_prov prov = {
.uuid = dev_uuid,
.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 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");
bt_mesh_reset();
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;
}
}
/*
* @brief Mesh Profile Setup
*/
/*-----------------------------------------------------------*/
static void mesh_init(void)
{
log_info("--func=%s", __FUNCTION__);
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);
}
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_TUYA_LIGHT) */