ESP8266 Door Access Control System
Loading...
Searching...
No Matches
WiFiMqttClient.cpp
Go to the documentation of this file.
1/**
2 * @file WiFiMqttClient.cpp
3 * @brief Implementation of the WifiMqttClient helper class.
4 *
5 * @ingroup infrastructure
6 */
7
8
9#include "WiFiMqttClient.h"
10#include <ESP8266WiFi.h>
11// #include <esp_system.h>
12
13/**
14 * @brief Human-readable device platform name.
15 *
16 * Included in published MQTT metadata.
17 */
18static constexpr const char* DEVICE_NAME = "ESP8266";
19
20/**
21 * @brief Default constructor.
22 *
23 * Initializes the PubSubClient instance with the internal WiFiClient.
24 */
26 : mqtt(wifiClient) {}
27
28/**
29 * @brief Initializes WiFi and MQTT configuration.
30 *
31 * Stores provided credentials, prepares base topic structure,
32 * configures WiFi and MQTT clients, and performs initial connections.
33 *
34 * @param wifiSsid WiFi network SSID.
35 * @param wifiPass WiFi network password.
36 * @param mqttHost MQTT broker hostname.
37 * @param mqttPort MQTT broker port.
38 * @param mqttUser MQTT username.
39 * @param mqttPass MQTT password.
40 * @param deviceId Device identifier used in topic hierarchy.
41 * @param site Site identifier used in topic hierarchy.
42 */
44 const char* wifiSsid,
45 const char* wifiPass,
46 const char* mqttHost,
47 uint16_t mqttPort,
48 const char* mqttUser,
49 const char* mqttPass,
50 const char* deviceId,
51 const char* site
52) {
53 // Store configuration parameters
54 this->wifiSsid = wifiSsid;
55 this->wifiPass = wifiPass;
56 this->mqttHost = mqttHost;
57 this->mqttPort = mqttPort;
58 this->mqttUser = mqttUser;
59 this->mqttPass = mqttPass;
60 this->deviceId = deviceId;
61 this->site = site;
62
63 // Retrieve unique chip identifier for client ID generation
64 chipId = ESP.getChipId();
65
66 // Construct base MQTT topic: <user>/<site>/<device>
67 baseTopic = String(mqttUser) + "/" + site + "/" + deviceId;
68
69 // Configure WiFi and MQTT clients
70 WiFi.mode(WIFI_STA);
71 mqtt.setServer(mqttHost, mqttPort);
72
73 // Increase MQTT buffer to support JSON payloads
74 mqtt.setBufferSize(1024);
75
76 // Perform initial connections
77 connectWifi();
78 connectMqtt();
79}
80
81/**
82 * @brief Main service loop.
83 *
84 * Ensures WiFi and MQTT connections remain active
85 * and processes incoming MQTT messages.
86 */
88
89 // Reconnect WiFi if connection was lost
90 if (WiFi.status() != WL_CONNECTED) {
91 connectWifi();
92 }
93
94 // Reconnect MQTT if disconnected
95 if (!mqtt.connected()) {
96 connectMqtt();
97 }
98
99 // Process MQTT client state machine
100 mqtt.loop();
101}
102
103/**
104 * @brief Checks whether the MQTT client is connected.
105 *
106 * @return true if connected, false otherwise.
107 */
109 return mqtt.connected();
110}
111
112/**
113 * @brief Establishes a WiFi connection.
114 *
115 * Blocks until connected or timeout occurs.
116 * Safe to call repeatedly.
117 */
118void WifiMqttClient::connectWifi() {
119
120 Serial.println();
121 Serial.println("=== WiFi: connect start ===");
122 Serial.print("SSID: ");
123 Serial.println(wifiSsid);
124
125 // Abort if already connected
126 if (WiFi.status() == WL_CONNECTED) return;
127
128 Serial.print("WiFi mode: ");
129 Serial.println(WIFI_STA ? "STA" : "UNKNOWN");
130
131 Serial.println("Calling WiFi.begin()");
132
133 // Start WiFi connection attempt
134 WiFi.begin(wifiSsid, wifiPass);
135
136 unsigned long start = millis();
137 uint8_t dots = 0;
138
139 // Wait until connected or timeout
140 while (WiFi.status() != WL_CONNECTED) {
141 Serial.print(".");
142 dots++;
143
144 // Print newline every 16 dots for readability
145 if (dots % 16 == 0) {
146 Serial.println();
147 }
148
149 // Abort after 15 seconds to avoid permanent blocking
150 if (millis() - start > 15000) {
151 Serial.println();
152 Serial.println("WiFi connect timeout (15s)");
153 return;
154 }
155
156 delay(500);
157 }
158}
159
160/**
161 * @brief Establishes an MQTT connection.
162 *
163 * Blocks until connected. Generates a unique
164 * client ID based on device and chip identifiers.
165 */
166void WifiMqttClient::connectMqtt() {
167
168 // Retry until MQTT connection succeeds
169 while (!mqtt.connected()) {
170
171 // Construct unique MQTT client ID
172 String clientId =
173 String(DEVICE_NAME) + "_" +
174 deviceId +
175 "_" +
176 String((uint32_t)chipId, HEX);
177
178 // Attempt MQTT connection using credentials
179 mqtt.connect(clientId.c_str(), mqttUser, mqttPass);
180
181 // Wait before retrying on failure
182 if (!mqtt.connected()) {
183 delay(2000);
184 }
185 }
186}
187
188/**
189 * @brief Constructs a fully qualified MQTT topic.
190 *
191 * Appends a suffix to the base topic:
192 * <user>/<site>/<deviceId>/<suffix>
193 *
194 * @param suffix Topic suffix.
195 * @return Constructed topic as an Arduino String.
196 */
197String WifiMqttClient::makeTopic(const char* suffix) const {
198 return baseTopic + "/" + suffix;
199}
200
201/**
202 * @brief Publishes a JSON document to an MQTT topic.
203 *
204 * Wraps the provided JSON data in a standard envelope
205 * containing device metadata and a timestamp.
206 *
207 * @param topicSuffix Topic suffix appended to the base topic.
208 * @param data JSON document containing application payload.
209 * @return true if publish succeeded, false otherwise.
210 */
212 const char* topicSuffix,
213 const JsonDocument& data
214) {
215 StaticJsonDocument<256> envelope;
216
217 // Embed device metadata
218 JsonObject device = envelope.createNestedObject("device");
219 device["id"] = deviceId;
220 device["platform"] = DEVICE_NAME;
221 device["chip_id"] = String((uint32_t)chipId, HEX);
222
223 // Attach timestamp and payload
224 envelope["sent_ts_ms"] = millis();
225 envelope["data"] = data;
226
227 // Serialize JSON into a temporary buffer
228 char buffer[512];
229 size_t len = serializeJson(envelope, buffer);
230
231 // Publish serialized payload
232 return mqtt.publish(
233 makeTopic(topicSuffix).c_str(),
234 buffer,
235 len
236 );
237}
238
239/**
240 * @brief Sets the MQTT message callback.
241 *
242 * @param MQTT_CALLBACK_SIGNATURE Callback function pointer.
243 */
244void WifiMqttClient::setCallback(MQTT_CALLBACK_SIGNATURE) {
245 mqtt.setCallback(callback);
246}
247
248/**
249 * @brief Subscribes to an MQTT topic.
250 *
251 * @param topic Full MQTT topic string.
252 * @return true if subscription succeeded, false otherwise.
253 */
254bool WifiMqttClient::subscribe(const char* topic) {
255 if (!mqtt.connected()) return false;
256 return mqtt.subscribe(topic);
257}
258
259/**
260 * @brief Unsubscribes from an MQTT topic.
261 *
262 * @param topic Full MQTT topic string.
263 * @return true if unsubscribe succeeded, false otherwise.
264 */
265bool WifiMqttClient::unsubscribe(const char* topic) {
266 if (!mqtt.connected()) return false;
267 return mqtt.unsubscribe(topic);
268}
static constexpr const char * DEVICE_NAME
Human-readable device platform name.
Lightweight WiFi + MQTT helper wrapper for ESP-based Arduino systems.
void callback(char *topic, byte *payload, unsigned int length)
MQTT message callback handler.
void loop()
Main service loop.
bool subscribe(const char *topic)
Subscribes to a topic.
bool connected()
Checks whether the MQTT client is currently connected.
WifiMqttClient()
Default constructor.
String makeTopic(const char *suffix) const
Constructs a fully qualified MQTT topic.
bool unsubscribe(const char *topic)
Unsubscribes from a topic.
void setCallback(MQTT_CALLBACK_SIGNATURE)
Sets the MQTT message callback.
bool publishJson(const char *topicSuffix, const JsonDocument &data)
Publishes a JSON document to an MQTT topic.
void begin(const char *wifiSsid, const char *wifiPass, const char *mqttHost, uint16_t mqttPort, const char *mqttUser, const char *mqttPass, const char *deviceId, const char *site)
Initializes WiFi and MQTT configuration.