Alex2ESP/examples/Lock/Lock.ino
David 69b4d59896 Examples as Arduino sketches: one folder per device type, each its own endpointId, Wi-Fi with a timeout
Light, DimmableLight, ColorTemperatureLight, ColorLight, TemperatureSensor, ContactSensor, Blind,
Thermostat, Lock, Scene, Doorbell and MultiDevice on the 2.0 API: addCapability(row, instance), one
onDirective() handler, the report helpers, ChangeReports from loop(), a deferred answer for the lock.
Wi-Fi is waited for 30 s, then the sketch says what to check and carries on.

examples/README.md has what each sketch does, its limits and the measured sizes for d1_mini: Light
takes 30,616 bytes of static RAM and 332,565 of flash; all 17 sketches build with 0 warnings.
test/test_examples compares the discovery object of every new sketch: 147 host tests (were 135).
The sketches were compiled, not run on a board.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-28 21:41:51 +00:00

117 lines
3.8 KiB
C++

// 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 <Arduino.h>
#include <ESP8266WiFi.h>
#include <Alex2ESP.h>
// 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();
}
}