For this project I'm using an Arduino Uno with a ESP8266. I'm reading an ADC pin/pin's voltage on the Uno and wirelessly sending the data to plotly's server for real-time visualization. Plotly is a free online browser based tool for visualizing data, which also has a streaming feature for displaying your data in real-time. Plotly is great because any device that has a browser and is connected to the internet (smartphone, PC, tablet, etc) can see the real-time data. This is not a full professional solution I'm offering here, just a quick demonstration on how this can be done with these common cheap parts.
*I highly recommend you check out my other project, where I run similar code natively (no Arduino needed) on the ESP. It is much less work then this project.
I was helping out someone who wanted to send real-time data wirelessly to a smartphone/PC on a tight budget. I found this method of displaying real-time data to be useful. Even though we did not end up using this solution, I can see someone using this for a home weather station or something similar.
The Arduino Sketch
Here is my Arduino sketch. It is made for Arduino Uno/Pro Mini paired with a ESP8266 running the 0018000902-AI03 firmware (I have inlcuded the firmware bin file in the instructions section). Originally my code was development on an Arduino Due which has 96 KB of SRAM. This is plenty of SRAM to store the strings used in the original sketch, but because the Uno has only 2 KB of SRAM it's much wiser to store this in flash. This sketch compiles with 30% flash and 25% SRAM used!! Leaves plenty of room for more development. The code makes use of hardware UART (digital pins 0 & 1) to communicate to the ESP8266 and soft UART (digital pin 11) for debugging purposes.
#define ssid "YourSSID"
#define pass "YourWiFiPassword"
#define userName "YourPlotlyUserName"
#define APIKey "xxxxxxxxxx"
#define fileName "test"
#define fileopt "overwrite"
#define nTraces 2
#define maxpoints "30"
#define world_readable true
#define convertTimestamp true
#define timezone "Australia/Melbourne"
#include <SoftwareSerial.h>
#include <avr/pgmspace.h>
#define prog_char char PROGMEM
char *tokens[nTraces] = {"AAAAAAAAAA","BBBBBBBBBB"};
char stream_site[25] = {0};
SoftwareSerial mySerial(10, 11); // RX, TX
const PROGMEM char cmd_0[] = {"AT\r\n"};
const PROGMEM char cmd_1[] = {"ATE1\r\n"};
const PROGMEM char cmd_2[] = {"AT+CWMODE=1\r\n"};
const PROGMEM char cmd_3[] = {"AT+CIPMODE=0\r\n"};
const PROGMEM char cmd_4[] = {"AT+RST\r\n"};
const PROGMEM char cmd_5[] = {"AT+CIPMUX=1\r\n"};
const PROGMEM char cmd_6[] = {"AT+CWJAP=\""};
const PROGMEM char cmd_7[] = {"AT+CIPCLOSE=\""};
const PROGMEM char cmd_8[] = {"AT+CIPSTART=0,\"TCP\",\""};
const PROGMEM char cmd_9[] = {"AT+CIPSEND=0,"};
const PROGMEM char resp_0[] = {"OK"};
const PROGMEM char resp_1[] = {"ready"};
const PROGMEM char resp_2[] = {"no change"};
const PROGMEM char resp_3[] = {"CONNECT"};
const PROGMEM char resp_4[] = {"Unlink"};
const PROGMEM char resp_5[] = {"Linked"};
const PROGMEM char resp_6[] = {">"};
const PROGMEM char resp_7[] = {"~"};
const PROGMEM char error[] = {"*ERROR "};
const PROGMEM char error_0[] = {"0*"};
const PROGMEM char error_1[] = {"1*"};
const PROGMEM char error_2[] = {"2*"};
const PROGMEM char error_3[] = {"3*"};
const PROGMEM char error_4[] = {"4*"};
const PROGMEM char error_5[] = {"5*"};
const PROGMEM char error_6[] = {"6*"};
const PROGMEM char error_7[] = {"7*"};
const PROGMEM char error_8[] = {"8*"};
const PROGMEM char error_9[] = {"9*"};
const PROGMEM char error_10[] = {"10*"};
const PROGMEM char error_11[] = {"11*"};
const PROGMEM char error_12[] = {"12*"};
const PROGMEM char error_13[] = {"13*"};
const PROGMEM char error_14[] = {"14*"};
const PROGMEM char error_15[] = {"15*"};
const PROGMEM char error_16[] = {"16*"};
const PROGMEM char string_0[] = {"Initializing plot with Plot.ly server...\r\n"};
const PROGMEM char string_1[] = {"\",\""};
const PROGMEM char string_2[] = {"\""};
const PROGMEM char string_3[] = {"\",80"};
const PROGMEM char string_4[] = {"POST /clientresp HTTP/1.1\r\n"};
const PROGMEM char string_5[] = {"Host: plot.ly:80\r\n"};
const PROGMEM char string_6[] = {"User-Agent: Arduino/0.6.0\r\n"};
const PROGMEM char string_7[] = {"Content-Type: application/x-www-form-urlencoded\r\n"};
const PROGMEM char string_8[] = {"Content-Length: "};
const PROGMEM char string_9[] = {"version=2.3&origin=plot&platform=arduino&un="};
const PROGMEM char string_10[] = {"&key="};
const PROGMEM char string_11[] = {"&args=["};
const PROGMEM char string_12[] = {"{\"y\": [], \"x\": [], \"type\": \"scatter\", \"stream\": {\"token\": \""};
const PROGMEM char string_13[] = {"\", \"maxpoints\": "};
const PROGMEM char string_14[] = {"}}"};
const PROGMEM char string_15[] = {", "};
const PROGMEM char string_16[] = {"]&kwargs={\"fileopt\": \""};
const PROGMEM char string_17[] = {"\", \"filename\": \""};
const PROGMEM char string_18[] = {"\", \"world_readable\": "};
const PROGMEM char string_19[] = {"true"};
const PROGMEM char string_20[] = {"false"};
const PROGMEM char string_21[] = {"}"};
const PROGMEM char string_22[] = {"Successfully Initialized.\r\n"};
const PROGMEM char string_23[] = {"Please visit: \"http://plot.ly/~"};
const PROGMEM char string_24[] = {"\"."};
const PROGMEM char string_25[] = {"POST / HTTP/1.1\r\n"};
const PROGMEM char string_26[] = {"Host: arduino.plot.ly\r\n"};
const PROGMEM char string_27[] = {"User-Agent: Arduino\r\n"};
const PROGMEM char string_28[] = {"Transfer-Encoding: chunked\r\n"};
const PROGMEM char string_29[] = {"Connection: close\r\n"};
const PROGMEM char string_30[] = {"plotly-convertTimestamp: \""};
const PROGMEM char string_31[] = {"\"\r\n\r\n"};
const PROGMEM char string_32[] = {"\r\n{\"x\": "};
const PROGMEM char string_33[] = {", \"y\": "};
const PROGMEM char string_34[] = {", \"streamtoken\": \""};
const PROGMEM char string_35[] = {"\"}\n\r\n0\r\n\r\n"};
const char* const resp_table[] PROGMEM = {resp_0, resp_1, resp_2, resp_3, resp_4, resp_5, resp_6, resp_7};
char buffer[20]={0};
/* -------------- Setup ------------------ */
void setup() {
// Setup Serial
mySerial.begin(9600);
Serial.begin(9600);
Serial.setTimeout(8000);
delay(1000);
ESP8266_Init();
plotly_init();
ESP8266_Disconnect(0); // Making sure disconnected
ESP8266_Connect("arduino.plot.ly");
}
/* ------------- Loop -------------------- */
void loop() {
mySerial.println("Reading ADC...");
int val = analogRead(0);
int val2 = analogRead(1);
mySerial.println("Sending Jason...");
long time_ms = millis();
while(!plotly_plot(time_ms,val,tokens[0])){ESP8266_Connect("arduino.plot.ly");}
while(!plotly_plot(time_ms,val2,tokens[1])){ESP8266_Connect("arduino.plot.ly");}
}
/* --------------- Functions -------------------- */
void mySerial_PrintConstString(const prog_char str[]) {
char c;
if(!str) return;
while((c = pgm_read_byte(str++)))
mySerial.write(c);
}
void Serial_PrintConstString(const prog_char str[]) {
char c;
if(!str) return;
while((c = pgm_read_byte(str++)))
Serial.write(c);
}
boolean find_resp(unsigned char a, const prog_char error_str[]){
strcpy_P(buffer, (char*)pgm_read_word(&(resp_table[a])));
if(!Serial.find(buffer)){
mySerial_PrintConstString(error);
mySerial_PrintConstString(error_str);
mySerial.println();
return false;
}
else{
return true;
}
}
void ESP8266_Init(){
mySerial_PrintConstString(string_0); // send debug "Initializing plot with Plot.ly server...\r\n"
// Startup test
Serial_PrintConstString(cmd_0); // send "AT\r\n"
if(!find_resp(0,error_0)){while(1){}} // find "OK"
// Turn ECHO off
Serial_PrintConstString(cmd_1); // send "ATE1\r\n"
if(!find_resp(0,error_1)){while(1){}} // find "OK"
// Put WiFi into Station mode
Serial_PrintConstString(cmd_2); // send "AT+CWMODE=1\r\n"
if(!find_resp(2,error_2)){ // find "no change"
Serial_PrintConstString(cmd_2); // send "AT+CWMODE=1\r\n"
if(!find_resp(0,error_3)){while(1){}} // find "OK"
}
// Set data transmission mode to no data (0)
Serial_PrintConstString(cmd_3); // send "AT+CIPMODE=0\r\n"
if(!find_resp(0,error_4)){while(1){}} // find "OK"
// Restart ESP8266
Serial_PrintConstString(cmd_4); // send "AT+RST\r\n"
if(!find_resp(1,error_5)){while(1){}} // find "ready"
// Enable multiple connections (MUX)
Serial_PrintConstString(cmd_5); // send "AT+CIPMUX=1\r\n"
if(!find_resp(0,error_6)){while(1){}} // find "OK"
Serial_PrintConstString(cmd_6); // send "AT+CWJAP=\"
Serial.print(ssid); // send ssid
Serial_PrintConstString(string_1); // send "\",\""
Serial.print(pass); // send password
Serial_PrintConstString(string_2); // send "\""
Serial.println();
if(!find_resp(0,error_7)){while(1){}} // find "OK"
}
void ESP8266_Disconnect(unsigned char id){
Serial_PrintConstString(cmd_7); // send "AT+CIPCLOSE=\""
Serial.print(id); // send id
Serial_PrintConstString(string_2); // send "\""
Serial.println();
if(!find_resp(4,error_8)){ // find "Unlink"
Serial_PrintConstString(cmd_7); // send "AT+CIPCLOSE=\""
Serial.print(id); // send id
Serial_PrintConstString(string_2); // send "\""
Serial.println();
while(1){}
}
}
void ESP8266_Connect(char *url){
int p = (int)url;
Serial_PrintConstString(cmd_8); // send "AT+CIPSTART=0,\"TCP\",\""
url = (char *)p;
while(*url){ // send URL
Serial.print(*url);
url++;
}
Serial_PrintConstString(string_3); // send "\",80"
Serial.println();
if(!find_resp(5,error_9)){ // find "Linked"
Serial_PrintConstString(cmd_8); // send "AT+CIPSTART=0,\"TCP\",\""
url = (char *)p;
while(*url){ // send URL
Serial.print(*url);
url++;
}
Serial_PrintConstString(string_3); // send "\",80"
Serial.println();
if(find_resp(3,error_10)){ // find "CONNECT"
ESP8266_Disconnect(0);
Serial_PrintConstString(cmd_8); // send "AT+CIPSTART=0,\"TCP\",\""
url = (char *)p;
while(*url){ // send URL
Serial.print(*url);
url++;
}
Serial_PrintConstString(string_3); // send "\",80"
Serial.println();
if(!find_resp(5,error_11)){while(1){}} // find "Linked"
}
}
}
void plotly_init(){
unsigned int i = 0;
char charbuffer;
unsigned int contentLength = 126 + strlen(userName) + strlen(fileopt) + nTraces*(87+strlen(maxpoints)) + (nTraces - 1)*2 + strlen(fileName);
if(world_readable){
contentLength += 4;
}
else{
contentLength += 5;
}
String contentLengthString = String(contentLength);
const char* contentLengthConstString = contentLengthString.c_str();
unsigned int postLength = contentLength + 141 + strlen(contentLengthConstString);
ESP8266_Connect("plot.ly");
Serial_PrintConstString(cmd_9); // send "AT+CIPSEND=0,"
Serial.println(postLength);
if(find_resp(6,error_12)){ // find ">"
Serial_PrintConstString(string_4); // send "POST /clientresp HTTP/1.1\r\n"
Serial_PrintConstString(string_5); // send "Host: plot.ly:80\r\n"
Serial_PrintConstString(string_6); // send "User-Agent: Arduino/0.6.0\r\n"
Serial_PrintConstString(string_7); // send "Content-Type: application/x-www-form-urlencoded\r\n"
Serial_PrintConstString(string_8); // send "Content-Length: "
Serial.print(contentLength);
Serial.println();
Serial.println();
Serial_PrintConstString(string_9); // send "version=2.3&origin=plot&platform=arduino&un="
Serial.print(userName); // send userName
Serial_PrintConstString(string_10); // send "&key="
Serial.print(APIKey); // send APIKey
Serial_PrintConstString(string_11); // send "&args=["
for(int i=0; i<nTraces; i++){
Serial_PrintConstString(string_12); // send "{\"y\": [], \"x\": [], \"type\": \"scatter\", \"stream\": {\"token\": \""
Serial.print(tokens[i]); // send token
Serial_PrintConstString(string_13); // send "\", \"maxpoints\": "
Serial.print(maxpoints); // send maxpoints
Serial_PrintConstString(string_14); // send "}}"
if(nTraces > 1 && i != nTraces-1){
Serial_PrintConstString(string_15); // send ", "
}
}
Serial_PrintConstString(string_16); // send "]&kwargs={\"fileopt\": \""
Serial.print(fileopt); // send fileopt
Serial_PrintConstString(string_17); // send "\", \"filename\": \""
Serial.print(fileName); // send fileName
Serial_PrintConstString(string_18); // send "\", \"world_readable\": "
if(world_readable){
Serial_PrintConstString(string_19); // send "true"
}
else{
Serial_PrintConstString(string_20); // send "false"
}
Serial_PrintConstString(string_21); // send "}"
if(find_resp(7,error_13)){ // find "~"
i=0;
while(1){
while(!Serial.available()){}
charbuffer = Serial.read();
if(charbuffer == 34){break;}
stream_site[i] = charbuffer;
i++;
}
if(!find_resp(0,error_14)){ // find "OK"
while(1){}
}
mySerial_PrintConstString(string_22); // send debug "Successfully Initialized.\r\n"
mySerial_PrintConstString(string_23); // send debug "Please visit: \"http://plot.ly/~"
i=0;
while(stream_site[i]){
mySerial.print(stream_site[i]);
i++;
}
mySerial_PrintConstString(string_24); // send debug "\"."
mySerial.println();
}
else{
while(1){}
}
}
else{
while(1){}
}
}
bool plotly_plot(unsigned long x, int y, char *token){
String xString = String(x);
String yString = String(y);
const char* xConstString = xString.c_str();
const char* yConstString = yString.c_str();
unsigned int jasonLength = 44 + strlen(xConstString) + strlen(yConstString);
String jasonLengthString = String(jasonLength, HEX);
const char* ConstJasonLengthString = jasonLengthString.c_str();
unsigned int postLength = 167 + strlen(ConstJasonLengthString) + jasonLength;
Serial_PrintConstString(cmd_9); // send "AT+CIPSEND=0,"
Serial.println(postLength);
if(find_resp(6,error_15)){ // find ">"
Serial_PrintConstString(string_25); // send "POST / HTTP/1.1\r\n"
Serial_PrintConstString(string_26); // send "Host: arduino.plot.ly\r\n"
Serial_PrintConstString(string_27); // send "User-Agent: Arduino\r\n"
Serial_PrintConstString(string_28); // send "Transfer-Encoding: chunked\r\n"
Serial_PrintConstString(string_29); // send "Connection: close\r\n"
Serial_PrintConstString(string_30); // send "plotly-convertTimestamp: \"Australia/Melbourne\"\r\n\r\n"
Serial.print(timezone);
Serial_PrintConstString(string_31);
}
else{
return false;
}
Serial.print(jasonLengthString);
Serial_PrintConstString(string_32); // send "\r\n{\"x\": "
Serial.print(x); // send x
Serial_PrintConstString(string_33); // send ", \"y\": "
Serial.print(y); // send y
Serial_PrintConstString(string_34); // send ", \"streamtoken\": \""
while(*token){ // send token
Serial.print(*token);
token++;
}
Serial_PrintConstString(string_35); // send "\"}\n\r\n0\r\n\r\n"
if(find_resp(0,error_16)){ // find "OK"
mySerial.println("Sent data!");
return true;
}
else{
mySerial.println("*Couldn't send data!!*");
return false;
}
}
Now you have created a real-time data logger/grapher using Plotly :).