plotly interface based on ardunio sample code
Library for plotting a simple x/y scatter chart on the plot.ly website.
See plotly_HelloWorld for sample usage.
Revision 8:d4f705ba2ea5, committed 2014-07-29
- Comitter:
- AndyA
- Date:
- Tue Jul 29 13:43:31 2014 +0000
- Parent:
- 7:9409a72ab6c0
- Commit message:
- Documentation tidy up and fix a stupid error in the merge between the documented and the multi-line branches.
Changed in this revision
diff -r 9409a72ab6c0 -r d4f705ba2ea5 plotly.cpp --- a/plotly.cpp Tue Jul 29 13:30:31 2014 +0000 +++ b/plotly.cpp Tue Jul 29 13:43:31 2014 +0000 @@ -4,13 +4,13 @@ #define plotlyURL "plot.ly" #define dataURL "arduino.plot.ly" -plotly::plotly(const char *username, const char *api_key, const char** stream_tokens, const char *filename, int nTraces) +plotly::plotly(const char *username, const char *api_key, const char* stream_tokens[], const char *filename, int nTraces) { log_level = 3; // 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; + stream_tokens_ = stream_tokens; filename_ = filename; maxpoints = 30; world_readable = true; @@ -39,14 +39,6 @@ bool plotly::init() { - - fprintf(stderr,"%d tokens\n",nTraces_); - wait(1); - for (int i = 0; i<nTraces_; i++) { - fprintf(stderr,"%s\n",*(stream_tokens_+i)); - wait(1); - } - // // Create plot with a REST post to plotly // See the clientresp section of https://plot.ly/rest/ for details
diff -r 9409a72ab6c0 -r d4f705ba2ea5 plotly.cpp.orig --- a/plotly.cpp.orig Tue Jul 29 13:30:31 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,378 +0,0 @@ -#include "plotly.h" -#include "mbed.h" - -#define plotlyURL "plot.ly" -#define dataURL "arduino.plot.ly" - -plotly::plotly(char *username, char *api_key, char* stream_token, char *filename) -{ - log_level = 3; // 0 = Debugging, 1 = Informational, 2 = Status, 3 = Errors, 4 = Quiet (// Serial Off) - dry_run = false; - username_ = username; - api_key_ = api_key; - stream_token_ = stream_token; - filename_ = filename; - maxpoints = 30; - world_readable = true; - convertTimestamp = false; - timezone = "America/Montreal"; - fileopt = "overwrite"; - - socket = NULL; - - initalised = false; -} - - -plotly::~plotly() -{ - closeStream(); - -} - - -bool plotly::init() -{ - - // - // Create plot with a REST post to plotly - // See the clientresp section of https://plot.ly/rest/ for details - // - 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"); - } - - if (!dry_run) { - int pause = 1; - socket = new TCPSocketConnection(); - while (socket->connect(plotlyURL, 80) < 0) { - if (pause > 30) { - fprintf(stderr,"Failed to connect. :-(\n"); - delete socket; - return false; - } - fprintf(stderr,"... Couldn\'t connect to plotly's REST servers... trying again!\n"); - wait(pause); - pause *= 2; - } - } - - 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_("plotly-streamtoken: "); - print_(stream_token_); - printNetTerminator_(); - - print_("Content-Length: "); - - // calculate the length of the packet payload and store it in the big buffer. - int lineLen = snprintf(buffer,k_bufferSize,"version=2.3&origin=plot&platform=arduino&un=%s&key=%s&args=[",username_,api_key_); - lineLen += snprintf((buffer+lineLen),k_bufferSize-lineLen,"{\"y\": [], \"x\": [], \"type\": \"scatter\", \"stream\": {\"token\": \"%s\", \"maxpoints\": %d}}",stream_token_,maxpoints); - lineLen += snprintf((buffer+lineLen),k_bufferSize-lineLen,"]&kwargs={\"fileopt\": \"%s\", \"filename\": \"%s\", \"world_readable\": %s}",fileopt,filename_,world_readable?"true":"false"); - - print_(lineLen); - - printNetTerminator_(); - printNetTerminator_(); - - sendFormatedText(buffer,lineLen); - - printNetTerminator_(); - - // - // 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!"; - 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; - 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) { - int32_t dataIn = socket->receive(buffer,k_bufferSize -1); - if (dataIn < 0) { - if(log_level < 3) fprintf(stderr,"error reading network socket\n"); - break; - } - if(dataIn > 0) { - buffer[dataIn]=0; - - if(log_level < 2) fprintf(stderr,buffer); - - for (int i = 0; i<dataIn; i++) { - c = buffer[i]; - // - // Attempt to read the "All streams go" msg if it exists - // by comparing characters as they roll in - // - - if(asgCnt == strlen(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 < strlen(url)) { - urlCnt += 1; - } else if(urlCnt > 0 && urlCnt < strlen(url)) { - // Reset counter - urlCnt = 0; - } - if(urlCnt == strlen(url) && fidCnt < 4 && !fidMatched) { - // We've counted through the url, start counting through the username - if(usernameCnt < strlen(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,"%c",fid[i]); - } - fprintf(stderr,"\n"); - } - } - - if (proceed || dry_run) { - initalised = true; - } - if (socket) { - delete socket; - socket=NULL; - } - return initalised; -} - -bool plotly::openStream() -{ - - if (!initalised) - return false; - - // - // Start request to stream servers - // - - if (socket) { - delete socket; - socket = NULL; - } - - - if(log_level < 3) fprintf(stderr,"... Connecting to plotly's streaming servers...\n"); - - - if (!dry_run) { - int pause = 1; - socket = new TCPSocketConnection(); - while (socket->connect(dataURL, 80) < 0) { - if (pause > 30) { - fprintf(stderr,"Failed to connect. :-(\n"); - delete socket; - return false; - } - fprintf(stderr,"... Couldn\'t connect to plotly's REST servers... trying again!\n"); - wait(pause); - pause *= 2; - } - } - - - 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: arduino.plot.ly\r\n"); - print_("User-Agent: Python\r\n"); - print_("Transfer-Encoding: chunked\r\n"); - print_("Connection: close\r\n"); - print_("plotly-streamtoken: "); - print_(stream_token_); - printNetTerminator_(); - if(convertTimestamp) { - print_("plotly-convertTimestamp: \""); - print_(timezone); - print_("\""); - printNetTerminator_(); - } - printNetTerminator_(); - - if(log_level < 3) fprintf(stderr,"... Done initializing, ready to stream!\n"); - return true; -} - -void plotly::closeStream() -{ - if (socket) { - if (socket->is_connected()) { - print_("0\r\n\r\n"); - socket->close(); - } - delete socket; - socket=NULL; - } -} - -void plotly::reconnectStream() -{ - while(!dry_run && (!socket || !socket->is_connected())) { - if(log_level<4) fprintf(stderr,"... Disconnected from streaming servers\n"); - closeStream(); - openStream(); - } -} - -void plotly::plot(unsigned long x, int y) -{ - if (!initalised) - return; - - reconnectStream(); - - // need to prefix with the length so print it once to get the content lenght and then a second time with the prefix. - int len = snprintf(buffer,k_bufferSize,"{\"x\": %lu, \"y\": %d}\n", x,y); - len = snprintf(buffer,k_bufferSize,"%x\r\n{\"x\": %lu, \"y\": %d}\n\r\n",len, x,y); - sendFormatedText(buffer,len); -} - -void plotly::plot(unsigned long x, float y) -{ - if (!initalised) - return; - - reconnectStream(); - - // need to prefix with the length so print it once to get the content lenght and then a second time with the prefix. - int len = snprintf(buffer,k_bufferSize,"{\"x\": %lu, \"y\": %.3f}\n", x,y); - len = snprintf(buffer,k_bufferSize,"%x\r\n{\"x\": %lu, \"y\": %.3f}\n\r\n",len, x,y); - sendFormatedText(buffer,len); -} - -void plotly::plot(float x, float y) -{ - if (!initalised) - return; - - reconnectStream(); - - // need to prefix with the length so print it once to get the content lenght and then a second time with the prefix. - int len = snprintf(buffer,k_bufferSize,"{\"x\": %.3f, \"y\": %.3f}\n", x,y); - len = snprintf(buffer,k_bufferSize,"%x\r\n{\"x\": %.3f, \"y\": %.3f}\n\r\n",len, x,y); - sendFormatedText(buffer,len); -} - - -bool plotly::print_(int d) -{ - char smallBuffer[10]; - int32_t len = snprintf(smallBuffer,10,"%d",d); - return sendFormatedText(smallBuffer,len); -} - -bool plotly::print_(unsigned long d) -{ - char smallBuffer[12]; - int32_t len = snprintf(smallBuffer,12,"%lu",d); - return sendFormatedText(smallBuffer,len); -} - -bool plotly::print_(float d) -{ - char smallBuffer[12]; - int32_t len = snprintf(smallBuffer,12,"%f",d); - return sendFormatedText(smallBuffer,len); -} - -bool plotly::printNetTerminator_() -{ - return sendFormatedText("\r\n",2); -} - - -bool plotly::print_(char *d) // strings could be long, use the big buffer -{ - int32_t len = snprintf(buffer,k_bufferSize,"%s",d); - return sendFormatedText(buffer,len); -} - - -bool plotly::sendFormatedText(char* data, int size) -{ - if(log_level < 2) { - fprintf(stderr,"%s",data); - } - if(!dry_run) { - if (!socket) { - fprintf(stderr,"\nTX failed, No network socket exists\n"); - return false; - } - if (!(socket->is_connected())) { - fprintf(stderr,"\nTX failed, Network socket not connected\n"); - return false; - } - - int32_t sent = socket->send_all(data,size); - if (sent == size) - return true; - else { - fprintf(stderr,"\nTX failed to send _%s_ Sent %d of %d bytes\n",data,sent,size); - return false; - } - } else - return true; -} -
diff -r 9409a72ab6c0 -r d4f705ba2ea5 plotly.h --- a/plotly.h Tue Jul 29 13:30:31 2014 +0000 +++ b/plotly.h Tue Jul 29 13:43:31 2014 +0000 @@ -10,16 +10,20 @@ /** Create a plot on plot.ly * -* Based on the Ardunio code supplied by plot.ly +* Creates a streaming X/Y scatter plot with line on the plot.ly site. +* Multiple lines can be drawn however each line currently requires a seperate network socket so you could easily run out of resources if you're not careful. +* I've only tested 1 and 2 line plots. * -* Creates a streaming X/Y scatter plot with line on the plot.ly site. -* Update periods can be between 50ms and 60s due to limitations imposed by plot.ly. +* In theory each line could be plotted by a different mbed but this would require some changes to the way charts are initalised. +* +* Update periods can be between 50ms and 60s. Anything faster will be filtered, anything slower will result in the connection getting closed. * * Requires an mbed with network support. * -* Provided as is, it works for me but your mileage may vary. Sorry, I don't have time to offer much support on this. +* Based on the Ardunio code supplied by plot.ly and provided as is, it works for me but your mileage may vary. +* Sorry, I don't have time to offer much support on this. * -* You will need to create a plot.ly account and then go to https://plot.ly/settings to get your API key and a streaming token. +* You will need to create a plot.ly account and then go to https://plot.ly/settings to get your API key and streaming tokens. * * See Plotly_HelloWorld for a sample implimentation. * @@ -35,7 +39,7 @@ @param filename The name of the file to save the chart as @param nTraces The number of traces (MUST match the size of stream_tokens, can be omitted for a single line) */ - plotly(const char *username, const char *api_key, const char ** stream_tokens, const char *filename, int nTraces = 1); + plotly(const char *username, const char *api_key, const char *stream_tokens[], const char *filename, int nTraces = 1); ~plotly(); @@ -45,7 +49,7 @@ Time taken for this function can vary depending on network delays. - If you wish to change any of the options line max points or world readability then make sure you change them BEFORE calling this function. + If you wish to change any of the options like max points or world readability then make sure you change them BEFORE calling this function. */ bool init(); @@ -85,7 +89,7 @@ */ void openStreams(); - /** close all the streaming connections + /** Close all the streaming connections Call to tidy up and free up system resources once there is no more data to send */
diff -r 9409a72ab6c0 -r d4f705ba2ea5 plotly.h.orig --- a/plotly.h.orig Tue Jul 29 13:30:31 2014 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,152 +0,0 @@ -#ifndef plotly_streaming_ethernet_h -#define plotly_streaming_ethernet_h - -#include <EthernetInterface.h> -#include <TCPSocketConnection.h> - -// size of the large buffer used for constructing messages. -#define k_bufferSize 400 - - -/** Create a plot on plot.ly -* -* Based on the Ardunio code supplied by plot.ly -* -* Creates a streaming X/Y scatter plot with line on the plot.ly site. -* Update periods can be between 50ms and 60s due to limitations imposed by plot.ly. -* -* Requires an mbed with network support. -* -* Provided as is, it works for me but your mileage may vary. Sorry, I don't have time to offer much support on this. -* -* You will need to create a plot.ly account and then go to https://plot.ly/settings to get your API key and a streaming token. -* -* See Plotly_HelloWorld for a sample implimentation. -* -*/ - -class plotly -{ - public: - /** - @param username Your plot.ly username - @param api_key Your plot.ly API key - @param stream_token A plot.ly streaming token for your plot.ly account - @param filename The name of the file to save the chart as - */ - plotly(char *username, char *api_key, char* stream_token, char *filename); - /** - */ - ~plotly(); - - /** Initalises the chart - - This fucntion creates a blank chart on the plot.ly system and configures it to recieve streamed data using the specified token - - Time taken for this function can vary depending on network delays. - - If you wish to change any of the options line max points or world readability then make sure you change them BEFORE calling this function. - */ - bool init(); - - /** - Adds a point to the chart. The chart MUST be initalised before calling this. - Note, if the streaming network port is closed then this will attempt to open the port and re-establish the stream connection, this could block for a while. - - @param x The X value. - @param y The y value. - */ - void plot(unsigned long x, int y); - /** - Adds a point to the chart. The chart MUST be initalised before calling this. - Note, if the streaming network port is closed then this will attempt to open the port and re-establish the stream connection, this could block for a while. - - @param x The X value. - @param y The y value. - */ - void plot(unsigned long x, float y); - - /** - Adds a point to the chart. The chart MUST be initalised before calling this. - Note, if the streaming network port is closed then this will attempt to open the port and re-establish the stream connection, this could block for a while. - - @param x The X value. - @param y The y value. - */ - void plot(float x, float y); - - /** - Opens the streaming connection. - - Normally you'd do this after a sucessful call to init() and before starting plotting in order to avoid a connection delays when you first call plot() - */ - bool openStream(); - /** - closes the streaming connection - */ - void closeStream(); - - /** - output message level - Messages are sent to stderr (which defaults to the mBed USB programing/debug port). - 0 = Debugging, 1 = Informational, 2 = Status, 3 = Errors (default), 4 = Quiet - */ - int log_level; - - /** - set true to not actually connect to the network.. - */ - bool dry_run; - - /** - Maximum points to display on the streaming chart, once you go over this old points will no longer be displayed. - Defaults to 30 - */ - int maxpoints; - - /** - Sets whether the chart will be public or not. - Defaults to true - */ - bool world_readable; - - /** - Converts timestamps to the local time zone - */ - bool convertTimestamp; - /** - Timezone string to use - */ - char *timezone; - /** - Sets what to do if the file already exists, valid options are: - "new","overwrite" (default),"append","extend" - */ - char *fileopt; - - private: - - void reconnectStream(); - - bool print_(int d); - bool print_(unsigned long d); - bool print_(float d); - bool print_(char *d); - bool printNetTerminator_(); - - - bool sendFormatedText(char* data, int size); - - char buffer[512]; -// char rxBuffer[128]; - TCPSocketConnection *socket; - - char *username_; - char *api_key_; - char *stream_token_; - char *filename_; - - bool initalised; - -}; -#endif