diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..8be2b2f --- /dev/null +++ b/Readme.md @@ -0,0 +1,188 @@ +MQTT library for multiple platforms including embedded targets. + +## Goals for this library + +1. Readable code, modern C +2. Simple and fool-proof API +3. High test coverage (aim is >85% of all lines tested) +4. Suitable for embedded or Unixy targets (no static allocation though) + +## Current state + +- MQTT connection as a client is working +- All packet types are implemented +- Supports MQTT 3.1.1 (aka. protocol level 4) +- Platform support for linux is working +- Test have a coverage of about 87% (lines) and 94% (functions), there is room for improvement when QoS Testing is implemented + +## TODO + +[ ] QoS testing, client -> server QoS partly works, server -> client does not +[ ] Running in MQTT Broker mode +[ ] Last will is implemented but not exposed in API +[ ] Implement Protocol level 3 (low prio) +[ ] Implement Draft Protocol level 5 (somewhat low prio as it's a draft spec) + +## How to use + +### Building under Linux + +1. Checkout the repo +2. Run make: +```bash +make -f Makefile.linux +``` +3. Grab `libmqtt.a` or `libmqtt-debug.a` and `src/mqtt.h` and add that into your project. + +### Testing under Linux + +Requirements: + +- Local MQTT broker, simplest is mosquitto (can be run by calling `mosquitto -v` in a terminal for debug output) +- `Gcov` and `Lcov` for coverage reports, be aware you'll need a git version of lcov for newer GCC versions (> 6.0.0) + +#### Running the tests: + +```bash +make -f Makefile.linux test +``` + +#### Building a coverage report + +```bash +make -f Makefile.linux coverage +``` + +Be aware to create the coverage report the tests must run, so you'll need the MQTT broker. + +### API + +#### Configuration + +Create an instance of the config struct as following: + +```c +MQTTConfig config = { 0 }; // IMPORTANT: zero out the struct before usage! (this is C99 style) + +config.client_id = "my_client"; +config.hostname = "localhost"; +``` + +The snippet above is the absolute minimum you'll need, but there's more to configure if you want: + +``` +typedef struct { + char *hostname; /**< Hostname to connect to, will do DNS resolution */ + uint16_t port; /**< Port the broker listens on, set to 0 for 1883 default */ + + char *client_id; /**< Client identification */ + + char *username; /**< User name, set to NULL to connect anonymously */ + char *password; /**< Password, set to NULL to connect without password */ +} MQTTConfig; +``` + +#### Connect to MQTT broker + +```c +MQTTHandle *mqtt_connect(MQTTConfig *config, MQTTEventHandler callback, void *callback_context, MQTTErrorHandler error_callback); +``` +- `config`: MQTT configuration +- `callback`: Callback function to call on successful connect +- `callback_context`: Just a void pointer that is stored and given to the callback on connection +- `error_callback`: Error handler callback +- Returns handle to mqtt connection or NULL on error + +If the error handler is called with Host not found or Connection refused, +the handler is in charge of freeing the handle by returning true +or re-trying by changing settings and calling mqtt_reconnect() and returning false + +The error handler should be defined like this: + +```c +bool error_handler(MQTTHandle *handle, MQTTErrorCode code) { + + return true; // kill the handle, return false to keep it +} +``` + +The event handler looks like this: + +```c +void event_handler(MQTTHandle *handle, void *context) { + // The context pointer is the same as the time when you called `mqtt_connect` +} +``` + +#### Re-Connect to MQTT broker + +```c +MQTTStatus mqtt_reconnect(MQTTHandle *handle, MQTTEventHandler callback, void *callback_context); +``` + +- `handle`: MQTT Handle from `mqtt_connect` +- `callback`: Callback to call when reconnect was successful +- `callback_context`: Pointer to give the callback +- Returns status code + +Usually called in the MQTTErrorHandler callback, if called on a working +connection the connection will be disconnected before reconnecting. + +If there were registered subscriptions they will be re-instated after +a successful reconnect. + +#### Subscribe to a topic + +```c +MQTTStatus mqtt_subscribe(MQTTHandle *handle, char *topic, MQTTPublishEventHandler callback); +``` +- `handle`: MQTT Handle from `mqtt_connect` +- `topic`: Topic to subscribe +- `callback`: Callback function to call when receiving something for that topic +- Returns status code + +You may use the same callback function for multiple topics. +The callback function looks like this: + +```c +void publish_handler(MQTTHandle *handle, char *topic, char *payload) { + // the payload is zero terminated by the library, so it can be used as + // a standard c-string immediately. +} +``` + +#### Un-Subscribe from a topic + +```c +MQTTStatus mqtt_unsubscribe(MQTTHandle *handle, char *topic); +``` + +- `handle`: MQTT Handle from `mqtt_connect` +- `topic`: Topic to unsubscribe +- Returns status code + +#### Publish something to the broker + +```c +MQTTStatus mqtt_publish(MQTTHandle *handle, char *topic, char *payload, MQTTQosLevel qos_level); +``` + +- `handle`: MQTT Handle from `mqtt_connect` +- `topic`: Topic to publish to +- `payload`: Message payload to publish +- Returns status code + +This uses a c-string as the payload, theoretically the protocol would allow for binary payloads, but this is currently +not supported. + +#### Disconnect from MQTT broker + +```c +MQTTStatus mqtt_disconnect(MQTTHandle *handle, MQTTEventHandler callback, void *callback_context); +``` + +- `handle`: MQTT Handle from `mqtt_connect` +- Returns status code + +Attention: do not use the handle after calling this function, +all resources will be freed, this handle is now invalid! diff --git a/src/mqtt.h b/src/mqtt.h index 9b6e645..b939c8a 100644 --- a/src/mqtt.h +++ b/src/mqtt.h @@ -52,7 +52,9 @@ typedef void (*MQTTPublishEventHandler)(MQTTHandle *handle, char *topic, char *p * Connect to MQTT broker * * @param config: MQTT configuration - * @param callback: Callback function to call on errors + * @param callback: Success callback + * @param callback_context: Context pointer for the callback + * @param error_callback: Callback function to call on errors * @returns handle to mqtt connection or NULL on error * * If the error handler is called with Host not found or Connection refused, @@ -71,6 +73,8 @@ MQTTHandle *mqtt_connect(MQTTConfig *config, MQTTEventHandler callback, void *ca * a successful reconnect. * * @param handle: MQTT Handle from `mqtt_connect` + * @param callback: Success callback + * @param callback_context: Context pointer for the callback * @returns: Status code */ MQTTStatus mqtt_reconnect(MQTTHandle *handle, MQTTEventHandler callback, void *callback_context);