Subversion Repositories ESP8266_P1_Meter

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 raymond 1
#ifndef __MQTT_REMOTE_H__
2
#define __MQTT_REMOTE_H__
3
 
4
#include "IMQTTRemote.h"
5
 
6
#include <freertos/FreeRTOS.h>
7
#include <freertos/event_groups.h>
8
#include <functional>
9
#include <map>
10
#include <mqtt_client.h>
11
#include <optional>
12
#include <string>
13
 
14
namespace MQTTRemoteLog {
15
const char TAG[] = "MQTTRemote";
16
} // namespace MQTTRemoteLog
17
 
18
namespace MQTTRemoteDefaults {
19
const uint32_t CONNECTION_STATUS_STACK_SIZE = 4096;
20
const uint32_t CONNECTION_STATUS_TASK_PRIORITY = 7;
21
} // namespace MQTTRemoteDefaults
22
 
23
/**
24
 * @brief MQTT wrapper for setting up MQTT connection (and will) and provide API for sending and subscribing to
25
 * messages.
26
 */
27
class MQTTRemote : public IMQTTRemote {
28
public:
29
  enum ConnectionState : uint8_t {
30
    Connected = BIT0,
31
    Disconnected = BIT1,
32
  };
33
 
34
  /**
35
   * Additional configuration where most user can go with defaults.
36
   */
37
  struct Configuration {
38
// ESP-IDF 4.4 backward compatibility
39
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
40
    typedef esp_mqtt_client_config_t::broker_t::verification_t verification_t;
41
#else
42
    // See
43
    // https://github.com/espressif/esp-mqtt/blob/ae53d799da294f03ef65c33e88fa33648e638134/include/mqtt_client.h#L244
44
    struct verification_t {
45
      bool use_global_ca_store = false;
46
      esp_err_t (*crt_bundle_attach)(void *conf) = nullptr;
47
      const char *certificate = nullptr;
48
      size_t certificate_len = 0;
49
      bool skip_cert_common_name_check = false;
50
      const struct psk_key_hint *psk_hint_key = nullptr;
51
      const char **alpn_protos = nullptr;
52
    };
53
#endif
54
 
55
    /**
56
     * Maximum message size, in bytes, for incoming messages. Messages larger than this will be truncated.
57
     * This will be allocated on the heap upon MQTTRemote object creation.
58
     */
59
    uint32_t rx_buffer_size = 1024;
60
 
61
    /**
62
     * Maximum message size, in bytes, for outgoing messages. Messages larger than this will be truncated.
63
     * This will be allocated on the heap upon MQTTRemote object creation.
64
     */
65
    uint32_t tx_buffer_size = 1024;
66
 
67
    /**
68
     * Task size to use for the ESP-IDF MQTT task. If you are handling large and/or many messages (queues), you might
69
     * need to increase this.
70
     * Normally defaults to 6144 bytes, setting this to non std::opt will override setting from menuconfig.
71
     */
72
    std::optional<uint32_t> task_size = 4096;
73
 
74
    /**
75
     * MQTT keep alive interval, in seconds. If the client fails to communicate with the broker within the specified
76
     * Keep Alive period, the LWT/Last Will message is sent (by the broker).
77
     */
78
    uint32_t keep_alive_s = 10;
79
 
80
    /**
81
     * Values, see esp_mqtt_transport_t:
82
     * - MQTT_TRANSPORT_OVER_TCP = mqtt
83
     * - MQTT_TRANSPORT_OVER_SSL = mqtts (TLS)
84
     * - MQTT_TRANSPORT_OVER_WS = ws (websockets)
85
     * - MQTT_TRANSPORT_OVER_WSS = wss (websockets with TLS)
86
     *
87
     * If not set, will try to decude the transport to use given the host, based on schemes `mqtt`, `mqtts`, `ws`,
88
     * `wss`. If no scheme is found, will default to `mqtt`.
89
     */
90
    std::optional<esp_mqtt_transport_t> transport = std::nullopt;
91
 
92
    /**
93
     * If using TLS, this configures the certificate verification. Must be set if using TLS.
94
     *
95
     * With this, you can configure to use your own private certificate, or using the global bundle.
96
     *
97
     * Example to use the global bundle:
98
     * ```cpp
99
     *   #include <esp_rls.h>
100
     *
101
     *  .vertification = {
102
     *    .use_global_ca_store = true,
103
     *  },
104
     * ```
105
     *
106
     * Then before calling start():
107
     *
108
     * ```cpp
109
     *   mbedtls_ssl_config conf;
110
     *   mbedtls_ssl_config_init(&conf);
111
     *   esp_tls_init_global_ca_store();
112
     * ```
113
     * Here you have the option to use other TLS implementation like mbedtls or wolfssl.
114
     *
115
     *
116
     * Example how to use letsencrypt (or similary for your own certificates):
117
     * 1. Download the ISRG Root X1 PEM file from https://letsencrypt.org/certs/isrgrootx1.pem
118
     * 2. Add in main directory
119
     * 3. Add `target_add_binary_data(${COMPONENT_TARGET} "isrgrootx1.pem" TEXT)` to your CMakeLists.txt
120
     *
121
     * Setup verification as follows:
122
     * ```cpp
123
     *   extern const uint8_t isrgrootx1_pem_start[] asm("_binary_isrgrootx1_pem_start");
124
     *
125
     *  .vertification = {
126
     *    .certificate = (const char *)isrgrootx1_pem_start,
127
     *  },
128
     * ```
129
     *
130
     * If using your own certificate, you might need to set `skip_cert_common_name_check` to true in the verification.
131
     */
132
    verification_t verification = {};
133
  };
134
 
135
  /**
136
   * @brief Construct a new MQTTRemote object
137
   *
138
   * To set log level for this object, use: esp_log_level_set(MQTTRemoteLog::TAG, ESP_LOG_*);
139
   *
140
   * A call to start() most follow.
141
   *
142
   * @param client_id Base ID for this device. This is used for the last will / status
143
   * topic. Example, if this is "esp_now_router", then the status/last will topic will be "esp_now_router/status". This
144
   * is also used as client ID for the MQTT connection. This has to be [a-zA-Z0-9_] only and unique among all MQTT
145
   * clients on the server. It should also be stable across connections.
146
   * @param host MQTT hostname or IP for MQTT server. Supports `mqtt`, `mqtts`, `ws`, `wss` schemes. For example,
147
   * mqtts://hostname or just hostname/IP. If not using schemes, transport must be set in Configuration. Remember to
148
   * also specify the correct port to use for your schema.
149
   * @param port MQTT port number.
150
   * @param username MQTT username.
151
   * @param password MQTT password.
152
   */
153
  MQTTRemote(std::string client_id, std::string host, int port, std::string username, std::string password)
154
      : MQTTRemote(std::move(client_id), std::move(host), port, std::move(username), std::move(password),
155
                   Configuration{}) {}
156
 
157
  /**
158
   * @brief Construct a new MQTTRemote object
159
   *
160
   * To set log level for this object, use: esp_log_level_set(MQTTRemoteLog::TAG, ESP_LOG_*);
161
   *
162
   * A call to start() most follow.
163
   *
164
   * @param client_id Base ID for this device. This is used for the last will / status
165
   * topic. Example, if this is "esp_now_router", then the status/last will topic will be "esp_now_router/status". This
166
   * is also used as client ID for the MQTT connection. This has to be [a-zA-Z0-9_] only and unique among all MQTT
167
   * clients on the server. It should also be stable across connections.
168
   * @param host MQTT hostname or IP for MQTT server.
169
   * @param port MQTT port number.
170
   * @param username MQTT username.
171
   * @param password MQTT password.
172
   * @param configuration Additional configuration where most user can go with defaults.
173
   */
174
  MQTTRemote(std::string client_id, std::string host, int port, std::string username, std::string password,
175
             Configuration configuration);
176
 
177
  /**
178
   * @brief Call once there is a WIFI connection on which the host can be reached.
179
   * Will connect to the server and setup any subscriptions as well as start the MQTT loop.
180
   * @param on_connection_change optional callback on connection state change. Will be called when the client is
181
   * connected to server (every time, so expect calls on reconnection), and on disconnect. The parameter will be true on
182
   * new connection and false on disconnection. This callback will run from a dedicated task. Task size and priority can
183
   * be set.
184
   * @param task_size the stack size for the task that will call the on_connection_change callback, if set.
185
   * @param task_priority the priority for the task that will call the on_connection_change callback, if set.
186
   *
187
   * NOTE: Can only be called once WIFI has been setup! ESP-IDF will assert otherwise.
188
   */
189
  void start(std::function<void(bool)> on_connection_change = {},
190
             unsigned long task_size = MQTTRemoteDefaults::CONNECTION_STATUS_STACK_SIZE,
191
             uint8_t task_priority = MQTTRemoteDefaults::CONNECTION_STATUS_TASK_PRIORITY);
192
 
193
  /**
194
   * @brief Call once there is a WIFI connection on which the host can be reached.
195
   * Will connect to the server and setup any subscriptions as well as start the MQTT loop.
196
   * @param connection_state_changed_event_group optional event group for connection state change. Will be be set with
197
   * ConnectionState::Connected when the client is connected to server (every time, so expect re-setting on
198
   * reconnection), and ConnectionState::Disconnected on disconnect.
199
   *
200
   * NOTE: Can only be called once WIFI has been setup! ESP-IDF will assert otherwise.
201
   */
202
  void start(EventGroupHandle_t connection_state_changed_event_group);
203
 
204
  /**
205
   * @brief Publish a message.
206
   *
207
   * @param topic the topic to publish to.
208
   * @param message The message to send. This cannot be larger than the value set for max_message_size in the
209
   * constructor.
210
   * @param retain True to set this message as retained.
211
   * @param qos quality of service for published message (0 (default), 1 or 2)
212
   * @returns true on success, or false on failure.
213
   */
214
  bool publishMessage(std::string topic, std::string message, bool retain = false, uint8_t qos = 0) override;
215
 
216
  /**
217
   * Same as publishMessage(), but will print the message and topic and the result on serial.
218
   */
219
  bool publishMessageVerbose(std::string topic, std::string message, bool retain = false, uint8_t qos = 0) override;
220
 
221
  /**
222
   * @brief returns if there is a connection to the MQTT server.
223
   */
224
  bool connected() override { return _connected; }
225
 
226
  /**
227
   * @brief Subscribe to a topic. The callback will be invoked on every new message.
228
   * There can only be one callback per topic. If trying to subscribe to an already subscribe topic, it will be ignored.
229
   * Don't do heavy operations in the callback or delays as this will block the MQTT callback.
230
   *
231
   * Can be called before being connected. All subscriptions will be (re-)subscribed to once a connection is
232
   * (re-)established.
233
   *
234
   * @param message_callback a message callback with the topic and the message. The topic is repeated for convenience,
235
   * but it will always be for the subscribed topic.
236
   * @return true if a subcription was successful. Will return false if there is no active MQTT connection. In this
237
   * case, the subscription will be performed once connected. Will return false if this subscription is already
238
   * subscribed to.
239
   */
240
  bool subscribe(std::string topic, IMQTTRemote::SubscriptionCallback message_callback) override;
241
 
242
  /**
243
   * @brief Unsubscribe a topic.
244
   */
245
  bool unsubscribe(std::string topic) override;
246
 
247
  /**
248
   * @brief The client ID for this device. This is used for the last will / status
249
   * topic.Example, if this is "esp_now_router", then the status/last will topic will be "esp_now_router/status". This
250
   * has to be [a-zA-Z0-9_] only.
251
   */
252
  std::string &clientId() override { return _client_id; }
253
 
254
private:
255
  void startInternal();
256
 
257
  /*
258
   * @brief Event handler registered to receive MQTT events
259
   *
260
   *  This function is called by the MQTT client event loop.
261
   *
262
   * @param handler_args user data registered to the event.
263
   * @param base Event base for the handler(always MQTT Base in this example).
264
   * @param event_id The id for the received event.
265
   * @param event_data The data for the event, esp_mqtt_event_handle_t.
266
   */
267
  static void onMqttEvent(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data);
268
 
269
  static void runTask(void *pvParams);
270
 
271
private:
272
  bool _started = false;
273
  std::string _client_id;
274
  bool _connected = false;
275
  std::string _last_will_topic;
276
  esp_mqtt_client_handle_t _mqtt_client;
277
  std::function<void(bool)> _on_connection_change;
278
  EventGroupHandle_t _connection_state_changed_event_group;
279
  std::map<std::string, SubscriptionCallback> _subscriptions;
280
};
281
 
282
#endif // __MQTT_REMOTE_H__