SMQ is an easy to use machine-to-machine (M2M)/"Internet of Things" connectivity protocol that follows the publish subscribe design pattern.

Dependencies:   EthernetInterface mbed-rtos mbed

Introduction

The SMQ Architecture is an Internet of Things (IoT) publish subscribe end-to-end solution that is optimized for embedded systems to provide instantaneous Device Edge Node connectivity, 1 to 1 Communications, and Ease of Transcending Firewalls. The solution is ideal for resource constrained devices that require real-time dynamic control, analytic information, and firmware updates in both LAN and WAN environments.

Architecture Component List

  • SMQ C Client (Non-secure)
  • SharkMQ (Secure) C Client
  • SMQjs (Javascript)
  • SMQ JAVA
  • SMQ Broker

/media/uploads/wini/smq_architecture.png

SMQ Client-example

A (non-secure) C Client implementation of the SMQ protocol demonstrating device control over the on board LEDs via any modern (WebSocket enabled) Browser interface.

  • Code Size: 3kB

See Also

How to setup your own SMQ IoT cloud server

Most IoT cloud server solutions, whether they provide ready-to-use hosted services or not, are based on a standard Virtual Private Server (VPS). Most developers probably think of Amazon or Microsoft Azure's services when considering the server side of their IoT solution. These high-end services are great if you need to scale up to millions of connected devices. However, for most small-scale operations and DIY projects, a low-cost VPS is more than adequate.

Committer:
wini
Date:
Mon May 23 14:13:28 2016 +0000
Revision:
5:bfd98bf43a59
Parent:
3:c718bd8c7b8f
Updated to include latest mbed release

Who changed what in which revision?

UserRevisionLine numberNew contents of line
wini 3:c718bd8c7b8f 1 /**
wini 3:c718bd8c7b8f 2 * ____ _________ __ _
wini 3:c718bd8c7b8f 3 * / __ \___ ____ _/ /_ __(_)___ ___ ___ / / ____ ____ _(_)____
wini 3:c718bd8c7b8f 4 * / /_/ / _ \/ __ `/ / / / / / __ `__ \/ _ \/ / / __ \/ __ `/ / ___/
wini 3:c718bd8c7b8f 5 * / _, _/ __/ /_/ / / / / / / / / / / / __/ /___/ /_/ / /_/ / / /__
wini 3:c718bd8c7b8f 6 * /_/ |_|\___/\__,_/_/ /_/ /_/_/ /_/ /_/\___/_____/\____/\__, /_/\___/
wini 3:c718bd8c7b8f 7 * /____/
wini 3:c718bd8c7b8f 8 *
wini 3:c718bd8c7b8f 9 ****************************************************************************
wini 3:c718bd8c7b8f 10 * PROGRAM MODULE
wini 3:c718bd8c7b8f 11 *
wini 3:c718bd8c7b8f 12 * $Id: m2m-led.c 3868 2016-03-25 22:39:50Z wini $
wini 3:c718bd8c7b8f 13 *
wini 3:c718bd8c7b8f 14 * COPYRIGHT: Real Time Logic LLC, 2014 - 2016
wini 3:c718bd8c7b8f 15 *
wini 3:c718bd8c7b8f 16 * This software is copyrighted by and is the sole property of Real
wini 3:c718bd8c7b8f 17 * Time Logic LLC. All rights, title, ownership, or other interests in
wini 3:c718bd8c7b8f 18 * the software remain the property of Real Time Logic LLC. This
wini 3:c718bd8c7b8f 19 * software may only be used in accordance with the terms and
wini 3:c718bd8c7b8f 20 * conditions stipulated in the corresponding license agreement under
wini 3:c718bd8c7b8f 21 * which the software has been supplied. Any unauthorized use,
wini 3:c718bd8c7b8f 22 * duplication, transmission, distribution, or disclosure of this
wini 3:c718bd8c7b8f 23 * software is expressly forbidden.
wini 3:c718bd8c7b8f 24 *
wini 3:c718bd8c7b8f 25 * This Copyright notice may not be removed or modified without prior
wini 3:c718bd8c7b8f 26 * written consent of Real Time Logic LLC.
wini 3:c718bd8c7b8f 27 *
wini 3:c718bd8c7b8f 28 * Real Time Logic LLC. reserves the right to modify this software
wini 3:c718bd8c7b8f 29 * without notice.
wini 3:c718bd8c7b8f 30 *
wini 3:c718bd8c7b8f 31 * https://realtimelogic.com
wini 3:c718bd8c7b8f 32 ****************************************************************************
wini 3:c718bd8c7b8f 33
wini 3:c718bd8c7b8f 34 SharkMQ (secure SMQ) LED example.
wini 3:c718bd8c7b8f 35
wini 3:c718bd8c7b8f 36
wini 3:c718bd8c7b8f 37 NOTE: This example uses the SharkMQ (secure SMQ) compatibility API
wini 3:c718bd8c7b8f 38 thus making it easy to upgraded to a secure version, if needed.
wini 3:c718bd8c7b8f 39
wini 3:c718bd8c7b8f 40 This code is the device side for the SMQ LED controller, an example
wini 3:c718bd8c7b8f 41 that enables LEDs in a device to be controlled from a browser and
wini 3:c718bd8c7b8f 42 Java (including Android).
wini 3:c718bd8c7b8f 43
wini 3:c718bd8c7b8f 44 When this code runs, it connects to our public SMQ test broker. The
wini 3:c718bd8c7b8f 45 device will show up as a tab on the following page:
wini 3:c718bd8c7b8f 46 http://simplemq.com/m2m-led/
wini 3:c718bd8c7b8f 47
wini 3:c718bd8c7b8f 48 Introductory information on how the complete SMQ LED demo works
wini 3:c718bd8c7b8f 49 (including the browser UI) can be found online. The device code
wini 3:c718bd8c7b8f 50 details can be found under section " Device C code".
wini 3:c718bd8c7b8f 51 https://goo.gl/phXnWp
wini 3:c718bd8c7b8f 52
wini 3:c718bd8c7b8f 53 The code is designed for embedded devices, but can also be run on a
wini 3:c718bd8c7b8f 54 host computer (Windows/Linux) as a simulated device with four
wini 3:c718bd8c7b8f 55 simulated LEDs.
wini 3:c718bd8c7b8f 56
wini 3:c718bd8c7b8f 57 The macro HOST_PLATFORM must be defined if compiled and run on a
wini 3:c718bd8c7b8f 58 non embedded platform. The code within this section sets up the
wini 3:c718bd8c7b8f 59 simulated environment. Do not set this macro if the code is to be
wini 3:c718bd8c7b8f 60 cross compiled for an embedded device.
wini 3:c718bd8c7b8f 61
wini 3:c718bd8c7b8f 62 When cross compiling for an embedded device, create a separate C
wini 3:c718bd8c7b8f 63 file and include ledctrl.h. The C file must have the following
wini 3:c718bd8c7b8f 64 functions (used by the generic code in this file):
wini 3:c718bd8c7b8f 65 * int getUniqueId(const U8* id);
wini 3:c718bd8c7b8f 66 * const LedInfo* getLedInfo(int* len);
wini 3:c718bd8c7b8f 67 * const char* getDevName(void);
wini 3:c718bd8c7b8f 68 * int setLed(int ledId, int on);
wini 3:c718bd8c7b8f 69 * int setLedFromDevice(int* ledId, int* on);
wini 3:c718bd8c7b8f 70 * int getLedState(int ledId, int on, int set);
wini 3:c718bd8c7b8f 71 * void setProgramStatus(ProgramStatus s);
wini 3:c718bd8c7b8f 72 * xprintf (selib.h)
wini 3:c718bd8c7b8f 73
wini 3:c718bd8c7b8f 74 The above functions are documented in ledctrl.h. You may also study
wini 3:c718bd8c7b8f 75 the simulated versions in the HOST_PLATFORM code section
wini 3:c718bd8c7b8f 76 below. Function xprintf is optional and used if macro XPRINTF=1. If
wini 3:c718bd8c7b8f 77 enabled, program status is printed during operation.
wini 3:c718bd8c7b8f 78
wini 3:c718bd8c7b8f 79 A ready to use (non secure) mbed version can be found on the ARM
wini 3:c718bd8c7b8f 80 mbed web site: https://goo.gl/Rnhp3b
wini 3:c718bd8c7b8f 81
wini 3:c718bd8c7b8f 82 To set up your own SMQ broker (server), download the Mako Server
wini 3:c718bd8c7b8f 83 and activate the tutorials. A ready to use broker is included in
wini 3:c718bd8c7b8f 84 the tutorials. The following non active copy of the tutorials
wini 3:c718bd8c7b8f 85 provide information on how the broker operates:
wini 3:c718bd8c7b8f 86 https://realtimelogic.com/bas-tutorials/IoT.lsp
wini 3:c718bd8c7b8f 87 */
wini 3:c718bd8c7b8f 88
wini 3:c718bd8c7b8f 89
wini 3:c718bd8c7b8f 90 /* Change the domain/url if you are running your own broker
wini 3:c718bd8c7b8f 91 Note: you can also set the domain at the command prompt when in
wini 3:c718bd8c7b8f 92 simulation mode.
wini 3:c718bd8c7b8f 93 */
wini 3:c718bd8c7b8f 94 #define SIMPLEMQ_DOMAIN "http://simplemq.com"
wini 3:c718bd8c7b8f 95 #define SIMPLEMQ_URL SIMPLEMQ_DOMAIN "/smq.lsp"
wini 3:c718bd8c7b8f 96
wini 3:c718bd8c7b8f 97 #include <SMQClient.h>
wini 3:c718bd8c7b8f 98 #include "ledctrl.h"
wini 3:c718bd8c7b8f 99 #include <ctype.h>
wini 3:c718bd8c7b8f 100 #include <stdlib.h>
wini 3:c718bd8c7b8f 101
wini 3:c718bd8c7b8f 102
wini 3:c718bd8c7b8f 103
wini 3:c718bd8c7b8f 104 /****************************************************************************
wini 3:c718bd8c7b8f 105 **************************-----------------------***************************
wini 3:c718bd8c7b8f 106 **************************| BOARD SPECIFIC CODE |***************************
wini 3:c718bd8c7b8f 107 **************************-----------------------***************************
wini 3:c718bd8c7b8f 108 ****************************************************************************/
wini 3:c718bd8c7b8f 109
wini 3:c718bd8c7b8f 110 #if HOST_PLATFORM
wini 3:c718bd8c7b8f 111
wini 3:c718bd8c7b8f 112 static const char* simpleMqUrl; /* defaults to SIMPLEMQ_DOMAIN */
wini 3:c718bd8c7b8f 113
wini 3:c718bd8c7b8f 114 #ifdef _WIN32
wini 3:c718bd8c7b8f 115 // For calculating unique ID
wini 3:c718bd8c7b8f 116 #include <Rpc.h>
wini 3:c718bd8c7b8f 117 #pragma comment(lib, "Rpcrt4.lib")
wini 3:c718bd8c7b8f 118 #endif
wini 3:c718bd8c7b8f 119
wini 3:c718bd8c7b8f 120 #include <stdio.h>
wini 3:c718bd8c7b8f 121
wini 3:c718bd8c7b8f 122
wini 3:c718bd8c7b8f 123 /* Enable simulated temperature */
wini 3:c718bd8c7b8f 124 #ifndef ENABLE_TEMP
wini 3:c718bd8c7b8f 125 #define ENABLE_TEMP
wini 3:c718bd8c7b8f 126 #endif
wini 3:c718bd8c7b8f 127
wini 3:c718bd8c7b8f 128 /*
wini 3:c718bd8c7b8f 129 The following is used by the logic managing the simulated temperature.
wini 3:c718bd8c7b8f 130 */
wini 3:c718bd8c7b8f 131 #define KEY_UP_ARROW 1000
wini 3:c718bd8c7b8f 132 #define KEY_DOWN_ARROW 1001
wini 3:c718bd8c7b8f 133 static int currentTemperature=0; /* simulated value */
wini 3:c718bd8c7b8f 134
wini 3:c718bd8c7b8f 135 /* Replace with a function that prints to a console or create a stub
wini 3:c718bd8c7b8f 136 * that does nothing.
wini 3:c718bd8c7b8f 137 */
wini 3:c718bd8c7b8f 138 void
wini 3:c718bd8c7b8f 139 _xprintf(const char* fmt, ...)
wini 3:c718bd8c7b8f 140 {
wini 3:c718bd8c7b8f 141 va_list varg;
wini 3:c718bd8c7b8f 142 va_start(varg, fmt);
wini 3:c718bd8c7b8f 143 vprintf(fmt, varg);
wini 3:c718bd8c7b8f 144 va_end(varg);
wini 3:c718bd8c7b8f 145 }
wini 3:c718bd8c7b8f 146
wini 3:c718bd8c7b8f 147
wini 3:c718bd8c7b8f 148 /* Not needed on host with printf support. This function is designed for
wini 3:c718bd8c7b8f 149 * embedded systems without console.
wini 3:c718bd8c7b8f 150 */
wini 3:c718bd8c7b8f 151 void setProgramStatus(ProgramStatus s)
wini 3:c718bd8c7b8f 152 {
wini 3:c718bd8c7b8f 153 (void)s;
wini 3:c718bd8c7b8f 154 }
wini 3:c718bd8c7b8f 155
wini 3:c718bd8c7b8f 156
wini 3:c718bd8c7b8f 157
wini 3:c718bd8c7b8f 158 /* The list of LEDs in the device, the name, color, and ID. Adapt this
wini 3:c718bd8c7b8f 159 list to the LEDs on your evaluation board (Ref-LED).
wini 3:c718bd8c7b8f 160
wini 3:c718bd8c7b8f 161 The LED name shows up in the UI, the LED type tells the UI the
wini 3:c718bd8c7b8f 162 color of the LED, and the ID is used as a handle by the UI. The UI
wini 3:c718bd8c7b8f 163 will send this handle to the device when sending LED control
wini 3:c718bd8c7b8f 164 messages. You can use any number sequence for the ID. We use the
wini 3:c718bd8c7b8f 165 sequence 1 to 4 so it's easy to map to a C array.
wini 3:c718bd8c7b8f 166
wini 3:c718bd8c7b8f 167 This data is encoded as JSON and sent to the UI when a new UI
wini 3:c718bd8c7b8f 168 requests the device capabilities.
wini 3:c718bd8c7b8f 169 */
wini 3:c718bd8c7b8f 170 static const LedInfo ledInfo[] = {
wini 3:c718bd8c7b8f 171 {
wini 3:c718bd8c7b8f 172 "LED 1",
wini 3:c718bd8c7b8f 173 LedColor_red,
wini 3:c718bd8c7b8f 174 1
wini 3:c718bd8c7b8f 175 },
wini 3:c718bd8c7b8f 176 {
wini 3:c718bd8c7b8f 177 "LED 2",
wini 3:c718bd8c7b8f 178 LedColor_yellow,
wini 3:c718bd8c7b8f 179 2
wini 3:c718bd8c7b8f 180 },
wini 3:c718bd8c7b8f 181 {
wini 3:c718bd8c7b8f 182 "LED 3",
wini 3:c718bd8c7b8f 183 LedColor_green,
wini 3:c718bd8c7b8f 184 3
wini 3:c718bd8c7b8f 185 },
wini 3:c718bd8c7b8f 186 {
wini 3:c718bd8c7b8f 187 "LED 4",
wini 3:c718bd8c7b8f 188 LedColor_blue,
wini 3:c718bd8c7b8f 189 4
wini 3:c718bd8c7b8f 190 }
wini 3:c718bd8c7b8f 191 };
wini 3:c718bd8c7b8f 192
wini 3:c718bd8c7b8f 193 const LedInfo*
wini 3:c718bd8c7b8f 194 getLedInfo(int* len)
wini 3:c718bd8c7b8f 195 {
wini 3:c718bd8c7b8f 196 *len = sizeof(ledInfo) / sizeof(ledInfo[0]);
wini 3:c718bd8c7b8f 197 return ledInfo;
wini 3:c718bd8c7b8f 198 }
wini 3:c718bd8c7b8f 199
wini 3:c718bd8c7b8f 200 /* The LEDs: used by getLedState and setLed
wini 3:c718bd8c7b8f 201 */
wini 3:c718bd8c7b8f 202 static int leds[sizeof(ledInfo)/sizeof(ledInfo[1])];
wini 3:c718bd8c7b8f 203
wini 3:c718bd8c7b8f 204 /* Returns the LED on/off state for led with ID 'ledId'. The 'ledId'
wini 3:c718bd8c7b8f 205 is the 'handle' sent by the UI.
wini 3:c718bd8c7b8f 206 */
wini 3:c718bd8c7b8f 207 int
wini 3:c718bd8c7b8f 208 getLedState(int ledId)
wini 3:c718bd8c7b8f 209 {
wini 3:c718bd8c7b8f 210 baAssert(ledId >= 1 && ledId <= sizeof(ledInfo)/sizeof(ledInfo[1]));
wini 3:c718bd8c7b8f 211 return leds[ledId-1];
wini 3:c718bd8c7b8f 212 }
wini 3:c718bd8c7b8f 213
wini 3:c718bd8c7b8f 214
wini 3:c718bd8c7b8f 215 /* Set LED on device. The 'ledId' is the 'handle' sent by the UI.
wini 3:c718bd8c7b8f 216 */
wini 3:c718bd8c7b8f 217 int
wini 3:c718bd8c7b8f 218 setLed(int ledId, int on)
wini 3:c718bd8c7b8f 219 {
wini 3:c718bd8c7b8f 220 if(ledId >= 1 && ledId <= sizeof(ledInfo)/sizeof(ledInfo[1]))
wini 3:c718bd8c7b8f 221 {
wini 3:c718bd8c7b8f 222 printf("Set LED %d %s\n", ledId, on ? "on" : "off");
wini 3:c718bd8c7b8f 223 leds[ledId-1] = on;
wini 3:c718bd8c7b8f 224 return 0;
wini 3:c718bd8c7b8f 225 }
wini 3:c718bd8c7b8f 226 return -1;
wini 3:c718bd8c7b8f 227 }
wini 3:c718bd8c7b8f 228
wini 3:c718bd8c7b8f 229
wini 3:c718bd8c7b8f 230 /* The optional function setLedFromDevice requires non blocking
wini 3:c718bd8c7b8f 231 * keyboard I/O in the simulated version. The following code sets this
wini 3:c718bd8c7b8f 232 * up for WIN and UNIX. Remove this code for embedded use and change
wini 3:c718bd8c7b8f 233 * setLedFromDevice as explained below.
wini 3:c718bd8c7b8f 234 */
wini 3:c718bd8c7b8f 235 #include <ctype.h>
wini 3:c718bd8c7b8f 236 #ifdef _WIN32
wini 3:c718bd8c7b8f 237
wini 3:c718bd8c7b8f 238 #include <conio.h>
wini 3:c718bd8c7b8f 239 #define xkbhit _kbhit
wini 3:c718bd8c7b8f 240
wini 3:c718bd8c7b8f 241 static int
wini 3:c718bd8c7b8f 242 xgetch()
wini 3:c718bd8c7b8f 243 {
wini 3:c718bd8c7b8f 244 int c = _getch();
wini 3:c718bd8c7b8f 245 if(c == 224)
wini 3:c718bd8c7b8f 246 {
wini 3:c718bd8c7b8f 247 switch(_getch())
wini 3:c718bd8c7b8f 248 {
wini 3:c718bd8c7b8f 249 case 72: return KEY_UP_ARROW;
wini 3:c718bd8c7b8f 250 case 80: return KEY_DOWN_ARROW;
wini 3:c718bd8c7b8f 251 }
wini 3:c718bd8c7b8f 252 return 'A'; /* dummy value */
wini 3:c718bd8c7b8f 253 }
wini 3:c718bd8c7b8f 254 return c;
wini 3:c718bd8c7b8f 255
wini 3:c718bd8c7b8f 256 }
wini 3:c718bd8c7b8f 257
wini 3:c718bd8c7b8f 258
wini 3:c718bd8c7b8f 259 #else
wini 3:c718bd8c7b8f 260
wini 3:c718bd8c7b8f 261 #include <sys/socket.h>
wini 3:c718bd8c7b8f 262 #include <arpa/inet.h>
wini 3:c718bd8c7b8f 263 #include <netdb.h>
wini 3:c718bd8c7b8f 264 #include <sys/ioctl.h>
wini 3:c718bd8c7b8f 265 #include <net/if.h>
wini 3:c718bd8c7b8f 266 #include <termios.h>
wini 3:c718bd8c7b8f 267 /* UNIX kbhit and getch simulation */
wini 3:c718bd8c7b8f 268 static struct termios orgTs;
wini 3:c718bd8c7b8f 269
wini 3:c718bd8c7b8f 270 static void
wini 3:c718bd8c7b8f 271 resetTerminalMode()
wini 3:c718bd8c7b8f 272 {
wini 3:c718bd8c7b8f 273 tcsetattr(0, TCSANOW, &orgTs);
wini 3:c718bd8c7b8f 274 }
wini 3:c718bd8c7b8f 275
wini 3:c718bd8c7b8f 276 static void
wini 3:c718bd8c7b8f 277 setConioTerminalMode()
wini 3:c718bd8c7b8f 278 {
wini 3:c718bd8c7b8f 279 struct termios asyncTs;
wini 3:c718bd8c7b8f 280 tcgetattr(0, &orgTs);
wini 3:c718bd8c7b8f 281 memcpy(&asyncTs, &orgTs, sizeof(asyncTs));
wini 3:c718bd8c7b8f 282 /* register cleanup handler, and set the new terminal mode */
wini 3:c718bd8c7b8f 283 atexit(resetTerminalMode);
wini 3:c718bd8c7b8f 284 cfmakeraw(&asyncTs);
wini 3:c718bd8c7b8f 285 asyncTs.c_oflag=orgTs.c_oflag;
wini 3:c718bd8c7b8f 286 tcsetattr(0, TCSANOW, &asyncTs);
wini 3:c718bd8c7b8f 287 }
wini 3:c718bd8c7b8f 288
wini 3:c718bd8c7b8f 289 static int
wini 3:c718bd8c7b8f 290 xkbhit()
wini 3:c718bd8c7b8f 291 {
wini 3:c718bd8c7b8f 292 struct timeval tv = { 0L, 0L };
wini 3:c718bd8c7b8f 293 fd_set fds;
wini 3:c718bd8c7b8f 294 FD_ZERO(&fds);
wini 3:c718bd8c7b8f 295 FD_SET(0, &fds);
wini 3:c718bd8c7b8f 296 return select(1, &fds, NULL, NULL, &tv);
wini 3:c718bd8c7b8f 297 }
wini 3:c718bd8c7b8f 298
wini 3:c718bd8c7b8f 299 static int
wini 3:c718bd8c7b8f 300 xgetch()
wini 3:c718bd8c7b8f 301 {
wini 3:c718bd8c7b8f 302 int r;
wini 3:c718bd8c7b8f 303 unsigned char c;
wini 3:c718bd8c7b8f 304 if ((r = read(0, &c, sizeof(c))) < 0)
wini 3:c718bd8c7b8f 305 return r;
wini 3:c718bd8c7b8f 306 if(c == 3) /* CTRL-C Linux */
wini 3:c718bd8c7b8f 307 exit(0);
wini 3:c718bd8c7b8f 308 if(c == 27)
wini 3:c718bd8c7b8f 309 {
wini 3:c718bd8c7b8f 310 U16 x;
wini 3:c718bd8c7b8f 311 read(0, &x, sizeof(x));
wini 3:c718bd8c7b8f 312 switch(x)
wini 3:c718bd8c7b8f 313 {
wini 3:c718bd8c7b8f 314 case 0x415B: return KEY_UP_ARROW;
wini 3:c718bd8c7b8f 315 case 0x425B: return KEY_DOWN_ARROW;
wini 3:c718bd8c7b8f 316 }
wini 3:c718bd8c7b8f 317 return 'A'; /* dummy value */
wini 3:c718bd8c7b8f 318 }
wini 3:c718bd8c7b8f 319 return c;
wini 3:c718bd8c7b8f 320 }
wini 3:c718bd8c7b8f 321
wini 3:c718bd8c7b8f 322 static void
wini 3:c718bd8c7b8f 323 die(const char* fmt, ...)
wini 3:c718bd8c7b8f 324 {
wini 3:c718bd8c7b8f 325 va_list varg;
wini 3:c718bd8c7b8f 326 va_start(varg, fmt);
wini 3:c718bd8c7b8f 327 vprintf(fmt, varg);
wini 3:c718bd8c7b8f 328 va_end(varg);
wini 3:c718bd8c7b8f 329 printf("\n");
wini 3:c718bd8c7b8f 330 exit(1);
wini 3:c718bd8c7b8f 331 }
wini 3:c718bd8c7b8f 332
wini 3:c718bd8c7b8f 333 static void
wini 3:c718bd8c7b8f 334 getMacAddr(char macaddr[6], const char* ifname)
wini 3:c718bd8c7b8f 335 {
wini 3:c718bd8c7b8f 336 char buf[8192] = {0};
wini 3:c718bd8c7b8f 337 struct ifconf ifc = {0};
wini 3:c718bd8c7b8f 338 struct ifreq *ifr = NULL;
wini 3:c718bd8c7b8f 339 int sck = 0;
wini 3:c718bd8c7b8f 340 int nInterfaces = 0;
wini 3:c718bd8c7b8f 341 int i = 0;
wini 3:c718bd8c7b8f 342 struct ifreq *item;
wini 3:c718bd8c7b8f 343 struct sockaddr *addr;
wini 3:c718bd8c7b8f 344 /* Get a socket handle. */
wini 3:c718bd8c7b8f 345 sck = socket(PF_INET, SOCK_DGRAM, 0);
wini 3:c718bd8c7b8f 346 if(sck < 0)
wini 3:c718bd8c7b8f 347 die("socket: %s",strerror(errno));
wini 3:c718bd8c7b8f 348 /* Query available interfaces. */
wini 3:c718bd8c7b8f 349 ifc.ifc_len = sizeof(buf);
wini 3:c718bd8c7b8f 350 ifc.ifc_buf = buf;
wini 3:c718bd8c7b8f 351 if(ioctl(sck, SIOCGIFCONF, &ifc) < 0)
wini 3:c718bd8c7b8f 352 die("ioctl(SIOCGIFCONF) %s", strerror(errno));
wini 3:c718bd8c7b8f 353 /* Iterate through the list of interfaces. */
wini 3:c718bd8c7b8f 354 ifr = ifc.ifc_req;
wini 3:c718bd8c7b8f 355 nInterfaces = ifc.ifc_len / sizeof(struct ifreq);
wini 3:c718bd8c7b8f 356 for(i = 0; i < nInterfaces; i++)
wini 3:c718bd8c7b8f 357 {
wini 3:c718bd8c7b8f 358 unsigned long ipaddr;
wini 3:c718bd8c7b8f 359 item = &ifr[i];
wini 3:c718bd8c7b8f 360 addr = &(item->ifr_addr);
wini 3:c718bd8c7b8f 361 /* Get the IP address*/
wini 3:c718bd8c7b8f 362 if(ioctl(sck, SIOCGIFADDR, item) < 0)
wini 3:c718bd8c7b8f 363 {
wini 3:c718bd8c7b8f 364 perror("ioctl(OSIOCGIFADDR)");
wini 3:c718bd8c7b8f 365 continue;
wini 3:c718bd8c7b8f 366 }
wini 3:c718bd8c7b8f 367 ipaddr = ((struct sockaddr_in *)addr)->sin_addr.s_addr;
wini 3:c718bd8c7b8f 368 if(0x100007F == ipaddr || 0 == ipaddr)
wini 3:c718bd8c7b8f 369 continue;
wini 3:c718bd8c7b8f 370 /* Get the MAC address */
wini 3:c718bd8c7b8f 371 if(ioctl(sck, SIOCGIFHWADDR, item) < 0) {
wini 3:c718bd8c7b8f 372 perror("ioctl(SIOCGIFHWADDR)");
wini 3:c718bd8c7b8f 373 continue;
wini 3:c718bd8c7b8f 374 }
wini 3:c718bd8c7b8f 375 break;
wini 3:c718bd8c7b8f 376 }
wini 3:c718bd8c7b8f 377 close(sck);
wini 3:c718bd8c7b8f 378 if(i == nInterfaces)
wini 3:c718bd8c7b8f 379 die("Cannot get a MAC address\n");
wini 3:c718bd8c7b8f 380 memcpy(macaddr, item->ifr_hwaddr.sa_data, 6);
wini 3:c718bd8c7b8f 381 }
wini 3:c718bd8c7b8f 382 #endif
wini 3:c718bd8c7b8f 383 /* Endif UNIX/Linux specific code */
wini 3:c718bd8c7b8f 384
wini 3:c718bd8c7b8f 385
wini 3:c718bd8c7b8f 386 /* Optional function that can be used to turn an LED on/off from the
wini 3:c718bd8c7b8f 387 device by using buttons. The function must return TRUE if a button
wini 3:c718bd8c7b8f 388 was pressed, otherwise FALSE must be returned. The LED state on/off
wini 3:c718bd8c7b8f 389 information is managed by the online web service.
wini 3:c718bd8c7b8f 390 */
wini 3:c718bd8c7b8f 391 int
wini 3:c718bd8c7b8f 392 setLedFromDevice(int* ledId, int* on)
wini 3:c718bd8c7b8f 393 {
wini 3:c718bd8c7b8f 394 int ledLen;
wini 3:c718bd8c7b8f 395 const LedInfo* ledInfo = getLedInfo(&ledLen);
wini 3:c718bd8c7b8f 396 if(xkbhit())
wini 3:c718bd8c7b8f 397 {
wini 3:c718bd8c7b8f 398 int base,i;
wini 3:c718bd8c7b8f 399 int c = xgetch();
wini 3:c718bd8c7b8f 400 if(c == KEY_UP_ARROW)
wini 3:c718bd8c7b8f 401 {
wini 3:c718bd8c7b8f 402 currentTemperature += 8; /* increment by 0.8 celcius */
wini 3:c718bd8c7b8f 403 return 0;
wini 3:c718bd8c7b8f 404 }
wini 3:c718bd8c7b8f 405 if(c == KEY_DOWN_ARROW)
wini 3:c718bd8c7b8f 406 {
wini 3:c718bd8c7b8f 407 currentTemperature -= 8; /* decrement by 0.8 celcius */
wini 3:c718bd8c7b8f 408 return 0;
wini 3:c718bd8c7b8f 409 }
wini 3:c718bd8c7b8f 410 if(isupper(c))
wini 3:c718bd8c7b8f 411 {
wini 3:c718bd8c7b8f 412 *on=1;
wini 3:c718bd8c7b8f 413 base='A';
wini 3:c718bd8c7b8f 414 }
wini 3:c718bd8c7b8f 415 else
wini 3:c718bd8c7b8f 416 {
wini 3:c718bd8c7b8f 417 *on=0;
wini 3:c718bd8c7b8f 418 base='a';
wini 3:c718bd8c7b8f 419 }
wini 3:c718bd8c7b8f 420 c -= base;
wini 3:c718bd8c7b8f 421 base=0;
wini 3:c718bd8c7b8f 422 for(i = 0 ; i < ledLen ; i++)
wini 3:c718bd8c7b8f 423 {
wini 3:c718bd8c7b8f 424 if(ledInfo[i].id == c)
wini 3:c718bd8c7b8f 425 {
wini 3:c718bd8c7b8f 426 base=1;
wini 3:c718bd8c7b8f 427 break;
wini 3:c718bd8c7b8f 428 }
wini 3:c718bd8c7b8f 429 }
wini 3:c718bd8c7b8f 430 if(base)
wini 3:c718bd8c7b8f 431 {
wini 3:c718bd8c7b8f 432 *ledId = c;
wini 3:c718bd8c7b8f 433 return 1;
wini 3:c718bd8c7b8f 434 }
wini 3:c718bd8c7b8f 435 printf("Invalid LedId %d. Valid keys (upper/lower): ",c);
wini 3:c718bd8c7b8f 436 for(i = 0 ; i < ledLen ; i++)
wini 3:c718bd8c7b8f 437 printf("%c ",'A'+ledInfo[i].id);
wini 3:c718bd8c7b8f 438 printf("\n");
wini 3:c718bd8c7b8f 439 }
wini 3:c718bd8c7b8f 440
wini 3:c718bd8c7b8f 441 { /* Print out usage info at startup */
wini 3:c718bd8c7b8f 442 static int oneshot=0;
wini 3:c718bd8c7b8f 443 if( ! oneshot )
wini 3:c718bd8c7b8f 444 {
wini 3:c718bd8c7b8f 445 oneshot = 1;
wini 3:c718bd8c7b8f 446 xprintf(
wini 3:c718bd8c7b8f 447 ("Set LED from keyboard. Uppercase = ON, lowercase = OFF.\n"
wini 3:c718bd8c7b8f 448 "Switching LED state updates UI in all connected browsers.\n"));
wini 3:c718bd8c7b8f 449 }
wini 3:c718bd8c7b8f 450 }
wini 3:c718bd8c7b8f 451 return 0;
wini 3:c718bd8c7b8f 452 }
wini 3:c718bd8c7b8f 453
wini 3:c718bd8c7b8f 454
wini 3:c718bd8c7b8f 455 int getTemp(void)
wini 3:c718bd8c7b8f 456 {
wini 3:c718bd8c7b8f 457 return currentTemperature;
wini 3:c718bd8c7b8f 458 }
wini 3:c718bd8c7b8f 459
wini 3:c718bd8c7b8f 460
wini 3:c718bd8c7b8f 461 const char* getDevName(void)
wini 3:c718bd8c7b8f 462 {
wini 3:c718bd8c7b8f 463 static char devInfo[100];
wini 3:c718bd8c7b8f 464 char* ptr;
wini 3:c718bd8c7b8f 465 strcpy(devInfo,"Simulated Device: ");
wini 3:c718bd8c7b8f 466 ptr = devInfo + strlen(devInfo);
wini 3:c718bd8c7b8f 467 gethostname(ptr, 100 - (ptr - devInfo));
wini 3:c718bd8c7b8f 468 return devInfo;
wini 3:c718bd8c7b8f 469 }
wini 3:c718bd8c7b8f 470
wini 3:c718bd8c7b8f 471
wini 3:c718bd8c7b8f 472 static void printUniqueID(const char* uuid, int uuidLen)
wini 3:c718bd8c7b8f 473 {
wini 3:c718bd8c7b8f 474 int i;
wini 3:c718bd8c7b8f 475 printf("UUID: ");
wini 3:c718bd8c7b8f 476 for(i = 0 ; i < uuidLen ; i++)
wini 3:c718bd8c7b8f 477 printf("%X ", (unsigned int)((U8)uuid[i]));
wini 3:c718bd8c7b8f 478 printf("\n");
wini 3:c718bd8c7b8f 479 }
wini 3:c718bd8c7b8f 480
wini 3:c718bd8c7b8f 481
wini 3:c718bd8c7b8f 482 int getUniqueId(const char** id)
wini 3:c718bd8c7b8f 483 {
wini 3:c718bd8c7b8f 484 #ifdef _WIN32
wini 3:c718bd8c7b8f 485 UUID winid;
wini 3:c718bd8c7b8f 486 static char uuid[8];
wini 3:c718bd8c7b8f 487 if(RPC_S_OK == UuidCreateSequential(&winid))
wini 3:c718bd8c7b8f 488 memcpy(uuid,winid.Data4,8);
wini 3:c718bd8c7b8f 489 else
wini 3:c718bd8c7b8f 490 memset(uuid,0,8);
wini 3:c718bd8c7b8f 491 printUniqueID(uuid, 8);
wini 3:c718bd8c7b8f 492 *id = uuid;
wini 3:c718bd8c7b8f 493 return 8;
wini 3:c718bd8c7b8f 494 #else
wini 3:c718bd8c7b8f 495 static char addr[6];
wini 3:c718bd8c7b8f 496 getMacAddr(addr, "eth0");
wini 3:c718bd8c7b8f 497 printUniqueID(addr, 6);
wini 3:c718bd8c7b8f 498 *id = addr;
wini 3:c718bd8c7b8f 499 return 6;
wini 3:c718bd8c7b8f 500 #endif
wini 3:c718bd8c7b8f 501 }
wini 3:c718bd8c7b8f 502
wini 3:c718bd8c7b8f 503 int main(int argc, char* argv[])
wini 3:c718bd8c7b8f 504 {
wini 3:c718bd8c7b8f 505 #ifdef _WIN32
wini 3:c718bd8c7b8f 506 WSADATA wsaData;
wini 3:c718bd8c7b8f 507 /* Windows specific: Start winsock library */
wini 3:c718bd8c7b8f 508 WSAStartup(MAKEWORD(1,1), &wsaData);
wini 3:c718bd8c7b8f 509 #else
wini 3:c718bd8c7b8f 510 setConioTerminalMode();
wini 3:c718bd8c7b8f 511 #endif
wini 3:c718bd8c7b8f 512
wini 3:c718bd8c7b8f 513 if(argc > 1)
wini 3:c718bd8c7b8f 514 {
wini 3:c718bd8c7b8f 515 simpleMqUrl = argv[1];
wini 3:c718bd8c7b8f 516 xprintf(("Overriding broker URL\n\tfrom %s\n\tto %s\n%s\n\n",
wini 3:c718bd8c7b8f 517 SIMPLEMQ_URL,simpleMqUrl,
wini 3:c718bd8c7b8f 518 "Note: error messages will include original broker name"));
wini 3:c718bd8c7b8f 519
wini 3:c718bd8c7b8f 520 }
wini 3:c718bd8c7b8f 521 else
wini 3:c718bd8c7b8f 522 {
wini 3:c718bd8c7b8f 523 simpleMqUrl = SIMPLEMQ_URL;
wini 3:c718bd8c7b8f 524 }
wini 3:c718bd8c7b8f 525
wini 3:c718bd8c7b8f 526 mainTask(0);
wini 3:c718bd8c7b8f 527 printf("Press return key to exit\n");
wini 3:c718bd8c7b8f 528 getchar();
wini 3:c718bd8c7b8f 529 return 0;
wini 3:c718bd8c7b8f 530 }
wini 3:c718bd8c7b8f 531
wini 3:c718bd8c7b8f 532 #endif /* HOST_PLATFORM */
wini 3:c718bd8c7b8f 533
wini 3:c718bd8c7b8f 534
wini 3:c718bd8c7b8f 535
wini 3:c718bd8c7b8f 536
wini 3:c718bd8c7b8f 537 /****************************************************************************
wini 3:c718bd8c7b8f 538 **************************----------------------****************************
wini 3:c718bd8c7b8f 539 **************************| GENERIC CODE BELOW |****************************
wini 3:c718bd8c7b8f 540 **************************----------------------****************************
wini 3:c718bd8c7b8f 541 ****************************************************************************/
wini 3:c718bd8c7b8f 542
wini 3:c718bd8c7b8f 543 #ifdef GETUNIQUEID
wini 3:c718bd8c7b8f 544 /*
wini 3:c718bd8c7b8f 545 The following getUniqueId function can be enabled on development
wini 3:c718bd8c7b8f 546 boards where you cannot fetch the MAC address or if the MAC address
wini 3:c718bd8c7b8f 547 is not globally unique (not set from factory).
wini 3:c718bd8c7b8f 548
wini 3:c718bd8c7b8f 549 The SMQ broker requires a persistent (non random) unique ID for each
wini 3:c718bd8c7b8f 550 client. The broker uses this ID to detect board restarts, where the
wini 3:c718bd8c7b8f 551 old connection still lingers in the broker. The ID must for this
wini 3:c718bd8c7b8f 552 reason be the same when the board restarts. The following logic
wini 3:c718bd8c7b8f 553 fetches the WAN address from a specially constructed service. This
wini 3:c718bd8c7b8f 554 address is then combined with the local LAN address. The LAN address
wini 3:c718bd8c7b8f 555 will most likely be the same when the board restarts, even with
wini 3:c718bd8c7b8f 556 DHCP. The address may change if the board has been offline for a
wini 3:c718bd8c7b8f 557 longer time, but in that case, the lingering connection in the
wini 3:c718bd8c7b8f 558 broker will have been removed by the time the board restarts.
wini 3:c718bd8c7b8f 559 */
wini 3:c718bd8c7b8f 560 int getUniqueId(const char** id)
wini 3:c718bd8c7b8f 561 {
wini 3:c718bd8c7b8f 562 SOCKET sock;
wini 3:c718bd8c7b8f 563 S32 len = -1;
wini 3:c718bd8c7b8f 564 if( ! se_connect(&sock, "simplemq.com", 80) )
wini 3:c718bd8c7b8f 565 {
wini 3:c718bd8c7b8f 566 /* The following service is designed to send back the WAN IP
wini 3:c718bd8c7b8f 567 * address without sending any HTTP headers.
wini 3:c718bd8c7b8f 568 */
wini 3:c718bd8c7b8f 569 static const char cmd[]={
wini 3:c718bd8c7b8f 570 "GET /addr.lsp HTTP/1.0\r\nHost:simplemq.com\r\n\r\n"};
wini 3:c718bd8c7b8f 571 static char uuid[40];
wini 3:c718bd8c7b8f 572 se_send(&sock, cmd, sizeof(cmd)-1);
wini 3:c718bd8c7b8f 573 len = se_recv(&sock, uuid, sizeof(uuid) - 16, 1000);
wini 3:c718bd8c7b8f 574 if(len > 0)
wini 3:c718bd8c7b8f 575 {
wini 3:c718bd8c7b8f 576 int status;
wini 3:c718bd8c7b8f 577 se_getSockName(&sock,uuid+len,&status);
wini 3:c718bd8c7b8f 578 if( status > 1 )
wini 3:c718bd8c7b8f 579 {
wini 3:c718bd8c7b8f 580 *id = uuid;
wini 3:c718bd8c7b8f 581 len += status;
wini 3:c718bd8c7b8f 582 #if HOST_PLATFORM
wini 3:c718bd8c7b8f 583 printUniqueID(uuid, len);
wini 3:c718bd8c7b8f 584 #endif
wini 3:c718bd8c7b8f 585 }
wini 3:c718bd8c7b8f 586 }
wini 3:c718bd8c7b8f 587 se_close(&sock);
wini 3:c718bd8c7b8f 588 }
wini 3:c718bd8c7b8f 589 return len;
wini 3:c718bd8c7b8f 590 }
wini 3:c718bd8c7b8f 591 #endif
wini 3:c718bd8c7b8f 592
wini 3:c718bd8c7b8f 593
wini 3:c718bd8c7b8f 594
wini 3:c718bd8c7b8f 595
wini 3:c718bd8c7b8f 596 static const char*
wini 3:c718bd8c7b8f 597 ledType2String(LedColor t)
wini 3:c718bd8c7b8f 598 {
wini 3:c718bd8c7b8f 599 switch(t)
wini 3:c718bd8c7b8f 600 {
wini 3:c718bd8c7b8f 601 case LedColor_red: return "red";
wini 3:c718bd8c7b8f 602 case LedColor_yellow: return "yellow";
wini 3:c718bd8c7b8f 603 case LedColor_green: return "green";
wini 3:c718bd8c7b8f 604 case LedColor_blue: return "blue";
wini 3:c718bd8c7b8f 605 }
wini 3:c718bd8c7b8f 606 baAssert(0);
wini 3:c718bd8c7b8f 607 return "";
wini 3:c718bd8c7b8f 608 }
wini 3:c718bd8c7b8f 609
wini 3:c718bd8c7b8f 610
wini 3:c718bd8c7b8f 611 /* Send the device capabilities as JSON to the browser. Note: we could
wini 3:c718bd8c7b8f 612 have used our JSON library for creating the JSON, but the library
wini 3:c718bd8c7b8f 613 adds additional code. We have opted to manually craft the JSON
wini 3:c718bd8c7b8f 614 instead of using the JSON library. This keeps the code size
wini 3:c718bd8c7b8f 615 down. Manually crafting JSON data is easy, parsing JSON is not
wini 3:c718bd8c7b8f 616 easy. However, we have no need for parsing JSON in this demo.
wini 3:c718bd8c7b8f 617
wini 3:c718bd8c7b8f 618 You can optionally rewrite this code and use the following JSON
wini 3:c718bd8c7b8f 619 library: https://realtimelogic.com/products/json/
wini 3:c718bd8c7b8f 620
wini 3:c718bd8c7b8f 621 When you manually craft JSON, it can be good to use a JSON lint
wini 3:c718bd8c7b8f 622 parser if you should get a parse error in the browser. The JSON
wini 3:c718bd8c7b8f 623 lint parser will give you much better error reporting:
wini 3:c718bd8c7b8f 624 http://jsonlint.com/
wini 3:c718bd8c7b8f 625 */
wini 3:c718bd8c7b8f 626 static int
wini 3:c718bd8c7b8f 627 sendDevInfo(SharkMQ* smq, const char* ipaddr, U32 tid, U32 subtid)
wini 3:c718bd8c7b8f 628 {
wini 3:c718bd8c7b8f 629 int i, ledLen;
wini 3:c718bd8c7b8f 630 char buf[11];
wini 3:c718bd8c7b8f 631 char *ptr;
wini 3:c718bd8c7b8f 632 int val;
wini 3:c718bd8c7b8f 633 const LedInfo* ledInfo = getLedInfo(&ledLen);
wini 3:c718bd8c7b8f 634
wini 3:c718bd8c7b8f 635 SharkMQ_wrtstr(smq, "{\"ipaddr\":\"");
wini 3:c718bd8c7b8f 636 SharkMQ_wrtstr(smq, ipaddr);
wini 3:c718bd8c7b8f 637 SharkMQ_wrtstr(smq, "\",\"devname\":\"");
wini 3:c718bd8c7b8f 638 SharkMQ_wrtstr(smq, getDevName());
wini 3:c718bd8c7b8f 639 SharkMQ_wrtstr(smq, "\",\"leds\":[");
wini 3:c718bd8c7b8f 640
wini 3:c718bd8c7b8f 641 /* Write JSON:
wini 3:c718bd8c7b8f 642 {
wini 3:c718bd8c7b8f 643 "id": number,
wini 3:c718bd8c7b8f 644 "name": string,
wini 3:c718bd8c7b8f 645 "color": string,
wini 3:c718bd8c7b8f 646 "on": boolean
wini 3:c718bd8c7b8f 647 }
wini 3:c718bd8c7b8f 648 */
wini 3:c718bd8c7b8f 649 for(i = 0 ; i < ledLen ; i++)
wini 3:c718bd8c7b8f 650 {
wini 3:c718bd8c7b8f 651 ptr = &buf[(sizeof buf) - 1];
wini 3:c718bd8c7b8f 652 val = ledInfo[i].id;
wini 3:c718bd8c7b8f 653 *ptr = 0;
wini 3:c718bd8c7b8f 654 if(i != 0)
wini 3:c718bd8c7b8f 655 SharkMQ_wrtstr(smq,",");
wini 3:c718bd8c7b8f 656 SharkMQ_wrtstr(smq, "{\"id\":");
wini 3:c718bd8c7b8f 657 do { /* convert number to string */
wini 3:c718bd8c7b8f 658 int r = val % 10U;
wini 3:c718bd8c7b8f 659 val /= 10U;
wini 3:c718bd8c7b8f 660 *--ptr = (char)('0' + r);
wini 3:c718bd8c7b8f 661 } while (val && ptr > buf);
wini 3:c718bd8c7b8f 662 SharkMQ_wrtstr(smq, ptr); /* number converted to string */
wini 3:c718bd8c7b8f 663 SharkMQ_wrtstr(smq, ",\"name\":\"");
wini 3:c718bd8c7b8f 664 SharkMQ_wrtstr(smq, ledInfo[i].name);
wini 3:c718bd8c7b8f 665 SharkMQ_wrtstr(smq, "\",\"color\":\"");
wini 3:c718bd8c7b8f 666 SharkMQ_wrtstr(smq, ledType2String(ledInfo[i].color));
wini 3:c718bd8c7b8f 667 SharkMQ_wrtstr(smq, "\",\"on\":");
wini 3:c718bd8c7b8f 668 SharkMQ_wrtstr(smq, getLedState(ledInfo[i].id)?"true":"false");
wini 3:c718bd8c7b8f 669 SharkMQ_wrtstr(smq, "}");
wini 3:c718bd8c7b8f 670 }
wini 3:c718bd8c7b8f 671 #ifdef ENABLE_TEMP
wini 3:c718bd8c7b8f 672 SharkMQ_wrtstr(smq, "],\"temp\":");
wini 3:c718bd8c7b8f 673 ptr = &buf[(sizeof buf) - 1];
wini 3:c718bd8c7b8f 674 *ptr = 0;
wini 3:c718bd8c7b8f 675 val = getTemp();
wini 3:c718bd8c7b8f 676 if(val < 0)
wini 3:c718bd8c7b8f 677 {
wini 3:c718bd8c7b8f 678 val = -val;
wini 3:c718bd8c7b8f 679 i=TRUE;
wini 3:c718bd8c7b8f 680 }
wini 3:c718bd8c7b8f 681 else
wini 3:c718bd8c7b8f 682 i=FALSE;
wini 3:c718bd8c7b8f 683 do { /* convert number to string */
wini 3:c718bd8c7b8f 684 int r = val % 10U;
wini 3:c718bd8c7b8f 685 val /= 10U;
wini 3:c718bd8c7b8f 686 *--ptr = (char)('0' + r);
wini 3:c718bd8c7b8f 687 } while (val && ptr > buf);
wini 3:c718bd8c7b8f 688 if(i)
wini 3:c718bd8c7b8f 689 *--ptr='-';
wini 3:c718bd8c7b8f 690 SharkMQ_wrtstr(smq, ptr); /* number converted to string */
wini 3:c718bd8c7b8f 691 SharkMQ_wrtstr(smq, "}");
wini 3:c718bd8c7b8f 692 #else
wini 3:c718bd8c7b8f 693 SharkMQ_wrtstr(smq, "]}");
wini 3:c718bd8c7b8f 694 #endif
wini 3:c718bd8c7b8f 695 return SharkMQ_pubflush(smq,tid,subtid);
wini 3:c718bd8c7b8f 696 }
wini 3:c718bd8c7b8f 697
wini 3:c718bd8c7b8f 698
wini 3:c718bd8c7b8f 699 /*
wini 3:c718bd8c7b8f 700 The M2M-LED main function does not return unless the code cannot
wini 3:c718bd8c7b8f 701 connect to the broker or the connection should break. The return
wini 3:c718bd8c7b8f 702 value 0 means that the caller can attempt to reconnect by calling
wini 3:c718bd8c7b8f 703 this function again.
wini 3:c718bd8c7b8f 704 */
wini 3:c718bd8c7b8f 705 static int
wini 3:c718bd8c7b8f 706 m2mled(SharkMQ* smq, SharkSslCon* scon,
wini 3:c718bd8c7b8f 707 const char* smqUniqueId, int smqUniqueIdLen)
wini 3:c718bd8c7b8f 708 {
wini 3:c718bd8c7b8f 709 U32 displayTid=0; /* Topic ID (Tid) for "/m2m/led/display" */
wini 3:c718bd8c7b8f 710 U32 ledSubTid=0; /* Sub Topic ID for "led" */
wini 3:c718bd8c7b8f 711 U32 deviceTid=0; /* Tid for "/m2m/led/device" */
wini 3:c718bd8c7b8f 712 U32 devInfoSubTid=0; /* Sub Topic ID for "devinfo" */
wini 3:c718bd8c7b8f 713 #ifdef ENABLE_TEMP
wini 3:c718bd8c7b8f 714 U32 tempTid=0; /* Tid for "/m2m/temp" */
wini 3:c718bd8c7b8f 715 S16 temperature = (S16)getTemp(); /* current temperature */
wini 3:c718bd8c7b8f 716 #endif
wini 3:c718bd8c7b8f 717 char ipaddr[16];
wini 3:c718bd8c7b8f 718
wini 3:c718bd8c7b8f 719 /* We make it possible to override the URL at the command prompt when
wini 3:c718bd8c7b8f 720 * in simulation mode.
wini 3:c718bd8c7b8f 721 */
wini 3:c718bd8c7b8f 722 #if HOST_PLATFORM
wini 3:c718bd8c7b8f 723 const char* str=simpleMqUrl;
wini 3:c718bd8c7b8f 724 #else
wini 3:c718bd8c7b8f 725 const char* str=SIMPLEMQ_URL;
wini 3:c718bd8c7b8f 726 #endif
wini 3:c718bd8c7b8f 727
wini 3:c718bd8c7b8f 728 xprintf(("Connecting to %s\n", str));
wini 3:c718bd8c7b8f 729 smq->timeout = 3000; /* Bail out if the connection takes this long */
wini 3:c718bd8c7b8f 730 setProgramStatus(ProgramStatus_Connecting);
wini 3:c718bd8c7b8f 731 if(SharkMQ_init(smq, scon, str, 0) < 0)
wini 3:c718bd8c7b8f 732 {
wini 3:c718bd8c7b8f 733 xprintf(("Cannot establish connection, status: %d\n", smq->status));
wini 3:c718bd8c7b8f 734 switch(smq->status)
wini 3:c718bd8c7b8f 735 {
wini 3:c718bd8c7b8f 736 case -1:
wini 3:c718bd8c7b8f 737 str="Socket error!";
wini 3:c718bd8c7b8f 738 setProgramStatus(ProgramStatus_SocketError);
wini 3:c718bd8c7b8f 739 break;
wini 3:c718bd8c7b8f 740 case -2:
wini 3:c718bd8c7b8f 741 str="Cannot resolve IP address for " SIMPLEMQ_DOMAIN ".";
wini 3:c718bd8c7b8f 742 setProgramStatus(ProgramStatus_DnsError);
wini 3:c718bd8c7b8f 743 break;
wini 3:c718bd8c7b8f 744 default:
wini 3:c718bd8c7b8f 745 str="Cannot connect to " SIMPLEMQ_DOMAIN ".";
wini 3:c718bd8c7b8f 746 setProgramStatus(ProgramStatus_ConnectionError);
wini 3:c718bd8c7b8f 747 break;
wini 3:c718bd8c7b8f 748 }
wini 3:c718bd8c7b8f 749 xprintf(("%s\n", str));
wini 3:c718bd8c7b8f 750 /* Cannot reconnect if any of the following are true: */
wini 3:c718bd8c7b8f 751 return smq->status==SMQE_BUF_OVERFLOW || smq->status==SMQE_INVALID_URL;
wini 3:c718bd8c7b8f 752 }
wini 3:c718bd8c7b8f 753
wini 3:c718bd8c7b8f 754 /* Fetch the IP address sent by the broker. We use this for the
wini 3:c718bd8c7b8f 755 * text shown in the left pane tab in the browser's user interface.
wini 3:c718bd8c7b8f 756 */
wini 3:c718bd8c7b8f 757 strncpy(ipaddr, (char*)smq->buf, 16);
wini 3:c718bd8c7b8f 758 ipaddr[15]=0;
wini 3:c718bd8c7b8f 759 if(SharkMQ_connect(smq,
wini 3:c718bd8c7b8f 760 smqUniqueId, smqUniqueIdLen,
wini 3:c718bd8c7b8f 761 0, 0, /* credentials */
wini 3:c718bd8c7b8f 762 getDevName(), strlen(getDevName())))
wini 3:c718bd8c7b8f 763 {
wini 3:c718bd8c7b8f 764 xprintf(("Connect failed, status: %d\n", smq->status));
wini 3:c718bd8c7b8f 765 return smq->status == SMQE_BUF_OVERFLOW || smq->status > 0;
wini 3:c718bd8c7b8f 766 }
wini 3:c718bd8c7b8f 767
wini 3:c718bd8c7b8f 768 xprintf(("\nConnected to %s.\n"
wini 3:c718bd8c7b8f 769 "Use a browser and navigate to this domain.\n\n",
wini 3:c718bd8c7b8f 770 str));
wini 3:c718bd8c7b8f 771 setProgramStatus(ProgramStatus_DeviceReady);
wini 3:c718bd8c7b8f 772
wini 3:c718bd8c7b8f 773 /* Request broker to return a topic ID for "/m2m/led/device", the
wini 3:c718bd8c7b8f 774 * topic where we publish the device capabilities as JSON data.
wini 3:c718bd8c7b8f 775 */
wini 3:c718bd8c7b8f 776 SharkMQ_create(smq, "/m2m/led/device");
wini 3:c718bd8c7b8f 777
wini 3:c718bd8c7b8f 778 #ifdef ENABLE_TEMP
wini 3:c718bd8c7b8f 779 /* Request broker to return a topic ID for "/m2m/temp", the
wini 3:c718bd8c7b8f 780 * topic where we publish the device temperature.
wini 3:c718bd8c7b8f 781 */
wini 3:c718bd8c7b8f 782 SharkMQ_create(smq, "/m2m/temp");
wini 3:c718bd8c7b8f 783 #endif
wini 3:c718bd8c7b8f 784
wini 3:c718bd8c7b8f 785
wini 3:c718bd8c7b8f 786 /** Request broker to create two sub-topic IDs. We use the
wini 3:c718bd8c7b8f 787 * sub-topic IDs to further refine the messages published to the
wini 3:c718bd8c7b8f 788 * browser(s).
wini 3:c718bd8c7b8f 789 */
wini 3:c718bd8c7b8f 790 SharkMQ_createsub(smq, "devinfo");
wini 3:c718bd8c7b8f 791 SharkMQ_createsub(smq, "led");
wini 3:c718bd8c7b8f 792
wini 3:c718bd8c7b8f 793 /* Subscribe to browser "hello" messages. We send the device
wini 3:c718bd8c7b8f 794 * capabilities as JSON data to the browser's ephemeral ID when we
wini 3:c718bd8c7b8f 795 * receive a hello message from a browser.
wini 3:c718bd8c7b8f 796 */
wini 3:c718bd8c7b8f 797 SharkMQ_subscribe(smq, "/m2m/led/display");
wini 3:c718bd8c7b8f 798
wini 3:c718bd8c7b8f 799 smq->timeout=50; /* Poll so we can locally update the LED buttons. */
wini 3:c718bd8c7b8f 800 for(;;)
wini 3:c718bd8c7b8f 801 {
wini 3:c718bd8c7b8f 802 U8* msg;
wini 3:c718bd8c7b8f 803 U8 outData[2];
wini 3:c718bd8c7b8f 804 int len = SharkMQ_getMessage(smq, &msg);
wini 3:c718bd8c7b8f 805 if(len < 0) /* We received a control message or an error code */
wini 3:c718bd8c7b8f 806 {
wini 3:c718bd8c7b8f 807 switch(len)
wini 3:c718bd8c7b8f 808 {
wini 3:c718bd8c7b8f 809 /* Control messages */
wini 3:c718bd8c7b8f 810
wini 3:c718bd8c7b8f 811 /* Manage responses for create, createsub, and subscribe */
wini 3:c718bd8c7b8f 812 case SMQ_SUBACK: /* ACK: "/m2m/led/display" */
wini 3:c718bd8c7b8f 813 displayTid = smq->ptid;
wini 3:c718bd8c7b8f 814 break;
wini 3:c718bd8c7b8f 815 case SMQ_CREATEACK: /* ACK: "/m2m/temp" or "/m2m/led/device" */
wini 3:c718bd8c7b8f 816 #ifdef ENABLE_TEMP
wini 3:c718bd8c7b8f 817 if( ! strcmp("/m2m/temp", (char*)msg ) )
wini 3:c718bd8c7b8f 818 tempTid = smq->ptid;
wini 3:c718bd8c7b8f 819 else
wini 3:c718bd8c7b8f 820 #endif
wini 3:c718bd8c7b8f 821 {
wini 3:c718bd8c7b8f 822 deviceTid = smq->ptid;
wini 3:c718bd8c7b8f 823 SharkMQ_observe(smq, deviceTid);
wini 3:c718bd8c7b8f 824 }
wini 3:c718bd8c7b8f 825 break;
wini 3:c718bd8c7b8f 826 case SMQ_CREATESUBACK: /* We get a total of two messages */
wini 3:c718bd8c7b8f 827 /* We get two suback messages ("devinfo" and "led") */
wini 3:c718bd8c7b8f 828 if( ! strcmp("led", (char*)msg ) )
wini 3:c718bd8c7b8f 829 { /* Ack for: SMQ_createsub(smq, "led"); */
wini 3:c718bd8c7b8f 830 ledSubTid = smq->ptid;
wini 3:c718bd8c7b8f 831 }
wini 3:c718bd8c7b8f 832 else /* Must be ACK for devinfo */
wini 3:c718bd8c7b8f 833 { /* Ack for: SMQ_createsub(smq, "devinfo"); */
wini 3:c718bd8c7b8f 834 baAssert( ! strcmp("devinfo", (char*)msg) );
wini 3:c718bd8c7b8f 835 baAssert(deviceTid); /* acks are in sequence */
wini 3:c718bd8c7b8f 836 devInfoSubTid = smq->ptid;
wini 3:c718bd8c7b8f 837 /* We have sufficient info for publishing the device
wini 3:c718bd8c7b8f 838 info message. All connected browsers will
wini 3:c718bd8c7b8f 839 receive this message and update their UI accordingly.
wini 3:c718bd8c7b8f 840 */
wini 3:c718bd8c7b8f 841 sendDevInfo(smq, ipaddr, deviceTid, devInfoSubTid);
wini 3:c718bd8c7b8f 842 }
wini 3:c718bd8c7b8f 843 break;
wini 3:c718bd8c7b8f 844
wini 3:c718bd8c7b8f 845 case SMQ_SUBCHANGE:
wini 3:c718bd8c7b8f 846 xprintf(("Connected browsers: %d\n", smq->status));
wini 3:c718bd8c7b8f 847 break;
wini 3:c718bd8c7b8f 848
wini 3:c718bd8c7b8f 849 /* Error codes */
wini 3:c718bd8c7b8f 850
wini 3:c718bd8c7b8f 851 case SMQE_DISCONNECT:
wini 3:c718bd8c7b8f 852 xprintf(("Disconnect request from server\n"));
wini 3:c718bd8c7b8f 853 setProgramStatus(ProgramStatus_CloseCommandReceived);
wini 3:c718bd8c7b8f 854 return -1;
wini 3:c718bd8c7b8f 855
wini 3:c718bd8c7b8f 856 case SMQE_BUF_OVERFLOW:
wini 3:c718bd8c7b8f 857 xprintf(("Increase buffer size\n"));
wini 3:c718bd8c7b8f 858 setProgramStatus(ProgramStatus_MemoryError);
wini 3:c718bd8c7b8f 859 return -1; /* Must exit */
wini 3:c718bd8c7b8f 860
wini 3:c718bd8c7b8f 861 default:
wini 3:c718bd8c7b8f 862 xprintf(("Rec Error: %d.\n",smq->status));
wini 3:c718bd8c7b8f 863 setProgramStatus(ProgramStatus_InvalidCommandError);
wini 3:c718bd8c7b8f 864 return 0;
wini 3:c718bd8c7b8f 865 }
wini 3:c718bd8c7b8f 866 }
wini 3:c718bd8c7b8f 867 else if(len > 0)
wini 3:c718bd8c7b8f 868 {
wini 3:c718bd8c7b8f 869 if(smq->tid == displayTid) /* topic "display" */
wini 3:c718bd8c7b8f 870 {
wini 3:c718bd8c7b8f 871 /* Send device info to the new display unit: Send to
wini 3:c718bd8c7b8f 872 * browser's ephemeral ID (ptid).
wini 3:c718bd8c7b8f 873 */
wini 3:c718bd8c7b8f 874 sendDevInfo(smq, ipaddr, smq->ptid, devInfoSubTid);
wini 3:c718bd8c7b8f 875 }
wini 3:c718bd8c7b8f 876 else if(smq->tid == smq->clientTid) /* sent to our ephemeral tid */
wini 3:c718bd8c7b8f 877 {
wini 3:c718bd8c7b8f 878 if(setLed(msg[0], msg[1]))
wini 3:c718bd8c7b8f 879 {
wini 3:c718bd8c7b8f 880 xprintf(("ptid %X attempting to set invalid LED %d\n",
wini 3:c718bd8c7b8f 881 smq->ptid, msg[0]));
wini 3:c718bd8c7b8f 882 return 0;
wini 3:c718bd8c7b8f 883 }
wini 3:c718bd8c7b8f 884 /* Update all display units */
wini 3:c718bd8c7b8f 885 outData[0] = msg[0];
wini 3:c718bd8c7b8f 886 outData[1] = msg[1];
wini 3:c718bd8c7b8f 887 /* Publish to "/m2m/led/device", sub-topic "led" */
wini 3:c718bd8c7b8f 888 SharkMQ_publish(smq, outData, 2, deviceTid, ledSubTid);
wini 3:c718bd8c7b8f 889 }
wini 3:c718bd8c7b8f 890 else
wini 3:c718bd8c7b8f 891 {
wini 3:c718bd8c7b8f 892 xprintf(("Received unknown tid %X\n", smq->tid));
wini 3:c718bd8c7b8f 893 return 0;
wini 3:c718bd8c7b8f 894 }
wini 3:c718bd8c7b8f 895 }
wini 3:c718bd8c7b8f 896 else /* timeout */
wini 3:c718bd8c7b8f 897 {
wini 3:c718bd8c7b8f 898 int x; /* used for storing ledId and temperature */
wini 3:c718bd8c7b8f 899 int on;
wini 3:c718bd8c7b8f 900 if(setLedFromDevice(&x,&on)) /* If a local button was pressed */
wini 3:c718bd8c7b8f 901 { /* Publish to all subscribed browsers and set the LED on/off */
wini 3:c718bd8c7b8f 902 outData[0] = (U8)x; /* x is ledId */
wini 3:c718bd8c7b8f 903 outData[1] = (U8)on;
wini 3:c718bd8c7b8f 904 /* Publish to "/m2m/led/device", sub-topic "led" */
wini 3:c718bd8c7b8f 905 SharkMQ_publish(smq, outData, 2, deviceTid, ledSubTid);
wini 3:c718bd8c7b8f 906 setLed(x, on); /* set the LED on/off */
wini 3:c718bd8c7b8f 907 }
wini 3:c718bd8c7b8f 908 #ifdef ENABLE_TEMP
wini 3:c718bd8c7b8f 909 x = getTemp();
wini 3:c718bd8c7b8f 910 if(x != (int)temperature)
wini 3:c718bd8c7b8f 911 {
wini 3:c718bd8c7b8f 912 temperature = (S16)x;
wini 3:c718bd8c7b8f 913 outData[0] = (U8)(temperature >> 8);
wini 3:c718bd8c7b8f 914 outData[1] = (U8)temperature;
wini 3:c718bd8c7b8f 915 SharkMQ_publish(smq, outData, 2, tempTid, 0);
wini 3:c718bd8c7b8f 916 }
wini 3:c718bd8c7b8f 917 #endif
wini 3:c718bd8c7b8f 918 }
wini 3:c718bd8c7b8f 919 }
wini 3:c718bd8c7b8f 920 }
wini 3:c718bd8c7b8f 921
wini 3:c718bd8c7b8f 922
wini 3:c718bd8c7b8f 923
wini 3:c718bd8c7b8f 924 #if SHARKSSL_ENABLE_SELECT_CIPHERSUITE == 1
wini 3:c718bd8c7b8f 925 static void
wini 3:c718bd8c7b8f 926 setChaChaCipher(SharkSslCon* scon)
wini 3:c718bd8c7b8f 927 {
wini 3:c718bd8c7b8f 928 if(scon)
wini 3:c718bd8c7b8f 929 SharkSslCon_selectCiphersuite(
wini 3:c718bd8c7b8f 930 scon, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256);
wini 3:c718bd8c7b8f 931 }
wini 3:c718bd8c7b8f 932 #else
wini 3:c718bd8c7b8f 933 #define setChaChaCipher(scon)
wini 3:c718bd8c7b8f 934 #endif
wini 3:c718bd8c7b8f 935
wini 3:c718bd8c7b8f 936
wini 3:c718bd8c7b8f 937 void
wini 3:c718bd8c7b8f 938 mainTask(SeCtx* ctx)
wini 3:c718bd8c7b8f 939 {
wini 3:c718bd8c7b8f 940 static SharkSsl sharkSsl;
wini 3:c718bd8c7b8f 941 static SharkSslCon* scon;
wini 3:c718bd8c7b8f 942 static SharkMQ smq;
wini 3:c718bd8c7b8f 943 static U8 buf[127];
wini 3:c718bd8c7b8f 944
wini 3:c718bd8c7b8f 945 const char* smqUniqueId;
wini 3:c718bd8c7b8f 946 int smqUniqueIdLen;
wini 3:c718bd8c7b8f 947 smqUniqueIdLen = getUniqueId(&smqUniqueId);
wini 3:c718bd8c7b8f 948 if(smqUniqueIdLen < 1)
wini 3:c718bd8c7b8f 949 {
wini 3:c718bd8c7b8f 950 xprintf(("Cannot get unique ID: aborting.\n"));
wini 3:c718bd8c7b8f 951 setProgramStatus(ProgramStatus_SocketError);
wini 3:c718bd8c7b8f 952 return;
wini 3:c718bd8c7b8f 953 }
wini 3:c718bd8c7b8f 954
wini 3:c718bd8c7b8f 955 SharkSsl_constructor(&sharkSsl,
wini 3:c718bd8c7b8f 956 SharkSsl_Client, /* Two options: client or server */
wini 3:c718bd8c7b8f 957 0, /* Not using SSL cache */
wini 3:c718bd8c7b8f 958 2000, /* initial inBuf size: Can grow */
wini 3:c718bd8c7b8f 959 2000); /* outBuf size: Fixed */
wini 3:c718bd8c7b8f 960
wini 3:c718bd8c7b8f 961
wini 3:c718bd8c7b8f 962 /* (Ref-CA)
wini 3:c718bd8c7b8f 963 The following construction force the server to authenticate itself by
wini 3:c718bd8c7b8f 964 using an Elliptic Curve Certificate.
wini 3:c718bd8c7b8f 965
wini 3:c718bd8c7b8f 966 CA_RTL_EC_256.pem is a Certificate Authority (CA) certificate
wini 3:c718bd8c7b8f 967 and we use this certificate to validate that we are in fact
wini 3:c718bd8c7b8f 968 connecting to simplemq.com and not someone pretending to
wini 3:c718bd8c7b8f 969 be this server.
wini 3:c718bd8c7b8f 970
wini 3:c718bd8c7b8f 971 The online server simplemq.com has two certificates installed:
wini 3:c718bd8c7b8f 972 one standard RSA certificate, which you can see if you navigate
wini 3:c718bd8c7b8f 973 to https://simplemq.com, and one Elliptic Curve Certificate
wini 3:c718bd8c7b8f 974 (ECC).
wini 3:c718bd8c7b8f 975
wini 3:c718bd8c7b8f 976 The online server is configured to favor the RSA certificate
wini 3:c718bd8c7b8f 977 which will be presented to the client unless the client tells
wini 3:c718bd8c7b8f 978 the server that it can only use ECC, at which point the server
wini 3:c718bd8c7b8f 979 is forced to send its ECC certificate to the client.
wini 3:c718bd8c7b8f 980
wini 3:c718bd8c7b8f 981 The following construction makes sure we only use ECC by either
wini 3:c718bd8c7b8f 982 using a SharkSSL library compiled with ECC support only (no RSA)
wini 3:c718bd8c7b8f 983 or by specifically setting a cipher using ECC.
wini 3:c718bd8c7b8f 984
wini 3:c718bd8c7b8f 985 See the following link for more information:
wini 3:c718bd8c7b8f 986 realtimelogic.com/ba/doc/en/C/shark/md_md_Certificate_Management.html
wini 3:c718bd8c7b8f 987 */
wini 3:c718bd8c7b8f 988 SharkSsl_setCAList(&sharkSsl, sharkSslCAList);
wini 3:c718bd8c7b8f 989
wini 3:c718bd8c7b8f 990 SharkMQ_constructor(&smq, buf, sizeof(buf));
wini 3:c718bd8c7b8f 991 SharkMQ_setCtx(&smq, ctx); /* Required for non RTOS env. */
wini 3:c718bd8c7b8f 992
wini 3:c718bd8c7b8f 993 /* It is very important to seed the SharkSSL RNG generator */
wini 3:c718bd8c7b8f 994 sharkssl_entropy(baGetUnixTime() ^ (U32)&sharkSsl);
wini 3:c718bd8c7b8f 995 scon = SharkSsl_createCon(&sharkSsl);
wini 3:c718bd8c7b8f 996 setChaChaCipher(scon);
wini 3:c718bd8c7b8f 997 setProgramStatus(ProgramStatus_Starting);
wini 3:c718bd8c7b8f 998 while(scon && ! m2mled(&smq, scon, smqUniqueId, smqUniqueIdLen) )
wini 3:c718bd8c7b8f 999 {
wini 3:c718bd8c7b8f 1000 xprintf(("Closing connection; status: %d\n",smq.status));
wini 3:c718bd8c7b8f 1001 SharkMQ_disconnect(&smq);
wini 3:c718bd8c7b8f 1002 SharkSsl_terminateCon(&sharkSsl, scon);
wini 3:c718bd8c7b8f 1003 scon = SharkSsl_createCon(&sharkSsl);
wini 3:c718bd8c7b8f 1004 setChaChaCipher(scon);
wini 3:c718bd8c7b8f 1005 setProgramStatus(ProgramStatus_Restarting);
wini 3:c718bd8c7b8f 1006 }
wini 3:c718bd8c7b8f 1007 SharkMQ_destructor(&smq);
wini 3:c718bd8c7b8f 1008 if(scon)
wini 3:c718bd8c7b8f 1009 SharkSsl_terminateCon(&sharkSsl, scon);
wini 3:c718bd8c7b8f 1010 else
wini 3:c718bd8c7b8f 1011 setProgramStatus(ProgramStatus_MemoryError);
wini 3:c718bd8c7b8f 1012 SharkSsl_destructor(&sharkSsl);
wini 3:c718bd8c7b8f 1013 }
wini 3:c718bd8c7b8f 1014