物联网平台为设备提供安全可靠的连接通信能力,向下连接海量设备,支撑设备数据采集上云;向上提供云端API,服务端通过调用云端API将指令下发至设备端,实现远程控制。官方定义
MQTT是一个基于客户端(设备端)-服务器的消息发布/订阅传输协议,消息通过Topic匹配规则路由到指定的Topic。MQTT协议轻量、简单、开放和易于实现,可作为即时通讯协议,在智能家居、移动应用智慧城市有着广泛的应用。
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
创建一个light_status发布主题,用来上报当前灯的状态数据。
创建一个light_control订阅主题,用来控制亮灯和关灯。
物联网平台预定义的基础功能通信Topic。
物联网平台预定义的物模型通信Topic。各物模型功能Topic消息的数据格式。
可以从https://github.com/knzjoint/ali-iot下载,放入项目的components目录中。
因为在项目的
从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
通过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
进入Component config —> ali_iot —> 选中Enable ali_iot
进入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
目前这种方法只适应于将设备认证信息固定在程序中,以后可以配合服务端根据设备唯一标识动态申请认证信息。
项目源码
阿里云物联网套件的Topic
阿里去物联网套件mqtt协议签名示例