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
M2XStreamClient.cpp@17:9db4a86b876a, 2015-12-28 (annotated)
- Committer:
- citrusbyte
- Date:
- Mon Dec 28 13:06:27 2015 +0000
- Revision:
- 17:9db4a86b876a
- Parent:
- 16:7903152de19f
- Child:
- 19:4dfa28d37b8f
Update comments to TimeService
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
jb8414 | 0:f479e4f4db0e | 1 | #include "M2XStreamClient.h" |
jb8414 | 0:f479e4f4db0e | 2 | |
citrusbyte | 17:9db4a86b876a | 3 | static int fill_iso8601_timestamp(int32_t seconds, int32_t milli, |
citrusbyte | 17:9db4a86b876a | 4 | char* buffer, int* length); |
jb8414 | 0:f479e4f4db0e | 5 | |
citrusbyte | 17:9db4a86b876a | 6 | TimeService::TimeService(M2XStreamClient* client) : _client(client) { |
jb8414 | 0:f479e4f4db0e | 7 | } |
jb8414 | 0:f479e4f4db0e | 8 | |
citrusbyte | 17:9db4a86b876a | 9 | int TimeService::init() { |
citrusbyte | 17:9db4a86b876a | 10 | _timer.start(); |
citrusbyte | 17:9db4a86b876a | 11 | return reset(); |
citrusbyte | 10:4ce9eba38dbe | 12 | } |
citrusbyte | 10:4ce9eba38dbe | 13 | |
citrusbyte | 17:9db4a86b876a | 14 | int TimeService::reset() { |
citrusbyte | 17:9db4a86b876a | 15 | int32_t ts; |
citrusbyte | 17:9db4a86b876a | 16 | int status = _client->getTimestamp32(&ts); |
citrusbyte | 16:7903152de19f | 17 | |
citrusbyte | 17:9db4a86b876a | 18 | if (m2x_status_is_success(status)) { |
citrusbyte | 17:9db4a86b876a | 19 | _server_timestamp = ts; |
citrusbyte | 17:9db4a86b876a | 20 | _local_last_milli = _timer.read_ms(); |
citrusbyte | 16:7903152de19f | 21 | } |
citrusbyte | 16:7903152de19f | 22 | |
citrusbyte | 16:7903152de19f | 23 | return status; |
citrusbyte | 16:7903152de19f | 24 | } |
citrusbyte | 16:7903152de19f | 25 | |
citrusbyte | 17:9db4a86b876a | 26 | int TimeService::getTimestamp(char* buffer, int* length) { |
citrusbyte | 17:9db4a86b876a | 27 | uint32_t now = _timer.read_ms(); |
citrusbyte | 17:9db4a86b876a | 28 | if (now < _local_last_milli) { |
citrusbyte | 17:9db4a86b876a | 29 | // In case of a timestamp overflow, we reset the server timestamp recorded. |
citrusbyte | 17:9db4a86b876a | 30 | // Notice that unlike Arduino, mbed would overflow every 30 minutes, |
citrusbyte | 17:9db4a86b876a | 31 | // so if 2 calls to this API are more than 30 minutes apart, we are |
citrusbyte | 17:9db4a86b876a | 32 | // likely to run into troubles. This is a limitation of the current |
citrusbyte | 17:9db4a86b876a | 33 | // mbed platform. However, we argue that it might be a rare case that |
citrusbyte | 17:9db4a86b876a | 34 | // 2 calls to this are 30 minutes apart. In most cases, we would call |
citrusbyte | 17:9db4a86b876a | 35 | // this API every few seconds or minutes, this won't be a huge problem. |
citrusbyte | 17:9db4a86b876a | 36 | // However, if you have a use case that would require 2 intervening |
citrusbyte | 17:9db4a86b876a | 37 | // calls to this be 30 minutes apart, you might want to leverage a RTC |
citrusbyte | 17:9db4a86b876a | 38 | // clock instead of the simple ticker here. |
citrusbyte | 17:9db4a86b876a | 39 | int status = reset(); |
citrusbyte | 17:9db4a86b876a | 40 | if (!m2x_status_is_success(status)) { return status; } |
citrusbyte | 17:9db4a86b876a | 41 | now = _timer.read_ms(); |
jb8414 | 0:f479e4f4db0e | 42 | } |
citrusbyte | 17:9db4a86b876a | 43 | if (now < _local_last_milli) { |
citrusbyte | 17:9db4a86b876a | 44 | // We have already reseted the timestamp, so this cannot happen, |
citrusbyte | 17:9db4a86b876a | 45 | // an HTTP request cannot last 30 minutes, something else must be wrong |
citrusbyte | 17:9db4a86b876a | 46 | // here. |
citrusbyte | 17:9db4a86b876a | 47 | return E_TIMESTAMP_ERROR; |
jb8414 | 0:f479e4f4db0e | 48 | } |
citrusbyte | 17:9db4a86b876a | 49 | uint32_t diff = now - _local_last_milli; |
citrusbyte | 17:9db4a86b876a | 50 | _local_last_milli = now; |
citrusbyte | 17:9db4a86b876a | 51 | _server_timestamp += (int32_t) (diff / 1000); // Milliseconds to seconds |
citrusbyte | 17:9db4a86b876a | 52 | return fill_iso8601_timestamp(_server_timestamp, (int32_t) (diff % 1000), |
citrusbyte | 17:9db4a86b876a | 53 | buffer, length); |
jb8414 | 0:f479e4f4db0e | 54 | } |
jb8414 | 0:f479e4f4db0e | 55 | |
citrusbyte | 17:9db4a86b876a | 56 | #define SIZE_ISO_8601 25 |
citrusbyte | 17:9db4a86b876a | 57 | static inline bool is_leap_year(int16_t y) { |
citrusbyte | 17:9db4a86b876a | 58 | return ((1970 + y) > 0) && |
citrusbyte | 17:9db4a86b876a | 59 | !((1970 + y) % 4) && |
citrusbyte | 17:9db4a86b876a | 60 | (((1970 + y) % 100) || !((1970 + y) % 400)); |
citrusbyte | 17:9db4a86b876a | 61 | } |
citrusbyte | 17:9db4a86b876a | 62 | static inline int32_t days_in_year(int16_t y) { |
citrusbyte | 17:9db4a86b876a | 63 | return is_leap_year(y) ? 366 : 365; |
citrusbyte | 17:9db4a86b876a | 64 | } |
citrusbyte | 17:9db4a86b876a | 65 | static const uint8_t MONTH_DAYS[]={31,28,31,30,31,30,31,31,30,31,30,31}; |
jb8414 | 0:f479e4f4db0e | 66 | |
citrusbyte | 17:9db4a86b876a | 67 | static int fill_iso8601_timestamp(int32_t timestamp, int32_t milli, |
citrusbyte | 17:9db4a86b876a | 68 | char* buffer, int* length) { |
citrusbyte | 17:9db4a86b876a | 69 | int16_t year; |
citrusbyte | 17:9db4a86b876a | 70 | int8_t month, month_length; |
citrusbyte | 17:9db4a86b876a | 71 | int32_t day; |
citrusbyte | 17:9db4a86b876a | 72 | int8_t hour, minute, second; |
jb8414 | 0:f479e4f4db0e | 73 | |
citrusbyte | 17:9db4a86b876a | 74 | if (*length < SIZE_ISO_8601) { |
citrusbyte | 17:9db4a86b876a | 75 | *length = SIZE_ISO_8601; |
citrusbyte | 17:9db4a86b876a | 76 | return E_BUFFER_TOO_SMALL; |
jb8414 | 0:f479e4f4db0e | 77 | } |
jb8414 | 0:f479e4f4db0e | 78 | |
citrusbyte | 17:9db4a86b876a | 79 | second = timestamp % 60; |
citrusbyte | 17:9db4a86b876a | 80 | timestamp /= 60; // now it is minutes |
jb8414 | 0:f479e4f4db0e | 81 | |
citrusbyte | 17:9db4a86b876a | 82 | minute = timestamp % 60; |
citrusbyte | 17:9db4a86b876a | 83 | timestamp /= 60; // now it is hours |
jb8414 | 0:f479e4f4db0e | 84 | |
citrusbyte | 17:9db4a86b876a | 85 | hour = timestamp % 24; |
citrusbyte | 17:9db4a86b876a | 86 | timestamp /= 24; // now it is days |
jb8414 | 0:f479e4f4db0e | 87 | |
citrusbyte | 17:9db4a86b876a | 88 | year = 0; |
citrusbyte | 17:9db4a86b876a | 89 | day = 0; |
citrusbyte | 17:9db4a86b876a | 90 | while ((day += days_in_year(year)) <= timestamp) { |
citrusbyte | 17:9db4a86b876a | 91 | year++; |
citrusbyte | 17:9db4a86b876a | 92 | } |
citrusbyte | 17:9db4a86b876a | 93 | day -= days_in_year(year); |
citrusbyte | 17:9db4a86b876a | 94 | timestamp -= day; // now it is days in this year, starting at 0 |
jb8414 | 0:f479e4f4db0e | 95 | |
citrusbyte | 17:9db4a86b876a | 96 | day = 0; |
citrusbyte | 17:9db4a86b876a | 97 | month_length = 0; |
citrusbyte | 17:9db4a86b876a | 98 | for (month = 0; month < 12; month++) { |
citrusbyte | 17:9db4a86b876a | 99 | if (month == 1) { |
citrusbyte | 17:9db4a86b876a | 100 | // February |
citrusbyte | 17:9db4a86b876a | 101 | month_length = is_leap_year(year) ? 29 : 28; |
citrusbyte | 17:9db4a86b876a | 102 | } else { |
citrusbyte | 17:9db4a86b876a | 103 | month_length = MONTH_DAYS[month]; |
jb8414 | 0:f479e4f4db0e | 104 | } |
citrusbyte | 10:4ce9eba38dbe | 105 | |
citrusbyte | 17:9db4a86b876a | 106 | if (timestamp >= month_length) { |
citrusbyte | 17:9db4a86b876a | 107 | timestamp -= month_length; |
citrusbyte | 17:9db4a86b876a | 108 | } else { |
citrusbyte | 17:9db4a86b876a | 109 | break; |
citrusbyte | 17:9db4a86b876a | 110 | } |
jb8414 | 0:f479e4f4db0e | 111 | } |
citrusbyte | 17:9db4a86b876a | 112 | year = 1970 + year; |
citrusbyte | 17:9db4a86b876a | 113 | month++; // offset by 1 |
citrusbyte | 17:9db4a86b876a | 114 | day = timestamp + 1; |
jb8414 | 0:f479e4f4db0e | 115 | |
citrusbyte | 17:9db4a86b876a | 116 | int i = 0, j = 0; |
jb8414 | 0:f479e4f4db0e | 117 | |
citrusbyte | 17:9db4a86b876a | 118 | // NOTE: It seems the snprintf implementation in Arduino has bugs, |
citrusbyte | 17:9db4a86b876a | 119 | // we have to manually piece the string together here. |
citrusbyte | 17:9db4a86b876a | 120 | #define INT_TO_STR(v_, width_) \ |
citrusbyte | 17:9db4a86b876a | 121 | for (j = 0; j < (width_); j++) { \ |
citrusbyte | 17:9db4a86b876a | 122 | buffer[i + (width_) - 1 - j] = '0' + ((v_) % 10); \ |
citrusbyte | 17:9db4a86b876a | 123 | (v_) /= 10; \ |
citrusbyte | 17:9db4a86b876a | 124 | } \ |
citrusbyte | 17:9db4a86b876a | 125 | i += (width_) |
jb8414 | 0:f479e4f4db0e | 126 | |
citrusbyte | 17:9db4a86b876a | 127 | INT_TO_STR(year, 4); |
citrusbyte | 17:9db4a86b876a | 128 | buffer[i++] = '-'; |
citrusbyte | 17:9db4a86b876a | 129 | INT_TO_STR(month, 2); |
citrusbyte | 17:9db4a86b876a | 130 | buffer[i++] = '-'; |
citrusbyte | 17:9db4a86b876a | 131 | INT_TO_STR(day, 2); |
citrusbyte | 17:9db4a86b876a | 132 | buffer[i++] = 'T'; |
citrusbyte | 17:9db4a86b876a | 133 | INT_TO_STR(hour, 2); |
citrusbyte | 17:9db4a86b876a | 134 | buffer[i++] = ':'; |
citrusbyte | 17:9db4a86b876a | 135 | INT_TO_STR(minute, 2); |
citrusbyte | 17:9db4a86b876a | 136 | buffer[i++] = ':'; |
citrusbyte | 17:9db4a86b876a | 137 | INT_TO_STR(second, 2); |
citrusbyte | 17:9db4a86b876a | 138 | buffer[i++] = '.'; |
citrusbyte | 17:9db4a86b876a | 139 | INT_TO_STR(milli, 3); |
citrusbyte | 17:9db4a86b876a | 140 | buffer[i++] = 'Z'; |
citrusbyte | 17:9db4a86b876a | 141 | buffer[i++] = '\0'; |
jb8414 | 0:f479e4f4db0e | 142 | |
citrusbyte | 17:9db4a86b876a | 143 | #undef INT_TO_STR |
jb8414 | 0:f479e4f4db0e | 144 | |
citrusbyte | 17:9db4a86b876a | 145 | *length = i; |
citrusbyte | 17:9db4a86b876a | 146 | return E_OK; |
jb8414 | 0:f479e4f4db0e | 147 | } |