本文介绍如何使用esp8266+MQTT协议,从硬件的角度如何上云,并将数据采集-发送到阿里云后台。

 

相关

完整项目介绍:https://blog.csdn.net/feit2417/article/details/86671932

码云(获取源码):https://gitee.com/hinzer/temp_hum_system

 

 

准备

乐鑫提供 ESP8266 SDK

阿里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文件烧写到设备,运行