Browse Source

implemented manual modes

Thomas Buck 1 month ago
parent
commit
cfd16ed794
7 changed files with 365 additions and 72 deletions
  1. 1
    0
      include/GPIOBank.h
  2. 48
    0
      include/Plants.h
  3. 8
    2
      include/Statemachine.h
  4. 6
    0
      src/GPIOBank.cpp
  5. 99
    0
      src/Plants.cpp
  6. 195
    58
      src/Statemachine.cpp
  7. 8
    12
      src/main.cpp

+ 1
- 0
include/GPIOBank.h View File

@@ -12,6 +12,7 @@ public:
12 12
     
13 13
     int getSize(void);
14 14
     void setPin(int n, bool state);
15
+    void setAll(bool state);
15 16
     bool getPin(int n);
16 17
     
17 18
 private:

+ 48
- 0
include/Plants.h View File

@@ -0,0 +1,48 @@
1
+#ifndef _PLANTS_H_
2
+#define _PLANTS_H_
3
+
4
+#include "GPIOBank.h"
5
+
6
+class Plants {
7
+public:
8
+    enum Waterlevel {
9
+        empty,
10
+        inbetween,
11
+        full,
12
+        invalid
13
+    };
14
+    
15
+    // valves: no of plants + 1 for water inlet
16
+    // pumps: no of fertilizers
17
+    // switches: 2, low and high level
18
+    Plants(int valve_count, int pump_count, int switch_count);
19
+    
20
+    void setValvePins(int pins[]);
21
+    void setPumpPins(int pins[]);
22
+    void setSwitchPins(int pins[], bool pullup);
23
+    
24
+    void abort(void);
25
+    
26
+    Waterlevel getWaterlevel(void);
27
+    void openWaterInlet(void);
28
+    void closeWaterInlet(void);
29
+    
30
+    int countFertilizers(void);
31
+    void startFertilizer(int id);
32
+    void stopFertilizer(int id);
33
+    void stopAllFertilizers(void);
34
+    
35
+    int countPlants(void);
36
+    void startPlant(int id);
37
+    void stopPlant(int id);
38
+    void stopAllPlants(void);
39
+    
40
+private:
41
+    GPIOBank valves;
42
+    GPIOBank pumps;
43
+    GPIOBank switches;
44
+};
45
+
46
+extern Plants plants;
47
+
48
+#endif // _PLANTS_H_

+ 8
- 2
include/Statemachine.h View File

@@ -1,6 +1,8 @@
1 1
 #ifndef _STATEMACHINE_H_
2 2
 #define _STATEMACHINE_H_
3 3
 
4
+#include <Arduino.h>
5
+
4 6
 class Statemachine {
5 7
 public:
6 8
     enum States {
@@ -22,7 +24,9 @@ public:
22 24
         menu_valves_time, // set runtime
23 25
         menu_valves_go, // running
24 26
         menu_valves_running,
25
-        menu_valves_done
27
+        menu_valves_done,
28
+        
29
+        error
26 30
     };
27 31
     
28 32
     class DigitBuffer {
@@ -61,12 +65,14 @@ private:
61 65
     uint32_t number_input(void);
62 66
     
63 67
     DigitBuffer db;
64
-    States state;
68
+    States state, old_state;
65 69
     print_fn print;
66 70
     backspace_fn backspace;
67 71
     
68 72
     uint32_t selected_id; // pump or valve id
69 73
     uint32_t selected_time; // runtime
74
+    unsigned long start_time, stop_time, last_animation_time;
75
+    String error_condition;
70 76
 };
71 77
 
72 78
 #endif // _STATEMACHINE_H_

+ 6
- 0
src/GPIOBank.cpp View File

@@ -42,6 +42,12 @@ void GPIOBank::setPin(int n, bool state) {
42 42
     }
43 43
 }
44 44
 
45
+void GPIOBank::setAll(bool state) {
46
+    for (int i = 0; i < size; i++) {
47
+        setPin(i, state);
48
+    }
49
+}
50
+
45 51
 bool GPIOBank::getPin(int n) {
46 52
     if ((n >= 0) && (n < size)) {
47 53
         return digitalRead(pins[n]);

+ 99
- 0
src/Plants.cpp View File

@@ -0,0 +1,99 @@
1
+#include <Arduino.h>
2
+#include "Plants.h"
3
+    
4
+// valves: no of plants + 1 for water inlet
5
+// pumps: no of fertilizers
6
+// switches: 2, low and high level
7
+Plants::Plants(int valve_count, int pump_count, int switch_count) :
8
+        valves(valve_count), pumps(pump_count), switches(switch_count) {
9
+}
10
+
11
+void Plants::setValvePins(int pins[]) {
12
+    valves.setPinNumbers(pins);
13
+    valves.setOutput();
14
+    valves.setAll(false);
15
+}
16
+
17
+void Plants::setPumpPins(int pins[]) {
18
+    pumps.setPinNumbers(pins);
19
+    pumps.setOutput();
20
+    pumps.setAll(false);
21
+}
22
+
23
+void Plants::setSwitchPins(int pins[], bool pullup) {
24
+    switches.setPinNumbers(pins);
25
+    switches.setInput(pullup);
26
+}
27
+
28
+void Plants::abort(void) {
29
+    closeWaterInlet();
30
+    stopAllFertilizers();
31
+    stopAllPlants();
32
+}
33
+
34
+Plants::Waterlevel Plants::getWaterlevel(void) {
35
+    bool low = !switches.getPin(0);
36
+    bool high = !switches.getPin(1);
37
+    
38
+    if ((!low) && (!high)) {
39
+        return empty;
40
+    } else if (low && (!high)) {
41
+        return inbetween;
42
+    } else if (low && high) {
43
+        return full;
44
+    } else {
45
+        return invalid;
46
+    }
47
+}
48
+
49
+void Plants::openWaterInlet(void) {
50
+    valves.setPin(countPlants(), true);
51
+}
52
+
53
+void Plants::closeWaterInlet(void) {
54
+    valves.setPin(countPlants(), false);
55
+}
56
+
57
+int Plants::countFertilizers(void) {
58
+    return pumps.getSize();
59
+}
60
+
61
+void Plants::startFertilizer(int id) {
62
+    if ((id >= 0) && (id < countFertilizers())) {
63
+        pumps.setPin(id, true);
64
+    }
65
+}
66
+
67
+void Plants::stopFertilizer(int id) {
68
+    if ((id >= 0) && (id < countFertilizers())) {
69
+        pumps.setPin(id, false);
70
+    }
71
+}
72
+
73
+void Plants::stopAllFertilizers(void) {
74
+    for (int i = 0; i < countFertilizers(); i++) {
75
+        stopFertilizer(i);
76
+    }
77
+}
78
+
79
+int Plants::countPlants(void) {
80
+    return valves.getSize() - 1;
81
+}
82
+
83
+void Plants::startPlant(int id) {
84
+    if ((id >= 0) && (id < countPlants())) {
85
+        valves.setPin(id, true);
86
+    }
87
+}
88
+
89
+void Plants::stopPlant(int id) {
90
+    if ((id >= 0) && (id < countPlants())) {
91
+        valves.setPin(id, false);
92
+    }
93
+}
94
+
95
+void Plants::stopAllPlants(void) {
96
+    for (int i = 0; i < countPlants(); i++) {
97
+        stopPlant(i);
98
+    }
99
+}

+ 195
- 58
src/Statemachine.cpp View File

@@ -1,4 +1,4 @@
1
-#include <Arduino.h>
1
+#include "Plants.h"
2 2
 #include "Statemachine.h"
3 3
 
4 4
 Statemachine::DigitBuffer::DigitBuffer(int _size) {
@@ -53,11 +53,16 @@ uint32_t Statemachine::DigitBuffer::getNumber(void) {
53 53
 Statemachine::Statemachine(print_fn _print, backspace_fn _backspace)
54 54
         : db(7) {
55 55
     state = init;
56
+    old_state = init;
56 57
     print = _print;
57 58
     backspace = _backspace;
58 59
     
59 60
     selected_id = 0;
60 61
     selected_time = 0;
62
+    start_time = 0;
63
+    stop_time = 0;
64
+    last_animation_time = 0;
65
+    error_condition = "";
61 66
 }
62 67
 
63 68
 void Statemachine::begin(void) {
@@ -78,15 +83,7 @@ void Statemachine::input(int n) {
78 83
             switch_to(init);
79 84
         }
80 85
     } else if (state == menu_auto) {
81
-        if ((n == -1) || (n == -2)) {
82
-            switch_to(menu);
83
-        } else if (n == 1) {
84
-            // water only
85
-            
86
-        } else if (n == 2) {
87
-            // with fertilizer
88
-            
89
-        }
86
+        switch_to(menu);
90 87
     } else if (state == menu_auto_mode) {
91 88
         switch_to(menu);
92 89
     } else if (state == menu_auto_go) {
@@ -108,8 +105,12 @@ void Statemachine::input(int n) {
108 105
             
109 106
             selected_id = number_input();
110 107
             
111
-            // TODO validate
112
-            switch_to(menu_pumps_time);
108
+            if ((selected_id <= 0) || (selected_id > plants.countFertilizers())) {
109
+                error_condition = "Invalid pump ID!";
110
+                switch_to(error);
111
+            } else {
112
+                switch_to(menu_pumps_time);
113
+            }
113 114
         } else {
114 115
             if (db.spaceLeft()) {
115 116
                 db.addDigit(n);
@@ -132,8 +133,12 @@ void Statemachine::input(int n) {
132 133
             
133 134
             selected_time = number_input();
134 135
             
135
-            // TODO validate
136
-            switch_to(menu_pumps_go);
136
+            if ((selected_time <= 0) || (selected_time > 120)) {
137
+                error_condition = "Invalid time range!";
138
+                switch_to(error);
139
+            } else {
140
+                switch_to(menu_pumps_go);
141
+            }
137 142
         } else {
138 143
             if (db.spaceLeft()) {
139 144
                 db.addDigit(n);
@@ -142,9 +147,29 @@ void Statemachine::input(int n) {
142 147
             }
143 148
         }
144 149
     } else if (state == menu_pumps_go) {
145
-        switch_to(menu);
150
+        if (n == -2) {
151
+            start_time = millis();
152
+            last_animation_time = start_time;
153
+            
154
+            auto wl = plants.getWaterlevel();
155
+            if ((wl != Plants::full) && (wl != Plants::invalid)) {
156
+                plants.startFertilizer(selected_id - 1);
157
+                switch_to(menu_pumps_running);
158
+            } else if (wl == Plants::full) {
159
+                stop_time = millis();
160
+                switch_to(menu_pumps_done);
161
+            } else if (wl == Plants::invalid) {
162
+                error_condition = "Invalid sensor state";
163
+                state = menu_pumps;
164
+                switch_to(error);
165
+            }
166
+        } else {
167
+            switch_to(menu_pumps_time);
168
+        }
146 169
     } else if (state == menu_pumps_running) {
147
-        switch_to(menu);
170
+            plants.abort();
171
+            stop_time = millis();
172
+            switch_to(menu_pumps_done);
148 173
     } else if (state == menu_pumps_done) {
149 174
         switch_to(menu);
150 175
     } else if (state == menu_valves) {
@@ -162,8 +187,12 @@ void Statemachine::input(int n) {
162 187
             
163 188
             selected_id = number_input();
164 189
             
165
-            // TODO validate
166
-            switch_to(menu_valves_time);
190
+            if ((selected_id <= 0) || (selected_id > (plants.countPlants() + 1))) {
191
+                error_condition = "Invalid valve ID!";
192
+                switch_to(error);
193
+            } else {
194
+                switch_to(menu_valves_time);
195
+            }
167 196
         } else {
168 197
             if (db.spaceLeft()) {
169 198
                 db.addDigit(n);
@@ -186,8 +215,12 @@ void Statemachine::input(int n) {
186 215
             
187 216
             selected_time = number_input();
188 217
             
189
-            // TODO validate
190
-            switch_to(menu_valves_go);
218
+            if ((selected_time <= 0) || (selected_time > 120)) {
219
+                error_condition = "Invalid time range!";
220
+                switch_to(error);
221
+            } else {
222
+                switch_to(menu_valves_go);
223
+            }
191 224
         } else {
192 225
             if (db.spaceLeft()) {
193 226
                 db.addDigit(n);
@@ -196,11 +229,42 @@ void Statemachine::input(int n) {
196 229
             }
197 230
         }
198 231
     } else if (state == menu_valves_go) {
199
-        switch_to(menu);
232
+        if (n == -2) {
233
+            start_time = millis();
234
+            last_animation_time = start_time;
235
+            
236
+            auto wl = plants.getWaterlevel();
237
+            if ((wl != Plants::full) && (wl != Plants::invalid)) {
238
+                if (selected_id >= (plants.countPlants() + 1)) {
239
+                    plants.openWaterInlet();
240
+                } else {
241
+                    plants.startPlant(selected_id - 1);
242
+                }
243
+                
244
+                switch_to(menu_valves_running);
245
+            } else if (wl == Plants::full) {
246
+                stop_time = millis();
247
+                switch_to(menu_valves_done);
248
+            } else if (wl == Plants::invalid) {
249
+                error_condition = "Invalid sensor state";
250
+                state = menu_valves;
251
+                switch_to(error);
252
+            }
253
+        } else {
254
+            switch_to(menu_valves_time);
255
+        }
200 256
     } else if (state == menu_valves_running) {
201
-        switch_to(menu);
257
+            plants.abort();
258
+            stop_time = millis();
259
+            switch_to(menu_valves_done);
202 260
     } else if (state == menu_valves_done) {
203 261
         switch_to(menu);
262
+    } else if (state == error) {
263
+        if (old_state != error) {
264
+            switch_to(old_state);
265
+        } else {
266
+            switch_to(menu);
267
+        }
204 268
     }
205 269
 }
206 270
 
@@ -219,10 +283,41 @@ uint32_t Statemachine::number_input(void) {
219 283
 }
220 284
 
221 285
 void Statemachine::act(void) {
286
+    if ((state == menu_pumps_running) || (state == menu_valves_running)) {
287
+        unsigned long runtime = millis() - start_time;
288
+        if ((runtime / 1000UL) >= selected_time) {
289
+            // stop if timeout has been reached
290
+            plants.abort();
291
+            stop_time = millis();
292
+            switch_to((state == menu_pumps_running) ? menu_pumps_done : menu_valves_done);
293
+        } else if ((millis() - last_animation_time) >= 500) {
294
+            // update animation if needed
295
+            last_animation_time = millis();
296
+            switch_to(state);
297
+        }
298
+    }
222 299
     
300
+    if ((state == menu_pumps_running) || ((state == menu_valves_running) && (selected_id == (plants.countPlants() + 1)))) {
301
+        // check water level state
302
+        auto wl = plants.getWaterlevel();
303
+        if (wl == Plants::full) {
304
+            plants.abort();
305
+            stop_time = millis();
306
+            switch_to((state == menu_pumps_running) ? menu_pumps_done : menu_valves_done);
307
+        } else if (wl == Plants::invalid) {
308
+            plants.abort();
309
+            error_condition = "Invalid sensor state";
310
+            state = (state == menu_pumps_running) ? menu_pumps : menu_valves;
311
+            switch_to(error);
312
+        }
313
+    }
223 314
 }
224 315
 
225 316
 void Statemachine::switch_to(States s) {
317
+    if (s == error) {
318
+        old_state = state;
319
+    }
320
+    
226 321
     state = s;
227 322
     
228 323
     if (s == init) {
@@ -239,87 +334,129 @@ void Statemachine::switch_to(States s) {
239 334
               -1);
240 335
     } else if (s == menu_auto) {
241 336
         print("------- Auto -------",
242
-              "1: Water only",
243
-              "2: With fertilizer",
337
+              "",
338
+              "TODO not implemented",
244 339
               "",
245 340
               -1);
246 341
     } else if (s == menu_auto_mode) {
247
-        print("",
248
-              "",
342
+        print("menu_auto_mode",
249 343
               "",
344
+              "TODO not implemented",
250 345
               "",
251 346
               -1);
252 347
     } else if (s == menu_auto_go) {
253
-        print("",
254
-              "",
348
+        print("menu_auto_go",
255 349
               "",
350
+              "TODO not implemented",
256 351
               "",
257 352
               -1);
258 353
     } else if (s == menu_auto_done) {
259
-        print("",
260
-              "",
354
+        print("menu_auto_done",
261 355
               "",
356
+              "TODO not implemented",
262 357
               "",
263 358
               -1);
264 359
     } else if (s == menu_pumps) {
360
+        String a = String("(Input 1 to ") + String(plants.countFertilizers()) + String(")");
361
+        
265 362
         print("------- Pump -------",
266 363
               "Please select pump",
267
-              "(Input 1 to 3)",
364
+              a.c_str(),
268 365
               "Pump: ",
269 366
               3);
270 367
     } else if (s == menu_pumps_time) {
271
-        print("------ Pump X ------",
368
+        String header = String("------ Pump ") + String(selected_id) + String(" ------");
369
+        
370
+        print(header.c_str(),
272 371
               "Please set runtime",
273 372
               "(Input in seconds)",
274 373
               "Runtime: ",
275 374
               3);
276 375
     } else if (s == menu_pumps_go) {
277
-        print("",
278
-              "",
279
-              "",
280
-              "",
376
+        String a = String("Pump No. ") + String(selected_id);
377
+        String b = String("Runtime ") + String(selected_time) + String('s');
378
+        
379
+        print("----- Confirm? -----",
380
+              a.c_str(),
381
+              b.c_str(),
382
+              "           # Confirm",
281 383
               -1);
282 384
     } else if (s == menu_pumps_running) {
283
-        print("",
284
-              "",
285
-              "",
286
-              "",
385
+        unsigned long runtime = millis() - start_time;
386
+        String a = String("Runtime: ") + String(runtime / 1000UL) + String("s / ") + String(selected_time) + String('s');
387
+        
388
+        unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
389
+        String b;
390
+        for (unsigned long i = 0; i < anim; i++) {
391
+            b += '#';
392
+        }
393
+        
394
+        print("---- Dispensing ----",
395
+              a.c_str(),
396
+              b.c_str(),
397
+              "Hit any key to stop!",
287 398
               -1);
288 399
     } else if (s == menu_pumps_done) {
289
-        print("",
290
-              "",
291
-              "",
292
-              "",
400
+        String a = String("after ") + String((stop_time - start_time) / 1000UL) + String("s.");
401
+        
402
+        print("------- Done -------",
403
+              "Dispensing finished",
404
+              a.c_str(),
405
+              "Hit any key for menu",
293 406
               -1);
294 407
     } else if (s == menu_valves) {
408
+        String a = String("(Input 1 to ") + String(plants.countPlants() + 1) + String(")");
409
+        
295 410
         print("------ Valves ------",
296 411
               "Please select valve",
297
-              "(Input 1 to 5)",
412
+              a.c_str(),
298 413
               "Valve: ",
299 414
               3);
300 415
     } else if (s == menu_valves_time) {
301
-        print("----- Valve XX -----",
416
+        String header = String("----- Valve  ") + String(selected_id) + String(" -----");
417
+        
418
+        print(header.c_str(),
302 419
               "Please set runtime",
303 420
               "(Input in seconds)",
304 421
               "Runtime: ",
305 422
               3);
306 423
     } else if (s == menu_valves_go) {
307
-        print("",
308
-              "",
309
-              "",
310
-              "",
424
+        String a = String("Valve No. ") + String(selected_id);
425
+        String b = String("Runtime ") + String(selected_time) + String('s');
426
+        
427
+        print("----- Confirm? -----",
428
+              a.c_str(),
429
+              b.c_str(),
430
+              "           # Confirm",
311 431
               -1);
312 432
     } else if (s == menu_valves_running) {
313
-        print("",
314
-              "",
315
-              "",
316
-              "",
433
+        unsigned long runtime = millis() - start_time;
434
+        String a = String("Runtime: ") + String(runtime / 1000UL) + String("s / ") + String(selected_time) + String('s');
435
+        
436
+        unsigned long anim = runtime * 20UL / (selected_time * 1000UL);
437
+        String b;
438
+        for (unsigned long i = 0; i <= anim; i++) {
439
+            b += '#';
440
+        }
441
+        
442
+        print("---- Dispensing ----",
443
+              a.c_str(),
444
+              b.c_str(),
445
+              "Hit any key to stop!",
317 446
               -1);
318 447
     } else if (s == menu_valves_done) {
319
-        print("",
320
-              "",
321
-              "",
322
-              "",
448
+        String a = String("after ") + String((stop_time - start_time) / 1000UL) + String("s.");
449
+        
450
+        print("------- Done -------",
451
+              "Dispensing finished",
452
+              a.c_str(),
453
+              "Hit any key for menu",
454
+              -1);
455
+    } else if (s == error) {
456
+        print("------ Error! ------",
457
+              "There is a problem:",
458
+              error_condition.c_str(),
459
+              "    Press any key...",
323 460
               -1);
324 461
     }
325 462
 }

+ 8
- 12
src/main.cpp View File

@@ -2,7 +2,7 @@
2 2
 #include "Keymatrix.h"
3 3
 #include "SerialLCD.h"
4 4
 #include "Statemachine.h"
5
-#include "GPIOBank.h"
5
+#include "Plants.h"
6 6
 
7 7
 //#define DEBUG_WAIT_FOR_SERIAL_CONN
8 8
 
@@ -14,11 +14,10 @@ SerialLCD lcd(9);
14 14
 Keymatrix keys(4, 3);
15 15
 int keymatrix_pins[4 + 3] = { 5, 6, 7, 8, 2, 3, 4 };
16 16
 
17
-GPIOBank valves(5);
18
-int valve_pins[5] = { 9, 10, 11, 12, 13 };
19
-
20
-GPIOBank pumps(3);
21
-int pump_pins[5] = { 14, 15, 16 };
17
+Plants plants(5, 3, 2);
18
+int valve_pins[5] = { 10, 11, 12, 13, 14 };
19
+int pump_pins[3] = { 15, 16, 17 };
20
+int switch_pins[2] = { 18, 19 };
22 21
 
23 22
 #define DISPLAY_BACKLIGHT_TIMEOUT (5UL * 60UL * 1000UL)
24 23
 unsigned long last_input_time = 0;
@@ -115,12 +114,9 @@ void setup() {
115 114
     Serial.println("Initializing Giess-o-mat");
116 115
     
117 116
     keys.setPins(keymatrix_pins);
118
-    
119
-    valves.setPinNumbers(valve_pins);
120
-    valves.setOutput();
121
-    
122
-    pumps.setPinNumbers(pump_pins);
123
-    pumps.setOutput();
117
+    plants.setValvePins(valve_pins);
118
+    plants.setPumpPins(pump_pins);
119
+    plants.setSwitchPins(switch_pins, true);
124 120
 
125 121
     Serial.println("Setting up LCD, please wait");
126 122
     delay(1000); // give LCD some time to boot

Loading…
Cancel
Save