tried to support aliot again(untested)

This commit is contained in:
strawmanbobi
2024-02-12 19:26:03 +08:00
parent c9d70c68c7
commit e96ab56e62
37 changed files with 123 additions and 19 deletions

View File

@@ -1 +0,0 @@
{"type": "library", "name": "AliyunIoTSDK", "version": "0.3.0", "spec": {"owner": "xinyu198736", "id": 6674, "name": "AliyunIoTSDK", "requirements": null, "url": null}}

View File

@@ -1,3 +0,0 @@
0.1
*

View File

@@ -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.

View File

@@ -1,192 +0,0 @@
# Arduino TopLevel Client for aliyun IoT Platform
`AliyunIoTSDK` 可以帮助你快速连接阿里云 IoT 平台,通过和阿里云物联网开发平台配合,可快速实现各种硬件应用,包括了很上层的封装,无需自己解析数据体,绑定事件即可,在 esp8266 平台充分测试NodeMCU 1.0
## update
- v0.2 增加属性发送 buffer5秒一次或者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.

View File

@@ -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)
#######################################

View File

@@ -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"
}
]
}

View File

@@ -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=*

View File

@@ -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

View File

@@ -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 */

View File

@@ -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 快速接入物联网平台。
第一步,在物联网创建产品,建立物模型,并创建设备,获取设备配置信息。
![image.png](https://blog.souche.com/content/images/2019/09/1568620395927-b7090d59-5958-4050-a0e7-7205faafb455.png)
创建产品,并定义物模型
![image.png](https://blog.souche.com/content/images/2019/09/1568620350930-270bbfa3-22b8-4711-b6b8-ff6f38a8d49a.png)
创建设备,与产品绑定,获取设备鉴权信息<br />
<br />第二步,用 arduino 连接 nodemcu 开发板,开始硬件端的开发。<br />
![IMG_2546.JPG](https://blog.souche.com/content/images/2019/09/1568642438839-a595c156-3afb-4ef8-8213-d67433be474a.jpeg)
具体接线可以自己研究,这里只是个展示,哈哈<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 />
![image.png](https://blog.souche.com/content/images/2019/09/1568630094545-d440e173-23f0-4d63-81e7-00d6b6ce8811.png)
布局界面,右侧可以设置各种控件属性
<br />
![image.png](https://blog.souche.com/content/images/2019/09/1568630244900-18ee8b99-1e80-409a-8482-981fd596d095.png)
数据源绑定,可以方便的和产品模型数据绑定
<br />
![image.png](https://blog.souche.com/content/images/2019/09/1568630294665-d5cf7a85-ad06-491c-a7bf-65f6439450f8.png)
交互绑定,可以与服务进行绑定,触发某个服务,并传入设备属性<br />
<br />
![image.png](https://blog.souche.com/content/images/2019/09/1568642695402-cc25fedc-8c3f-4e32-90a9-89c0eec2f712.png)
控件可以给自定义变量赋值,其他组件可以与变量绑定
使用上述的功能,加上各种设备绑定功能,基本可以完成一个很复杂的界面的开发,但是如果你需要通过交互触发远程指令,或者监听远程的数据做一些判断逻辑,光有界面是不够的。
<a name="D26H8"></a>
### 服务开发
上面提到的逻辑开发,就需要使用“服务开发”了,服务开发的触发点主要是两类:设备触发、界面交互触发。
![image.png](https://blog.souche.com/content/images/2019/09/1568643025480-f10c038d-8a9b-44a1-b270-51625b56a137.png)
设备触发的服务,中间的路径选择是用来做条件判断分流的,最右侧是触发通知逻辑节点<br />
<br />
![image.png](https://blog.souche.com/content/images/2019/09/1568643229960-62754bc2-4ddf-4724-ba99-ed81459f9bc2.png)
一个温度控制服务,通过界面控件触发,最终生成设备控制逻辑<br />
<a name="UnwgK"></a>
## 结语
本文不是 arduino 教学,也不是 esp8266 教学,更不是硬件教学,所以没有展开细节,需要读者具备一定的基础,请各位谅解,如果对 arduino 开发感兴趣可以加我微信交流微信号mier963注明 arduino
最后展示一下成果:<br />
![IMG_7254.JPG](https://blog.souche.com/content/images/2019/09/1568643588452-48f528a4-29e2-41f9-99bb-0a004600bd8a.jpeg)
一个成品,包含四个传感器模块,包括一个硬件按钮<br />
<br />
![image.png](https://blog.souche.com/content/images/2019/09/1568643652167-2f7d14b9-77d4-4a36-8172-ad7e9b9bc2ae.png)
空调远程控制界面<br />
<br />另外,还可以给这个控制器加入一些很有意思的功能,例如定点关闭所有设备,根据环境亮度开关空调等。