跳到主要内容

项目实战

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

项目一:智能温控风扇​

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

功能需求​

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

硬件清单​

器件数量说明
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 项目开发的完整流程。接下来可以尝试自己设计项目,或者对这些项目进行扩展和改进。