目錄
- 序言
- 準備工作
- 巴法云注冊與使用
- Arduino準備
- 開發
- 開始配網
序言
本文部分內容摘抄原創作者巴法云-做優秀的物聯網平臺
代碼有部分修改并測試運行正常
巴法云支持免費用戶通過開發對接實現各智能音箱設備語音控制智能家居設備,并有自己的App進行配網和控制,在開發過程中省去了很多工作,推薦大家使用
巴法云關于個人開發者使用描述
準備工作
巴法云注冊與使用
巴法云開發者文檔
進入巴法云
下載App
首先需要在巴法云注冊用戶。
注冊完成之后下載App并登錄
至此巴法云準備工作就完成啦
Arduino準備
本次開發基于Arduino
請移步ESP32入門之arduino IDE環境搭建 1
或轉載
開發
以下是實現的具體配網流程代碼
//需要在arduino IDE軟件中---工具-->管理庫-->搜索arduinojson并安裝
#include <WiFi.h>
#include <WiFiUDP.h>
#include <ArduinoJson.h>
#include <EEPROM.h>
#include <Ticker.h>
#include <HTTPClient.h>//根據需要修改的信息
String aptype = "009"; //設備類型,001插座設備,002燈類設備,003風扇設備,005空調,006開關,009窗簾
String Name = "窗簾"; //設備昵稱,可隨意修改
String verSion = "3.1"; //3是tcp設備端口8344,1是MQTT設備
String room = "臥室"; //房間。例如客廳、臥室等,默認空
int protoType = 3; //3是tcp設備端口8344,1是MQTT設備
int adminID = 0; //默認空即可。企業id,建議企業用戶配置,該設備會自動綁定到該企業下,獲取id方法見接入文檔5.17節
WiFiClient client_bemfa_WiFiClient;
HTTPClient http_bemfa_HTTPClient;//檢測是否是第一次連接WIFI
bool firstWIfiConfig = false;
String topic = "";
struct config_type {char stassid[32];char stapsw[16];char cuid[40];char ctopic[32];uint8_t reboot;uint8_t magic;
};
config_type config;char config_flag = 0; //判斷是否配網
#define MAGIC_NUMBER 0xAA //判斷是否配網
char packetBuffer[255]; //發送數據包
WiFiUDP Udp;/** 從EEPROM加載參數
*/
uint8_t* p = (uint8_t*)(&config);
void loadConfig() {uint8_t mac[6];Serial.println(" LoadConfig.......");WiFi.macAddress(mac);EEPROM.begin(512);for (int i = 0; i < sizeof(config); i++) {*(p + i) = EEPROM.read(i);}config.reboot = config.reboot + 1;if (config.reboot >= 4) {restoreFactory();}if (config.magic != 0xAA) {config_flag = 1;}EEPROM.begin(512);for (int i = 0; i < sizeof(config); i++) {EEPROM.write(i, *(p + i));}EEPROM.commit();delay(2000);Serial.println("loadConfig Over");EEPROM.begin(512);config.reboot = 0;for (int i = 0; i < sizeof(config); i++) {EEPROM.write(i, *(p + i));}EEPROM.commit();
}/* * 恢復出廠設置
*/
void restoreFactory() {Serial.println("\r\n Restore Factory....... ");config.magic = 0x00;strcpy(config.stassid, "");strcpy(config.stapsw, "");strcpy(config.cuid, "");strcpy(config.ctopic, "");config.magic = 0x00;saveConfig();delayRestart(1);
}
/*
保存WIFI信息
*/
void saveConfig() {config.reboot = 0;EEPROM.begin(512); // 與loadConfig統一為512字節(足夠存儲config結構體)uint8_t* p = (uint8_t*)(&config);for (int i = 0; i < sizeof(config); i++) {EEPROM.write(i, *(p + i));}EEPROM.commit();
}
Ticker delayTimer;
void delayRestart(float t) {delayTimer.attach(t, []() {ESP.restart();});
}
void apConfig(String mac) {if (config_flag == 1) {WiFi.softAP("bemfa_" + mac);Udp.begin(8266);Serial.println("Started Ap Config...");}topic = mac + aptype;// Removed blocking while loop
}/*第一次配網檢查WIFI,保存WIFI配置信息,并創建主題
*/
void checkFirstConfig() {if (firstWIfiConfig) {// 設置目標 URLhttp_bemfa_HTTPClient.begin(client_bemfa_WiFiClient, "http://pro.bemfa.com/vs/web/v1/deviceAddTopic");// 創建 JSON 對象StaticJsonDocument<200> jsonDoc;jsonDoc["uid"] = config.cuid;jsonDoc["name"] = Name;jsonDoc["topic"] = topic;jsonDoc["type"] = protoType;jsonDoc["room"] = room;jsonDoc["adminID"] = adminID;jsonDoc["wifiConfig"] = 1; //必填字段// 將 JSON 對象轉換為字符串String jsonString;serializeJson(jsonDoc, jsonString);http_bemfa_HTTPClient.addHeader("Content-Type", "application/json; charset=UTF-8");// 發送請求int httpCode = http_bemfa_HTTPClient.POST(jsonString);if (httpCode == 200) {Serial.println("POST succeeded with code:");Serial.println(httpCode);String payload = http_bemfa_HTTPClient.getString();Serial.println(payload);//json數據解析StaticJsonDocument<200> doc;DeserializationError error = deserializeJson(doc, payload);if (error) {Serial.print(F("deserializeJson() failed: "));Serial.println(error.c_str());}int code = doc["code"];if (code == 0) {int resCode = doc["data"]["code"];if (resCode == 40006 || resCode == 0) {String docUID = doc["uid"];Serial.print("create topic ok:");Serial.println(topic);if (firstWIfiConfig) {config.reboot = 0;config.magic = 0xAA;saveConfig();}} else {Serial.println(" config ERROR.........");}} else {Serial.println(" config ERROR.........");}} else if (httpCode != 200) {Serial.println("POST failed with code:");Serial.println(httpCode);} else {Serial.println("Unknown error");}http_bemfa_HTTPClient.end();}
}// 復位或上電后運行一次:
void setup() {//在這里加入初始化相關代碼,只運行一次:Serial.begin(115200);String mac = WiFi.macAddress();mac.replace(":", ""); //去掉:號topic = mac.substring(8) + aptype; //取mac地址的后半部分做主題用,并拼接設備類型// 初始化WiFi模式以確保MAC地址正確獲取(關鍵修改)WiFi.mode(WIFI_STA); // 設置為STA模式初始化硬件delay(1000); // 等待WiFi模塊初始化完成// 初始化配網(此時可正確獲取MAC地址)mac = WiFi.macAddress();mac.replace(":", "");loadConfig(); //加載存儲的數據apConfig(mac); //加載ap// Only try to connect to WiFi if not in config modeif (config_flag == 0) {Serial.println("Connecting to WiFi...");WiFi.disconnect(); //斷開連接WiFi.mode(WIFI_STA); //STA模式WiFi.begin(config.stassid, config.stapsw); //連接路由器// Removed blocking while loop here}
}//一直循環執行:
void loop() {if (config_flag == 1) { // If in config mode, handle UDP packetsint packetSize = Udp.parsePacket();if (packetSize) {Serial.print("Received packet of size ");Serial.println(packetSize);Serial.print("From ");IPAddress remoteIp = Udp.remoteIP();Serial.print(remoteIp);Serial.print(", port ");Serial.println(Udp.remotePort());int len = Udp.read(packetBuffer, 255);if (len > 0) {packetBuffer[len] = 0;}Serial.println("Contents:");Serial.println(packetBuffer);StaticJsonDocument<200> doc;DeserializationError error = deserializeJson(doc, packetBuffer);if (error) {Serial.print(F("deserializeJson() failed: "));Serial.println(error.f_str());return;}int cmdType = doc["cmdType"].as<int>();if (cmdType == 1) {const char* ssid = doc["ssid"];const char* password = doc["password"];const char* token = doc["token"];strcpy(config.stassid, ssid);strcpy(config.stapsw, password);strcpy(config.cuid, token);//收到信息,并回復String ReplyBuffer = "{\"cmdType\":2,\"productId\":\"" + topic + "\",\"deviceName\":\"" + Name + "\",\"protoVersion\":\"" + verSion + "\"}";const char* replyBufferData = ReplyBuffer.c_str();size_t replyBufferLength = ReplyBuffer.length();Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());Udp.write((const uint8_t*)replyBufferData, replyBufferLength);Udp.endPacket();} else if (cmdType == 3) {config_flag = 0;firstWIfiConfig = true;// 徹底停止UDP并清理網絡資源Udp.stop();WiFi.disconnect(true); // 強制斷開所有連接(包括AP和STA)WiFi.mode(WIFI_OFF); // 關閉所有WiFi模式delay(1500); // 延長等待時間確保硬件完成釋放// 切換為STA模式并連接WiFi(添加連接前的參數校驗)Serial.println("Connecting to WiFi after provisioning...");if (strlen(config.stassid) == 0 || strlen(config.stapsw) == 0) {Serial.println("Error: SSID or password is empty");return;}WiFi.mode(WIFI_STA);WiFi.begin(config.stassid, config.stapsw);// 等待連接結果(設置超時避免永久阻塞)unsigned long start = millis();while (WiFi.status() != WL_CONNECTED && (millis() - start) < 15000) {delay(100);}if (WiFi.status() == WL_CONNECTED) {Serial.printf("Connected to %s, IP: %s\n", config.stassid, WiFi.localIP().toString().c_str());checkFirstConfig();} else {Serial.println("WiFi connection failed (timeout)");}}}} else { // If not in config mode, run normal operation// Your normal device operation code goes here// Serial.println("Config success"); // This will print repeatedly, move to setup or only print once// Removed delay(1000)}
}
代碼中的aptype與Name變量將是你在配往后自動添加的設備類型與名稱
代碼中的protoType參數將會控制您在巴法云建立MQTT主題還是TCP主題,根據智能家居類型自行配置
自行燒錄哦,如不會燒錄請查詢官方文檔
開始配網
第一次開機后硬件會檢查是否已配置網絡,沒有配置將會打開WiFi熱點,名稱為bemfa_mac地址
- 連接
ESP32需要連接的WiFi
,不是ESP32的WiFi哦
- 打開
巴法App-右上角+號
或微信小程序搜索巴法
,找到一鍵配網
小程序 - 進入配網頁面,切換協議為
AP配網
,小程序為Soft AP 配網
- WiFi名稱會自動填寫你當前連接的WiFi,
輸入WiFi密碼
,點擊開始配網
- 此時會提示您連接到設備WiFi,點擊
打開設置
,在WiFi界面連接bemfa
開頭的WiFi - 回到App或小程序,此時將開始配網流程,硬件接收到信息后開始連接目標WiFi
- 連接成功后會在用戶的巴法云控制臺建立硬件主題,主題一般為:
硬件mac地址+設備類型
- App或小程序檢測到主題創建成功,代表配網結束,在App中就可以看到該設備啦
至此,配網功能結束
當然,該文章僅為使用巴法云方式實現配網功能,開發者也可以通過代碼邏輯,來建立自己的MQTT服務,進行自己的個人后端開發,實現家庭智能
ESP32-C6接入巴法云,Arduino方式 ??