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.
Dependencies: FXOS8700CQ MODSERIAL mbed
Revision 81:a5df87708b9a, committed 2016-09-02
- Comitter:
- sveljko
- Date:
- Fri Sep 02 17:44:55 2016 +0000
- Parent:
- 80:d635c0eddd6e
- Commit message:
- First version that works, forked from official AT&T IoT starter kit repository.
Changed in this revision
--- a/config_me.h Tue Aug 16 13:55:59 2016 +0000 +++ b/config_me.h Fri Sep 02 17:44:55 2016 +0000 @@ -23,23 +23,31 @@ #define BUF_SIZE_FOR_N_MAX_SOCKREAD (10) #define MAX_WNC_SOCKREAD_PAYLOAD (1500) -// This is the server's base URL name. Example "www.google.com" -// Note that when you Fork a FLOW, it will typically assign either -// "run-east.att.io" or "run-west.att.io", so be sure to check this. -static const char * MY_SERVER_URL = "run-west.att.io"; +// This is the server's base URL name. Pubnub SDKs refer to this as "the origin". +// In general, you should leave this to "pubsub.pubnub.com", but in some cases +// you may want to user a different origin. +static const char * MY_SERVER_URL = "pubsub.pubnub.com"; -// These are FLOW fields from the Endpoints tab: -#define FLOW_BASE_URL "/1e464b19cdcde/774c88d68202/86694923d5bf28a/in/flow" -#define FLOW_INPUT_NAME "/climate" + +/** Put your publish key here. If you don't have one, "demo" is the default + public key. But, it has limitations. + */ +#define PUBNUB_PUBLISH_KEY "demo" -// Unless you want to use a different protocol, this field should be left as is: -#define FLOW_URL_TYPE " HTTP/1.1\r\nHost: " +/** Put your subscribe key here. If you don't have one, "demo" is the default + public key. + */ +#define PUBNUB_SUBSCRIBE_KEY "demo" -// This identifier specifies with which FLOW device you are communicating. -// If you only have one devive there then you can just leave this as is. -// Once your FLOW device has been initialized (Virtual Device Initialize clicked), -// the Virtual Device will show up in M2X. This is its "DEVICE SERIAL" field -#define FLOW_DEVICE_NAME "vstarterkit001" +/** Define the Pubnub channel to use here. For the "demo/demo" keys, a well-known + channel is "hello_world", but you can use any you like, channels are dynamic + on Pubnub +*/ +#define PUBNUB_CHANNEL "hello_world" + +// This identifier specifies a "device name" to be sent in the JSON message. +// You can also use it as the UUID, if you wish. +#define THE_DEVICE_NAME "vstarterkit001" // This constant defines how often sensors are read and sent up to FLOW #define SENSOR_UPDATE_INTERVAL_MS 5000; //5 seconds
--- a/main.cpp Tue Aug 16 13:55:59 2016 +0000 +++ b/main.cpp Fri Sep 02 17:44:55 2016 +0000 @@ -23,6 +23,8 @@ #include "cell_modem.h" #include "hardware.h" +#include "pubnub.h" + I2C i2c(PTC11, PTC10); //SDA, SCL -- define the I2C pins being used MODSERIAL pc(USBTX, USBRX, 256, 256); // tx, rx with default tx, rx buffer sizes MODSERIAL mdm(PTD3, PTD2, 4096, 4096); @@ -71,42 +73,69 @@ PUTS("\r\n\r\nApp Firmware: Release 1.0 - built: "__DATE__" "__TIME__"\r\n\r\n"); } -void GenerateModemString(char * modem_string) + +static void GeneratePubnubJSON(char *s, unsigned n) { - switch(iSensorsToReport) - { - case TEMP_HUMIDITY_ONLY: - { - sprintf(modem_string, "GET %s%s?serial=%s&temp=%s&humidity=%s %s%s\r\n\r\n", FLOW_BASE_URL, FLOW_INPUT_NAME, FLOW_DEVICE_NAME, SENSOR_DATA.Temperature, SENSOR_DATA.Humidity, FLOW_URL_TYPE, MY_SERVER_URL); - break; - } - case TEMP_HUMIDITY_ACCELEROMETER: - { - sprintf(modem_string, "GET %s%s?serial=%s&temp=%s&humidity=%s&accelX=%s&accelY=%s&accelZ=%s %s%s\r\n\r\n", FLOW_BASE_URL, FLOW_INPUT_NAME, FLOW_DEVICE_NAME, SENSOR_DATA.Temperature, SENSOR_DATA.Humidity, SENSOR_DATA.AccelX,SENSOR_DATA.AccelY,SENSOR_DATA.AccelZ, FLOW_URL_TYPE, MY_SERVER_URL); - break; - } - case TEMP_HUMIDITY_ACCELEROMETER_GPS: - { - sprintf(modem_string, "GET %s%s?serial=%s&temp=%s&humidity=%s&accelX=%s&accelY=%s&accelZ=%s&gps_satellites=%s&latitude=%s&longitude=%s&altitude=%s&speed=%s&course=%s %s%s\r\n\r\n", FLOW_BASE_URL, FLOW_INPUT_NAME, FLOW_DEVICE_NAME, SENSOR_DATA.Temperature, SENSOR_DATA.Humidity, SENSOR_DATA.AccelX,SENSOR_DATA.AccelY,SENSOR_DATA.AccelZ,SENSOR_DATA.GPS_Satellites,SENSOR_DATA.GPS_Latitude,SENSOR_DATA.GPS_Longitude,SENSOR_DATA.GPS_Altitude,SENSOR_DATA.GPS_Speed,SENSOR_DATA.GPS_Course, FLOW_URL_TYPE, MY_SERVER_URL); - break; - } - case TEMP_HUMIDITY_ACCELEROMETER_PMODSENSORS: - { - sprintf(modem_string, "GET %s%s?serial=%s&temp=%s&humidity=%s&accelX=%s&accelY=%s&accelZ=%s&proximity=%s&light_uv=%s&light_vis=%s&light_ir=%s %s%s\r\n\r\n", FLOW_BASE_URL, FLOW_INPUT_NAME, FLOW_DEVICE_NAME, SENSOR_DATA.Temperature, SENSOR_DATA.Humidity, SENSOR_DATA.AccelX,SENSOR_DATA.AccelY,SENSOR_DATA.AccelZ, SENSOR_DATA.Proximity, SENSOR_DATA.UVindex, SENSOR_DATA.AmbientLightVis, SENSOR_DATA.AmbientLightIr, FLOW_URL_TYPE, MY_SERVER_URL); - break; - } - case TEMP_HUMIDITY_ACCELEROMETER_PMODSENSORS_VIRTUALSENSORS: - { - sprintf(modem_string, "GET %s%s?serial=%s&temp=%s&humidity=%s&accelX=%s&accelY=%s&accelZ=%s&proximity=%s&light_uv=%s&light_vis=%s&light_ir=%s&virt_sens1=%s&virt_sens2=%s&virt_sens3=%s&virt_sens4=%s&virt_sens5=%s&virt_sens6=%s&virt_sens7=%s&virt_sens8=%s %s%s\r\n\r\n", FLOW_BASE_URL, FLOW_INPUT_NAME, FLOW_DEVICE_NAME, SENSOR_DATA.Temperature, SENSOR_DATA.Humidity, SENSOR_DATA.AccelX,SENSOR_DATA.AccelY,SENSOR_DATA.AccelZ, SENSOR_DATA.Proximity, SENSOR_DATA.UVindex, SENSOR_DATA.AmbientLightVis, SENSOR_DATA.AmbientLightIr, SENSOR_DATA.Virtual_Sensor1, SENSOR_DATA.Virtual_Sensor2, SENSOR_DATA.Virtual_Sensor3, SENSOR_DATA.Virtual_Sensor4, SENSOR_DATA.Virtual_Sensor5, SENSOR_DATA.Virtual_Sensor6, SENSOR_DATA.Virtual_Sensor7, SENSOR_DATA.Virtual_Sensor8, FLOW_URL_TYPE, MY_SERVER_URL); - break; - } - default: - { - sprintf(modem_string, "Invalid sensor selected\r\n\r\n"); - break; - } - } //switch(iSensorsToReport) -} //GenerateModemString + switch (iSensorsToReport) { + case TEMP_HUMIDITY_ONLY: + snprintf(s, n, "{\"serial\":\"%s\",\"temp\":%s,\"humidity\":%s}", THE_DEVICE_NAME, SENSOR_DATA.Temperature, SENSOR_DATA.Humidity); + break; + case TEMP_HUMIDITY_ACCELEROMETER: + snprintf(s, n, "{\"serial\":\"%s\",\"temp\":%s,\"humidity\":%s,\"accelX\":%s,\"accelY\":%s,\"accelZ\":%s}", THE_DEVICE_NAME, SENSOR_DATA.Temperature, SENSOR_DATA.Humidity, SENSOR_DATA.AccelX, SENSOR_DATA.AccelY, SENSOR_DATA.AccelZ); + break; + case TEMP_HUMIDITY_ACCELEROMETER_PMODSENSORS: + snprintf(s, n, "{\"serial\":\"%s\",\"temp\":%s,\"humidity\":%s,\"accelX\":%s,\"accelY\":%s,\"accelZ\":%s,\"proximity\":%s,\"light_uv\":%s,\"light_vis\":%s,\"light_ir\":%s}", THE_DEVICE_NAME, SENSOR_DATA.Temperature, SENSOR_DATA.Humidity, SENSOR_DATA.AccelX,SENSOR_DATA.AccelY,SENSOR_DATA.AccelZ, SENSOR_DATA.Proximity, SENSOR_DATA.UVindex, SENSOR_DATA.AmbientLightVis, SENSOR_DATA.AmbientLightIr); + break; + case TEMP_HUMIDITY_ACCELEROMETER_PMODSENSORS_VIRTUALSENSORS: + snprintf(s, n, "{\"serial\":\"%s\",\"temp\":%s,\"humidity\":%s,\"accelX\":%s,\"accelY\":%s,\"accelZ\":%s,\"proximity\":%s,\"light_uv\":%s,\"light_vis\":%s,\"light_ir\":%s,\"virt_sens1\":%s,\"virt_sens2\":%s,\"virt_sens3\":%s,\"virt_sens4\":%s,\"virt_sens5\":%s,\"virt_sens6\":%s,\"virt_sens7\":%s,\"virt_sens8\":%s}", + THE_DEVICE_NAME, + SENSOR_DATA.Temperature, SENSOR_DATA.Humidity, + SENSOR_DATA.AccelX,SENSOR_DATA.AccelY,SENSOR_DATA.AccelZ, + SENSOR_DATA.Proximity, SENSOR_DATA.UVindex, SENSOR_DATA.AmbientLightVis, SENSOR_DATA.AmbientLightIr, + SENSOR_DATA.Virtual_Sensor1, SENSOR_DATA.Virtual_Sensor2, + SENSOR_DATA.Virtual_Sensor3, SENSOR_DATA.Virtual_Sensor4, + SENSOR_DATA.Virtual_Sensor5, SENSOR_DATA.Virtual_Sensor6, + SENSOR_DATA.Virtual_Sensor7, SENSOR_DATA.Virtual_Sensor8); + break; + default: + snprintf(s, n, "\"Invalid sensors selected: %d\"", iSensorsToReport); + break; + } +} + + +static void print_pubnub_result(pubnub_ctx::result r) +{ + switch (r) { + case pubnub_ctx::format_error: + PRINTF(RED "Pubnub response format error" DEF "\r\n"); + break; + case pubnub_ctx::response_too_short: + PRINTF(RED "Pubnub response too short" DEF "\r\n"); + break; + case pubnub_ctx::missing_open_bracket: + PRINTF(RED "Pubnub response missing open bracket `[`" DEF "\r\n"); + break; + case pubnub_ctx::missing_close_bracket: + PRINTF(RED "Pubnub response missing close bracket `]`" DEF "\r\n"); + break; + case pubnub_ctx::missing_time_token: + PRINTF(RED "Pubnub subscribe response missing time token" DEF "\r\n"); + break; + case pubnub_ctx::bad_time_token: + PRINTF(RED "Pubnub subscribe response bad time token" DEF "\r\n"); + break; + case pubnub_ctx::publish_failed: + PRINTF(RED "Pubnub publish failed" DEF "\r\n"); + break; + case pubnub_ctx::ok: + PRINTF(GRN "Pubnub transaction success" DEF "\r\n"); + break; + default: + PRINTF(RED "Unknown Pubnub erorr %d" DEF "\r\n", static_cast<int>(r)); + break; + } +} //Periodic timer @@ -141,9 +170,9 @@ //******************************************************************************************************************************************** //* Process the JSON response. In this example we are only extracting a LED color. //******************************************************************************************************************************************** -bool parse_JSON(char* json_string) +bool parse_JSON(char const* json_string) { - char* beginquote; + char const* beginquote; char token[] = "\"LED\":\""; beginquote = strstr(json_string, token ); if ((beginquote != 0)) @@ -205,7 +234,9 @@ } } //parse_JSON -int main() { + +int main() +{ static unsigned ledOnce = 0; //delay so that the debug terminal can open after power-on reset: wait (5.0); @@ -233,26 +264,38 @@ iTimer1Interval_ms = SENSOR_UPDATE_INTERVAL_MS; OneMsTicker.attach(OneMsFunction, 0.001f) ; + // Create the Pubnub context and message vector + pubnub_ctx pb(PUBNUB_PUBLISH_KEY, PUBNUB_SUBSCRIBE_KEY); + std::vector<std::string> messages; + + // If you wish, you can set the UUID to use - which will make that UUID + // visible for Presence related Pubnub features + // pb.set_uuid(THE_DEVICE_NAME); + // Send and receive data perpetually while(1) { - #ifdef USE_VIRTUAL_SENSORS +#ifdef USE_VIRTUAL_SENSORS ProcessUsbInterface(); - #endif +#endif if (bTimerExpiredFlag) { bTimerExpiredFlag = false; read_sensors(); //read available external sensors from a PMOD and the on-board motion sensor - char modem_string[512]; - GenerateModemString(&modem_string[0]); - char myJsonResponse[512]; - if (cell_modem_Sendreceive(&modem_string[0], &myJsonResponse[0])) - { - if (!ledOnce) - { - ledOnce = 1; - SetLedColor(0x2); //Green - } - parse_JSON(&myJsonResponse[0]); + + char json_string[512]; + GeneratePubnubJSON(json_string, sizeof json_string); + print_pubnub_result(pb.publish(PUBNUB_CHANNEL, json_string)); + + messages.clear(); + print_pubnub_result(pb.subscribe(PUBNUB_CHANNEL, messages)); + if (!ledOnce && !messages.empty()) { + ledOnce = 1; + SetLedColor(0x2); + } + for (std::vector<std::string>::iterator it = messages.begin(); it != messages.end(); ++it) { + char const *s = it->c_str(); + PRINTF(BLU "Pubnub message: %s" DEF "\n", s); + parse_JSON(s); } } //bTimerExpiredFlag } //forever loop
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pubnub.cpp Fri Sep 02 17:44:55 2016 +0000 @@ -0,0 +1,183 @@ +#include "pubnub.h" + +#include "mbed.h" +#include <string> +#include "wnc_control.h" + +#include <stdio.h> + + +pubnub_ctx::pubnub_ctx(char const* pub_key, char const *key_sub) + : d_pub_key(pub_key) + , d_key_sub(key_sub) + , d_token("0") +{ +} + + +pubnub_ctx::~pubnub_ctx() +{ +} + + +static void append_epilogue(std::string &s, std::string const& uuid, std::string const& auth) +{ + s += "?pnsdk=AvnetATTmbed"; + if (!uuid.empty()) { + s += "&uuid="; + s += uuid; + } + if (!auth.empty()) { + s += "&auth="; + s += auth; + } + s += " HTTP/1.1\r\nHost: pubsub.pubnub.com\r\n\r\n"; +} + + +pubnub_ctx::result pubnub_ctx::publish(char const* channel, char const* message) +{ + char const *pmessage = message; + std::string s("GET /publish/"); + s += d_pub_key; s += "/"; + s += d_key_sub; s += "/0/"; + s += channel; s += "/0/"; + + while (pmessage[0]) { + /* RFC 3986 Unreserved characters plus few + * safe reserved ones. */ + size_t okspan = strspn(pmessage, "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.~" ",=:;@[]"); + if (okspan > 0) { + s.append(pmessage, okspan); + pmessage += okspan; + } + if (pmessage[0]) { + /* %-encode a non-ok character. */ + s.append(1, '%'); + s.append(1, "0123456789ABCDEF"[pmessage[0] / 16]); + s.append(1, "0123456789ABCDEF"[pmessage[0] % 16]); + ++pmessage; + } + } + + append_epilogue(s, d_uuid, d_auth); + + sockwrite_mdm(s.c_str()); + string response; + unsigned read = sockread_mdm(&response, 1024, 50); + + if (read < 5) { + extern Serial pc; + pc.printf("read = %d\r\n", read); + return response_too_short; + } + char const* start = strchr(response.data(), '['); + if (NULL == start) { + return missing_open_bracket; + } + if (NULL == strrchr(response.data() + response.size() - 1, ']')) { + return missing_close_bracket; + } + + if (1 != strtol(start + 1, NULL, 10)) { + return publish_failed; + } + + return ok; +} + +pubnub_ctx::result pubnub_ctx::subscribe(char const* channel, std::vector<std::string>& messages) +{ + std::string s("GET /subscribe/"); + s += d_key_sub; s += "/"; + s += channel; s += "/0/"; + s += d_token;; + append_epilogue(s, d_uuid, d_auth); + + sockwrite_mdm(s.c_str()); + string response; + unsigned read = sockread_mdm(&response, 1024, 50); + + if (read < 10) { + return response_too_short; + } + char const* start = strchr(response.c_str(), '['); + if (NULL == start) { + return missing_open_bracket; + } + if (start[1] != '[') { + return missing_open_bracket; + } + enum { + idle, + in_string, + escape, + done + } state = idle; + start += 2; + char const* end; + int bracket_level = 1; + for (end = start; (*end != '\0') && (state != done); ++end) { + char c = *end; + switch (state) { + case idle: + switch (c) { + case '"': + state = in_string; + break; + case ',': + if (bracket_level == 1) { + messages.push_back(std::string(start, end-start)); + start = end + 1; + } + break; + case '{': + case '[': + ++bracket_level; + break; + case '}': + --bracket_level; + break; + case ']': + if (--bracket_level <= 0) { + if (end-start-1 > 0) { + messages.push_back(std::string(start, end-start)); + } + state = done; + } + break; + } + break; + case in_string: + switch (c) { + case '"': + state = idle; + break; + case '\\': + state = escape; + break; + } + break; + case escape: + state = in_string; + break; + default: + state = done; + break; + } + } + if (state != done) { + return format_error; + } + if ((end[0] != ',') && (end[1] != '"')) { + return missing_time_token; + } + start = end + 2; + end = strchr(start, '"'); + if (NULL == end) { + return bad_time_token; + } + d_token.assign(start, end-start); + + return ok; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pubnub.h Fri Sep 02 17:44:55 2016 +0000 @@ -0,0 +1,73 @@ +#if !defined INC_PUBNUB +#define INC_PUBNUB + +#include <string> +#include <vector> + + +/** A pubnub context. +*/ +class pubnub_ctx { +public: + /** Give publish key as @p pub_key and subscribe key as @p key_sub + to initialize the context. + */ + pubnub_ctx(char const* pub_key, char const* key_sub); + + ~pubnub_ctx(); + + /** Result of a Pubnub transaction */ + enum result { + format_error, + response_too_short, + missing_open_bracket, + missing_close_bracket, + missing_time_token, + bad_time_token, + publish_failed, + ok + }; + + /** Publish a JSON @p message to a pubnub @p channel (or channels, + separated by commas). + This is a synchronous call, so it will take a while to return + (depending on your Internet connection, could be 100s of ms). + */ + result publish(char const* channel, char const* message); + + /** Subscribe on a @p channel (or channels, separated by commas). + Messages received (if any) are pushed back to the @p messages + vector. + + This is a synchronous call, but it will not wait the full Pubnub ~5 min + for a message to appear on the channel. It will be much less, ~1 s. + + @return vector of messages - empty if there are no messages (like on + the very first subscribe) + */ + result subscribe(char const* channel, std::vector<std::string>& messages); + + std::string uuid() const { return d_uuid; } + void set_uuid(char const *s) { d_uuid = s; } + + std::string auth() const { return d_auth; } + void set_auth(char const *s) { d_auth = s; } + +private: + /// The publish key to use + std::string d_pub_key; + + /// The subscribe key to use + std::string d_key_sub; + + /// the last timetoken + std::string d_token; + + /// The UUID to use (empty - do not use) + std::string d_uuid; + + /// The auth key to use (empty - do not use) + std::string d_auth; +}; + +#endif // !defined INC_PUBNUB