Hello Community!
I'm using the MQTT library in my Boron firmware for sending data to a server and optimising traffic instead using the use of HTTP data sending... It works but a connection to a broker breaks a moment after the connection is established. Question: why? And how to fix it? My code:
// This #include statement was automatically added by the Particle IDE.
// #include <HttpClient.h>
// This #include statement was automatically added by the Particle IDE.
#include <google-maps-device-locator.h>
// This #include statement was automatically added by the Particle IDE.
#include <MQTT.h>
String deviceName = "WP-O-Meter-" + System.deviceID();
String status = "OFF";
/* MEASUREMENT PARAMETERS */
const int pressureTransducerPin = A0;
double pressure;
const double minVoltage = 0.66; // Voltage at 0 psi
const double midVoltage = 1.28; // Mid voltage at 32 psi
const double maxVoltage = 3.3; // Voltage at 150 psi
const double minPressure = 0; // Pressure at 0 psi
const double midPressure = 32; // Mid pressure at 1.21V (32 psi)
const double maxPressure = 150; // Pressure at 150 psi
const double lowerScale = (midPressure - minPressure) / (midVoltage - minVoltage);
const double upperScale = (maxPressure - midPressure) / (maxVoltage - midVoltage);
/* ---------------------- */
/* HTTP AND LOCATOR SETTINGS */
// HttpClient http;
// const char* serverUrl = "X.XX.XXX.XX";
// const int port = 3099;
// const char* endpoint = "/api/v1/status";
// http_request_t request;
// http_response_t response;
// http_header_t headers[] = {
// { "Content-Type", "application/json" },
// { "Accept" , "*/*"},
// { NULL, NULL }
// };
GoogleMapsDeviceLocator locator;
float latitude;
float longitude;
String location;
const int numReadings = 10; // Number of readings to average
const int readingInterval = 3000; // Interval between readings in milliseconds (3 seconds)
const int sendingInterval = 4800000; // Interval between sendings data to the server (80 minutes)
double pressureReadingsSum = 0;
/* ------------------------- */
/* MQTT SETTING */
const char* mqttBroker = "3.69.177.92"; // Replace with your MQTT broker's hostname or IP address
const int mqttPort = 1883; // Replace with the appropriate port number
const char* clientId = deviceName.c_str(); // Replace with a unique client ID
MQTT client(mqttBroker, mqttPort, mqttCallback);
const char* statusRequestTopic = "/device/status/request"; // MQTT topic to receive status requests from the server
const char* statusResponseTopic = "/device/status/response"; // MQTT topic to publish status responses
// const char* measurementTopic = "/device/status/measurement"; // MQTT topic to publish status responses
const unsigned int idlingStep = sendingInterval / 70; // 0.4 min
/* ------------ */
void setup() {
Serial.begin(9600);
Network.on();
Network.connect();
pinMode(pressureTransducerPin, INPUT);
locator.withSubscribe(locationCallback).withLocatePeriodic(50);
// Connect to the MQTT broker
client.connect(clientId);
client.subscribe(statusRequestTopic);
}
void loop() {
listening();
}
void listening() {
unsigned int counter = 0;
while (counter < sendingInterval) {
if (client.isConnected()) {
client.loop();
}
else {
collectData();
client.connect(clientId);
postMeasurement(pressureReadingsSum/numReadings, location, status);
pressureReadingsSum = 0;
}
counter += idlingStep;
delay(idlingStep);
}
}
void collectData() {
for (int i = 0; i < numReadings; i++) {
pressureReadingsSum += getPressure();
delay(readingInterval);
}
status = getStatus();
pressure = getPressure();
location = getLocation(latitude, longitude);
}
There are a few issues with this code:
As I wrote above, the MQTT connection breaks after it is established.
Sending data in listening() function works and yes, the connection breaks after sending data. But the same actions as in listening() don't work in loop() function.
I thought that the problem may be caused by delay() state of the device... but when I tested by removing all from loop() it (the connection established in setup()) breaks anyway.
Hi @kirn0al, I can't provide any direct advice for the MQTT library as it is not officially supported as means of cloud communication from Particle.
However, looking at your data, I see that it could likely fit nicely into a Particle.publish(). Is there any reason why you are not using our first-party edge-to-cloud communication primitives for your project? They are significantly easier to implement (single line of code), provide E2E encryption by default, and you get visibility into your data transmission through our console.
Happy to answer any questions you have about them and get you on the right path for your application.
@dreamER, sure, there are certainly use cases and technical requirements for which our events system is not a fit. One of the best parts about Particle is that you are free to solve your problems as you see fit using either our solutions, or ones from the community.
However, there is a cost to owning your own communication layer and if the technical limitations of our comms layer are not the primary driver for choosing an alternate path, the fact remains that using Particle.publish() is easier to implement, maintain, and monitor at scale. We process billions of such events every month on behalf of our developers and customers. Our approach is clearly meeting the needs of many and is not something to be dismissed on principle alone.
My offer remains to @kirn0al to help identify the best path forward for the use case. I'd love to learn more specifically why you chose MQTT and what, if any, limitations you are finding with Particle.publish().
The issue with Particle.publish() is in the place where data publishing. I don't know how to connect my server to the Particle system and, honestly, do not have too much time to learn this. And plus I have experience of work with MQTT protocol.
In the current step of development of my project, this approach is a bit excessive.
Looks good, but I'm not sure about using millis() as if() condition. As showed the experience, in one moment device with such firmware gets mad and starts to send data every moment.
@kirn0al — all you would need to do is set up a webhook, which only takes a few minutes. The basic steps are:
Particle.publish() has 2 main arguments: a topic name, and your data field. The topic name is what our pub/sub system uses to route events, so give it a short descriptive name such as "pressure"
Set up a webhook to subscribe to the "pressure", give it an endpoint (your server), and optionally customize the data you are sending along with it with our templating engine
Webhooks also work on prefixes, so you can set up a single webhook to handle multiple different publishes. For example, you could set up a webhook to subscribe to "data", and name all of your publish topics "data/pressure", "data/temp", etc.
@Dan-Kouba Can we have less sales in a technical community forum and more help on the request please.
Your collect data blocks for at least 30 seconds as you have a 3 second delay and that's repeated 10 times.
try adding in a client.loop(); inside the loop so it keeps the mqtt connection alive.
eg
for (int i = 0; i < numReadings; i++) {
pressureReadingsSum += getPressure();
client.loop();
delay(readingInterval);
}
if (client.isConnected()) {
client.loop();
}else{
reconnect();
}
MQTT/TCP connection will recover by reconnect() function, even if MQTT/TCP connection is disconnected.
This library send/recv the MQTT ping/pong packet for the keep the MQTT/TCP connection by automatically(defined by MQTT_DEFAULT_KEEPALIVE ), and that packet send/recv in client.loop() function. MQTT/TCP connection is lost by the several reasons(MQTT/TCP timeout, L3/WiFi router TCP port mappings...etc).
So MQTT ping/pong packet keep the MQTT/TCP connection, but MQTT/TCP connection will lost by something reason, like a not exec the client.loop() function in MQTT_DEFAULT_KEEPALIVE. (I think that network connections are not always trusted)
So I think @dreamER pointed reconnect() function is good.
Thank you.