本文介绍如何使用esp8266+MQTT协议,从硬件的角度如何上云,并将数据采集-发送到阿里云后台。
相关
完整项目介绍:https://blog.csdn.net/feit2417/article/details/86671932
码云(获取源码):https://gitee.com/hinzer/temp_hum_system
准备
阿里Link 设备端SDK iotkit-embedded-2.3.0
我们使用基于TCP的MQTT协议来连接阿里云Link平台,需要遵循Alink协议规范,对阿里C-SDK进行移植到ESP8266
下面来介绍一下移植过程。
项目模板
1.使用ESP8266-sdk目录下examples/mqtt_demo,以此为模板。放置在根目录下。
2.修改项目目录下的·gen_misc.sh·和·Makefile·文件,
export SDK_PATH=$/mnt/hgfs/sharefolder-esp8266/ESP8266_RTOS_SDK-2.0.0 //SDK根目录
export BIN_PATH=$/mnt/hgfs/sharefolder-esp8266/ESP8266_RTOS_SDK-2.0.0/bin //输出bin文件目录
parent_dir:=$(abspath $(shell pwd)/$(lastword $(MAKEFILE_LIST)))
parent_dir:=$(shell dirname $(parent_dir))
parent_dir:=$(shell dirname $(parent_dir))
SDK_PATH= $(parent_dir)
BIN_PATH=$(SDK_PATH)/bin
3.编译项目,没有问题
移植相关文件
使用MQTT-TCP连接Link平台,需要mqttClientId、mqttUsername、mqttPassword这三个参数认证。但是这些参数并不是直接提供给我们的,需要使用加密机制来对三元组数据生成。
1.找到Link-SDK中的utils\digest目录,复制.c和.h文件到工程目录下
- .c文件复制到user目录下
- .h文件复制到include目录下
- 复制iot_export_errno.h到include目录下
2.清除不必要的头文件包含,去除log_err函数(如果有的话)
3.每个.h文件中,包涵
#include "esp_common.h"
#include "iot_export_errno.h"
4.编译,直到无错误
OK,基本移植完成了。
开始修改代码
实现设备连接到云平台
参考官方文档:https://help.aliyun.com/document_detail/73742.html?spm=a2c4g.11186623.6.644.44a53f86Ed7mht
1.在`user_config.h`中修改wifi信息,和MQTT代理服务器地址
#define SSID "espressif" /* Wi-Fi SSID */
#define PASSWORD "1234567890" /* Wi-Fi Password */
#define MQTT_BROKER "a1u8k40ci7d.iot-as-mqtt.cn-shanghai.aliyuncs.com" /* 代理服务地址,参考官方文档*/
#define MQTT_PORT 1883 /* MQTT Port*/
2.`MQTTEcho.c`中修改配置
//设备信息
char *clientId = "你的设备id";
char *timestamp = "789";
char *deviceName = "你的设备名";
char *productKey = "产品key值";
char *devicesecret = "设备secret值";
//mqtt连接信息
char mqttClientId[100];
char mqttUsername[100];
char mqttPassword[100];
char hmac_source[512];
3.获取mqttClientId、mqttUsername、mqttPassword这三个参数认证
void GetmqttClientId(void)
{//获取mqttClientId
sprintf(mqttClientId,"%s|securemode=3,signmethod=hmacsha1,timestamp=%s|",clientId,timestamp);
printf("%s\r\n",mqttClientId);
}
void GetmqttUsername(void)
{//获取mqttUsername
sprintf(mqttUsername,"%s&%s",deviceName,productKey);
printf("%s\r\n",mqttUsername);
}
void GetmqttPassword(void)
{//获取mqttPassword
sprintf(hmac_source,
"clientId%s" "deviceName%s" "productKey%s" "timestamp%s",
clientId,deviceName,productKey,timestamp);
utils_hmac_sha1(hmac_source,strlen(hmac_source),
mqttPassword,devicesecret,strlen(devicesecret));
printf("%s\r\n",mqttPassword);
}
4.在线程`mqtt_client_thread`中,进行参数获取,并进行认证
GetmqttClientId();
GetmqttUsername();
GetmqttPassword();
//GetMqttTopicPropertyPost();
//GetMqttTopicPropertyPostReply();
connectData.clientID.cstring = mqttClientId;
connectData.username.cstring = mqttUsername;
connectData.password.cstring = mqttPassword;
connectData.MQTTVersion = 4; //mqtt v3.1
if ((rc = MQTTConnect(&client, &connectData)) != 0) {
printf("Return code from MQTT connect is %d\n", rc);
} else {
printf("MQTT Connected\n");
}
vTaskDelete(NULL);
return;
ps:需要修改sendbuf和readbuf的缓冲大小为300
5.再修改工程目录下Makefile文件。
#CONFIGURATION_DEFINES = -DICACHE_FLASH -DMQTT_TASK
CONFIGURATION_DEFINES = -DICACHE_FLASH
6.编译程序,直至成功
现在设备可以连接到阿里云了
采集温湿度数据并上报至云端
设备通过MQTT协议,发布订阅的方式与平台进行交互。Alink协议采用json格式,需要cJSON库对采集到的数据进行封装。数据采集部分使用dht11完成温度、湿度的采集,这里使用现成的函数库。
1.从ESP8266 SDK 目录中`driver_lib/`,添加库文件到工程目录
- 将include/*.h文件copy到工程目录include/
- 将driver文件copy到工程目录
- 添加第三方库文件(third_party/)cJSON.c、cJSON.h到工程目录
- 添加第三方库文件cJSON.c、cJSON.h到工程目录
2.修改Makefile
- 添加子目录 driver/
- 生成静态库 libdriver.a
SUBDIRS= \
user \
driver
COMPONENTS_eagle.app.v6 = \
user/libuser.a \
driver/libdriver.a
3.修改工程
封装信息
//封装发布主题(上报数据)和订阅主题(订阅发布响应)
#define TOPIC_PROPERTY_POST "/sys/%s/%s/thing/event/property/post"
#define TOPIC_PROPERTY_POST_REPLY "/sys/%s/%s/thing/event/property/post_reply"
//对数据的jison格式进行封装
#define DATAPAYLOAD "{\r\n\"id\" : \"1123\",\r\n\"version\":\"1.0\",\r\n\"params\" : {\r\n\"tempture\": %d,\r\n\"humidity\": %d,\r\n},\r\n\"method\":\"thing.event.property.post\"\r\n}"
mqtt_client线程中进行每10s一次主题上报
static void mqtt_client_thread(void* pvParameters)
{
printf("mqtt client thread starts\n");
MQTTClient client;
Network network;
unsigned char sendbuf[300], readbuf[300] = {0};
int rc = 0, count = 0;
MQTTPacket_connectData connectData = MQTTPacket_connectData_initializer;
pvParameters = 0;
NetworkInit(&network);
MQTTClientInit(&client, &network, 30000, sendbuf, sizeof(sendbuf), readbuf, sizeof(readbuf));
char* address = MQTT_BROKER;
printf("address = %s\r\n",address);
if ((rc = NetworkConnect(&network, address, MQTT_PORT)) != 0) {
printf("Return code from network connect is %d\n", rc);
}
#if defined(MQTT_TASK)
if ((rc = MQTTStartTask(&client)) != pdPASS) {
printf("Return code from start tasks is %d\n", rc);
} else {
printf("Use MQTTStartTask\n");
}
#endif
GetmqttClientId();
GetmqttUsername();
GetmqttPassword();
GetMqttTopicPropertyPost();
GetMqttTopicPropertyPostReply();
connectData.clientID.cstring = mqttClientId;
connectData.username.cstring = mqttUsername;
connectData.password.cstring = mqttPassword;
connectData.MQTTVersion = 4; //mqtt v3.1
printf("connectData.struct_id = %s\r\n",connectData.struct_id);
printf("connectData.struct_version = %d\r\n",connectData.struct_version);
printf("connectData.MQTTVersion = %d\r\n",connectData.MQTTVersion);
printf("connectData.clientID = %s\r\n",connectData.clientID.cstring);
printf("connectData.keepAliveInterval = %d\r\n",connectData.keepAliveInterval);
printf("connectData.cleansession = %d\r\n",connectData.cleansession);
printf("connectData.willFlag = %d\r\n",connectData.willFlag);
printf("connectData.username = %s\r\n",connectData.username.cstring);
printf("connectData.password = %s\r\n",connectData.password.cstring);
if ((rc = MQTTConnect(&client, &connectData)) != 0) {
printf("Return code from MQTT connect is %d\n", rc);
} else {
printf("MQTT Connected\n");
}
//订阅主题响应...
if ((rc = MQTTSubscribe(&client, MqttTopicPropertyPostReply, 2, messageArrived)) != 0) {
printf("Return code from MQTT subscribe is %d\n", rc);
} else {
printf("MQTT subscribe to topic %s\n",MqttTopicPropertyPostReply);
}
while (++count) {
GetJsonData(); //获取jison对象
MQTTMessage message;
message.qos = QOS0;
message.retained = 0;
message.payload = JsonDataPayLoad;
printf("JSON is %s\r\n",message.payload);
message.payloadlen = strlen(JsonDataPayLoad);
//发布数据主题
if ((rc = MQTTPublish(&client, MqttTopicPropertyPost, &message)) != 0) {
printf("Return code from MQTT publish is %d\n", rc);
} else {
printf("MQTT publish topic %s, message is %s\n",MqttTopicPropertyPost,message.payload);
}
//等待应答主题
MQTTYield(&client,200);
//定时10s发送
vTaskDelay(10000 / portTICK_RATE_MS); //send every 10 seconds
}
printf("mqtt_client_thread going to be deleted\n");
vTaskDelete(NULL);
return;
}
编译后的bin文件烧写到设备,运行