ARM mbed M2X API Client: The ARM mbed client library is used to send/receive data to/from AT&T's M2X service from mbed LPC1768 microcontrollers.

Dependents:   m2x-demo-all M2X_MTS_ACCEL_DEMO M2X_MTS_Accel M2X_K64F_ACCEL ... more

Committer:
defmacro
Date:
Wed Jun 15 12:36:55 2016 +0000
Revision:
22:4d895e732765
Parent:
21:6878944d2ce2
Update to new single header based implementation

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jb8414 0:f479e4f4db0e 1 #ifndef M2XStreamClient_h
jb8414 0:f479e4f4db0e 2 #define M2XStreamClient_h
jb8414 0:f479e4f4db0e 3
defmacro 22:4d895e732765 4 #if (!defined(ARDUINO_PLATFORM)) && (!defined(ESP8266_PLATFORM)) && (!defined(MBED_PLATFORM))
defmacro 22:4d895e732765 5 #error "Platform definition is missing!"
jb8414 0:f479e4f4db0e 6 #endif
jb8414 0:f479e4f4db0e 7
defmacro 22:4d895e732765 8 #define M2X_VERSION "2.2.0"
jb8414 0:f479e4f4db0e 9
jb8414 0:f479e4f4db0e 10 #ifdef ARDUINO_PLATFORM
defmacro 22:4d895e732765 11 #include "m2x-arduino.h"
defmacro 22:4d895e732765 12 #endif /* ARDUINO_PLATFORM */
defmacro 22:4d895e732765 13
defmacro 22:4d895e732765 14 #ifdef ESP8266_PLATFORM
defmacro 22:4d895e732765 15 #include "m2x-esp8266.h"
defmacro 22:4d895e732765 16 #endif /* ESP8266_PLATFORM */
jb8414 0:f479e4f4db0e 17
jb8414 0:f479e4f4db0e 18 #ifdef MBED_PLATFORM
defmacro 22:4d895e732765 19 #include "m2x-mbed.h"
defmacro 22:4d895e732765 20 #endif /* MBED_PLATFORM */
defmacro 22:4d895e732765 21
defmacro 22:4d895e732765 22 /* If we don't have DBG defined, provide dump implementation */
defmacro 22:4d895e732765 23 #ifndef DBG
jb8414 0:f479e4f4db0e 24 #define DBG(fmt_, data_)
jb8414 0:f479e4f4db0e 25 #define DBGLN(fmt_, data_)
jb8414 0:f479e4f4db0e 26 #define DBGLNEND
defmacro 22:4d895e732765 27 #endif /* DBG */
jb8414 0:f479e4f4db0e 28
defmacro 22:4d895e732765 29 #define MIN(a, b) (((a) > (b))?(b):(a))
defmacro 22:4d895e732765 30 #define TO_HEX(t_) ((char) (((t_) > 9) ? ((t_) - 10 + 'A') : ((t_) + '0')))
jb8414 0:f479e4f4db0e 31 #define MAX_DOUBLE_DIGITS 7
jb8414 0:f479e4f4db0e 32
defmacro 22:4d895e732765 33 /* For tolower */
defmacro 22:4d895e732765 34 #include <ctype.h>
defmacro 22:4d895e732765 35
jb8414 0:f479e4f4db0e 36 static const int E_OK = 0;
jb8414 0:f479e4f4db0e 37 static const int E_NOCONNECTION = -1;
jb8414 0:f479e4f4db0e 38 static const int E_DISCONNECTED = -2;
jb8414 0:f479e4f4db0e 39 static const int E_NOTREACHABLE = -3;
jb8414 0:f479e4f4db0e 40 static const int E_INVALID = -4;
jb8414 0:f479e4f4db0e 41 static const int E_JSON_INVALID = -5;
citrusbyte 16:7903152de19f 42 static const int E_BUFFER_TOO_SMALL = -6;
citrusbyte 16:7903152de19f 43 static const int E_TIMESTAMP_ERROR = -8;
citrusbyte 16:7903152de19f 44
defmacro 22:4d895e732765 45 static const char* DEFAULT_M2X_HOST = "api-m2x.att.com";
defmacro 22:4d895e732765 46 static const int DEFAULT_M2X_PORT = 80;
defmacro 22:4d895e732765 47
citrusbyte 16:7903152de19f 48 static inline bool m2x_status_is_success(int status) {
citrusbyte 16:7903152de19f 49 return (status == E_OK) || (status >= 200 && status <= 299);
citrusbyte 16:7903152de19f 50 }
citrusbyte 16:7903152de19f 51
citrusbyte 16:7903152de19f 52 static inline bool m2x_status_is_client_error(int status) {
citrusbyte 16:7903152de19f 53 return status >= 400 && status <= 499;
citrusbyte 16:7903152de19f 54 }
citrusbyte 16:7903152de19f 55
citrusbyte 16:7903152de19f 56 static inline bool m2x_status_is_server_error(int status) {
citrusbyte 16:7903152de19f 57 return status >= 500 && status <= 599;
citrusbyte 16:7903152de19f 58 }
citrusbyte 16:7903152de19f 59
citrusbyte 16:7903152de19f 60 static inline bool m2x_status_is_error(int status) {
citrusbyte 16:7903152de19f 61 return m2x_status_is_client_error(status) ||
citrusbyte 16:7903152de19f 62 m2x_status_is_server_error(status);
citrusbyte 16:7903152de19f 63 }
jb8414 0:f479e4f4db0e 64
defmacro 22:4d895e732765 65 // Null Print class used to calculate length to print
defmacro 22:4d895e732765 66 class NullPrint : public Print {
defmacro 22:4d895e732765 67 public:
defmacro 22:4d895e732765 68 size_t counter;
defmacro 22:4d895e732765 69
defmacro 22:4d895e732765 70 virtual size_t write(uint8_t b) {
defmacro 22:4d895e732765 71 counter++;
defmacro 22:4d895e732765 72 return 1;
defmacro 22:4d895e732765 73 }
defmacro 22:4d895e732765 74
defmacro 22:4d895e732765 75 virtual size_t write(const uint8_t* buf, size_t size) {
defmacro 22:4d895e732765 76 counter += size;
defmacro 22:4d895e732765 77 return size;
defmacro 22:4d895e732765 78 }
defmacro 22:4d895e732765 79 };
defmacro 22:4d895e732765 80
defmacro 22:4d895e732765 81 // Encodes and prints string using Percent-encoding specified
defmacro 22:4d895e732765 82 // in RFC 1738, Section 2.2
defmacro 22:4d895e732765 83 static inline int print_encoded_string(Print* print, const char* str) {
defmacro 22:4d895e732765 84 int bytes = 0;
defmacro 22:4d895e732765 85 for (int i = 0; str[i] != 0; i++) {
defmacro 22:4d895e732765 86 if (((str[i] >= 'A') && (str[i] <= 'Z')) ||
defmacro 22:4d895e732765 87 ((str[i] >= 'a') && (str[i] <= 'z')) ||
defmacro 22:4d895e732765 88 ((str[i] >= '0') && (str[i] <= '9')) ||
defmacro 22:4d895e732765 89 (str[i] == '-') || (str[i] == '_') ||
defmacro 22:4d895e732765 90 (str[i] == '.') || (str[i] == '~')) {
defmacro 22:4d895e732765 91 bytes += print->print(str[i]);
defmacro 22:4d895e732765 92 } else {
defmacro 22:4d895e732765 93 // Encode all other characters
defmacro 22:4d895e732765 94 bytes += print->print('%');
defmacro 22:4d895e732765 95 bytes += print->print(TO_HEX(str[i] / 16));
defmacro 22:4d895e732765 96 bytes += print->print(TO_HEX(str[i] % 16));
defmacro 22:4d895e732765 97 }
defmacro 22:4d895e732765 98 }
defmacro 22:4d895e732765 99 return bytes;
defmacro 22:4d895e732765 100 }
defmacro 22:4d895e732765 101
defmacro 22:4d895e732765 102 #ifdef M2X_ENABLE_READER
citrusbyte 10:4ce9eba38dbe 103 /*
citrusbyte 10:4ce9eba38dbe 104 * +type+ indicates the value type: 1 for string, 2 for number
citrusbyte 10:4ce9eba38dbe 105 * NOTE that the value type here only contains a hint on how
citrusbyte 10:4ce9eba38dbe 106 * you can use the value. Even though 2 is returned, the value
citrusbyte 10:4ce9eba38dbe 107 * is still stored in (const char *), and atoi/atof is needed to
citrusbyte 10:4ce9eba38dbe 108 * get the actual value
citrusbyte 10:4ce9eba38dbe 109 */
jb8414 0:f479e4f4db0e 110 typedef void (*stream_value_read_callback)(const char* at,
jb8414 0:f479e4f4db0e 111 const char* value,
jb8414 0:f479e4f4db0e 112 int index,
citrusbyte 10:4ce9eba38dbe 113 void* context,
citrusbyte 10:4ce9eba38dbe 114 int type);
jb8414 0:f479e4f4db0e 115
jb8414 0:f479e4f4db0e 116 typedef void (*location_read_callback)(const char* name,
jb8414 0:f479e4f4db0e 117 double latitude,
jb8414 0:f479e4f4db0e 118 double longitude,
jb8414 0:f479e4f4db0e 119 double elevation,
jb8414 0:f479e4f4db0e 120 const char* timestamp,
jb8414 0:f479e4f4db0e 121 int index,
jb8414 0:f479e4f4db0e 122 void* context);
jb8414 0:f479e4f4db0e 123
defmacro 22:4d895e732765 124 typedef void (*m2x_command_read_callback)(const char* id,
defmacro 22:4d895e732765 125 const char* name,
defmacro 22:4d895e732765 126 int index,
defmacro 22:4d895e732765 127 void *context);
defmacro 22:4d895e732765 128 #endif /* M2X_ENABLE_READER */
defmacro 22:4d895e732765 129
defmacro 22:4d895e732765 130 typedef void (*m2x_fill_data_callback)(Print *print, void *context);
defmacro 22:4d895e732765 131
jb8414 0:f479e4f4db0e 132 class M2XStreamClient {
jb8414 0:f479e4f4db0e 133 public:
jb8414 0:f479e4f4db0e 134 M2XStreamClient(Client* client,
jb8414 0:f479e4f4db0e 135 const char* key,
defmacro 22:4d895e732765 136 void (* idlefunc)(void) = NULL,
jb8414 0:f479e4f4db0e 137 int case_insensitive = 1,
defmacro 22:4d895e732765 138 const char* host = DEFAULT_M2X_HOST,
defmacro 22:4d895e732765 139 int port = DEFAULT_M2X_PORT,
NetArc 14:205076b587fe 140 const char* path_prefix = NULL);
jb8414 0:f479e4f4db0e 141
citrusbyte 10:4ce9eba38dbe 142 // Push data stream value using PUT request, returns the HTTP status code
defmacro 22:4d895e732765 143 // NOTE: if you want to update by a serial, use "serial/<serial ID>" as
defmacro 22:4d895e732765 144 // the device ID here.
jb8414 0:f479e4f4db0e 145 template <class T>
citrusbyte 13:0d574742208f 146 int updateStreamValue(const char* deviceId, const char* streamName, T value);
jb8414 0:f479e4f4db0e 147
jb8414 0:f479e4f4db0e 148 // Post multiple values to M2X all at once.
citrusbyte 13:0d574742208f 149 // +deviceId+ - id of the device to post values
jb8414 0:f479e4f4db0e 150 // +streamNum+ - Number of streams to post
jb8414 0:f479e4f4db0e 151 // +names+ - Array of stream names, the length of the array should
jb8414 0:f479e4f4db0e 152 // be exactly +streamNum+
jb8414 0:f479e4f4db0e 153 // +counts+ - Array of +streamNum+ length, each item in this array
jb8414 0:f479e4f4db0e 154 // containing the number of values we want to post for each stream
jb8414 0:f479e4f4db0e 155 // +ats+ - Timestamps for each value, the length of this array should
jb8414 0:f479e4f4db0e 156 // be the some of all values in +counts+, for the first +counts[0]+
jb8414 0:f479e4f4db0e 157 // items, the values belong to the first stream, for the following
jb8414 0:f479e4f4db0e 158 // +counts[1]+ number of items, the values belong to the second stream,
citrusbyte 10:4ce9eba38dbe 159 // etc. Notice that timestamps are required here: you must provide
citrusbyte 10:4ce9eba38dbe 160 // a timestamp for each value posted.
jb8414 0:f479e4f4db0e 161 // +values+ - Values to post. This works the same way as +ats+, the
jb8414 0:f479e4f4db0e 162 // first +counts[0]+ number of items contain values to post to the first
jb8414 0:f479e4f4db0e 163 // stream, the succeeding +counts[1]+ number of items contain values
jb8414 0:f479e4f4db0e 164 // for the second stream, etc. The length of this array should be
jb8414 0:f479e4f4db0e 165 // the sum of all values in +counts+ array.
defmacro 22:4d895e732765 166 // NOTE: if you want to update by a serial, use "serial/<serial ID>" as
defmacro 22:4d895e732765 167 // the device ID here.
jb8414 0:f479e4f4db0e 168 template <class T>
citrusbyte 13:0d574742208f 169 int postDeviceUpdates(const char* deviceId, int streamNum,
citrusbyte 13:0d574742208f 170 const char* names[], const int counts[],
citrusbyte 13:0d574742208f 171 const char* ats[], T values[]);
jb8414 0:f479e4f4db0e 172
citrusbyte 19:4dfa28d37b8f 173 // Post multiple values of a single device at once.
citrusbyte 19:4dfa28d37b8f 174 // +deviceId+ - id of the device to post values
citrusbyte 19:4dfa28d37b8f 175 // +streamNum+ - Number of streams to post
citrusbyte 19:4dfa28d37b8f 176 // +names+ - Array of stream names, the length of the array should
citrusbyte 19:4dfa28d37b8f 177 // be exactly +streamNum+
citrusbyte 19:4dfa28d37b8f 178 // +values+ - Array of values to post, the length of the array should
citrusbyte 19:4dfa28d37b8f 179 // be exactly +streamNum+. Notice that the array of +values+ should
citrusbyte 19:4dfa28d37b8f 180 // match the array of +names+, and that the ith value in +values+ is
citrusbyte 19:4dfa28d37b8f 181 // exactly the value to post for the ith stream name in +names+
defmacro 22:4d895e732765 182 // NOTE: if you want to update by a serial, use "serial/<serial ID>" as
defmacro 22:4d895e732765 183 // the device ID here.
citrusbyte 19:4dfa28d37b8f 184 template <class T>
defmacro 22:4d895e732765 185 int postDeviceUpdate(const char* deviceId, int streamNum,
defmacro 22:4d895e732765 186 const char* names[], T values[],
defmacro 22:4d895e732765 187 const char* at = NULL);
citrusbyte 19:4dfa28d37b8f 188
defmacro 22:4d895e732765 189 #ifdef M2X_ENABLE_READER
jb8414 0:f479e4f4db0e 190 // Fetch values for a particular data stream. Since memory is
jb8414 0:f479e4f4db0e 191 // very limited on an Arduino, we cannot parse and get all the
jb8414 0:f479e4f4db0e 192 // data points in memory. Instead, we use callbacks here: whenever
jb8414 0:f479e4f4db0e 193 // a new data point is parsed, we call the callback using the values,
jb8414 0:f479e4f4db0e 194 // after that, the values will be thrown away to make space for new
jb8414 0:f479e4f4db0e 195 // values.
jb8414 0:f479e4f4db0e 196 // Note that you can also pass in a user-specified context in this
jb8414 0:f479e4f4db0e 197 // function, this context will be passed to the callback function
jb8414 0:f479e4f4db0e 198 // each time we get a data point.
jb8414 0:f479e4f4db0e 199 // For each data point, the callback will be called once. The HTTP
jb8414 0:f479e4f4db0e 200 // status code will be returned. And the content is only parsed when
jb8414 0:f479e4f4db0e 201 // the status code is 200.
citrusbyte 13:0d574742208f 202 int listStreamValues(const char* deviceId, const char* streamName,
citrusbyte 13:0d574742208f 203 stream_value_read_callback callback, void* context,
citrusbyte 13:0d574742208f 204 const char* query = NULL);
defmacro 22:4d895e732765 205 #endif /* M2X_ENABLE_READER */
jb8414 0:f479e4f4db0e 206
jb8414 0:f479e4f4db0e 207 // Update datasource location
jb8414 0:f479e4f4db0e 208 // NOTE: On an Arduino Uno and other ATMEGA based boards, double has
jb8414 0:f479e4f4db0e 209 // 4-byte (32 bits) precision, which is the same as float. So there's
jb8414 0:f479e4f4db0e 210 // no natural double-precision floating number on these boards. With
jb8414 0:f479e4f4db0e 211 // a float value, we have a precision of roughly 7 digits, that means
jb8414 0:f479e4f4db0e 212 // either 5 or 6 digits after the floating point. According to wikipedia,
jb8414 0:f479e4f4db0e 213 // a difference of 0.00001 will give us ~1.1132m distance. If this
jb8414 0:f479e4f4db0e 214 // precision is good for you, you can use the double-version we provided
jb8414 0:f479e4f4db0e 215 // here. Otherwise, you may need to use the string-version and do the
jb8414 0:f479e4f4db0e 216 // actual conversion by yourselves.
jb8414 0:f479e4f4db0e 217 // However, with an Arduino Due board, double has 8-bytes (64 bits)
jb8414 0:f479e4f4db0e 218 // precision, which means you are free to use the double-version only
jb8414 0:f479e4f4db0e 219 // without any precision problems.
jb8414 0:f479e4f4db0e 220 // Returned value is the http status code.
defmacro 22:4d895e732765 221 // NOTE: if you want to update by a serial, use "serial/<serial ID>" as
defmacro 22:4d895e732765 222 // the device ID here.
jb8414 0:f479e4f4db0e 223 template <class T>
citrusbyte 13:0d574742208f 224 int updateLocation(const char* deviceId, const char* name,
jb8414 0:f479e4f4db0e 225 T latitude, T longitude, T elevation);
jb8414 0:f479e4f4db0e 226
defmacro 22:4d895e732765 227 #ifdef M2X_ENABLE_READER
citrusbyte 13:0d574742208f 228 // Read location information for a device. Also used callback to process
jb8414 0:f479e4f4db0e 229 // data points for memory reasons. The HTTP status code is returned,
jb8414 0:f479e4f4db0e 230 // response is only parsed when the HTTP status code is 200
citrusbyte 13:0d574742208f 231 int readLocation(const char* deviceId, location_read_callback callback,
jb8414 0:f479e4f4db0e 232 void* context);
defmacro 22:4d895e732765 233 #endif /* M2X_ENABLE_READER */
citrusbyte 10:4ce9eba38dbe 234
citrusbyte 10:4ce9eba38dbe 235 // Delete values from a data stream
citrusbyte 10:4ce9eba38dbe 236 // You will need to provide from and end date/time strings in the ISO8601
citrusbyte 10:4ce9eba38dbe 237 // format "yyyy-mm-ddTHH:MM:SS.SSSZ" where
citrusbyte 10:4ce9eba38dbe 238 // yyyy: the year
citrusbyte 10:4ce9eba38dbe 239 // mm: the month
citrusbyte 10:4ce9eba38dbe 240 // dd: the day
citrusbyte 10:4ce9eba38dbe 241 // HH: the hour (24 hour format)
citrusbyte 10:4ce9eba38dbe 242 // MM: the minute
citrusbyte 10:4ce9eba38dbe 243 // SS.SSS: the seconds (to the millisecond)
citrusbyte 10:4ce9eba38dbe 244 // NOTE: the time is given in Zulu (GMT)
citrusbyte 10:4ce9eba38dbe 245 // M2X will delete all values within the from to end date/time range.
citrusbyte 10:4ce9eba38dbe 246 // The status code is 204 on success and 400 on a bad request (e.g. the
citrusbyte 10:4ce9eba38dbe 247 // timestamp is not in ISO8601 format or the from timestamp is not less than
citrusbyte 10:4ce9eba38dbe 248 // or equal to the end timestamp.
citrusbyte 13:0d574742208f 249 int deleteValues(const char* deviceId, const char* streamName,
citrusbyte 10:4ce9eba38dbe 250 const char* from, const char* end);
citrusbyte 16:7903152de19f 251
defmacro 22:4d895e732765 252 #ifdef M2X_ENABLE_READER
defmacro 22:4d895e732765 253 // Fetch commands available for this device, notice that for memory constraints,
defmacro 22:4d895e732765 254 // we only keep ID and name of the command received here.
defmacro 22:4d895e732765 255 // You can tweak the command receiving via the query parameter.
defmacro 22:4d895e732765 256 int listCommands(const char* deviceId,
defmacro 22:4d895e732765 257 m2x_command_read_callback callback, void* context,
defmacro 22:4d895e732765 258 const char* query = NULL);
defmacro 22:4d895e732765 259 #endif /* M2X_ENABLE_READER */
defmacro 22:4d895e732765 260
defmacro 22:4d895e732765 261 // Mark a command as processed.
defmacro 22:4d895e732765 262 // Link: https://m2x.att.com/developer/documentation/v2/commands#Device-Marks-a-Command-as-Processed
defmacro 22:4d895e732765 263 // To make sure the minimal amount of memory is needed, this API works with
defmacro 22:4d895e732765 264 // a callback function. The callback function is then used to fill the request
defmacro 22:4d895e732765 265 // data, note that in order to correctly set the content length in HTTP header,
defmacro 22:4d895e732765 266 // the callback function will be called twice, the caller must make sure both
defmacro 22:4d895e732765 267 // calls fill the Print object with exactly the same data.
defmacro 22:4d895e732765 268 // If you have a pre-allocated buffer filled with the data to post, you can
defmacro 22:4d895e732765 269 // use markCommandProcessedWithData API below.
defmacro 22:4d895e732765 270 int markCommandProcessed(const char* deviceId, const char* commandId,
defmacro 22:4d895e732765 271 m2x_fill_data_callback callback, void *context);
defmacro 22:4d895e732765 272
defmacro 22:4d895e732765 273 // Mark a command as processed with a data buffer
defmacro 22:4d895e732765 274 // Link: https://m2x.att.com/developer/documentation/v2/commands#Device-Marks-a-Command-as-Processed
defmacro 22:4d895e732765 275 // This is exactly like markCommandProcessed, except that a buffer is use to
defmacro 22:4d895e732765 276 // contain the data to post as the request body.
defmacro 22:4d895e732765 277 int markCommandProcessedWithData(const char* deviceId, const char* commandId,
defmacro 22:4d895e732765 278 const char* data);
defmacro 22:4d895e732765 279
defmacro 22:4d895e732765 280 // Mark a command as rejected.
defmacro 22:4d895e732765 281 // Link: https://m2x.att.com/developer/documentation/v2/commands#Device-Marks-a-Command-as-Rejected
defmacro 22:4d895e732765 282 // To make sure the minimal amount of memory is needed, this API works with
defmacro 22:4d895e732765 283 // a callback function. The callback function is then used to fill the request
defmacro 22:4d895e732765 284 // data, note that in order to correctly set the content length in HTTP header,
defmacro 22:4d895e732765 285 // the callback function will be called twice, the caller must make sure both
defmacro 22:4d895e732765 286 // calls fill the Print object with exactly the same data.
defmacro 22:4d895e732765 287 // If you have a pre-allocated buffer filled with the data to post, you can
defmacro 22:4d895e732765 288 // use markCommandRejectedWithData API below.
defmacro 22:4d895e732765 289 int markCommandRejected(const char* deviceId, const char* commandId,
defmacro 22:4d895e732765 290 m2x_fill_data_callback callback, void *context);
defmacro 22:4d895e732765 291
defmacro 22:4d895e732765 292 // Mark a command as rejected with a data buffer
defmacro 22:4d895e732765 293 // Link: https://m2x.att.com/developer/documentation/v2/commands#Device-Marks-a-Command-as-Rejected
defmacro 22:4d895e732765 294 // This is exactly like markCommandRejected, except that a buffer is use to
defmacro 22:4d895e732765 295 // contain the data to post as the request body.
defmacro 22:4d895e732765 296 int markCommandRejectedWithData(const char* deviceId, const char* commandId,
defmacro 22:4d895e732765 297 const char* data);
defmacro 22:4d895e732765 298
citrusbyte 16:7903152de19f 299 // Fetches current timestamp in seconds from M2X server. Since we
citrusbyte 16:7903152de19f 300 // are using signed 32-bit integer as return value, this will only
citrusbyte 16:7903152de19f 301 // return valid results before 03:14:07 UTC on 19 January 2038. If
citrusbyte 16:7903152de19f 302 // the device is supposed to work after that, this function should
citrusbyte 16:7903152de19f 303 // not be used.
citrusbyte 16:7903152de19f 304 //
citrusbyte 16:7903152de19f 305 // The returned value will contain the status code(positive values)
citrusbyte 16:7903152de19f 306 // or the error code(negative values).
citrusbyte 16:7903152de19f 307 // In case of success, the current timestamp will be filled in the
citrusbyte 16:7903152de19f 308 // +ts+ pointer passed in as argument.
citrusbyte 16:7903152de19f 309 //
citrusbyte 16:7903152de19f 310 // NOTE: although returning uint32_t can give us a larger space,
citrusbyte 16:7903152de19f 311 // we prefer to cope with the unix convention here.
citrusbyte 16:7903152de19f 312 int getTimestamp32(int32_t* ts);
citrusbyte 16:7903152de19f 313
citrusbyte 16:7903152de19f 314 // Fetches current timestamp in seconds from M2X server.
citrusbyte 16:7903152de19f 315 // This function will return the timestamp as an integer literal
citrusbyte 16:7903152de19f 316 // in the provided buffer. Hence there's no problem working after
citrusbyte 16:7903152de19f 317 // 03:14:07 UTC on 19 January 2038. The drawback part here, is that
citrusbyte 16:7903152de19f 318 // you will have to work with 64-bit integer, which is not available
citrusbyte 16:7903152de19f 319 // on certain platform(such as Arduino), a bignum library or alike
citrusbyte 16:7903152de19f 320 // is needed in this case.
citrusbyte 16:7903152de19f 321 //
citrusbyte 16:7903152de19f 322 // Notice +bufferLength+ is supposed to contain the length of the
citrusbyte 16:7903152de19f 323 // buffer when calling this function. It is also the caller's
citrusbyte 16:7903152de19f 324 // responsibility to ensure the buffer is big enough, otherwise
citrusbyte 16:7903152de19f 325 // the library will return an error indicating the buffer is too
citrusbyte 16:7903152de19f 326 // small.
citrusbyte 16:7903152de19f 327 // While this is not accurate all the time, one trick here is to
citrusbyte 16:7903152de19f 328 // pass in 0 as the bufferLength, in which case we will always return
citrusbyte 16:7903152de19f 329 // the buffer-too-small error. However, the correct buffer length
citrusbyte 16:7903152de19f 330 // can be found this way so a secound execution is most likely to work
citrusbyte 16:7903152de19f 331 // (unless we are at the edge of the buffer length increasing, for
citrusbyte 16:7903152de19f 332 // example, when the timestamp jumps from 9999999999 to 10000000000,
citrusbyte 16:7903152de19f 333 // which is highly unlikely to happend). However, given that the
citrusbyte 16:7903152de19f 334 // maximum 64-bit integer can be stored in 19 bytes, there's not
citrusbyte 16:7903152de19f 335 // much need to use this trick.)
citrusbyte 16:7903152de19f 336 //
citrusbyte 16:7903152de19f 337 // The returned value will contain the status code(positive values)
citrusbyte 16:7903152de19f 338 // or the error code(negative values).
citrusbyte 16:7903152de19f 339 // In case of success, the current timestamp will be filled in the
citrusbyte 16:7903152de19f 340 // passed +buffer+ pointer, and the actual used buffer length will
citrusbyte 16:7903152de19f 341 // be returned in +bufferLength+ pointer.
citrusbyte 16:7903152de19f 342 // NOTE: as long as we can read the returned buffer length, it will
citrusbyte 16:7903152de19f 343 // be used to fill in the +bufferLength+ variable even though other
citrusbyte 16:7903152de19f 344 // errors occur(buffer is not enough, network is shutdown before
citrusbyte 16:7903152de19f 345 // reading the whole buffer, etc.)
citrusbyte 16:7903152de19f 346 int getTimestamp(char* buffer, int* bufferLength);
jb8414 0:f479e4f4db0e 347 private:
jb8414 0:f479e4f4db0e 348 Client* _client;
jb8414 0:f479e4f4db0e 349 const char* _key;
jb8414 0:f479e4f4db0e 350 int _case_insensitive;
jb8414 0:f479e4f4db0e 351 const char* _host;
jb8414 0:f479e4f4db0e 352 int _port;
defmacro 22:4d895e732765 353 void (* _idlefunc)(void);
NetArc 14:205076b587fe 354 const char* _path_prefix;
jb8414 0:f479e4f4db0e 355 NullPrint _null_print;
jb8414 0:f479e4f4db0e 356
jb8414 0:f479e4f4db0e 357 // Writes the HTTP header part for updating a stream value
citrusbyte 13:0d574742208f 358 void writePutHeader(const char* deviceId,
citrusbyte 10:4ce9eba38dbe 359 const char* streamName,
citrusbyte 10:4ce9eba38dbe 360 int contentLength);
citrusbyte 10:4ce9eba38dbe 361 // Writes the HTTP header part for deleting stream values
citrusbyte 13:0d574742208f 362 void writeDeleteHeader(const char* deviceId,
citrusbyte 10:4ce9eba38dbe 363 const char* streamName,
citrusbyte 10:4ce9eba38dbe 364 int contentLength);
jb8414 0:f479e4f4db0e 365 // Writes HTTP header lines including M2X API Key, host, content
jb8414 0:f479e4f4db0e 366 // type and content length(if the body exists)
jb8414 0:f479e4f4db0e 367 void writeHttpHeader(int contentLength);
jb8414 0:f479e4f4db0e 368 // Parses HTTP response header and return the content length.
jb8414 0:f479e4f4db0e 369 // Note that this function does not parse all http headers, as long
jb8414 0:f479e4f4db0e 370 // as the content length is found, this function will return
jb8414 0:f479e4f4db0e 371 int readContentLength();
jb8414 0:f479e4f4db0e 372 // Skips all HTTP response header part. Return minus value in case
jb8414 0:f479e4f4db0e 373 // the connection is closed before we got all headers
jb8414 0:f479e4f4db0e 374 int skipHttpHeader();
jb8414 0:f479e4f4db0e 375 // Parses and returns the HTTP status code, note this function will
jb8414 0:f479e4f4db0e 376 // return immediately once it gets the status code
jb8414 0:f479e4f4db0e 377 int readStatusCode(bool closeClient);
jb8414 0:f479e4f4db0e 378 // Waits for a certain string pattern in the HTTP header, and returns
jb8414 0:f479e4f4db0e 379 // once the pattern is found. In the pattern, you can use '*' to denote
jb8414 0:f479e4f4db0e 380 // any character
jb8414 0:f479e4f4db0e 381 int waitForString(const char* str);
jb8414 0:f479e4f4db0e 382 // Closes the connection
jb8414 0:f479e4f4db0e 383 void close();
defmacro 22:4d895e732765 384
defmacro 22:4d895e732765 385 #ifdef M2X_ENABLE_READER
jb8414 0:f479e4f4db0e 386 // Parses JSON response of stream value API, and calls callback function
jb8414 0:f479e4f4db0e 387 // once we get a data point
jb8414 0:f479e4f4db0e 388 int readStreamValue(stream_value_read_callback callback, void* context);
jb8414 0:f479e4f4db0e 389 // Parses JSON response of location API, and calls callback function once
jb8414 0:f479e4f4db0e 390 // we get a data point
jb8414 0:f479e4f4db0e 391 int readLocation(location_read_callback callback, void* context);
defmacro 22:4d895e732765 392 // Parses JSON response of command API, and calls callback function once
defmacro 22:4d895e732765 393 // we get a data point
defmacro 22:4d895e732765 394 int readCommand(m2x_command_read_callback callback, void* context);
defmacro 22:4d895e732765 395 #endif /* M2X_ENABLE_READER */
defmacro 22:4d895e732765 396 };
defmacro 22:4d895e732765 397
defmacro 22:4d895e732765 398
defmacro 22:4d895e732765 399 // A ISO8601 timestamp generation service for M2X.
defmacro 22:4d895e732765 400 // It uses the Time API provided by the M2X server to initialize
defmacro 22:4d895e732765 401 // clock, then uses millis() function provided by Arduino to calculate
defmacro 22:4d895e732765 402 // time advancements so as to reduce API query times.
defmacro 22:4d895e732765 403 //
defmacro 22:4d895e732765 404 // Right now, this service only works with 32-bit timestamp, meaning that
defmacro 22:4d895e732765 405 // this service won't work after 03:14:07 UTC on 19 January 2038. However,
defmacro 22:4d895e732765 406 // a similar service that uses 64-bit timestamp can be implemented following
defmacro 22:4d895e732765 407 // the logic here.
defmacro 22:4d895e732765 408 class TimeService {
defmacro 22:4d895e732765 409 public:
defmacro 22:4d895e732765 410 TimeService(M2XStreamClient* client);
defmacro 22:4d895e732765 411
defmacro 22:4d895e732765 412 // Initialize the time service. Notice the TimeService instance is only
defmacro 22:4d895e732765 413 // working after calling this function successfully.
defmacro 22:4d895e732765 414 int init();
defmacro 22:4d895e732765 415
defmacro 22:4d895e732765 416 // Reset the internal recorded time by calling M2X Time API again. Normally,
defmacro 22:4d895e732765 417 // you don't need to call this manually. TimeService will handle Arduino clock
defmacro 22:4d895e732765 418 // overflow automatically
defmacro 22:4d895e732765 419 int reset();
defmacro 22:4d895e732765 420
defmacro 22:4d895e732765 421 // Fills ISO8601 formatted timestamp into the buffer provided. +length+ should
defmacro 22:4d895e732765 422 // contains the maximum supported length of the buffer when calling. For now,
defmacro 22:4d895e732765 423 // the buffer should be able to store 25 characters for a full ISO8601 formatted
defmacro 22:4d895e732765 424 // timestamp, otherwise, an error will be returned.
defmacro 22:4d895e732765 425 int getTimestamp(char* buffer, int* length);
defmacro 22:4d895e732765 426 private:
defmacro 22:4d895e732765 427 M2XStreamClient* _client;
defmacro 22:4d895e732765 428 int32_t _server_timestamp;
defmacro 22:4d895e732765 429 uint32_t _local_last_milli;
defmacro 22:4d895e732765 430 M2XTimer _timer;
jb8414 0:f479e4f4db0e 431 };
jb8414 0:f479e4f4db0e 432
defmacro 22:4d895e732765 433
defmacro 22:4d895e732765 434 // Implementations
defmacro 22:4d895e732765 435 M2XStreamClient::M2XStreamClient(Client* client,
defmacro 22:4d895e732765 436 const char* key,
defmacro 22:4d895e732765 437 void (* idlefunc)(void),
defmacro 22:4d895e732765 438 int case_insensitive,
defmacro 22:4d895e732765 439 const char* host,
defmacro 22:4d895e732765 440 int port,
defmacro 22:4d895e732765 441 const char* path_prefix) : _client(client),
defmacro 22:4d895e732765 442 _key(key),
defmacro 22:4d895e732765 443 _idlefunc(idlefunc),
defmacro 22:4d895e732765 444 _case_insensitive(case_insensitive),
defmacro 22:4d895e732765 445 _host(host),
defmacro 22:4d895e732765 446 _port(port),
defmacro 22:4d895e732765 447 _path_prefix(path_prefix),
defmacro 22:4d895e732765 448 _null_print() {
defmacro 22:4d895e732765 449 }
defmacro 22:4d895e732765 450
defmacro 22:4d895e732765 451 template <class T>
defmacro 22:4d895e732765 452 int M2XStreamClient::updateStreamValue(const char* deviceId, const char* streamName, T value) {
defmacro 22:4d895e732765 453 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 454 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 455 writePutHeader(deviceId, streamName,
defmacro 22:4d895e732765 456 // for {"value": and }
defmacro 22:4d895e732765 457 _null_print.print(value) + 12);
defmacro 22:4d895e732765 458 _client->print("{\"value\":\"");
defmacro 22:4d895e732765 459 _client->print(value);
defmacro 22:4d895e732765 460 _client->print("\"}");
defmacro 22:4d895e732765 461 } else {
defmacro 22:4d895e732765 462 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 463 return E_NOCONNECTION;
defmacro 22:4d895e732765 464 }
defmacro 22:4d895e732765 465
defmacro 22:4d895e732765 466 return readStatusCode(true);
defmacro 22:4d895e732765 467 }
defmacro 22:4d895e732765 468
defmacro 22:4d895e732765 469 template <class T>
defmacro 22:4d895e732765 470 inline int write_multiple_values(Print* print, int streamNum,
defmacro 22:4d895e732765 471 const char* names[], const int counts[],
defmacro 22:4d895e732765 472 const char* ats[], T values[]) {
defmacro 22:4d895e732765 473 int bytes = 0, value_index = 0;
defmacro 22:4d895e732765 474 bytes += print->print("{\"values\":{");
defmacro 22:4d895e732765 475 for (int i = 0; i < streamNum; i++) {
defmacro 22:4d895e732765 476 bytes += print->print("\"");
defmacro 22:4d895e732765 477 bytes += print->print(names[i]);
defmacro 22:4d895e732765 478 bytes += print->print("\":[");
defmacro 22:4d895e732765 479 for (int j = 0; j < counts[i]; j++) {
defmacro 22:4d895e732765 480 bytes += print->print("{\"timestamp\": \"");
defmacro 22:4d895e732765 481 bytes += print->print(ats[value_index]);
defmacro 22:4d895e732765 482 bytes += print->print("\",\"value\": \"");
defmacro 22:4d895e732765 483 bytes += print->print(values[value_index]);
defmacro 22:4d895e732765 484 bytes += print->print("\"}");
defmacro 22:4d895e732765 485 if (j < counts[i] - 1) { bytes += print->print(","); }
defmacro 22:4d895e732765 486 value_index++;
defmacro 22:4d895e732765 487 }
defmacro 22:4d895e732765 488 bytes += print->print("]");
defmacro 22:4d895e732765 489 if (i < streamNum - 1) { bytes += print->print(","); }
defmacro 22:4d895e732765 490 }
defmacro 22:4d895e732765 491 bytes += print->print("}}");
defmacro 22:4d895e732765 492 return bytes;
defmacro 22:4d895e732765 493 }
defmacro 22:4d895e732765 494
defmacro 22:4d895e732765 495 template <class T>
defmacro 22:4d895e732765 496 int M2XStreamClient::postDeviceUpdates(const char* deviceId, int streamNum,
defmacro 22:4d895e732765 497 const char* names[], const int counts[],
defmacro 22:4d895e732765 498 const char* ats[], T values[]) {
defmacro 22:4d895e732765 499 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 500 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 501 int length = write_multiple_values(&_null_print, streamNum, names,
defmacro 22:4d895e732765 502 counts, ats, values);
defmacro 22:4d895e732765 503 _client->print("POST ");
defmacro 22:4d895e732765 504 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 505 _client->print("/v2/devices/");
defmacro 22:4d895e732765 506 _client->print(deviceId);
defmacro 22:4d895e732765 507 _client->println("/updates HTTP/1.0");
defmacro 22:4d895e732765 508 writeHttpHeader(length);
defmacro 22:4d895e732765 509 write_multiple_values(_client, streamNum, names, counts, ats, values);
defmacro 22:4d895e732765 510 } else {
defmacro 22:4d895e732765 511 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 512 return E_NOCONNECTION;
defmacro 22:4d895e732765 513 }
defmacro 22:4d895e732765 514 return readStatusCode(true);
defmacro 22:4d895e732765 515 }
defmacro 22:4d895e732765 516
defmacro 22:4d895e732765 517 template <class T>
defmacro 22:4d895e732765 518 inline int write_single_device_values(Print* print, int streamNum,
defmacro 22:4d895e732765 519 const char* names[], T values[],
defmacro 22:4d895e732765 520 const char* at) {
defmacro 22:4d895e732765 521 int bytes = 0;
defmacro 22:4d895e732765 522 bytes += print->print("{\"values\":{");
defmacro 22:4d895e732765 523 for (int i = 0; i < streamNum; i++) {
defmacro 22:4d895e732765 524 bytes += print->print("\"");
defmacro 22:4d895e732765 525 bytes += print->print(names[i]);
defmacro 22:4d895e732765 526 bytes += print->print("\": \"");
defmacro 22:4d895e732765 527 bytes += print->print(values[i]);
defmacro 22:4d895e732765 528 bytes += print->print("\"");
defmacro 22:4d895e732765 529 if (i < streamNum - 1) { bytes += print->print(","); }
defmacro 22:4d895e732765 530 }
defmacro 22:4d895e732765 531 bytes += print->print("}");
defmacro 22:4d895e732765 532 if (at != NULL) {
defmacro 22:4d895e732765 533 bytes += print->print(",\"timestamp\":\"");
defmacro 22:4d895e732765 534 bytes += print->print(at);
defmacro 22:4d895e732765 535 bytes += print->print("\"");
defmacro 22:4d895e732765 536 }
defmacro 22:4d895e732765 537 bytes += print->print("}");
defmacro 22:4d895e732765 538 return bytes;
defmacro 22:4d895e732765 539 }
defmacro 22:4d895e732765 540
defmacro 22:4d895e732765 541 template <class T>
defmacro 22:4d895e732765 542 int M2XStreamClient::postDeviceUpdate(const char* deviceId, int streamNum,
defmacro 22:4d895e732765 543 const char* names[], T values[],
defmacro 22:4d895e732765 544 const char* at) {
defmacro 22:4d895e732765 545 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 546 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 547 int length = write_single_device_values(&_null_print, streamNum, names,
defmacro 22:4d895e732765 548 values, at);
defmacro 22:4d895e732765 549 _client->print("POST ");
defmacro 22:4d895e732765 550 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 551 _client->print("/v2/devices/");
defmacro 22:4d895e732765 552 _client->print(deviceId);
defmacro 22:4d895e732765 553 _client->println("/update HTTP/1.0");
defmacro 22:4d895e732765 554 writeHttpHeader(length);
defmacro 22:4d895e732765 555 write_single_device_values(_client, streamNum, names, values, at);
defmacro 22:4d895e732765 556 } else {
defmacro 22:4d895e732765 557 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 558 return E_NOCONNECTION;
defmacro 22:4d895e732765 559 }
defmacro 22:4d895e732765 560 return readStatusCode(true);
defmacro 22:4d895e732765 561 }
defmacro 22:4d895e732765 562
defmacro 22:4d895e732765 563 template <class T>
defmacro 22:4d895e732765 564 static int write_location_data(Print* print, const char* name,
defmacro 22:4d895e732765 565 T latitude, T longitude,
defmacro 22:4d895e732765 566 T elevation) {
defmacro 22:4d895e732765 567 int bytes = 0;
defmacro 22:4d895e732765 568 bytes += print->print("{\"name\":\"");
defmacro 22:4d895e732765 569 bytes += print->print(name);
defmacro 22:4d895e732765 570 bytes += print->print("\",\"latitude\":\"");
defmacro 22:4d895e732765 571 bytes += print->print(latitude);
defmacro 22:4d895e732765 572 bytes += print->print("\",\"longitude\":\"");
defmacro 22:4d895e732765 573 bytes += print->print(longitude);
defmacro 22:4d895e732765 574 bytes += print->print("\",\"elevation\":\"");
defmacro 22:4d895e732765 575 bytes += print->print(elevation);
defmacro 22:4d895e732765 576 bytes += print->print("\"}");
defmacro 22:4d895e732765 577 return bytes;
defmacro 22:4d895e732765 578 }
defmacro 22:4d895e732765 579
defmacro 22:4d895e732765 580 static int write_location_data(Print* print, const char* name,
defmacro 22:4d895e732765 581 double latitude, double longitude,
defmacro 22:4d895e732765 582 double elevation) {
defmacro 22:4d895e732765 583 int bytes = 0;
defmacro 22:4d895e732765 584 bytes += print->print("{\"name\":\"");
defmacro 22:4d895e732765 585 bytes += print->print(name);
defmacro 22:4d895e732765 586 bytes += print->print("\",\"latitude\":\"");
defmacro 22:4d895e732765 587 bytes += print->print(latitude, MAX_DOUBLE_DIGITS);
defmacro 22:4d895e732765 588 bytes += print->print("\",\"longitude\":\"");
defmacro 22:4d895e732765 589 bytes += print->print(longitude, MAX_DOUBLE_DIGITS);
defmacro 22:4d895e732765 590 bytes += print->print("\",\"elevation\":\"");
defmacro 22:4d895e732765 591 bytes += print->print(elevation);
defmacro 22:4d895e732765 592 bytes += print->print("\"}");
defmacro 22:4d895e732765 593 return bytes;
defmacro 22:4d895e732765 594 }
defmacro 22:4d895e732765 595
defmacro 22:4d895e732765 596 template <class T>
defmacro 22:4d895e732765 597 int M2XStreamClient::updateLocation(const char* deviceId,
defmacro 22:4d895e732765 598 const char* name,
defmacro 22:4d895e732765 599 T latitude,
defmacro 22:4d895e732765 600 T longitude,
defmacro 22:4d895e732765 601 T elevation) {
defmacro 22:4d895e732765 602 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 603 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 604
defmacro 22:4d895e732765 605 int length = write_location_data(&_null_print, name, latitude, longitude,
defmacro 22:4d895e732765 606 elevation);
defmacro 22:4d895e732765 607 _client->print("PUT ");
defmacro 22:4d895e732765 608 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 609 _client->print("/v2/devices/");
defmacro 22:4d895e732765 610 _client->print(deviceId);
defmacro 22:4d895e732765 611 _client->println("/location HTTP/1.0");
defmacro 22:4d895e732765 612
defmacro 22:4d895e732765 613 writeHttpHeader(length);
defmacro 22:4d895e732765 614 write_location_data(_client, name, latitude, longitude, elevation);
defmacro 22:4d895e732765 615 } else {
defmacro 22:4d895e732765 616 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 617 return E_NOCONNECTION;
defmacro 22:4d895e732765 618 }
defmacro 22:4d895e732765 619 return readStatusCode(true);
defmacro 22:4d895e732765 620 }
defmacro 22:4d895e732765 621
defmacro 22:4d895e732765 622 static inline int write_delete_values(Print* print, const char* from,
defmacro 22:4d895e732765 623 const char* end) {
defmacro 22:4d895e732765 624 int bytes = 0;
defmacro 22:4d895e732765 625 bytes += print->print("{\"from\":\"");
defmacro 22:4d895e732765 626 bytes += print->print(from);
defmacro 22:4d895e732765 627 bytes += print->print("\",\"end\":\"");
defmacro 22:4d895e732765 628 bytes += print->print(end);
defmacro 22:4d895e732765 629 bytes += print->print("\"}");
defmacro 22:4d895e732765 630 return bytes;
defmacro 22:4d895e732765 631 }
defmacro 22:4d895e732765 632
defmacro 22:4d895e732765 633 int M2XStreamClient::deleteValues(const char* deviceId, const char* streamName,
defmacro 22:4d895e732765 634 const char* from, const char* end) {
defmacro 22:4d895e732765 635 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 636 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 637 int length = write_delete_values(&_null_print, from, end);
defmacro 22:4d895e732765 638 writeDeleteHeader(deviceId, streamName, length);
defmacro 22:4d895e732765 639 write_delete_values(_client, from, end);
defmacro 22:4d895e732765 640 } else {
defmacro 22:4d895e732765 641 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 642 return E_NOCONNECTION;
defmacro 22:4d895e732765 643 }
defmacro 22:4d895e732765 644
defmacro 22:4d895e732765 645 return readStatusCode(true);
defmacro 22:4d895e732765 646 }
defmacro 22:4d895e732765 647
defmacro 22:4d895e732765 648 int M2XStreamClient::markCommandProcessed(const char* deviceId,
defmacro 22:4d895e732765 649 const char* commandId,
defmacro 22:4d895e732765 650 m2x_fill_data_callback callback,
defmacro 22:4d895e732765 651 void *context) {
defmacro 22:4d895e732765 652 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 653 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 654 _null_print.counter = 0;
defmacro 22:4d895e732765 655 callback(&_null_print, context);
defmacro 22:4d895e732765 656 int length = _null_print.counter;
defmacro 22:4d895e732765 657 _client->print("POST ");
defmacro 22:4d895e732765 658 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 659 _client->print("/v2/devices/");
defmacro 22:4d895e732765 660 _client->print(deviceId);
defmacro 22:4d895e732765 661 _client->print("/commands/");
defmacro 22:4d895e732765 662 _client->print(commandId);
defmacro 22:4d895e732765 663 _client->print("/process");
defmacro 22:4d895e732765 664 _client->println(" HTTP/1.0");
defmacro 22:4d895e732765 665 writeHttpHeader(length);
defmacro 22:4d895e732765 666 callback(_client, context);
defmacro 22:4d895e732765 667 } else {
defmacro 22:4d895e732765 668 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 669 return E_NOCONNECTION;
defmacro 22:4d895e732765 670 }
defmacro 22:4d895e732765 671 return readStatusCode(true);
defmacro 22:4d895e732765 672 }
defmacro 22:4d895e732765 673
defmacro 22:4d895e732765 674 static void m2x_fixed_buffer_filling_callback(Print *print, void *context) {
defmacro 22:4d895e732765 675 if (context) {
defmacro 22:4d895e732765 676 print->print((const char *) context);
defmacro 22:4d895e732765 677 }
defmacro 22:4d895e732765 678 }
defmacro 22:4d895e732765 679
defmacro 22:4d895e732765 680 int M2XStreamClient::markCommandProcessedWithData(const char* deviceId,
defmacro 22:4d895e732765 681 const char* commandId,
defmacro 22:4d895e732765 682 const char* data) {
defmacro 22:4d895e732765 683 return markCommandProcessed(deviceId, commandId,
defmacro 22:4d895e732765 684 m2x_fixed_buffer_filling_callback, (void *) data);
defmacro 22:4d895e732765 685 }
defmacro 22:4d895e732765 686
defmacro 22:4d895e732765 687 int M2XStreamClient::markCommandRejected(const char* deviceId,
defmacro 22:4d895e732765 688 const char* commandId,
defmacro 22:4d895e732765 689 m2x_fill_data_callback callback,
defmacro 22:4d895e732765 690 void *context) {
defmacro 22:4d895e732765 691 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 692 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 693 _null_print.counter = 0;
defmacro 22:4d895e732765 694 callback(&_null_print, context);
defmacro 22:4d895e732765 695 int length = _null_print.counter;
defmacro 22:4d895e732765 696 _client->print("POST ");
defmacro 22:4d895e732765 697 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 698 _client->print("/v2/devices/");
defmacro 22:4d895e732765 699 _client->print(deviceId);
defmacro 22:4d895e732765 700 _client->print("/commands/");
defmacro 22:4d895e732765 701 _client->print(commandId);
defmacro 22:4d895e732765 702 _client->print("/reject");
defmacro 22:4d895e732765 703 _client->println(" HTTP/1.0");
defmacro 22:4d895e732765 704 writeHttpHeader(length);
defmacro 22:4d895e732765 705 callback(_client, context);
defmacro 22:4d895e732765 706 } else {
defmacro 22:4d895e732765 707 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 708 return E_NOCONNECTION;
defmacro 22:4d895e732765 709 }
defmacro 22:4d895e732765 710 return readStatusCode(true);
defmacro 22:4d895e732765 711 }
defmacro 22:4d895e732765 712
defmacro 22:4d895e732765 713 int M2XStreamClient::markCommandRejectedWithData(const char* deviceId,
defmacro 22:4d895e732765 714 const char* commandId,
defmacro 22:4d895e732765 715 const char* data) {
defmacro 22:4d895e732765 716 return markCommandRejected(deviceId, commandId,
defmacro 22:4d895e732765 717 m2x_fixed_buffer_filling_callback, (void *) data);
defmacro 22:4d895e732765 718 }
defmacro 22:4d895e732765 719
defmacro 22:4d895e732765 720 int M2XStreamClient::getTimestamp32(int32_t *ts) {
defmacro 22:4d895e732765 721 // The maximum value of signed 64-bit integer is 0x7fffffffffffffff,
defmacro 22:4d895e732765 722 // which is 9223372036854775807. It consists of 19 characters, so a
defmacro 22:4d895e732765 723 // buffer of 20 is definitely enough here
defmacro 22:4d895e732765 724 int length = 20;
defmacro 22:4d895e732765 725 char buffer[20];
defmacro 22:4d895e732765 726 int status = getTimestamp(buffer, &length);
defmacro 22:4d895e732765 727 if (status == 200) {
defmacro 22:4d895e732765 728 int32_t result = 0;
defmacro 22:4d895e732765 729 for (int i = 0; i < length; i++) {
defmacro 22:4d895e732765 730 result = result * 10 + (buffer[i] - '0');
defmacro 22:4d895e732765 731 }
defmacro 22:4d895e732765 732 if (ts != NULL) { *ts = result; }
defmacro 22:4d895e732765 733 }
defmacro 22:4d895e732765 734 return status;
defmacro 22:4d895e732765 735 }
defmacro 22:4d895e732765 736
defmacro 22:4d895e732765 737 int M2XStreamClient::getTimestamp(char* buffer, int *bufferLength) {
defmacro 22:4d895e732765 738 if (bufferLength == NULL) { return E_INVALID; }
defmacro 22:4d895e732765 739 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 740 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 741 _client->print("GET ");
defmacro 22:4d895e732765 742 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 743 _client->println("/v2/time/seconds HTTP/1.0");
defmacro 22:4d895e732765 744
defmacro 22:4d895e732765 745 writeHttpHeader(-1);
defmacro 22:4d895e732765 746 } else {
defmacro 22:4d895e732765 747 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 748 return E_NOCONNECTION;
defmacro 22:4d895e732765 749 }
defmacro 22:4d895e732765 750 int status = readStatusCode(false);
defmacro 22:4d895e732765 751 if (status == 200) {
defmacro 22:4d895e732765 752 int length = readContentLength();
defmacro 22:4d895e732765 753 if (length < 0) {
defmacro 22:4d895e732765 754 close();
defmacro 22:4d895e732765 755 return length;
defmacro 22:4d895e732765 756 }
defmacro 22:4d895e732765 757 if (*bufferLength < length) {
defmacro 22:4d895e732765 758 *bufferLength = length;
defmacro 22:4d895e732765 759 return E_BUFFER_TOO_SMALL;
defmacro 22:4d895e732765 760 }
defmacro 22:4d895e732765 761 *bufferLength = length;
defmacro 22:4d895e732765 762 int index = skipHttpHeader();
defmacro 22:4d895e732765 763 if (index != E_OK) {
defmacro 22:4d895e732765 764 close();
defmacro 22:4d895e732765 765 return index;
defmacro 22:4d895e732765 766 }
defmacro 22:4d895e732765 767 index = 0;
defmacro 22:4d895e732765 768 while (index < length) {
defmacro 22:4d895e732765 769 DBG("%s", "Received Data: ");
defmacro 22:4d895e732765 770 while ((index < length) && _client->available()) {
defmacro 22:4d895e732765 771 buffer[index++] = _client->read();
defmacro 22:4d895e732765 772 DBG("%c", buffer[index - 1]);
defmacro 22:4d895e732765 773 }
defmacro 22:4d895e732765 774 DBGLNEND;
defmacro 22:4d895e732765 775
defmacro 22:4d895e732765 776 if ((!_client->connected()) &&
defmacro 22:4d895e732765 777 (index < length)) {
defmacro 22:4d895e732765 778 close();
defmacro 22:4d895e732765 779 return E_NOCONNECTION;
defmacro 22:4d895e732765 780 }
defmacro 22:4d895e732765 781
defmacro 22:4d895e732765 782 if (_idlefunc!=NULL) {
defmacro 22:4d895e732765 783 _idlefunc();
defmacro 22:4d895e732765 784 } else {
defmacro 22:4d895e732765 785 delay(200);
defmacro 22:4d895e732765 786 }
defmacro 22:4d895e732765 787 }
defmacro 22:4d895e732765 788 }
defmacro 22:4d895e732765 789 close();
defmacro 22:4d895e732765 790 return status;
defmacro 22:4d895e732765 791 }
defmacro 22:4d895e732765 792
defmacro 22:4d895e732765 793
defmacro 22:4d895e732765 794 void M2XStreamClient::writePutHeader(const char* deviceId,
defmacro 22:4d895e732765 795 const char* streamName,
defmacro 22:4d895e732765 796 int contentLength) {
defmacro 22:4d895e732765 797 _client->print("PUT ");
defmacro 22:4d895e732765 798 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 799 _client->print("/v2/devices/");
defmacro 22:4d895e732765 800 _client->print(deviceId);
defmacro 22:4d895e732765 801 _client->print("/streams/");
defmacro 22:4d895e732765 802 print_encoded_string(_client, streamName);
defmacro 22:4d895e732765 803 _client->println("/value HTTP/1.0");
defmacro 22:4d895e732765 804
defmacro 22:4d895e732765 805 writeHttpHeader(contentLength);
defmacro 22:4d895e732765 806 }
defmacro 22:4d895e732765 807
defmacro 22:4d895e732765 808 void M2XStreamClient::writeDeleteHeader(const char* deviceId,
defmacro 22:4d895e732765 809 const char* streamName,
defmacro 22:4d895e732765 810 int contentLength) {
defmacro 22:4d895e732765 811 _client->print("DELETE ");
defmacro 22:4d895e732765 812 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 813 _client->print("/v2/devices/");
defmacro 22:4d895e732765 814 _client->print(deviceId);
defmacro 22:4d895e732765 815 _client->print("/streams/");
defmacro 22:4d895e732765 816 print_encoded_string(_client, streamName);
defmacro 22:4d895e732765 817 _client->print("/values");
defmacro 22:4d895e732765 818 _client->println(" HTTP/1.0");
defmacro 22:4d895e732765 819
defmacro 22:4d895e732765 820 writeHttpHeader(contentLength);
defmacro 22:4d895e732765 821 }
defmacro 22:4d895e732765 822
defmacro 22:4d895e732765 823 void M2XStreamClient::writeHttpHeader(int contentLength) {
defmacro 22:4d895e732765 824 _client->println(USER_AGENT);
defmacro 22:4d895e732765 825 _client->print("X-M2X-KEY: ");
defmacro 22:4d895e732765 826 _client->println(_key);
defmacro 22:4d895e732765 827
defmacro 22:4d895e732765 828 _client->print("Host: ");
defmacro 22:4d895e732765 829 print_encoded_string(_client, _host);
defmacro 22:4d895e732765 830 if (_port != DEFAULT_M2X_PORT) {
defmacro 22:4d895e732765 831 _client->print(":");
defmacro 22:4d895e732765 832 // port is an integer, does not need encoding
defmacro 22:4d895e732765 833 _client->print(_port);
defmacro 22:4d895e732765 834 }
defmacro 22:4d895e732765 835 _client->println();
defmacro 22:4d895e732765 836
defmacro 22:4d895e732765 837 if (contentLength > 0) {
defmacro 22:4d895e732765 838 _client->println("Content-Type: application/json");
defmacro 22:4d895e732765 839 DBG("%s", "Content Length: ");
defmacro 22:4d895e732765 840 DBGLN("%d", contentLength);
defmacro 22:4d895e732765 841
defmacro 22:4d895e732765 842 _client->print("Content-Length: ");
defmacro 22:4d895e732765 843 _client->println(contentLength);
defmacro 22:4d895e732765 844 }
defmacro 22:4d895e732765 845 _client->println();
defmacro 22:4d895e732765 846 }
defmacro 22:4d895e732765 847
defmacro 22:4d895e732765 848 int M2XStreamClient::waitForString(const char* str) {
defmacro 22:4d895e732765 849 int currentIndex = 0;
defmacro 22:4d895e732765 850 if (str[currentIndex] == '\0') return E_OK;
defmacro 22:4d895e732765 851
defmacro 22:4d895e732765 852 while (true) {
defmacro 22:4d895e732765 853 while (_client->available()) {
defmacro 22:4d895e732765 854 char c = _client->read();
defmacro 22:4d895e732765 855 DBG("%c", c);
defmacro 22:4d895e732765 856
defmacro 22:4d895e732765 857 int cmp;
defmacro 22:4d895e732765 858 if (_case_insensitive) {
defmacro 22:4d895e732765 859 cmp = tolower(c) - tolower(str[currentIndex]);
defmacro 22:4d895e732765 860 } else {
defmacro 22:4d895e732765 861 cmp = c - str[currentIndex];
defmacro 22:4d895e732765 862 }
defmacro 22:4d895e732765 863
defmacro 22:4d895e732765 864 if ((str[currentIndex] == '*') || (cmp == 0)) {
defmacro 22:4d895e732765 865 currentIndex++;
defmacro 22:4d895e732765 866 if (str[currentIndex] == '\0') {
defmacro 22:4d895e732765 867 return E_OK;
defmacro 22:4d895e732765 868 }
defmacro 22:4d895e732765 869 } else {
defmacro 22:4d895e732765 870 // start from the beginning
defmacro 22:4d895e732765 871 currentIndex = 0;
defmacro 22:4d895e732765 872 }
defmacro 22:4d895e732765 873 }
defmacro 22:4d895e732765 874
defmacro 22:4d895e732765 875 if (!_client->connected()) {
defmacro 22:4d895e732765 876 DBGLN("%s", "ERROR: The client is disconnected from the server!");
defmacro 22:4d895e732765 877
defmacro 22:4d895e732765 878 close();
defmacro 22:4d895e732765 879 return E_DISCONNECTED;
defmacro 22:4d895e732765 880 }
defmacro 22:4d895e732765 881 if (_idlefunc!=NULL)
defmacro 22:4d895e732765 882 _idlefunc();
defmacro 22:4d895e732765 883 else
defmacro 22:4d895e732765 884 delay(200);
defmacro 22:4d895e732765 885 }
defmacro 22:4d895e732765 886 // never reached here
defmacro 22:4d895e732765 887 return E_NOTREACHABLE;
defmacro 22:4d895e732765 888 }
defmacro 22:4d895e732765 889
defmacro 22:4d895e732765 890 int M2XStreamClient::readStatusCode(bool closeClient) {
defmacro 22:4d895e732765 891 int responseCode = 0;
defmacro 22:4d895e732765 892 int ret = waitForString("HTTP/*.* ");
defmacro 22:4d895e732765 893 if (ret != E_OK) {
defmacro 22:4d895e732765 894 if (closeClient) close();
defmacro 22:4d895e732765 895 return ret;
defmacro 22:4d895e732765 896 }
defmacro 22:4d895e732765 897
defmacro 22:4d895e732765 898 // ret is not needed from here(since it must be E_OK), so we can use it
defmacro 22:4d895e732765 899 // as a regular variable now.
defmacro 22:4d895e732765 900 ret = 0;
defmacro 22:4d895e732765 901 while (true) {
defmacro 22:4d895e732765 902 while (_client->available()) {
defmacro 22:4d895e732765 903 char c = _client->read();
defmacro 22:4d895e732765 904 DBG("%c", c);
defmacro 22:4d895e732765 905
defmacro 22:4d895e732765 906 responseCode = responseCode * 10 + (c - '0');
defmacro 22:4d895e732765 907 ret++;
defmacro 22:4d895e732765 908 if (ret == 3) {
defmacro 22:4d895e732765 909 if (closeClient) close();
defmacro 22:4d895e732765 910 return responseCode;
defmacro 22:4d895e732765 911 }
defmacro 22:4d895e732765 912 }
defmacro 22:4d895e732765 913
defmacro 22:4d895e732765 914 if (!_client->connected()) {
defmacro 22:4d895e732765 915 DBGLN("%s", "ERROR: The client is disconnected from the server!");
defmacro 22:4d895e732765 916
defmacro 22:4d895e732765 917 if (closeClient) close();
defmacro 22:4d895e732765 918 return E_DISCONNECTED;
defmacro 22:4d895e732765 919 }
defmacro 22:4d895e732765 920 if (_idlefunc!=NULL)
defmacro 22:4d895e732765 921 _idlefunc();
defmacro 22:4d895e732765 922 else
defmacro 22:4d895e732765 923 delay(200);
defmacro 22:4d895e732765 924 }
defmacro 22:4d895e732765 925
defmacro 22:4d895e732765 926 // never reached here
defmacro 22:4d895e732765 927 return E_NOTREACHABLE;
defmacro 22:4d895e732765 928 }
defmacro 22:4d895e732765 929
defmacro 22:4d895e732765 930 int M2XStreamClient::readContentLength() {
defmacro 22:4d895e732765 931 int ret = waitForString("Content-Length: ");
defmacro 22:4d895e732765 932 if (ret != E_OK) {
defmacro 22:4d895e732765 933 return ret;
defmacro 22:4d895e732765 934 }
defmacro 22:4d895e732765 935
defmacro 22:4d895e732765 936 // From now on, ret is not needed, we can use it
defmacro 22:4d895e732765 937 // to keep the final result
defmacro 22:4d895e732765 938 ret = 0;
defmacro 22:4d895e732765 939 while (true) {
defmacro 22:4d895e732765 940 while (_client->available()) {
defmacro 22:4d895e732765 941 char c = _client->read();
defmacro 22:4d895e732765 942 DBG("%c", c);
defmacro 22:4d895e732765 943
defmacro 22:4d895e732765 944 if ((c == '\r') || (c == '\n')) {
defmacro 22:4d895e732765 945 return (ret == 0) ? (E_INVALID) : (ret);
defmacro 22:4d895e732765 946 } else {
defmacro 22:4d895e732765 947 ret = ret * 10 + (c - '0');
defmacro 22:4d895e732765 948 }
defmacro 22:4d895e732765 949 }
defmacro 22:4d895e732765 950
defmacro 22:4d895e732765 951 if (!_client->connected()) {
defmacro 22:4d895e732765 952 DBGLN("%s", "ERROR: The client is disconnected from the server!");
defmacro 22:4d895e732765 953
defmacro 22:4d895e732765 954 return E_DISCONNECTED;
defmacro 22:4d895e732765 955 }
defmacro 22:4d895e732765 956 if (_idlefunc!=NULL)
defmacro 22:4d895e732765 957 _idlefunc();
defmacro 22:4d895e732765 958 else
defmacro 22:4d895e732765 959 delay(200);
defmacro 22:4d895e732765 960 }
defmacro 22:4d895e732765 961
defmacro 22:4d895e732765 962 // never reached here
defmacro 22:4d895e732765 963 return E_NOTREACHABLE;
defmacro 22:4d895e732765 964 }
defmacro 22:4d895e732765 965
defmacro 22:4d895e732765 966 int M2XStreamClient::skipHttpHeader() {
defmacro 22:4d895e732765 967 return waitForString("\n\r\n");
defmacro 22:4d895e732765 968 }
defmacro 22:4d895e732765 969
defmacro 22:4d895e732765 970 void M2XStreamClient::close() {
defmacro 22:4d895e732765 971 // Eats up buffered data before closing
defmacro 22:4d895e732765 972 _client->flush();
defmacro 22:4d895e732765 973 _client->stop();
defmacro 22:4d895e732765 974 }
defmacro 22:4d895e732765 975
defmacro 22:4d895e732765 976 static inline int fill_iso8601_timestamp(int32_t seconds, int32_t milli,
defmacro 22:4d895e732765 977 char* buffer, int* length);
defmacro 22:4d895e732765 978
defmacro 22:4d895e732765 979 TimeService::TimeService(M2XStreamClient* client) : _client(client) {
defmacro 22:4d895e732765 980 }
defmacro 22:4d895e732765 981
defmacro 22:4d895e732765 982 int TimeService::init() {
defmacro 22:4d895e732765 983 _timer.start();
defmacro 22:4d895e732765 984 return reset();
defmacro 22:4d895e732765 985 }
defmacro 22:4d895e732765 986
defmacro 22:4d895e732765 987 int TimeService::reset() {
defmacro 22:4d895e732765 988 int32_t ts;
defmacro 22:4d895e732765 989 int status = _client->getTimestamp32(&ts);
defmacro 22:4d895e732765 990
defmacro 22:4d895e732765 991 if (m2x_status_is_success(status)) {
defmacro 22:4d895e732765 992 _server_timestamp = ts;
defmacro 22:4d895e732765 993 _local_last_milli = _timer.read_ms();
defmacro 22:4d895e732765 994 }
defmacro 22:4d895e732765 995
defmacro 22:4d895e732765 996 return status;
defmacro 22:4d895e732765 997 }
defmacro 22:4d895e732765 998
defmacro 22:4d895e732765 999 int TimeService::getTimestamp(char* buffer, int* length) {
defmacro 22:4d895e732765 1000 uint32_t now = _timer.read_ms();
defmacro 22:4d895e732765 1001 if (now < _local_last_milli) {
defmacro 22:4d895e732765 1002 // In case of a timestamp overflow(happens once every 50 days on
defmacro 22:4d895e732765 1003 // Arduino), we reset the server timestamp recorded.
defmacro 22:4d895e732765 1004 // NOTE: while on an Arduino this might be okay, the situation is worse
defmacro 22:4d895e732765 1005 // on mbed, see the notes in m2x-mbed.h for details
defmacro 22:4d895e732765 1006 int status = reset();
defmacro 22:4d895e732765 1007 if (!m2x_status_is_success(status)) { return status; }
defmacro 22:4d895e732765 1008 now = _timer.read_ms();
defmacro 22:4d895e732765 1009 }
defmacro 22:4d895e732765 1010 if (now < _local_last_milli) {
defmacro 22:4d895e732765 1011 // We have already reseted the timestamp, so this cannot happen
defmacro 22:4d895e732765 1012 // (an HTTP request can take longer than 50 days to finished? You
defmacro 22:4d895e732765 1013 // must be kidding here). Something else must be wrong here
defmacro 22:4d895e732765 1014 return E_TIMESTAMP_ERROR;
defmacro 22:4d895e732765 1015 }
defmacro 22:4d895e732765 1016 uint32_t diff = now - _local_last_milli;
defmacro 22:4d895e732765 1017 _local_last_milli = now;
defmacro 22:4d895e732765 1018 _server_timestamp += (int32_t) (diff / 1000); // Milliseconds to seconds
defmacro 22:4d895e732765 1019 return fill_iso8601_timestamp(_server_timestamp, (int32_t) (diff % 1000),
defmacro 22:4d895e732765 1020 buffer, length);
defmacro 22:4d895e732765 1021 }
defmacro 22:4d895e732765 1022
defmacro 22:4d895e732765 1023 #define SIZE_ISO_8601 25
defmacro 22:4d895e732765 1024 static inline bool is_leap_year(int16_t y) {
defmacro 22:4d895e732765 1025 return ((1970 + y) > 0) &&
defmacro 22:4d895e732765 1026 !((1970 + y) % 4) &&
defmacro 22:4d895e732765 1027 (((1970 + y) % 100) || !((1970 + y) % 400));
defmacro 22:4d895e732765 1028 }
defmacro 22:4d895e732765 1029 static inline int32_t days_in_year(int16_t y) {
defmacro 22:4d895e732765 1030 return is_leap_year(y) ? 366 : 365;
defmacro 22:4d895e732765 1031 }
defmacro 22:4d895e732765 1032 static const uint8_t MONTH_DAYS[]={31,28,31,30,31,30,31,31,30,31,30,31};
defmacro 22:4d895e732765 1033
defmacro 22:4d895e732765 1034 static inline int fill_iso8601_timestamp(int32_t timestamp, int32_t milli,
defmacro 22:4d895e732765 1035 char* buffer, int* length) {
defmacro 22:4d895e732765 1036 int16_t year;
defmacro 22:4d895e732765 1037 int8_t month, month_length;
defmacro 22:4d895e732765 1038 int32_t day;
defmacro 22:4d895e732765 1039 int8_t hour, minute, second;
defmacro 22:4d895e732765 1040
defmacro 22:4d895e732765 1041 if (*length < SIZE_ISO_8601) {
defmacro 22:4d895e732765 1042 *length = SIZE_ISO_8601;
defmacro 22:4d895e732765 1043 return E_BUFFER_TOO_SMALL;
defmacro 22:4d895e732765 1044 }
defmacro 22:4d895e732765 1045
defmacro 22:4d895e732765 1046 second = timestamp % 60;
defmacro 22:4d895e732765 1047 timestamp /= 60; // now it is minutes
defmacro 22:4d895e732765 1048
defmacro 22:4d895e732765 1049 minute = timestamp % 60;
defmacro 22:4d895e732765 1050 timestamp /= 60; // now it is hours
defmacro 22:4d895e732765 1051
defmacro 22:4d895e732765 1052 hour = timestamp % 24;
defmacro 22:4d895e732765 1053 timestamp /= 24; // now it is days
defmacro 22:4d895e732765 1054
defmacro 22:4d895e732765 1055 year = 0;
defmacro 22:4d895e732765 1056 day = 0;
defmacro 22:4d895e732765 1057 while ((day += days_in_year(year)) <= timestamp) {
defmacro 22:4d895e732765 1058 year++;
defmacro 22:4d895e732765 1059 }
defmacro 22:4d895e732765 1060 day -= days_in_year(year);
defmacro 22:4d895e732765 1061 timestamp -= day; // now it is days in this year, starting at 0
defmacro 22:4d895e732765 1062
defmacro 22:4d895e732765 1063 day = 0;
defmacro 22:4d895e732765 1064 month_length = 0;
defmacro 22:4d895e732765 1065 for (month = 0; month < 12; month++) {
defmacro 22:4d895e732765 1066 if (month == 1) {
defmacro 22:4d895e732765 1067 // February
defmacro 22:4d895e732765 1068 month_length = is_leap_year(year) ? 29 : 28;
defmacro 22:4d895e732765 1069 } else {
defmacro 22:4d895e732765 1070 month_length = MONTH_DAYS[month];
defmacro 22:4d895e732765 1071 }
defmacro 22:4d895e732765 1072
defmacro 22:4d895e732765 1073 if (timestamp >= month_length) {
defmacro 22:4d895e732765 1074 timestamp -= month_length;
defmacro 22:4d895e732765 1075 } else {
defmacro 22:4d895e732765 1076 break;
defmacro 22:4d895e732765 1077 }
defmacro 22:4d895e732765 1078 }
defmacro 22:4d895e732765 1079 year = 1970 + year;
defmacro 22:4d895e732765 1080 month++; // offset by 1
defmacro 22:4d895e732765 1081 day = timestamp + 1;
defmacro 22:4d895e732765 1082
defmacro 22:4d895e732765 1083 int i = 0, j = 0;
defmacro 22:4d895e732765 1084
defmacro 22:4d895e732765 1085 // NOTE: It seems the snprintf implementation in Arduino has bugs,
defmacro 22:4d895e732765 1086 // we have to manually piece the string together here.
defmacro 22:4d895e732765 1087 #define INT_TO_STR(v_, width_) \
defmacro 22:4d895e732765 1088 for (j = 0; j < (width_); j++) { \
defmacro 22:4d895e732765 1089 buffer[i + (width_) - 1 - j] = '0' + ((v_) % 10); \
defmacro 22:4d895e732765 1090 (v_) /= 10; \
defmacro 22:4d895e732765 1091 } \
defmacro 22:4d895e732765 1092 i += (width_)
defmacro 22:4d895e732765 1093
defmacro 22:4d895e732765 1094 INT_TO_STR(year, 4);
defmacro 22:4d895e732765 1095 buffer[i++] = '-';
defmacro 22:4d895e732765 1096 INT_TO_STR(month, 2);
defmacro 22:4d895e732765 1097 buffer[i++] = '-';
defmacro 22:4d895e732765 1098 INT_TO_STR(day, 2);
defmacro 22:4d895e732765 1099 buffer[i++] = 'T';
defmacro 22:4d895e732765 1100 INT_TO_STR(hour, 2);
defmacro 22:4d895e732765 1101 buffer[i++] = ':';
defmacro 22:4d895e732765 1102 INT_TO_STR(minute, 2);
defmacro 22:4d895e732765 1103 buffer[i++] = ':';
defmacro 22:4d895e732765 1104 INT_TO_STR(second, 2);
defmacro 22:4d895e732765 1105 buffer[i++] = '.';
defmacro 22:4d895e732765 1106 INT_TO_STR(milli, 3);
defmacro 22:4d895e732765 1107 buffer[i++] = 'Z';
defmacro 22:4d895e732765 1108 buffer[i++] = '\0';
defmacro 22:4d895e732765 1109
defmacro 22:4d895e732765 1110 #undef INT_TO_STR
defmacro 22:4d895e732765 1111
defmacro 22:4d895e732765 1112 *length = i;
defmacro 22:4d895e732765 1113 return E_OK;
defmacro 22:4d895e732765 1114 }
defmacro 22:4d895e732765 1115
defmacro 22:4d895e732765 1116 /* Reader functions */
defmacro 22:4d895e732765 1117 #ifdef M2X_ENABLE_READER
defmacro 22:4d895e732765 1118
defmacro 22:4d895e732765 1119 // Data structures and functions used to parse stream values
defmacro 22:4d895e732765 1120
defmacro 22:4d895e732765 1121 #define STREAM_BUF_LEN 32
defmacro 22:4d895e732765 1122
defmacro 22:4d895e732765 1123 typedef struct {
defmacro 22:4d895e732765 1124 uint8_t state;
defmacro 22:4d895e732765 1125 char at_str[STREAM_BUF_LEN + 1];
defmacro 22:4d895e732765 1126 char value_str[STREAM_BUF_LEN + 1];
defmacro 22:4d895e732765 1127 int index;
defmacro 22:4d895e732765 1128
defmacro 22:4d895e732765 1129 stream_value_read_callback callback;
defmacro 22:4d895e732765 1130 void* context;
defmacro 22:4d895e732765 1131 } stream_parsing_context_state;
defmacro 22:4d895e732765 1132
defmacro 22:4d895e732765 1133 #define WAITING_AT 0x1
defmacro 22:4d895e732765 1134 #define GOT_AT 0x2
defmacro 22:4d895e732765 1135 #define WAITING_VALUE 0x4
defmacro 22:4d895e732765 1136 #define GOT_VALUE 0x8
defmacro 22:4d895e732765 1137
defmacro 22:4d895e732765 1138 #define GOT_STREAM (GOT_AT | GOT_VALUE)
defmacro 22:4d895e732765 1139 #define TEST_GOT_STREAM(state_) (((state_) & GOT_STREAM) == GOT_STREAM)
defmacro 22:4d895e732765 1140
defmacro 22:4d895e732765 1141 #define TEST_IS_AT(state_) (((state_) & (WAITING_AT | GOT_AT)) == WAITING_AT)
defmacro 22:4d895e732765 1142 #define TEST_IS_VALUE(state_) (((state_) & (WAITING_VALUE | GOT_VALUE)) == \
defmacro 22:4d895e732765 1143 WAITING_VALUE)
defmacro 22:4d895e732765 1144
defmacro 22:4d895e732765 1145 static void on_stream_key_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1146 jsonlite_token* token)
defmacro 22:4d895e732765 1147 {
defmacro 22:4d895e732765 1148 stream_parsing_context_state* state =
defmacro 22:4d895e732765 1149 (stream_parsing_context_state*) context->client_state;
defmacro 22:4d895e732765 1150 if (strncmp((const char*) token->start, "timestamp", 9) == 0) {
defmacro 22:4d895e732765 1151 state->state |= WAITING_AT;
defmacro 22:4d895e732765 1152 } else if ((strncmp((const char*) token->start, "value", 5) == 0) &&
defmacro 22:4d895e732765 1153 (token->start[5] != 's')) { // get rid of "values"
defmacro 22:4d895e732765 1154 state->state |= WAITING_VALUE;
defmacro 22:4d895e732765 1155 }
defmacro 22:4d895e732765 1156 }
defmacro 22:4d895e732765 1157
defmacro 22:4d895e732765 1158 static void on_stream_value_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1159 jsonlite_token* token,
defmacro 22:4d895e732765 1160 int type)
defmacro 22:4d895e732765 1161 {
defmacro 22:4d895e732765 1162 stream_parsing_context_state* state =
defmacro 22:4d895e732765 1163 (stream_parsing_context_state*) context->client_state;
defmacro 22:4d895e732765 1164
defmacro 22:4d895e732765 1165 if (TEST_IS_AT(state->state)) {
defmacro 22:4d895e732765 1166 strncpy(state->at_str, (const char*) token->start,
defmacro 22:4d895e732765 1167 MIN(token->end - token->start, STREAM_BUF_LEN));
defmacro 22:4d895e732765 1168 state->at_str[MIN(token->end - token->start, STREAM_BUF_LEN)] = '\0';
defmacro 22:4d895e732765 1169 state->state |= GOT_AT;
defmacro 22:4d895e732765 1170 } else if (TEST_IS_VALUE(state->state)) {
defmacro 22:4d895e732765 1171 strncpy(state->value_str, (const char*) token->start,
defmacro 22:4d895e732765 1172 MIN(token->end - token->start, STREAM_BUF_LEN));
defmacro 22:4d895e732765 1173 state->value_str[MIN(token->end - token->start, STREAM_BUF_LEN)] = '\0';
defmacro 22:4d895e732765 1174 state->state |= GOT_VALUE;
defmacro 22:4d895e732765 1175 }
defmacro 22:4d895e732765 1176
defmacro 22:4d895e732765 1177 if (TEST_GOT_STREAM(state->state)) {
defmacro 22:4d895e732765 1178 state->callback(state->at_str, state->value_str,
defmacro 22:4d895e732765 1179 state->index++, state->context, type);
defmacro 22:4d895e732765 1180 state->state = 0;
defmacro 22:4d895e732765 1181 }
defmacro 22:4d895e732765 1182 }
defmacro 22:4d895e732765 1183
defmacro 22:4d895e732765 1184 static void on_stream_string_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1185 jsonlite_token* token)
defmacro 22:4d895e732765 1186 {
defmacro 22:4d895e732765 1187 on_stream_value_found(context, token, 1);
defmacro 22:4d895e732765 1188 }
defmacro 22:4d895e732765 1189
defmacro 22:4d895e732765 1190 static void on_stream_number_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1191 jsonlite_token* token)
defmacro 22:4d895e732765 1192 {
defmacro 22:4d895e732765 1193 on_stream_value_found(context, token, 2);
defmacro 22:4d895e732765 1194 }
defmacro 22:4d895e732765 1195
defmacro 22:4d895e732765 1196 // Data structures and functions used to parse locations
defmacro 22:4d895e732765 1197
defmacro 22:4d895e732765 1198 #define LOCATION_BUF_LEN 20
defmacro 22:4d895e732765 1199
defmacro 22:4d895e732765 1200 typedef struct {
defmacro 22:4d895e732765 1201 uint16_t state;
defmacro 22:4d895e732765 1202 char name_str[LOCATION_BUF_LEN + 1];
defmacro 22:4d895e732765 1203 double latitude;
defmacro 22:4d895e732765 1204 double longitude;
defmacro 22:4d895e732765 1205 double elevation;
defmacro 22:4d895e732765 1206 char timestamp_str[LOCATION_BUF_LEN + 1];
defmacro 22:4d895e732765 1207 int index;
defmacro 22:4d895e732765 1208
defmacro 22:4d895e732765 1209 location_read_callback callback;
defmacro 22:4d895e732765 1210 void* context;
defmacro 22:4d895e732765 1211 } location_parsing_context_state;
defmacro 22:4d895e732765 1212
defmacro 22:4d895e732765 1213 #define WAITING_NAME 0x1
defmacro 22:4d895e732765 1214 #define WAITING_LATITUDE 0x2
defmacro 22:4d895e732765 1215 #define WAITING_LONGITUDE 0x4
defmacro 22:4d895e732765 1216 #define WAITING_ELEVATION 0x8
defmacro 22:4d895e732765 1217 #define WAITING_TIMESTAMP 0x10
defmacro 22:4d895e732765 1218
defmacro 22:4d895e732765 1219 #define GOT_NAME 0x20
defmacro 22:4d895e732765 1220 #define GOT_LATITUDE 0x40
defmacro 22:4d895e732765 1221 #define GOT_LONGITUDE 0x80
defmacro 22:4d895e732765 1222 #define GOT_ELEVATION 0x100
defmacro 22:4d895e732765 1223 #define GOT_TIMESTAMP 0x200
defmacro 22:4d895e732765 1224
defmacro 22:4d895e732765 1225 #define GOT_LOCATION (GOT_NAME | GOT_LATITUDE | GOT_LONGITUDE | GOT_ELEVATION | GOT_TIMESTAMP)
defmacro 22:4d895e732765 1226 #define TEST_GOT_LOCATION(state_) (((state_) & GOT_LOCATION) == GOT_LOCATION)
defmacro 22:4d895e732765 1227
defmacro 22:4d895e732765 1228 #define TEST_IS_NAME(state_) (((state_) & (WAITING_NAME | GOT_NAME)) == WAITING_NAME)
defmacro 22:4d895e732765 1229 #define TEST_IS_LATITUDE(state_) (((state_) & (WAITING_LATITUDE | GOT_LATITUDE)) \
defmacro 22:4d895e732765 1230 == WAITING_LATITUDE)
defmacro 22:4d895e732765 1231 #define TEST_IS_LONGITUDE(state_) (((state_) & (WAITING_LONGITUDE | GOT_LONGITUDE)) \
defmacro 22:4d895e732765 1232 == WAITING_LONGITUDE)
defmacro 22:4d895e732765 1233 #define TEST_IS_ELEVATION(state_) (((state_) & (WAITING_ELEVATION | GOT_ELEVATION)) \
defmacro 22:4d895e732765 1234 == WAITING_ELEVATION)
defmacro 22:4d895e732765 1235 #define TEST_IS_TIMESTAMP(state_) (((state_) & (WAITING_TIMESTAMP | GOT_TIMESTAMP)) \
defmacro 22:4d895e732765 1236 == WAITING_TIMESTAMP)
defmacro 22:4d895e732765 1237
defmacro 22:4d895e732765 1238 static void on_location_key_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1239 jsonlite_token* token) {
defmacro 22:4d895e732765 1240 location_parsing_context_state* state =
defmacro 22:4d895e732765 1241 (location_parsing_context_state*) context->client_state;
defmacro 22:4d895e732765 1242 if (strncmp((const char*) token->start, "waypoints", 9) == 0) {
defmacro 22:4d895e732765 1243 // only parses those locations in waypoints, skip the outer one
defmacro 22:4d895e732765 1244 state->state = 0;
defmacro 22:4d895e732765 1245 } else if (strncmp((const char*) token->start, "name", 4) == 0) {
defmacro 22:4d895e732765 1246 state->state |= WAITING_NAME;
defmacro 22:4d895e732765 1247 } else if (strncmp((const char*) token->start, "latitude", 8) == 0) {
defmacro 22:4d895e732765 1248 state->state |= WAITING_LATITUDE;
defmacro 22:4d895e732765 1249 } else if (strncmp((const char*) token->start, "longitude", 9) == 0) {
defmacro 22:4d895e732765 1250 state->state |= WAITING_LONGITUDE;
defmacro 22:4d895e732765 1251 } else if (strncmp((const char*) token->start, "elevation", 9) == 0) {
defmacro 22:4d895e732765 1252 state->state |= WAITING_ELEVATION;
defmacro 22:4d895e732765 1253 } else if (strncmp((const char*) token->start, "timestamp", 9) == 0) {
defmacro 22:4d895e732765 1254 state->state |= WAITING_TIMESTAMP;
defmacro 22:4d895e732765 1255 }
defmacro 22:4d895e732765 1256 }
defmacro 22:4d895e732765 1257
defmacro 22:4d895e732765 1258 static void on_location_string_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1259 jsonlite_token* token) {
defmacro 22:4d895e732765 1260 location_parsing_context_state* state =
defmacro 22:4d895e732765 1261 (location_parsing_context_state*) context->client_state;
defmacro 22:4d895e732765 1262
defmacro 22:4d895e732765 1263 if (TEST_IS_NAME(state->state)) {
defmacro 22:4d895e732765 1264 strncpy(state->name_str, (const char*) token->start,
defmacro 22:4d895e732765 1265 MIN(token->end - token->start, LOCATION_BUF_LEN));
defmacro 22:4d895e732765 1266 state->name_str[MIN(token->end - token->start, LOCATION_BUF_LEN)] = '\0';
defmacro 22:4d895e732765 1267 state->state |= GOT_NAME;
defmacro 22:4d895e732765 1268 } else if (TEST_IS_LATITUDE(state->state)) {
defmacro 22:4d895e732765 1269 state->latitude = atof((const char*) token->start);
defmacro 22:4d895e732765 1270 state->state |= GOT_LATITUDE;
defmacro 22:4d895e732765 1271 } else if (TEST_IS_LONGITUDE(state->state)) {
defmacro 22:4d895e732765 1272 state->longitude = atof((const char*) token->start);
defmacro 22:4d895e732765 1273 state->state |= GOT_LONGITUDE;
defmacro 22:4d895e732765 1274 } else if (TEST_IS_ELEVATION(state->state)) {
defmacro 22:4d895e732765 1275 state->elevation = atof((const char*) token->start);
defmacro 22:4d895e732765 1276 state->state |= GOT_ELEVATION;
defmacro 22:4d895e732765 1277 } else if (TEST_IS_TIMESTAMP(state->state)) {
defmacro 22:4d895e732765 1278 strncpy(state->timestamp_str, (const char*) token->start,
defmacro 22:4d895e732765 1279 MIN(token->end - token->start, LOCATION_BUF_LEN));
defmacro 22:4d895e732765 1280 state->timestamp_str[MIN(token->end - token->start, LOCATION_BUF_LEN)] = '\0';
defmacro 22:4d895e732765 1281 state->state |= GOT_TIMESTAMP;
defmacro 22:4d895e732765 1282 }
defmacro 22:4d895e732765 1283
defmacro 22:4d895e732765 1284 if (TEST_GOT_LOCATION(state->state)) {
defmacro 22:4d895e732765 1285 state->callback(state->name_str, state->latitude, state->longitude,
defmacro 22:4d895e732765 1286 state->elevation, state->timestamp_str, state->index++,
defmacro 22:4d895e732765 1287 state->context);
defmacro 22:4d895e732765 1288 state->state = 0;
defmacro 22:4d895e732765 1289 }
defmacro 22:4d895e732765 1290 }
defmacro 22:4d895e732765 1291
defmacro 22:4d895e732765 1292 #ifndef M2X_COMMAND_BUF_LEN
defmacro 22:4d895e732765 1293 #define M2X_COMMAND_BUF_LEN 40
defmacro 22:4d895e732765 1294 #endif /* M2X_COMMAND_BUF_LEN */
defmacro 22:4d895e732765 1295
defmacro 22:4d895e732765 1296 typedef struct {
defmacro 22:4d895e732765 1297 uint8_t state;
defmacro 22:4d895e732765 1298 char id_str[M2X_COMMAND_BUF_LEN + 1];
defmacro 22:4d895e732765 1299 char name_str[M2X_COMMAND_BUF_LEN + 1];
defmacro 22:4d895e732765 1300 int index;
defmacro 22:4d895e732765 1301
defmacro 22:4d895e732765 1302 m2x_command_read_callback callback;
defmacro 22:4d895e732765 1303 void* context;
defmacro 22:4d895e732765 1304 } m2x_command_parsing_context_state;
defmacro 22:4d895e732765 1305
defmacro 22:4d895e732765 1306 #define M2X_COMMAND_WAITING_ID 0x1
defmacro 22:4d895e732765 1307 #define M2X_COMMAND_GOT_ID 0x2
defmacro 22:4d895e732765 1308 #define M2X_COMMAND_WAITING_NAME 0x4
defmacro 22:4d895e732765 1309 #define M2X_COMMAND_GOT_NAME 0x8
defmacro 22:4d895e732765 1310
defmacro 22:4d895e732765 1311 #define M2X_COMMAND_GOT_COMMAND (M2X_COMMAND_GOT_ID | M2X_COMMAND_GOT_NAME)
defmacro 22:4d895e732765 1312 #define M2X_COMMAND_TEST_GOT_COMMAND(state_) \
defmacro 22:4d895e732765 1313 (((state_) & M2X_COMMAND_GOT_COMMAND) == M2X_COMMAND_GOT_COMMAND)
defmacro 22:4d895e732765 1314
defmacro 22:4d895e732765 1315 #define M2X_COMMAND_TEST_IS_ID(state_) \
defmacro 22:4d895e732765 1316 (((state_) & (M2X_COMMAND_WAITING_ID | M2X_COMMAND_GOT_ID)) == \
defmacro 22:4d895e732765 1317 M2X_COMMAND_WAITING_ID)
defmacro 22:4d895e732765 1318 #define M2X_COMMAND_TEST_IS_NAME(state_) \
defmacro 22:4d895e732765 1319 (((state_) & (M2X_COMMAND_WAITING_NAME | M2X_COMMAND_GOT_NAME)) == \
defmacro 22:4d895e732765 1320 M2X_COMMAND_WAITING_NAME)
defmacro 22:4d895e732765 1321
defmacro 22:4d895e732765 1322 static void m2x_on_command_key_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1323 jsonlite_token* token)
defmacro 22:4d895e732765 1324 {
defmacro 22:4d895e732765 1325 m2x_command_parsing_context_state* state =
defmacro 22:4d895e732765 1326 (m2x_command_parsing_context_state*) context->client_state;
defmacro 22:4d895e732765 1327 if (strncmp((const char*) token->start, "id", 2) == 0) {
defmacro 22:4d895e732765 1328 state->state |= M2X_COMMAND_WAITING_ID;
defmacro 22:4d895e732765 1329 } else if (strncmp((const char*) token->start, "name", 4) == 0) {
defmacro 22:4d895e732765 1330 state->state |= M2X_COMMAND_WAITING_NAME;
defmacro 22:4d895e732765 1331 }
defmacro 22:4d895e732765 1332 }
defmacro 22:4d895e732765 1333
defmacro 22:4d895e732765 1334 static void m2x_on_command_value_found(jsonlite_callback_context* context,
defmacro 22:4d895e732765 1335 jsonlite_token* token)
defmacro 22:4d895e732765 1336 {
defmacro 22:4d895e732765 1337 m2x_command_parsing_context_state* state =
defmacro 22:4d895e732765 1338 (m2x_command_parsing_context_state*) context->client_state;
defmacro 22:4d895e732765 1339 if (M2X_COMMAND_TEST_IS_ID(state->state)) {
defmacro 22:4d895e732765 1340 strncpy(state->id_str, (const char*) token->start,
defmacro 22:4d895e732765 1341 MIN(token->end - token->start, M2X_COMMAND_BUF_LEN));
defmacro 22:4d895e732765 1342 state->id_str[MIN(token->end - token->start, M2X_COMMAND_BUF_LEN)] = '\0';
defmacro 22:4d895e732765 1343 state->state |= M2X_COMMAND_GOT_ID;
defmacro 22:4d895e732765 1344 } else if (M2X_COMMAND_TEST_IS_NAME(state->state)) {
defmacro 22:4d895e732765 1345 strncpy(state->name_str, (const char*) token->start,
defmacro 22:4d895e732765 1346 MIN(token->end - token->start, M2X_COMMAND_BUF_LEN));
defmacro 22:4d895e732765 1347 state->name_str[MIN(token->end - token->start, M2X_COMMAND_BUF_LEN)] = '\0';
defmacro 22:4d895e732765 1348 state->state |= M2X_COMMAND_GOT_NAME;
defmacro 22:4d895e732765 1349 }
defmacro 22:4d895e732765 1350
defmacro 22:4d895e732765 1351 if (M2X_COMMAND_TEST_GOT_COMMAND(state->state)) {
defmacro 22:4d895e732765 1352 state->callback(state->id_str, state->name_str,
defmacro 22:4d895e732765 1353 state->index++, state->context);
defmacro 22:4d895e732765 1354 state->state = 0;
defmacro 22:4d895e732765 1355 }
defmacro 22:4d895e732765 1356 }
defmacro 22:4d895e732765 1357
defmacro 22:4d895e732765 1358 int M2XStreamClient::listStreamValues(const char* deviceId, const char* streamName,
defmacro 22:4d895e732765 1359 stream_value_read_callback callback, void* context,
defmacro 22:4d895e732765 1360 const char* query) {
defmacro 22:4d895e732765 1361 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 1362 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 1363 _client->print("GET ");
defmacro 22:4d895e732765 1364 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 1365 _client->print("/v2/devices/");
defmacro 22:4d895e732765 1366 _client->print(deviceId);
defmacro 22:4d895e732765 1367 _client->print("/streams/");
defmacro 22:4d895e732765 1368 print_encoded_string(_client, streamName);
defmacro 22:4d895e732765 1369 _client->print("/values");
defmacro 22:4d895e732765 1370
defmacro 22:4d895e732765 1371 if (query) {
defmacro 22:4d895e732765 1372 if (query[0] != '?') {
defmacro 22:4d895e732765 1373 _client->print('?');
defmacro 22:4d895e732765 1374 }
defmacro 22:4d895e732765 1375 _client->print(query);
defmacro 22:4d895e732765 1376 }
defmacro 22:4d895e732765 1377
defmacro 22:4d895e732765 1378 _client->println(" HTTP/1.0");
defmacro 22:4d895e732765 1379 writeHttpHeader(-1);
defmacro 22:4d895e732765 1380 } else {
defmacro 22:4d895e732765 1381 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 1382 return E_NOCONNECTION;
defmacro 22:4d895e732765 1383 }
defmacro 22:4d895e732765 1384 int status = readStatusCode(false);
defmacro 22:4d895e732765 1385 if (status == 200) {
defmacro 22:4d895e732765 1386 readStreamValue(callback, context);
defmacro 22:4d895e732765 1387 }
defmacro 22:4d895e732765 1388
defmacro 22:4d895e732765 1389 close();
defmacro 22:4d895e732765 1390 return status;
defmacro 22:4d895e732765 1391 }
defmacro 22:4d895e732765 1392
defmacro 22:4d895e732765 1393 int M2XStreamClient::readLocation(const char* deviceId,
defmacro 22:4d895e732765 1394 location_read_callback callback,
defmacro 22:4d895e732765 1395 void* context) {
defmacro 22:4d895e732765 1396 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 1397 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 1398 _client->print("GET ");
defmacro 22:4d895e732765 1399 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 1400 _client->print("/v2/devices/");
defmacro 22:4d895e732765 1401 _client->print(deviceId);
defmacro 22:4d895e732765 1402 _client->println("/location HTTP/1.0");
defmacro 22:4d895e732765 1403
defmacro 22:4d895e732765 1404 writeHttpHeader(-1);
defmacro 22:4d895e732765 1405 } else {
defmacro 22:4d895e732765 1406 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 1407 return E_NOCONNECTION;
defmacro 22:4d895e732765 1408 }
defmacro 22:4d895e732765 1409 int status = readStatusCode(false);
defmacro 22:4d895e732765 1410 if (status == 200) {
defmacro 22:4d895e732765 1411 readLocation(callback, context);
defmacro 22:4d895e732765 1412 }
defmacro 22:4d895e732765 1413
defmacro 22:4d895e732765 1414 close();
defmacro 22:4d895e732765 1415 return status;
defmacro 22:4d895e732765 1416 }
defmacro 22:4d895e732765 1417
defmacro 22:4d895e732765 1418 int M2XStreamClient::listCommands(const char* deviceId,
defmacro 22:4d895e732765 1419 m2x_command_read_callback callback,
defmacro 22:4d895e732765 1420 void* context,
defmacro 22:4d895e732765 1421 const char* query) {
defmacro 22:4d895e732765 1422 if (_client->connect(_host, _port)) {
defmacro 22:4d895e732765 1423 DBGLN("%s", "Connected to M2X server!");
defmacro 22:4d895e732765 1424 _client->print("GET ");
defmacro 22:4d895e732765 1425 if (_path_prefix) { _client->print(_path_prefix); }
defmacro 22:4d895e732765 1426 _client->print("/v2/devices/");
defmacro 22:4d895e732765 1427 _client->print(deviceId);
defmacro 22:4d895e732765 1428 _client->print("/commands");
defmacro 22:4d895e732765 1429
defmacro 22:4d895e732765 1430 if (query) {
defmacro 22:4d895e732765 1431 if (query[0] != '?') {
defmacro 22:4d895e732765 1432 _client->print('?');
defmacro 22:4d895e732765 1433 }
defmacro 22:4d895e732765 1434 _client->print(query);
defmacro 22:4d895e732765 1435 }
defmacro 22:4d895e732765 1436
defmacro 22:4d895e732765 1437 _client->println(" HTTP/1.0");
defmacro 22:4d895e732765 1438 writeHttpHeader(-1);
defmacro 22:4d895e732765 1439 } else {
defmacro 22:4d895e732765 1440 DBGLN("%s", "ERROR: Cannot connect to M2X server!");
defmacro 22:4d895e732765 1441 return E_NOCONNECTION;
defmacro 22:4d895e732765 1442 }
defmacro 22:4d895e732765 1443 int status = readStatusCode(false);
defmacro 22:4d895e732765 1444 if (status == 200) {
defmacro 22:4d895e732765 1445 readCommand(callback, context);
defmacro 22:4d895e732765 1446 }
defmacro 22:4d895e732765 1447
defmacro 22:4d895e732765 1448 close();
defmacro 22:4d895e732765 1449 return status;
defmacro 22:4d895e732765 1450 }
defmacro 22:4d895e732765 1451
defmacro 22:4d895e732765 1452
defmacro 22:4d895e732765 1453 int M2XStreamClient::readStreamValue(stream_value_read_callback callback,
defmacro 22:4d895e732765 1454 void* context) {
defmacro 22:4d895e732765 1455 const int BUF_LEN = 64;
defmacro 22:4d895e732765 1456 char buf[BUF_LEN];
defmacro 22:4d895e732765 1457
defmacro 22:4d895e732765 1458 int length = readContentLength();
defmacro 22:4d895e732765 1459 if (length < 0) {
defmacro 22:4d895e732765 1460 close();
defmacro 22:4d895e732765 1461 return length;
defmacro 22:4d895e732765 1462 }
defmacro 22:4d895e732765 1463
defmacro 22:4d895e732765 1464 int index = skipHttpHeader();
defmacro 22:4d895e732765 1465 if (index != E_OK) {
defmacro 22:4d895e732765 1466 close();
defmacro 22:4d895e732765 1467 return index;
defmacro 22:4d895e732765 1468 }
defmacro 22:4d895e732765 1469 index = 0;
defmacro 22:4d895e732765 1470
defmacro 22:4d895e732765 1471 stream_parsing_context_state state;
defmacro 22:4d895e732765 1472 state.state = state.index = 0;
defmacro 22:4d895e732765 1473 state.callback = callback;
defmacro 22:4d895e732765 1474 state.context = context;
defmacro 22:4d895e732765 1475
defmacro 22:4d895e732765 1476 jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
defmacro 22:4d895e732765 1477 cbs.key_found = on_stream_key_found;
defmacro 22:4d895e732765 1478 cbs.number_found = on_stream_number_found;
defmacro 22:4d895e732765 1479 cbs.string_found = on_stream_string_found;
defmacro 22:4d895e732765 1480 cbs.context.client_state = &state;
defmacro 22:4d895e732765 1481
defmacro 22:4d895e732765 1482 jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
defmacro 22:4d895e732765 1483 jsonlite_parser_set_callback(p, &cbs);
defmacro 22:4d895e732765 1484
defmacro 22:4d895e732765 1485 jsonlite_result result = jsonlite_result_unknown;
defmacro 22:4d895e732765 1486 while (index < length) {
defmacro 22:4d895e732765 1487 int i = 0;
defmacro 22:4d895e732765 1488
defmacro 22:4d895e732765 1489 DBG("%s", "Received Data: ");
defmacro 22:4d895e732765 1490 while ((i < BUF_LEN) && _client->available()) {
defmacro 22:4d895e732765 1491 buf[i++] = _client->read();
defmacro 22:4d895e732765 1492 DBG("%c", buf[i - 1]);
defmacro 22:4d895e732765 1493 }
defmacro 22:4d895e732765 1494 DBGLNEND;
defmacro 22:4d895e732765 1495
defmacro 22:4d895e732765 1496 if ((!_client->connected()) &&
defmacro 22:4d895e732765 1497 (!_client->available()) &&
defmacro 22:4d895e732765 1498 ((index + i) < length)) {
defmacro 22:4d895e732765 1499 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1500 close();
defmacro 22:4d895e732765 1501 return E_NOCONNECTION;
defmacro 22:4d895e732765 1502 }
defmacro 22:4d895e732765 1503
defmacro 22:4d895e732765 1504 result = jsonlite_parser_tokenize(p, buf, i);
defmacro 22:4d895e732765 1505 if ((result != jsonlite_result_ok) &&
defmacro 22:4d895e732765 1506 (result != jsonlite_result_end_of_stream)) {
defmacro 22:4d895e732765 1507 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1508 close();
defmacro 22:4d895e732765 1509 return E_JSON_INVALID;
defmacro 22:4d895e732765 1510 }
defmacro 22:4d895e732765 1511
defmacro 22:4d895e732765 1512 index += i;
defmacro 22:4d895e732765 1513 }
defmacro 22:4d895e732765 1514
defmacro 22:4d895e732765 1515 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1516 close();
defmacro 22:4d895e732765 1517 return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
defmacro 22:4d895e732765 1518 }
defmacro 22:4d895e732765 1519
defmacro 22:4d895e732765 1520 int M2XStreamClient::readLocation(location_read_callback callback,
defmacro 22:4d895e732765 1521 void* context) {
defmacro 22:4d895e732765 1522 const int BUF_LEN = 40;
defmacro 22:4d895e732765 1523 char buf[BUF_LEN];
defmacro 22:4d895e732765 1524
defmacro 22:4d895e732765 1525 int length = readContentLength();
defmacro 22:4d895e732765 1526 if (length < 0) {
defmacro 22:4d895e732765 1527 close();
defmacro 22:4d895e732765 1528 return length;
defmacro 22:4d895e732765 1529 }
defmacro 22:4d895e732765 1530
defmacro 22:4d895e732765 1531 int index = skipHttpHeader();
defmacro 22:4d895e732765 1532 if (index != E_OK) {
defmacro 22:4d895e732765 1533 close();
defmacro 22:4d895e732765 1534 return index;
defmacro 22:4d895e732765 1535 }
defmacro 22:4d895e732765 1536 index = 0;
defmacro 22:4d895e732765 1537
defmacro 22:4d895e732765 1538 location_parsing_context_state state;
defmacro 22:4d895e732765 1539 state.state = state.index = 0;
defmacro 22:4d895e732765 1540 state.callback = callback;
defmacro 22:4d895e732765 1541 state.context = context;
defmacro 22:4d895e732765 1542
defmacro 22:4d895e732765 1543 jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
defmacro 22:4d895e732765 1544 cbs.key_found = on_location_key_found;
defmacro 22:4d895e732765 1545 cbs.string_found = on_location_string_found;
defmacro 22:4d895e732765 1546 cbs.context.client_state = &state;
defmacro 22:4d895e732765 1547
defmacro 22:4d895e732765 1548 jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
defmacro 22:4d895e732765 1549 jsonlite_parser_set_callback(p, &cbs);
defmacro 22:4d895e732765 1550
defmacro 22:4d895e732765 1551 jsonlite_result result = jsonlite_result_unknown;
defmacro 22:4d895e732765 1552 while (index < length) {
defmacro 22:4d895e732765 1553 int i = 0;
defmacro 22:4d895e732765 1554
defmacro 22:4d895e732765 1555 DBG("%s", "Received Data: ");
defmacro 22:4d895e732765 1556 while ((i < BUF_LEN) && _client->available()) {
defmacro 22:4d895e732765 1557 buf[i++] = _client->read();
defmacro 22:4d895e732765 1558 DBG("%c", buf[i - 1]);
defmacro 22:4d895e732765 1559 }
defmacro 22:4d895e732765 1560 DBGLNEND;
defmacro 22:4d895e732765 1561
defmacro 22:4d895e732765 1562 if ((!_client->connected()) &&
defmacro 22:4d895e732765 1563 (!_client->available()) &&
defmacro 22:4d895e732765 1564 ((index + i) < length)) {
defmacro 22:4d895e732765 1565 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1566 close();
defmacro 22:4d895e732765 1567 return E_NOCONNECTION;
defmacro 22:4d895e732765 1568 }
defmacro 22:4d895e732765 1569
defmacro 22:4d895e732765 1570 result = jsonlite_parser_tokenize(p, buf, i);
defmacro 22:4d895e732765 1571 if ((result != jsonlite_result_ok) &&
defmacro 22:4d895e732765 1572 (result != jsonlite_result_end_of_stream)) {
defmacro 22:4d895e732765 1573 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1574 close();
defmacro 22:4d895e732765 1575 return E_JSON_INVALID;
defmacro 22:4d895e732765 1576 }
defmacro 22:4d895e732765 1577
defmacro 22:4d895e732765 1578 index += i;
defmacro 22:4d895e732765 1579 }
defmacro 22:4d895e732765 1580
defmacro 22:4d895e732765 1581 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1582 close();
defmacro 22:4d895e732765 1583 return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
defmacro 22:4d895e732765 1584 }
defmacro 22:4d895e732765 1585
defmacro 22:4d895e732765 1586 int M2XStreamClient::readCommand(m2x_command_read_callback callback,
defmacro 22:4d895e732765 1587 void* context) {
defmacro 22:4d895e732765 1588 const int BUF_LEN = 60;
defmacro 22:4d895e732765 1589 char buf[BUF_LEN];
defmacro 22:4d895e732765 1590
defmacro 22:4d895e732765 1591 int length = readContentLength();
defmacro 22:4d895e732765 1592 if (length < 0) {
defmacro 22:4d895e732765 1593 close();
defmacro 22:4d895e732765 1594 return length;
defmacro 22:4d895e732765 1595 }
defmacro 22:4d895e732765 1596
defmacro 22:4d895e732765 1597 int index = skipHttpHeader();
defmacro 22:4d895e732765 1598 if (index != E_OK) {
defmacro 22:4d895e732765 1599 close();
defmacro 22:4d895e732765 1600 return index;
defmacro 22:4d895e732765 1601 }
defmacro 22:4d895e732765 1602 index = 0;
defmacro 22:4d895e732765 1603
defmacro 22:4d895e732765 1604 m2x_command_parsing_context_state state;
defmacro 22:4d895e732765 1605 state.state = 0;
defmacro 22:4d895e732765 1606 state.index = 0;
defmacro 22:4d895e732765 1607 state.callback = callback;
defmacro 22:4d895e732765 1608 state.context = context;
defmacro 22:4d895e732765 1609
defmacro 22:4d895e732765 1610 jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
defmacro 22:4d895e732765 1611 cbs.key_found = m2x_on_command_key_found;
defmacro 22:4d895e732765 1612 cbs.string_found = m2x_on_command_value_found;
defmacro 22:4d895e732765 1613 cbs.context.client_state = &state;
defmacro 22:4d895e732765 1614
defmacro 22:4d895e732765 1615 jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
defmacro 22:4d895e732765 1616 jsonlite_parser_set_callback(p, &cbs);
defmacro 22:4d895e732765 1617
defmacro 22:4d895e732765 1618 jsonlite_result result = jsonlite_result_unknown;
defmacro 22:4d895e732765 1619 while (index < length) {
defmacro 22:4d895e732765 1620 int i = 0;
defmacro 22:4d895e732765 1621
defmacro 22:4d895e732765 1622 DBG("%s", "Received Data: ");
defmacro 22:4d895e732765 1623 while ((i < BUF_LEN) && _client->available()) {
defmacro 22:4d895e732765 1624 buf[i++] = _client->read();
defmacro 22:4d895e732765 1625 DBG("%c", buf[i - 1]);
defmacro 22:4d895e732765 1626 }
defmacro 22:4d895e732765 1627 DBGLNEND;
defmacro 22:4d895e732765 1628
defmacro 22:4d895e732765 1629 if ((!_client->connected()) &&
defmacro 22:4d895e732765 1630 (!_client->available()) &&
defmacro 22:4d895e732765 1631 ((index + i) < length)) {
defmacro 22:4d895e732765 1632 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1633 close();
defmacro 22:4d895e732765 1634 return E_NOCONNECTION;
defmacro 22:4d895e732765 1635 }
defmacro 22:4d895e732765 1636
defmacro 22:4d895e732765 1637 result = jsonlite_parser_tokenize(p, buf, i);
defmacro 22:4d895e732765 1638 if ((result != jsonlite_result_ok) &&
defmacro 22:4d895e732765 1639 (result != jsonlite_result_end_of_stream)) {
defmacro 22:4d895e732765 1640 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1641 close();
defmacro 22:4d895e732765 1642 return E_JSON_INVALID;
defmacro 22:4d895e732765 1643 }
defmacro 22:4d895e732765 1644
defmacro 22:4d895e732765 1645 index += i;
defmacro 22:4d895e732765 1646 }
defmacro 22:4d895e732765 1647
defmacro 22:4d895e732765 1648 jsonlite_parser_release(p);
defmacro 22:4d895e732765 1649 close();
defmacro 22:4d895e732765 1650 return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
defmacro 22:4d895e732765 1651 }
defmacro 22:4d895e732765 1652
defmacro 22:4d895e732765 1653 #endif /* M2X_ENABLE_READER */
jb8414 0:f479e4f4db0e 1654
jb8414 0:f479e4f4db0e 1655 #endif /* M2XStreamClient_h */