Merge pull request #965 from jonnteolsson/mqtt-contrib

Mqtt contrib
This commit is contained in:
George Oikonomou 2015-02-17 22:37:56 +01:00
commit 5e25ca9bf5
9 changed files with 2855 additions and 0 deletions

1
apps/mqtt/Makefile.mqtt Normal file
View File

@ -0,0 +1 @@
mqtt_src = mqtt.c

1484
apps/mqtt/mqtt.c Normal file

File diff suppressed because it is too large Load Diff

509
apps/mqtt/mqtt.h Normal file
View File

@ -0,0 +1,509 @@
/*
* Copyright (c) 2015, 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 apps
* @{
*
* \defgroup mqtt-engine An implementation of MQTT v3.1
* @{
*
* This application is an engine for MQTT v3.1. It supports QoS Levels 0 and 1.
*
* MQTT is a Client Server publish/subscribe messaging transport protocol.
* It is light weight, open, simple, and designed so as to be easy to implement.
* These characteristics make it ideal for use in many situations, including
* constrained environments such as for communication in Machine to Machine
* (M2M) and Internet of Things (IoT) contexts where a small code footprint is
* required and/or network bandwidth is at a premium.
*
* The protocol runs over TCP/IP, more specifically tcp_socket.
* Its features include:
*
* - Use of the publish/subscribe message pattern which provides
* one-to-many message distribution and decoupling of applications.
* - A messaging transport that is agnostic to the content of the payload.
* Three qualities of service for message delivery:
* -- "At most once" (0), where messages are delivered according to the best
* efforts of the operating environment. Message loss can occur.
* This level could be used, for example, with ambient sensor data where it
* does not matter if an individual reading is lost as the next one will be
* published soon after.
* --"At least once" (1), where messages are assured to arrive but duplicates
* can occur.
* -- "Exactly once" (2), where message are assured to arrive exactly once.
* This level could be used, for example, with billing systems where duplicate
* or lost messages could lead to incorrect charges being applied. This QoS
* level is currently not supported in this implementation.
*
* - A small transport overhead and protocol exchanges minimized to reduce
* network traffic.
* - A mechanism, Last Will, to notify interested parties when an abnormal
* disconnection occurs.
*
* The protocol specification and other useful information can be found
* here: http://mqtt.org
*
*/
/**
* \file
* Header file for the Contiki MQTT engine
*
* \author
* Texas Instruments
*/
/*---------------------------------------------------------------------------*/
#ifndef MQTT_H_
#define MQTT_H_
/*---------------------------------------------------------------------------*/
#include "contiki.h"
#include "contiki-net.h"
#include "contiki-lib.h"
#include "lib/random.h"
#include "sys/ctimer.h"
#include "sys/etimer.h"
#include "net/rpl/rpl.h"
#include "net/ip/uip.h"
#include "net/ipv6/uip-ds6.h"
#include "dev/leds.h"
#include "tcp-socket.h"
#include "udp-socket.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
/*---------------------------------------------------------------------------*/
/* Protocol constants */
#define MQTT_CLIENT_ID_MAX_LEN 23
/* Size of the underlying TCP buffers */
#define MQTT_TCP_INPUT_BUFF_SIZE 512
#define MQTT_TCP_OUTPUT_BUFF_SIZE 512
#define MQTT_INPUT_BUFF_SIZE 512
#define MQTT_MAX_TOPIC_LENGTH 64
#define MQTT_MAX_TOPICS_PER_SUBSCRIBE 1
#define MQTT_FHDR_SIZE 1
#define MQTT_MAX_REMAINING_LENGTH_BYTES 4
#define MQTT_PROTOCOL_VERSION 3
#define MQTT_PROTOCOL_NAME "MQIsdp"
#define MQTT_TOPIC_MAX_LENGTH 128
/*---------------------------------------------------------------------------*/
/*
* Debug configuration, this is similar but not exactly like the Debugging
* System discussion at https://github.com/contiki-os/contiki/wiki.
*/
#define DEBUG_MQTT 0
#if DEBUG_MQTT == 1
#define DBG(...) printf(__VA_ARGS__)
#else
#define DBG(...)
#endif /* DEBUG */
/*---------------------------------------------------------------------------*/
extern process_event_t mqtt_update_event;
/* Forward declaration */
struct mqtt_connection;
typedef enum {
MQTT_RETAIN_OFF,
MQTT_RETAIN_ON,
} mqtt_retain_t;
/**
* \brief MQTT engine events
*/
typedef enum {
MQTT_EVENT_CONNECTED,
MQTT_EVENT_DISCONNECTED,
MQTT_EVENT_SUBACK,
MQTT_EVENT_UNSUBACK,
MQTT_EVENT_PUBLISH,
MQTT_EVENT_PUBACK,
/* Errors */
MQTT_EVENT_ERROR = 0x80,
MQTT_EVENT_PROTOCOL_ERROR,
MQTT_EVENT_CONNECTION_REFUSED_ERROR,
MQTT_EVENT_DNS_ERROR,
MQTT_EVENT_NOT_IMPLEMENTED_ERROR,
/* Add more */
} mqtt_event_t;
typedef enum {
MQTT_STATUS_OK,
MQTT_STATUS_OUT_QUEUE_FULL,
/* Errors */
MQTT_STATUS_ERROR = 0x80,
MQTT_STATUS_NOT_CONNECTED_ERROR,
MQTT_STATUS_INVALID_ARGS_ERROR,
MQTT_STATUS_DNS_ERROR,
} mqtt_status_t;
typedef enum {
MQTT_QOS_LEVEL_0,
MQTT_QOS_LEVEL_1,
MQTT_QOS_LEVEL_2,
} mqtt_qos_level_t;
typedef enum {
MQTT_QOS_STATE_NO_ACK,
MQTT_QOS_STATE_GOT_ACK,
/* Expand for QoS 2 */
} mqtt_qos_state_t;
/*---------------------------------------------------------------------------*/
/*
* This is the state of the connection itself.
*
* N.B. The order is important because of runtime checks on how far the
* connection has proceeded.
*/
typedef enum {
MQTT_CONN_STATE_ERROR,
MQTT_CONN_STATE_DNS_ERROR,
MQTT_CONN_STATE_DISCONNECTING,
MQTT_CONN_STATE_NOT_CONNECTED,
MQTT_CONN_STATE_DNS_LOOKUP,
MQTT_CONN_STATE_TCP_CONNECTING,
MQTT_CONN_STATE_TCP_CONNECTED,
MQTT_CONN_STATE_CONNECTING_TO_BROKER,
MQTT_CONN_STATE_CONNECTED_TO_BROKER,
MQTT_CONN_STATE_SENDING_MQTT_DISCONNECT,
MQTT_CONN_STATE_ABORT_IMMEDIATE,
} mqtt_conn_state_t;
/*---------------------------------------------------------------------------*/
struct mqtt_string {
char *string;
uint16_t length;
};
/*
* Note that the pairing mid <-> QoS level only applies one-to-one if we only
* allow the subscription of one topic at a time. Otherwise we will have an
* ordered list of QoS levels corresponding to the order of topics.
*
* This could be part of a union of event data structures.
*/
struct mqtt_suback_event {
uint16_t mid;
mqtt_qos_level_t qos_level;
};
/* This is the MQTT message that is exposed to the end user. */
struct mqtt_message {
uint32_t mid;
char topic[MQTT_MAX_TOPIC_LENGTH + 1]; /* +1 for string termination */
uint8_t *payload_chunk;
uint16_t payload_chunk_length;
uint8_t first_chunk;
uint16_t payload_length;
uint16_t payload_left;
};
/* This struct represents a packet received from the MQTT server. */
struct mqtt_in_packet {
/* Used by the list interface, must be first in the struct. */
struct mqtt_connection *next;
/* Total bytes read so far. Compared to the remaining length to to decide when
* we've read the payload. */
uint32_t byte_counter;
uint8_t packet_received;
uint8_t fhdr;
uint16_t remaining_length;
uint16_t mid;
/* Helper variables needed to decode the remaining_length */
uint8_t remaining_multiplier;
uint8_t has_remaining_length;
uint8_t remaining_length_bytes;
/* Not the same as payload in the MQTT sense, it also contains the variable
* header.
*/
uint8_t payload_pos;
uint8_t payload[MQTT_INPUT_BUFF_SIZE];
/* Message specific data */
uint16_t topic_len;
uint16_t topic_pos;
uint8_t topic_len_received;
uint8_t topic_received;
};
/* This struct represents a packet sent to the MQTT server. */
struct mqtt_out_packet {
uint8_t fhdr;
uint32_t remaining_length;
uint8_t remaining_length_enc[MQTT_MAX_REMAINING_LENGTH_BYTES];
uint8_t remaining_length_enc_bytes;
uint16_t mid;
char *topic;
uint16_t topic_length;
uint8_t *payload;
uint32_t payload_size;
mqtt_qos_level_t qos;
mqtt_qos_state_t qos_state;
mqtt_retain_t retain;
};
/*---------------------------------------------------------------------------*/
/**
* \brief MQTT event callback function
* \param m A pointer to a MQTT connection
* \param event The event number
* \param data A user-defined pointer
*
* The MQTT socket event callback function gets called whenever there is an
* event on a MQTT connection, such as the connection getting connected
* or closed.
*/
typedef void (*mqtt_event_callback_t)(struct mqtt_connection *m,
mqtt_event_t event,
void *data);
typedef void (*mqtt_topic_callback_t)(struct mqtt_connection *m,
struct mqtt_message *msg);
/*---------------------------------------------------------------------------*/
struct mqtt_will {
struct mqtt_string topic;
struct mqtt_string message;
mqtt_qos_level_t qos;
};
struct mqtt_credentials {
struct mqtt_string username;
struct mqtt_string password;
};
struct mqtt_connection {
/* Used by the list interface, must be first in the struct */
struct mqtt_connection *next;
struct timer t;
struct mqtt_string client_id;
uint8_t connect_vhdr_flags;
uint8_t auto_reconnect;
uint16_t keep_alive;
struct ctimer keep_alive_timer;
uint8_t waiting_for_pingresp;
struct mqtt_will will;
struct mqtt_credentials credentials;
mqtt_conn_state_t state;
mqtt_event_callback_t event_callback;
/* Internal data */
uint16_t mid_counter;
/* Used for communication between MQTT API and APP */
uint8_t out_queue_full;
struct process *app_process;
/* Outgoing data related */
uint8_t *out_buffer_ptr;
uint8_t out_buffer[MQTT_TCP_OUTPUT_BUFF_SIZE];
uint8_t out_buffer_sent;
struct mqtt_out_packet out_packet;
struct pt out_proto_thread;
uint32_t out_write_pos;
uint16_t max_segment_size;
/* Incoming data related */
uint8_t in_buffer[MQTT_TCP_INPUT_BUFF_SIZE];
struct mqtt_in_packet in_packet;
struct mqtt_message in_publish_msg;
/* TCP related information */
char *server_host;
uip_ipaddr_t server_ip;
uint16_t server_port;
struct tcp_socket socket;
};
/* This is the API exposed to the user. */
/*---------------------------------------------------------------------------*/
/**
* \brief Initializes the MQTT engine.
* \param conn A pointer to the MQTT connection.
* \param app_process A pointer to the application process handling the MQTT
* connection.
* \param client_id A pointer to the MQTT client ID.
* \param event_callback Callback function responsible for handling the
* callback from MQTT engine.
* \param max_segment_size The TCP segment size to use for this MQTT/TCP
* connection.
* \return MQTT_STATUS_OK or MQTT_STATUS_INVALID_ARGS_ERROR
*
* This function initializes the MQTT engine and shall be called before any
* other MQTT function.
*/
mqtt_status_t mqtt_register(struct mqtt_connection *conn,
struct process *app_process,
char *client_id,
mqtt_event_callback_t event_callback,
uint16_t max_segment_size);
/*---------------------------------------------------------------------------*/
/**
* \brief Connects to a MQTT broker.
* \param conn A pointer to the MQTT connection.
* \param host IP address of the broker to connect to.
* \param port Port of the broker to connect to, default is MQTT port is 1883.
* \param keep_alive Keep alive timer in seconds. Used by broker to handle
* client disc. Defines the maximum time interval between two messages
* from the client. Shall be min 1.5 x report interval.
* \return MQTT_STATUS_OK or an error status
*
* This function connects to a MQTT broker.
*/
mqtt_status_t mqtt_connect(struct mqtt_connection *conn,
char *host,
uint16_t port,
uint16_t keep_alive);
/*---------------------------------------------------------------------------*/
/**
* \brief Disconnects from a MQTT broker.
* \param conn A pointer to the MQTT connection.
*
* This function disconnects from a MQTT broker.
*/
void mqtt_disconnect(struct mqtt_connection *conn);
/*---------------------------------------------------------------------------*/
/**
* \brief Subscribes to a MQTT topic.
* \param conn A pointer to the MQTT connection.
* \param mid A pointer to message ID.
* \param topic A pointer to the topic to subscribe to.
* \param qos_level Quality Of Service level to use. Currently supports 0, 1.
* \return MQTT_STATUS_OK or some error status
*
* This function subscribes to a topic on a MQTT broker.
*/
mqtt_status_t mqtt_subscribe(struct mqtt_connection *conn,
uint16_t *mid,
char *topic,
mqtt_qos_level_t qos_level);
/*---------------------------------------------------------------------------*/
/**
* \brief Unsubscribes from a MQTT topic.
* \param conn A pointer to the MQTT connection.
* \param mid A pointer to message ID.
* \param topic A pointer to the topic to unsubscribe from.
* \return MQTT_STATUS_OK or some error status
*
* This function unsubscribes from a topic on a MQTT broker.
*/
mqtt_status_t mqtt_unsubscribe(struct mqtt_connection *conn,
uint16_t *mid,
char *topic);
/*---------------------------------------------------------------------------*/
/**
* \brief Publish to a MQTT topic.
* \param conn A pointer to the MQTT connection.
* \param mid A pointer to message ID.
* \param topic A pointer to the topic to subscribe to.
* \param payload A pointer to the topic payload.
* \param payload_size Payload size.
* \param qos_level Quality Of Service level to use. Currently supports 0, 1.
* \param retain If the RETAIN flag is set to 1, in a PUBLISH Packet sent by a
* Client to a Server, the Server MUST store the Application Message
* and its QoS, so that it can be delivered to future subscribers whose
* subscriptions match its topic name
* \return MQTT_STATUS_OK or some error status
*
* This function publishes to a topic on a MQTT broker.
*/
mqtt_status_t mqtt_publish(struct mqtt_connection *conn,
uint16_t *mid,
char *topic,
uint8_t *payload,
uint32_t payload_size,
mqtt_qos_level_t qos_level,
mqtt_retain_t retain);
/*---------------------------------------------------------------------------*/
/**
* \brief Set the user name and password for a MQTT client.
* \param conn A pointer to the MQTT connection.
* \param username A pointer to the user name.
* \param password A pointer to the password.
*
* This function sets clients user name and password to use when connecting to
* a MQTT broker.
*/
void mqtt_set_username_password(struct mqtt_connection *conn,
char *username,
char *password);
/*---------------------------------------------------------------------------*/
/**
* \brief Set the last will topic and message for a MQTT client.
* \param conn A pointer to the MQTT connection.
* \param topic A pointer to the Last Will topic.
* \param message A pointer to the Last Will message (payload).
* \param qos The desired QoS level.
*
* This function sets clients Last Will topic and message (payload).
* If the Will Flag is set to 1 (using the function) this indicates that,
* if the Connect request is accepted, a Will Message MUST be stored on the
* Server and associated with the Network Connection. The Will Message MUST
* be published when the Network Connection is subsequently closed.
*
* This functionality can be used to get notified that a device has
* disconnected from the broker.
*
*/
void mqtt_set_last_will(struct mqtt_connection *conn,
char *topic,
char *message,
mqtt_qos_level_t qos);
#define mqtt_connected(conn) \
((conn)->state == MQTT_CONN_STATE_CONNECTED_TO_BROKER ? 1 : 0)
#define mqtt_ready(conn) \
(!(conn)->out_queue_full && mqtt_connected((conn)))
/*---------------------------------------------------------------------------*/
#endif /* MQTT_H_ */
/*---------------------------------------------------------------------------*/
/**
* @}
* @}
*/

View File

@ -0,0 +1,10 @@
DEFINES+=PROJECT_CONF_H=\"project-conf.h\"
all: mqtt-demo
CONTIKI_WITH_IPV6 = 1
APPS += mqtt
CONTIKI=../../..
include $(CONTIKI)/Makefile.include

View File

@ -0,0 +1 @@
TARGET = cc2538dk

View File

@ -0,0 +1,62 @@
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
Publishing
----------
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/).
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:<device-id>`, where `<device-id>` 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 <broker IP> -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`.
The device will then try to connect to IBM's quickstart over NAT64, so you will
need a NAT64 gateway in your network to make this work. A guide on how to
setup NAT64 is out of scope here.
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.

View File

@ -0,0 +1,735 @@
/*
* Copyright (c) 2014, 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-examples
* @{
*
* \defgroup cc2538-mqtt-demo CC2538DK MQTT Demo Project
*
* Demonstrates MQTT functionality. Works with IBM Quickstart as well as
* mosquitto.
* @{
*
* \file
* An MQTT example for the cc2538dk platform
*/
/*---------------------------------------------------------------------------*/
#include "contiki-conf.h"
#include "rpl/rpl-private.h"
#include "mqtt.h"
#include "net/rpl/rpl.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/leds.h"
#include "dev/adc-sensor.h"
#include <string.h>
/*---------------------------------------------------------------------------*/
/*
* IBM server: messaging.quickstart.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 >> 2)
#define NO_NET_LED_DURATION (NET_CONNECT_PERIODIC >> 1)
/*---------------------------------------------------------------------------*/
/* Default configuration values */
#define DEFAULT_TYPE_ID "cc2538"
#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 512
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;
int16_t value;
seq_nr_value++;
buf_ptr = app_buffer;
len = snprintf(buf_ptr, remaining,
"{"
"\"d\":{"
"\"myName\":\"%s\","
"\"Seq #\":%d,"
"\"Uptime (sec)\":%lu",
BOARD_STRING, 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;
value = adc_sensor.value(ADC_SENSOR_TEMP);
len = snprintf(buf_ptr, remaining, ",\"On-Chip Temp (mC)\":%d",
25000 + ((value >> 4) - 1422) * 10000 / 42);
if(len < 0 || len >= remaining) {
printf("Buffer too short. Have %d, need %d + \\0\n", remaining, len);
return;
}
remaining -= len;
buf_ptr += len;
value = adc_sensor.value(ADC_SENSOR_VDD_3);
len = snprintf(buf_ptr, remaining, ",\"VDD3 (mV)\":%d",
value * (3 * 1190) / (2047 << 4));
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);
/* 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)) {
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();
}
/*---------------------------------------------------------------------------*/
/**
* @}
* @}
*/

View File

@ -0,0 +1,52 @@
/*
* 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
#define MQTT_DEMO_PUBLISH_TRIGGER &button_right_sensor
/* If undefined, the demo will attempt to connect to IBM's quickstart */
#define MQTT_DEMO_BROKER_IP_ADDR "aaaa::1"
/*---------------------------------------------------------------------------*/
#endif /* PROJECT_CONF_H_ */
/*---------------------------------------------------------------------------*/
/** @} */

View File

@ -14,6 +14,7 @@ webserver-ipv6/cc2538dk \
cc2538dk/cc2538dk \
cc2538dk/udp-ipv6-echo-server/cc2538dk \
cc2538dk/sniffer/cc2538dk \
cc2538dk/mqtt-demo/cc2538dk \
ipv6/multicast/econotag \
ipv6/multicast/cc2538dk \