# Question
Title: Changing ESP8266 WiFi network via Access Point
I have an ESP8266 project that requires setting the SSID and password for a network via an Access Point. Everything works fine, the only issue is if you send incorrect credentials to the access point, the server returns an empty response before the timeout period elapses, and the error message is never sent to the client.

If the connection is successful then the success message is appropriately sent to the client. It's almost like the `WiFi.begin()` function detects the connection fails on its own, and something there is preventing me from sending my own response.

```
%  curl -X POST "http://192.168.1.1/authenticate?ssid=FakeNetworkpassword=87654321"
curl: (52) Empty reply from server

```

Did some research and saw some mentions to keep the `loop` running, so I attempted to do so below, but to no avail. Would appreciate any ideas, thanks.

```
#include "ESP8266WiFi.h"
#include "ESP8266HTTPClient.h"
#include "ESP8266WebServer.h"

HTTPClient http;
WiFiClient client;

ESP8266WebServer server(80);
IPAddress ipAP(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

unsigned long previousMillisConnection = 0;
unsigned long startingMillisConnection = 0;
const unsigned long CONNECTION_PERIOD = 1000;
const unsigned long CONNECTION_TIMEOUT = 10000;

int DELAY = 40;

char ssid[64] = { 0 };
char password[64] = { 0 };

char ssidNew[64];
char passNew[64];

const int STATE_DEFAULT = 0;
const int STATE_CREDS = 1;
int state = STATE_DEFAULT;

void setup() {
  Serial.begin(9600);

  WiFi.softAPConfig(ipAP, gateway, subnet);
  WiFi.softAP("ESP8266", "12345678");
  delay(100);

  server.on("/authenticate", HTTP_POST, onSetCredentials);
  server.begin();

  performWifiConnect(ssid, password);
  WiFi.waitForConnectResult(CONNECTION_TIMEOUT);

  Serial.println("Initialization complete");
}

void loop() {
  if (state == STATE_DEFAULT) {
    server.handleClient();
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("Connected!");
    }
  }

  else if (state == STATE_CREDS) {
    whileConnecting();
  }

  delay(DELAY);
}

void performWifiConnect(char ssid[], char password[]) {
  Serial.println("Attempting to connect to " + String(ssid) + "...");
  WiFi.setAutoReconnect(false);
  WiFi.persistent(false);
  WiFi.begin(ssid, password);
}

void onWifiConnected() {
  Serial.println("WiFi connected " + WiFi.localIP().toString());
  WiFi.setAutoReconnect(true);
  WiFi.persistent(true);
}

void onSetCredentials() {
  Serial.println("onSetCredentials");

  server.arg("ssid").toCharArray(ssidNew, 64);
  server.arg("password").toCharArray(passNew, 64);

  if (String(ssidNew) == String(ssid) && WiFi.status() == WL_CONNECTED) {
    Serial.println("Already connected to " + String(ssidNew));
    server.send(200, "application/json", "{ \"success\": true, \"changed\": false }");
  } else {
    Serial.println("Switching from " + String(ssid) + " to " + String(ssidNew));
    state = STATE_CREDS;
    performWifiConnect(ssidNew, passNew);
    startingMillisConnection = millis();
  }
}

void whileConnecting() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillisConnection >= CONNECTION_PERIOD) {
    previousMillisConnection = currentMillis;

    Serial.println("checking connection...");

    // Success
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("Connected to " + String(ssidNew) + " successfully!");

      strcpy(ssid, ssidNew);
      strcpy(password, passNew);

      server.send(200, "application/json", "{ \"success\": true, \"changed\": true }");

      state = STATE_DEFAULT;
    }

    // Failure
    if (currentMillis - startingMillisConnection >= CONNECTION_TIMEOUT) {
      Serial.println("connection failed, attempting to reconnect to old network!");
      Serial.println("ssidOld: " + String(ssid));
      Serial.println("passOld: " + String(password));

      String errorMessage = "Timed out while attempting to connect to " + String(ssidNew);
      Serial.println(errorMessage);
      server.send(500, "application/json", "{\"errorMessage\":\"" + errorMessage + "\"}");

      performWifiConnect(ssid, password);

      WiFi.waitForConnectResult(CONNECTION_TIMEOUT);

      state = STATE_DEFAULT;

      if (WiFi.status() != WL_CONNECTED) {
        Serial.println("Could not return to old network!");
      }
    }
  }
}

```

# Answer
> 1 votes
I recommend you to use the WiFiManager library. It has the WiFi provisioning solved.

---

To join an AP esp8266 must change the WiFi radio channel to the channel of the AP. Since the esp8266 has only one radio, the SoftAP must change the channel too. This causes the SoftAP clients to disconnect.

The solution is respond to the configuration request before connecting to AP, then let the configuration page do a new request for the result.

To illustrate here is a simple example:

```
#ifdef ESP32
#include <WiFi.h>
#else
#include <ESP8266WiFi.h>
#endif

void setup() {

  Serial.begin(115200);
  delay(500);

#ifdef ESP32
//  WiFi.begin(); // use SSID and password stored by SDK. commented out to test the Configuration AP
#else
//  WiFi.disconnect(); // forget the persistent connection to test the Configuration AP
#endif

  // waiting for connection to remembered  Wifi network
  Serial.println("Waiting for connection to WiFi");
  WiFi.waitForConnectResult();

  if (WiFi.status() != WL_CONNECTED) {
    Serial.println();
    Serial.println("Could not connect to WiFi. Starting configuration AP...");
    configAP();
  } else {
    Serial.println("WiFi connected");
  }
}

void loop() {
}

void configAP() {

  WiFiServer configWebServer(80);

  WiFi.mode(WIFI_AP_STA); // starts the default AP (factory default or setup as persistent)

  Serial.print("Connect your computer to the WiFi network ");
#ifdef ESP32
  Serial.print("to SSID of you ESP32"); // no getter for SoftAP SSID
#else
  Serial.print(WiFi.softAPSSID());
#endif
  Serial.println();
  IPAddress ip = WiFi.softAPIP();
  Serial.print("and enter http://");
  Serial.print(ip);
  Serial.println(" in a Web browser");

  configWebServer.begin();

  while (true) {

    WiFiClient client = configWebServer.available();
    if (client) {
      char line[64];
      int l = client.readBytesUntil('\n', line, sizeof(line));
      line[l] = 0;
      client.find((char*) "\r\n\r\n");
      if (strncmp_P(line, PSTR("POST"), strlen("POST")) == 0) {
        l = client.readBytes(line, sizeof(line));
        line[l] = 0;

        // parse the parameters sent by the html form
        const char* delims = "=&";
        strtok(line, delims);
        const char* ssid = strtok(NULL, delims);
        strtok(NULL, delims);
        const char* pass = strtok(NULL, delims);

        // send a response before attemting to connect to the WiFi network
        // because it will reset the SoftAP and disconnect the client station
        client.println(F("HTTP/1.1 200 OK"));
        client.println(F("Connection: close"));
        client.println(F("Refresh: 10")); // send a request after 10 seconds
        client.println();
        client.println(F("<html><body><h3>Configuration AP</h3><br>connecting...</body></html>"));
        client.stop();

        Serial.println();
        Serial.print("Attempting to connect to WPA SSID: ");
        Serial.println(ssid);
        WiFi.persistent(true);
        WiFi.setAutoConnect(true);
        WiFi.begin(ssid, pass);
        WiFi.waitForConnectResult();

        // configuration continues with the next request

      } else {

        client.println(F("HTTP/1.1 200 OK"));
        client.println(F("Connection: close"));
        client.println();
        client.println(F("<html><body><h3>Configuration AP</h3><br>"));

        int status = WiFi.status();
        if (status == WL_CONNECTED) {
          client.println(F("Connection successful. Ending AP."));
        } else {
          client.println(F("<form action='/' method='POST'>WiFi connection failed. Enter valid parameters, please.<br><br>"));
          client.println(F("SSID:<br><input type='text' name='i'><br>"));
          client.println(F("Password:<br><input type='password' name='p'><br><br>"));
          client.println(F("<input type='submit' value='Submit'></form>"));
        }
        client.println(F("</body></html>"));
        client.stop();

        if (status == WL_CONNECTED) {
          delay(1000); // to let the SDK finish the communication
          Serial.println("Connection successful. Ending AP.");
          configWebServer.stop();
          WiFi.mode(WIFI_STA);
          return;
        }
      }
    }
  }
}

```

# Answer
> 1 votes
There are several intertwined issues in this one.

Firstly, `setup()` has some code running before it should be.

That first problem is here:

```
  server.on("/authenticate", HTTP_POST, onSetCredentials);
  server.begin();

  performWifiConnect(ssid, password);
  WiFi.waitForConnectResult(CONNECTION_TIMEOUT);

```

You've already set `ssid` and `password` to be null, and server.begin() won't run the server yet (I'll get to that issue in a minute), so this is setting your credentials to be null at the start of the program. There's no point in running `waitForConnectResult()` then, because there is nothing to connect. There may be logic in setting `performWifiConnect()` though, since if you've ever set the Wi-Fi credentials with `wifi.begin()` *before* turning off persistent, those credentials are still remembered (it just won't remember new ones).

The next issue is that you're only handling server requests while the connection *is not invalid*. Thus, when there is an invalid credential for the network, the server doesn't run, and you don't get a response.

```
void loop() {
  if (state == STATE_DEFAULT) {
    server.handleClient();
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("Connected!");
    }
  }

  else if (state == STATE_CREDS) {
    whileConnecting();
  }

  delay(DELAY);
}

```

As you can see, `server.handleclient()` (the actual server; the ability to respond to requests) is only running while it does not have `state == STATE_DEFAULT`. *That* is then changed, `state = STATE_CREDS;`, during the callback for getting network credentials. You'll probably get a reply from the request handler that's currently running, but this will otherwise cause you problems since the server then never responds to any future requests. You **must** continually call `server.handleclient()` or nothing server-related will work.

But, something else is also wrong. You can only pass replies back from the server if you actually send them *while the server is still processing a request.*

```
else {
    Serial.println("Switching from " + String(ssid) + " to " + String(ssidNew));
    state = STATE_CREDS;
    performWifiConnect(ssidNew, passNew);
    startingMillisConnection = millis();
  }

```

As you can see, there is no `server.send()` in this branch, so you'll get no reply. This is likely the most immediate cause of your problem with an empty response.

Instead, you have that in the function `whileConnecting()`. As noted earlier, the server cannot send replies that aren't in the function it calls when it gets a request, or in functions called by that function. The server does not call `whileConnecting()`, `loop()` does. So it can't see or send that reply.

(Side note: This requirement of the server is implemented pretty weirdly, since you're calling server methods while one of its methods calls your code, only for the server to then ask itself what you told it while it was busy with your request, grab that buffer, and return it to the client. But it **only** does that check somewhere inside `server.handleClient()`, and other `send()` calls will either be ignored or get queued to send the next time the client makes a request.)

If you were to wait for a connection inside the server's request handler instead, and then send the error/success messages there too, this might work. It's still bad practice since the server (that part I said you always want to **not** hold up) and the entire rest of your code will be on hold while it waits to connect, but you'll *probably* have it work **as long as there's nothing else time-critical or important going on**.

The best approach would be to have your client poll a second address (or just the same address with no POST values, then have the server check whether there are no args) and have that say whether it is currently connected.

On that note, your connection checking code also has a problem. `loop()` has a delay in it, which is already going to bog things down. But you also have the whileConnecting function check for elapsed time. This *is* the proper way to coordinate multiple independent check-back-and-do-stuff operations, so you're aware of it, but this hasn't been transferred to the rest of the code.

Except, **also** in whileConnecting, you then call `WiFi.waitForConnectResult(CONNECTION_TIMEOUT);`. This will **block** that function until a result is present, which thus halts everything else for however long that timeout is. The only place you "should" use this (again, probably bad practice) is inside the server if you were trying to have it directly reply with the connection status **or** if there's not anything else important going on (which there is, so this is an issue). Since you're already running code periodically on a timer, just have this code check for the timeout directly (easiest method: call the connect function with the credentials, set the check-back time by `timeout`, then check again), and just reconnect then instead of forcing a wait. This will leave the server and the rest of your code responsive.

Now, with all (well, most) of that put together: (NOTE: This code will need some changes made to the rest of your code, and may require adjustment itself, but it should get you very close. I suggest making a backup copy of your code first in case you need to reference or fix something. I also wrote this in a text editor, as I don't currently have access to the IDE to test this. There might be minor errors.)

Solution A (have the server wait and tell you the results): (NOTE: also, delete `whileConnecting()` and all the `state` if you use this method; you want to make sure server.handleClient() is always being called until the connection is actually successful, or just plain always.)

```
void onSetCredentials() {
  Serial.println("onSetCredentials");

  server.arg("ssid").toCharArray(ssidNew, 64);
  server.arg("password").toCharArray(passNew, 64);

  if (String(ssidNew) == String(ssid) && WiFi.status() == WL_CONNECTED) {
    Serial.println("Already connected to " + String(ssidNew));
    server.send(200, "application/json", "{ \"success\": true, \"changed\": false }");
  } else {
    Serial.println("Switching from " + String(ssid) + " to " + String(ssidNew));
    performWifiConnect(ssidNew, passNew);
    startingMillisConnection = millis();
    //code to detect the server connection status and respond starts here//
    WiFi.waitForConnectResult(CONNECTION_TIMEOUT);
    if (WiFi.status() == WL_CONNECTED) {
      Serial.println("Connected to " + String(ssidNew) + " successfully!");

      strcpy(ssid, ssidNew);
      strcpy(password, passNew);

      server.send(200, "application/json", "{ \"success\": true, \"changed\": true }");
    }

    // Failure
    if (currentMillis - startingMillisConnection >= CONNECTION_TIMEOUT) {
      Serial.println("connection failed, attempting to reconnect to old network!");
      Serial.println("ssidOld: " + String(ssid));
      Serial.println("passOld: " + String(password));

      String errorMessage = "Timed out while attempting to connect to " + String(ssidNew);
      Serial.println(errorMessage);
      server.send(500, "application/json", "{\"errorMessage\":\"" + errorMessage + "\"}");
    }
  }
}

```

This solution (A) will *probably* give you the connection status back, assuming the Wi-Fi connection attempt times out before the cURL request/server does. I haven't actually tested this and I did mention it's kinda bad practice.

Solution B (Poll the server for connection status rather than waiting for a reply): (Note: Again, delete any `state` logic and `whileConnecting()` from the other code.)

```
void onSetCredentials() {
  Serial.println("onSetCredentials");
  if (!server.hasArg("ssid")){ //check for no-arg request and return conn status//
    server.send("200", "text/plain", (WiFi.status() != WL_CONNECTED)? "Connected" : "Not Connected"); //or whatever other message(s) you want
    //You may also want to add some sort of Serial.print()-type logging here too, since I didn't.//
  }
  else{ //existing connection setup request handler resumes here//
    server.arg("ssid").toCharArray(ssidNew, 64);
    server.arg("password").toCharArray(passNew, 64);

    if (String(ssidNew) == String(ssid) && WiFi.status() == WL_CONNECTED) {
      Serial.println("Already connected to " + String(ssidNew));
    } else {
      Serial.println("Switching from " + String(ssid) + " to " + String(ssidNew));
      performWifiConnect(ssidNew, passNew);
      startingMillisConnection = millis();
    }
  }
}

```

With Solution B, you can call the cURL command to the ESP8266 *without* the `ssid` and `password` set, and you'll get a connection status back instead. This may take a few attempts while it's still in the process of connecting, but if it *remains* not connected, you know the credentials are wrong.

---
Tags: esp8266
---


# Question
Title: Ramping servo motor taking longer than calculated
I'm learning how to code a "BOE Shield bot" with a partner at my university (first year). To clarify for those that do not know: a BOE Shield bot is a small robot equipped with 2 servo motors, a battery pack and an Arduino Uno. If you google the name you'll find plenty of pictures.

We were tasked to code a program that would make the robot accelerate the continuous rotational servo motors according to a given velocity-time graph in RPMs and seconds. We wrote the code, followed a pretty logical method, and passed the assignment. However one thing, and I spent days on trying to fix it, is still confusing me. The code is ahead, I've tried to make it as readible as possible. The RPM to PWM conversion for the motors is:
PWM = 2 \times RPM + 1500

```
#include <Servo.h>

class motor {
public:
  double Speed = 0;  //The actual speed of the servo in RPM
public:
  Servo servo;

  //returns true if the desired speed "newSpeed" is reached, otherwise increments the current speed and returns false.
  //increment is the calculated speed increase based on the differance in time (interval) and the acceleration which is constant.
  bool accel(double increment, double newSpeed) {
    if (abs(Speed - newSpeed) < 0.1) {
      return true;
    }

    Speed += increment;
    return false;
  }

  void writeMS(double value) {
    servo.writeMicroseconds(value);
  }
};

motor servoLeft = motor();
motor servoRight = motor();

void setup() {
  Serial.begin(9600);
  servoLeft.servo.attach(11);   //vänster -2 * speedLeft + 1500
  servoRight.servo.attach(10);  //höger 2 * speedLeft + 1500
}

//This nested array contains instructions for both servo motors: the desired speed, how long it should take to accelerate to it and how long
//the instruction duration is. The first instruction starts from the second row. The first row is skipped and only used for calculations.
//The columns are in the following order: {instruction length in ms, speed of the left motor in RPM, speed of right motor in RPM, acceleration time}.
double instructions[7][4] = {
  { 0, 0, 0, 0 },
  { 3000, 25, 25, 500 },
  { 2550, 50, 10, 500 },
  { 3000, 25, 25, 500 },
  { 2850, 10, 50, 500 },
  { 3000, 25, 25, 500 },
  { 3000, 15, -15, 500 },
};

//time stuff
unsigned long currentMillis;
unsigned long previousMillis = 0;
unsigned long lastInstructionMillis = 0;
const double interval = 20;

//i is the index for the instructions.
int i = 0;
bool updateL = false;
bool updateR = false;

//the speed increments for each servo. Calculated in the loop.
double incrementL;
double incrementR;

void loop() {
  //This is to help keep track of the time so we can calculate the increments. It essentially means the speeds are only updated ever 20ms.
  currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    //if the desired speed is reached, stop updating. This is just to optimize code.
    if (updateL)
      updateL = !servoLeft.accel(incrementL, instructions[i][1]);

    if (updateR)
      updateR = !servoRight.accel(incrementR, instructions[i][2]);
  }

  //this runs only once after each instruction.
  if (currentMillis - lastInstructionMillis >= instructions[i][0]) {
    lastInstructionMillis += instructions[i][0];
    i++;

    //exit program after all instructions have been completed.
    if (i == 7)
      exit(0);

    //the increments are calculated like such: increment = acceleration * interval
    //where constant acceleration = (newSpeed - oldSpeed) / accelerationTime
    incrementL = (instructions[i][1] - instructions[i - 1][1]) * interval / instructions[i][3];
    incrementR = (instructions[i][2] - instructions[i - 1][2]) * interval / instructions[i][3];

    updateL = true;
    updateR = true;
  }

  Serial.println((String)servoLeft.Speed + " " + (String)servoRight.Speed + " " + (String)currentMillis + " " + (String)i);

  //the actual ms signlas sent to the servos.
  servoLeft.writeMS(2 * servoLeft.Speed + 1500);
  servoRight.writeMS(-2 * servoRight.Speed + 1500);
}

```

For some reason the acceleration is taking longer than specified in the instructions. For example, the first instruction specifies that both servos will accelerate from 0 to 25 RPM within 500ms. But printing out the speeds and time to the serial monitor shows that it actually takes around 860ms. That is if interval is set to 20. If it's set to 50, it takes 560ms.

Suspected causes were:

1. **Void loop taking longer than the interval**

Not the case as measuring it with micros() proved it never took longer than 0.26ms; this was the done with complete program as well.

2. **Rounding errors**

Not the case as switching pretty much all the number types to double still didn't fix anything. And double is more than enough precise for this program.

3. **millis() not being accurate enough**

I don't believe this is the case. I researched how accurate it actually is, and while it's nowhere near perfect, it should be accurate enough for the program. I tried using micros() instead and that lead to the same result, I do not know if that means something.

Other than that I have no idea what the cause could be. The code itself runs fine, and the robot actually seems to be following the instructions perfectly with no errors. It's just that the speeds and accelerations aren't accurate for some reason.

# Answer
There are two things going on here.

You are printing too much to the serial port. After the first second (between 1000 and 9999 ms), each line you print is 20 bytes, including the CRLF terminator. At 9600 b/s, each bytes takes 1.04 ms (1 start bit, 8 data bites and 1 stop bit). This is about 20.8 ms for the whole line, which is slightly longer than `interval`. You should either print less often (say, every other loop iteration) or faster. Just moving to the next standard speed (19200 b/s) will be enough to ensure you do not miss any `interval`. You can go faster though.

The second issue is the way you keep track of `previousMillis`:

```
if (currentMillis - previousMillis >= interval) {
  previousMillis = currentMillis;
  // ...
}

```

This is prone to accumulating errors. The test will most likely not happen exactly at `previousMillis + interval`, mostly because of the serial port. You are a little bit late and, by updating `previousMillis` this way, these errors are *cumulative*. You can avoid this by updating `previousMillis` as:

```
previousMillis += interval;

```

You will still have timing errors, but these will manifest themselves in the form of *jitter*, not as a systematic timing drift.

Interestingly, you are updating `lastInstructionMillis` the right way!

---

**Edit**: Here are some extra comments about your question and your sketch, not necessarily related to the problem you are seeing.

You wrote:

> measuring \[loop()\] with micros() proved it never took longer than 0.26ms.

It seems you measured incorrectly. Maybe you measured a version of the program that was not printing so much to the serial port. Or maybe you looked only at the first iterations of the loop, where `Serial.println()` is fast because the output buffer hasn't filled yet.

The following code is fragile:

> ```
> bool accel(double increment, double newSpeed) {
>   if (abs(Speed - newSpeed) < 0.1) {
>     return true;
>   }
>   // ...
> }
>
> ```

The value 0.1 is arbitrary, and the “right choice” is dependent on the program timing. I suggest something more robust, like:

```
bool accel(double increment, double newSpeed) {
  if (increment > 0) {
    Speed = min(Speed + increment, newSpeed);
  } else {
    Speed = max(Speed + increment, newSpeed);
  }
  return Speed == newSpeed;
}

```

Note that, although it is generally recommended to not test floating point numbers for exact equality, in this case it is safe, because `min()` and `max()` return *exactly* one of their arguments.

The optimization consisting of calling `motor::accel()` only during the acceleration phase is futile: this methods takes an very tiny fraction of the `loop()` time. In the same vein, there would be no harm in calling `Servo::writeMicroseconds()` on every loop iteration, even if some of the calls turn out to be useless.

The code would be more readable if `instructions` was an array of `struct` rather than an array of arrays: this way the fields of each instruction could have evocative names rather than numeric indices.

You are using too much floating point here. Most of the data fields, including all the contents of `instructions`, could be integers of suitable length.
> 2 votes

---
Tags: arduino-uno, timers, code-review, continuous-servo
---


# Question
Title: ATTiny85(Digispark) not connected/recognized
I have 1xDigispark(ATTiny85) from Amazon https://www.amazon.pl/gp/product/B076KS2QDS/ref=ppx\_yo\_dt\_b\_asin\_title\_o00\_s00?ie=UTF8&th=1.

When i connect it into USB port nothing happen i didn't get windows sound alert. And Arduino IDE not detecting it(Not displayed as a port).  I have this link to the drivers in settings and drivers installed. When I try to upload a code with "COM1" port nothing relly happens.

Regards, Wiktor Wołos

# Answer
You have the wrong board selected in your IDE

This

Is a "Digispark (Default 16.5 mhz)" (sic) not a "Digispark Pro". The latter is based on an ATtiny167 not an ATtiny85.

I have bought the exact same item from the same seller.
> 0 votes

---
Tags: arduino-ide, usb, digispark
---


title: Interrupt on button press + debouncing

text:
# Question
Title: Interrupt on button press + debouncing
I want to have an interrupt function executed whenever a button is pressed. The button is connected to `pin 2` and the `GND`. Therefore, the pin is turned to `LOW` whenever the button is pressed. In addition to that, proper debouncing should be used As a proof of concept, the interrupt function should just toggle the `BUILTIN_LED` whenever the button is pressed.

I have tried many different approaches but i cannot make it work.

This is my last iteration:

```
bool led_status = LOW; // current state of output pin
int buttonState; // the current reading from the input pin
int lastButtonState = HIGH;  //the previous reading from the input pin

// the following variables are unsigned longs because the time, measured in
// milliseconds, will quickly become a bigger number than can be stored in an int.
unsigned long lastDebounceTime = 0;  // the last time the output pin was toggled
unsigned long debounceDelay = 50;    // the debounce time; increase if the output flickers

int button_switch = 2; // external interrupt pin
bool initialisation_complete = false; // inhibit any interrupts until initialisation is complete

// ISR for handling interrupt triggers arising from associated button switch
// check to see if you just pressed the button
// (i.e. the input went from LOW to HIGH), and you've waited long enough
// since the last press to ignore any noise:
void button_interrupt_handler()
{
  //static long int elapse_timer;

  if (initialisation_complete == true) //only able to run the ISR when arduino has finished initialization
  {
    // new interrupt so okay start a new button read process -
    // now need to wait for button release plus debounce period to elapse
    // this will be done in the button_read function

    int reading = digitalRead(button_switch);
    if ( reading != lastButtonState) // If the switch changed, due to noise or pressing:
    {
      lastDebounceTime = millis(); // reset the debouncing timer
    }

    // whatever the reading is at, it's been there for longer than the debounce
    // delay, so take it as the actual current state:
    if ( (millis() - lastDebounceTime) > debounceDelay)
    {
      if (reading != buttonState) // if the button state has changed:
      {
        buttonState = reading;
        if (buttonState == HIGH) // only toggle the LED if the new button state is HIGH
        {
          led_status = !led_status;
        }
      }
    }
    digitalWrite(LED_BUILTIN, led_status); //set the LED
    lastButtonState = reading; // save the reading. Next time through the loop, it'll be the lastButtonState:
  }
} // end of button_interrupt_handler

void setup()
{
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(button_switch, INPUT_PULLUP); //no res = change to INPUT_PULLUP
  attachInterrupt(digitalPinToInterrupt(button_switch), button_interrupt_handler, FALLING); //no res = change to FALLING
  digitalWrite(LED_BUILTIN, led_status);
  initialisation_complete = true; // open interrupt processing for business
}

void loop()
{
  //do nothing
}

```

I am not sure what I am doing wrong or why this does not work.

EDIT: When I press the button, nothing happens at all.

# Answer
Here is a non-blocking debouncing solution which is suitable also for electrically noisy environments. For example where there are long wires to the buttons which could pick up spurious signals. In this case, you cannot rely on short pulses being the direct result of button activity and must check that the button state has been stable for X ms before accepting it. It also handles potential bounces on the release of the button. It uses a simple finite state machine approach and does not use interrupts.

It can be tried also in a simulator: https://wokwi.com/projects/351021820324872788

```

const uint8_t ledPin = 13 ;
const uint8_t button_switch = 2;  // the button is wired between this pin and ground (LOW side)
const unsigned long debounceDelay = 100 ;    // the debounce time; increase if the output flickers

enum ButtonState  { PENDING_PRESS, IN_PRESS, PENDING_RELEASE} ;
ButtonState buttonState = ButtonState::PENDING_PRESS ;
uint32_t stateEnteredAtMs = millis() ;

void setup() {
  Serial.begin(115200);
  pinMode( ledPin, OUTPUT) ;
  pinMode( button_switch, INPUT_PULLUP) ;

}

void loop() {
  uint32_t ms = millis() ;
  static uint32_t lastButtonCheckAtMs = 0 ;

  if( ms - lastButtonCheckAtMs > 10 ) {

    switch ( buttonState ) {

     case ButtonState::PENDING_PRESS : {
        if ( digitalRead(button_switch)  == LOW ) {
          buttonState = ButtonState::IN_PRESS ;
          stateEnteredAtMs = ms ;
        }
        break ;
     }

     case ButtonState::IN_PRESS : {
        if ( digitalRead(button_switch) == HIGH )  {
          if ( ms - stateEnteredAtMs < debounceDelay ) {
            stateEnteredAtMs = ms ;
            buttonState = ButtonState::PENDING_PRESS ;
          }
          else {
            stateEnteredAtMs = ms ;
            buttonState = ButtonState::PENDING_RELEASE ;
            digitalWrite( ledPin, ! digitalRead(ledPin)) ;  // toggle led
          }
        }
        break ;
     }

     case ButtonState::PENDING_RELEASE : {
        if ( ms - stateEnteredAtMs > debounceDelay ) {
            stateEnteredAtMs = ms ;
            buttonState = ButtonState::PENDING_PRESS ;
          }
        break ;
     }
    }
    lastButtonCheckAtMs = ms ;
  }
}

```
> 2 votes

# Answer
In almost any run of the ISR it will read the button state as LOW, because the ISR is triggered by the falling edge. The time from the falling edge to the reading is very short, about microseconds, and therefore a level change is improbable.

As others already suggest, you can make the ISR "deaf" for some time after the accepted falling edge. You *can* do this inside the ISR. The approach is to accept subsequent triggers only if they are long enough after the former one.

Please be aware that some buttons bounce even when released. You might experience a trigger in such cases.

Anyway, do not base a decision on the reading of button state after its falling edge.

---

The most simple solution to read buttons is a cyclic timer with a period of several milliseconds. Take a sample each 50ms or so, and detect the button change in software. You don't even need to add debouncing code, if the period is longer than the bouncing time of your button.

Since this timer will commonly trigger an ISR, you have already an interrupt as source of the button state for other parts of your software.

If you want an extra interrupt, trigger some unused interrupt in software.
> 1 votes

# Answer
Take a look at this line in your code - the very first thing that is done in the ISR,

*int reading = digitalRead(button\_switch);*

I think that the reason that your program does not “do anything” is because you are reading the button state inside the ISR and, because you trigger an interrupt on a falling edge, you will ALWAYS see 0 as the button state at that point. It can never change when you read it once at the beginning of the ISR, it will always =0, unless you stay in the ISR and wait for a change, which is a very, very, bad idea.

The rest of your comparisons and detections all fail because within the ISR, the button read is always going to be the same and = 0, because that is how you got to the ISR. As far as your program is concerned, the button has never been pressed or depressed.

It is true, depending on how you configure the button; a single state change will be detected because of initialization, but that is besides the point.

You can test this yourself by moving all of your variables to volatile globals and then in your loop run this:

```
void loop()
{
Serial.print("lastButtonState ");
Serial.println(lastButtonState);
Serial.print("buttonState ");
Serial.println(buttonState);
Serial.print("reading ");
Serial.println(reading);
}

```

You may have other problems, but that one stuck out to me.

I would suggest that you take a look at this project and see how he is doing it - https://create.arduino.cc/projecthub/ronbentley1/button-switch-using-an-external-interrupt-7879df

He does read the switch within the interrupt \[I don't see why he needs to test the value since in his case it will always be HIGH\]. The difference is that he uses the ISR only as a trigger. The rest of the reading of the switch to detect when there are state changes, is all done outside of the ISR.

Additionally, once an interrupt occurs, a triggered status is set and, thereafter the ISR just leaves until the status is changed to "not triggered" and that is done outside of the ISR. The accompanying article explains what he is doing in the code and why.

You are doing, literally nothing \[after setup()\] outside of the ISR and I think maybe you should re-think that approach.

I tested his program and it works fine. I note that he functionally defines a "press" as pressing and releasing the button (with associated debouncing). So, you don't see the led toggle until you press and release the button. That's not always how I would like it, but it is also besides the point.

Two other points...

Keep your ISR as short as you can. I don't have any hard core evidence, but, right or wrong, yours looks longer than I would be comfortable with.

Finally, I think you would enjoy this excellent thread on millis() inside an ISR. Using millis() and micros() inside an interrupt routine

To be sure, you are NOT using delays inside an ISR and there is nothing wrong with using millis() inside an ISR, but you need to be aware of the behavior.

That's my two cents - hope it helps.
> 0 votes

---
Tags: led, interrupt, button, debounce
---


# Question
Title: Unable to acquire AP network IP and DNS on a ESP32 MCU
I'm getting the following error on serial output (verbose mode) when my smart device , ESP32 S3 tries to connect over WIFI to the Access point (AP). The SSID is `TheScientist`

Below is the serial output:

```
Connecting to TheScientist
[  1064][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 0 - WIFI_READY
[  1098][V][WiFiGeneric.cpp:338] _arduino_event_cb(): STA Started
[  1099][V][WiFiGeneric.cpp:97] set_esp_interface_ip(): Configuring Station static IP: 0.0.0.0, MASK: 0.0.0.0, GW: 0.0.0.0
[  1099][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 2 - STA_START
.......[  4750][V][WiFiGeneric.cpp:360] _arduino_event_cb(): STA Disconnected: SSID: TheScientist, BSSID: 3c:cd:5d:a7:f1:13, Reason: 15
[  4750][D][WiFiGeneric.cpp:929] _eventCallback(): Arduino Event: 5 - STA_DISCONNECTED
[  4758][W][WiFiGeneric.cpp:950] _eventCallback(): Reason: 15 - 4WAY_HANDSHAKE_TIMEOUT

```

# Answer
Although the verbose debug serial output shows a

`Reason: 15 - 4WAY_HANDSHAKE_TIMEOUT`

in reality the timeout is due to usage of a password longer than the 32 char allowed on ESP32 MCUs . In summary, any ESP32 MCU to successfully connect to any WIFI Access Point , the Network password must not exceed 32 char long.
> 1 votes

---
Tags: esp32
---


# Question
Title: ESP32 can not deep sleep longer than 35 minutes
I'm trying to get my Lilygo T5 4.7" epaper to deep sleep for 12 hours. But I only seem to be able to get about half an hour (2100s) of deepsleep on it. If I set the timer for longer, it just reboots right away.

```
#include <Arduino.h>
#include "epd_driver.h"

long DEEP_SLEEP_TIME_SEC = 43200;

void start_deep_sleep()
{
    epd_poweroff_all();
    esp_sleep_enable_timer_wakeup(1000000L * DEEP_SLEEP_TIME_SEC);
    Serial.println("Starting deep-sleep period...");
    esp_deep_sleep_start();
}

void setup() {
  Serial.begin(115200);
  Serial.println("\n- - - - - - - - -INITIALIZING - - - - - - - - - -");
  delay(10000);
  start_deep_sleep();
}

void loop() {
}

```

The sbove code gives the following:

```
16:44:25.104 -> - - - - - - - - -INITIALIZING - - - - - - - - - -

16:44:35.105 -> Starting deep-sleep period...

16:44:35.105 -> ets Jul 29 2019 12:21:46

```
* and it just continues to reboot..

Maybe I'm using the wrong variables in some way?

Regards Kasper

# Answer
So, formalizing this into an answer:

```
long DEEP_SLEEP_TIME_SEC = 43200;
// ...
esp_sleep_enable_timer_wakeup(1000000L * DEEP_SLEEP_TIME_SEC);

```

This is attempting to calculate the result of `1000000L * DEEP_SLEEP_TIME_SEC` as a `long` type because both operands are of `long` type. The language does not consider the values of the operands (your constant and variable) of an operator (the multiplication) when determining the type of the result (the product) nor what you do with the result after that (passing it to the function).

The `long` type on ESP32 has a maximum value of 2147483647 which is as you said, "about half an hour" (not quite 36 minutes) worth of microseconds.

According to the documentation `esp_sleep_enable_timer_wakeup` accepts a `uint64_t` type for its requested duration parameter. I don't know what the *valid* maximum value to call with is, but the original poster confirmed it was large enough for 12 hours.

An `unsigned long long` is 64-bit on the ESP32 and the ULL suffix on `1000000ULL` forces the compiler to only consider types which are unsigned and which are not smaller than a long long. In practice it means make me an `unsigned long long` with value 1000000. In

```
esp_sleep_enable_timer_wakeup(1000000ULL * DEEP_SLEEP_TIME_SEC);

```

the compiler follows something called the usual arithmetic conversions which will cause it to upconvert the `long` type of `DEEP_SLEEP_TIME_SEC` to match the `unsigned long long` that it is finding in `1000000ULL`. The multiplication is therefore carried out in the `unsigned long long` type, so that the calculation itself accommodates more microseconds than you'll ever know what to do with. What the actual maximum is for `esp_sleep_enable_timer_wakeup` is still unclear. If I find out I will update with that information.

---

The ESP32-Arduino support comes with a fairly complete C++ standard library. So, if you want to get fancy you can use the `<chrono>` header. Just to give an example of what that could look like in your context:

```
#include <chrono>
// ...
static constexpr auto DEEP_SLEEP_TIME = std::chrono::hours{12};
// ...
esp_sleep_enable_timer_wakeup(
    std::chrono::duration_cast<std::chrono::microseconds>(
        DEEP_SLEEP_TIME
    ).count()
);

```

I'm being very explicit in the above. But you can import parts of the chrono namespace so you don't have to name everything fully. ~~You can also import chrono literals support so that you can use time constants that look more like natural language~~(not yet apparently; there's still no C++14 support in Arduino-ESP32 I guess). You can control the underlying integer type used to represent the unit. It is also possible to create and name your own duration types to represent things like bit durations in a serial protocol etc. The default underlying type for `chrono::microseconds` is large enough for what you're doing; the default widths of underlying types are generally pretty sensible. Different time units are different `std::chrono` types so you can't mix them up accidentally. `duration_cast` is a means of explicit conversion of time units. The `.count()` in the above is just a way to get the raw integer value out of the chrono type for when you need to do that, like calling this function.

If you wrap the function with your own taking `std::chrono::milliseconds` the unit conversion could just happen automatically in the call to your own wrapper:

```
static constexpr auto DEEP_SLEEP_TIME = std::chrono::hours{12};
//...
esp_err_t esp_sleep_enable_timer_wakeup_using_chrono(
  std::chrono::milliseconds duration
) {
  return esp_sleep_enable_timer_wakeup(
    duration.count()
  );
}
// ...
esp_sleep_enable_timer_wakeup_using_chrono(DEEP_SLEEP_TIME);

```

The way durations work in chrono is such that you don't need to `duration_cast` when you're going to type that can represent the same values as the source type. The the prior example the point of the `duration_cast` was just so that `.count()` was getting a count of microseconds. In other words, the `duration_cast` performed the multiplication and would have selected a more appropriate underlying integer size. Here that conversion takes place during the call. Anyway, it may be worth some effort to learn about as it tends to help avoid problems with unit conversions and integer ranges.
> 3 votes

---
Tags: esp32, sleep, memory, deep, integer
---


# Question
Title: DS18B20 temperature sensor crashes MbedOS on Arduino Nano RP2040?
I'm trying to use the DS18B20 sensor with Arduino Nano RP2040. No matter which library I use, it seems to crash MbedOS as soon as the driver is initialized. I get 4 short and 4 long flashes of the orange LED, which as I found somewhere indicates a crash.

I was trying with DS18B20 library (https://github.com/matmunk/DS18B20) as well as the DallasTemperature one (https://github.com/milesburton/Arduino-Temperature-Control-Library).
At first I suspected that the issue is in my code as it interfaces with multiple different devices (EEPROM, SHT30, etc.) and this may interfere with 1-wire interface, but if I upload just the examples from these libraries the effect is the same.

I know that the Arduino is OK, as it can run other code absolutely fine, and the sensor is OK as well since I can read it with RaspberryPi.

Does anyone have similar problems? Is there some workaround?

# Answer
#### Test Setup

I have wired an DS18B20+ to an RPI Pico and used a 4.7K pullup. The RPI Pico is a stand-in for your board. It uses the `arduino:mbed_rp2040:pico` core rather than the `arduino:mbed_nano:nanorp2040connect` that yours does. However, I'm guessing they're similar enough.

#### Test result

When I attempt the to use the matmonk DS18B20 library you mention, I see similar pattern on the RPI Pico's onboard LED. Just the attempt to construct the object was sufficient to crash. Construction fairly directly defers to the OneWire library. So, I tried the OneWire library's own example for reading the DS18B20 (with some appropriate modification) and it crashes in the same manner. This lead to searching for and finding issues for the RP2040 on the OneWire github page resulting in (among others) this find. The answer mentions OneWireNG.

#### The OneWireNG DallasTemperature example works

Or rather, it's the library that works. But, this is the relevant example for it that works for me. I made the following modifications.

* All `Serial` usages were replaced with `SerialUSB` because I am using the USB connection for the Serial Monitor (well, a terminal emulator).
* I changed the one wire object to use GPIO28 because it was conveniently close to the pins where I was picking up 3.3V and GND: `OneWire ds(28);`

~~It seems plausible **that you could change the `#include <DS18B20.h>` usages** in the two libraries you cited to `#include "OneWireNg_CurrentPlatform.h"` and find that they then work fine. I have not tried this yet. If that's of particular interest to you I will try it and update~~(**Update below**). However, this stock example from the OneWireNG library is not crashing and is printing temperatures that rise and fall expected when I manipulate the temperature of the sensor.

#### OneWireNg with Matmunk and DallasTemperature libraries

##### In general

It turns out that it is *unnecessary* to modify libraries that were using the original OneWire, not even just changing the `#include`. Despite the OneWireNg example using `"OneWireNg_CurrentPlatform.h"` it also provides a OneWire.h header. It is sufficient to have OneWireNg installed but **the original OneWire library must uninstalled** (or moved out of the libraries directory) so as not to confuse the build process into selecting the original library based on the header name over the OneWireNg library. The OneWireNg authors may have used the OneWireNg specific header in the examples to avoid this problem wit their examples when both libraries were installed. They've gone out of their way to make it compatible with the original, but below are reports of specific tests with the two libraries mentioned anyway.

##### Matmunk library with OneWireNg

With OneWireNg installed and the original OneWire *not* installed, the Multiple example form the matmunk library worked with the same minor modifications mentioned before (I used pin 28 rather than 2 and `SerialUSB` rather than `Serial`). In case your wondering the only reason why I switched from testing Single to Multiple is the latter didn't require that I first find and enter my part's address. That example is printing temperatures that rise and fall in accordance with manipulation of the sensor.

##### DallasTemperature with OneWireNg

Likewise I tested with the Multiple example from the DallasTemperature library. Same kind of modifications to the example. I changed `ONE_WIRE_BUS` to `28` for my setup. I changed `Serial` to `SerialUSB`. And runs normally and prints expected temperatures under manipulation of the sensor.
> 2 votes

---
Tags: ds18b20, arduino-nano-rp2040
---


title: Can the WiFi on ESP8266 be disabled?

text:
# Question
Title: Can the WiFi on ESP8266 be disabled?
I've been reading a lot on the ESP8266, but still my understanding is shaky. I have the Arduino IDE running on a PC, which uses wires to connect to the ESP8266 via a USB device (Device Manager \> Ports (COM & LPT) \> Silicon Labs CP210x USB to UART Bridge). This is how I uploaded a simple sketch to the ESP8266.

It works and has been installed in the wall. I'm happy and relieved to complete the project... except for one small thing: I noticed that it shows up on WiFi. I can connect to it without a password, with a "Limited" connection. After that I'm lost, but I wonder if someone who knows what they are doing can use this unsecured connection to hack my install. I'd like to disable the WiFi, if possible, since I'm not using it.

The worst part is my learning curve now starts over. I thought that when my sketch gets uploaded, it replaces the original firmware. Now "firmware" is an elusive concept. I keep thinking that it shows up on WiFi because of something running in firmware. My sketch hasn't any code for WiFi. What am I missing/do I have wrong?

# Answer
> 5 votes
1. To turn of the WiFi add the WiFi library and use WiFi.forceSleepBegin();
2. Firmware is the program/sketch that controls a MicroController Unit aka MCU (and in this case a Arduino).

Best explanation I know is here: https://en.wikipedia.org/wiki/Firmware

# Answer
> 2 votes
Many esp8266 devices by default start in (WIFI\_AP) SoftAP mode, and will appear as a wifi network access point, waiting for stations to connect.

User code in any sketch, should explicitly set wifi to the appropriate mode. If you do not need wifi, set the wifi mode off. To do this with the Arduino IDE, an esp8266 must run a sketch at least once that includes the wifi class to use its .mode() method.

```
#include <ESP8266WiFi.h>

void setup() {
   Serial.begin(115200);
   WiFi.mode(WIFI_OFF);
   if (WiFi.getMode() == WIFI_OFF)
      Serial.println(F("\nWifi mode is WIFI_OFF, until it is explicitly changed"));
}

```

The mode setting persists across power on and restart/reset cycles, until user code specifies a different mode. So once run on the esp8266, the all the lines above (except setup()) could optionally be removed from subsequent sketches uploaded to the esp8266, and the wifi will remain off.

Firmware is lower-level software that runs between the hardware and your higher-level sketch. Many esp8266 came with Espressif firmware and a sketch installed.

If you are concerned about energy consumption, investigate waking up the esp8266 with the radio disabled, eg.

```
ESP.deepSleepInstant (microseconds, WAKE_RF_DISABLED);

```

# Answer
> 1 votes
You control WiFi using `wifi.setmode()`.

ESP8266's WiFi is very versatil. You can be a client and/or an access point. You can get your IP from DHCP or static.

Per default, ESP8266 start in STATIONAP mode (as client and access point).

This is the pertinent part from the official page

**wifi.setmode()**

Configures the WiFi mode to use. NodeMCU can run in one of four WiFi modes:

* Station mode, where the NodeMCU device joins an existing network
* Access point (AP) mode, where it creates its own network that others can join
* Station + AP mode, where it both creates its own network while at the same time being joined to another existing network
* **WiFi off**

When using the combined Station + AP mode, the same channel will be used for both networks as the radio can only listen on a single channel.

**Note**

WiFi configuration will be retained until changed even if device is turned off.

**Syntax**

```
wifi.setmode(mode[, save])

```

**Parameters**

```
mode value should be one of
    wifi.STATION for when the device is connected to a WiFi router. This is often done to give the device access to the Internet.
    wifi.SOFTAP for when the device is acting only as an access point. This will allow you to see the device in the list of WiFi networks (unless you hide the SSID, of course). In this mode your computer can connect to the device, creating a local area network. Unless you change the value, the NodeMCU device will be given a local IP address of 192.168.4.1 and assign your computer the next available IP address, such as 192.168.4.2.
    wifi.STATIONAP is the combination of wifi.STATION and wifi.SOFTAP. It allows you to create a local WiFi connection and connect to another WiFi router.
    wifi.NULLMODE changing WiFi mode to NULL_MODE will put wifi into a low power state similar to MODEM_SLEEP, provided wifi.nullmodesleep(false) has not been called.

save choose whether or not to save wifi mode to flash
    true WiFi mode configuration will be retained through power cycle. (Default)
    false WiFi mode configuration will not be retained through power cycle.

```

**Returns**

current mode after setup

**Example**

```
wifi.setmode(wifi.STATION);

```

**See also**

wifi.getmode() wifi.getdefaultmode()

# Answer
> 1 votes
This is the best solution I have found so far (source: GitHub gist)

The two functions below are all you need to toggle your WiFi chip. The huge advantage of using these is that they work without having to invoke an `ESP.deepsleep()` call.

Mind you, if you have already done an `ESP.deepsleep(time, WAKE_RF_DISABLED)` that means that your WiFi chip has been disabled altogether, and calling `WiFiOn()` will NOT be able to toggle it back ON. In this case you MUST do `ESP.deepsleep(time, WAKE_RF_DEFAULT)`. But once you WiFi chip has been re-enabled, you can call `WiFiOn()` and `WiFiOff()` as many times as you please, they will work fine.

```
extern "C" {
    #include "user_interface.h"  // Required for wifi_station_connect() to work
}

#include <Arduino.h>
#include <ESP8266WiFi.h>

#define FPM_SLEEP_MAX_TIME 0xFFFFFFF

void WiFiOn();
void WiFiOff();

//------------------------------------------------------------------------------

void setup() {

    WiFiOn();
    delay(5000);
    WiFiOff();
    delay(5000);
    ESP.deepSleep(1e6 * 10, WAKE_RF_DEFAULT); // sleep 10 seconds

}

void loop() {
    ;
}
//------------------------------------------------------------------------------

void WiFiOn() {

    wifi_fpm_do_wakeup();
    wifi_fpm_close();

    //Serial.println("Reconnecting");
    wifi_set_opmode(STATION_MODE);
    wifi_station_connect();
}

void WiFiOff() {

    //Serial.println("diconnecting client and wifi");
    //client.disconnect();
    wifi_station_disconnect();
    wifi_set_opmode(NULL_MODE);
    wifi_set_sleep_type(MODEM_SLEEP_T);
    wifi_fpm_open();
    wifi_fpm_do_sleep(FPM_SLEEP_MAX_TIME);

}

```

---
Tags: esp8266, wifi, firmware
---


# Question
Title: ESP8266 does not reconnect to WiFi on reboot
The ESP8266 is supposed to remember the WiFi credentials and reconnect to the network (if it is available) after each reboot. That was the topic of this question: Does the ESP8266 somehow remember wifi access data?

I was observing that behavior and something has changed, I think when I updated via Board Manager in the Arduino IDE. Now the credentials are lost each time that the device reboots. I've tried going back to earlier versions, but I have not been able to roll back to a version of the board definition that restores the old behavior.

Here's a sample sketch that I boiled down for testing:

```
#include <ESP8266WiFi.h>

char ssid[] = "ssid";
char pss[]  = "pass";

void setup() {
  Serial.begin(115200);

  Serial.println("Started");

  pinMode(0, OUTPUT);
  pinMode(2, OUTPUT);

  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("Not connected");
    WiFi.begin(ssid, pss);
    digitalWrite(0, LOW);
    digitalWrite(2, HIGH);
  } else {
    Serial.println("Connected");
    digitalWrite(0, HIGH);
    digitalWrite(2, LOW);
  }
}

void loop() {
  Serial.println("Loop");
  delay(500);
}

```

The first time through, of course, it hits the first case and prints "Not connected." On reboot, however, I was expecting it to hit the second case and print "Connected".

I'm programming via the Arduino IDE. The board is the Adafruit Feather HUZZAH ESP8266. I've tried `lwIP v1.4` and `lwIP v2` in version 2.4.1 of the board. I started rolling back versions from there got the same run-time results.

So, questions:

1. Is this a known bug of the ESP8266 modules in recent versions?
2. Is there some aspect of the WiFi configuration that's preserved on the board when I upload a new sketch? If so, how do I roll back?

# Answer
The esp8266 SDK libraries starts the STA connection, but it is handled by other thread then the Arduino sketch. You can wait for this connection in setup with waitForConnectResult. It returns status().

```
  if (WiFi.waitForConnectResult() != WL_CONNECTED) {
    Serial.println("Not connected");
    WiFi.begin(ssid, pss);
    digitalWrite(0, LOW);
    digitalWrite(2, HIGH);
  } else {
    Serial.println("Connected");
    digitalWrite(0, HIGH);
    digitalWrite(2, LOW);
  }

```

to connect with remembered STA mode, ssid and password, but with static IP address

```
  WiFi.setAutoConnect(false);
  IPAddress ip(192, 168, 1, 8);
  IPAddress gw(192, 168, 1, 1);
  IPAddress sn(255, 255, 255, 0);
  WiFi.config(ip, gw, sn, gw);
  WiFi.begin(); // uses remembered ssid and password
  WiFi.waitForConnectResult();

```
> 2 votes

# Answer
First, let's answer the OP's questions directly:

1. Is this a known bug?

Ans: There are and will continue to be '\[issues\]\[1\],' but as @Juraj's answer points out it was not an issue, but a sketch that wasn't waiting for the quickest method to get wifi + IP ready on an esp8266, the SDK connect.
2. Is there ... WiFi configuration that's preserved on the board when I upload a new sketch? How to roll back?

Ans: There is no 'rolling back,' only erase & write new data to flash to recover. At the time of sketch upload, 'wifi settings' can only be erased. The user would choose in the Arduino IDE menu \> Tools \> Erase Flash: Sketch + Wifi Settings. Those 'wifi settings' are typically written to flash by calling

```
WiFi.begin(ssid,passphrase);  // or,
WiFi.begin(ssid,passphrase,channel,bssid,connect);  // about 2x quicker

```

The final arg can be false; we don't even need to connect to wifi to save the settings, eg.

```
begin("ssid", "passphrase", 1, {0xA4,0xB1,0xE9,0xBC,0x6A,0x28}, false);
ESP.restart();

```

Then wait for the SDK firmware to connect.

And now let's conclude with the 'newer' information, due to changes in the Arduino esp8266 core 3.0, circa mid-2020.

Beginning in core 3.0, the SDK connect feature was disabled by default. Since then, sketches wanting to take advantage of the quick SDK connect (about 220 ms, or twice as fast as any WiFi.begin), the following line must be included early in the setup() function

```
enableWiFiAtBootTime();                // prevents shutdown of sdk connect

```
> 1 votes

---
Tags: esp8266, wifi
---


# Question
Title: Using a limit switch to stop a stepper motor
I am building a machine with a moving bed, the bed moves forward and backward. I need to incorporate a limit switch at the two ends that makes sure the motors stop at the two extremes. I have 2 stepper motors controlled by TB6600 motor drivers, I am trying to start with 1 limit switch (SS-5Gl2) and advance from there.

My 1st question is how I get the code to stop steppers if limit switch is closed?

```
// Define stepper motor connections and steps per revolution:
#define dirPin1 2
#define stepPin1 3
#define dirPin2 5
#define stepPin2 6
#define stepsPerRevolution 200 // We may need to change 200 to 1600 to get more precise control, i.e. decimals verse integers

#define Lim1 8

int direction = 0;
int remainder = 0;
long travel = 0;
long revolutions = 0;

void setup() {

  // Declare pins as output:
  pinMode(stepPin1, OUTPUT);
  pinMode(dirPin1, OUTPUT);
  pinMode(stepPin2, OUTPUT);
  pinMode(dirPin2, OUTPUT);

  pinMode(Lim1, OUTPUT);

 //Desired travel distance, in mm !!!! set here !!!!
travel = 1;
//Desired direction !!!! set here !!!! move die in(ccw) = 1, back out(cw) = 0 !!!!
//direction = 0; did not work, need boolean?

// Set the spinning direction counterclockwise (die in) or clockwise (die out):
//if(direction = 1) digitalWrite(dirPin, HIGH);
//if(direction = 0) digitalWrite(dirPin, LOW);

//set direction manually, uncomment HIGH for die in, LOW for die back out
digitalWrite(dirPin1, LOW);
digitalWrite(dirPin2, LOW);
//digitalWrite(dirPin, LOW);

digitalWrite(Lim1, OFF) //

//calculating number of revolutions based on mm input. Does not yet account for partial revs - variable is truncated, integers are default. Would need another loop to handle the remainder
//6.659 (microns) constant is based on 1.25mm acutator screw pitch, and ratio in gearbox
revolutions = 1000.00*travel/6.659;
//attempt to output the stored value to PC for debug purposes

Serial.print("revolutions = ");
Serial.print(revolutions,4);
}

void loop() {

   // Spin the stepper motor X revolutions fast, speed (delay) apparently limited by voltage. 42v should go faster?
   //Required a nested loop because integers on the UNO are limited to ~32k
  for (int i = 0; i < revolutions; i++) {

   if (digitalRead(Lim1 == OFF)){}

   else {
      for (int j = 0; j < stepsPerRevolution; j++) {
       // These four lines result in 1 revolution:
      digitalWrite(stepPin1, HIGH);
      digitalWrite(stepPin2, HIGH);
      delayMicroseconds(250);
      digitalWrite(stepPin1, LOW);
      digitalWrite(stepPin2, LOW);
      delayMicroseconds(250);
      }
  }
  }

   while(1){}

}

```

# Answer
You are doing every step manually directly via `digitalWrite()`, so the principal would be to read the limit switch pin on every iteration of the inner `for` loop in an `if` statement. What to do in that `if` statement depends on what you want to do on hitting the limit switch. Currently you have an infinite empty loop after your motor code. So if you want to stop forever in that case you can create another infinite loop there. So something like this:

```
...
for (int j = 0; j < stepsPerRevolution; j++) {
   if(!digitalRead(Lim1)){
       while(true);
   }
   // These four lines result in 1 revolution:
   digitalWrite(stepPin1, HIGH);
   digitalWrite(stepPin2, HIGH);
   delayMicroseconds(250);
   digitalWrite(stepPin1, LOW);
   digitalWrite(stepPin2, LOW);
   delayMicroseconds(250);
}

```

Note that this assumes that you have connected the limit switch to ground and use the digital pin with a pullup resistor.

---

But you have 2 other problems with your limit switch code:

1. You set your limit switch pin `Lim1` as output, though to read a switch you need to configure it as input. And if you want to use a pullup resistor (in contrast to a pulldown resistor) you can activate the internal pullup in the same step:

```
 pinMode(Lim1, INPUT_PULLUP);

```
2. When reading the limit switch inside the outer `for` loop you write

```
 if (digitalRead(Lim1 == OFF)){}

```

You are doing the equality comparison between `OFF` and the pin number and not between `OFF` and the return value of `digitalRead()`. You need to place the parentheses here correctly:

```
 if(digitalRead(Lim1) == OFF){}

```

Above I used the equivalent form

```
 if(!digitalRead(Lim1)){}

```

with the negation operator `!`

Besides that you are missing the `Serial.begin()` with the baudrate, that you want to use, in `setup()`.
> 0 votes

# Answer
```
// Define stepper motor connections and steps per revolution:
#define dirPin1 5
#define stepPin1 2
#define dirPin2 6
#define stepPin2 3
#define stepsPerRevolution 200 // We may need to change 200 to 1600 to
get more precise control, i.e. decimals verse integers'
#define limPin1 8
#define CW 0x1
#define CCW 0x0

int remainder = 0;
long travel = 0;
long revolutions = 0;
bool homed = false;

void setup() {

  // Declare pins as output:
  pinMode(stepPin1, OUTPUT);
  pinMode(dirPin1, OUTPUT);
  pinMode(stepPin2, OUTPUT);
  pinMode(dirPin2, OUTPUT);
  pinMode(limPin1, INPUT_PULLUP);

//Desired direction !!!! set here !!!! move die in(ccw) = 1, back
out(cw) = 0 !!!!
//direction = 0; did not work, need boolean?

// Set the spinning direction counterclockwise (die in) or clockwise
(die out):
//if(direction = 1) digitalWrite(dirPin, HIGH);
//if(direction = 0) digitalWrite(dirPin, LOW);

//set direction manually, uncomment HIGH for die in, LOW for die back
out
digitalWrite(dirPin1, LOW);
digitalWrite(dirPin2, LOW);

//attempt to output the stored value to PC for debug purposes
Serial.begin(9600);
Serial.println("test");
}

//main routine
void loop() {
Homing();
}

void Homing() {
     // Spin the stepper motor X revolutions fast, speed (delay)
     apparently limited by voltage. 42v should go faster?
     //Required a nested loop because integers on the UNO are limited to
     ~32k
     if (homed == false)
  {
    //calculating number of revolutions based on mm input. Does not yet
   account for partial revs - variable is truncated, integers are
default.
   Would need another loop to handle the remainder
        //6.659 (microns) constant is based on 1.25mm acutator screw
pitch,
    and ratio in gearbox
        float travel = 2.0;
        float revolutions = 1000.00*travel/6.659;
    int totalStepsOut = revolutions * stepsPerRevolution;
    Serial.print("revolutions = ");
    Serial.println(revolutions,4);
    digitalWrite(dirPin1, CCW);
    digitalWrite(dirPin2, CCW);
    for (int j = 0; j < totalStepsOut; j++) {
      bool limState = digitalRead(limPin1);
      if (limState == false)
      {
        //Limit is triggered
        HomingReturn();
        break;
    }
    else
    {
        //Limit is not triggered
        // These six lines result in 1 revolution:
      digitalWrite(stepPin1, HIGH);
      digitalWrite(stepPin2, HIGH);
      delayMicroseconds(250);
      digitalWrite(stepPin1, LOW);
      digitalWrite(stepPin2, LOW);
      delayMicroseconds(250);
      }
    }
   }
   //Add stuff here to do whatever you want after homing
   Serial.println("Homed");
}

void HomingReturn()
{
  //set direction for reverse
  digitalWrite(dirPin1, CW);
  digitalWrite(dirPin2, CW);
  Serial.println("Reversing");
  //Desired travel distance, in mm !!!! set here !!!!
  float travel = 0.1;
  float revolutions = 1000.00*travel/6.659;
  int totalStepsOut = revolutions * stepsPerRevolution;
  for (int j = 0; j < totalStepsOut; j++) {
    //drive back
    digitalWrite(stepPin1, HIGH);
    digitalWrite(stepPin2, HIGH);
    delayMicroseconds(250);
    digitalWrite(stepPin1, LOW);
    digitalWrite(stepPin2, LOW);
    delayMicroseconds(250);
      }
      homed = true;
     Serial.println("Done");
   }

```
> -1 votes

---
Tags: stepper
---


# Question
Title: how do I wire a 2nd Adafruit TMP117 board to my Adafruit Metro by I2C?
I want to connect two TMP117 boards to one Metro (ie. Arduino).

(the first, addressed at 48 the 2nd at 49.)

Should 2nd board's 4 wires simply attach (ie. solder) in parallel to 1st board's 4 wires? (ie. 3.3v, gnd, SCL, SDA)

background...
I want to read temperatures from two boards at once, in near real time and write to Windows file.

# Answer
> 0 votes
You are correct almost. Connection in parallel is correct however you cannot read both at once, the bus supports talking to only device at a time. You will need to read one then the other. I do not know for sure if it is possible to get them to synchronize conversion but I think not.

---
Tags: sensors, i2c, adafruit, temperature-sensor
---


# Question
Title: Detecting the difference between a deep sleep wake signal and a manual wake button press
When my ESP8266 wakes itself from sleep (i.e. D0 sends wake signal), I want it to do something different to when I manually wake the device with the reset button.

I have the RST and D0 pins connected, so that the device can wake itself from sleep. I also have a button connected to RST which pulls to ground when pressed, so that I can wake the device manually on demand.

How do I differentiate between the device being awoken by itself, or manually by the button press?

I tried using `system_get_rst_info`/`getResetInfoPtr` to determine what action has awoken the device, but it seems that if I reset manually after the device went to sleep, the `REASON_DEEP_SLEEP_AWAKE` reason is returned since that value is based on how the device went to sleep/reset rather than how it woke up.

Disclaimer: I already found a solution (answer below), but it's more complex than I wanted, so I am posting this question to see if anyone else can think of a simpler solution.

# Answer
I came up with two solutions (3 if you count the combo), both involve connecting the manual reset button to another pin on the MCU (so I can check if it's still pressed when starting).

**Solution 1: Generate a reset pulse**

The problem with the manual reset button is that it's only on release that it resets, since while the RST pin is held low, the MCU won't start.

To solve this, I used a positive edge monostable circuit to pulse the RST pin when the manual reset button is pressed down.

That way, I can hold the button down and the device starts. In the `setup()` function, I can check if the button is still pressed.

**Solution 2: Use an RC delay circuit**

I also considered that I could simply wake the device on button release, and use a debounce cap (very slow discharge) to see if the button was pressed in the last second (by checking the pin on the MCU to which the charged cap is connected).

**Solution 3: A combination of 1 & 2**

Combining 1 and 2, allowed me to wake the device on button down, and use the debounce cap to see if the button was pressed recently (in the last second).
> 0 votes

---
Tags: esp8266
---


# Question
Title: Program Arduino using Eclipse on Mac in 2022
I started experimenting with Arduino a few weeks ago. My host is Mac OS and I have used Eclipse for many years, so my first choice is to use an Eclipse plugin. The most recent discussion of the Eclipse/Arduino solution is years old and I have been unable to get anything working.

The marketplace has "Sloeber, the Arduino Plugin for Eclipse". When I try to install this I get these errors:

```
An error occurred while collecting items to be installed
session context was:(profile=_Applications_Eclipse_embedcpp-2022-12_Eclipse.app_Contents_Eclipse, phase=org.eclipse.equinox.internal.p2.engine.phases.Collect, operand=, action=).
No repository found containing: osgi.bundle,org.eclipse.nebula.cwt,1.1.0.202212191054
No repository found containing: osgi.bundle,org.eclipse.nebula.widgets.cdatetime,1.5.0.202212191054
No repository found containing: osgi.bundle,org.eclipse.nebula.widgets.oscilloscope,1.4.0.202212191054
No repository found containing: org.eclipse.update.feature,org.eclipse.nebula.widgets.oscilloscope.feature,1.4.0.202212191054
No repository found containing: osgi.bundle,org.eclipse.nebula.widgets.oscilloscope.source,1.4.0.202212191054

```

# Answer
> -1 votes
I am using the Sloeber plugin now. The installation required changing the nebula repository to url to "http" in the "Install New Software" as described by Juraj in the first comment.

However, connecting from Sloeber to the Arduino was unreliable at first. Following Juraj's suggestion, I've been deleting and rebuilding the serial connection after each upload or power cycle to form a connection reliably.

If I forget to reset the serial connection it sometimes forms a partial connection where the host can see serial output from the Arduino but not be able to send commands back.

---
Tags: ide, mac-os, eclipse
---


# Question
Title: Blynk app, DotStar bluetooth control code help
This is my setup:

* Arduino Mega;
* HM-10 bluetooth module (bought off ebay, so not sure if genuine but works);
* Adafruit Dotstar addressable LED strip;
* iOS Blynk app;

Hardware-wise, everything is connected and working properly.

My ultimate goal is to implement this LED strip on my car in the headlights.

At this point though, I want to have everything working off the Blynk app with 3 on/off switch buttons, linked to 3 virtual pins and not consider any code priority for the lights.

I have 3 light "shows" for the strip, which are in their own void functions.

These are:

* Daytime runing lights: DRL();
* Signal turn indicator: STI();
* Police display: POL();

This is my first serious project so consider me a first time coder. Each switch button should be linked to each light show and turn the strip on and off.

What I want more specifically: when I press the DRL switch, DRL should stay on. Only turn off when switch is pressed again. This works fine.

The STI I want to loop. but when I push the switch, it goes through the function only once. This is what I don't understand, as the `Blynk.run()` is in the void loop function, and the state of the virtual pin hasn't changed.

It's the same for the police lights, it goes through the sequence only once, but i want it looped.

So my question is: How can I loop the STI and POL functions in my code?

I have tried with a while function but i can't get out of it by reading the the virtual pin within the function.

Here is the entire code:

I am aware that the code might be messy, any input is appreciated.

```
#define BLYNK_PRINT Serial
#include <SPI.h>
#include <BlynkSimpleSerialBLE.h>

#include <Adafruit_DotStar.h>
#define NUMPIXELS 72

#include <SoftwareSerial.h>

Adafruit_DotStar strip = Adafruit_DotStar(NUMPIXELS, DOTSTAR_BRG);

// You should get Auth Token in the Blynk App.
// Go to the Project Settings (nut icon).
char auth[] = "***";

uint32_t amber = 0x020600;
uint32_t white = 0x020202;
uint32_t red = 0x000200;
uint32_t blue = 0x000002;
//SoftwareSerial SerialBLE(10, 11); // RX, TX

BLYNK_WRITE(V1)
{
  int pinValue1 = param.asInt(); // assigning incoming value from pin V1     to a variable

  if (pinValue1 == 1) {
    Serial.print("Daytime running lights: ON ");
    Serial.println(pinValue1); //showing the actual virtual pin status 0/1
    DRL();
  }
  else {
    Serial.print("Daytime running lights: OFF ");
    Serial.println(pinValue1); //showing the actual virtual pin status 0/1
    OFF();
    Serial.print("off ok"); // this just helps me check where the code has   progressed
  }
}

BLYNK_WRITE(V2)
{
  int pinValue2 = param.asInt(); // assigning incoming value from pin V2 to    a variable

  if (pinValue2 == 1) {
    Serial.print("Dynamic turn indicator: ON ");
    Serial.println(pinValue2);  //showing the actual virtual pin status 0/1
    STI();
  }
  else {
    Serial.print("Dynamic turn indicator: OFF ");
    Serial.println(pinValue2);  //showing the actual virtual pin status 0/1
    OFF();
    Serial.print("off ok"); // this just helps me check where the code has  progressed
  }
}

BLYNK_WRITE(V3)
{
  int pinValue3 = param.asInt(); // assigning incoming value from pin V3 to a variable

  if (pinValue3 == 1) {
    Serial.print("Police Lights: ON ");
    Serial.println(pinValue3);
    POL();
  }
  else {
    Serial.print("Police Lights: OFF ");
    Serial.println(pinValue3);
    OFF();
    Serial.print("off ok"); // this just helps me check where the code has progressed
  }
}

void setup()
{
  Serial.begin(9600);
  Serial1.begin(9600);
  Blynk.begin(Serial1, auth);
  Serial.println("Waiting for connections...");

  strip.begin();  //initializes the strip
  strip.show();  //turn off LEDs within NUMPIXELS
}

void OFF() {
  for (int i; i <= 72; i++) { //turn off strip
    strip.setPixelColor(i, 0);
    strip.show();
  }
}

void DRL() {
  for (int i = 0; i <= 72; i++) {
    strip.setPixelColor(i, white);
    strip.show();
    delay(4);
  }
}

void STI() {
  //turn on dinamic indicator
  for (int i = 0; i <= 72; i++) {
    strip.setPixelColor(i, amber);
    strip.show();
    delay(2);
  }

  //turn off dinamic indicator
  for (int i; i <= 72; i++) {
    strip.setPixelColor(i, 0);
  }
  delay (200);  //on condition delay
  strip.show();
  delay (500);  //off condition delay
}

void POL() {

  for (int i = 0; i <= 36; i++) {
    strip.setPixelColor(i, red);
    strip.show();
  }
  delay(30);

  for (int i = 0; i <= 36; i++) {
    strip.setPixelColor(i, 0);
    strip.show();
  }
  delay(30);

  for (int i = 0; i <= 36; i++) {
    strip.setPixelColor(i, red);
    strip.show();
  }
  delay(30);

  for (int i = 0; i <= 36; i++) {
    strip.setPixelColor(i, 0);
    strip.show();
  }
  delay(30);

  for (int i = 0; i <= 36; i++) {
    strip.setPixelColor(i, red);
    strip.show();
  }
  delay(30);

  for (int i = 0; i <= 36; i++) {
    strip.setPixelColor(i, 0);
    strip.show();
  }
  delay(30);

  for (int i = 0; i <= 36; i++) {
    strip.setPixelColor(i, red);
    strip.show();
  }
  delay(30);

  for (int i = 0; i <= 36; i++) {
    strip.setPixelColor(i, 0);
    strip.show();
  }
  delay(100);

  for (int i = 37; i <= 72; i++) {
    strip.setPixelColor(i, blue);
    strip.show();
  }
  delay(30);

  for (int i = 37; i <= 72; i++) {
    strip.setPixelColor(i, 0);
    strip.show();
  }
  delay(30);

  for (int i = 37; i <= 72; i++) {
    strip.setPixelColor(i, blue);
    strip.show();
  }
  delay(30);

  for (int i = 37; i <= 72; i++) {
    strip.setPixelColor(i, 0);
    strip.show();
  }
  delay(30);

  for (int i = 37; i <= 72; i++) {
    strip.setPixelColor(i, blue);
    strip.show();
  }
  delay(30);

  for (int i = 37; i <= 72; i++) {
    strip.setPixelColor(i, 0);
    strip.show();
  }
  delay(30);

  for (int i = 37; i <= 72; i++) {
    strip.setPixelColor(i, blue);
    strip.show();
  }
  delay(30);

  for (int i = 37; i <= 72; i++) {
    strip.setPixelColor(i, 0);
    strip.show();
  }
  delay(100);
}

void loop()
{
  Blynk.run();
}

```

# Answer
> 1 votes
The way the `BLYNK_WRITE` functions works is that they are called every time device gets an update of Virtual Pin value from the app. Now if you want to run something on a timer, or in a loop there is a function called `BlynkTimer`. You write your own function that you want to be looped and use this feature to define how often it should repeat. You can control execution by setting a boolean flag in the `BLYNK_WRITE` function and checking it in the loop, or by running the `enable()` and `disable()` methods of the timer. So, you can do something like this (without libraries, just an example):

```
BlynkTimer timer; // create timer

void myTimer()
{
    // toggle an LED
    static bool state;
    digitalWrite(13, state);
    state = !state;
}

void setup()
{
  Blynk.begin(details); // blynk start
  timer.setInterval(1000L, myTimer); // repeat the timer once a second
  pinMode(13, OUTPUT); // LED pin as output
}

void loop()
{
  Blynk.run(); // Runs all Blynk stuff
  timer.run(); // runs BlynkTimer
}

```

You can read more about it in the docs.

---
Tags: led, adafruit, blynk
---


# Question
Title: DHT11 temperature and humidity sensor Code
I’ve got a question about this code I’ve attached please- would it be correct to read it like this:

After defining the variable temperature and humidity and assigning measurement\_timestamp to millis(), an if loop first checks if millis - measurement\_timestamp \< 3000 unsigned long (which doesn’t make sense to me) and then if a reading is read, it = true?

Is this correct please?

Code:

```
static bool measure_environment( float *temperature, float *humidity )
{
  static unsigned long measurement_timestamp = millis( );

  /* Measure once every four seconds. */
  if( millis( ) - measurement_timestamp > 3000ul )
  {
    if( dht_sensor.measure( temperature, humidity ) == true )
    {
      measurement_timestamp = millis( );
      return( true );
    }
  }

  return( false );
}

```

# Answer
> 0 votes
The code makes sense to me.

The comment, "/\* Measure once every four seconds. \*/" is inaccurate. It will measure when the time since last measurement is greater than 3 seconds.

Because of "static unsigned long measurement\_timestamp = millis( );", measurement\_timestamp will NOT be initialized to millis() every time the function is called - it will only be initialized once for lifetime of the run. Otherwise, it would never cause a measurement since "if( millis( ) - measurement\_timestamp \> 3000ul )" would never be true.

The first call sets measurement\_timestamp= to millis() \[the current timer value\] and the function returns "false"

When called 3.001 sec later, "if( millis( ) - measurement\_timestamp \> 3000ul )" is true - a measurement is taken, "measurement\_timestamp" is updated to the current millis() and the function returns "true".

It returns "false" for the next 3 seconds and then "true" and so on.

see https://stackoverflow.com/questions/5033627/static-variable-inside-of-a-function-in-c

and How can I handle the millis() rollover?

Hope it helps.

---
Tags: arduino-uno, sketch, code-review, temperature-sensor, dht11
---


# Question
Title: Attach the Arduino ISR function to the class member
I intended to use a timer interrupt for limited instances of a class in my ESP32 Arduino project. My first code conception was as follows:

```
portEXIT_CRITICAL_ISR(&lock0);
  }

hw_timer_t * timer[3] = NULL;

class myClass
{
private:
  irqCallback timmerCB;
  volatile uint8_t pri_var1, pri_var2;
  static uint8_t class_instance_number=0;
public:
  volatile uint8_t pub_var1, pub_var2;
  myClass(/* args */);
  ~myClass();
};

myClass::myClass(/* args */)
{
  timmerCB=timerCBArray[class_instance_number];
  timer[class_instance_number] = timerBegin(0, 80, true);
  timerAttachInterrupt(timer[class_instance_number], &timmerCB, true);
  class_instance_number++;
}

```

However, I have this problem: how can I change the variable inside the interrupt regarding to each class instance?

# Answer
> 1 votes
I know this is a late answer but I think it is possible to get past the problem with `this` if you use std::bind

Below is a full example but if that is too long to read, here is a minimal example.

```
class ISRClass {
public:
  ISRClass(int pin) : pin_(pin) {}

  void setup() {
    attachInterrupt(digitalPinToInterrupt(pin_),
                    std::bind(&ISRClass::sensePinIsr, this), CHANGE);
  }

private:
  int pin_;
  ulong change_count_;

  void IRAM_ATTR sensePinIsr() {
    // do something fast and simple
    change_count_++;
  }
};

```

The function returned by std::bind takes no arguments, not even this.

Full example follows.

Here is an example. This class uses interrupts to simply set the state of a member variable when the pin has changed state. It then only reads the pin when needed. You still have to query the member variable in the loop, but that is about 10x faster than reading the pin.

```
#include "FunctionalInterrupt.h"
#include <Arduino.h>
#include <functional>
#include <optional>
#include <map>

using CallBack = std::function<void(int)>;

class PinMonitor {
public:
  PinMonitor(int pin) : pin_(pin) {}
  void setup() {
    pin_state_ = digitalRead(pin_);
    attachInterrupt(digitalPinToInterrupt(pin_),
                    std::bind(&PinMonitor::sensePinIsr, this), CHANGE);
  }

  std::optional<int> CheckForNewPinState(int debounce) {
    if (!change_count_) {
      return std::nullopt;
    }
    if (!is_debouncing_) {
      is_debouncing_ = true;
      debouce_started_at_ = millis();
      return std::nullopt;
    }
    auto time = millis();
    if (time < debouce_started_at_) {
      debouce_started_at_ = time; // handle timer rollover
    }
    if (time < debouce_started_at_ + debounce) {
      return std::nullopt;
    }
    is_debouncing_ = false;
    change_count_ = 0;
    auto new_pin_state = digitalRead(pin_);
    if (new_pin_state != pin_state_) {
      pin_state_ = new_pin_state;
      for ( auto [_, callback] : callbacks_) {
        callback(pin_state_);
      }
      return pin_state_;
    }
    return std::nullopt;
  }

  int DoIfPinStateChanges(const CallBack callback) {
    next_callback_key++;
    callbacks_[next_callback_key] = callback;
    return next_callback_key;
  }

  void RemoveCallback(int key) {
    callbacks_.erase(key);
  }

private:
  int pin_;
  int pin_state_;
  ulong change_count_;
  CallBack callback_ = [](auto a) {};
  std::map<int, CallBack> callbacks_;
  bool is_debouncing_{};
  ulong debouce_started_at_{};
  int next_callback_key{};

  void IRAM_ATTR sensePinIsr() { change_count_++; }
};

```

Then in main you could use this like below.

```
NetworkConnection networkConnection;
OTAHandler otaHandler;
Logger &logger = Logger::getInstance();

PinMonitor pinMonitor1(16);
PinMonitor pinMonitor2(17);

int call_count{};
int removable_callback{};

void setup() {
  networkConnection.connect();
  logger.startWebSocket();
  otaHandler.setup();
  pinMode(16, INPUT_PULLDOWN);
  pinMode(17, INPUT_PULLDOWN);
  auto chip_id = ESP.getEfuseMac();

  pinMonitor1.setup();
  pinMonitor1.DoIfPinStateChanges([chip_id](auto a) {
    logger.infoln("chip %12llx, reports pin1 new state of %d detected", chip_id, a);
  });
  removable_callback = pinMonitor1.DoIfPinStateChanges([chip_id](auto a) {
    logger.infoln("second call back was called on pin1, new state of %d detected", a);
    call_count++;
  });
  pinMonitor2.setup();
  pinMonitor2.DoIfPinStateChanges([chip_id](auto a) {
    logger.infoln("chip %12llx, reports pin2 new state of %d detected", chip_id, a);
  });

}

void loop() {
  otaHandler.loop();
  logger.checkWebSocket();

  std::optional<int> new_pin_state1;
  std::optional<int> new_pin_state2;

  new_pin_state1 = pinMonitor1.CheckForNewPinState(100);
  if (new_pin_state1) {logger.infoln("new pin1 state is %d", *new_pin_state1);}

  new_pin_state2 = pinMonitor2.CheckForNewPinState(100);
  if (new_pin_state2) {logger.infoln("new pin2 state is %d", *new_pin_state2);}

  if (call_count > 5) {pinMonitor1.RemoveCallback(removable_callback);}

}

```

# Answer
> 12 votes
# Interrupt Service Routine (ISR) outside a class

Let's consider a simple use of interrupts:

```
volatile bool switchChanged;

void switchPressed ()
  {
  switchChanged = true;
  }  // end of switchPressed

void setup ()
  {
  pinMode (2, INPUT_PULLUP);
  attachInterrupt (0, switchPressed, CHANGE);
  }  // end of setup

void loop ()
  {
  // whatever
  }  // end of loop

```

That compiles fine.

---

# ISR inside a class as a class function (method)

Now let's imagine we want to put the interrupt handling into a class for our convenience.

```
class myClass
  {
  volatile bool switchChanged;

  public:

  void begin ()
    {
    pinMode (2, INPUT_PULLUP);
    attachInterrupt (0, switchPressed, CHANGE);   // <--- line 10
    }  // end of myClass::begin

  void switchPressed ()
    {
    switchChanged = true;
    }  // end of myClass::switchPressed

  };  // end of class myClass

myClass foo;  // make an instance of myClass

void setup ()
  {
  foo.begin ();
  }  // end of setup

void loop ()
  {
  // whatever
  }  // end of loop

```

That does **not** compile:

```
ISR_in_class_test.ino: In member function ‘void myClass::begin()’:
ISR_in_class_test:10: error: argument of type ‘void (myClass::)()’ does not match ‘void (*)()’

```

What is going on here?

ISRs have to be static functions, taking no arguments. However (non-static) class functions have an implied **this-\>** pointer which points to the particular *instance* of the class.

For example, if we have two instances:

```
myClass foo;
myClass bar;

```

If we call foo.begin() then **this-\>** points to "foo", and if we call bar.begin then **this-\>** points to "bar".

However an ISR, when fired by the processor, cannot know whether **this-\>** is "foo" or "bar" or something else. Thus the compiler cannot compile that line.

---

# ISR inside a class as a static class function

We can try to work around this by making the class function **static**. Doing that means that the function is not tied to any particular instance, and thus the attachInterrupt line will compile.

```
class myClass
  {
  volatile bool switchChanged;   // <--- line 3

  public:

  void begin ()
    {
    pinMode (2, INPUT_PULLUP);
    attachInterrupt (0, switchPressed, CHANGE);
    }  // end of myClass::begin

  static void switchPressed ()
    {
    switchChanged = true;   // <--- line 15
    }  // end of myClass::switchPressed

  };  // end of class myClass

myClass foo;  // make an instance of myClass

void setup ()
  {
  foo.begin ();
  }  // end of setup

void loop ()
  {
  // whatever
  }  // end of loop

```

However now we have a different problem:

```
ISR_in_class_test.ino: In static member function ‘static void myClass::switchPressed()’:
ISR_in_class_test:3: error: invalid use of member ‘myClass::switchChanged’ in static member function
ISR_in_class_test:15: error: from this location

```

A non-static class variable cannot be called from a static class function. Why? Because the compiler doesn't know *which* variable you want. Is it foo.switchChanged or bar.switchChanged?

---

# ISR inside a class as a static class function with static variables

To make the static function work, it can only access static variables. So we can make switchChanged static. Plus we need to define an instance of this static variable.

```
class myClass
  {
  static volatile bool switchChanged;  // declare

  public:

  void begin ()
    {
    pinMode (2, INPUT_PULLUP);
    attachInterrupt (0, switchPressed, CHANGE);
    }  // end of myClass::begin

  static void switchPressed ()
    {
    switchChanged = true;
    }  // end of myClass::switchPressed

  };  // end of class myClass

volatile bool myClass::switchChanged;  // define

myClass foo;  // make an instance of myClass

void setup ()
  {
  foo.begin ();
  }  // end of setup

void loop ()
  {
  // whatever
  }  // end of loop

```

Now the class compiles. However we have thrown away most of the advantages of having a class in the first place, as we are forced to use a static function, and that static function can only access static variables.

---

# Glue routines

To work around this problem we can write short "glue" routines. These are functions that interface between an ISR and an **instance** of a class.

```
class myClass
  {
  volatile bool switchChanged;

  static myClass * instances [2];

  static void switchPressedExt0 ()
    {
    if (myClass::instances [0] != NULL)
      myClass::instances [0]->switchPressed ();
    }  // end of myClass::switchPressedExt0

  static void switchPressedExt1 ()
    {
    if (myClass::instances [1] != NULL)
      myClass::instances [1]->switchPressed ();
    }  // end of myClass::switchPressedExt1

  public:

  void begin (const byte whichPin)
    {
    pinMode (whichPin, INPUT_PULLUP);
    switch (whichPin)
      {
      case 2:
        attachInterrupt (0, switchPressedExt0, CHANGE);
        instances [0] = this;
        break;

      case 3:
        attachInterrupt (1, switchPressedExt1, CHANGE);
        instances [1] = this;
        break;

      } // end of switch
    }  // end of myClass::begin

  void switchPressed ()
    {
    switchChanged = true;
    }

  };  // end of class myClass

myClass * myClass::instances [2] = { NULL, NULL };

// instances of our class
myClass foo;
myClass bar;

void setup ()
  {
  foo.begin (2);   // pin D2
  bar.begin (3);   // pin D3
  }  // end of setup

void loop ()
  {
  // whatever
  }  // end of loop

```

This is a bit fiddly, however what it is doing is remembering in an array which instance of the class is associated with which interrupt. The "glue" routines switchPressedExt0 and switchPressedExt1 call the appropriate instance of the switchPressed function by using the remembered class pointer.

Now the non-static function switchPressed can access non-static class variables.

---

This answer was reproduced from my forum, but I can't post link-only answers here.

---
Tags: esp32, interrupt, timers, class
---