ESP32 ESP-IDF 通过阿里云物联网平台控制LED

术语

阿里云物联网平台(以下简称物联网平台)

物联网平台为设备提供安全可靠的连接通信能力,向下连接海量设备,支撑设备数据采集上云;向上提供云端API,服务端通过调用云端API将指令下发至设备端,实现远程控制。官方定义

MQTT(Message Queuing Telemetry Transport,消息队列遥测传输协议)

MQTT是一个基于客户端(设备端)-服务器的消息发布/订阅传输协议,消息通过Topic匹配规则路由到指定的Topic。MQTT协议轻量、简单、开放和易于实现,可作为即时通讯协议,在智能家居、移动应用智慧城市有着广泛的应用。
引用https://zhuanlan.zhihu.com/p/20888181

MQTT 协议的内容

概述

通过修改ESP-IDF中MQTT的tcp的demo修改成连接物联网平台控制LED灯开关的实例。该实例会让我们对物联网中的感知层、网络传输层有个初步的印象。
下行的数据需要提前订阅一个light_control主题。
上行数据发送到指定的light_status主题处理。
本项目是参考了examples\protocols\mqtt\tcp的示例。

数据结构

上行数据结构

lightStatus的状态off表示电灯关闭,on表示电灯状态是打开。

{
    "lightStatus": "on"
}

下行数据结构

switch 的值为off时控制电灯的关闭,值为on时控制电灯的打开。

{
    "switch": "on"
}

物联网平台操作

连接物联网平台首先要获取一机一密设备认证信息:ProductKey、ProductSecret、DeviceName、DeviceSecret,然后根据主题进行发布和订阅消息。

创建产品

在物联网的控制台中展开设备管理->产品->创建产品,在创建产品的表单中输入如下一个标准灯类的直连产品。产品的名称为home-light。

创建设备

在控制台中展开设备管理->设备->添加设备,设备放在产品home-light下,设备名称为light-1

# 获取认证信息

在设备管理->设备->设备详情,在设备详情里可以看到认证信息ProductKey、ProductSecret、DeviceName、DeviceSecret。

创建自定义Topic

在产品详情->Topic类列表->自定义 Topic

基础通讯Topic和物模型通信Topic

创建一个light_status发布主题,用来上报当前灯的状态数据。
创建一个light_control订阅主题,用来控制亮灯和关灯。

基础通讯Topic

物联网平台预定义的基础功能通信Topic。

  • 固件升级相关Topic
  • 设备标签相关Topic
  • 时钟同步相关Topic
  • 设备影子相关Topic
  • 配置更新相关Topic
  • 广播Topic

物模型通信Topic

物联网平台预定义的物模型通信Topic。各物模型功能Topic消息的数据格式。

编码

文件结构

添加ali_iot模块

可以从https://github.com/knzjoint/ali-iot下载,放入项目的components目录中。

添加wifi配置模块(可选)

因为在项目的
从esp-idf的examples\common_components中将protocol_examples_common复制到项目的components目录下。
原先的protocol_example_common是在项目中的CMakeLists.txt里配置set(EXTRA_COMPONENT_DIRS $ENV{IDF_PATH}/examples/common_components/protocol_examples_common),如果自己是这这个目录复制到项目文件下,这条语句可以在CmakeLists.txt中注释掉。

添加固定信息

通过宏添加阿里云物联网平台认证信息和硬件参数。获取认证信息

// 下面的几个宏用于定义设备的阿里云身份认证信息:ProductKey、ProductSecret、DeviceSecret、DeviceName
// 在实际产品开发中,设备的身份认证信息应该是设备厂商将其加密后存放于设备Flash中或者某个文件中,
// 设备上电时将其读出后使用
#define LIGHT_PRODUCT_KEY "a1CEwF7bES6"
#define LIGHT_PRODUCT_SECRET "xxxxxxxx"
#define LIGHT_DEVICE_SECRET "xxxxxxxx"
#define LIGHT_DEVICE_NAME "light-1"

// 控制Light开关的Topic
#define LIGHT_CONTROL_TOPIC "/a1CEwF7bES6/light-1/user/light_control"
// 上报Light状态的Topic
#define LIGHT_STATUS_TOPIC "/a1CEwF7bES6/light-1/user/light_status"

// 控制LED的 GPIO
gpio_num_t gpio_led_num = GPIO_NUM_32; // 连接LED的GPIO

mqtt连接设备

通过IOT_Sign_MQTT函数获取连接阿里去物联网平台的签名。

// 下面的代码是将上面静态定义的设备身份信息赋值给meta_info
memcpy(meta_info.product_key, LIGHT_PRODUCT_KEY, strlen(LIGHT_PRODUCT_KEY));
memcpy(meta_info.product_secret, LIGHT_PRODUCT_SECRET, strlen(LIGHT_PRODUCT_SECRET));
memcpy(meta_info.device_name, LIGHT_DEVICE_NAME, strlen(LIGHT_DEVICE_NAME));
memcpy(meta_info.device_secret, LIGHT_DEVICE_SECRET, strlen(LIGHT_DEVICE_SECRET));

// 调用签名函数,生成MQTT连接时需要的各种数据,IOTX_CLOUD_REGION_SHANGHAI 指连接站点是华东2(上海)
IOT_Sign_MQTT(IOTX_CLOUD_REGION_SHANGHAI, &meta_info, &sign_mqtt);

esp_mqtt_client_config_t mqtt_cfg = {
    // .uri = CONFIG_BROKER_URL,
    .host = sign_mqtt.hostname,         // 完整的阿里云物联网站点域名
    .port = 1883,                       // 阿里云站点的端口号
    .password = sign_mqtt.password,     // MQTT建立连接时需要指定的Password。把提交给服务器的参数按字典排序并拼接后,使用hmacsha256方法和设备的DeviceSecret,加签生成Password。
    .client_id = sign_mqtt.clientid,    // MQTT建立连接时需要指定的ClientID。建议使用设备的MAC地址或SN码,64字符内。
    .username = sign_mqtt.username,     // MQTT建立连接时需要指定的Username。由设备名DeviceName、符号(&)和产品ProductKey组成,格式:deviceName+"&"+productKey。示例:Device1&alSseIs****。
    .event_handle = mqtt_event_handler, // mqtt客户端启动成功后对连接、断开连接、订阅、取消订阅、发布、接收数据等事件的处理。
};

订阅设备的主题消息

在客户端连接到物联网平台时订阅light_control主题。

case MQTT_EVENT_CONNECTED: // MQTT 客户端连接上服务器事件
    msg_id = esp_mqtt_client_subscribe(client, LIGHT_CONTROL_TOPIC, 1);
    ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
    break;

消息处理

当前客户端收到light_control主题消息,需要根据主题路由到指定的处理函数,处理函数需要根据数据格式对数据进行解析,解析的结果对设备进行操作。
处理topic

if (0 == strncmp(event->topic, LIGHT_CONTROL_TOPIC, event->topic_len))
{
    ESP_LOGI(TAG, "deal with topic :%s", event->topic);
    char dest[512] = ""; // event事件未初始化,使用ESP_LOGI()打印的内容部分会出现乱码
    memcpy(dest, event->data, event->data_len);
    ESP_LOGI(TAG, "DATA=%s", dest);
    // 解析JSON格式数据
    parse_json_data(dest);
}

解析数据、操作设备和上报状态

if (0 == strncmp(item->string, "switch", sizeof("switch")))
{
    // 获取switch的value值
    value_str = item->valuestring;
    ESP_LOGI(TAG, "parsed cmd_id:%s", value_str);
    // 判断 switch的值是否为on
    if (0 == strncmp(value_str, "on", sizeof("on")))
    {
        // 开灯
        switch_led(1);
        // 上报当前灯的状态
        msg_id = esp_mqtt_client_publish(client, LIGHT_STATUS_TOPIC, "{\"lightStatus\": \"on\"}", 0, 0, 0);
        ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
        break;
    }
    else if (0 == strncmp(value_str, "off", sizeof("off")))
    {
        // 关灯
        switch_led(0);
        // 上传当前灯的状态
        msg_id = esp_mqtt_client_publish(client, LIGHT_STATUS_TOPIC, "{\"lightStatus\": \"off\"}", 0, 0, 0);
        ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
        break;
    }
}

编译

编译前需要配置ali-iot库和wifi的账户和密码,运行idf.py menuconfig

配置ali-iot库

进入Component config —> ali_iot —> 选中Enable ali_iot

配置wifi账户和密码

进入Example Connection Configuration —> 分别配置 WiFi SSID(账户) 和 WiFi Password(密码)

烧录

运行idf.py flash 烧录

监控

运行idf.py monitor监测程序,出现以下内容表示连接物联网平台成功。

I (13258) wifi:new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:1
I (14018) wifi:state: init -> auth (b0)
I (14028) wifi:state: auth -> assoc (0)
I (14038) wifi:state: assoc -> run (10)
I (14048) wifi:connected with maker_knz, aid = 1, channel 1, BW20, bssid = d8:63:75:62:02:b3
I (14048) wifi:security type: 3, phy: bgn, rssi: -75
I (14058) wifi:pm start, type: 1

I (14138) wifi:AP's beacon interval = 102400 us, DTIM period = 2
I (14648) esp_netif_handlers: sta ip: 192.168.43.192, mask: 255.255.255.0, gw: 192.168.43.1
I (14648) example_connect: Got IP event!
I (15648) example_connect: Got IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fe96:f830, type: ESP_IP6_ADDR_IS_LINK_LOCAL
I (15648) example_connect: Connected to maker_knz
I (15648) example_connect: IPv4 address: 192.168.43.192
I (15658) example_connect: IPv6 address: fe80:0000:0000:0000:32ae:a4ff:fe96:f830
I (15668) MQTT_ALI_IOT: Other event id:7
I (15738) MQTT_CLIENT: Sending MQTT CONNECT message, type: 1, id: 0000
I (15808) MQTT_ALI_IOT: sent subscribe successful, msg_id=43235
I (15848) MQTT_ALI_IOT: MQTT_EVENT_SUBSCRIBED, msg_id=43235
I (15858) MQTT_ALI_IOT: sent publish successful, msg_id=0

物联网平台联调

从物联网平台发送消息控制LED。


查看设备当前状态。

总结

目前这种方法只适应于将设备认证信息固定在程序中,以后可以配合服务端根据设备唯一标识动态申请认证信息。
项目源码

参考文档

阿里云物联网套件的Topic
阿里去物联网套件mqtt协议签名示例

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章