plotly interface based on ardunio sample code

Dependents:   Plotly_HelloWorld

Library for plotting a simple x/y scatter chart on the plot.ly website.

See plotly_HelloWorld for sample usage.

Files at this revision

API Documentation at this revision

Comitter:
AndyA
Date:
Wed Jul 02 08:47:09 2014 +0000
Child:
1:d532e96fca12
Commit message:
First pass based on ardunio sample network code. Not currently working.

Changed in this revision

plotly.cpp Show annotated file Show diff for this revision Revisions of this file
plotly.h Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plotly.cpp	Wed Jul 02 08:47:09 2014 +0000
@@ -0,0 +1,372 @@
+#include "plotly.h"
+#include "mbed.h"
+
+#define plotlyURL "plot.ly"
+
+#define dataURL "arduino.plot.ly"
+
+
+//#define plotlyURL "192.168.1.87"
+//#define dataURL "192.168.1.87"
+
+plotly::plotly(char *username, char *api_key, char* stream_tokens[], char *filename, int nTraces)
+{
+    log_level = 0;  // 0 = Debugging, 1 = Informational, 2 = Status, 3 = Errors, 4 = Quiet (// Serial Off)
+    dry_run = false;
+    username_ = username;
+    api_key_ = api_key;
+    stream_tokens_ = stream_tokens;
+    filename_ = filename;
+    nTraces_ = nTraces;
+    maxpoints = 30;
+    fibonacci_ = 1;
+    world_readable = true;
+    convertTimestamp = true;
+    timezone = "America/Montreal";
+    fileopt = "overwrite";
+}
+
+
+plotly::~plotly() {
+closeStream();    
+    }
+
+bool plotly::init()
+{
+
+    //
+    //  Validate a stream with a REST post to plotly
+    //
+    if(dry_run && log_level < 3) {
+        fprintf(stderr,"... This is a dry run, we are not connecting to plotly's servers...\n");
+    } else if(log_level < 3) {
+        fprintf(stderr,"... Attempting to connect to plotly's REST servers\n");
+    }
+
+    while ( !socket.connect(plotlyURL, 80) ) {
+        if(log_level < 4) {
+            fprintf(stderr,"... Couldn\'t connect to plotly's REST servers... trying again!\n");
+        }
+        fibonacci_ += fibonacci_;
+        wait(10);
+    }
+    fibonacci_ = 1;
+    if(log_level < 3) {} fprintf(stderr,"... Connected to plotly's REST servers\n");
+    if(log_level < 3) {} fprintf(stderr,"... Sending HTTP Post to plotly\n");
+    print_("POST /clientresp HTTP/1.1\r\n");
+    print_("Host: 107.21.214.199\r\n");
+    print_("User-Agent: Arduino/0.5.1\r\n");
+
+    print_("Content-Length: ");
+    int contentLength = 126 + len_(username_) + len_(fileopt) + nTraces_*(87+len_(maxpoints)) + (nTraces_-1)*2 + len_(filename_);
+    if(world_readable) {
+        contentLength += 4;
+    } else {
+        contentLength += 5;
+    }
+    print_(contentLength);
+    // contentLength =
+    //   44  // first part of querystring below
+    // + len_(username)  // upper bound on username length
+    // + 5   // &key=
+    // + 10  // api_key length
+    // + 7  // &args=[...
+    // + nTraces*(87+len(maxpoints)) // len({\"y\": [], \"x\": [], \"type\": \"scatter\", \"stream\": {\"token\": \") + 10 + len(\", "maxpoints": )+len(maxpoints)+len(}})
+    // + (nTraces-1)*2 // ", " in between trace objects
+    // + 22  // ]&kwargs={\"fileopt\": \"
+    // + len_(fileopt)
+    // + 16  // \", \"filename\": \"
+    // + len_(filename)
+    // + 21 // ", "world_readable":
+    // + 4 if world_readable, 5 otherwise
+    // + 1   // closing }
+    //------
+    // 126 + len_(username) + len_(fileopt) + nTraces*(86+len(maxpoints)) + (nTraces-1)*2 + len_(filename)
+    //
+    // Terminate headers with new lines
+    print_("\r\n\r\n");
+
+    // Start printing querystring body
+    print_("version=2.2&origin=plot&platform=arduino&un=");
+    print_(username_);
+    print_("&key=");
+    print_(api_key_);
+    print_("&args=[");
+    // print a trace for each token supplied
+    for(int i=0; i<nTraces_; i++) {
+        print_("{\"y\": [], \"x\": [], \"type\": \"scatter\", \"stream\": {\"token\": \"");
+        print_(stream_tokens_[i]);
+        print_("\", \"maxpoints\": ");
+        print_(maxpoints);
+        print_("}}");
+        if(nTraces_ > 1 && i != nTraces_-1) {
+            print_(", ");
+        }
+    }
+    print_("]&kwargs={\"fileopt\": \"");
+    print_(fileopt);
+    print_("\", \"filename\": \"");
+    print_(filename_);
+    print_("\", \"world_readable\": ");
+    if(world_readable) {
+        print_("true");
+    } else {
+        print_("false");
+    }
+    print_("}");
+    // final newline to terminate the POST
+    print_("\r\n");
+
+    //
+    // Wait for a response
+    // Parse the response for the "All Streams Go!" and proceed to streaming
+    // if we find it
+    //
+    char allStreamsGo[] = "All Streams Go!";
+    char error[] = "\"error\": \"";
+    int asgCnt = 0; // asg stands for All Streams Go
+    char url[] = "\"url\": \"http://107.21.214.199/~";
+    char fid[4];
+    int fidCnt = 0;
+    int urlCnt = 0;
+    int usernameCnt = 0;
+    int urlLower = 0;
+    int urlUpper = 0;
+    bool proceed = false;
+    bool fidMatched = false;
+    char c;
+
+    if(log_level < 2) {
+        fprintf(stderr,"... Sent message, waiting for plotly's response...\n");
+    }
+
+    if(!dry_run) {
+        while(!proceed) {
+            uint32_t dataIn = socket.receive_all(rxBuffer,127);
+            if(dataIn > 0) {
+                rxBuffer[dataIn]=0;
+
+                if(log_level < 2) fprintf(stderr,rxBuffer);
+
+                for (int i = 0; i<dataIn; i++) {
+                    c = rxBuffer[i];
+                    //
+                    // Attempt to read the "All streams go" msg if it exists
+                    // by comparing characters as they roll in
+                    //
+
+                    if(asgCnt == len_(allStreamsGo) && !proceed) {
+                        proceed = true;
+                    } else if(allStreamsGo[asgCnt]==c) {
+                        asgCnt += 1;
+                    } else if(asgCnt > 0) {
+                        // reset counter
+                        asgCnt = 0;
+                    }
+
+                    //
+                    // Extract the last bit of the URL from the response
+                    // The url is in the form http://107.21.214.199/~USERNAME/FID
+                    // We'll character-count up through char url[] and through username_, then start
+                    // filling in characters into fid
+                    //
+
+                    if(log_level < 3) {
+                        if(url[urlCnt]==c && urlCnt < len_(url)) {
+                            urlCnt += 1;
+                        } else if(urlCnt > 0 && urlCnt < len_(url)) {
+                            // Reset counter
+                            urlCnt = 0;
+                        }
+                        if(urlCnt == len_(url) && fidCnt < 4 && !fidMatched) {
+                            // We've counted through the url, start counting through the username
+                            if(usernameCnt < len_(username_)+2) {
+                                usernameCnt += 1;
+                            } else {
+                                // the url ends with "
+                                if(c != '"') {
+                                    fid[fidCnt] = c;
+                                    fidCnt += 1;
+                                } else if(fidCnt>0) {
+                                    fidMatched = true;
+                                }
+
+                            }
+                        }
+                    }
+                }
+            }
+            wait(0.1);
+        }
+    }
+
+    if(!dry_run && !proceed && log_level < 4) {
+        fprintf(stderr,"... Error initializing stream, aborting. Try again or get in touch with Chris at chris@plot.ly\n");
+    }
+
+    if(!dry_run && proceed && log_level < 3) {
+        fprintf(stderr,"... A-ok from plotly, All Streams Go!\n");
+        if(fidMatched) {
+            fprintf(stderr,"... View your streaming plot here: https://plot.ly/~");
+            fprintf(stderr,username_);
+            fprintf(stderr,"/");
+            for(int i=0; i<fidCnt; i++) {
+                fprintf(stderr,"%d ",fid[i]);
+            }
+            fprintf(stderr,"\n");
+        }
+    }
+    return proceed;
+}
+
+void plotly::openStream()
+{
+    //
+    // Start request to stream servers
+    //
+    if(log_level < 3) {} fprintf(stderr,"... Connecting to plotly's streaming servers...\n");
+    char server[] = dataURL;
+    int port = 80;
+    while ( !socket.connect(server, port) ) {
+        if(log_level < 4) fprintf(stderr,"... Couldn\'t connect to servers... trying again!\n");
+        fibonacci_ += fibonacci_;
+        wait(10);
+    }
+    fibonacci_ = 1;
+    if(log_level < 3) {} fprintf(stderr,"... Connected to plotly's streaming servers\n... Initializing stream\n");
+
+    print_("POST / HTTP/1.1\r\n");
+    print_("Host: 127.0.0.1\r\n");
+    print_("User-Agent: Python\r\n");
+    print_("Transfer-Encoding: chunked\r\n");
+    print_("Connection: close\r\n");
+    if(convertTimestamp) {
+        print_("plotly-convertTimestamp: \"");
+        print_(timezone);
+        print_("\"");
+    }
+    print_("\r\n\r\n");
+
+    if(log_level < 3) {} fprintf(stderr,"... Done initializing, ready to stream!\n");
+}
+
+void plotly::closeStream()
+{
+    print_("0\r\n\r\n");
+    socket.close();
+}
+void plotly::reconnectStream()
+{
+    while(!socket.is_connected()) {
+        if(log_level<4) fprintf(stderr,"... Disconnected from streaming servers\n");
+        closeStream();
+        openStream();
+    }
+}
+void plotly::jsonStart(int i)
+{
+    // Print the length of the message in hex:
+    // 15 char for the json that wraps the data: {"x": , "y": }\n
+    // + 23 char for the token: , "token": "abcdefghij"
+    // = 38
+    printHex_(i+44);
+    print_("\r\n{\"x\": ");
+}
+void plotly::jsonMiddle()
+{
+    print_(", \"y\": ");
+}
+void plotly::jsonEnd(char *token)
+{
+    print_(", \"streamtoken\": \"");
+    print_(token);
+    print_("\"}\n\r\n");
+}
+
+int plotly::len_(int i)
+{
+    // int range: -32,768 to 32,767
+    if(i > 9999) return 5;
+    else if(i > 999) return 4;
+    else if(i > 99) return 3;
+    else if(i > 9) return 2;
+    else if(i > -1) return 1;
+    else if(i > -10) return 2;
+    else if(i > -100) return 3;
+    else if(i > -1000) return 4;
+    else if(i > -10000) return 5;
+    else return 6;
+}
+int plotly::len_(unsigned long i)
+{
+    // max length of unsigned long: 4294967295
+    if(i > 999999999) return 10;
+    else if(i > 99999999) return 9;
+    else if(i > 9999999) return 8;
+    else if(i > 999999) return 7;
+    else if(i > 99999) return 6;
+    else if(i > 9999) return 5;
+    else if(i > 999) return 4;
+    else if(i > 99) return 3;
+    else if(i > 9) return 2;
+    else return 1;
+}
+int plotly::len_(char *i)
+{
+    return strlen(i);
+}
+void plotly::plot(unsigned long x, int y, char *token)
+{
+    reconnectStream();
+    jsonStart(len_(x)+len_(y));
+    print_(x);
+    jsonMiddle();
+    print_(y);
+    jsonEnd(token);
+}
+
+void plotly::plot(unsigned long x, float y, char *token)
+{
+    reconnectStream();
+
+    char s_[15];
+    snprintf(s_,15,"%2.3lf",y);
+
+    jsonStart(len_(x)+len_(s_)-1);
+    print_(x);
+    jsonMiddle();
+    print_(y);
+    jsonEnd(token);
+}
+
+void plotly::print_(int d)
+{
+    uint32_t len = snprintf(txBuffer,128,"%d",d);
+    if(log_level < 2) fprintf(stderr,"%s",txBuffer);
+    if(!dry_run) socket.send_all(txBuffer,len); // skip the trailing 0
+}
+void plotly::print_(unsigned long d)
+{
+    uint32_t len = snprintf(txBuffer,128,"%lu",d);
+    if(log_level < 2) fprintf(stderr,"%s",txBuffer);
+    if(!dry_run) socket.send_all(txBuffer,len); // skip the trailing 0
+}
+void plotly::print_(float d)
+{
+    uint32_t len = snprintf(txBuffer,128,"%f",d);
+    if(log_level < 2) fprintf(stderr,"%s",txBuffer);
+    if(!dry_run) socket.send_all(txBuffer,len); // skip the trailing 0
+}
+void plotly::print_(char *d)
+{
+    uint32_t len = snprintf(txBuffer,128,"%s",d);
+    if(log_level < 2) fprintf(stderr,"%s",txBuffer);
+    if(!dry_run) socket.send_all(txBuffer,len); // skip the trailing 0
+}
+
+void plotly::printHex_(uint16_t d)
+{
+    uint32_t len = snprintf(txBuffer,128,"%X",d);
+    if(log_level < 2) fprintf(stderr,"%s",txBuffer);
+    if(!dry_run) socket.send_all(txBuffer,len); // skip the trailing 0
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/plotly.h	Wed Jul 02 08:47:09 2014 +0000
@@ -0,0 +1,85 @@
+#ifndef plotly_streaming_ethernet_h
+#define plotly_streaming_ethernet_h
+
+#include <EthernetInterface.h>
+#include <TCPSocketConnection.h>
+
+EthernetInterface eth;
+
+/*********************************
+in main.cpp
+#define num_traces 1
+// Sign up to plotly here: https://plot.ly
+// View your API key and stream tokens in your settings: https://plot.ly/settings
+char *streaming_tokens[num_traces] = {"leofdu7cun"};
+plotly graph = plotly("AndyA", "acchq52p7j", streaming_tokens, "test1", num_traces);
+
+void plotSetup() {
+
+//    eth.init();
+//    eth.connect();
+
+  // Initialize a streaming graph in your plotly account
+    pc.printf("plot init..\n");
+  graph.init();
+    pc.printf("Open stream..\n");
+  // Initialize plotly's streaming service
+  graph.openStream(); 
+}
+
+void plotGenerateData() {
+  static int counter = 0;
+  graph.plot(counter, counter*counter, streaming_tokens[0]);
+  counter++;
+}
+
+
+********************************************/
+class plotly
+{
+    public:
+        plotly(char *username, char *api_key, char* stream_tokens[], char *filename, int nTraces);
+        ~plotly();
+        bool init();
+        void openStream();
+        void closeStream();
+        void reconnectStream();
+        void jsonStart(int i);
+        void jsonMiddle();
+        void jsonEnd(char *token);
+
+        void plot(unsigned long x, int y, char *token);
+        void plot(unsigned long x, float y, char *token);
+
+        int log_level;
+        bool dry_run;
+        int maxpoints;
+        bool world_readable;
+        bool convertTimestamp;
+        char *timezone;
+        char *fileopt;
+
+    private:
+        void print_(int d);
+        void print_(unsigned long d);
+        void print_(float d);
+        void print_(char *d);
+        void printHex_(uint16_t d);
+
+        int len_(int i);
+        int len_(unsigned long i);
+        int len_(char *i);
+
+        char txBuffer[128];
+        char rxBuffer[128];
+        TCPSocketConnection socket;
+        
+        unsigned long fibonacci_;
+        char *username_;
+        char *api_key_;
+        char** stream_tokens_;
+        char *filename_;
+        int nTraces_;
+
+};
+#endif