天津开发区建网站公司/简述获得友情链接的途径
忘记过去,超越自己
- ❤️ 博客主页 单片机菜鸟哥,一个野生非专业硬件IOT爱好者 ❤️
- ❤️ 本篇创建记录 2022-03-19 ❤️
- ❤️ 本篇更新记录 2022-03-19 ❤️
- 🎉 欢迎关注 🔎点赞 👍收藏 ⭐️留言📝
- 🙏 此博客均由博主单独编写,不存在任何商业团队运营,如发现错误,请留言轰炸哦!及时修正!感谢支持!
- 🔥 Arduino ESP8266教程累计帮助过超过1W+同学入门学习硬件网络编程,入选过选修课程,刊登过无线电杂志 🔥
快速导读
- 1、背景说明
- 2、涉及内容
- 2.1 AliyunIoTSDK.h 增加存储自定义主题
- 2.2 AliyunIoTSDK.h 增加两个自定义订阅、取消订阅主题方法
- 2.3 AliyunIoTSDK.cpp 实现订阅、取消订阅方法
- 2.4 AliyunIoTSDK.cpp 检测自定义主题修改点
- 3、具体实例使用(只是说明用法)
- 4、具体库(直接copy覆盖即可)
- 4.1 AliyunIoTSDK.h
- 4.2 AliyunIoTSDK.cpp
手把手代码注释,完整案例讲解开发过程以及细节,一键式运行代码。
ESP保姆级付费专栏群707958244
,不喜勿加,凭借付费专栏订单号加入
- ESP 保姆级教程 系列导读(此专栏非常适合刚刚入门ESP开发的初学者)
1、背景说明
- 之前使用AliyunIoTSDK的时候发现不支持订阅自定义主题,所以干脆就升级一下,方便使用
2、涉及内容
AliyunIoTSDK只有两个文件:
2.1 AliyunIoTSDK.h 增加存储自定义主题
// 最多绑定20个回调
static poniter_desc poniter_array[20];
// 最多绑定10个主题
static poniter_desc poniter_topic[10];
static p_poniter_desc p_poniter_array;
2.2 AliyunIoTSDK.h 增加两个自定义订阅、取消订阅主题方法
/*** 绑定事件回调,云服务下发的特定事件会进入回调* @param eventId 事件名*/// static void bindEvent(const char * eventId, MQTT_CALLBACK_SIGNATURE);/*** 绑定属性回调,云服务下发的数据包含此 key 会进入回调,用于监听特定数据的下发* @param key 物模型的key*/static int bindData(char *key, poniter_fun fp);/*** 卸载某个 key 的所有回调(慎用)* @param key 物模型的key*/static int unbindData(char *key);static int bindTopic(char *topic, poniter_fun fp);/*** 卸载某个 key 的所有回调(慎用)* @param key 物模型的key*/static int unbindTopic(char *topic);
2.3 AliyunIoTSDK.cpp 实现订阅、取消订阅方法
int AliyunIoTSDK::bindTopic(char *topic, poniter_fun fp)
{int i;for (i = 0; i < 10; i++){if (!poniter_topic[i].fp){poniter_topic[i].key = topic;poniter_topic[i].fp = fp;return 0;}}return -1;
}int AliyunIoTSDK::unbindTopic(char *topic)
{int i;for (i = 0; i < 10; i++){if (!strcmp(poniter_topic[i].key, topic)){poniter_topic[i].key = NULL;poniter_topic[i].fp = NULL;return 0;}}return -1;
}
2.4 AliyunIoTSDK.cpp 检测自定义主题修改点
void AliyunIoTSDK::mqttCheckConnect()
{if (client != NULL && !mqttConnecting){if (!client->connected()){client->disconnect();Serial.println("Connecting to MQTT Server ...");mqttConnecting = true;if (client->connect(clientId, mqttUsername, mqttPwd)){// 自定义主题订阅for (int i = 0; i < 10; i++){if (poniter_topic[i].fp){client->subscribe(poniter_topic[i].key);}}Serial.println("MQTT Connected!");}else{Serial.print("MQTT Connect err:");Serial.println(client->state());retry_count++;if(retry_count > RETRY_CRASH_COUNT){resetFunc();}}mqttConnecting = false;}else{Serial.println("state is connected");retry_count = 0;}}
}static void topicPass(char *topic,JsonVariant parm)
{for (int i = 0; i < 10; i++){if (poniter_topic[i].key){if (strstr(topic, poniter_topic[i].key)){poniter_topic[i].fp(parm);}}}
}
3、具体实例使用(只是说明用法)
/*** 功能: smartArduino** 详细描述:* 1、esp8266连接上wifi热点* 2、通过mqtt client方式连接上阿里云物联网平台* 3、监听阿里云控制信息,控制ws2812开关以及颜色*/
#include <ESP8266WiFi.h> // 引入Arduino ESP8266核心库
#include "AliyunIoTSDK.h" // 引入阿里云 IoT SDKconst char* WIFI_SSID = "TP-LINK_5344"; // WiFi账号密码,更改成自己的
const char* WIFI_PASSWORD = "xxxxx"; // WiFi密码,更改成自己的//-------- 设置产品和设备的信息,从阿里云物联网设备信息里查看 -------//
#define PRODUCT_KEY "gw5eDB82FOO"
#define DEVICE_NAME "device_8266"
#define DEVICE_SECRET "xxxxx"
#define REGION_ID "cn-shanghai"
#define TOPIC_RGB "/gw5eDB82FOO/device_8266/user/rgb"
#define TOPIC_STATUS "/gw5eDB82FOO/device_8266/user/status"
//-------- 设置产品和设备的信息,从阿里云物联网设备信息里查看 -------///******************* 函数声明 **********************/
void rgbCallback(JsonVariant p);
/******************* 函数声明 **********************/int red = 255; // 红色
int green = 255; // 绿色
int blue = 255; // 蓝色static WiFiClient espClient; // TCP Clientvoid setup() {// 延时2s,让电源稍微稳定一下delay(2000);// 初始化串口Serial.begin(115200);Serial.println("Project Running~");// 初始化网络wifi_station_set_auto_connect(0);//关闭自动连接ESP.wdtEnable(5000); // 启用看门狗doWiFiTick();// 设置两个自定义主题处理函数AliyunIoTSDK::bindTopic(TOPIC_STATUS,statusCallback);AliyunIoTSDK::bindTopic(TOPIC_RGB,rgbCallback);// 初始化 iot,需传入 wifi 的 client,和设备产品信息AliyunIoTSDK::begin(espClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET, REGION_ID);
}void loop() {// 定时喂狗ESP.wdtFeed();// wifi连接状态检测以及重连doWiFiTick();// Wifi处于连接状态if (WiFi.status() == WL_CONNECTED) {// 检测MQTT 阿里云AliyunIoTSDK::loop();}delay(1000);
}// 控制开关的回调函数
// 数据协议:{"s":xxxx}
void statusCallback(JsonVariant p)
{Serial.println(F("statusCallback"));int status = p["s"];if (status == 1){// 启动设备switchWS2812(true);} else {// 关闭设备switchWS2812(false);}
}// rgb颜色控制的回调函数
// 数据协议:{"r":xxxx,"g":xxxx,"b":xxxx}
void rgbCallback(JsonVariant p)
{Serial.println(F("rgbCallback"));red = p["r"]; // 红色green = p["g"]; // 绿色blue = p["b"]; // 蓝色handleWS2812RGB(red,green,blue);
}/*** 功能:设置ws2812颜色*/
void handleWS2812RGB(int red,int green,int blue){
}/*** 功能:开关ws2812*/
void switchWS2812(bool open){
}/*** 功能:连接Wifi路由心跳函数*/
void doWiFiTick() {static bool taskStarted = false;static bool startSTAFlag = false;static uint32_t lastWiFiCheckTick = 0;if (!startSTAFlag) {startSTAFlag = true;Serial.print(F("connect to ap:"));Serial.println(WIFI_SSID);// 断开网络WiFi.disconnect();// 设置为Station模式WiFi.mode(WIFI_STA);// 连接到wifi热点WiFi.begin(WIFI_SSID, WIFI_PASSWORD);Serial.printf("Heap size:%d\r\n", ESP.getFreeHeap());int cnt = 0;while (WiFi.status() != WL_CONNECTED) {delay(500);cnt++;Serial.print(".");if(cnt>=40){cnt = 0;//重启系统delayRestart(1);}}}//未连接1s重连if ( WiFi.status() != WL_CONNECTED ) {if (millis() - lastWiFiCheckTick > 1000) {lastWiFiCheckTick = millis();Serial.print(".");}}//连接成功建立else {if (taskStarted == false) {taskStarted = true;Serial.print(F("\r\nGet IP Address: "));Serial.println(WiFi.localIP());}}
}
4、具体库(直接copy覆盖即可)
4.1 AliyunIoTSDK.h
#ifndef ALIYUN_IOT_SDK_H
#define ALIYUN_IOT_SDK_H
#include <Arduino.h>
#include <ArduinoJson.h>
#include "Client.h"typedef void (*poniter_fun)(JsonVariant ele); //定义一个函数指针typedef struct poniter_desc
{char *key;poniter_fun fp;
} poniter_desc, *p_poniter_desc;// 最多绑定20个回调
static poniter_desc poniter_array[20];
// 最多绑定10个主题
static poniter_desc poniter_topic[10];
static p_poniter_desc p_poniter_array;class AliyunIoTSDK
{
private:// mqtt 链接信息,动态生成的static char mqttPwd[256];static char clientId[256];static char mqttUsername[100];static char domain[150];// 定时检查 mqtt 链接static void mqttCheckConnect();static void messageBufferCheck();static void sendBuffer();
public:// 标记一些 topic 模板static char ALINK_TOPIC_PROP_POST[150];static char ALINK_TOPIC_PROP_SET[150];static char ALINK_TOPIC_EVENT[150];// 在主程序 loop 中调用,检查连接和定时发送信息static void loop();/*** 初始化程序* @param ssid wifi名* @param passphrase wifi密码*/static void begin(Client &espClient,const char *_productKey,const char *_deviceName,const char *_deviceSecret,const char *_region);/*** 发送数据* @param param 字符串形式的json 数据,例如 {"${key}":"${value}"}*/static void send(const char *param);/*** 发送 float 格式数据* @param key 数据的 key* @param number 数据的值*/static void send(char *key, float number);/*** 发送 int 格式数据* @param key 数据的 key* @param number 数据的值*/static void send(char *key, int number);/*** 发送 double 格式数据* @param key 数据的 key* @param number 数据的值*/static void send(char *key, double number);/*** 发送 string 格式数据* @param key 数据的 key* @param text 数据的值*/static void send(char *key, char *text);/*** 发送事件到云平台(附带数据)* @param eventId 事件名,在阿里云物模型中定义好的* @param param 字符串形式的json 数据,例如 {"${key}":"${value}"}*/static void sendEvent(const char *eventId, const char *param);/*** 发送事件到云平台(空数据)* @param eventId 事件名,在阿里云物模型中定义好的*/static void sendEvent(const char *eventId);/*** 绑定回调,所有云服务下发的数据都会进回调*/// static void bind(MQTT_CALLBACK_SIGNATURE);/*** 绑定事件回调,云服务下发的特定事件会进入回调* @param eventId 事件名*/// static void bindEvent(const char * eventId, MQTT_CALLBACK_SIGNATURE);/*** 绑定属性回调,云服务下发的数据包含此 key 会进入回调,用于监听特定数据的下发* @param key 物模型的key*/static int bindData(char *key, poniter_fun fp);/*** 卸载某个 key 的所有回调(慎用)* @param key 物模型的key*/static int unbindData(char *key);static int bindTopic(char *topic, poniter_fun fp);/*** 卸载某个 key 的所有回调(慎用)* @param key 物模型的key*/static int unbindTopic(char *topic);
};
#endif
4.2 AliyunIoTSDK.cpp
#include "AliyunIoTSDK.h"
#include "PubSubClient.h"
#include <SHA256.h>#define CHECK_INTERVAL 10000
#define MESSAGE_BUFFER_SIZE 10
#define RETRY_CRASH_COUNT 5static const char *deviceName = NULL;
static const char *productKey = NULL;
static const char *deviceSecret = NULL;
static const char *region = NULL;struct DeviceProperty
{String key;String value;
};DeviceProperty PropertyMessageBuffer[MESSAGE_BUFFER_SIZE];#define MQTT_PORT 1883#define SHA256HMAC_SIZE 32
#define DATA_CALLBACK_SIZE 20#define ALINK_BODY_FORMAT "{\"id\":\"123\",\"version\":\"1.0\",\"method\":\"thing.event.property.post\",\"params\":%s}"
#define ALINK_EVENT_BODY_FORMAT "{\"id\": \"123\",\"version\": \"1.0\",\"params\": %s,\"method\": \"thing.event.%s.post\"}"static unsigned long lastMs = 0;
static int retry_count = 0;static PubSubClient *client = NULL;char AliyunIoTSDK::clientId[256] = "";
char AliyunIoTSDK::mqttUsername[100] = "";
char AliyunIoTSDK::mqttPwd[256] = "";
char AliyunIoTSDK::domain[150] = "";char AliyunIoTSDK::ALINK_TOPIC_PROP_POST[150] = "";
char AliyunIoTSDK::ALINK_TOPIC_PROP_SET[150] = "";
char AliyunIoTSDK::ALINK_TOPIC_EVENT[150] = "";static String hmac256(const String &signcontent, const String &ds)
{byte hashCode[SHA256HMAC_SIZE];SHA256 sha256;const char *key = ds.c_str();size_t keySize = ds.length();sha256.resetHMAC(key, keySize);sha256.update((const byte *)signcontent.c_str(), signcontent.length());sha256.finalizeHMAC(key, keySize, hashCode, sizeof(hashCode));String sign = "";for (byte i = 0; i < SHA256HMAC_SIZE; ++i){sign += "0123456789ABCDEF"[hashCode[i] >> 4];sign += "0123456789ABCDEF"[hashCode[i] & 0xf];}return sign;
}static void parmPass(JsonVariant parm)
{// const char *method = parm["method"];for (int i = 0; i < DATA_CALLBACK_SIZE; i++){if (poniter_array[i].key){bool hasKey = parm["params"].containsKey(poniter_array[i].key);if (hasKey){poniter_array[i].fp(parm["params"]);}}}
}static void topicPass(char *topic,JsonVariant parm)
{for (int i = 0; i < 10; i++){if (poniter_topic[i].key){if (strstr(topic, poniter_topic[i].key)){poniter_topic[i].fp(parm);}}}
}// 所有云服务的回调都会首先进入这里,例如属性下发
static void callback(char *topic, byte *payload, unsigned int length)
{Serial.print("Message arrived [");Serial.print(topic);Serial.print("] ");payload[length] = '\0';Serial.println((char *)payload);if (strstr(topic, AliyunIoTSDK::ALINK_TOPIC_PROP_SET)){StaticJsonDocument<200> doc;DeserializationError error = deserializeJson(doc, payload); //反序列化JSON数据if (!error) //检查反序列化是否成功{parmPass(doc.as<JsonVariant>()); //将参数传递后打印输出}} else {StaticJsonDocument<200> doc;DeserializationError error = deserializeJson(doc, payload); //反序列化JSON数据if (!error) //检查反序列化是否成功{topicPass(topic,doc.as<JsonVariant>()); //将参数传递后打印输出}}
}static bool mqttConnecting = false;
void(* resetFunc) (void) = 0; //制造重启命令
void AliyunIoTSDK::mqttCheckConnect()
{if (client != NULL && !mqttConnecting){if (!client->connected()){client->disconnect();Serial.println("Connecting to MQTT Server ...");mqttConnecting = true;if (client->connect(clientId, mqttUsername, mqttPwd)){// 自定义主题订阅for (int i = 0; i < 10; i++){if (poniter_topic[i].fp){client->subscribe(poniter_topic[i].key);}}Serial.println("MQTT Connected!");}else{Serial.print("MQTT Connect err:");Serial.println(client->state());retry_count++;if(retry_count > RETRY_CRASH_COUNT){resetFunc();}}mqttConnecting = false;}else{Serial.println("state is connected");retry_count = 0;}}
}void AliyunIoTSDK::begin(Client &espClient,const char *_productKey,const char *_deviceName,const char *_deviceSecret,const char *_region)
{client = new PubSubClient(espClient);productKey = _productKey;deviceName = _deviceName;deviceSecret = _deviceSecret;region = _region;long times = millis();String timestamp = String(times);sprintf(clientId, "%s|securemode=3,signmethod=hmacsha256,timestamp=%s|", deviceName, timestamp.c_str());String signcontent = "clientId";signcontent += deviceName;signcontent += "deviceName";signcontent += deviceName;signcontent += "productKey";signcontent += productKey;signcontent += "timestamp";signcontent += timestamp;String pwd = hmac256(signcontent, deviceSecret);strcpy(mqttPwd, pwd.c_str());sprintf(mqttUsername, "%s&%s", deviceName, productKey);sprintf(ALINK_TOPIC_PROP_POST, "/sys/%s/%s/thing/event/property/post", productKey, deviceName);sprintf(ALINK_TOPIC_PROP_SET, "/sys/%s/%s/thing/service/property/set", productKey, deviceName);sprintf(ALINK_TOPIC_EVENT, "/sys/%s/%s/thing/event", productKey, deviceName);sprintf(domain, "%s.iot-as-mqtt.%s.aliyuncs.com", productKey, region);client->setServer(domain, MQTT_PORT); /* 连接WiFi之后,连接MQTT服务器 */client->setCallback(callback);mqttCheckConnect();
}void AliyunIoTSDK::loop()
{client->loop();if (millis() - lastMs >= CHECK_INTERVAL){lastMs = millis();mqttCheckConnect();messageBufferCheck();}
}void AliyunIoTSDK::sendEvent(const char *eventId, const char *param)
{char topicKey[156];sprintf(topicKey, "%s/%s/post", ALINK_TOPIC_EVENT, eventId);char jsonBuf[1024];sprintf(jsonBuf, ALINK_EVENT_BODY_FORMAT, param, eventId);Serial.println(jsonBuf);boolean d = client->publish(topicKey, jsonBuf);Serial.print("publish:0 成功:");Serial.println(d);
}
void AliyunIoTSDK::sendEvent(const char *eventId)
{sendEvent(eventId, "{}");
}
unsigned long lastSendMS = 0;// 检查是否有数据需要发送
void AliyunIoTSDK::messageBufferCheck()
{int bufferSize = 0;for (int i = 0; i < MESSAGE_BUFFER_SIZE; i++){if (PropertyMessageBuffer[i].key.length() > 0){bufferSize++;}}// Serial.println("bufferSize:");// Serial.println(bufferSize);if (bufferSize > 0){if (bufferSize >= MESSAGE_BUFFER_SIZE){sendBuffer();}else{unsigned long nowMS = millis();// 3s 发送一次数据if (nowMS - lastSendMS > 5000){sendBuffer();lastSendMS = nowMS;}}}
}// 发送 buffer 数据
void AliyunIoTSDK::sendBuffer()
{int i;String buffer;for (i = 0; i < MESSAGE_BUFFER_SIZE; i++){if (PropertyMessageBuffer[i].key.length() > 0){buffer += "\"" + PropertyMessageBuffer[i].key + "\":" + PropertyMessageBuffer[i].value + ",";PropertyMessageBuffer[i].key = "";PropertyMessageBuffer[i].value = "";}}buffer = "{" + buffer.substring(0, buffer.length() - 1) + "}";send(buffer.c_str());
}void addMessageToBuffer(char *key, String value)
{int i;for (i = 0; i < MESSAGE_BUFFER_SIZE; i++){if (PropertyMessageBuffer[i].key.length() == 0){PropertyMessageBuffer[i].key = key;PropertyMessageBuffer[i].value = value;break;}}
}
void AliyunIoTSDK::send(const char *param)
{char jsonBuf[1024];sprintf(jsonBuf, ALINK_BODY_FORMAT, param);Serial.println(jsonBuf);boolean d = client->publish(ALINK_TOPIC_PROP_POST, jsonBuf);Serial.print("publish:0 成功:");Serial.println(d);
}
void AliyunIoTSDK::send(char *key, float number)
{addMessageToBuffer(key, String(number));messageBufferCheck();
}
void AliyunIoTSDK::send(char *key, int number)
{addMessageToBuffer(key, String(number));messageBufferCheck();
}
void AliyunIoTSDK::send(char *key, double number)
{addMessageToBuffer(key, String(number));messageBufferCheck();
}void AliyunIoTSDK::send(char *key, char *text)
{addMessageToBuffer(key, "\"" + String(text) + "\"");messageBufferCheck();
}int AliyunIoTSDK::bindData(char *key, poniter_fun fp)
{int i;for (i = 0; i < DATA_CALLBACK_SIZE; i++){if (!poniter_array[i].fp){poniter_array[i].key = key;poniter_array[i].fp = fp;return 0;}}return -1;
}int AliyunIoTSDK::bindTopic(char *topic, poniter_fun fp)
{int i;for (i = 0; i < 10; i++){if (!poniter_topic[i].fp){poniter_topic[i].key = topic;poniter_topic[i].fp = fp;return 0;}}return -1;
}int AliyunIoTSDK::unbindTopic(char *topic)
{int i;for (i = 0; i < 10; i++){if (!strcmp(poniter_topic[i].key, topic)){poniter_topic[i].key = NULL;poniter_topic[i].fp = NULL;return 0;}}return -1;
}int AliyunIoTSDK::unbindData(char *key)
{int i;for (i = 0; i < DATA_CALLBACK_SIZE; i++){if (!strcmp(poniter_array[i].key, key)){poniter_array[i].key = NULL;poniter_array[i].fp = NULL;return 0;}}return -1;
}