forked from steff393/hgdo
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathkeyPanel.cpp
189 lines (161 loc) · 5.41 KB
/
keyPanel.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
// Copyright (c) 2021 steff393, MIT license
// based on https://github.com/joeyoung/arduino_keypads
#include <Arduino.h>
#include <autoClose.h>
#include <globalConfig.h>
#include <Keypad_I2C.h>
#include <Keypad.h>
#include <LittleFS.h>
#include <logger.h>
#include <uap.h>
#include <Wire.h>
#define I2C_Addr 0x20 // I2C Address of PCF8574-board: 0x20 - 0x27
#define KEYP_CODE_MAX 20 // different codes
#define KEYP_CODE_LEN 5 // 4 digits, e.g. "1234" + string termination
#define KEYP_TYPE_LEN 2 // 1 digit for the action type + string termination
#define KEYP_NAME_LEN 11 // 10 chars for the person name + string termination
#define MAX_LINE_LEN KEYP_CODE_LEN + KEYP_TYPE_LEN + KEYP_NAME_LEN + 4
#define MAX_TIME_FOR_CODE 8000 // 8s
#define WRONG_CODE_LIMIT 10 // 10 wrong tries are possible
#define WRONG_CODE_DELAY 900000 // 15min
static const uint8_t m = 6;
static const byte ROWS = 4; //four rows
static const byte COLS = 4; //four columns
//define the symbols on the buttons of the keypads
static char keyPadLayout[ROWS][COLS] = {
{'1','2','3','A'},
{'4','5','6','B'},
{'7','8','9','C'},
{'*','0','#','D'}
};
static byte rowPins[ROWS] = {0, 1, 2, 3}; //connect to the row pinouts of the keypad
static byte colPins[COLS] = {4, 5, 6, 7}; //connect to the column pinouts of the keypad
//initialize an instance of class NewKeypad
static Keypad_I2C i2cKeypad( makeKeymap(keyPadLayout), rowPins, colPins, ROWS, COLS, I2C_Addr);
static char code[KEYP_CODE_MAX][KEYP_CODE_LEN];
static char type[KEYP_CODE_MAX][KEYP_TYPE_LEN];
static char name[KEYP_CODE_MAX][KEYP_NAME_LEN];
static char input[KEYP_CODE_LEN];
static uint8_t pos = 0;
static boolean enabled = false;
static uint16_t wrongCodeCnt = 0;
static uint32_t wrongCodeTimer = 0;
static uint32_t startTime = 0;
static boolean readCodes() {
File file = LittleFS.open(F("/code.txt"), "r");
if (!file) {
LOG(m, "Disabled (code.txt not found)", "");
return(false);
}
uint8_t k = 0;
//LOGN(m, "Codes: ", "");
while (file.available() && k < KEYP_CODE_MAX) {
// split the line into tokens limited by ;
char line[MAX_LINE_LEN + 1];
char * pch;
uint16_t nrChars;
nrChars = file.readBytesUntil('\n', line, MAX_LINE_LEN);
pch = strtok(line, ";\n"); *code[k] = '\0'; strncat(code[k], pch, KEYP_CODE_LEN - 1);
pch = strtok(NULL, ";\n"); *type[k] = '\0'; strncat(type[k], pch, KEYP_TYPE_LEN - 1);
pch = strtok(NULL, ";\n"); *name[k] = '\0'; strncat(name[k], pch, KEYP_NAME_LEN - 1);
while (nrChars >= MAX_LINE_LEN) {
// read rest of the line, but simply ignore it
nrChars = file.readBytesUntil('\n', line, MAX_LINE_LEN);
}
// if (k > 0 ) { LOGN(0, ", ", ""); }
// LOGN(0, "%s > %s", code[k], name[k]);
k++;
}
file.close();
return(true);
}
static void checkCode(char * input) {
uint8_t k = 0;
char msg[50];
sprintf_P(msg, PSTR("Tastenfeld: Code %s"), input);
log_file(msg);
if (wrongCodeCnt >= WRONG_CODE_LIMIT) {
// all inputs will be ignored until time has elapsed
return;
}
// Search if this fits to a known code
while (strncasecmp(input, code[k], KEYP_CODE_LEN - 1) != 0 && k < KEYP_CODE_MAX) {
k++;
};
if (k < KEYP_CODE_MAX) {
LOG(0, "found: idx=%d, name=%s => opening", k, name[k]);
switch (atoi(type[k])) {
case 0: {
// open garage door
uap_triggerAction(UAP_ACTION_OPEN, SRC_KEYPAD); break;
}
case 1: {
//start the package drop function, if time is within the allowed range
uint32_t seconds = log_getSecSinceMidnight();
if ((seconds >= (uint32_t)cfgPdTimeOn * 3600) && (seconds < (uint32_t)cfgPdTimeOff * 3600)) {
pd_trigger();
}
break;
}
default: ; // nothing
}
wrongCodeCnt = 0;
} else {
wrongCodeCnt++;
wrongCodeTimer = millis();
LOG(0, "unknown, cnt=%d", wrongCodeCnt);
}
}
void key_setup() {
if (!readCodes()) {
// there is no code.txt file => function disabled
enabled = false;
return;
}
enabled = true;
Wire.begin(PIN_SDA, PIN_SCL);
Wire.beginTransmission(I2C_Addr); // Try to establish connection
if (Wire.endTransmission() != 0) { // No Connection established
LOG(m, "No device found on 0x%02x", I2C_Addr);
} else {
LOG(m, "Device found on 0x%02x", I2C_Addr);
}
i2cKeypad.begin( );
}
void key_loop() {
if (enabled == false) {
// avoid unnecessary calls
return;
}
char key = i2cKeypad.getKey();
if (key != NO_KEY) {
input[pos] = key;
pos++;
if (pos == 1) {
// entering first digit starts a timer
startTime = millis();
}
if (pos == KEYP_CODE_LEN - 1) {
input[pos] = '\0'; // terminate the code
LOGN(m, "%s entered, ", input);
checkCode(input);
pos = 0;
}
if (key == 'D' && pos == 1) {
// when 'D' is the first pressed button, then close
uap_triggerAction(UAP_ACTION_CLOSE, SRC_KEYPAD);
pos = 0; // needed for multiple press of 'D', but this also means, that no code must start with 'D' !!
}
}
if (pos > 0 && millis() - startTime > MAX_TIME_FOR_CODE) {
LOG(m, "Time elapsed", "");
pos = 0;
}
if (wrongCodeCnt && (millis() - wrongCodeTimer > WRONG_CODE_DELAY)) {
wrongCodeCnt--;
wrongCodeTimer = millis();
}
}
boolean key_getEnabled() {
return(enabled);
}