Files
iris-kit/esp8285/lib/AliyunIoTSDK/src/AliyunIoTSDK.cpp
2025-01-03 09:42:38 +08:00

358 lines
10 KiB
C++

/**
*
* 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] = { 0 };
SHA256 sha256;
const char *key = ds.c_str();
size_t keySize = ds.length();
#if defined SIGN_DEBUG_VERBOSE
Serial.print("DEBUG\tAliot sign src = ");
Serial.print(signcontent.length());
Serial.print(" : ");
Serial.println(signcontent.c_str());
Serial.print("DEBUG\tAliot sign key = ");
Serial.print(keySize);
Serial.print(" : ");
Serial.println(key);
#endif
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("INFO\tMessage 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:\tAliot connection retry: ");
Serial.println(connectRetry);
mqttStatus = -1;
if (connectRetry > MQTT_CONNECT_RETRY_MAX) {
Serial.println("ERROR:\tMax connect retry times reached");
break;
}
}
mqttConnecting = false;
}
}
}
return mqttStatus;
}
int AliyunIoTSDK::begin(PubSubClient &mqtt_client,
const char *_productKey,
const char *_deviceName,
const char *_deviceSecret,
const char *_region) {
if (NULL == client) {
client = &mqtt_client;
}
productKey = _productKey;
deviceName = _deviceName;
deviceSecret = _deviceSecret;
region = _region;
long times = millis();
String timestamp = String(times);
String deviceId = String(productKey) + String(".") + String(deviceName);
sprintf(clientId, "%s.%s|securemode=2,signmethod=hmacsha256,timestamp=%s|", productKey, deviceName, timestamp.c_str());
String signcontent = "clientId";
signcontent += deviceId;
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("INFO\tConnection check in begin\n");
return mqttCheckConnect();
}
int AliyunIoTSDK::loop() {
int mqttStatus = 0;
client->loop();
unsigned long thisMs = millis();
if (thisMs - lastMs >= CHECK_INTERVAL) {
mqttStatus = mqttCheckConnect();
lastMs = thisMs;
}
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.print("INFO\t");
Serial.println(jsonBuf);
boolean d = client->publish(topicKey, jsonBuf);
Serial.print("INFO\tpublish: 0 successfully: ");
Serial.println(d);
}
void AliyunIoTSDK::sendEvent(const char *eventId) {
sendEvent(eventId, "{}");
}
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++;
}
}
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.print("INFO\t");
Serial.println(jsonBuf);
boolean d = client->publish(ALINK_TOPIC_PROP_POST, jsonBuf);
Serial.print("INFO\tpublish: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