|
1 |
| -#include "toggle_switch.h" |
2 |
| -#include "debounce.h" |
| 1 | +#include <avr/wdt.h> |
| 2 | + |
| 3 | +#include <LiquidCrystal.h> |
| 4 | +#include <DS3232RTC.h> //http://github.com/JChristensen/DS3232RTC |
| 5 | +#include <Time.h> //http://www.arduino.cc/playground/Code/Time |
| 6 | +#include <Wire.h> //http://arduino.cc/en/Reference/Wire (included with Arduino IDE) |
| 7 | + |
| 8 | +#include "config.h" |
| 9 | +#include "ds3232.h" |
| 10 | +#include "buttons.h" |
| 11 | +#include "lcd.h" |
| 12 | +#include "frames.h" |
3 | 13 | #include "busy_wait.h"
|
4 | 14 |
|
5 |
| -#define BIT_IS_SET(x, n) ((x) & (1<<(n))) |
6 |
| -#define BIT_SET(x, n) ((x) |= (1<<(n))) |
7 |
| -#define BIT_UNSET(x, n) ((x) &= ~(1<<(n))) |
8 |
| -#define BIT_TOGGLE(x, n) ((x) ^= (1<<(n))) |
| 15 | +pump_t pump = {.state = PUMP_OFF, .state_start = 0, .count = 0}; |
9 | 16 |
|
10 |
| -#define RELAY_BIT_POS 0 |
11 |
| -#define TOGGLE_SWITCH_BIT_POS 1 |
12 |
| -#define FLOAT_SWITCH_BIT_POS 2 |
| 17 | +void setup() { |
| 18 | + uint8_t arrow[8] = { |
| 19 | + 0b00000, |
| 20 | + 0b00100, |
| 21 | + 0b00010, |
| 22 | + 0b11111, |
| 23 | + 0b00010, |
| 24 | + 0b00100, |
| 25 | + 0b00000, |
| 26 | + 0b00000 |
| 27 | + }; |
| 28 | + lcd.createChar(0, arrow); |
| 29 | + lcd.begin(COLS, ROWS); |
| 30 | + |
| 31 | + lcd.print("WATER PUMPER 1.0"); |
| 32 | + uint16_t _delay = 1000; |
| 33 | + uint16_t _char_delay = _delay / COLS; |
| 34 | + |
| 35 | + uint8_t i, j = 0; |
| 36 | + for (j = 0; j < COLS; ++j) { |
| 37 | + if (j > 0) { |
| 38 | + busyWait(_char_delay); |
| 39 | + } |
13 | 40 |
|
14 |
| -/* Input/Output pins */ |
15 |
| -#define RELAY_PIN 4 |
16 |
| -#define OFF_SWITCH 3 |
17 |
| -#define FLOAT_SWITCH_PIN 2 |
| 41 | + lcd.setCursor(0, 1); |
| 42 | + for (i = 0; i < j; ++i) { |
| 43 | + lcd.print('-'); |
| 44 | + } |
| 45 | + lcd.write(byte(0)); |
| 46 | + } |
18 | 47 |
|
19 |
| -/* LEDs */ |
20 |
| -#define OFF_SWITCH_LED_PIN 5 |
21 |
| -#define FLOAT_SWITCH_LED_PIN 6 |
22 |
| -#define PUMP_LED_PIN 7 |
| 48 | + setSyncProvider(RTC.get); |
| 49 | + if (timeStatus() != timeSet) { |
| 50 | + lcd.print("Error: Invalid time status"); |
| 51 | + } |
23 | 52 |
|
24 |
| -/* watering schedule */ |
25 |
| -#define TIME_BETWEEN_WATERING 10000 |
26 |
| -#define WATER_TIME 5000 |
| 53 | + delay(1000); |
27 | 54 |
|
28 |
| -#define DEBUG 1 |
| 55 | + /* configure our buttons, according to: |
29 | 56 |
|
30 |
| -volatile uint8_t state_mask; |
| 57 | + [analog A0] |
| 58 | + | |
| 59 | + 5v --> [1kΩ] -+--> [270Ω] +-> [390Ω] + |
| 60 | + | | | |
| 61 | + v v v |
| 62 | + [ btn ] [ btn ] [ btn ] |
| 63 | + | | | |
| 64 | + gnd <---------+-----------+----------+ |
| 65 | + */ |
| 66 | + button_init(1000, 0); |
| 67 | + button_init(1000, 270); |
| 68 | + button_init(1000, 270 + 390); |
31 | 69 |
|
32 |
| -volatile toggle_switch_t button; |
33 |
| -volatile toggle_switch_t float_switch; |
34 |
| -debounce_t button_debounce; |
35 |
| -debounce_t float_switch_debounce; |
| 70 | + readAlarm(ALARM_2, &alarm2_def); |
36 | 71 |
|
37 |
| -unsigned long pump_state_msec; |
| 72 | + // clear alarm flags |
| 73 | + RTC.alarm(ALARM_1); |
| 74 | + RTC.alarm(ALARM_2); |
38 | 75 |
|
39 |
| -void setup() { |
40 |
| - Serial.begin(9600); |
41 |
| - pinMode(RELAY_PIN, OUTPUT); |
42 |
| - pinMode(OFF_SWITCH_LED_PIN, OUTPUT); |
43 |
| - pinMode(PUMP_LED_PIN, OUTPUT); |
44 |
| - pinMode(FLOAT_SWITCH_LED_PIN, OUTPUT); |
45 |
| - |
46 |
| - /* pullup the toggle switch to 10k internal resistor */ |
47 |
| - pinMode(OFF_SWITCH, INPUT_PULLUP); |
48 |
| - /* pullup the float switch to 10k internal resistor */ |
49 |
| - pinMode(FLOAT_SWITCH_PIN, INPUT_PULLUP); |
50 |
| - |
51 |
| - /* because of INPUT_PULLUP, we need to take action when circuit goes from HIGH -> LOW or LOW -> HIGH */ |
52 |
| - attachInterrupt(digitalPinToInterrupt(OFF_SWITCH), handleToggleButton, CHANGE); |
53 |
| - attachInterrupt(digitalPinToInterrupt(FLOAT_SWITCH_PIN), handleFloatSwitch, CHANGE); |
54 |
| - |
55 |
| - debounce_init(&button_debounce); |
56 |
| - debounce_init(&float_switch_debounce); |
57 |
| - toggle_switch_init((toggle_switch_t*)&button, OFF_SWITCH, 1); |
58 |
| - toggle_switch_init((toggle_switch_t*)&float_switch, FLOAT_SWITCH_PIN); |
59 |
| - |
60 |
| - pump_state_msec = 0; |
61 |
| - |
62 |
| - /* if toggle switch is in ON position at startup ... */ |
63 |
| - if (toggle_switch_on(&button)) { |
64 |
| - BIT_SET(state_mask, TOGGLE_SWITCH_BIT_POS); |
65 |
| - } |
| 76 | + RTC.squareWave(SQWAVE_NONE); |
66 | 77 |
|
67 |
| - if (toggle_switch_on(&float_switch)) { |
68 |
| - BIT_SET(state_mask, FLOAT_SWITCH_BIT_POS); |
69 |
| - } |
| 78 | + current_time = now(); |
70 | 79 |
|
71 |
| - delay(1000); |
72 |
| -} |
| 80 | + RTC.setAlarm(ALM1_EVERY_SECOND, 0, 0, 0, 0); // every second |
| 81 | + //RTC.setAlarm(ALM2_MATCH_MINUTES, 0, 0, 0, 0); // every zero-th minute |
| 82 | + RTC.alarmInterrupt(ALARM_1, true); |
| 83 | + //RTC.alarmInterrupt(ALARM_2, true); |
73 | 84 |
|
74 |
| -void handleToggleButton() { |
75 |
| - if (debounce_toggle_switch(&button, &button_debounce, 2)) { |
76 |
| - toggle_switch_read_state(&button); |
| 85 | + pinMode(RTC_INT_PIN, INPUT_PULLUP); |
| 86 | + attachInterrupt(digitalPinToInterrupt(RTC_INT_PIN), alarmChange, FALLING); |
77 | 87 |
|
78 |
| - if (toggle_switch_on(&button)) { |
79 |
| - BIT_SET(state_mask, TOGGLE_SWITCH_BIT_POS); |
80 |
| - } else { |
81 |
| - BIT_UNSET(state_mask, TOGGLE_SWITCH_BIT_POS); |
82 |
| - } |
| 88 | + pinMode(FLOAT_SWITCH_INT_PIN, INPUT_PULLUP); |
| 89 | + attachInterrupt(digitalPinToInterrupt(FLOAT_SWITCH_INT_PIN), floatSwitchInterrupt, CHANGE); |
| 90 | + |
| 91 | + pinMode(PUMP_PIN, OUTPUT); |
| 92 | + digitalWrite(PUMP_PIN, LOW); |
| 93 | +} |
| 94 | + |
| 95 | +void pump_on() { |
| 96 | + if (digitalRead(FLOAT_SWITCH_INT_PIN) == FLOAT_SWITCH_OFF) { |
| 97 | + return; |
83 | 98 | }
|
| 99 | + |
| 100 | + pump.state = PUMP_ON; |
| 101 | + pump.state_start = millis(); |
| 102 | + pump.count++; |
| 103 | + digitalWrite(PUMP_PIN, HIGH); |
| 104 | + |
| 105 | + /* configure watchdog to 1 second */ |
| 106 | + watchdog_init(WDTO_1S); |
84 | 107 | }
|
85 | 108 |
|
86 |
| -void handleFloatSwitch() { |
87 |
| - if (debounce_toggle_switch(&float_switch, &float_switch_debounce, 2)) { |
88 |
| - toggle_switch_read_state(&float_switch); |
| 109 | +void pump_off() { |
| 110 | + wdt_disable(); |
89 | 111 |
|
90 |
| - if (toggle_switch_on(&float_switch)) { |
91 |
| - BIT_SET(state_mask, FLOAT_SWITCH_BIT_POS); |
92 |
| - } else { |
93 |
| - BIT_UNSET(state_mask, FLOAT_SWITCH_BIT_POS); |
94 |
| - } |
95 |
| - } |
| 112 | + digitalWrite(PUMP_PIN, LOW); |
| 113 | + pump.state = PUMP_OFF; |
| 114 | + pump.state_start = millis(); |
96 | 115 | }
|
97 | 116 |
|
98 |
| -void loop() { |
99 |
| - writeStateMaskToLeds(); |
| 117 | +uint16_t get_pump_count() { |
| 118 | + return pump.count; |
| 119 | +} |
100 | 120 |
|
101 |
| - if (!BIT_IS_SET(state_mask, TOGGLE_SWITCH_BIT_POS) || !BIT_IS_SET(state_mask, FLOAT_SWITCH_BIT_POS)) { |
102 |
| - if (BIT_IS_SET(state_mask, RELAY_BIT_POS)) { |
103 |
| - digitalWrite(RELAY_PIN, LOW); |
104 |
| - pump_state_msec = 0; |
105 |
| - BIT_UNSET(state_mask, RELAY_BIT_POS); |
106 |
| - } |
107 |
| - return; |
| 121 | +/* |
| 122 | + RTC Alarm ISR |
| 123 | + */ |
| 124 | +void alarmChange() { |
| 125 | + alarmCalled = 1; |
| 126 | +} |
| 127 | + |
| 128 | +/* |
| 129 | + Float switch ISR |
| 130 | + */ |
| 131 | +void floatSwitchInterrupt() { |
| 132 | + if (digitalRead(FLOAT_SWITCH_INT_PIN) == FLOAT_SWITCH_OFF && pump.state == PUMP_ON) { |
| 133 | + pump_off(); |
108 | 134 | }
|
| 135 | +} |
109 | 136 |
|
110 |
| - if (!BIT_IS_SET(state_mask, RELAY_BIT_POS)) { |
111 |
| - /* scheduler is active but pump is not running */ |
| 137 | +void watchdog_init(uint8_t mask) { |
| 138 | + /* Clear the reset flag. */ |
| 139 | + MCUSR &= ~(1 << WDRF); |
112 | 140 |
|
113 |
| - if (pump_state_msec == 0 || ((millis() - pump_state_msec) > TIME_BETWEEN_WATERING)) { |
114 |
| - digitalWrite(RELAY_PIN, HIGH); |
115 |
| - pump_state_msec = millis(); |
116 |
| - BIT_SET(state_mask, RELAY_BIT_POS); |
117 |
| - } |
118 |
| - return; |
119 |
| - } |
| 141 | + /* In order to change WDE or the prescaler, we need to |
| 142 | + * set WDCE (This will allow updates for 4 clock cycles). |
| 143 | + */ |
| 144 | + WDTCSR |= (1<<WDCE) | (1<<WDE); |
120 | 145 |
|
121 |
| - /* scheduler is active and pump is running */ |
| 146 | + /* set new watchdog timeout prescaler value */ |
| 147 | + WDTCSR = mask; |
122 | 148 |
|
123 |
| - if ((millis() - pump_state_msec) > WATER_TIME) { |
124 |
| - digitalWrite(RELAY_PIN, LOW); |
125 |
| - pump_state_msec = millis(); |
126 |
| - BIT_UNSET(state_mask, RELAY_BIT_POS); |
127 |
| - } |
| 149 | + /* Enable the WD interrupt (note no reset). */ |
| 150 | + WDTCSR |= _BV(WDIE); |
128 | 151 | }
|
129 | 152 |
|
130 |
| -void turnLedOn(int pin) { |
131 |
| - if (digitalRead(pin) == LOW) { |
132 |
| - digitalWrite(pin, HIGH); |
133 |
| - } |
| 153 | +/* |
| 154 | + Watchdog ISR |
| 155 | + */ |
| 156 | +ISR(WDT_vect) { |
| 157 | + if ((millis() - pump.state_start) >= WATER_RUNTIME) { |
| 158 | + pump_off(); |
| 159 | + } |
134 | 160 | }
|
135 | 161 |
|
136 |
| -void turnLedOff(int pin) { |
137 |
| - if (digitalRead(pin) == HIGH) { |
138 |
| - digitalWrite(pin, LOW); |
139 |
| - } |
140 |
| -} |
| 162 | +void loop() { |
| 163 | + if (alarmCalled) { |
| 164 | + alarmCalled = 0; |
| 165 | + if (RTC.alarm(ALARM_1)) { |
| 166 | + handle_alarm1(); |
| 167 | + } |
141 | 168 |
|
142 |
| -void writeStateMaskToLeds() { |
143 |
| - if (BIT_IS_SET(state_mask, TOGGLE_SWITCH_BIT_POS)) { |
144 |
| - turnLedOn(OFF_SWITCH_LED_PIN); |
145 |
| - } else { |
146 |
| - turnLedOff(OFF_SWITCH_LED_PIN); |
| 169 | + if (RTC.alarm(ALARM_2) && is_alarm_active(ALARM_2)) { |
| 170 | + handle_alarm2(); |
| 171 | + } |
147 | 172 | }
|
148 | 173 |
|
149 |
| - if (BIT_IS_SET(state_mask, FLOAT_SWITCH_BIT_POS)) { |
150 |
| - turnLedOn(FLOAT_SWITCH_LED_PIN); |
151 |
| - } else { |
152 |
| - turnLedOff(FLOAT_SWITCH_LED_PIN); |
| 174 | + switch (get_active_frame()) { |
| 175 | + case FRAME_0: |
| 176 | + /* root frame */ |
| 177 | + frame0(); |
| 178 | + break; |
| 179 | + |
| 180 | + case FRAME_1: |
| 181 | + /* menu frame */ |
| 182 | + frame1(); |
| 183 | + break; |
| 184 | + |
| 185 | + case FRAME_2: |
| 186 | + /* menu option frame */ |
| 187 | + frame2(); |
| 188 | + break; |
153 | 189 | }
|
154 | 190 |
|
155 |
| - if (BIT_IS_SET(state_mask, RELAY_BIT_POS)) { |
156 |
| - turnLedOn(PUMP_LED_PIN); |
157 |
| - } else { |
158 |
| - turnLedOff(PUMP_LED_PIN); |
| 191 | + set_prev_frame(get_active_frame()); |
| 192 | + |
| 193 | + if (get_next_frame() != -1) { |
| 194 | + set_active_frame(get_next_frame()); |
| 195 | + set_next_frame(-1); |
| 196 | + set_last_draw(); |
159 | 197 | }
|
160 | 198 | }
|
0 commit comments