Http server with textboxes converted to int

Hello everybody, I am trying to solve this simple sketch for turning on the LEDs via http server with some modifications (choosing the duration of light by values from textbox), I have flow diagram in my head but little difficulties with converting it from head to program.

Compiling is going without problem but I have problems with understading HTML and serial com relations. I don't know if I am using right type of textbox for this purpose.

The problem is that every time I submit textbox, the URL copies the previous command together with the new, I will post complete code, there are comments about this. Please correct me also if I made some mistakes in the String durations as I'm more to electronics and relay automation than programming but this should be nice start for me, thank you in advance.

#include <SPI.h>
#include <WiFiNINA.h>
#include "arduino_secrets.h" 
///////please enter your sensitive data in the Secret tab/arduino_secrets.h
char ssid[] = SECRET_SSID;        // your network SSID (name)
char pass[] = SECRET_PASS;    // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0;                // your network key index number (needed only for WEP)

int led =  12;
int led2 = 13;
int led3 = 14;
int duration1;
int status = WL_IDLE_STATUS;
WiFiServer server(80);

void setup() {
  //Initialize serial and wait for port to open:
  Serial.begin(9600);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }

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

  pinMode(led, OUTPUT);      // set the LED pin mode
  pinMode(led2, OUTPUT);
  pinMode(led3, OUTPUT);

  // check for the WiFi module:
  if (WiFi.status() == WL_NO_MODULE) {
    Serial.println("Communication with WiFi module failed!");
    // don't continue
    while (true);
  }

  String fv = WiFi.firmwareVersion();
  if (fv < WIFI_FIRMWARE_LATEST_VERSION) {
    Serial.println("Please upgrade the firmware");
  }

  // by default the local IP address will be 192.168.4.1
  // you can override it with the following:
  // WiFi.config(IPAddress(10, 0, 0, 1));

  // print the network name (SSID);
  Serial.print("Creating access point named: ");
  Serial.println(ssid);

  // Create open network. Change this line if you want to create an WEP network:
  status = WiFi.beginAP(ssid, pass);
  if (status != WL_AP_LISTENING) {
    Serial.println("Creating access point failed");
    // don't continue
    while (true);
  }

  // wait 10 seconds for connection:
  delay(10000);

  // start the web server on port 80
  server.begin();

  // you're connected now, so print out the status
  printWiFiStatus();
}


void loop() {
  // compare the previous status to the current status
  if (status != WiFi.status()) {
    // it has changed update the variable
    status = WiFi.status();

    if (status == WL_AP_CONNECTED) {
      // a device has connected to the AP
      Serial.println("Device connected to AP");
    } else {
      // a device has disconnected from the AP, and we are back in listening mode
      Serial.println("Device disconnected from AP");
    }
  }
  
  WiFiClient client = server.available();   // listen for incoming clients

  if (client) {                             // if you get a client,
    Serial.println("new client");           // print a message out the serial port
    String currentLine = "";                // make a String to hold incoming data from the client
    

    
    while (client.connected()) {            // loop while the client's connected
      delayMicroseconds(10);                // This is required for the Arduino Nano RP2040 Connect - otherwise it will loop so fast that SPI will never be served.
      if (client.available()) {             // if there's bytes to read from the client,
        char c = client.read();             // read a byte, then
        Serial.write(c);                    // print it out the serial monitor
        if (c == '\n') {                    // if the byte is a newline character

          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println();

            // the content of the HTTP response follows the header:
             client.print("<span style=font-size:4em>");                            

             
             
             client.print("Click <a href=\"/H1\">here</a> turn the OP12 on<br>");
             
             client.print("Click <a href=\"/L1\">here</a> turn the OP12 off<br>");

             client.print("Click <a href=\"/H2\">here</a> turn the OP13 on<br>");

             client.print("Click <a href=\"/L2\">here</a> turn the OP13 off<br>");

             client.print("Click <a href=\"/H3\">here</a> turn the OP14 on<br>");

             client.print("Click <a href=\"/L3\">here</a> turn the OP14 off<br>");
          

             client.print("<CENTER><form method=get>OP 12 on for:<input type=text size=3 name=r>OP 13 on for:<input type=text size=3 name=g>OP 14 on for:<input type=text size=3 name=b>&nbsp;<input type=submit value=submit></form></CENTER></body></html><br>");
            
            
            // break out of the while loop:
            break;
          }
          else {      // if you got a newline, then clear currentLine:
            currentLine = "";
          }
        }
        else if (c != '\r') {    // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
 
        // Check to see if the client request was "GET /H" or "GET /L":
        if (currentLine.endsWith("GET /H1")) {
          digitalWrite(led, HIGH);               // GET /H1 turns the LED on
        }
        if (currentLine.endsWith("GET /L1")) {
          digitalWrite(led, LOW);                // GET /L1 turns the LED off
        }
        if (currentLine.endsWith("GET /H2")) {
          digitalWrite(led2, HIGH);               // GET /H2 turns the LED on
        }
        if (currentLine.endsWith("GET /L2")) {
          digitalWrite(led2, LOW);                   // GET /L2 turns the LED off
        }

        if (currentLine.endsWith("GET /H3")) {
          digitalWrite(led3, HIGH);                   // GET /H3 turns the LED off
        }

        if (currentLine.endsWith("GET /L3")) {
          digitalWrite(led3, LOW);                     // GET /L3 turns the LED off         Until here everything is fine, except why is GET included here, when there is no any mentioning of GET in the skecth... For this example I will use only first textbox value.
        }

        if (currentLine.startsWith("GET /?"))        // URL request when submiting should start like this example /?r=16&g=32&b=64 - so I want to choose Question mark ? as a identificator
                                                    // lot of times when I click submit button i get: GET /L2?r=747&g=&b= HTTP/1.1 , I don't seem to find why I get in the link old commands
                                                    // Is maybe some better way for that, should I go with endsWith and add some other identifier to the end of the line when submiting textbox?
        {                                      
          String durations = currentLine;         //I want to use string to declare part of the text from where I will take duration for led on i.e. 16 seconds
          int questpos;                           // Position of the question mark
          int andpos;                             // Position of and mark
          questpos = durations.indexOf('?');
          andpos = durations.indexOf('&');          
          durations = durations.substring(questpos+3,andpos-1); //durations is value between = and & that is 16
          duration1 = durations.toInt();                        // Now I should have duration1 as integer
          
          digitalWrite(led, HIGH);                               // turns the LED on for duration1
          delay(duration1*1000);
          digitalWrite(led, LOW);
          
        }
      }
    }
    // close the connection:
    client.stop();
    Serial.println("client disconnected");
  }
}

void printWiFiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());

  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);

  // print where to go in a browser:
  Serial.print("To see this page in action, open a browser to http://");
  Serial.println(ip);

}

I don't know if I understand your problem correctly but I guess you simply have to add the attribute action with a value of "/" to your form element to make it work.

Thank you for your answer pylon, problem with text box was also connected to browsers on my cell phones and PC, which are not the same and arduino interpreted them differently.

I will now be short as possible in my biggest issue, it's about taking value from GET line - example: GET /L2?r=8&g=16&b=32

When I want to add this GET line to the string and print that string with serialprint, controller start to be slow (letter per second in serial monitor) so obviously I did something wrong.

    if (currentLine.endsWith("GET /H3")) {
          digitalWrite(led3, HIGH);                   // GET /H3 turns the LED off
        }

        if (currentLine.endsWith("GET /L3")) {
          digitalWrite(led3, LOW);                     // GET /L3 turns the LED off
        
        }


// Until here everything is fine. For next example I will use only first textbox value.


        if (currentLine.indexOf('&')>1)     // If textbox is submitted, currentLine: GET /L2?r=8&g=16&b=32


        { 
          String durations = currentLine    // Is this possible? To copy that line with GET to the String
                                            // I also tried with first declaring durations empty "" and then adding current line with += but same result  
          Serial.print (durations);
         
          questposition = durations.indexOf('?');
          andposition = durations.indexOf('&');          
          durationR = durations.substring(questpos+3,andpos-1); //durations is value between = and & that is 8
        
         
          digitalWrite(led, HIGH);                               // turns the LED on for durationR
          delay(durationR*1000);
          digitalWrite(led, LOW);
          
        }

Your main problem is the usage of the String class. You shouldn't use this on AVR Arduinos as it fragments the RAM rather fast and afterwards the behavior is unpredictable.

What could be then the best choice, char arrays or something else? I am close to the point when I will make hundreds if lines for any possible inputs, but I want to learn more proficient ways.

Character arrays are C-strings and are supported by most of the standard C string functions on the Arduino platform too (p.e. strtok, strncmp, etc.).
Hundreds of if lines don't make sense here. Tokenizing the input line and parsing it correctly will solve your problems in a RAM-efficient way.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.