Browse Source

first commit

Thomas Buck 1 month ago
commit
923b30a0ef
13 changed files with 1136 additions and 0 deletions
  1. 1
    0
      .gitignore
  2. 22
    0
      include/GPIOBank.h
  3. 55
    0
      include/Keymatrix.h
  4. 28
    0
      include/SerialLCD.h
  5. 72
    0
      include/Statemachine.h
  6. 46
    0
      lib/README
  7. 23
    0
      platformio.ini
  8. 51
    0
      src/GPIOBank.cpp
  9. 157
    0
      src/Keymatrix.cpp
  10. 92
    0
      src/SerialLCD.cpp
  11. 325
    0
      src/Statemachine.cpp
  12. 253
    0
      src/main.cpp
  13. 11
    0
      test/README

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
1
+.pio

+ 22
- 0
include/GPIOBank.h View File

@@ -0,0 +1,22 @@
1
+#ifndef _GPIO_BANK_H_
2
+#define _GPIO_BANK_H_
3
+
4
+class GPIOBank {
5
+public:
6
+    GPIOBank(int _size);
7
+    ~GPIOBank(void);
8
+    
9
+    void setPinNumbers(int _pins[]);
10
+    void setOutput(void);
11
+    void setInput(bool pullup);
12
+    
13
+    int getSize(void);
14
+    void setPin(int n, bool state);
15
+    bool getPin(int n);
16
+    
17
+private:
18
+    int size;
19
+    int *pins;
20
+};
21
+
22
+#endif // _GPIO_BANK_H_

+ 55
- 0
include/Keymatrix.h View File

@@ -0,0 +1,55 @@
1
+#ifndef _KEYMATRIX_H_
2
+#define _KEYMATRIX_H_
3
+
4
+#include <CircularBuffer.h>
5
+
6
+class Keymatrix {
7
+public:
8
+    class Event {
9
+    public:
10
+        enum EventType {
11
+            button_down,
12
+            button_up,
13
+            no_event
14
+        };
15
+        
16
+        Event(EventType _type, int _row, int _col);
17
+        EventType getType(void);
18
+        int getRow(void);
19
+        int getCol(void);
20
+        
21
+        // helper for 4x3 telephone keypad
22
+        // -1 is *, -2 is #, or digits 0-9
23
+        int getNum(void);
24
+        
25
+    private:
26
+        EventType type;
27
+        int row, col;
28
+    };
29
+    
30
+    Keymatrix(int _rows, int _cols);
31
+    ~Keymatrix(void);
32
+    
33
+    // first rows, then cols
34
+    void setPins(int _pins[]);
35
+    void setDebounce(unsigned long ms);
36
+    
37
+    void scan(void);
38
+    
39
+    bool hasEvent(void);
40
+    Event getEvent(void);
41
+
42
+private:
43
+    unsigned long debounce;
44
+    const static unsigned long default_debounce = 5;
45
+    unsigned long last_scan_time;
46
+    
47
+    int rows, cols;
48
+    int *pins;
49
+    bool *lastPressed;
50
+    bool *lastState;
51
+    
52
+    CircularBuffer<Event *, 32> events;
53
+};
54
+
55
+#endif // _KEYMATRIX_H_

+ 28
- 0
include/SerialLCD.h View File

@@ -0,0 +1,28 @@
1
+#ifndef _SERIAL_LCD_H_
2
+#define _SERIAL_LCD_H_
3
+
4
+#include <SendOnlySoftwareSerial.h>
5
+
6
+class SerialLCD {
7
+public:
8
+    SerialLCD(int tx_pin);
9
+    ~SerialLCD(void);
10
+    
11
+    void init(void);
12
+    void clear(void);
13
+    void setBacklight(uint8_t val);
14
+    
15
+    // 0 no cursor, 1 underline, 2 blinking, 3 both
16
+    void cursor(int style);
17
+    
18
+    void position(int line, int col);
19
+    
20
+    void write(const char *text);
21
+    void write(int line, const char *text);
22
+    void write(int line, int col, const char *text);
23
+    
24
+private:
25
+    SendOnlySoftwareSerial *lcd;
26
+};
27
+
28
+#endif // _SERIAL_LCD_H_

+ 72
- 0
include/Statemachine.h View File

@@ -0,0 +1,72 @@
1
+#ifndef _STATEMACHINE_H_
2
+#define _STATEMACHINE_H_
3
+
4
+class Statemachine {
5
+public:
6
+    enum States {
7
+        init,
8
+        menu, // auto, pumps, valves
9
+        
10
+        menu_auto, // select plant
11
+        menu_auto_mode, // select mode
12
+        menu_auto_go, // running
13
+        menu_auto_done,
14
+        
15
+        menu_pumps, // selet pump
16
+        menu_pumps_time, // set runtime
17
+        menu_pumps_go, // running
18
+        menu_pumps_running,
19
+        menu_pumps_done,
20
+        
21
+        menu_valves, // select valve
22
+        menu_valves_time, // set runtime
23
+        menu_valves_go, // running
24
+        menu_valves_running,
25
+        menu_valves_done
26
+    };
27
+    
28
+    class DigitBuffer {
29
+    public:
30
+        DigitBuffer(int _size);
31
+        ~DigitBuffer();
32
+        
33
+        bool spaceLeft(void);
34
+        bool hasDigits(void);
35
+        int countDigits(void);
36
+        
37
+        void addDigit(int d);
38
+        void removeDigit(void);
39
+        void clear(void);
40
+        
41
+        uint32_t getNumber(void);
42
+        
43
+    private:
44
+        int size;
45
+        int pos;
46
+        int *digits;
47
+    };
48
+    
49
+    typedef void (*print_fn)(const char *, const char *, const char *, const char *, int);
50
+    
51
+    typedef void (*backspace_fn)(void);
52
+    
53
+    Statemachine(print_fn _print, backspace_fn _backspace);
54
+    void begin(void);
55
+    
56
+    void input(int n);
57
+    void act(void);
58
+    
59
+private:
60
+    void switch_to(States s);
61
+    uint32_t number_input(void);
62
+    
63
+    DigitBuffer db;
64
+    States state;
65
+    print_fn print;
66
+    backspace_fn backspace;
67
+    
68
+    uint32_t selected_id; // pump or valve id
69
+    uint32_t selected_time; // runtime
70
+};
71
+
72
+#endif // _STATEMACHINE_H_

+ 46
- 0
lib/README View File

@@ -0,0 +1,46 @@
1
+
2
+This directory is intended for project specific (private) libraries.
3
+PlatformIO will compile them to static libraries and link into executable file.
4
+
5
+The source code of each library should be placed in a an own separate directory
6
+("lib/your_library_name/[here are source files]").
7
+
8
+For example, see a structure of the following two libraries `Foo` and `Bar`:
9
+
10
+|--lib
11
+|  |
12
+|  |--Bar
13
+|  |  |--docs
14
+|  |  |--examples
15
+|  |  |--src
16
+|  |     |- Bar.c
17
+|  |     |- Bar.h
18
+|  |  |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
19
+|  |
20
+|  |--Foo
21
+|  |  |- Foo.c
22
+|  |  |- Foo.h
23
+|  |
24
+|  |- README --> THIS FILE
25
+|
26
+|- platformio.ini
27
+|--src
28
+   |- main.c
29
+
30
+and a contents of `src/main.c`:
31
+```
32
+#include <Foo.h>
33
+#include <Bar.h>
34
+
35
+int main (void)
36
+{
37
+  ...
38
+}
39
+
40
+```
41
+
42
+PlatformIO Library Dependency Finder will find automatically dependent
43
+libraries scanning project source files.
44
+
45
+More information about PlatformIO Library Dependency Finder
46
+- https://docs.platformio.org/page/librarymanager/ldf.html

+ 23
- 0
platformio.ini View File

@@ -0,0 +1,23 @@
1
+; PlatformIO Project Configuration File
2
+;
3
+;   Build options: build flags, source filter
4
+;   Upload options: custom upload port, speed and extra flags
5
+;   Library options: dependencies, extra library storages
6
+;   Advanced options: extra scripting
7
+;
8
+; Please visit documentation for the other options and examples
9
+; https://docs.platformio.org/page/projectconf.html
10
+
11
+[platformio]
12
+default_envs = leonardo
13
+
14
+[env:leonardo]
15
+platform = atmelavr
16
+board = leonardo
17
+framework = arduino
18
+upload_port = /dev/ttyACM0
19
+monitor_port = /dev/ttyACM0
20
+monitor_speed = 115200
21
+lib_deps =
22
+  https://github.com/nickgammon/SendOnlySoftwareSerial
23
+  https://github.com/rlogiacco/CircularBuffer

+ 51
- 0
src/GPIOBank.cpp View File

@@ -0,0 +1,51 @@
1
+#include <Arduino.h>
2
+#include "GPIOBank.h"
3
+
4
+GPIOBank::GPIOBank(int _size) {
5
+    size = _size;
6
+    pins = new int[size];
7
+}
8
+
9
+GPIOBank::~GPIOBank(void) {
10
+    delete pins;
11
+}
12
+
13
+void GPIOBank::setPinNumbers(int _pins[]) {
14
+    for (int i = 0; i < size; i++) {
15
+        pins[i] = _pins[i];
16
+    }
17
+}
18
+
19
+void GPIOBank::setOutput(void) {
20
+    for (int i = 0; i < size; i++) {
21
+        pinMode(pins[i], OUTPUT);
22
+    }
23
+}
24
+
25
+void GPIOBank::setInput(bool pullup) {
26
+    for (int i = 0; i < size; i++) {
27
+        if (pullup) {
28
+            pinMode(pins[i], INPUT_PULLUP);
29
+        } else {
30
+            pinMode(pins[i], INPUT);
31
+        }
32
+    }
33
+}
34
+
35
+int GPIOBank::getSize(void) {
36
+    return size;
37
+}
38
+
39
+void GPIOBank::setPin(int n, bool state) {
40
+    if ((n >= 0) && (n < size)) {
41
+        digitalWrite(pins[n], state ? HIGH : LOW);
42
+    }
43
+}
44
+
45
+bool GPIOBank::getPin(int n) {
46
+    if ((n >= 0) && (n < size)) {
47
+        return digitalRead(pins[n]);
48
+    } else {
49
+        return LOW;
50
+    }
51
+}

+ 157
- 0
src/Keymatrix.cpp View File

@@ -0,0 +1,157 @@
1
+#include <Arduino.h>
2
+#include "Keymatrix.h"
3
+
4
+//#define DEBUG_PRINT_MATRIX_STATE
5
+
6
+Keymatrix::Event::Event(EventType _type, int _row, int _col) {
7
+    type = _type;
8
+    row = _row;
9
+    col = _col;
10
+}
11
+
12
+Keymatrix::Event::EventType Keymatrix::Event::getType(void) {
13
+    return type;
14
+}
15
+
16
+int Keymatrix::Event::getRow(void) {
17
+    return row;
18
+}
19
+
20
+int Keymatrix::Event::getCol(void) {
21
+    return col;
22
+}
23
+
24
+// -1 is *, -2 is #, or digits 0-9
25
+int Keymatrix::Event::getNum(void) {
26
+    if (row == 3) {
27
+        switch (col) {
28
+        case 0:
29
+            return -1; // *
30
+            
31
+        case 2:
32
+            return -2; // #
33
+            
34
+        default:
35
+            return 0;
36
+        }
37
+    } else {
38
+        return (row * 3) + col + 1;
39
+    }
40
+}
41
+
42
+Keymatrix::Keymatrix(int _rows, int _cols) {
43
+    debounce = default_debounce;
44
+    last_scan_time = 0;
45
+    
46
+    rows = _rows;
47
+    cols = _cols;
48
+    
49
+    pins = new int[rows + cols];
50
+    lastPressed = new bool[rows * cols];
51
+    lastState = new bool[rows * cols];
52
+    
53
+    for (int i = 0; i < (rows * cols); i++) {
54
+        lastPressed[i] = false;
55
+        lastState[i] = false;
56
+    }
57
+}
58
+
59
+Keymatrix::~Keymatrix(void) {
60
+    delete pins;
61
+    delete lastPressed;
62
+    delete lastState;
63
+}
64
+
65
+// first rows, then cols
66
+void Keymatrix::setPins(int _pins[]) {
67
+    for (int i = 0; i < (rows + cols); i++) {
68
+        pins[i] = _pins[i];
69
+        
70
+        // rows as outputs, cols as inputs
71
+        if (i < rows) {
72
+            //pinMode(pins[i], OUTPUT);
73
+            pinMode(pins[i], INPUT);
74
+        } else {
75
+            pinMode(pins[i], INPUT_PULLUP);
76
+        }
77
+    }
78
+}
79
+
80
+void Keymatrix::setDebounce(unsigned long ms) {
81
+    debounce = ms;
82
+}
83
+
84
+void Keymatrix::scan(void) {
85
+    // only continue when enough time has passed
86
+    unsigned long current_time = millis();
87
+    if (current_time < (last_scan_time + debounce)) {
88
+        return;
89
+    }
90
+    last_scan_time = current_time;
91
+    
92
+    // disable all rows
93
+    for (int r = 0; r < rows; r++) {
94
+        //digitalWrite(pins[r], HIGH);
95
+        pinMode(pins[r], INPUT);
96
+    }
97
+    
98
+    int buttons = rows * cols;
99
+    bool pressed[buttons];
100
+    
101
+    // go through all rows
102
+    for (int r = 0; r < rows; r++) {
103
+        // enable current row
104
+        pinMode(pins[r], OUTPUT);
105
+        digitalWrite(pins[r], LOW);
106
+        
107
+        // read out all columns
108
+        for (int c = 0; c < cols; c++) {
109
+            int v = digitalRead(pins[rows + c]);
110
+            pressed[(r * cols) + c] = (v == LOW);
111
+        }
112
+        
113
+        // disable current row
114
+        //digitalWrite(pins[r], HIGH);
115
+        pinMode(pins[r], INPUT);
116
+    }
117
+    
118
+#ifdef DEBUG_PRINT_MATRIX_STATE
119
+    for (int i = 0; i < buttons; i++) {
120
+        Serial.print(pressed[i] ? "1" : "0");
121
+        if (i < (buttons - 1)) {
122
+            Serial.print(" ");
123
+        } else {
124
+            Serial.println();
125
+        }
126
+    }
127
+#endif
128
+    
129
+    for (int i = 0; i < buttons; i++) {
130
+        // debounce - compare to previous state
131
+        if ((lastPressed[i] == pressed[i]) && (pressed[i] != lastState[i])) {
132
+            lastState[i] = pressed[i];
133
+            int c = i % cols;
134
+            int r = i / cols;
135
+            Event::EventType et = (pressed[i]) ? Event::button_down : Event::button_up;
136
+            events.push(new Event(et, r, c));
137
+        }
138
+        
139
+        // save current state for next time
140
+        lastPressed[i] = pressed[i];
141
+    }
142
+}
143
+
144
+bool Keymatrix::hasEvent(void) {
145
+    return !events.isEmpty();
146
+}
147
+
148
+Keymatrix::Event Keymatrix::getEvent(void) {
149
+    if (hasEvent()) {
150
+        Event *e = events.shift();
151
+        Event e_copy = *e;
152
+        delete e;
153
+        return e_copy;
154
+    } else {
155
+        return Keymatrix::Event(Event::no_event, -1, -1);
156
+    }
157
+}

+ 92
- 0
src/SerialLCD.cpp View File

@@ -0,0 +1,92 @@
1
+#include <Arduino.h>
2
+#include "SerialLCD.h"
3
+
4
+#define LCD_DELAY 3 // 3
5
+
6
+SerialLCD::SerialLCD(int tx_pin) {
7
+    lcd = new SendOnlySoftwareSerial(tx_pin);
8
+    lcd->begin(9600);
9
+}
10
+
11
+SerialLCD::~SerialLCD(void) {
12
+    delete lcd;
13
+}
14
+
15
+void SerialLCD::init(void) {
16
+    clear();
17
+    cursor(0);
18
+    setBacklight(255);
19
+    
20
+    lcd->write(0x7C);
21
+    lcd->write(0x03); // 0x03=20 chars. 0x04=16 chars
22
+    delay(LCD_DELAY);
23
+    
24
+    lcd->write(0x7C);
25
+    lcd->write(0x05); // 0x05=4 lines, 0x06=2 lines
26
+    delay(LCD_DELAY);
27
+}
28
+
29
+void SerialLCD::clear(void) {
30
+    lcd->write(0xFE);
31
+    lcd->write(0x01);
32
+    delay(LCD_DELAY);
33
+}
34
+
35
+// 0 no cursor, 1 underline, 2 blinking, 3 both
36
+void SerialLCD::cursor(int style) {
37
+    lcd->write(0xFE);
38
+    lcd->write(0x0C); // display on, no cursor
39
+    delay(LCD_DELAY);
40
+    
41
+    if (style == 1) {
42
+        lcd->write(0xFE);
43
+        lcd->write(0x0E); // underline cursor on
44
+        delay(LCD_DELAY);
45
+    } else if (style == 2) {
46
+        lcd->write(0xFE);
47
+        lcd->write(0x0D); // blinking cursor on
48
+        delay(LCD_DELAY);
49
+    } else if (style == 3) {
50
+        lcd->write(0xFE);
51
+        lcd->write(0x0F); // both cursors on
52
+        delay(LCD_DELAY);
53
+    }
54
+}
55
+
56
+void SerialLCD::setBacklight(uint8_t val) {
57
+    val = map(val, 0, 255, 0, 30);
58
+    lcd->write(0x7C);
59
+    lcd->write(0x80 + val);
60
+    
61
+    delay(LCD_DELAY);
62
+}
63
+
64
+void SerialLCD::position(int line, int col) {
65
+    int cursor = 0;
66
+    if (line ==  1) {
67
+        cursor = 64;
68
+    } else if (line == 2) {
69
+        cursor = 20;
70
+    } else if (line == 3) {
71
+        cursor = 84;
72
+    }
73
+    
74
+    lcd->write(0xFE);
75
+    lcd->write(0x80 + cursor + col);
76
+    delay(LCD_DELAY);
77
+}
78
+
79
+void SerialLCD::write(const char *text) {
80
+    lcd->print(text);
81
+    delay(LCD_DELAY);
82
+}
83
+
84
+void SerialLCD::write(int line, const char *text) {
85
+    position(line, 0);
86
+    write(text);
87
+}
88
+
89
+void SerialLCD::write(int line, int col, const char *text) {
90
+    position(line, col);
91
+    write(text);
92
+}

+ 325
- 0
src/Statemachine.cpp View File

@@ -0,0 +1,325 @@
1
+#include <Arduino.h>
2
+#include "Statemachine.h"
3
+
4
+Statemachine::DigitBuffer::DigitBuffer(int _size) {
5
+    size = _size;
6
+    pos = 0;
7
+    digits = new int[size];
8
+}
9
+
10
+Statemachine::DigitBuffer::~DigitBuffer() {
11
+    delete digits;
12
+}
13
+
14
+bool Statemachine::DigitBuffer::spaceLeft(void) {
15
+    return (pos < size);
16
+}
17
+
18
+bool Statemachine::DigitBuffer::hasDigits(void) {
19
+    return (pos > 0);
20
+}
21
+
22
+int Statemachine::DigitBuffer::countDigits(void) {
23
+    return pos;
24
+}
25
+
26
+void Statemachine::DigitBuffer::addDigit(int d) {
27
+    if (spaceLeft()) {
28
+        digits[pos] = d;
29
+        pos++;
30
+    }
31
+}
32
+
33
+void Statemachine::DigitBuffer::removeDigit(void) {
34
+    if (hasDigits()) {
35
+        pos--;
36
+    }
37
+}
38
+
39
+void Statemachine::DigitBuffer::clear(void) {
40
+    pos = 0;
41
+}
42
+
43
+uint32_t Statemachine::DigitBuffer::getNumber(void) {
44
+    uint32_t fact = 1;
45
+    uint32_t sum = 0;
46
+    for (int i = (pos - 1); i >= 0; i--) {
47
+        sum += digits[i] * fact;
48
+        fact *= 10;
49
+    }
50
+    return sum;
51
+}
52
+
53
+Statemachine::Statemachine(print_fn _print, backspace_fn _backspace)
54
+        : db(7) {
55
+    state = init;
56
+    print = _print;
57
+    backspace = _backspace;
58
+    
59
+    selected_id = 0;
60
+    selected_time = 0;
61
+}
62
+
63
+void Statemachine::begin(void) {
64
+    switch_to(init);
65
+}
66
+
67
+void Statemachine::input(int n) {
68
+    if (state == init) {
69
+        switch_to(menu);
70
+    } else if (state == menu) {
71
+        if (n == 1) {
72
+            switch_to(menu_auto);
73
+        } else if (n == 2) {
74
+            switch_to(menu_pumps);
75
+        } else if (n == 3) {
76
+            switch_to(menu_valves);
77
+        } else if ((n == -1) || (n == -2)) {
78
+            switch_to(init);
79
+        }
80
+    } 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
+        }
90
+    } else if (state == menu_auto_mode) {
91
+        switch_to(menu);
92
+    } else if (state == menu_auto_go) {
93
+        switch_to(menu);
94
+    } else if (state == menu_auto_done) {
95
+        switch_to(menu);
96
+    } else if (state == menu_pumps) {
97
+        if (n == -1) {
98
+            if (db.hasDigits()) {
99
+                backspace();
100
+                db.removeDigit();
101
+            } else {
102
+                switch_to(menu);
103
+            }
104
+        } else if (n == -2) {
105
+            if (!db.hasDigits()) {
106
+                return;
107
+            }
108
+            
109
+            selected_id = number_input();
110
+            
111
+            // TODO validate
112
+            switch_to(menu_pumps_time);
113
+        } else {
114
+            if (db.spaceLeft()) {
115
+                db.addDigit(n);
116
+            } else {
117
+                backspace();
118
+            }
119
+        }
120
+    } else if (state == menu_pumps_time) {
121
+        if (n == -1) {
122
+            if (db.hasDigits()) {
123
+                backspace();
124
+                db.removeDigit();
125
+            } else {
126
+                switch_to(menu_pumps);
127
+            }
128
+        } else if (n == -2) {
129
+            if (!db.hasDigits()) {
130
+                return;
131
+            }
132
+            
133
+            selected_time = number_input();
134
+            
135
+            // TODO validate
136
+            switch_to(menu_pumps_go);
137
+        } else {
138
+            if (db.spaceLeft()) {
139
+                db.addDigit(n);
140
+            } else {
141
+                backspace();
142
+            }
143
+        }
144
+    } else if (state == menu_pumps_go) {
145
+        switch_to(menu);
146
+    } else if (state == menu_pumps_running) {
147
+        switch_to(menu);
148
+    } else if (state == menu_pumps_done) {
149
+        switch_to(menu);
150
+    } else if (state == menu_valves) {
151
+        if (n == -1) {
152
+            if (db.hasDigits()) {
153
+                backspace();
154
+                db.removeDigit();
155
+            } else {
156
+                switch_to(menu);
157
+            }
158
+        } else if (n == -2) {
159
+            if (!db.hasDigits()) {
160
+                return;
161
+            }
162
+            
163
+            selected_id = number_input();
164
+            
165
+            // TODO validate
166
+            switch_to(menu_valves_time);
167
+        } else {
168
+            if (db.spaceLeft()) {
169
+                db.addDigit(n);
170
+            } else {
171
+                backspace();
172
+            }
173
+        }
174
+    } else if (state == menu_valves_time) {
175
+        if (n == -1) {
176
+            if (db.hasDigits()) {
177
+                backspace();
178
+                db.removeDigit();
179
+            } else {
180
+                switch_to(menu_valves);
181
+            }
182
+        } else if (n == -2) {
183
+            if (!db.hasDigits()) {
184
+                return;
185
+            }
186
+            
187
+            selected_time = number_input();
188
+            
189
+            // TODO validate
190
+            switch_to(menu_valves_go);
191
+        } else {
192
+            if (db.spaceLeft()) {
193
+                db.addDigit(n);
194
+            } else {
195
+                backspace();
196
+            }
197
+        }
198
+    } else if (state == menu_valves_go) {
199
+        switch_to(menu);
200
+    } else if (state == menu_valves_running) {
201
+        switch_to(menu);
202
+    } else if (state == menu_valves_done) {
203
+        switch_to(menu);
204
+    }
205
+}
206
+
207
+uint32_t Statemachine::number_input(void) {
208
+    for (int i = 0; i < db.countDigits(); i++) {
209
+        backspace();
210
+    }
211
+    
212
+    uint32_t n = db.getNumber();
213
+    db.clear();
214
+    
215
+    Serial.print("Whole number input: ");
216
+    Serial.println(n);
217
+    
218
+    return n;
219
+}
220
+
221
+void Statemachine::act(void) {
222
+    
223
+}
224
+
225
+void Statemachine::switch_to(States s) {
226
+    state = s;
227
+    
228
+    if (s == init) {
229
+        print("- Giess-o-mat V0.1 -",
230
+              "Usage:  Enter number",
231
+              "* Delete prev. digit",
232
+              "# Execute input num.",
233
+              -1);
234
+    } else if (s == menu) {
235
+        print("------- Menu -------",
236
+              "1: Automatic program",
237
+              "2: Fertilizer pumps",
238
+              "3: Outlet valves",
239
+              -1);
240
+    } else if (s == menu_auto) {
241
+        print("------- Auto -------",
242
+              "1: Water only",
243
+              "2: With fertilizer",
244
+              "",
245
+              -1);
246
+    } else if (s == menu_auto_mode) {
247
+        print("",
248
+              "",
249
+              "",
250
+              "",
251
+              -1);
252
+    } else if (s == menu_auto_go) {
253
+        print("",
254
+              "",
255
+              "",
256
+              "",
257
+              -1);
258
+    } else if (s == menu_auto_done) {
259
+        print("",
260
+              "",
261
+              "",
262
+              "",
263
+              -1);
264
+    } else if (s == menu_pumps) {
265
+        print("------- Pump -------",
266
+              "Please select pump",
267
+              "(Input 1 to 3)",
268
+              "Pump: ",
269
+              3);
270
+    } else if (s == menu_pumps_time) {
271
+        print("------ Pump X ------",
272
+              "Please set runtime",
273
+              "(Input in seconds)",
274
+              "Runtime: ",
275
+              3);
276
+    } else if (s == menu_pumps_go) {
277
+        print("",
278
+              "",
279
+              "",
280
+              "",
281
+              -1);
282
+    } else if (s == menu_pumps_running) {
283
+        print("",
284
+              "",
285
+              "",
286
+              "",
287
+              -1);
288
+    } else if (s == menu_pumps_done) {
289
+        print("",
290
+              "",
291
+              "",
292
+              "",
293
+              -1);
294
+    } else if (s == menu_valves) {
295
+        print("------ Valves ------",
296
+              "Please select valve",
297
+              "(Input 1 to 5)",
298
+              "Valve: ",
299
+              3);
300
+    } else if (s == menu_valves_time) {
301
+        print("----- Valve XX -----",
302
+              "Please set runtime",
303
+              "(Input in seconds)",
304
+              "Runtime: ",
305
+              3);
306
+    } else if (s == menu_valves_go) {
307
+        print("",
308
+              "",
309
+              "",
310
+              "",
311
+              -1);
312
+    } else if (s == menu_valves_running) {
313
+        print("",
314
+              "",
315
+              "",
316
+              "",
317
+              -1);
318
+    } else if (s == menu_valves_done) {
319
+        print("",
320
+              "",
321
+              "",
322
+              "",
323
+              -1);
324
+    }
325
+}

+ 253
- 0
src/main.cpp View File

@@ -0,0 +1,253 @@
1
+#include <Arduino.h>
2
+#include "Keymatrix.h"
3
+#include "SerialLCD.h"
4
+#include "Statemachine.h"
5
+#include "GPIOBank.h"
6
+
7
+//#define DEBUG_WAIT_FOR_SERIAL_CONN
8
+
9
+#define DEBUG_ENABLE_LCD_OUTPUT_ON_SERIAL
10
+#define DEBUG_ENABLE_KEYPAD_INPUT_ON_SERIAL
11
+
12
+SerialLCD lcd(9);
13
+
14
+Keymatrix keys(4, 3);
15
+int keymatrix_pins[4 + 3] = { 5, 6, 7, 8, 2, 3, 4 };
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 };
22
+
23
+#define DISPLAY_BACKLIGHT_TIMEOUT (5UL * 60UL * 1000UL)
24
+unsigned long last_input_time = 0;
25
+bool backlight_state = true;
26
+
27
+bool doing_multi_input = false;
28
+
29
+void write_to_all(const char *a, const char *b,
30
+                  const char *c, const char *d, int num_input) {
31
+    lcd.clear();
32
+    
33
+    if (num_input >= 0) {
34
+        lcd.write(0, a);
35
+        if (num_input >= 1) {
36
+            lcd.write(1, b);
37
+        }
38
+        if (num_input >= 2) {
39
+            lcd.write(2, c);
40
+        }
41
+        if (num_input >= 3) {
42
+            lcd.write(3, d);
43
+        }
44
+        
45
+        lcd.cursor(3);
46
+        doing_multi_input = true;
47
+    } else {
48
+        lcd.write(0, a);
49
+        lcd.write(1, b);
50
+        lcd.write(2, c);
51
+        lcd.write(3, d);
52
+        
53
+        lcd.cursor(0);
54
+        doing_multi_input = false;
55
+    }
56
+    
57
+#ifdef DEBUG_ENABLE_LCD_OUTPUT_ON_SERIAL
58
+    int la = strlen(a);
59
+    int lb = strlen(b);
60
+    int lc = strlen(c);
61
+    int ld = strlen(d);
62
+    
63
+    Serial.println();
64
+    Serial.println(" ----------------------");
65
+    
66
+    Serial.print("| ");
67
+    Serial.print(a);
68
+    if (la < 20) {
69
+        for (int i = 0; i < (20 - la); i++) {
70
+            Serial.print(' ');
71
+        }
72
+    }
73
+    Serial.println(" |");
74
+    
75
+    Serial.print("| ");
76
+    Serial.print(b);
77
+    if (lb < 20) {
78
+        for (int i = 0; i < (20 - lb); i++) {
79
+            Serial.print(' ');
80
+        }
81
+    }
82
+    Serial.println(" |");
83
+    
84
+    Serial.print("| ");
85
+    Serial.print(c);
86
+    if (lc < 20) {
87
+        for (int i = 0; i < (20 - lc); i++) {
88
+            Serial.print(' ');
89
+        }
90
+    }
91
+    Serial.println(" |");
92
+    
93
+    Serial.print("| ");
94
+    Serial.print(d);
95
+    if (ld < 20) {
96
+        for (int i = 0; i < (20 - ld); i++) {
97
+            Serial.print(' ');
98
+        }
99
+    }
100
+    Serial.println(" |");
101
+    
102
+    Serial.println(" ----------------------");
103
+    Serial.println("Please provide keypad input:");
104
+#endif
105
+}
106
+
107
+void backspace(void) {
108
+    lcd.write("\b");
109
+}
110
+
111
+Statemachine sm(write_to_all, backspace);
112
+
113
+void setup() {
114
+    Serial.begin(115200);
115
+    Serial.println("Initializing Giess-o-mat");
116
+    
117
+    keys.setPins(keymatrix_pins);
118
+    
119
+    valves.setPinNumbers(valve_pins);
120
+    valves.setOutput();
121
+    
122
+    pumps.setPinNumbers(pump_pins);
123
+    pumps.setOutput();
124
+
125
+    Serial.println("Setting up LCD, please wait");
126
+    delay(1000); // give LCD some time to boot
127
+    lcd.init();
128
+    
129
+#ifdef DEBUG_WAIT_FOR_SERIAL_CONN
130
+    lcd.write(0, "Waiting for serial");
131
+    lcd.write(1, "connection on debug");
132
+    lcd.write(2, "USB port...");
133
+    
134
+    while (!Serial);
135
+    
136
+    lcd.clear();
137
+#endif
138
+    
139
+    Serial.println("Ready, starting main loop");
140
+    sm.begin();
141
+}
142
+
143
+void blink_lcd(int n, int wait = 200) {
144
+    for (int i = 0; i < n; i++) {
145
+        lcd.setBacklight(0);
146
+        delay(wait);
147
+        
148
+        lcd.setBacklight(255);
149
+        if (i < (n - 1))
150
+            delay(wait);
151
+    }
152
+}
153
+
154
+void loop() {
155
+    keys.scan();
156
+    while (keys.hasEvent()) {
157
+        auto ke = keys.getEvent();
158
+        if (ke.getType() == Keymatrix::Event::button_down) {
159
+            last_input_time = millis();
160
+            if (!backlight_state) {
161
+                backlight_state = true;
162
+                lcd.setBacklight(255);
163
+                
164
+                // swallow input when used to activate light
165
+                continue;
166
+            }
167
+            
168
+            int n = ke.getNum();
169
+            Serial.print("Got keypad input: \"");
170
+            
171
+            if (n < 0) {
172
+                Serial.print((n == -1) ? '*' : '#');
173
+            } else {
174
+                Serial.print(n);
175
+                
176
+                if (doing_multi_input) {
177
+                    char s[2] = { (char)(n + '0'), '\0' };
178
+                    lcd.write(s);
179
+                }
180
+            }
181
+            
182
+            Serial.println("\"");
183
+            
184
+            blink_lcd(1, 100);
185
+            sm.input(n);
186
+        }
187
+    }
188
+    
189
+#ifdef DEBUG_ENABLE_KEYPAD_INPUT_ON_SERIAL
190
+    if (Serial.available() > 0) {
191
+        last_input_time = millis();
192
+        if (!backlight_state) {
193
+            backlight_state = true;
194
+            lcd.setBacklight(255);
195
+        }
196
+        
197
+        int c = Serial.read();
198
+        if (c == '*') {
199
+            Serial.write(c);
200
+            Serial.write('\n');
201
+            
202
+            if (doing_multi_input) {
203
+                char s[2] = { (char)(c), '\0' };
204
+                lcd.write(s);
205
+            }
206
+            
207
+            sm.input(-1);
208
+        } else if  (c == '#') {
209
+            Serial.write(c);
210
+            Serial.write('\n');
211
+            
212
+            if (doing_multi_input) {
213
+                char s[2] = { (char)(c), '\0' };
214
+                lcd.write(s);
215
+            }
216
+            
217
+            sm.input(-2);
218
+        } else if  (c == '\n') {
219
+            Serial.write('#');
220
+            Serial.write('\n');
221
+            
222
+            if (doing_multi_input) {
223
+                char s[2] = { '#', '\0' };
224
+                lcd.write(s);
225
+            }
226
+            
227
+            sm.input(-2);
228
+        } else if (c == '\b') {
229
+            Serial.write(c);
230
+            sm.input(-1);
231
+        } else if ((c >= '0') && (c <= '9')) {
232
+            Serial.write(c);
233
+            if (!doing_multi_input) {
234
+                Serial.write('\n');
235
+            }
236
+            
237
+            if (doing_multi_input) {
238
+                char s[2] = { (char)(c), '\0' };
239
+                lcd.write(s);
240
+            }
241
+            
242
+            sm.input(c - '0');
243
+        }
244
+    }
245
+#endif
246
+    
247
+    sm.act();
248
+    
249
+    if (backlight_state && (millis() >= (last_input_time + DISPLAY_BACKLIGHT_TIMEOUT))) {
250
+        backlight_state = false;
251
+        lcd.setBacklight(0);
252
+    }
253
+}

+ 11
- 0
test/README View File

@@ -0,0 +1,11 @@
1
+
2
+This directory is intended for PlatformIO Unit Testing and project tests.
3
+
4
+Unit Testing is a software testing method by which individual units of
5
+source code, sets of one or more MCU program modules together with associated
6
+control data, usage procedures, and operating procedures, are tested to
7
+determine whether they are fit for use. Unit testing finds problems early
8
+in the development cycle.
9
+
10
+More information about PlatformIO Unit Testing:
11
+- https://docs.platformio.org/page/plus/unit-testing.html

Loading…
Cancel
Save