ESP32对接巴法云实现配网

news2025/6/5 16:37:06

目录

    • 序言
    • 准备工作
      • 巴法云注册与使用
      • 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) {
    // 设置目标 URL
    http_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 mode
  if (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 packets
    int 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地址

  1. 连接ESP32需要连接的WiFi不是ESP32的WiFi哦
  2. 打开巴法App-右上角+号或微信小程序搜索巴法,找到一键配网小程序
  3. 进入配网页面,切换协议为AP配网,小程序为Soft AP 配网
  4. WiFi名称会自动填写你当前连接的WiFi,输入WiFi密码,点击开始配网
  5. 此时会提示您连接到设备WiFi,点击打开设置,在WiFi界面连接bemfa开头的WiFi
  6. 回到App或小程序,此时将开始配网流程,硬件接收到信息后开始连接目标WiFi
  7. 连接成功后会在用户的巴法云控制台建立硬件主题,主题一般为:硬件mac地址+设备类型
  8. App或小程序检测到主题创建成功,代表配网结束,在App中就可以看到该设备啦

至此,配网功能结束
当然,该文章仅为使用巴法云方式实现配网功能,开发者也可以通过代码逻辑,来建立自己的MQTT服务,进行自己的个人后端开发,实现家庭智能


  1. ESP32-C6接入巴法云,Arduino方式 ↩︎

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2397975.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++语法系列之类型转换

前言 类型转换是经常存在的情况&#xff0c;类型转换分为隐式类型转化 和 显式类型转化 隐式类型转化&#xff1a;编译器在编译阶段自动进行&#xff0c;能转就转&#xff0c;不能转就编译失败 double i 3.3; int b i; //隐式类型转化 double -> intC搞出来了四种强制类…

详解开漏输出和推挽输出

开漏输出和推挽输出 以上是 GPIO 配置为输出时的内部示意图&#xff0c;我们要关注的其实就是这两个 MOS 管的开关状态&#xff0c;可以组合出四种状态&#xff1a; 两个 MOS 管都关闭时&#xff0c;输出处于一个浮空状态&#xff0c;此时他对其他点的电阻是无穷大的&#xff…

【八股消消乐】索引失效与优化方法总结

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一个正在变秃、变强的文艺倾年。 &#x1f514;本专栏《八股消消乐》旨在记录个人所背的八股文&#xff0c;包括Java/Go开发、Vue开发、系统架构、大模型开发、具身智能、机器学习、深度学习、力扣算法等相关知识点&#xff…

一步一步配置 Ubuntu Server 的 NodeJS 服务器详细实录——4. 配置服务器终端环境 zsh , oh my zsh, vim

前言 通过前面几篇文章&#xff0c;我们顺利的 安装了 ubuntu server 服务器&#xff0c;并且配置好了 ssh 免密登录服务器&#xff0c;也安装好了 服务器常用软件安装,接下来&#xff0c;我们要仔细的配置一下我们的终端环境&#xff0c;让服务器的终端更加好用。 一般情况下…

数据安全合规体系构建的“三道防线“

引言 "三道防线"模型架构图 #mermaid-svg-wbeppAbwa3Vb3nL2 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-wbeppAbwa3Vb3nL2 .error-icon{fill:#552222;}#mermaid-svg-wbeppAbwa3Vb3nL2 .error-text{fi…

【Spring底层分析】Spring AOP基本使用+万字底层源码阅读分析

一、AOP基本使用 三步&#xff1a; 将业务逻辑组件和切面类都加入到容器中&#xff0c;告诉Spring哪个是切面类&#xff08;Aspect&#xff09;在切面类上的每一个通知方法上标注通知注解&#xff0c;告诉Spring何时&#xff08;Before、After、Around……&#xff09;何地运…

新德通科技:以创新驱动光通信一体化发展,赋能全球智能互联

在数字经济与AI技术高速发展的今天&#xff0c;光通信作为信息传输的核心基础设施&#xff0c;正迎来前所未有的升级浪潮。深圳新德通科技有限公司&#xff08;以下简称“新德通科技”&#xff09;凭借其深厚的技术积累与一体化产品布局&#xff0c;成为行业内的中坚力量。本文…

C++ 内存泄漏检测器设计

文章目录 1. C中的动态内存分配2. 什么是内存泄漏3. 内存泄漏的代码案例4. 内存泄漏检查器的设计模块1&#xff1a;位置信息捕获&#xff1a;模块2&#xff1a;内存分配跟踪&#xff1a;模块3&#xff1a;内存释放跟踪&#xff1a;模块4&#xff1a;泄漏记录存储&#xff1a;模…

破局与进阶:ueBIM 在国产 BIM 赛道的差距认知与创新实践

作为国产BIM领域的探索者&#xff0c;斯维尔ueBIM自诞生以来始终以追赶国际头部技术为目标&#xff0c;但不可否认的是&#xff0c;在核心功能覆盖、行业生态成熟度以及全球市场占有率等方面&#xff0c;我们与Autodesk Revit、Bentley Systems等国际巨头仍存在显著差距。这种差…

分布式流处理与消息传递——向量时钟 (Vector Clocks) 算法详解

Java 实现向量时钟 (Vector Clocks) 算法详解 一、向量时钟核心原理 #mermaid-svg-JcZ1GT0r1ZNSy6W7 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-JcZ1GT0r1ZNSy6W7 .error-icon{fill:#552222;}#mermaid-svg-JcZ…

20250603在荣品的PRO-RK3566开发板的Android13下的命令行查看RK3566的温度

20250603在荣品的PRO-RK3566开发板的Android13下的命令行查看RK3566的温度 2025/6/3 11:58 RK3566的cpu运行效率 top rk3566_t:/ # rk3566_t:/ # rk3566_t:/ # cd /sys/class/thermal/ rk3566_t:/sys/class/thermal # ls -l rk3566_t:/sys/class/thermal # cd thermal_zone0/ r…

帝可得 - 设备管理

一. 需求说明 设备管理主要涉及到三个功能模块&#xff0c;业务流程如下&#xff1a; 新增设备类型: 允许管理员定义新的售货机型号&#xff0c;包括其规格和容量。 新增设备: 在新的设备类型定义后&#xff0c;系统应允许添加新的售货机实例&#xff0c;并将它们分配到特定的…

【iOS安全】使用LLDB调试iOS App | LLDB基本架构 | LLDB安装和配置

LLDB基本架构 参考&#xff1a; https://crifan.github.io/ios_re_dynamic_debug/website/debug_code/lldb_debugserver.html https://book.crifan.org/books/ios_re_debug_debugserver_lldb/website/ LLDB安装和配置 1. 让iPhone中出现/Developer/usr/bin/debugserver 最初…

Idea 配置 Maven 环境

下载 Maven 官网&#xff1a;https://maven.apache.org/index.html 点击左侧 Downloads&#xff0c;然后选择 Files 中的 zip 包下载&#xff08;下载慢可以使用迅雷&#xff09; 配置 Maven 将压缩包解压&#xff0c;比如我解压后放到了 D:\developer\environment\apache-…

Kafka 如何保证不重复消费

在消息队列的使用场景中&#xff0c;避免消息重复消费是保障数据准确性和业务逻辑正确性的关键。对于 Kafka 而言&#xff0c;保证不重复消费并非单一机制就能实现&#xff0c;而是需要从生产者、消费者以及业务层等多个维度协同配合。接下来&#xff0c;我们将结合图文详细解析…

RNN结构扩展与改进:从简单循环网络到时间间隔网络的技术演进

本文系统介绍 RNN 结构的常见扩展与改进方案。涵盖 简单循环神经网络&#xff08;SRN&#xff09;、双向循环神经网络&#xff08;BRNN&#xff09;、深度循环神经网络&#xff08;Deep RNN&#xff09; 等多种变体&#xff0c;解析其核心架构、技术特点及应用场景&#xff0c;…

类 Excel 数据填报

类 Excel 填报模式&#xff0c;满足用户 Excel 使用习惯 数据填报&#xff0c;可作为独立的功能模块&#xff0c;用于管理业务流程、汇总采集数据&#xff0c;以及开发各类数据报送系统&#xff0c;因此&#xff0c;对于报表工具而言&#xff0c;其典型场景之一就是利用报表模…

Office文档图片批量导出工具

软件介绍 本文介绍一款专业的Office文档图片批量导出工具。 软件特点 这款软件能够批量导出Word、Excel和PPT中的图片&#xff0c;采用绿色单文件设计&#xff0c;体积小巧仅344KB。 基本操作流程 使用方法十分简单&#xff1a;直接将Word、Excel或PPT文件拖入软件&#xf…

【iOS】ARC 与 Autorelease

ARC 与 Autorelease 文章目录 ARC 与 Autorelease前言何为ARC内存管理考虑方式自己生成的对象,自己持有非自己生成的对象,自己也可以持有不再需要自己持有的对象时释放非自己持有的对象无法释放 ARC的具体实现编译期和运行期ARC做的事情ARC实现: __autoreleasing 与 Autoreleas…

铁电液晶破局 VR/AR:10000PPI 重构元宇宙显示体验

一、VR/AR 沉浸感困境&#xff1a;传统显示技术的天花板在哪&#xff1f; &#xff08;一&#xff09;纱窗效应与眩晕感&#xff1a;近眼显示的双重枷锁 当用户戴上 VR 头显&#xff0c;眼前像素网格形成的 “纱窗效应” 瞬间打破沉浸感。传统液晶 500-600PPI 的像素密度&…