智能烧水壶 (Bluetooth版)04——云端控制篇

Posted 三明治开发社区

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了智能烧水壶 (Bluetooth版)04——云端控制篇相关的知识,希望对你有一定的参考价值。

1.配网状态检测

要实现和云端进行数据交互,首先要使设备配网。根据功能简述中介绍可知,我们需要先进行以下配网检测处理:

设备状态执行动作绿灯提示
上电时,检测到已被用户绑定不等待配网不闪烁
上电时,检测到未被用户绑定开始等待配网开始闪烁
长按保温键5秒时,检测到未被用户绑定开始等待配网开始闪烁
开始等待配网后3分钟内,检测到已被用户连接停止等待配网停止闪烁
开始等待配网3分钟后,检测到仍未被用户绑定停止等待配网,停止蓝牙广播停止闪烁

下面我们介绍配网状态检测与处理的实现过程。

a. 在上电初始化时,先对蓝牙连接状态进行判断和标记:

/* 蓝牙连接状态初始化 */
static void ble_connect_status_init(void)
{
    tuya_ble_connect_status_t ble_conn_sta;
	/* 使用TUYA BLE SDK提供的API获取当前蓝牙连接状态 */
    ble_conn_sta = tuya_ble_connect_status_get();
    TUYA_APP_LOG_DEBUG("ble connect status: %d", ble_conn_sta);
	/* 判断与标记 */
    if ((ble_conn_sta == BONDING_UNCONN) ||
        (ble_conn_sta == BONDING_CONN)   ||
        (ble_conn_sta == BONDING_UNAUTH_CONN)) {
        F_BLE_BONDING = SET;					/* 标记为已绑定 */
        F_WAIT_BLE_CONN = CLR;					/* 标记为无需等待 */
        set_led_green_mode(LED_MODE_FIX);		/* 绿灯不闪烁 */
    } else {
        F_BLE_BONDING = CLR;					/* 标记为未绑定 */
        F_WAIT_BLE_CONN = SET;					/* 标记为开始等待 */
        set_led_green_mode(LED_MODE_TWINKLE);	/* 绿灯开始闪烁 */
    }
}

b. 在主循环中执行等待蓝牙连接与3分钟计时时间达到后关闭蓝牙广播的过程:

#define TIME_ALLOW_CONNECT      (3*60*1000) /* 3min */
static uint32_t sg_ble_tm = 0;
FLAG_BIT g_kettle_flag;
    #define F_BLE_BONDING       g_kettle_flag.bit0
    #define F_WAIT_BLE_CONN     g_kettle_flag.bit1

/* 等待蓝牙连接 */
static void wait_ble_connect(void)
{
	/* 3分钟定时 */
    if (!clock_time_exceed(sg_ble_tm, TIME_ALLOW_CONNECT*1000)) {
        return;
    }
    F_WAIT_BLE_CONN = CLR;                  /* 关闭等待连接标志 */
    set_led_green_mode(LED_MODE_FIX);       /* 绿灯停止闪烁 */
    bls_ll_setAdvEnable(0);                 /* 关闭蓝牙广播 */
}

/* 更新蓝牙状态 */
static void update_ble_status(void)
{
    if (F_BLE_BONDING == CLR) {				/* 未绑定? */
        if (F_WAIT_BLE_CONN == SET) {		/* 等待连接标志打开? */
            wait_ble_connect();				/* 等待蓝牙连接 */
        }
    }
}

c. 在蓝牙连接状态发生改变时做如下处理:

/* 蓝牙连接状态改变时的处理函数 */
void tuya_app_kettle_ble_connect_status_change_handler(tuya_ble_connect_status_t status)
{
    if (status == BONDING_CONN) {               /* 蓝牙已连接? */
        report_all_dp_data();                   /* 上报所有DP数据,保证APP显示与设备状态一致 */
        if (F_WAIT_BLE_CONN == SET) {           /* 等待连接标志打开? */
            F_BLE_BONDING = SET;                /* 标记为已绑定 */
            F_WAIT_BLE_CONN = CLR;              /* 停止配网等待 */
            set_led_green_mode(LED_MODE_FIX);   /* 停止闪烁 */
        }
    }
    if (status == UNBONDING_UNCONN) {           /* 设备被解绑? */
        F_BLE_BONDING = CLR;                    /* 标记为未绑定 */
        bls_ll_setAdvEnable(0);                 /* 停止蓝牙广播 */
    }
}

/* 处理BLE SDK消息的callback函数 [tuya_ble_app_demo.c] */
static void tuya_cb_handler(tuya_ble_cb_evt_param_t* event)
{
	...
    case TUYA_BLE_CB_EVT_CONNECTE_STATUS:	/* 在状态改变事件发生时调用 */
        tuya_app_kettle_ble_connect_status_change_handler(event->connect_status);
        TUYA_APP_LOG_INFO("received tuya ble conncet status update event, current connect status = %d", event->connect_status);
        break;
    ...
}

d. 保温键长按5秒时,重新尝试等待配网的执行函数如下:

/* 尝试配网 */
static void try_to_connect_ble(void)
{
    F_WAIT_BLE_CONN = SET;                  /* 打开等待连接标志 */
    set_led_green_mode(LED_MODE_TWINKLE);   /* 绿灯闪烁 */
    bls_ll_setAdvEnable(1);                 /* 打开蓝牙广播 */
    sg_ble_tm = clock_time();               /* 记录当前时间 */
}

2.本地数据上报

在设备配网后,就可以使用APP来控制设备和查看设备上报的数据,下面是数据上报的实现过程。

/* DP ID */
#define DP_ID_BOIL              101
#define DP_ID_KEEP_WARM         102
#define DP_ID_TEMP_CUR          103
#define DP_ID_TEMP_SET          104
#define DP_ID_WATER_TYPE        105
#define DP_ID_FAULT             106
/* DP TYPE */
#define DP_TYPE_BOIL            DT_BOOL
#define DP_TYPE_KEEP_WARM       DT_BOOL
#define DP_TYPE_TEMP_CUR        DT_VALUE
#define DP_TYPE_TEMP_SET        DT_VALUE
#define DP_TYPE_WATER_TYPE      DT_ENUM
#define DP_TYPE_FAULT           DT_ENUM

/* 用于数据上报 */
typedef struct {
    uint8_t id;		/* DP点ID */
    dp_type type;	/* DP点数据类型 */
    uint8_t len;	/* DP点数据长度 */
    uint8_t value;	/* DP点数据 */
} DP_DATA_T;

/* 用于存储烧水壶模式和DP点参数 */
typedef struct {
    MODE_E mode;			/* 模式 */
    uint8_t boil_turn;		/* 煮沸开/关 */
    uint8_t temp_cur;		/* 当前温度 */
    uint8_t temp_set;		/* 保温温度 */
    uint8_t keep_warm_turn;	/* 保温开/关 */
    WATER_TYPE_E water_type;/* 用水类型 */
    FAULT_E fault;			/* 故障 */
} KETTLE_T;
KETTLE_T g_kettle;

/* 获取DP点类型 */
static uint8_t get_dp_type(uint8_t dp_id)
{
    dp_type type = 0;
    switch (dp_id) {
    case DP_ID_BOIL:
        type = DP_TYPE_BOIL;
        break;
    case DP_ID_TEMP_CUR:
        type = DP_TYPE_TEMP_CUR;
        break;
    case DP_ID_TEMP_SET:
        type = DP_TYPE_TEMP_SET;
        break;
    case DP_ID_KEEP_WARM:
        type = DP_TYPE_KEEP_WARM;
        break;
    case DP_ID_WATER_TYPE:
        type = DP_TYPE_WATER_TYPE;
        break;
    case DP_ID_FAULT:
        type = DP_TYPE_FAULT;
        break;
    default:
        break;
    }
    return type;
}

/* 上报一个DP点 */
static void report_one_dp_data(uint8_t dp_id, uint8_t dp_value)
{
    DP_DATA_T dp_data_s;
    dp_data_s.id = dp_id;
    dp_data_s.type = get_dp_type(dp_id);
    dp_data_s.len = 0x01;
    dp_data_s.value = dp_value;
    /* 使用TUYA BLE SDK提供的API上报DP点数据 */
    tuya_ble_dp_data_report((uint8_t *)&dp_data_s, sizeof(DP_DATA_T));
}

/* 上报所有DP点 */
static void report_all_dp_data(void)
{
    report_one_dp_data(DP_ID_BOIL, g_kettle.boil_turn);
    report_one_dp_data(DP_ID_KEEP_WARM, g_kettle.keep_warm_turn);
    report_one_dp_data(DP_ID_TEMP_CUR, g_kettle.temp_cur);
    report_one_dp_data(DP_ID_TEMP_SET, g_kettle.temp_set);
    report_one_dp_data(DP_ID_WATER_TYPE, g_kettle.water_type);
    report_one_dp_data(DP_ID_FAULT, g_kettle.fault);
}

3.接收数据处理

在App上改变设备状态时,会从云端下发控制数据,设备在接收到数据后进行如下处理,即可实现云端任务。

/* 设置煮沸功能开/关 */
static void set_boil_turn(uint8_t on_off)
{
    if (g_kettle.fault != FAULT_NORMAL) {	/* 故障发生时不执行 */
        return;
    }
    g_kettle.boil_turn = on_off;			/* 煮沸功能打开/关闭 */
    report_one_dp_data(DP_ID_BOIL, g_kettle.boil_turn);
    TUYA_APP_LOG_DEBUG("boil turn: %d", g_kettle.boil_turn);
    set_led_red(on_off);					/* 红色LED点亮/关闭 */
}

/* 设置保温功能开/关 */
static void set_keep_warm_turn(uint8_t on_off)
{
    if (g_kettle.fault != FAULT_NORMAL) {	/* 故障发生时不执行 */
        return;
    }
    g_kettle.keep_warm_turn = on_off;		/* 保温功能打开/关闭 */
    report_one_dp_data(DP_ID_KEEP_WARM, g_kettle.keep_warm_turn);
    TUYA_APP_LOG_DEBUG("keep warm turn: %d", g_kettle.keep_warm_turn);
    set_led_orange(on_off);					/* 橙色LED点亮/关闭 */
}

/* 设置保温温度 */
static void set_keep_warm_temp(uint8_t temp)
{
    if (g_kettle.temp_set != temp) {
        if ((temp >= 45) && (temp <= 90)) {
            g_kettle.temp_set = temp;	/* 在保温温度设定范围内时才更新参数值 */
        }
    }
}

/* 设置用水类型 */
static void set_water_type(WATER_TYPE_E type)
{
    g_kettle.water_type = type;
}

/* DP点数据接收处理 */
void tuya_app_kettle_dp_data_handler(uint8_t *dp_data)
{
    switch (dp_data[0]) {
    case DP_ID_BOIL:
        set_boil_turn(dp_data[3]);
        *(dp_data + 3) = g_kettle.boil_turn;
        break;
    case DP_ID_KEEP_WARM:
        set_keep_warm_turn(dp_data[3]);
        *(dp_data + 3) = g_kettle.keep_warm_turn;
        break;
    case DP_ID_TEMP_SET:
        set_keep_warm_temp(dp_data[6]);
        *(dp_data + 6) = g_kettle.temp_set;	/* VALUE类型下发时数据长度为4字节 */
        break;
    case DP_ID_WATER_TYPE:
        set_water_type(dp_data[3]);
        *(dp_data + 3) = g_kettle.water_type;
        break;
    case DP_ID_TEMP_CUR:
    case DP_ID_FAULT:
    default:
        break;
    }
}

/* 处理BLE SDK消息的callback函数 [tuya_ble_app_demo.c] */
static void tuya_cb_handler(tuya_ble_cb_evt_param_t* event)
{
	...
    case TUYA_BLE_CB_EVT_DP_WRITE:	/* 在接收到DP数据时调用 */
        dp_data_len = event->dp_write_data.data_len;
        memset(dp_data_array, 0, sizeof(dp_data_array));
        memcpy(dp_data_array, event->dp_write_data.p_data, dp_data_len);        
        tuya_app_kettle_dp_data_handler(dp_data_array);
        TUYA_APP_LOG_HEXDUMP_DEBUG("received dp write data :", dp_data_array, dp_data_len);
        sn = 0;
        tuya_ble_dp_data_report(dp_data_array, dp_data_len);
        break;
	...
}

4.预约功能实现

预约功能可通过云定时功能实现,即在涂鸦IoT平台上产品开发的功能定义页中,打开高级云功能的云定时功能:

打开云定时功能后,还需在设备面板中修改云定时功能的属性,配置煮沸这个dp点,就可以在APP中设置定时煮沸任务,到达定时时间后云端就会下发控制命令触发dp_boil这个dp点数据下发,从而触发烧水壶执行煮沸功能。

小结

​ 至此智能恒温烧水壶就完成了,它可以APP控制,按键控制。具有水质模式切换,保温温度设定,定时烧水,故障告警等功能。在这款智能烧水壶的基础上还有很多功能可以深入开发,使体验更加人性化,智能化。同时您可以基于涂鸦 IoT 平台丰富它的功能,也可以更加方便的搭建更多智能产品原型,加速智能产品的开发流程。

以上是关于智能烧水壶 (Bluetooth版)04——云端控制篇的主要内容,如果未能解决你的问题,请参考以下文章

智能烧水壶(Bluetooth LE版)——硬件设计篇

智能烧水壶 (Bluetooth 版)02——系统创建篇

涂鸦智能烧水壶软件实现之云端控制(完结)

智能烧水壶(WIFI版)01——硬件设计篇

涂鸦智能烧水壶软件实现之温度采集和过温报警功能

涂鸦智能烧水壶软件实现之水温调节和灯光提示