Example program that shows how to use the Secure (TLS) SharkMQ library included in SharkSSL-Lite

Dependencies:   EthernetInterface SharkSSL-Lite 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 (this mbed example)
  • SMQjs (Javascript)
  • SMQ JAVA
  • SMQ Broker

/media/uploads/wini/smq_architecture.png

SharkMQ-LED-Demo

An example of (Secure) device control via a Browser interface. It includes the LED demonstration example code, SharkMQ, and SharkSSL SSL|TLS stack for authentication and encryption.

Video Tutorials

How to setup your own secure 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.

SMQ LED Demo

See Also

Committer:
wini
Date:
Fri Apr 08 16:44:27 2016 +0000
Revision:
5:6526b95442e7
Parent:
0:10b4212318d4
Readme file fix

Who changed what in which revision?

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