从智能灯带到迷你气象站:手把手教你用ESP32的PWM、ADC和I2C玩转三个小项目
当你第一次拿到ESP32开发板时,面对密密麻麻的引脚,是否感到无从下手?别担心,今天我们就通过三个有趣的小项目,带你深入理解ESP32的PWM、ADC和I2C功能。这些项目不仅实用,还能让你在动手实践中掌握核心概念,为后续更复杂的物联网开发打下坚实基础。
1. 炫彩灯带:PWM调光实战
RGB LED灯带是展示PWM功能的绝佳选择。ESP32的PWM(脉冲宽度调制)功能可以精确控制LED的亮度和颜色渐变效果。我们选择GPIO16、GPIO17和GPIO18分别控制红、绿、蓝三色LED,因为这些引脚都支持PWM输出,且位于开发板边缘,方便接线。
1.1 硬件准备
你需要准备以下材料:
- ESP32开发板
- WS2812B RGB LED灯带(30颗/米)
- 470Ω电阻(保护GPIO引脚)
- 杜邦线若干
- 5V电源适配器(为灯带供电)
接线示意图:
ESP32 GPIO16 → 灯带DIN ESP32 GND → 灯带GND 5V电源+ → 灯带5V 5V电源- → 共地1.2 代码实现
首先安装FastLED库,这是控制WS2812B灯带的最佳选择。在Arduino IDE中,通过库管理器搜索并安装FastLED。
#include <FastLED.h> #define LED_PIN 16 #define NUM_LEDS 30 CRGB leds[NUM_LEDS]; void setup() { FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS); FastLED.setBrightness(50); // 初始亮度设为50% } void loop() { // 彩虹渐变效果 for(int hue=0; hue<255; hue++) { fill_rainbow(leds, NUM_LEDS, hue, 7); FastLED.show(); delay(30); } }这段代码实现了灯带的彩虹渐变效果。FastLED.setBrightness()函数就是通过PWM调节整体亮度的典型应用。ESP32的PWM分辨率可达16位,这意味着你可以实现极其平滑的亮度过渡。
提示:如果灯带出现闪烁或颜色异常,检查电源是否充足。长灯带需要单独供电,避免通过ESP32供电。
2. 智能监测:ADC读取环境数据
ADC(模数转换)是物联网设备感知环境的关键。我们将使用ESP32的ADC引脚读取土壤湿度传感器的模拟信号,构建一个简易的植物监测系统。选择GPIO36(VP)作为输入引脚,因为它是专为ADC设计的超低噪声引脚。
2.1 传感器选择与连接
推荐使用YL-69土壤湿度传感器,它价格便宜且易于使用。接线方式如下:
传感器VCC → ESP32 3.3V 传感器GND → ESP32 GND 传感器AO → ESP32 GPIO362.2 校准与代码
土壤湿度传感器的读数会因土壤类型不同而变化,因此需要先进行校准:
- 将传感器完全干燥时读取的值记为
dryValue - 将传感器完全浸入水中时读取的值记为
wetValue - 实际湿度百分比 =
(rawValue - dryValue) / (wetValue - dryValue) * 100
const int sensorPin = 36; // GPIO36 int dryValue = 2500; // 根据实际测量调整 int wetValue = 1000; // 根据实际测量调整 void setup() { Serial.begin(115200); analogReadResolution(12); // 使用12位ADC分辨率 } void loop() { int rawValue = analogRead(sensorPin); int moisture = map(rawValue, dryValue, wetValue, 0, 100); Serial.print("Raw: "); Serial.print(rawValue); Serial.print(" | Moisture: "); Serial.print(moisture); Serial.println("%"); delay(1000); }ESP32的ADC默认范围为0-3.3V,12位分辨率(0-4095)。analogReadResolution()函数可以调整分辨率,但注意更高的分辨率需要更长的采样时间。
2.3 数据可视化
将数据上传到ThingSpeak等物联网平台,可以创建实时监测图表。以下是发送数据到ThingSpeak的示例代码片段:
#include <WiFi.h> #include <HTTPClient.h> const char* ssid = "your_SSID"; const char* password = "your_PASSWORD"; const char* server = "api.thingspeak.com"; String apiKey = "YOUR_API_KEY"; void sendToThingSpeak(int value) { HTTPClient http; String url = "http://" + String(server) + "/update?api_key=" + apiKey + "&field1=" + String(value); http.begin(url); int httpCode = http.GET(); http.end(); }3. 迷你气象站:I2C连接环境传感器
I2C协议让我们可以用两根线(SDA和SCL)连接多个传感器。我们将使用BME280传感器(温湿度、气压)和SSD1306 OLED显示屏,构建一个完整的迷你气象站。
3.1 硬件连接
ESP32的默认I2C引脚是:
- GPIO21 (SDA)
- GPIO22 (SCL)
接线如下:
BME280 VCC → 3.3V BME280 GND → GND BME280 SDA → GPIO21 BME280 SCL → GPIO22 OLED VCC → 3.3V OLED GND → GND OLED SDA → GPIO21 OLED SCL → GPIO223.2 库安装与初始化
需要安装以下库:
- Adafruit_BME280(环境传感器)
- Adafruit_SSD1306(OLED显示)
- Adafruit_GFX(图形支持)
#include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <Adafruit_Sensor.h> #include <Adafruit_BME280.h> #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 #define OLED_RESET -1 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET); Adafruit_BME280 bme; void setup() { Serial.begin(115200); if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println("OLED分配失败"); while(1); } if(!bme.begin(0x76)) { Serial.println("找不到BME280传感器"); while(1); } display.clearDisplay(); display.setTextSize(1); display.setTextColor(WHITE); }3.3 数据显示与刷新
在loop函数中读取传感器数据并显示:
void loop() { float temp = bme.readTemperature(); float humidity = bme.readHumidity(); float pressure = bme.readPressure() / 100.0F; display.clearDisplay(); display.setCursor(0,0); display.print("温度: "); display.print(temp); display.println(" C"); display.print("湿度: "); display.print(humidity); display.println(" %"); display.print("气压: "); display.print(pressure); display.println(" hPa"); display.display(); delay(5000); // 每5秒更新一次 }I2C总线允许多个设备共享同一组引脚,只需确保每个设备有唯一的地址。BME280默认地址是0x76或0x77,SSD1306通常是0x3C。
注意:如果同时连接多个I2C设备出现通信问题,尝试降低I2C时钟速度:
Wire.setClock(100000);// 100kHz
4. 项目集成与优化
现在,我们将三个项目整合为一个完整的智能环境监测系统:灯带根据环境数据改变颜色,OLED显示实时数据,传感器数据同时上传到云端。
4.1 系统架构
完整的系统包含:
- 环境感知层(BME280传感器)
- 数据处理层(ESP32)
- 显示层(OLED)
- 反馈层(RGB灯带)
- 云端层(ThingSpeak)
4.2 综合代码实现
// 包含所有必要的库 #include <FastLED.h> #include <Wire.h> #include <Adafruit_GFX.h> #include <Adafruit_SSD1306.h> #include <Adafruit_BME280.h> #include <WiFi.h> #include <HTTPClient.h> // 灯带配置 #define LED_PIN 16 #define NUM_LEDS 30 CRGB leds[NUM_LEDS]; // OLED配置 #define SCREEN_WIDTH 128 #define SCREEN_HEIGHT 64 Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1); // BME280配置 Adafruit_BME280 bme; // WiFi配置 const char* ssid = "your_SSID"; const char* password = "your_PASSWORD"; const char* server = "api.thingspeak.com"; String apiKey = "YOUR_API_KEY"; void setup() { Serial.begin(115200); // 初始化灯带 FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS); FastLED.setBrightness(50); // 初始化OLED if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) { Serial.println("OLED分配失败"); while(1); } // 初始化BME280 if(!bme.begin(0x76)) { Serial.println("找不到BME280传感器"); while(1); } // 连接WiFi WiFi.begin(ssid, password); while(WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("WiFi连接成功"); } void loop() { // 读取传感器数据 float temp = bme.readTemperature(); float humidity = bme.readHumidity(); // 根据温度改变灯带颜色 updateLEDs(temp); // 在OLED上显示数据 displayData(temp, humidity); // 上传数据到ThingSpeak sendToCloud(temp, humidity); delay(30000); // 每30秒更新一次 } void updateLEDs(float temperature) { // 温度低于20°C显示蓝色,20-25°C绿色,高于25°C红色 if(temperature < 20) { fill_solid(leds, NUM_LEDS, CRGB::Blue); } else if(temperature >= 20 && temperature <= 25) { fill_solid(leds, NUM_LEDS, CRGB::Green); } else { fill_solid(leds, NUM_LEDS, CRGB::Red); } FastLED.show(); } void displayData(float temp, float humidity) { display.clearDisplay(); display.setCursor(0,0); display.print("环境监测系统"); display.setCursor(0,20); display.print("温度: "); display.print(temp); display.println(" C"); display.print("湿度: "); display.print(humidity); display.println(" %"); display.display(); } void sendToCloud(float temp, float humidity) { if(WiFi.status() == WL_CONNECTED) { HTTPClient http; String url = "http://" + String(server) + "/update?api_key=" + apiKey; url += "&field1=" + String(temp); url += "&field2=" + String(humidity); http.begin(url); int httpCode = http.GET(); http.end(); } }4.3 电源管理与优化
为了降低功耗,可以考虑以下优化:
- 使用深度睡眠模式,间隔唤醒采集数据
- 降低CPU频率
- 关闭未使用的外设
- 使用电池供电时,考虑添加低电量检测
以下是深度睡眠的示例代码片段:
#define uS_TO_S_FACTOR 1000000 // 微秒到秒的转换因子 #define TIME_TO_SLEEP 300 // 休眠时间(秒) void setup() { // ...其他初始化代码... // 配置唤醒源 esp_sleep_enable_timer_wakeup(TIME_TO_SLEEP * uS_TO_S_FACTOR); // 执行一次数据采集和上传 readAndSendData(); // 进入深度睡眠 esp_deep_sleep_start(); } void loop() { // 不会执行到这里,因为每次唤醒后都会重新开始setup() }通过这三个项目的实践,你不仅掌握了ESP32的PWM、ADC和I2C功能,还构建了一个完整的物联网监测系统。在实际部署时,可以根据需要调整采样频率、显示内容和报警阈值,打造真正符合个人需求的智能设备。