The eBus core (doc/core.md) on Linux and macOS: a HomieTransport over the Eclipse Paho MQTT C client, the clock and log bindings, a demo device, a demo controller, and end-to-end tests against a real mosquitto. CMake only; PlatformIO never builds this directory, because the root library.json limits a PlatformIO build to mqtt/src/, discovery/src/, homie/src/ and link/src/.
| Path | Contents |
|---|---|
include/ebus_posix/paho_transport.h, src/paho_transport.cpp |
PahoTransport: connect, reconnect, Last Will, inbound deferral, queue_publish() from any thread |
include/ebus_posix/posix_port.h, src/posix_port.cpp |
Clock (CLOCK_MONOTONIC, nanosleep), log (stderr), settable table storage, ebus_posix_register_settable() |
apps/device_main.cpp |
ebus-posix-device |
apps/controller_main.cpp |
ebus-posix-controller |
test/ |
ebus_posix_e2e (mosquitto) |
cmake -S ports/posix -B ports/posix/build
cmake --build ports/posix/build -j
ctest --test-dir ports/posix/build --output-on-failurePaho MQTT C v1.3.16 is fetched and checked against its SHA-256, and built as a static, plain-TCP library. An installed Paho is used instead when CMake finds its eclipse-paho-mqtt-c package; -DEBUS_POSIX_PAHO=fetch or =system forces one or the other. To build offline, pass -DFETCHCONTENT_SOURCE_DIR_PAHO_MQTT_C=<checkout> (and FETCHCONTENT_SOURCE_DIR_ARDUINOJSON for the core). -DEBUS_POSIX_WERROR=ON makes warnings in the port, demos and tests errors, as CI does.
The end-to-end tests need mosquitto (Homebrew: brew install mosquitto; Debian/Ubuntu: apt-get install mosquitto). Each test starts its own on a free 127.0.0.1 port and leaves its logs in build/e2e/<test>/. Without mosquitto, CMake warns and registers no test.
ports/posix/build/ebus-posix-device --host <broker> --device-id posix-demo
ports/posix/build/ebus-posix-controller --host <broker>
ports/posix/build/ebus-posix-controller --host <broker> --set posix-demo/switch/on=true --duration-s 5Both take --host, --port (1883), --user, --password (or EBUS_MQTT_PASSWORD, which stays out of ps), --domain (ebus), --reconnect-ms (2000) and --quiet. The device adds --device-id and --period-ms (temperature update period); the controller adds --set DEVICE/NODE/PROPERTY=VALUE and --duration-s. Both take --link SOURCES=>TARGET, repeatable, each followed by optional --link-format, --link-decimals and --link-interval-ms: a property link in the device or in the controller (doc/link.md). Plain TCP only: Paho is built without TLS.
The device publishes <domain>/5/<id> with switch/on (boolean, settable) and sensor/temperature (float, a simulated reading), and a child device <id>-child with status/uptime, which a worker thread publishes through queue_publish(). Ctrl-C sets $state to disconnected and disconnects cleanly; a kill leaves the broker to publish the will, lost. The controller prints one line per change on stdout (state, device, property, set) and the core's log on stderr.
| Piece | Here |
|---|---|
| Inbound messages | Paho's receive thread copies each message into a mutex-guarded arena (EBUS_POSIX_INBOX_BYTES, 64 KB) and returns. PahoTransport::loop(), on the application's thread, hands each one to settable_dispatch() if the settable table claims the topic, else to the fallback (controller_mqtt_callback() for a controller). Nothing that publishes or subscribes runs on Paho's thread. |
queue_publish() |
PahoTransport overrides the generic overload (ebus_mqtt's MqttTransport); a Property reaches it through HomieTransport's adapter. Any thread: copies into a fixed ring (EBUS_POSIX_PUBLISH_QUEUE, 64 entries of Property::VALUE_MAX), which loop() sends. The completion callback is called exactly once per call, from loop(), or at once when the ring is full or the message too long. |
| Connect | Clean session, keepalive 60 s, Last Will <root>/$state = lost, retained, at homie_qos(true). After each connect, mqtt_after_connect() runs, in order: flush the hold, subscribe every settable topic, call the application's on_connected(first). The device publishes the tree on the first; on later ones it calls forgetDescriptionHash() on every device and publishes the tree again, so a broker that lost its retained store gets every $description and value back (doc/core.md). |
| Link down | publish() holds or drops (below) and returns false; loop() reconnects at once, then every --reconnect-ms. |
| Settable table | EBUS_POSIX_SETTABLE_CAPACITY (64) static entries. No NodeEntity on this port, so no driver call is bound; a settable reaches the application through a settable_handler_t, registered with ebus_posix_register_settable(), which also works before the first connect. |
publish() passes each message to ebus_mqtt's PublishHold, which applies the rules of ebus-mqtt-client (doc/mqtt.md), and logs what it evicts or drops as too large. The hold is flushed on connect before the subscriptions and before on_connected; a flush the link interrupts keeps what it did not send. disconnect() discards the hold.
- No TLS.