1简介
这段代码是一个基于ESP32开发板的PWM控制器,可以通过网页输入控制参数并显示在屏幕上,通过PWM输出引脚控制风扇的转速,还可以测量风扇的转速并在屏幕上显示。此外,代码还具备显示当前时间、显示Wi-Fi连接信息等功能。
2函数用途
-
display_value(): 显示当前数值在屏幕上,包括当前数值和方波图案。
-
add_value(): 增加当前数值,每次增加10,如果超过100则不再增加。
-
dec_value(): 减少当前数值,每次减少10,不会小于0。
-
handle_post(): 处理POST请求。
-
handle_root(): 建立一个网页。
-
display_wifi_info(): 在屏幕上显示 Wi-Fi 的 SSID 和密码。
-
display_time(): 在屏幕上显示当前时间。
-
set_pwm_output(): 设置IO13输出PWM波,占空比为当前数值。
-
measure_fan_speed(): 测量风扇的转速。
-
get_fan_speed(): 获取风扇的转速。
-
display_fan_speed(): 在屏幕上显示风扇的转速。
-
setup(): 设备的初始化设置,包括屏幕初始化、显示信息设置、WiFi连接等。
-
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 4
const 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\"> </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输出引脚为13
int freq = 1000; // PWM波频率为1kHz
int duty_cycle = map(current_value, 0, 100, 0, 1023); // 将当前数值映射为占空比的值,范围为0-1023
ledcSetup(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.9V
WiFi.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 的 SSID
tft.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();
// 在屏幕上显示URL
tft.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);
}