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