Both limits were 2047 bytes. An answer repeats the correlationToken of its directive, which is most of a large directive, and adds 140 to 170 bytes for every property. So a directive between about 1,750 and 2,047 bytes was accepted, the sketch acted on it, and the answer was refused: the lamp switched and Alexa reported a device that does not respond.
The limit for messages now follows the limit for directives unless it is set: 2047 + 1024 = 3071 bytes, room for six properties on top of the largest directive. Both defines moved to src/AlexaLimits.h, next to each other, with the relation in the comment. The limit also applies to the discovery object of a device, which may now be 3071 bytes.
For a sketch: a report between 2,048 and 3,071 bytes is sent, where it was refused with an error. A project that sets ALEX2ESP_MAX_MESSAGE keeps its value.
Measured with the bridge built for the host against a fake MQTT client (not in the repository), a TurnOn of 2,047 bytes answered with two properties:
8ea7778 handler ran, send() false, "2374 bytes not sent, the limit is 2047"
this commit handler ran, send() true, 2,374 bytes published
Tests: test_largest_directive_can_be_answered puts a directive of ALEX2ESP_MAX_DIRECTIVE bytes through the receive buffer, builds the Response of a lamp to it and checks it against ALEX2ESP_MAX_MESSAGE, also with six properties. 33 host tests pass.
examples/basicLight.cpp for d1_mini, static RAM / flash in bytes: 34,116 / 336,757 -> 34,116 / 336,757, no warnings.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
8ea7778 introduced AlexaLogLevel::NONE, ERROR, INFO and DEBUG. A sketch that starts with "#define DEBUG 1", or a project built with -DDEBUG, did not compile any more: "expected identifier before numeric constant" at the enumerator, and with the build flag also in the library's own files, because ALEX2ESP_LOGD spelled AlexaLogLevel::DEBUG wherever it expanded. The same sketch builds against 1.1.0.
AlexaLog.h declares the enumerators between #pragma push_macro / #undef and #pragma pop_macro of the four names: a macro of the sketch is set aside for the declaration and is back after it. ALEX2ESP_LOG takes the level as its number and casts it, so no call site names an enumerator. AlexaLog.cpp, the one file that names the levels, undefines the four names after its includes.
For a sketch: nothing changes unless it defines such a macro. One that does cannot write AlexaLogLevel::DEBUG, because the preprocessor replaces the name; setLogLevel(static_cast<AlexaLogLevel>(3)) selects the same level. NONE is set aside in AlexaLog.h as well, but a project that defines NONE fails for another reason: AsyncMqttClient has an enumerator NONE (ParsingInformation.hpp).
Built for d1_mini with empty credentials (PlatformIO 6.2.0, espressif8266 4.2.1), examples/basicLight.cpp, static RAM / flash in bytes, 8ea7778 -> this commit, no warnings:
as committed 34,116 / 336,757 -> 34,116 / 336,757
"#define DEBUG 1" as the first line does not compile -> 34,116 / 336,757
DEBUG, ERROR and INFO defined in front 34,116 / 336,757
build_flags = -DDEBUG does not compile (28 errors) -> 34,116 / 336,757
build_flags = -DDEBUG -DERROR -DINFO 34,116 / 336,757
The other four examples are unchanged as well (34,232 / 340,517, 34,380 / 341,177, 34,024 / 335,457, 34,256 / 338,925).
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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>