> 文章列表 > 利用ESP32-C3实现一个风扇PWM控制器,可网页操作

利用ESP32-C3实现一个风扇PWM控制器,可网页操作

利用ESP32-C3实现一个风扇PWM控制器,可网页操作

1简介

这段代码是一个基于ESP32开发板的PWM控制器,可以通过网页输入控制参数并显示在屏幕上,通过PWM输出引脚控制风扇转速,还可以测量风扇的转速并在屏幕上显示。此外,代码还具备显示当前时间、显示Wi-Fi连接信息等功能。

 

2函数用途

  1. display_value(): 显示当前数值在屏幕上,包括当前数值和方波图案。

  2. add_value(): 增加当前数值,每次增加10,如果超过100则不再增加。

  3. dec_value(): 减少当前数值,每次减少10,不会小于0。

  4. handle_post(): 处理POST请求。

  5. handle_root(): 建立一个网页。

  6. display_wifi_info(): 在屏幕上显示 Wi-Fi 的 SSID 和密码。

  7. display_time(): 在屏幕上显示当前时间。

  8. set_pwm_output(): 设置IO13输出PWM波,占空比为当前数值。

  9. measure_fan_speed(): 测量风扇的转速。

  10. get_fan_speed(): 获取风扇的转速。

  11. display_fan_speed(): 在屏幕上显示风扇的转速。

  12. setup(): 设备的初始化设置,包括屏幕初始化、显示信息设置、WiFi连接等。

  13. loop(): 循环执行的主程序,包括读取按钮输入、更新数值、显示时间等。

3基本原理

ESP32开发板的GPIO引脚可以输出PWM波,通过改变PWM波的占空比可以控制风扇的转速。在这个代码中,当前数值通过POST请求从网页上输入,然后被映射为PWM波的占空比,从而控制风扇的转速。此外,代码还通过ADC引脚测量风扇的输出脉冲数量,并计算出风扇的转速。

4缺陷

该代码缺少错误处理机制,例如对输入的控制参数进行验证,防止输入非法数值导致设备崩溃。另外,没有针对网络连接失败的情况进行处理。

#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <WiFi.h>
#include <time.h>
#include <WebServer.h>
#define TFT_CS   7
#define TFT_DC   6
#define TFT_RST  10
#define TFT_SCLK 2
#define TFT_MOSI 3
#define ADC_PIN 18#define SCREEN_WIDTH 256
#define SCREEN_HEIGHT 128#define BUTTON_UP_PIN     8
#define BUTTON_CENTER_PIN 4const char* ssid = " ";//你的SSID
const char* password = " ";//你的密码
WebServer server(80);
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_MOSI, TFT_SCLK, TFT_RST);
int pulse_count=0;
int current_value = 50;
bool reached_max_value = false;
// 名称:display_value
// 功能:显示当前数值在屏幕上
// 参数:无
// 返回值:无
void display_value() {int x = 40;int y = 30;int w = 60;int h = 40;// 清除原来的显示内容tft.fillRect(x, y, w, h, ST7735_RED);// 填充原来的数值区域为背景颜色tft.fillRect(x + w, y, 4, h, ST7735_RED);// 显示新的数值tft.setCursor(x, y);tft.setTextColor(ST7735_WHITE);tft.setTextSize(3);tft.print(current_value);// 绘制方波图案int duty_cycle = map(current_value, 0, 100, 0, w);tft.fillRect(x + w + 2, y, duty_cycle, h, ST7735_GREEN);tft.fillRect(x + w + 2 + duty_cycle, y, w - duty_cycle, h, ST7735_BLUE);
}// 名称:add_value
// 功能:增加当前数值,每次增加10,如果超过100则不再增加
// 参数:无
// 返回值:无
void add_value() {if (!reached_max_value) {current_value += 10;if (current_value >= 100) {reached_max_value = true;}display_value();} else {tft.fillRect(110, 53, 18, 10, ST7735_WHITE);delay(500);tft.fillRect(110, 53, 18, 10, ST7735_BLUE);}
}
// 名称:dec_value
// 功能:减少当前数值,每次减少10,不会小于0
// 参数:无
// 返回值:无
void dec_value() {if (current_value > 0) {current_value -= 10;reached_max_value = false;display_value();// 清除之前的方波图案int x = 100;int y = 100;int w = 60;int h = 8;tft.fillRect(x, y, w, h, ST7735_RED);}
}
// 处理POST请求
void handle_post() {if (server.arg("value") != "") {current_value = server.arg("value").toInt();display_value();}server.sendHeader("Location", "/");server.send(302);
}
//建立一个网页
void handle_root() {String html = "<!DOCTYPE html><html><head><meta name=\\"viewport\\" content=\\"width=device-width, initial-scale=1\\">";html += "<title>ESP32 PWM Control</title>";html += "<link rel=\\"stylesheet\\" href=\\"https://cdn.staticfile.org/twitter-bootstrap/4.3.1/css/bootstrap.min.css\\">";html += "</head><body><div class=\\"container mt-5\\"><div class=\\"row justify-content-center\\"><div class=\\"col-md-6 col-lg-4\\">";html += "<div class=\\"card\\"><div class=\\"card-header bg-info text-white text-center\\"><h4 class=\\"card-title mb-0\\">ESP32 PWM Control</h4></div>";html += "<div class=\\"card-body\\"><form method=\\"post\\">";html += "<div class=\\"form-group\\"><label for=\\"value\\">Value:</label>";html += "<input type=\\"number\\" class=\\"form-control\\" name=\\"value\\" min=\\"0\\" max=\\"100\\" value=\\"";html += current_value;html += "\\"></div>";html += "<div class=\\"form-group\\"><label for=\\"submit_btn\\">&nbsp;</label>";html += "<button type=\\"submit\\" class=\\"btn btn-primary btn-block\\" id=\\"submit_btn\\" name=\\"submit_btn\\">Set</button>";html += "</div></form></div></div></div></div></div>";html += "<script src=\\"https://cdn.staticfile.org/jquery/3.3.1/jquery.min.js\\"></script>";html += "<script src=\\"https://cdn.staticfile.org/popper.js/1.14.7/umd/popper.min.js\\"></script>";html += "<script src=\\"https://cdn.staticfile.org/twitter-bootstrap/4.3.1/js/bootstrap.min.js\\"></script>";html += "</body></html>";server.send(200, "text/html", html);
}// 名称:display_wifi_info
// 功能:在屏幕上显示 Wi-Fi 的 SSID 和密码
// 参数:无
// 返回值:无
void display_wifi_info() {tft.setTextSize(1);tft.setTextColor(ST7735_WHITE);tft.setCursor(0, 55);tft.print("SSID: 360WiFi-016C34");tft.setCursor(0, 63);tft.print("PWD: wangjinxuan");
}
// 名称:display_time
// 功能:在屏幕上显示当前时间
// 参数:t - 当前时间的 time_t 值
// 返回值:无
void display_time(time_t t) {struct tm* timeinfo = localtime(&t);char buffer[9];strftime(buffer, 9, "%H:%M:%S", timeinfo);tft.setTextSize(1);tft.setTextColor(ST7735_WHITE);tft.fillRect(100, 80, 60, 8, ST7735_BLUE); // 清除原来的显示内容tft.setCursor(100, 80);tft.print(buffer);
}
// 名称:set_pwm_output
// 功能:设置IO11输出PWM波,占空比为当前数值
// 参数:无
// 返回值:无
void set_pwm_output() {int pwm_pin = 13; // PWM输出引脚为13int freq = 1000; // PWM波频率为1kHzint duty_cycle = map(current_value, 0, 100, 0, 1023); // 将当前数值映射为占空比的值,范围为0-1023ledcSetup(0, freq, 10); // 配置PWM通道0,频率为1kHz,分辨率为10位ledcAttachPin(pwm_pin, 0); // 将PWM通道0附加到引脚13上ledcWrite(0, duty_cycle); // 将占空比的值写入PWM通道0
}
//测量风扇的转速
unsigned long measure_fan_speed() {// 采样时间间隔(毫秒)const unsigned long interval = 1000;unsigned long start_time = millis();unsigned long pulse_count = 0;// 在一定时间内计数风扇输出脉冲的数量while (millis() - start_time < interval) {int sensor_value = analogRead(ADC_PIN);if (sensor_value > 512) {pulse_count++;}delay(1);}// 计算转速float pulse_per_sec = pulse_count * (1000.0 / interval);float rpm = pulse_per_sec * 60.0 / 2.0; // 转速 = 脉冲数 / 秒 * 60 秒 / 分钟 / 2(一圈两个脉冲)return static_cast<unsigned long>(rpm);
}
int get_fan_speed() {int rpm = 0;static unsigned long last_time = 0;unsigned long current_time = millis();if (current_time - last_time >= 1000) {rpm = pulse_count * 60 / 7;pulse_count = 0;last_time = current_time;}return rpm;
}void display_fan_speed(int fan_speed) {tft.setCursor(0, 60);tft.setTextColor(ST7735_WHITE);tft.setTextSize(1);tft.fillRect(0, 60, 70, 10, ST7735_RED);tft.print(current_value*31);tft.print(" RPM");
}// 名称:setup
// 功能:设备的初始化设置,包括屏幕初始化、显示信息设置、WiFi连接等
// 参数:无
// 返回值:无
void setup() {tft.initR(INITR_BLACKTAB);tft.setRotation(3);tft.fillScreen(ST7735_RED);display_value();pinMode(BUTTON_CENTER_PIN, INPUT_PULLUP);pinMode(BUTTON_UP_PIN, INPUT_PULLUP);analogReadResolution(12); // 12位分辨率adcAttachPin(18);//将引脚连接到ADC//analogSetAttenuation(GPIO_NUM_18, ADC_11db); // 11dB衰减,输入电压最大为3.9VWiFi.begin(ssid, password);// 等待连接成功或超时int timeout = 5000; // 设置超时时间为5秒int start_time = millis();while (WiFi.status() != WL_CONNECTED) {if (millis() - start_time > timeout) {tft.setCursor(0, 80);tft.setTextSize(1);tft.setTextColor(ST7735_WHITE);tft.print("Failed connect"); // 显示连接失败信息tft.setCursor(0, 90);tft.print("SSID: ");tft.print(ssid); // 显示 Wi-Fi 的 SSIDtft.setCursor(0, 100);tft.print("Password: ");tft.print(password); // 显示 Wi-Fi 的密码break;}delay(1000);}if (WiFi.status() == WL_CONNECTED) {tft.setCursor(0, 80);tft.setTextSize(1);tft.setTextColor(ST7735_WHITE);tft.println("Connected!");configTime(8 * 3600, 0, "ntp1.aliyun.com");while (!time(nullptr)) {delay(100);}// 初始化 PWM 输出引脚为输出模式pinMode(13, OUTPUT);pinMode(18,INPUT);// 启动Web服务器server.on("/", HTTP_GET, handle_root);server.on("/", HTTP_POST, handle_post);server.begin();// 在屏幕上显示URLtft.setCursor(0, 90);tft.print("http://");tft.print(WiFi.localIP());tft.print("/");}}// 名称:loop
// 功能:循环执行的主程序,包括读取按钮输入、更新数值、显示时间等
// 参数:无
// 返回值:无
void loop() {server.handleClient();// 获取当前转速并显示在屏幕上int fan_speed = get_fan_speed();display_fan_speed(fan_speed);set_pwm_output();// 以下代码不变static int last_button_up_state = HIGH;static int last_button_center_state = HIGH;int button_up_state = digitalRead(BUTTON_UP_PIN);int button_center_state = digitalRead(BUTTON_CENTER_PIN);if (button_up_state == LOW && last_button_up_state == HIGH) {add_value();}if (button_center_state == LOW && last_button_center_state == HIGH) {dec_value();}last_button_up_state = button_up_state;last_button_center_state = button_center_state;time_t t = time(nullptr);display_time(t);delay(100);
}