Closed mhaberler closed 2 months ago
The reason behind this limitation of the server is that I wanted to allow publishing messages in chunks.
Writing in chunks can be done by using the begin_publish()
method (see https://github.com/mlesniew/PicoMQTT?tab=readme-ov-file#arbitrary-sized-messages). The method returns an object that has a Print
interface, so it can be written to using the usual methods (like write
, print
, printf
, println
). The object will allow using these methods multiple times, until the full declared size of the payload is reached.
This way the library can send the MQTT header right away (when begin_publish()
is called) and then send the payload bit by bit with every write/print call. There is some optional buffering done to not submit chunks that are very small, but in general this reduces the memory use significantly.
With this approach we can send MQTT messages even bigger than the RAM size. It's useful when the message content can be generated on the fly like in the ArduinoJson examples -- it saves a lot of memory.
The consequence of this is that we may never have the full payload of the message we're sending in any buffer. This means we can't fire a subscription callback, because we can't provide it with a complete payload
argument.
To make things even more complicated, the subscription API also supports reading messages in a streaming fashion...
Now regarding your specific use case. You can probably make things work using the following approach:
PicoMQTT::Server mqtt;
LocalSensor local_sensor;
void on_sensor_update(const char * topic, const char * payload) {
// update UI
...
}
void publish_wrapper(const String & topic, const String & payload) {
mqtt.publish(topic, payload);
on_sensor_update(topic.c_str(), payload.c_str());
}
void setup() {
...
mqtt.subscribe("sensor/#", on_sensor_update);
mqtt.begin();
}
void loop() {
mqtt.loop();
if (local_sensor.updated()) {
// sensor has a new reading
const String topic = String("sensor/") + local_sensor.get_name();
const String payload = String(local.sensor.get_reading());
publish_wrapper(topic, payload);
}
...
}
You could also subclass PicoMQTT::Server
and override the publish
methods to do something similar (but you'll have to avoid using begin_publish
).
the first method (bypass the broker via on_sensor_update
) works around the issue
however, compared to a subscription it has the disadvantage of send-always instead of send-on-payload-change
I've added some changes to support local subscribe -- they're currently on the server_local_subscribe branch.
Give it a try, probably all you'll have to change in your code is use PicoMQTT::ServerLocalSubscribe
instead of PicoMQTT::Server
, but have a look at this section of the readme and the example.
really appreciated!
will try it out and report.
The changes from the branch are now merged into master and included in release 1.1.0
did try it out - found no issues
Great, in that case, I'm closing the issue
you write:
PicoMQTT::Server
will not deliver published messages locally. This means that if you set up aPicoMQTT::Server
and usesubscribe
, your callback will only be called when messages from clients are received. Messages published on the same device will not trigger the callback.is there a way to override this behavior - either on a publish or subscribe basis?
to explain the use case (whacky design alert):
I guess it could work with a local client, not the server publish API?