The library fetched every directive over HTTP (GET /Alex2ESP/<token>, the token taken from <root>/<id>/alexaDirective_e) and posted every report back over HTTP. The detour was built in 2024 because AsyncMqttClient hands a message larger than one TCP segment to onMessage in fragments. Alex2MQTT publishes the whole directive on <root>/<id>/alexaDirective, so the fragments are reassembled instead: by index/total, into one heap block of total + 1 bytes that is freed as soon as loop() has parsed it.
Removed: ESP8266HTTPClient with processHttpGet/processHttpPost and their two HTTPClient and two WiFiClient members; the 5-slot packet, topic and receive queues, combinedData, receivePayload and AlexaStatusMessage::outputString (17,264 bytes of .bss); the {MESSAGE_PAYLOAD_SPLIT} and {REPLACE_WITH_DATETIME} conventions; the subscription to the _e token topic; AlexaUtils.cpp (AlexaUtils::printMemoryInfo stays, in the header).
Receive: the bridge subscribes <root>/discover and <root>/+/alexaDirective. The MQTT callback only collects; loop() parses and dispatches, and answers discovery. A directive over ALEX2ESP_MAX_DIRECTIVE (2047) is refused. One directive is in flight: a second one that arrives before loop() ran is dropped. Both are logged. A byte-identical repeat of the waiting directive and a repeat of one of the last four messageIds (kept as 64-bit hashes, 32 bytes) are ignored, because the broker mirror delivers every message twice. The wildcard subscription also delivers the directives of other boards of the account; they are recognised by their topic before anything is allocated.
Publish: measureJson first. A message is refused when it is over ALEX2ESP_MAX_MESSAGE (2047), when its document overflowed, when there is no session, when the packet does not fit the largest free block, or when AsyncMqttClient returns 0. Every refusal is logged and returned as an AlexaSendResult; AlexaStatusMessage::send() keeps its bool. The limit now also applies to the discovery object of a device.
Time: timeOfSample is an ISO 8601 UTC instant from the clock of the board (gmtime_r + snprintf, no strftime). begin() calls configTime(0, 0, "pool.ntp.org", "time.nist.gov") and loop() holds the first connect until the clock is set or 5 s have passed. setTimeSource(false) leaves the clock to the sketch. AddContextProp() fills timeOfSample in when a hand-built property has none or carries the old placeholder.
Log: AlexaLog, with a level at run time (setLogLevel) and a ceiling at build time (ALEX2ESP_LOG_MAX), replaces the Serial prints and the Alex2ESP_DEBUG define; the new receive and publish paths need a line for every refusal. The log and the time stamp call vsnprintf/snprintf with a PROGMEM format and not the _P variants: newlib keeps those in one object with printf_P and sprintf_P, which links the FILE-based printf. Measured on basicLight: 5,776 bytes of flash.
For a sketch: the 1.x API is unchanged, the five examples compile unmodified and without warnings. loop() no longer blocks for two HTTP round trips per directive. send() publishes at once and returns false without a session, where 1.1.0 queued the report. The MQTT session opens from loop(), up to 5 s after begin(). getState() is CONNECTED once both subscriptions are acknowledged. The 1.2.0 section of readme.md lists every change; its transport section is rewritten.
Measured for d1_mini with empty credentials (PlatformIO 6.2.0, espressif8266 4.2.1, Arduino core 3.1.2), static RAM / flash in bytes, 1.1.0 -> this commit:
basicLight 52,768 / 350,885 -> 34,116 / 336,757
lightWithBrightness 52,880 / 354,729 -> 34,232 / 340,517
lightWithColorTemp 53,028 / 355,389 -> 34,380 / 341,177
tempSensor 52,676 / 349,441 -> 34,024 / 335,457
blindControl 52,900 / 353,069 -> 34,256 / 338,925
basicLight with -DALEX2ESP_LOG_MAX=0: 34,108 / 333,289. SNTP and the time stamp are 1,848 bytes of the flash figure.
Tests: platformio.ini with [env:native] and test/test_bridge_logic, 32 host tests of the logic in src/AlexaBridgeLogic.cpp (reassembly at every fragment size, both limits, repeats, publish checks, topics, time stamps). They also pass under -fsanitize=address,undefined. .gitignore no longer hides /test.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
291 lines
22 KiB
Markdown
291 lines
22 KiB
Markdown
# Alex2ESP
|
||
|
||
Alex2ESP is a lightweight Arduino/PlatformIO library for integrating ESP8266 microcontrollers with Amazon Alexa smart home APIs through the [Alex2MQTT](https://alex2mqtt.stormysdream.club/) skill. The library simplifies the process of creating Alexa-compatible devices using MQTT.
|
||
|
||
**Supported target: ESP8266** (developed and tested on a Wemos D1 mini). ESP32: untested - the code carries include guards for it, but it has never been compiled or run there.
|
||
|
||
For more details on how to configure Alex2ESP, visit [Alex2MQTT Documentation](https://alex2mqtt.stormysdream.club/).
|
||
|
||
---
|
||
|
||
|
||
|
||
## Features
|
||
- Supports "All" Alexa smart home capabilities.
|
||
- Provides action mapping for advanced directive customization including Open,Close,Raise,and Lower commands.
|
||
- Allows custom JSON injection in both discovery and state reporting to allow for unsupported devices.
|
||
- Event-driven architecture for handling Alexa directives.
|
||
---
|
||
|
||
## Alternatives
|
||
|
||
one of the most popular library for controlling esp like devices using alexa is [FauxmoESP](https://github.com/vintlabs/fauxmoESP)
|
||
however FauxmoESP works by emulating a light bulb so it is limited in what commands it accepts.
|
||
|
||
---
|
||
|
||
## Quick Start
|
||
### Installation
|
||
|
||
The library lives on Forgejo at https://git.stormysdream.club/platformio/Alex2ESP (public). It is not in the PlatformIO registry or the Arduino Library Manager: install it from git.
|
||
|
||
#### PlatformIO
|
||
|
||
```ini
|
||
[env:d1_mini]
|
||
platform = espressif8266
|
||
board = d1_mini
|
||
framework = arduino
|
||
monitor_speed = 74880
|
||
lib_deps =
|
||
https://git.stormysdream.club/platformio/Alex2ESP.git
|
||
marvinroger/AsyncMqttClient@^0.9.0
|
||
bblanchon/ArduinoJson@^7
|
||
```
|
||
|
||
To pin a release instead of `main`, append the tag: `https://git.stormysdream.club/platformio/Alex2ESP.git#v1.1.0`. AsyncMqttClient pulls in ESPAsyncTCP by itself; if PlatformIO notes that more than one `ESPAsyncTCP` package matches, add `esp32async/ESPAsyncTCP@^2.0.0` to `lib_deps` to pick one explicitly. The examples print at 74880 baud, the ESP8266 boot-ROM rate, so the boot messages stay readable in the same monitor.
|
||
|
||
#### Arduino IDE
|
||
|
||
Download the repository as a ZIP (https://git.stormysdream.club/platformio/Alex2ESP/archive/main.zip) and add it with *Sketch -> Include Library -> Add .ZIP Library*. Install **AsyncMqttClient**, **ArduinoJson** (7.x) and **ESP Async TCP** (by ESP32Async) from the Library Manager. Install the ESP8266 board package and select your board (for example *LOLIN(WEMOS) D1 R2 & mini*).
|
||
|
||
Then include the library in your project:
|
||
```cpp
|
||
#include <Alex2ESP.h>
|
||
```
|
||
|
||
Complete sketches are in [`examples/`](examples/): `basicLight.cpp`, `lightWithBrightness.cpp`, `lightWithColorTemp.cpp`, `tempSensor.cpp` and `blindControl.cpp`. Every example joins Wi-Fi with `WiFi.begin(WIFI_SSID, WIFI_PASSWORD)`; fill in the SSID, the password and your Alex2MQTT credentials before flashing.
|
||
|
||
---
|
||
|
||
## Creating a Basic Device
|
||
### Example: Light Control
|
||
|
||
Here’s how to create a simple device that controls a light:
|
||
|
||
First create an instance of the alexa client and initilize it with your MQTT Credentials
|
||
```cpp
|
||
Alex2ESP alexClient;
|
||
```
|
||
|
||
Credentials can be found at https://alex2mqtt.stormysdream.club/ after logging in with amazon. `begin()` takes the MQTT username, the MQTT password and the root topic, in that order (the same order as alex2node's `new Alex2MQTT(username, password, rootTopic)`).
|
||
```cpp
|
||
alexClient.begin(ALEXA_USERNAME, ALEXA_PASSWORD, ALEXA_ROOT_TOPIC);
|
||
```
|
||
|
||
Once initilized you can begin to add virtual devices, in this example we add a power controller to toggle a light
|
||
```cpp
|
||
|
||
AlexaDevice* device1 = alexClient.getDevice("Test Lamp", "ESP-01");
|
||
device1->setDisplayCategory(DisplayCategory::LIGHT);
|
||
device1->addCapability(AlexaInterfaceType::POWER_CONTROLLER);
|
||
|
||
device1->registerEvent("ReportState", [](const JsonDocument& directive, const AlexaInterfaceType& type) {
|
||
// Build and send status report
|
||
device1->buildStatusMessage(directive["header"]["correlationToken"])
|
||
.AddHealthProp(EndpointHealth::OK)
|
||
.AddPowerControllerProp(outputState)
|
||
.send();
|
||
});
|
||
|
||
// Register event listener for "Event" directive to handle state changes
|
||
device1->registerEvent("Event", [](const JsonDocument& directive, const AlexaInterfaceType& type) {
|
||
if (type == AlexaInterfaceType::POWER_CONTROLLER) {
|
||
if (directive["header"]["name"] == "TurnOn") {
|
||
outputState = PowerController::ON;
|
||
Serial.println("Turning ON");
|
||
} else if (directive["header"]["name"] == "TurnOff") {
|
||
outputState = PowerController::OFF;
|
||
Serial.println("Turning OFF");
|
||
}
|
||
|
||
// Send the updated state after change
|
||
device1->buildStatusMessage(directive["header"]["correlationToken"], true)
|
||
.AddHealthProp(EndpointHealth::OK)
|
||
.AddPowerControllerProp(outputState)
|
||
.send();
|
||
}
|
||
});
|
||
```
|
||
|
||
The pointers returned by `getDevice()` and `addCapability()` stay valid for the lifetime of the client, so keeping them in globals and adding more devices or capabilities later is fine.
|
||
|
||
---
|
||
### Example: Toggle Controller for Blinds
|
||
Alex2ESP also supports action mapping so we can map keywords such as "open" and "close" to a toggle controller
|
||
For a comprehensive list of Alexa actions and mappings, visit the [Alexa Developer Documentation](https://developer.amazon.com/en-US/docs/alexa/device-apis/alexa-discovery-objects.html#action-mapping)
|
||
|
||
```cpp
|
||
AlexaDevice* device = alexClient.getDevice("Bedroom Blinds", "ESP-01");
|
||
device->setDisplayCategory(DisplayCategory::INTERIOR_BLIND);
|
||
|
||
AlexaInterface* toggleController = device->addCapability(AlexaInterfaceType::TOGGLE_CONTROLLER);
|
||
toggleController->setInstance("ESP-01.Toggle");
|
||
toggleController->addFriendlyName("Bedroom Blinds", "en-US");
|
||
|
||
ActionMapping closeMapping({AlexaActions::Close}, "TurnOn");
|
||
ActionMapping openMapping({AlexaActions::Open}, "TurnOff");
|
||
toggleController->addActionMapping(closeMapping);
|
||
toggleController->addActionMapping(openMapping);
|
||
```
|
||
|
||
An `ActionMapping` takes an optional third argument, the directive payload as JSON text (for example `"{\"rangeValue\": 0}"` for a RangeController). It is emitted as an object in the discovery response and omitted when empty.
|
||
|
||
---
|
||
|
||
## How it talks to Alex2MQTT
|
||
|
||
Everything goes over MQTT (port 1883 of `alex2mqtt.stormysdream.club`); the library makes no HTTP requests.
|
||
|
||
- **Session.** `begin()` starts SNTP (`pool.ntp.org`, `time.nist.gov`) and returns; `loop()` opens the MQTT session once the clock is set, or after 5 s without an answer, and subscribes to `<root>/discover` and `<root>/+/alexaDirective`. `getState()` is `CONNECTED` when the broker has acknowledged both subscriptions. A sketch that sets the clock itself (its own `configTime()` with a time zone, an RTC) calls `alexClient.setTimeSource(false)` before `begin()`.
|
||
- **Discovery.** On `<root>/discover` the library answers with one discovery object per device on `<root>/discover_r`. The backend accepts one endpoint object per message and collects everything that arrives within 1 s for Alexa's discovery answer (up to 5 s for its proactive AddOrUpdate push), so all devices are published back to back from the next `loop()`. Each object sits on the heap (about 1 KB) until the MQTT client has sent it; when the client cannot take another one (free heap under 4 KB), the library prints `[Alex2ESP] discovery deferred at <endpointId>` and sends the rest from `loop()` as the queue drains, for up to 5 s after the request. `[Alex2ESP] error: discovery gave up: N device(s) not announced` means those devices missed this answer - on the backend's proactive discovery that can remove them from Alexa until the next one.
|
||
- **Directives.** The directive arrives as JSON on `<root>/<endpointId>/alexaDirective`. A directive larger than one TCP segment arrives in fragments, which are put together in one heap block that exists only until `loop()` has parsed it. `loop()` then fires `ReportState` or `Event` (and `DirectiveReceived`, if registered) with the directive: `directive["header"]`, `directive["endpoint"]`, `directive["payload"]`. One directive is handled at a time; a directive that arrives twice (a broker that mirrors its topics delivers every message twice) is handled once.
|
||
- **Reports.** `send()` publishes the report on `<root>/<endpointId>/alexaResponce` at once. The backend waits 7 s for it, so answer from the event handler. Every property carries the board's UTC time as `timeOfSample`.
|
||
- **Limits.** A directive and a report (or the discovery object of one device) may be 2047 bytes each; `-DALEX2ESP_MAX_DIRECTIVE=<bytes>` and `-DALEX2ESP_MAX_MESSAGE=<bytes>` in `build_flags` change that. `send()` returns `false` when the report was not sent: no session with the broker, the MQTT client or the heap cannot take it, or it is too large. Nothing is ever sent truncated, and nothing is dropped without a line on Serial.
|
||
- **Serial output.** Every line of the library starts with `[Alex2ESP]`, a problem with `[Alex2ESP] error:`. `alexClient.setLogLevel(AlexaLogLevel::ERROR)` leaves only the problems, `AlexaLogLevel::NONE` nothing; the default, `AlexaLogLevel::INFO`, adds the session, discovery and one line per directive (`[Alex2ESP] ESP-01 <- Alexa.PowerController.TurnOn`). `AlexaLogLevel::DEBUG` (sizes and free heap per message) has to be compiled in with `-DALEX2ESP_LOG_MAX=3`; `-DALEX2ESP_LOG_MAX=0` compiles every line out. Credentials and correlation tokens are never printed.
|
||
|
||
Boards that run 1.1.0 or older keep working: the backend still publishes the token on `<root>/<endpointId>/alexaDirective_e` and serves the HTTP routes they use.
|
||
|
||
---
|
||
|
||
## Interface Types
|
||
| Alexa Interface Type | Status |
|
||
|-----------------------------------------------------|-------------|
|
||
| AlexaInterfaceType::ENDPOINT_HEALTH | Fully Supported |
|
||
| AlexaInterfaceType::POWER_CONTROLLER | Fully Supported |
|
||
| AlexaInterfaceType::BRIGHTNESS_CONTROLLER | Fully Supported |
|
||
| AlexaInterfaceType::TOGGLE_CONTROLLER | Fully Supported |
|
||
| AlexaInterfaceType::TEMPERATURE_SENSOR | Fully Supported |
|
||
| AlexaInterfaceType::COLOR_TEMPERATURE_CONTROLLER | Fully Supported |
|
||
| AlexaInterfaceType::AUTOMATION_MANAGEMENT | Supported* |
|
||
| AlexaInterfaceType::CHANNEL_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::COLOR_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::CONTACT_SENSOR | Supported* |
|
||
| AlexaInterfaceType::APPLICATION_STATE_REPORTER | Supported* |
|
||
| AlexaInterfaceType::AUDIO_PLAY_QUEUE | Supported* |
|
||
| AlexaInterfaceType::AUTHORIZATION_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::AUTOMOTIVE_VEHICLE_DATA | Supported* |
|
||
| AlexaInterfaceType::CAMERA_LIVE_VIEW_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::CAMERA_STREAM_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::COMMISSIONABLE | Supported* |
|
||
| AlexaInterfaceType::CONSENT_MANAGEMENT_CONSENT_REQUIRED_REPORTER | Supported* |
|
||
| AlexaInterfaceType::COOKING | Supported* |
|
||
| AlexaInterfaceType::DATA_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::DEVICE_USAGE_ESTIMATION | Supported* |
|
||
| AlexaInterfaceType::DEVICE_USAGE_METER | Supported* |
|
||
| AlexaInterfaceType::DOORBELL_EVENT_SOURCE | Supported* |
|
||
| AlexaInterfaceType::EQUALIZER_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::INPUT_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::INVENTORY_LEVEL_SENSOR | Supported* |
|
||
| AlexaInterfaceType::INVENTORY_LEVEL_USAGE_SENSOR | Supported* |
|
||
| AlexaInterfaceType::INVENTORY_USAGE_SENSOR | Supported* |
|
||
| AlexaInterfaceType::KEYPAD_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::LAUNCHER | Supported* |
|
||
| AlexaInterfaceType::LOCK_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::MEDIA_PLAYBACK | Supported* |
|
||
| AlexaInterfaceType::MEDIA_SEARCH | Supported* |
|
||
| AlexaInterfaceType::MODE_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::MOTION_SENSOR | Supported* |
|
||
| AlexaInterfaceType::PERCENTAGE_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::PLAYBACK_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::PLAYBACK_STATE_REPORTER | Supported* |
|
||
| AlexaInterfaceType::PROACTIVE_NOTIFICATION_SOURCE | Supported* |
|
||
| AlexaInterfaceType::RANGE_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::RECORD_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::REMOTE_VIDEO_PLAYER | Supported* |
|
||
| AlexaInterfaceType::RTC_SESSION_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::SCENE_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::SECURITY_PANEL_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::SEEK_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::SIMPLE_EVENT_SOURCE | Supported* |
|
||
| AlexaInterfaceType::SMART_VISION_OBJECT_DETECTION_SENSOR | Supported* |
|
||
| AlexaInterfaceType::SMART_VISION_SNAPSHOT_PROVIDER | Supported* |
|
||
| AlexaInterfaceType::SPEAKER | Supported* |
|
||
| AlexaInterfaceType::STEP_SPEAKER | Supported* |
|
||
| AlexaInterfaceType::THERMOSTAT_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::THERMOSTAT_CONTROLLER_CONFIGURATION | Supported* |
|
||
| AlexaInterfaceType::THERMOSTAT_CONTROLLER_HVAC_COMPONENTS | Supported* |
|
||
| AlexaInterfaceType::THERMOSTAT_CONTROLLER_SCHEDULE | Supported* |
|
||
| AlexaInterfaceType::TIME_HOLD_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::UI_CONTROLLER | Supported* |
|
||
| AlexaInterfaceType::USER_PREFERENCE | Supported* |
|
||
| AlexaInterfaceType::VIDEO_RECORDER | Supported* |
|
||
| AlexaInterfaceType::WAKE_ON_LAN_CONTROLLER | Supported* |
|
||
|
||
|
||
(*Partial support or limited implementation advanced configuration is required)
|
||
|
||
---
|
||
|
||
|
||
## Advanced usage
|
||
For devices with limited or partial support, Alex2ESP provides functions that allow you to attach custom JSON objects to the status report. For example, to report the status of a PowerController type, you can use the `AddPowerControllerProp` function. However, if a specific "add props" function does not exist for your use case, you can utilize the `AddContextProp` function to pass a custom JSON object.
|
||
|
||
For instance, to manually report the state of a PowerController, you can use the following code:
|
||
|
||
```cpp
|
||
JsonDocument doc;
|
||
|
||
doc["namespace"] = "Alexa.PowerController";
|
||
doc["name"] = "powerState";
|
||
doc["value"] = "ON";
|
||
doc["uncertaintyInMilliseconds"] = 0;
|
||
AddContextProp(doc.as<JsonObject>())
|
||
```
|
||
|
||
`AddContextProp` sets `timeOfSample` to the current time when the object has none. (Until 1.1.0 a sketch wrote `"{REPLACE_WITH_DATETIME}"` there for the server to replace; the library now replaces that itself.)
|
||
|
||
---
|
||
|
||
## Changelog
|
||
|
||
### 1.2.0
|
||
|
||
Behaviour changes:
|
||
- Directives arrive over MQTT. The library subscribes to `<root>/+/alexaDirective`, where Alex2MQTT has always published the whole directive, instead of fetching it over HTTP with the token from `<root>/<endpointId>/alexaDirective_e`. The HTTP detour dates from 2024, when a directive larger than one TCP segment reached the MQTT callback in pieces; the pieces are now put together by their offset and the total length, in one heap block that lives until `loop()` has parsed the directive. `loop()` no longer stalls for two HTTP round trips per directive.
|
||
- Reports leave over MQTT. `send()` publishes on `<root>/<endpointId>/alexaResponce` at once, where 1.1.0 queued the report for an HTTP POST from a later `loop()`. It returns `false` when there is no session with the broker, when the MQTT client or the heap cannot take the report, or when the report is over 2047 bytes (`ALEX2ESP_MAX_MESSAGE`); each case prints its reason. The 5-slot send queue is gone.
|
||
- `timeOfSample` is the board's own time in UTC, for example `2026-09-28T13:05:09Z`. 1.1.0 sent the placeholder `{REPLACE_WITH_DATETIME}`, which only the backend's HTTP route replaced. `begin()` starts SNTP (`pool.ntp.org`, `time.nist.gov`) and no longer connects itself: `loop()` opens the MQTT session once the clock is set, or after 5 s without an answer, so the session comes up a few seconds later than before. `alexClient.setTimeSource(false)` before `begin()` leaves the clock to the sketch. `AddContextProp()` fills `timeOfSample` in when the property has none or carries the old placeholder.
|
||
- A directive is parsed and handed to the sketch from `loop()`, one at a time. One that arrives while the previous one still waits for `loop()` is dropped, as is one over 2047 bytes (`ALEX2ESP_MAX_DIRECTIVE`); both print an error.
|
||
- A directive that arrives twice is handled once: the Alex2MQTT broker currently delivers every message twice through a mirror. The `messageId`s of the last four directives are remembered; the repeat prints `repeated directive ... ignored`.
|
||
- The subscription delivers the directives of every endpoint of the account. Those for endpoints of another board are recognised by their topic and neither buffered nor parsed.
|
||
- Discovery is answered from `loop()`, not inside the MQTT callback. A discovery object over 2047 bytes is refused with an error; the other devices are still announced.
|
||
- `getState()` stays `INITIALIZED` until the first connect, and becomes `CONNECTED` when the broker has acknowledged both subscriptions (1.1.0: the first of them). A refused subscription prints an error.
|
||
- Serial output goes through one log with levels. `alexClient.setLogLevel()` takes `AlexaLogLevel::NONE`, `ERROR`, `INFO` (the default) or `DEBUG`; `-DALEX2ESP_LOG_MAX=<0..3>` in `build_flags` sets the highest level that is compiled in (default 2, `INFO`). This replaces the `Alex2ESP_DEBUG` define inside `AlexaUtils.cpp`. 1.1.0 printed the memory figures for every MQTT message and the whole directive, correlation token included, for every directive; both are gone.
|
||
- `getDevice()` before `begin()` prints an error: the device would have no root topic. A second `begin()` is ignored with an error.
|
||
- New: `Alex2ESP::setLogLevel()`, `Alex2ESP::setTimeSource()`, `AlexaDevice::hasEndpointId()`, `AlexaLog`, `AlexaSendResult`.
|
||
- Removed: the queues and buffers of `AlexaUtils` (`enqueue`, `dequeue`, `dequeueVals`, `enqueueReceive`, `dequeueReceive`, `isQueueEmpty`, `isQueueFull`, `isReceiveQueueEmpty`, `isReceiveQueueFull`, `receivePayload`, `nextMessageId`) and its `log`/`logln`, which printed nothing unless the library was edited; `AlexaUtils::printMemoryInfo()` stays. `MAX_STATUS_REPORT_SIZE` (the limit is `ALEX2ESP_MAX_MESSAGE`). The library no longer includes `ESP8266HTTPClient`.
|
||
|
||
Memory: `examples/basicLight.cpp` for a D1 mini takes 34,116 bytes of static RAM (1.1.0: 52,768) and 336,757 bytes of flash (1.1.0: 350,885), as PlatformIO reports them (espressif8266 4.2.1, Arduino core 3.1.2). The static RAM was the five 2 KB queue slots, three more 2 KB buffers and the two HTTP clients. SNTP and the time stamp are 1.8 KB of the flash figure.
|
||
|
||
Tests: `pio test -e native` in the repository runs 32 host tests of the receive and publish logic (reassembly of fragments, the two size limits, repeated directives, topics, time stamps). No board is needed.
|
||
|
||
Boards that run 1.1.0 are not affected: the backend keeps the token topic and the HTTP routes.
|
||
|
||
### 1.1.0
|
||
|
||
Behaviour changes:
|
||
- `begin()` now takes `(username, password, rootTopic)`, the order every example and this readme always used (and the order alex2node uses). 1.0.0 declared `(rootTopic, username, password)`, so a sketch written from the examples could not authenticate with the broker.
|
||
- Discovery answers go straight to MQTT (`<root>/discover_r`), one object per device, all devices at once; if the MQTT client cannot take another object (free heap under 4 KB) the rest is sent from `loop()` as the client's queue drains, for up to 5 s. 1.0.0 pushed them one per `loop()` through a 5-slot HTTP queue and silently dropped the sixth device onwards, which the backend then removed from Alexa.
|
||
- `AlexaStatusMessage::send()` returns `bool`: an oversized report (over 2047 bytes) or a full queue is reported on Serial and dropped instead of being sent truncated. `AlexaUtils::enqueue()` refuses packets that would not fit instead of truncating them.
|
||
- Discovery JSON: `semantics` is emitted only when a capability has action mappings; an `ActionMapping` directive payload is emitted as a JSON object (parsed from the text you pass) or omitted when empty, instead of the string `"{}"`.
|
||
- `Response`/`StateReport` events include the required `"payload": {}`.
|
||
- Devices and capabilities are stored in `std::deque`; pointers from `getDevice()`/`addCapability()` no longer dangle once another device or capability is added.
|
||
- The MQTT payload is copied using its length (no write past the client's buffer); fragmented directive ids are ignored (a Discover is answered on its first fragment, its payload is unused).
|
||
- `begin()` no longer prints the MQTT credentials to Serial in debug builds.
|
||
- `DirectiveReceived` no longer prints "No event registered" when the sketch has not registered it.
|
||
- Removed: `Alex2ESP::messageSplitAndSend` (declared, never defined) and `AlexaStatusMessage::setEndpointId` (did nothing).
|
||
- Reported `softwareVersion`/`firmwareVersion` in the discovery attributes are now `1.1.0`.
|
||
|
||
Examples: `WiFi.begin(WIFI_SSID, WIFI_PASSWORD)` (1.0.0 could only join open networks), `LED_BUILTIN` fallback, fixed brightness/colour-temperature Serial output, blinds use `DisplayCategory::INTERIOR_BLIND`.
|
||
|
||
Packaging: `library.json` and `library.properties` restored with the Forgejo URL and the dependencies, `LICENSE` (MIT), a real `keywords.txt`, LF line endings (`.gitattributes`), ArduinoJson 7 deprecated calls replaced (warning-free build). ESP8266 is the supported target; ESP32 is untested.
|
||
|
||
---
|
||
|
||
## Contributing
|
||
Feel free to submit pull requests or issues for feature requests and bug fixes. `pio test -e native` in the repository root runs the host tests (no board needed); they have to pass.
|
||
|
||
---
|
||
|
||
## License
|
||
This project is licensed under the MIT License. See the LICENSE file for details.
|