Web server ajax_inputs error

Hi All,

I’m working on a sketch that will be included in a much larger program. It sets up an AP, serves a page with some fields, including wifi network, user, and password, then connects to the wifi network. The problem I’m having is that although the GET request appears to be correct, an EMPTY_RESPONSE error is thrown at request.send(). Eventually about 60 fields will be included, that’s the reason for using an array for just a few fields while the program is debugged. Almost all fields added later will be combo boxes or radio buttons.

Help is appreciated, and if there is a better way to do this, please let me know. Thanks in advance.

#include <SPI.h>
#include <WiFi101.h>
//#include <HashMap.h>

#define DEBUG

// Constants
const String FIELD_USERNAME = "Username";
const String FIELD_PASSWORD = "Password";
const String ALL_FIELDS[] = {FIELD_USERNAME, FIELD_PASSWORD};
byte numFields = sizeof(ALL_FIELDS) / sizeof(ALL_FIELDS[0]);

//const byte HASH_SIZE = sizeof(ALL_FIELDS);
//HashType<String, String> hashRawArray[HASH_SIZE];
//HashMap<String, String> fieldMap = HashMap<String, String>(hashRawArray, HASH_SIZE);

char apssid[] = "ZEV";
int status = WL_IDLE_STATUS;
WiFiServer server(80);
String HTTP_req;
boolean readingNetwork = false;
boolean readingPassword = false;
String password = "";
String network = "";
boolean needCredentials = true;
boolean needWiFi = false;
int netIndex;
int passIndex;
String xnetwork;
String xpassword;
String w[10];

WiFiClient client;

char *strToChar(String str) {
  int len = str.length() + 1;
  char c[len];
  str.toCharArray(c, len);
  return c;
}

void setup() {

  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }


  int numSsid = WiFi.scanNetworks();
  if (numSsid > 10) numSsid = 10;
  for (byte x = 0; x < 10; x++) {
    //   w[x] = WiFi.SSID(x);

  }

  Serial.println("Access Point Web Server");

#ifdef DEBUG
  Serial.print("Creating access point named: ");
  Serial.println(apssid);
#endif


  if (WiFi.beginAP(apssid) != WL_AP_LISTENING) {
#ifdef DEBUG
    Serial.println("Creating access point failed");
#endif

    while (true);
  }

  delay(1000);
  server.begin();
  printAPStatus();
}

void loop() {
  if (needCredentials) {
    getCredentials();
  }
  if (needWiFi) {
    getWiFi();
  }
}

void getCredentials() {
  client = server.available();
  if (client) {
    Serial.println("new client");
    String currentLine = "";
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        Serial.print(c);
        if (c == '\n') {
          if (currentLine.length() == 0) {
            sendRequestHeaders(client);
            sendHTMLHead(client);
            sendHTMLBody(client);
            sendHTMLFooter(client);
            break;
          }
          else {
            currentLine = "";
          }
        }
        else if (c != '\r') {
          currentLine += c;
        }
        if (c == ',' && currentLine.substring(0, 3) == "GET") {
          Serial.println("test");
          Serial.println(currentLine.substring(0, 3));
          currentLine.replace("%20", "");

          //          for (int fieldIndex = 0; fieldIndex < numFields); fieldIndex++) {
          //            String fieldName = ALL_FIELDS[fieldIndex];
          //            if (currentLine.indexOf("?" + fieldName + "=") != -1) {
          //              int indexOnCurrentLine = currentLine.indexOf("?" + fieldName + "=");
          //              String fieldValue = currentLine.substring(indexOnCurrentLine + sizeof(fieldName) + 2, ...
          //              fieldMap[fieldIndex](fieldName, currentLine.indexOf("?" + fieldName + "="));
          //            }
          //          }
          if (currentLine.indexOf("?Username=") != -1) netIndex = currentLine.indexOf("?Username=");
          if (currentLine.indexOf("?Password=") != -1)passIndex = currentLine.indexOf("?Password=");


          xnetwork = currentLine.substring(netIndex + 9, passIndex);
          xpassword = currentLine.substring(passIndex + 10, currentLine.indexOf(","));



          client.stop();
          WiFi.end();
          readingPassword = false;
          needCredentials = false;
          needWiFi = true;
          Serial.println();
          Serial.println(xnetwork);
          Serial.println(xpassword);
        }
      }
    }
    delay(1);
    client.stop();
    Serial.println("client disconnected");
    Serial.println();
  }
}

void sendRequestHeaders(WiFiClient client) {
  client.println("HTTP/1.1 200 OK");
  client.println("Content-type:text/html");
  client.println();
}

void sendHTMLHead(WiFiClient client) {
  client.println("<html>");
  client.println("<head>");
  client.println("<style type=\"text/css\"> body {font-family: sans-serif; margin:50px; padding:20px; line-height: 250% } </style>");
  client.println("<title>Arduino Setup</title>");
  client.println("</head>");
}

void sendHTMLBody(WiFiClient client) {

  client.println("<body>");
  client.println("<h2>WiFi Networks</h2>");
  for (byte x = 0; x < 10; x++) {
    if (w[x] != "") {
      client.println(w[x]);
      client.println("
");
    }
  }
  client.println("<h2>WiFi Credentials</h2>");
  for (int fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
    String fieldName = ALL_FIELDS[fieldIndex];
    client.print(fieldName + ": ");
    client.print("<input id=\"" + fieldName + "\">
");
  }
  client.print("
");
  client.print("<button type=\"button\" onclick=\"SendText()\">Enter</button>");
  client.println("</body>");
}

void sendHTMLFooter(WiFiClient client) {
  client.println("<script>");
  for (int fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
    String fieldName = ALL_FIELDS[fieldIndex];
    client.println("var " + fieldName + " = document.querySelector('#" + fieldName + "');");
  }
  client.println("function SendText() {");
  client.println("nocache=\"&nocache=\" + Math.random();");
  //    client.println("nocache=\"&nocache=\" + Math.random() * 100000;");

  client.println("var request = new XMLHttpRequest();");
  client.print("var netText = \"&txt=");
  bool first = true;
  for (int fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
    String fieldName = ALL_FIELDS[fieldIndex];
    if (first) client.print("?");
    else client.print("\"&?");
    first = false;
    client.print(fieldName + "=\"" + "+" + fieldName + ".value" + "+");
  }
  client.println("\",&end=end\";");

  client.println("request.open(\"GET\", \"ajax_inputs\" + netText + nocache, true);");
  client.println("request.send()");
  client.println("Username.value=''");
  client.println("Password.value=''}");
  client.println("</script>");
  client.println("</html>");
  client.println();
}

void getWiFi() {
  if (xnetwork == "" or xpassword == "") {
    Serial.println("Invalid WiFi credentials");
    while (true);
  }

  while (WiFi.status() != WL_CONNECTED) {
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(xnetwork);
    WiFi.begin(xnetwork, xpassword);
    delay(10000);
  }
  Serial.println("WiFi connection successful");
  printWiFiStatus();
  needWiFi = false;
  delay(1000);
}

void printWiFiStatus() {
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
  Serial.print("signal strength (RSSI):");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
}

void printAPStatus() {
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
  Serial.print("signal strength (RSSI):");
  Serial.print(WiFi.RSSI());
  Serial.println(" dBm");
  Serial.print("To connect, open a browser to http://");
  Serial.println(ip);
}

if there is a better way to do this, please let me know

First thing to do is ditch the String class.

String w[10];

w is a really meaningless name for an array of Strings.

char *strToChar(String str) {
  int len = str.length() + 1;
  char c[len];
  str.toCharArray(c, len);
  return c;
}

c goes out of scope when the function ends. A pointer to memory that has been reused is useless and dangerous.

          if (currentLine.indexOf("?Username=") != -1) netIndex = currentLine.indexOf("?Username=");
          if (currentLine.indexOf("?Password=") != -1)passIndex = currentLine.indexOf("?Password=");


          xnetwork = currentLine.substring(netIndex + 9, passIndex);

What values do netIndex and passIndex hold, if the current line does not contain Username? If it does not contain Password? Why are you extracting substrings based on possibly bogus values?

Your page has one submit button. That will cause ONE GET request to be made, with name=value pairs for both user name and password. The names will NOT both follow the ? name/value pairs are separated by &.

Well, that's enough for now.

Thanks for your reply.

I'll remove Strings. I don't know exactly how at the moment, but I'll figure it out.

I probably should have removed some of the code that is not yet finished, or doesn't apply to this issue. Here is the GET statement, and the error, from the browser:

GET http://192.168.1.1/ajax_inputs&txt=?Username=test&?Password=abc,&end=end&nocache=0.5755776159582631 Failed to load resource: net::ERR_EMPTY_RESPONSE

And I tried without '&' before each field to search, same error:

GET http://192.168.1.1/ajax_inputs&txt=?Username=test?Password=abc,&end=end&nocache=0.6293617638576421 net::ERR_EMPTY_RESPONSE

The error is thrown on the next statement, 'request.send()'.

Please let me know if you see anything wrong with the GET.

?Password=abc,&end=end&nocache=0.6293617638576421

Are you sure that comma belongs in your query string?

GET http://192.168.1.1/ajax_inputs&txt=&!Username=abc!Password=123&end=end&nocache=85092.70525461601 net::ERR_EMPTY_RESPONSE

Tried without, but still getting the same error. Thanks for the suggestion.

Any ajax request will need a server response back to the browser.
a typical successful response is

HTTP/1.1 200 OK
Content-Type: text/xml

<success>true</success>

You could parse the value from to confirm if the data was received or do nothing. Up to you.

Thanks. Request.send() gives this error ERR_EMPTY_RESPONSE.

There must be something wrong with the syntax of the netText variable. Without it, no error is generated, but no fields are returned.

A GET request has name/value pairs separated by &. The first name/value pair appears after a ?

GET /ajax_inputs?name1=value1&name2=value2...

The GET request does NOT include the http:// bit. It does NOT include the server name.

I made the changes, but still no luck.

GET http://192.168.1.1/ajax_inputs?Username=abc&Password=123&end=end&nocache=27292.01037141522 net::ERR_EMPTY_RESPONSE

Also tried with "&txt=" added back:

GET http://192.168.1.1/ajax_inputs&txt=?Username=abc&Password=123&end=end&nocache=49393.940232239045 net::ERR_EMPTY_RESPONSE

Code:

client.println("request.open(\"GET\", \"ajax_inputs\" + netText + nocache, true);");
  client.println("request.send()");

The browser must be adding the URL between GET and \ajax...

Thanks again for the help.

The browser must be adding the URL between GET and \ajax...

No. The browser connects to the server, just like the Arduino. Then, it makes a GET request, just like the Arduino. The difference is that the browser code writer know how to make a GET request, unlike the Arduino sketch writer.

That is probably true, as I have no prior experience with HTML. If the get request is still incorrect, please point out the error.

If the get request is still incorrect, please point out the error.

I already have. You didn't pay attention then, Why should I repeat myself?