addd ssd1306 lib

This commit is contained in:
2021-06-14 08:24:14 +02:00
parent 5c031f67ca
commit 5a5f977a5f
136 changed files with 17364 additions and 168 deletions

View File

@@ -0,0 +1,7 @@
[Makefile]
indent_style = tab
indent_size = 4
[src/*.h,src/*.cpp,examples/**.ino]
indent_style = space
indent_size = 2

2
P1_gateway_FW/lib/MQTT/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
.DS_Store
cmake-build-debug/

View File

@@ -0,0 +1 @@
{"type": "library", "name": "MQTT", "version": "2.4.8", "spec": {"owner": "256dpi", "id": 617, "name": "MQTT", "requirements": null, "url": null}}

View File

@@ -0,0 +1,46 @@
language: generic
env:
global:
- IDE_VERSION=1.8.7
matrix:
- EXAMPLE="AdafruitHuzzahESP8266" BOARD="esp8266:esp8266:huzzah:eesz=4M3M,xtal=80"
- EXAMPLE="AdafruitHuzzahESP8266Secure" BOARD="esp8266:esp8266:huzzah:eesz=4M3M,xtal=80"
- EXAMPLE="ArduinoEthernetShield" BOARD="arduino:avr:uno"
- EXAMPLE="ArduinoMKRGSM1400" BOARD="arduino:samd:mkrgsm1400"
- EXAMPLE="ArduinoMKRGSM1400Secure" BOARD="arduino:samd:mkrgsm1400"
- EXAMPLE="ArduinoWiFi101Secure" BOARD="arduino:avr:uno"
- EXAMPLE="ArduinoWiFiShield" BOARD="arduino:avr:uno"
- EXAMPLE="ArduinoYun" BOARD="arduino:avr:yun"
- EXAMPLE="ArduinoYunSecure" BOARD="arduino:avr:yun"
- EXAMPLE="ESP32DevelopmentBoard" BOARD="espressif:esp32:esp32:FlashFreq=80"
- EXAMPLE="ESP32DevelopmentBoardSecure" BOARD="espressif:esp32:esp32:FlashFreq=80"
before_install:
- /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_1.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :1 -ac -screen 0 1280x1024x16
- sleep 3
- export DISPLAY=:1.0
- wget http://downloads.arduino.cc/arduino-$IDE_VERSION-linux64.tar.xz
- tar xf arduino-$IDE_VERSION-linux64.tar.xz
- mv arduino-$IDE_VERSION ~/arduino-ide
- export PATH=$PATH:~/arduino-ide
- if [[ "$BOARD" =~ "esp8266:esp8266:" ]]; then
arduino --pref "boardsmanager.additional.urls=http://arduino.esp8266.com/stable/package_esp8266com_index.json" --install-boards esp8266:esp8266;
arduino --pref "boardsmanager.additional.urls=" --save-prefs;
fi
- if [[ "$BOARD" =~ "espressif:esp32:" ]]; then
mkdir -p ~/Arduino/hardware/espressif &&
cd ~/Arduino/hardware/espressif &&
git clone https://github.com/espressif/arduino-esp32.git esp32 &&
cd esp32/tools/ &&
python get.py &&
cd $TRAVIS_BUILD_DIR;
fi
- if [[ "$BOARD" =~ "arduino:samd:mkrgsm1400" ]]; then
arduino --install-boards arduino:samd;
arduino --install-library MKRGSM;
fi
- arduino --install-library WiFi101
install:
- mkdir -p ~/Arduino/libraries
- ln -s $PWD ~/Arduino/libraries/.
script:
- arduino --verbose-build --verify --board $BOARD $PWD/examples/$EXAMPLE/$EXAMPLE.ino;

View File

@@ -0,0 +1,39 @@
# Uncompilable CMake File to enable project editing with CLion IDE
cmake_minimum_required(VERSION 3.13)
project(arduino-mqtt)
include_directories(
/Applications/Arduino.app/Contents/Java/hardware/arduino/avr/cores/arduino
/Users/256dpi/Development/Arduino/libraries/Ethernet/src
/Users/256dpi/Development/Arduino/libraries/WiFi101/src
/Users/256dpi/Development/Arduino/libraries/MKRGSM/src
/Applications/Arduino.app/Contents/Java/libraries/Bridge/src
/Users/256dpi/Library/Arduino15/packages/esp8266/hardware/esp8266/2.3.0/libraries/ESP8266WiFi/src
/Users/256dpi/Library/Arduino15/packages/esp32/libraries/WiFi/src
/Users/256dpi/Library/Arduino15/packages/esp32/libraries/WiFiClientSecure/src
src)
include_directories(src/)
set(CMAKE_CXX_FLAGS -std=c++11)
set(SOURCE_FILES
examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino
examples/AdafruitHuzzahESP8266Secure/AdafruitHuzzahESP8266Secure.ino
examples/ArduinoEthernetShield/ArduinoEthernetShield.ino
examples/ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino
examples/ArduinoMKRGSM1400Secure/ArduinoMKRGSM1400Secure.ino
examples/ArduinoWiFi101/ArduinoWiFi101.ino
examples/ArduinoWiFi101Secure/ArduinoWiFi101Secure.ino
examples/ArduinoWiFiShield/ArduinoWiFiShield.ino
examples/ArduinoYun/ArduinoYun.ino
examples/ArduinoYunSecure/ArduinoYunSecure.ino
examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino
examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino
src/lwmqtt
src/MQTT.h
src/MQTTClient.h
src/MQTTClient.cpp)
add_executable(arduino-mqtt ${SOURCE_FILES})

View File

@@ -0,0 +1,21 @@
The MIT License (MIT)
Copyright (c) 2015 Joël Gähwiler
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,14 @@
all: fmt
fmt:
clang-format -i src/*.h src/*.cpp -style="{BasedOnStyle: Google, ColumnLimit: 120}"
update:
rm -rf ./lwmqtt
git clone --branch v0.6.4 https://github.com/256dpi/lwmqtt.git ./lwmqtt
mkdir -p ./src/lwmqtt
cp -r ./lwmqtt/src/*.c ./src/lwmqtt/
cp -r ./lwmqtt/src/*.h ./src/lwmqtt/
cp -r ./lwmqtt/include/*.h ./src/lwmqtt/
rm -rf ./lwmqtt
sed -i '' "s/<lwmqtt.h>/\"lwmqtt.h\"/g" ./src/lwmqtt/*

View File

@@ -0,0 +1,260 @@
# arduino-mqtt
[![Build Status](https://travis-ci.org/256dpi/arduino-mqtt.svg?branch=master)](https://travis-ci.org/256dpi/arduino-mqtt)
[![GitHub release](https://img.shields.io/github/release/256dpi/arduino-mqtt.svg)](https://github.com/256dpi/arduino-mqtt/releases)
This library bundles the [lwmqtt](https://github.com/256dpi/lwmqtt) MQTT 3.1.1 client and adds a thin wrapper to get an Arduino like API.
Download the latest version from the [release](https://github.com/256dpi/arduino-mqtt/releases) section. Or even better use the builtin Library Manager in the Arduino IDE and search for "MQTT".
The library is also available on [PlatformIO](https://platformio.org/lib/show/617/MQTT). You can install it by running: `pio lib install "MQTT"`.
## Compatibility
The following examples show how you can use the library with various Arduino compatible hardware:
- [Arduino Yun & Yun-Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoYun/ArduinoYun.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoYunSecure/ArduinoYunSecure.ino))
- [Arduino Ethernet Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoEthernetShield/ArduinoEthernetShield.ino)
- [Arduino WiFi Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFiShield/ArduinoWiFiShield.ino)
- [Adafruit HUZZAH ESP8266](https://github.com/256dpi/arduino-mqtt/blob/master/examples/AdafruitHuzzahESP8266/AdafruitHuzzahESP8266.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/AdafruitHuzzahESP8266Secure/AdafruitHuzzahESP8266Secure.ino))
- [Arduino/Genuino WiFi101 Shield](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFi101/ArduinoWiFi101.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoWiFi101Secure/ArduinoWiFi101Secure.ino))
- [Arduino MKR GSM 1400](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoMKRGSM1400/ArduinoMKRGSM1400.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ArduinoMKRGSM1400Secure/ArduinoMKRGSM1400Secure.ino))
- [ESP32 Development Board](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ESP32DevelopmentBoard/ESP32DevelopmentBoard.ino) ([Secure](https://github.com/256dpi/arduino-mqtt/blob/master/examples/ESP32DevelopmentBoardSecure/ESP32DevelopmentBoardSecure.ino))
Other shields and boards should also work if they provide a [Client](https://www.arduino.cc/en/Reference/ClientConstructor) based network implementation.
**Check out the [Wiki](https://github.com/256dpi/arduino-mqtt/wiki) to find more examples.**
## Notes
- The maximum size for packets being published and received is set by default to 128 bytes. To change the buffer sizes, you need to use `MQTTClient client(256)` instead of just `MQTTClient client` on the top of your sketch. The passed value denotes the read and write buffer size.
- On the ESP8266 it has been reported that an additional `delay(10);` after `client.loop();` fixes many stability issues with WiFi connections.
- To use the library with shiftr.io, you need to provide the token key (username) and token secret (password) as the second and third argument to `client.connect(name, key, secret)`.
## Example
The following example uses an Arduino MKR1000 to connect to shiftr.io. You can check on your device after a successful connection here: https://shiftr.io/try.
```c++
#include <SPI.h>
#include <WiFi101.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}
```
## API
Initialize the object using the hostname of the broker, the brokers port (default: `1883`) and the underlying Client class for network transport:
```c++
void begin(Client &client);
void begin(const char hostname[], Client &client);
void begin(const char hostname[], int port, Client &client);
void begin(IPAddress address, Client &client);
void begin(IPAddress address, int port, Client &client);
```
- Specify port `8883` when using secure clients for encrypted connections.
- Local domain names (e.g. `Computer.local` on OSX) are not supported by Arduino. You need to set the IP address directly.
The hostname and port can also be changed after calling `begin()`:
```c++
void setHost(const char hostname[]);
void setHost(const char hostname[], int port);
void setHost(IPAddress address);
void setHost(IPAddress address, int port);
```
Set a will message (last testament) that gets registered on the broker after connecting. `setWill()` has to be called before calling `connect()`:
```c++
void setWill(const char topic[]);
void setWill(const char topic[], const char payload[]);
void setWill(const char topic[], const char payload[], bool retained, int qos);
void clearWill();
```
Register a callback to receive messages:
```c++
void onMessage(MQTTClientCallbackSimple);
// Callback signature: void messageReceived(String &topic, String &payload) {}
void onMessageAdvanced(MQTTClientCallbackAdvanced);
// Callback signature: void messageReceived(MQTTClient *client, char topic[], char payload[], int payload_length) {}
```
- The set callback is mostly called during a call to `loop()` but may also be called during a call to `subscribe()`, `unsubscribe()` or `publish() // QoS > 0` if messages have been received before receiving the required acknowledgement. Therefore, it is strongly recommended to not call `subscribe()`, `unsubscribe()` or `publish() // QoS > 0` directly in the callback.
- In case you need a reference to an object that manages the client, use the `void * ref` property on the client to store a pointer, and access it directly from the advanced callback..
Set more advanced options:
```c++
void setKeepAlive(int keepAlive);
void setCleanSession(bool cleanSession);
void setTimeout(int timeout);
void setOptions(int keepAlive, bool cleanSession, int timeout);
```
- The `keepAlive` option controls the keep alive interval in seconds (default: 10).
- The `cleanSession` option controls the session retention on the broker side (default: true).
- The `timeout` option controls the default timeout for all commands in milliseconds (default: 1000).
Set a custom clock source "custom millis" callback to enable deep sleep applications:
```c++
void setClockSource(MQTTClientClockSource);
// Callback signature: uint32_t clockSource() {}
```
- The specified callback is used by the internal timers to get a monotonic time in milliseconds. Since the clock source for the built-in `millis` is stopped when the the Arduino goes into deep sleep, you need to provide a custom callback that first syncs with a built-in or external Real Time Clock (RTC). You can pass `NULL` to reset to the default implementation.
Connect to broker using the supplied client id and an optional username and password:
```c++
bool connect(const char clientID[], bool skip = false);
bool connect(const char clientID[], const char username[], bool skip = false);
bool connect(const char clientID[], const char username[], const char password[], bool skip = false);
```
- If the `skip` option is set to true, the client will skip the network level connection and jump to the MQTT level connection. This option can be used in order to establish and verify TLS connections manually before giving control to the MQTT client.
- The functions return a boolean that indicates if the connection has been established successfully (true).
Publishes a message to the broker with an optional payload:
```c++
bool publish(const String &topic);
bool publish(const char topic[]);
bool publish(const String &topic, const String &payload);
bool publish(const String &topic, const String &payload, bool retained, int qos);
bool publish(const char topic[], const String &payload);
bool publish(const char topic[], const String &payload, bool retained, int qos);
bool publish(const char topic[], const char payload[]);
bool publish(const char topic[], const char payload[], bool retained, int qos);
bool publish(const char topic[], const char payload[], int length);
bool publish(const char topic[], const char payload[], int length, bool retained, int qos);
```
- The functions return a boolean that indicates if the publish has been successful (true).
Subscribe to a topic:
```c++
bool subscribe(const String &topic);
bool subscribe(const String &topic, int qos);
bool subscribe(const char topic[]);
bool subscribe(const char topic[], int qos);
```
- The functions return a boolean that indicates if the subscribe has been successful (true).
Unsubscribe from a topic:
```c++
bool unsubscribe(const String &topic);
bool unsubscribe(const char topic[]);
```
- The functions return a boolean that indicates if the unsubscribe has been successful (true).
Sends and receives packets:
```c++
bool loop();
```
- This function should be called in every `loop`.
- The function returns a boolean that indicates if the loop has been successful (true).
Check if the client is currently connected:
```c++
bool connected();
```
Access low-level information for debugging:
```c++
lwmqtt_err_t lastError();
lwmqtt_return_code_t returnCode();
```
- The error codes can be found [here](https://github.com/256dpi/lwmqtt/blob/master/include/lwmqtt.h#L11).
- The return codes can be found [here](https://github.com/256dpi/lwmqtt/blob/master/include/lwmqtt.h#L243).
Disconnect from the broker:
```c++
bool disconnect();
```
- The function returns a boolean that indicates if the disconnect has been successful (true).
## Release Management
- Update version in `library.properties`.
- Create release on GitHub.

View File

@@ -0,0 +1,74 @@
// This example uses an Adafruit Huzzah ESP8266
// to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <ESP8266WiFi.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
delay(10); // <- fixes some issues with WiFi stability
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@@ -0,0 +1,81 @@
// This example uses an Adafruit Huzzah ESP8266
// to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <ESP8266WiFi.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClientSecure net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
// do not verify tls certificate
// check the following example for methods to verify the server:
// https://github.com/esp8266/Arduino/blob/master/libraries/ESP8266WiFi/examples/BearSSL_Validation/BearSSL_Validation.ino
net.setInsecure();
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
//
// MQTT brokers usually use port 8883 for secure connections.
client.begin("broker.shiftr.io", 8883, net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
delay(10); // <- fixes some issues with WiFi stability
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@@ -0,0 +1,67 @@
// This example uses an Arduino Uno together with
// an Ethernet Shield to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <Ethernet.h>
#include <MQTT.h>
byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED};
byte ip[] = {192, 168, 1, 177}; // <- change to match your network
EthernetClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("connecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
Ethernet.begin(mac, ip);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@@ -0,0 +1,89 @@
// This example uses an Arduino MKR GSM 1400 board
// to connect to shiftr.io.
//
// IMPORTANT: This example uses the new MKRGSM library.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Sandeep Mistry
// https://github.com/256dpi/arduino-mqtt
#include <MKRGSM.h>
#include <MQTT.h>
const char pin[] = "";
const char apn[] = "apn";
const char login[] = "login";
const char password[] = "password";
GSMClient net;
GPRS gprs;
GSM gsmAccess;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
// connection state
bool connected = false;
Serial.print("connecting to cellular network ...");
// After starting the modem with gsmAccess.begin()
// attach to the GPRS network with the APN, login and password
while (!connected) {
if ((gsmAccess.begin(pin) == GSM_READY) &&
(gprs.attachGPRS(apn, login, password) == GPRS_READY)) {
connected = true;
} else {
Serial.print(".");
delay(1000);
}
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@@ -0,0 +1,91 @@
// This example uses an Arduino MKR GSM 1400 board
// to securely connect to shiftr.io.
//
// IMPORTANT: This example uses the new MKRGSM library.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Sandeep Mistry
// https://github.com/256dpi/arduino-mqtt
#include <MKRGSM.h>
#include <MQTT.h>
const char pin[] = "";
const char apn[] = "apn";
const char login[] = "login";
const char password[] = "password";
GSMSSLClient net;
GPRS gprs;
GSM gsmAccess;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
// connection state
bool connected = false;
Serial.print("connecting to cellular network ...");
// After starting the modem with gsmAccess.begin()
// attach to the GPRS network with the APN, login and password
while (!connected) {
if ((gsmAccess.begin(pin) == GSM_READY) &&
(gprs.attachGPRS(apn, login, password) == GPRS_READY)) {
connected = true;
} else {
Serial.print(".");
delay(1000);
}
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
//
// MQTT brokers usually use port 8883 for secure connections.
client.begin("broker.shiftr.io", 8883, net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@@ -0,0 +1,75 @@
// This example uses an Arduino/Genuino Zero together with
// a WiFi101 Shield or a MKR1000 to connect to shiftr.io.
//
// IMPORTANT: This example uses the new WiFi101 library.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Gilberto Conti
// https://github.com/256dpi/arduino-mqtt
#include <WiFi101.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@@ -0,0 +1,80 @@
// This example uses an Arduino/Genuino Zero together with
// a WiFi101 Shield or a MKR1000 to connect to shiftr.io.
//
// IMPORTANT: This example uses the new WiFi101 library.
//
// IMPORTANT: You need to install/update the SSL certificates first:
// https://github.com/arduino-libraries/WiFi101-FirmwareUpdater#to-update-ssl-certificates
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Gilberto Conti
// https://github.com/256dpi/arduino-mqtt
#include <WiFi101.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiSSLClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
//
// MQTT brokers usually use port 8883 for secure connections.
client.begin("broker.shiftr.io", 8883, net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@@ -0,0 +1,73 @@
// This example uses an Arduino Uno together with
// a WiFi Shield to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <WiFi.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@@ -0,0 +1,65 @@
// This example uses an Arduino Yun or a Yun-Shield
// and the MQTTClient to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <Bridge.h>
#include <BridgeClient.h>
#include <MQTT.h>
BridgeClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("connecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Bridge.begin();
Serial.begin(115200);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@@ -0,0 +1,67 @@
// This example uses an Arduino Yun or a Yun-Shield
// and the MQTTClient to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <Bridge.h>
#include <BridgeSSLClient.h>
#include <MQTT.h>
BridgeSSLClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("connecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Bridge.begin();
Serial.begin(115200);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
//
// MQTT brokers usually use port 8883 for secure connections.
client.begin("broker.shiftr.io", 8883, net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@@ -0,0 +1,74 @@
// This example uses an ESP32 Development Board
// to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <WiFi.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClient net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
client.begin("broker.shiftr.io", net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
delay(10); // <- fixes some issues with WiFi stability
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@@ -0,0 +1,76 @@
// This example uses an ESP32 Development Board
// to connect to shiftr.io.
//
// You can check on your device after a successful
// connection here: https://shiftr.io/try.
//
// by Joël Gähwiler
// https://github.com/256dpi/arduino-mqtt
#include <WiFiClientSecure.h>
#include <MQTT.h>
const char ssid[] = "ssid";
const char pass[] = "pass";
WiFiClientSecure net;
MQTTClient client;
unsigned long lastMillis = 0;
void connect() {
Serial.print("checking wifi...");
while (WiFi.status() != WL_CONNECTED) {
Serial.print(".");
delay(1000);
}
Serial.print("\nconnecting...");
while (!client.connect("arduino", "try", "try")) {
Serial.print(".");
delay(1000);
}
Serial.println("\nconnected!");
client.subscribe("/hello");
// client.unsubscribe("/hello");
}
void messageReceived(String &topic, String &payload) {
Serial.println("incoming: " + topic + " - " + payload);
// Note: Do not use the client in the callback to publish, subscribe or
// unsubscribe as it may cause deadlocks when other things arrive while
// sending and receiving acknowledgments. Instead, change a global variable,
// or push to a queue and handle it in the loop after calling `client.loop()`.
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, pass);
// Note: Local domain names (e.g. "Computer.local" on OSX) are not supported
// by Arduino. You need to set the IP address directly.
//
// MQTT brokers usually use port 8883 for secure connections.
client.begin("broker.shiftr.io", 8883, net);
client.onMessage(messageReceived);
connect();
}
void loop() {
client.loop();
delay(10); // <- fixes some issues with WiFi stability
if (!client.connected()) {
connect();
}
// publish a message roughly every second.
if (millis() - lastMillis > 1000) {
lastMillis = millis();
client.publish("/hello", "world");
}
}

View File

@@ -0,0 +1,9 @@
name=MQTT
version=2.4.8
author=Joel Gaehwiler <joel.gaehwiler@gmail.com>
maintainer=Joel Gaehwiler <joel.gaehwiler@gmail.com>
sentence=MQTT library for Arduino
paragraph=This library bundles the lwmqtt client and adds a thin wrapper to get an Arduino like API.
category=Communication
url=https://github.com/256dpi/arduino-mqtt
architectures=*

View File

@@ -0,0 +1,6 @@
#ifndef MQTT_H
#define MQTT_H
#include "MQTTClient.h"
#endif

View File

@@ -0,0 +1,422 @@
#include "MQTTClient.h"
inline void lwmqtt_arduino_timer_set(void *ref, uint32_t timeout) {
// cast timer reference
auto t = (lwmqtt_arduino_timer_t *)ref;
// set future end time
if (t->millis != nullptr) {
t->end = (uint32_t)(t->millis() + timeout);
} else {
t->end = (uint32_t)(millis() + timeout);
}
}
inline int32_t lwmqtt_arduino_timer_get(void *ref) {
// cast timer reference
auto t = (lwmqtt_arduino_timer_t *)ref;
// get difference to end time
if (t->millis != nullptr) {
return (int32_t)(t->end - t->millis());
} else {
return (int32_t)(t->end - millis());
}
}
inline lwmqtt_err_t lwmqtt_arduino_network_read(void *ref, uint8_t *buffer, size_t len, size_t *read,
uint32_t timeout) {
// cast network reference
auto n = (lwmqtt_arduino_network_t *)ref;
// set timeout
uint32_t start = millis();
// reset counter
*read = 0;
// read until all bytes have been read or timeout has been reached
while (len > 0 && (millis() - start < timeout)) {
// read from connection
int r = n->client->read(buffer, len);
// handle read data
if (r > 0) {
buffer += r;
*read += r;
len -= r;
continue;
}
// wait/unblock for some time (RTOS based boards may otherwise fail since
// the wifi task cannot provide the data)
delay(0);
// otherwise check status
if (!n->client->connected()) {
return LWMQTT_NETWORK_FAILED_READ;
}
}
// check counter
if (*read == 0) {
return LWMQTT_NETWORK_TIMEOUT;
}
return LWMQTT_SUCCESS;
}
inline lwmqtt_err_t lwmqtt_arduino_network_write(void *ref, uint8_t *buffer, size_t len, size_t *sent,
uint32_t /*timeout*/) {
// cast network reference
auto n = (lwmqtt_arduino_network_t *)ref;
// write bytes
*sent = n->client->write(buffer, len);
if (*sent <= 0) {
return LWMQTT_NETWORK_FAILED_WRITE;
}
return LWMQTT_SUCCESS;
}
static void MQTTClientHandler(lwmqtt_client_t * /*client*/, void *ref, lwmqtt_string_t topic,
lwmqtt_message_t message) {
// get callback
auto cb = (MQTTClientCallback *)ref;
// null terminate topic
char terminated_topic[topic.len + 1];
memcpy(terminated_topic, topic.data, topic.len);
terminated_topic[topic.len] = '\0';
// null terminate payload if available
if (message.payload != nullptr) {
message.payload[message.payload_len] = '\0';
}
// call the advanced callback and return if available
if (cb->advanced != nullptr) {
cb->advanced(cb->client, terminated_topic, (char *)message.payload, (int)message.payload_len);
return;
}
// return if simple callback is not set
if (cb->simple == nullptr) {
return;
}
// create topic string
String str_topic = String(terminated_topic);
// create payload string
String str_payload;
if (message.payload != nullptr) {
str_payload = String((const char *)message.payload);
}
// call simple callback
cb->simple(str_topic, str_payload);
}
MQTTClient::MQTTClient(int bufSize) {
// allocate buffers
this->bufSize = (size_t)bufSize;
this->readBuf = (uint8_t *)malloc((size_t)bufSize + 1);
this->writeBuf = (uint8_t *)malloc((size_t)bufSize);
}
MQTTClient::~MQTTClient() {
// free will
this->clearWill();
// free hostname
if (this->hostname != nullptr) {
free((void *)this->hostname);
}
// free buffers
free(this->readBuf);
free(this->writeBuf);
}
void MQTTClient::begin(Client &_client) {
// set client
this->netClient = &_client;
// initialize client
lwmqtt_init(&this->client, this->writeBuf, this->bufSize, this->readBuf, this->bufSize);
// set timers
lwmqtt_set_timers(&this->client, &this->timer1, &this->timer2, lwmqtt_arduino_timer_set, lwmqtt_arduino_timer_get);
// set network
lwmqtt_set_network(&this->client, &this->network, lwmqtt_arduino_network_read, lwmqtt_arduino_network_write);
// set callback
lwmqtt_set_callback(&this->client, (void *)&this->callback, MQTTClientHandler);
}
void MQTTClient::onMessage(MQTTClientCallbackSimple cb) {
// set callback
this->callback.client = this;
this->callback.simple = cb;
this->callback.advanced = nullptr;
}
void MQTTClient::onMessageAdvanced(MQTTClientCallbackAdvanced cb) {
// set callback
this->callback.client = this;
this->callback.simple = nullptr;
this->callback.advanced = cb;
}
void MQTTClient::setClockSource(MQTTClientClockSource cb) {
this->timer1.millis = cb;
this->timer2.millis = cb;
}
void MQTTClient::setHost(IPAddress _address, int _port) {
// set address and port
this->address = _address;
this->port = _port;
}
void MQTTClient::setHost(const char _hostname[], int _port) {
// free hostname if set
if (this->hostname != nullptr) {
free((void *)this->hostname);
}
// set hostname and port
this->hostname = strdup(_hostname);
this->port = _port;
}
void MQTTClient::setWill(const char topic[], const char payload[], bool retained, int qos) {
// return if topic is missing
if (topic == nullptr || strlen(topic) == 0) {
return;
}
// clear existing will
this->clearWill();
// allocate will
this->will = (lwmqtt_will_t *)malloc(sizeof(lwmqtt_will_t));
memset(this->will, 0, sizeof(lwmqtt_will_t));
// set topic
this->will->topic = lwmqtt_string(strdup(topic));
// set payload if available
if (payload != nullptr && strlen(payload) > 0) {
this->will->payload = lwmqtt_string(strdup(payload));
}
// set flags
this->will->retained = retained;
this->will->qos = (lwmqtt_qos_t)qos;
}
void MQTTClient::clearWill() {
// return if not set
if (this->will == nullptr) {
return;
}
// free payload if set
if (this->will->payload.len > 0) {
free(this->will->payload.data);
}
// free topic if set
if (this->will->topic.len > 0) {
free(this->will->topic.data);
}
// free will
free(this->will);
this->will = nullptr;
}
void MQTTClient::setKeepAlive(int _keepAlive) { this->keepAlive = _keepAlive; }
void MQTTClient::setCleanSession(bool _cleanSession) { this->cleanSession = _cleanSession; }
void MQTTClient::setTimeout(int _timeout) { this->timeout = _timeout; }
bool MQTTClient::publish(const char topic[], const char payload[], int length, bool retained, int qos) {
// return immediately if not connected
if (!this->connected()) {
return false;
}
// prepare message
lwmqtt_message_t message = lwmqtt_default_message;
message.payload = (uint8_t *)payload;
message.payload_len = (size_t)length;
message.retained = retained;
message.qos = lwmqtt_qos_t(qos);
// publish message
this->_lastError = lwmqtt_publish(&this->client, lwmqtt_string(topic), message, this->timeout);
if (this->_lastError != LWMQTT_SUCCESS) {
// close connection
this->close();
return false;
}
return true;
}
bool MQTTClient::connect(const char clientID[], const char username[], const char password[], bool skip) {
// close left open connection if still connected
if (!skip && this->connected()) {
this->close();
}
// save client
this->network.client = this->netClient;
// connect to host
if (!skip) {
int ret;
if (this->hostname != nullptr) {
ret = this->netClient->connect(this->hostname, (uint16_t)this->port);
} else {
ret = this->netClient->connect(this->address, (uint16_t)this->port);
}
if (ret <= 0) {
this->_lastError = LWMQTT_NETWORK_FAILED_CONNECT;
return false;
}
}
// prepare options
lwmqtt_options_t options = lwmqtt_default_options;
options.keep_alive = this->keepAlive;
options.clean_session = this->cleanSession;
options.client_id = lwmqtt_string(clientID);
// set username and password if available
if (username != nullptr) {
options.username = lwmqtt_string(username);
if (password != nullptr) {
options.password = lwmqtt_string(password);
}
}
// connect to broker
this->_lastError = lwmqtt_connect(&this->client, options, this->will, &this->_returnCode, this->timeout);
if (this->_lastError != LWMQTT_SUCCESS) {
// close connection
this->close();
return false;
}
// set flag
this->_connected = true;
return true;
}
bool MQTTClient::subscribe(const char topic[], int qos) {
// return immediately if not connected
if (!this->connected()) {
return false;
}
// subscribe to topic
this->_lastError = lwmqtt_subscribe_one(&this->client, lwmqtt_string(topic), (lwmqtt_qos_t)qos, this->timeout);
if (this->_lastError != LWMQTT_SUCCESS) {
// close connection
this->close();
return false;
}
return true;
}
bool MQTTClient::unsubscribe(const char topic[]) {
// return immediately if not connected
if (!this->connected()) {
return false;
}
// unsubscribe from topic
this->_lastError = lwmqtt_unsubscribe_one(&this->client, lwmqtt_string(topic), this->timeout);
if (this->_lastError != LWMQTT_SUCCESS) {
// close connection
this->close();
return false;
}
return true;
}
bool MQTTClient::loop() {
// return immediately if not connected
if (!this->connected()) {
return false;
}
// get available bytes on the network
auto available = (size_t)this->netClient->available();
// yield if data is available
if (available > 0) {
this->_lastError = lwmqtt_yield(&this->client, available, this->timeout);
if (this->_lastError != LWMQTT_SUCCESS) {
// close connection
this->close();
return false;
}
}
// keep the connection alive
this->_lastError = lwmqtt_keep_alive(&this->client, this->timeout);
if (this->_lastError != LWMQTT_SUCCESS) {
// close connection
this->close();
return false;
}
return true;
}
bool MQTTClient::connected() {
// a client is connected if the network is connected, a client is available and
// the connection has been properly initiated
return this->netClient != nullptr && this->netClient->connected() == 1 && this->_connected;
}
bool MQTTClient::disconnect() {
// return immediately if not connected anymore
if (!this->connected()) {
return false;
}
// cleanly disconnect
this->_lastError = lwmqtt_disconnect(&this->client, this->timeout);
// close
this->close();
return this->_lastError == LWMQTT_SUCCESS;
}
void MQTTClient::close() {
// set flag
this->_connected = false;
// close network
this->netClient->stop();
}

View File

@@ -0,0 +1,151 @@
#ifndef MQTT_CLIENT_H
#define MQTT_CLIENT_H
#include <Arduino.h>
#include <Client.h>
#include <Stream.h>
extern "C" {
#include "lwmqtt/lwmqtt.h"
}
typedef uint32_t (*MQTTClientClockSource)();
typedef struct {
uint32_t end;
MQTTClientClockSource millis;
} lwmqtt_arduino_timer_t;
typedef struct {
Client *client;
} lwmqtt_arduino_network_t;
class MQTTClient;
typedef void (*MQTTClientCallbackSimple)(String &topic, String &payload);
typedef void (*MQTTClientCallbackAdvanced)(MQTTClient *client, char topic[], char bytes[], int length);
typedef struct {
MQTTClient *client = nullptr;
MQTTClientCallbackSimple simple = nullptr;
MQTTClientCallbackAdvanced advanced = nullptr;
} MQTTClientCallback;
class MQTTClient {
private:
size_t bufSize = 0;
uint8_t *readBuf = nullptr;
uint8_t *writeBuf = nullptr;
uint16_t keepAlive = 10;
bool cleanSession = true;
uint32_t timeout = 1000;
Client *netClient = nullptr;
const char *hostname = nullptr;
IPAddress address;
int port = 0;
lwmqtt_will_t *will = nullptr;
MQTTClientCallback callback;
lwmqtt_arduino_network_t network = {nullptr};
lwmqtt_arduino_timer_t timer1 = {0, nullptr};
lwmqtt_arduino_timer_t timer2 = {0, nullptr};
lwmqtt_client_t client = lwmqtt_client_t();
bool _connected = false;
lwmqtt_return_code_t _returnCode = (lwmqtt_return_code_t)0;
lwmqtt_err_t _lastError = (lwmqtt_err_t)0;
public:
void *ref = nullptr;
explicit MQTTClient(int bufSize = 128);
~MQTTClient();
void begin(Client &_client);
void begin(const char _hostname[], Client &_client) { this->begin(_hostname, 1883, _client); }
void begin(const char _hostname[], int _port, Client &_client) {
this->begin(_client);
this->setHost(_hostname, _port);
}
void begin(IPAddress _address, Client &_client) { this->begin(_address, 1883, _client); }
void begin(IPAddress _address, int _port, Client &_client) {
this->begin(_client);
this->setHost(_address, _port);
}
void onMessage(MQTTClientCallbackSimple cb);
void onMessageAdvanced(MQTTClientCallbackAdvanced cb);
void setClockSource(MQTTClientClockSource cb);
void setHost(const char _hostname[]) { this->setHost(_hostname, 1883); }
void setHost(const char hostname[], int port);
void setHost(IPAddress _address) { this->setHost(_address, 1883); }
void setHost(IPAddress _address, int port);
void setWill(const char topic[]) { this->setWill(topic, ""); }
void setWill(const char topic[], const char payload[]) { this->setWill(topic, payload, false, 0); }
void setWill(const char topic[], const char payload[], bool retained, int qos);
void clearWill();
void setKeepAlive(int keepAlive);
void setCleanSession(bool cleanSession);
void setTimeout(int timeout);
void setOptions(int _keepAlive, bool _cleanSession, int _timeout) {
this->setKeepAlive(_keepAlive);
this->setCleanSession(_cleanSession);
this->setTimeout(_timeout);
}
bool connect(const char clientId[], bool skip = false) { return this->connect(clientId, nullptr, nullptr, skip); }
bool connect(const char clientId[], const char username[], bool skip = false) {
return this->connect(clientId, username, nullptr, skip);
}
bool connect(const char clientID[], const char username[], const char password[], bool skip = false);
bool publish(const String &topic) { return this->publish(topic.c_str(), ""); }
bool publish(const char topic[]) { return this->publish(topic, ""); }
bool publish(const String &topic, const String &payload) { return this->publish(topic.c_str(), payload.c_str()); }
bool publish(const String &topic, const String &payload, bool retained, int qos) {
return this->publish(topic.c_str(), payload.c_str(), retained, qos);
}
bool publish(const char topic[], const String &payload) { return this->publish(topic, payload.c_str()); }
bool publish(const char topic[], const String &payload, bool retained, int qos) {
return this->publish(topic, payload.c_str(), retained, qos);
}
bool publish(const char topic[], const char payload[]) {
return this->publish(topic, (char *)payload, (int)strlen(payload));
}
bool publish(const char topic[], const char payload[], bool retained, int qos) {
return this->publish(topic, (char *)payload, (int)strlen(payload), retained, qos);
}
bool publish(const char topic[], const char payload[], int length) {
return this->publish(topic, payload, length, false, 0);
}
bool publish(const char topic[], const char payload[], int length, bool retained, int qos);
bool subscribe(const String &topic) { return this->subscribe(topic.c_str()); }
bool subscribe(const String &topic, int qos) { return this->subscribe(topic.c_str(), qos); }
bool subscribe(const char topic[]) { return this->subscribe(topic, 0); }
bool subscribe(const char topic[], int qos);
bool unsubscribe(const String &topic) { return this->unsubscribe(topic.c_str()); }
bool unsubscribe(const char topic[]);
bool loop();
bool connected();
lwmqtt_err_t lastError() { return this->_lastError; }
lwmqtt_return_code_t returnCode() { return this->_returnCode; }
bool disconnect();
private:
void close();
};
#endif

View File

@@ -0,0 +1,618 @@
#include "packet.h"
void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf,
size_t read_buf_size) {
client->last_packet_id = 1;
client->keep_alive_interval = 0;
client->pong_pending = false;
client->write_buf = write_buf;
client->write_buf_size = write_buf_size;
client->read_buf = read_buf;
client->read_buf_size = read_buf_size;
client->callback = NULL;
client->callback_ref = NULL;
client->network = NULL;
client->network_read = NULL;
client->network_write = NULL;
client->keep_alive_timer = NULL;
client->command_timer = NULL;
client->timer_set = NULL;
client->timer_get = NULL;
}
void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write) {
client->network = ref;
client->network_read = read;
client->network_write = write;
}
void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *command_timer, lwmqtt_timer_set_t set,
lwmqtt_timer_get_t get) {
client->keep_alive_timer = keep_alive_timer;
client->command_timer = command_timer;
client->timer_set = set;
client->timer_get = get;
client->timer_set(client->keep_alive_timer, 0);
client->timer_set(client->command_timer, 0);
}
void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb) {
client->callback_ref = ref;
client->callback = cb;
}
static uint16_t lwmqtt_get_next_packet_id(lwmqtt_client_t *client) {
// check overflow
if (client->last_packet_id == 65535) {
client->last_packet_id = 1;
return 1;
}
// increment packet id
client->last_packet_id++;
return client->last_packet_id;
}
static lwmqtt_err_t lwmqtt_read_from_network(lwmqtt_client_t *client, size_t offset, size_t len) {
// check read buffer capacity
if (client->read_buf_size < offset + len) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// prepare counter
size_t read = 0;
// read while data is missing
while (read < len) {
// check remaining time
int32_t remaining_time = client->timer_get(client->command_timer);
if (remaining_time <= 0) {
return LWMQTT_NETWORK_TIMEOUT;
}
// read
size_t partial_read = 0;
lwmqtt_err_t err = client->network_read(client->network, client->read_buf + offset + read, len - read,
&partial_read, (uint32_t)remaining_time);
if (err != LWMQTT_SUCCESS) {
return err;
}
// increment counter
read += partial_read;
}
return LWMQTT_SUCCESS;
}
static lwmqtt_err_t lwmqtt_write_to_network(lwmqtt_client_t *client, size_t offset, size_t len) {
// prepare counter
size_t written = 0;
// write while data is left
while (written < len) {
// check remaining time
int32_t remaining_time = client->timer_get(client->command_timer);
if (remaining_time <= 0) {
return LWMQTT_NETWORK_TIMEOUT;
}
// write
size_t partial_write = 0;
lwmqtt_err_t err = client->network_write(client->network, client->write_buf + offset + written, len - written,
&partial_write, (uint32_t)remaining_time);
if (err != LWMQTT_SUCCESS) {
return err;
}
// increment counter
written += partial_write;
}
return LWMQTT_SUCCESS;
}
static lwmqtt_err_t lwmqtt_read_packet_in_buffer(lwmqtt_client_t *client, size_t *read,
lwmqtt_packet_type_t *packet_type) {
// preset packet type
*packet_type = LWMQTT_NO_PACKET;
// read or wait for header byte
lwmqtt_err_t err = lwmqtt_read_from_network(client, 0, 1);
if (err == LWMQTT_NETWORK_TIMEOUT) {
// this is ok as no data has been read at all
return LWMQTT_SUCCESS;
} else if (err != LWMQTT_SUCCESS) {
return err;
}
// detect packet type
err = lwmqtt_detect_packet_type(client->read_buf, 1, packet_type);
if (err != LWMQTT_SUCCESS) {
return err;
}
// prepare variables
size_t len = 0;
uint32_t rem_len = 0;
do {
// adjust len
len++;
// read next byte
err = lwmqtt_read_from_network(client, len, 1);
if (err != LWMQTT_SUCCESS) {
return err;
}
// attempt to detect remaining length
err = lwmqtt_detect_remaining_length(client->read_buf + 1, len, &rem_len);
} while (err == LWMQTT_BUFFER_TOO_SHORT);
// check final error
if (err != LWMQTT_SUCCESS) {
return err;
}
// read the rest of the buffer if needed
if (rem_len > 0) {
err = lwmqtt_read_from_network(client, 1 + len, rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// adjust counter
*read += 1 + len + rem_len;
return LWMQTT_SUCCESS;
}
static lwmqtt_err_t lwmqtt_send_packet_in_buffer(lwmqtt_client_t *client, size_t length) {
// write to network
lwmqtt_err_t err = lwmqtt_write_to_network(client, 0, length);
if (err != LWMQTT_SUCCESS) {
return err;
}
// reset keep alive timer
client->timer_set(client->keep_alive_timer, client->keep_alive_interval);
return LWMQTT_SUCCESS;
}
static lwmqtt_err_t lwmqtt_cycle(lwmqtt_client_t *client, size_t *read, lwmqtt_packet_type_t *packet_type) {
// read next packet from the network
lwmqtt_err_t err = lwmqtt_read_packet_in_buffer(client, read, packet_type);
if (err != LWMQTT_SUCCESS) {
return err;
} else if (*packet_type == LWMQTT_NO_PACKET) {
return LWMQTT_SUCCESS;
}
switch (*packet_type) {
// handle publish packets
case LWMQTT_PUBLISH_PACKET: {
// decode publish packet
bool dup;
uint16_t packet_id;
lwmqtt_string_t topic;
lwmqtt_message_t msg;
err = lwmqtt_decode_publish(client->read_buf, client->read_buf_size, &dup, &packet_id, &topic, &msg);
if (err != LWMQTT_SUCCESS) {
return err;
}
// call callback if set
if (client->callback != NULL) {
client->callback(client, client->callback_ref, topic, msg);
}
// break early on qos zero
if (msg.qos == LWMQTT_QOS0) {
break;
}
// define ack packet
lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET;
if (msg.qos == LWMQTT_QOS1) {
ack_type = LWMQTT_PUBACK_PACKET;
} else if (msg.qos == LWMQTT_QOS2) {
ack_type = LWMQTT_PUBREC_PACKET;
}
// encode ack packet
size_t len;
err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, ack_type, false, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send ack packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
break;
}
// handle pubrec packets
case LWMQTT_PUBREC_PACKET: {
// decode pubrec packet
bool dup;
uint16_t packet_id;
err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_PUBREC_PACKET, &dup, &packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// encode pubrel packet
size_t len;
err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBREL_PACKET, 0, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send pubrel packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
break;
}
// handle pubrel packets
case LWMQTT_PUBREL_PACKET: {
// decode pubrec packet
bool dup;
uint16_t packet_id;
err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_PUBREL_PACKET, &dup, &packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// encode pubcomp packet
size_t len;
err = lwmqtt_encode_ack(client->write_buf, client->write_buf_size, &len, LWMQTT_PUBCOMP_PACKET, 0, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send pubcomp packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
break;
}
// handle pingresp packets
case LWMQTT_PINGRESP_PACKET: {
// set flag
client->pong_pending = false;
break;
}
// handle all other packets
default: { break; }
}
return LWMQTT_SUCCESS;
}
static lwmqtt_err_t lwmqtt_cycle_until(lwmqtt_client_t *client, lwmqtt_packet_type_t *packet_type, size_t available,
lwmqtt_packet_type_t needle) {
// prepare counter
size_t read = 0;
// loop until timeout has been reached
do {
// do one cycle
lwmqtt_err_t err = lwmqtt_cycle(client, &read, packet_type);
if (err != LWMQTT_SUCCESS) {
return err;
}
// return when one packet has been successfully read when no availability has been given
if (needle == LWMQTT_NO_PACKET && available == 0) {
return LWMQTT_SUCCESS;
}
// otherwise check if needle has been found
if (*packet_type == needle) {
return LWMQTT_SUCCESS;
}
} while (client->timer_get(client->command_timer) > 0 && (available == 0 || read < available));
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// cycle until timeout has been reached
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
lwmqtt_err_t err = lwmqtt_cycle_until(client, &packet_type, available, LWMQTT_NO_PACKET);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will,
lwmqtt_return_code_t *return_code, uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// save keep alive interval
client->keep_alive_interval = (uint32_t)(options.keep_alive) * 1000;
// set keep alive timer
client->timer_set(client->keep_alive_timer, client->keep_alive_interval);
// reset pong pending flag
client->pong_pending = false;
// initialize return code
*return_code = LWMQTT_UNKNOWN_RETURN_CODE;
// encode connect packet
size_t len;
lwmqtt_err_t err = lwmqtt_encode_connect(client->write_buf, client->write_buf_size, &len, options, will);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// wait for connack packet
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_CONNACK_PACKET);
if (err != LWMQTT_SUCCESS) {
return err;
} else if (packet_type != LWMQTT_CONNACK_PACKET) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// decode connack packet
bool session_present;
err = lwmqtt_decode_connack(client->read_buf, client->read_buf_size, &session_present, return_code);
if (err != LWMQTT_SUCCESS) {
return err;
}
// return error if connection was not accepted
if (*return_code != LWMQTT_CONNECTION_ACCEPTED) {
return LWMQTT_CONNECTION_DENIED;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos,
uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// encode subscribe packet
size_t len;
lwmqtt_err_t err = lwmqtt_encode_subscribe(client->write_buf, client->write_buf_size, &len,
lwmqtt_get_next_packet_id(client), count, topic_filter, qos);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// wait for suback packet
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_SUBACK_PACKET);
if (err != LWMQTT_SUCCESS) {
return err;
} else if (packet_type != LWMQTT_SUBACK_PACKET) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// decode packet
int suback_count = 0;
lwmqtt_qos_t granted_qos[count];
uint16_t packet_id;
err = lwmqtt_decode_suback(client->read_buf, client->read_buf_size, &packet_id, count, &suback_count, granted_qos);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check suback codes
for (int i = 0; i < suback_count; i++) {
if (granted_qos[i] == LWMQTT_QOS_FAILURE) {
return LWMQTT_FAILED_SUBSCRIPTION;
}
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos,
uint32_t timeout) {
return lwmqtt_subscribe(client, 1, &topic_filter, &qos, timeout);
}
lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// encode unsubscribe packet
size_t len;
lwmqtt_err_t err = lwmqtt_encode_unsubscribe(client->write_buf, client->write_buf_size, &len,
lwmqtt_get_next_packet_id(client), count, topic_filter);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send unsubscribe packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// wait for unsuback packet
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
err = lwmqtt_cycle_until(client, &packet_type, 0, LWMQTT_UNSUBACK_PACKET);
if (err != LWMQTT_SUCCESS) {
return err;
} else if (packet_type != LWMQTT_UNSUBACK_PACKET) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// decode unsuback packet
bool dup;
uint16_t packet_id;
err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, LWMQTT_UNSUBACK_PACKET, &dup, &packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout) {
return lwmqtt_unsubscribe(client, 1, &topic_filter, timeout);
}
lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t message,
uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// add packet id if at least qos 1
uint16_t packet_id = 0;
if (message.qos == LWMQTT_QOS1 || message.qos == LWMQTT_QOS2) {
packet_id = lwmqtt_get_next_packet_id(client);
}
// encode publish packet
size_t len = 0;
lwmqtt_err_t err =
lwmqtt_encode_publish(client->write_buf, client->write_buf_size, &len, 0, packet_id, topic, message);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// immediately return on qos zero
if (message.qos == LWMQTT_QOS0) {
return LWMQTT_SUCCESS;
}
// define ack packet
lwmqtt_packet_type_t ack_type = LWMQTT_NO_PACKET;
if (message.qos == LWMQTT_QOS1) {
ack_type = LWMQTT_PUBACK_PACKET;
} else if (message.qos == LWMQTT_QOS2) {
ack_type = LWMQTT_PUBCOMP_PACKET;
}
// wait for ack packet
lwmqtt_packet_type_t packet_type = LWMQTT_NO_PACKET;
err = lwmqtt_cycle_until(client, &packet_type, 0, ack_type);
if (err != LWMQTT_SUCCESS) {
return err;
} else if (packet_type != ack_type) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// decode ack packet
bool dup;
err = lwmqtt_decode_ack(client->read_buf, client->read_buf_size, ack_type, &dup, &packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// encode disconnect packet
size_t len;
lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_DISCONNECT_PACKET);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send disconnected packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout) {
// set command timer
client->timer_set(client->command_timer, timeout);
// return immediately if keep alive interval is zero
if (client->keep_alive_interval == 0) {
return LWMQTT_SUCCESS;
}
// return immediately if no ping is due
if (client->timer_get(client->keep_alive_timer) > 0) {
return LWMQTT_SUCCESS;
}
// a ping is due
// fail immediately if a pong is already pending
if (client->pong_pending) {
return LWMQTT_PONG_TIMEOUT;
}
// encode pingreq packet
size_t len;
lwmqtt_err_t err = lwmqtt_encode_zero(client->write_buf, client->write_buf_size, &len, LWMQTT_PINGREQ_PACKET);
if (err != LWMQTT_SUCCESS) {
return err;
}
// send packet
err = lwmqtt_send_packet_in_buffer(client, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// set flag
client->pong_pending = true;
return LWMQTT_SUCCESS;
}

View File

@@ -0,0 +1,249 @@
#include <string.h>
#include "helpers.h"
uint8_t lwmqtt_read_bits(uint8_t byte, int pos, int num) { return (byte & (uint8_t)((~(0xFF << num)) << pos)) >> pos; }
void lwmqtt_write_bits(uint8_t *byte, uint8_t value, int pos, int num) {
*byte = (*byte & ~(uint8_t)((~(0xFF << num)) << pos)) | (value << pos);
}
lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len) {
// check zero length
if (len == 0) {
*data = NULL;
return LWMQTT_SUCCESS;
}
// check buffer size
if ((size_t)(buf_end - (*buf)) < len) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// read data
*data = *buf;
// advance pointer
*buf += len;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len) {
// check zero length
if (len == 0) {
return LWMQTT_SUCCESS;
}
// check buffer size
if ((size_t)(buf_end - (*buf)) < len) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// write data
memcpy(*buf, data, len);
// advance pointer
*buf += len;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num) {
// check buffer size
if ((size_t)(buf_end - (*buf)) < 2) {
*num = 0;
return LWMQTT_BUFFER_TOO_SHORT;
}
// read two byte integer
*num = (uint16_t)256 * (*buf)[0] + (*buf)[1];
// adjust pointer
*buf += 2;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num) {
// check buffer size
if ((size_t)(buf_end - (*buf)) < 2) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// write bytes
(*buf)[0] = (uint8_t)(num / 256);
(*buf)[1] = (uint8_t)(num % 256);
// adjust pointer
*buf += 2;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str) {
// read length
uint16_t len;
lwmqtt_err_t err = lwmqtt_read_num(buf, buf_end, &len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// read data
err = lwmqtt_read_data(buf, buf_end, (uint8_t **)&str->data, len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// set length
str->len = len;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str) {
// write string length
lwmqtt_err_t err = lwmqtt_write_num(buf, buf_end, str.len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write data
err = lwmqtt_write_data(buf, buf_end, (uint8_t *)str.data, str.len);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte) {
// check buffer size
if ((size_t)(buf_end - (*buf)) < 1) {
*byte = 0;
return LWMQTT_BUFFER_TOO_SHORT;
}
// read byte
*byte = (*buf)[0];
// adjust pointer
*buf += 1;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte) {
// check buffer size
if ((size_t)(buf_end - (*buf)) < 1) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// write byte
(*buf)[0] = byte;
// adjust pointer
*buf += 1;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len) {
if (varnum < 128) {
*len = 1;
return LWMQTT_SUCCESS;
} else if (varnum < 16384) {
*len = 2;
return LWMQTT_SUCCESS;
} else if (varnum < 2097151) {
*len = 3;
return LWMQTT_SUCCESS;
} else if (varnum < 268435455) {
*len = 4;
return LWMQTT_SUCCESS;
} else {
*len = 0;
return LWMQTT_VARNUM_OVERFLOW;
}
}
lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum) {
// prepare last byte
uint8_t byte;
// prepare multiplier
uint32_t multiplier = 1;
// prepare length
size_t len = 0;
// initialize number
*varnum = 0;
// decode variadic number
do {
// increment length
len++;
// return error if buffer is to small
if ((size_t)(buf_end - (*buf)) < len) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// return error if the length has overflowed
if (len > 4) {
return LWMQTT_VARNUM_OVERFLOW;
}
// read byte
byte = (*buf)[len - 1];
// add byte to number
*varnum += (byte & 127) * multiplier;
// increase multiplier
multiplier *= 128;
} while ((byte & 128) != 0);
// adjust pointer
*buf += len;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum) {
// init len counter
size_t len = 0;
// encode variadic number
do {
// check overflow
if (len == 4) {
return LWMQTT_VARNUM_OVERFLOW;
}
// return error if buffer is to small
if ((size_t)(buf_end - (*buf)) < len + 1) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// calculate current byte
uint8_t byte = (uint8_t)(varnum % 128);
// change remaining length
varnum /= 128;
// set the top bit of this byte if there are more to encode
if (varnum > 0) {
byte |= 0x80;
}
// write byte
(*buf)[len++] = byte;
} while (varnum > 0);
// adjust pointer
*buf += len;
return LWMQTT_SUCCESS;
}

View File

@@ -0,0 +1,137 @@
#ifndef LWMQTT_HELPERS_H
#define LWMQTT_HELPERS_H
#include "lwmqtt.h"
/**
* Reads bits from a byte.
*
* @param byte - The byte to read from.
* @param pos - The position of the first bit.
* @param num - The number of bits to read.
* @return The read bits as a byte.
*/
uint8_t lwmqtt_read_bits(uint8_t byte, int pos, int num);
/**
* Write bits to a byte.
*
* @param byte - The byte to write bits to.
* @param value - The bits to write as a byte.
* @param pos - The position of the first bit.
* @param num - The number of bits to write.
*/
void lwmqtt_write_bits(uint8_t *byte, uint8_t value, int pos, int num);
/**
* Reads arbitrary data from the specified buffer. The pointer is incremented by bytes read.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param data - Pointer to beginning of data.
* @param len - The amount of data to read.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_read_data(uint8_t **buf, const uint8_t *buf_end, uint8_t **data, size_t len);
/**
* Writes arbitrary data to the specified buffer. The pointer is incremented by the bytes written.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param data - Pointer to the to be written data.
* @param len - The amount of data to write.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_write_data(uint8_t **buf, const uint8_t *buf_end, uint8_t *data, size_t len);
/**
* Reads two byte number from the specified buffer. The pointer is incremented by two.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param num - The read number.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_read_num(uint8_t **buf, const uint8_t *buf_end, uint16_t *num);
/**
* Writes a two byte number to the specified buffer. The pointer is incremented by two.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param num - The number to write.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_write_num(uint8_t **buf, const uint8_t *buf_end, uint16_t num);
/**
* Reads a string from the specified buffer into the passed object. The pointer is incremented by the bytes read.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param str - The object into which the data is to be read.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_read_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t *str);
/**
* Writes a string to the specified buffer. The pointer is incremented by the bytes written.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param str - The string to write.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_write_string(uint8_t **buf, const uint8_t *buf_end, lwmqtt_string_t str);
/**
* Reads one byte from the buffer. The pointer is incremented by one.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param byte - The read byte.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_read_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t *byte);
/**
* Writes one byte to the specified buffer. The pointer is incremented by one.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param byte - The byte to write.
* @return LWMQTT_SUCCESS or LWMQTT_BUFFER_TOO_SHORT.
*/
lwmqtt_err_t lwmqtt_write_byte(uint8_t **buf, const uint8_t *buf_end, uint8_t byte);
/**
* Returns the amount of bytes required by the variable number.
*
* @param varnum - The number to check.
* @param len - The required length;
* @return LWMQTT_SUCCESS or LWMQTT_VARNUM_OVERFLOW.
*/
lwmqtt_err_t lwmqtt_varnum_length(uint32_t varnum, int *len);
/**
* Reads a variable number from the specified buffer. The pointer is incremented by the bytes read.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param varnum - The read varnum.
* @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW.
*/
lwmqtt_err_t lwmqtt_read_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t *varnum);
/**
* Writes a variable number to the specified buffer. The pointer is incremented by the bytes written.
*
* @param buf - Pointer to the buffer.
* @param buf_end - Pointer to the end of the buffer.
* @param varnum - The number to write.
* @return LWMQTT_SUCCESS, LWMQTT_BUFFER_TOO_SHORT or LWMQTT_VARNUM_OVERFLOW.
*/
lwmqtt_err_t lwmqtt_write_varnum(uint8_t **buf, const uint8_t *buf_end, uint32_t varnum);
#endif

View File

@@ -0,0 +1,381 @@
#ifndef LWMQTT_H
#define LWMQTT_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
/**
* The error type used by all exposed APIs.
*
* If a function returns an error that operates on a connected client (e.g publish, keep_alive, etc.) the caller should
* switch into a disconnected state, close and cleanup the current connection and start over by creating a new
* connection.
*/
typedef enum {
LWMQTT_SUCCESS = 0,
LWMQTT_BUFFER_TOO_SHORT = -1,
LWMQTT_VARNUM_OVERFLOW = -2,
LWMQTT_NETWORK_FAILED_CONNECT = -3,
LWMQTT_NETWORK_TIMEOUT = -4,
LWMQTT_NETWORK_FAILED_READ = -5,
LWMQTT_NETWORK_FAILED_WRITE = -6,
LWMQTT_REMAINING_LENGTH_OVERFLOW = -7,
LWMQTT_REMAINING_LENGTH_MISMATCH = -8,
LWMQTT_MISSING_OR_WRONG_PACKET = -9,
LWMQTT_CONNECTION_DENIED = -10,
LWMQTT_FAILED_SUBSCRIPTION = -11,
LWMQTT_SUBACK_ARRAY_OVERFLOW = -12,
LWMQTT_PONG_TIMEOUT = -13,
} lwmqtt_err_t;
/**
* A common string object.
*/
typedef struct {
uint16_t len;
char *data;
} lwmqtt_string_t;
/**
* The initializer for string objects.
*/
#define lwmqtt_default_string \
{ 0, NULL }
/**
* Returns a string object for the passed C string.
*
* @param str - The C string.
* @return A string object.
*/
lwmqtt_string_t lwmqtt_string(const char *str);
/**
* Compares a string object to a C string.
*
* @param a - The string object to compare.
* @param b - The C string to compare.
* @return Similarity e.g. strcmp().
*/
int lwmqtt_strcmp(lwmqtt_string_t a, const char *b);
/**
* The available QOS levels.
*/
typedef enum { LWMQTT_QOS0 = 0, LWMQTT_QOS1 = 1, LWMQTT_QOS2 = 2, LWMQTT_QOS_FAILURE = 128 } lwmqtt_qos_t;
/**
* The message object used to publish and receive messages.
*/
typedef struct {
lwmqtt_qos_t qos;
bool retained;
uint8_t *payload;
size_t payload_len;
} lwmqtt_message_t;
/**
* The initializer for message objects.
*/
#define lwmqtt_default_message \
{ LWMQTT_QOS0, false, NULL, 0 }
/**
* Forward declaration of the client object.
*/
typedef struct lwmqtt_client_t lwmqtt_client_t;
/**
* The callback used to read from a network object.
*
* The callbacks is expected to read up to the amount of bytes in to the passed buffer. It should block the specified
* timeout and wait for more incoming data.
*
* @param ref - A custom reference.
* @param buf - The buffer.
* @param len - The length of the buffer.
* @param read - Variable that must be set with the amount of read bytes.
* @param timeout - The timeout in milliseconds for the operation.
*/
typedef lwmqtt_err_t (*lwmqtt_network_read_t)(void *ref, uint8_t *buf, size_t len, size_t *read, uint32_t timeout);
/**
* The callback used to write to a network object.
*
* The callback is expected to write up to the amount of bytes from the passed buffer. It should wait up to the
* specified timeout to write the specified data to the network.
*
* @param ref - A custom reference.
* @param buf - The buffer.
* @param len - The length of the buffer.
* @param sent - Variable that must be set with the amount of written bytes.
* @param timeout - The timeout in milliseconds for the operation.
*/
typedef lwmqtt_err_t (*lwmqtt_network_write_t)(void *ref, uint8_t *buf, size_t len, size_t *sent, uint32_t timeout);
/**
* The callback used to set a timer.
*
* @param ref - A custom reference.
* @param timeout - The amount of milliseconds until the deadline.
*/
typedef void (*lwmqtt_timer_set_t)(void *ref, uint32_t timeout);
/**
* The callback used to get a timers value.
*
* @param - A custom reference.
* @return The amount of milliseconds until the deadline. May return negative numbers if the deadline has been reached.
*/
typedef int32_t (*lwmqtt_timer_get_t)(void *ref);
/**
* The callback used to forward incoming messages.
*
* Note: The callback is mostly executed because of a call to lwmqtt_yield() that processes incoming messages. However,
* it is possible that the callback is also executed during a call to lwmqtt_subscribe(), lwmqtt_publish() or
* lwmqtt_unsubscribe() if incoming messages are received between the required acknowledgements. It is therefore not
* recommended to call any further lwmqtt methods in the callback as this might result in weird call stacks. The
* callback should place the received messages in a queue and dispatch them after the caller has returned.
*/
typedef void (*lwmqtt_callback_t)(lwmqtt_client_t *client, void *ref, lwmqtt_string_t str, lwmqtt_message_t msg);
/**
* The client object.
*/
struct lwmqtt_client_t {
uint16_t last_packet_id;
uint32_t keep_alive_interval;
bool pong_pending;
size_t write_buf_size, read_buf_size;
uint8_t *write_buf, *read_buf;
lwmqtt_callback_t callback;
void *callback_ref;
void *network;
lwmqtt_network_read_t network_read;
lwmqtt_network_write_t network_write;
void *keep_alive_timer;
void *command_timer;
lwmqtt_timer_set_t timer_set;
lwmqtt_timer_get_t timer_get;
};
/**
* Will initialize the specified client object.
*
* @param client - The client object.
* @param write_buf - The write buffer.
* @param write_buf_size - The write buffer size.
* @param read_buf - The read buffer.
* @param read_buf_size - The read buffer size.
*/
void lwmqtt_init(lwmqtt_client_t *client, uint8_t *write_buf, size_t write_buf_size, uint8_t *read_buf,
size_t read_buf_size);
/**
* Will set the network reference and callbacks for this client object.
*
* @param client - The client object.
* @param ref - The reference to the network object.
* @param read - The read callback.
* @param write - The write callback.
*/
void lwmqtt_set_network(lwmqtt_client_t *client, void *ref, lwmqtt_network_read_t read, lwmqtt_network_write_t write);
/**
* Will set the timer references and callbacks for this client object.
*
* @param client - The client object.
* @param keep_alive_timer - The reference to the keep alive timer.
* @param command_timer - The reference to the command timer.
* @param set - The set callback.
* @param get - The get callback.
*/
void lwmqtt_set_timers(lwmqtt_client_t *client, void *keep_alive_timer, void *command_timer, lwmqtt_timer_set_t set,
lwmqtt_timer_get_t get);
/**
* Will set the callback used to receive incoming messages.
*
* @param client - The client object.
* @param ref - A custom reference that will passed to the callback.
* @param cb - The callback to be called.
*/
void lwmqtt_set_callback(lwmqtt_client_t *client, void *ref, lwmqtt_callback_t cb);
/**
* The object defining the last will of a client.
*/
typedef struct {
lwmqtt_string_t topic;
lwmqtt_qos_t qos;
bool retained;
lwmqtt_string_t payload;
} lwmqtt_will_t;
/**
* The default initializer for the will object.
*/
#define lwmqtt_default_will \
{ lwmqtt_default_string, LWMQTT_QOS0, false, lwmqtt_default_string }
/**
* The object containing the connection options for a client.
*/
typedef struct {
lwmqtt_string_t client_id;
uint16_t keep_alive;
bool clean_session;
lwmqtt_string_t username;
lwmqtt_string_t password;
} lwmqtt_options_t;
/**
* The default initializer for the options object.
*/
#define lwmqtt_default_options \
{ lwmqtt_default_string, 60, true, lwmqtt_default_string, lwmqtt_default_string }
/**
* The available return codes transported by the connack packet.
*/
typedef enum {
LWMQTT_CONNECTION_ACCEPTED = 0,
LWMQTT_UNACCEPTABLE_PROTOCOL = 1,
LWMQTT_IDENTIFIER_REJECTED = 2,
LWMQTT_SERVER_UNAVAILABLE = 3,
LWMQTT_BAD_USERNAME_OR_PASSWORD = 4,
LWMQTT_NOT_AUTHORIZED = 5,
LWMQTT_UNKNOWN_RETURN_CODE = 6
} lwmqtt_return_code_t;
/**
* Will send a connect packet and wait for a connack response and set the return code.
*
* The network object must already be connected to the server. An error is returned if the broker rejects the
* connection.
*
* @param client - The client object.
* @param options - The options object.
* @param will - The will object.
* @param return_code - The variable that will receive the return code.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_connect(lwmqtt_client_t *client, lwmqtt_options_t options, lwmqtt_will_t *will,
lwmqtt_return_code_t *return_code, uint32_t timeout);
/**
* Will send a publish packet and wait for all acks to complete.
*
* Note: The message callback might be called with incoming messages as part of this call.
*
* @param client - The client object.
* @param topic - The topic.
* @param message - The message.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_publish(lwmqtt_client_t *client, lwmqtt_string_t topic, lwmqtt_message_t msg, uint32_t timeout);
/**
* Will send a subscribe packet with multiple topic filters plus QOS levels and wait for the suback to complete.
*
* Note: The message callback might be called with incoming messages as part of this call.
*
* @param client - The client object.
* @param count - The number of topic filters and QOS levels.
* @param topic_filter - The list of topic filters.
* @param qos - The list of QOS levels.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_subscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, lwmqtt_qos_t *qos,
uint32_t timeout);
/**
* Will send a subscribe packet with a single topic filter plus QOS level and wait for the suback to complete.
*
* Note: The message callback might be called with incoming messages as part of this call.
*
* @param client - The client object.
* @param topic_filter - The topic filter.
* @param qos - The QOS level.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_subscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, lwmqtt_qos_t qos,
uint32_t timeout);
/**
* Will send an unsubscribe packet with multiple topic filters and wait for the unsuback to complete.
*
* Note: The message callback might be called with incoming messages as part of this call.
*
* @param client - The client object.
* @param count - The number of topic filters.
* @param topic_filter - The topic filter.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_unsubscribe(lwmqtt_client_t *client, int count, lwmqtt_string_t *topic_filter, uint32_t timeout);
/**
* Will send an unsubscribe packet with a single topic filter and wait for the unsuback to complete.
*
* Note: The message callback might be called with incoming messages as part of this call.
*
* @param client - The client object.
* @param topic_filter - The topic filter.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_unsubscribe_one(lwmqtt_client_t *client, lwmqtt_string_t topic_filter, uint32_t timeout);
/**
* Will send a disconnect packet and finish the client.
*
* @param client - The client object.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_disconnect(lwmqtt_client_t *client, uint32_t timeout);
/**
* Will yield control to the client and receive incoming packets from the network.
*
* Single-threaded applications may peek on the network and assess if data is available to read before calling yield and
* potentially block until the timeout is reached. Multi-threaded applications may select on the socket and block until
* data is available and then yield to the client if data is available. All applications may specify the amount of bytes
* available to read in order to constrain the yield to only receive packets that are already in-flight.
*
* If no availability info is given the yield will return after one packet has been successfully read or the deadline
* has been reached but no single bytes has been received.
*
* Note: The message callback might be called with incoming messages as part of this call.
*
* @param client - The client object.
* @param available - The available bytes to read.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_yield(lwmqtt_client_t *client, size_t available, uint32_t timeout);
/**
* Will yield control to the client to keep the connection alive.
*
* This functions must be called at a rate slightly lower than 25% of the configured keep alive. If keep alive is zero,
* the function must not be called at all.
*
* @param client - The client object.
* @param timeout - The command timeout.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_keep_alive(lwmqtt_client_t *client, uint32_t timeout);
#endif // LWMQTT_H

View File

@@ -0,0 +1,742 @@
#include "packet.h"
lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type) {
// set default packet type
*packet_type = LWMQTT_NO_PACKET;
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// prepare header
uint8_t header;
// read header
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// get packet type
*packet_type = (lwmqtt_packet_type_t)lwmqtt_read_bits(header, 4, 4);
// check if packet type is correct and can be received
switch (*packet_type) {
case LWMQTT_CONNACK_PACKET:
case LWMQTT_PUBLISH_PACKET:
case LWMQTT_PUBACK_PACKET:
case LWMQTT_PUBREC_PACKET:
case LWMQTT_PUBREL_PACKET:
case LWMQTT_PUBCOMP_PACKET:
case LWMQTT_SUBACK_PACKET:
case LWMQTT_UNSUBACK_PACKET:
case LWMQTT_PINGRESP_PACKET:
return LWMQTT_SUCCESS;
default:
*packet_type = LWMQTT_NO_PACKET;
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
}
lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len) {
// prepare pointer
uint8_t *ptr = buf;
// attempt to decode remaining length
lwmqtt_err_t err = lwmqtt_read_varnum(&ptr, buf + buf_len, rem_len);
if (err == LWMQTT_VARNUM_OVERFLOW) {
*rem_len = 0;
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
} else if (err != LWMQTT_SUCCESS) {
*rem_len = 0;
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options,
lwmqtt_will_t *will) {
// prepare pointers
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// fixed header is 10
uint32_t rem_len = 10;
// add client id to remaining length
rem_len += options.client_id.len + 2;
// add will if present to remaining length
if (will != NULL) {
rem_len += will->topic.len + 2 + will->payload.len + 2;
}
// add username if present to remaining length
if (options.username.len > 0) {
rem_len += options.username.len + 2;
// add password if present to remaining length
if (options.password.len > 0) {
rem_len += options.password.len + 2;
}
}
// check remaining length length
int rem_len_len;
lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len);
if (err == LWMQTT_VARNUM_OVERFLOW) {
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
}
// prepare header
uint8_t header = 0;
lwmqtt_write_bits(&header, LWMQTT_CONNECT_PACKET, 4, 4);
// write header
err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write remaining length
err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write version string
err = lwmqtt_write_string(&buf_ptr, buf_end, lwmqtt_string("MQTT"));
if (err != LWMQTT_SUCCESS) {
return err;
}
// write version number
err = lwmqtt_write_byte(&buf_ptr, buf_end, 4);
if (err != LWMQTT_SUCCESS) {
return err;
}
// prepare flags
uint8_t flags = 0;
// set clean session
lwmqtt_write_bits(&flags, (uint8_t)(options.clean_session), 1, 1);
// set will flags if present
if (will != NULL) {
lwmqtt_write_bits(&flags, 1, 2, 1);
lwmqtt_write_bits(&flags, will->qos, 3, 2);
lwmqtt_write_bits(&flags, (uint8_t)(will->retained), 5, 1);
}
// set username flag if present
if (options.username.len > 0) {
lwmqtt_write_bits(&flags, 1, 7, 1);
// set password flag if present
if (options.password.len > 0) {
lwmqtt_write_bits(&flags, 1, 6, 1);
}
}
// write flags
err = lwmqtt_write_byte(&buf_ptr, buf_end, flags);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write keep alive
err = lwmqtt_write_num(&buf_ptr, buf_end, options.keep_alive);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write client id
err = lwmqtt_write_string(&buf_ptr, buf_end, options.client_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write will if present
if (will != NULL) {
// write topic
err = lwmqtt_write_string(&buf_ptr, buf_end, will->topic);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write payload length
err = lwmqtt_write_num(&buf_ptr, buf_end, (uint16_t)will->payload.len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write payload
err = lwmqtt_write_data(&buf_ptr, buf_end, (uint8_t *)will->payload.data, will->payload.len);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// write username if present
if (options.username.len > 0) {
err = lwmqtt_write_string(&buf_ptr, buf_end, options.username);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// write password if present
if (options.username.len > 0 && options.password.len > 0) {
err = lwmqtt_write_string(&buf_ptr, buf_end, options.password);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// set written length
*len = buf_ptr - buf;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present,
lwmqtt_return_code_t *return_code) {
// prepare pointers
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// read header
uint8_t header;
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check packet type
if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_CONNACK_PACKET) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// read remaining length
uint32_t rem_len;
err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check remaining length
if (rem_len != 2) {
return LWMQTT_REMAINING_LENGTH_MISMATCH;
}
// read flags
uint8_t flags;
err = lwmqtt_read_byte(&buf_ptr, buf_end, &flags);
if (err != LWMQTT_SUCCESS) {
return err;
}
// read return code
uint8_t raw_return_code;
err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_return_code);
if (err != LWMQTT_SUCCESS) {
return err;
}
// get session present
*session_present = lwmqtt_read_bits(flags, 7, 1) == 1;
// get return code
switch (raw_return_code) {
case 0:
*return_code = LWMQTT_CONNECTION_ACCEPTED;
break;
case 1:
*return_code = LWMQTT_UNACCEPTABLE_PROTOCOL;
break;
case 2:
*return_code = LWMQTT_IDENTIFIER_REJECTED;
break;
case 3:
*return_code = LWMQTT_SERVER_UNAVAILABLE;
break;
case 4:
*return_code = LWMQTT_BAD_USERNAME_OR_PASSWORD;
break;
case 5:
*return_code = LWMQTT_NOT_AUTHORIZED;
break;
default:
*return_code = LWMQTT_UNKNOWN_RETURN_CODE;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// write header
uint8_t header = 0;
lwmqtt_write_bits(&header, packet_type, 4, 4);
lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write remaining length
err = lwmqtt_write_varnum(&buf_ptr, buf_end, 0);
if (err != LWMQTT_SUCCESS) {
return err;
}
// set length
*len = buf_ptr - buf;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t packet_type, bool *dup,
uint16_t *packet_id) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// read header
uint8_t header = 0;
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check packet type
if (lwmqtt_read_bits(header, 4, 4) != packet_type) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// get dup
*dup = lwmqtt_read_bits(header, 3, 1) == 1;
// read remaining length
uint32_t rem_len;
err = lwmqtt_read_varnum(&buf_ptr, buf + buf_len, &rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check remaining length
if (rem_len != 2) {
return LWMQTT_REMAINING_LENGTH_MISMATCH;
}
// read packet id
err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup,
uint16_t packet_id) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// prepare header
uint8_t header = 0;
// set packet type
lwmqtt_write_bits(&header, packet_type, 4, 4);
// set dup
lwmqtt_write_bits(&header, (uint8_t)(dup), 3, 1);
// set qos
lwmqtt_write_bits(&header, (uint8_t)(packet_type == LWMQTT_PUBREL_PACKET ? LWMQTT_QOS1 : LWMQTT_QOS0), 1, 2);
// write header
lwmqtt_err_t err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write remaining length
err = lwmqtt_write_varnum(&buf_ptr, buf_end, 2);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write packet id
err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// set written length
*len = buf_ptr - buf;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic,
lwmqtt_message_t *msg) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// read header
uint8_t header;
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check packet type
if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_PUBLISH_PACKET) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// get dup
*dup = lwmqtt_read_bits(header, 3, 1) == 1;
// get retained
msg->retained = lwmqtt_read_bits(header, 0, 1) == 1;
// get qos
switch (lwmqtt_read_bits(header, 1, 2)) {
case 0:
msg->qos = LWMQTT_QOS0;
break;
case 1:
msg->qos = LWMQTT_QOS1;
break;
case 2:
msg->qos = LWMQTT_QOS2;
break;
default:
msg->qos = LWMQTT_QOS0;
break;
}
// read remaining length
uint32_t rem_len;
err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check remaining length (topic length)
if (rem_len < 2) {
return LWMQTT_REMAINING_LENGTH_MISMATCH;
}
// check buffer capacity
if ((uint32_t)(buf_end - buf_ptr) < rem_len) {
return LWMQTT_BUFFER_TOO_SHORT;
}
// reset buf end
buf_end = buf_ptr + rem_len;
// read topic
err = lwmqtt_read_string(&buf_ptr, buf_end, topic);
if (err != LWMQTT_SUCCESS) {
return err;
}
// read packet id if qos is at least 1
if (msg->qos > 0) {
err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
} else {
*packet_id = 0;
}
// set payload length
msg->payload_len = buf_end - buf_ptr;
// read payload
err = lwmqtt_read_data(&buf_ptr, buf_end, &msg->payload, buf_end - buf_ptr);
if (err != LWMQTT_SUCCESS) {
return err;
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id,
lwmqtt_string_t topic, lwmqtt_message_t msg) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// calculate remaining length
uint32_t rem_len = 2 + topic.len + (uint32_t)msg.payload_len;
if (msg.qos > 0) {
rem_len += 2;
}
// check remaining length length
int rem_len_len;
lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len);
if (err == LWMQTT_VARNUM_OVERFLOW) {
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
}
// prepare header
uint8_t header = 0;
// set packet type
lwmqtt_write_bits(&header, LWMQTT_PUBLISH_PACKET, 4, 4);
// set dup
lwmqtt_write_bits(&header, (uint8_t)(dup), 3, 1);
// set qos
lwmqtt_write_bits(&header, msg.qos, 1, 2);
// set retained
lwmqtt_write_bits(&header, (uint8_t)(msg.retained), 0, 1);
// write header
err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write remaining length
err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write topic
err = lwmqtt_write_string(&buf_ptr, buf_end, topic);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write packet id if qos is at least 1
if (msg.qos > 0) {
err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// write payload
err = lwmqtt_write_data(&buf_ptr, buf_end, msg.payload, msg.payload_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// set length
*len = buf_ptr - buf;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count,
lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// calculate remaining length
uint32_t rem_len = 2;
for (int i = 0; i < count; i++) {
rem_len += 2 + topic_filters[i].len + 1;
}
// check remaining length length
int rem_len_len;
lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len);
if (err == LWMQTT_VARNUM_OVERFLOW) {
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
}
// prepare header
uint8_t header = 0;
// set packet type
lwmqtt_write_bits(&header, LWMQTT_SUBSCRIBE_PACKET, 4, 4);
// set qos
lwmqtt_write_bits(&header, LWMQTT_QOS1, 1, 2);
// write header
err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write remaining length
err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write packet id
err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write all subscriptions
for (int i = 0; i < count; i++) {
// write topic
err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write qos level
err = lwmqtt_write_byte(&buf_ptr, buf_end, (uint8_t)qos_levels[i]);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// set length
*len = buf_ptr - buf;
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count,
lwmqtt_qos_t *granted_qos_levels) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// read header
uint8_t header;
lwmqtt_err_t err = lwmqtt_read_byte(&buf_ptr, buf_end, &header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check packet type
if (lwmqtt_read_bits(header, 4, 4) != LWMQTT_SUBACK_PACKET) {
return LWMQTT_MISSING_OR_WRONG_PACKET;
}
// read remaining length
uint32_t rem_len;
err = lwmqtt_read_varnum(&buf_ptr, buf_end, &rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// check remaining length (packet id + min. one suback code)
if (rem_len < 3) {
return LWMQTT_REMAINING_LENGTH_MISMATCH;
}
// read packet id
err = lwmqtt_read_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// read all suback codes
for (*count = 0; *count < (int)rem_len - 2; (*count)++) {
// check max count
if (*count > max_count) {
return LWMQTT_SUBACK_ARRAY_OVERFLOW;
}
// read qos level
uint8_t raw_qos_level;
err = lwmqtt_read_byte(&buf_ptr, buf_end, &raw_qos_level);
if (err != LWMQTT_SUCCESS) {
return err;
}
// set qos level
switch (raw_qos_level) {
case 0:
granted_qos_levels[*count] = LWMQTT_QOS0;
break;
case 1:
granted_qos_levels[*count] = LWMQTT_QOS1;
break;
case 2:
granted_qos_levels[*count] = LWMQTT_QOS2;
break;
default:
granted_qos_levels[*count] = LWMQTT_QOS_FAILURE;
break;
}
}
return LWMQTT_SUCCESS;
}
lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count,
lwmqtt_string_t *topic_filters) {
// prepare pointer
uint8_t *buf_ptr = buf;
uint8_t *buf_end = buf + buf_len;
// calculate remaining length
uint32_t rem_len = 2;
for (int i = 0; i < count; i++) {
rem_len += 2 + topic_filters[i].len;
}
// check remaining length length
int rem_len_len;
lwmqtt_err_t err = lwmqtt_varnum_length(rem_len, &rem_len_len);
if (err == LWMQTT_VARNUM_OVERFLOW) {
return LWMQTT_REMAINING_LENGTH_OVERFLOW;
}
// prepare header
uint8_t header = 0;
// set packet type
lwmqtt_write_bits(&header, LWMQTT_UNSUBSCRIBE_PACKET, 4, 4);
// set qos
lwmqtt_write_bits(&header, LWMQTT_QOS1, 1, 2);
// write header
err = lwmqtt_write_byte(&buf_ptr, buf_end, header);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write remaining length
err = lwmqtt_write_varnum(&buf_ptr, buf_end, rem_len);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write packet id
err = lwmqtt_write_num(&buf_ptr, buf_end, packet_id);
if (err != LWMQTT_SUCCESS) {
return err;
}
// write topics
for (int i = 0; i < count; i++) {
err = lwmqtt_write_string(&buf_ptr, buf_end, topic_filters[i]);
if (err != LWMQTT_SUCCESS) {
return err;
}
}
// set length
*len = buf_ptr - buf;
return LWMQTT_SUCCESS;
}

View File

@@ -0,0 +1,185 @@
#ifndef LWMQTT_PACKET_H
#define LWMQTT_PACKET_H
#include "helpers.h"
/**
* The available packet types.
*/
typedef enum {
LWMQTT_NO_PACKET = 0,
LWMQTT_CONNECT_PACKET = 1,
LWMQTT_CONNACK_PACKET,
LWMQTT_PUBLISH_PACKET,
LWMQTT_PUBACK_PACKET,
LWMQTT_PUBREC_PACKET,
LWMQTT_PUBREL_PACKET,
LWMQTT_PUBCOMP_PACKET,
LWMQTT_SUBSCRIBE_PACKET,
LWMQTT_SUBACK_PACKET,
LWMQTT_UNSUBSCRIBE_PACKET,
LWMQTT_UNSUBACK_PACKET,
LWMQTT_PINGREQ_PACKET,
LWMQTT_PINGRESP_PACKET,
LWMQTT_DISCONNECT_PACKET
} lwmqtt_packet_type_t;
/**
* Will detect the packet type from the at least one byte long buffer.
*
* @param buf - The buffer from which the packet type will be detected.
* @param buf_len - The length of the specified buffer.
* @param packet_type - The packet type.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_detect_packet_type(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t *packet_type);
/**
* Will detect the remaining length form the at least on byte long buffer.
*
* It will return LWMQTT_BUFFER_TOO_SHORT if the buffer is to short and an additional byte should be read from the
* network. In case the remaining length is overflowed it will return LWMQTT_REMAINING_LENGTH_OVERFLOW.
*
* @param buf - The buffer from which the remaining length will be detected.
* @param buf_len - The length of the specified buffer.
* @param rem_len - The detected remaining length.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_detect_remaining_length(uint8_t *buf, size_t buf_len, uint32_t *rem_len);
/**
* Encodes a connect packet into the supplied buffer.
*
* @param buf - The buffer into which the packet will be encoded.
* @param buf_len - The length of the specified buffer.
* @param len - The encoded length of the packet.
* @param options - The options to be used to build the connect packet.
* @param will - The last will and testament.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_encode_connect(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_options_t options,
lwmqtt_will_t *will);
/**
* Decodes a connack packet from the supplied buffer.
*
* @param buf - The raw buffer data.
* @param buf_len - The length of the specified buffer.
* @param session_present - The session present flag.
* @param return_code - The return code.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_decode_connack(uint8_t *buf, size_t buf_len, bool *session_present,
lwmqtt_return_code_t *return_code);
/**
* Encodes a zero (disconnect, pingreq) packet into the supplied buffer.
*
* @param buf - The buffer into which the packet will be encoded.
* @param buf_len - The length of the specified buffer.
* @param len - The encoded length of the packet.
* @param packet_type - The packets type.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_encode_zero(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type);
/**
* Decodes an ack (puback, pubrec, pubrel, pubcomp, unsuback) packet from the supplied buffer.
*
* @param buf - The raw buffer data.
* @param buf_len - The length of the specified buffer.
* @param packet_type - The packet type.
* @param dup - The dup flag.
* @param packet_id - The packet id.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_decode_ack(uint8_t *buf, size_t buf_len, lwmqtt_packet_type_t packet_type, bool *dup,
uint16_t *packet_id);
/**
* Encodes an ack (puback, pubrec, pubrel, pubcomp) packet into the supplied buffer.
*
* @param buf - The buffer into which the packet will be encoded.
* @param buf_len - The length of the specified buffer.
* @param len - The encoded length of the packet.
* @param packet_type - The packets type.
* @param dup - The dup flag.
* @param packet_id - The packet id.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_encode_ack(uint8_t *buf, size_t buf_len, size_t *len, lwmqtt_packet_type_t packet_type, bool dup,
uint16_t packet_id);
/**
* Decodes a publish packet from the supplied buffer.
*
* @param buf - The raw buffer data.
* @param buf_len - The length of the specified buffer.
* @param dup - The dup flag.
* @param packet_id - The packet id.
* @param topic - The topic.
* @parma msg - The message.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_decode_publish(uint8_t *buf, size_t buf_len, bool *dup, uint16_t *packet_id, lwmqtt_string_t *topic,
lwmqtt_message_t *msg);
/**
* Encodes a publish packet into the supplied buffer.
*
* @param buf - The buffer into which the packet will be encoded.
* @param buf_len - The length of the specified buffer.
* @param len - The encoded length of the packet.
* @param dup - The dup flag.
* @param packet_id - The packet id.
* @param topic - The topic.
* @param msg - The message.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_encode_publish(uint8_t *buf, size_t buf_len, size_t *len, bool dup, uint16_t packet_id,
lwmqtt_string_t topic, lwmqtt_message_t msg);
/**
* Encodes a subscribe packet into the supplied buffer.
*
* @param buf - The buffer into which the packet will be encoded.
* @param buf_len - The length of the specified buffer.
* @param len - The encoded length of the packet.
* @param packet_id - The packet id.
* @param count - The number of members in the topic_filters and qos_levels array.
* @param topic_filters - The array of topic filter.
* @param qos_levels - The array of requested QoS levels.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_encode_subscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count,
lwmqtt_string_t *topic_filters, lwmqtt_qos_t *qos_levels);
/**
* Decodes a suback packet from the supplied buffer.
*
* @param buf - The raw buffer data.
* @param buf_len - The length of the specified buffer.
* @param packet_id - The packet id.
* @param max_count - The maximum number of members allowed in the granted_qos_levels array.
* @param count - The number of members in the granted_qos_levels array.
* @param granted_qos_levels - The granted QoS levels.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_decode_suback(uint8_t *buf, size_t buf_len, uint16_t *packet_id, int max_count, int *count,
lwmqtt_qos_t *granted_qos_levels);
/**
* Encodes the supplied unsubscribe data into the supplied buffer, ready for sending
*
* @param buf - The buffer into which the packet will be encoded.
* @param buf_len - The length of the specified buffer.
* @param len - The encoded length of the packet.
* @param packet_id - The packet id.
* @param count - The number of members in the topic_filters array.
* @param topic_filters - The array of topic filters.
* @return An error value.
*/
lwmqtt_err_t lwmqtt_encode_unsubscribe(uint8_t *buf, size_t buf_len, size_t *len, uint16_t packet_id, int count,
lwmqtt_string_t *topic_filters);
#endif // LWMQTT_PACKET_H

View File

@@ -0,0 +1,38 @@
#include <string.h>
#include "lwmqtt.h"
lwmqtt_string_t lwmqtt_string(const char *str) {
// check for null
if (str == NULL) {
return (lwmqtt_string_t){0, NULL};
}
// get length
uint16_t len = (uint16_t)strlen(str);
// check zero length
if (len == 0) {
return (lwmqtt_string_t){0, NULL};
}
return (lwmqtt_string_t){len, (char *)str};
}
int lwmqtt_strcmp(lwmqtt_string_t a, const char *b) {
// get string of b
lwmqtt_string_t b_str = lwmqtt_string(b);
// return if both are zero length
if (a.len == 0 && b_str.len == 0) {
return 0;
}
// return if lengths are different
if (a.len != b_str.len) {
return -1;
}
// compare memory of same length
return strncmp(a.data, b_str.data, a.len);
}