Skip to content

Commit

Permalink
Add update location function
Browse files Browse the repository at this point in the history
  • Loading branch information
xxuejie committed Oct 16, 2013
1 parent 0acb073 commit 83a0cad
Show file tree
Hide file tree
Showing 3 changed files with 229 additions and 47 deletions.
179 changes: 132 additions & 47 deletions M2XStreamClient/M2XStreamClient.cpp
Expand Up @@ -2,10 +2,10 @@

#include "utility/jsonlite_parser.h"

#define HEX(t_) (((t_) > 9) ? ((t_) - 10 + 'A') : ((t_) + '0'))
#define HEX(t_) ((char) (((t_) > 9) ? ((t_) - 10 + 'A') : ((t_) + '0')))

const char* M2XStreamClient::kDefaultM2XHost = "api-m2x.att.com";
const char* kUserAgentLine = "User-Agent: Arduino Client/0.1";
const char* kUserAgentLine = "User-Agent: M2X Arduino Client/0.1";

#define WAITING_AT 0x1
#define GOT_AT 0x2
Expand All @@ -22,6 +22,8 @@ const char* kUserAgentLine = "User-Agent: Arduino Client/0.1";
#define AT_BUF_LEN 20
#define VALUE_BUF_LEN 20 // enlarge this if you need more chars

#define MAX_DOUBLE_DIGITS 7

typedef struct {
uint8_t state;
char at_str[AT_BUF_LEN + 1];
Expand All @@ -32,6 +34,8 @@ typedef struct {
void* context;
} json_parsing_context_state;

static int print_encoded_string(Print* print, const char* str);

M2XStreamClient::M2XStreamClient(Client* client,
const char* key,
const char* host,
Expand All @@ -54,7 +58,7 @@ int M2XStreamClient::send(const char* feedId,
_null_print.print(value) + 6);

_client->print("value=");
// value is a double, does not need encoding, either
// value is a double, does not need encoding
_client->print(value);
} else {
#ifdef DEBUG
Expand All @@ -78,7 +82,7 @@ int M2XStreamClient::send(const char* feedId,
_null_print.print(value) + 6);

_client->print("value=");
// value is a double, does not need encoding, either
// value is a long, does not need encoding
_client->print(value);
} else {
#ifdef DEBUG
Expand All @@ -102,7 +106,7 @@ int M2XStreamClient::send(const char* feedId,
_null_print.print(value) + 6);

_client->print("value=");
// value is a double, does not need encoding, either
// value is an int, does not need encoding
_client->print(value);
} else {
#ifdef DEBUG
Expand All @@ -126,8 +130,7 @@ int M2XStreamClient::send(const char* feedId,
_null_print.print(value) + 6);

_client->print("value=");
// value is a double, does not need encoding, either
printEncodedString(value);
print_encoded_string(_client, value);
} else {
#ifdef DEBUG
Serial.println("ERROR: Cannot connect to M2X server!");
Expand All @@ -145,23 +148,12 @@ int M2XStreamClient::receive(const char* feedId, const char* streamName,
Serial.println("Connected to M2X server!");
#endif
_client->print("GET /v1/feeds/");
printEncodedString(feedId);
print_encoded_string(_client, feedId);
_client->print("/streams/");
printEncodedString(streamName);
print_encoded_string(_client, streamName);
_client->println("/values HTTP/1.0");

_client->println(kUserAgentLine);
_client->print("X-M2X-KEY: ");
_client->println(_key);
_client->print("Host: ");
printEncodedString(_host);
if (_port != kDefaultM2XPort) {
_client->print(":");
// port is an integer, does not need encoding
_client->print(_port);
}
_client->println();
_client->println();
writeHttpHeader(-1);
} else {
#ifdef DEBUG
Serial.println("ERROR: Cannot connect to M2X server!");
Expand All @@ -177,33 +169,144 @@ int M2XStreamClient::receive(const char* feedId, const char* streamName,
return status;
}

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
Serial.println("Connected to M2X server!");
#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
Serial.println("ERROR: Cannot connect to M2X server!");
#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
Serial.println("Connected to M2X server!");
#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
Serial.println("ERROR: Cannot connect to M2X server!");
#endif
return E_NOCONNECTION;
}
return readStatusCode(true);
}

void M2XStreamClient::writeSendHeader(const char* feedId,
const char* streamName,
int contentLength) {
_client->print("PUT /v1/feeds/");
printEncodedString(feedId);
print_encoded_string(_client, feedId);
_client->print("/streams/");
printEncodedString(streamName);
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: ");
printEncodedString(_host);
print_encoded_string(_client, _host);
if (_port != kDefaultM2XPort) {
_client->print(":");
// port is an integer, does not need encoding
_client->print(_port);
}
_client->println();
_client->println("Content-Type: application/x-www-form-urlencoded");

if (contentLength > 0) {
_client->println("Content-Type: application/x-www-form-urlencoded");
#ifdef DEBUG
Serial.print("Content Length: ");
Serial.println(contentLength);
Serial.print("Content Length: ");
Serial.println(contentLength);
#endif
_client->print("Content-Length: ");
_client->println(contentLength);
_client->print("Content-Length: ");
_client->println(contentLength);
}
_client->println();
}

Expand Down Expand Up @@ -330,24 +433,6 @@ void M2XStreamClient::close() {
_client->stop();
}


void M2XStreamClient::printEncodedString(const char* str) {
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] == '~')) {
_client->print(str[i]);
} else {
// Encode all other characters
_client->print('%');
_client->print(HEX(str[i] / 16));
_client->print(HEX(str[i] % 16));
}
}
}

static void on_key_found(jsonlite_callback_context* context,
jsonlite_token* token)
{
Expand Down
22 changes: 22 additions & 0 deletions M2XStreamClient/M2XStreamClient.h
Expand Up @@ -23,12 +23,33 @@ class M2XStreamClient {
const char* key,
const char* host = kDefaultM2XHost,
int port = kDefaultM2XPort);

// Update data stream
int send(const char* feedId, const char* streamName, double value);
int send(const char* feedId, const char* streamName, long value);
int send(const char* feedId, const char* streamName, int value);
int send(const char* feedId, const char* streamName, const char* value);
int receive(const char* feedId, const char* streamName,
stream_value_read_callback callback, void* context);

// Update datasource location
// NOTE: On an Arduino Uno and other ATMEGA based boards, double has
// 4-byte (32 bits) precision, which is the same as float. So there's
// no natural double-precision floating number on these boards. With
// a float value, we have a precision of roughly 7 digits, that means
// either 5 or 6 digits after the floating point. According to wikipedia,
// a difference of 0.00001 will give us ~1.1132m distance. If this
// precision is good for you, you can use the double-version we provided
// here. Otherwise, you may need to use the string-version and do the
// actual conversion by yourselves.
// However, with an Arduino Due board, double has 8-bytes (64 bits)
// precision, which means you are free to use the double-version only
// without any precision problems.
int updateLocation(const char* feedId, const char* name,
double latitude, double longitude, double elevation);
int updateLocation(const char* feedId, const char* name,
const char* latitude, const char* longitude,
const char* elevation);
private:
Client* _client;
const char* _key;
Expand All @@ -39,6 +60,7 @@ class M2XStreamClient {
void writeSendHeader(const char* feedId,
const char* streamName,
int contentLength);
void writeHttpHeader(int contentLength);
int readContentLength();
int skipHttpHeader();
int readStatusCode(bool closeClient);
Expand Down
@@ -0,0 +1,75 @@
#include <SPI.h>
#include <WiFi.h>

#include "M2XStreamClient.h"

char ssid[] = "<ssid>"; // your network SSID (name)
char pass[] = "<WPA password>"; // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0; // your network key Index number (needed only for WEP)

int status = WL_IDLE_STATUS;
char server[] = "api-m2x.att.com"; // M2X API server
int port = 80;

char feedId[] = "<feed id>"; // Feed you want to post to
char m2xKey[] = "<M2X access key>"; // Your M2X access key

char name[] = "<location name>"; // Name of current location of datasource
double latitude = -37.97884;
double longitude = -57.54787; // You can also read those values from a GPS
double elevation = 15;

WiFiClient client;
M2XStreamClient m2xClient(&client, m2xKey, server, port);

void setup() {
Serial.begin(9600);
while (!Serial) {
; // wait for serial port to connect. Needed for Leonardo only
}

if (WiFi.status() == WL_NO_SHIELD) {
Serial.println("WiFi shield not present");
while(true);
}

while ( status != WL_CONNECTED) {
Serial.print("Attempting to connect to SSID: ");
Serial.println(ssid);
// Connect to WPA/WPA2 network. Change this line if using open or WEP network:
status = WiFi.begin(ssid, pass);

// wait 10 seconds for connection:
delay(10000);
}
Serial.println("Connected to wifi");
printWifiStatus();
}

void loop() {
int response = m2xClient.updateLocation(feedId, name, latitude, longitude,
elevation);
Serial.print("M2x client response code: ");
Serial.println(response);

if (response == -1) while(1) ;

delay(5000);
}

void printWifiStatus() {
// print the SSID of the network you're attached to:
Serial.print("SSID: ");
Serial.println(WiFi.SSID());

// print your WiFi shield's IP address:
IPAddress ip = WiFi.localIP();
Serial.print("IP Address: ");
Serial.println(ip);

// print the received signal strength:
long rssi = WiFi.RSSI();
Serial.print("signal strength (RSSI):");
Serial.print(rssi);
Serial.println(" dBm");
}

0 comments on commit 83a0cad

Please sign in to comment.