自动浇水的代码 writeor的博客 wr的小窝喔~
  • 欢迎访问wr的小窝~,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站.
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏吧
  • 嘟嘟嘟嘟嘟嘟啦~~

自动浇水的代码

未分类 writeor 10个月前 (12-19) 247次浏览 已收录 0个评论

#include
#include
#include
#include
#include

// WiFi配置
const char* ssid = “wrTi”;
const char* password = “chenzeqi”;

ESP8266WebServer server(80);

Servo myservo;
const int servoPin = D1; // 舵机信号线连接到D1 (GPIO5)

int currentServoAngle = 0; // 当前舵机角度

// NTP 时间配置(中国时区,东八区)
const char* ntpServer = “pool.ntp.org”;
const long gmtOffset_sec = 8 * 3600;
const int daylightOffset_sec = 0;

// 最大支持闹钟数量
#define MAX_ALARMS 5

struct Alarm {
int hour = 0;
int minute = 0;
int second = 0; // 新增秒字段
int startAngle = 0;
int targetAngle = 0;
bool active = false;
bool triggeredToday = false;
};

Alarm alarms[MAX_ALARMS];

// 全局变量存储脉冲范围 – 扩展为400-4000微秒
int servoMinPulse = 400; // 0度对应的最小脉宽
int servoMaxPulse = 4000; // 360度对应的最大脉宽

// EEPROM相关定义
const int EEPROM_SIZE = 512;
const int EEPROM_START_ADDR = 0;
const int EEPROM_ANGLE_ADDR = 100; // 舵机角度存储地址

// 网页HTML,添加秒选择功能
const char webpage[] PROGMEM = R”=====(




智能浇水舵机控制


智能浇水舵机实时控制

当前系统时间: –:–:–

手动控制舵机角度


舵机测试









脉冲宽度微调




设置浇水闹钟 (最多5个)

时间:
时:
分:
秒:
起始角度:
目标角度:


当前闹钟列表




    )=====”;

    // 辅助函数,把0~360度映射到微秒范围
    int angleToPulse(int angle) {
    angle = constrain(angle, 0, 360);

    // 直接将0-360度线性映射到servoMinPulse-servoMaxPulse微秒
    long pulse = map(angle, 0, 360, servoMinPulse, servoMaxPulse);

    // 确保脉冲在安全范围内
    pulse = constrain(pulse, servoMinPulse, servoMaxPulse);

    return pulse;
    }

    // 从EEPROM读取舵机角度
    int loadAngleFromEEPROM() {
    EEPROM.begin(EEPROM_SIZE);
    int angle = EEPROM.read(EEPROM_ANGLE_ADDR);
    EEPROM.end();

    // 验证角度值是否有效
    if (angle < 0 || angle > 360) {
    angle = 0; // 默认值
    }
    return angle;
    }

    // 保存舵机角度到EEPROM
    void saveAngleToEEPROM(int angle) {
    EEPROM.begin(EEPROM_SIZE);
    EEPROM.write(EEPROM_ANGLE_ADDR, angle);
    EEPROM.commit();
    EEPROM.end();
    }

    // 从EEPROM读取脉冲范围
    void loadPulseRangeFromEEPROM() {
    EEPROM.begin(EEPROM_SIZE);
    int minPulse = EEPROM.read(EEPROM_ANGLE_ADDR + 1);
    int maxPulse = EEPROM.read(EEPROM_ANGLE_ADDR + 2);

    // 如果读取的值无效,使用默认值
    if (minPulse >= 200 && minPulse <= 2000) { servoMinPulse = minPulse; } if (maxPulse >= 2000 && maxPulse <= 5000) { servoMaxPulse = maxPulse; } EEPROM.end(); } // 保存脉冲范围到EEPROM void savePulseRangeToEEPROM() { EEPROM.begin(EEPROM_SIZE); EEPROM.write(EEPROM_ANGLE_ADDR + 1, servoMinPulse); EEPROM.write(EEPROM_ANGLE_ADDR + 2, servoMaxPulse); EEPROM.commit(); EEPROM.end(); } // EEPROM读取闹钟数据 void loadAlarmsFromEEPROM() { EEPROM.begin(EEPROM_SIZE); for (int i = 0; i < MAX_ALARMS; i++) { int baseAddr = EEPROM_START_ADDR + i * sizeof(Alarm); alarms[i].hour = EEPROM.read(baseAddr); alarms[i].minute = EEPROM.read(baseAddr + 1); alarms[i].second = EEPROM.read(baseAddr + 2); // 读取秒 alarms[i].startAngle = EEPROM.read(baseAddr + 3); alarms[i].targetAngle = EEPROM.read(baseAddr + 4); alarms[i].active = EEPROM.read(baseAddr + 5); alarms[i].triggeredToday = false; if (alarms[i].hour > 23 || alarms[i].minute > 59 || alarms[i].second > 59 ||
    alarms[i].startAngle > 360 || alarms[i].targetAngle > 360 ||
    (alarms[i].active != 0 && alarms[i].active != 1)) {
    alarms[i].active = false;
    }
    }
    EEPROM.end();
    }

    // EEPROM写入闹钟数据
    void saveAlarmsToEEPROM() {
    EEPROM.begin(EEPROM_SIZE);
    for (int i = 0; i < MAX_ALARMS; i++) { int baseAddr = EEPROM_START_ADDR + i * sizeof(Alarm); EEPROM.write(baseAddr, alarms[i].hour); EEPROM.write(baseAddr + 1, alarms[i].minute); EEPROM.write(baseAddr + 2, alarms[i].second); // 保存秒 EEPROM.write(baseAddr + 3, alarms[i].startAngle); EEPROM.write(baseAddr + 4, alarms[i].targetAngle); EEPROM.write(baseAddr + 5, alarms[i].active ? 1 : 0); } EEPROM.commit(); EEPROM.end(); } void setupTime() { configTime(gmtOffset_sec, daylightOffset_sec, ntpServer); Serial.print("等待时间同步"); time_t now = time(nullptr); int timeout = 0; while (now < 8 * 3600 * 2 && timeout < 20) { delay(500); Serial.print("."); now = time(nullptr); timeout++; } Serial.println(); } bool myGetLocalTime(struct tm * info) { time_t t = time(nullptr); if (t == 0) return false; struct tm *timeinfo = localtime(&t); if (timeinfo == nullptr) return false; *info = *timeinfo; return true; } void notFound() { server.send(404, "text/plain", "Not Found"); } void setup() { Serial.begin(115200); delay(1000); // 给串口时间初始化 // 先加载EEPROM数据 loadAlarmsFromEEPROM(); loadPulseRangeFromEEPROM(); // 从EEPROM读取上次保存的舵机角度 currentServoAngle = loadAngleFromEEPROM(); Serial.printf("从EEPROM读取舵机角度: %d\n", currentServoAngle); Serial.printf("从EEPROM读取脉冲范围: %d - %d 微秒\n", servoMinPulse, servoMaxPulse); // 初始化舵机 - 关键修改:避免默认的90度位置 Serial.println("初始化舵机..."); // 重要:在连接舵机之前,先设置引脚模式和初始脉冲 pinMode(servoPin, OUTPUT); // 直接发送目标脉冲,避免舵机库的默认行为 int pulseWidth = angleToPulse(currentServoAngle); digitalWrite(servoPin, HIGH); delayMicroseconds(pulseWidth); digitalWrite(servoPin, LOW); delay(20); // 等待一个PWM周期 // 现在连接舵机,但使用我们预定义的脉冲范围 bool attached = myservo.attach(servoPin, servoMinPulse, servoMaxPulse); if(attached) { Serial.println("舵机连接成功"); } else { Serial.println("舵机连接失败"); } // 立即设置舵机角度,确保不会转到默认位置 myservo.writeMicroseconds(pulseWidth); Serial.printf("初始化舵机角度为: %d度 (脉宽: %d微秒)\n", currentServoAngle, pulseWidth); // 延迟一段时间让舵机到达指定位置 delay(1000); WiFi.mode(WIFI_STA); WiFi.begin(ssid, password); Serial.print("正在连接WiFi"); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\nWiFi已连接!"); Serial.print("IP地址为: "); Serial.println(WiFi.localIP()); setupTime(); // 网页主页 server.on("/", [](){ server.send_P(200, "text/html; charset=utf-8", webpage); }); // 设置舵机角度,支持0-360度 server.on("/setAngle", [](){ if (server.hasArg("value")) { int angle = server.arg("value").toInt(); if (angle >= 0 && angle <= 360) { currentServoAngle = angle; int pulseWidth = angleToPulse(currentServoAngle); // 先分离再重新连接舵机,确保脉冲范围正确 if(myservo.attached()) { myservo.detach(); delay(50); } myservo.attach(servoPin, servoMinPulse, servoMaxPulse); delay(50); myservo.writeMicroseconds(pulseWidth); // 保存角度到EEPROM saveAngleToEEPROM(currentServoAngle); Serial.printf("舵机角度设为: %d度 (脉宽: %d微秒)\n", currentServoAngle, pulseWidth); // 保持连接一小段时间确保舵机接收到信号 delay(300); server.send(200, "text/plain", "OK"); return; } } server.send(400, "text/plain", "非法参数"); }); // 获取当前舵机角度 server.on("/getCurrentAngle", [](){ String json = "{"; json += "\"angle\":" + String(currentServoAngle); json += "}"; server.send(200, "application/json", json); }); // 获取当前脉冲范围 server.on("/getPulseRange", [](){ String json = "{"; json += "\"minPulse\":" + String(servoMinPulse) + ","; json += "\"maxPulse\":" + String(servoMaxPulse); json += "}"; server.send(200, "application/json", json); }); // 设置脉冲范围 server.on("/setPulseRange", [](){ if (server.hasArg("min") && server.hasArg("max")) { int minPulse = server.arg("min").toInt(); int maxPulse = server.arg("max").toInt(); // 扩展参数检查范围 if (minPulse >= 200 && minPulse <= 2000 && maxPulse >= 2000 && maxPulse <= 5000 && minPulse < maxPulse) { servoMinPulse = minPulse; servoMaxPulse = maxPulse; // 保存脉冲范围到EEPROM savePulseRangeToEEPROM(); // 重新连接舵机使用新的脉冲范围 if(myservo.attached()) { myservo.detach(); delay(50); } myservo.attach(servoPin, servoMinPulse, servoMaxPulse); delay(50); // 使用新的脉冲范围重新设置当前角度 int pulseWidth = angleToPulse(currentServoAngle); myservo.writeMicroseconds(pulseWidth); Serial.printf("脉冲范围更新为: %d - %d 微秒\n", servoMinPulse, servoMaxPulse); Serial.printf("当前角度 %d度 对应脉宽: %d微秒\n", currentServoAngle, pulseWidth); server.send(200, "text/plain", "脉冲范围已更新"); return; } } server.send(400, "text/plain", "非法脉冲范围"); }); // 添加闹钟,支持秒 server.on("/addAlarm", [](){ if (server.hasArg("hour") && server.hasArg("minute") && server.hasArg("second") && server.hasArg("start") && server.hasArg("target")) { int hour = server.arg("hour").toInt(); int minute = server.arg("minute").toInt(); int second = server.arg("second").toInt(); int startAngle = server.arg("start").toInt(); int targetAngle = server.arg("target").toInt(); if (hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59 ||
    startAngle < 0 || startAngle > 360 || targetAngle < 0 || targetAngle > 360) {
    server.send(400, “text/plain”, “参数超出范围”);
    return;
    }

    for (int i = 0; i < MAX_ALARMS; i++) { if (!alarms[i].active) { alarms[i].hour = hour; alarms[i].minute = minute; alarms[i].second = second; alarms[i].startAngle = startAngle; alarms[i].targetAngle = targetAngle; alarms[i].active = true; alarms[i].triggeredToday = false; Serial.printf("新增闹钟: %02d:%02d:%02d 从%d°转到%d°\n", hour, minute, second, startAngle, targetAngle); saveAlarmsToEEPROM(); server.send(200, "text/plain", "闹钟添加成功"); return; } } server.send(200, "text/plain", "闹钟数量已满"); } else { server.send(400, "text/plain", "参数缺失"); } }); // 获取闹钟列表 server.on("/getAlarms", [](){ String json = "["; bool first = true; for (int i = 0; i < MAX_ALARMS; i++) { if (alarms[i].active) { if (!first) { json += ","; } else { first = false; } json += "{"; json += "\"hour\":" + String(alarms[i].hour) + ","; json += "\"minute\":" + String(alarms[i].minute) + ","; json += "\"second\":" + String(alarms[i].second) + ","; json += "\"startAngle\":" + String(alarms[i].startAngle) + ","; json += "\"targetAngle\":" + String(alarms[i].targetAngle); json += "}"; } } json += "]"; server.send(200, "application/json", json); }); // 删除闹钟 server.on("/deleteAlarm", []() { if (server.hasArg("index")) { int idx = server.arg("index").toInt(); int activeCount = 0; for(int i=0; i= activeCount) {
    server.send(400, “text/plain”, “索引超出范围”);
    return;
    }

    int curIndex = 0;
    for(int i=0; i


    wr的小窝 , 版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
    转载请注明原文链接:自动浇水的代码
    喜欢 (0)赏
    [[email protected]]
    分享 (0)

    您必须 登录 才能发表评论!