Skip to content

Commit

Permalink
Add docs
Browse files Browse the repository at this point in the history
  • Loading branch information
bertmelis authored May 24, 2022
1 parent 06fcf1e commit ed2fe38
Show file tree
Hide file tree
Showing 8 changed files with 366 additions and 31 deletions.
2 changes: 2 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Do you use TLS or not?
Do you use an IDE (Arduino, Platformio...)?
Which version of the Arduino framework?

Please include any debug output and/or decoded stack trace if applicable.

**Expected behaviour**
A clear and concise description of what you expected to happen.

Expand Down
39 changes: 9 additions & 30 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,36 +22,9 @@ Aims to be a non-blocking fully compliant MQTT 3.1.1 client.

** strikethrough features are in development

# Runtime behaviour
# Documentation

A normal cycle of an MQTT client goes like this:
1. setup the client
2. connect to the broker
3. subscribe/publish/receive
4. disconnect/reconnect when disconnected

## Setup

Setting up the client means to tell which host and port to connect to, possible credentials to use and so on. espMqttClient has a set of methods to configure the client. Setup is generally done in the `setup()` function of the Arduino framework.
One important thing to remember is that there are a number of settings that are not stored inside the library: `username`, `password`, `willTopic`, `willPayload`, `clientId` and `host`. Make sure these variables stay available during the lifetime of the `espMqttClient`.

For TLS secured connections, the relevant methods from `WiFiClientSecure` have been made available.

## Connecting

After setting up the client, you are ready to connect. A simple call to `connect()` does the trick. If you set a `OnConnectCallback`, you will be notified when the connection has been made. On failure, `OnDisconnectCallback` will be called.

## Subscribing, publishing and receiving

Once connected, you can subscribe, publish and receive. The methods to do this return the packetId of the generated packet or `1` for packets without ID. In case of an error, the method returns `0`. When the client is not connected, you cannot subscribe, unsubscribe or publish.

Receiving packets is done via the `onMessage`-callback. This callback gives you the topic, properties (qos, dup, retain, packetId) and payload. For the payload, you get a pointer to the data, the index, length and total length. On long payloads it is normal that you get multiple callbacks for the same packet. This way, you can receive payloads longer than what could fit in the microcontroller's RAM-memory.

> Beware that MQTT payloads are binary. MQTT payloads are **not** c-strings unless explicitely constructed like that. You therefore can **not** print the payload to your Serial monitor without supporting code.
## Disconnecting

You can disconnect from the broker by calling `disconnect()`. If you do not force-disconnect, the client will first send the remaining messages that are in the queue and disconnect afterwards. During this period however, no new incoming PUBLISH messages will be processed.
See [documentation](docs)

## Limitations

Expand All @@ -71,7 +44,13 @@ This library aims to be fully non-blocking. It is however limited by the underly

Please use Github's facilities to get in touch.

# About this library

This client wouldn't exist without [Async-mqtt-client](https://github.com/marvinroger/async-mqtt-client). It has been my go-to MQTT client for many years. It was fast and -despite other opinions- reliable and had features that were non-existing in alternative libraries. However, the underlying async TCP libraries are lacking updates so I decided to write my own library, from scratch.

The result is an almost non-blocking library with no external dependencies. Except for a few type differences, the library is a drop-in replacement for the async-mqtt-client.

# License

This library is released under the MIT Licence. A copy is included in the repo.
Parts of this library are based upon [Async MQTT client for ESP8266 and ESP32](https://github.com/marvinroger/async-mqtt-client).
Parts of this library are based on [Async MQTT client for ESP8266 and ESP32](https://github.com/marvinroger/async-mqtt-client).
6 changes: 6 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# espMqttClient Documentation

1. [Runtime behaviour](runtime-behaviour.md)
2. [API Reference](api-reference.md)
3. [Compile-time configuration](compile-time-configuration.md)
4. [Code samples](code-samples)
187 changes: 187 additions & 0 deletions docs/api-reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
# API reference
espMqttClient
## espMqttClient() - espMqttClientSecure()

Instantiate a new espMqttClient or espMqttSecure object.
On ESP32, two optional parameters are available: `espMqttClient(uint8_t priority = 1, uint8_t core = 1)`. This will change the priority of the MQTT client task and the core on which it runs.

## Configuration

### espMqttClient& setKeepAlive(uint16_t `keepAlive`)

Set the keep alive. Defaults to 15 seconds.

* **`keepAlive`**: Keep alive in seconds

### espMqttClient& setClientId(const char\* `clientId`)

Set the client ID. Defaults to `esp8266<chip ID on 6 hex caracters>`.
The library only stores a pointer to the client ID. Make sure the variable pointed to stays available throughout the lifetime of espMqttClient.

* **`clientId`**: Client ID, expects a null-terminated char array (c-string)

### espMqttClient& setCleanSession(bool `cleanSession`)

Set the CleanSession flag. Defaults to `true`.

* **`cleanSession`**: clean session wanted or not

### espMqttClient& setCredentials(const char\* `username`, const char\* `password`)

Set the username/password. Defaults to non-auth.
The library only stores a pointer to the username and password. Make sure the variable to pointed stays available throughout the lifetime of espMqttClient.

* **`username`**: Username, expects a null-terminated char array (c-string)
* **`password`**: Password, expects a null-terminated char array (c-string)

### espMqttClient& setWill(const char\* `topic`, uint8_t `qos`, bool `retain`, const uint8_t\* `payload`, size_t `length`)

Set the Last Will Testament. Defaults to none.
The library only stores a pointer to the topic and payload. Make sure the variable pointed to stays available throughout the lifetime of espMqttClient.

* **`topic`**: Topic of the LWT, expects a null-terminated char array (c-string)
* **`qos`**: QoS of the LWT
* **`retain`**: Retain flag of the LWT
* **`payload`**: Payload of the LWT.
* **`length`**: Payload length

### espMqttClient& setWill(const char\* `topic`, uint8_t `qos`, bool `retain`, const char\* `payload`)

Set the Last Will Testament. Defaults to none.
The library only stores a pointer to the topic and payload. Make sure the variable pointed to stays available throughout the lifetime of espMqttClient.

* **`topic`**: Topic of the LWT, expects a null-terminated char array (c-string)
* **`qos`**: QoS of the LWT
* **`retain`**: Retain flag of the LWT
* **`payload`**: Payload of the LWT, expects a null-terminated char array (c-string). It's lenght will be calculated using `strlen(payload)`

### espMqttClient& setServer(IPAddress `ip`, uint16_t `port`)

Set the server. Mind that when using `espMqttClientSecure` with a certificate, the hostname will be chacked against the certificate. OFten IP-addresses are not valid and the connection will fail.

* **`ip`**: IP of the server
* **`port`**: Port of the server

### espMqttClient& setServer(const char\* `host`, uint16_t `port`)

Set the server.

* **`host`**: Host of the server, expects a null-terminated char array (c-string)
* **`port`**: Port of the server

### Options for TLS connections

All common options from WiFiClientSecure to setup an encrypted connection are made available. These include:

* espMqttClientSecure& setInsecure()
* espMqttClientSecure& setCACert(const char* rootCA) (ESP32 only)
* espMqttClientSecure& setCertificate(const char* clientCa) (ESP32 only)
* espMqttClientSecure& setPrivateKey(const char* privateKey) (ESP32 only)
* espMqttClientSecure& setPreSharedKey(const char* pskIdent, const char* psKey) (ESP32 only)
* espMqttClientSecure& setFingerprint(const uint8_t fingerprint[20]) (ESP8266 only)
* espMqttClientSecure& setTrustAnchors(const X509List *ta) (ESP8266 only)
* espMqttClientSecure& setClientRSACert(const X509List *cert, const PrivateKey *sk) (ESP8266 only)
* espMqttClientSecure& setClientECCert(const X509List *cert, const PrivateKey *sk, unsigned allowed_usages, unsigned cert_issuer_key_type) (ESP8266 only)
* espMqttClientSecure& setCertStore(CertStoreBase *certStore) (ESP8266 only)

For documenation, please go to [ESP8266's documentation](https://arduino-esp8266.readthedocs.io/en/latest/esp8266wifi/readme.html#bearssl-client-secure-and-server-secure) or [ESP32's documentation](https://github.com/espressif/arduino-esp32/tree/master/libraries/WiFiClientSecure)

### Events handlers

#### espMqttClient& onConnect(espMqttClientTypes::OnConnectCallback `callback`)

Add a connect event handler.

* **`callback`**: Function to call

#### espMqttClient& onDisconnect(espMqttClientTypes::OnDisconnectCallback `callback`)

Add a disconnect event handler.

* **`callback`**: Function to call

#### espMqttClient& onSubscribe(espMqttClientTypes::OnSubscribeCallback `callback`)

Add a subscribe acknowledged event handler.

* **`callback`**: Function to call

#### espMqttClient& onUnsubscribe(espMqttClientTypes::OnUnsubscribeCallback `callback`)

Add an unsubscribe acknowledged event handler.

* **`callback`**: Function to call

#### espMqttClient& onMessage(espMqttClientTypes::OnMessageCallback `callback`)

Add a publish received event handler.

* **`callback`**: Function to call

#### espMqttClient& onPublish(espMqttClientTypes::OnPublishCallback `callback`)

Add a publish acknowledged event handler.

* **`callback`**: Function to call

### Operation functions

#### bool connected()

Return if the client is currently connected to the broker or not.

#### void connect()

Connect to the server.

#### void disconnect(bool `force` = false)

Disconnect from the server.
when disconnecting with `force` false, the client first tries to handle all the outgoing messages in the queue and disconnect cleanly afterwards. during this time, no incoming PUBLISH messages are handled.

* **`force`**: Whether to force the disconnection. Defaults to `false` (clean disconnection).

#### uint16_t subscribe(const char\* `topic`, uint8_t `qos`)

Subscribe to the given topic at the given QoS. Return the packet ID or 0 if failed.

* **`topic`**: Topic, expects a null-terminated char array (c-string)
* **`qos`**: QoS

#### uint16_t unsubscribe(const char\* `topic`)

Unsubscribe from the given topic. Return the packet ID or 0 if failed.

* **`topic`**: Topic, expects a null-terminated char array (c-string)

#### uint16_t publish(const char\* `topic`, uint8_t `qos`, bool `retain`, const uint8\* `payload`, size_t `length`)

Publish a packet. Return the packet ID (or 1 if QoS 0) or 0 if failed. The topic and payload will be buffered by the library.

* **`topic`**: Topic, expects a null-terminated char array (c-string)
* **`qos`**: QoS
* **`retain`**: Retain flag
* **`payload`**: Payload
* **`length`**: Payload length

#### uint16_t publish(const char\* `topic`, uint8_t `qos`, bool `retain`, const char\* `payload`)

Publish a packet. Return the packet ID (or 1 if QoS 0) or 0 if failed. The topic and payload will be buffered by the library.

* **`topic`**: Topic, expects a null-terminated char array (c-string)
* **`qos`**: QoS
* **`retain`**: Retain flag
* **`payload`**: Payload, expects a null-terminated char array (c-string). It's lenght will be calculated using `strlen(payload)`

#### void clearQueue()

When disconnected, clears all queued messages.
Keep in mind that this also deletes any session data and therefore is no MQTT compliant.

#### void loop()

This is the worker function of the MQTT client. For ESP8266 you must call this function in the Arduino loop. For ESP32 this function is only used internally and is not available in the API.

#### const char* getClientId() const

Retuns the client ID.
83 changes: 83 additions & 0 deletions docs/code-samples.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# Code samples

A number of examples are in the [examples](/examples) directory. These include basic operation on ESP8266 and ESP32. Please examine these to understand the basic operation of the MQTT client.

Below are examples on specific points for working with this library

## Printing payloads

MQTT 3.1.1 payloads have no format and need to be treated as binary. If you want to print a payload to the Arduino serial console, you have to make sure that the payload is null-terminated (c-strin).

```cpp
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) {
Serial.println("Publish received:");
Serial.printf(" topic: %s\n payload:", topic);
const char* p = reinterpret_cast<const char*>(payload);
for (size_t i = 0; i < len; ++i) {
Serial.print(p[i]);
}
Serial.print("\n");
}
```
```cpp
void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) {
Serial.println("Publish received:");
Serial.printf(" topic: %s\n payload:", topic);
char* p = new char[len + 1];
memcpy(p, payload, len);
payload[len] = "\0";
Serial.println(p);
delete[] p;
}
```

## Assembling chunked messages

The `onMessage`-callback is called as data comes in. So if the data comes in chuncked, the callback will be called on every receipt on a chunk, with the proper `index`, (chunk)`size` and `total` set. With little code, you can reassemble chunked messages yourself.

```cpp

const size_t maxPayloadSize = 8192;
uint8_t* payloadbuffer = nullptr;
size_t payloadbufferSize = 0;
size_t payloadbufferIndex = 0;

void onOversizedMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) {
// handle oversized messages
}

void onCompleteMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) {
// handle oversized messages
}

void onMqttMessage(const espMqttClientTypes::MessageProperties& properties, const char* topic, const uint8_t* payload, size_t len, size_t index, size_t total) {
// payload is bigger then max: return chunked
if (total > maxPayloadSize) {
onOversizedMqttMessage(properties, topic, payload, len, index, total);
return;
}

// start new packet, increase buffer size if neccesary
if (index == 0 || !payloadBuffer) {
if (total > payloadbufferSize) {
delete[] payloadbuffer;
payloadbufferSize = total;
payloadbuffer = new uint8_t[payloadbufferSize]; // you may want to check for nullptr after this operation
}
payloadbufferIndex = 0;
}

// add data and dispatch when done
memcpy(&payloadbuffer[payloadbufferIndex], payload, len);
payloadbufferIndex += len;
if (payloadbufferIndex == total) {
// message is complete here
onCompleteMqttMessage(properties, topic, payloadBuffer, total, 0, total);
// optionally:
delete[] payloadBuffer;
payloadBuffer = nullptr;
payloadbufferSize = 0;
}
}
```
42 changes: 42 additions & 0 deletions docs/compile-time-configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Runtime behaviour

A number of constants which influence the behaviour or the client can be set at compile time. You can set these options in the `Config.h` file or pass the values as compiler flags.

### EMC_RX_BUFFER_SIZE 1440

The client copies incoming data into a buffer before parsing. This sets the buffer size.

### EMC_MAX_TOPIC_LENGTH 128

For **incoming** messages, a maximum topic length is set. Topics longer then this will be truncated.

### EMC_PAYLOAD_BUFFER_SIZE 32

Set the incoming payload buffer size for SUBACK messages. Although the library needs this value, it doesn't currently influence the behaviour.

### EMC_MIN_FREE_MEMORY 4096

The client keeps all outgoing packets in a queue which stores it's data in heap memory. With this option, you can set the minimum available (contiguous) heap memory that needs to be available for adding a message to the queue.

### EMC_ESP8266_MULTITHREADING 0

ESP8266 doesn't use multithreading and is only single-core. There is therefore no need for semaphores/mutexes on this platform. However, you can still enable this.

### EMC_CLIENTID_LENGTH 18 + 1

The (maximum) length of the client ID.

### EMC_TASK_STACK_SIZE 10000

Only used on ESP32. Sets the stack size (in words) of the MQTT client worker task.

## Logging

You have to enable logging at compile time. This is done differently on ESP32 and ESP8266.

ESP8266:
* Enable logging for Arduino [see docs](https://arduino-esp8266.readthedocs.io/en/latest/Troubleshooting/debugging.html)
* Pass the `DEBUG_ESP_MQTT_CLIENT` flag to the compiler

ESP32
* Enable logging for Arduino [see docs](https://docs.espressif.com/projects/arduino-esp32/en/latest/guides/tools_menu.html?#core-debug-level)
Loading

0 comments on commit ed2fe38

Please sign in to comment.