#include <LiquidCrystal_I2C.h>
#include <RTClib.h>
#include <Wire.h>
#include <DHT.h>
// 定义引脚
#define LIGHT_SENSOR A0 // 光敏电阻输入
#define POTENTIOMETER A1 // 电位器输入
#define LED_PIN 13 // LED指示灯
#define MOTOR_DIR_PIN 9 // 电机方向控制
#define MOTOR_PWM_PIN 10 // 电机速度控制( PWM )
#define DHT_PIN 2 // DHT传感器数据引脚
#define DHT_TYPE DHT11 // DHT11或DHT22
// 初始化LCD对象 (I2C地址0x27, 16列2行)
LiquidCrystal_I2C lcd(0x27, 16, 2);
RTC_DS1307 rtc;
DHT dht(DHT_PIN, DHT_TYPE);
// 窗帘控制参数
int curtainPosition = 0; // 窗帘位置 (0-100%)
int targetPosition = 0; // 目标位置
bool curtainMoving = false; // 窗帘是否正在移动
unsigned long lastMotorUpdate = 0; // 电机上次更新时间
const int MOTOR_UPDATE_INTERVAL = 50; // 电机更新间隔(毫秒)
void setup() {
// 初始化串口通信
Serial.begin(9600);
// 初始化LCD
lcd.begin(16,2);
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("Smart Curtain");
lcd.setCursor(0, 1);
lcd.print("System Starting...");
delay(2000);
// 初始化RTC时钟模块
if (!rtc.begin()) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("RTC Error!");
while (1);
}
// 如果RTC时钟没有设置时间,设置一个初始时间
if (!rtc.isrunning()) {
Serial.println("RTC is NOT running!");
// 设置时间为编译时间
rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
}
// 初始化DHT传感器
dht.begin();
// 初始化LED和电机引脚
pinMode(LED_PIN, OUTPUT);
pinMode(MOTOR_DIR_PIN, OUTPUT);
pinMode(MOTOR_PWM_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
digitalWrite(MOTOR_DIR_PIN, LOW);
analogWrite(MOTOR_PWM_PIN, 0); // 电机停止
// 读取初始窗帘位置
updateCurtainPosition();
// 初始显示
updateDisplay();
}
void loop() {
// 获取当前时间
DateTime now = rtc.now();
// 时间控制逻辑
if (now.hour() == 8 && now.minute() == 0 && now.second() < 10) {
// 早上8点:打开窗帘
setCurtainPosition(100); // 100%表示完全打开
} else if (now.hour() == 20 && now.minute() == 0 && now.second() < 10) {
// 晚上8点:关闭窗帘
setCurtainPosition(0); // 0%表示完全关闭
}
// 光线控制逻辑(可选)
int lightLevel = analogRead(LIGHT_SENSOR);
if (lightLevel < 100 && now.hour() >= 6 && now.hour() < 18) {
// 如果光线太暗且在白天,自动打开窗帘
setCurtainPosition(100);
}
// 窗帘控制逻辑
controlCurtain();
// 每10秒更新一次显示
static unsigned long lastUpdateTime = 0;
if (millis() - lastUpdateTime > 10000) {
updateDisplay();
lastUpdateTime = millis();
}
delay(100); // 短暂延时减少CPU负载
}
// 设置窗帘目标位置
void setCurtainPosition(int position) {
if (position != targetPosition) {
targetPosition = constrain(position, 0, 100);
curtainMoving = true;
digitalWrite(LED_PIN, HIGH); // 打开LED指示工作状态
Serial.print("Setting curtain to ");
Serial.print(targetPosition);
Serial.println("%");
}
}
// 更新当前窗帘位置
void updateCurtainPosition() {
// 读取当前电位器位置 (0-1023)
int potValue = analogRead(POTENTIOMETER);
// 将读数转换为百分比 (0-100%)
curtainPosition = map(potValue, 0, 1023, 0, 100);
// 限制在有效范围内
curtainPosition = constrain(curtainPosition, 0, 100);
}
// 控制窗帘电机
void controlCurtain() {
if (!curtainMoving) return;
// 仅在更新间隔后才更新电机状态
if (millis() - lastMotorUpdate < MOTOR_UPDATE_INTERVAL) return;
lastMotorUpdate = millis();
// 更新当前位置
updateCurtainPosition();
// 计算位置误差
int error = targetPosition - curtainPosition;
// 简单的PID控制逻辑(这里简化为比例控制)
if (abs(error) > 5) { // 误差大于5%时才移动
int motorSpeed = constrain(abs(error) * 2, 100, 255); // 速度与误差成正比
if (error > 0) {
// 需要打开窗帘
digitalWrite(MOTOR_DIR_PIN, HIGH); // 设置方向
analogWrite(MOTOR_PWM_PIN, motorSpeed); // 设置速度
} else {
// 需要关闭窗帘
digitalWrite(MOTOR_DIR_PIN, LOW); // 设置方向
analogWrite(MOTOR_PWM_PIN, motorSpeed); // 设置速度
}
} else {
// 已到达目标位置
analogWrite(MOTOR_PWM_PIN, 0); // 停止电机
curtainMoving = false;
digitalWrite(LED_PIN, LOW); // 关闭LED
Serial.print("Curtain position reached: ");
Serial.println(curtainPosition);
}
}
// 更新LCD显示内容
void updateDisplay() {
lcd.clear();
// 显示当前时间
DateTime now = rtc.now();
lcd.setCursor(0, 0);
lcd.print(now.hour(), DEC);
lcd.print(':');
if(now.minute() < 10) lcd.print('0');
lcd.print(now.minute(), DEC);
lcd.print(':');
if(now.second() < 10) lcd.print('0');
lcd.print(now.second(), DEC);
lcd.print(" ");
lcd.print(now.month(), DEC);
lcd.print('/');
lcd.print(now.day(), DEC);
lcd.print('/');
lcd.print(now.year(), DEC);
// 读取并显示温度和湿度
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
lcd.setCursor(0, 1);
if (isnan(temperature) || isnan(humidity)) {
lcd.print("Sensor Error!");
} else {
lcd.print("Temp: ");
lcd.print(temperature);
lcd.print(" C Hum: ");
lcd.print(humidity);
lcd.print("%");
}
// 显示窗帘状态
lcd.setCursor(12, 1);
lcd.print(curtainPosition);
lcd.print("%");
}