跳到主要内容

项目实战

本章将通过几个完整的实战项目,综合运用前面学到的所有知识。每个项目都包含硬件连接、代码实现和扩展思路。

项目一:智能温控风扇

根据环境温度自动调节风扇转速,实现舒适节能。

功能需求

  • 实时显示当前温度
  • 温度超过阈值时启动风扇
  • 温度越高,风扇转速越快
  • 可手动调节温度阈值
  • 配置断电保存

硬件清单

器件数量说明
Arduino Uno1主控板
DHT22 温湿度传感器1检测温度
LCD1602 I2C1显示信息
直流电机 + L298N1风扇驱动
电位器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);
}

扩展思路

  1. 添加 WiFi 模块实现远程控制和监控
  2. 增加定时功能
  3. 添加多个温度传感器,取平均值
  4. 实现温度曲线记录

项目二:智能浇花系统

自动监测土壤湿度并浇水,支持定时和手动模式。

功能需求

  • 检测土壤湿度
  • 湿度低于阈值自动浇水
  • 支持定时浇水
  • LCD 显示系统状态
  • 断电保存设置

硬件清单

器件数量说明
Arduino Uno1主控板
土壤湿度传感器1检测湿度
继电器模块1控制水泵
水泵15V 直流水泵
LCD1602 I2C1显示信息
按钮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);
}

扩展思路

  1. 添加水位检测,防止水箱空转
  2. 增加多路控制,管理多盆植物
  3. 添加 WiFi 实现远程监控
  4. 记录浇水历史

项目三:简易气象站

监测多种环境参数并显示,支持数据记录。

功能需求

  • 检测温度、湿度、气压、光线
  • LCD 显示当前数据
  • SD 卡记录历史数据
  • 可设置记录间隔

硬件清单

器件数量说明
Arduino Uno1主控板
DHT221温湿度
BMP2801气压传感器
光敏电阻1光线检测
LCD1602 I2C1显示
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("=====================");
}

扩展思路

  1. 添加 WiFi 将数据上传到物联网平台
  2. 增加风速、风向传感器
  3. 实现数据图表显示
  4. 添加天气预报功能

项目开发流程

开发 Arduino 项目时,建议遵循以下流程:

  1. 需求分析:明确项目功能和性能要求
  2. 硬件选型:选择合适的传感器和模块
  3. 电路设计:绘制连接图,规划引脚
  4. 模块开发:逐个模块编写和测试代码
  5. 系统集成:整合各模块,解决冲突
  6. 功能测试:测试所有功能和边界条件
  7. 优化调试:优化性能,修复问题
  8. 文档记录:记录项目细节和使用说明

常见问题排查

项目不工作

  1. 检查电源供电是否正常
  2. 检查所有连接是否正确
  3. 使用串口输出调试信息
  4. 逐个模块测试,定位问题

传感器读数异常

  1. 检查传感器供电是否稳定
  2. 确认传感器地址(I2C 设备)
  3. 添加适当的延时让传感器稳定
  4. 使用示例代码验证传感器

系统不稳定

  1. 检查电源是否足够
  2. 添加去耦电容
  3. 减少内存使用
  4. 检查是否有数组越界

总结

通过这三个项目,你应该已经掌握了 Arduino 项目开发的完整流程。接下来可以尝试自己设计项目,或者对这些项目进行扩展和改进。