-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathDoorSM.cpp
237 lines (205 loc) · 8.41 KB
/
DoorSM.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
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
#include "DoorSM.h"
#include "PinDefines.h"
#include <string>
// Constructor
DoorSM::DoorSM() {
doorTimer.reset(new Timer(1000, &DoorSM::onTimeout, *this, true)); // Timer(period, callback, oneshot)
}
void DoorSM::init(SupervisorState_t supState,std::bitset<SIZE_OF_FLAGS_ENUM> *flags){
// read the last motor pins from EEPROM
EEPROM.get(MOTOR_DIR_ADDR, motorDir);
if(motorDir == 0xFF) {
Serial.println("DoorSM: motorDir not in EEPROM, initializing.");
// EEPROM was empty -> initialize value
setMotorDir(true);
} else{
Serial.println("DoorSM: setting motor direction from EEPROM");
setMotorDir(motorDir);
}
// initialize state
if(flags->test(SWITCH_UPPER_FLAG)) {
transitionTo(DOOR_OPEN);
}else if (flags->test(SWITCH_LOWER_FLAG)) {
transitionTo(DOOR_CLOSED);
}else{
switch(supState){
case AUTO_OPEN:
transitionTo(DOOR_OPENING);
break;
case AUTO_CLOSED:
transitionTo(DOOR_CLOSING);
break;
case MANUAL_MODE: // Should never get here, always startup in auto mode
transitionTo(DOOR_CLOSING);
break;
}
oscCount = 0;
}
// consume exit event so we don't do this the first time we enter runSM()
isExit = false;
}
/*********************** STATE MACHINE **************************************/
void DoorSM::runSM(std::bitset<SIZE_OF_FLAGS_ENUM> *flags) {
switch(currentState) {
case DOOR_CLOSED:
if(isEntry) {
oscCount = 0; // reset oscillation count
// stop motor in free running mode
analogWrite(MOTOR_PWM_PIN, 0);
flags->set(EVENT_CLOSED_FLAG); // report door closed event
isEntry = false;
}
if(flags->test(COMMAND_OPEN_FLAG)) { // open door command
transitionTo(DOOR_OPENING);
}
break;
case DOOR_OPENING:
if(isEntry) {
// start motor in opening direction
digitalWrite(motorOpenPin, HIGH);
digitalWrite(motorClosePin, LOW);
analogWrite(MOTOR_PWM_PIN, OPENING_SPEED);
doorTimer->changePeriod(NO_SWITCH_TIMEOUT);
doorTimer->start();
isEntry = false;
}
if(flags->test(SWITCH_UPPER_FLAG)) { // door opened
transitionTo(DOOR_OPEN);
} else if(flags->test(COMMAND_CLOSE_FLAG)){ // close door command
transitionTo(DOOR_CLOSING);
} else if(timeoutOccurred) { // NO_SWITCH_TIMEOUT occurred, report the error and switch to closing
timeoutOccurred = false; // clear flag
flags->set(FAULT_OPENING_FLAG); // report the timeout
oscCount++; // increment oscillation count
if(oscCount > MAX_OSCILLATIONS) {
transitionTo(NO_RECOVER);
}else{
transitionTo(DOOR_CLOSING);
}
}
if(isExit) {
doorTimer->stop();
}
break;
case DOOR_OPEN:
if(isEntry) {
oscCount = 0; // reset oscillation count
// start motor in brake mode
digitalWrite(motorOpenPin, LOW);
digitalWrite(motorClosePin, LOW);
analogWrite(MOTOR_PWM_PIN, OPENING_SPEED);
flags->set(EVENT_OPENED_FLAG);
isEntry = false;
}
if(flags->test(COMMAND_CLOSE_FLAG)){ // close door command
transitionTo(DOOR_CLOSING);
}
break;
case DOOR_CLOSING:
if(isEntry) {
// start motor in close direction
digitalWrite(motorOpenPin, LOW);
digitalWrite(motorClosePin, HIGH);
analogWrite(MOTOR_PWM_PIN, CLOSING_SPEED);
doorTimer->changePeriod(UPPER_RELEASE_TIMEOUT); // timer for releasing the upper pin, will catch if we're going the wrong way
doorTimer->start();
isReleased = false;
isEntry = false;
}
if(flags->test(SWITCH_LOWER_FLAG)) {
transitionTo(DOOR_CLOSED);
}
else if(!isReleased && !flags->test(SWITCH_UPPER_FLAG)) {
Serial.println("DoorSM:upper switch released");
isReleased = true;
doorTimer->changePeriod(NO_SWITCH_TIMEOUT); // now change to timing the close
doorTimer->start();
}
else if(timeoutOccurred) {
timeoutOccurred = false; // clear flag
if(isReleased) { // NO_SWITCH_TIMEOUT occurred, report the error and switch to opening
flags->set(FAULT_CLOSING_FLAG); // report timeout
oscCount++;
if(oscCount>MAX_OSCILLATIONS) {
transitionTo(NO_RECOVER);
}else{
transitionTo(DOOR_OPENING); // go the other way
}
}else{ // UPPER_RELEASE_TIMEOUT occured, we're pulling against the upper limit switch
flags->set(FAULT_NO_RELEASE_ON_CLOSE_FLAG); // report the error
setMotorDir(!motorDir); // switch dir pins
oscCount++; // increment oscillation count TODO: should we count this as an oscillation? when would this happen where reversing wouldn't fix it?
if(oscCount>MAX_OSCILLATIONS) {
transitionTo(NO_RECOVER);
}else{
transitionTo(DOOR_CLOSING); // transition back to this state, which will start motor in opp direction
}
}
}
else if (isReleased && flags->test(SWITCH_UPPER_FLAG)){ // We wrapped around to the upper pin
flags->set(FAULT_MISSEDCLOSE_FLAG); // report the error
setMotorDir(!motorDir); // switch dir pins
oscCount++;
if(oscCount>MAX_OSCILLATIONS) {
transitionTo(NO_RECOVER);
}else{
transitionTo(DOOR_CLOSING); // transition back to this state, which will start motor in opp sirection
}
}else if(flags->test(COMMAND_OPEN_FLAG)) { // open door command received
transitionTo(DOOR_OPENING);
}else if(flags->test(SWITCH_BUMPER_FLAG)) { // dog squished, open the door to prevent further squishing
flags->set(FAULT_DOGSQUISHED_FLAG);
transitionTo(DOOR_OPENING);
}
if(isExit) {
doorTimer->stop();
}
break;
case NO_RECOVER:
if(isEntry) {
analogWrite(MOTOR_PWM_PIN, 0); // stop the motor in freerunning mode
flags->set(FAULT_NONRECOVER_FLAG); // report the error
}
// No way out
break;
}
// clear command flags, they should have been dealt with appropriately by this point
flags->reset(COMMAND_CLOSE_FLAG);
flags->reset(COMMAND_OPEN_FLAG);
// ensure that isExit is false, if there were exit actions they should have been resolved by here
isExit = false;
}
DoorState_t DoorSM::getState() {
return currentState;
}
void DoorSM::transitionTo(DoorState_t to) {
char message[100];
sprintf(message, "DoorSM: Transitioning from %s to %s", DoorStateNames[currentState].c_str(), DoorStateNames[to].c_str());
Serial.println(message);
Particle.publish("Log", message);
currentState = to;
isEntry = true;
isExit = true;
char publishString[10];
sprintf(publishString, "%d", to);
// publish the event
Particle.publish("DoorState", publishString);
}
void DoorSM::setMotorDir(bool dir) {
if(dir) {
motorOpenPin = MOTOR_DIR1_PIN;
motorClosePin = MOTOR_DIR2_PIN;
}else{
motorOpenPin = MOTOR_DIR2_PIN;
motorClosePin = MOTOR_DIR1_PIN;
}
if(dir != motorDir) {
// we changed the value, so write it to EEPROM
motorDir = dir;
EEPROM.put(MOTOR_DIR_ADDR, motorDir);
}
}
/*************** TIMER AND INTERRUPT CALLBACKS*********************/
void DoorSM::onTimeout() {
timeoutOccurred = true;
}