From 38481c513d3f7222a738973ccbbd17e012f0a898 Mon Sep 17 00:00:00 2001 From: Wojciech Bober Date: Sat, 9 Jan 2016 14:44:58 +0100 Subject: [PATCH] nrf52dk: added examples --- examples/nrf52dk/blink-hello/Makefile | 9 + examples/nrf52dk/blink-hello/README.md | 14 + examples/nrf52dk/blink-hello/blink-hello.c | 182 +++++ examples/nrf52dk/coap-demo/Makefile | 35 + examples/nrf52dk/coap-demo/README.md | 63 ++ examples/nrf52dk/coap-demo/coap-client.c | 185 +++++ examples/nrf52dk/coap-demo/coap-server.c | 130 ++++ examples/nrf52dk/coap-demo/project-conf.h | 85 +++ .../nrf52dk/coap-demo/resources/res-leds.c | 106 +++ examples/nrf52dk/mqtt-demo/Makefile | 11 + examples/nrf52dk/mqtt-demo/README.md | 74 ++ examples/nrf52dk/mqtt-demo/mqtt-demo.c | 721 ++++++++++++++++++ examples/nrf52dk/mqtt-demo/project-conf.h | 51 ++ examples/nrf52dk/timer-test/Makefile | 9 + examples/nrf52dk/timer-test/README.md | 20 + examples/nrf52dk/timer-test/timer-test.c | 131 ++++ 16 files changed, 1826 insertions(+) create mode 100644 examples/nrf52dk/blink-hello/Makefile create mode 100644 examples/nrf52dk/blink-hello/README.md create mode 100644 examples/nrf52dk/blink-hello/blink-hello.c create mode 100644 examples/nrf52dk/coap-demo/Makefile create mode 100644 examples/nrf52dk/coap-demo/README.md create mode 100644 examples/nrf52dk/coap-demo/coap-client.c create mode 100644 examples/nrf52dk/coap-demo/coap-server.c create mode 100644 examples/nrf52dk/coap-demo/project-conf.h create mode 100644 examples/nrf52dk/coap-demo/resources/res-leds.c create mode 100644 examples/nrf52dk/mqtt-demo/Makefile create mode 100644 examples/nrf52dk/mqtt-demo/README.md create mode 100644 examples/nrf52dk/mqtt-demo/mqtt-demo.c create mode 100644 examples/nrf52dk/mqtt-demo/project-conf.h create mode 100644 examples/nrf52dk/timer-test/Makefile create mode 100644 examples/nrf52dk/timer-test/README.md create mode 100644 examples/nrf52dk/timer-test/timer-test.c diff --git a/examples/nrf52dk/blink-hello/Makefile b/examples/nrf52dk/blink-hello/Makefile new file mode 100644 index 000000000..5b056a518 --- /dev/null +++ b/examples/nrf52dk/blink-hello/Makefile @@ -0,0 +1,9 @@ +CONTIKI_PROJECT = blink-hello + +CONTIKI_WITH_RPL=0 +NRF52_WITHOUT_SOFTDEVICE=1 + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. +include $(CONTIKI)/Makefile.include diff --git a/examples/nrf52dk/blink-hello/README.md b/examples/nrf52dk/blink-hello/README.md new file mode 100644 index 000000000..00064f993 --- /dev/null +++ b/examples/nrf52dk/blink-hello/README.md @@ -0,0 +1,14 @@ +Blink Hello example +=================== +This example shows basic usage of DK's buttons and LEDs. It also shows basic +usage of Contiki's processes. The application autostarts 5 processes: 4 processes +for button and LED control and 1 to display current temperature to the console. + +A process reacts to a press of a respective button (process 1 reacts to button 1, etc.) +and doubles the current blinking frequency. The cycle restarts for beginning when blinking +frequency is greater than 8Hz. + +The example requires one DK and it doesn't use SoftDevice. To compile and flash the +example run: + + make TARGET=nrf52dk blink-hello.flash \ No newline at end of file diff --git a/examples/nrf52dk/blink-hello/blink-hello.c b/examples/nrf52dk/blink-hello/blink-hello.c new file mode 100644 index 000000000..ebc1e451c --- /dev/null +++ b/examples/nrf52dk/blink-hello/blink-hello.c @@ -0,0 +1,182 @@ +/* This is a very simple hello_world program. + * It aims to demonstrate the co-existence of two processes: + * One of them prints a hello world message and the other blinks the LEDs + * + * It is largely based on hello_world in $(CONTIKI)/examples/sensinode + * + * Author: George Oikonomou - + */ + +/** + * \addtogroup nrf52dk nRF52 Development Kit + * @{ + * + * \addtogroup nrf52dk-examples Demo projects for nRF52 DK + * @{ + * + * \defgroup nrf52dk-blink-hello Basic sensors and LEDs demo + * @{ + * + * This demo demonstrates use of Contiki processes, sensors, and LEDs + * on nRF52 DK. Pressing a button will start a timer that blinks a + * respective LED (e.g., button 1 controls LED 1). Each time the button + * is pressed blinking frequency is doubled. On 4th press the LED is + * switched off and the sequence can be started from the beginning. + * + * \file Main file for Basic sensors and LEDs demo. + */ +#include /* For printf() */ +#include +#include "contiki.h" +#include "dev/leds.h" +#include "dev/temperature-sensor.h" +#include "lib/sensors.h" +#include "button-sensor.h" + +/*---------------------------------------------------------------------------*/ +PROCESS(blink_process_1, "LED1 blink process"); +PROCESS(blink_process_2, "LED2 blink process"); +PROCESS(blink_process_3, "LED3 blink process"); +PROCESS(blink_process_4, "LED4 blink process"); +PROCESS(temp, "Temperautre"); + +AUTOSTART_PROCESSES( + &blink_process_1, + &blink_process_2, + &blink_process_3, + &blink_process_4, + &temp +); + +struct blink_process_ctx { + struct etimer et_blink; + unsigned char c; + const struct sensors_sensor *button; + unsigned char led; +}; + +static void handle_event(process_event_t ev, process_data_t data, struct blink_process_ctx *ctx) +{ + if (ev == PROCESS_EVENT_TIMER && etimer_expired(&ctx->et_blink)) { + leds_toggle(ctx->led); + etimer_set(&ctx->et_blink, CLOCK_SECOND / ctx->c); + printf("Blink %d\n", ctx->led); + } else if (ev == sensors_event && data == ctx->button) { + if (ctx->button->value(BUTTON_SENSOR_VALUE_STATE) == 0) { + if (ctx->c == 0) { + ctx->c = 1; + } else if (ctx->c < 8){ + ctx->c <<= 1; + } else { + ctx->c = 0; + leds_off(ctx->led); + } + if (ctx->c) { + etimer_set(&ctx->et_blink, CLOCK_SECOND / ctx->c); + } else { + etimer_stop(&ctx->et_blink); + } + } + } +} + +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(blink_process_1, ev, data) +{ + static struct blink_process_ctx ctx; + + PROCESS_BEGIN(); + + ctx.button = &button_1; + ctx.c = 0; + ctx.led = LEDS_1; + ctx.button->configure(SENSORS_ACTIVE, 1); + + while (1) { + PROCESS_WAIT_EVENT(); + handle_event(ev, data, &ctx); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(blink_process_2, ev, data) +{ + static struct blink_process_ctx ctx; + + PROCESS_BEGIN(); + + ctx.button = &button_2; + ctx.c = 0; + ctx.led = LEDS_2; + ctx.button->configure(SENSORS_ACTIVE, 1); + + while (1) { + PROCESS_WAIT_EVENT(); + handle_event(ev, data, &ctx); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(blink_process_3, ev, data) +{ + static struct blink_process_ctx ctx; + + PROCESS_BEGIN(); + + ctx.button = &button_3; + ctx.c = 0; + ctx.led = LEDS_3; + ctx.button->configure(SENSORS_ACTIVE, 1); + + while (1) { + PROCESS_WAIT_EVENT(); + handle_event(ev, data, &ctx); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(blink_process_4, ev, data) +{ + static struct blink_process_ctx ctx; + + PROCESS_BEGIN(); + + ctx.button = &button_4; + ctx.c = 0; + ctx.led = LEDS_4; + ctx.button->configure(SENSORS_ACTIVE, 1); + + while (1) { + PROCESS_WAIT_EVENT(); + handle_event(ev, data, &ctx); + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(temp, ev, data) +{ + static struct etimer tick; + PROCESS_BEGIN(); + + etimer_set(&tick, CLOCK_SECOND); + + while (1) { + PROCESS_WAIT_EVENT(); + if (ev == PROCESS_EVENT_TIMER && etimer_expired(&tick)) { + int32_t temp = temperature_sensor.value(0); + printf("temp: %"PRId32".%02"PRId32"\n", temp >> 2, (temp & 0x03)*25); + etimer_reset(&tick); + } + } + + PROCESS_END(); +} +/** + * @} + * @} + * @} + */ diff --git a/examples/nrf52dk/coap-demo/Makefile b/examples/nrf52dk/coap-demo/Makefile new file mode 100644 index 000000000..077e469be --- /dev/null +++ b/examples/nrf52dk/coap-demo/Makefile @@ -0,0 +1,35 @@ +CONTIKI=../../.. +CFLAGS += -DPROJECT_CONF_H=\"project-conf.h\" + +ifeq ($(MAKECMDGOALS),) +$(error Please specify whether coap-client or coap-server should be built) +endif + +ifneq ($(filter coap-client coap-client.flash, $(MAKECMDGOALS)),) +ifeq ($(SERVER_IPV6_ADDR),) +$(error Please define SERVER_IPV6_ADDR=) +else +CFLAGS += -DSERVER_IPV6_ADDR=\"$(SERVER_IPV6_ADDR)\" +CFLAGS += -DDEVICE_NAME=\"nRF52_DK_CoAP_Client\" +endif +else +CFLAGS += -DDEVICE_NAME=\"nRF52-DK-CoAP-Server\" +endif + +# automatically build RESTful resources +REST_RESOURCES_DIR = ./resources +REST_RESOURCES_FILES = $(notdir $(shell find $(REST_RESOURCES_DIR) -name '*.c' ! -name 'res-plugtest*')) + +PROJECTDIRS += $(REST_RESOURCES_DIR) +PROJECT_SOURCEFILES += $(REST_RESOURCES_FILES) + +# linker optimizations +SMALL=1 + +# REST Engine shall use Erbium CoAP implementation +APPS += er-coap +APPS += rest-engine + +CONTIKI_WITH_RPL = 0 + +include $(CONTIKI)/Makefile.include diff --git a/examples/nrf52dk/coap-demo/README.md b/examples/nrf52dk/coap-demo/README.md new file mode 100644 index 000000000..1262ce114 --- /dev/null +++ b/examples/nrf52dk/coap-demo/README.md @@ -0,0 +1,63 @@ +A CoAP demo for nRF52 DK +======================== +This demo contains two applications: coap-server and coap-client which are similar to +[Coap Observable Server] and [Coap Observer Client] examples provided by the nRF5 IoT SDK. + +Note that before any CoAP requests can be made you'll need to configure an IPv6 connection +to the device and assign a routable IPv6 address. + +For details how to do this please refer to sections 'Establishing an IPv6 connection' +and 'Distributing routable IPv6 prefix' in `platform/nrf52dk/README-BLE-6LoWPAN.md`. + +CoAP Server +=========== +The server exposes the following resources: + + host + |-- .well-known + | `-- core + `-- lights + `-- led3 + +The state of LED 3 can be set and queried via CoAP through the observable resource `lights/led3`. Current +state of LED 3 is returned as a text string in the payload. The value 0 means that LED 3 is off, 1 otherwise. + +Button 1 can be used to toggle state of the LED 3. This will cause a notification to be sent to +any subscriber observing `lights/led3`. The state of the resource can also be changed by using POST/PUT to +the resource with desired state encoded as a text in the payload. Send 1 to switch on and 0 to switch off. + +In order to compile and flash the CoAP server to a DK execute: + + make TARGET=nrf52dk coap-server.flash + +Note, if you haven't previously used a given device with Contiki it is recommended +to erase the device and flash SoftDevice before flashing CoAP application, i.e., + + make TARGET=nrf52dk erase + make TARGET=nrf52dk softdevice.flash + +Please refer to the *Testing* and *Python Example* sections of [Coap Observable Server] tutorial for detailed description how to query the Coap Server using a PC. + +CoAP Client +=========== +CoAP client compliments the CoAP server application. When Button 1 on the DK is pressed the the +client subscribes to `lights/led3` resource. If successful the LED 4 will blink briefly. From this moment +any change of the `lights/led3` resource will be automatically reflected by the client's LED 3. + +Note that the client must know the server's IPv6 address. The address is specified as a make variable +during compliation. + +In order to compile and flash the CoAP client to DK execute: + + make TARGET=nrf52dk SERVER_IPV6_ADDR= coap-client.flash + +Note, that you can use `NRF52_JLINK_SN=` to select a particular devkit in a case when +you have more than one boards connected to PC. Please refer to `platform/README.md` for +details. + +Please refer to the *Testing* and *Python Server Example* sections of [Coap Observer Client] tutorial for detailed description how to use CoAP client demo with a PC. + +Resources +========= +[Coap Observable Server] http://developer.nordicsemi.com/nRF5_IoT_SDK/doc/0.9.0/html/a00054.html +[Coap Observer Client] http://developer.nordicsemi.com/nRF5_IoT_SDK/doc/0.9.0/html/a00051.html diff --git a/examples/nrf52dk/coap-demo/coap-client.c b/examples/nrf52dk/coap-demo/coap-client.c new file mode 100644 index 000000000..2ec1f5ab1 --- /dev/null +++ b/examples/nrf52dk/coap-demo/coap-client.c @@ -0,0 +1,185 @@ +/* + * Copyright (c) 2014, Daniele Alessandrelli. + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \addtogroup nrf52dk-examples Demo projects for nRF52 DK + * @{ + * + * \defgroup nrf52dk-coap-demo CoAP demo for nRF52 DK + * @{ + * + * \file + * Erbium (Er) CoAP observe client example. + * \author + * Daniele Alessandrelli + * \author + * Wojciech Bober + */ + +#include +#include +#include +#include "contiki.h" +#include "contiki-net.h" +#include "er-coap-engine.h" +#include "dev/button-sensor.h" +#include "dev/leds.h" + +#define DEBUG 0 +#if DEBUG +#include +#define PRINTF(...) printf(__VA_ARGS__) +#else +#define PRINTF(...) +#endif + +/*----------------------------------------------------------------------------*/ +#define REMOTE_PORT UIP_HTONS(COAP_DEFAULT_PORT) +#define OBS_RESOURCE_URI "lights/led3" +#define SUBS_LED LEDS_4 +#define OBS_LED LEDS_3 + +/*----------------------------------------------------------------------------*/ +static uip_ipaddr_t server_ipaddr[1]; /* holds the server ip address */ +static coap_observee_t *obs; +static struct ctimer ct; +/*----------------------------------------------------------------------------*/ +PROCESS(er_example_observe_client, "nRF52 DK Coap Observer Client"); +AUTOSTART_PROCESSES(&er_example_observe_client); +/*----------------------------------------------------------------------------*/ +static void +observe_led_off(void *d) +{ + leds_off(SUBS_LED); +} +/*----------------------------------------------------------------------------*/ +/* + * Handle the response to the observe request and the following notifications + */ +static void +notification_callback(coap_observee_t *obs, void *notification, + coap_notification_flag_t flag) +{ + int len = 0; + const uint8_t *payload = NULL; + + + PRINTF("Notification handler\n"); + PRINTF("Observee URI: %s\n", obs->url); + + if (notification) { + len = coap_get_payload(notification, &payload); + } + + (void)len; + + switch (flag) { + case NOTIFICATION_OK: + PRINTF("NOTIFICATION OK: %*s\n", len, (char *)payload); + if (*payload == '1') { + leds_on(OBS_LED); + } else { + leds_off(OBS_LED); + } + break; + case OBSERVE_OK: /* server accepeted observation request */ + PRINTF("OBSERVE_OK: %*s\n", len, (char *)payload); + if (*payload == '1') { + leds_on(OBS_LED); + } else { + leds_off(OBS_LED); + } + leds_on(SUBS_LED); + ctimer_set(&ct, CLOCK_SECOND, observe_led_off, NULL); + break; + case OBSERVE_NOT_SUPPORTED: + PRINTF("OBSERVE_NOT_SUPPORTED: %*s\n", len, (char *)payload); + obs = NULL; + break; + case ERROR_RESPONSE_CODE: + PRINTF("ERROR_RESPONSE_CODE: %*s\n", len, (char *)payload); + obs = NULL; + break; + case NO_REPLY_FROM_SERVER: + PRINTF("NO_REPLY_FROM_SERVER: " + "removing observe registration with token %x%x\n", + obs->token[0], obs->token[1]); + obs = NULL; + break; + } +} +/*----------------------------------------------------------------------------*/ +/* + * The main (proto-)thread. It starts/stops the observation of the remote + * resource every time the timer elapses or the button (if available) is + * pressed + */ +PROCESS_THREAD(er_example_observe_client, ev, data) +{ + PROCESS_BEGIN(); + + uiplib_ipaddrconv(SERVER_IPV6_ADDR, server_ipaddr); + + /* receives all CoAP messages */ + coap_init_engine(); + +#if PLATFORM_HAS_BUTTON + SENSORS_ACTIVATE(button_1); + SENSORS_ACTIVATE(button_2); +#endif + + /* toggle observation every time the timer elapses or the button is pressed */ + while (1) { + PROCESS_YIELD(); +#if PLATFORM_HAS_BUTTON + if (ev == sensors_event) { + if (data == &button_1 && button_1.value(BUTTON_SENSOR_VALUE_STATE) == 0) { + PRINTF("Starting observation\n"); + obs = coap_obs_request_registration(server_ipaddr, REMOTE_PORT, + OBS_RESOURCE_URI, notification_callback, + NULL); + } + if (data == &button_2 && button_2.value(BUTTON_SENSOR_VALUE_STATE) == 0) { + PRINTF("Stopping observation\n"); + coap_obs_remove_observee(obs); + obs = NULL; + } + } +#endif + } + PROCESS_END(); +} + +/** + * @} + * @} + */ diff --git a/examples/nrf52dk/coap-demo/coap-server.c b/examples/nrf52dk/coap-demo/coap-server.c new file mode 100644 index 000000000..9bab7e60d --- /dev/null +++ b/examples/nrf52dk/coap-demo/coap-server.c @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * Copyright (c) 2015, Nordic Semiconductor + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \addtogroup nrf52dk-examples Demo projects for nRF52 DK + * @{ + * + * \defgroup nrf52dk-coap-demo CoAP demo for nRF52dk + * @{ + * + * \file + * Erbium (Er) REST Engine example. + * \author + * Matthias Kovatsch + * \author + * Wojciech Bober + */ + +#include +#include +#include +#include "contiki.h" +#include "contiki-net.h" +#include "rest-engine.h" +#include "uip.h" +#include "dev/button-sensor.h" +#include "dev/leds.h" + +#define DEBUG DEBUG_PRINT +#include "net/ip/uip-debug.h" + +/* + * Resources to be activated need to be imported through the extern keyword. + * The build system automatically compiles the resources in the corresponding sub-directory. + */ +extern resource_t res_led3; + +static void +print_local_addresses(void) +{ + int i; + uint8_t state; + + PRINTF("Server IPv6 addresses:\n"); + for(i = 0; i < UIP_DS6_ADDR_NB; i++) { + state = uip_ds6_if.addr_list[i].state; + if(uip_ds6_if.addr_list[i].isused && (state == ADDR_TENTATIVE || state + == ADDR_PREFERRED)) { + PRINTF(" "); + PRINT6ADDR(&uip_ds6_if.addr_list[i].ipaddr); + PRINTF("\n"); + if(state == ADDR_TENTATIVE) { + uip_ds6_if.addr_list[i].state = ADDR_PREFERRED; + } + } + } +} + +PROCESS(er_example_server, "nRF52 DK Coap Server"); +AUTOSTART_PROCESSES(&er_example_server); + +PROCESS_THREAD(er_example_server, ev, data) +{ + PROCESS_BEGIN(); + PROCESS_PAUSE(); + + PRINTF("Starting Erbium Example Server\n"); + + PRINTF("uIP buffer: %u\n", UIP_BUFSIZE); + PRINTF("LL header: %u\n", UIP_LLH_LEN); + PRINTF("IP+UDP header: %u\n", UIP_IPUDPH_LEN); + PRINTF("REST max chunk: %u\n", REST_MAX_CHUNK_SIZE); + + print_local_addresses(); + + /* Initialize the REST engine. */ + rest_init_engine(); + rest_activate_resource(&res_led3, "lights/led3"); + + SENSORS_ACTIVATE(button_1); + + /* Define application-specific events here. */ + while (1) { + PROCESS_WAIT_EVENT(); + + if (ev == sensors_event) { + if (data == &button_1 && button_1.value(BUTTON_SENSOR_VALUE_STATE) == 0) { + leds_toggle(LEDS_3); + res_led3.trigger(); + } + } + } + + PROCESS_END(); +} + +/** + * @} + * @} + */ diff --git a/examples/nrf52dk/coap-demo/project-conf.h b/examples/nrf52dk/coap-demo/project-conf.h new file mode 100644 index 000000000..2b097e8a8 --- /dev/null +++ b/examples/nrf52dk/coap-demo/project-conf.h @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \addtogroup nrf52dk-examples Demo projects for nRF52 DK + * @{ + * + * \defgroup nrf52dk-coap-demo CoAP demo for nRF52dk + * @{ + * + * \file + * Erbium (Er) example project configuration. + * \author + * Matthias Kovatsch + */ + +#ifndef __PROJECT_ERBIUM_CONF_H__ +#define __PROJECT_ERBIUM_CONF_H__ + +/* Disabling TCP on CoAP nodes. */ +#undef UIP_CONF_TCP +#define UIP_CONF_TCP 0 + +/* Increase rpl-border-router IP-buffer when using more than 64. */ +#undef REST_MAX_CHUNK_SIZE +#define REST_MAX_CHUNK_SIZE 48 + +/* Estimate your header size, especially when using Proxy-Uri. */ +/* + #undef COAP_MAX_HEADER_SIZE + #define COAP_MAX_HEADER_SIZE 70 + */ + +/* Multiplies with chunk size, be aware of memory constraints. */ +#undef COAP_MAX_OPEN_TRANSACTIONS +#define COAP_MAX_OPEN_TRANSACTIONS 4 + +/* Must be <= open transactions, default is COAP_MAX_OPEN_TRANSACTIONS-1. */ +/* + #undef COAP_MAX_OBSERVERS + #define COAP_MAX_OBSERVERS 2 + */ + +/* Filtering .well-known/core per query can be disabled to save space. */ +#undef COAP_LINK_FORMAT_FILTERING +#define COAP_LINK_FORMAT_FILTERING 0 +#undef COAP_PROXY_OPTION_PROCESSING +#define COAP_PROXY_OPTION_PROCESSING 0 + +/* Enable client-side support for COAP observe */ +#define COAP_OBSERVE_CLIENT 1 +#endif /* __PROJECT_ERBIUM_CONF_H__ */ + +/** + * @} + * @} + */ diff --git a/examples/nrf52dk/coap-demo/resources/res-leds.c b/examples/nrf52dk/coap-demo/resources/res-leds.c new file mode 100644 index 000000000..468a165bf --- /dev/null +++ b/examples/nrf52dk/coap-demo/resources/res-leds.c @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2013, Institute for Pervasive Computing, ETH Zurich + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * This file is part of the Contiki operating system. + */ + +/** + * \file + * Example resource + * \author + * Matthias Kovatsch + * \author + * Wojciech Bober + */ + +#include +#include "contiki.h" +#include "rest-engine.h" +#include "dev/leds.h" + +#define DEBUG DEBUG_NONE +#include "net/ip/uip-debug.h" + +static void +res_post_put_handler(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset); + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset); + +static void +res_event_handler(); + +/*A simple actuator example, depending on the color query parameter and post variable mode, corresponding led is activated or deactivated*/ +EVENT_RESOURCE(res_led3, + "title=\"LED3\"; obs", + res_get_handler, + res_post_put_handler, + res_post_put_handler, + NULL, + res_event_handler + ); + +static void +res_post_put_handler(void *request, void *response, uint8_t *buffer, + uint16_t preferred_size, int32_t *offset) +{ + const uint8_t *payload; + REST.get_request_payload(request, &payload); + + if (*payload == '0' || *payload == '1') { + if (*payload == '1') { + leds_on(LEDS_3); + } else { + leds_off(LEDS_3); + } + REST.notify_subscribers(&res_led3); + REST.set_response_status(response, REST.status.CHANGED); + } else { + REST.set_response_status(response, REST.status.BAD_REQUEST); + } +} + +static void +res_get_handler(void *request, void *response, uint8_t *buffer, uint16_t preferred_size, int32_t *offset) +{ + REST.set_header_content_type(response, REST.type.TEXT_PLAIN); + REST.set_response_payload(response, buffer, snprintf((char *)buffer, preferred_size, "%d", (leds_get() & LEDS_3) ? 1 : 0)); +} + +/* + * Additionally, res_event_handler must be implemented for each EVENT_RESOURCE. + * It is called through .trigger(), usually from the server process. + */ +static void +res_event_handler() +{ + /* Notify the registered observers which will trigger the res_get_handler to create the response. */ + REST.notify_subscribers(&res_led3); +} diff --git a/examples/nrf52dk/mqtt-demo/Makefile b/examples/nrf52dk/mqtt-demo/Makefile new file mode 100644 index 000000000..bfdeccfb7 --- /dev/null +++ b/examples/nrf52dk/mqtt-demo/Makefile @@ -0,0 +1,11 @@ +DEFINES+=PROJECT_CONF_H=\"project-conf.h\" + +all: mqtt-demo + +CONTIKI_WITH_IPV6 = 1 +CONTIKI_WITH_RPL = 0 + +APPS += mqtt + +CONTIKI=../../.. +include $(CONTIKI)/Makefile.include diff --git a/examples/nrf52dk/mqtt-demo/README.md b/examples/nrf52dk/mqtt-demo/README.md new file mode 100644 index 000000000..a4a9a6ff1 --- /dev/null +++ b/examples/nrf52dk/mqtt-demo/README.md @@ -0,0 +1,74 @@ +MQTT Demo +========= +The MQTT client can be used to: + +* Publish sensor readings to an MQTT broker. +* Subscribe to a topic and receive commands from an MQTT broker + +The demo will give some visual feedback with the green LED: +* Very fast blinking: Searching for a network +* Fast blinking: Connecting to broker +* Slow, long blinking: Sending a publish message + +Note that before any MQTT messages can be sent or received you'll +need to configure an IPv6 connection to the device and assign a routable +IPv6 address. + +For details how to do this please refer to sections 'Establishing an IPv6 connection' +and 'Distributing routable IPv6 prefix' in `platform/nrf52dk/README-BLE-6LoWPAN.md`. + +Broker setup +------------ +By default the example will attempt to publish readings to an MQTT broker +running on the IPv6 address specified as `MQTT_DEMO_BROKER_IP_ADDR` in +`project-conf.h`. This functionality was tested successfully with +[mosquitto](http://mosquitto.org/). + +On Ubuntu you can install and run mosquitto broker using the following +commands: + + apt-get install mosquitto mosquitto_clients + killall mosquitto + mosquitto -p 1883 -v + +Publishing +---------- +The publish messages include sensor readings but also some other information, +such as device uptime in seconds and a message sequence number. The demo will +publish to topic `iot-2/evt/status/fmt/json`. The device will connect using +client-id `d:quickstart:cc2538:`, where `` gets +constructed from the device's IEEE address. + +Subscribing +----------- +You can also subscribe to topics and receive commands, but this will only +work if you use "Org ID" != 'quickstart'. To achieve this, you will need to +change 'Org ID' (`DEFAULT_ORG_ID`). In this scenario, the device will subscribe +to: + +`iot-2/cmd/+/fmt/json` + +You can then use this to toggle LEDs. To do this, you can for example +use mosquitto client to publish to `iot-2/cmd/leds/fmt/json`. So, to change +the state of an LED, you would do this: + +`mosquitto_pub -h -m "1" -t iot-2/cmd/leds/fmt/json` + +Where `broker IP` should be replaced with the IP address of your mosquitto +broker (the one where you device has subscribed). Replace `-m "1'` with `-m "0"` +to turn the LED back off. + +Bear in mind that, even though the topic suggests that messages are of json +format, they are in fact not. This was done in order to avoid linking a json +parser into the firmware. This comment only applies to parsing incoming +messages, outgoing publish messages use proper json payload. + +IBM Quickstart Service +---------------------- +It is also possible to publish to IBM's quickstart service. To do so, you need +to undefine `MQTT_DEMO_BROKER_IP_ADDR`. + +If you want to use IBM's cloud service with a registered device, change +'Org ID' (`DEFAULT_ORG_ID`) and provide the 'Auth Token' (`DEFAULT_AUTH_TOKEN`), +which acts as a 'password', but bear in mind that it gets transported in clear +text. \ No newline at end of file diff --git a/examples/nrf52dk/mqtt-demo/mqtt-demo.c b/examples/nrf52dk/mqtt-demo/mqtt-demo.c new file mode 100644 index 000000000..6a3482073 --- /dev/null +++ b/examples/nrf52dk/mqtt-demo/mqtt-demo.c @@ -0,0 +1,721 @@ +/* + * Copyright (c) 2014, Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (c) 2015, Nordic Semiconductor + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup nrf52dk-examples Demo projects for nRF52 DK + * @{ + * + * \defgroup nrf52dk-mqtt-demo nRF52 DK MQTT Demo Project + * + * Demonstrates MQTT functionality. Works with mosquitto. + * @{ + * + * \file + * An MQTT example for the nrf52dk platform + */ +/*---------------------------------------------------------------------------*/ +#include "contiki-conf.h" +#include "mqtt.h" +#include "net/ip/uip.h" +#include "net/ipv6/uip-icmp6.h" +#include "net/ipv6/sicslowpan.h" +#include "sys/etimer.h" +#include "sys/ctimer.h" +#include "lib/sensors.h" +#include "dev/button-sensor.h" +#include "dev/temperature-sensor.h" +#include "dev/leds.h" + +#include +/*---------------------------------------------------------------------------*/ +/* + * IBM server: quickstart.messaging.internetofthings.ibmcloud.com + * (184.172.124.189) mapped in an NAT64 (prefix 64:ff9b::/96) IPv6 address + * Note: If not able to connect; lookup the IP address again as it may change. + * + * Alternatively, publish to a local MQTT broker (e.g. mosquitto) running on + * the node that hosts your border router + */ +#ifdef MQTT_DEMO_BROKER_IP_ADDR +static const char *broker_ip = MQTT_DEMO_BROKER_IP_ADDR; +#define DEFAULT_ORG_ID "mqtt-demo" +#else +static const char *broker_ip = "0064:ff9b:0000:0000:0000:0000:b8ac:7cbd"; +#define DEFAULT_ORG_ID "quickstart" +#endif +/*---------------------------------------------------------------------------*/ +/* + * A timeout used when waiting for something to happen (e.g. to connect or to + * disconnect) + */ +#define STATE_MACHINE_PERIODIC (CLOCK_SECOND >> 1) +/*---------------------------------------------------------------------------*/ +/* Provide visible feedback via LEDS during various states */ +/* When connecting to broker */ +#define CONNECTING_LED_DURATION (CLOCK_SECOND >> 2) + +/* Each time we try to publish */ +#define PUBLISH_LED_ON_DURATION (CLOCK_SECOND) +/*---------------------------------------------------------------------------*/ +/* Connections and reconnections */ +#define RETRY_FOREVER 0xFF +#define RECONNECT_INTERVAL (CLOCK_SECOND * 2) + +/* + * Number of times to try reconnecting to the broker. + * Can be a limited number (e.g. 3, 10 etc) or can be set to RETRY_FOREVER + */ +#define RECONNECT_ATTEMPTS RETRY_FOREVER +#define CONNECTION_STABLE_TIME (CLOCK_SECOND * 5) +static struct timer connection_life; +static uint8_t connect_attempt; +/*---------------------------------------------------------------------------*/ +/* Various states */ +static uint8_t state; +#define STATE_INIT 0 +#define STATE_REGISTERED 1 +#define STATE_CONNECTING 2 +#define STATE_CONNECTED 3 +#define STATE_PUBLISHING 4 +#define STATE_DISCONNECTED 5 +#define STATE_NEWCONFIG 6 +#define STATE_CONFIG_ERROR 0xFE +#define STATE_ERROR 0xFF +/*---------------------------------------------------------------------------*/ +#define CONFIG_ORG_ID_LEN 32 +#define CONFIG_TYPE_ID_LEN 32 +#define CONFIG_AUTH_TOKEN_LEN 32 +#define CONFIG_EVENT_TYPE_ID_LEN 32 +#define CONFIG_CMD_TYPE_LEN 8 +#define CONFIG_IP_ADDR_STR_LEN 64 +/*---------------------------------------------------------------------------*/ +#define RSSI_MEASURE_INTERVAL_MAX 86400 /* secs: 1 day */ +#define RSSI_MEASURE_INTERVAL_MIN 5 /* secs */ +#define PUBLISH_INTERVAL_MAX 86400 /* secs: 1 day */ +#define PUBLISH_INTERVAL_MIN 5 /* secs */ +/*---------------------------------------------------------------------------*/ +/* A timeout used when waiting to connect to a network */ +#define NET_CONNECT_PERIODIC CLOCK_SECOND +#define NO_NET_LED_DURATION (NET_CONNECT_PERIODIC >> 1) +/*---------------------------------------------------------------------------*/ +/* Default configuration values */ +#define DEFAULT_TYPE_ID "nrf52dk" +#define DEFAULT_AUTH_TOKEN "AUTHZ" +#define DEFAULT_EVENT_TYPE_ID "status" +#define DEFAULT_SUBSCRIBE_CMD_TYPE "+" +#define DEFAULT_BROKER_PORT 1883 +#define DEFAULT_PUBLISH_INTERVAL (30 * CLOCK_SECOND) +#define DEFAULT_KEEP_ALIVE_TIMER 60 +#define DEFAULT_RSSI_MEAS_INTERVAL (CLOCK_SECOND * 30) +/*---------------------------------------------------------------------------*/ +/* Take a sensor reading on button press */ +#define PUBLISH_TRIGGER (&button_sensor) + +/* Payload length of ICMPv6 echo requests used to measure RSSI with def rt */ +#define ECHO_REQ_PAYLOAD_LEN 20 +/*---------------------------------------------------------------------------*/ +PROCESS_NAME(mqtt_demo_process); +AUTOSTART_PROCESSES(&mqtt_demo_process); +/*---------------------------------------------------------------------------*/ +/** + * \brief Data structure declaration for the MQTT client configuration + */ +typedef struct mqtt_client_config { + char org_id[CONFIG_ORG_ID_LEN]; + char type_id[CONFIG_TYPE_ID_LEN]; + char auth_token[CONFIG_AUTH_TOKEN_LEN]; + char event_type_id[CONFIG_EVENT_TYPE_ID_LEN]; + char broker_ip[CONFIG_IP_ADDR_STR_LEN]; + char cmd_type[CONFIG_CMD_TYPE_LEN]; + clock_time_t pub_interval; + int def_rt_ping_interval; + uint16_t broker_port; +} mqtt_client_config_t; +/*---------------------------------------------------------------------------*/ +/* Maximum TCP segment size for outgoing segments of our socket */ +#define MAX_TCP_SEGMENT_SIZE 32 +/*---------------------------------------------------------------------------*/ +#define STATUS_LED LEDS_GREEN +/*---------------------------------------------------------------------------*/ +/* + * Buffers for Client ID and Topic. + * Make sure they are large enough to hold the entire respective string + * + * d:quickstart:status:EUI64 is 32 bytes long + * iot-2/evt/status/fmt/json is 25 bytes + * We also need space for the null termination + */ +#define BUFFER_SIZE 64 +static char client_id[BUFFER_SIZE]; +static char pub_topic[BUFFER_SIZE]; +static char sub_topic[BUFFER_SIZE]; +/*---------------------------------------------------------------------------*/ +/* + * The main MQTT buffers. + * We will need to increase if we start publishing more data. + */ +#define APP_BUFFER_SIZE 128 +static struct mqtt_connection conn; +static char app_buffer[APP_BUFFER_SIZE]; +/*---------------------------------------------------------------------------*/ +#define QUICKSTART "quickstart" +/*---------------------------------------------------------------------------*/ +static struct mqtt_message *msg_ptr = 0; +static struct etimer publish_periodic_timer; +static struct ctimer ct; +static char *buf_ptr; +static uint16_t seq_nr_value = 0; +/*---------------------------------------------------------------------------*/ +/* Parent RSSI functionality */ +static struct uip_icmp6_echo_reply_notification echo_reply_notification; +static struct etimer echo_request_timer; +static int def_rt_rssi = 0; +/*---------------------------------------------------------------------------*/ +static mqtt_client_config_t conf; +/*---------------------------------------------------------------------------*/ +PROCESS(mqtt_demo_process, "MQTT Demo"); +/*---------------------------------------------------------------------------*/ +int +ipaddr_sprintf(char *buf, uint8_t buf_len, const uip_ipaddr_t *addr) +{ + uint16_t a; + uint8_t len = 0; + int i, f; + for(i = 0, f = 0; i < sizeof(uip_ipaddr_t); i += 2) { + a = (addr->u8[i] << 8) + addr->u8[i + 1]; + if(a == 0 && f >= 0) { + if(f++ == 0) { + len += snprintf(&buf[len], buf_len - len, "::"); + } + } else { + if(f > 0) { + f = -1; + } else if(i > 0) { + len += snprintf(&buf[len], buf_len - len, ":"); + } + len += snprintf(&buf[len], buf_len - len, "%x", a); + } + } + + return len; +} +/*---------------------------------------------------------------------------*/ +static void +echo_reply_handler(uip_ipaddr_t *source, uint8_t ttl, uint8_t *data, + uint16_t datalen) +{ + if(uip_ip6addr_cmp(source, uip_ds6_defrt_choose())) { + def_rt_rssi = sicslowpan_get_last_rssi(); + } +} +/*---------------------------------------------------------------------------*/ +static void +publish_led_off(void *d) +{ + leds_off(STATUS_LED); +} +/*---------------------------------------------------------------------------*/ +static void +pub_handler(const char *topic, uint16_t topic_len, const uint8_t *chunk, + uint16_t chunk_len) +{ + DBG("Pub Handler: topic='%s' (len=%u), chunk_len=%u\n", topic, topic_len, + chunk_len); + + /* If we don't like the length, ignore */ + if(topic_len != 23 || chunk_len != 1) { + printf("Incorrect topic or chunk len. Ignored\n"); + return; + } + + /* If the format != json, ignore */ + if(strncmp(&topic[topic_len - 4], "json", 4) != 0) { + printf("Incorrect format\n"); + } + + if(strncmp(&topic[10], "leds", 4) == 0) { + if(chunk[0] == '1') { + leds_on(LEDS_RED); + } else if(chunk[0] == '0') { + leds_off(LEDS_RED); + } + return; + } +} +/*---------------------------------------------------------------------------*/ +static void +mqtt_event(struct mqtt_connection *m, mqtt_event_t event, void *data) +{ + switch(event) { + case MQTT_EVENT_CONNECTED: { + DBG("APP - Application has a MQTT connection\n"); + timer_set(&connection_life, CONNECTION_STABLE_TIME); + state = STATE_CONNECTED; + break; + } + case MQTT_EVENT_DISCONNECTED: { + DBG("APP - MQTT Disconnect. Reason %u\n", *((mqtt_event_t *)data)); + + state = STATE_DISCONNECTED; + process_poll(&mqtt_demo_process); + break; + } + case MQTT_EVENT_PUBLISH: { + msg_ptr = data; + + /* Implement first_flag in publish message? */ + if(msg_ptr->first_chunk) { + msg_ptr->first_chunk = 0; + DBG("APP - Application received a publish on topic '%s'. Payload " + "size is %i bytes. Content:\n\n", + msg_ptr->topic, msg_ptr->payload_length); + } + + pub_handler(msg_ptr->topic, strlen(msg_ptr->topic), msg_ptr->payload_chunk, + msg_ptr->payload_length); + break; + } + case MQTT_EVENT_SUBACK: { + DBG("APP - Application is subscribed to topic successfully\n"); + break; + } + case MQTT_EVENT_UNSUBACK: { + DBG("APP - Application is unsubscribed to topic successfully\n"); + break; + } + case MQTT_EVENT_PUBACK: { + DBG("APP - Publishing complete\n"); + break; + } + default: + DBG("APP - Application got a unhandled MQTT event: %i\n", event); + break; + } +} +/*---------------------------------------------------------------------------*/ +static int +construct_pub_topic(void) +{ + int len = snprintf(pub_topic, BUFFER_SIZE, "iot-2/evt/%s/fmt/json", + conf.event_type_id); + + /* len < 0: Error. Len >= BUFFER_SIZE: Buffer too small */ + if(len < 0 || len >= BUFFER_SIZE) { + printf("Pub Topic: %d, Buffer %d\n", len, BUFFER_SIZE); + return 0; + } + + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +construct_sub_topic(void) +{ + int len = snprintf(sub_topic, BUFFER_SIZE, "iot-2/cmd/%s/fmt/json", + conf.cmd_type); + + /* len < 0: Error. Len >= BUFFER_SIZE: Buffer too small */ + if(len < 0 || len >= BUFFER_SIZE) { + printf("Sub Topic: %d, Buffer %d\n", len, BUFFER_SIZE); + return 0; + } + + return 1; +} +/*---------------------------------------------------------------------------*/ +static int +construct_client_id(void) +{ + int len = snprintf(client_id, BUFFER_SIZE, "d:%s:%s:%02x%02x%02x%02x%02x%02x", + conf.org_id, conf.type_id, + linkaddr_node_addr.u8[0], linkaddr_node_addr.u8[1], + linkaddr_node_addr.u8[2], linkaddr_node_addr.u8[5], + linkaddr_node_addr.u8[6], linkaddr_node_addr.u8[7]); + + /* len < 0: Error. Len >= BUFFER_SIZE: Buffer too small */ + if(len < 0 || len >= BUFFER_SIZE) { + printf("Client ID: %d, Buffer %d\n", len, BUFFER_SIZE); + return 0; + } + + return 1; +} +/*---------------------------------------------------------------------------*/ +static void +update_config(void) +{ + if(construct_client_id() == 0) { + /* Fatal error. Client ID larger than the buffer */ + state = STATE_CONFIG_ERROR; + return; + } + + if(construct_sub_topic() == 0) { + /* Fatal error. Topic larger than the buffer */ + state = STATE_CONFIG_ERROR; + return; + } + + if(construct_pub_topic() == 0) { + /* Fatal error. Topic larger than the buffer */ + state = STATE_CONFIG_ERROR; + return; + } + + /* Reset the counter */ + seq_nr_value = 0; + + state = STATE_INIT; + + /* + * Schedule next timer event ASAP + * + * If we entered an error state then we won't do anything when it fires. + * + * Since the error at this stage is a config error, we will only exit this + * error state if we get a new config. + */ + etimer_set(&publish_periodic_timer, 0); + + return; +} +/*---------------------------------------------------------------------------*/ +static int +init_config() +{ + /* Populate configuration with default values */ + memset(&conf, 0, sizeof(mqtt_client_config_t)); + + memcpy(conf.org_id, DEFAULT_ORG_ID, strlen(DEFAULT_ORG_ID)); + memcpy(conf.type_id, DEFAULT_TYPE_ID, strlen(DEFAULT_TYPE_ID)); + memcpy(conf.auth_token, DEFAULT_AUTH_TOKEN, strlen(DEFAULT_AUTH_TOKEN)); + memcpy(conf.event_type_id, DEFAULT_EVENT_TYPE_ID, + strlen(DEFAULT_EVENT_TYPE_ID)); + memcpy(conf.broker_ip, broker_ip, strlen(broker_ip)); + memcpy(conf.cmd_type, DEFAULT_SUBSCRIBE_CMD_TYPE, 1); + + conf.broker_port = DEFAULT_BROKER_PORT; + conf.pub_interval = DEFAULT_PUBLISH_INTERVAL; + conf.def_rt_ping_interval = DEFAULT_RSSI_MEAS_INTERVAL; + + return 1; +} +/*---------------------------------------------------------------------------*/ +static void +subscribe(void) +{ + /* Publish MQTT topic in IBM quickstart format */ + mqtt_status_t status; + + status = mqtt_subscribe(&conn, NULL, sub_topic, MQTT_QOS_LEVEL_0); + + DBG("APP - Subscribing!\n"); + if(status == MQTT_STATUS_OUT_QUEUE_FULL) { + DBG("APP - Tried to subscribe but command queue was full!\n"); + } +} +/*---------------------------------------------------------------------------*/ +static void +publish(void) +{ + /* Publish MQTT topic in IBM quickstart format */ + int len; + int remaining = APP_BUFFER_SIZE; + + seq_nr_value++; + + buf_ptr = app_buffer; + + len = snprintf(buf_ptr, remaining, + "{" + "\"d\":{" + "\"Seq #\":%d," + "\"Uptime (sec)\":%lu", seq_nr_value, clock_seconds()); + + if(len < 0 || len >= remaining) { + printf("Buffer too short. Have %d, need %d + \\0\n", remaining, len); + return; + } + + remaining -= len; + buf_ptr += len; + + /* Put our Default route's string representation in a buffer */ + char def_rt_str[64]; + memset(def_rt_str, 0, sizeof(def_rt_str)); + ipaddr_sprintf(def_rt_str, sizeof(def_rt_str), uip_ds6_defrt_choose()); + + len = snprintf(buf_ptr, remaining, ",\"Def Route\":\"%s\",\"RSSI (dBm)\":%d", + def_rt_str, def_rt_rssi); + + if(len < 0 || len >= remaining) { + printf("Buffer too short. Have %d, need %d + \\0\n", remaining, len); + return; + } + remaining -= len; + buf_ptr += len; + + len = snprintf(buf_ptr, remaining, ",\"On-Chip Temp (deg.C)\":%d", + temperature_sensor.value(0)); + + if(len < 0 || len >= remaining) { + printf("Buffer too short. Have %d, need %d + \\0\n", remaining, len); + return; + } + remaining -= len; + buf_ptr += len; + + len = snprintf(buf_ptr, remaining, "}}"); + + if(len < 0 || len >= remaining) { + printf("Buffer too short. Have %d, need %d + \\0\n", remaining, len); + return; + } + + mqtt_publish(&conn, NULL, pub_topic, (uint8_t *)app_buffer, + strlen(app_buffer), MQTT_QOS_LEVEL_0, MQTT_RETAIN_OFF); + + DBG("APP - Publish!\n"); +} +/*---------------------------------------------------------------------------*/ +static void +connect_to_broker(void) +{ + /* Connect to MQTT server */ + mqtt_connect(&conn, conf.broker_ip, conf.broker_port, + conf.pub_interval * 3); + + state = STATE_CONNECTING; +} +/*---------------------------------------------------------------------------*/ +static void +ping_parent(void) +{ + if(uip_ds6_get_global(ADDR_PREFERRED) == NULL) { + return; + } + + uip_icmp6_send(uip_ds6_defrt_choose(), ICMP6_ECHO_REQUEST, 0, + ECHO_REQ_PAYLOAD_LEN); +} +/*---------------------------------------------------------------------------*/ +static void +state_machine(void) +{ + switch(state) { + case STATE_INIT: + /* If we have just been configured register MQTT connection */ + mqtt_register(&conn, &mqtt_demo_process, client_id, mqtt_event, + MAX_TCP_SEGMENT_SIZE); + + /* + * If we are not using the quickstart service (thus we are an IBM + * registered device), we need to provide user name and password + */ + if(strncasecmp(conf.org_id, QUICKSTART, strlen(conf.org_id)) != 0) { + if(strlen(conf.auth_token) == 0) { + printf("User name set, but empty auth token\n"); + state = STATE_ERROR; + break; + } else { + mqtt_set_username_password(&conn, "use-token-auth", + conf.auth_token); + } + } + + /* _register() will set auto_reconnect. We don't want that. */ + conn.auto_reconnect = 0; + connect_attempt = 1; + + state = STATE_REGISTERED; + DBG("Init\n"); + /* Continue */ + case STATE_REGISTERED: + if(uip_ds6_get_global(ADDR_PREFERRED) != NULL) { + /* Registered and with a public IP. Connect */ + DBG("Registered. Connect attempt %u\n", connect_attempt); + ping_parent(); + connect_to_broker(); + } else { + leds_on(STATUS_LED); + ctimer_set(&ct, NO_NET_LED_DURATION, publish_led_off, NULL); + } + etimer_set(&publish_periodic_timer, NET_CONNECT_PERIODIC); + return; + break; + case STATE_CONNECTING: + leds_on(STATUS_LED); + ctimer_set(&ct, CONNECTING_LED_DURATION, publish_led_off, NULL); + /* Not connected yet. Wait */ + DBG("Connecting (%u)\n", connect_attempt); + break; + case STATE_CONNECTED: + /* Don't subscribe unless we are a registered device */ + if(strncasecmp(conf.org_id, QUICKSTART, strlen(conf.org_id)) == 0) { + DBG("Using 'quickstart': Skipping subscribe\n"); + state = STATE_PUBLISHING; + } + /* Continue */ + case STATE_PUBLISHING: + /* If the timer expired, the connection is stable. */ + if(timer_expired(&connection_life)) { + /* + * Intentionally using 0 here instead of 1: We want RECONNECT_ATTEMPTS + * attempts if we disconnect after a successful connect + */ + connect_attempt = 0; + } + + if(mqtt_ready(&conn) && conn.out_buffer_sent) { + /* Connected. Publish */ + if(state == STATE_CONNECTED) { + subscribe(); + state = STATE_PUBLISHING; + } else { + leds_on(STATUS_LED); + ctimer_set(&ct, PUBLISH_LED_ON_DURATION, publish_led_off, NULL); + publish(); + } + etimer_set(&publish_periodic_timer, conf.pub_interval); + + DBG("Publishing\n"); + /* Return here so we don't end up rescheduling the timer */ + return; + } else { + /* + * Our publish timer fired, but some MQTT packet is already in flight + * (either not sent at all, or sent but not fully ACKd). + * + * This can mean that we have lost connectivity to our broker or that + * simply there is some network delay. In both cases, we refuse to + * trigger a new message and we wait for TCP to either ACK the entire + * packet after retries, or to timeout and notify us. + */ + DBG("Publishing... (MQTT state=%d, q=%u)\n", conn.state, + conn.out_queue_full); + } + break; + case STATE_DISCONNECTED: + DBG("Disconnected\n"); + if(connect_attempt < RECONNECT_ATTEMPTS || + RECONNECT_ATTEMPTS == RETRY_FOREVER) { + /* Disconnect and backoff */ + clock_time_t interval; + mqtt_disconnect(&conn); + connect_attempt++; + + interval = connect_attempt < 3 ? RECONNECT_INTERVAL << connect_attempt : + RECONNECT_INTERVAL << 3; + + DBG("Disconnected. Attempt %u in %lu ticks\n", connect_attempt, interval); + + etimer_set(&publish_periodic_timer, interval); + + state = STATE_REGISTERED; + return; + } else { + /* Max reconnect attempts reached. Enter error state */ + state = STATE_ERROR; + DBG("Aborting connection after %u attempts\n", connect_attempt - 1); + } + break; + case STATE_CONFIG_ERROR: + /* Idle away. The only way out is a new config */ + printf("Bad configuration\n"); + return; + case STATE_ERROR: + default: + leds_on(STATUS_LED); + /* + * 'default' should never happen. + * + * If we enter here it's because of some error. Stop timers. The only thing + * that can bring us out is a new config event + */ + printf("Default case: State=0x%02x\n", state); + return; + } + + /* If we didn't return so far, reschedule ourselves */ + etimer_set(&publish_periodic_timer, STATE_MACHINE_PERIODIC); +} +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(mqtt_demo_process, ev, data) +{ + + PROCESS_BEGIN(); + + printf("MQTT Demo Process\n"); + + if(init_config() != 1) { + PROCESS_EXIT(); + } + + update_config(); + + def_rt_rssi = 0x8000000; + uip_icmp6_echo_reply_callback_add(&echo_reply_notification, + echo_reply_handler); + etimer_set(&echo_request_timer, conf.def_rt_ping_interval); + + PUBLISH_TRIGGER->configure(SENSORS_ACTIVE, 1); + + /* Main loop */ + while(1) { + + PROCESS_YIELD(); + + if(ev == sensors_event && data == PUBLISH_TRIGGER) { + if(state == STATE_ERROR) { + connect_attempt = 1; + state = STATE_REGISTERED; + } + } + + if((ev == PROCESS_EVENT_TIMER && data == &publish_periodic_timer) || + ev == PROCESS_EVENT_POLL || + (ev == sensors_event && data == PUBLISH_TRIGGER && PUBLISH_TRIGGER->value(BUTTON_SENSOR_VALUE_STATE) == 0)) { + state_machine(); + } + + if(ev == PROCESS_EVENT_TIMER && data == &echo_request_timer) { + ping_parent(); + etimer_set(&echo_request_timer, conf.def_rt_ping_interval); + } + } + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + * @} + */ diff --git a/examples/nrf52dk/mqtt-demo/project-conf.h b/examples/nrf52dk/mqtt-demo/project-conf.h new file mode 100644 index 000000000..b52e1a48f --- /dev/null +++ b/examples/nrf52dk/mqtt-demo/project-conf.h @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2012, Texas Instruments Incorporated - http://www.ti.com/ + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the copyright holder nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*---------------------------------------------------------------------------*/ +/** + * \addtogroup cc2538-mqtt-demo + * @{ + * + * \file + * Project specific configuration defines for the MQTT demo + */ +/*---------------------------------------------------------------------------*/ +#ifndef PROJECT_CONF_H_ +#define PROJECT_CONF_H_ +/*---------------------------------------------------------------------------*/ +/* User configuration */ +#define MQTT_DEMO_STATUS_LED LEDS_GREEN + +/* If undefined, the demo will attempt to connect to IBM's quickstart */ +#define MQTT_DEMO_BROKER_IP_ADDR "fd00::215:83ff:fed2:dbd7" +/*---------------------------------------------------------------------------*/ +#endif /* PROJECT_CONF_H_ */ +/*---------------------------------------------------------------------------*/ +/** @} */ diff --git a/examples/nrf52dk/timer-test/Makefile b/examples/nrf52dk/timer-test/Makefile new file mode 100644 index 000000000..ac89a34a4 --- /dev/null +++ b/examples/nrf52dk/timer-test/Makefile @@ -0,0 +1,9 @@ +CONTIKI_PROJECT = timer-test + +CONTIKI_WITH_RPL=0 +NRF52_WITHOUT_SOFTDEVICE=1 + +all: $(CONTIKI_PROJECT) + +CONTIKI = ../../.. +include $(CONTIKI)/Makefile.include diff --git a/examples/nrf52dk/timer-test/README.md b/examples/nrf52dk/timer-test/README.md new file mode 100644 index 000000000..9ce7cad94 --- /dev/null +++ b/examples/nrf52dk/timer-test/README.md @@ -0,0 +1,20 @@ +Timers test +=========== +Timers test is an application allows for testing clocks implementation for nRF52 DK. +The results of different tests are output to the console. + +There are 4 tests performed: + + 1) TEST clock_delay_usec() - measures duration of a NOP delay using rtimer. It's expected + that difference is close to 0. + + 2) TEST rtimer - schedules an rtimer callback after 1 second. Prints actual time difference + in rtimer and clock ticks. It's expected that both values are close to 0. + + 3) TEST etimer - schedules an event timer and measures time difference. It is expected that + the value is close to 0. + +The example requires one DK and it doesn't use SoftDevice. To compile and flash the +example run: + + make TARGET=nrf52dk timer-test.flash \ No newline at end of file diff --git a/examples/nrf52dk/timer-test/timer-test.c b/examples/nrf52dk/timer-test/timer-test.c new file mode 100644 index 000000000..e0ced7293 --- /dev/null +++ b/examples/nrf52dk/timer-test/timer-test.c @@ -0,0 +1,131 @@ +/** + * \file + * Tests related to clocks and timers + * This is based on clock_test.c from the original sensinode port + * + * \author + * Zach Shelby (Original) + * George Oikonomou - (rtimer code) + * Wojciech Bober (nRF52 DK adaptation) + * + */ + +/** + * \addtogroup nrf52dk-examples Demo projects for nRF52 DK + * @{ + */ +#include "contiki.h" +#include "sys/clock.h" +#include "sys/rtimer.h" +#include "dev/leds.h" + +#include "nrf_delay.h" + +#include +/*---------------------------------------------------------------------------*/ +#define TEST_CLOCK_DELAY_USEC 1 +#define TEST_RTIMER 1 +#define TEST_ETIMER 1 +/*---------------------------------------------------------------------------*/ +static struct etimer et; + +#if TEST_CLOCK_DELAY_USEC +static rtimer_clock_t start_count, end_count, diff; +#endif + +#if TEST_ETIMER +static clock_time_t count; +#endif + +#if TEST_RTIMER +static struct rtimer rt; +static clock_time_t ct_now; +rtimer_clock_t rt_now, rt_until; + +static volatile rtimer_clock_t rt_now_cb; +static volatile clock_time_t ct_cb; +#endif + +static uint8_t i; +/*---------------------------------------------------------------------------*/ +PROCESS(clock_test_process, "Clock test process"); +AUTOSTART_PROCESSES(&clock_test_process); +/*---------------------------------------------------------------------------*/ +#if TEST_RTIMER +void +rt_callback(struct rtimer *t, void *ptr) +{ + rt_now_cb = RTIMER_NOW(); + ct_cb = clock_time(); +} +#endif +/*---------------------------------------------------------------------------*/ +PROCESS_THREAD(clock_test_process, ev, data) +{ + + PROCESS_BEGIN(); + etimer_set(&et, 2 * CLOCK_SECOND); + PROCESS_YIELD(); + + printf("RTIMER_SECOND=%d CLOCK_SECOND=%d\n", RTIMER_SECOND, CLOCK_SECOND); + +#if TEST_CLOCK_DELAY_USEC + printf("=======================\n"); + printf("TEST clock_delay_usec()\n"); + printf("=======================\n"); + i = 1; + while(i < 7) { + start_count = RTIMER_NOW(); + clock_delay_usec(10000 * i); + end_count = RTIMER_NOW(); + diff = end_count - start_count; + printf("difference [usec]: %ld\n", 10000 * i - (diff*(1000000/RTIMER_SECOND))); + i++; + } +#endif + +#if TEST_RTIMER + printf("=======================\n"); + printf("TEST rtimer\n"); + printf("=======================\n"); + i = 0; + while(i < 5) { + etimer_set(&et, 2 * CLOCK_SECOND); + rt_now = RTIMER_NOW(); + ct_now = clock_time(); + rt_until = rt_now + RTIMER_SECOND; + printf("now [ticks]: %lu until[ticks]: %lu\n", rt_now, rt_until); + if (rtimer_set(&rt, rt_until, 1, rt_callback, NULL) != RTIMER_OK) { + printf("Error setting\n"); + } + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + printf("rtimer difference [ticks]: %ld\n", RTIMER_SECOND - (rt_now_cb - rt_now)); + printf("clock difference [ticks]: %ld\n", CLOCK_SECOND - (ct_cb - ct_now)); + i++; + } +#endif + +#if TEST_ETIMER + printf("=======================\n"); + printf("TEST etimer\n"); + printf("=======================\n"); + i = 0; + while(i < 5) { + etimer_set(&et, i*CLOCK_SECOND); + count = clock_time(); + PROCESS_WAIT_EVENT_UNTIL(etimer_expired(&et)); + etimer_reset(&et); + printf("difference [ticks]: %lu\n", i*CLOCK_SECOND - (clock_time() - count)); + leds_toggle(LEDS_RED); + i++; + } +#endif + + printf("Done!\n"); + + PROCESS_END(); +} +/*---------------------------------------------------------------------------*/ +/** + * @} + */