tried to support aliot again(untested)
This commit is contained in:
@@ -1 +0,0 @@
|
||||
{"type": "library", "name": "AliyunIoTSDK", "version": "0.3.0", "spec": {"owner": "xinyu198736", "id": 6674, "name": "AliyunIoTSDK", "requirements": null, "url": null}}
|
||||
@@ -1,3 +0,0 @@
|
||||
|
||||
0.1
|
||||
*
|
||||
@@ -1,20 +0,0 @@
|
||||
Copyright (c) 2008-2015 Nicholas O'Leary
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
@@ -1,192 +0,0 @@
|
||||
# Arduino TopLevel Client for aliyun IoT Platform
|
||||
|
||||
`AliyunIoTSDK` 可以帮助你快速连接阿里云 IoT 平台,通过和阿里云物联网开发平台配合,可快速实现各种硬件应用,包括了很上层的封装,无需自己解析数据体,绑定事件即可,在 esp8266 平台充分测试(NodeMCU 1.0)
|
||||
|
||||
## update
|
||||
|
||||
- v0.2 增加属性发送 buffer,5秒一次或者10条buffer满,才会一起发送数据,节省请求次数
|
||||
- v0.1 上线
|
||||
|
||||
## Usage 使用示例
|
||||
|
||||
```c++
|
||||
// 引入 wifi 模块,并实例化,不同的芯片这里的依赖可能不同
|
||||
#include <ESP8266WiFi.h>
|
||||
static WiFiClient espClient;
|
||||
|
||||
// 引入阿里云 IoT SDK
|
||||
#include <AliyunIoTSDK.h>
|
||||
|
||||
// 设置产品和设备的信息,从阿里云设备信息里查看
|
||||
#define PRODUCT_KEY "xxx"
|
||||
#define DEVICE_NAME "Device_D"
|
||||
#define DEVICE_SECRET "xxxxxxxxxxxxxx"
|
||||
#define REGION_ID "cn-shanghai"
|
||||
|
||||
// 设置 wifi 信息
|
||||
#define WIFI_SSID "xxxxx"
|
||||
#define WIFI_PASSWD "xxxxx"
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
// 初始化 wifi
|
||||
wifiInit(WIFI_SSID, WIFI_PASSWD);
|
||||
|
||||
// 初始化 iot,需传入 wifi 的 client,和设备产品信息
|
||||
AliyunIoTSDK::begin(espClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET, REGION_ID);
|
||||
|
||||
// 绑定一个设备属性回调,当远程修改此属性,会触发 powerCallback
|
||||
// PowerSwitch 是在设备产品中定义的物联网模型的 id
|
||||
AliyunIoTSDK::bindData("PowerSwitch", powerCallback);
|
||||
|
||||
// 发送一个数据到云平台,LightLuminance 是在设备产品中定义的物联网模型的 id
|
||||
AliyunIoTSDK::send("LightLuminance", 100);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
AliyunIoTSDK::loop();
|
||||
}
|
||||
|
||||
// 初始化 wifi 连接
|
||||
void wifiInit(const char *ssid, const char *passphrase)
|
||||
{
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, passphrase);
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
delay(1000);
|
||||
Serial.println("WiFi not Connect");
|
||||
}
|
||||
Serial.println("Connected to AP");
|
||||
}
|
||||
|
||||
// 电源属性修改的回调函数
|
||||
void powerCallback(JsonVariant p)
|
||||
{
|
||||
int PowerSwitch = p["PowerSwitch"];
|
||||
if (PowerSwitch == 1)
|
||||
{
|
||||
// 启动设备
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API 可用方法
|
||||
|
||||
```c++
|
||||
// 在主程序 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);
|
||||
```
|
||||
|
||||
## Examples 示例
|
||||
|
||||
buiding...
|
||||
|
||||
## Limitations 使用限制和说明
|
||||
|
||||
- 本库不包含 wifi 连接的代码,需先建立连接,然后将 client 传入
|
||||
- 依赖 PubSubClient ,在使用前,请务必修改 PubSubClient 的连接参数,否则无法使用
|
||||
- PubSubClient 中的 MQTT_MAX_PACKET_SIZE 修改为 1024
|
||||
- PubSubClient 中的 MQTT_KEEPALIVE 修改为 60
|
||||
- 掉线后会一直尝试重新连接,可能会触发阿里云的一些限流规则(已经做了规避),并且会导致挤掉其他同设备 ID 的设备
|
||||
- 默认 5000ms 检测一次连接状态,可以通过 CHECK_INTERVAL 修改此值
|
||||
|
||||
|
||||
## Compatible Hardware 适用硬件
|
||||
|
||||
本 SDK 基于 PubSubClient 底层库开发,兼容列表与 PubSubClient 相同。
|
||||
|
||||
The library uses the Arduino Ethernet Client api for interacting with the underlying network hardware. This means it Just Works with a growing number of boards and shields, including:
|
||||
|
||||
- Arduino Ethernet
|
||||
- Arduino Ethernet Shield
|
||||
- Arduino YUN – use the included YunClient in place of EthernetClient, and be sure to do a Bridge.begin() first
|
||||
- Arduino WiFi Shield - if you want to send packets > 90 bytes with this shield, enable the MQTT_MAX_TRANSFER_SIZE define in PubSubClient.h.
|
||||
- Sparkfun WiFly Shield – library
|
||||
- TI CC3000 WiFi - library
|
||||
- Intel Galileo/Edison
|
||||
- ESP8266
|
||||
- ESP32
|
||||
|
||||
The library cannot currently be used with hardware based on the ENC28J60 chip – such as the Nanode or the Nuelectronics Ethernet Shield. For those, there is an alternative library available.
|
||||
|
||||
## License
|
||||
|
||||
This code is released under the MIT License.
|
||||
@@ -1,23 +0,0 @@
|
||||
#######################################
|
||||
# Syntax Coloring Map For AliyunIoTSDK
|
||||
#######################################
|
||||
|
||||
#######################################
|
||||
# Datatypes (KEYWORD1)
|
||||
#######################################
|
||||
|
||||
AliyunIoTSDK KEYWORD1
|
||||
|
||||
#######################################
|
||||
# Methods and Functions (KEYWORD2)
|
||||
#######################################
|
||||
|
||||
begin KEYWORD2
|
||||
send KEYWORD2
|
||||
sendEvent KEYWORD2
|
||||
bindData KEYWORD2
|
||||
unbindData KEYWORD2
|
||||
|
||||
#######################################
|
||||
# Constants (LITERAL1)
|
||||
#######################################
|
||||
@@ -1,25 +0,0 @@
|
||||
{
|
||||
"name": "AliyunIoTSDK",
|
||||
"keywords": "ethernet, aliyun, mqtt, m2m, iot",
|
||||
"description": "A toplevel client library for aliyun MQTT messaging. 此库可以帮助你快速连接阿里云 IoT 平台,通过和阿里云物联网开发平台配合,可快速实现各种硬件应用,包括了很上层的封装,无需自己解析数据体,绑定事件即可。",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/xinyu198736/AliyunIoTSDK-Esp8266"
|
||||
},
|
||||
"version": "0.3",
|
||||
"examples": "examples/*/*.ino",
|
||||
"frameworks": "arduino",
|
||||
"platforms": ["espressif8266", "espressif32"],
|
||||
"dependencies": [
|
||||
{
|
||||
"name": "PubSubClient",
|
||||
"platforms": ["espressif8266", "espressif32"]
|
||||
},
|
||||
{
|
||||
"name": "ArduinoJson"
|
||||
},
|
||||
{
|
||||
"name": "Crypto"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
name=AliyunIoTSDK
|
||||
version=0.3
|
||||
author=yutou <xinyu198736@gmail.com>
|
||||
maintainer=yutou <xinyu198736@gmail.com>
|
||||
sentence=A toplevel client library for aliyun MQTT messaging.
|
||||
paragraph=此库可以帮助你快速连接阿里云 IoT 平台,通过和阿里云物联网开发平台配合,可快速实现各种硬件应用,包括了很上层的封装,无需自己解析数据体,绑定事件即可。
|
||||
category=Communication
|
||||
url=https://github.com/xinyu198736/arduino-aliyun-iot-sdk
|
||||
architectures=*
|
||||
@@ -1,353 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Filename: AliyunIoTSDK.cpp
|
||||
*
|
||||
* Description: basic SDK for ESP32
|
||||
*
|
||||
* Created by strawmanbobi 2022-01-03
|
||||
*
|
||||
* Copyright (c) 2016-2022 IRext
|
||||
*
|
||||
**/
|
||||
|
||||
#include "AliyunIoTSDK.h"
|
||||
#include <PubSubClient.h>
|
||||
#include <SHA256.h>
|
||||
|
||||
#define CHECK_INTERVAL 30000
|
||||
#define MESSAGE_BUFFER_SIZE 10
|
||||
#define MQTT_CONNECT_RETRY_MAX 3
|
||||
|
||||
static 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 MQTT_WAIT_GENERIC (10000)
|
||||
|
||||
#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 PubSubClient *client = NULL;
|
||||
|
||||
// bind callbacks, support at most 28 callbacks
|
||||
static pointerDesc pointerArray[20];
|
||||
static pPointerDesc pPointerArray;
|
||||
|
||||
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) {
|
||||
for (int i = 0; i < DATA_CALLBACK_SIZE; i++) {
|
||||
if (pointerArray[i].key) {
|
||||
bool hasKey = parm["params"].containsKey(pointerArray[i].key);
|
||||
if (hasKey) {
|
||||
pointerArray[i].fp(parm["params"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (!error) {
|
||||
parmPass(doc.as<JsonVariant>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool mqttConnecting = false;
|
||||
int AliyunIoTSDK::mqttCheckConnect() {
|
||||
int mqttStatus = 0;
|
||||
int connectRetry = 0;
|
||||
|
||||
Serial.println("INFO:\tAlink MQTT connection checking...");
|
||||
|
||||
if (client != NULL && false == mqttConnecting) {
|
||||
Serial.print("INFO:\tAlink MQTT client state = ");
|
||||
Serial.println(client->state());
|
||||
if (MQTT_CONNECTED != client->state()) {
|
||||
connectRetry = 0;
|
||||
while (false == client->connected()) {
|
||||
client->disconnect();
|
||||
Serial.print("INFO:\tConnecting to MQTT Server, clientId = ");
|
||||
Serial.print(clientId);
|
||||
Serial.print(", mqttUserName = ");
|
||||
Serial.print(mqttUsername);
|
||||
Serial.print(", mqttPwd = ");
|
||||
Serial.println(mqttPwd);
|
||||
mqttConnecting = true;
|
||||
if (client->connect(clientId, mqttUsername, mqttPwd)) {
|
||||
Serial.println("INFO:\tMQTT Connected!");
|
||||
} else {
|
||||
Serial.print("ERROR:\tMQTT Connect err: ");
|
||||
Serial.println(client->state());
|
||||
delay(MQTT_WAIT_GENERIC);
|
||||
connectRetry++;
|
||||
Serial.print("INFO:\tretry: ");
|
||||
Serial.println(connectRetry);
|
||||
mqttStatus = -1;
|
||||
if (connectRetry > MQTT_CONNECT_RETRY_MAX) {
|
||||
Serial.println("ERROR:\t max connect retry times reached");
|
||||
break;
|
||||
}
|
||||
}
|
||||
mqttConnecting = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return mqttStatus;
|
||||
}
|
||||
|
||||
int 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);
|
||||
|
||||
#if defined USE_STANDARD_THING_MODEL_TOPIC
|
||||
client->setCallback(callback);
|
||||
#endif
|
||||
Serial.print("connection check in begin\n");
|
||||
return mqttCheckConnect();
|
||||
}
|
||||
|
||||
int AliyunIoTSDK::loop() {
|
||||
int mqttStatus = 0;
|
||||
client->loop();
|
||||
if (millis() - lastMs >= CHECK_INTERVAL) {
|
||||
lastMs = millis();
|
||||
mqttStatus = mqttCheckConnect();
|
||||
// Serial.print("MQTT connect return: ");
|
||||
// Serial.println(mqttStatus);
|
||||
}
|
||||
|
||||
if (0 == mqttStatus) {
|
||||
messageBufferCheck();
|
||||
}
|
||||
|
||||
return mqttStatus;
|
||||
}
|
||||
|
||||
void AliyunIoTSDK::sendEvent(const char *eventId, const char *param) {
|
||||
char topicKey[156];
|
||||
snprintf(topicKey, sizeof(topicKey) - 1, "%d/%s/post", 0, 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 successfully: ");
|
||||
Serial.println(d);
|
||||
}
|
||||
|
||||
void AliyunIoTSDK::sendEvent(const char *eventId) {
|
||||
sendEvent(eventId, "{}");
|
||||
}
|
||||
|
||||
void AliyunIoTSDK::sendCustom(const char *topic, const char *eventBody) {
|
||||
boolean d = client->publish(topic, eventBody);
|
||||
Serial.print("publish:0 sucessfully:");
|
||||
Serial.println(d);
|
||||
}
|
||||
|
||||
void AliyunIoTSDK::sendCustomData(const char *topic, const uint8_t *data, int length) {
|
||||
boolean d = client->publish(topic, data, length);
|
||||
Serial.print("publish:0 sucessfully:");
|
||||
Serial.println(d);
|
||||
}
|
||||
|
||||
boolean AliyunIoTSDK::subscribe(const char* topic, int qos) {
|
||||
return client->subscribe(topic, qos);
|
||||
}
|
||||
|
||||
void AliyunIoTSDK::registerCustomCallback(MQTT_CALLBACK_SIGNATURE) {
|
||||
client->setCallback(callback);
|
||||
}
|
||||
|
||||
unsigned long lastSendMS = 0;
|
||||
|
||||
// check data sending buffer
|
||||
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();
|
||||
// send every 5 seconds
|
||||
if (nowMS - lastSendMS > 5000) {
|
||||
sendBuffer();
|
||||
lastSendMS = nowMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send data in 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 sucessfully:");
|
||||
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();
|
||||
}
|
||||
|
||||
#if defined USE_STANDARD_THING_MODEL_TOPIC
|
||||
int AliyunIoTSDK::bindData(char *key, pFuncPointer fp) {
|
||||
int i;
|
||||
for (i = 0; i < DATA_CALLBACK_SIZE; i++) {
|
||||
if (!pointerArray[i].fp) {
|
||||
pointerArray[i].key = key;
|
||||
pointerArray[i].fp = fp;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int AliyunIoTSDK::unbindData(char *key) {
|
||||
int i;
|
||||
for (i = 0; i < DATA_CALLBACK_SIZE; i++) {
|
||||
if (!strcmp(pointerArray[i].key, key)) {
|
||||
pointerArray[i].key = NULL;
|
||||
pointerArray[i].fp = NULL;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
@@ -1,179 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* Filename: AliyunIoTSDK.h
|
||||
*
|
||||
* Description: header file of basic SDK for ESP32
|
||||
*
|
||||
* Created by strawmanbobi 2022-01-03
|
||||
*
|
||||
* Copyright (c) 2016-2022 IRext
|
||||
*
|
||||
**/
|
||||
|
||||
#ifndef ALIYUN_IOT_SDK_H
|
||||
#define ALIYUN_IOT_SDK_H
|
||||
|
||||
#include <Arduino.h>
|
||||
#include <ArduinoJson.h>
|
||||
#include <PubSubClient.h>
|
||||
#include "Client.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*funcPointer)(JsonVariant ele);
|
||||
|
||||
typedef struct {
|
||||
char *key;
|
||||
funcPointer fp;
|
||||
} pointerDesc, *pPointerDesc;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
class AliyunIoTSDK {
|
||||
private:
|
||||
// MQTT client related parameters
|
||||
static char mqttPwd[256];
|
||||
static char clientId[256];
|
||||
static char mqttUsername[100];
|
||||
static char domain[150];
|
||||
|
||||
static void messageBufferCheck();
|
||||
static void sendBuffer();
|
||||
public:
|
||||
|
||||
// MQTT keep alive handler
|
||||
static int mqttCheckConnect();
|
||||
|
||||
// offical defined topic templates (not used)
|
||||
static char ALINK_TOPIC_PROP_POST[150];
|
||||
static char ALINK_TOPIC_PROP_SET[150];
|
||||
static char ALINK_TOPIC_EVENT[150];
|
||||
|
||||
// MQTT keep alive task
|
||||
static int loop();
|
||||
|
||||
/**
|
||||
* Initialize and connect to AliyunIoT
|
||||
* @param espClient : WiFi client
|
||||
* @param _productKey : AliyunIoT product key
|
||||
* @param _deviceName : AliyunIoT device name
|
||||
* @param _deviceSecret : AliyunIoT device secret
|
||||
* @param _region : AliyunIoT region
|
||||
*/
|
||||
static int begin(Client &espClient,
|
||||
const char *_productKey,
|
||||
const char *_deviceName,
|
||||
const char *_deviceSecret,
|
||||
const char *_region);
|
||||
|
||||
/**
|
||||
* Send data
|
||||
* @param param : JSON formated string with key and value : {"${key}":"${value}"}
|
||||
*/
|
||||
static void send(const char *param);
|
||||
|
||||
/**
|
||||
* Send single data in float
|
||||
* @param key : key
|
||||
* @param number : value
|
||||
*/
|
||||
static void send(char *key, float number);
|
||||
|
||||
/**
|
||||
* Send single data in integer
|
||||
* @param key : key
|
||||
* @param number : value
|
||||
*/
|
||||
static void send(char *key, int number);
|
||||
|
||||
/**
|
||||
* Send single data in double
|
||||
* @param key : key
|
||||
* @param number : value
|
||||
*/
|
||||
static void send(char *key, double number);
|
||||
|
||||
/**
|
||||
* Send single data in string
|
||||
* @param key : key
|
||||
* @param text : value
|
||||
*/
|
||||
static void send(char *key, char *text);
|
||||
|
||||
/**
|
||||
* Send standard thing model data
|
||||
* @param eventId : eventId predefined in AliyunIoT
|
||||
* @param param : JSON formated string with key and value : {"${key}":"${value}"}
|
||||
*/
|
||||
static void sendEvent(const char *eventId, const char *param);
|
||||
|
||||
/**
|
||||
* Send empty thing model data
|
||||
* @param eventId : eventId predefined in AliyunIoT
|
||||
*/
|
||||
static void sendEvent(const char *eventId);
|
||||
|
||||
/**
|
||||
* Send customized topic data
|
||||
*
|
||||
* @param topic : topic in string
|
||||
* @param eventBody : event body in string
|
||||
*/
|
||||
static void sendCustom(const char *topic, const char *eventBody);
|
||||
|
||||
/**
|
||||
* Send customized topic payload data
|
||||
*
|
||||
* @param topic : topic in string
|
||||
* @param data : data payload
|
||||
* @param length : payload length
|
||||
*/
|
||||
static void sendCustomData(const char *topic, const uint8_t *data, int length);
|
||||
|
||||
/**
|
||||
* Subscribe MQTT topic for Aliot
|
||||
*
|
||||
* @param topic : topic in string
|
||||
* @param qos : MQTT qos param
|
||||
* @return if succeeded
|
||||
*/
|
||||
static boolean subscribe(const char* topic, int qos);
|
||||
|
||||
/**
|
||||
* Register customized MQTT message callback
|
||||
*
|
||||
* @param callback : callback pointer
|
||||
*/
|
||||
static void registerCustomCallback(MQTT_CALLBACK_SIGNATURE);
|
||||
|
||||
#if defined USE_STANDARD_THING_MODEL_TOPIC
|
||||
/**
|
||||
* Register callback for downstream MQTT message
|
||||
*/
|
||||
static void bind(MQTT_CALLBACK_SIGNATURE);
|
||||
|
||||
/**
|
||||
* Register callback for downstream MQTT message with specific eventId
|
||||
* @param eventId : eventId predefined in AliyunIoT
|
||||
*/
|
||||
static void bindEvent(const char * eventId, MQTT_CALLBACK_SIGNATURE);
|
||||
|
||||
/**
|
||||
* Register callback for downstream MQTT message with specific key
|
||||
* @param key : key predefined in thing model
|
||||
*/
|
||||
static int bindData(char *key, funcPointer fp);
|
||||
|
||||
/**
|
||||
* Unregister callback for specified key
|
||||
* @param key : key predefined in thing model
|
||||
*/
|
||||
static int unbindData(char *key);
|
||||
#endif
|
||||
|
||||
};
|
||||
#endif /* ALIYUN_IOT_SDK_H */
|
||||
@@ -1,210 +0,0 @@
|
||||
> 文中提到的 AliyunIoTSDK 这个 arduino 库,可以在 arduino 库商店里搜索到(搜索 AliyunIoTSDK),但是版本可能不一定是最新的,也可以手动把 github 上的项目 clone 下来,放到 arduino 的 library 库下,保证功能是最新的。
|
||||
|
||||
> github 地址:[https://github.com/xinyu198736/arduino-aliyun-iot-sdk](https://github.com/xinyu198736/arduino-aliyun-iot-sdk)
|
||||
|
||||
> 有问题,欢迎提 issues 或 PR 共建,我对 c++ 不是特别熟悉,写出来的代码可能不一定是最合理的。
|
||||
|
||||
## 阿里云物联网平台简介
|
||||
|
||||
阿里云物联网平台提供接入物联网场景的一整套基础设施,强大的在线配置能力,基本可以实现无代码开发接入各类应用,实现大规模的物联网应用场景。
|
||||
|
||||
核心能力模块包括:
|
||||
|
||||
- 产品&设备创建和管理
|
||||
- 服务可视化开发
|
||||
- 控制界面可视化开发
|
||||
- 数据分析和监控运维等辅助设施
|
||||
|
||||
基于以上功能模块,可以快速在阿里云定义设备模型和事件等信息,然后快速创建和接入设备,再利用可视化的开发平台,可以零代码实现非常强大的功能。
|
||||
|
||||
不过,今天我们要介绍一个比较特殊的接入场景,使用嵌入式系统快速接入阿里云物联网平台,这里以 esp8266 为例。
|
||||
|
||||
> esp8266 是一款集成网络模块的嵌入式芯片,自带 32 位处理器,各种外设接口,wifi 模块等,可以使用 arduino 平台进行编程,兼容大量 arduino 库。
|
||||
|
||||
|
||||
本文就是以 arduino 编程方式,使用的 esp8266 开发板为 NodeMCU 1.0 ,淘宝成本仅为 8~10 元。
|
||||
|
||||
<a name="c931653c"></a>
|
||||
## 场景
|
||||
|
||||
本次我们想利用 ESP8266 芯片实现一个简单的可远程控制的空调控制器,具备以下功能:
|
||||
|
||||
- 批量部署,可通过云平台集中管理和远程控制空调开关、温度、模式等
|
||||
- 芯片可感知环境温度、亮度,并上报物联网平台
|
||||
- 芯片可远程触发事件,上报物联网平台
|
||||
- 成本尽量低,不超过 30 元
|
||||
|
||||
首先,我们进行硬件选型,经过功能评估,选用以下硬件:
|
||||
|
||||
- nodemcu 核心板(基于 esp8266) 10.89元/个
|
||||
- DS18B20模块 温度传感 3.18元/个
|
||||
- GY-30 光照传感 4.38元/个
|
||||
- 红外发射板 3.5元/个
|
||||
- 杜邦线母对母 1.45元/串
|
||||
- 电源插头 1.4元/个
|
||||
- 数据线 1.8元/个
|
||||
- 按钮开关 0.75元/个
|
||||
|
||||
<a name="dfe89ca1"></a>
|
||||
## 开始接入
|
||||
|
||||
具体的连线,我们这里不展开,我们主要探讨如何使用 arduino 快速接入物联网平台。
|
||||
|
||||
第一步,在物联网创建产品,建立物模型,并创建设备,获取设备配置信息。
|
||||
|
||||

|
||||
创建产品,并定义物模型
|
||||
|
||||

|
||||
创建设备,与产品绑定,获取设备鉴权信息<br />
|
||||
<br />第二步,用 arduino 连接 nodemcu 开发板,开始硬件端的开发。<br />
|
||||

|
||||
具体接线可以自己研究,这里只是个展示,哈哈<br />
|
||||
|
||||
<a name="qMAHi"></a>
|
||||
## 硬件端开发
|
||||
这里不讨论如何使用 arduino 进行 esp8266 芯片开发,直接进入代码环节,相关基础知识可以百度一下。
|
||||
|
||||
首先,亮相一个我自己封装的 aliyun iot 上层 SDK( [arduino-aliyun-iot-sdk](https://github.com/xinyu198736/arduino-aliyun-iot-sdk) ),底层连接基于 PubSubClient 库,并且对模型操作做了一些上层封装,可以方便的发送数据和订阅远程指令。
|
||||
```cpp
|
||||
// 引入 wifi 模块,并实例化,不同的芯片这里的依赖可能不同
|
||||
#include <ESP8266WiFi.h>
|
||||
static WiFiClient espClient;
|
||||
|
||||
// 引入阿里云 IoT SDK
|
||||
#include <AliyunIoTSDK.h>
|
||||
|
||||
// 设置产品和设备的信息,从阿里云设备信息里查看
|
||||
#define PRODUCT_KEY "xxx"
|
||||
#define DEVICE_NAME "Device_D"
|
||||
#define DEVICE_SECRET "xxxxxxxxxxxxxx"
|
||||
#define REGION_ID "cn-shanghai"
|
||||
|
||||
// 设置 wifi 信息
|
||||
#define WIFI_SSID "xxxxx"
|
||||
#define WIFI_PASSWD "xxxxx"
|
||||
|
||||
void setup()
|
||||
{
|
||||
Serial.begin(115200);
|
||||
|
||||
// 初始化 wifi
|
||||
wifiInit(WIFI_SSID, WIFI_PASSWD);
|
||||
|
||||
// 初始化 iot,需传入 wifi 的 client,和设备产品信息
|
||||
AliyunIoTSDK::begin(espClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET, REGION_ID);
|
||||
|
||||
// 绑定一个设备属性回调,当远程修改此属性,会触发 powerCallback
|
||||
// PowerSwitch 是在设备产品中定义的物联网模型的 id
|
||||
AliyunIoTSDK::bindData("PowerSwitch", powerCallback);
|
||||
|
||||
// 发送一个数据到云平台,LightLuminance 是在设备产品中定义的物联网模型的 id
|
||||
AliyunIoTSDK::send("LightLuminance", 100);
|
||||
}
|
||||
|
||||
void loop()
|
||||
{
|
||||
AliyunIoTSDK::loop();
|
||||
}
|
||||
|
||||
// 初始化 wifi 连接
|
||||
void wifiInit(const char *ssid, const char *passphrase)
|
||||
{
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.begin(ssid, passphrase);
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
delay(1000);
|
||||
Serial.println("WiFi not Connect");
|
||||
}
|
||||
Serial.println("Connected to AP");
|
||||
}
|
||||
|
||||
// 电源属性修改的回调函数
|
||||
void powerCallback(JsonVariant p)
|
||||
{
|
||||
int PowerSwitch = p["PowerSwitch"];
|
||||
if (PowerSwitch == 1)
|
||||
{
|
||||
// 启动设备
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
核心代码只有三句:
|
||||
|
||||
```cpp
|
||||
// 初始化 iot,需传入 wifi 的 client,和设备产品信息
|
||||
AliyunIoTSDK::begin(espClient, PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET, REGION_ID);
|
||||
|
||||
// 绑定一个设备属性回调,当远程修改此属性,会触发 powerCallback
|
||||
// PowerSwitch 是在设备产品中定义的物联网模型的 id
|
||||
AliyunIoTSDK::bindData("PowerSwitch", powerCallback);
|
||||
|
||||
// 发送一个数据到云平台,LightLuminance 是在设备产品中定义的物联网模型的 id
|
||||
AliyunIoTSDK::send("LightLuminance", 100);
|
||||
```
|
||||
|
||||
注意这里的数据绑定和数据发送,是非常有用和方便的,不需要自己去写做匹配的代码,也不需要自己组装数据,并且会自动合并数据,节约上报的频率。
|
||||
|
||||
将设备配置信息配置进代码,烧录到板子,运行即可,是不是炒鸡方便呢。
|
||||
|
||||
注意事项:
|
||||
|
||||
- 阿里云对客户端的链接参数有一些限制,需要修改 PubSubClient 的连接参数,否则无法使用
|
||||
- PubSubClient 中的 MQTT_MAX_PACKET_SIZE 修改为 1024
|
||||
- PubSubClient 中的 MQTT_KEEPALIVE 修改为 60
|
||||
- 默认 5000ms 检测一次连接状态,可以通过 AliyunIoTSDK.cpp 中的 CHECK_INTERVAL 修改此值
|
||||
- 掉线后会一直尝试重新连接,可能会触发阿里云的一些限流规则(已经做了规避),并且会导致挤掉其他同设备 ID 的设备
|
||||
<a name="ZzbqJ"></a>
|
||||
### 资源
|
||||
目前 AliyunIoTSDK 这个 arduino 库,可以在 arduino 库商店里搜索到(搜索 AliyunIoTSDK),但是版本可能不一定是最新的,可以手动把 github 上的项目 clone 下来,放到 arduino 的 library 库下,保证功能是最新的。
|
||||
|
||||
github 地址:[https://github.com/xinyu198736/arduino-aliyun-iot-sdk](https://github.com/xinyu198736/arduino-aliyun-iot-sdk)
|
||||
|
||||
有问题,欢迎提 issues 或 PR 共建,我对 c++ 不是特别熟悉,写出来的代码可能不一定是最合理的。
|
||||
|
||||
<a name="gycpL"></a>
|
||||
## 可视化开发界面和服务
|
||||
完成了设备的连接之后,已经迈出了第一步,如果你想要自己开发一个界面,可视化的控制空调,或者想利用一些远程设备的数据触发一些自定义的规则和逻辑(例如如果某个数据超标,报警到钉钉群等),用阿里云的物联网平台的话,这些都不需要自己去开发,直接使用物联网平台的“开发服务”拖拖拽拽即可完成。
|
||||
|
||||
<a name="7z0mV"></a>
|
||||
### 界面开发
|
||||
界面开发的核心是 拖动布局、数据源绑定、交互绑定、自定义变量等。
|
||||
<br />
|
||||

|
||||
布局界面,右侧可以设置各种控件属性
|
||||
<br />
|
||||

|
||||
数据源绑定,可以方便的和产品模型数据绑定
|
||||
<br />
|
||||

|
||||
交互绑定,可以与服务进行绑定,触发某个服务,并传入设备属性<br />
|
||||
<br />
|
||||

|
||||
控件可以给自定义变量赋值,其他组件可以与变量绑定
|
||||
|
||||
使用上述的功能,加上各种设备绑定功能,基本可以完成一个很复杂的界面的开发,但是如果你需要通过交互触发远程指令,或者监听远程的数据做一些判断逻辑,光有界面是不够的。
|
||||
|
||||
<a name="D26H8"></a>
|
||||
### 服务开发
|
||||
上面提到的逻辑开发,就需要使用“服务开发”了,服务开发的触发点主要是两类:设备触发、界面交互触发。
|
||||
|
||||

|
||||
设备触发的服务,中间的路径选择是用来做条件判断分流的,最右侧是触发通知逻辑节点<br />
|
||||
<br />
|
||||

|
||||
一个温度控制服务,通过界面控件触发,最终生成设备控制逻辑<br />
|
||||
|
||||
<a name="UnwgK"></a>
|
||||
## 结语
|
||||
本文不是 arduino 教学,也不是 esp8266 教学,更不是硬件教学,所以没有展开细节,需要读者具备一定的基础,请各位谅解,如果对 arduino 开发感兴趣,可以加我微信交流(微信号:mier963,注明 arduino)。
|
||||
|
||||
最后展示一下成果:<br />
|
||||

|
||||
一个成品,包含四个传感器模块,包括一个硬件按钮<br />
|
||||
<br />
|
||||

|
||||
空调远程控制界面<br />
|
||||
<br />另外,还可以给这个控制器加入一些很有意思的功能,例如定点关闭所有设备,根据环境亮度开关空调等。
|
||||
|
||||
Reference in New Issue
Block a user