wifimanager库快速使用手册

wifimanager库快速使用手册

日志

2024/12/23:新增局域网OTA烧录功能

2024/12/20:记录wifimanager库的使用,以及部分拓展(网页控制,文件响应)

前言

esp32系列的产品我一直很喜欢使用,因为其模组外围电路简单,还能结合arudinoIDE开发。自带wifi蓝牙,可以说十分契合智能家居,小型DIY这样的项目。此篇文章记录一个广泛使用的esp系列芯片的wifi控制库。包括如下几个部分:

  • wifimanager库函数的使用,例如:固定ip的设置(对DIY产品来说很重要)
  • 结合webserver响应相关网页,以及基本通信逻辑,结构
  • 关于配置网络时,参数读取的逻辑,使用SPFIS文件管理系统

我想写的精简一点,日后若遗忘可以快速查看。

wifimanager库基本使用

我将wifimanager库封装成函数,以便主程序逻辑清晰,以下是函数.cpp.h源码

其中.cpp文件中,相关函数注释已经标明

.CPP文件

#include "wifihelper.h"

WiFiManager wifiManager;
bool shouldSaveConfig = false;
char mqtt_id[100];
WebServer server(80);
const int ledPin = 2;

/*****************************wifi********************************/
void WifiManager_init() {

  WiFiManagerParameter custom_mqtt_id("id", "客户端名称", mqtt_id, 100);
  wifiManager.addParameter(&custom_mqtt_id);

  wifiManager.setConfigPortalTimeout(180);  // 设置门户配置超时时间
  wifiManager.setConnectTimeout(10);        // 设置 WiFi 连接超时时间为10秒
  wifiManager.setDebugOutput(false);        // 打印调试内容
  wifiManager.setMinimumSignalQuality(30);  // 设置最小信号强度
  wifiManager.setRemoveDuplicateAPs(true);  // 设置过滤重复的AP

  wifiManager.setAPStaticIPConfig(IPAddress(192, 168, 5, 1), IPAddress(192, 168, 5, 1), IPAddress(255, 255, 255, 0));         // 设置固定AP信息
  wifiManager.setSTAStaticIPConfig(IPAddress(192, 168, 137, 100), IPAddress(192, 168, 137, 1), IPAddress(255, 255, 255, 0));  // 设置静态ip

  wifiManager.setAPCallback(configModeCallback);          // 设置进入AP模式的回调
  wifiManager.setSaveConfigCallback(saveConfigCallback);  // 设置点击保存的回调

  if (!wifiManager.autoConnect("Apple", "12345678")) {
    Serial.println("Failed to connect, Timeout reached");
    ESP.restart();  // 超时未连接成功,重启设备(wifimanager对于密码错误不会进行提示)
  } else {
    Serial.print("Connected to:");
    Serial.println(WiFi.SSID());
    Serial.print("local IP:");
    Serial.println(WiFi.localIP());
  }

  if (shouldSaveConfig) {
    strcpy(mqtt_id, custom_mqtt_id.getValue());
    Serial.println(mqtt_id);
  }
}

void configModeCallback(WiFiManager *myWiFiManager) {
  Serial.print("Creat AP SSID:");
  Serial.println(myWiFiManager->getConfigPortalSSID());
  Serial.print("AP Address:");
  Serial.println(WiFi.softAPIP());
}

void saveConfigCallback() {
  shouldSaveConfig = true;
}

/*****************************SPIFS********************************/
void spifs_init() {
  if (!SPIFFS.begin(true)) {
    Serial.println("SPIFFS Mount Failed");
    return;
  }
}

/*****************************webserver****************************/
void server_init() {
  server.on("/", HTTP_GET, handleRoot);
  server.on("/controlLED", HTTP_GET, handleControlLED);
  server.begin();
}

// 首页路由,提供网页文件(发送文件到网页示例)
void handleRoot() {
  File file = SPIFFS.open("/index.html", "r");
  if (file) {
    server.streamFile(file, "text/html");
    file.close();
  } else {
    server.send(404, "text/plain", "File Not Found");
  }
}

// 控制 LED 开关(网页控制引脚示例)
void handleControlLED() {
  String state = server.arg("state");
  if (state == "ON") {
    digitalWrite(ledPin, HIGH);
    server.send(200, "text/plain", "LED is ON");
  } else if (state == "OFF") {
    digitalWrite(ledPin, LOW);
    server.send(200, "text/plain", "LED is OFF");
  } else {
    server.send(400, "text/plain", "Invalid state");
  }
}

.H文件

#ifndef _WIFIHELPER_H
#define _WIFIHELPER_H

#include <WiFiManager.h>
#include <WiFi.h>
#include <SPIFFS.h>
#include <WebServer.h>

extern WiFiManager wifiManager;
extern bool shouldSaveConfig;
extern char mqtt_id[100];
extern WebServer server;
extern const int ledPin;

/*****************************wifi********************************/
void WifiManager_init();
void configModeCallback(WiFiManager *myWiFiManager);
void saveConfigCallback();
/*****************************SPIFS*******************************/
void spifs_init();
/*****************************webserver***************************/
void server_init();
void handleRoot();
void handleControlLED();

#endif

.INO文件

#include "wifihelper.h"

void setup() {
  Serial.begin(115200);

  WifiManager_init();
  spifs_init();
  server_init();

  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
}

void loop() {
  server.handleClient();
}

.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>PID Control Simulation</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            margin: 0;
            padding: 20px;
            background-color: #f0f0f0;
        }

        h1 {
            margin-bottom: 30px;
        }

        .slider-container {
            margin: 20px 0;
        }

        .slider {
            width: 100%;
        }

        .chart-container {
            margin-top: 20px;
            position: relative;
            height: 300px;
            width: 100%;
            background-color: #fff;
            border: 1px solid #ccc;
        }

        #chart {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
        }

        .controls {
            display: flex;
            justify-content: space-around;
            margin-top: 20px;
        }

        .control {
            text-align: center;
        }

        .label {
            margin-bottom: 10px;
        }

        .value-display {
            margin-top: 20px;
        }

        .value-display p {
            font-size: 18px;
            margin: 5px 0;
        }

        .parameter-display {
            margin-top: 10px;
            font-size: 14px;
            color: #666;
        }

        .target-display {
            margin-top: 20px;
            font-size: 18px;
        }

        .start-btn, .stop-btn, .reset-btn {
            padding: 10px 20px;
            font-size: 16px;
            margin-top: 20px;
            background-color: #4CAF50;
            color: white;
            border: none;
            cursor: pointer;
        }

        .start-btn:disabled, .stop-btn:disabled, .reset-btn:disabled {
            background-color: #ccc;
            cursor: not-allowed;
        }
    </style>
</head>
<body>

<h1>PID Control Simulation</h1>

<!-- Set Target Value -->
<div>
    <label for="targetInput" class="label">Set Target Value</label>
    <input type="number" id="targetInput" value="1" step="0.1" min="0" max="10">
</div>

<!-- PID control sliders -->
<div class="controls">
    <div class="control">
        <label for="kp" class="label">Kp (Proportional)</label>
        <input type="range" id="kp" class="slider" min="0" max="2" step="0.1" value="1">
        <p id="kpValue">Kp: 1</p>
    </div>
    <div class="control">
        <label for="ki" class="label">Ki (Integral)</label>
        <input type="range" id="ki" class="slider" min="0" max="1" step="0.01" value="0.1">
        <p id="kiValue">Ki: 0.1</p>
    </div>
    <div class="control">
        <label for="kd" class="label">Kd (Derivative)</label>
        <input type="range" id="kd" class="slider" min="0" max="1" step="0.01" value="0.1">
        <p id="kdValue">Kd: 0.1</p>
    </div>
</div>

<!-- Display current target and system state -->
<div class="value-display">
    <p id="targetValue">Target: 1</p>
    <p id="currentValue">Current Value: 0</p> <!-- This will be updated -->
</div>

<!-- Graph container -->
<div class="chart-container">
    <canvas id="chart"></canvas>
</div>

<!-- Display coordinates and parameter details -->
<div class="parameter-display">
    <p>Time Step: <span id="timeStep">0.1</span> s</p>
    <p>Max X value: <span id="maxXValue">800</span></p>
    <p>Max Y value: <span id="maxYValue">1.5</span></p>
</div>

<!-- Custom X, Y axis max inputs -->
<div class="controls">
    <div class="control">
        <label for="maxXInput" class="label">Max X Value</label>
        <input type="number" id="maxXInput" value="800" step="1" min="1" max="2000">
    </div>
    <div class="control">
        <label for="maxYInput" class="label">Max Y Value</label>
        <input type="number" id="maxYInput" value="1.5" step="0.1" min="0.1" max="3">
    </div>
</div>

<!-- Start, Stop, Reset Control Buttons -->
<button id="startBtn" class="start-btn" onclick="startPIDControl()">Start PID Control</button>
<button id="stopBtn" class="stop-btn" onclick="stopPIDControl()" disabled>Stop PID Control</button>
<button id="resetBtn" class="reset-btn" onclick="resetGraph()" disabled>Reset Graph</button>

<script>
    let Kp = 1, Ki = 0.1, Kd = 0.1;
    let target = 1;
    let systemState = 0;
    let previousError = 0;
    let integral = 0;
    let timeStep = 0.1;
    let maxX = 800; // Default max X value
    let maxY = 1.5; // Default max Y value
    let isRunning = false; // To control when the PID loop starts
    let animationFrameId; // Store animation frame id for stopping the loop

    let canvas = document.getElementById('chart');
    let ctx = canvas.getContext('2d');
    let chartData = { x: [], y: [] };

    // Canvas settings
    const width = canvas.width = 1200;  // Increase canvas width for wider chart display
    const height = canvas.height = 300;
    const margin = 40;

    // Update PID parameters
    function updatePID() {
        Kp = parseFloat(document.getElementById('kp').value);
        Ki = parseFloat(document.getElementById('ki').value);
        Kd = parseFloat(document.getElementById('kd').value);

        // Display updated PID values
        document.getElementById('kpValue').innerText = `Kp: ${Kp}`;
        document.getElementById('kiValue').innerText = `Ki: ${Ki}`;
        document.getElementById('kdValue').innerText = `Kd: ${Kd}`;
    }

    // Listen to slider changes
    document.getElementById('kp').addEventListener('input', updatePID);
    document.getElementById('ki').addEventListener('input', updatePID);
    document.getElementById('kd').addEventListener('input', updatePID);

    // Update the target value based on user input
    document.getElementById('targetInput').addEventListener('input', function() {
        target = parseFloat(this.value);
        document.getElementById('targetValue').innerText = `Target: ${target}`;
    });

    // Update X and Y max values
    document.getElementById('maxXInput').addEventListener('input', function() {
        maxX = parseInt(this.value);
        document.getElementById('maxXValue').innerText = `Max X: ${maxX}`;
    });

    document.getElementById('maxYInput').addEventListener('input', function() {
        maxY = parseFloat(this.value);
        document.getElementById('maxYValue').innerText = `Max Y: ${maxY}`;
    });

    // Reset the graph and restore the initial conditions
    function resetGraph() {
        // Stop PID control before resetting
        stopPIDControl();

        // Clear the chart and reset values
        chartData = { x: [], y: [] };
        previousError = 0;
        integral = 0;
        systemState = 0;

        // Reset the UI to show the initial values
        document.getElementById('currentValue').innerText = `Current Value: 0`;
        document.getElementById('targetValue').innerText = `Target: ${target}`;

        // Clear the chart area
        ctx.clearRect(0, 0, width, height);

        // Optionally re-enable buttons after reset
        document.getElementById('startBtn').disabled = false;
        document.getElementById('stopBtn').disabled = true;
        document.getElementById('resetBtn').disabled = false;
    }

    // Draw the graph with X and Y axis
    function drawGraph() {
        ctx.clearRect(0, 0, width, height);

        // Draw left X axis
        ctx.beginPath();
        ctx.moveTo(margin, height - margin);
        ctx.lineTo(margin, margin);
        ctx.strokeStyle = 'black';
        ctx.stroke();

        // Draw right X axis
        ctx.beginPath();
        ctx.moveTo(width - margin, height - margin);
        ctx.lineTo(width - margin, margin);
        ctx.strokeStyle = 'black';
        ctx.stroke();

        // Draw Y axis
        ctx.beginPath();
        ctx.moveTo(margin, height - margin);
        ctx.lineTo(width - margin, height - margin);
        ctx.strokeStyle = 'black';
        ctx.stroke();

        // Draw the graph data
        ctx.beginPath();
        for (let i = 0; i < chartData.x.length; i++) {
            let x = margin + (chartData.x[i] / maxX) * (width - 2 * margin);
            let y = height - margin - (chartData.y[i] / maxY) * (height - 2 * margin);
            ctx.lineTo(x, y);
        }
        ctx.strokeStyle = 'blue';
        ctx.stroke();
    }

    // Run the PID loop
    function runPID() {
        let currentValue = systemState;

        // Calculate error
        let error = target - currentValue;
        integral += error * timeStep;
        let derivative = (error - previousError) / timeStep;

        // Calculate PID output
        let output = Kp * error + Ki * integral + Kd * derivative;

        // Update system state (simulating control response)
        systemState += output * timeStep;

        // Save data for graph
        chartData.x.push(chartData.x.length);
        chartData.y.push(systemState);

        // Ensure we do not exceed maxX
        if (chartData.x.length > maxX) {
            stopPIDControl(); // Stop PID control once max X is reached
            return;
        }

        // Update the display of current value
        document.getElementById('currentValue').innerText = `Current Value: ${systemState.toFixed(2)}`;

        // Draw the updated graph
        drawGraph();

        // Update the previous error
        previousError = error;

        if (isRunning) {
            animationFrameId = requestAnimationFrame(runPID);
        }
    }

    // Start PID control
    function startPIDControl() {
        isRunning = true;
        document.getElementById('startBtn').disabled = true;
        document.getElementById('stopBtn').disabled = false;
        document.getElementById('resetBtn').disabled = false;
        runPID();
    }

    // Stop PID control
    function stopPIDControl() {
        isRunning = false;
        cancelAnimationFrame(animationFrameId);
        document.getElementById('startBtn').disabled = false;
        document.getElementById('stopBtn').disabled = true;
        document.getElementById('resetBtn').disabled = false;
    }
</script>

</body>
</html>
如果您觉得本博客内容有所帮助,可以打赏一下吗~,此打赏将会用于博客维护运营,十分感谢!
作者:Clif
版权声明: 本博客所有文章除未特别声明外,均采用CC BY-NC-SA 4.0协议。转载请注明文章地址及作者!
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇