1.设备端sdk移除

This commit is contained in:
zhuangpeng.li
2024-08-08 23:13:54 +08:00
parent 93c73501a2
commit ebad891de7
2354 changed files with 0 additions and 780048 deletions

View File

@@ -1,157 +0,0 @@
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
String randomName="wumei-device"+(String)random(1000);
const char *ap_ssid =randomName.c_str();
const char *ap_password = ""; //开放式网络
char sta_ssid[32] = {0};
char sta_password[64] = {0};
char sta_user_id[32] = {0};
IPAddress local_IP(192, 168, 4, 1);
IPAddress gateway(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);
void initApConfig();
void initWebServer();
void handleConfig();
void handleStatus();
void handleNotFound();
ESP8266WebServer server(80);
void setup(void)
{
//打开串行端口:
Serial.begin(115200);
// AP模式
initApConfig();
// web服务
initWebServer();
}
void loop(void)
{
// Web服务端
server.handleClient();
}
/**
* AP模式
*/
void initApConfig()
{
WiFi.mode(WIFI_AP_STA);
WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP(ap_ssid, ap_password);
printMsg("已启动AP配网IP地址" + WiFi.softAPIP().toString()+" 热点名称:"+(String)ap_ssid);
}
/**
* 初始化webserver配置
*/
void initWebServer()
{
server.on("/status", HTTP_GET, handleStatus);
server.on("/config", HTTP_POST, handleConfig);
server.onNotFound(handleNotFound);
server.enableCORS(true);
server.begin();
printMsg("HTTP服务已启动");
}
/**
* 连接WIFI
*/
void connectWifi()
{
printMsg("连接WIFI");
WiFi.begin(sta_ssid, sta_password);
int cnt = 0;
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
cnt++;
Serial.print(".");
if (cnt >= 30)
{
printMsg("设备连接WIFI超时请重新配网");
return;
}
}
server.close();
WiFi.softAPdisconnect(false);
printMsg("Http服务和热点已关闭设备已连接WIFI");
}
/**
* 检测设备状态
*/
void handleStatus(){
server.send(200, "text/plain;charset=utf-8", "AP配网已准备就绪");
}
/**
* 配网:下发配置信息
*/
void handleConfig()
{
printMsg("进入配网......");
// wifi名称、wifi密码、用户编号
if (server.hasArg("SSID") && server.hasArg("password") && server.hasArg("userId"))
{
strcpy(sta_ssid, server.arg("SSID").c_str());
strcpy(sta_password, server.arg("password").c_str());
strcpy(sta_user_id, server.arg("userId").c_str());
printMsg("收到WIFI名称" + (String)sta_ssid);
printMsg("收到WIFI密码" + (String)sta_password);
printMsg("收到用户编号:" + (String)sta_user_id);
}
else
{
printMsg("配网必须传递用户编号、WIFI名称和WIFI密码,配网失败");
server.send(500, "text/plain;charset=utf-8", "配网必须传递用户编号、WIFI名称和WIFI密码配网失败");
return;
}
// 可选字段
if (server.hasArg("deviceNum"))
{
printMsg("收到设备编号:" + server.arg("deviceNum"));
}
if (server.hasArg("extra"))
{
printMsg("收到补充信息:" + server.arg("extra"));
}
// TODO 可增加设备连接WIFI测试
server.send(200, "text/plain;charset=utf-8", "设备已更新WIFI配置开始连接WIFI...");
connectWifi();
}
void handleNotFound()
{
printMsg("进入预检请求或请求地址找不到");
if (server.method() == HTTP_OPTIONS)
{
// 处理浏览器跨域问题
server.sendHeader("Access-Control-Max-Age", "10000");
server.sendHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS");
server.sendHeader("Access-Control-Allow-Headers", "*");
server.send(204);
}
else
{
server.send(404, "text/plain;charset=utf-8", "请求的地址找不到或无法访问");
}
}
//打印提示信息
void printMsg(String msg)
{
Serial.print("\r\n[");
Serial.print(millis());
Serial.print("ms]");
Serial.print(msg);
}

View File

@@ -1,7 +0,0 @@
{
"port": "COM3",
"configuration": "FlashFreq=80,UploadSpeed=921600",
"board": "esp32:esp32:nano32",
"output": "./build",
"sketch": "Arduino-Esp32.ino"
}

View File

@@ -1,85 +0,0 @@
/***********************************************************
* author: LaoHuang
* create: 2022-04-14
* emailrememberyousaid@163.com
* source:https://github.com/kerwincui/wumei-smart
* board:esp32 版本 1.0.6
***********************************************************/
#include "Helper.h"
long lastMqttConn; // 上次mqtt连接时间
long lastPublishMonitor; // 上次发布监测数据时间
long lastTimerMonitor; // 上次定时发布监测数据
/**
* 启动
*/
void setup()
{
//打开串行端口:
Serial.begin(115200);
printMsg("wumei smart device starting...");
connectWifi();
connectMqtt();
}
/**
* 循环执行
*/
void loop()
{
// Wifi掉线重连
if (WiFi.status() != WL_CONNECTED)
{
connectWifi();
}
// 非阻塞Mqtt重连间隔30秒
if (WiFi.status() == WL_CONNECTED)
{
long now = millis();
if (!mqttClient.connected())
{
if (now - lastMqttConn > 30000)
{
lastMqttConn = now;
connectMqtt();
}
}
else
{
mqttClient.loop();
}
}
// 非阻塞发布实时监测数据,间隔默认1秒
if(WiFi.status() == WL_CONNECTED && monitorCount>0){
long now = millis();
if (now - lastPublishMonitor > monitorInterval)
{
lastPublishMonitor = now;
monitorCount--;
publishMonitor();
}
}
// 非阻塞定时上报测试用60秒发布一次
if(WiFi.status() == WL_CONNECTED){
long now = millis();
if (now - lastTimerMonitor > 60000)
{
lastTimerMonitor = now;
printMsg("执行定时上报");
// 发布事件
publishEvent();
// 发布时钟同步
publishNtp();
// 发布属性(监测值)
String msg=randomPropertyData();
publishProperty(msg);
}
}
}

View File

@@ -1,137 +0,0 @@
#include "Base64.h"
#if (defined(__AVR__))
#include <avr\pgmspace.h>
#else
#include <pgmspace.h>
#endif
const char PROGMEM b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
/* 'Private' declarations */
inline void a3_to_a4(unsigned char * a4, unsigned char * a3);
inline void a4_to_a3(unsigned char * a3, unsigned char * a4);
inline unsigned char b64_lookup(char c);
int base64_encode(char *output, char *input, int inputLen) {
int i = 0, j = 0;
int encLen = 0;
unsigned char a3[3];
unsigned char a4[4];
while(inputLen--) {
a3[i++] = *(input++);
if(i == 3) {
a3_to_a4(a4, a3);
for(i = 0; i < 4; i++) {
output[encLen++] = pgm_read_byte(&b64_alphabet[a4[i]]);
}
i = 0;
}
}
if(i) {
for(j = i; j < 3; j++) {
a3[j] = '\0';
}
a3_to_a4(a4, a3);
for(j = 0; j < i + 1; j++) {
output[encLen++] = pgm_read_byte(&b64_alphabet[a4[j]]);
}
while((i++ < 3)) {
output[encLen++] = '=';
}
}
output[encLen] = '\0';
return encLen;
}
int base64_decode(char * output, char * input, int inputLen) {
int i = 0, j = 0;
int decLen = 0;
unsigned char a3[3];
unsigned char a4[4];
while (inputLen--) {
if(*input == '=') {
break;
}
a4[i++] = *(input++);
if (i == 4) {
for (i = 0; i <4; i++) {
a4[i] = b64_lookup(a4[i]);
}
a4_to_a3(a3,a4);
for (i = 0; i < 3; i++) {
output[decLen++] = a3[i];
}
i = 0;
}
}
if (i) {
for (j = i; j < 4; j++) {
a4[j] = '\0';
}
for (j = 0; j <4; j++) {
a4[j] = b64_lookup(a4[j]);
}
a4_to_a3(a3,a4);
for (j = 0; j < i - 1; j++) {
output[decLen++] = a3[j];
}
}
output[decLen] = '\0';
return decLen;
}
int base64_enc_len(int plainLen) {
int n = plainLen;
return (n + 2 - ((n + 2) % 3)) / 3 * 4;
}
int base64_dec_len(char * input, int inputLen) {
int i = 0;
int numEq = 0;
for(i = inputLen - 1; input[i] == '='; i--) {
numEq++;
}
return ((6 * inputLen) / 8) - numEq;
}
inline void a3_to_a4(unsigned char * a4, unsigned char * a3) {
a4[0] = (a3[0] & 0xfc) >> 2;
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
a4[3] = (a3[2] & 0x3f);
}
inline void a4_to_a3(unsigned char * a3, unsigned char * a4) {
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
}
inline unsigned char b64_lookup(char c) {
if(c >='A' && c <='Z') return c - 'A';
if(c >='a' && c <='z') return c - 71;
if(c >='0' && c <='9') return c + 4;
if(c == '+') return 62;
if(c == '/') return 63;
return -1;
}

View File

@@ -1,79 +0,0 @@
/*
* Copyright (c) 2013 Adam Rudd.
* See LICENSE for more information
*/
#ifndef _BASE64_H
#define _BASE64_H
/* b64_alphabet:
* Description: Base64 alphabet table, a mapping between integers
* and base64 digits
* Notes: This is an extern here but is defined in Base64.c
*/
extern const char b64_alphabet[];
/* base64_encode:
* Description:
* Encode a string of characters as base64
* Parameters:
* output: the output buffer for the encoding, stores the encoded string
* input: the input buffer for the encoding, stores the binary to be encoded
* inputLen: the length of the input buffer, in bytes
* Return value:
* Returns the length of the encoded string
* Requirements:
* 1. output must not be null or empty
* 2. input must not be null
* 3. inputLen must be greater than or equal to 0
*/
int base64_encode(char *output, char *input, int inputLen);
/* base64_decode:
* Description:
* Decode a base64 encoded string into bytes
* Parameters:
* output: the output buffer for the decoding,
* stores the decoded binary
* input: the input buffer for the decoding,
* stores the base64 string to be decoded
* inputLen: the length of the input buffer, in bytes
* Return value:
* Returns the length of the decoded string
* Requirements:
* 1. output must not be null or empty
* 2. input must not be null
* 3. inputLen must be greater than or equal to 0
*/
int base64_decode(char *output, char *input, int inputLen);
/* base64_enc_len:
* Description:
* Returns the length of a base64 encoded string whose decoded
* form is inputLen bytes long
* Parameters:
* inputLen: the length of the decoded string
* Return value:
* The length of a base64 encoded string whose decoded form
* is inputLen bytes long
* Requirements:
* None
*/
int base64_enc_len(int inputLen);
/* base64_dec_len:
* Description:
* Returns the length of the decoded form of a
* base64 encoded string
* Parameters:
* input: the base64 encoded string to be measured
* inputLen: the length of the base64 encoded string
* Return value:
* Returns the length of the decoded form of a
* base64 encoded string
* Requirements:
* 1. input must not be null
* 2. input must be greater than or equal to zero
*/
int base64_dec_len(char *input, int inputLen);
#endif // _BASE64_H

View File

@@ -1,555 +0,0 @@
/***********************************************************
* author: LaoHuang
* create: 2022-04-14
* emailrememberyousaid@163.com
* source:https://github.com/kerwincui/wumei-smart
* board:esp32 版本1.0.6
***********************************************************/
#include "Helper.h"
String g_time;
WiFiClient wifiClient;
PubSubClient mqttClient;
float rssi = 0;
char wumei_iv[17] = "wumei-smart-open";
int monitorCount = 0;
long monitorInterval = 1000;
//==================================== 这是需要配置的项 ===============================
// Wifi配置
char *wifiSsid = "wifi-ssid";
char *wifiPwd = "wifi-password";
// 设备信息配置
String deviceNum = "D6329VL54419L1Y0";
String userId = "1";
String productId = "2";
float firmwareVersion = 1.0;
// 经度和纬度可选,如果产品使用设备定位,则必须传
float latitude=0;
float longitude=0;
// Mqtt配置
char *mqttHost = "wumei.live";
int mqttPort = 1883;
char *mqttUserName = "wumei-smart";
char *mqttPwd = "P5FJKZJHIR82GNB2";
char mqttSecret[17] = "K63C4EA3AI5TER97";
// 产品启用授权码,则授权码不能为空
String authCode="";
// NTP地址用于获取时间,可选的修改为自己部署项目的地址)
String ntpServer = "http://wumei.live:8080/iot/tool/ntp?deviceSendTime=";
//====================================================================================
// 订阅的主题
String prefix = "/" + productId + "/" + deviceNum;
String sInfoTopic = prefix + "/info/get";
String sOtaTopic = prefix + "/ota/get";
String sNtpTopic = prefix + "/ntp/get";
String sPropertyTopic = prefix + "/property/get";
String sFunctionTopic = prefix + "/function/get";
String sPropertyOnline = prefix + "/property-online/get";
String sFunctionOnline = prefix + "/function-online/get";
String sMonitorTopic = prefix + "/monitor/get";
// 发布的主题
String pInfoTopic = prefix + "/info/post";
String pNtpTopic = prefix + "/ntp/post";
String pPropertyTopic = prefix + "/property/post";
String pFunctionTopic = prefix + "/function/post";
String pMonitorTopic = prefix + "/monitor/post";
String pEventTopic = prefix + "/event/post";
// 物模型-属性处理
void processProperty(String payload)
{
StaticJsonDocument<1024> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error)
{
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
for (JsonObject object : doc.as<JsonArray>())
{
// 匹配云端定义的属性(不包含属性中的监测数据)
const char *id = object["id"];
const char *value = object["value"];
printMsg((String)id + "" + (String)value);
}
// 最后发布属性,服务端订阅存储(重要)
publishProperty(payload);
}
// 物模型-功能处理
void processFunction(String payload)
{
StaticJsonDocument<1024> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error)
{
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
for (JsonObject object : doc.as<JsonArray>())
{
// 匹配云端定义的功能
const char *id = object["id"];
const char *value = object["value"];
if (strcmp(id, "switch") == 0)
{
printMsg("开关 switch" + (String)value);
}
else if (strcmp(id, "gear") == 0)
{
printMsg("档位 gear" + (String)value);
}
else if (strcmp(id, "light_color") == 0)
{
printMsg("灯光颜色 light_color" + (String)value);
}
else if (strcmp(id, "message") == 0)
{
printMsg("屏显消息 message" + (String)value);
}
else if (strcmp(id, "report_monitor") == 0)
{
String msg = randomPropertyData();
printMsg("订阅到上报监测数据指令,上报数据:");
printMsg(msg);
publishProperty(msg);
}
}
// 最后发布功能,服务端订阅存储(重要)
publishFunction(payload);
}
// Mqtt回调
void callback(char *topic, byte *payload, unsigned int length)
{
blink();
printMsg("接收数据:");
String data = "";
for (int i = 0; i < length; i++)
{
Serial.print((char)payload[i]);
data += (char)payload[i];
}
if (strcmp(topic, sOtaTopic.c_str()) == 0)
{
printMsg("订阅到设备升级指令...");
StaticJsonDocument<256> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error)
{
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
String newVersion = doc["version"];
String downloadUrl = doc["downloadUrl"];
printMsg("固件版本:"+newVersion);
printMsg("下载地址:"+downloadUrl);
}
else if (strcmp(topic, sInfoTopic.c_str()) == 0)
{
printMsg("订阅到设备信息...");
// 发布设备信息
publishInfo();
}
else if (strcmp(topic, sNtpTopic.c_str()) == 0)
{
printMsg("订阅到NTP时间...");
StaticJsonDocument<256> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error)
{
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
float deviceSendTime = doc["deviceSendTime"];
float serverSendTime = doc["serverSendTime"];
float serverRecvTime = doc["serverRecvTime"];
float deviceRecvTime = millis();
float now = (serverSendTime + serverRecvTime + deviceRecvTime - deviceSendTime) / 2;
printMsg("当前时间:" + String(now, 0));
}
else if (strcmp(topic, sPropertyTopic.c_str()) == 0 || strcmp(topic, sPropertyOnline.c_str()) == 0)
{
printMsg("订阅到属性指令...");
processProperty(data);
}
else if (strcmp(topic, sFunctionTopic.c_str()) == 0 || strcmp(topic, sFunctionOnline.c_str()) == 0)
{
printMsg("订阅到功能指令...");
processFunction(data);
}
else if (strcmp(topic, sMonitorTopic.c_str()) == 0)
{
printMsg("订阅到实时监测指令...");
StaticJsonDocument<128> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error)
{
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
monitorCount = doc["count"];
monitorInterval = doc["interval"];
}
}
// 连接wifi
void connectWifi()
{
printMsg("连接 ");
Serial.print(wifiSsid);
WiFi.mode(WIFI_STA);
WiFi.begin(wifiSsid, wifiPwd);
while (WiFi.status() != WL_CONNECTED)
{
delay(500);
Serial.print(".");
}
printMsg("WiFi连接成功");
printMsg("IP地址: ");
Serial.print(WiFi.localIP());
}
// 连接mqtt
void connectMqtt()
{
printMsg("连接Mqtt服务器...");
// 生成mqtt认证密码(设备加密认证密码加密格式为mqtt密码 & 过期时间 & 授权码,其中授权码为可选)
String password = generationPwd();
String encryptPassword = encrypt(password, mqttSecret, wumei_iv);
printMsg("密码(已加密)" + encryptPassword);
mqttClient.setClient(wifiClient);
mqttClient.setServer(mqttHost, mqttPort);
mqttClient.setCallback(callback);
mqttClient.setBufferSize(1024);
mqttClient.setKeepAlive(10);
//连接 设备mqtt客户端Id格式为认证类型(E=加密、S=简单) & 设备编号 & 产品ID & 用户ID
String clientId = "E&" + deviceNum + "&" + productId +"&" + userId;
bool connectResult = mqttClient.connect(clientId.c_str(), mqttUserName, encryptPassword.c_str());
if (connectResult)
{
printMsg("连接成功");
// 订阅(OTA、NTP、属性、功能、实时监测、信息)
mqttClient.subscribe(sInfoTopic.c_str(), 1);
mqttClient.subscribe(sOtaTopic.c_str(), 1);
mqttClient.subscribe(sNtpTopic.c_str(), 1);
mqttClient.subscribe(sPropertyTopic.c_str(), 1);
mqttClient.subscribe(sFunctionTopic.c_str(), 1);
mqttClient.subscribe(sPropertyOnline.c_str(), 1);
mqttClient.subscribe(sFunctionOnline.c_str(), 1);
mqttClient.subscribe(sMonitorTopic.c_str(), 1);
printMsg("订阅主题:" + sInfoTopic);
printMsg("订阅主题:" + sOtaTopic);
printMsg("订阅主题:" + sNtpTopic);
printMsg("订阅主题:" + sPropertyTopic);
printMsg("订阅主题:" + sFunctionTopic);
printMsg("订阅主题:" + sPropertyOnline);
printMsg("订阅主题:" + sFunctionOnline);
printMsg("订阅主题:" + sMonitorTopic);
// 发布设备信息
publishInfo();
}
else
{
printMsg("连接失败, rc=");
Serial.print(mqttClient.state());
}
}
// 1.发布设备信息
void publishInfo()
{
StaticJsonDocument<256> doc;
doc["rssi"] = WiFi.RSSI();
doc["firmwareVersion"] = firmwareVersion;
doc["status"] = 3; // 1-未激活2-禁用3-在线4-离线)
doc["userId"] = (String)userId;
doc["longitude"] = longitude; //经度 可选
doc["latitude"] = latitude; // 纬度 可选
// 设备摘要,可选(自定义配置信息,不限数量)
JsonObject summary = doc.createNestedObject("summary");
summary["name"]="wumei-smart";
summary["chip"]="esp8266";
summary["author"]="kerwincui";
summary["version"]=1.6;
summary["create"]="2022-06-06";
printMsg("发布设备信息:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
mqttClient.publish(pInfoTopic.c_str(), output.c_str());
}
// 2.发布时钟同步信,用于获取当前时间(可选)
void publishNtp()
{
StaticJsonDocument<128> doc;
doc["deviceSendTime"] = millis();
printMsg("发布NTP信息:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
mqttClient.publish(pNtpTopic.c_str(), output.c_str());
}
// 3.发布属性
void publishProperty(String msg)
{
printMsg("发布属性:" + msg);
mqttClient.publish(pPropertyTopic.c_str(), msg.c_str());
}
// 4.发布功能
void publishFunction(String msg)
{
printMsg("发布功能:" + msg);
mqttClient.publish(pFunctionTopic.c_str(), msg.c_str());
}
// 5.发布事件
void publishEvent()
{
// 匹配云端的事件
StaticJsonDocument<512> doc;
JsonObject objTmeperature = doc.createNestedObject();
objTmeperature["id"] = "height_temperature";
objTmeperature["value"] = "40";
objTmeperature["remark"] = "温度过高警告";
JsonObject objException = doc.createNestedObject();
objException["id"] = "exception";
objException["value"] = "异常消息消息内容XXXXXXXX";
objException["remark"] = "设备发生错误";
printMsg("发布事件:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
mqttClient.publish(pEventTopic.c_str(), output.c_str());
}
// 6.发布实时监测数据
void publishMonitor()
{
String msg = randomPropertyData();
// 发布为实时监测数据,不会存储
printMsg("发布实时监测数据:" + msg);
mqttClient.publish(pMonitorTopic.c_str(), msg.c_str());
}
// 随机生成监测值
String randomPropertyData()
{
// 匹配云端定义的监测数据,随机数代替监测结果
float randFloat = 0;
int randInt = 0;
StaticJsonDocument<1024> doc;
JsonObject objTmeperature = doc.createNestedObject();
objTmeperature["id"] = "temperature";
randFloat = random(1000, 3000);
objTmeperature["value"] = (String)(randFloat / 100);
objTmeperature["remark"] = (String)millis();
JsonObject objHumidity = doc.createNestedObject();
objHumidity["id"] = "humidity";
randFloat = random(3000, 6000);
objHumidity["value"] = (String)(randFloat / 100);
objHumidity["remark"] = (String)millis();
JsonObject objCo2 = doc.createNestedObject();
objCo2["id"] = "co2";
randInt = random(400, 1000);
objCo2["value"] = (String)(randInt);
objCo2["remark"] = (String)millis();
JsonObject objBrightness = doc.createNestedObject();
objBrightness["id"] = "brightness";
randInt = random(1000, 10000);
objBrightness["value"] = (String)(randInt);
objBrightness["remark"] = (String)millis();
printMsg("随机生成监测数据值:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
return output;
}
// 生成密码
String generationPwd()
{
String jsonTime = getTime();
printMsg("getTime()= " + jsonTime);
// 128字节内存池容量
StaticJsonDocument<128> doc;
// 解析JSON
DeserializationError error = deserializeJson(doc, jsonTime);
if (error)
{
printMsg("Json解析失败");
Serial.print(error.f_str());
return "";
}
// 获取当前时间
float deviceSendTime = doc["deviceSendTime"];
float serverSendTime = doc["serverSendTime"];
float serverRecvTime = doc["serverRecvTime"];
float deviceRecvTime = millis();
float now = (serverSendTime + serverRecvTime + deviceRecvTime - deviceSendTime) / 2;
// 过期时间 = 当前时间 + 1小时
float expireTime = now + 1 * 60 * 60 * 1000;
// 密码加密格式为mqtt密码 & 过期时间 & 授权码(可选),如果产品启用了授权码就必须加上
String password="";
if(authCode == ""){
password = (String)mqttPwd + "&" + String(expireTime, 0);
}else{
password = (String)mqttPwd + "&" + String(expireTime, 0) + "&" + authCode;
}
printMsg("密码(未加密):" + password);
return password;
}
// HTTP获取时间
String getTime()
{
while (WiFi.status() == WL_CONNECTED)
{
HTTPClient http;
printMsg("获取时间...");
if (http.begin(wifiClient, (ntpServer + (String)millis()).c_str()))
{
// 发送请求
int httpCode = http.GET();
if (httpCode > 0)
{
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY)
{
g_time = http.getString();
printMsg("获取时间成功data:");
Serial.print(g_time);
return g_time;
}
}
else
{
printMsg("获取时间失败error:");
Serial.printf(http.errorToString(httpCode).c_str());
}
http.end();
}
else
{
printMsg("连接Http失败");
}
delay(500);
}
return "1672524366000";
}
//打印提示信息
void printMsg(String msg)
{
Serial.print("\r\n[");
Serial.print(millis());
Serial.print("ms]");
Serial.print(msg);
}
// 控制指示灯闪烁
void blink()
{
printMsg("指示灯闪烁...");
pinMode(15, OUTPUT);
for (int i = 0; i < 2; i++)
{
digitalWrite(15, HIGH);
delay(200);
digitalWrite(15, LOW);
delay(200);
}
}
// 加密 (AES-CBC-128-pkcs5padding)
String encrypt(String plain_data, char *wumei_key, char *wumei_iv)
{
int i;
// pkcs7padding填充 Block Size : 16
int len = plain_data.length();
int n_blocks = len / 16 + 1;
uint8_t n_padding = n_blocks * 16 - len;
uint8_t data[n_blocks * 16];
memcpy(data, plain_data.c_str(), len);
for (i = len; i < n_blocks * 16; i++)
{
data[i] = n_padding;
}
uint8_t key[16], iv[16];
uint8_t crypt_data[3 * 16] = {0};
memcpy(key, wumei_key, 16);
memcpy(iv, wumei_iv, 16);
memset(crypt_data, 0, 48);
len = n_blocks * 16;
// 加密
mbedtls_aes_context aes_ctx;
mbedtls_aes_init(&aes_ctx);
mbedtls_aes_setkey_enc(&aes_ctx, key, 128);
mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_ENCRYPT, len, iv, data, crypt_data);
// Base64编码
char encoded_data[base64_enc_len(len)];
base64_encode(encoded_data, (char *)crypt_data, len);
return String(encoded_data);
}
// 解密 (AES-CBC-128-pkcs5padding)
String decrypt(String encoded_data_str, char *wumei_key, char *wumei_iv)
{
int input_len = encoded_data_str.length();
char *encoded_data = const_cast<char *>(encoded_data_str.c_str());
int len = base64_dec_len(encoded_data, input_len);
uint8_t data[len];
base64_decode((char *)data, encoded_data, input_len);
uint8_t key[16], iv[16];
memcpy(key, wumei_key, 16);
memcpy(iv, wumei_iv, 16);
int n_blocks = len / 16;
uint8_t n_padding = data[n_blocks * 16 - 1];
len = n_blocks * 16 - n_padding;
char plain_data[len + 1];
//密文空间
mbedtls_aes_context aes_ctx;
mbedtls_aes_init(&aes_ctx);
//设置解密密钥
mbedtls_aes_setkey_dec(&aes_ctx, key, 128);
mbedtls_aes_crypt_cbc(&aes_ctx, MBEDTLS_AES_DECRYPT, 48, iv, (unsigned char *)encoded_data, (unsigned char *)plain_data);
mbedtls_aes_free(&aes_ctx);
// PKCS#7 Padding 填充
plain_data[len] = '\0';
return String(plain_data);
}

View File

@@ -1,79 +0,0 @@
/***********************************************************
* author: LaoHuang
* create: 2022-04-14
* emailrememberyousaid@163.com
* source:https://github.com/kerwincui/wumei-smart
* board:esp32 版本1.0.6
***********************************************************/
#ifndef _HELPER_H
#define _HELPER_H
#include <mbedtls/aes.h>
#include "Base64.h"
#include <WiFiClient.h>
//#include <Ethernet.h>
#include <HTTPClient.h>
#include <PubSubClient.h> // 版本2.8.0
#include <ArduinoJson.h> // 版本6.19.4
extern WiFiClient wifiClient;
extern PubSubClient mqttClient;
extern String deviceNum ; // 设备编号重要同时是Mqtt的clientId
extern String userId; // 用户ID
extern String productId; // 产品ID
extern float rssi; // 信号强度信号极好4格[-55— 0]信号好3格[-70— -55]信号一般2格[-85— -70]信号差1格[-100— -85]
extern float firmwareVersion; // 固件版本
extern char *wifiSsid; // WIFI的SSID
extern char *wifiPwd; // WIFI的密码
extern char *mqttHost; // Mqtt消息服务器地址
extern int mqttPort; // Mqtt消息服务器端口
extern char *mqttUserName; // Mqtt消息服务器账号
extern char *mqttPwd; // Mqtt消息服务器密码
extern char mqttSecret[17]; // Mqtt秘钥,16位
extern char wumei_iv[17]; // AES加密偏移量固定值16位
extern String ntpServer; // NTP服务地址用于获取当前时间
extern int monitorCount; // 发布监测数据的最大次数
extern long monitorInterval; // 发布监测数据的间隔默认1000毫秒
// 连接wifi
void connectWifi();
// 连接mqtt
void connectMqtt();
// Mqtt回调
void callback(char *topic, byte *payload, unsigned int length);
// 发布设备信息
void publishInfo();
// 发布时钟同步信息
void publishNtp();
// 发布事件
void publishEvent();
// 发布实时监测数据
void publishMonitor();
// 随机生成监测值
String randomPropertyData();
// 发布属性
void publishProperty(String msg);
// 发布功能
void publishFunction(String msg);
// 属性处理
void processProperty(String payload);
// 功能处理
void processFunction(String payload);
// 生成密码
String generationPwd();
// 获取时间
String getTime();
// AES加密
String encrypt(String plain_data,char *wumei_key,char *wumei_iv);
// AES解密
String decrypt(String encoded_data_str,char *wumei_key,char *wumei_iv);
//打印提示信息
void printMsg(String tips);
// 控制指示灯闪烁
void blink();
#endif

View File

@@ -1,120 +0,0 @@
/*********************************************************************
* function 设备AP配网
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#include "ApConfig.h"
String randomName = "fastbee-device" + (String)random(1000);
const char *ap_ssid = randomName.c_str();
//开放式网络,不设置密码
const char *ap_password = "";
IPAddress local_IP(192, 168, 4, 1);
IPAddress gateway(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);
ESP8266WebServer server(80);
/**
* 启动AP配网
*/
void startApConfig() {
ledStatus(true);
WiFi.mode(WIFI_AP_STA);
WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP(ap_ssid, ap_password);
printMsg("已启动AP配网IP地址" + WiFi.softAPIP().toString() + " 热点名称:" + (String)ap_ssid);
// 启动web服务
startWebServer();
}
/**
* 启动Web服务
*/
void startWebServer() {
isApMode = true;
server.on("/status", HTTP_GET, handleStatus);
server.on("/config", HTTP_POST, handleConfig);
server.onNotFound(handleNotFound);
server.enableCORS(true);
server.begin();
printMsg("HTTP服务已启动");
}
/**
* 检测设备接口
*/
void handleStatus() {
server.send(200, "text/plain;charset=utf-8", "AP配网已准备就绪");
}
/**
* AP配网接口
*/
void handleConfig() {
printMsg("进入配网......");
config_type config;
// wifi名称、wifi密码、用户编号
if (server.hasArg("SSID") && server.hasArg("password") && server.hasArg("userId")) {
// 分配空间
wifiSsid = (char *)malloc(32 * sizeof(char));
wifiPwd = (char *)malloc(64 * sizeof(char));
userId = (char *)malloc(16 * sizeof(char));
strcpy(config.stassid, server.arg("SSID").c_str());
strcpy(wifiSsid, server.arg("SSID").c_str());
strcpy(config.stapsw, server.arg("password").c_str());
strcpy(wifiPwd, server.arg("password").c_str());
strcpy(config.userId, server.arg("userId").c_str());
strcpy(userId, server.arg("userId").c_str());
printMsg("收到WIFI名称" + (String)config.stassid);
printMsg("收到WIFI密码" + (String)config.stapsw);
printMsg("收到用户编号:" + (String)config.userId);
} else {
printMsg("配网必须传递用户编号、WIFI名称和WIFI密码,配网失败");
server.send(500, "text/plain;charset=utf-8", "配网必须传递用户编号、WIFI名称和WIFI密码配网失败");
return;
}
// 可选字段
if (server.hasArg("deviceNum")) {
deviceNum = (char *)malloc(32 * sizeof(char));
strcpy(config.deviceNum, server.arg("deviceNum").c_str());
strcpy(deviceNum, server.arg("deviceNum").c_str());
printMsg("收到设备编号:" + server.arg("deviceNum"));
}
if (server.hasArg("authCode")) {
authCode = (char *)malloc(32 * sizeof(char));
strcpy(config.authCode, server.arg("authCode").c_str());
strcpy(authCode, server.arg("authCode").c_str());
printMsg("收到产品授权码:" + server.arg("authCode"));
}
if (server.hasArg("extra")) {
printMsg("收到补充信息:" + server.arg("extra"));
}
server.send(200, "text/plain;charset=utf-8", "设备已更新WIFI配置开始连接WIFI...");
// 统一设置Mqtt消息主题前缀
prefix = "/" + (String)productId + "/" + (String)deviceNum;
// 存储配置
saveConfig(config);
// 连接Wifi
connectWifi();
}
/**
* 找不到页面和跨域处理
*/
void handleNotFound() {
printMsg("进入预检请求或请求地址找不到");
if (server.method() == HTTP_OPTIONS) {
// 处理浏览器跨域问题
server.sendHeader("Access-Control-Max-Age", "10000");
server.sendHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS");
server.sendHeader("Access-Control-Allow-Headers", "*");
server.send(204);
} else {
server.send(404, "text/plain;charset=utf-8", "请求的地址找不到或无法访问");
}
}

View File

@@ -1,28 +0,0 @@
/*********************************************************************
* function 设备AP配网
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#ifndef _APCONFIG_H
#define _APCONFIG_H
#include "Config.h"
#include <ESP8266WebServer.h>
extern ESP8266WebServer server;
// 启动AP配网
void startApConfig();
// 启动Web服务
void startWebServer();
// 配网接口
void handleConfig();
// 检测设备接口
void handleStatus();
// 找不到页面和浏览器跨域处理
void handleNotFound();
#endif

View File

@@ -1,156 +0,0 @@
/*********************************************************************
* function 设备认证
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#include "Auth.h"
/*
* 连接MQTT加密认证
*/
void connectMqtt() {
printMsg("连接Mqtt服务器...");
mqttClient.setClient(wifiClient);
mqttClient.setServer(mqttHost, mqttPort);
mqttClient.setBufferSize(1024);
mqttClient.setKeepAlive(5);
// 设置Mqtt回调函数
mqttClient.setCallback(mqttCallback);
// 生成mqtt加密密码
String aesPassword = generationAESPwd();
// 连接 设备mqtt客户端Id格式为认证类型(E=加密、S=简单) & 设备编号 & 产品ID & 用户ID
String clientId = "E&" + (String)deviceNum + "&" + (String)productId + "&" + (String)userId;
printMsg("客户端ID" + clientId);
bool connectResult = mqttClient.connect(clientId.c_str(), mqttUserName, aesPassword.c_str());
if (connectResult) {
printMsg("连接Mqtt成功");
// 订阅系统主题
subscribeTopic();
// 发布设备信息,设备上电都需要发布一次
publishInfo();
} else {
printMsg("连接失败, rc=");
Serial.print(mqttClient.state());
}
}
/*
* 生成加密密码
*/
String generationAESPwd() {
// 获取NTP时间
String jsonTime = getTime();
StaticJsonDocument<128> doc;
DeserializationError error = deserializeJson(doc, jsonTime);
if (error) {
printMsg("Json解析失败");
Serial.print(error.f_str());
return "";
}
// 获取NTP时间=(设备发送消息时间 + 服务器接收消息时间 + 服务器发送消息时间 - 设备接收时间 最后除于2
float deviceSendTime = doc["deviceSendTime"];
float serverSendTime = doc["serverSendTime"];
float serverRecvTime = doc["serverRecvTime"];
float deviceRecvTime = millis();
float now = (serverSendTime + serverRecvTime + deviceRecvTime - deviceSendTime) / 2;
// 过期时间 = 当前时间 + 1小时
float expireTime = now + 1 * 60 * 60 * 1000;
// 加密认证密码格式为mqtt密码 & 过期时间 & 授权码(可选),如果产品启用了授权码就必须加上
String password = "";
if (authCode == "") {
password = (String)mqttPwd + "&" + String(expireTime, 0);
} else {
password = (String)mqttPwd + "&" + String(expireTime, 0) + "&" + authCode;
}
// 密码加密
printMsg("密码(未加密):" + password);
String encryptPassword = encrypt(password, mqttSecret, wumei_iv);
printMsg("密码(已加密)" + encryptPassword);
return encryptPassword;
}
/*
* 通过HTTP获取NTP时间
*/
String getTime() {
while (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
printMsg("获取时间...");
if (http.begin(wifiClient, (ntpServer + (String)millis()).c_str())) {
// 发送请求
int httpCode = http.GET();
if (httpCode > 0) {
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
printMsg("获取时间成功data:");
Serial.print(http.getString());
return http.getString();
}
} else {
printMsg("获取时间失败error:");
Serial.printf(http.errorToString(httpCode).c_str());
}
http.end();
} else {
printMsg("连接Http失败");
}
delay(500);
}
return "1672524366000";
}
/*
* AES加密 (AES-CBC-128-pkcs5padding)
*/
String encrypt(String plain_data, char *wumei_key, char *wumei_iv) {
int i;
// pkcs7padding填充 Block Size : 16
int len = plain_data.length();
int n_blocks = len / 16 + 1;
uint8_t n_padding = n_blocks * 16 - len;
uint8_t data[n_blocks * 16];
memcpy(data, plain_data.c_str(), len);
for (i = len; i < n_blocks * 16; i++) {
data[i] = n_padding;
}
uint8_t key[16], iv[16];
memcpy(key, wumei_key, 16);
memcpy(iv, wumei_iv, 16);
// 加密
br_aes_big_cbcenc_keys encCtx;
br_aes_big_cbcenc_init(&encCtx, key, 16);
br_aes_big_cbcenc_run(&encCtx, iv, data, n_blocks * 16);
// Base64编码
len = n_blocks * 16;
char encoded_data[base64_enc_len(len)];
base64_encode(encoded_data, (char *)data, len);
return String(encoded_data);
}
/*
* AES解密 (AES-CBC-128-pkcs5padding)
*/
String decrypt(String encoded_data_str, char *wumei_key, char *wumei_iv) {
int input_len = encoded_data_str.length();
char *encoded_data = const_cast<char *>(encoded_data_str.c_str());
int len = base64_dec_len(encoded_data, input_len);
uint8_t data[len];
base64_decode((char *)data, encoded_data, input_len);
uint8_t key[16], iv[16];
memcpy(key, wumei_key, 16);
memcpy(iv, wumei_iv, 16);
int n_blocks = len / 16;
br_aes_big_cbcdec_keys decCtx;
br_aes_big_cbcdec_init(&decCtx, key, 16);
br_aes_big_cbcdec_run(&decCtx, iv, data, n_blocks * 16);
// PKCS#7 Padding 填充
uint8_t n_padding = data[n_blocks * 16 - 1];
len = n_blocks * 16 - n_padding;
char plain_data[len + 1];
memcpy(plain_data, data, len);
plain_data[len] = '\0';
return String(plain_data);
}

View File

@@ -1,29 +0,0 @@
/*********************************************************************
* function 设备认证
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#ifndef _AUTH_H
#define _AUTH_H
#include "Config.h"
#include "User.h"
#include "Mqtt.h"
#include <Ethernet.h>
#include <ESP8266HTTPClient.h>
// 连接mqtt, AES加密认证
void connectMqtt();
// 生成加密密码
String generationAESPwd();
// 获取时间
String getTime();
// AES加密
String encrypt(String plain_data, char *wumei_key, char *wumei_iv);
// AES解密
String decrypt(String encoded_data_str, char *wumei_key, char *wumei_iv);
#endif

View File

@@ -1,173 +0,0 @@
/*********************************************************************
* function Base64编码和解码
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: Copyright (c) 2013 Adam Rudd.
********************************************************************/
#include "Base64.h"
#if (defined(__AVR__))
#include <avr\pgmspace.h>
#else
#include <pgmspace.h>
#endif
const char PROGMEM b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
/* 'Private' declarations */
inline void a3_to_a4(unsigned char *a4, unsigned char *a3);
inline void a4_to_a3(unsigned char *a3, unsigned char *a4);
inline unsigned char b64_lookup(char c);
int base64_encode(char *output, char *input, int inputLen)
{
int i = 0, j = 0;
int encLen = 0;
unsigned char a3[3];
unsigned char a4[4];
while (inputLen--)
{
a3[i++] = *(input++);
if (i == 3)
{
a3_to_a4(a4, a3);
for (i = 0; i < 4; i++)
{
output[encLen++] = pgm_read_byte(&b64_alphabet[a4[i]]);
}
i = 0;
}
}
if (i)
{
for (j = i; j < 3; j++)
{
a3[j] = '\0';
}
a3_to_a4(a4, a3);
for (j = 0; j < i + 1; j++)
{
output[encLen++] = pgm_read_byte(&b64_alphabet[a4[j]]);
}
while ((i++ < 3))
{
output[encLen++] = '=';
}
}
output[encLen] = '\0';
return encLen;
}
int base64_decode(char *output, char *input, int inputLen)
{
int i = 0, j = 0;
int decLen = 0;
unsigned char a3[3];
unsigned char a4[4];
while (inputLen--)
{
if (*input == '=')
{
break;
}
a4[i++] = *(input++);
if (i == 4)
{
for (i = 0; i < 4; i++)
{
a4[i] = b64_lookup(a4[i]);
}
a4_to_a3(a3, a4);
for (i = 0; i < 3; i++)
{
output[decLen++] = a3[i];
}
i = 0;
}
}
if (i)
{
for (j = i; j < 4; j++)
{
a4[j] = '\0';
}
for (j = 0; j < 4; j++)
{
a4[j] = b64_lookup(a4[j]);
}
a4_to_a3(a3, a4);
for (j = 0; j < i - 1; j++)
{
output[decLen++] = a3[j];
}
}
output[decLen] = '\0';
return decLen;
}
int base64_enc_len(int plainLen)
{
int n = plainLen;
return (n + 2 - ((n + 2) % 3)) / 3 * 4;
}
int base64_dec_len(char *input, int inputLen)
{
int i = 0;
int numEq = 0;
for (i = inputLen - 1; input[i] == '='; i--)
{
numEq++;
}
return ((6 * inputLen) / 8) - numEq;
}
inline void a3_to_a4(unsigned char *a4, unsigned char *a3)
{
a4[0] = (a3[0] & 0xfc) >> 2;
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
a4[3] = (a3[2] & 0x3f);
}
inline void a4_to_a3(unsigned char *a3, unsigned char *a4)
{
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
}
inline unsigned char b64_lookup(char c)
{
if (c >= 'A' && c <= 'Z')
return c - 'A';
if (c >= 'a' && c <= 'z')
return c - 71;
if (c >= '0' && c <= '9')
return c + 4;
if (c == '+')
return 62;
if (c == '/')
return 63;
return -1;
}

View File

@@ -1,84 +0,0 @@
/*********************************************************************
* function Base64编码和解码
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: Copyright (c) 2013 Adam Rudd.
********************************************************************/
#ifndef _BASE64_H
#define _BASE64_H
/* b64_alphabet:
* Description: Base64 alphabet table, a mapping between integers
* and base64 digits
* Notes: This is an extern here but is defined in Base64.c
*/
extern const char b64_alphabet[];
/* base64_encode:
* Description:
* Encode a string of characters as base64
* Parameters:
* output: the output buffer for the encoding, stores the encoded string
* input: the input buffer for the encoding, stores the binary to be encoded
* inputLen: the length of the input buffer, in bytes
* Return value:
* Returns the length of the encoded string
* Requirements:
* 1. output must not be null or empty
* 2. input must not be null
* 3. inputLen must be greater than or equal to 0
*/
int base64_encode(char *output, char *input, int inputLen);
/* base64_decode:
* Description:
* Decode a base64 encoded string into bytes
* Parameters:
* output: the output buffer for the decoding,
* stores the decoded binary
* input: the input buffer for the decoding,
* stores the base64 string to be decoded
* inputLen: the length of the input buffer, in bytes
* Return value:
* Returns the length of the decoded string
* Requirements:
* 1. output must not be null or empty
* 2. input must not be null
* 3. inputLen must be greater than or equal to 0
*/
int base64_decode(char *output, char *input, int inputLen);
/* base64_enc_len:
* Description:
* Returns the length of a base64 encoded string whose decoded
* form is inputLen bytes long
* Parameters:
* inputLen: the length of the decoded string
* Return value:
* The length of a base64 encoded string whose decoded form
* is inputLen bytes long
* Requirements:
* None
*/
int base64_enc_len(int inputLen);
/* base64_dec_len:
* Description:
* Returns the length of the decoded form of a
* base64 encoded string
* Parameters:
* input: the base64 encoded string to be measured
* inputLen: the length of the base64 encoded string
* Return value:
* Returns the length of the decoded form of a
* base64 encoded string
* Requirements:
* 1. input must not be null
* 2. input must be greater than or equal to zero
*/
int base64_dec_len(char *input, int inputLen);
#endif // _BASE64_H

View File

@@ -1,175 +0,0 @@
/*********************************************************************
* function 设备配置和系统功能
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#include "Config.h"
#define LED 15 // LED指示灯引脚
WiFiClient wifiClient;
PubSubClient mqttClient;
float rssi = 0;
char wumei_iv[17] = "wumei-smart-open";
int monitorCount = 0;
long monitorInterval = 1000;
bool isApMode = false;
// Mqtt订阅的主题前缀格式为 /productId/devicenumber
String prefix = "";
String sInfoTopic = "/info/get";
String sOtaTopic = "/ota/get";
String sNtpTopic = "/ntp/get";
String sPropertyTopic = "/property/get";
String sFunctionTopic = "/function/get";
String sPropertyOnline = "/property-online/get";
String sFunctionOnline = "/function-online/get";
String sMonitorTopic = "/monitor/get";
// Mqtt发布的主题
String pInfoTopic = "/info/post";
String pNtpTopic = "/ntp/post";
String pPropertyTopic = "/property/post";
String pFunctionTopic = "/function/post";
String pMonitorTopic = "/monitor/post";
String pEventTopic = "/event/post";
/********************************** begin 可配置的项 **********************************/
// wifi信息
char *wifiSsid = "";
char *wifiPwd = "";
char *userId = "1";
// 产品启用授权码,则授权码不能为空
char *authCode = "";
// 设备信息配置
char *deviceNum = "D1FJTWOT3HIB";
char *productId = "588";
float firmwareVersion = 1.0;
// 经度和纬度可选,如果产品使用设备定位,则必须传
float latitude = 0;
float longitude = 0;
// Mqtt配置
char *mqttHost = "fastbee.cn";
int mqttPort = 1883;
char *mqttUserName = "FastBee";
char *mqttPwd = "P63653937TRQ8F27";
char mqttSecret[17] = "KV52PPZ813EFCQD8";
// NTP地址用于获取时间,修改为自己部署项目的接口地址)
String ntpServer = "http://fastbee.cn:8080/iot/tool/ntp?deviceSendTime=";
/********************************** end 可配置的项 **********************************/
// 连接wifi
void connectWifi() {
if (isApMode) {
// 关闭AP配网模式延迟2秒确保返回状态码给手机
isApMode = false;
delay(2000);
server.stop();
ledStatus(false);
}
printMsg("连接Wifi ");
Serial.print(wifiSsid);
WiFi.mode(WIFI_STA);
WiFi.begin(wifiSsid, wifiPwd);
}
// 存储配置
void saveConfig(config_type config) {
// 标识为已经存储数据
config.flag = 1;
EEPROM.begin(240);
printMsg("存储配置...");
uint8_t *p = (uint8_t *)(&config);
for (int i = 0; i < sizeof(config); i++) {
EEPROM.write(i, *(p + i));
}
EEPROM.end();
}
// 加载配置
void loadConfig() {
config_type config;
EEPROM.begin(240);
printMsg("加载配置...");
uint8_t *p = (uint8_t *)(&config);
for (int i = 0; i < sizeof(config); i++) {
*(p + i) = EEPROM.read(i);
}
if (config.flag != 1) {
printMsg("flash暂无数据");
return;
}
// wifi名称
if (strlen(config.stassid) != 0) {
wifiSsid = (char *)malloc(32 * sizeof(char));
strcpy(wifiSsid, config.stassid);
}
// wifi密码
if (strlen(config.stapsw) != 0) {
wifiPwd = (char *)malloc(64 * sizeof(char));
strcpy(wifiPwd, config.stapsw);
}
// 设备编号
if (strlen(config.deviceNum) != 0) {
deviceNum = (char *)malloc(32 * sizeof(char));
strcpy(deviceNum, config.deviceNum);
}
// 用户编号
if (strlen(config.userId) != 0) {
userId = (char *)malloc(16 * sizeof(char));
strcpy(userId, config.userId);
}
// 授权码
if (strlen(config.authCode) != 0) {
authCode = (char *)malloc(32 * sizeof(char));
strcpy(authCode, config.authCode);
}
// 统一设置Mqtt消息主题前缀
prefix = "/" + (String)productId + "/" + (String)deviceNum;
}
// 清空配置
void clearConfig() {
EEPROM.begin(240);
for (int i = 0; i < 240; i++) {
EEPROM.write(i, 0);
}
EEPROM.end();
}
//打印提示信息
void printMsg(String msg) {
Serial.print("\r\n[");
Serial.print(millis());
Serial.print("ms]");
Serial.print(msg);
}
// 控制指示灯闪烁
void blink() {
printMsg("指示灯闪烁...");
pinMode(LED, OUTPUT);
for (int i = 0; i < 2; i++) {
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
delay(100);
}
}
// 控制指示灯状态
void ledStatus(bool status) {
printMsg("更改指示灯状态");
pinMode(LED, OUTPUT);
if (status) {
digitalWrite(LED, HIGH);
} else {
digitalWrite(LED, LOW);
}
}

View File

@@ -1,87 +0,0 @@
/*********************************************************************
* function 设备配置和系统功能
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#ifndef _CONFIG_H
#define _CONFIG_H
#include "Apconfig.h"
#include "Base64.h"
#include <ESP8266WiFi.h>
#include <EEPROM.h>
#include <PubSubClient.h> // 版本2.8.0
#include <ArduinoJson.h> // 版本6.19.1
// 存储的配置类型结构
struct config_type {
char flag; // 是否有数据标识等于1表示有数据
char stassid[32]; // SSID配置项
char stapsw[64]; // Password配置项
char deviceNum[32]; // 设备编号配置项
char userId[16]; // 用户ID配置项
char authCode[32]; // 授权码配置项
};
extern WiFiClient wifiClient;
extern PubSubClient mqttClient;
// 全局变量
extern char *deviceNum; // 设备编号重要同时是Mqtt的clientId
extern char *userId; // 用户ID
extern char *productId; // 产品ID
extern float rssi; // 信号强度信号极好4格[-55— 0]信号好3格[-70— -55]信号一般2格[-85— -70]信号差1格[-100— -85]
extern float firmwareVersion; // 固件版本
extern float latitude; // 设备精度
extern float longitude; // 设备维度
extern char *wifiSsid; // WIFI的SSID
extern char *wifiPwd; // WIFI的密码
extern char *mqttHost; // Mqtt消息服务器地址
extern int mqttPort; // Mqtt消息服务器端口
extern char *mqttUserName; // Mqtt消息服务器账号
extern char *mqttPwd; // Mqtt消息服务器密码
extern char mqttSecret[17]; // Mqtt秘钥,16位
extern char wumei_iv[17]; // AES加密偏移量固定值16位
extern char *authCode; // 产品授权码,产品未启用时为空字符串
extern String ntpServer; // NTP服务地址用于获取当前时间
extern int monitorCount; // 发布监测数据的最大次数
extern long monitorInterval; // 发布监测数据的间隔默认1000毫秒
extern bool isApMode; // 是否进入AP配网模式
// 订阅的主题
extern String prefix; // Mqtt消息主题前缀
extern String sInfoTopic; // 订阅设备信息
extern String sOtaTopic; // 订阅OTA升级
extern String sNtpTopic; // 订阅NTP时间
extern String sPropertyTopic; // 订阅属性
extern String sFunctionTopic; // 订阅功能
extern String sPropertyOnline; // 订阅属性-在线模式
extern String sFunctionOnline; // 订阅功能-在线模式
extern String sMonitorTopic; // 订阅实时监测
// 发布的主题
extern String pInfoTopic; // 发布设备信息
extern String pNtpTopic; // 发布NTP时间
extern String pPropertyTopic; // 发布属性
extern String pFunctionTopic; // 发布功能
extern String pMonitorTopic; // 发布实时监测数据
extern String pEventTopic; // 发布事件
// 连接WIFI
void connectWifi();
// 加载配置
void loadConfig();
// 保存配置
void saveConfig(config_type config);
// 清空配置
void clearConfig();
//打印提示信息
void printMsg(String msg);
// 控制指示灯闪烁
void blink();
// 控制指示灯状态
void ledStatus(bool status);
#endif

View File

@@ -1,127 +0,0 @@
/*********************************************************************
* function 程序入口
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#include "Config.h"
#include "Auth.h"
#include "Apconfig.h"
#include "User.h"
long lastWifiConn; // 上次wifi连接时间
long lastMqttConn; // 上次mqtt连接时间
long lastPublishMonitor; // 上次发布监测数据时间
long lastPublishSimulateData; // 上次发布测试数据时间
/**
* 启动
*/
void setup() {
//打开串行端口:
Serial.begin(115200);
printMsg("FastBee device starting...");
// 加载配置
loadConfig();
// 初始化用户配置
initUser();
if (strcmp(wifiSsid, "") == 0) {
// 启动配网
startApConfig();
} else {
// 连接Wifi
connectWifi();
}
}
/**
* 循环执行
*/
void loop() {
// 监测按钮
button.tick();
if (isApMode) {
// 配网时的Web服务
server.handleClient();
} else {
// Wifi重连
wifiReconnectionClient();
// Mqtt连接
mqttReconnectionClient();
// 发布实时监测数据
publicMonitorClient();
// 发布模拟数据,测试用
publishSimulateDataClient();
}
ESP.wdtFeed(); // 喂软件看门狗,防止程序跑偏
}
/*
* Wifi掉线重连(非阻塞间隔5s)
*/
void wifiReconnectionClient() {
long now = millis();
if (WiFi.status() != WL_CONNECTED) {
if (now - lastWifiConn > 5000) {
lastWifiConn = now;
WiFi.reconnect();
}
}
}
/*
* mqtt连接(非阻塞、间隔5s)
*/
void mqttReconnectionClient() {
if (WiFi.status() == WL_CONNECTED) {
long now = millis();
if (!mqttClient.connected()) {
if (now - lastMqttConn > 5000) {
lastMqttConn = now;
connectMqtt();
}
} else {
mqttClient.loop();
}
}
}
/*
* 发布实时监测数据非阻塞、间隔默认1秒
*/
void publicMonitorClient() {
if (WiFi.status() == WL_CONNECTED && monitorCount > 0) {
long now = millis();
if (now - lastPublishMonitor > monitorInterval) {
lastPublishMonitor = now;
monitorCount--;
String msg = randomPropertyData();
publishMonitor(msg);
}
}
}
/*
* 发布模拟数据非阻塞、仅用于测试间隔3600秒
*/
void publishSimulateDataClient() {
if (WiFi.status() == WL_CONNECTED) {
long now = millis();
if (now - lastPublishSimulateData > 3600000) {
lastPublishSimulateData = now;
printMsg("执行定时上报");
// 发布事件
processEvent();
// 发布时钟同步
publishNtp();
// 发布属性(监测值)
String msg = randomPropertyData();
publishProperty(msg);
}
}
}

View File

@@ -1,87 +0,0 @@
/*********************************************************************
* function 设备交互
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#include "Mqtt.h"
// 订阅系统主题
void subscribeTopic() {
mqttClient.subscribe((prefix + sInfoTopic).c_str(), 1);
mqttClient.subscribe((prefix + sOtaTopic).c_str(), 1);
mqttClient.subscribe((prefix + sNtpTopic).c_str(), 1);
mqttClient.subscribe((prefix + sPropertyTopic).c_str(), 1);
mqttClient.subscribe((prefix + sFunctionTopic).c_str(), 1);
mqttClient.subscribe((prefix + sPropertyOnline).c_str(), 1);
mqttClient.subscribe((prefix + sFunctionOnline).c_str(), 1);
mqttClient.subscribe((prefix + sMonitorTopic).c_str(), 1);
printMsg("订阅主题完成");
}
// 1.发布设备信息
void publishInfo() {
StaticJsonDocument<256> doc;
doc["rssi"] = WiFi.RSSI();
doc["firmwareVersion"] = firmwareVersion;
doc["status"] = 3; // 1-未激活2-禁用3-在线4-离线)
doc["userId"] = (String)userId;
doc["longitude"] = longitude; //经度 可选
doc["latitude"] = latitude; // 纬度 可选
// 设备摘要,可选(自定义配置信息)
JsonObject summary = doc.createNestedObject("summary");
summary["name"] = "wumei-smart";
summary["chip"] = "esp8266";
summary["author"] = "kerwincui";
summary["version"] = 1.6;
summary["create"] = "2022-06-06";
printMsg("发布设备信息:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
printMsg("主题为:" + prefix + pInfoTopic);
mqttClient.publish((prefix + pInfoTopic).c_str(), output.c_str());
}
// 2.发布时钟同步信,用于获取当前时间(可选)
void publishNtp() {
StaticJsonDocument<128> doc;
doc["deviceSendTime"] = millis();
printMsg("发布主题:" + prefix + pNtpTopic);
printMsg("信息:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
mqttClient.publish((prefix + pNtpTopic).c_str(), output.c_str());
}
// 3.发布属性
void publishProperty(String msg) {
printMsg("发布属性:" + prefix + pPropertyTopic);
printMsg("消息:" + msg);
mqttClient.publish((prefix + pPropertyTopic).c_str(), msg.c_str());
}
// 4.发布功能
void publishFunction(String msg) {
printMsg("发布功能:" + prefix + pFunctionTopic);
printMsg("消息:" + msg);
mqttClient.publish((prefix + pFunctionTopic).c_str(), msg.c_str());
}
// 5.发布事件
void publishEvent(String msg) {
printMsg("发布事件:" + prefix + pEventTopic);
printMsg("消息:" + msg);
mqttClient.publish((prefix + pEventTopic).c_str(), msg.c_str());
}
// 6.发布实时监测数据
void publishMonitor(String msg) {
// 发布实时监测数据(不会存储,需要实时存储则发布为属性)
printMsg("发布实时监测消息:" + msg);
mqttClient.publish((prefix + pMonitorTopic).c_str(), msg.c_str());
}

View File

@@ -1,29 +0,0 @@
/*********************************************************************
* function 设备交互
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#ifndef _MQTT_H
#define _MQTT_H
#include "Config.h"
// 订阅系统主题
void subscribeTopic();
// 发布设备信息
void publishInfo();
// 发布时钟同步信息
void publishNtp();
// 发布事件
void publishEvent(String msg);
// 发布实时监测数据(不会存储,需要实时存储则发布属性)
void publishMonitor(String msg);
// 发布属性
void publishProperty(String msg);
// 发布功能
void publishFunction(String msg);
#endif

View File

@@ -1,255 +0,0 @@
/*********************************************************************
* function 用户自定义功能
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#include "User.h"
#define BUTTON 14 // 按键引脚
#define RELAY 12 // 继电器引脚
OneButton button;
// 按钮单击事件
static void buttonClick();
// 按钮双击事件
static void buttonDoubleClick();
// 按钮长按事件
static void buttonLongPress();
// 初始化用户配置
void initUser() {
// 初始化按键为低电平,并添加单击、双击、长按事件
button = OneButton(BUTTON, true, true);
button.attachClick(buttonClick);
button.attachDoubleClick(buttonDoubleClick);
button.attachLongPressStart(buttonLongPress);
}
// Mqtt回调
void mqttCallback(char *topic, byte *payload, unsigned int length) {
blink();
printMsg("接收数据:");
String data = "";
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
data += (char)payload[i];
}
if (strcmp(topic, (prefix + sOtaTopic).c_str()) == 0) {
printMsg("订阅到设备升级指令...");
StaticJsonDocument<256> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
String newVersion = doc["version"];
String downloadUrl = doc["downloadUrl"];
printMsg("固件版本:" + newVersion);
printMsg("下载地址:" + downloadUrl);
} else if (strcmp(topic, (prefix + sInfoTopic).c_str()) == 0) {
printMsg("订阅到设备信息...");
// 发布设备信息
publishInfo();
} else if (strcmp(topic, (prefix + sNtpTopic).c_str()) == 0) {
printMsg("订阅到NTP时间...");
StaticJsonDocument<256> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
// 计算设备当前时间:(${serverRecvTime} + ${serverSendTime} + ${deviceRecvTime} - ${deviceSendTime}) / 2
float deviceSendTime = doc["deviceSendTime"];
float serverSendTime = doc["serverSendTime"];
float serverRecvTime = doc["serverRecvTime"];
float deviceRecvTime = millis();
float now = (serverSendTime + serverRecvTime + deviceRecvTime - deviceSendTime) / 2;
printMsg("当前时间:" + String(now, 0));
} else if (strcmp(topic, (prefix + sPropertyTopic).c_str()) == 0 || strcmp(topic, (prefix + sPropertyOnline).c_str()) == 0) {
printMsg("订阅到属性指令...");
processProperty(data);
} else if (strcmp(topic, (prefix + sFunctionTopic).c_str()) == 0 || strcmp(topic, (prefix + sFunctionOnline).c_str()) == 0) {
printMsg("订阅到功能指令...");
processFunction(data);
} else if (strcmp(topic, (prefix + sMonitorTopic).c_str()) == 0) {
printMsg("订阅到实时监测指令...");
StaticJsonDocument<128> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
monitorCount = doc["count"];
monitorInterval = doc["interval"];
}
}
// 随机生成监测值
String randomPropertyData() {
// 匹配云端定义的监测数据,随机数代替监测值
float randFloat = 0;
int randInt = 0;
StaticJsonDocument<1024> doc;
JsonObject objTmeperature = doc.createNestedObject();
objTmeperature["id"] = "light_current";
randFloat = random(1000, 3000);
objTmeperature["value"] = (String)(randFloat / 100);
objTmeperature["remark"] = (String)millis();
JsonObject objHumidity = doc.createNestedObject();
objHumidity["id"] = "humidity";
randFloat = random(3000, 6000);
objHumidity["value"] = (String)(randFloat / 100);
objHumidity["remark"] = (String)millis();
JsonObject objCo2 = doc.createNestedObject();
objCo2["id"] = "co2";
randInt = random(400, 1000);
objCo2["value"] = (String)(randInt);
objCo2["remark"] = (String)millis();
JsonObject objBrightness = doc.createNestedObject();
objBrightness["id"] = "brightness";
randInt = random(1000, 10000);
objBrightness["value"] = (String)(randInt);
objBrightness["remark"] = (String)millis();
printMsg("模拟监测数据值:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
return output;
}
// 物模型-属性处理
void processProperty(String msg) {
StaticJsonDocument<1024> doc;
DeserializationError error = deserializeJson(doc, msg);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
for (JsonObject object : doc.as<JsonArray>()) {
// 匹配云端定义的属性(不包含属性中的监测数据)
const char *id = object["id"];
const char *value = object["value"];
printMsg((String)id + "" + (String)value);
}
// 最后发布属性,服务端订阅存储(重要)
publishProperty(msg);
}
// 物模型-功能处理
void processFunction(String msg) {
StaticJsonDocument<1024> doc;
DeserializationError error = deserializeJson(doc, msg);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
for (JsonObject object : doc.as<JsonArray>()) {
// 匹配云端定义的功能
const char *id = object["id"];
const char *value = object["value"];
if (strcmp(id, "light_switch") == 0) {
printMsg("开关 switch" + (String)value);
if (strcmp(value, "1") == 0) {
// 打开继电器
relayStatus(true);
} else if (strcmp(value, "0") == 0) {
// 关闭继电器
relayStatus(false);
}
} else if (strcmp(id, "gear") == 0) {
printMsg("档位 gear" + (String)value);
} else if (strcmp(id, "light_color") == 0) {
printMsg("灯光颜色 light_color" + (String)value);
} else if (strcmp(id, "message") == 0) {
printMsg("屏显消息 message" + (String)value);
} else if (strcmp(id, "report_monitor") == 0) {
int number = atoi(value);
number > 10 ? number = 10 : number;
for (int i = 0; i < number; i++) {
Serial.println(number);
String msg = randomPropertyData();
printMsg("订阅到上报监测数据指令,上报数据:");
printMsg(msg);
publishProperty(msg);
delay(1000);
}
}
}
// 最后发布功能,服务端订阅存储(重要)
publishFunction(msg);
}
// 物模型-事件上传
void processEvent() {
// 匹配云端的事件
StaticJsonDocument<512> doc;
JsonObject objTmeperature = doc.createNestedObject();
objTmeperature["id"] = "height_temperature";
objTmeperature["value"] = "40";
objTmeperature["remark"] = "温度过高警告";
JsonObject objException = doc.createNestedObject();
objException["id"] = "exception";
objException["value"] = "异常消息消息内容XXXXXXXX";
objException["remark"] = "设备发生错误";
printMsg("发布事件:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
// 最后发布功能,服务端订阅存储(重要)
publishEvent(output);
}
//打开继电器A
void relayStatus(bool status) {
pinMode(RELAY, OUTPUT);
if (status) {
digitalWrite(RELAY, HIGH);
} else {
digitalWrite(RELAY, LOW);
}
}
// 按钮单击事件
static void buttonClick() {
printMsg("检测到按键单击,打开继电器");
relayStatus(true);
// 匹配云端定义的开关,格式如:[{"id":"switch","value":"1"}]
String msg = "[{\"id\":\"switch\",\"value\":\"1\"}]";
publishProperty(msg);
}
// 按钮双击事件
static void buttonDoubleClick() {
printMsg("检测到按键双击,关闭继电器");
relayStatus(false);
// 匹配云端定义的开关,格式如:[{"id":"switch","value":"0"}]
String msg = "[{\"id\":\"switch\",\"value\":\"0\"}]";
publishProperty(msg);
}
// 按钮长按事件,进入配网模式
static void buttonLongPress() {
printMsg("检测到按键长按");
if (isApMode) {
printMsg("设备重启...");
ESP.restart();
} else {
printMsg("开始AP配网");
startApConfig();
}
}

View File

@@ -1,33 +0,0 @@
/*********************************************************************
* function 用户自定义功能
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#ifndef _USER_H
#define _USER_H
#include "Config.h"
#include "Mqtt.h"
#include <OneButton.h> // 版本2.0.4
extern OneButton button;
// 初始化用户配置
void initUser();
// Mqtt回调
void mqttCallback(char *topic, byte *payload, unsigned int length);
// 属性处理(物模型)
void processProperty(String msg);
// 功能处理(物模型)
void processFunction(String msg);
// 事件处理(无模型)
void processEvent();
// 模拟监测值
String randomPropertyData();
// 控制继电器状态
void relayStatus(bool status);
#endif

View File

@@ -1,120 +0,0 @@
/*********************************************************************
* function 设备AP配网
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#include "ApConfig.h"
String randomName = "wumei-device" + (String)random(1000);
const char *ap_ssid = randomName.c_str();
//开放式网络,不设置密码
const char *ap_password = "";
IPAddress local_IP(192, 168, 4, 1);
IPAddress gateway(192, 168, 4, 1);
IPAddress subnet(255, 255, 255, 0);
ESP8266WebServer server(80);
/**
* 启动AP配网
*/
void startApConfig() {
ledStatus(true);
WiFi.mode(WIFI_AP_STA);
WiFi.softAPConfig(local_IP, gateway, subnet);
WiFi.softAP(ap_ssid, ap_password);
printMsg("已启动AP配网IP地址" + WiFi.softAPIP().toString() + " 热点名称:" + (String)ap_ssid);
// 启动web服务
startWebServer();
}
/**
* 启动Web服务
*/
void startWebServer() {
isApMode = true;
server.on("/status", HTTP_GET, handleStatus);
server.on("/config", HTTP_POST, handleConfig);
server.onNotFound(handleNotFound);
server.enableCORS(true);
server.begin();
printMsg("HTTP服务已启动");
}
/**
* 检测设备接口
*/
void handleStatus() {
server.send(200, "text/plain;charset=utf-8", "AP配网已准备就绪");
}
/**
* AP配网接口
*/
void handleConfig() {
printMsg("进入配网......");
config_type config;
// wifi名称、wifi密码、用户编号
if (server.hasArg("SSID") && server.hasArg("password") && server.hasArg("userId")) {
// 分配空间
wifiSsid = (char *)malloc(32 * sizeof(char));
wifiPwd = (char *)malloc(64 * sizeof(char));
userId = (char *)malloc(16 * sizeof(char));
strcpy(config.stassid, server.arg("SSID").c_str());
strcpy(wifiSsid, server.arg("SSID").c_str());
strcpy(config.stapsw, server.arg("password").c_str());
strcpy(wifiPwd, server.arg("password").c_str());
strcpy(config.userId, server.arg("userId").c_str());
strcpy(userId, server.arg("userId").c_str());
printMsg("收到WIFI名称" + (String)config.stassid);
printMsg("收到WIFI密码" + (String)config.stapsw);
printMsg("收到用户编号:" + (String)config.userId);
} else {
printMsg("配网必须传递用户编号、WIFI名称和WIFI密码,配网失败");
server.send(500, "text/plain;charset=utf-8", "配网必须传递用户编号、WIFI名称和WIFI密码配网失败");
return;
}
// 可选字段
if (server.hasArg("deviceNum")) {
deviceNum = (char *)malloc(32 * sizeof(char));
strcpy(config.deviceNum, server.arg("deviceNum").c_str());
strcpy(deviceNum, server.arg("deviceNum").c_str());
printMsg("收到设备编号:" + server.arg("deviceNum"));
}
if (server.hasArg("authCode")) {
authCode = (char *)malloc(32 * sizeof(char));
strcpy(config.authCode, server.arg("authCode").c_str());
strcpy(authCode, server.arg("authCode").c_str());
printMsg("收到产品授权码:" + server.arg("authCode"));
}
if (server.hasArg("extra")) {
printMsg("收到补充信息:" + server.arg("extra"));
}
server.send(200, "text/plain;charset=utf-8", "设备已更新WIFI配置开始连接WIFI...");
// 统一设置Mqtt消息主题前缀
prefix = "/" + (String)productId + "/" + (String)deviceNum;
// 存储配置
saveConfig(config);
// 连接Wifi
connectWifi();
}
/**
* 找不到页面和跨域处理
*/
void handleNotFound() {
printMsg("进入预检请求或请求地址找不到");
if (server.method() == HTTP_OPTIONS) {
// 处理浏览器跨域问题
server.sendHeader("Access-Control-Max-Age", "10000");
server.sendHeader("Access-Control-Allow-Methods", "PUT,POST,GET,OPTIONS");
server.sendHeader("Access-Control-Allow-Headers", "*");
server.send(204);
} else {
server.send(404, "text/plain;charset=utf-8", "请求的地址找不到或无法访问");
}
}

View File

@@ -1,28 +0,0 @@
/*********************************************************************
* function 设备AP配网
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#ifndef _APCONFIG_H
#define _APCONFIG_H
#include "Config.h"
#include <ESP8266WebServer.h>
extern ESP8266WebServer server;
// 启动AP配网
void startApConfig();
// 启动Web服务
void startWebServer();
// 配网接口
void handleConfig();
// 检测设备接口
void handleStatus();
// 找不到页面和浏览器跨域处理
void handleNotFound();
#endif

View File

@@ -1,156 +0,0 @@
/*********************************************************************
* function 设备认证
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#include "Auth.h"
/*
* 连接MQTT加密认证
*/
void connectMqtt() {
printMsg("连接Mqtt服务器...");
mqttClient.setClient(wifiClient);
mqttClient.setServer(mqttHost, mqttPort);
mqttClient.setBufferSize(1024);
mqttClient.setKeepAlive(5);
// 设置Mqtt回调函数
mqttClient.setCallback(mqttCallback);
// 生成mqtt加密密码
String aesPassword = generationAESPwd();
// 连接 设备mqtt客户端Id格式为认证类型(E=加密、S=简单) & 设备编号 & 产品ID & 用户ID
String clientId = "E&" + (String)deviceNum + "&" + (String)productId + "&" + (String)userId;
printMsg("客户端ID" + clientId);
bool connectResult = mqttClient.connect(clientId.c_str(), mqttUserName, aesPassword.c_str());
if (connectResult) {
printMsg("连接Mqtt成功");
// 订阅系统主题
subscribeTopic();
// 发布设备信息,设备上电都需要发布一次
publishInfo();
} else {
printMsg("连接失败, rc=");
Serial.print(mqttClient.state());
}
}
/*
* 生成加密密码
*/
String generationAESPwd() {
// 获取NTP时间
String jsonTime = getTime();
StaticJsonDocument<128> doc;
DeserializationError error = deserializeJson(doc, jsonTime);
if (error) {
printMsg("Json解析失败");
Serial.print(error.f_str());
return "";
}
// 获取NTP时间=(设备发送消息时间 + 服务器接收消息时间 + 服务器发送消息时间 - 设备接收时间 最后除于2
float deviceSendTime = doc["deviceSendTime"];
float serverSendTime = doc["serverSendTime"];
float serverRecvTime = doc["serverRecvTime"];
float deviceRecvTime = millis();
float now = (serverSendTime + serverRecvTime + deviceRecvTime - deviceSendTime) / 2;
// 过期时间 = 当前时间 + 1小时
float expireTime = now + 1 * 60 * 60 * 1000;
// 加密认证密码格式为mqtt密码 & 过期时间 & 授权码(可选),如果产品启用了授权码就必须加上
String password = "";
if (authCode == "") {
password = (String)mqttPwd + "&" + String(expireTime, 0);
} else {
password = (String)mqttPwd + "&" + String(expireTime, 0) + "&" + authCode;
}
// 密码加密
printMsg("密码(未加密):" + password);
String encryptPassword = encrypt(password, mqttSecret, wumei_iv);
printMsg("密码(已加密)" + encryptPassword);
return encryptPassword;
}
/*
* 通过HTTP获取NTP时间
*/
String getTime() {
while (WiFi.status() == WL_CONNECTED) {
HTTPClient http;
printMsg("获取时间...");
if (http.begin(wifiClient, (ntpServer + (String)millis()).c_str())) {
// 发送请求
int httpCode = http.GET();
if (httpCode > 0) {
if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
printMsg("获取时间成功data:");
Serial.print(http.getString());
return http.getString();
}
} else {
printMsg("获取时间失败error:");
Serial.printf(http.errorToString(httpCode).c_str());
}
http.end();
} else {
printMsg("连接Http失败");
}
delay(500);
}
return "1672524366000";
}
/*
* AES加密 (AES-CBC-128-pkcs5padding)
*/
String encrypt(String plain_data, char *wumei_key, char *wumei_iv) {
int i;
// pkcs7padding填充 Block Size : 16
int len = plain_data.length();
int n_blocks = len / 16 + 1;
uint8_t n_padding = n_blocks * 16 - len;
uint8_t data[n_blocks * 16];
memcpy(data, plain_data.c_str(), len);
for (i = len; i < n_blocks * 16; i++) {
data[i] = n_padding;
}
uint8_t key[16], iv[16];
memcpy(key, wumei_key, 16);
memcpy(iv, wumei_iv, 16);
// 加密
br_aes_big_cbcenc_keys encCtx;
br_aes_big_cbcenc_init(&encCtx, key, 16);
br_aes_big_cbcenc_run(&encCtx, iv, data, n_blocks * 16);
// Base64编码
len = n_blocks * 16;
char encoded_data[base64_enc_len(len)];
base64_encode(encoded_data, (char *)data, len);
return String(encoded_data);
}
/*
* AES解密 (AES-CBC-128-pkcs5padding)
*/
String decrypt(String encoded_data_str, char *wumei_key, char *wumei_iv) {
int input_len = encoded_data_str.length();
char *encoded_data = const_cast<char *>(encoded_data_str.c_str());
int len = base64_dec_len(encoded_data, input_len);
uint8_t data[len];
base64_decode((char *)data, encoded_data, input_len);
uint8_t key[16], iv[16];
memcpy(key, wumei_key, 16);
memcpy(iv, wumei_iv, 16);
int n_blocks = len / 16;
br_aes_big_cbcdec_keys decCtx;
br_aes_big_cbcdec_init(&decCtx, key, 16);
br_aes_big_cbcdec_run(&decCtx, iv, data, n_blocks * 16);
// PKCS#7 Padding 填充
uint8_t n_padding = data[n_blocks * 16 - 1];
len = n_blocks * 16 - n_padding;
char plain_data[len + 1];
memcpy(plain_data, data, len);
plain_data[len] = '\0';
return String(plain_data);
}

View File

@@ -1,29 +0,0 @@
/*********************************************************************
* function 设备认证
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#ifndef _AUTH_H
#define _AUTH_H
#include "Config.h"
#include "User.h"
#include "Mqtt.h"
#include <Ethernet.h>
#include <ESP8266HTTPClient.h>
// 连接mqtt, AES加密认证
void connectMqtt();
// 生成加密密码
String generationAESPwd();
// 获取时间
String getTime();
// AES加密
String encrypt(String plain_data, char *wumei_key, char *wumei_iv);
// AES解密
String decrypt(String encoded_data_str, char *wumei_key, char *wumei_iv);
#endif

View File

@@ -1,173 +0,0 @@
/*********************************************************************
* function Base64编码和解码
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: Copyright (c) 2013 Adam Rudd.
********************************************************************/
#include "Base64.h"
#if (defined(__AVR__))
#include <avr\pgmspace.h>
#else
#include <pgmspace.h>
#endif
const char PROGMEM b64_alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
/* 'Private' declarations */
inline void a3_to_a4(unsigned char *a4, unsigned char *a3);
inline void a4_to_a3(unsigned char *a3, unsigned char *a4);
inline unsigned char b64_lookup(char c);
int base64_encode(char *output, char *input, int inputLen)
{
int i = 0, j = 0;
int encLen = 0;
unsigned char a3[3];
unsigned char a4[4];
while (inputLen--)
{
a3[i++] = *(input++);
if (i == 3)
{
a3_to_a4(a4, a3);
for (i = 0; i < 4; i++)
{
output[encLen++] = pgm_read_byte(&b64_alphabet[a4[i]]);
}
i = 0;
}
}
if (i)
{
for (j = i; j < 3; j++)
{
a3[j] = '\0';
}
a3_to_a4(a4, a3);
for (j = 0; j < i + 1; j++)
{
output[encLen++] = pgm_read_byte(&b64_alphabet[a4[j]]);
}
while ((i++ < 3))
{
output[encLen++] = '=';
}
}
output[encLen] = '\0';
return encLen;
}
int base64_decode(char *output, char *input, int inputLen)
{
int i = 0, j = 0;
int decLen = 0;
unsigned char a3[3];
unsigned char a4[4];
while (inputLen--)
{
if (*input == '=')
{
break;
}
a4[i++] = *(input++);
if (i == 4)
{
for (i = 0; i < 4; i++)
{
a4[i] = b64_lookup(a4[i]);
}
a4_to_a3(a3, a4);
for (i = 0; i < 3; i++)
{
output[decLen++] = a3[i];
}
i = 0;
}
}
if (i)
{
for (j = i; j < 4; j++)
{
a4[j] = '\0';
}
for (j = 0; j < 4; j++)
{
a4[j] = b64_lookup(a4[j]);
}
a4_to_a3(a3, a4);
for (j = 0; j < i - 1; j++)
{
output[decLen++] = a3[j];
}
}
output[decLen] = '\0';
return decLen;
}
int base64_enc_len(int plainLen)
{
int n = plainLen;
return (n + 2 - ((n + 2) % 3)) / 3 * 4;
}
int base64_dec_len(char *input, int inputLen)
{
int i = 0;
int numEq = 0;
for (i = inputLen - 1; input[i] == '='; i--)
{
numEq++;
}
return ((6 * inputLen) / 8) - numEq;
}
inline void a3_to_a4(unsigned char *a4, unsigned char *a3)
{
a4[0] = (a3[0] & 0xfc) >> 2;
a4[1] = ((a3[0] & 0x03) << 4) + ((a3[1] & 0xf0) >> 4);
a4[2] = ((a3[1] & 0x0f) << 2) + ((a3[2] & 0xc0) >> 6);
a4[3] = (a3[2] & 0x3f);
}
inline void a4_to_a3(unsigned char *a3, unsigned char *a4)
{
a3[0] = (a4[0] << 2) + ((a4[1] & 0x30) >> 4);
a3[1] = ((a4[1] & 0xf) << 4) + ((a4[2] & 0x3c) >> 2);
a3[2] = ((a4[2] & 0x3) << 6) + a4[3];
}
inline unsigned char b64_lookup(char c)
{
if (c >= 'A' && c <= 'Z')
return c - 'A';
if (c >= 'a' && c <= 'z')
return c - 71;
if (c >= '0' && c <= '9')
return c + 4;
if (c == '+')
return 62;
if (c == '/')
return 63;
return -1;
}

View File

@@ -1,84 +0,0 @@
/*********************************************************************
* function Base64编码和解码
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: Copyright (c) 2013 Adam Rudd.
********************************************************************/
#ifndef _BASE64_H
#define _BASE64_H
/* b64_alphabet:
* Description: Base64 alphabet table, a mapping between integers
* and base64 digits
* Notes: This is an extern here but is defined in Base64.c
*/
extern const char b64_alphabet[];
/* base64_encode:
* Description:
* Encode a string of characters as base64
* Parameters:
* output: the output buffer for the encoding, stores the encoded string
* input: the input buffer for the encoding, stores the binary to be encoded
* inputLen: the length of the input buffer, in bytes
* Return value:
* Returns the length of the encoded string
* Requirements:
* 1. output must not be null or empty
* 2. input must not be null
* 3. inputLen must be greater than or equal to 0
*/
int base64_encode(char *output, char *input, int inputLen);
/* base64_decode:
* Description:
* Decode a base64 encoded string into bytes
* Parameters:
* output: the output buffer for the decoding,
* stores the decoded binary
* input: the input buffer for the decoding,
* stores the base64 string to be decoded
* inputLen: the length of the input buffer, in bytes
* Return value:
* Returns the length of the decoded string
* Requirements:
* 1. output must not be null or empty
* 2. input must not be null
* 3. inputLen must be greater than or equal to 0
*/
int base64_decode(char *output, char *input, int inputLen);
/* base64_enc_len:
* Description:
* Returns the length of a base64 encoded string whose decoded
* form is inputLen bytes long
* Parameters:
* inputLen: the length of the decoded string
* Return value:
* The length of a base64 encoded string whose decoded form
* is inputLen bytes long
* Requirements:
* None
*/
int base64_enc_len(int inputLen);
/* base64_dec_len:
* Description:
* Returns the length of the decoded form of a
* base64 encoded string
* Parameters:
* input: the base64 encoded string to be measured
* inputLen: the length of the base64 encoded string
* Return value:
* Returns the length of the decoded form of a
* base64 encoded string
* Requirements:
* 1. input must not be null
* 2. input must be greater than or equal to zero
*/
int base64_dec_len(char *input, int inputLen);
#endif // _BASE64_H

View File

@@ -1,175 +0,0 @@
/*********************************************************************
* function 设备配置和系统功能
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#include "Config.h"
#define LED 15 // LED指示灯引脚
WiFiClient wifiClient;
PubSubClient mqttClient;
float rssi = 0;
char wumei_iv[17] = "wumei-smart-open";
int monitorCount = 0;
long monitorInterval = 1000;
bool isApMode = false;
// Mqtt订阅的主题前缀格式为 /productId/devicenumber
String prefix = "";
String sInfoTopic = "/info/get";
String sOtaTopic = "/ota/get";
String sNtpTopic = "/ntp/get";
String sPropertyTopic = "/property/get";
String sFunctionTopic = "/function/get";
String sPropertyOnline = "/property-online/get";
String sFunctionOnline = "/function-online/get";
String sMonitorTopic = "/monitor/get";
// Mqtt发布的主题
String pInfoTopic = "/info/post";
String pNtpTopic = "/ntp/post";
String pPropertyTopic = "/property/post";
String pFunctionTopic = "/function/post";
String pMonitorTopic = "/monitor/post";
String pEventTopic = "/event/post";
/********************************** begin 可配置的项 **********************************/
// wifi信息
char *wifiSsid = (char*)"MERCURY100";
char *wifiPwd = (char*)"FastBee";
char *userId = (char*)"1";
// 产品启用授权码,则授权码不能为空
char *authCode = (char*)"";
// 设备信息配置
char *deviceNum = (char*)"D1PGLPG58K88";
char *productId = (char*)"96";
float firmwareVersion = 1.0;
// 经度和纬度可选,如果产品使用设备定位,则必须传
float latitude = 0;
float longitude = 0;
// Mqtt配置
char *mqttHost = (char*)"iot.fastbee.cn";
int mqttPort = 1883;
char *mqttUserName = (char*)"FastBee";
char *mqttPwd = (char*)"P467433O1MT8MXS2";
char mqttSecret[17] = "KWF32S3H95LH14LO";
// NTP地址用于获取时间,修改为自己部署项目的接口地址)
String ntpServer = "http://fastbee.cn:8080/iot/tool/ntp?deviceSendTime=";
/********************************** end 可配置的项 **********************************/
// 连接wifi
void connectWifi() {
if (isApMode) {
// 关闭AP配网模式延迟2秒确保返回状态码给手机
isApMode = false;
delay(2000);
server.stop();
ledStatus(false);
}
printMsg("连接Wifi ");
Serial.print(wifiSsid);
WiFi.mode(WIFI_STA);
WiFi.begin(wifiSsid, wifiPwd);
}
// 存储配置
void saveConfig(config_type config) {
// 标识为已经存储数据
config.flag = 1;
EEPROM.begin(240);
printMsg("存储配置...");
uint8_t *p = (uint8_t *)(&config);
for (int i = 0; i < sizeof(config); i++) {
EEPROM.write(i, *(p + i));
}
EEPROM.end();
}
// 加载配置
void loadConfig() {
config_type config;
EEPROM.begin(240);
printMsg("加载配置...");
uint8_t *p = (uint8_t *)(&config);
for (int i = 0; i < sizeof(config); i++) {
*(p + i) = EEPROM.read(i);
}
if (config.flag != 1) {
printMsg("flash暂无数据");
return;
}
// wifi名称
if (strlen(config.stassid) != 0) {
wifiSsid = (char *)malloc(32 * sizeof(char));
strcpy(wifiSsid, config.stassid);
}
// wifi密码
if (strlen(config.stapsw) != 0) {
wifiPwd = (char *)malloc(64 * sizeof(char));
strcpy(wifiPwd, config.stapsw);
}
// 设备编号
if (strlen(config.deviceNum) != 0) {
deviceNum = (char *)malloc(32 * sizeof(char));
strcpy(deviceNum, config.deviceNum);
}
// 用户编号
if (strlen(config.userId) != 0) {
userId = (char *)malloc(16 * sizeof(char));
strcpy(userId, config.userId);
}
// 授权码
if (strlen(config.authCode) != 0) {
authCode = (char *)malloc(32 * sizeof(char));
strcpy(authCode, config.authCode);
}
// 统一设置Mqtt消息主题前缀
prefix = "/" + (String)productId + "/" + (String)deviceNum;
}
// 清空配置
void clearConfig() {
EEPROM.begin(240);
for (int i = 0; i < 240; i++) {
EEPROM.write(i, 0);
}
EEPROM.end();
}
//打印提示信息
void printMsg(String msg) {
Serial.print("\r\n[");
Serial.print(millis());
Serial.print("ms]");
Serial.print(msg);
}
// 控制指示灯闪烁
void blink() {
printMsg("指示灯闪烁...");
pinMode(LED, OUTPUT);
for (int i = 0; i < 2; i++) {
digitalWrite(LED, HIGH);
delay(100);
digitalWrite(LED, LOW);
delay(100);
}
}
// 控制指示灯状态
void ledStatus(bool status) {
printMsg("更改指示灯状态");
pinMode(LED, OUTPUT);
if (status) {
digitalWrite(LED, HIGH);
} else {
digitalWrite(LED, LOW);
}
}

View File

@@ -1,87 +0,0 @@
/*********************************************************************
* function 设备配置和系统功能
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#ifndef _CONFIG_H
#define _CONFIG_H
#include "Apconfig.h"
#include "Base64.h"
#include <ESP8266WiFi.h>
#include <EEPROM.h>
#include <PubSubClient.h> // 版本2.8.0
#include <ArduinoJson.h> // 版本6.19.1
// 存储的配置类型结构
struct config_type {
char flag; // 是否有数据标识等于1表示有数据
char stassid[32]; // SSID配置项
char stapsw[64]; // Password配置项
char deviceNum[32]; // 设备编号配置项
char userId[16]; // 用户ID配置项
char authCode[32]; // 授权码配置项
};
extern WiFiClient wifiClient;
extern PubSubClient mqttClient;
// 全局变量
extern char *deviceNum; // 设备编号重要同时是Mqtt的clientId
extern char *userId; // 用户ID
extern char *productId; // 产品ID
extern float rssi; // 信号强度信号极好4格[-55— 0]信号好3格[-70— -55]信号一般2格[-85— -70]信号差1格[-100— -85]
extern float firmwareVersion; // 固件版本
extern float latitude; // 设备精度
extern float longitude; // 设备维度
extern char *wifiSsid; // WIFI的SSID
extern char *wifiPwd; // WIFI的密码
extern char *mqttHost; // Mqtt消息服务器地址
extern int mqttPort; // Mqtt消息服务器端口
extern char *mqttUserName; // Mqtt消息服务器账号
extern char *mqttPwd; // Mqtt消息服务器密码
extern char mqttSecret[17]; // Mqtt秘钥,16位
extern char wumei_iv[17]; // AES加密偏移量固定值16位
extern char *authCode; // 产品授权码,产品未启用时为空字符串
extern String ntpServer; // NTP服务地址用于获取当前时间
extern int monitorCount; // 发布监测数据的最大次数
extern long monitorInterval; // 发布监测数据的间隔默认1000毫秒
extern bool isApMode; // 是否进入AP配网模式
// 订阅的主题
extern String prefix; // Mqtt消息主题前缀
extern String sInfoTopic; // 订阅设备信息
extern String sOtaTopic; // 订阅OTA升级
extern String sNtpTopic; // 订阅NTP时间
extern String sPropertyTopic; // 订阅属性
extern String sFunctionTopic; // 订阅功能
extern String sPropertyOnline; // 订阅属性-在线模式
extern String sFunctionOnline; // 订阅功能-在线模式
extern String sMonitorTopic; // 订阅实时监测
// 发布的主题
extern String pInfoTopic; // 发布设备信息
extern String pNtpTopic; // 发布NTP时间
extern String pPropertyTopic; // 发布属性
extern String pFunctionTopic; // 发布功能
extern String pMonitorTopic; // 发布实时监测数据
extern String pEventTopic; // 发布事件
// 连接WIFI
void connectWifi();
// 加载配置
void loadConfig();
// 保存配置
void saveConfig(config_type config);
// 清空配置
void clearConfig();
//打印提示信息
void printMsg(String msg);
// 控制指示灯闪烁
void blink();
// 控制指示灯状态
void ledStatus(bool status);
#endif

View File

@@ -1,126 +0,0 @@
/*********************************************************************
* function 程序入口
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#include "Config.h"
#include "Auth.h"
#include "Apconfig.h"
#include "User.h"
long lastWifiConn; // 上次wifi连接时间
long lastMqttConn; // 上次mqtt连接时间
long lastPublishMonitor; // 上次发布监测数据时间
long lastPublishSimulateData; // 上次发布测试数据时间
/**
* 启动
*/
void setup() {
//打开串行端口:
Serial.begin(115200);
printMsg("FastBee device starting...");
// 加载配置
loadConfig();
// 初始化用户配置
initUser();
if (strcmp(wifiSsid, "") == 0) {
// 启动配网
startApConfig();
} else {
// 连接Wifi
connectWifi();
}
}
/**
* 循环执行
*/
void loop() {
// 监测按钮
button.tick();
if (isApMode) {
// 配网时的Web服务
server.handleClient();
} else {
// Wifi重连
wifiReconnectionClient();
// Mqtt连接
mqttReconnectionClient();
// 发布实时监测数据
publicMonitorClient();
// 上报监测数据
publishReportDataClient();
}
}
/*
* Wifi掉线重连(非阻塞间隔5s)
*/
void wifiReconnectionClient() {
long now = millis();
if (WiFi.status() != WL_CONNECTED) {
if (now - lastWifiConn > 5000) {
lastWifiConn = now;
WiFi.reconnect();
}
}
}
/*
* mqtt连接(非阻塞、间隔5s)
*/
void mqttReconnectionClient() {
if (WiFi.status() == WL_CONNECTED) {
long now = millis();
if (!mqttClient.connected()) {
if (now - lastMqttConn > 5000) {
lastMqttConn = now;
connectMqtt();
}
} else {
mqttClient.loop();
}
}
}
/*
* 发布实时监测数据非阻塞、间隔默认1秒
*/
void publicMonitorClient() {
if (WiFi.status() == WL_CONNECTED && monitorCount > 0) {
long now = millis();
if (now - lastPublishMonitor > monitorInterval) {
lastPublishMonitor = now;
monitorCount--;
String msg = randomMonitorData();
publishMonitor(msg);
}
}
}
/*
* 上报监测数据非阻塞、仅用于测试间隔3600秒
*/
void publishReportDataClient() {
if (WiFi.status() == WL_CONNECTED) {
long now = millis();
if (now - lastPublishSimulateData > 3600000) {
lastPublishSimulateData = now;
printMsg("执行定时上报");
// 发布事件
processEvent();
// 发布时钟同步
publishNtp();
// 发布属性(监测值)
String msg = randomPropertyData();
publishProperty(msg);
}
}
}

View File

@@ -1,87 +0,0 @@
/*********************************************************************
* function 设备交互
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#include "Mqtt.h"
// 订阅系统主题
void subscribeTopic() {
mqttClient.subscribe((prefix + sInfoTopic).c_str(), 1);
mqttClient.subscribe((prefix + sOtaTopic).c_str(), 1);
mqttClient.subscribe((prefix + sNtpTopic).c_str(), 1);
mqttClient.subscribe((prefix + sPropertyTopic).c_str(), 1);
mqttClient.subscribe((prefix + sFunctionTopic).c_str(), 1);
mqttClient.subscribe((prefix + sPropertyOnline).c_str(), 1);
mqttClient.subscribe((prefix + sFunctionOnline).c_str(), 1);
mqttClient.subscribe((prefix + sMonitorTopic).c_str(), 1);
printMsg("订阅主题完成");
}
// 1.发布设备信息
void publishInfo() {
StaticJsonDocument<256> doc;
doc["rssi"] = WiFi.RSSI();
doc["firmwareVersion"] = firmwareVersion;
doc["status"] = 3; // 1-未激活2-禁用3-在线4-离线)
doc["userId"] = (String)userId;
doc["longitude"] = longitude; //经度 可选
doc["latitude"] = latitude; // 纬度 可选
// 设备摘要,可选(自定义配置信息)
JsonObject summary = doc.createNestedObject("summary");
summary["name"] = "wumei-smart";
summary["chip"] = "esp8266";
summary["author"] = "kerwincui";
summary["version"] = 1.6;
summary["create"] = "2022-06-06";
printMsg("发布设备信息:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
printMsg("主题为:" + prefix + pInfoTopic);
mqttClient.publish((prefix + pInfoTopic).c_str(), output.c_str());
}
// 2.发布时钟同步信,用于获取当前时间(可选)
void publishNtp() {
StaticJsonDocument<128> doc;
doc["deviceSendTime"] = millis();
printMsg("发布主题:" + prefix + pNtpTopic);
printMsg("信息:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
mqttClient.publish((prefix + pNtpTopic).c_str(), output.c_str());
}
// 3.发布属性
void publishProperty(String msg) {
printMsg("发布属性:" + prefix + pPropertyTopic);
printMsg("消息:" + msg);
mqttClient.publish((prefix + pPropertyTopic).c_str(), msg.c_str());
}
// 4.发布功能
void publishFunction(String msg) {
printMsg("发布属性(功能):" + prefix + pPropertyTopic);
printMsg("消息:" + msg);
mqttClient.publish((prefix + pPropertyTopic).c_str(), msg.c_str());
}
// 5.发布事件
void publishEvent(String msg) {
printMsg("发布事件:" + prefix + pEventTopic);
printMsg("消息:" + msg);
mqttClient.publish((prefix + pEventTopic).c_str(), msg.c_str());
}
// 6.发布实时监测数据
void publishMonitor(String msg) {
// 发布实时监测数据(不会存储,需要实时存储则发布为属性)
printMsg("发布实时监测消息:" + msg);
mqttClient.publish((prefix + pMonitorTopic).c_str(), msg.c_str());
}

View File

@@ -1,29 +0,0 @@
/*********************************************************************
* function 设备交互
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#ifndef _MQTT_H
#define _MQTT_H
#include "Config.h"
// 订阅系统主题
void subscribeTopic();
// 发布设备信息
void publishInfo();
// 发布时钟同步信息
void publishNtp();
// 发布事件
void publishEvent(String msg);
// 发布实时监测数据(不会存储,需要实时存储则发布属性)
void publishMonitor(String msg);
// 发布属性
void publishProperty(String msg);
// 发布功能
void publishFunction(String msg);
#endif

View File

@@ -1,509 +0,0 @@
/*********************************************************************
* function 用户自定义功能
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#include "User.h"
#define BUTTON 14 // 按键引脚
#define RELAY 12 // 继电器引脚
OneButton button;
// 按钮单击事件
static void buttonClick();
// 按钮双击事件
static void buttonDoubleClick();
// 按钮长按事件
static void buttonLongPress();
// 初始化用户配置
void initUser() {
// 初始化按键为低电平,并添加单击、双击、长按事件
button = OneButton(BUTTON, true, true);
button.attachClick(buttonClick);
button.attachDoubleClick(buttonDoubleClick);
button.attachLongPressStart(buttonLongPress);
}
// Mqtt回调
void mqttCallback(char *topic, byte *payload, unsigned int length) {
blink();
printMsg("接收数据:");
String data = "";
for (int i = 0; i < length; i++) {
Serial.print((char)payload[i]);
data += (char)payload[i];
}
if (strcmp(topic, (prefix + sOtaTopic).c_str()) == 0) {
printMsg("订阅到设备升级指令...");
StaticJsonDocument<256> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
String newVersion = doc["version"];
String downloadUrl = doc["downloadUrl"];
printMsg("固件版本:" + newVersion);
printMsg("下载地址:" + downloadUrl);
} else if (strcmp(topic, (prefix + sInfoTopic).c_str()) == 0) {
printMsg("订阅到设备信息...");
// 发布设备信息
publishInfo();
} else if (strcmp(topic, (prefix + sNtpTopic).c_str()) == 0) {
printMsg("订阅到NTP时间...");
StaticJsonDocument<256> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
// 计算设备当前时间:(${serverRecvTime} + ${serverSendTime} + ${deviceRecvTime} - ${deviceSendTime}) / 2
float deviceSendTime = doc["deviceSendTime"];
float serverSendTime = doc["serverSendTime"];
float serverRecvTime = doc["serverRecvTime"];
float deviceRecvTime = millis();
float now = (serverSendTime + serverRecvTime + deviceRecvTime - deviceSendTime) / 2;
printMsg("当前时间:" + String(now, 0));
} else if (strcmp(topic, (prefix + sPropertyTopic).c_str()) == 0 || strcmp(topic, (prefix + sPropertyOnline).c_str()) == 0) {
printMsg("订阅到属性指令...");
printMsg("新版本属性和功能合并为功能主题...");
// processProperty(data);
} else if (strcmp(topic, (prefix + sFunctionTopic).c_str()) == 0 || strcmp(topic, (prefix + sFunctionOnline).c_str()) == 0) {
printMsg("订阅到功能指令...");
processFunction(data);
} else if (strcmp(topic, (prefix + sMonitorTopic).c_str()) == 0) {
printMsg("订阅到实时监测指令...");
StaticJsonDocument<128> doc;
DeserializationError error = deserializeJson(doc, payload);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
monitorCount = doc["count"];
monitorInterval = doc["interval"];
}
}
// 随机生成属性值
String randomPropertyData() {
// 匹配云端定义的数据,随机数代替传感器值
float randFloat = 0;
int randInt = 0;
StaticJsonDocument<1024> doc;
JsonObject objTmeperature = doc.createNestedObject();
objTmeperature["id"] = "temperature";
randFloat = random(1000, 3000);
objTmeperature["value"] = (String)(randFloat / 100);
objTmeperature["remark"] = (String)millis();
JsonObject objBrightness = doc.createNestedObject();
objBrightness["id"] = "brightness";
randInt = random(1000, 10000);
objBrightness["value"] = (String)(randInt);
objBrightness["remark"] = (String)millis();
JsonObject category_light = doc.createNestedObject();
category_light["id"] = "category_light";
randInt = random(0, 100);
category_light["value"] = (String)(randInt);
category_light["remark"] = (String)millis();
JsonObject category_humidity = doc.createNestedObject();
category_humidity["id"] = "category_humidity";
randInt = random(0, 100);
category_humidity["value"] = (String)(randInt);
category_humidity["remark"] = (String)millis();
JsonObject category_temperature = doc.createNestedObject();
category_temperature["id"] = "category_temperature";
randInt = random(0, 100);
category_temperature["value"] = (String)(randInt);
category_temperature["remark"] = (String)millis();
// 子设备数据,使用前缀array_00标识
JsonObject array_00_device_co2 = doc.createNestedObject();
array_00_device_co2["id"] = "array_00_device_co2";
randInt = random(100, 6000);
array_00_device_co2["value"] = (String)(randInt);
array_00_device_co2["remark"] = (String)millis();
JsonObject array_00_device_temperature = doc.createNestedObject();
array_00_device_temperature["id"] = "array_00_device_temperature";
randInt = random(0, 100);
array_00_device_temperature["value"] = (String)(randInt);
array_00_device_temperature["remark"] = (String)millis();
JsonObject array_01_device_co2 = doc.createNestedObject();
array_01_device_co2["id"] = "array_01_device_co2";
randInt = random(100, 6000);
array_01_device_co2["value"] = (String)(randInt);
array_01_device_co2["remark"] = (String)millis();
JsonObject array_01_device_temperature = doc.createNestedObject();
array_01_device_temperature["id"] = "array_01_device_temperature";
randInt = random(0, 100);
array_01_device_temperature["value"] = (String)(randInt);
array_01_device_temperature["remark"] = (String)millis();
JsonObject array_02_device_co2 = doc.createNestedObject();
array_02_device_co2["id"] = "array_02_device_co2";
randInt = random(100, 6000);
array_02_device_co2["value"] = (String)(randInt);
array_02_device_co2["remark"] = (String)millis();
JsonObject array_02_device_temperature = doc.createNestedObject();
array_02_device_temperature["id"] = "array_02_device_temperature";
randInt = random(0, 100);
array_02_device_temperature["value"] = (String)(randInt);
array_02_device_temperature["remark"] = (String)millis();
JsonObject array_03_device_co2 = doc.createNestedObject();
array_03_device_co2["id"] = "array_03_device_co2";
randInt = random(100, 6000);
array_03_device_co2["value"] = (String)(randInt);
array_03_device_co2["remark"] = (String)millis();
JsonObject array_03_device_temperature = doc.createNestedObject();
array_03_device_temperature["id"] = "array_03_device_temperature";
randInt = random(0, 100);
array_03_device_temperature["value"] = (String)(randInt);
array_03_device_temperature["remark"] = (String)millis();
JsonObject array_04_device_co2 = doc.createNestedObject();
array_04_device_co2["id"] = "array_04_device_co2";
randInt = random(100, 6000);
array_04_device_co2["value"] = (String)(randInt);
array_04_device_co2["remark"] = (String)millis();
JsonObject array_04_device_temperature = doc.createNestedObject();
array_04_device_temperature["id"] = "array_04_device_temperature";
randInt = random(0, 100);
array_04_device_temperature["value"] = (String)(randInt);
array_04_device_temperature["remark"] = (String)millis();
printMsg("模拟监测数据值:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
return output;
}
// 随机生成实时监测值
String randomMonitorData() {
// 匹配云端定义的监测数据,随机数代替监测值
float randFloat = 0;
int randInt = 0;
StaticJsonDocument<1024> doc;
JsonObject objTmeperature = doc.createNestedObject();
objTmeperature["id"] = "temperature";
randFloat = random(1000, 3000);
objTmeperature["value"] = (String)(randFloat / 100);
objTmeperature["remark"] = (String)millis();
JsonObject objBrightness = doc.createNestedObject();
objBrightness["id"] = "brightness";
randInt = random(1000, 10000);
objBrightness["value"] = (String)(randInt);
objBrightness["remark"] = (String)millis();
JsonObject category_light = doc.createNestedObject();
category_light["id"] = "category_light";
randInt = random(0, 100);
category_light["value"] = (String)(randInt);
category_light["remark"] = (String)millis();
JsonObject category_humidity = doc.createNestedObject();
category_humidity["id"] = "category_humidity";
randInt = random(0, 100);
category_humidity["value"] = (String)(randInt);
category_humidity["remark"] = (String)millis();
// 子设备数据,使用前缀array_00标识
JsonObject array_00_device_co2 = doc.createNestedObject();
array_00_device_co2["id"] = "array_00_device_co2";
randInt = random(100, 6000);
array_00_device_co2["value"] = (String)(randInt);
array_00_device_co2["remark"] = (String)millis();
JsonObject array_01_device_co2 = doc.createNestedObject();
array_01_device_co2["id"] = "array_01_device_co2";
randInt = random(100, 6000);
array_01_device_co2["value"] = (String)(randInt);
array_01_device_co2["remark"] = (String)millis();
JsonObject array_02_device_co2 = doc.createNestedObject();
array_02_device_co2["id"] = "array_02_device_co2";
randInt = random(100, 6000);
array_02_device_co2["value"] = (String)(randInt);
array_02_device_co2["remark"] = (String)millis();
JsonObject array_03_device_co2 = doc.createNestedObject();
array_03_device_co2["id"] = "array_03_device_co2";
randInt = random(100, 6000);
array_03_device_co2["value"] = (String)(randInt);
array_03_device_co2["remark"] = (String)millis();
JsonObject array_04_device_co2 = doc.createNestedObject();
array_04_device_co2["id"] = "array_04_device_co2";
randInt = random(100, 6000);
array_04_device_co2["value"] = (String)(randInt);
array_04_device_co2["remark"] = (String)millis();
printMsg("模拟监测数据值:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
return output;
}
// 物模型-属性处理
void processProperty(String msg) {
StaticJsonDocument<1024> doc;
DeserializationError error = deserializeJson(doc, msg);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
for (JsonObject object : doc.as<JsonArray>()) {
// 匹配云端定义的属性(不包含属性中的监测数据)
const char *id = object["id"];
const char *value = object["value"];
printMsg((String)id + "" + (String)value);
// 处理子设备的数据上报
processSubDeviceReport(id, value);
}
// 最后发布属性,服务端订阅存储(重要)
publishProperty(msg);
}
// 处理子设备和功能分组数据上报
void processSubDeviceReport(const char *id, const char *value) {
int number = atoi(value);
number = number > 10 ? 10 : number;
int randInt = 0;
for (int i = 0; i < number; i++) {
StaticJsonDocument<1024> doc;
if (strcmp(id, "array_00_device_report_monitor") == 0) {
JsonObject array_00_device_co2 = doc.createNestedObject();
array_00_device_co2["id"] = "array_00_device_co2";
randInt = random(100, 6000);
array_00_device_co2["value"] = (String)(randInt);
array_00_device_co2["remark"] = (String)millis();
JsonObject array_00_device_temperature = doc.createNestedObject();
array_00_device_temperature["id"] = "array_00_device_temperature";
randInt = random(0, 100);
array_00_device_temperature["value"] = (String)(randInt);
array_00_device_temperature["remark"] = (String)millis();
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
publishProperty(output);
delay(1000);
} else if (strcmp(id, "array_01_device_report_monitor") == 0) {
JsonObject array_01_device_co2 = doc.createNestedObject();
array_01_device_co2["id"] = "array_01_device_co2";
randInt = random(100, 6000);
array_01_device_co2["value"] = (String)(randInt);
array_01_device_co2["remark"] = (String)millis();
JsonObject array_01_device_temperature = doc.createNestedObject();
array_01_device_temperature["id"] = "array_01_device_temperature";
randInt = random(0, 100);
array_01_device_temperature["value"] = (String)(randInt);
array_01_device_temperature["remark"] = (String)millis();
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
publishProperty(output);
delay(1000);
} else if (strcmp(id, "array_02_device_report_monitor") == 0) {
JsonObject array_02_device_co2 = doc.createNestedObject();
array_02_device_co2["id"] = "array_02_device_co2";
randInt = random(100, 6000);
array_02_device_co2["value"] = (String)(randInt);
array_02_device_co2["remark"] = (String)millis();
JsonObject array_02_device_temperature = doc.createNestedObject();
array_02_device_temperature["id"] = "array_02_device_temperature";
randInt = random(0, 100);
array_02_device_temperature["value"] = (String)(randInt);
array_02_device_temperature["remark"] = (String)millis();
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
publishProperty(output);
delay(1000);
} else if (strcmp(id, "array_03_device_report_monitor") == 0) {
JsonObject array_03_device_co2 = doc.createNestedObject();
array_03_device_co2["id"] = "array_03_device_co2";
randInt = random(100, 6000);
array_03_device_co2["value"] = (String)(randInt);
array_03_device_co2["remark"] = (String)millis();
JsonObject array_03_device_temperature = doc.createNestedObject();
array_03_device_temperature["id"] = "array_03_device_temperature";
randInt = random(0, 100);
array_03_device_temperature["value"] = (String)(randInt);
array_03_device_temperature["remark"] = (String)millis();
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
publishProperty(output);
delay(1000);
} else if (strcmp(id, "array_04_device_report_monitor") == 0) {
JsonObject array_04_device_co2 = doc.createNestedObject();
array_04_device_co2["id"] = "array_04_device_co2";
randInt = random(100, 6000);
array_04_device_co2["value"] = (String)(randInt);
array_04_device_co2["remark"] = (String)millis();
JsonObject array_04_device_temperature = doc.createNestedObject();
array_04_device_temperature["id"] = "array_04_device_temperature";
randInt = random(0, 100);
array_04_device_temperature["value"] = (String)(randInt);
array_04_device_temperature["remark"] = (String)millis();
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
publishProperty(output);
delay(1000);
} else if (strcmp(id, "category_report_monitor") == 0) {
JsonObject category_light = doc.createNestedObject();
category_light["id"] = "category_light";
randInt = random(0, 100);
category_light["value"] = (String)(randInt);
category_light["remark"] = (String)millis();
JsonObject category_humidity = doc.createNestedObject();
category_humidity["id"] = "category_humidity";
randInt = random(0, 100);
category_humidity["value"] = (String)(randInt);
category_humidity["remark"] = (String)millis();
JsonObject category_temperature = doc.createNestedObject();
category_temperature["id"] = "category_temperature";
randInt = random(0, 100);
category_temperature["value"] = (String)(randInt);
category_temperature["remark"] = (String)millis();
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
publishProperty(output);
delay(1000);
}
}
}
// 物模型-功能处理
void processFunction(String msg) {
StaticJsonDocument<1024> doc;
DeserializationError error = deserializeJson(doc, msg);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
for (JsonObject object : doc.as<JsonArray>()) {
// 匹配云端定义的功能
const char *id = object["id"];
const char *value = object["value"];
if (strcmp(id, "switch") == 0) {
printMsg("开关 switch" + (String)value);
if (strcmp(value, "1") == 0) {
// 打开继电器
relayStatus(true);
} else if (strcmp(value, "0") == 0) {
// 关闭继电器
relayStatus(false);
}
} else if (strcmp(id, "gear") == 0) {
printMsg("档位 gear" + (String)value);
} else if (strcmp(id, "light_color") == 0) {
printMsg("灯光颜色 light_color" + (String)value);
} else if (strcmp(id, "message") == 0) {
printMsg("屏显消息 message" + (String)value);
} else if (strcmp(id, "report_monitor") == 0) {
int number = atoi(value);
number > 10 ? number = 10 : number;
for (int i = 0; i < number; i++) {
Serial.println(number);
String msg = randomPropertyData();
printMsg("订阅到上报监测数据指令,上报数据:");
printMsg(msg);
publishProperty(msg);
delay(1000);
}
}
// 处理子设备的数据上报
processSubDeviceReport(id, value);
}
// 最后发布功能,服务端订阅存储(重要)
publishFunction(msg);
}
// 物模型-事件上传
void processEvent() {
// 匹配云端的事件
StaticJsonDocument<512> doc;
JsonObject objTmeperature = doc.createNestedObject();
objTmeperature["id"] = "height_temperature";
objTmeperature["value"] = "40";
objTmeperature["remark"] = "温度过高警告";
JsonObject objException = doc.createNestedObject();
objException["id"] = "exception";
objException["value"] = "异常消息消息内容XXXXXXXX";
objException["remark"] = "设备发生错误";
printMsg("发布事件:");
serializeJson(doc, Serial);
String output;
serializeJson(doc, output);
// 最后发布功能,服务端订阅存储(重要)
publishEvent(output);
}
//打开继电器A
void relayStatus(bool status) {
pinMode(RELAY, OUTPUT);
if (status) {
digitalWrite(RELAY, HIGH);
} else {
digitalWrite(RELAY, LOW);
}
}
// 按钮单击事件
static void buttonClick() {
printMsg("检测到按键单击,打开继电器");
relayStatus(true);
// 匹配云端定义的开关,格式如:[{"id":"switch","value":"1"}]
String msg = "[{\"id\":\"switch\",\"value\":\"1\"}]";
publishProperty(msg);
}
// 按钮双击事件
static void buttonDoubleClick() {
printMsg("检测到按键双击,关闭继电器");
relayStatus(false);
// 匹配云端定义的开关,格式如:[{"id":"switch","value":"0"}]
String msg = "[{\"id\":\"switch\",\"value\":\"0\"}]";
publishProperty(msg);
}
// 按钮长按事件,进入配网模式
static void buttonLongPress() {
printMsg("检测到按键长按");
if (isApMode) {
printMsg("设备重启...");
ESP.restart();
} else {
printMsg("开始AP配网");
startApConfig();
}
}

View File

@@ -1,37 +0,0 @@
/*********************************************************************
* function 用户自定义功能
* board: esp8266 core for arduino v3.0.2
* library PubSubClient2.8.0 & ArduinoJson6.19.1 & OneButton2.0.4
* source: https://gitee.com/kerwincui/wumei-smart
* copyright: FastBee and kerwincui all rights reserved.
********************************************************************/
#ifndef _USER_H
#define _USER_H
#include "Config.h"
#include "Mqtt.h"
#include <OneButton.h> // 版本2.0.4
extern OneButton button;
// 初始化用户配置
void initUser();
// Mqtt回调
void mqttCallback(char *topic, byte *payload, unsigned int length);
// 属性处理(物模型)
void processProperty(String msg);
// 处理子设备数据上报
void processSubDeviceReport(const char *id, const char *value);
// 功能处理(物模型)
void processFunction(String msg);
// 事件处理(无模型)
void processEvent();
// 模拟属性值
String randomPropertyData();
// 模拟实时监测值
String randomMonitorData();
// 控制继电器状态
void relayStatus(bool status);
#endif

View File

View File

@@ -1,47 +0,0 @@
FROM espressif/idf
ARG DEBIAN_FRONTEND=nointeractive
ARG CONTAINER_USER=esp
ARG USER_UID=1050
ARG USER_GID=$USER_UID
RUN apt-get update \
&& apt install -y -q \
cmake \
git \
libglib2.0-0 \
libnuma1 \
libpixman-1-0 \
&& rm -rf /var/lib/apt/lists/*
# QEMU
ENV QEMU_REL=esp_develop_8.2.0_20240122
ENV QEMU_SHA256=e7c72ef5705ad1444d391711088c8717fc89f42e9bf6d1487f9c2a326b8cfa83
ENV QEMU_DIST=qemu-xtensa-softmmu-${QEMU_REL}-x86_64-linux-gnu.tar.xz
ENV QEMU_URL=https://github.com/espressif/qemu/releases/download/esp-develop-8.2.0-20240122/${QEMU_DIST}
ENV LC_ALL=C.UTF-8
ENV LANG=C.UTF-8
RUN wget --no-verbose ${QEMU_URL} \
&& echo "${QEMU_SHA256} *${QEMU_DIST}" | sha256sum --check --strict - \
&& tar -xf $QEMU_DIST -C /opt \
&& rm ${QEMU_DIST}
ENV PATH=/opt/qemu/bin:${PATH}
RUN groupadd --gid $USER_GID $CONTAINER_USER \
&& adduser --uid $USER_UID --gid $USER_GID --disabled-password --gecos "" ${CONTAINER_USER} \
&& usermod -a -G root $CONTAINER_USER && usermod -a -G dialout $CONTAINER_USER
RUN chmod -R 775 /opt/esp/python_env/
USER ${CONTAINER_USER}
ENV USER=${CONTAINER_USER}
WORKDIR /home/${CONTAINER_USER}
RUN echo "source /opt/esp/idf/export.sh > /dev/null 2>&1" >> ~/.bashrc
ENTRYPOINT [ "/opt/esp/entrypoint.sh" ]
CMD ["/bin/bash", "-c"]

View File

@@ -1,36 +0,0 @@
{
"name": "ESP-IDF QEMU",
"build": {
"dockerfile": "Dockerfile"
},
"customizations": {
"vscode": {
"settings": {
"terminal.integrated.defaultProfile.linux": "bash",
"idf.espIdfPath": "/opt/esp/idf",
"idf.customExtraPaths": "",
"idf.pythonBinPath": "/opt/esp/python_env/idf5.4_py3.12_env/bin/python",
"idf.toolsPath": "/opt/esp",
"idf.gitPath": "/usr/bin/git"
},
"extensions": [
"espressif.esp-idf-extension"
]
},
"codespaces": {
"settings": {
"terminal.integrated.defaultProfile.linux": "bash",
"idf.espIdfPath": "/opt/esp/idf",
"idf.customExtraPaths": "",
"idf.pythonBinPath": "/opt/esp/python_env/idf5.4_py3.12_env/bin/python",
"idf.toolsPath": "/opt/esp",
"idf.gitPath": "/usr/bin/git"
},
"extensions": [
"espressif.esp-idf-extension",
"espressif.esp-idf-web"
]
}
},
"runArgs": ["--privileged"]
}

View File

@@ -1,2 +0,0 @@
build/
.idea

View File

@@ -1,23 +0,0 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"D:/soft/esp-idf/Espressif/frameworks/esp-idf-v4.4.7/components/**",
"./esp-aliyun/**",
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"windowsSdkVersion": "10.0.22621.0",
"compilerPath": "cl.exe",
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64"
}
],
"version": 4
}

View File

@@ -1,35 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "ESP-IDF Debug: Launch",
"type": "espidf",
"request": "launch"
},
{
"name": "GDB",
"type": "cppdbg",
"request": "launch",
"MIMode": "gdb",
"miDebuggerPath": "${command:espIdf.getXtensaGdb}",
"program": "${workspaceFolder}/build/${command:espIdf.getProjectName}.elf",
"windows": {
"program": "${workspaceFolder}\\build\\${command:espIdf.getProjectName}.elf"
},
"cwd": "${workspaceFolder}",
"environment": [{ "name": "PATH", "value": "${config:idf.customExtraPaths}" }],
"setupCommands": [
{ "text": "set remotetimeout 100" },
{ "text": "target extended-remote :3333" },
{ "text": "set remote hardware-watchpoint-limit 2"},
{ "text": "mon reset halt" },
{ "text": "thb app_main" },
{ "text": "flushregs" }
],
"externalConsole": false,
"logging": {
"engineLogging": true
}
}
]
}

View File

@@ -1,19 +0,0 @@
{
"idf.portWin": "COM6",
"idf.adapterTargetName": "esp32s3",
"idf.openOcdConfigs": [
"board/esp32s3-builtin.cfg"
],
"idf.flashType": "UART",
"files.associations": {
"infra_config.h": "c",
"cjson.h": "c",
"dm_wrapper.h": "c",
"infra_defs.h": "c",
"freertos.h": "c",
"stddef.h": "c",
"string.h": "c",
"esp_log.h": "c",
"mqtt_client.h": "c"
}
}

View File

@@ -1,12 +0,0 @@
# (Automatically converted from project Makefile by convert_to_cmake.py.)
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
set (EXTRA_COMPONENT_DIRS "./esp-aliyun")
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp_fastbee_aliyun)

View File

@@ -1,11 +0,0 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := esp_fastbee_aliyun
EXTRA_COMPONENT_DIRS := $(realpath ./esp-aliyun)
SDKCONFIG_DEFAULTS := sdkconfig_$(chip).defaults
include $(IDF_PATH)/make/project.mk

View File

@@ -1,152 +0,0 @@
# ESP 设备对接Fastbee物联网云平台指南
# 目录
- [1.目的](#aim)
- [2.硬件准备](#hardwareprepare)
- [3.云平台准备](#fastbeeprepare)
- [4.环境搭建](#compileprepare)
- [5.SDK 准备](#sdkprepare)
- [6.编译&烧写&运行](#makeflash)
# <span id = "aim">1.目的</span>
本项目基于esp-aliyun SDK进行修改介绍 ESP 设备对接Fastbee物联网云平台阿里云平台的具体流程。
主要实现的功能:
- 支持 <Fastbee物联网云平台> 配网&接入&控制
- 支持 <云智能> APP 一键配网
- 支持 <云智能> APP 控制设备
- 支持 <天猫精灵智能音箱> 控制设备
- 支持 <天猫精灵智能音箱> 配网并控制设备
- 支持 LED 控制(开关,颜色等)
- 支持 OTA 升级
# <span id = "hardwareprepare">2.硬件准备</span>
- **linux 环境**
用来编译 & 烧写 & 运行等操作的必须环境。
> windows 用户可安装虚拟机,在虚拟机中安装 linux。
- **ESP 设备**
ESP 设备包括 [ESP芯片](https://www.espressif.com/zh-hans/products/hardware/socs)[ESP模组](https://www.espressif.com/zh-hans/products/hardware/modules)[ESP开发板](https://www.espressif.com/zh-hans/products/hardware/development-boards)等。
- **USB 线**
连接 PC 和 ESP 设备,用来烧写/下载程序,查看 log 等。
# <span id = "compileprepare">3.云平台准备</span>
# <span id = "compileprepare">4.环境搭建</span>
**如果您熟悉 ESP 开发环境,可以很顺利理解下面步骤; 如果您不熟悉某个部分,比如编译,烧录,需要您结合官方的相关文档来理解。如您需阅读 [ESP-IDF 编程指南](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/index.html)文档等。**
## 4.1 编译器环境搭建
- ESP8266 平台: 根据[官方链接](https://github.com/espressif/ESP8266_RTOS_SDK)中 **Get toolchain**,获取 toolchain
- ESP32 & ESP32S2 平台:根据[官方链接](https://github.com/espressif/esp-idf/blob/master/docs/zh_CN/get-started/linux-setup.rst)中 **工具链的设置**,下载 toolchain
toolchain 设置参考 [ESP-IDF 编程指南](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/get-started/index.html#get-started-setup-toolchain)。
## 4.2 烧录工具/下载工具获取
- ESP8266 平台:烧录工具位于 [ESP8266_RTOS_SDK](https://github.com/espressif/ESP8266_RTOS_SDK) 下 `./components/esptool_py/esptool/esptool.py`
- ESP32 & ESP32S2 平台:烧录工具位于 [esp-idf](https://github.com/espressif/esp-idf) 下 `./components/esptool_py/esptool/esptool.py`
esptool 功能参考:
```
$ ./components/esptool_py/esptool/esptool.py --help
```
# <span id = "sdkprepare">5.SDK 准备</span>
- [esp-aliyun SDK](https://github.com/espressif/esp-aliyun), 通过该 SDK 可实现使用 MQTT 协议,连接 ESP 设备到阿里云。
- Espressif SDK
- ESP32 & ESP32S2 平台: [ESP-IDF](https://github.com/espressif/esp-idf)
- ESP8266 平台: [ESP8266_RTOS_SDK](https://github.com/espressif/ESP8266_RTOS_SDK)
> Espressif SDK 下载好后:
> ESP-IDF: 请切换到 v4.2 分支: `git checkout v4.2`
如果需要使用 ESP32S2 模组,请切换到 v4.2 版本: `git checkout v4.2`
> ESP8266_RTOS_SDK: 请切换到 v3.3 分支: `git checkout v3.3`
# <span id = "makeflash">6.编译 & 烧写 & 运行</span>
## 6.1 编译
### 6.1.1 导出编译器
参考 [工具链的设置](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/get-started/linux-setup.html)
### 6.1.2 编译 demo 示例
**由于 esp32 和 esp8266 将会采用不同的 sdkconfig.defaults 和对应的 partitions.csv在对应的 make 命令中加入了对应的芯片选项,如 chip=esp32 或 chip=esp8266。**
当 chip=esp32 时将默认使用 sdkconfig_esp32.defaults 以及 partitions_esp32.csv。
当 chip=esp8266 时将默认使用 sdkconfig_esp8266.defaults 以及 partitions_esp8266.csv。
当使用 esp32s2 时,将默认使用 sdkconfig.defaults sdkconfig.defaults.esp32s2 以及 partitions_esp32s2.csv编译方式与 8266 & 32 都不一样,需要使用 cmake 进行编译。
以上需要特别注意。
在 esp-aliyun 目录下执行:
```
cd fastbee\sdk\ESP-IDF\esp-aliyun
make chip=esp32 defconfig
make menuconfig
```
如果需要编译esp32s2版本, 请按照如下步骤编译:
在 esp-aliyun 目录下执行:
```
cd fastbee\sdk\ESP-IDF\esp-aliyun
idf.py set-target esp32s2
idf.py menuconfig
```
- 配置烧写串口
- 配置 `WIFI_SSID`, `WIFI_PASSWORD`
如果需要编译esp32s3版本, 请按照如下步骤编译:
在 esp-aliyun 目录下执行:
```
cd fastbee\sdk\ESP-IDF\esp-aliyun
idf.py set-target esp32s3
idf.py menuconfig
```
2.生成最终 bin
```
make -j8
```
使用 esp32s2/esp32s3 生成 bin
```
idf.py build
```
## 6.2 擦除 & 编译烧写 & 下载固件 & 查看 log
将 USB 线连接好 ESP 设备和 PC,确保烧写端口正确。
### 6.2.1[可选] 擦除 flash
```
make erase_flash
```
> 注:无需每次擦除,擦除后需要重做 6.2.3。
### 6.2.2 烧录程序
```
make flash
```
使用 esp32s2/esp32s3 擦除 flash
```
idf.py -p (PORT) erase_flash
```
### 6.2.3 烧录三元组信息
参考 [量产说明](./aliyunsdk/config/mass_mfg/README.md) 文档烧录三元组 NVS 分区。
## 6.2.4 运行
```
make monitor
```
> 也可执行 `make flash monitor` 来编译烧写和查看 log。

View File

@@ -1,127 +0,0 @@
# Smart Light 解决方案
### 解决方案部署
#### 1.参考 [README](./README.md) 文档进行硬件准备、环境搭建、SDK 准备
#### 2.阿里云平台部署
在阿里云 [生活物联网平台](https://living.aliyun.com/#/) 创建产品, 参考[创建产品文档](https://living.aliyun.com/doc#readygo.html).
> 配置较多, 如果不太懂, 也不用纠结, 后续都可以修改.
部署自己的产品, 可参考如下:
新增 RGB 调色功能:
![](_static/p1.png)
新增测试设备, 此处即可以获得`三元组`, 后续需要烧录到 NVS 分区.
![](_static/p2.png)
![](_static/p3.png)
选择面板, 手机 APP 上会显示同样界面; `配网二维码`是贴在产品包装上, 终端客户给设备配网中需扫描此二维码.
![](_static/p4.png)
选择面板时, 主题面板在手机上仅能显示标准界面, 没有 RGB 调色功能. 可以自定义面板, 增加 RGB 调色.
![](_static/p5.png)
![](_static/p6.png)
配网方案选择:
![](_static/p7.png)
完成
![](_static/p8.png)
#### 3.下载本工程
```
git clone https://github.com/espressif/esp-aliyun.git
cd esp-aliyun
```
#### 4.烧录三元组信息
- 参考 [量产说明](../../../config/mass_mfg/README.md) 文档烧录三元组 NVS 分区.
> 如果执行了 `make erase_flash`, 需要重新烧录三元组.
#### 5.配置 `smart light example`
- RGB 灯分别接 ESP32/ESP8266 开发板上 `GPIO0`, `GPIO2`, `GPIO4` (可在 `lightbulb.c` 中修改)
#### 6.编译 `smart light` 并烧录运行
```
cd examples/solutions/smart_light
make chip=esp32 defconfig 或者 make chip=esp8266 defconfig
make -j8 flash monitor
```
使用 esp32s2 请参考根目录 README。
> 在测试配网中, 请先执行 `make erase_flash` .
#### 7.设备第一次运行时, 会进入配网
![](_static/p9.png)
#### 8.手机从[阿里云官网](https://living.aliyun.com/doc#muti-app.html) 下载 `云智能` 公版 APP, 国内用户版.
#### 9.注册好账号后,进入 APP, 右上角扫描, 扫描第二步的二维码配网.
设备端配网成功后会保存 `ssid` 和 `password` :
![](_static/p10.png)
设备与手机绑定成功后, APP 上会弹出灯的配置页面. 返回主页显示灯 `在线`.
![](_static/p11.png)
![](_static/p12.png)
#### 10.控制智能灯
在 APP 上打开灯, 设备端收到消息:
![](_static/p13.png)
在 APP 上设置 RGB 调色:
![](_static/p14.png)
设备端即解析 RGB 颜色, 并设置到具体的灯产品上.
![](_static/p15.png)
#### 11.重新配网
快速重启设备 5 次, 设备会擦除配置信息, 重新进入配网状态.
可以配置快速重启的次数和超时时间.
```
cd examples/solutions/smart_light
make menuconfig
```
![](_static/p20.png)
![](_static/p21.png)
#### 12.OTA 支持
参考 examples/ota/ota_example_mqtt 示例下的 [README](../../ota/ota_example_mqtt/README.md) , 向管理控制台上传固件, 验证固件后, 下发升级指令.
设备端收到升级指令后, 即开始 OTA:
![](_static/p16.png)
升级完成后, 会检查固件的有效性, 下图说明固件有效.
![](_static/p17.png)
iotkit-embedded 目前没有设置软重启操作, 可以手动按模组重启键运行新固件:
![](_static/p18.png)
#### 13.天猫精灵
##### 13.1 天猫精灵控制设备
针对使用公版 APP 的产品,用户可以一键开通天猫精灵,实现天猫精灵音箱对设备的控制. 使用步骤参照[阿里云文档](https://living.aliyun.com/doc#TmallGenie.html).
- 在阿里云 [生活物联网平台](https://living.aliyun.com/#/)上一键开通天猫精灵, 查看功能映射.
- 在 `云智能` [公版 APP]((https://living.aliyun.com/doc#muti-app.html))上绑定天猫精灵账号(即淘宝账号).
注意最后步骤, 否则天猫精灵无法找到设备:
> 在天猫精灵 APP 找到 "阿里智能" 技能, 手动进行 "尝试" 或 "设备同步"(后期会进行自动同步)
> 即可在 "我家" 的设备列表中看到您的设备
完成以上步骤后,您可以通过天猫精灵音箱控制您的设备了. 您可以对天猫精灵说 "天猫精灵,开灯", "天猫精灵, 关灯", "天猫精灵, 把灯调成红色" 或者其他您希望设置的颜色, 设备即响应相应的命令.
##### 13.2 天猫精灵配网并控制设备
阿里云设备支持 `零配` 的配网方式.
使设备进入配网状态, 对天猫精灵说 "天猫精灵,发现设备"
天猫精灵回复 "正在为您扫描, 发现了智能灯, 现在连接吗"
对天猫精灵说 "连接" 或者 "是的"
天猫精灵回复 "好的, 设备连接中, 稍等一下下哦"
设备收到天猫精灵发送的管理帧配网信息, 进行联网:
![](_static/p19.png)
等待联网成功, 天猫精灵说 "智能设备联网成功, 现在用语音控制它试试", 这时您可以通过天猫精灵音箱控制您的设备了.
如果您之前通过云智能 APP 配网, 天猫精灵配网成功后, 云智能 APP 将不再显示设备. 如果继续通过云智能 APP 配网, APP 会配网失败, 显示 "设备添加失败, 设备已被管理员绑定, 请联系管理员解绑或将设备分享给您".
> 在天猫精灵 APP 删除设备, 云智能 APP 再进行配网可以配置成功并显示设备.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 248 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 133 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 170 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 130 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 KiB

View File

@@ -1,7 +0,0 @@
set(COMPONENT_ADD_INCLUDEDIRS .)
set(COMPONENT_SRCS "factory_restore.c")
set(COMPONENT_REQUIRES "esp-aliyun")
register_component()

View File

@@ -1,15 +0,0 @@
menu "[Aliyun]Factory restore"
config FACTORY_QUICK_REBOOT_TIMEOUT
int "Quick reboot timeout, unit second"
default 6
help
Timeout for exiting quick reboot timer, users should reboot device before this timeout.
config FACTORY_QUICK_REBOOT_MAX_TIMES
int "Quick reboot times max record"
default 5
help
If quick reboot times reach this max value, device will do factory restore.
endmenu

View File

@@ -1,2 +0,0 @@
COMPONENT_ADD_INCLUDEDIRS := ./
COMPONENT_SRCDIRS := ./

View File

@@ -1,103 +0,0 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
* it is 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.
*
*/
#include <string.h>
#include <esp_log.h>
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
#include "esp_sleep.h"
#include "dm_wrapper.h"
#include "conn_mgr.h"
#define FACTORY_QUICK_REBOOT_TIMEOUT (CONFIG_FACTORY_QUICK_REBOOT_TIMEOUT * 1000)
#define FACTORY_QUICK_REBOOT_MAX_TIMES CONFIG_FACTORY_QUICK_REBOOT_MAX_TIMES
#define FACTORY_QUICK_REBOOT_TIMES "q_rt"
#define AWSS_KV_RST "awss.rst"
static const char *TAG = "factory_rst";
static esp_err_t factory_restore_handle(void)
{
esp_err_t ret = ESP_OK;
int quick_reboot_times = 0;
/**< If the device restarts within the instruction time, the event_mdoe value will be incremented by one */
int length = sizeof(int);
ret = HAL_Kv_Get(FACTORY_QUICK_REBOOT_TIMES, &quick_reboot_times, &length);
quick_reboot_times++;
ret = HAL_Kv_Set(FACTORY_QUICK_REBOOT_TIMES, &quick_reboot_times, sizeof(int), 0);
if (quick_reboot_times >= FACTORY_QUICK_REBOOT_MAX_TIMES) {
char rst = 0x01;
/* since we cannot report reset status to cloud in this stage, just set the reset flag.
when connects to cloud, awss will do the reset report. */
ret = HAL_Kv_Set(AWSS_KV_RST, &rst, sizeof(rst), 0);
ret = HAL_Kv_Del(FACTORY_QUICK_REBOOT_TIMES);
ESP_LOGW(TAG, "factory restore");
conn_mgr_reset_wifi_config();
} else {
ESP_LOGI(TAG, "quick reboot times %d, don't need to restore", quick_reboot_times);
}
return ret;
}
static void factory_restore_timer_handler(void *timer)
{
if (!xTimerStop(timer, 0)) {
ESP_LOGE(TAG, "xTimerStop timer %p", timer);
}
if (!xTimerDelete(timer, 0)) {
ESP_LOGE(TAG, "xTimerDelete timer %p", timer);
}
/* erase reboot times record */
HAL_Kv_Del(FACTORY_QUICK_REBOOT_TIMES);
ESP_LOGI(TAG, "Quick reboot timeout, clear reboot times");
}
esp_err_t factory_restore_init(void)
{
#ifndef CONFIG_IDF_TARGET_ESP8266
if (esp_sleep_get_wakeup_cause() != ESP_SLEEP_WAKEUP_UNDEFINED) {
HAL_Kv_Del(FACTORY_QUICK_REBOOT_TIMES);
return ESP_OK;
}
#endif
TimerHandle_t timer = xTimerCreate("factory_clear", FACTORY_QUICK_REBOOT_TIMEOUT / portTICK_RATE_MS,
false, NULL, factory_restore_timer_handler);
xTimerStart(timer, portMAX_DELAY);
return factory_restore_handle();
}

View File

@@ -1,50 +0,0 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2018 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on ESPRESSIF SYSTEMS products only, in which case,
* it is 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.
*
*/
#pragma once
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Initialize factory restore
*
* @note For some devices which don't have a physical restore key or button, such as light, we use
* a quick reboot method to record the reboot times, if users quickly reboot the device within
* a specific time, configured by FACTORY_QUICK_REBOOT_TIMEOUT, and reboot several times,
* configured by FACTORY_QUICK_REBOOT_MAX_TIMES, the device will do a factory restore, clear
* stored ssid and password.
*
* @return
* - ESP_OK : OK
* - others : fail
*/
esp_err_t factory_restore_init(void);
#ifdef __cplusplus
}
#endif

View File

@@ -1,7 +0,0 @@
set(COMPONENT_ADD_INCLUDEDIRS "./include")
set(COMPONENT_SRCS "fastbee.c" "Config.c" "Mqtt.c")
set(COMPONENT_REQUIRES "esp-aliyun" "mqtt" "json")
register_component()

View File

@@ -1,174 +0,0 @@
/*********************************************************************
* function 设备交互
* board: esp8266,esp32,esp32s2,esp32s3 core for esp-idf v4.4.7
* source: https://gitee.com/zhuangpengli/IOTDeviceSDK
* copyright: zhuangpeng.li all rights reserved.
********************************************************************/
#include "Config.h"
#include "esp_err.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "nvs.h"
#include "dm_wrapper.h"
// 设备信息配置
char g_userId[USER_ID_LEN + 1] = {0};
char g_productId[PRODUCT_ID_LEN + 1] = {0};
char g_deviceSN[DEVICE_SN_LEN + 1] = {0};
// Mqtt配置
char g_mqtt_url[MQTT_URL_LEN + 1] = {0};
char g_mqtt_username[MQTT_USERNAME_LEN + 1] = {0};
char g_mqtt_password[MQTT_PASSWORD_LEN + 1] = {0};
char g_mqtt_clientid[MQTT_CLIENTID_LEN + 1] = {0};
char g_mqtt_prefix[MQTT_PREFIX_LEN + 1] = {0};
float rssi = 0;
char wumei_iv[17] = "wumei-smart-open";
int monitorCount = 0;
long monitorInterval = 1000;
bool isApMode = false;
/********************************** begin 可配置的项 **********************************/
// wifi信息
char *wifiSsid = "";
char *wifiPwd = "";
// 产品启用授权码,则授权码不能为空
char *authCode = "";
// 设备信息配置
float firmwareVersion = 1.0;
// 经度和纬度可选,如果产品使用设备定位,则必须传
float latitude = 0;
float longitude = 0;
// Mqtt配置
char mqttSecret[17] = "KV52PPZ813EFCQD8";
// NTP地址用于获取时间,修改为自己部署项目的接口地址)
char *ntpServer = "http://fastbee.cn:8080/iot/tool/ntp?deviceSendTime=";
/********************************** end 可配置的项 **********************************/
static const char *TAG = "FASTBEE_CONFIG";
static bool s_part_init_flag;
static esp_err_t HAL_ProductParam_init(void)
{
esp_err_t ret = ESP_OK;
do {
if (s_part_init_flag == false) {
if ((ret = nvs_flash_init_partition(MFG_FASTBEE_PARTITION_NAME)) != ESP_OK) {
ESP_LOGE(TAG, "NVS Flash init %s failed, Please check that you have flashed fctry partition!!!", MFG_FASTBEE_PARTITION_NAME);
break;
}
s_part_init_flag = true;
}
} while (0);
return ret;
}
static int HAL_GetProductParam(char *param_name, const char *param_name_str)
{
esp_err_t ret;
size_t read_len = 0;
nvs_handle handle;
do {
if (HAL_ProductParam_init() != ESP_OK) {
break;
}
if (param_name == NULL) {
ESP_LOGE(TAG, "%s param %s NULL", __func__, param_name);
break;
}
ret = nvs_open_from_partition(MFG_FASTBEE_PARTITION_NAME, NVS_FASTBEE_PRODUCT, NVS_READONLY, &handle);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s nvs_open failed with %x", __func__, ret);
break;
}
ret = nvs_get_str(handle, param_name_str, NULL, (size_t *)&read_len);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s nvs_get_str get %s failed with %x", __func__, param_name_str, ret);
break;
}
ret = nvs_get_str(handle, param_name_str, param_name, (size_t *)&read_len);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "%s nvs_get_str get %s failed with %x", __func__, param_name_str, ret);
} else {
ESP_LOGV(TAG, "%s %s %s", __func__, param_name_str, param_name);
}
nvs_close(handle);
} while (0);
return read_len;
}
int HAL_GetMqttUrl(char mqtt_url[MQTT_URL_LEN + 1])
{
return HAL_GetProductParam(mqtt_url, "MQTTUrl");
}
int HAL_GetMqttUsername(char mqtt_username[MQTT_USERNAME_LEN + 1])
{
return HAL_GetProductParam(mqtt_username, "MQTTUsername");
}
int HAL_GetMqttPassword(char mqtt_password[MQTT_PASSWORD_LEN + 1])
{
return HAL_GetProductParam(mqtt_password, "MQTTPassword");
}
int HAL_GetMqttClientId(char mqtt_clientid[MQTT_CLIENTID_LEN + 1])
{
return HAL_GetProductParam(mqtt_clientid, "MQTTClientId");
}
int HAL_GetUserId(char userid[USER_ID_LEN + 1])
{
return HAL_GetProductParam(userid, "UserId");
}
int HAL_GetDeviceSN(char deviceSN[DEVICE_SN_LEN + 1])
{
return HAL_GetProductParam(deviceSN, "DeviceSN");
}
int HAL_GetProductId(char productId[PRODUCT_ID_LEN + 1])
{
return HAL_GetProductParam(productId, "ProductId");
}
void loadConfig() {
HAL_GetMqttUrl(g_mqtt_url);
if (strlen(g_mqtt_url) == 0) {
//加载默认配置
memcpy(g_mqtt_url, CONFIG_ESP_MQTT_PLATFORM_FASTBEE_URL, strlen(CONFIG_ESP_MQTT_PLATFORM_FASTBEE_URL));
}
HAL_GetMqttUsername(g_mqtt_username);
if (strlen(g_mqtt_username) == 0) {
memcpy(g_mqtt_username, CONFIG_ESP_MQTT_PLATFORM_FASTBEE_USERNAME, strlen(CONFIG_ESP_MQTT_PLATFORM_FASTBEE_USERNAME));
}
HAL_GetMqttPassword(g_mqtt_password);
if (strlen(g_mqtt_password) == 0) {
memcpy(g_mqtt_password, CONFIG_ESP_MQTT_PLATFORM_FASTBEE_PASSWORD, strlen(CONFIG_ESP_MQTT_PLATFORM_FASTBEE_PASSWORD));
}
HAL_GetMqttClientId(g_mqtt_clientid);
if (strlen(g_mqtt_clientid) == 0) {
HAL_Snprintf(g_mqtt_clientid, sizeof(g_mqtt_clientid), "S&%s&%s&%s", CONFIG_ESP_MQTT_PLATFORM_FASTBEE_DEVICESN,CONFIG_ESP_MQTT_PLATFORM_FASTBEE_PRODICTID,CONFIG_ESP_MQTT_PLATFORM_FASTBEE_USERID);
}
HAL_GetUserId(g_userId);
if (strlen(g_userId) == 0) {
memcpy(g_userId, CONFIG_ESP_MQTT_PLATFORM_FASTBEE_USERID, strlen(CONFIG_ESP_MQTT_PLATFORM_FASTBEE_USERID));
}
HAL_GetDeviceSN(g_productId);
if (strlen(g_productId) == 0) {
memcpy(g_productId, CONFIG_ESP_MQTT_PLATFORM_FASTBEE_PRODICTID, strlen(CONFIG_ESP_MQTT_PLATFORM_FASTBEE_PRODICTID));
}
HAL_GetProductId(g_deviceSN);
if (strlen(g_deviceSN) == 0) {
memcpy(g_deviceSN, CONFIG_ESP_MQTT_PLATFORM_FASTBEE_DEVICESN, strlen(CONFIG_ESP_MQTT_PLATFORM_FASTBEE_DEVICESN));
}
HAL_Snprintf(g_mqtt_prefix, sizeof(g_mqtt_prefix), "/%s/%s", g_productId,g_deviceSN);
}

View File

@@ -1,158 +0,0 @@
/*********************************************************************
* function 设备交互
* board: esp8266,esp32,esp32s2,esp32s3 core for esp-idf v4.4.7
* source: https://gitee.com/zhuangpengli/IOTDeviceSDK
* copyright: zhuangpeng.li all rights reserved.
********************************************************************/
#include "Mqtt.h"
#include "Config.h"
#include "cJSON.h"
#include "esp_log.h"
#include "dm_wrapper.h"
char *sInfoTopic = "/info/get";
char *sOtaTopic = "/ota/get";
char *sNtpTopic = "/ntp/get";
char *sPropertyTopic = "/property/get";
char *sFunctionTopic = "/function/get";
char *sPropertyOnline = "/property-online/get";
char *sFunctionOnline = "/function-online/get";
char *sMonitorTopic = "/monitor/get";
// Mqtt发布的主题
char *pInfoTopic = "/info/post";
char *pNtpTopic = "/ntp/post";
char *pPropertyTopic = "/property/post";
char *pFunctionTopic = "/function/post";
char *pMonitorTopic = "/monitor/post";
char *pEventTopic = "/event/post";
static const char *TAG = "FASTBEE_MQTT";
// 订阅系统主题
void subscribeTopic(esp_mqtt_client_handle_t client) {
char topic[128] = {0};
int msg_id;
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, sInfoTopic);
msg_id = esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
memset(topic, 0, sizeof(topic));
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, sOtaTopic);
msg_id = esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
memset(topic, 0, sizeof(topic));
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, sNtpTopic);
msg_id = esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
memset(topic, 0, sizeof(topic));
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, sPropertyTopic);
msg_id = esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
memset(topic, 0, sizeof(topic));
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, sFunctionTopic);
msg_id = esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
memset(topic, 0, sizeof(topic));
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, sPropertyOnline);
msg_id = esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
memset(topic, 0, sizeof(topic));
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, sFunctionOnline);
msg_id = esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
memset(topic, 0, sizeof(topic));
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, sMonitorTopic);
msg_id = esp_mqtt_client_subscribe(client, topic, 0);
ESP_LOGI(TAG, "sent subscribe successful, msg_id=%d", msg_id);
}
// 1.发布设备信息
void publishInfo(esp_mqtt_client_handle_t client) {
char topic[128] = {0};
int msg_id;
cJSON *json = cJSON_CreateObject();
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, pInfoTopic);
// 向JSON对象中添加数据
cJSON_AddNumberToObject(json, "rssi", 3);
cJSON_AddNumberToObject(json, "firmwareVersion", firmwareVersion);
cJSON_AddNumberToObject(json, "status", 3);
cJSON_AddStringToObject(json, "userId", g_userId);
cJSON_AddNumberToObject(json, "longitude", longitude);
cJSON_AddNumberToObject(json, "latitude", latitude);
cJSON *summary = cJSON_CreateObject();
cJSON_AddStringToObject(summary, "name", "fastbee");
cJSON_AddStringToObject(summary, "chip", "esp32");
cJSON_AddStringToObject(summary, "author", "zhuangpeng.li");
cJSON_AddNumberToObject(summary, "version", 1.6);
cJSON_AddStringToObject(summary, "create", "2024-07-07");
cJSON_AddItemToObject(json, "summary", summary);
char *json_string = cJSON_Print(json);
msg_id = esp_mqtt_client_publish(client, topic, json_string, 0, 0, 0);
ESP_LOGI(TAG, "sent publish %s, msg_id= %d, msg: %s", topic, msg_id, json_string);
cJSON_Delete(json);
free(json_string);
}
// 2.发布时钟同步信,用于获取当前时间(可选)
void publishNtp(esp_mqtt_client_handle_t client) {
char topic[128] = {0};
int msg_id;
cJSON *json = cJSON_CreateObject();
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, pNtpTopic);
cJSON_AddStringToObject(json, "deviceSendTime", "test");
char *json_string = cJSON_Print(json);
msg_id = esp_mqtt_client_publish(client, topic, json_string, 0, 0, 0);
ESP_LOGI(TAG, "sent publish %s, msg_id= %d, msg: %s", topic, msg_id, json_string);
cJSON_Delete(json);
free(json_string);
}
// 3.发布属性
void publishProperty(esp_mqtt_client_handle_t client, const char *msg) {
char topic[128] = {0};
int msg_id;
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, pPropertyTopic);
msg_id = esp_mqtt_client_publish(client, topic, msg, 0, 0, 0);
ESP_LOGI(TAG, "发布属性主题: %s", topic);
ESP_LOGI(TAG, "消息id= %d, 消息: %s", msg_id, msg);
}
// 4.发布功能
void publishFunction(esp_mqtt_client_handle_t client, const char *msg) {
char topic[128] = {0};
int msg_id;
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, pFunctionTopic);
msg_id = esp_mqtt_client_publish(client, topic, msg, 0, 0, 0);
ESP_LOGI(TAG, "发布功能主题: %s", topic);
ESP_LOGI(TAG, "消息id= %d, 消息: %s", msg_id, msg);
}
// 5.发布事件
void publishEvent(esp_mqtt_client_handle_t client, const char *msg) {
char topic[128] = {0};
int msg_id;
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, pEventTopic);
msg_id = esp_mqtt_client_publish(client, topic, msg, 0, 0, 0);
ESP_LOGI(TAG, "发布事件主题: %s", topic);
ESP_LOGI(TAG, "消息id= %d, 消息: %s", msg_id, msg);
}
// 6.发布实时监测数据
void publishMonitor(esp_mqtt_client_handle_t client, const char *msg) {
char topic[128] = {0};
int msg_id;
HAL_Snprintf(topic, sizeof(topic), "%s%s", g_mqtt_prefix, pMonitorTopic);
// 发布实时监测数据(不会存储,需要实时存储则发布为属性)
msg_id = esp_mqtt_client_publish(client, topic, msg, 0, 0, 0);
ESP_LOGI(TAG, "发布实时监测主题: %s", topic);
ESP_LOGI(TAG, "消息id= %d, 消息: %s", msg_id, msg);
}

View File

@@ -1,2 +0,0 @@
COMPONENT_ADD_INCLUDEDIRS := ./include
COMPONENT_SRCDIRS := ./

View File

@@ -1,8 +0,0 @@
fastbee-key,namespace,
MQTTUrl,data,string
MQTTUsername,data,string
MQTTPassword,data,string
MQTTClientId,data,string
UserId,data,string
DeviceSN,data,string
ProductId,data,string
1 fastbee-key namespace
2 MQTTUrl data string
3 MQTTUsername data string
4 MQTTPassword data string
5 MQTTClientId data string
6 UserId data string
7 DeviceSN data string
8 ProductId data string

View File

@@ -1,4 +0,0 @@
id,MQTTUrl,MQTTUsername,MQTTPassword,MQTTClientId,UserId,DeviceSN,ProductId
1,fastbee.cn:1883,FastBee,P63653937TRQ8F27,S&DCDA0C11FEBC&588&1,1,DCDA0C11FEBC,588
2,fastbee.cn:1883,FastBee,P63653937TRQ8F27,S&DCDA0C11FEBC&588&1,1,DCDA0C11FEBC,588
3,fastbee.cn:1883,FastBee,P63653937TRQ8F27,S&DCDA0C11FEBC&588&1,1,DCDA0C11FEBC,588
1 id MQTTUrl MQTTUsername MQTTPassword MQTTClientId UserId DeviceSN ProductId
2 1 fastbee.cn:1883 FastBee P63653937TRQ8F27 S&DCDA0C11FEBC&588&1 1 DCDA0C11FEBC 588
3 2 fastbee.cn:1883 FastBee P63653937TRQ8F27 S&DCDA0C11FEBC&588&1 1 DCDA0C11FEBC 588
4 3 fastbee.cn:1883 FastBee P63653937TRQ8F27 S&DCDA0C11FEBC&588&1 1 DCDA0C11FEBC 588

View File

@@ -1,9 +0,0 @@
key,type,encoding,value
fastbee-key,namespace,,
MQTTUrl,data,string,fastbee.cn:1883
MQTTUsername,data,string,FastBee
MQTTPassword,data,string,P63653937TRQ8F27
MQTTClientId,data,string,S&DCDA0C11FEBC&588&1
UserId,data,string,1
DeviceSN,data,string,DCDA0C11FEBC
ProductId,data,string,588
1 key type encoding value
2 fastbee-key namespace
3 MQTTUrl data string fastbee.cn:1883
4 MQTTUsername data string FastBee
5 MQTTPassword data string P63653937TRQ8F27
6 MQTTClientId data string S&DCDA0C11FEBC&588&1
7 UserId data string 1
8 DeviceSN data string DCDA0C11FEBC
9 ProductId data string 588

View File

@@ -1,98 +0,0 @@
/*********************************************************************
* function 设备交互
* board: esp8266,esp32,esp32s2,esp32s3 core for esp-idf v4.4.7
* source: https://gitee.com/zhuangpengli/IOTDeviceSDK
* copyright: zhuangpeng.li all rights reserved.
********************************************************************/
#include "fastbee.h"
#include "config.h"
#include "mqtt.h"
#include "esp_log.h"
#include "dm_wrapper.h"
static const char *TAG = "FASTBEE";
static void log_error_if_nonzero(const char *message, int error_code)
{
if (error_code != 0) {
ESP_LOGE(TAG, "Last error %s: 0x%x", message, error_code);
}
}
/*
* @brief Event handler registered to receive MQTT events
*
* This function is called by the MQTT client event loop.
*
* @param handler_args user data registered to the event.
* @param base Event base for the handler(always MQTT Base in this example).
* @param event_id The id for the received event.
* @param event_data The data for the event, esp_mqtt_event_handle_t.
*/
static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data)
{
ESP_LOGD(TAG, "Event dispatched from event loop base=%s, event_id=%d", base, event_id);
esp_mqtt_event_handle_t event = event_data;
esp_mqtt_client_handle_t client = event->client;
int msg_id;
switch ((esp_mqtt_event_id_t)event_id) {
case MQTT_EVENT_CONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
subscribeTopic(client);
publishInfo(client);
break;
case MQTT_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED");
break;
case MQTT_EVENT_SUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED, msg_id=%d", event->msg_id);
msg_id = esp_mqtt_client_publish(client, "/topic/qos0", "data", 0, 0, 0);
ESP_LOGI(TAG, "sent publish successful, msg_id=%d", msg_id);
break;
case MQTT_EVENT_UNSUBSCRIBED:
ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_PUBLISHED:
ESP_LOGI(TAG, "MQTT_EVENT_PUBLISHED, msg_id=%d", event->msg_id);
break;
case MQTT_EVENT_DATA:
ESP_LOGI(TAG, "MQTT_EVENT_DATA");
printf("TOPIC=%.*s\r\n", event->topic_len, event->topic);
printf("DATA=%.*s\r\n", event->data_len, event->data);
break;
case MQTT_EVENT_ERROR:
ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
if (event->error_handle->error_type == MQTT_ERROR_TYPE_TCP_TRANSPORT) {
log_error_if_nonzero("reported from esp-tls", event->error_handle->esp_tls_last_esp_err);
log_error_if_nonzero("reported from tls stack", event->error_handle->esp_tls_stack_err);
log_error_if_nonzero("captured as transport's socket errno", event->error_handle->esp_transport_sock_errno);
ESP_LOGI(TAG, "Last errno string (%s)", strerror(event->error_handle->esp_transport_sock_errno));
}
break;
default:
ESP_LOGI(TAG, "Other event id:%d", event->event_id);
break;
}
}
static int fastbee_thread(void *paras){
loadConfig();
esp_mqtt_client_config_t mqtt_cfg = {
.uri = g_mqtt_url,
.client_id = g_mqtt_clientid,
.username = g_mqtt_username,
.password = g_mqtt_password,
};
esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
/* The last argument may be used to pass data to the event handler, in this example mqtt_event_handler */
esp_mqtt_client_register_event(client, ESP_EVENT_ANY_ID, mqtt_event_handler, NULL);
esp_mqtt_client_start(client);
return 0;
}
void fastbee_main(void *paras)
{
while (1) {
fastbee_thread(NULL);
}
}

View File

@@ -1,58 +0,0 @@
/*********************************************************************
* function 设备交互
* board: esp8266,esp32,esp32s2,esp32s3 core for esp-idf v4.4.7
* source: https://gitee.com/zhuangpengli/IOTDeviceSDK
* copyright: zhuangpeng.li all rights reserved.
********************************************************************/
#ifndef _CONFIG_H
#define _CONFIG_H
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#define MQTT_URL_LEN (64)
#define MQTT_USERNAME_LEN (64)
#define MQTT_PASSWORD_LEN (64)
#define MQTT_CLIENTID_LEN (64)
#define USER_ID_LEN (16)
#define DEVICE_SN_LEN (64)
#define PRODUCT_ID_LEN (16)
#define MQTT_PREFIX_LEN (128)
#define MFG_FASTBEE_PARTITION_NAME "fctry"
#define NVS_FASTBEE_PRODUCT "fastbee-key"
extern char g_userId[USER_ID_LEN + 1]; // 用户ID
extern char g_productId[PRODUCT_ID_LEN + 1]; // 产品ID
extern char g_deviceSN[DEVICE_SN_LEN + 1]; // 设备编号重要同时是Mqtt的clientId
// Mqtt配置
extern char g_mqtt_url[MQTT_URL_LEN + 1];
extern char g_mqtt_username[MQTT_USERNAME_LEN + 1];
extern char g_mqtt_password[MQTT_PASSWORD_LEN + 1];
extern char g_mqtt_clientid[MQTT_CLIENTID_LEN + 1];
extern char g_mqtt_prefix[MQTT_PREFIX_LEN + 1];
extern float rssi; // 信号强度信号极好4格[-55— 0]信号好3格[-70— -55]信号一般2格[-85— -70]信号差1格[-100— -85]
extern char wumei_iv[17]; // AES加密偏移量固定值16位
extern int monitorCount; // 发布监测数据的最大次数
extern long monitorInterval; // 发布监测数据的间隔默认1000毫秒
extern bool isApMode; // 是否进入AP配网模式
/********************************** begin 可配置的项 **********************************/
extern char *wifiSsid; // WIFI的SSID
extern char *wifiPwd; // WIFI的密码
extern char *authCode; // 产品授权码,产品未启用时为空字符串
extern float firmwareVersion; // 固件版本
extern float latitude; // 设备精度
extern float longitude; // 设备维度
extern char mqttSecret[17]; // Mqtt秘钥,16位
extern char *ntpServer; // NTP服务地址用于获取当前时间
/********************************** end 可配置的项 **********************************/
// 加载配置
void loadConfig();
#endif

View File

@@ -1,43 +0,0 @@
/*********************************************************************
* function 设备交互
* board: esp8266,esp32,esp32s2,esp32s3 core for esp-idf v4.4.7
* source: https://gitee.com/zhuangpengli/IOTDeviceSDK
* copyright: zhuangpeng.li all rights reserved.
********************************************************************/
#ifndef _FASTBEE_MQTT_H
#define _FASTBEE_MQTT_H
#include "mqtt_client.h"
// 订阅的主题
extern char *sInfoTopic; // 订阅设备信息
extern char *sOtaTopic; // 订阅OTA升级
extern char *sNtpTopic; // 订阅NTP时间
extern char *sPropertyTopic; // 订阅属性
extern char *sFunctionTopic; // 订阅功能
extern char *sPropertyOnline; // 订阅属性-在线模式
extern char *sFunctionOnline; // 订阅功能-在线模式
extern char *sMonitorTopic; // 订阅实时监测
// 发布的主题
extern char *pInfoTopic; // 发布设备信息
extern char *pNtpTopic; // 发布NTP时间
extern char *pPropertyTopic; // 发布属性
extern char *pFunctionTopic; // 发布功能
extern char *pMonitorTopic; // 发布实时监测数据
extern char *pEventTopic; // 发布事件
// 订阅系统主题
void subscribeTopic(esp_mqtt_client_handle_t client);
// 发布设备信息
void publishInfo(esp_mqtt_client_handle_t client);
// 发布时钟同步信息
void publishNtp(esp_mqtt_client_handle_t client);
// 发布事件
void publishEvent(esp_mqtt_client_handle_t client, const char *msg);
// 发布实时监测数据(不会存储,需要实时存储则发布属性)
void publishMonitor(esp_mqtt_client_handle_t client, const char *msg);
// 发布属性
void publishProperty(esp_mqtt_client_handle_t client, const char *msg);
// 发布功能
void publishFunction(esp_mqtt_client_handle_t client, const char *msg);
#endif

View File

@@ -1,12 +0,0 @@
/*********************************************************************
* function 设备交互
* board: esp8266,esp32,esp32s2,esp32s3 core for esp-idf v4.4.7
* source: https://gitee.com/zhuangpengli/IOTDeviceSDK
* copyright: zhuangpeng.li all rights reserved.
********************************************************************/
#ifndef _FASTBEE_H
#define _FASTBEE_H
void fastbee_main(void *paras);
#endif

View File

@@ -1,7 +0,0 @@
set(COMPONENT_ADD_INCLUDEDIRS .)
set(COMPONENT_SRCS "lightbulb.c")
set(COMPONENT_REQUIRES)
register_component()

View File

@@ -1,2 +0,0 @@
COMPONENT_ADD_INCLUDEDIRS := ./
COMPONENT_SRCDIRS := ./

View File

@@ -1,327 +0,0 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
* it is 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.
*
*/
#include <stdio.h>
#include <sdkconfig.h>
#ifdef CONFIG_IDF_TARGET_ESP8266
#include "driver/pwm.h"
#else
#include "driver/ledc.h"
#endif
#include "esp_log.h"
typedef struct rgb {
uint8_t r; // 0-100 %
uint8_t g; // 0-100 %
uint8_t b; // 0-100 %
} rgb_t;
typedef struct hsp {
uint16_t h; // 0-360
uint16_t s; // 0-100
uint16_t b; // 0-100
} hsp_t;
/* LED numbers below are for ESP-WROVER-KIT */
/* Red LED */
#define LEDC_IO_0 (0)
/* Green LED */
#define LEDC_IO_1 (2)
/* Blued LED */
#define LEDC_IO_2 (4)
#define PWM_DEPTH (1023)
#define PWM_TARGET_DUTY 8192
static hsp_t s_hsb_val;
static uint16_t s_brightness;
static bool s_on = false;
static const char *TAG = "lightbulb";
#ifdef CONFIG_IDF_TARGET_ESP8266
#define PWM_PERIOD (500)
#define PWM_IO_NUM 3
// pwm pin number
const uint32_t pin_num[PWM_IO_NUM] = {
LEDC_IO_0,
LEDC_IO_1,
LEDC_IO_2
};
// dutys table, (duty/PERIOD)*depth
uint32_t duties[PWM_IO_NUM] = {
250, 250, 250,
};
// phase table, (phase/180)*depth
int16_t phase[PWM_IO_NUM] = {
0, 0, 50,
};
#define LEDC_CHANNEL_0 0
#define LEDC_CHANNEL_1 1
#define LEDC_CHANNEL_2 2
#endif
/**
* @brief transform lightbulb's "RGB" and other parameter
*/
static void lightbulb_set_aim(uint32_t r, uint32_t g, uint32_t b, uint32_t cw, uint32_t ww, uint32_t period)
{
#ifdef CONFIG_IDF_TARGET_ESP8266
pwm_stop(0x3);
pwm_set_duty(LEDC_CHANNEL_0, r);
pwm_set_duty(LEDC_CHANNEL_1, g);
pwm_set_duty(LEDC_CHANNEL_2, b);
pwm_start();
#else
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, r);
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, g);
ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_2, b);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1);
ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_2);
#endif
}
/**
* @brief transform lightbulb's "HSV" to "RGB"
*/
static bool lightbulb_set_hsb2rgb(uint16_t h, uint16_t s, uint16_t v, rgb_t *rgb)
{
bool res = true;
uint16_t hi, F, P, Q, T;
if (!rgb)
return false;
if (h > 360) return false;
if (s > 100) return false;
if (v > 100) return false;
hi = (h / 60) % 6;
F = 100 * h / 60 - 100 * hi;
P = v * (100 - s) / 100;
Q = v * (10000 - F * s) / 10000;
T = v * (10000 - s * (100 - F)) / 10000;
switch (hi) {
case 0:
rgb->r = v;
rgb->g = T;
rgb->b = P;
break;
case 1:
rgb->r = Q;
rgb->g = v;
rgb->b = P;
break;
case 2:
rgb->r = P;
rgb->g = v;
rgb->b = T;
break;
case 3:
rgb->r = P;
rgb->g = Q;
rgb->b = v;
break;
case 4:
rgb->r = T;
rgb->g = P;
rgb->b = v;
break;
case 5:
rgb->r = v;
rgb->g = P;
rgb->b = Q;
break;
default:
return false;
}
return res;
}
/**
* @brief set the lightbulb's "HSV"
*/
static bool lightbulb_set_aim_hsv(uint16_t h, uint16_t s, uint16_t v)
{
rgb_t rgb_tmp;
bool ret = lightbulb_set_hsb2rgb(h, s, v, &rgb_tmp);
if (ret == false)
return false;
lightbulb_set_aim(rgb_tmp.r * PWM_TARGET_DUTY / 100, rgb_tmp.g * PWM_TARGET_DUTY / 100,
rgb_tmp.b * PWM_TARGET_DUTY / 100, (100 - s) * 5000 / 100, v * 2000 / 100, 1000);
return true;
}
/**
* @brief update the lightbulb's state
*/
static void lightbulb_update()
{
lightbulb_set_aim_hsv(s_hsb_val.h, s_hsb_val.s, s_hsb_val.b);
}
/**
* @brief initialize the lightbulb lowlevel module
*/
void lightbulb_init(void)
{
#ifdef CONFIG_IDF_TARGET_ESP8266
pwm_init(PWM_PERIOD, duties, PWM_IO_NUM, pin_num);
pwm_set_channel_invert(0x1 << 0);
pwm_set_phases(phase);
pwm_start();
#else
// enable ledc module
periph_module_enable(PERIPH_LEDC_MODULE);
// config the timer
ledc_timer_config_t ledc_timer = {
//set timer counter bit number
.bit_num = LEDC_TIMER_13_BIT,
//set frequency of pwm
.freq_hz = 5000,
//timer mode,
.speed_mode = LEDC_LOW_SPEED_MODE,
//timer index
.timer_num = LEDC_TIMER_0
};
ledc_timer_config(&ledc_timer);
//config the channel
ledc_channel_config_t ledc_channel = {
//set LEDC channel 0
.channel = LEDC_CHANNEL_0,
//set the duty for initialization.(duty range is 0 ~ ((2**bit_num)-1)
.duty = 100,
//GPIO number
.gpio_num = LEDC_IO_0,
//GPIO INTR TYPE, as an example, we enable fade_end interrupt here.
.intr_type = LEDC_INTR_FADE_END,
//set LEDC mode, from ledc_mode_t
.speed_mode = LEDC_LOW_SPEED_MODE,
//set LEDC timer source, if different channel use one timer,
//the frequency and bit_num of these channels should be the same
.timer_sel = LEDC_TIMER_0
};
//set the configuration
ledc_channel_config(&ledc_channel);
//config ledc channel1
ledc_channel.channel = LEDC_CHANNEL_1;
ledc_channel.gpio_num = LEDC_IO_1;
ledc_channel_config(&ledc_channel);
//config ledc channel2
ledc_channel.channel = LEDC_CHANNEL_2;
ledc_channel.gpio_num = LEDC_IO_2;
ledc_channel_config(&ledc_channel);
#endif
}
/**
* @brief deinitialize the lightbulb's lowlevel module
*/
void lightbulb_deinit(void)
{
#ifdef CONFIG_IDF_TARGET_ESP8266
#else
ledc_stop(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_0, 0);
ledc_stop(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_1, 0);
ledc_stop(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL_2, 0);
#endif
}
/**
* @brief turn on/off the lowlevel lightbulb
*/
int lightbulb_set_on(bool value)
{
ESP_LOGI(TAG, "lightbulb_set_on : %s", value == true ? "true" : "false");
if (value == true) {
s_hsb_val.b = s_brightness;
s_on = true;
} else {
s_brightness = s_hsb_val.b;
s_hsb_val.b = 0;
s_on = false;
}
lightbulb_update();
return 0;
}
/**
* @brief set the saturation of the lowlevel lightbulb
*/
int lightbulb_set_saturation(float value)
{
ESP_LOGI(TAG, "lightbulb_set_saturation : %f", value);
s_hsb_val.s = value;
if (true == s_on)
lightbulb_update();
return 0;
}
/**
* @brief set the hue of the lowlevel lightbulb
*/
int lightbulb_set_hue(float value)
{
ESP_LOGI(TAG, "lightbulb_set_hue : %f", value);
s_hsb_val.h = value;
if (true == s_on)
lightbulb_update();
return 0;
}
/**
* @brief set the brightness of the lowlevel lightbulb
*/
int lightbulb_set_brightness(int value)
{
ESP_LOGI(TAG, "lightbulb_set_brightness : %d", value);
s_hsb_val.b = value;
s_brightness = s_hsb_val.b;
if (true == s_on)
lightbulb_update();
return 0;
}

View File

@@ -1,89 +0,0 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
* it is 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.
*
*/
#ifndef _LIGHTBULB_H_
#define _LIGHTBULB_H_
#include <stdbool.h>
/**
* @brief initialize the lightbulb lowlevel module
*
* @param none
*
* @return none
*/
void lightbulb_init(void);
/**
* @brief deinitialize the lightbulb's lowlevel module
*
* @param none
*
* @return none
*/
void lightbulb_deinit(void);
/**
* @brief turn on/off the lowlevel lightbulb
*
* @param value The "On" value
*
* @return none
*/
int lightbulb_set_on(bool value);
/**
* @brief set the saturation of the lowlevel lightbulb
*
* @param value The Saturation value
*
* @return
* - 0 : OK
* - others : fail
*/
int lightbulb_set_saturation(float value);
/**
* @brief set the hue of the lowlevel lightbulb
*
* @param value The Hue value
*
* @return
* - 0 : OK
* - others : fail
*/
int lightbulb_set_hue(float value);
/**
* @brief set the brightness of the lowlevel lightbulb
*
* @param value The Brightness value
*
* @return
* - 0 : OK
* - others : fail
*/
int lightbulb_set_brightness(int value);
#endif /* _LIGHTBULB_H_ */

View File

@@ -1,7 +0,0 @@
set(COMPONENT_ADD_INCLUDEDIRS .)
set(COMPONENT_SRCS "linkkit_solo.c")
set(COMPONENT_REQUIRES "esp-aliyun" "json" "lightbulb")
register_component()

View File

@@ -1,2 +0,0 @@
COMPONENT_ADD_INCLUDEDIRS := ./
COMPONENT_SRCDIRS := ./

View File

@@ -1,468 +0,0 @@
/*
* Copyright (C) 2015-2018 Alibaba Group Holding Limited
*/
#include "infra_config.h"
void HAL_Printf(const char *fmt, ...);
int HAL_Snprintf(char *str, const int len, const char *fmt, ...);
#ifdef DEPRECATED_LINKKIT
#include "solo.c"
#else
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "infra_types.h"
#include "infra_defs.h"
#include "infra_compat.h"
#include "infra_compat.h"
#ifdef INFRA_MEM_STATS
#include "infra_mem_stats.h"
#endif
#include "dev_model_api.h"
#include "dm_wrapper.h"
#include "cJSON.h"
#ifdef ATM_ENABLED
#include "at_api.h"
#endif
#include "lightbulb.h"
#include "esp_log.h"
static const char* TAG = "linkkit_example_solo";
#define EXAMPLE_TRACE(...) \
do { \
HAL_Printf("\033[1;32;40m%s.%d: ", __func__, __LINE__); \
HAL_Printf(__VA_ARGS__); \
HAL_Printf("\033[0m\r\n"); \
} while (0)
#define EXAMPLE_MASTER_DEVID (0)
#define EXAMPLE_YIELD_TIMEOUT_MS (200)
typedef struct {
int master_devid;
int cloud_connected;
int master_initialized;
} user_example_ctx_t;
/**
* These PRODUCT_KEY|PRODUCT_SECRET|DEVICE_NAME|DEVICE_SECRET are listed for demo only
*
* When you created your own devices on iot.console.com, you SHOULD replace them with what you got from console
*
*/
char PRODUCT_KEY[IOTX_PRODUCT_KEY_LEN + 1] = {0};
char PRODUCT_SECRET[IOTX_PRODUCT_SECRET_LEN + 1] = {0};
char DEVICE_NAME[IOTX_DEVICE_NAME_LEN + 1] = {0};
char DEVICE_SECRET[IOTX_DEVICE_SECRET_LEN + 1] = {0};
static user_example_ctx_t g_user_example_ctx;
/** Awss Status event callback */
static int user_awss_status_event_handler(int status)
{
EXAMPLE_TRACE("Awss Status %d", status);
return SUCCESS_RETURN;
}
/** cloud connected event callback */
static int user_connected_event_handler(void)
{
EXAMPLE_TRACE("Cloud Connected");
g_user_example_ctx.cloud_connected = 1;
return 0;
}
/** cloud connect fail event callback */
static int user_connect_fail_event_handler(void)
{
EXAMPLE_TRACE("Cloud Connect Fail");
return SUCCESS_RETURN;
}
/** cloud disconnected event callback */
static int user_disconnected_event_handler(void)
{
EXAMPLE_TRACE("Cloud Disconnected");
g_user_example_ctx.cloud_connected = 0;
return 0;
}
/** cloud raw_data arrived event callback */
static int user_rawdata_arrived_event_handler(const int devid, const unsigned char *request, const int request_len)
{
EXAMPLE_TRACE("Cloud Rawdata Arrived");
return 0;
}
/* device initialized event callback */
static int user_initialized(const int devid)
{
EXAMPLE_TRACE("Device Initialized");
g_user_example_ctx.master_initialized = 1;
return 0;
}
/** recv property post response message from cloud **/
static int user_report_reply_event_handler(const int devid, const int msgid, const int code, const char *reply,
const int reply_len)
{
EXAMPLE_TRACE("Message Post Reply Received, Message ID: %d, Code: %d, Reply: %.*s", msgid, code,
reply_len,
(reply == NULL)? ("NULL") : (reply));
return 0;
}
/** recv event post response message from cloud **/
static int user_trigger_event_reply_event_handler(const int devid, const int msgid, const int code, const char *eventid,
const int eventid_len, const char *message, const int message_len)
{
EXAMPLE_TRACE("Trigger Event Reply Received, Message ID: %d, Code: %d, EventID: %.*s, Message: %.*s",
msgid, code,
eventid_len,
eventid, message_len, message);
return 0;
}
static int user_property_set_event_handler(const int devid, const char *request, const int request_len)
{
int res = 0;
cJSON *root = NULL, *LightSwitch = NULL, *LightColor = NULL;
ESP_LOGI(TAG,"Property Set Received, Devid: %d, Request: %s", devid, request);
lightbulb_set_brightness(78);
lightbulb_set_saturation(100);
if (!request) {
return NULL_VALUE_ERROR;
}
/* Parse Root */
root = cJSON_Parse(request);
if (!root) {
ESP_LOGI(TAG,"JSON Parse Error");
return FAIL_RETURN;
}
/** Switch Lightbulb On/Off */
LightSwitch = cJSON_GetObjectItem(root, "LightSwitch");
if (LightSwitch) {
lightbulb_set_on(LightSwitch->valueint);
}
/** Switch Lightbulb Hue */
LightSwitch = cJSON_GetObjectItem(root, "RGBColor");
if (LightSwitch) {
LightColor = cJSON_GetObjectItem(LightSwitch, "Red");
lightbulb_set_hue(LightColor ? LightColor->valueint : 0);
LightColor = cJSON_GetObjectItem(LightSwitch, "Green");
lightbulb_set_hue(LightColor ? LightColor->valueint : 120);
LightColor = cJSON_GetObjectItem(LightSwitch, "Blue");
lightbulb_set_hue(LightColor ? LightColor->valueint : 240);
}
cJSON_Delete(root);
res = IOT_Linkkit_Report(EXAMPLE_MASTER_DEVID, ITM_MSG_POST_PROPERTY,
(unsigned char *)request, request_len);
ESP_LOGI(TAG,"Post Property Message ID: %d", res);
return SUCCESS_RETURN;
}
static int user_property_desired_get_reply_event_handler(const char *serviceid, const int serviceid_len)
{
ESP_LOGI(TAG, "ITE_PROPERTY_DESIRED_GET_REPLY");
return SUCCESS_RETURN;
}
static int user_property_get_event_handler(const int devid, const char *serviceid, const int serviceid_len, char **response, int *response_len)
{
ESP_LOGI(TAG,"Get Property Message ID: %d", devid);
return SUCCESS_RETURN;
}
static int user_service_request_event_handler(const int devid, const char *serviceid, const int serviceid_len,
const char *request, const int request_len,
char **response, int *response_len)
{
int contrastratio = 0, to_cloud = 0;
cJSON *root = NULL, *item_transparency = NULL, *item_from_cloud = NULL;
ESP_LOGI(TAG,"Service Request Received, Devid: %d, Service ID: %.*s, Payload: %s", devid, serviceid_len,
serviceid,
request);
/* Parse Root */
root = cJSON_Parse(request);
if (root == NULL || !cJSON_IsObject(root)) {
ESP_LOGI(TAG,"JSON Parse Error");
return -1;
}
if (strlen("Custom") == serviceid_len && memcmp("Custom", serviceid, serviceid_len) == 0) {
/* Parse Item */
const char *response_fmt = "{\"Contrastratio\":%d}";
item_transparency = cJSON_GetObjectItem(root, "transparency");
if (item_transparency == NULL || !cJSON_IsNumber(item_transparency)) {
cJSON_Delete(root);
return -1;
}
ESP_LOGI(TAG,"transparency: %d", item_transparency->valueint);
contrastratio = item_transparency->valueint + 1;
/* Send Service Response To Cloud */
*response_len = strlen(response_fmt) + 10 + 1;
*response = malloc(*response_len);
if (*response == NULL) {
ESP_LOGW(TAG,"Memory Not Enough");
return -1;
}
memset(*response, 0, *response_len);
snprintf(*response, *response_len, response_fmt, contrastratio);
*response_len = strlen(*response);
} else if (strlen("SyncService") == serviceid_len && memcmp("SyncService", serviceid, serviceid_len) == 0) {
/* Parse Item */
const char *response_fmt = "{\"ToCloud\":%d}";
item_from_cloud = cJSON_GetObjectItem(root, "FromCloud");
if (item_from_cloud == NULL || !cJSON_IsNumber(item_from_cloud)) {
cJSON_Delete(root);
return -1;
}
ESP_LOGI(TAG,"FromCloud: %d", item_from_cloud->valueint);
to_cloud = item_from_cloud->valueint + 1;
/* Send Service Response To Cloud */
*response_len = strlen(response_fmt) + 10 + 1;
*response = malloc(*response_len);
if (*response == NULL) {
ESP_LOGW(TAG,"Memory Not Enough");
return -1;
}
memset(*response, 0, *response_len);
snprintf(*response, *response_len, response_fmt, to_cloud);
*response_len = strlen(*response);
}
cJSON_Delete(root);
return 0;
}
static int user_timestamp_reply_event_handler(const char *timestamp)
{
EXAMPLE_TRACE("Current Timestamp: %s", timestamp);
return SUCCESS_RETURN;
}
static int user_toplist_reply_event_handler(const int devid, const int msgid, const int code, const char *eventid, const int eventid_len)
{
EXAMPLE_TRACE("ITE_TOPOLIST_REPLY");
return SUCCESS_RETURN;
}
static int user_permit_join_event_handler(const int devid, const int msgid, const int code, const char *eventid, const int eventid_len)
{
EXAMPLE_TRACE("ITE_PERMIT_JOIN");
return SUCCESS_RETURN;
}
/** fota event handler **/
static int user_fota_event_handler(int type, const char *version)
{
char buffer[1024] = {0};
int buffer_length = 1024;
/* 0 - new firmware exist, query the new firmware */
if (type == 0) {
EXAMPLE_TRACE("New Firmware Version: %s", version);
IOT_Linkkit_Query(EXAMPLE_MASTER_DEVID, ITM_MSG_QUERY_FOTA_DATA, (unsigned char *)buffer, buffer_length);
}
return 0;
}
/* cota event handler */
static int user_cota_event_handler(int type, const char *config_id, int config_size, const char *get_type,
const char *sign, const char *sign_method, const char *url)
{
char buffer[128] = {0};
int buffer_length = 128;
/* type = 0, new config exist, query the new config */
if (type == 0) {
EXAMPLE_TRACE("New Config ID: %s", config_id);
EXAMPLE_TRACE("New Config Size: %d", config_size);
EXAMPLE_TRACE("New Config Type: %s", get_type);
EXAMPLE_TRACE("New Config Sign: %s", sign);
EXAMPLE_TRACE("New Config Sign Method: %s", sign_method);
EXAMPLE_TRACE("New Config URL: %s", url);
IOT_Linkkit_Query(EXAMPLE_MASTER_DEVID, ITM_MSG_QUERY_COTA_DATA, (unsigned char *)buffer, buffer_length);
}
return 0;
}
static int user_mqtt_connect_succ_event_handler(void)
{
EXAMPLE_TRACE("ITE_MQTT_CONNECT_SUCC");
return SUCCESS_RETURN;
}
static void user_post_property(void)
{
static int cnt = 0;
int res = 0;
char property_payload[30] = {0};
HAL_Snprintf(property_payload, sizeof(property_payload), "{\"Counter\": %d}", cnt++);
res = IOT_Linkkit_Report(EXAMPLE_MASTER_DEVID, ITM_MSG_POST_PROPERTY,
(unsigned char *)property_payload, strlen(property_payload));
EXAMPLE_TRACE("Post Property Message ID: %d", res);
}
static void user_post_event(void)
{
int res = 0;
char *event_id = "HardwareError";
char *event_payload = "{\"ErrorCode\": 0}";
res = IOT_Linkkit_TriggerEvent(EXAMPLE_MASTER_DEVID, event_id, strlen(event_id),
event_payload, strlen(event_payload));
EXAMPLE_TRACE("Post Event Message ID: %d", res);
}
static void user_deviceinfo_update(void)
{
int res = 0;
char *device_info_update = "[{\"attrKey\":\"abc\",\"attrValue\":\"hello,world\"}]";
res = IOT_Linkkit_Report(EXAMPLE_MASTER_DEVID, ITM_MSG_DEVICEINFO_UPDATE,
(unsigned char *)device_info_update, strlen(device_info_update));
EXAMPLE_TRACE("Device Info Update Message ID: %d", res);
}
static void user_deviceinfo_delete(void)
{
int res = 0;
char *device_info_delete = "[{\"attrKey\":\"abc\"}]";
res = IOT_Linkkit_Report(EXAMPLE_MASTER_DEVID, ITM_MSG_DEVICEINFO_DELETE,
(unsigned char *)device_info_delete, strlen(device_info_delete));
EXAMPLE_TRACE("Device Info Delete Message ID: %d", res);
}
static int linkkit_thread(void *paras)
{
int res = 0;
iotx_linkkit_dev_meta_info_t master_meta_info;
int domain_type = 0, dynamic_register = 0, post_reply_need = 0;
#ifdef ATM_ENABLED
if (IOT_ATM_Init() < 0) {
EXAMPLE_TRACE("IOT ATM init failed!\n");
return -1;
}
#endif
memset(&g_user_example_ctx, 0, sizeof(user_example_ctx_t));
HAL_GetProductKey(PRODUCT_KEY);
HAL_GetProductSecret(PRODUCT_SECRET);
HAL_GetDeviceName(DEVICE_NAME);
HAL_GetDeviceSecret(DEVICE_SECRET);
memset(&master_meta_info, 0, sizeof(iotx_linkkit_dev_meta_info_t));
memcpy(master_meta_info.product_key, PRODUCT_KEY, strlen(PRODUCT_KEY));
memcpy(master_meta_info.product_secret, PRODUCT_SECRET, strlen(PRODUCT_SECRET));
memcpy(master_meta_info.device_name, DEVICE_NAME, strlen(DEVICE_NAME));
memcpy(master_meta_info.device_secret, DEVICE_SECRET, strlen(DEVICE_SECRET));
/* Register Callback */
IOT_RegisterCallback(ITE_AWSS_STATUS, user_awss_status_event_handler);
IOT_RegisterCallback(ITE_CONNECT_SUCC, user_connected_event_handler);
IOT_RegisterCallback(ITE_CONNECT_FAIL, user_connect_fail_event_handler);
IOT_RegisterCallback(ITE_DISCONNECTED, user_disconnected_event_handler);
IOT_RegisterCallback(ITE_RAWDATA_ARRIVED, user_rawdata_arrived_event_handler);
IOT_RegisterCallback(ITE_SERVICE_REQUEST, user_service_request_event_handler);
IOT_RegisterCallback(ITE_PROPERTY_SET, user_property_set_event_handler);
IOT_RegisterCallback(ITE_PROPERTY_GET, user_property_get_event_handler);
IOT_RegisterCallback(ITE_PROPERTY_DESIRED_GET_REPLY, user_property_desired_get_reply_event_handler);
IOT_RegisterCallback(ITE_REPORT_REPLY, user_report_reply_event_handler);
IOT_RegisterCallback(ITE_TRIGGER_EVENT_REPLY, user_trigger_event_reply_event_handler);
IOT_RegisterCallback(ITE_TIMESTAMP_REPLY, user_timestamp_reply_event_handler);
IOT_RegisterCallback(ITE_TOPOLIST_REPLY, user_toplist_reply_event_handler);
IOT_RegisterCallback(ITE_PERMIT_JOIN, user_permit_join_event_handler);
IOT_RegisterCallback(ITE_INITIALIZE_COMPLETED, user_initialized);
IOT_RegisterCallback(ITE_FOTA, user_fota_event_handler);
IOT_RegisterCallback(ITE_COTA, user_cota_event_handler);
IOT_RegisterCallback(ITE_MQTT_CONNECT_SUCC, user_mqtt_connect_succ_event_handler);
#if CONFIG_MQTT_DIRECT
domain_type = IOTX_CLOUD_REGION_SHANGHAI;
#else
domain_type = IOTX_CLOUD_REGION_SINGAPORE;
#endif
IOT_Ioctl(IOTX_IOCTL_SET_DOMAIN, (void *)&domain_type);
/* Choose Login Method */
dynamic_register = 0;
IOT_Ioctl(IOTX_IOCTL_SET_DYNAMIC_REGISTER, (void *)&dynamic_register);
/* post reply doesn't need */
post_reply_need = 1;
IOT_Ioctl(IOTX_IOCTL_RECV_EVENT_REPLY, (void *)&post_reply_need);
/* Create Master Device Resources */
g_user_example_ctx.master_devid = IOT_Linkkit_Open(IOTX_LINKKIT_DEV_TYPE_MASTER, &master_meta_info);
if (g_user_example_ctx.master_devid < 0) {
EXAMPLE_TRACE("IOT_Linkkit_Open Failed\n");
return -1;
}
/* Start Connect Aliyun Server */
res = IOT_Linkkit_Connect(g_user_example_ctx.master_devid);
if (res < 0) {
EXAMPLE_TRACE("IOT_Linkkit_Connect Failed\n");
IOT_Linkkit_Close(g_user_example_ctx.master_devid);
return -1;
}
while (1) {
IOT_Linkkit_Yield(EXAMPLE_YIELD_TIMEOUT_MS);
}
IOT_Linkkit_Close(g_user_example_ctx.master_devid);
IOT_DumpMemoryStats(IOT_LOG_DEBUG);
IOT_SetLogLevel(IOT_LOG_NONE);
return 0;
}
void linkkit_main(void *paras)
{
while (1) {
linkkit_thread(NULL);
}
}
#endif

View File

@@ -1,29 +0,0 @@
/*
* ESPRESSIF MIT License
*
* Copyright (c) 2019 <ESPRESSIF SYSTEMS (SHANGHAI) PTE LTD>
*
* Permission is hereby granted for use on all ESPRESSIF SYSTEMS products, in which case,
* it is 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.
*
*/
#ifndef _LINKKIT_SOLO_H__
#define _LINKKIT_SOLO_H__
void linkkit_main(void *paras);
#endif

View File

@@ -1,66 +0,0 @@
set(COMPONENT_PRIV_INCLUDEDIRS
iotkit-embedded/coap_server/CoAPPacket
iotkit-embedded/coap_server/server
iotkit-embedded/dev_bind/impl
iotkit-embedded/dev_bind/impl/os
iotkit-embedded/dev_bind/impl/awss_reset
iotkit-embedded/wifi_provision/dev_ap
iotkit-embedded/wifi_provision/frameworks
iotkit-embedded/wifi_provision/frameworks/aplist
iotkit-embedded/wifi_provision/frameworks/ieee80211
iotkit-embedded/wifi_provision/frameworks/statics
iotkit-embedded/wifi_provision/frameworks/utils
iotkit-embedded/wifi_provision/p2p
iotkit-embedded/wifi_provision/phone_ap
iotkit-embedded/wifi_provision/router_ap
iotkit-embedded/wifi_provision/smartconfig
iotkit-embedded/wifi_provision/zero_config)
set(COMPONENT_ADD_INCLUDEDIRS
conn_mgr
iotkit-embedded/coap_server
iotkit-embedded/dev_bind
iotkit-embedded/dev_model
iotkit-embedded/dev_sign
iotkit-embedded/dynamic_register
iotkit-embedded/mqtt
iotkit-embedded/ota
iotkit-embedded/infra
iotkit-embedded/wifi_provision
iotkit-embedded
wrappers)
# Edit following two lines to set component requirements (see docs)
set(COMPONENT_REQUIRES "nvs_flash" "app_update" "esp-tls")
set(COMPONENT_PRIV_REQUIRES )
set(COMPONENT_SRCDIRS
conn_mgr
iotkit-embedded/certs
iotkit-embedded/dev_bind/impl
iotkit-embedded/dev_bind/impl/os
iotkit-embedded/dev_bind/impl/awss_reset
iotkit-embedded/dev_model
iotkit-embedded/dev_reset
iotkit-embedded/dev_sign
iotkit-embedded/dynamic_register
iotkit-embedded/infra
iotkit-embedded/mqtt
iotkit-embedded/ota
iotkit-embedded/wifi_provision/dev_ap
iotkit-embedded/wifi_provision/frameworks
iotkit-embedded/wifi_provision/frameworks/aplist
iotkit-embedded/wifi_provision/frameworks/ieee80211
iotkit-embedded/wifi_provision/frameworks/statics
iotkit-embedded/wifi_provision/frameworks/utils
iotkit-embedded/wifi_provision/p2p
iotkit-embedded/wifi_provision/phone_ap
iotkit-embedded/wifi_provision/router_ap
iotkit-embedded/wifi_provision/smartconfig
iotkit-embedded/wifi_provision/zero_config
iotkit-embedded/coap_server/CoAPPacket
iotkit-embedded/coap_server/server
wrappers)
register_component()
component_compile_options(-DAUTH_MODE_CERT)

View File

@@ -1,143 +0,0 @@
menu "iotkit embedded"
menu "Aliyun linkkit device version"
config LINKKIT_FIRMWARE_VERSION
string "Device version"
default "0.0.1"
help
aliyun linkkit device version
endmenu
menu "Aliyun linkkit awss config"
config AWSS_ENCRYPT_TYPE
int "Get the security level of the `smartconfig` service"
range 1 5
default 3
help
Get the security level of the `smartconfig` service
config AWSS_CONN_ENCRYPT_TYPE
int "Get Security level for wifi configuration with connection.Used for AP solution of router and App"
range 3 5
default 4
help
Get Security level for wifi configuration with connection.Used for AP solution of router and App
config AWSS_TIMEOUT_INTERVAL_MS
int "Get the timeout period of the distribution service (`AWSS`), in milliseconds"
range 0 1800000
default 180000
help
Get the timeout period of the distribution service (`AWSS`), in milliseconds
config AWSS_CHANNELSCAN_INTERVAL_MS
int "Get the length of time scanned on each channel (`channel`), in milliseconds"
range 0 1000
default 200
help
Get the length of time scanned on each channel (`channel`), in milliseconds
endmenu
menu "Aliyun linkkit network config"
config SUPPORT_TCP
bool "Enable tcp connection"
default n
help
Select this option to enable tcp connection
config TCP_ESTABLISH_TIMEOUT_MS
int "Device tcp connection timeout wait time milliseconds"
range 0 100000
default 10000
help
Device tcp connection timeout wait time milliseconds
config TLS_ESTABLISH_TIMEOUT_MS
int "Device tls connection timeout wait time milliseconds"
range 0 100000
default 10000
help
Device tls connection timeout wait time milliseconds
endmenu
menu "Aliyun linkkit device model config"
config DEVICE_MODEL_GATEWAY
bool "Enable device model gateway"
default n
help
Select this option to enable device model gateway
endmenu
menu "Aliyun linkkit local control"
config DEVICE_ALCS_ENABLE
bool "Enable device local control"
default n
help
Select this option to enable device local control
endmenu
menu "Aliyun linkkit security OTA"
config SUPPORT_SECURITY_OTA
bool "Enable Security OTA"
default n
help
Select this option to enable security ota
endmenu
menu "Aliyun linkkit dynamic register"
config DYNAMIC_REGISTER
bool "Enable dynamic register"
default y
help
Select this option to enable dynamic register
endmenu
menu "Aliyun linkkit mqtt config"
config MQTT_DIRECT
bool "MQTT DIRECT"
default y
help
Directly connect MQTT server without perform HTTP authenticate to another HTTP server ahead
Switching to "y" leads to connect MQTT server directly and MQTT_DIRECT included into CFLAGS
Switching to "n" leads to legacy authenticate mode: connnect HTTP server first, then connect MQTT server afterwards
endmenu
config HAL_SEM_MAX_COUNT
int "The maximum count value that can be reached of the semaphore"
default 255
range 0 255
help
The recommended value of maximum count of the semaphore is 255
config HAL_SEM_INIT_COUNT
int "The count value assigned to the semaphore when it is created"
default 0
range 0 10
help
The recommended value of count of the semaphore is 0
config HAL_TLS_HANDSHAKE_TIMEOUT
int "TLS Handsake Timeout"
default 180
range 0 65535
help
Default TLS handshake timeout to host in seconds
config HAL_USE_CUSTOMER_AP_SSID
bool "Enable use customer softap SSID"
default n
help
Select this option to enable use customer softap SSID
config AP_SSID_KEY
string "AP SSID KV Key value"
default "apssid"
help
The KV key value which store AP SSID
config USE_SOFTAP_CONFIG
bool "Use softap config in smart light example"
default n
help
smart light example will use softap config when enable this option. Otherwise use smartconfig.
endmenu

View File

@@ -1,65 +0,0 @@
# Makefile
COMPONENT_ADD_LDFLAGS += -u ota_pubn_buf
COMPONENT_PRIV_INCLUDEDIRS := \
iotkit-embedded/coap_server/CoAPPacket \
iotkit-embedded/coap_server/server \
iotkit-embedded/dev_bind/impl \
iotkit-embedded/dev_bind/impl/os \
iotkit-embedded/dev_bind/impl/awss_reset \
iotkit-embedded/wifi_provision/dev_ap \
iotkit-embedded/wifi_provision/frameworks \
iotkit-embedded/wifi_provision/frameworks/aplist \
iotkit-embedded/wifi_provision/frameworks/ieee80211 \
iotkit-embedded/wifi_provision/frameworks/statics \
iotkit-embedded/wifi_provision/frameworks/utils \
iotkit-embedded/wifi_provision/p2p \
iotkit-embedded/wifi_provision/phone_ap \
iotkit-embedded/wifi_provision/router_ap \
iotkit-embedded/wifi_provision/smartconfig \
iotkit-embedded/wifi_provision/zero_config
COMPONENT_ADD_INCLUDEDIRS := \
conn_mgr \
iotkit-embedded/atm \
iotkit-embedded/coap_server \
iotkit-embedded/dev_bind \
iotkit-embedded/dev_model \
iotkit-embedded/dev_sign \
iotkit-embedded/dynamic_register \
iotkit-embedded/mqtt \
iotkit-embedded/ota \
iotkit-embedded/infra \
iotkit-embedded/wifi_provision \
iotkit-embedded \
wrappers
COMPONENT_SRCDIRS := \
conn_mgr \
iotkit-embedded/atm \
iotkit-embedded/certs \
iotkit-embedded/dev_bind/impl \
iotkit-embedded/dev_bind/impl/os \
iotkit-embedded/dev_bind/impl/awss_reset \
iotkit-embedded/dev_model \
iotkit-embedded/dev_reset \
iotkit-embedded/dev_sign \
iotkit-embedded/dynamic_register \
iotkit-embedded/infra \
iotkit-embedded/mqtt \
iotkit-embedded/ota \
iotkit-embedded/wifi_provision/dev_ap \
iotkit-embedded/wifi_provision/frameworks \
iotkit-embedded/wifi_provision/frameworks/aplist \
iotkit-embedded/wifi_provision/frameworks/ieee80211 \
iotkit-embedded/wifi_provision/frameworks/statics \
iotkit-embedded/wifi_provision/frameworks/utils \
iotkit-embedded/wifi_provision/p2p \
iotkit-embedded/wifi_provision/phone_ap \
iotkit-embedded/wifi_provision/router_ap \
iotkit-embedded/wifi_provision/smartconfig \
iotkit-embedded/wifi_provision/zero_config \
iotkit-embedded/coap_server/CoAPPacket \
iotkit-embedded/coap_server/server \
wrappers
CFLAGS += -Wno-char-subscripts

View File

@@ -1,87 +0,0 @@
# 量产说明
aliyun 在使用一机一密的情况下,需要在每个设备上烧写与设备对应的 ProductKey、ProductSecret、DeviceName、DeviceSecret。
为了使烧录和软件读取流程尽量简化,我们将使用 IDF 的 NVS 分区功能,将 ProductKey、ProductSecret、DeviceName、DeviceSecret 信息通过 NVS 分区生成工具或量产工具生成对应的 NVS 分区,该分区中利用 NVS 结构保存了 ProductKey、ProductSecret、DeviceName、DeviceSecret 的键值对。生成后的 NVS 分区 bin 可以通过 esptool 或其他烧写工具直接烧录到 NVS 分区对应的起始扇区partition 分区表中指明了该 NVS 分区的起始地址。软件可以通过 NVS 相关接口读取到 ProductKey、ProductSecret、DeviceName、DeviceSecret 的具体值。
请参照 partitions_esp32.csv 和 partitions_esp8266.csv 中 fctry 的起始地址进行烧录,也可根据实际项目对 partitions 进行调整,但一定要保证 partitons 中 fctry 的实际地址与烧录地址保持吻合。
关于 NVS、NVS 分区生成工具、量产工具,请参考:
- [NVS](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/nvs_flash.html)
- [NVS 分区生成工具](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/nvs_partition_gen.html)
- [量产工具](https://docs.espressif.com/projects/esp-idf/en/latest/api-reference/storage/mass_mfg.html)
## 单个 bin 生成
在调试过程中,建议使用该方式。
mass_mfg 目录中有一参考配置single_mfg_config.csv请拷贝成自己的配置文件如 my_single_mfg_config.csv。
```
cp single_mfg_config.csv my_single_mfg_config.csv
```
使用自己的 ProductKey、ProductSecret、DeviceName、DeviceSecret 对 my_single_mfg_config.csv 进行修改:
```
key,type,encoding,value
aliyun-key,namespace,,
DeviceName,data,string,config
DeviceSecret,data,string,dsj3RuY74pgCBJ3zczKz1LaLK7RGApqh
ProductKey,data,string,a10BnLLzGv4
ProductSecret,data,string,pVfLpS1u3A9JM0go
```
将 configdsj3RuY74pgCBJ3zczKz1LaLK7RGApqha10BnLLzGv4pVfLpS1u3A9JM0go 修改为你对应的值。
修改完成后,使用如下命令生成对应的 NVS 分区:
```
$IDF_PATH/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py --input single_mfg_config.csv --output single_mfg.bin --size 0x4000
python nvs_partition_gen.py generate single_mfg_config.csv single_mfg.bin 0x4000
```
如针对 esp8266 平台,请使用如下命令:
```
$IDF_PATH/components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py --input my_single_mfg_config.csv --output my_single_mfg.bin --size 0x4000
```
> 说明esp8266 的 NVS 格式当前为 v1, 默认已设置。
可以使用 esptool 工具将生成的包含秘钥的 NVS 分区烧入对应的 sector针对 example 中默认提供的 partitionsesp32 和 esp8266 将烧写到不同的分区,其中 esp32 的默认烧录地址为 0x210000esp8266 的默认烧录地址为 0x100000。
针对 esp32
```
$IDF_PATH/components/esptool_py/esptool/esptool.py write_flash 0x210000 my_single_mfg.bin
```
针对 esp8266
```
$IDF_PATH/components/esptool_py/esptool/esptool.py --port /dev/ttyUSB0 write_flash 0x100000 my_single_mfg.bin
```
> 注,当前 esp32 默认使用 4MB 的模组esp8266 默认使用 2MB 的模组,如使用其他大小的 Flash请适当调整 partitions 分区表,并确认烧写地址。
## 多个 bin 批量生成
量产过程中如采用以上方法单个生成对应的 NVS 分区会很繁琐,因此采用 IDF 中的量产工具,该量产工具也是基于 NVS 分区生成工具的扩充。
mass_mfg 目录中提供了一套参考的配置,其中 multipule_mfg_config.csv 为参数区配置文件,已做好针对 aliyun 的配置用户无需修改multipule_mfg_values.csv 中可以包含所有需要生成 NVS 分区的 ProductKey、ProductSecret、DeviceName、DeviceSecret 信息,请将 multipule_mfg_values.csv 复制为 my_multipule_mfg_values.csv 并对该文件进行修改,包含所有希望用于量产的 ProductKey、ProductSecret、DeviceName、DeviceSecret 信息。
my_multipule_mfg_values.csv 中内容如下:
```
id,DeviceName,DeviceSecret,ProductKey,ProductSecret
1,config,dsj3RuY74pgCBJ3zczKz1LaLK7RGApqh,a10BnLLzGv4,pVfLpS1u3A9JM0go
2,config,dsj3RuY74pgCBJ3zczKz1LaLK7RGApqh,a10BnLLzGv4,pVfLpS1u3A9JM0go
3,config,dsj3RuY74pgCBJ3zczKz1LaLK7RGApqh,a10BnLLzGv4,pVfLpS1u3A9JM0go
```
每一行代表了一组秘钥信息,第一列的为 id 信息,不会生成到对应的 NVS 分区中,仅用作标号。
采用如下命令批量生成 NVS 分区。
针对 esp32
```
$IDF_PATH/tools/mass_mfg/mfg_gen.py --conf multipule_mfg_config.csv --values my_multipule_mfg_values.csv --prefix Fan --size 0x4000
```
针对 esp8266
```
$IDF_PATH/tools/mass_mfg/mfg_gen.py --conf multipule_mfg_config.csv --values my_multipule_mfg_values.csv --prefix Fan --size 0x4000 --version v1
```
其中 --prefix 为生成的批量文件的前缀名称,可以修改为所需要的产品名称。执行完成后,会在当前目录下生成一 bin 目录,里面保持了所有可用于量产的 NVS 分区 bin。
> 注,当前不知道 aliyun 能否批量导出 ProductKey、ProductSecret、DeviceName、DeviceSecret如有需要可以编写特定脚本来生成 my_multipule_mfg_values.csv。

Some files were not shown because too many files have changed in this diff Show More