Search code examples
ajaxxmlarduinoesp8266teensy

ESP8266 + Teensyduino Web Server


Im using a Teensy 3.2 microcontroller paired with an ESP8266. Right now im just trying to serve a simple HTML web page that is updated with ajax. I can connect to the ESP and serve a page but im having trouble updating the page with XML data. The problem is somewhere in the loop function. Im not sure how to get the ESP to properly send XML data, or maybe im missing a critical function. Help greatly appreciated!

#define LED1 11
#define LED2 12

#define BUFFER_SIZE 4096
#define SSID "xxxx" // change this to match your WiFi SSID
#define PASS "xxxx" // change this to match your WiFi password
#define PORT "8080" //  Port 8080 is default webserver port

char buffer[BUFFER_SIZE];
int n = 0;

String webSite, javaScript, XML, header, content;

void buildWebsite() {

  header = "HTTP/1.1 200 OK\r\n";
  header += "Content-Type: text/html\r\n";
  header += "Connection: close\r\n";
  //header += "Refresh: 5\r\n";

  buildJavascript();
  content = "<!DOCTYPE HTML>\n";
  content += javaScript;
  content += "<BODY onload='process()'>\n";
  content += "<BR>This is the ESP website.<BR>\n";
  content += "Runtime = <A id='runtime'></A>\n";
  content += "</BODY>\n";
  content += "</HTML>\n";

  header += "Content-Length:";
  header += (int)(content.length());
  header += "\r\n\r\n";

  webSite = header + content;

}

void buildJavascript() {
  javaScript = "<SCRIPT>\n";
  javaScript += "var xmlHttp = createXmlHttpObject();\n";

  javaScript += "function createXmlHttpObject() {\n";
  javaScript += " if(window.XMLHttpRequest) {\n";
  javaScript += "    xmlHttp = new XMLHttpRequest();\n";
  javaScript += " } else {\n";
  javaScript += "    xmlHttp = new ActiveXObject('Microsoft.XMLHTTP');\n";
  javaScript += " }\n";
  javaScript += " return xmlHttp;\n";
  javaScript += "}\n";

  javaScript += "function process(){\n";
  javaScript += " if(xmlHttp.readyState == 0 || xmlHttp.readyState == 4){\n";
  javaScript += "   xmlHttp.open('GET','xml',true);\n";
  javaScript += "   xmlHttp.onreadystatechange = handleServerResponse();\n"; // no brackets?????
  javaScript += "   xmlHttp.send();\n";
  javaScript += " }\n";
  javaScript += " setTimeout('process()',1000);\n";
  javaScript += "}\n";

  javaScript += "function handleServerResponse(){\n";
  javaScript += " if(xmlHttp.readyState == 4 && xmlHttp.status == 200){\n";
  javaScript += "   xmlResponse = xmlHttp.responseXML;\n";
  javaScript += "   xmldoc = xmlResponse.getElementsByTagName('response');\n";
  javaScript += "   message = xmldoc[0].firstChild.nodeValue;\n";
  javaScript += "   document.getElementById('runtime').innerHTML = message;\n";
  javaScript += " }\n";
  javaScript += "}\n";
  javaScript += "</SCRIPT>\n";
}

void buildXML() {
  XML = "<?xml version='1.0' encoding='UTF-8'?>\n";
  XML += "<response>\n";
  XML += millis2time();
  XML += "</response>\n";
}

String millis2time() {
  String Time = "";
  unsigned long ss;
  byte mm, hh;
  ss = millis() / 1000;
  hh = ss / 3600;
  mm = (ss - hh * 3600) / 60;
  ss = (ss - hh * 3600) - mm * 60;
  if (hh < 10)Time += "0";
  Time += (String)hh + ":";
  if (mm < 10)Time += "0";
  Time += (String)mm + ":";
  if (ss < 10)Time += "0";
  Time += (String)ss;
  return Time;
}

/*******************************************************************
* PROGRAM SETUP
********************************************************************/

void setup() {

  delay(1000);

  Serial1.begin(115200); // Teensy to ESP8266
  Serial.begin(115200); // Teensy to USB Serial
  Serial.println("Begin program.");

  pinMode(LED1, OUTPUT);
  pinMode(LED2, OUTPUT);

  // Initialize ESP8266.
  setupWiFi();

}

/*******************************************************************
* DEVICE FUNCTIONS
********************************************************************/

// Read line responses from ESP8266.
bool read_till_eol() {
  static int i = 0;
  if (Serial1.available()) {
    buffer[i++] = Serial1.read();
    if (i == BUFFER_SIZE) i = 0;
    if (i > 1 && buffer[i - 2] == 13 && buffer[i - 1] == 10) {
      buffer[i] = 0;
      i = 0;
      Serial.print(buffer);
      return true;
    }
  }
  return false;
}

// Listen for ESP8266 response. By default we are looking for OK\r\n
char OK[] = "OK\r\n";
byte wait_for_esp_response(int timeout, char* term = OK) {
  unsigned long t = millis();
  bool found = false;
  int i = 0;
  int len = strlen(term); // compute length of (string)
  // wait for at most timeout milliseconds, or if OK\r\n is found
  while (millis() < t + timeout) {
    if (Serial1.available()) {
      digitalWrite(LED2, HIGH);
      buffer[i++] = Serial1.read();
      if (i >= len) {
        if (strncmp(buffer + i - len, term, len) == 0) {
          found = true;
          break;
        }
      }
      digitalWrite(LED2, LOW);
    }
  }
  buffer[i] = 0;
  Serial.print(buffer);
  return found;
}

/*******************************************************************
* LOOP
********************************************************************/

void loop() {
  int ch_id, packet_len;
  char *pb;
  // Look for received IDP (unsolicited data packet) from browser refresh.
  if (read_till_eol()) {
    if (strncmp(buffer, "+IPD,", 5) == 0) // If strings match...
    {
      // Request: (+IPD, connection channel, data length)
      sscanf(buffer + 5, "%d,%d", &ch_id, &packet_len);
      if (packet_len > 0) {
        // Read serial until packet_len character received
        // start from :
        pb = buffer + 5;
        while (*pb != ':') pb++;
        pb++;
        if (strncmp(pb, "GET / HTTP", 10) == 0)
        {
          // Send HTML data.
          wait_for_esp_response(2000);
          Serial.println("Serving HTML ->");
          buildWebsite();
          serve(webSite, ch_id);
        }
        else if (strncmp(pb, "GET /xml", 8) == 0)
        {
          // Send XML data.
          wait_for_esp_response(2000);
          Serial.println("Serving XML ->");
          buildXML();
          serve(XML, ch_id);
          Serial.println(millis2time());
        }
      }
    }
  }
}

/*******************************************************************
* SEND DATA
********************************************************************/

// Send the data to the ESP8266.
void serve(String data, int ch_id)
{
  Serial1.print("AT+CIPSEND=");
  Serial1.print(ch_id);
  Serial1.print(",");
  Serial1.println(data.length());
  if (wait_for_esp_response(1000)) {
    Serial1.print(data);
  }
  else {
    Serial1.print("AT+CIPCLOSE=");
    Serial1.println(ch_id);
  }
}

/*******************************************************************
* SETUP WIFI
********************************************************************/

void setupWiFi() {

  // Turn on echo.
  Serial1.println("ATE1");
  wait_for_esp_response(1000);

  // Set mode 3 (client + AP).
  Serial1.println("AT+CWMODE=3");
  wait_for_esp_response(1000);

  // Reset WiFi module.
  Serial1.print("AT+RST\r\n");
  wait_for_esp_response(1500);

  // Join AP.
  Serial1.print("AT+CWJAP=\"");
  Serial1.print(SSID);
  Serial1.print("\",\"");
  Serial1.print(PASS);
  Serial1.println("\"");
  wait_for_esp_response(5000);

  // Start server.
  Serial1.println("AT+CIPMUX=1");
  wait_for_esp_response(1000);

  // Create TCP Server.
  Serial1.print("AT+CIPSERVER=1,");
  Serial1.println(PORT);
  wait_for_esp_response(1000);

  // Set the automatic socket client disconnection timeout from 1 to 28800 seconds.
  Serial1.println("AT+CIPSTO=6000");
  wait_for_esp_response(1000);

  Serial1.println("AT+GMR");
  wait_for_esp_response(1000);

  Serial1.println("AT+CWJAP?");
  wait_for_esp_response(1000);

  Serial1.println("AT+CIPSTA?");
  wait_for_esp_response(1000);

  Serial1.println("AT+CWMODE?");
  wait_for_esp_response(1000);

  Serial1.println("AT+CIFSR");
  wait_for_esp_response(5000);

  Serial1.println("AT+CWLAP");
  wait_for_esp_response(5000);

  Serial1.println("AT+CIPSTATUS");
  wait_for_esp_response(5000);

  Serial.println("------------------------------------");

}

Solution

  • One of the problems is here:

    xmlHttp.onreadystatechange = handleServerResponse();
    

    You are not binding your handler to the readystatechange event. You are just setting what handleServerResponse() returns to xmlHttp.onreadystatechange.

    It should be:

    xmlHttp.onreadystatechange = handleServerResponse;
    

    The second problem is that you aren't sending your XML as a proper HTTP response.
    You should send it with HTTP headers like you are sending your HTML. And your XML response should set the Content-Type header as text/xml.

    There are other possible problems/improvements like:

    1. You are putting your <script> tag directly into the <html> tag. It should be under the <head> tag.
    2. You are using Refresh: 5 header, but you are already refreshing your page with AJAX.
    3. You don't need so send XML, if you just want a single value. You could just send what millis2time() returns and use document.getElementById('runtime').innerHTML = xmlHttp.responseText; in your JavaScript.
    4. Your HTML code is static and doesn't change between requests. It doesn't make sense to build the whole request on every HTTP request.
      You could just put the whole request string into a const variable and then serve that.