mirror of
https://github.com/alexhopeoconnor/arduino-home-assistant.git
synced 2026-10-04 02:48:13 +10:00
docs: correct MQTT example and ESP8266 guidance
This commit is contained in:
@@ -8,7 +8,11 @@ ArduinoHA lets an Arduino, ESP8266, or ESP32 application publish MQTT discovery
|
|||||||
## Start with a sensor
|
## Start with a sensor
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
#if defined(ESP8266)
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
|
#elif defined(ESP32)
|
||||||
|
#include <WiFi.h>
|
||||||
|
#endif
|
||||||
#include <ArduinoHA.h>
|
#include <ArduinoHA.h>
|
||||||
|
|
||||||
WiFiClient client;
|
WiFiClient client;
|
||||||
@@ -24,11 +28,11 @@ void setup() {
|
|||||||
// Your application connects Wi-Fi before MQTT begins.
|
// Your application connects Wi-Fi before MQTT begins.
|
||||||
temperature.setName("Temperature");
|
temperature.setName("Temperature");
|
||||||
temperature.setUnitOfMeasurement("°C");
|
temperature.setUnitOfMeasurement("°C");
|
||||||
mqtt.begin("mqtt.local", "mqtt_user", "mqtt_password");
|
mqtt.begin("mqtt.local", "mqtt_user", "mqtt_password"); // Connection work begins in mqtt.loop().
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
mqtt.loop();
|
mqtt.loop(); // Maintains MQTT and publishes discovery after connecting.
|
||||||
// Call temperature.setValue(...) when your reading changes.
|
// Call temperature.setValue(...) when your reading changes.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
# ESP8266 Postmortem linker workaround
|
||||||
|
|
||||||
|
The ESP8266 Arduino framework pin used by the DeviceFramework, WiFiManager,
|
||||||
|
and DFTE test projects is intentional:
|
||||||
|
|
||||||
|
```ini
|
||||||
|
platform_packages =
|
||||||
|
platformio/framework-arduinoespressif8266 @ https://github.com/esp8266/Arduino.git#521ae60a89e64bb0d1eb7a0b7addf620ced5cad3
|
||||||
|
```
|
||||||
|
|
||||||
|
That upstream commit fixes Postmortem's large-jump failure,
|
||||||
|
[`dangerous relocation: j: cannot encode`](https://github.com/esp8266/Arduino/commit/521ae60a89e64bb0d1eb7a0b7addf620ced5cad3).
|
||||||
|
It changes the restart wrapper to use a relaxed jump and adds an EPC1 address
|
||||||
|
check. The failure is a framework linker/runtime-support issue, not an ArduinoHA
|
||||||
|
or application-source error.
|
||||||
|
|
||||||
|
Keep this exact framework snapshot in ESP8266 environments that need the
|
||||||
|
maintained test contract. It is unrelated to ESP32, whose pioarduino platform
|
||||||
|
selects its framework and compiler as a unit. Do not replace the SHA with a
|
||||||
|
version range: remove or advance the pin only after an upstream release includes
|
||||||
|
the fix and the affected large firmware has compiled successfully.
|
||||||
|
|
||||||
|
The corresponding ESP32 Core 3 pin and shared PlatformIO-cache recovery steps
|
||||||
|
are documented in [DeviceFramework's toolchain guide](https://github.com/alexhopeoconnor/DeviceFramework/blob/main/docs/TOOLCHAINS.md).
|
||||||
|
|
||||||
|
Back to the [documentation map](README.md).
|
||||||
@@ -6,6 +6,7 @@
|
|||||||
| [Device & discovery](device-and-discovery.md) | `HADevice`, discovery modes, metadata, identifiers, and migration |
|
| [Device & discovery](device-and-discovery.md) | `HADevice`, discovery modes, metadata, identifiers, and migration |
|
||||||
| [MQTT usage](mqtt-usage.md) | Callbacks, custom topics, availability, logging, and footprint flags |
|
| [MQTT usage](mqtt-usage.md) | Callbacks, custom topics, availability, logging, and footprint flags |
|
||||||
| [Entities](entities.md) | Supported Home Assistant entity classes and the best matching example |
|
| [Entities](entities.md) | Supported Home Assistant entity classes and the best matching example |
|
||||||
|
| [ESP8266 linker workaround](ESP8266-LINKER-WORKAROUND.md) | Exact framework pin for the Postmortem large-jump fix |
|
||||||
| [Examples](../examples/README.md) | Guided PlatformIO projects and focused entity recipes |
|
| [Examples](../examples/README.md) | Guided PlatformIO projects and focused entity recipes |
|
||||||
| [Compatibility baseline](compatibility.md) | Supported Home Assistant capabilities and fork-specific compatibility notes |
|
| [Compatibility baseline](compatibility.md) | Supported Home Assistant capabilities and fork-specific compatibility notes |
|
||||||
|
|
||||||
|
|||||||
@@ -40,8 +40,11 @@ Defaults:
|
|||||||
Override before `begin()` if needed:
|
Override before `begin()` if needed:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
mqtt.setDiscoveryPrefix("myHaPrefix");
|
void configureTopicPrefixes() {
|
||||||
mqtt.setDataPrefix("myDataPrefix");
|
// Set both before mqtt.begin(...) publishes any discovery data.
|
||||||
|
mqtt.setDiscoveryPrefix("myHaPrefix");
|
||||||
|
mqtt.setDataPrefix("myDataPrefix");
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### Single-component vs device discovery
|
### Single-component vs device discovery
|
||||||
@@ -106,13 +109,16 @@ The migration stage is held in RAM. If the board reboots before completion, star
|
|||||||
Device discovery can also publish richer origin/device metadata, for example:
|
Device discovery can also publish richer origin/device metadata, for example:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
device.setModelId("esp32-s3-devkit");
|
void configureDeviceDiscovery() {
|
||||||
device.setHardwareVersion("rev-b");
|
// These strings are borrowed, so keep literals or other long-lived storage.
|
||||||
device.setSerialNumber("SN-00042");
|
device.setModelId("esp32-s3-devkit");
|
||||||
device.setSuggestedArea("Garage");
|
device.setHardwareVersion("rev-b");
|
||||||
device.setViaDevice("main_gateway");
|
device.setSerialNumber("SN-00042");
|
||||||
device.addConnection("mac", "AA:BB:CC:DD:EE:FF");
|
device.setSuggestedArea("Garage");
|
||||||
mqtt.setOriginSupportUrl("https://example.com/device-help");
|
device.setViaDevice("main_gateway");
|
||||||
|
device.addConnection("mac", "AA:BB:CC:DD:EE:FF");
|
||||||
|
mqtt.setOriginSupportUrl("https://example.com/device-help");
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
For entity identifiers in Home Assistant, prefer **`setDefaultEntityId()`** over legacy **`setObjectId()`**.
|
For entity identifiers in Home Assistant, prefer **`setDefaultEntityId()`** over legacy **`setObjectId()`**.
|
||||||
|
|||||||
+12
-5
@@ -40,20 +40,24 @@ HADevice device(mac, sizeof(mac));
|
|||||||
HAMqtt mqtt(client, device);
|
HAMqtt mqtt(client, device);
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
Ethernet.begin(mac);
|
Ethernet.begin(mac); // Bring up the network Client before MQTT can connect.
|
||||||
mqtt.begin("192.168.1.50", "mqtt_user", "mqtt_password");
|
mqtt.begin("192.168.1.50", "mqtt_user", "mqtt_password"); // Connection begins in mqtt.loop().
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
Ethernet.maintain();
|
Ethernet.maintain(); // Renews DHCP leases where the Ethernet library requires it.
|
||||||
mqtt.loop();
|
mqtt.loop(); // Services MQTT connection, discovery, and entity traffic.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### ESP8266 / ESP32 (example)
|
### ESP8266 / ESP32 (example)
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
#if defined(ESP8266)
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
|
#elif defined(ESP32)
|
||||||
|
#include <WiFi.h>
|
||||||
|
#endif
|
||||||
#include <ArduinoHA.h>
|
#include <ArduinoHA.h>
|
||||||
|
|
||||||
WiFiClient client;
|
WiFiClient client;
|
||||||
@@ -66,15 +70,18 @@ void setup() {
|
|||||||
device.setUniqueId(mac, sizeof(mac));
|
device.setUniqueId(mac, sizeof(mac));
|
||||||
|
|
||||||
WiFi.begin("SSID", "password");
|
WiFi.begin("SSID", "password");
|
||||||
|
// Keep this blocking wait only in a minimal sketch. Production firmware
|
||||||
|
// should retry, time out, or hand control to its provisioning flow.
|
||||||
while (WiFi.status() != WL_CONNECTED) {
|
while (WiFi.status() != WL_CONNECTED) {
|
||||||
delay(500);
|
delay(500);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// begin() stores broker settings; mqtt.loop() performs connection and recovery.
|
||||||
mqtt.begin("192.168.1.50", "mqtt_user", "mqtt_password");
|
mqtt.begin("192.168.1.50", "mqtt_user", "mqtt_password");
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
mqtt.loop();
|
mqtt.loop(); // Service reconnects, subscriptions, and discovery publishing.
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
+76
-40
@@ -2,21 +2,25 @@
|
|||||||
|
|
||||||
## Callbacks and tuning
|
## Callbacks and tuning
|
||||||
|
|
||||||
|
The following snippets extend a sketch that already owns long-lived `HADevice`, `HAMqtt`, and entity objects. Configure callbacks, discovery metadata, and availability before `mqtt.begin(...)`; call the publishing snippets later from normal application logic.
|
||||||
|
|
||||||
`HAMqtt` supports optional callbacks and PubSubClient tuning:
|
`HAMqtt` supports optional callbacks and PubSubClient tuning:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
|
// These callbacks are registered before mqtt.begin(...).
|
||||||
void onMessage(const char* topic, const uint8_t* payload, uint16_t length) { /* ... */ }
|
void onMessage(const char* topic, const uint8_t* payload, uint16_t length) { /* ... */ }
|
||||||
void onConnected() { /* ... */ }
|
void onConnected() { /* ... */ }
|
||||||
void onDisconnected() { /* ... */ }
|
void onDisconnected() { /* ... */ }
|
||||||
void onStateChanged(HAMqtt::ConnectionState state) { /* ... */ }
|
void onStateChanged(HAMqtt::ConnectionState state) { /* ... */ }
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
// A successful reconnect creates a new MQTT session, so onConnected runs again.
|
||||||
mqtt.onMessage(onMessage);
|
mqtt.onMessage(onMessage);
|
||||||
mqtt.onConnected(onConnected);
|
mqtt.onConnected(onConnected);
|
||||||
mqtt.onDisconnected(onDisconnected);
|
mqtt.onDisconnected(onDisconnected);
|
||||||
mqtt.onStateChanged(onStateChanged);
|
mqtt.onStateChanged(onStateChanged);
|
||||||
mqtt.setBufferSize(512); // default 256
|
mqtt.setBufferSize(512); // Increase only when a larger MQTT packet is required.
|
||||||
mqtt.setKeepAlive(60); // seconds, default 15
|
mqtt.setKeepAlive(60); // Seconds; the default is 15.
|
||||||
mqtt.begin("192.168.1.50", "user", "pass");
|
mqtt.begin("192.168.1.50", "user", "pass");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -27,6 +31,7 @@ Subscribe after each successful connection (for example in `onConnected`), becau
|
|||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
void onConnected() {
|
void onConnected() {
|
||||||
|
// MQTT subscriptions belong to this session and must be restored after reconnecting.
|
||||||
mqtt.subscribe("my/custom/topic");
|
mqtt.subscribe("my/custom/topic");
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -36,8 +41,11 @@ Handle payloads in `onMessage`.
|
|||||||
## Publishing arbitrary payloads
|
## Publishing arbitrary payloads
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
mqtt.publish("customTopic", "payload", false); // not retained
|
// Call after mqtt.begin(...); retain controls whether the broker keeps this value.
|
||||||
mqtt.publish("customTopic", "payload", true); // retained
|
void publishApplicationState() {
|
||||||
|
mqtt.publish("customTopic", "payload", false); // Not retained.
|
||||||
|
mqtt.publish("customTopic", "payload", true); // Retained.
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Availability
|
## Availability
|
||||||
@@ -45,11 +53,14 @@ mqtt.publish("customTopic", "payload", true); // retained
|
|||||||
**Shared availability (recommended):** one availability topic for the whole device — works well with **Last Will** (LWT):
|
**Shared availability (recommended):** one availability topic for the whole device — works well with **Last Will** (LWT):
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
device.enableSharedAvailability();
|
// Configure device-wide availability before mqtt.begin(...).
|
||||||
device.setPayloadAvailable("up");
|
void configureSharedAvailability() {
|
||||||
device.setPayloadNotAvailable("down");
|
device.enableSharedAvailability();
|
||||||
device.enableLastWill(); // broker publishes offline when TCP drops
|
device.setPayloadAvailable("up");
|
||||||
// device.setAvailability(false); // optional: start as offline
|
device.setPayloadNotAvailable("down");
|
||||||
|
device.enableLastWill(); // Broker publishes offline when TCP drops.
|
||||||
|
// device.setAvailability(false); // Optional: start as offline.
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
**Per-entity availability:** call `someEntity.setAvailability(true/false)` on each type. Does not use LWT the same way as shared mode; see examples under `examples/availability/`.
|
**Per-entity availability:** call `someEntity.setAvailability(true/false)` on each type. Does not use LWT the same way as shared mode; see examples under `examples/availability/`.
|
||||||
@@ -57,16 +68,22 @@ device.enableLastWill(); // broker publishes offline when TCP drops
|
|||||||
Custom per-entity payloads are supported:
|
Custom per-entity payloads are supported:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
sensor.setPayloadAvailable("ready");
|
// Configure the relevant entity before mqtt.begin(...).
|
||||||
sensor.setPayloadNotAvailable("lost");
|
void configureSensorAvailability() {
|
||||||
|
sensor.setPayloadAvailable("ready");
|
||||||
|
sensor.setPayloadNotAvailable("lost");
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
For multi-topic availability discovery, add full MQTT topics and a mode:
|
For multi-topic availability discovery, add full MQTT topics and a mode:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
sensor.setAvailabilityMode("all");
|
// `sensor` is the entity whose availability depends on these external topics.
|
||||||
sensor.addAvailabilityEntry("bridge/status");
|
void configureExternalAvailability() {
|
||||||
sensor.addAvailabilityEntry("sensor/status", "{{ value_json.state }}");
|
sensor.setAvailabilityMode("all");
|
||||||
|
sensor.addAvailabilityEntry("bridge/status");
|
||||||
|
sensor.addAvailabilityEntry("sensor/status", "{{ value_json.state }}");
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Discovery helpers by entity
|
## Discovery helpers by entity
|
||||||
@@ -74,40 +91,53 @@ sensor.addAvailabilityEntry("sensor/status", "{{ value_json.state }}");
|
|||||||
Common entity discovery metadata is available on most entity classes:
|
Common entity discovery metadata is available on most entity classes:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
entity.setEnabledByDefault(false);
|
// `entity` is an entity type that supports these common discovery fields.
|
||||||
entity.setEntityPicture("https://example.com/entity.png");
|
void configureCommonDiscovery(HABaseDeviceType& entity) {
|
||||||
entity.setQos(1);
|
entity.setEnabledByDefault(false);
|
||||||
entity.setEncoding("utf-8");
|
entity.setEntityPicture("https://example.com/entity.png");
|
||||||
entity.setEntityCategory("diagnostic");
|
entity.setQos(1);
|
||||||
|
entity.setEncoding("utf-8");
|
||||||
|
entity.setEntityCategory("diagnostic");
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Read-only sensor presentation/template helpers:
|
Read-only sensor presentation/template helpers:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
sensor.setSuggestedDisplayPrecision(2);
|
// `sensor` is a read-only HASensor instance.
|
||||||
sensor.setValueTemplate("{{ value_json.temperature }}");
|
void configureSensorPresentation(HASensor& sensor) {
|
||||||
sensor.setJsonAttributesTemplate("{{ value_json.attrs | tojson }}");
|
sensor.setSuggestedDisplayPrecision(2);
|
||||||
sensor.setLastResetValueTemplate("{{ value_json.last_reset }}");
|
sensor.setValueTemplate("{{ value_json.temperature }}");
|
||||||
sensor.setDeviceClass("enum");
|
sensor.setJsonAttributesTemplate("{{ value_json.attrs | tojson }}");
|
||||||
sensor.setOptions("idle;charging;discharging;fault");
|
sensor.setLastResetValueTemplate("{{ value_json.last_reset }}");
|
||||||
|
sensor.setDeviceClass("enum");
|
||||||
|
sensor.setOptions("idle;charging;discharging;fault");
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Writable entity template/payload helpers:
|
Writable entity template/payload helpers:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
mySwitch.setPayloadOn("ENABLE");
|
// These names refer to the matching writable entity instances in the application.
|
||||||
mySwitch.setPayloadOff("DISABLE");
|
void configureWritableEntities(HASwitch& mySwitch,
|
||||||
mySwitch.setStateOn("running");
|
HANumber& myNumber,
|
||||||
mySwitch.setStateOff("stopped");
|
HASelect& mySelect,
|
||||||
mySwitch.setValueTemplate("{{ value_json.state }}");
|
HAText& myText,
|
||||||
mySwitch.setCommandTemplate("{{ value_json.command }}");
|
HAButton& myButton) {
|
||||||
|
mySwitch.setPayloadOn("ENABLE");
|
||||||
|
mySwitch.setPayloadOff("DISABLE");
|
||||||
|
mySwitch.setStateOn("running");
|
||||||
|
mySwitch.setStateOff("stopped");
|
||||||
|
mySwitch.setValueTemplate("{{ value_json.state }}");
|
||||||
|
mySwitch.setCommandTemplate("{{ value_json.command }}");
|
||||||
|
|
||||||
myNumber.setPayloadReset("RESET");
|
myNumber.setPayloadReset("RESET");
|
||||||
myNumber.setCommandTemplate("{{ value | float | round(1) }}");
|
myNumber.setCommandTemplate("{{ value | float | round(1) }}");
|
||||||
|
|
||||||
mySelect.setCommandTemplate("{{ value_json.choice }}");
|
mySelect.setCommandTemplate("{{ value_json.choice }}");
|
||||||
myText.setCommandTemplate("{{ value_json.text }}");
|
myText.setCommandTemplate("{{ value_json.text }}");
|
||||||
myButton.setPayloadPress("PRESS");
|
myButton.setPayloadPress("PRESS");
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Compiler macros
|
## Compiler macros
|
||||||
@@ -121,12 +151,9 @@ Defined in `ArduinoHADefines.h` or via build flags.
|
|||||||
lifecycle callbacks remain ordinary function pointers.
|
lifecycle callbacks remain ordinary function pointers.
|
||||||
- **`ARDUINOHA_DEBUG`** — enables ArduinoHA logging by default and sets the initial maximum verbosity to `Debug`. Without this flag, structured logs are compiled in but remain disabled until you call `arduinoHASetLogEnabled(true)`.
|
- **`ARDUINOHA_DEBUG`** — enables ArduinoHA logging by default and sets the initial maximum verbosity to `Debug`. Without this flag, structured logs are compiled in but remain disabled until you call `arduinoHASetLogEnabled(true)`.
|
||||||
|
|
||||||
Structured logging is available through:
|
Structured logging is available through a long-lived sink:
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
arduinoHASetLogEnabled(true);
|
|
||||||
arduinoHASetLogLevel(ArduinoHALogLevel::Trace);
|
|
||||||
|
|
||||||
class MyLogSink : public ArduinoHALogSink {
|
class MyLogSink : public ArduinoHALogSink {
|
||||||
public:
|
public:
|
||||||
void log(const ArduinoHALogMessage& msg) override {
|
void log(const ArduinoHALogMessage& msg) override {
|
||||||
@@ -138,6 +165,15 @@ public:
|
|||||||
Serial.println(msg.text);
|
Serial.println(msg.text);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MyLogSink logSink; // The installed sink must outlive ArduinoHA logging.
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
Serial.begin(115200);
|
||||||
|
arduinoHASetLogSink(&logSink);
|
||||||
|
arduinoHASetLogEnabled(true);
|
||||||
|
arduinoHASetLogLevel(ArduinoHALogLevel::Trace);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
`arduinoHALog(...)` and `arduinoHALogf(...)` support subsystem-tagged messages such as `mqtt`, `discovery`, `availability`, `serializer`, `entity`, and `device`. Install a sink when you want those messages to join your application’s normal serial or structured log stream.
|
`arduinoHALog(...)` and `arduinoHALogf(...)` support subsystem-tagged messages such as `mqtt`, `discovery`, `availability`, `serializer`, `entity`, and `device`. Install a sink when you want those messages to join your application’s normal serial or structured log stream.
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ HAMqtt mqtt(client, device);
|
|||||||
void onMqttMessage(const char* topic, const uint8_t* payload, uint16_t length) {
|
void onMqttMessage(const char* topic, const uint8_t* payload, uint16_t length) {
|
||||||
// This callback is called when message from MQTT broker is received.
|
// This callback is called when message from MQTT broker is received.
|
||||||
// Please note that you should always verify if the message's topic is the one you expect.
|
// Please note that you should always verify if the message's topic is the one you expect.
|
||||||
// For example: if (memcmp(topic, "myCustomTopic") == 0) { ... }
|
// For example: if (strcmp(topic, "myCustomTopic") == 0) { ... }
|
||||||
|
|
||||||
Serial.print("New message on topic: ");
|
Serial.print("New message on topic: ");
|
||||||
Serial.println(topic);
|
Serial.println(topic);
|
||||||
Serial.print("Data: ");
|
Serial.print("Data: ");
|
||||||
Serial.println((const char*)payload);
|
// MQTT payloads are length-delimited; they are not guaranteed to end in NUL.
|
||||||
|
Serial.write(payload, length);
|
||||||
|
Serial.println();
|
||||||
|
|
||||||
mqtt.publish("myPublishTopic", "hello");
|
mqtt.publish("myPublishTopic", "hello");
|
||||||
}
|
}
|
||||||
|
|||||||
+31
-1
@@ -8,6 +8,32 @@ required=(
|
|||||||
examples/README.md
|
examples/README.md
|
||||||
)
|
)
|
||||||
|
|
||||||
|
check_cpp_fence_scope() {
|
||||||
|
local markdown="$1"
|
||||||
|
awk '
|
||||||
|
function brace_delta(line, copy) {
|
||||||
|
copy = line
|
||||||
|
return gsub(/\{/, "{", copy) - gsub(/\}/, "}", copy)
|
||||||
|
}
|
||||||
|
/^```cpp[[:space:]]*$/ { in_cpp = 1; depth = 0; next }
|
||||||
|
in_cpp && /^```[[:space:]]*$/ { in_cpp = 0; next }
|
||||||
|
in_cpp {
|
||||||
|
line = $0
|
||||||
|
sub(/^[[:space:]]+/, "", line)
|
||||||
|
if (depth == 0 &&
|
||||||
|
(line ~ /^(if|for|while|switch)[[:space:]]*\(/ ||
|
||||||
|
line ~ /^[A-Za-z_][A-Za-z0-9_:]*::[A-Za-z0-9_]+[[:space:]]*\(/ ||
|
||||||
|
line ~ /^[A-Za-z_][A-Za-z0-9_]*\./ ||
|
||||||
|
line ~ /^[A-Za-z_][A-Za-z0-9_]*[[:space:]]*\(/)) {
|
||||||
|
printf "%s:%d: C++ expression appears at namespace scope; wrap it in a function.\n", FILENAME, FNR > "/dev/stderr"
|
||||||
|
failed = 1
|
||||||
|
}
|
||||||
|
depth += brace_delta($0)
|
||||||
|
}
|
||||||
|
END { exit failed }
|
||||||
|
' "$markdown"
|
||||||
|
}
|
||||||
|
|
||||||
for path in "${required[@]}"; do
|
for path in "${required[@]}"; do
|
||||||
[[ -f "$root/$path" ]] || { echo "Missing required documentation: $path" >&2; exit 1; }
|
[[ -f "$root/$path" ]] || { echo "Missing required documentation: $path" >&2; exit 1; }
|
||||||
done
|
done
|
||||||
@@ -22,7 +48,7 @@ while IFS= read -r -d '' markdown; do
|
|||||||
esac
|
esac
|
||||||
[[ -e "$candidate" ]] || { echo "Broken relative link in ${markdown#$root/}: $target" >&2; exit 1; }
|
[[ -e "$candidate" ]] || { echo "Broken relative link in ${markdown#$root/}: $target" >&2; exit 1; }
|
||||||
done < <(sed -nE 's/.*\]\(([^ )]+)( "[^"]*")?\).*/\1/p' "$markdown")
|
done < <(sed -nE 's/.*\]\(([^ )]+)( "[^"]*")?\).*/\1/p' "$markdown")
|
||||||
done < <(find "$root" -path "$root/.git" -prune -o -name '*.md' -type f -print0)
|
done < <(find "$root" -path "$root/.git" -prune -o -path '*/.pio' -prune -o -name '*.md' -type f -print0)
|
||||||
|
|
||||||
while IFS= read -r example; do
|
while IFS= read -r example; do
|
||||||
for required in README.md platformio.ini; do
|
for required in README.md platformio.ini; do
|
||||||
@@ -34,4 +60,8 @@ while IFS= read -r example; do
|
|||||||
}
|
}
|
||||||
done < <(find "$root/examples" -mindepth 1 -maxdepth 1 -type d -name '[0-9][0-9]-*' -print | sort)
|
done < <(find "$root/examples" -mindepth 1 -maxdepth 1 -type d -name '[0-9][0-9]-*' -print | sort)
|
||||||
|
|
||||||
|
while IFS= read -r markdown; do
|
||||||
|
check_cpp_fence_scope "$markdown"
|
||||||
|
done < <(find "$root" -path "$root/.git" -prune -o -path '*/.pio' -prune -o -name '*.md' -type f -print)
|
||||||
|
|
||||||
echo "Documentation links and required files passed"
|
echo "Documentation links and required files passed"
|
||||||
|
|||||||
Reference in New Issue
Block a user