Skip to content

Commit

Permalink
Merge pull request #96 from embhorn/sensornet
Browse files Browse the repository at this point in the history
MQTT-Sensor Network client
  • Loading branch information
dgarske authored Nov 6, 2018
2 parents 4694fa6 + 0190491 commit 3aba67e
Show file tree
Hide file tree
Showing 16 changed files with 3,139 additions and 24 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -67,4 +67,5 @@ examples/azure/azureiothub
examples/aws/awsiot
examples/wiot/wiot
wolfmqtt/options.h
/IDE/Microchip-Harmony/wolfmqtt_client/firmware/mqtt_client.X/dist/default/
/IDE/Microchip-Harmony/wolfmqtt_client/firmware/mqtt_client.X/dist/default/
examples/sn-client/sn-client
29 changes: 23 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ This is an implementation of the MQTT Client written in C for embedded use, whic
2. `./configure` (to see a list of build options use `./configure --help`)
3. `make`

If `wolfssl` was recently installed run `sudo ldconfig` to update the linker cache.

### Windows Visual Studio

For building wolfMQTT with TLS support in Visual Studio:
Expand Down Expand Up @@ -101,23 +103,25 @@ Here are the steps for creating your own implementation.
## Examples

### Client Example
The example MQTT client is located in /examples/mqttclient/. This example exercises many of the exposed API’s and prints any incoming publish messages for subscription topic “wolfMQTT/example/testTopic”. This client contains examples of many MQTTv5 features, including the property callback and server assignment of client ID. The mqqtclient example is a good starting template for your MQTT application.
The example MQTT client is located in `/examples/mqttclient/`. This example exercises many of the exposed API’s and prints any incoming publish messages for subscription topic “wolfMQTT/example/testTopic”. This client contains examples of many MQTTv5 features, including the property callback and server assignment of client ID. The mqqtclient example is a good starting template for your MQTT application.

### Non-Blocking Client Example
The example MQTT client is located in /examples/nbclient/. This example uses non-blocking I/O for message exchange. The wolfMQTT library must be configured with the `--enable-nonblock` option (or built with `WOLFMQTT_NONBLOCK`).
The example MQTT client is located in `/examples/nbclient/`. This example uses non-blocking I/O for message exchange. The wolfMQTT library must be configured with the `--enable-nonblock` option (or built with `WOLFMQTT_NONBLOCK`).

### Firmware Example
The MQTT firmware update is located in /examples/firmware/. This example has two parts. The first is called “fwpush”, which signs and publishes a firmware image. The second is called “fwclient”, which receives the firmware image and verifies the signature. This example publishes message on the topic “wolfMQTT/example/firmware”. The "fwpush" application is an example of using a publish callback to send the payload data.
The MQTT firmware update is located in `/examples/firmware/`. This example has two parts. The first is called “fwpush”, which signs and publishes a firmware image. The second is called “fwclient”, which receives the firmware image and verifies the signature. This example publishes message on the topic “wolfMQTT/example/firmware”. The "fwpush" application is an example of using a publish callback to send the payload data.

### Azure IoT Hub Example
We setup a wolfMQTT IoT Hub on the Azure server for testing. We added a device called `demoDevice`, which you can connect and publish to. The example demonstrates creation of a SasToken, which is used as the password for the MQTT connect packet. It also shows the topic names for publishing events and listening to `devicebound` messages. This example only works with `ENABLE_MQTT_TLS` set and the wolfSSL library present because it requires Base64 Encode/Decode and HMAC-SHA256. Note: The wolfSSL library must be built with `./configure --enable-base64encode` or `#define WOLFSSL_BASE64_ENCODE`. The `wc_GetTime` API was added in 3.9.1 and if not present you'll need to implement your own version of this to get current UTC seconds or update your wolfSSL library.

### AWS IoT Example
We setup an AWS IoT endpoint and testing device certificate for testing. The AWS server uses TLS client certificate for authentication. The example is located in /examples/aws/. The example subscribes to `$aws/things/"AWSIOT_DEVICE_ID"/shadow/update/delta` and publishes to `$aws/things/"AWSIOT_DEVICE_ID"/shadow/update`.
We setup an AWS IoT endpoint and testing device certificate for testing. The AWS server uses TLS client certificate for authentication. The example is located in `/examples/aws/`. The example subscribes to `$aws/things/"AWSIOT_DEVICE_ID"/shadow/update/delta` and publishes to `$aws/things/"AWSIOT_DEVICE_ID"/shadow/update`.

### Watson IoT Example
This example enables the wolfMQTT client to connect to the IBM Watson Internet of Things (WIOT) Platform. The WIOT Platform has a limited test broker called "Quickstart" that allows non-secure connections to exercise the component. The example is located in /examples/wiot/. Works with MQTT v5 support enabled.
This example enables the wolfMQTT client to connect to the IBM Watson Internet of Things (WIOT) Platform. The WIOT Platform has a limited test broker called "Quickstart" that allows non-secure connections to exercise the component. The example is located in `/examples/wiot/`. Works with MQTT v5 support enabled.

### Mqtt-SN Example
The Sensor Network client implements the MQTT-SN protocol for low-bandwidth networks. There are several differences from MQTT, including the ability to use a two byte Topic ID instead the full topic during subscribe and publish. The SN client requires an MQTT-SN gateway. The gateway acts as an intermediary between the SN clients and the broker. This client was tested with the Eclipse Paho MQTT-SN Gateway, which connects by default to the public Eclipse broker, much like our wolfMQTT Client example. The address of the gateway must be configured as the host. The example is located in `/examples/sn-client/`.

## v5.0 Specification Support
The wolfMQTT client supports connecting to v5 enabled brokers when configured with the `--enable-mqtt5` option. Handling properties received from the server is accomplished via a callback when the `--enable-propcb` option is set. The following v5.0 specification features are supported by the wolfMQTT client:
Expand All @@ -134,7 +138,7 @@ The wolfMQTT client supports connecting to v5 enabled brokers when configured wi

The v5 enabled wolfMQTT client was tested with the following MQTT v5 brokers:
* Flespi
** Requires an account tied token that is regnerated hourly.
** Requires an account tied token that is regenerated hourly.
** `./examples/mqttclient/mqttclient -h "mqtt.flespi.io" -u "<your-flespi-token>"`
* VerneMQ MQTTv5 preview
** Runs locally.
Expand All @@ -145,6 +149,19 @@ The v5 enabled wolfMQTT client was tested with the following MQTT v5 brokers:
* Watson IoT Quickserver
** `./examples/wiot/wiot`

## Sensor Network Specification Support
The wolfMQTT SN Client implementation is based on the OASIS MQTT-SN v1.2 specification. The SN API is configured with the `--enable-sn` option. There is a separate API for the sensor network API, which all begin with the "SN_" prefix. The wolfMQTT SN Client operates over UDP, which is distinct from the wolfMQTT clients that use TCP. The following features are supported by the wolfMQTT SN Client:
* Register
* Will topic and message set up
* Will topic and message update
* All QoS levels
* Variable-sized packet length field

Unsupported features:
* Automatic gateway discovery is not implemented
* Multiple gateway handling

The SN client was tested using the Eclipse Paho MQTT-SN Gateway (https://github.com/eclipse/paho.mqtt-sn.embedded-c) running locally and on a separate network node. Instructions for building and running the gateway are in the project README.

## Release Notes

Expand Down
15 changes: 15 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,20 @@ fi
AM_CONDITIONAL([BUILD_STDINCAP], [test "x$ENABLED_STDINCAP" = "xyes"])


# MQTT-SN Sensor Network
AC_ARG_ENABLE([sn],
[AS_HELP_STRING([--enable-sn],[Enable MQTT-SN support (default: disabled)])],
[ ENABLED_SN=$enableval ],
[ ENABLED_SN=no ]
)

if test "x$ENABLED_SN" = "xyes"
then
AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFMQTT_SN"
fi

AM_CONDITIONAL([BUILD_SN], [test "x$ENABLED_SN" = "xyes"])

# MQTT v5.0
AC_ARG_ENABLE([mqtt5],
[AS_HELP_STRING([--enable-mqtt5],[Enable MQTT v5.0 support (default: disabled)])],
Expand Down Expand Up @@ -337,6 +351,7 @@ echo " * LIB Flags: $LIB"

echo " * Disconnect Callback: $ENABLED_DISCB"
echo " * Error Strings: $ENABLED_ERROR_STRINGS"
echo " * Enable MQTT-SN: $ENABLED_SN"
echo " * Enable MQTT v5.0: $ENABLED_MQTTV50"
echo " * Property Callback: $ENABLED_PROPCB"
echo " * Examples: $ENABLED_EXAMPLES"
Expand Down
21 changes: 20 additions & 1 deletion examples/include.am
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ noinst_PROGRAMS += examples/mqttclient/mqttclient \
examples/azure/azureiothub \
examples/aws/awsiot \
examples/wiot/wiot
if BUILD_SN
noinst_PROGRAMS += examples/sn-client/sn-client
endif

noinst_HEADERS += examples/mqttclient/mqttclient.h \
examples/nbclient/nbclient.h \
Expand All @@ -20,7 +23,9 @@ noinst_HEADERS += examples/mqttclient/mqttclient.h \
examples/wiot/wiot.h \
examples/mqttnet.h \
examples/mqttexample.h

if BUILD_SN
noinst_HEADERS += examples/sn-client/sn-client.h
endif

examples_mqttclient_mqttclient_SOURCES = examples/mqttclient/mqttclient.c \
examples/mqttnet.c \
Expand Down Expand Up @@ -77,6 +82,14 @@ examples_wiot_wiot_LDADD = src/libwolfmqtt.la
examples_wiot_wiot_DEPENDENCIES = src/libwolfmqtt.la
examples_wiot_wiot_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS)

if BUILD_SN
examples_sn_client_sn_client_SOURCES = examples/sn-client/sn-client.c \
examples/mqttnet.c \
examples/mqttexample.c
examples_sn_client_sn_client_LDADD = src/libwolfmqtt.la
examples_sn_client_sn_client_DEPENDENCIES = src/libwolfmqtt.la
examples_sn_client_sn_client_CPPFLAGS = -I$(top_srcdir)/examples $(AM_CPPFLAGS)
endif
endif

dist_example_DATA+= examples/mqttnet.c \
Expand All @@ -88,6 +101,9 @@ dist_example_DATA+= examples/mqttnet.c \
examples/azure/azureiothub.c \
examples/aws/awsiot.c \
examples/wiot/wiot.c
if BUILD_SN
dist_example_DATA+= examples/sn-client/sn-client.c
endif

DISTCLEANFILES+= examples/mqttclient/.libs/mqttclient \
examples/nbclient/.libs/nbclient \
Expand All @@ -96,6 +112,9 @@ DISTCLEANFILES+= examples/mqttclient/.libs/mqttclient \
examples/azure/.libs/azureiothub \
examples/aws/.libs/awsiot \
examples/wiot/.libs/wiot
if BUILD_SN
DISTCLEANFILES+= examples/sn-client/.libs/sn-client
endif

EXTRA_DIST+= examples/mqttuart.c \
examples/mqttclient/mqttclient.vcxproj \
Expand Down
141 changes: 135 additions & 6 deletions examples/mqttnet.c
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,8 @@ static int NetConnect(void *context, const char* host, word16 port,
return rc;
}

static int NetRead(void *context, byte* buf, int buf_len, int timeout_ms)
static int NetRead(void *context, byte* buf, int buf_len,
int timeout_ms)
{
SocketContext *sock = (SocketContext*)context;
int rc = -1, timeout = 0;
Expand Down Expand Up @@ -523,7 +524,6 @@ static void tcp_set_nonblocking(SOCKET_T* sockfd)
#endif /* WOLFMQTT_NONBLOCK */
#endif /* !WOLFMQTT_NO_TIMEOUT */


static int NetConnect(void *context, const char* host, word16 port,
int timeout_ms)
{
Expand Down Expand Up @@ -643,6 +643,88 @@ static int NetConnect(void *context, const char* host, word16 port,
return rc;
}

#ifdef WOLFMQTT_SN
static int SN_NetConnect(void *context, const char* host, word16 port,
int timeout_ms)
{
SocketContext *sock = (SocketContext*)context;
int type = SOCK_DGRAM;
int rc;
SOERROR_T so_error = 0;
struct addrinfo *result = NULL;
struct addrinfo hints;

/* Get address information for host and locate IPv4 */
XMEMSET(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */

XMEMSET(&sock->addr, 0, sizeof(sock->addr));
sock->addr.sin_family = AF_INET;

rc = getaddrinfo(host, NULL, &hints, &result);
if (rc >= 0 && result != NULL) {
struct addrinfo* res = result;

/* prefer ip4 addresses */
while (res) {
if (res->ai_family == AF_INET) {
result = res;
break;
}
res = res->ai_next;
}

if (result->ai_family == AF_INET) {
sock->addr.sin_port = htons(port);
sock->addr.sin_family = AF_INET;
sock->addr.sin_addr =
((SOCK_ADDR_IN*)(result->ai_addr))->sin_addr;
}
else {
rc = -1;
}

freeaddrinfo(result);
}

if (rc == 0) {

/* Create the socket */
sock->fd = socket(sock->addr.sin_family, type, 0);
if (sock->fd == SOCKET_INVALID) {
rc = -1;
}
}

if (rc == 0)
{
#ifndef WOLFMQTT_NO_TIMEOUT
fd_set fdset;
struct timeval tv;

/* Setup timeout and FD's */
setup_timeout(&tv, timeout_ms);
FD_ZERO(&fdset);
FD_SET(sock->fd, &fdset);
#else
(void)timeout_ms;
#endif /* !WOLFMQTT_NO_TIMEOUT */

/* Start connect */
rc = SOCK_CONNECT(sock->fd, (struct sockaddr*)&sock->addr, sizeof(sock->addr));
}

/* Show error */
if (rc != 0) {
close(sock->fd);
PRINTF("NetConnect: Rc=%d, SoErr=%d", rc, so_error);
}

return rc;
}
#endif

static int NetWrite(void *context, const byte* buf, int buf_len,
int timeout_ms)
{
Expand Down Expand Up @@ -687,13 +769,14 @@ static int NetWrite(void *context, const byte* buf, int buf_len,
return rc;
}

static int NetRead(void *context, byte* buf, int buf_len,
int timeout_ms)
static int NetRead_ex(void *context, byte* buf, int buf_len,
int timeout_ms, byte peek)
{
SocketContext *sock = (SocketContext*)context;
int rc = -1, timeout = 0;
SOERROR_T so_error = 0;
int bytes = 0;
int flags = 0;
#if !defined(WOLFMQTT_NO_TIMEOUT) && !defined(WOLFMQTT_NONBLOCK)
fd_set recvfds;
fd_set errfds;
Expand All @@ -704,6 +787,10 @@ static int NetRead(void *context, byte* buf, int buf_len,
return MQTT_CODE_ERROR_BAD_ARG;
}

if (peek == 1) {
flags |= MSG_PEEK;
}

#if !defined(WOLFMQTT_NO_TIMEOUT) && !defined(WOLFMQTT_NONBLOCK)
/* Setup timeout and FD's */
setup_timeout(&tv, timeout_ms);
Expand Down Expand Up @@ -737,7 +824,7 @@ static int NetRead(void *context, byte* buf, int buf_len,
rc = (int)SOCK_RECV(sock->fd,
&buf[bytes],
buf_len - bytes,
0);
flags);
if (rc <= 0) {
rc = -1;
goto exit; /* Error */
Expand Down Expand Up @@ -798,6 +885,18 @@ static int NetRead(void *context, byte* buf, int buf_len,
return rc;
}

static int NetRead(void *context, byte* buf, int buf_len, int timeout_ms)
{
return NetRead_ex(context, buf, buf_len, timeout_ms, 0);
}

#ifdef WOLFMQTT_SN
static int NetPeek(void *context, byte* buf, int buf_len, int timeout_ms)
{
return NetRead_ex(context, buf, buf_len, timeout_ms, 1);
}
#endif

static int NetDisconnect(void *context)
{
SocketContext *sock = (SocketContext*)context;
Expand All @@ -815,7 +914,6 @@ static int NetDisconnect(void *context)
#endif



/* Public Functions */
int MqttClientNet_Init(MqttNet* net)
{
Expand Down Expand Up @@ -872,6 +970,37 @@ int MqttClientNet_Init(MqttNet* net)
return MQTT_CODE_SUCCESS;
}

#ifdef WOLFMQTT_SN
int SN_ClientNet_Init(MqttNet* net)
{
if (net) {
XMEMSET(net, 0, sizeof(MqttNet));
net->connect = SN_NetConnect;
net->read = NetRead;
net->write = NetWrite;
net->peek = NetPeek;
net->disconnect = NetDisconnect;
net->context = (SocketContext *)WOLFMQTT_MALLOC(sizeof(SocketContext));
if (net->context == NULL) {
return MQTT_CODE_ERROR_MEMORY;
}
XMEMSET(net->context, 0, sizeof(SocketContext));
((SocketContext*)(net->context))->stat = SOCK_BEGIN;

#if 0 //TODO: multicast support
net->multi_ctx = (SocketContext *)WOLFMQTT_MALLOC(sizeof(SocketContext));
if (net->multi_ctx == NULL) {
return MQTT_CODE_ERROR_MEMORY;
}
XMEMSET(net->multi_ctx, 0, sizeof(SocketContext));
((SocketContext*)(net->multi_ctx))->stat = SOCK_BEGIN;
#endif
}

return MQTT_CODE_SUCCESS;
}
#endif

int MqttClientNet_DeInit(MqttNet* net)
{
if (net) {
Expand Down
4 changes: 3 additions & 1 deletion examples/mqttnet.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
/* Functions used to handle the MqttNet structure creation / destruction */
int MqttClientNet_Init(MqttNet* net);
int MqttClientNet_DeInit(MqttNet* net);

#ifdef WOLFMQTT_SN
int SN_ClientNet_Init(MqttNet* net);
#endif

#ifdef __cplusplus
} /* extern "C" */
Expand Down
Loading

0 comments on commit 3aba67e

Please sign in to comment.