前言:
两个月前,我们完成了连接腾讯云的工作,当时配网引导页面,就只是配置下引导页面,如今配网引导页面可以选择不同配网方式,然后再进行引导页面设置,对此,腾讯云维护的qcloud-iot-esp-wifi SDK也进行了不小的更新,本文我们将同步腾讯云更新的SDK,并实现微信小程序控制设备的目标。
一、基础知识
1.设备配网方式
正式更新SDK之前,咱们先修改下后台配网页面,点击交互开发,进入如下页面
然后点击配网引导一行配置按钮,然后选择首选配网方式为SmartConfig,次选配网方式暂时不用,接着点击保存按钮
然后进行首选配网方式引导页配置,使用默认即可,点击保存
2.SDK更新说明
上一节中的代码结构如下,将腾讯云SDK内嵌到了ESP8266_RTOS_SDK中
由于腾讯云更新比较频繁,按照之前的方式,每次更新SDK都需要不少工作量,因此,本节开始,我们将腾讯云Demo SDK和ESP8266_RTOS_SDK独立起来,腾讯云提供demo SDK不再内嵌到ESP8266_RTOS_SDK中,如此一来,之后如果需要更新腾讯云固件,直接替换就行了。
Makefile代码也稍作修改如下,重新制定下IDF路径也就是ESP8266_RTOS_SDK的路径。
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := esp8266-qcloud-iot
IDF_DIR := ESP8266_RTOS_SDK
PWD := /home/Administrator
export IDF_PATH ?= $(PWD)/$(IDF_DIR)
SDKCONFIG_DEFAULTS := $(PWD)/qcloud-iot-esp8266-demo/sdkconfig.qcloud
include $(IDF_PATH)/make/project.mk
二、核心代码
1.腾讯云SDK更新
首先直接将腾讯云提供的Deo和SDK全部复制到和ESP8266_RTOS_SDK 在/home/Administrator文件夹下,然后修改Makefile内容如上图,全部复制过去,然后修改components/qcloud_iot/qcloud_iot_c_sdk/platform/HAL_Device_freertos.c中产品参数配置,将product_id等信息修改为自己创建的即可
2.配网方式修改
打开qcloud_demo_task()函数中start_smartconfig(),注释掉softAP配网函数start_softAP(“ESP8266-SAP”, “12345678”, 0);
void qcloud_demo_task(void* parm)
{
bool wifi_connected = false;
Log_i("qcloud_demo_task start");
#if CONFIG_WIFI_CONFIG_ENABLED
/* to use WiFi config and device binding with Wechat mini program */
int wifi_config_state;
//int ret = start_softAP("ESP8266-SAP", "12345678", 0);
int ret = start_smartconfig();
if (ret) {
Log_e("start wifi config failed: %d", ret);
} else {
/* max waiting: 150 * 2000ms */
int wait_cnt = 150;
do {
Log_d("waiting for wifi config result...");
HAL_SleepMs(2000);
wifi_config_state = query_wifi_config_state();
} while (wifi_config_state == WIFI_CONFIG_GOING_ON && wait_cnt--);
}
wifi_connected = is_wifi_config_successful();
if (!wifi_connected) {
Log_e("wifi config failed!");
// setup a softAP to upload log to mini program
start_log_softAP();
}
#else
/* init wifi STA and start connection with expected BSS */
esp_wifi_initialise();
/* 20 * 1000ms */
wifi_connected = wait_for_wifi_ready(CONNECTED_BIT, 20, 1000);
#endif
if (wifi_connected) {
setup_sntp();
Log_i("WiFi is ready, to do Qcloud IoT demo");
Log_d("timestamp now:%d", HAL_Timer_current_sec());
#ifdef CONFIG_QCLOUD_IOT_EXPLORER_ENABLED
qcloud_iot_explorer_demo(CONFIG_DEMO_EXAMPLE_SELECT);
#else
qcloud_iot_hub_demo();
#endif
} else {
Log_e("WiFi is not ready, please check configuration");
}
Log_w("qcloud_demo_task quit");
vTaskDelete(NULL);
}
同时注意,替换掉qcloud_wifi_config.c中函数app_reply_dev_info中代码,主要修改内容是,直接自己组包,将配网结果信息返回给小程序,源代码中cJSON_PrintPreallocated出现错误,导致配网失败。
static int app_reply_dev_info(comm_peer_t *peer)
{
int ret;
DeviceInfo devinfo;
ret = HAL_GetDevInfo(&devinfo);
if (ret) {
Log_e("load dev info failed: %d", ret);
app_send_error_log(peer, CUR_ERR, ERR_APP_CMD, ret);
return -1;
}
cJSON *reply_json = cJSON_CreateObject();
cJSON_AddNumberToObject(reply_json, "cmdType", (int)CMD_DEVICE_REPLY);
cJSON_AddStringToObject(reply_json, "productId", devinfo.product_id);
cJSON_AddStringToObject(reply_json, "deviceName", devinfo.device_name);
cJSON_AddStringToObject(reply_json, "protoVersion", SOFTAP_BOARDING_VERSION);
char json_str[256] = {0};
// if (0 == cJSON_PrintPreallocated(reply_json, json_str, sizeof(json_str), 0)) {
// Log_e("cJSON_PrintPreallocated failed!");
// cJSON_Delete(reply_json);
// app_send_error_log(peer, CUR_ERR, ERR_APP_CMD, ERR_JSON_PRINT);
// return -1;
// }
/* append msg delimiter */
// strcat(json_str, "\r\n");
// cJSON_Delete(reply_json);
snprintf(json_str, 256, "{\"cmdType\":2,\"productId\":\"%s\",\"deviceName\":\"%s\",\"protoVersion\":\"%s\"}",devinfo.product_id, devinfo.device_name, SOFTAP_BOARDING_VERSION);
Log_d("============== json_str is %s\r\n", json_str);
strcat(json_str, "\r\n");
int udp_resend_cnt = 3;
udp_resend:
ret = sendto(peer->socket_id, json_str, strlen(json_str), 0, peer->socket_addr, peer->addr_len);
if (ret < 0) {
Log_e("send error: %s", strerror(errno));
push_error_log(ERR_SOCKET_SEND, errno);
return -1;
}
/* UDP packet could be lost, send it again */
/* NOT necessary for TCP */
if (peer->socket_addr != NULL && --udp_resend_cnt) {
HAL_SleepMs(1000);
goto udp_resend;
}
HAL_Printf("Report dev info: %s", json_str);
return 0;
}
3.驱动RGB三色灯
RGB三色灯内部原理图如下:
我们将RGB三色灯正极连接NodeMCU的VCC R、G、B三个引脚分别连接D0、D1、D2,只需要给D0 D1 D2高低电平即可点亮三色灯。
我们使用的是NodeMCU,引脚对应图如下,我们需要驱动RGB三色灯,注意D0对应GPIO16,D1和D2分别对应GPIO5 GPIO4。
点亮RGB三色灯
esp_err_t set_rgb_led_state(eRGBState rgb, uint32_t state)
{
switch ( rgb )
{
case RGB_LED_RED:
return gpio_set_level(GPIO_RGB_RED_LED, state);
break;
case RGB_LED_GREEN:
return gpio_set_level(GPIO_RGB_GREEN_LED, state);
break;
case RGB_LED_BLUE:
return gpio_set_level(GPIO_RGB_BLUE_LED, state);
break;
default:
break;
}
return ESP_FAIL;
}
/* demo for light logic conttrol */
static void deal_down_stream_user_logic(void *client, ProductDataDefine *light)
{
int i;
char brightness_bar[] = "||||||||||||||||||||";
int brightness_bar_len = strlen(brightness_bar);
/* using console log to show brightness change */
brightness_bar_len =
(light->m_brightness >= 100) ? brightness_bar_len : (int)((light->m_brightness * brightness_bar_len) / 100);
for (i = brightness_bar_len; i < strlen(brightness_bar); i++) {
brightness_bar[i] = '-';
}
if (light->m_light_switch) {
HAL_Printf("\n[ light is on ]|[brightness:%s]|[%s]\n\n", brightness_bar, light->m_name);
} else {
HAL_Printf("\n[ light is off ]|[brightness:%s]|[%s]\n\n", brightness_bar, light->m_name);
}
/** hardware control **/
if (light->m_light_switch) {
// set_relay_led_state(RELAY_LED_ON);
set_rgb_led_state(RGB_LED_GREEN, GPIO_SET);
set_rgb_led_state(RGB_LED_BLUE, GPIO_SET);
set_rgb_led_state(RGB_LED_RED, GPIO_CLEAR);
} else {
// set_relay_led_state(RELAY_LED_OFF);
set_rgb_led_state(RGB_LED_GREEN, GPIO_SET);
set_rgb_led_state(RGB_LED_BLUE, GPIO_SET);
set_rgb_led_state(RGB_LED_RED, GPIO_SET);
}
switch (light->m_color)
{
case eCOLOR_RED:
set_rgb_led_state(RGB_LED_GREEN, GPIO_SET);
set_rgb_led_state(RGB_LED_BLUE, GPIO_SET);
set_rgb_led_state(RGB_LED_RED, GPIO_CLEAR);
break;
case eCOLOR_GREEN:
set_rgb_led_state(RGB_LED_RED, GPIO_SET);
set_rgb_led_state(RGB_LED_BLUE, GPIO_SET);
set_rgb_led_state(RGB_LED_GREEN, GPIO_CLEAR);
break;
case eCOLOR_BLUE:
set_rgb_led_state(RGB_LED_RED, GPIO_SET);
set_rgb_led_state(RGB_LED_GREEN, GPIO_SET);
set_rgb_led_state(RGB_LED_BLUE, GPIO_CLEAR);
break;
default:
break;
}
#ifdef EVENT_POST_ENABLED
if (eCHANGED == get_property_state(&light->m_light_switch)) {
if (light->m_light_switch) {
// memset(sg_message, 0, MAX_EVENT_STR_MESSAGE_LEN);
// strcpy(sg_message,"light on");
// sg_status = 1;
*(TYPE_DEF_TEMPLATE_BOOL *)g_events[0].pEventData[0].data = 1;
memset((TYPE_DEF_TEMPLATE_STRING *)g_events[0].pEventData[1].data, 0, MAX_EVENT_STR_MESSAGE_LEN);
strcpy((TYPE_DEF_TEMPLATE_STRING *)g_events[0].pEventData[1].data, "light on");
} else {
// memset(sg_message, 0, MAX_EVENT_STR_MESSAGE_LEN);
// strcpy(sg_message,"light off");
// sg_status = 0;
*(TYPE_DEF_TEMPLATE_BOOL *)g_events[0].pEventData[0].data = 0;
memset((TYPE_DEF_TEMPLATE_STRING *)g_events[0].pEventData[1].data, 0, MAX_EVENT_STR_MESSAGE_LEN);
strcpy((TYPE_DEF_TEMPLATE_STRING *)g_events[0].pEventData[1].data, "light off");
}
// switch state changed set EVENT0 flag, the events will be posted by eventPostCheck
IOT_Event_setFlag(client, FLAG_EVENT0);
}
#endif
}
使用light_data_template_sample.c中文件,deal_down_stream_user_logic函数负责处理云端下发的命令,我们根据light->m_color数值,来控制RGB三色灯。
三、运行
烧录程序到设备中后,需要首先打开后台,查看配网二维码,使用小程序对设备进行配网,所谓配网,是指通过小程序将当前环境路由器SSID和密码下发给设备,然后设备就可以愉快的连上服务器进行之后的操作了。关于配网原理请查看官方文档:https://cloud.tencent.com/document/product/1081/43696 ,目前文档提供了三种配网协议说明,如下图
进入后台,点击批量投产,查看后台二维码
直接打开微信,使用扫一扫功能扫描二维码
这里出现了产品配网入口页面,重启NodeMCU lua V3,默认进入配网状态,然后点击小程序立即绑定按钮
然后勾选我已确认上述操作,点击下一步
输入SSID和密码后,点击下一步,然后耐心等待即可,注意这个过程手机需要连接到需要设置的WIFI热点
如果出现上述页面,则说明配网绑定已经成功了,接下来就可以进行远程监控了,点击完成按钮后,可以发现小程序主页上已经有了一个LED灯设备
点击图标,打开设备详情页
点击设置RGB三色灯颜色为绿色,可以看到设备Log打印{“color”:1}表示设备成功收到了云端下发的指令
{“color”:1}1代表颜色为绿色,至于为什么是1,我们可以回看产品定义中功能参数定义
设备收到指令后点亮RGB三色灯
至于红色和蓝色都是一样的,不再放图了。
四、结语
如您在使用过程中有任何问题,请加QQ群进一步交流。
QQ交流群:906015840 (备注:物联网项目交流)
源码获取:扫描下方二维码,关注公众号,回复"小程序控制esp8266"即可
一叶孤沙出品:一沙一世界,一叶一菩提