diff --git a/examples/paho-mqtt/Makefile b/examples/paho-mqtt/Makefile new file mode 100644 index 0000000000..31907c9777 --- /dev/null +++ b/examples/paho-mqtt/Makefile @@ -0,0 +1,59 @@ +APPLICATION = paho-mqtt-example + +# If no BOARD is found in the environment, use this default: +BOARD ?= native + +# This has to be the absolute path to the RIOT base directory: +RIOTBASE ?= $(CURDIR)/../.. + +# Comment this out to disable code in RIOT that does safety checking +# which is not needed in a production environment but helps in the +# development process: +DEVELHELP ?= 1 + +# Change this to 0 show compiler invocation lines by default: +QUIET ?= 1 + +WIFI_SSID ?= "Your_WiFi_name" +WIFI_PASS ?= "Your_secure_password" + +USEMODULE += shell +USEMODULE += shell_commands +USEMODULE += ps +USEMODULE += netdev_default +USEPKG += paho-mqtt + +# paho-mqtt depends on TCP support, choose the stack you want +LWIP_IPV4 ?= 0 + +ifneq (0, $(LWIP_IPV4)) + USEMODULE += ipv4_addr + USEMODULE += lwip_arp + USEMODULE += lwip_ipv4 + USEMODULE += lwip_dhcp_auto + USEMODULE += lwip_sock_udp + CFLAGS += -DETHARP_SUPPORT_STATIC_ENTRIES=1 + LWIP_IPV6 ?= 0 +else + LWIP_IPV6 ?= 1 +endif + +ifneq (0, $(LWIP_IPV6)) + USEMODULE += ipv6_addr + USEMODULE += lwip_ipv6_autoconfig +endif + +USEMODULE += lwip_netdev +USEMODULE += lwip lwip_sock_ip +USEMODULE += lwip_tcp lwip_sock_tcp +USEMODULE += lwip_sock_async + +USEMODULE += sock_async_event +#### + +include $(RIOTBASE)/Makefile.include + +ifneq (,$(filter arch_esp,$(FEATURES_USED))) + CFLAGS += -DESP_WIFI_SSID=\"$(WIFI_SSID)\" + CFLAGS += -DESP_WIFI_PASS=\"$(WIFI_PASS)\" +endif diff --git a/examples/paho-mqtt/Makefile.board.dep b/examples/paho-mqtt/Makefile.board.dep new file mode 100644 index 0000000000..4000952c28 --- /dev/null +++ b/examples/paho-mqtt/Makefile.board.dep @@ -0,0 +1,9 @@ +# Put board specific dependencies here + +ifneq (,$(filter arch_esp,$(FEATURES_USED))) + USEMODULE += esp_wifi +endif + +ifeq ($(BOARD),native) + USEMODULE += netdev_default +endif diff --git a/examples/paho-mqtt/Makefile.ci b/examples/paho-mqtt/Makefile.ci new file mode 100644 index 0000000000..2192ac766a --- /dev/null +++ b/examples/paho-mqtt/Makefile.ci @@ -0,0 +1,29 @@ +BOARD_INSUFFICIENT_MEMORY := \ + airfy-beacon \ + calliope-mini \ + i-nucleo-lrwan1 \ + im880b \ + microbit \ + nrf6310 \ + nucleo-f030r8 \ + nucleo-f031k6 \ + nucleo-f042k6 \ + nucleo-f070rb \ + nucleo-f072rb \ + nucleo-f303k8 \ + nucleo-f334r8 \ + nucleo-l031k6 \ + nucleo-l053r8 \ + nucleo-f302r8 \ + nrf51dongle \ + stm32f030f4-demo \ + stm32f0discovery \ + stm32l0538-disco \ + bluepill \ + blackpill \ + hifive1 \ + hifive1b \ + saml11-xpro \ + saml10-xpro \ + yunjia-nrf51822 \ + # diff --git a/examples/paho-mqtt/README.md b/examples/paho-mqtt/README.md new file mode 100644 index 0000000000..e4a56a2c66 --- /dev/null +++ b/examples/paho-mqtt/README.md @@ -0,0 +1,50 @@ +### About +This application demonstrates the usage of the Eclipse paho MQTT package in RIOT. + +### Setup +For using this example, two prerequisites have to be fulfilled: + +1. You need a running MQTT broker like Mosquitto broker for example. Take a look at +[Mosquitto Broker](https://mosquitto.org/). Check online any guide that will +help you setting up the broker into some port (a). +For example this one for debian base linux users +[How to setup a Mosquitto MQTT Server and receive data](https://www.digitalocean.com/community/questions/how-to-setup-a-mosquitto-mqtt-server-and-receive-data-from-owntracks). + +2. Your RIOT node needs to be able to speak to that broker at the same port you set in 1. + +### Setting up RIOT `native` on Linux +- Run `sudo ./dist/tools/tapsetup/tapsetup -c 1` + +## Running the example +- Run on RIOT's root directory: + + make -C examples/paho-mqtt all term + +- To connect to a broker, use the `con` command: +``` +con [port] [clientID] [user] [password] [keepalivetime] +``` + * *broker ip addr*: IPv6 or IPv4 broker address. + * *port*: broker port. Default 1883 + * *client ID*: is the client id you set up on the broker. Default can be set + through DEFAULT_MQTT_CLIENT_ID in your makefile. Otherwise is an empty string. + * *user*: the one set in the broker, check online tutorial to do it regarding chosen broker. + Default user can be set through DEFAULT_MQTT_USER in your makefile. Otherwise is an empty string. + * *password*: the one set in the broker, check online tutorial to do it regarding chosen broker. + Default user can be set through DEFAULT_MQTT_PWD in your makefile. Otherwise is an empty string. + * *keepalivetime*: keep alive in seconds for your client. Default 10 secs. + +- To subscribe to a topic, run `sub` with the topic name as parameter and a QoS + level between 1 to 3, e.g. +``` +sub hello/world 2 +``` +- To unsubscribe to a topic, run `unsub` with the topic name e.g. +``` +unsub hello/world +``` + +- For publishing, use the `pub` command with a QoS level between 1 to 3: +``` +pub hello/world "One more beer, please." 2 +``` diff --git a/examples/paho-mqtt/main.c b/examples/paho-mqtt/main.c new file mode 100644 index 0000000000..30f8b90495 --- /dev/null +++ b/examples/paho-mqtt/main.c @@ -0,0 +1,312 @@ +/* + * Copyright (C) 2019 Javier FILEIV + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup examples + * @{ + * + * @file main.c + * @brief Example using MQTT Paho package from RIOT + * + * @author Javier FILEIV + * + * @} + */ + +#include +#include +#include +#include "xtimer.h" +#include "shell.h" +#include "thread.h" +#include "mutex.h" +#include "paho_mqtt.h" +#include "MQTTClient.h" + +#define BUF_SIZE 1024 +#define MQTT_VERSION_v311 4 /* MQTT v3.1.1 version is 4 */ +#define COMMAND_TIMEOUT_MS 4000 + +#ifndef DEFAULT_MQTT_CLIENT_ID +#define DEFAULT_MQTT_CLIENT_ID "" +#endif + +#ifndef DEFAULT_MQTT_USER +#define DEFAULT_MQTT_USER "" +#endif + +#ifndef DEFAULT_MQTT_PWD +#define DEFAULT_MQTT_PWD "" +#endif + +/** + * @brief Default MQTT port + */ +#define DEFAULT_MQTT_PORT 1883 + +/** + * @brief Keepalive timeout in seconds + */ +#define DEFAULT_KEEPALIVE_SEC 10 + +#ifndef MAX_LEN_TOPIC +#define MAX_LEN_TOPIC 100 +#endif + +#ifndef MAX_TOPICS +#define MAX_TOPICS 4 +#endif + +#define IS_CLEAN_SESSION 1 +#define IS_RETAINED_MSG 0 + +static MQTTClient client; +static Network network; +static int topic_cnt = 0; +static char _topic_to_subscribe[MAX_TOPICS][MAX_LEN_TOPIC]; + +static unsigned get_qos(const char *str) +{ + int qos = atoi(str); + + switch (qos) { + case 1: return QOS1; + case 2: return QOS2; + default: return QOS0; + } +} + +static void _on_msg_received(MessageData *data) +{ + printf("paho_mqtt_example: message received on topic" + " %.*s: %.*s\n", + (int)data->topicName->lenstring.len, + data->topicName->lenstring.data, (int)data->message->payloadlen, + (char *)data->message->payload); +} + +static int _cmd_discon(int argc, char **argv) +{ + (void)argc; + (void)argv; + + topic_cnt = 0; + int res = MQTTDisconnect(&client); + if (res < 0) { + printf("mqtt_example: Unable to disconnect\n"); + } + else { + printf("mqtt_example: Disconnect successful\n"); + } + + NetworkDisconnect(&network); + return res; +} + +static int _cmd_con(int argc, char **argv) +{ + if (argc < 2) { + printf( + "usage: %s [port] [clientID] [user] [password] " + "[keepalivetime]\n", + argv[0]); + return 1; + } + + char *remote_ip = argv[1]; + + int ret = -1; + + /* ensure client isn't connected in case of a new connection */ + if (client.isconnected) { + printf("mqtt_example: client already connected, disconnecting it\n"); + MQTTDisconnect(&client); + NetworkDisconnect(&network); + } + + int port = DEFAULT_MQTT_PORT; + if (argc > 2) { + port = atoi(argv[2]); + } + + MQTTPacket_connectData data = MQTTPacket_connectData_initializer; + data.MQTTVersion = MQTT_VERSION_v311; + + data.clientID.cstring = DEFAULT_MQTT_CLIENT_ID; + if (argc > 3) { + data.username.cstring = argv[3]; + } + + data.username.cstring = DEFAULT_MQTT_USER; + if (argc > 4) { + data.username.cstring = argv[4]; + } + + data.password.cstring = DEFAULT_MQTT_PWD; + if (argc > 5) { + data.password.cstring = argv[5]; + } + + data.keepAliveInterval = DEFAULT_KEEPALIVE_SEC; + if (argc > 6) { + data.keepAliveInterval = atoi(argv[6]); + } + + data.cleansession = IS_CLEAN_SESSION; + data.willFlag = 0; + + printf("mqtt_example: Connecting to MQTT Broker from %s %d\n", + remote_ip, port); + printf("mqtt_example: Trying to connect to %s , port: %d\n", + remote_ip, port); + ret = NetworkConnect(&network, remote_ip, port); + if (ret < 0) { + printf("mqtt_example: Unable to connect\n"); + return ret; + } + + printf("user:%s clientId:%s password:%s\n", data.username.cstring, + data.clientID.cstring, data.password.cstring); + ret = MQTTConnect(&client, &data); + if (ret < 0) { + printf("mqtt_example: Unable to connect client %d\n", ret); + _cmd_discon(0, NULL); + return ret; + } + else { + printf("mqtt_example: Connection successfully\n"); + } + + return (ret > 0) ? 0 : 1; +} + +static int _cmd_pub(int argc, char **argv) +{ + enum QoS qos = QOS0; + + if (argc < 3) { + printf("usage: %s [QoS level]\n", + argv[0]); + return 1; + } + if (argc == 4) { + qos = get_qos(argv[3]); + } + MQTTMessage message; + message.qos = qos; + message.retained = IS_RETAINED_MSG; + message.payload = argv[2]; + message.payloadlen = strlen(message.payload); + + int rc; + if ((rc = MQTTPublish(&client, argv[1], &message)) < 0) { + printf("mqtt_example: Unable to publish (%d)\n", rc); + } + else { + printf("mqtt_example: Message (%s) has been published to topic %s" + "with QOS %d\n", + (char *)message.payload, argv[1], (int)message.qos); + } + + return rc; +} + +static int _cmd_sub(int argc, char **argv) +{ + enum QoS qos = QOS0; + + if (argc < 2) { + printf("usage: %s [QoS level]\n", argv[0]); + return 1; + } + + if (argc >= 3) { + qos = get_qos(argv[2]); + } + + if (topic_cnt > MAX_TOPICS) { + printf("mqtt_example: Already subscribed to max %d topics," + "call 'unsub' command\n", topic_cnt); + return -1; + } + + if (strlen(argv[1]) > MAX_LEN_TOPIC) { + printf("mqtt_example: Not subscribing, topic too long %s\n", argv[1]); + return -1; + } + strncpy(_topic_to_subscribe[topic_cnt], argv[1], strlen(argv[1])); + + printf("mqtt_example: Subscribing to %s\n", _topic_to_subscribe[topic_cnt]); + int ret = MQTTSubscribe(&client, + _topic_to_subscribe[topic_cnt], qos, _on_msg_received); + if (ret < 0) { + printf("mqtt_example: Unable to subscribe to %s (%d)\n", + _topic_to_subscribe[topic_cnt], ret); + _cmd_discon(0, NULL); + } + else { + printf("mqtt_example: Now subscribed to %s, QOS %d\n", + argv[1], (int) qos); + topic_cnt++; + } + return ret; +} + +static int _cmd_unsub(int argc, char **argv) +{ + if (argc < 2) { + printf("usage %s \n", argv[0]); + return 1; + } + + int ret = MQTTUnsubscribe(&client, argv[1]); + + if (ret < 0) { + printf("mqtt_example: Unable to unsubscribe from topic: %s\n", argv[1]); + _cmd_discon(0, NULL); + } + else { + printf("mqtt_example: Unsubscribed from topic:%s\n", argv[1]); + topic_cnt--; + } + return ret; +} + +static const shell_command_t shell_commands[] = +{ + { "con", "connect to MQTT broker", _cmd_con }, + { "discon", "disconnect from the current broker", _cmd_discon }, + { "pub", "publish something", _cmd_pub }, + { "sub", "subscribe topic", _cmd_sub }, + { "unsub", "unsubscribe from topic", _cmd_unsub }, + { NULL, NULL, NULL } +}; + +static unsigned char buf[BUF_SIZE]; +static unsigned char readbuf[BUF_SIZE]; + +int main(void) +{ +#ifdef MODULE_LWIP + /* let LWIP initialize */ + xtimer_sleep(1); +#endif + + NetworkInit(&network); + + MQTTClientInit(&client, &network, COMMAND_TIMEOUT_MS, buf, BUF_SIZE, + readbuf, + BUF_SIZE); + printf("Running mqtt paho example. Type help for commands info\n"); + + MQTTStartTask(&client); + + char line_buf[SHELL_DEFAULT_BUFSIZE]; + shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); + return 0; +}