// ContactSensor: a door or window contact that tells Alexa when it opens and closes (Alexa.ContactSensor). // // A reed switch connects the pin to ground while the door is closed; the internal pull-up makes the pin high when // it opens. A level has to hold for 50 ms before it counts. Every change is sent as a ChangeReport; ReportState // is answered with the state of the contact. A change while there is no session with the broker is not reported // later: Alexa gets the state with its next ReportState. #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 CONTACT_PIN = 14; // D5 on a Wemos D1 mini const unsigned long DEBOUNCE_MS = 50; Alex2ESP alexa; AlexaDevice *contact; bool isOpen = false; bool lastLevel = false; unsigned long lastEdge = 0; // 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()) { directive.stateReport().addHealthProp(EndpointHealth::OK).addContactSensorProp(isOpen).send(); return; } directive.error(AlexaErrorType::INVALID_DIRECTIVE, "A contact takes no directives").send(); } void setup() { Serial.begin(74880); pinMode(CONTACT_PIN, INPUT_PULLUP); isOpen = lastLevel = digitalRead(CONTACT_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 contact = alexa.getDevice("Back Door", "esp-contact"); contact->setDisplayCategory(DisplayCategory::CONTACT_SENSOR); contact->addCapability(AlexaInterfaces::ContactSensor)->setProactivelyReported(true); contact->addCapability(AlexaInterfaces::EndpointHealth); contact->onDirective(onDirective); } void loop() { alexa.loop(); bool level = digitalRead(CONTACT_PIN) == HIGH; if (level != lastLevel) { lastLevel = level; lastEdge = millis(); } if (level != isOpen && millis() - lastEdge >= DEBOUNCE_MS) { isOpen = level; Serial.println(isOpen ? "[CONTACT] open" : "[CONTACT] closed"); contact->changeReport(AlexaCause::PHYSICAL_INTERACTION) .addContactSensorProp(isOpen) .context() .addHealthProp(EndpointHealth::OK) .send(); } }