软件方面的实现了如下功能:
1.一周七天可以设置开启时间和关闭时间,只能通过串口设置;
2.手动开启和关闭,手动开始2分钟后自动关闭;
3.时间显示功能;
4.设置保存和读取功能,使用mcu自带的eeprom保存。
第三贴
/*
Automatic Watering System 2025.03.01
*/
#include <Ticker.h>
#include <RtcDS1302.h>
#include <EEPROM.h>
#include "TM1637.h"
#define VALVE_PIN1 8
#define VALVE_PIN2 9
#define KEY_PIN 2
#define MANUAL_5_min 300000 //5 minutes
#define MANUAL_10_min 120000 //2 minutes
#define DISPLAY_TIME_INTERVAL 5000 //5 second
#define DAYS 8 //0 day is today, 1 day is tomorrow ...
#define NUMBEROFTIMES 2
#define CLK 11 //pins definitions for TM1637 and can be changed to other ports
#define DIO 10
#define KEY_DELAY 15
TM1637 tm1637(CLK, DIO);
ThreeWire myWire(4, 5, 3); // IO, SCLK, CE
RtcDS1302<ThreeWire> Rtc(myWire);
const int ledPin = LED_BUILTIN;
const int valvePin[] = { VALVE_PIN1, VALVE_PIN2 };
const int on_time = 0;
const int off_time = 1;
const int days_of_plan = DAYS;
const int minutes_per_day = 1440;
const int minutes_per_hour = 60;
int interval_day = 0;
int interval_time_per_day = 10;
int day_work_complete = 0;
int valve_state = 0; //0 open, 1 close
volatile int key_state = 0;
//the number of minutes since midnight
unsigned int onOffTimes[days_of_plan][NUMBEROFTIMES] = { { 1394, 1400 }, { 1394, 1400 }, { 1394, 1400 }, { 1390, 1400 }, { 1390, 1400 }, { 1390, 1400 }, { 1390, 1400 }, { 1390, 1400 } };
unsigned int onOffTimes_eeprom[days_of_plan][NUMBEROFTIMES] = {};
int8_t TimeDisp[] = { 0x00, 0x00, 0x00, 0x00 };
RtcDateTime now;
RtcDateTime start_time;
void close_all_valve();
void display_time();
Ticker timer_manual_10_min(close_all_valve, MANUAL_10_min, 1);
// Ticker timer_manual_5_min(close_all_valve, MANUAL_5_min);
Ticker timer_display_time(display_time, DISPLAY_TIME_INTERVAL);
void close_all_valve() {
valve_close(0);
valve_close(1);
Serial.println("in close all function");
// timer_manual_10_min.stop();
}
// valve open
void valve_open(int i) {
digitalWrite(valvePin[i], LOW);
tm1637.displayStr("Open");
valve_state = 1;
}
// valve close
void valve_close(int i) {
digitalWrite(valvePin[i], HIGH);
tm1637.displayStr("Clos");
valve_state = 0;
}
void display_time() {
now = Rtc.GetDateTime();
// Serial.println();
// Serial.println();
// printDateTime(now);
// Serial.println();
// printDateTime(start_time);
// Serial.println();
// Serial.print("Days from start:");
// Serial.print(now.TotalDays() - start_time.TotalDays());
// Serial.println();
// Serial.print("Days of week is ");
// Serial.println(now.DayOfWeek());
if (!now.IsValid()) {
// Common Causes:
// 1) the battery on the device is low or even missing and the power line was disconnected
Serial.println("RTC lost confidence in the DateTime!");
}
TimeDisp[0] = now.Hour() / 10;
TimeDisp[1] = now.Hour() % 10;
TimeDisp[2] = now.Minute() / 10;
TimeDisp[3] = now.Minute() % 10;
tm1637.display(TimeDisp);
tm1637.point(now.Second() % 2);
// Serial.println();
// Serial.println();
// Serial.print("Type 'P' to print settings or ");
// Serial.println("'S2N13:45' to set valve 2 ON time to 13:34");
// Serial.println("Please enter O to open valves");
// Serial.println("Please enter C to close valves");
}
void setup() {
//start serial connection
Serial.begin(9600);
//configure pin 2 as an input and enable the internal pull-up resistor
pinMode(ledPin, OUTPUT);
pinMode(valvePin[0], OUTPUT);
pinMode(valvePin[1], OUTPUT);
pinMode(KEY_PIN, INPUT_PULLUP);
digitalWrite(valvePin[0], HIGH); // close valve at begin
digitalWrite(ledPin, LOW); // LED off
//RTC information display
Serial.print("compiled: ");
Serial.print(__DATE__);
Serial.println(__TIME__);
Rtc.Begin();
RtcDateTime compiled = RtcDateTime(__DATE__, __TIME__);
printDateTime(compiled);
Serial.println();
if (!Rtc.IsDateTimeValid()) {
// Common Causes:
// 1) first time you ran and the device wasn't running yet
// 2) the battery on the device is low or even missing
Serial.println("RTC lost confidence in the DateTime!");
Rtc.SetDateTime(compiled);
}
if (Rtc.GetIsWriteProtected()) {
Serial.println("RTC was write protected, enabling writing now");
Rtc.SetIsWriteProtected(false);
}
if (!Rtc.GetIsRunning()) {
Serial.println("RTC was not actively running, starting now");
Rtc.SetIsRunning(true);
}
now = Rtc.GetDateTime();
if (now < compiled) {
Serial.println("RTC is older than compile time! (Updating DateTime)");
Rtc.SetDateTime(compiled);
} else if (now > compiled) {
Serial.println("RTC is newer than compile time. (this is expected)");
} else if (now == compiled) {
Serial.println("RTC is the same as compile time! (not expected but all is fine)");
}
start_time = now; // set system start time
attachInterrupt(digitalPinToInterrupt(KEY_PIN), buttonPressed, CHANGE);
timer_display_time.start();
read_control_data();
tm1637.set();
tm1637.init();
}
void loop() {
int sensorVal = digitalRead(KEY_PIN); //read the pushbutton value into a variable
timer_display_time.update();
// timer_manual_5_min.update();
timer_manual_10_min.update();
checkUserInteraction();
check_and_action();
if (key_state == 1)
control_by_key();
}
#define countof(a) (sizeof(a) / sizeof(a[0]))
void printDateTime(const RtcDateTime& dt) {
char datestring[26];
snprintf_P(datestring,
countof(datestring),
PSTR("%02u/%02u/%04u %02u:%02u:%02u"),
dt.Month(),
dt.Day(),
dt.Year(),
dt.Hour(),
dt.Minute(),
dt.Second());
Serial.print(datestring);
}
void buttonPressed() {
if (key_state == 0)
key_state = 1;
return;
}
void control_by_key() {
int buttonState = digitalRead(KEY_PIN);
if (buttonState == LOW) {
delay(KEY_DELAY);
if (digitalRead(KEY_PIN) != LOW) {
key_state = 0;
return;
}
if (timer_manual_10_min.state() == STOPPED) {
timer_manual_10_min.start();
valve_open(0);
valve_open(1);
Serial.println("Valve open");
key_state = 0;
return;
} else if (timer_manual_10_min.state() == RUNNING) {
close_all_valve();
Serial.println("Valve close");
timer_manual_10_min.stop();
}
}
key_state = 0;
return;
}
void checkUserInteraction() {
// Check for user interaction
while (Serial.available() > 0) {
// The first character tells us what to expect
// for the rest of the line
char temp = Serial.read();
// If the first character is 'P' then
// print the current settings
// and break out of the while() loop
if (temp == 'p') {
// printSettings();
print_eeprom();
Serial.flush();
break;
} // end of printing current settings
// If first character is 'S' then the rest will be a setting
else if (temp == 'S') {
Serial.flush();
expectValveSetting();
save_control_data();
Serial.flush();
read_control_data();
break;
}
// open valve
else if (temp == 'o') {
valve_open(0);
valve_open(1);
Serial.flush();
break;
}
// close valve
else if (temp == 'c') {
valve_close(0);
valve_close(1);
Serial.flush();
break;
}
// display control data
else if (temp == 'd') {
print_control_data();
Serial.flush();
break;
}
// Otherwise, it's an error.
// Remind the user what the choices are
// and break out of the while() loop
else {
printMenu();
Serial.flush();
break;
}
} // end of processing user interaction
}
void printMenu() {
Serial.println(
"Please enter p to print the current settings");
Serial.println(
"Please enter S2N13:45 to set 2 day of week ON time to 13:34");
Serial.println("Please enter o to open valves");
Serial.println("Please enter c to close valves");
Serial.println("Please enter d to display control data");
Serial.println("Please enter p to display eeprom data");
}
void print_eeprom() {
Serial.println("EEPROM content is:");
for (int i = 0; i < EEPROM.length()/10; i++) {
Serial.print(EEPROM.read(i));
Serial.print(" ");
if ((i+1) % 10 == 0 )
Serial.println();
}
Serial.println();
}
void save_control_data() {
noInterrupts();
EEPROM.put(0, onOffTimes);
delay(5);
interrupts();
}
void read_control_data() {
EEPROM.get(0, onOffTimes_eeprom);
}
void print_control_data() {
Serial.println("Control data is:");
for (int i = 0; i < days_of_plan; i++)
for (int j = 0; j < NUMBEROFTIMES; j++) {
Serial.print(onOffTimes[i][j]);
Serial.print(" ");
}
Serial.println();
}
void check_and_action() {
if (now.Hour() * minutes_per_hour + now.Minute() >= onOffTimes[now.DayOfWeek()][0] && now.Hour() * minutes_per_hour + now.Minute() < onOffTimes[now.DayOfWeek()][1] && valve_state == 0) {
valve_open(0);
valve_open(1);
}
if (now.Hour() * minutes_per_hour + now.Minute() >= onOffTimes[now.DayOfWeek()][0] && now.Hour() * minutes_per_hour + now.Minute() >= onOffTimes[now.DayOfWeek()][1] && valve_state == 1) {
valve_close(0);
valve_close(1);
valve_close(0);
valve_close(1);
}
}
void expectValveSetting() {
// The first integer should be the day of week, only have one valve
int day_of_week = Serial.parseInt();
// the next character should be either N or F
char onOff = Serial.read();
// next should come the hour
int desiredHour = Serial.parseInt();
// the next character should be ':'
if (Serial.read() != ':') {
Serial.println("no : found"); // Sanity check
Serial.flush();
return;
}
// next should come the minutes
int desiredMinutes = Serial.parseInt();
// finally expect a newline which is the end of
// the sentence:
if (Serial.read() != '\n') { // Sanity check
Serial.println(
"Make sure to end your request with a Newline");
Serial.flush();
return;
}
// Convert the desired hour and minute time
// to the number of minutes since midnight
int desiredMinutesSinceMidnight = (desiredHour * 60 + desiredMinutes);
// Now that we have all the information set it into the array
// in the correct row and column
if (onOff == 'N') { // it's an ON time
onOffTimes[day_of_week][on_time] = desiredMinutesSinceMidnight;
} else if (onOff == 'F') { // it's an OFF time
onOffTimes[day_of_week][off_time] = desiredMinutesSinceMidnight;
} else { // user didn't use N or F
Serial.print("You must use upper case N or F ");
Serial.println("to indicate ON time or OFF time");
Serial.flush();
return;
}
} // end of expectValveSetting()