)
1. 实例代码这是一个完整的 ESP32-S3 温湿度数据上报项目支持 AP 配网和 MQTT 通信。代码实现了以下核心功能AP 配网模式当无法连接保存的 WiFi 时设备会自动开启 AP 热点用户可通过网页配置 WiFi 和 MQTT 参数参数持久化使用 Preferences 库将配置保存到 NVS 闪存重启后自动加载MQTT 通信支持 QoS1 的消息发布和订阅确保数据传输可靠性定时上报每 30 秒自动上报模拟温湿度数据到 MQTT 服务器远程控制可通过 MQTT 发送 report 指令触发立即上报#include Arduino.h #include WiFi.h #include WebServer.h #include PubSubClient.h #include Preferences.h #include ArduinoJson.h // AP热点参数 const char AP_SSID[] ESP32S3_CONFIG_AP; const char AP_PASS[] 12345678; IPAddress apIp(192, 168, 4, 1); IPAddress apMask(255, 255, 255, 0); WebServer server(80); WiFiClient wifiClient; PubSubClient mqttClient(wifiClient); Preferences prefs; // MQTT固定配置 const String MQTT_CLIENT_ID dzc12345678; const String MQTT_SUB_TOPIC /esp32s3/cmd; // 订阅指令主题 const String MQTT_PUB_TOPIC /esp32s3/upload; // 上报温湿度主题 const uint32_t UPLOAD_INTERVAL 30000; // 30秒上报一次 const uint8_t MQTT_QOS 1; // 全局统一QoS1 // 存储参数 String staSsid, staPass; String mqttHost; uint16_t mqttPort; String mqttUser, mqttPass; // 定时上报计时器 static uint32_t lastUploadTime 0; // 前置函数声明 void publishTempHumiData(); void mqttCallback(char* topic, uint8_t* payload, unsigned int len); // 配置页面HTML 修复中文乱码支持参数回填 const char pageConfig[] PROGMEM RHTML( !DOCTYPE html html head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 titleESP32-S3 WiFiMQTT配置/title style *{box-sizing:border-box;font-family:system-ui,SimHei} .wrap{max-width:460px;margin:24px auto;padding:20px;border:1px #ddd solid;border-radius:12px;box-shadow:0 2px 8px #eee} .item{margin:16px 0} label{display:block;margin-bottom:6px;font-weight:bold;color:#333} input{width:100%;padding:10px;border:1px #bbb solid;border-radius:6px;font-size:15px} button{width:100%;padding:12px;background:#007acc;color:#fff;border:none;border-radius:6px;font-size:16px;margin-top:8px} hr{margin:20px 0;border:0;border-top:1px #eee solid} .tip{font-size:13px;color:#666;margin:4px 0} /style /head body div classwrap h2 aligncenterWiFi MQTT 参数设置/h2 form action/save methodPOST div classitem labelWiFi名称 SSID/label input typetext namessid value$SSID$ required div classtip当前已保存$SSID_TIP$/div /div div classitem labelWiFi密码/label input typepassword namewpass placeholder不修改留空 /div hr div classitem labelMQTT服务器IP/域名/label input typetext namemhost value$MHOST$ placeholder192.168.1.100 required /div div classitem labelMQTT端口/label input typenumber namemport value$MPORT$ required /div div classitem labelMQTT用户名/label input typetext namemuser value$MUSER$ /div div classitem labelMQTT密码/label input typepassword namempass placeholder不修改留空 /div div classitem button typesubmit保存配置并重启设备/button /div /form /div /body /html )HTML; // 状态页面 void handleIndex() { String html div stylemax-width:460px;margin:20px auto;padding:20px; html h2设备运行状态/h2; html pWiFi连接 String(WiFi.isConnected() ? span stylecolor:green已连接/span : span stylecolor:red未连接/span) /p; if (WiFi.isConnected()) html p本机IP WiFi.localIP().toString() /p; html pMQTT状态 String(mqttClient.connected() ? span stylecolor:green在线/span : span stylecolor:red离线/span) /p; html pMQTT服务 mqttHost : String(mqttPort) /p; html p客户端ID MQTT_CLIENT_ID /p; html p上报周期30秒/次 | QoS1/p; html bra href/configbutton stylepadding:10px 20px;font-size:15px重新配置参数/button/a; html /div; server.send(200, text/html;charsetutf-8, html); } // 配置页面回填已有参数解决乱码 void handleConfig() { String page String(pageConfig); page.replace($SSID$, staSsid); page.replace($SSID_TIP$, staSsid.isEmpty() ? 无保存WiFi : staSsid); page.replace($MHOST$, mqttHost); page.replace($MPORT$, String(mqttPort)); page.replace($MUSER$, mqttUser); server.send(200, text/html;charsetutf-8, page); } // 保存表单参数到NVS闪存 void handleSave() { String newSSID server.arg(ssid); String newWpass server.arg(wpass); String newMhost server.arg(mhost); uint16_t newMport server.arg(mport).toInt(); String newMuser server.arg(muser); String newMpass server.arg(mpass); prefs.begin(cfg, false); prefs.putString(ssid, newSSID); if (!newWpass.isEmpty()) prefs.putString(wpass, newWpass); prefs.putString(mhost, newMhost); prefs.putUInt(mport, newMport); prefs.putString(muser, newMuser); if (!newMpass.isEmpty()) prefs.putString(mpass, newMpass); prefs.end(); String tip div styletext-align:center;margin-top:40px;font-size:18px; tip h2 stylecolor:#007acc参数保存成功/h2; tip p1.5秒后自动重启设备生效/p/div; server.send(200, text/html;charsetutf-8, tip); delay(1500); ESP.restart(); } // 开启AP配网模式 void startAP() { Serial.println( WiFi connect fail, start AP config ); WiFi.mode(WIFI_AP); WiFi.softAPConfig(apIp, apIp, apMask); WiFi.softAP(AP_SSID, AP_PASS); Serial.print(AP SSID: ); Serial.println(AP_SSID); Serial.print(Config Web IP: ); Serial.println(apIp.toString()); } // 连接保存的WiFi STA bool connectWifi() { if (staSsid.isEmpty()) return false; Serial.print(Try connect WiFi: ); Serial.println(staSsid); WiFi.mode(WIFI_STA); WiFi.begin(staSsid.c_str(), staPass.c_str()); uint32_t timeout millis() 8000; while (millis() timeout) { if (WiFi.isConnected()) { Serial.print(WiFi connected, IP: ); Serial.println(WiFi.localIP().toString()); return true; } delay(400); } Serial.println(WiFi connect timeout 8s, switch AP mode); return false; } // MQTT重连函数 bool mqttReconnect() { mqttClient.setServer(mqttHost.c_str(), mqttPort); bool connectRet; if (mqttUser.isEmpty()) { connectRet mqttClient.connect(MQTT_CLIENT_ID.c_str()); } else { connectRet mqttClient.connect(MQTT_CLIENT_ID.c_str(), mqttUser.c_str(), mqttPass.c_str()); } if (connectRet) { Serial.println(MQTT connect success, ClientID: MQTT_CLIENT_ID); // 修改1订阅主题指定QoS1 mqttClient.subscribe(MQTT_SUB_TOPIC.c_str(), MQTT_QOS); Serial.println(Subscribed topic: MQTT_SUB_TOPIC QoS1); return true; } Serial.print(MQTT connect fail code: ); Serial.println(mqttClient.state()); return false; } // MQTT消息接收回调 void mqttCallback(char* topic, uint8_t* payload, unsigned int len) { Serial.print([SUB] Topic: ); Serial.print(topic); Serial.print( Msg: ); String msg; for (int i 0; i len; i) msg (char)payload[i]; Serial.println(msg); if (msg report) { Serial.println(Receive report cmd, upload data immediately); publishTempHumiData(); } } // 发布温湿度数据 void publishTempHumiData() { float temp random(200, 300) / 10.0f; float humi random(400, 700) / 10.0f; JsonDocument doc; doc[temp] temp; doc[humi] humi; doc[device] MQTT_CLIENT_ID; String jsonStr; serializeJson(doc, jsonStr); // 修改2publish传入第三个参数qos1 if (mqttClient.publish(MQTT_PUB_TOPIC.c_str(), jsonStr.c_str(), MQTT_QOS)) { Serial.print(Timer upload success QoS1 - ); Serial.println(jsonStr); } else { Serial.println(MQTT upload failed); } } void setup() { Serial.begin(115200); delay(500); Serial.println( ESP32-S3 AP Config WiFi MQTT TempHumi Upload Start ); randomSeed(millis()); prefs.begin(cfg, true); staSsid prefs.getString(ssid, dengdeng); staPass prefs.getString(wpass, DZC12345678); mqttHost prefs.getString(mhost, 1.tcp.cpolar.cn); mqttPort prefs.getUInt(mport, 24396); mqttUser prefs.getString(muser, admin); mqttPass prefs.getString(mpass, djq12345678); prefs.end(); if (!connectWifi()) startAP(); server.on(/, handleIndex); server.on(/config, handleConfig); server.on(/save, HTTP_POST, handleSave); server.begin(); Serial.println(Web server started port 80); mqttClient.setCallback(mqttCallback); } void loop() { server.handleClient(); if (!WiFi.isConnected()) return; if (!mqttClient.connected()) { static uint32_t lastMqttTry 0; if (millis() - lastMqttTry 3000) { lastMqttTry millis(); mqttReconnect(); } } mqttClient.loop(); if (millis() - lastUploadTime UPLOAD_INTERVAL) { lastUploadTime millis(); publishTempHumiData(); } }2. platform.ini 配置文件这是 PlatformIO 项目的配置文件用于指定 ESP32-S3 开发板的编译和上传参数开发板配置使用 ESP32-S3 DevKitC-1 开发板框架为 Arduino串口设置上传和监视波特率均为 115200USB 模式启用 USB CDC 模式支持串口通信库依赖仅需 PubSubClient 和 ArduinoJson 库WiFi、WebServer 和 Preferences 库已内置[env:esp32-s3-devkitc-1] platform espressif326.9.0 board esp32-s3-devkitc-1 framework arduino upload_speed 115200 monitor_speed 115200 upload_port /dev/ttyACM0 monitor_port /dev/ttyACM0 build_flags -D ARDUINO_USB_MODE1 -D ARDUINO_USB_CDC_ON_BOOT1 board_build.flash_size 8MB 只留MQTT库WiFi/WebServer/Preferences都是ESP32内置自带 lib_deps knolleary/PubSubClient2.8 bblanchon/ArduinoJson7.2.0使用说明将上述代码保存到 PlatformIO 项目的src/main.cpp文件中将platform.ini配置文件放在项目根目录然后编译上传到 ESP32-S3 开发板即可运行。