项目实战
本章将通过几个完整的实战项目,综合运用前面学到的所有知识。每个项目都包含硬件连接、代码实现和扩展思路。
项目一:智能温控风扇
根据环境温度自动调节风扇转速,实现舒适节能。
功能需求
- 实时显示当前温度
- 温度超过阈值时启动风扇
- 温度越高,风扇转速越快
- 可手动调节温度阈值
- 配置断电保存
硬件清单
| 器件 | 数量 | 说明 |
|---|---|---|
| Arduino Uno | 1 | 主控板 |
| DHT22 温湿度传感器 | 1 | 检测温度 |
| LCD1602 I2C | 1 | 显示信息 |
| 直流电机 + L298N | 1 | 风扇驱动 |
| 电位器 | 1 | 调节阈值 |
| 按钮开关 | 1 | 模式切换 |
硬件连接
DHT22:
VCC → 5V
DATA → D2
GND → GND
LCD1602 I2C:
VCC → 5V
GND → GND
SCL → A5
SDA → A4
L298N:
ENA → D9 (PWM)
IN1 → D8
IN2 → D7
GND → GND (与 Arduino 共地)
+12V → 外部电源
电位器:
两端 → 5V 和 GND
中间 → A0
按钮:
一端 → D3 (内部上拉)
另一端 → GND
完整代码
#include <DHT.h>
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
// 引脚定义
#define DHT_PIN 2
#define DHT_TYPE DHT22
#define LCD_ADDR 0x27
#define FAN_PWM_PIN 9
#define FAN_DIR1_PIN 8
#define FAN_DIR2_PIN 7
#define POT_PIN A0
#define BUTTON_PIN 3
// 设备对象
DHT dht(DHT_PIN, DHT_TYPE);
LiquidCrystal_I2C lcd(LCD_ADDR, 16, 2);
// 配置结构体
struct Config {
float tempThreshold; // 温度阈值
float hysteresis; // 滞回值
int minFanSpeed; // 最小风扇转速
int maxFanSpeed; // 最大风扇转速
bool autoMode; // 自动模式
};
const int CONFIG_ADDR = 0;
Config config;
// 状态变量
float currentTemp = 0;
float currentHumidity = 0;
int currentFanSpeed = 0;
bool lastButtonState = HIGH;
unsigned long lastDisplayUpdate = 0;
void setup() {
// 初始化引脚
pinMode(FAN_PWM_PIN, OUTPUT);
pinMode(FAN_DIR1_PIN, OUTPUT);
pinMode(FAN_DIR2_PIN, OUTPUT);
pinMode(BUTTON_PIN, INPUT_PULLUP);
// 初始化传感器
dht.begin();
// 初始化显示
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Smart Fan v1.0");
delay(1000);
lcd.clear();
// 加载配置
loadConfig();
// 设置风扇方向
digitalWrite(FAN_DIR1_PIN, HIGH);
digitalWrite(FAN_DIR2_PIN, LOW);
Serial.begin(9600);
Serial.println("智能温控风扇已启动");
}
void loop() {
// 读取传感器
readSensors();
// 处理按钮
handleButton();
// 处理电位器(手动模式)
handlePotentiometer();
// 控制风扇
controlFan();
// 更新显示
updateDisplay();
}
void readSensors() {
static unsigned long lastRead = 0;
// DHT22 需要至少 2 秒间隔
if (millis() - lastRead > 2000) {
float temp = dht.readTemperature();
float humidity = dht.readHumidity();
if (!isnan(temp) && !isnan(humidity)) {
currentTemp = temp;
currentHumidity = humidity;
}
lastRead = millis();
}
}
void handleButton() {
bool buttonState = digitalRead(BUTTON_PIN);
// 检测下降沿(按钮按下)
if (buttonState == LOW && lastButtonState == HIGH) {
config.autoMode = !config.autoMode;
saveConfig();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(config.autoMode ? "自动模式" : "手动模式");
delay(500);
lcd.clear();
}
lastButtonState = buttonState;
}
void handlePotentiometer() {
if (!config.autoMode) {
// 手动模式:用电位器控制风扇速度
int potValue = analogRead(POT_PIN);
currentFanSpeed = map(potValue, 0, 1023, 0, 255);
}
}
void controlFan() {
if (config.autoMode) {
// 自动模式:根据温度控制风扇
if (currentTemp >= config.tempThreshold) {
// 温度超过阈值,计算风扇速度
float tempDiff = currentTemp - config.tempThreshold;
float speedRatio = tempDiff / 5.0; // 每超过 5°C 达到最大速度
speedRatio = constrain(speedRatio, 0, 1);
currentFanSpeed = config.minFanSpeed +
(config.maxFanSpeed - config.minFanSpeed) * speedRatio;
} else if (currentTemp < config.tempThreshold - config.hysteresis) {
// 温度低于阈值减去滞回值,关闭风扇
currentFanSpeed = 0;
}
}
// 应用风扇速度
analogWrite(FAN_PWM_PIN, currentFanSpeed);
}
void updateDisplay() {
// 每 500ms 更新一次显示
if (millis() - lastDisplayUpdate > 500) {
lcd.setCursor(0, 0);
// 显示温度
lcd.print("T:");
lcd.print(currentTemp, 1);
lcd.print("C ");
// 显示湿度
lcd.print("H:");
lcd.print(currentHumidity, 0);
lcd.print("%");
lcd.setCursor(0, 1);
// 显示风扇状态
lcd.print("Fan:");
int speedPercent = map(currentFanSpeed, 0, 255, 0, 100);
lcd.print(speedPercent);
lcd.print("% ");
// 显示模式
lcd.print(config.autoMode ? "AUTO" : "MAN");
lastDisplayUpdate = millis();
}
}
void loadConfig() {
EEPROM.get(CONFIG_ADDR, config);
// 检查配置是否有效
if (config.tempThreshold < 10 || config.tempThreshold > 40) {
// 无效配置,使用默认值
config.tempThreshold = 25.0;
config.hysteresis = 2.0;
config.minFanSpeed = 80;
config.maxFanSpeed = 255;
config.autoMode = true;
saveConfig();
}
}
void saveConfig() {
EEPROM.put(CONFIG_ADDR, config);
}
扩展思路
- 添加 WiFi 模块实现远程控制和监控
- 增加定时功能
- 添加多个温度传感器,取平均值
- 实现温度曲线记录
项目二:智能浇花系统
自动监测土壤湿度并浇水,支持定时和手动模式。
功能需求
- 检测土壤湿度
- 湿度低于阈值自动浇水
- 支持定时浇水
- LCD 显示系统状态
- 断电保存设置
硬件清单
| 器件 | 数量 | 说明 |
|---|---|---|
| Arduino Uno | 1 | 主控板 |
| 土壤湿度传感器 | 1 | 检测湿度 |
| 继电器模块 | 1 | 控制水泵 |
| 水泵 | 1 | 5V 直流水泵 |
| LCD1602 I2C | 1 | 显示信息 |
| 按钮 | 3 | 设置和手动控制 |
硬件连接
土壤湿度传感器:
VCC → 5V
GND → GND
AO → A0
继电器模块:
VCC → 5V
GND → GND
IN → D7
LCD1602 I2C:
标准 I2C 连接
按钮:
BTN_MODE → D2 (模式切换)
BTN_UP → D3 (增加)
BTN_DOWN → D4 (减少)
都使用内部上拉,另一端接 GND
完整代码
#include <LiquidCrystal_I2C.h>
#include <EEPROM.h>
// 引脚定义
#define SOIL_PIN A0
#define PUMP_PIN 7
#define BTN_MODE_PIN 2
#define BTN_UP_PIN 3
#define BTN_DOWN_PIN 4
#define LCD_ADDR 0x27
// 设备对象
LiquidCrystal_I2C lcd(LCD_ADDR, 16, 2);
// 系统配置
struct Config {
int moistureThreshold; // 湿度阈值(低于此值浇水)
int waterDuration; // 浇水持续时间(秒)
int checkInterval; // 检测间隔(分钟)
bool autoMode; // 自动模式
};
Config config;
const int CONFIG_ADDR = 0;
// 状态变量
int currentMoisture = 0;
enum State { STATE_IDLE, STATE_WATERING, STATE_MENU };
State systemState = STATE_IDLE;
int menuIndex = 0;
// 按钮状态
struct Button {
int pin;
bool lastState;
bool pressed;
unsigned long lastDebounce;
};
Button buttons[3] = {
{BTN_MODE_PIN, HIGH, false, 0},
{BTN_UP_PIN, HIGH, false, 0},
{BTN_DOWN_PIN, HIGH, false, 0}
};
unsigned long lastCheck = 0;
unsigned long wateringStart = 0;
void setup() {
// 初始化引脚
pinMode(PUMP_PIN, OUTPUT);
digitalWrite(PUMP_PIN, LOW);
for (int i = 0; i < 3; i++) {
pinMode(buttons[i].pin, INPUT_PULLUP);
}
// 初始化显示
lcd.init();
lcd.backlight();
showStartup();
// 加载配置
loadConfig();
Serial.begin(9600);
Serial.println("智能浇花系统启动");
}
void loop() {
// 处理按钮
handleButtons();
// 根据状态执行
switch (systemState) {
case STATE_IDLE:
idleLoop();
break;
case STATE_WATERING:
wateringLoop();
break;
case STATE_MENU:
menuLoop();
break;
}
// 更新显示
updateDisplay();
}
void showStartup() {
lcd.setCursor(0, 0);
lcd.print("Smart Watering");
lcd.setCursor(0, 1);
lcd.print("System v1.0");
delay(1500);
lcd.clear();
}
void handleButtons() {
const unsigned long debounceDelay = 50;
for (int i = 0; i < 3; i++) {
bool reading = digitalRead(buttons[i].pin);
if (reading != buttons[i].lastState) {
buttons[i].lastDebounce = millis();
}
if ((millis() - buttons[i].lastDebounce) > debounceDelay) {
if (reading == LOW && buttons[i].lastState == HIGH) {
buttons[i].pressed = true;
}
}
buttons[i].lastState = reading;
}
}
void idleLoop() {
// 模式按钮:进入菜单
if (buttons[0].pressed) {
buttons[0].pressed = false;
systemState = STATE_MENU;
menuIndex = 0;
lcd.clear();
return;
}
// 上按钮:手动浇水
if (buttons[1].pressed) {
buttons[1].pressed = false;
startWatering();
return;
}
// 下按钮:立即检测
if (buttons[2].pressed) {
buttons[2].pressed = false;
readMoisture();
return;
}
// 自动模式:定时检测
if (config.autoMode) {
if (millis() - lastCheck > config.checkInterval * 60000UL) {
readMoisture();
if (currentMoisture < config.moistureThreshold) {
startWatering();
}
lastCheck = millis();
}
}
}
void wateringLoop() {
// 检查浇水是否完成
if (millis() - wateringStart > config.waterDuration * 1000UL) {
stopWatering();
return;
}
// 任何按钮可以停止浇水
for (int i = 0; i < 3; i++) {
if (buttons[i].pressed) {
buttons[i].pressed = false;
stopWatering();
return;
}
}
}
void menuLoop() {
// 模式按钮:切换菜单项或退出
if (buttons[0].pressed) {
buttons[0].pressed = false;
menuIndex++;
if (menuIndex > 4) {
systemState = STATE_IDLE;
saveConfig();
lcd.clear();
}
return;
}
// 上按钮:增加值
if (buttons[1].pressed) {
buttons[1].pressed = false;
adjustValue(1);
return;
}
// 下按钮:减少值
if (buttons[2].pressed) {
buttons[2].pressed = false;
adjustValue(-1);
return;
}
}
void adjustValue(int delta) {
switch (menuIndex) {
case 0: // 湿度阈值
config.moistureThreshold = constrain(
config.moistureThreshold + delta * 5, 100, 900);
break;
case 1: // 浇水时间
config.waterDuration = constrain(
config.waterDuration + delta, 1, 60);
break;
case 2: // 检测间隔
config.checkInterval = constrain(
config.checkInterval + delta, 1, 1440);
break;
case 3: // 自动模式
config.autoMode = !config.autoMode;
break;
}
}
void readMoisture() {
// 多次采样取平均
long sum = 0;
for (int i = 0; i < 10; i++) {
sum += analogRead(SOIL_PIN);
delay(10);
}
currentMoisture = sum / 10;
// 转换为湿度百分比(传感器值越小越湿)
// 根据实际传感器校准
}
void startWatering() {
digitalWrite(PUMP_PIN, HIGH);
wateringStart = millis();
systemState = STATE_WATERING;
lcd.clear();
}
void stopWatering() {
digitalWrite(PUMP_PIN, LOW);
systemState = STATE_IDLE;
lcd.clear();
}
void updateDisplay() {
static unsigned long lastUpdate = 0;
if (millis() - lastUpdate < 200) return;
lastUpdate = millis();
switch (systemState) {
case STATE_IDLE:
displayIdle();
break;
case STATE_WATERING:
displayWatering();
break;
case STATE_MENU:
displayMenu();
break;
}
}
void displayIdle() {
lcd.setCursor(0, 0);
lcd.print("Moisture: ");
lcd.print(currentMoisture);
lcd.print(" ");
lcd.setCursor(0, 1);
lcd.print(config.autoMode ? "AUTO " : "MAN ");
lcd.print("Thr:");
lcd.print(config.moistureThreshold);
}
void displayWatering() {
unsigned long elapsed = (millis() - wateringStart) / 1000;
int remaining = config.waterDuration - elapsed;
lcd.setCursor(0, 0);
lcd.print("WATERING...");
lcd.setCursor(0, 1);
lcd.print("Time left: ");
lcd.print(remaining);
lcd.print("s ");
}
void displayMenu() {
lcd.setCursor(0, 0);
switch (menuIndex) {
case 0:
lcd.print("Threshold: ");
lcd.print(config.moistureThreshold);
break;
case 1:
lcd.print("Water Time: ");
lcd.print(config.waterDuration);
lcd.print("s");
break;
case 2:
lcd.print("Interval: ");
lcd.print(config.checkInterval);
lcd.print("min");
break;
case 3:
lcd.print("Auto: ");
lcd.print(config.autoMode ? "ON" : "OFF");
break;
case 4:
lcd.print("Save & Exit");
break;
}
lcd.setCursor(0, 1);
lcd.print("+/- to adjust");
}
void loadConfig() {
EEPROM.get(CONFIG_ADDR, config);
if (config.moistureThreshold < 100 || config.moistureThreshold > 900) {
config.moistureThreshold = 400;
config.waterDuration = 5;
config.checkInterval = 30;
config.autoMode = true;
saveConfig();
}
}
void saveConfig() {
EEPROM.put(CONFIG_ADDR, config);
}
扩展思路
- 添加水位检测,防止水箱空转
- 增加多路控制,管理多盆植物
- 添加 WiFi 实现远程监控
- 记录浇水历史
项目三:简易气象站
监测多种环境参数并显示,支持数据记录。
功能需求
- 检测温度、湿度、气压、光线
- LCD 显示当前数据
- SD 卡记录历史数据
- 可设置记录间隔
硬件清单
| 器件 | 数量 | 说明 |
|---|---|---|
| Arduino Uno | 1 | 主控板 |
| DHT22 | 1 | 温湿度 |
| BMP280 | 1 | 气压传感器 |
| 光敏电阻 | 1 | 光线检测 |
| LCD1602 I2C | 1 | 显示 |
| SD 卡模块 | 1 | 数据存储 |
完整代码
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_BMP280.h>
#include <LiquidCrystal_I2C.h>
#include <SD.h>
#include <SPI.h>
// 引脚定义
#define DHT_PIN 2
#define DHT_TYPE DHT22
#define LDR_PIN A0
#define CHIP_SELECT 10
#define LCD_ADDR 0x27
// 设备对象
DHT dht(DHT_PIN, DHT_TYPE);
Adafruit_BMP280 bmp;
LiquidCrystal_I2C lcd(LCD_ADDR, 16, 2);
// 传感器数据
struct SensorData {
float temperature;
float humidity;
float pressure;
int light;
unsigned long timestamp;
};
SensorData data;
unsigned long lastLogTime = 0;
int logInterval = 60; // 记录间隔(秒)
void setup() {
Serial.begin(9600);
// 初始化传感器
dht.begin();
if (!bmp.begin(0x76)) {
Serial.println("BMP280 初始化失败");
}
// 初始化 LCD
lcd.init();
lcd.backlight();
lcd.print("Weather Station");
delay(1000);
lcd.clear();
// 初始化 SD 卡
if (!SD.begin(CHIP_SELECT)) {
lcd.print("SD Card Failed");
Serial.println("SD 卡初始化失败");
delay(2000);
} else {
lcd.print("SD Card OK");
initLogFile();
delay(1000);
}
lcd.clear();
}
void loop() {
// 读取传感器数据
readAllSensors();
// 显示数据
displayData();
// 记录数据
if (millis() - lastLogTime > logInterval * 1000UL) {
logData();
lastLogTime = millis();
}
// 串口输出
printData();
delay(2000);
}
void readAllSensors() {
data.timestamp = millis();
// 温湿度
data.temperature = dht.readTemperature();
data.humidity = dht.readHumidity();
// 气压
data.pressure = bmp.readPressure() / 100.0F; // hPa
// 光线
data.light = analogRead(LDR_PIN);
// 检查读取是否成功
if (isnan(data.temperature)) data.temperature = 0;
if (isnan(data.humidity)) data.humidity = 0;
}
void displayData() {
// 第一行:温度和湿度
lcd.setCursor(0, 0);
lcd.print("T:");
lcd.print(data.temperature, 1);
lcd.print("C H:");
lcd.print(data.humidity, 0);
lcd.print("%");
// 第二行:气压
lcd.setCursor(0, 1);
lcd.print("P:");
lcd.print(data.pressure, 0);
lcd.print("hPa");
}
void initLogFile() {
if (!SD.exists("weather.csv")) {
File file = SD.open("weather.csv", FILE_WRITE);
if (file) {
file.println("timestamp,temperature,humidity,pressure,light");
file.close();
}
}
}
void logData() {
File file = SD.open("weather.csv", FILE_WRITE);
if (file) {
file.print(data.timestamp);
file.print(",");
file.print(data.temperature);
file.print(",");
file.print(data.humidity);
file.print(",");
file.print(data.pressure);
file.print(",");
file.println(data.light);
file.close();
Serial.println("数据已记录");
}
}
void printData() {
Serial.println("===== 传感器数据 =====");
Serial.print("温度: ");
Serial.print(data.temperature);
Serial.println(" °C");
Serial.print("湿度: ");
Serial.print(data.humidity);
Serial.println(" %");
Serial.print("气压: ");
Serial.print(data.pressure);
Serial.println(" hPa");
Serial.print("光线: ");
Serial.println(data.light);
Serial.println("=====================");
}
扩展思路
- 添加 WiFi 将数据上传到物联网平台
- 增加风速、风向传感器
- 实现数据图表显示
- 添加天气预报功能
项目开发流程
开发 Arduino 项目时,建议遵循以下流程:
- 需求分析:明确项目功能和性能要求
- 硬件选型:选择合适的传感器和模块
- 电路设计:绘制连接图,规划引脚
- 模块开发:逐个模块编写和测试代码
- 系统集成:整合各模块,解决冲突
- 功能测试:测试所有功能和边界条件
- 优化调试:优化性能,修复问题
- 文档记录:记录项目细节和使用说明
常见问题排查
项目不工作
- 检查电源供电是否正常
- 检查所有连接是否正确
- 使用串口输出调试信息
- 逐个模块测试,定位问题
传感器读数异常
- 检查传感器供电是否稳定
- 确认传感器地址(I2C 设备)
- 添加适当的延时让传感器稳定
- 使用示例代码验证传感器
系统不稳定
- 检查电源是否足够
- 添加去耦电容
- 减少内存使用
- 检查是否有数组越界
总结
通过这三个项目,你应该已经掌握了 Arduino 项目开发的完整流程。接下来可以尝试自己设计项目,或者对这些项目进行扩展和改进。