Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependents: WNCInterface_M2Xdemo ATT_WNCInterface_Info WNCInterface_HTTP_example Public_IoT_M2X_Cellular_Demo
Fork of M2XStreamClient by
M2XStreamClient.cpp
- Committer:
- citrusbyte
- Date:
- 2013-10-24
- Revision:
- 5:ea68c8980ad8
- Child:
- 6:e6d66d99dd6f
File content as of revision 5:ea68c8980ad8:
#include "M2XStreamClient.h"
#include <jsonlite.h>
#include "StreamParseFunctions.h"
#include "LocationParseFunctions.h"
#define HEX(t_) ((char) (((t_) > 9) ? ((t_) - 10 + 'A') : ((t_) + '0')))
#define MAX_DOUBLE_DIGITS 7
const char* M2XStreamClient::kDefaultM2XHost = "api-m2x.att.com";
const char* kUserAgentLine = "User-Agent: M2X Arduino Client/0.1";
static int print_encoded_string(Print* print, const char* str);
M2XStreamClient::M2XStreamClient(Client* client,
const char* key,
const char* host,
int port) : _client(client),
_key(key),
_host(host),
_port(port),
_null_print() {
}
int M2XStreamClient::send(const char* feedId,
const char* streamName,
double value) {
if (_client->connect(_host, _port)) {
#ifdef DEBUG
printf("Connected to M2X server!\n");
#endif
writeSendHeader(feedId, streamName,
// 6 for "value="
_null_print.print(value) + 6);
_client->print("value=");
// value is a double, does not need encoding
_client->print(value);
} else {
#ifdef DEBUG
printf("ERROR: Cannot connect to M2X server!\n");
#endif
return E_NOCONNECTION;
}
return readStatusCode(true);
}
int M2XStreamClient::send(const char* feedId,
const char* streamName,
long value) {
if (_client->connect(_host, _port)) {
#ifdef DEBUG
printf("Connected to M2X server!\n");
#endif
writeSendHeader(feedId, streamName,
// 6 for "value="
_null_print.print(value) + 6);
_client->print("value=");
// value is a long, does not need encoding
_client->print(value);
} else {
#ifdef DEBUG
printf("ERROR: Cannot connect to M2X server!\n");
#endif
return E_NOCONNECTION;
}
return readStatusCode(true);
}
int M2XStreamClient::send(const char* feedId,
const char* streamName,
int value) {
if (_client->connect(_host, _port)) {
#ifdef DEBUG
printf("Connected to M2X server!\n");
#endif
writeSendHeader(feedId, streamName,
// 6 for "value="
_null_print.print(value) + 6);
_client->print("value=");
// value is an int, does not need encoding
_client->print(value);
} else {
#ifdef DEBUG
printf("ERROR: Cannot connect to M2X server!\n");
#endif
return E_NOCONNECTION;
}
return readStatusCode(true);
}
int M2XStreamClient::send(const char* feedId,
const char* streamName,
const char* value) {
if (_client->connect(_host, _port)) {
#ifdef DEBUG
printf("Connected to M2X server!\n");
#endif
writeSendHeader(feedId, streamName,
// 6 for "value="
_null_print.print(value) + 6);
_client->print("value=");
print_encoded_string(_client, value);
} else {
#ifdef DEBUG
printf("ERROR: Cannot connect to M2X server!\n");
#endif
return E_NOCONNECTION;
}
return readStatusCode(true);
}
int M2XStreamClient::receive(const char* feedId, const char* streamName,
stream_value_read_callback callback, void* context) {
if (_client->connect(_host, _port)) {
#ifdef DEBUG
printf("Connected to M2X server!\n");
#endif
_client->print("GET /v1/feeds/");
print_encoded_string(_client, feedId);
_client->print("/streams/");
print_encoded_string(_client, streamName);
_client->println("/values HTTP/1.0");
writeHttpHeader(-1);
} else {
#ifdef DEBUG
printf("ERROR: Cannot connect to M2X server!\n");
#endif
return E_NOCONNECTION;
}
int status = readStatusCode(false);
if (status == 200) {
readStreamValue(callback, context);
}
close();
return status;
}
int M2XStreamClient::readLocation(const char* feedId,
location_read_callback callback,
void* context) {
if (_client->connect(_host, _port)) {
#ifdef DEBUG
printf("Connected to M2X server!\n");
#endif
_client->print("GET /v1/feeds/");
print_encoded_string(_client, feedId);
_client->println("/location HTTP/1.0");
writeHttpHeader(-1);
} else {
#ifdef DEBUG
printf("ERROR: Cannot connect to M2X server!\n");
#endif
return E_NOCONNECTION;
}
int status = readStatusCode(false);
if (status == 200) {
readLocation(callback, context);
}
close();
return status;
}
// Encodes and prints string using Percent-encoding specified
// in RFC 1738, Section 2.2
static int print_encoded_string(Print* print, const char* str) {
int bytes = 0;
for (int i = 0; str[i] != 0; i++) {
if (((str[i] >= 'A') && (str[i] <= 'Z')) ||
((str[i] >= 'a') && (str[i] <= 'z')) ||
((str[i] >= '0') && (str[i] <= '9')) ||
(str[i] == '-') || (str[i] == '_') ||
(str[i] == '.') || (str[i] == '~')) {
bytes += print->print(str[i]);
} else {
// Encode all other characters
bytes += print->print('%');
bytes += print->print(HEX(str[i] / 16));
bytes += print->print(HEX(str[i] % 16));
}
}
return bytes;
}
static int write_location_data(Print* print, const char* name,
double latitude, double longitude,
double elevation) {
int bytes = 0;
bytes += print->print("name=");
bytes += print_encoded_string(print, name);
bytes += print->print("&latitude=");
bytes += print->print(latitude, MAX_DOUBLE_DIGITS);
bytes += print->print("&longitude=");
bytes += print->print(longitude, MAX_DOUBLE_DIGITS);
bytes += print->print("&elevation=");
bytes += print->print(elevation);
return bytes;
}
static int write_location_data(Print* print, const char* name,
const char* latitude, const char* longitude,
const char* elevation) {
int bytes = 0;
bytes += print->print("name=");
bytes += print_encoded_string(print, name);
bytes += print->print("&latitude=");
bytes += print_encoded_string(print, latitude);
bytes += print->print("&longitude=");
bytes += print_encoded_string(print, longitude);
bytes += print->print("&elevation=");
bytes += print_encoded_string(print, elevation);
return bytes;
}
int M2XStreamClient::updateLocation(const char* feedId,
const char* name,
double latitude,
double longitude,
double elevation) {
if (_client->connect(_host, _port)) {
#ifdef DEBUG
printf("Connected to M2X server!\n");
#endif
int length = write_location_data(&_null_print, name, latitude, longitude,
elevation);
_client->print("PUT /v1/feeds/");
print_encoded_string(_client, feedId);
_client->println("/location HTTP/1.0");
writeHttpHeader(length);
write_location_data(_client, name, latitude, longitude, elevation);
} else {
#ifdef DEBUG
printf("ERROR: Cannot connect to M2X server!\n");
#endif
return E_NOCONNECTION;
}
return readStatusCode(true);
}
int M2XStreamClient::updateLocation(const char* feedId,
const char* name,
const char* latitude,
const char* longitude,
const char* elevation) {
if (_client->connect(_host, _port)) {
#ifdef DEBUG
printf("Connected to M2X server!\n");
#endif
int length = write_location_data(&_null_print, name, latitude, longitude,
elevation);
_client->print("PUT /v1/feeds/");
print_encoded_string(_client, feedId);
_client->println("/location HTTP/1.0");
writeHttpHeader(length);
write_location_data(_client, name, latitude, longitude, elevation);
} else {
#ifdef DEBUG
printf("ERROR: Cannot connect to M2X server!\n");
#endif
return E_NOCONNECTION;
}
return readStatusCode(true);
}
void M2XStreamClient::writeSendHeader(const char* feedId,
const char* streamName,
int contentLength) {
_client->print("PUT /v1/feeds/");
print_encoded_string(_client, feedId);
_client->print("/streams/");
print_encoded_string(_client, streamName);
_client->println(" HTTP/1.0");
writeHttpHeader(contentLength);
}
void M2XStreamClient::writeHttpHeader(int contentLength) {
_client->println(kUserAgentLine);
_client->print("X-M2X-KEY: ");
_client->println(_key);
_client->print("Host: ");
print_encoded_string(_client, _host);
if (_port != kDefaultM2XPort) {
_client->print(":");
// port is an integer, does not need encoding
_client->print(_port);
}
_client->println();
if (contentLength > 0) {
_client->println("Content-Type: application/x-www-form-urlencoded");
#ifdef DEBUG
printf("Content Length: %d\n", contentLength);
#endif
_client->print("Content-Length: ");
_client->println(contentLength);
}
_client->println();
}
int M2XStreamClient::waitForString(const char* str) {
int currentIndex = 0;
if (str[currentIndex] == '\0') return E_OK;
while (true) {
while (_client->available()) {
char c = _client->read();
#ifdef DEBUG
printf("%c", c);
#endif
if ((str[currentIndex] == '*') ||
(c == str[currentIndex])) {
currentIndex++;
if (str[currentIndex] == '\0') {
return E_OK;
}
} else {
// start from the beginning
currentIndex = 0;
}
}
if (!_client->connected()) {
#ifdef DEBUG
printf("ERROR: The client is disconnected from the server!\n");
#endif
close();
return E_DISCONNECTED;
}
delay(1000);
}
// never reached here
return E_NOTREACHABLE;
}
int M2XStreamClient::readStatusCode(bool closeClient) {
int responseCode = 0;
int ret = waitForString("HTTP/*.* ");
if (ret != E_OK) {
if (closeClient) close();
return ret;
}
// ret is not needed from here(since it must be E_OK), so we can use it
// as a regular variable now.
ret = 0;
while (true) {
while (_client->available()) {
char c = _client->read();
#ifdef DEBUG
printf("%c", c);
#endif
responseCode = responseCode * 10 + (c - '0');
ret++;
if (ret == 3) {
if (closeClient) close();
return responseCode;
}
}
if (!_client->connected()) {
#ifdef DEBUG
printf("ERROR: The client is disconnected from the server!\n");
#endif
if (closeClient) close();
return E_DISCONNECTED;
}
delay(1000);
}
// never reached here
return E_NOTREACHABLE;
}
int M2XStreamClient::readContentLength() {
int ret = waitForString("Content-Length: ");
if (ret != E_OK) {
return ret;
}
// From now on, ret is not needed, we can use it
// to keep the final result
ret = 0;
while (true) {
while (_client->available()) {
char c = _client->read();
#ifdef DEBUG
printf("%c", c);
#endif
if ((c == '\r') || (c == '\n')) {
return (ret == 0) ? (E_INVALID) : (ret);
} else {
ret = ret * 10 + (c - '0');
}
}
if (!_client->connected()) {
#ifdef DEBUG
printf("ERROR: The client is disconnected from the server!\n");
#endif
return E_DISCONNECTED;
}
delay(1000);
}
// never reached here
return E_NOTREACHABLE;
}
int M2XStreamClient::skipHttpHeader() {
return waitForString("\r\n\r\n");
}
void M2XStreamClient::close() {
// Eats up buffered data before closing
_client->flush();
_client->stop();
}
int M2XStreamClient::readStreamValue(stream_value_read_callback callback,
void* context) {
const int BUF_LEN = 32;
char buf[BUF_LEN];
int length = readContentLength();
if (length < 0) {
close();
return length;
}
int index = skipHttpHeader();
if (index != E_OK) {
close();
return index;
}
index = 0;
stream_parsing_context_state state;
state.state = state.index = 0;
state.callback = callback;
state.context = context;
jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
cbs.key_found = on_stream_key_found;
cbs.string_found = on_stream_string_found;
cbs.context.client_state = &state;
jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
jsonlite_parser_set_callback(p, &cbs);
jsonlite_result result = jsonlite_result_unknown;
while (index < length) {
int i = 0;
#ifdef DEBUG
printf("Received Data: ");
#endif
while ((i < BUF_LEN) && _client->available()) {
buf[i++] = _client->read();
#ifdef DEBUG
printf("%c", buf[i - 1]);
#endif
}
#ifdef DEBUG
printf("\n");
#endif
if ((!_client->connected()) &&
(!_client->available()) &&
((index + i) < length)) {
jsonlite_parser_release(p);
close();
return E_NOCONNECTION;
}
result = jsonlite_parser_tokenize(p, buf, i);
if ((result != jsonlite_result_ok) &&
(result != jsonlite_result_end_of_stream)) {
jsonlite_parser_release(p);
close();
return E_JSON_INVALID;
}
index += i;
}
jsonlite_parser_release(p);
close();
return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
}
int M2XStreamClient::readLocation(location_read_callback callback,
void* context) {
const int BUF_LEN = 40;
char buf[BUF_LEN];
int length = readContentLength();
if (length < 0) {
close();
return length;
}
int index = skipHttpHeader();
if (index != E_OK) {
close();
return index;
}
index = 0;
location_parsing_context_state state;
state.state = state.index = 0;
state.callback = callback;
state.context = context;
jsonlite_parser_callbacks cbs = jsonlite_default_callbacks;
cbs.key_found = on_location_key_found;
cbs.string_found = on_location_string_found;
cbs.context.client_state = &state;
jsonlite_parser p = jsonlite_parser_init(jsonlite_parser_estimate_size(5));
jsonlite_parser_set_callback(p, &cbs);
jsonlite_result result = jsonlite_result_unknown;
while (index < length) {
int i = 0;
#ifdef DEBUG
printf("Received Data: ");
#endif
while ((i < BUF_LEN) && _client->available()) {
buf[i++] = _client->read();
#ifdef DEBUG
printf("%c", buf[i - 1]);
#endif
}
#ifdef DEBUG
printf("\n");
#endif
if ((!_client->connected()) &&
(!_client->available()) &&
((index + i) < length)) {
jsonlite_parser_release(p);
close();
return E_NOCONNECTION;
}
result = jsonlite_parser_tokenize(p, buf, i);
if ((result != jsonlite_result_ok) &&
(result != jsonlite_result_end_of_stream)) {
jsonlite_parser_release(p);
close();
return E_JSON_INVALID;
}
index += i;
}
jsonlite_parser_release(p);
close();
return (result == jsonlite_result_ok) ? (E_OK) : (E_JSON_INVALID);
}
