// GarageDoorWithPosition Rev4 20th May 2015 // Rev 4 Compiles with V2.2 of ESP8266.com plug-in // Rev 3 Auto reconnects to WiFi network, ignores user input for position slider // Rev 2 disables Open / Close button for 2 sec after press to prevent double press, enables Stop/Start button instead // Rev 1 uses pfodESP8266WiFi library and BufferedClient // Also now pushbutton also opens/closes door. // // To connect to your network // Turn off power // hold down pushbutton // Turn on power // release pushbutton // connect to WiFi AccessPoint pfodWifiWebConfig suing password given below // Open 10.1.1.1 in web browser and complete config // /* ===== pfod Command for Garage Door Angle ==== */ // Using OLIMEX_ESP8266_EVB board /* * (c)2014-2015 Forward Computing and Control Pty. Ltd. * NSW Australia, www.forward.com.au * This generated code may be freely used for both private and commerical use */ // Download pfodESP8266WiFi.zip library from http://www.forward.com.au/pfod/pfodParserLibraries/index.html #include #include #include "pfodESP8266Utils.h" #include "pfodESP8266WebConfig.h" #include "ESP8266WebServer.h" #include "pfodESP8266BufferedClient.h" // Download DeboucedSwitch.zip library from http://www.forward.com.au/pfod/ArduinoProgramming/DebouncedSwitch/DebouncedSwitch.html #include // Download pfodParser.zip library from http://www.forward.com.au/pfod/pfodParserLibraries/index.html #include pfodSecurity parser; // create a parser to handle the pfod messages // normally DEBUG is commented out //#define DEBUG WiFiServer server(80); WiFiClient client; pfodESP8266BufferedClient bufferedClient; // =============== start of pfodESP8266WebConfig settings ============== // update this define with the password from your QR code // http://www.forward.com.au/pfod/secureChallengeResponse/keyGenerator/index.html #define pfodWifiWebConfigPASSWORD "b0Ux9akSiwKkwCtcnjTnpWp" #define pfodWifiWebConfigAP "pfodWifiWebConfig" // note pfodSecurity uses 19 bytes of eeprom usually starting from 0 so // start the eeprom address from 20 for configureWifiConfig const uint8_t webConfigEEPROMStartAddress = 20; int wifiSetup_pin = 2; // name the input pin for setup mode detection GPIO2 on most ESP8266 boards // =============== end of pfodESP8266WebConfig settings ============== int wifiSetup_GND_pin = 0; // the pin that provides the GND for the setup Pin. GPIO0 on most ESP8266 boards, -1 if not used, i.e. on NON-ESP8266 boards pfodESP8266WebConfig webConfig(webConfigEEPROMStartAddress); // webConfig for ESP8266 DebouncedSwitch sw(wifiSetup_pin); // monitor a switch on input wifiSetup_pin uint32_t timeout = 0; unsigned long currentTime = 0; const unsigned long ADC_INTERVAL = 10; // 10mS unsigned long ADCtimerStart = 0; const int ADC_PIN = 17; // PIN for adc const int FILTER_SIZE_POWER = 5; const size_t FILTER_SIZE = (1 << FILTER_SIZE_POWER) - 1; uint16_t filter[FILTER_SIZE + 1]; // storage for filter 0 to 15 unsigned long filterSum; // current sum size_t filterIdx = 0; // index into filter int filteredValue = 0; // the current filter output long percent = 0; // door open % const int CLOSED_LIMIT_PIN = 12; const int OPENED_LIMIT_PIN = 13; // set pin for controlling reed relay const int relayPin = 5; unsigned long pulseStartTime = 0; // the time when we started the relay pulse bool pulseTimerRunning = false; // set to true when we have a timer running unsigned long RELAY_PULSE_LENGTH = 750; // 0.75sec keep all timing in unsigned long unsigned long doublePressDelayStartTime = 0; unsigned long DOUBLE_PRESS_DELAY = 2000; // 2 sec for Open and close buttons only bool closeLimitTripped = false; bool openLimitTripped = false; bool isOpenPosition = false; // true when mobile will show OPEN button bool isClosePosition = false; // true when mobile will show CLOSE button void setup ( void ) { EEPROM.begin(512); // must be greater than (wifiConfigStartAddress + EEPROM_storageSize) delay(10); pinMode(0, OUTPUT); digitalWrite(wifiSetup_GND_pin, LOW); // make GPIO0 low after ESP8266 has initialized for (int i = 4; i > 0; i--) { delay(500); if (digitalRead(wifiSetup_pin) == LOW) { break; // continue to config mode } } #ifdef DEBUG // initial baud rate in DEBUG is 115200 Serial.begin(115200); Serial.println(); Serial.println(F("Starting Setup")); #endif //============ pfodESP8266WebConfig config in Access Point mode ==================== // see if config button is pressed if (digitalRead(wifiSetup_pin) == LOW) { #ifdef DEBUG Serial.println(F("Setting up Access Point for pfodWifiWebConfig")); #endif // connect to temporary wifi network for setup // the features determine the format of the {set...} command webConfig.setupAP(pfodWifiWebConfigAP, pfodWifiWebConfigPASSWORD); // sets WiFi.mode(WIFI_AP); while (1) { webConfig.handleClient(); // process the requests delay(0); } // Need to reboot afterwards } //============ end pfodESP8266WebConfig config ==================== // else button was not pressed continue to load the stored network settings //else use configured setttings from EEPROM WiFi.mode(WIFI_STA); // load existing config if there is one pfodESP8266WebConfig::BASIC_STORAGE *storage = webConfig.loadConfig(); server.close(); // close any existing server server = WiFiServer(storage->portNo); // Start the server server.begin(); #ifdef DEBUG Serial.print("Server started on "); Serial.println(storage->portNo); #endif // initialize client client = server.available(); // evaluates to false if no connection // <<<<<<<<< Your extra setup code goes here pinMode(relayPin, OUTPUT); digitalWrite(relayPin, LOW); // off pinMode(OPENED_LIMIT_PIN, INPUT_PULLUP); pinMode(CLOSED_LIMIT_PIN, INPUT_PULLUP); initializeFilter(); currentTime = millis(); ADCtimerStart = currentTime; } void initializeFilter() { for (size_t i = 0; i <= FILTER_SIZE; i++) { filter[i] = 0; } filterSum = 0; filterIdx = 0; filteredValue = 0; } int filterReading(int adcReading) { // get current reading in this idx int idxReading = filter[filterIdx]; // update sum filterSum += adcReading; filterSum -= idxReading; filter[filterIdx] = adcReading; filterIdx++; filterIdx &= FILTER_SIZE; // loop if needed filteredValue = filterSum >> FILTER_SIZE_POWER; // divide by filter size a power of 2 // limit to 1023 filteredValue &= 1023; return filteredValue; } boolean alreadyConnected = false; // whether or not the client was connected previously // the loop routine runs over and over again forever: void loop() { webConfig.joinWiFiIfNotConnected(); // rejoin if we lost WiFi network if (!client) { // see if a client is available client = server.available(); // evaluates to false if no connection } else { // have client if (!client.connected()) { if (alreadyConnected) { // client closed so clean up closeConnection(parser.getPfodAppStream()); } } else { // have connected client if (!alreadyConnected) { #ifdef DEBUG Serial.println("Got connection "); #endif client.setNoDelay(true); // does not do much if anything parser.connect(bufferedClient.connect(&client), F(pfodWifiWebConfigPASSWORD)); // sets new io stream to read from and write to EEPROM.commit(); // does nothing if nothing to do alreadyConnected = true; } byte cmd = parser.parse(); // pass it to the parser // parser returns non-zero when a pfod command is fully parsed if (cmd != 0) { // have parsed a complete msg { to } byte* pfodFirstArg = parser.getFirstArg(); // may point to \0 if no arguments in this msg. long pfodLongRtn; // used for parsing long return arguments, if any if ('.' == cmd) { // pfodApp has connected and sent {.} , it is asking for the main menu // send back the menu designed sendMainMenu(); // now handle commands returned from button/sliders } else if ('o' == cmd) { // user pressed -- 'Stop/Start' // always process this command startRelayPulse(); sendMainMenuUpdate(); } else if (('O' == cmd) || ('C' == cmd)) { // user pressed -- 'Open', 'Close' if ((closeLimitTripped || openLimitTripped) && (!openClosedPressedInLast2sec())) { // only process this command if still at Open or Closed limit // at least 2sec since last Open/Close press startRelayPulse(); doublePressDelayStartTime = millis(); // start open/closed timer again } sendMainMenuUpdate(); // always send update even it cmd NOT processed. // ignore 'A' cmd // } else if ('A' == cmd) { // user pressed slider } else if ('!' == cmd) { // CloseConnection command closeConnection(parser.getPfodAppStream()); } else { // unknown command parser.print(F("{}")); // always send back a pfod msg otherwise pfodApp will disconnect. } } } } // <<<<<<<<<<< Your other loop() code goes here sw.update(); // call this every loop to update switch state if (sw.isChanged()) { // debounced switch changed state Up or Down // isChanged() is only true for one loop(), cleared when update() called again if (!sw.isDown()) { // switch was just released // pulse relay on release, if already running just extends pulse startRelayPulse(); } } controlRelay(); // call this each loop checkOpenedClosedLimitSwitches(); currentTime = millis(); if ((currentTime - ADCtimerStart) > ADC_INTERVAL) { ADCtimerStart = currentTime; int i = analogRead(ADC_PIN); delay(0); int filteredADC = filterReading(i); percent = (filteredADC - 678); percent = 100 * percent; delay(0); percent = percent / 317; // division takes time delay(0); updateOpenClose(); } } bool openClosedPressedInLast2sec() { currentTime = millis(); return ((currentTime - doublePressDelayStartTime) < DOUBLE_PRESS_DELAY); } void updateOpenClose() { isOpenPosition = false; isClosePosition = false; if (percent > 90) { isOpenPosition = true; } if (percent < 1) { isClosePosition = true; } if (isClosePosition && isOpenPosition) { // invalid isOpenPosition = false; isClosePosition = false; } } void checkOpenedClosedLimitSwitches() { openLimitTripped = (digitalRead(OPENED_LIMIT_PIN) == LOW); closeLimitTripped = (digitalRead(CLOSED_LIMIT_PIN) == LOW); if (openLimitTripped && closeLimitTripped) { // invalid openLimitTripped = false; closeLimitTripped = false; } if (openClosedPressedInLast2sec()) { // assume the door is going to move soon openLimitTripped = false; closeLimitTripped = false; } } // this is called on receiving // a command to open/close // sets up time and turns relay on void startRelayPulse() { pulseStartTime = millis(); pulseTimerRunning = true; // make pin high digitalWrite(relayPin, HIGH); #ifdef DEBUG Serial.println("Start pulse"); #endif } // this is called each loop // turns relay off once time // exceeds stopTime void controlRelay() { // else if (pulseTimerRunning && ((millis() - pulseStartTime) > RELAY_PULSE_LENGTH)) { pulseTimerRunning = false; // timer finished // make pin low digitalWrite(relayPin, LOW); #ifdef DEBUG Serial.println("End pulse"); #endif } } void closeConnection(Stream *io) { #ifdef DEBUG Serial.println("closeConnection called "); #endif // add any special code here to force connection to be dropped parser.closeConnection(); // nulls io stream alreadyConnected = false; bufferedClient.stop(); // clears client reference WiFiClient::stopAll(); // stop all clients and release memory. client = server.available(); // evaluates to false if no connection } void sendMainMenu() { parser.print(F("{^")); // start a Menu screen pfod message send_menuContents(); // send the menu contents for Garage Door Angle parser.print(F("}")); // close pfod message } void sendMainMenuUpdate() { parser.print(F("{:")); // start an Update Menu pfod message send_menuContents(); // send the menu contents for Garage Door Angle parser.print(F("}")); // close pfod message } // modify this method if you need to update the menu to reflect state changes void send_menuContents() { checkOpenedClosedLimitSwitches(); // do this here to clear limitTripped if have just pressed open or close button parser.print("<+4>Garage Door`1000|||O~"); if (closeLimitTripped) { parser.print("<+2>Open"); } parser.print("|C~"); if (openLimitTripped) { parser.print("<+2>Close"); } parser.print("|o~"); if (openLimitTripped || closeLimitTripped) { // do not show stop start } else { parser.print("Stop/Start"); } parser.print(F(" | A~<+1>")); if (isClosePosition) { parser.print(""); parser.print(F("Door Closed (`")); parser.print(percent); parser.print("`100`0~% open)"); } else { if (isOpenPosition) { parser.print(""); } else { parser.print(""); } parser.print(F("Door `")); // disable user input parser.print(percent); parser.print("`100`0~% open"); } // ============ end of menu item =========== } // ============= end generated code =========