-
Notifications
You must be signed in to change notification settings - Fork 0
/
doggiedoor.ino
283 lines (241 loc) · 9.4 KB
/
doggiedoor.ino
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
#include "DoorSM.h"
#include "SupervisorSM.h"
#include "AutoCloseSM.h"
#include "FlagDefines.h"
#include "PinDefines.h"
#include "HX711.h"
#include <bitset>
std::bitset<SIZE_OF_FLAGS_ENUM> myFlags; // initialize all flags false
const bool DEBUG = false;
bool status = false;
int calibration_factor = 23000;
//const unsigned int MAX_SENSOR_PULSEWIDTH = 23200;
//const unsigned int INRANGE_THRESH_INCHES = 20;
//unsigned long t1;
//unsigned long t2;
//unsigned long pulse_width;
//float inches;
//Timer sensorTriggerTimer(1000, sendTrigger, true);
Timer sendRSSITimer(15*60*1000, publishRSSI);
DoorSM myDoor;
SupervisorSM mySupervisor(6, 22); // default to start at 6AM, end at 10PM
AutoCloseSM myAutoClose;
HX711 *scale = NULL;
void setup()
{
Time.zone(-8); // use PST
Particle.function("getSupState", getSupState);
Particle.function("setIsAuto", setIsAuto);
Particle.function("getDoorState", getDoorState);
Particle.function("openDoor", openDoor);
Particle.function("closeDoor", closeDoor);
//initialize pins
pinMode(UPPER_LIMIT_PIN, INPUT);
pinMode(LOWER_LIMIT_PIN, INPUT);
pinMode(BUMPER_LIMIT_PIN, INPUT);
pinMode(MOTOR_PWM_PIN, OUTPUT);
pinMode(MOTOR_DIR1_PIN, OUTPUT);
pinMode(MOTOR_DIR2_PIN, OUTPUT);
pinMode(STATUS_LED_PIN, OUTPUT);
Serial.begin(9600);
// attach interrupts for sensor
/* attachInterrupt(SENSOR_ECHO_PIN, echoChange, CHANGE);
digitalWrite(SENSOR_TRIGGER_PIN, LOW);
sendTrigger(); // start the sensor */
digitalWrite(STATUS_LED_PIN, LOW);
if(!DEBUG) checkInputs(); // check all switches so we set the flags before we call init on statemachines
// initialize state machines
Particle.publish("Log", "************INITIALIZING DOOR ***************");
mySupervisor.init();
myDoor.init(mySupervisor.getState(), &myFlags);
// publish the RSSI signal strength on start and every x minutes after
publishRSSI();
sendRSSITimer.start();
}
void loop()
{
digitalWrite(STATUS_LED_PIN, status);
myFlags.reset(SENSOR_INRANGE_FLAG); // TODO: For now, sensor is always low until we find a fix
// run the state machines
myDoor.runSM(&myFlags);
mySupervisor.runSM(&myFlags);
// Only run AutoClose state machine if supervisor is in the right state
if(mySupervisor.getState() == AUTO_CLOSED){
// Check if we need to initialize the state machine
if(myFlags.test(INIT_AUTOCLOSE_FLAG)) {
myAutoClose.init(myDoor.getState(), &myFlags);
myFlags.reset(INIT_AUTOCLOSE_FLAG); // clear flag
}
myAutoClose.runSM(&myFlags);
}
// print out events and statements
if(myFlags.test(EVENT_CLOSED_FLAG)) {
if(DEBUG) Serial.println("Main: Door closed event");
if(mySupervisor.getState() == MANUAL_MODE){
Particle.publish("Log", "Event: Door closed manually");
}else{
Particle.publish("Log", "Event: Door closed automatically");
}
myFlags.reset(EVENT_CLOSED_FLAG);
}
if(myFlags.test(EVENT_OPENED_FLAG)) {
if(DEBUG) Serial.println("Main: Door open event");
switch(mySupervisor.getState()){
case MANUAL_MODE:
Particle.publish("Log", "Event: Door opened manually");
break;
case AUTO_OPEN:
Particle.publish("Log", "Event: Door opened automatically");
break;
case AUTO_CLOSED:
Particle.publish("Log", "Event: Door opened automatically");
Particle.publish("Error", "Door opened during autoclose"); // TODO: take this out when we fix sensor
break;
}
myFlags.reset(EVENT_OPENED_FLAG);
}
if(myFlags.test(FAULT_OPENING_FLAG)) {
if(DEBUG) Serial.println("Main: Door opening fault");
Particle.publish("Log", "Fault: Door timed out on open");
myFlags.reset(FAULT_OPENING_FLAG);
}
if(myFlags.test(FAULT_CLOSING_FLAG)) {
if(DEBUG) Serial.println("Main: Door closing timeout");
Particle.publish("Log", "Fault: Door timed out on close");
myFlags.reset(FAULT_CLOSING_FLAG);
}
if(myFlags.test(FAULT_MISSEDCLOSE_FLAG)) {
if(DEBUG) Serial.println("Main: Missed the lower limit switch during close");
Particle.publish("Log", "Fault: Missed the lower limit switch");
myFlags.reset(FAULT_MISSEDCLOSE_FLAG);
}
if(myFlags.test(FAULT_DOGSQUISHED_FLAG)) {
if(DEBUG) Serial.println("Main: Dog squished");
Particle.publish("Log", "Fault: Dog squished");
myFlags.reset(FAULT_DOGSQUISHED_FLAG);
}
if(myFlags.test(FAULT_NO_RELEASE_ON_CLOSE_FLAG)) {
if(DEBUG) Serial.println("Main: Upper limit switch not released during open, possibly wrong direction.");
Particle.publish("Log", "Fault: Upper limit switch not released during open, possibly wrong direction.");
myFlags.reset(FAULT_NO_RELEASE_ON_CLOSE_FLAG);
}
if(myFlags.test(FAULT_NONRECOVER_FLAG)) { // NOTE this fault goes last, no others will report after it since it enters a while(true) statement
// nonrecoverable error, caused by too many oscillations of the door
String message = "NONRECOVERABLE!!! Door tried to open and close too many times. Check door and restart";
if(DEBUG) Serial.println("Main: Nonrecoverable, door tried to open and close too many times. Check door and restart");
Particle.publish("Log", "Fault: NONRECOVERABLE!!! Door tried to open and close too many times. Check door and restart");
Particle.publish("Error", "NONRECOVERABLE!!! Door tried to open and close too many times. Check door and restart"); // TODO: take this out when we fix sensor
while(true) Particle.process(); // maintain cloud connection but don't allow any other commands
}
if(!DEBUG)checkInputs(); // check inputs at end so that serial events override it
}
void serialEvent()
{
// only look at serial inputs if we're debugging
if(DEBUG) {
// this is called after loop() so we can change flags without worrying about "thread safety"
char c = Serial.read();
// this is just for debugging (i.e. stepping through code) so lets make sure we reset the sw at a time by resetting the switch flags
//myFlags.reset(SWITCH_UPPER_FLAG);
myFlags.reset(SWITCH_LOWER_FLAG);
myFlags.reset(SWITCH_BUMPER_FLAG);
switch(c) {
case 'u': // upper limit switch
myFlags.flip(SWITCH_UPPER_FLAG);
Serial.printf("Main: upper now set to %s\n", myFlags.test(SWITCH_UPPER_FLAG)?"true":"false");
break;
case 'l': // lower limit switch
myFlags.set(SWITCH_LOWER_FLAG);
break;
case 'b': // bumper limit switch
myFlags.set(SWITCH_BUMPER_FLAG);
break;
case 'p': //PIR sensor
myFlags.set(SENSOR_INRANGE_FLAG);
break;
case 'o': // open command
myFlags.set(COMMAND_OPEN_FLAG);
break;
case 'c': // closed command
myFlags.set(COMMAND_CLOSE_FLAG);
break;
case 'q': // query state:
Serial.printf("Door state: %s\n", DoorStateNames[myDoor.getState()].c_str());
Serial.printf("Supervisor state: %s\n", SupervisorStateNames[mySupervisor.getState()].c_str());
if(mySupervisor.getState() == AUTO_CLOSED) {
Serial.printf("AutoClose state: %s\n", AutoCloseStateNames[myAutoClose.getState()].c_str());
}
break;
case 's': // set start time
mySupervisor.setStartHour(Serial.parseInt());
break;
case 'e': // set end time
mySupervisor.setEndHour(Serial.parseInt());
break;
case 'm': // toggle manual mode
mySupervisor.setIsAuto(!mySupervisor.getIsAuto());
break;
case 'f': // print out bit from bitset
Serial.printf("Bitset = %s\n", myFlags.to_string().c_str());
break;
case ' ': // AUX
//Serial.printf("Current hour = %i, Start time = %i, End time = %i\n",Time.hour(), mySupervisor.getStartHour(), mySupervisor.getEndHour());
break;
break;
}
}
}
void checkInputs() {
// Check if the switch flags are HIGH
digitalRead(LOWER_LIMIT_PIN)?myFlags.set(SWITCH_LOWER_FLAG):myFlags.reset(SWITCH_LOWER_FLAG);
digitalRead(UPPER_LIMIT_PIN)?myFlags.set(SWITCH_UPPER_FLAG):myFlags.reset(SWITCH_UPPER_FLAG);
digitalRead(BUMPER_LIMIT_PIN)?myFlags.set(SWITCH_BUMPER_FLAG):myFlags.reset(SWITCH_BUMPER_FLAG);
}
/*********************Particle functions ************************************/
int getSupState(String command) {
return mySupervisor.getState();
}
int setIsAuto(String command) {
mySupervisor.setIsAuto(strcmp(command,"true")==0); // strcmp returns 0 if they match!!
return 0;
}
int openDoor(String command) {
myFlags.set(COMMAND_OPEN_FLAG);
return 0;
}
int closeDoor(String command) {
myFlags.set(COMMAND_CLOSE_FLAG);
return 0;
}
int getDoorState(String command) {
return myDoor.getState();
}
/**************** sensor interrupts ****************/
/*void echoChange() {
if (digitalRead(SENSOR_ECHO_PIN)) {
t1 = micros();
}else{
status = !status;
// stop timing and calculate distance
t2 = micros();
pulse_width = t2 - t1;
inches = pulse_width / 148.0;
// set sensor flag
inches < INRANGE_THRESH_INCHES ? myFlags.set(SENSOR_INRANGE_FLAG) : myFlags.reset(SENSOR_INRANGE_FLAG);
// start timer for sending trigger
sensorTriggerTimer.startFromISR();
}
}
void sendTrigger() {
// send a trigger to the ultrasonic sensor
digitalWrite(SENSOR_TRIGGER_PIN, HIGH);
delayMicroseconds(10); // Hold the trigger pin high for at least 10 us
digitalWrite(SENSOR_TRIGGER_PIN, LOW);
}*/
/****************** util ******************/
void publishRSSI() {
// send rssi value to the logs
char message[10];
sprintf(message, "RSSI = %d dB\n", WiFi.RSSI());
Particle.publish("RSSI", message);
}