// // NOTE: Bottom & Top servos are the other way round ('Top' is fitted to the bottom of the door & vice versa) // #include Servo TopLockServo; // create a servo object to control the top servo// Servo BottomLockServo; // create a servo object to control the bottom servo #include #include #include #include #include // V2.31 or higher int swap01(int); // method prototype for slider end swaps pfodSecurity parser("V3"); // create a parser with menu version string to handle the pfod messages pfodESP8266BufferedClient bufferedClient; #define WLAN_SSID "**********" // cannot be longer than 32 characters! #define WLAN_PASS "**********" //#define DEBUG #define pfodSecurityCode "" const char staticIP[] = "192.168.0.132"; // set this the static IP you want, e.g. "10.1.1.200" or leave it as "" for DHCP. DHCP is not recommended. const int portNo = 4988; // What TCP port to listen on for connections. const int Button = 0; // the number of the pushbutton pin c/w Pullup (Silk Screen Label: D8) const int TopServoSig = 4; // Pin for the servo signal (Silk Screen Label: D15/SCL/D3) const int BottomServoSig = 5; // Pin for the servo signal (Silk Screen Label: D14/SDA/D4) const int IRPin = 12; // IR sensor pin (Silk Screen Label: D12/MISO) const int Power = 13; // Onboard LED (lit when locked) (Silk Screen Label: Tx1/D9) const int AutoClosePin = 14; // Auto close on or off (Silk Screen Label: D13/SCK/D5) const int DebounceDelay = 50; // the debounce time; increase if the output flickers const int Open = 0; // Angle (0-180) to set the servo arm for the bolt open const int Close = 180; // Angle (0-180) to set the servo arm for the bolt closed const int TopMin = 640; // Min PWM setting for 0 degrees (in us; default is 554) : Top servo const int TopMax = 2530; // Max PWM setting for 180 degrees (in us; default is 2400) : Top servo const int BottomMin = 520; // Min PWM setting for 0 degrees (in us; default is 554) : Bottom servo const int BottomMax = 2460; // Max PWM setting for 180 degrees (in us; default is 2400) : Bottom servo boolean BoltShut; // Toggle to decide if it needs to open or close next boolean DoorShut; // Flag for door shut or not boolean AutoBoltTimer = false; // Shuts the bolt automatically after x seconds boolean AutoClose = false; // Flag to use autoclose or not boolean PowerOn = true; // Flag for when the servo power is on or not int lastButtonSteadyState; // The previous steady state from the input pin int lastFlickerableState; // The previous flickerable state from the input pin int currentButtonState; // The current reading from the input pin int lastIRSteadyState; int currentIRState; int TimeToAutoShut; // Count down timer for phone display unsigned long lastDebounceTime = 0; // The last time the output pin was toggled unsigned long CurrentBoltDelayMillis = 0; unsigned long PreviousBoltDelayMillis = 0; unsigned long BoltDelayInterval = 15000; unsigned long CurrentPowerUpMillis; unsigned long PreviousPowerUpMillis; unsigned long PowerUpInterval = 2000; // Time the servo power stays on WiFiServer server(portNo); WiFiClient client; WiFiClient* clientPtr; // non-null if have connected client int cmd_G_var = 2; // name the variable for 'Door is' 0=Open, 1=Closed but Unlocked, 2=Closed and Locked int cmd_H_var = 1; // name the variable for '(Auto-Lock is' 0=Off) 1=On) int cmd_A_var = 15; // name the variable for 'Auto-Locking in' unsigned long plot_mSOffset = 0; // set by {@} response bool clearPlot = false; // set by the {@} response code // the setup routine runs once on reset: void setup() { WiFi.mode(WIFI_STA); EEPROM.begin(512); // only use 20bytes for pfodSecurity but reserve 512 (pfodWifiConfig uses more) #ifdef DEBUG Serial.begin(9600); Serial.println(); #endif if (*staticIP != '\0') { // Initialise wifi module IPAddress ip(pfodESP8266Utils::ipStrToNum(staticIP)); IPAddress gateway(ip[0], ip[1], ip[2], 1); // set gatway #ifdef DEBUG Serial.print(F("Setting gateway to: ")); Serial.println(gateway); #endif IPAddress subnet(255, 255, 255, 0); WiFi.config(ip, gateway, subnet); } WiFi.begin(WLAN_SSID, WLAN_PASS); while (WiFi.status() != WL_CONNECTED) { delay(500); #ifdef DEBUG Serial.print("."); #endif } #ifdef DEBUG Serial.println(); Serial.println("WiFi connected"); #endif server.begin(); // Start the server #ifdef DEBUG Serial.println("Server started"); #endif #ifdef DEBUG Serial.println(WiFi.localIP()); // Print the IP address #endif pinMode(Button, INPUT_PULLUP); // the pull-up input pin will be HIGH when the switch is open and LOW when the switch is closed. pinMode(IRPin, INPUT); // Sets the data from the IR sensor to input pinMode(Power, OUTPUT); // Set the onboard LED pin to output pinMode(AutoClosePin, INPUT); // Sets the data from the Auto Close switch sensor to input TopLockServo.attach(TopServoSig, TopMin, TopMax); // attaches the servo object on pin 4 & trims the 0 & 180 deg positions BottomLockServo.attach(BottomServoSig, BottomMin, BottomMax); // attaches the servo object on pin 5 & trims the 0 & 180 deg positions PowerOn = true; TopLockServo.write(Close); // Close the bolts (otherwise they default to 90 deg on start-up) delay(10); BottomLockServo.write(Close); delay(10); BoltShut = true; } void loop() { if (clientPtr && (!clientPtr->connected())) { closeConnection(parser.getPfodAppStream()); } if (server.hasClient()) { // new connection if (!clientPtr) { // no existing connection client = server.available(); // get any new client clientPtr = &client; // have connected client parser.connect(bufferedClient.connect(clientPtr), F(pfodSecurityCode)); // sets new io stream to read from and write to EEPROM.commit(); // does nothing if nothing to do } else { // already have client so just stop the new one WiFiClient newClient = server.available(); // get any new client newClient.stop(); } } if (clientPtr) { uint8_t cmd = parser.parse(); // parse incoming data from connection if (cmd != 0) { // parser returns non-zero when a pfod command is fully parsed so have parsed a complete msg { to } uint8_t* pfodFirstArg = parser.getFirstArg(); // may point to \0 if no arguments in this msg. pfod_MAYBE_UNUSED(pfodFirstArg); // may not be used, just suppress warning long pfodLongRtn; // used for parsing long return arguments, if any pfod_MAYBE_UNUSED(pfodLongRtn); // may not be used, just suppress warning if ('.' == cmd) { // pfodApp has connected and sent {.} , it is asking for the main menu if (!parser.isRefresh()) { sendMainMenu(); // send back the menu designed } else { sendMainMenuUpdate(); // menu is cached just send update } } else if ('@' == cmd) { // handle {@} request - pfodApp requested 'current' time plot_mSOffset = millis(); // capture current millis as offset rawdata timestamps clearPlot = true; // clear plot on reconnect as have new plot_mSOffset parser.print(F("{@`0}")); // return `0 as 'current' raw data milliseconds } else if ('D' == cmd) { // user pressed -- 'Lock' ToggleBolt(); parser.print(F("{}")); // change this return as needed.// always send back a pfod msg otherwise pfodApp will disconnect. } else if ('!' == cmd) { closeConnection(parser.getPfodAppStream()); // CloseConnection command } else { // unknown command parser.print(F("{}")); // always send back a pfod msg otherwise pfodApp will disconnect. } } } if (PowerOn == true) PowerUp(); // Switches on the power relay with toggle bolt or auto lock #ifdef DEBUG Serial.print (" IR Pin = "); Serial.println(digitalRead(IRPin)); // Serial.print (" "); // Serial.print (" Test Pin = "); // Serial.println(digitalRead(TestPin)); #endif cmd_A_var = TimeToAutoShut; // cmd_A_var is the variable for the 'time to autolock' slider if (digitalRead(AutoClosePin) == HIGH) AutoClose = true; // Sets autolock on or off else AutoClose = false; if (AutoClose) { AutoBolt(); cmd_H_var = 1; // cmd_H_var: =0 'Auto-Lock is Off', =1 'Auto-Lock is On' } else cmd_H_var = 0; currentButtonState = digitalRead(Button); // read the current state of the button: if (digitalRead(IRPin) == HIGH) { DoorShut = true; // DoorShut is true when the beam's broken if (BoltShut)cmd_G_var = 2; // Prints 'Door is Closed and Locked' to the phone else cmd_G_var = 1; // Prints 'Door is Closed, but Unlocked' to the phone } else { cmd_G_var = 0; // Prints 'Door is Open' to the phone DoorShut = false; } if (currentButtonState != lastFlickerableState) { // If the switch/button changed, due to noise or pressing: lastDebounceTime = millis(); // reset the debouncing timer lastFlickerableState = currentButtonState; // save the the last flickerable state } if ((millis() - lastDebounceTime) > DebounceDelay) { // whatever the reading is at, it's been there for longer than the debounce if (lastButtonSteadyState == HIGH && currentButtonState == LOW) {// if the button state has changed from High to Low: ToggleBolt(); } lastButtonSteadyState = currentButtonState; // save the the last steady state } } void closeConnection(Stream *io) { (void)(io); // unused parser.closeConnection(); // add any special code here to force connection to be dropped// nulls io stream bufferedClient.stop(); // clears client reference clientPtr->stop(); clientPtr = NULL; } void sendMainMenu() { parser.print(F("{,")); // start a Menu screen pfod message parser.print(F("<-2>~Garage Side Door Lock v1`1000")); // send menu background, format, prompt, refresh and version parser.sendVersion(); // send the menu version parser.print(F("|!B")); // send menu items parser.print(F("~")); parser.print(F("|!G<-2>")); parser.print('`'); parser.print(cmd_G_var); // output the current value parser.print(F("~~~Door is Open\\Door is Closed but Unlocked\\<00FF00>Door is Closed and Locked~t")); parser.print(F("|!H<-2>")); parser.print('`'); parser.print(cmd_H_var); // output the current value parser.print(F("~~~(Auto-Lock is Off)\\<00FF00>(Auto-Lock is On)~t")); parser.print(F("|!F<+12>")); parser.print(F("~")); parser.print(F("|!C-<+6>")); parser.print(F("~")); parser.print(F("|D<+2>")); parser.print(F("~UnLock")); parser.print(F("|!E<+12>")); parser.print(F("~")); parser.print(F("|!A-<-2>")); parser.print('`'); parser.print(cmd_A_var); // output the current value parser.print(F("~Auto-Locking in ~ secs`0`15~0~15~ ")); parser.print(F("}")); // close pfod message } void sendMainMenuUpdate() { parser.print(F("{;")); // start an Update Menu pfod message // send menu items parser.print(F("|!B")); parser.print(F("|!G")); parser.print('`'); parser.print(cmd_G_var); // output the current value parser.print(F("|!H")); parser.print('`'); parser.print(cmd_H_var); // output the current value parser.print(F("|!F")); parser.print(F("|!C-")); parser.print(F("~")); if (cmd_G_var == 1) { parser.print(F("|D<+2>")); parser.print(F("~Lock")); } else if (cmd_G_var == 2) { parser.print(F("|D<+2>")); parser.print(F("<00FF00>~UnLock")); } else { parser.print(F("~")); parser.print(F("|D-")); } parser.print('`'); parser.print(F("|!E")); if (cmd_A_var == 0 || BoltShut || !AutoClose) parser.print(F("|!A-")); else { parser.print(F("|D-")); parser.print(F("|!A")); } parser.print('`'); parser.print(cmd_A_var); // output the current value parser.print(F("}")); // close pfod message // ============ end of menu =========== } int swap01(int in) { return (in == 0) ? 1 : 0; } void AutoBolt() { // Function to auto close the bolt (when on Auto-Close) currentIRState = digitalRead(IRPin); // read the current state of the IR Sensor: if (lastIRSteadyState == LOW && currentIRState == HIGH) { // if the IR state has changed from High to Low (when door shuts): AutoBoltTimer = true; // Set the timer to active PreviousBoltDelayMillis = CurrentBoltDelayMillis; // Set to zero } lastIRSteadyState = currentIRState; // save the the last steady state CurrentBoltDelayMillis = millis(); // Update the timer if (AutoBoltTimer && !BoltShut) TimeToAutoShut = round((BoltDelayInterval - (CurrentBoltDelayMillis - PreviousBoltDelayMillis)) / 1000); // Time in secs before the bolt auto-shuts if (TimeToAutoShut >= BoltDelayInterval) TimeToAutoShut = 0; // Stops the countdown timer trying to divide by zero if (CurrentBoltDelayMillis - PreviousBoltDelayMillis > BoltDelayInterval) { // After the 'Interval' PreviousBoltDelayMillis = CurrentBoltDelayMillis; if (AutoBoltTimer && DoorShut) { // Don't want the bolts to fire if the door has been opened during countdown. CurrentPowerUpMillis = millis(); // Set the power timer start time PowerOn = true; // Set the power flag PreviousPowerUpMillis = CurrentPowerUpMillis; // Zero the counter digitalWrite(Power, LOW); // Turn on the servo power relay TopLockServo.write(Close); // Close the bolts delay(10); BottomLockServo.write(Close); delay(10); BoltShut = true; // Set the bolt closed flag AutoBoltTimer = false; } } } void ToggleBolt() { if (BoltShut) { // If it's shut, open it, switch off the LED & set the shut flag CurrentPowerUpMillis = millis(); PowerOn = true; PreviousPowerUpMillis = CurrentPowerUpMillis; TopLockServo.write(Open); delay(10); // For stability BottomLockServo.write(Open); delay(10); // For stability BoltShut = false; AutoBoltTimer = false; digitalWrite(Power, LOW); } else { // Else it's open, so close it, switch on the power relay & set the shut flag if (DoorShut) { CurrentPowerUpMillis = millis(); PowerOn = true; PreviousPowerUpMillis = CurrentPowerUpMillis; TopLockServo.write(Close); delay(10); // For stability BottomLockServo.write(Close); delay(10); // For stability BoltShut = true; digitalWrite(Power, LOW); } } } void PowerUp() { CurrentPowerUpMillis = millis(); if (CurrentPowerUpMillis - PreviousPowerUpMillis > PowerUpInterval) { digitalWrite(Power, HIGH); PreviousPowerUpMillis = CurrentPowerUpMillis; PowerOn = false; } } ////////////////////////////////THE END//////////////////////////////////////////////