
3 changed files with 420 additions and 0 deletions
@ -0,0 +1,44 @@
|
||||
# name of your application
|
||||
APPLICATION = emcute
|
||||
|
||||
# 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)/../..
|
||||
|
||||
BOARD_INSUFFICIENT_MEMORY := arduino-duemilanove arduino-mega2560 arduino-uno \
|
||||
chronos msb-430 msb-430h nucleo-f030 nucleo-f070 \
|
||||
nucleo-f072 nucleo-f334 nucleo32-f031 nucleo32-f303 \
|
||||
nucleo32-f042 stm32f0discovery telosb \
|
||||
waspmote-pro weio wsn430-v1_3b wsn430-v1_4 z1
|
||||
|
||||
# Include packages that pull up and auto-init the link layer.
|
||||
# NOTE: 6LoWPAN will be included if IEEE802.15.4 devices are present
|
||||
USEMODULE += gnrc_netdev_default
|
||||
USEMODULE += auto_init_gnrc_netif
|
||||
# Specify the mandatory networking modules for IPv6 and UDP
|
||||
USEMODULE += gnrc_sock_udp
|
||||
USEMODULE += gnrc_ipv6_default
|
||||
# Include MQTT-SN
|
||||
USEMODULE += emcute
|
||||
# Add also the shell, some shell commands
|
||||
USEMODULE += shell
|
||||
USEMODULE += shell_commands
|
||||
USEMODULE += ps
|
||||
# For testing we also include the ping6 command and some stats
|
||||
USEMODULE += gnrc_icmpv6_echo
|
||||
|
||||
# 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:
|
||||
CFLAGS += -DDEVELHELP
|
||||
|
||||
# Comment this out to join RPL DODAGs even if DIOs do not contain
|
||||
# DODAG Configuration Options (see the doc for more info)
|
||||
# CFLAGS += -DGNRC_RPL_DODAG_CONF_OPTIONAL_ON_JOIN
|
||||
|
||||
# Change this to 0 show compiler invocation lines by default:
|
||||
QUIET ?= 1
|
||||
|
||||
include $(RIOTBASE)/Makefile.include |
@ -0,0 +1,97 @@
|
||||
## About |
||||
This application demonstrates the usage of the emCute (MQTT-SN) module in RIOT. |
||||
|
||||
## Setup |
||||
For using this example, two prerequisites have to be fullfilled: |
||||
|
||||
1. You need a running MQTT broker that supports MQTT-SN or a running MQTT-SN |
||||
gateway that is connected to a running MQTT broker |
||||
2. Your RIOT node needs to be able to speak to that broker/gateway |
||||
|
||||
|
||||
### Setting up a broker |
||||
In general, any MQTT-SN capable broker or broker/gateway setup will do. |
||||
Following a quick instruction on how-to setup the Mosquitto Real Simple Message |
||||
Broker: |
||||
|
||||
1. Get the RSMB here: https://github.com/eclipse/mosquitto.rsmb: |
||||
``` |
||||
git clone https://github.com/eclipse/mosquitto.rsmb.git |
||||
``` |
||||
|
||||
2. Go into the source folder and build the RSMB |
||||
``` |
||||
cd mosquitto.rsmb/rsmb/src |
||||
make |
||||
``` |
||||
|
||||
3. Create a config file. In this case we run the RSMB as MQTT and MQTT-SN |
||||
capable broker, using port 1885 for MQTT-SN and 1886 for MQTT and enabling |
||||
IPv6, so save the following to `config.conf`: |
||||
``` |
||||
# add some debug output |
||||
trace_output protocol |
||||
|
||||
# listen for MQTT-SN traffic on UDP port 1885 |
||||
listener 1885 INADDR_ANY mqtts |
||||
ipv6 true |
||||
|
||||
# listen to MQTT connections on tcp port 1886 |
||||
listener 1886 INADDR_ANY |
||||
ipv6 true |
||||
``` |
||||
|
||||
4. Start the broker: |
||||
``` |
||||
./broker_mqtts config.conf |
||||
``` |
||||
|
||||
You can refer to |
||||
https://rawgit.com/MichalFoksa/rsmb/master/rsmb/doc/gettingstarted.htm for more |
||||
configuration options. |
||||
|
||||
|
||||
### Setting up RIOT `native` |
||||
When running this example under native, we must configure some global addresses, |
||||
as the RSMB doesn't seems to be able to handle link-local addresses. So for a |
||||
single RIOT native instance, we can do the following: |
||||
|
||||
1. Setup `tap` and `tabbr` devices using RIOT's `tapsetup` script: |
||||
``` |
||||
./RIOTDIR/dist/tools/tapsetup/tapsetup |
||||
``` |
||||
|
||||
2. Assign a site-global prefix to the `tabbr0` interface (the name could be |
||||
different on OSX etc): |
||||
``` |
||||
sudo ip a a fec0:affe::1/64 dev tapbr0 |
||||
``` |
||||
|
||||
3. Assign a site-global address with the same prefix to the RIOT `native` |
||||
instance: |
||||
``` |
||||
ifconfig 5 add fec0:affe::99 |
||||
``` |
||||
|
||||
|
||||
## Usage |
||||
This example maps all available MQTT-SN functions to shell commands. Simply type |
||||
`help` to see the available commands. The most important steps are explained |
||||
below: |
||||
|
||||
- To connect to a broker, use the `con` command: |
||||
``` |
||||
con fec0:affe::1 1885 |
||||
``` |
||||
|
||||
- To subscribe to a topic, run `sub` with the topic name as parameter, e.g. |
||||
``` |
||||
sub hello/world |
||||
``` |
||||
|
||||
- For publishing, use the `pub` command: |
||||
``` |
||||
pub hello/world "One more beer, please." |
||||
``` |
||||
|
||||
That's it, happy publishing! |
@ -0,0 +1,279 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Freie Universität Berlin |
||||
* |
||||
* 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 |
||||
* @brief Example application for demonstrating the RIOT network stack |
||||
* |
||||
* @author Hauke Petersen <hauke.petersen@fu-berlin.de> |
||||
* |
||||
* @} |
||||
*/ |
||||
|
||||
#include <stdio.h> |
||||
#include <string.h> |
||||
#include <stdlib.h> |
||||
|
||||
#include "shell.h" |
||||
#include "msg.h" |
||||
#include "net/emcute.h" |
||||
#include "net/ipv6/addr.h" |
||||
|
||||
#define EMCUTE_PORT (1883U) |
||||
#define EMCUTE_ID ("gertrud") |
||||
#define EMCUTE_PRIO (THREAD_PRIORITY_MAIN - 1) |
||||
|
||||
#define NUMOFSUBS (16U) |
||||
#define TOPIC_MAXLEN (64U) |
||||
|
||||
static char stack[THREAD_STACKSIZE_DEFAULT]; |
||||
static msg_t queue[8]; |
||||
|
||||
static emcute_sub_t subscriptions[NUMOFSUBS]; |
||||
static char topics[NUMOFSUBS][TOPIC_MAXLEN]; |
||||
|
||||
static void *emcute_thread(void *arg) |
||||
{ |
||||
(void)arg; |
||||
emcute_run(EMCUTE_PORT, EMCUTE_ID); |
||||
return NULL; /* should never be reached */ |
||||
} |
||||
|
||||
static void on_pub(const emcute_topic_t *topic, void *data, size_t len) |
||||
{ |
||||
char *in = (char *)data; |
||||
|
||||
printf("### got publication for topic '%s' [%i] ###\n", |
||||
topic->name, (int)topic->id); |
||||
for (size_t i = 0; i < len; i++) { |
||||
printf("%c", in[i]); |
||||
} |
||||
puts(""); |
||||
} |
||||
|
||||
static unsigned get_qos(const char *str) |
||||
{ |
||||
int qos = atoi(str); |
||||
switch (qos) { |
||||
case 1: return EMCUTE_QOS_1; |
||||
case 2: return EMCUTE_QOS_2; |
||||
default: return EMCUTE_QOS_0; |
||||
} |
||||
} |
||||
|
||||
static int cmd_con(int argc, char **argv) |
||||
{ |
||||
sock_udp_ep_t gw = { .family = AF_INET6, .port = EMCUTE_PORT }; |
||||
char *topic = NULL; |
||||
char *message = NULL; |
||||
size_t len = 0; |
||||
|
||||
if (argc < 2) { |
||||
printf("usage: %s <ipv6 addr> [port] [<will topic> <will message>]\n", |
||||
argv[0]); |
||||
return 1; |
||||
} |
||||
|
||||
/* parse address */ |
||||
if (ipv6_addr_from_str((ipv6_addr_t *)&gw.addr.ipv6, argv[1]) == NULL) { |
||||
printf("error parsing IPv6 address\n"); |
||||
return 1; |
||||
} |
||||
|
||||
if (argc >= 3) { |
||||
gw.port = (uint16_t)atoi(argv[2]); |
||||
} |
||||
if (argc >= 5) { |
||||
topic = argv[3]; |
||||
message = argv[4]; |
||||
len = strlen(message); |
||||
} |
||||
|
||||
if (emcute_con(&gw, true, topic, message, len, 0) != EMCUTE_OK) { |
||||
printf("error: unable to connect to [%s]:%i\n", argv[1], (int)gw.port); |
||||
} |
||||
printf("Successfully connected to gateway at [%s]:%i\n", |
||||
argv[1], (int)gw.port); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int cmd_discon(int argc, char **argv) |
||||
{ |
||||
(void)argc; |
||||
(void)argv; |
||||
|
||||
int res = emcute_discon(); |
||||
if (res == EMCUTE_NOGW) { |
||||
puts("error: not connected to any broker"); |
||||
return 1; |
||||
} |
||||
else if (res != EMCUTE_OK) { |
||||
puts("error: unable to disconnect"); |
||||
return 1; |
||||
} |
||||
puts("Disconnect successful"); |
||||
return 0; |
||||
} |
||||
|
||||
static int cmd_pub(int argc, char **argv) |
||||
{ |
||||
emcute_topic_t t; |
||||
unsigned flags = EMCUTE_QOS_0; |
||||
|
||||
if (argc < 3) { |
||||
printf("usage: %s <topic name> <data> [QoS level]\n", argv[0]); |
||||
return 1; |
||||
} |
||||
|
||||
/* parse QoS level */ |
||||
if (argc >= 4) { |
||||
flags |= get_qos(argv[3]); |
||||
} |
||||
|
||||
printf("pub with topic: %s and name %s and flags 0x%02x\n", argv[1], argv[2], (int)flags); |
||||
|
||||
/* step 1: get topic id */ |
||||
t.name = argv[1]; |
||||
if (emcute_reg(&t) != EMCUTE_OK) { |
||||
puts("error: unable to obtain topic ID"); |
||||
return 1; |
||||
} |
||||
|
||||
/* step 2: publish data */ |
||||
if (emcute_pub(&t, argv[2], strlen(argv[2]), flags) != EMCUTE_OK) { |
||||
printf("error: unable to publish data to topic '%s [%i]'\n", |
||||
t.name, (int)t.id); |
||||
return 1; |
||||
} |
||||
|
||||
printf("Published %i bytes to topic '%s [%i]'\n", |
||||
(int)strlen(argv[2]), t.name, t.id); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int cmd_sub(int argc, char **argv) |
||||
{ |
||||
unsigned flags = EMCUTE_QOS_0; |
||||
|
||||
if (argc < 2) { |
||||
printf("usage: %s <topic name> [QoS level]\n", argv[0]); |
||||
return 1; |
||||
} |
||||
|
||||
if (strlen(argv[1]) > TOPIC_MAXLEN) { |
||||
puts("error: topic name exceeds maximum possible size"); |
||||
return 1; |
||||
} |
||||
if (argc >= 3) { |
||||
flags |= get_qos(argv[2]); |
||||
} |
||||
|
||||
/* find empty subscription slot */ |
||||
unsigned i = 0; |
||||
for (; (i < NUMOFSUBS) && (subscriptions[i].topic.id != 0); i++) {} |
||||
if (i == NUMOFSUBS) { |
||||
puts("error: no memory to store new subscriptions"); |
||||
return 1; |
||||
} |
||||
|
||||
subscriptions[i].cb = on_pub; |
||||
strcpy(topics[i], argv[1]); |
||||
subscriptions[i].topic.name = topics[i]; |
||||
if (emcute_sub(&subscriptions[i], flags) != EMCUTE_OK) { |
||||
printf("error: unable to subscribe to %s\n", argv[1]); |
||||
return 1; |
||||
} |
||||
|
||||
printf("Now subscribed to %s\n", argv[1]); |
||||
return 0; |
||||
} |
||||
|
||||
static int cmd_unsub(int argc, char **argv) |
||||
{ |
||||
if (argc < 2) { |
||||
printf("usage %s <topic name>\n", argv[0]); |
||||
return 1; |
||||
} |
||||
|
||||
/* find subscriptions entry */ |
||||
for (unsigned i = 0; i < NUMOFSUBS; i++) { |
||||
if (subscriptions[i].topic.name && |
||||
(strcmp(subscriptions[i].topic.name, argv[1]) == 0)) { |
||||
if (emcute_unsub(&subscriptions[i]) == EMCUTE_OK) { |
||||
memset(&subscriptions[i], 0, sizeof(emcute_sub_t)); |
||||
printf("Unsubscribed from '%s'\n", argv[1]); |
||||
} |
||||
else { |
||||
printf("Unsubscription form '%s' failed\n", argv[1]); |
||||
} |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
printf("error: no subscription for topic '%s' found\n", argv[1]); |
||||
return 1; |
||||
} |
||||
|
||||
static int cmd_will(int argc, char **argv) |
||||
{ |
||||
if (argc < 3) { |
||||
printf("usage %s <will topic name> <will message content>\n", argv[0]); |
||||
return 1; |
||||
} |
||||
|
||||
if (emcute_willupd_topic(argv[1], 0) != EMCUTE_OK) { |
||||
puts("error: unable to update the last will topic"); |
||||
return 1; |
||||
} |
||||
if (emcute_willupd_msg(argv[2], strlen(argv[2])) != EMCUTE_OK) { |
||||
puts("error: unable to update the last will message"); |
||||
return 1; |
||||
} |
||||
|
||||
puts("Successfully updated last will topic and message"); |
||||
return 0; |
||||
} |
||||
|
||||
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 }, |
||||
{ "will", "register a last will", cmd_will }, |
||||
{ NULL, NULL, NULL } |
||||
}; |
||||
|
||||
int main(void) |
||||
{ |
||||
puts("MQTT-SN example application\n"); |
||||
puts("Type 'help' to get started. Have a look at the README.md for more" |
||||
"information."); |
||||
|
||||
/* the main thread needs a msg queue to be able to run `ping6`*/ |
||||
msg_init_queue(queue, (sizeof(queue) / sizeof(msg_t))); |
||||
|
||||
/* initialize our subscription buffers */ |
||||
memset(subscriptions, 0, (NUMOFSUBS * sizeof(emcute_sub_t))); |
||||
|
||||
/* start the emcute thread */ |
||||
thread_create(stack, sizeof(stack), EMCUTE_PRIO, 0, |
||||
emcute_thread, NULL, "emcute"); |
||||
|
||||
/* start shell */ |
||||
char line_buf[SHELL_DEFAULT_BUFSIZE]; |
||||
shell_run(shell_commands, line_buf, SHELL_DEFAULT_BUFSIZE); |
||||
|
||||
/* should be never reached */ |
||||
return 0; |
||||
} |
Loading…
Reference in new issue