// Lock: a door lock whose bolt takes 3 s to move (Alexa.LockController), answered with a deferred response. // // Lock and Unlock start the bolt and are answered at once with a DeferredResponse that names 5 s. When the bolt // has arrived, loop() sends the answer itself, with the correlationToken of the directive, which the sketch has // kept. While the bolt moves, another Lock or Unlock is refused with ENDPOINT_BUSY. // // A switch from BLOCKED_PIN to ground stands for a bolt that cannot move: the answer then reports JAMMED. #include #include #include // Wi-Fi and the credentials of your Alex2MQTT account const char *WIFI_SSID = ""; const char *WIFI_PASSWORD = ""; const char *ALEXA_USERNAME = ""; const char *ALEXA_PASSWORD = ""; const char *ALEXA_ROOT_TOPIC = ""; const uint8_t BOLT_PIN = 5; // D1 on a Wemos D1 mini: high = locked const uint8_t BLOCKED_PIN = 14; // D5: low = the bolt is blocked const unsigned long BOLT_MS = 3000; Alex2ESP alexa; AlexaDevice *doorLock; AlexaLockState state = AlexaLockState::LOCKED; AlexaLockState goal = AlexaLockState::LOCKED; bool moving = false; unsigned long movingSince = 0; String token; // the correlationToken of the directive that is being carried out // Joins the Wi-Fi network and returns after 30 s at the latest. Without a connection the sketch carries on: the // ESP8266 keeps trying, and alexa.loop() opens the MQTT session once Wi-Fi is up. void connectWiFi() { WiFi.mode(WIFI_STA); WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.printf("\n[WIFI] Connecting to \"%s\"\n", WIFI_SSID); unsigned long started = millis(); while (WiFi.status() != WL_CONNECTED && millis() - started < 30000) { delay(100); } if (WiFi.status() == WL_CONNECTED) { Serial.printf("[WIFI] Connected, IP address %s\n", WiFi.localIP().toString().c_str()); } else { Serial.printf("[WIFI] No connection after 30 s (status %d): check WIFI_SSID and WIFI_PASSWORD. Still trying.\n", WiFi.status()); } } void onDirective(AlexaDirective &directive) { if (directive.isReportState()) { // While the bolt moves this is the state it left directive.stateReport().addHealthProp(EndpointHealth::OK).addLockControllerProp(state).send(); return; } if (!directive.is("Lock") && !directive.is("Unlock")) { directive.error(AlexaErrorType::INVALID_DIRECTIVE, "This lock locks and unlocks").send(); return; } if (moving) { directive.error(AlexaErrorType::ENDPOINT_BUSY, "The bolt is moving").send(); return; } goal = directive.is("Lock") ? AlexaLockState::LOCKED : AlexaLockState::UNLOCKED; token = directive.correlationToken; // a copy: the text of the directive ends with the handler moving = true; movingSince = millis(); digitalWrite(BOLT_PIN, goal == AlexaLockState::LOCKED ? HIGH : LOW); directive.deferred(5).send(); } void setup() { Serial.begin(74880); pinMode(BOLT_PIN, OUTPUT); pinMode(BLOCKED_PIN, INPUT_PULLUP); digitalWrite(BOLT_PIN, HIGH); connectWiFi(); // MQTT user name, MQTT password, root topic alexa.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC); // The name Alexa shows, and the id of the endpoint: every device of an account has its own doorLock = alexa.getDevice("Front Door Lock", "esp-lock"); doorLock->setDisplayCategory(DisplayCategory::SMARTLOCK); doorLock->addCapability(AlexaInterfaces::LockController); doorLock->addCapability(AlexaInterfaces::EndpointHealth); doorLock->onDirective(onDirective); } void loop() { alexa.loop(); if (moving && millis() - movingSince >= BOLT_MS) { moving = false; state = digitalRead(BLOCKED_PIN) == LOW ? AlexaLockState::JAMMED : goal; doorLock->response(token).addHealthProp(EndpointHealth::OK).addLockControllerProp(state).sendAsync(); } }