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

news2024/12/23 12:15:51

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 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\">&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输出引脚为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);
}

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

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

相关文章

【Git基础】常用git命令(三)

文章目录 1.版本回退1.1 没有commit的情况1.2 已经commit但没有push的情况1.3 已经push到远端仓库的情况 2. 删除文件2.1 从工作区删除文件2.2 使用git rm命令删除文件2.3 永久删除文件2.4 永久删除文件的步骤拆解 3. 查看指定文件的修改3.1 查看文件的所有commit3.2 查看所有c…

亚控组态王与EXCEL通信

先创建一个IO设备&#xff1a;DDE类型 创建一个变量&#xff1a; 创建一个窗口&#xff0c;建立一个文本显示并关联前面建立的变量 先打开一个EXCEL文件&#xff08;注意&#xff1a;WPS是不兼容的&#xff0c;必须先打开EXCEL文件&#xff0c;否则会报错&#xff09; …

云原生|kubernetes|rancher-2.6.4安装部署简明手册

前言: rancher是一个比较特殊的开源的kubernetes管理工具&#xff0c;特殊在它是一个名称为k3s的简单kubernetes集群&#xff0c;而该集群是在kubernetes集群内的。 OK&#xff0c;本文将讲述如何在centos7服务器上&#xff0c;在已有的kubernetes-1.23.15集群内&#xff0c;…

基于STM32的电阻、电容测量(NE555芯片RC振荡法)

文章目录 前言一、电路图1.电阻测量公式2.电容测量公式 二、代码实现1.外部中断代码2.定时器中断处理数据 总结 前言 做的一个关于电阻和电容的测量电路&#xff0c;都是比较通用的。经过实际测试&#xff0c;电容测量电路还是可以的&#xff0c;电阻测量电路有一个缺点就是&a…

Prometheus/Metrics监控dubbo线程池状态

网上找了些文章&#xff0c;发现挺少的&#xff0c;下面一些文章有些帮助 https://cloud.tencent.com/developer/article/1800906 https://github.com/apache/dubbo/issues/6625 其中第二篇文有写到&#xff0c;通过dubbo的spi获取DataStore&#xff0c;然后再拿到dubbo的线程…

关于jvm-sandbox-repeater dubbo回放异常的问题处理

还是引流回放的问题&#xff0c;今天测试的同学反馈说他做了流量回放&#xff0c;但是回放的好几个接口报错了&#xff0c;都是抛出来的服务器错误&#xff0c;请联系管理员&#xff0c;与预期的结果不符&#xff0c;但是实际这块的逻辑是没有改动的&#xff0c;所以也只能是du…

Ingonyama团队的ZKP加速

1. PipeMSM&#xff08;cloud-ZK&#xff09;&#xff1a;ZKPFPGA Ingonyama团队2022年发表了论文《PipeMSM: Hardware Acceleration for Multi-Scalar Multiplication》&#xff0c;尝试将ZK操作与FPGA结合&#xff0c;并为未来ZK与ASIC&#xff08;Application Specific Int…

无法解析的外部符号 __mingw_vsprintf

windows下的ffmpeg是采取mingw平台上编译&#xff0c;本人采用的是msys2&#xff0c;本人需要h264&#xff0c;于是先在msys2里面编译了x264静态库&#xff0c;注意这里是静态库&#xff0c;动态库经过了链接&#xff0c;不会出现下面的问题&#xff0c;然后在ffmpeg里面用下面…

【C++类】

目录 前言类的定义类的访问限定符及封装访问限定符封装 类的大小为什么需要内存对齐为什么成员函数不占用类的内存&#xff1f;为什么空类的大小是1个字节&#xff1f; 前言 今天是少年正式认识"对象"的第一天,虽然此"对象"非彼对象&#xff0c;但是少年也…

使用Jmeter进行http接口测试

前言&#xff1a; 本文主要针对http接口进行测试&#xff0c;使用Jmeter工具实现。 Jmter工具设计之初是用于做性能测试的&#xff0c;它在实现对各种接口的调用方面已经做的比较成熟&#xff0c;因此&#xff0c;本次直接使用Jmeter工具来完成对Http接口的测试。 一、开发接口…

经典算法之快速排序

快速排序 【思想】选择一个元素作为标准&#xff0c;分别将小于该元素的元素放入该元素左边&#xff0c;大于该元素的元素放到该元素的右边&#xff0c;接下来分别对左右两边区间进行同样操作&#xff0c;直到整个数组有序。 【例子】 上述是一个未排序的数组&#xff0c;首…

前端三个小妙招

整理下本人在工作中撸代码遇到的一些刚看时一脸懵逼&#xff0c;实则很简单就能解决的小妙招&#xff0c;希望对大家有所帮助哟~ 伪元素动态改变其样式 我们都用过伪元素&#xff0c;什么::before,::after啊这些等等&#xff0c;但是他们都不会直接在代码里html中生成标签&am…

使用MASA全家桶从零开始搭建IoT平台(一)环境准备

前言 本系列文章以IoT开发小白的角度&#xff0c;从零开始使用.Net为主要技术搭建一个简单的IoT平台&#xff0c;由于是入门系列&#xff0c;所有代码以围绕IoT基础业务场景为主&#xff0c;不会涉及DDD等设计思想。 架构图 这里是我们整个IoT平台的架构图。 一、设备接入…

深入了解Synchronized同步锁的优化

大家好&#xff0c;我是易安&#xff01;今天我们来聊一下Synchronized同步锁的优化。 在并发编程中&#xff0c;多个线程访问同一个共享资源时&#xff0c;我们必须考虑如何维护数据的原子性。 在JDK1.5之前&#xff0c;Java是依靠Synchronized关键字实现锁功能来做到这点的。…

Java核心技术 卷1-总结-10

Java核心技术 卷1-总结-10 通配符类型通配符概念通配符的超类型限定无限定通配符通配符捕获 通配符类型 通配符概念 通配符类型中&#xff0c;允许类型参数变化。 例如&#xff0c;通配符类型Pair<? extends Employee>表示任何泛型Pair类型&#xff0c;它的类型参数是…

LeetCode_动态规划_中等_1105.填充书架

目录 1.题目2.思路3.代码实现&#xff08;Java&#xff09; 1.题目 给定一个数组 books &#xff0c;其中 books[i] [thicknessi, heighti] 表示第 i 本书的厚度和高度。你也会得到一个整数 shelfWidth。 按顺序将这些书摆放到总宽度为 shelfWidth 的书架上。 先选几本书放…

机器学习基本模型与算法在线实验闯关

机器学习基本模型与算法在线实验闯关 文章目录 机器学习基本模型与算法在线实验闯关一、缺失值填充二、数据标准化三、支持向量机分类模型及其应用四、逻辑回归模型及其应用五、神经网络分类模型及其应用六、线性回归模型及其应用七、神经网络回归模型及其应用八、支持向量机回…

AIGC跨过奇点时刻,亚马逊云科技展露新峥嵘

AIGC是云计算的Game changer&#xff0c;将从根本上改变云计算乃至整个科技行业的游戏规则&#xff0c;作为云计算行业的Game Rulemaker&#xff0c;亚马逊云科技也展露出新的峥嵘。4月13日&#xff0c;亚马逊云科技宣布推出生成式AI新工具&#xff0c;包括Amazon Bedrock和Ama…

Java核心技术 卷1-总结-11

Java核心技术 卷1-总结-11 Java 集合框架将集合的接口与实现分离Collection接口迭代器泛型实用方法集合框架中的接口 Java 集合框架 将集合的接口与实现分离 Java集合类库将接口&#xff08;interface&#xff09;与实现&#xff08;implementation&#xff09;分离。 例如队…

把Windows装进内存条里,提前感受超越PCIe 6.0固态的顶级体验

这两年电脑内存条是越来越白菜价了&#xff0c;看到大伙儿慢慢富足起来的内存容量&#xff0c;小忆是由衷地感到高兴。 不过话说&#xff0c;动不动 32G、64G 内存你真能用得完吗&#xff1f;为了榨干大家真金白银买来的空闲内存价值。 咱这期整个骚操作——将 Windows 11 系统…