Using an ESP8266 in AP mode to control a blinking LED

Hi, I am attempting to use an ESP8266 in access point mode to make an LED blink or turn off. I found some code here that allows me to turn an LED on and off using a button on the website it creates. This is what the sketch looks like currently.

/*
 * Sketch: ESP8266_LED_Control_02C
 * Now with added CSS and a single button
 * Control an LED from a web browser
 * Intended to be run on an ESP8266
 * 
 * connect to the ESP8266 AP then
 * use web broswer to go to 192.168.4.1
 * 
 */
 
 
#include <ESP8266WiFi.h>
const char WiFiPassword[] = "12345678";
const char AP_NameChar[] = "LEDControl" ;
 
WiFiServer server(80);
 
String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
String html_1 = "<!DOCTYPE html><html><head><meta name='viewport' content='width=device-width, initial-scale=1.0'/><meta charset='utf-8'><style>body {font-size:140%;} #main {display: table; margin: auto;  padding: 0 10px 0 10px; } h2,{text-align:center; } .button { padding:10px 10px 10px 10px; width:100%;  background-color: #4CAF50; font-size: 120%;}</style><title>LED Control</title></head><body><div id='main'><h2>LED Control</h2>";
String html_2 = "";
String html_4 = "</div></body></html>";
 
String request = "";
int LED_Pin = D1;
 
void setup() 
{
    pinMode(LED_Pin, OUTPUT); 
 
    boolean conn = WiFi.softAP(AP_NameChar, WiFiPassword);
    server.begin();
 
} // void setup()
 
 
 
void loop() 
{
 
    // Check if a client has connected
    WiFiClient client = server.available();
    if (!client)  {  return;  }
 
    // Read the first line of the request
    request = client.readStringUntil('\r');
 
    if       ( request.indexOf("LEDON") > 0 )  { digitalWrite(LED_Pin, HIGH);  }
    else if  ( request.indexOf("LEDOFF") > 0 ) { digitalWrite(LED_Pin, LOW);   }
 
 
    // Get the LED pin status and create the LED status message
    if (digitalRead(LED_Pin) == HIGH) 
    {
        // the LED is on so the button needs to say turn it off
       html_2 = "<form id='F1' action='LEDOFF'><input class='button' type='submit' value='Turn of the LED' ></form>
";
    }
    else                              
    {
        // the LED is off so the button needs to say turn it on
        html_2 = "<form id='F1' action='LEDON'><input class='button' type='submit' value='Turn on the LED' ></form>
";
    }
 
 
    client.flush();
 
    client.print( header );
    client.print( html_1 );    
    client.print( html_2 );
    client.print( html_4);
 
    delay(5);
  // The client will actually be disconnected when the function returns and 'client' object is detroyed
 
} // void loop()

I was hoping to be able to change it so that instead of turning the LED on, it would make it blink. I have close to zero experience with Arduino, but I tried a couple things to see if I could get it to work. Here's what I was hoping would work:

    if       ( request.indexOf("LEDON") > 0 )  { 
      for(int i = 0; i < 1000; i++)
      {
         digitalWrite(LED_Pin, HIGH);
         delay(1000);
         digitalWrite(LED_Pin, LOW);  
      }
    }
    else if  ( request.indexOf("LEDOFF") > 0 ) { digitalWrite(LED_Pin, LOW);   }

When I upload this and go to the address, I can turn on the LED, but it doesn't blink. Additionally, I cannot turn off the LED. I'd appreciate it if someone could let me know what I am doing wrong and tell me the correct way to do it. Thanks!

Your loop code gets called over and over again automatically. So you do not want to stay in that loop for any more time that you need to. This way you can make sure lots of things can happen when you extend your program.

First have a look at the example BlinkyWithoutDelay ( Arduino IDE -> File -> Examples -> 02.Digital -> BlinkyWithoutDelay).

Next in your code if you get the request change a variable e.g. a bool in one case true and false in the other. This variable can the ne used in the blink code.

Then ether extend the if statement in the example or place another one around the code. Whatever works for you.

Try it yourself first and when you need anymore help, just reply with your question. :slight_smile: Have fun.

I know this is a dumb question, but based on the bool arduino example, I saw that you want to set the bool above the void setup, like this:

int LEDpin = 5;     // LED on pin 5
int switchPin = 13; // momentary switch on 13, other side connected to ground

bool running = false;

void setup() {
  pinMode(LEDpin, OUTPUT);
  pinMode(switchPin, INPUT);
  digitalWrite(switchPin, HIGH);  // turn on pullup resistor
}

void loop() {
  if (digitalRead(switchPin) == LOW) {
    // switch is pressed - pullup keeps pin high normally
    delay(100);                     // delay to debounce switch
    running = !running;             // toggle running variable
    digitalWrite(LEDpin, running);  // indicate via LED
  }
}

What I am wondering is what the difference is between putting some code in the void setup vs above it. Like what does the void setup do that just putting code outside of it doesn't?

bool running = false;

This statement is actually doing three things. It reserves a memory location, gives it a name and it assigns it a value. To make sure names can be reused many times and memory is used efficiently there are rules around this. You can find lots of C basic tutorials online. Here are the basics.

In your case "running" is a global variable. It is visible everywhere in your program.

If you have the statement inside the setup() function, it is called a local variable. You cannot use the variable in other functions.

You want to use as much local variables as possible.

The setup function is run only once. When the Arduino is switched on.
The loop function gets run over and over again.

Beware, if you declare a variable in the loop function, it will be erased when you leave loop and it will get created new when the loop function gets called again. You can make the variable static, if you need the variable to keep the value from one call of loop to another.

Try the following exercise. Some of the statements in my code are not allowed. The compiler will give you errors. Fist remove the lines that are not allowed. Then add prints to all the variables and see what happens in the Serial Monitor.

int variable1 = 0; // global variable

void setup() {
  int variable2 = 0;

  variable1++;
  Serial.print( "Variable1: " );
  Serial.println( variable1 );

  variable2++;
  variable3++;
}

void loop() {
  int variable3 = 0;

  static int variable 4 = 0;

  variable1++;
  variable2++;
  variable3++;
  variable4++;
}

Klaus_K:
Your loop code gets called over and over again automatically. So you do not want to stay in that loop for any more time that you need to. This way you can make sure lots of things can happen when you extend your program.

First have a look at the example BlinkyWithoutDelay ( Arduino IDE -> File -> Examples -> 02.Digital -> BlinkyWithoutDelay).

Next in your code if you get the request change a variable e.g. a bool in one case true and false in the other. This variable can the ne used in the blink code.

Then ether extend the if statement in the example or place another one around the code. Whatever works for you.

Try it yourself first and when you need anymore help, just reply with your question. :slight_smile: Have fun.

I have attempted to combine the two, and it makes sense when I read the code to myself, but now I can't get the led to turn on. Here is what I've got so far.

/*
 * Sketch: ESP8266_LED_Control_02C
 * Now with added CSS and a single button
 * Control an LED from a web browser
 * Intended to be run on an ESP8266
 * 
 * connect to the ESP8266 AP then
 * use web broswer to go to 192.168.4.1
 * 
 */
 
 
#include <ESP8266WiFi.h>
const char WiFiPassword[] = "12345678";
const char AP_NameChar[] = "LEDControl" ;
 
WiFiServer server(80);
 
String header = "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n";
String html_1 = "<!DOCTYPE html><html><head><meta name='viewport' content='width=device-width, initial-scale=1.0'/><meta charset='utf-8'><style>body {font-size:140%;} #main {display: table; margin: auto;  padding: 0 10px 0 10px; } h2,{text-align:center; } .button { padding:10px 10px 10px 10px; width:100%;  background-color: #4CAF50; font-size: 120%;}</style><title>LED Control</title></head><body><div id='main'><h2>LED Control</h2>";
String html_2 = "";
String html_4 = "</div></body></html>";
 
String request = "";
int ledPin = D1;

unsigned long previousMillis = 0;        // will store last time LED was updated
const long interval = 1000;           // interval at which to blink (milliseconds)

bool ledOn = false;
 
void setup() 
{
    pinMode(ledPin, OUTPUT); 
 
    boolean conn = WiFi.softAP(AP_NameChar, WiFiPassword);
    server.begin();
    
}
 
 
 
void loop() 
{


   unsigned long currentMillis = millis();

    // Check if a client has connected
    WiFiClient client = server.available();
    if (!client)  {  return;  }
 
    // Read the first line of the request
    request = client.readStringUntil('\r');
 
    if       ( request.indexOf("LEDON") > 0 )  { 
      bool ledOn = true;
    }
    else if  ( request.indexOf("LEDOFF") > 0 ) { 
      bool ledOn = false;   
      digitalWrite(ledPin, LOW);
    }

  if (currentMillis - previousMillis >= interval) {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledOn) {
      if (digitalRead(ledPin) == LOW) {
         digitalWrite(ledPin, HIGH);
    }
    } else {
         digitalWrite(ledPin, LOW);
    }
  }
  
    // Get the LED pin status and create the LED status message
    if (digitalRead(ledPin) == HIGH) 
    {
        // the LED is on so the button needs to say turn it off
       html_2 = "<form id='F1' action='LEDOFF'><input class='button' type='submit' value='Turn of the LED' ></form>
";
    }
    else                              
    {
        // the LED is off so the button needs to say turn it on
        html_2 = "<form id='F1' action='LEDON'><input class='button' type='submit' value='Turn on the LED' ></form>
";
    }
 
 
    client.flush();
 
    client.print( header );
    client.print( html_1 );    
    client.print( html_2 );
    client.print( html_4);
 
    delay(5);
  // The client will actually be disconnected when the function returns and 'client' object is detroyed
 
}

Klaus_K:
bool running = false;

This statement is actually doing three things. It reserves a memory location, gives it a name and it assigns it a value. To make sure names can be reused many times and memory is used efficiently there are rules around this. You can find lots of C basic tutorials online. Here are the basics.

In your case "running" is a global variable. It is visible everywhere in your program.

If you have the statement inside the setup() function, it is called a local variable. You cannot use the variable in other functions.

You want to use as much local variables as possible.

The setup function is run only once. When the Arduino is switched on.
The loop function gets run over and over again.

Beware, if you declare a variable in the loop function, it will be erased when you leave loop and it will get created new when the loop function gets called again. You can make the variable static, if you need the variable to keep the value from one call of loop to another.

Try the following exercise. Some of the statements in my code are not allowed. The compiler will give you errors. Fist remove the lines that are not allowed. Then add prints to all the variables and see what happens in the Serial Monitor.

int variable1 = 0; // global variable

void setup() {
  int variable2 = 0;

variable1++;
  Serial.print( "Variable1: " );
  Serial.println( variable1 );

variable2++;
  variable3++;
}

void loop() {
  int variable3 = 0;

static int variable 4 = 0;

variable1++;
  variable2++;
  variable3++;
  variable4++;
}

I see that because the variable 1 is set to 0 outside of the loop, it adds up by 1. The variable 3 gets set to 0 each loop, so it stays at 1. Makes sense, thanks for the explanation!

if ( request.indexOf("LEDON") > 0 ) {
bool ledOn = true;
}

This code creates another local variable with the same name as your global variable, and as soon as you leave the if statement the variable is detroyed.
Variables are only valid within the {} they where created.

if ( request.indexOf("LEDON") > 0 ) {
ledOn = true;
}

The same is true for the other case.

Klaus_K:
if ( request.indexOf("LEDON") > 0 ) {
bool ledOn = true;
}

This code creates another local variable with the same name as your global variable, and as soon as you leave the if statement the variable is detroyed.
Variables are only valid within the {} they where created.

if ( request.indexOf("LEDON") > 0 ) {
ledOn = true;
}

The same is true for the other case.

Okay, that makes sense. I fixed that, but it's no longer blinking. I read over it again, but didn't see anything wrong with what I've got now.

Check your code from line 69 to 75. Align your code in a way that closing brackets are in the same column as opening brackets. Then ask yourself which if and else statement belong together.

if()
{
 if()
 {
 
 }
}
else
{

}

Additionally, please read the following post. It will tell you how you can autoformat your code to align the brackets automatically.

https://forum.arduino.cc/index.php?topic=644713.0

I've formatted everything so it looks like this:

  if (currentMillis - previousMillis >= interval)
  {
    // save the last time you blinked the LED
    previousMillis = currentMillis;

    // if the LED is off turn it on and vice-versa:
    if (ledOn)
    {
      if (digitalRead(ledPin) == LOW)
      {
        digitalWrite(ledPin, HIGH);
      }
    }
    else 
    {
      digitalWrite(ledPin, LOW);
    }
  }

It still looked correct to me, but I tried moving the else statement into the if statement so it read like this:

if
  if
  else

...but it made no difference. I'll work on the autoformatting now.

I just realized I didn't have it set it to off if it was on, so I added another else statement. This is what I've got now.

    // if the LED is off turn it on and vice-versa:
    if (ledOn)
    {
      if (digitalRead(ledPin) == LOW)
      {
        digitalWrite(ledPin, HIGH);
        Serial.println(F("LED was off, so I set it to on"));
      }
      else
      {
        digitalWrite(ledPin, LOW);
        Serial.println(F("LED was on, so I set it to off."));
      }
    }
    else
    {
      digitalWrite(ledPin, LOW);
      Serial.println(F("LED was set to turn off, so I set it to off"));
    }
  }

The problem is, the first else isn't getting run. I made everything print to serial, and everything else is working, but I am still having the same issue where the light doesn't blink.

OK, I found a few issues. I was just looking at the blinking led before. Let's start with that.

1.) The else statement does not belong to the inner if statement. It belongs to the outer if. So you need to add an else statement switching the led off.

2.) You need to move the entire blinking code before the WiFi code. There is a "return", that stops the timer code from beeing executed when no client is connected. Note: I also reduced the inner if-else into a Read-Invert-Write statement for simple led toggle.

void loop()
{

  unsigned long currentMillis = millis();

  if ( currentMillis - previousMillis >= interval )
  {
    // save the last time you blinked the LED
    previousMillis = currentMillis;
    
    // if the LED is off turn it on and vice-versa:
    if ( ledOn )
    {
      // Toggle LED
      digitalWrite( ledPin, !digitalRead( ledPin ) );
    }
    else
    {
      digitalWrite( ledPin, LOW );
    }
  }

3.) You do not know what requests are comming from the client. I added a Serial.print to look at them in the Serial Monitor. With this, I found there are two request comming with LEDON/LEDOFF. One is a GET and the other is a "Referer: ". So supressed the second request.

 // Read the first line of the request
  request = client.readStringUntil( '\r' );
  Serial.println( request );

  if( request.indexOf( "LEDON" ) > 0 && request.indexOf( "Referer" ) == -1)
  {
    ledOn = true;
    Serial.println( "Request LED ON" );
  }
  else if( request.indexOf( "LEDOFF" ) > 0 && request.indexOf( "Referer" ) == -1 )
  {
    ledOn = false;
    Serial.println( "Request LED OFF" );
  }

4.) In the if statement for the html2 you read the ledPin. Because the LED is a blinking LED you need to read the state variable that we created not the pin.

 if ( ledOn )  
  {
    // the LED is on so the button needs to say turn it off
    html_2 = "<form id='F1' action='LEDOFF'><input class='button' type='submit' value='Turn of the LED' ></form>
";
  }
  else
  {
    // the LED is off so the button needs to say turn it on
    html_2 = "<form id='F1' action='LEDON'><input class='button' type='submit' value='Turn on the LED' ></form>
";
  }

5.) There is still some issue with the web page display on my iPad. I see the whole thing 11 times. But I can turn the led on/blinking and off.

I followed your steps and re-ordered some things and it works! Thank you! I've got another thing I'd like to add, but I'll give it a shot myself before replying.

Okay, I've worked on this for a while and short of using a different web server library and starting from scratch, I'm not sure how to implement this feature. I was hoping to be able to specify the blink interval with a form on the site. I have managed to set this up so that when I input a number into the field, it adds it to the end of the URL like this: 192.168.4.1/get?input1=1000. The problem is that this writes over the /LEDOFF. I'm thinking the first step is to convert the old redirect to a query like the new one, allowing me to have two variables in the URL at a time. Then, I would need to find a way using the ESP8266WiFi.h to read the queries. These last two steps are where I am stuck. This is all assuming that using GET is the best way to do it. I'm not actually sure whether I should be using POST or GET.

The examples I have found for using queries all rely on different web server libraries(not sure if this is the correct word) than the one I am currently using.

If you've got any ideas/recommendations, I'd appreciate hearing them. Thanks for the help.

Well, I am not a big fan of running a webserver on a small microcontroller. The code looks not good. You need to manually create a HTML page. Strings are a real issue as well. I have read multiple times, that people have their Arduino crash because they use strings. Memory is limited and gets fragmented very easy. It is possible to do, but not very elegant.

There are different options. But this depend a little bit on what your end application needs.

If you just want to be able to configure a local device, Bluetooth LE seems to be a good option. It can run on devices that use batteries. It’s easy to setup and you could write a really nice app for a phone if you wanted a pretty interface. But you can use generic app like BLE Scanner to read and write settings.

If you want internet access and nice data presentation you can connect your Wi-Fi microcontroller to the cloud or your own solution on Raspberry Pi/PC and use a protocol like MQTT to send and receive data. This way the microcontroller does what it is good at and all the web stuff is done on application processors with high end operating systems, web frame works, javascript, node-red ...

I Use the ESPwebserver a lot and it works like a breeze. I always go for the Specific Arguments example from your first link. Whether you use post or get doesn'r matter much for the ESP and you can include multiple variabes within 1 form (or multiple forms within 1 page as in the second link)

Strings are a real issue as well. I have read multiple times, that people have their Arduino crash because they use strings. Memory is limited and gets fragmented very easy. It is possible to do, but not very elegant.

On an ESP this is not really an issue, definitely not if you keep the Strings as local variables.

Klaus_K:
If you just want to be able to configure a local device, Bluetooth LE seems to be a good option. It can run on devices that use batteries. It’s easy to setup and you could write a really nice app for a phone if you wanted a pretty interface. But you can use generic app like BLE Scanner to read and write settings.

I do like the idea of using bluetooth for this because I plan on running this on batteries. However, I would still like to complete this project using wifi just for the learning opportunity before I redo the project using bluetooth. If I were to recreate this project using bluetooth, which Arduino BLE board would you recommend? I'd prefer it to be standalone so it doesn't need an arduino to operate, but if I can get a BLE module and arduino for cheap, I don't mind. I will need to buy multiple of these, so I am trying to keep price in mind.

EDIT: I have done some research and it looks like using a HM-10 with an Arduino Nano clone is probably the best setup for me. I found this app that should allow me to add buttons and a slider: ArduinoBlue

Let me know your thoughts.

Deva_Rishi:
I Use the ESPwebserver a lot and it works like a breeze. I always go for the Specific Arguments example from your first link. Whether you use post or get doesn'r matter much for the ESP and you can include multiple variabes within 1 form (or multiple forms within 1 page as in the second link)
On an ESP this is not really an issue, definitely not if you keep the Strings as local variables.

So you're suggesting I transfer over some of the code I'm using now to the example that I linked and convert it to using post or get? I don't mind doing this, as long as it isn't possible using what I've got now. Thanks for the input.

I have a Arduino Nano 33 BLE. The price is a bit higher than your solution, but it is worth the money and you are supporting the development of the Arduino IDE.

The HM-10 is not a generic BLE solution. It is a BLE serial bridge, which provides a quick solution for someone who wants to replace a serial link, but you will not be able to make use of the smart ideas behind BLE.

One of them is characteristics. On a device that has data for other things e.g. a sensor, you just define these characteristics e.g. a temperature and then you store new values into them. Let’s say every 10 seconds.

If a display device connects to the sensor it can discover the characteristics and then choose which one it wants. It then reads the value and shows it on the display. If another display device is used that can show humidity as well it can read that value.

You as a programmer do not need to worry how the data needs to be send and received. You do not need to create a serial protocol to combine temperature and humidity on one side and then separate them on the other side. Also, the display can decide it only wants to show the value every 5 minutes, so it only reads the value every 5 minutes. You do not need to change the sensor to slow down the transmit interval. And the values are not transmitted unnecessarily.

Klaus_K:
I have a Arduino Nano 33 BLE. The price is a bit higher than your solution, but it is worth the money and you are supporting the development of the Arduino IDE.

The HM-10 is not a generic BLE solution. It is a BLE serial bridge, which provides a quick solution for someone who wants to replace a serial link, but you will not be able to make use of the smart ideas behind BLE.

One of them is characteristics. On a device that has data for other things e.g. a sensor, you just define these characteristics e.g. a temperature and then you store new values into them. Let’s say every 10 seconds.

If a display device connects to the sensor it can discover the characteristics and then choose which one it wants. It then reads the value and shows it on the display. If another display device is used that can show humidity as well it can read that value.

You as a programmer do not need to worry how the data needs to be send and received. You do not need to create a serial protocol to combine temperature and humidity on one side and then separate them on the other side. Also, the display can decide it only wants to show the value every 5 minutes, so it only reads the value every 5 minutes. You do not need to change the sensor to slow down the transmit interval. And the values are not transmitted unnecessarily.

I think I see what you mean. For my use, it doesn't seem like I will need to take advantage of these characteristics, but correct me if I am wrong. I do want to support Arduino, but the $20 price plus shipping makes these cost prohibitive for me. I've found this on amazon, let me know if you think it's a better solution: https://www.amazon.com/Emakefun-Wireless-Micro-USB-Interface-Compatible/dp/B07QM6KHS4/
The reviews are pretty good and include some information on using it with the IDE, but I'm unsure about how I talk to the bluetooth module. Would I treat it just like the HM-10?