# Question
Title: Why do I need to connect a potentiometer to a +5V source in 1602 tutorial?
I'm getting starter with the Arduino and I'm going through the classic tutorial, it says to connect the VO to a pot's wiper, and pot itself to +5v and ground, so I did.

I tuned it and it worked, but then I thought, why do I need a pot if I don't plan to move it from this value? Why don't I just replace it with a resistor? But I'm still very new to electronics, Ohm's law, etc, and I got confused.

Why do I need to connect a potentiometer to +5v source, if the resistor between VO and the GND will do? I unplugged the +5v from the pot and it still worked. I had to adjust the position, but it works just fine without it, so my question is, if it works without, why all tutorials tell us to connect it to a +5v?

# Answer
VO controls the contrast of the LCD, which is quite temperature-dependent.

You can get away with a fixed resistor between VO and GND because of the built-in resistor between VCC and VO, producing a voltage divider.

In many cases you can also use a direct connection between VO and GND, resulting in a voltage of zero at VO.

However, be aware that the contrast changes if the ambient temperature leaves your "comfy zone". Depending on where you want to use the LCD, you need to check that out "in the field."
> 1 votes

# Answer
> why do I need to connect, a potentiometer to +5v source, if the resistor between VO and the GND will do?

That's because the 1602 board itself (usually) has a resistor between VO and VCC. So to make voltage divider you only need to add one extra resistor from VO to Gnd (ca. 1-2K probably).

So, yes, you can go with only one resistor (or with the pot disconnected from VCC) if the board already has another resistor pre-soldered.
> 0 votes

---
Tags: potentiometer
---


# Question
Title: Which WiFi module is able to host a custom web UI and can act as a station and AP?
I am an Arduino beginner. I am searching for a WiFi module that can act as a station and access point. It should have a external antenna.

I need to upload a script that starts a web server, so I can host my custom UI where the user can enter his WLAN credentials. After the credentials are set, it should try to connect to that WLAN like in the example script below, from a YouTube video.

I've tried the ESP8266, but uploading my script also overwrote the firmware.

Someone suggested the "Wemos D1 mini", but it does not have a big antenna and the one that I found with an antenna on Amazon has really really bad reviews.

```
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
ESP8266WebServer server(80);

struct settings {
  char ssid[30];
  char password[30];
} user_wifi = {};

void setup() {
  EEPROM.begin(sizeof(struct settings) );
  EEPROM.get(0, user_wifi);
  WiFi.mode(WIFI_STA);
  WiFi.begin(user_wifi.ssid, user_wifi.password);
  byte tries = 0;
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    if (tries++ > 30) {
      WiFi.mode(WIFI_AP);
      WiFi.softAP("Setup Portal", "mrdiy.ca");
      break;
    }
  }
  server.on("/", handlePortal);
  server.begin();
}

void loop() {
  server.handleClient();
}

void handlePortal() {
  if (server.method() == HTTP_POST) {
    strncpy(user_wifi.ssid, server.arg("ssid").c_str(), sizeof(user_wifi.ssid));
    strncpy(user_wifi.password, server.arg("password").c_str(), sizeof(user_wifi.password));
    user_wifi.ssid[server.arg("ssid").length()] = user_wifi.password[server.arg("password").length()] = '\0';
    EEPROM.put(0, user_wifi);
    EEPROM.commit();
    server.send(200, "text/html", "<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'><title>Wifi Setup</title><style>*,::after,::before{box-sizing:border-box;}body{margin:0;font-family:'Segoe UI',Roboto,'Helvetica Neue',Arial,'Noto Sans','Liberation Sans';font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#f5f5f5;}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);border:1px solid #ced4da;}button{border:1px solid transparent;color:#fff;background-color:#007bff;border-color:#007bff;padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem;width:100%}.form-signin{width:100%;max-width:400px;padding:15px;margin:auto;}h1,p{text-align: center}</style> </head> <body><main class='form-signin'> <h1>Wifi Setup</h1> <br/> <p>Your settings have been saved successfully!<br />Please restart the device.</p></main></body></html>" );
  } else {
    server.send(200, "text/html", "<!doctype html><html lang='en'><head><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'><title>Wifi Setup</title> <style>*,::after,::before{box-sizing:border-box;}body{margin:0;font-family:'Segoe UI',Roboto,'Helvetica Neue',Arial,'Noto Sans','Liberation Sans';font-size:1rem;font-weight:400;line-height:1.5;color:#212529;background-color:#f5f5f5;}.form-control{display:block;width:100%;height:calc(1.5em + .75rem + 2px);border:1px solid #ced4da;}button{cursor: pointer;border:1px solid transparent;color:#fff;background-color:#007bff;border-color:#007bff;padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem;width:100%}.form-signin{width:100%;max-width:400px;padding:15px;margin:auto;}h1{text-align: center}</style> </head> <body><main class='form-signin'> <form action='/' method='post'> <h1 class=''>Wifi Setup</h1><br/><div class='form-floating'><label>SSID</label><input type='text' class='form-control' name='ssid'> </div><div class='form-floating'><br/><label>Password</label><input type='password' class='form-control' name='password'></div><br/><br/><button type='submit'>Save</button><p style='text-align: right'><a href='https://www.mrdiy.ca' style='color: #32C5FF'>mrdiy.ca</a></p></form></main> </body></html>" );
  }
}

```

# Answer
I will try to answer some of the misunderstandings, though this is not really a full answer, since I currently don't know what the exact problem is.

First some terms:

* **Firmware**: A firmware is a program running on a device. What we refer to as firmware is commonly the result of the compiled code, so a program in machine language. (Though sometimes people talk about firmware and mean the firmware code, which is not compiled at that time). In the world of microcontrollers, a firmware runs directly on a microcontroller's hardware (as opposed to a PC, where there is an OS in between). Thus only 1 firmware can run on the microcontroller at any given time. You cannot upload multiple firmwares to a microcontroller at the same time. Thus uploading a new one will overwrite the previous one.
* **Sketch**: This is the Arduino term for your programs code. When you compile your sketch, the compiler will translate your code into assembler and then into machine language. The resulting file can then be uploaded to the microcontroller, where it overwrites the previous content of the flash memory.
* **SDK**: A Software Development Kit is a set of code that provides easy to use access to important functionalities. The Arduino framework itself can be called a SDK. It provides easy to use access for commonly used microcontroller functionality, like digital and analog inputs. If you program an ESP through the Arduino IDE, it will have a copy of the ESP SDK, which gives easier access to complex things like Wifi handling. This ESP SDK comes from the company that makes the ESPs: Espressif. If someone wants to write a program for an ESP, like a firmware that speaks AT commands over serial that someone will get the ESP SDK from Espressif.
* **AT firmware**: The AT firmware is not really different to your own program that you write/compile in the Arduino IDE. It uses the ESP SDK to handle things like Wifi. And it listens on the ESPs serial port for AT commands and reacts to them. You can write your own AT firmware, if you want to. This type of firmware is to provide simpler microcontrollers without Wifi hardware a network connection through Wifi. The simple microcontroller can send AT commands over serial to control the Wifi connection. Though this is of course inefficient and limited. It can be good to incorporate Wifi into an existing project, though when you try to do more complex stuff it is often better to ditch the simpler microcontroller and do everything directly on the ESP with your own custom firmware. No need for AT commands then.

> why the WiFi and AT commands are not working anymore after uploading a sketch

As described above, you have created and uploaded your own firmware, by writing a sketch for the ESP in the Arduino IDE and uploading it. That program overwrites the one that was previously saved on the ESP. Thus the AT firmware is gone now. If you want to restore it, I'm sure you can find a fitting one for your board online and upload that again.

Though from what you have written so far it seems to me that you don't really need an AT firmware. Programming a custom portal for Wifi credentials is something where you need your own firmware anyway. You are now sitting at the place of the AT firmware with your own program. You can tell it what to do by writing the corresponding code.

> Which WiFi module is able to host a custom web UI and can act as a station and AP?

Every ESP board is capable of that as far as I know. Though I haven't seen an AT firmware yet that would support setting up a custom web UI. So you would need to write your own firmware.

> Someone suggested the "Wemos D1 mini", but it does not have a big antenna and the one that I found with an antenna on Amazon has really really bad reviews.

Most ESP boards are not made with an external antenna as a priority. Even the ones with a socket for an external antenna might require some work to set them from internal to external antenna (like desoldering a resistor). You might be able to find one that doesn't require this. The Wemos D1 mini pro unfortunately requires the desoldering of the 0Ohm resistor. That is set by the manufacturer, so any reseller would do the work themselves. Not many will do that.

I cannot help you with finding such a board, as I don't know one (didn't need an external antenna yet). I would also just Google it.

And for better quality you need to go to reputable sources. Amazon is often just as a bad gamble as directly buying from China, AliExpress or similar. If you buy there, expect the quality to be rather low and some boards might even be broken. Sometimes that is OK, sometimes not.

Note: I'm really not an expert in RF, but honestly I would be surprised if connecting the antenna via a multiple meter-long cable wouldn't impact signal strength significantly. The ESP Wifi hardware is probably not built for driving the charge into such a long antenna. If this is the case it might be better to rethink your project design. You could put the ESP on top of the well (easily in reach of the Wifi network) and connect whatever you have down in the well from there. Or you could use another microcontroller handling the things in the well, connecting to the ESP via cable. Or if you absolutely need Wifi to connect down there, you might use an extender at the top of the well (can also be done by an ESP, if you don't want a full Wifi extender there). This very much depends on your exact requirements, so only a few ideas here.

---

Also I've noticed that in your `handlePortal()` function you are copying the SSID and password into the `user_Wifi` struct, but you are not actually changing the Wifi status. I would expect the code to do a change from AP to Station mode or reconnecting to the new Wifi.

You are saving the credentials into EEPROM (which is just flash on an ESP) and retrieving it on the next startup. I've made that error many times myself: when the expected Wifi network from the ESP doesn't show up, are you sure that there are no valid Wifi credentials saved from your previous run?

---

I hope that this helps you a bit.
> 2 votes

---
Tags: wifi
---


title: ESP8266 I2C doesn't respond

text:
# Question
Title: ESP8266 I2C doesn't respond
I've got a huge problem using an ESP8266 from a Wemos D1 mini.

I am using an STM32L073RZ to send a request to an ESP8266 to send me the time it has acquired from a WiFi connection.

ESP code:

```
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <Wire.h>

// Zastąp poniższe wartości danymi Twojej sieci WiFi
const char* ssid = ":>>>>>>>>>>>";
const char* password = "NOPE";

#define Status D4

WiFiUDP ntpUDP;
NTPClient timeClient(ntpUDP, "europe.pool.ntp.org", 3600, 60000);

void setup() {
  pinMode(D4, OUTPUT); // Pin odpowiedzialny za gotowość urządzenia do komunikacji przez I2C
  digitalWrite(D4, LOW); // Ustawiamy go na początku na "0".

  Serial.begin(9600);
  WiFi.begin(ssid, password);

  while ( WiFi.status() != WL_CONNECTED ) {
    delay ( 500 );
    Serial.print ( "." );
  }

  timeClient.begin();

  Wire.begin(8); // Odpala D2 jako SDA i D1 jako SCL jako slave
  Wire.onRequest(sendTime); // Rejestracja funkcji zwrotnej, która zostanie wywołana, gdy urządzenie nadrzędne zażąda danych

  digitalWrite(D4, HIGH); // Tutaj konfiguracja z siecią jak i też konfiguracja z pinami i ustawienie I2C jest zakończone
                          // i sygnalizowane stanem wysokim na D4
}

void loop() {
  timeClient.update();
  Serial.println(timeClient.getFormattedTime());
  delay(1000);
}

void sendTime() {
  String time = timeClient.getFormattedTime();
  Wire.write(time.c_str()); // Wysłanie czasu do urządzenia nadrzędnego będzie to razem 8 bajtów bo jeszcze znaki oddzielające.
}

```

STM simple code :

```
xD = HAL_I2C_Master_Receive(&hi2c2, 0x08<<1, czas, 8, 1000);

```

I tried everything. Adding pull ups, changing the GPIO speeds, etc.

Here is what my logic analyser gave me:

Still nothing. I've also checked which pins are the SDA and SCL which are D2 and D1. I've checked is the connection was correct and it should be, but still no response.

I don't know anymore what is wrong or what is not.

Schematic :

It is already powered so I didn't need to add 5 V and G pins to it, but I also tested with adding G pin to ground of the STM32, and also with 5 V to the 5 V of the STM32, but it still didn't work.

# Answer
> 5 votes
I2C slave doesn't work on esp8266 because it doesn't have hardware support for I2C.

From the esp8266 Wire library documentation:

> Wire library currently supports master mode ...

https://arduino-esp8266.readthedocs.io/en/3.1.2/libraries.html#i2c-wire-library

# Answer
> 2 votes
The devices must share a common ground, and also you may need 4.7k pull-up resistors on both SDA and SCL.

See http://gammon.com.au/i2c for a whole lot more detail.

# Answer
> 0 votes
Both other answers are correct, but there is yet another problem with your design.

As can be clearly seen from the logic analyzer output, the master runs its clock at 100kb/sec (the standard i2c rate). That leaves the slave ca. 5us to react. For a 80 MHz chip this might be quite a tight timing by itself and requires thorough inspection and optimization of code. But a UDP request into the big net? Seriously?!

The only way it *might* work is if both parties could agree on a clock stretching. However, the i2c spec never defines precise timings for the stretched clock, so, the chances are, any master would probably break the session assuming the slave hung up long before the UDP request could be served.

Therefore, such requests need to be split into two parts. First, the master asks the slave to initiate the process of measurement/exchange/computation/whatever. And second, after an appropriate delay, the master issues another request for the buffered result from slave.

---
Tags: esp8266
---


# Question
Title: I cannot resolve the errors with attachInterrupt() (error: invalid use of non-static member function)
I have been trying for days to get this code to work, but I cannot find a solution. Can anyone please help me find what I am missing here?

**My sketch:**

```
#include <ControlInterrupt.h>

const int commonPin = 2;
const int buttonPins[] = {4,5,6,7,8};

ControlInterrupt controlInterrupt(commonPin, buttonPins);

void setup() {
  // put your setup code here, to run once:
  controlInterrupt.configureCommon(); // Setup pins for  interrupt
  attachInterrupt(digitalPinToInterrupt(commonPin), controlInterrupt.pressInterrupt, FALLING);
  controlInterrupt.begin();
}

void loop() {
  // Empty
}

```

**.h**

```
/*
  ControlInterrupt.h - A library for handling multiple buttons using a single interrupt.
                       This is done by using an interrupt pin effectively as the ground,
                       eliminating the need for additional components.
  Authors: Svizel_pritula 05/05/2019
           TheCodeGeek 01/13/2024
*/
#ifndef ControlInterrupt_h
#define ControlInterrupt_h
#include <Arduino.h>
class ControlInterrupt
{
public:
  ControlInterrupt(int commonPin, int buttonPins[]);
  void begin();
  void pressInterrupt();
  void configureCommon();
  void configureDistinct();
  //void getCommand(int button);

private:
  int _commonPin;
  //int _arrLength;
  int _buttonPins[];
};
#endif

```

**.cpp**

```
/*
  ControlInterrupt.h - A library for handling multiple buttons using a single interrupt.
                       This is done by using an interrupt pin effectively as the ground,
                       eliminating the need for additional components.
  Authors: Svizel_pritula 05/05/2019
           TheCodeGeek 01/13/2024
*/
#include <Arduino.h>
#include <ControlInterrupt.h>

unsigned long lastFire;

ControlInterrupt::ControlInterrupt(int commonPin, int buttonPins[])
//, String commandOrder[]
{
  _commonPin = commonPin;
  _arrLength = sizeof(buttonPins);
  _buttonPins[arrLength] = buttonPins;
  //_commandOrder[arrLength] = commandOrder;

  lastFire = 0;
}

void ControlInterrupt::begin()
{
  Serial.begin(9600);
}

void ControlInterrupt::pressInterrupt()
{ // ISR
  if (millis() - lastFire < 200)
  { // Debounce
    return;
  }
  lastFire = millis();

  configureDistinct(); // Setup  pins for testing individual buttons

  for (int i = 0; i < (sizeof(buttonPins)/sizeof(int)); i++)
  { // Test each button for press
    if (!digitalRead(_buttonPins[i]))
    {
      getCommand(i);
    }
  }

  configureCommon(); // Return to original  state
}

void ControlInterrupt::configureCommon()
{
  pinMode(_commonPin, INPUT_PULLUP);

  for (int i = 0; i < (sizeof(buttonPins)/sizeof(int)); i++)
  {
    pinMode(_buttonPins[i], OUTPUT);
    digitalWrite(_buttonPins[i], LOW);
  }
}

void ControlInterrupt::configureDistinct()
{
  pinMode(_commonPin, OUTPUT);
  digitalWrite(_commonPin, LOW);

  for (int i = 0; i < (sizeof(buttonPins)/sizeof(int)); i++)
  {
    pinMode(_buttonPins[i], INPUT_PULLUP);
  }
}

void ControlInterrupt::getCommand(int button)
//String ControlInterrupt::getCommand(int button)
{               // Our handler
  String label; // This stores the string to print when controls are used

  Serial.print(button + 1);
  Serial.print(": ");
  switch (button)
  {
  case 0:
    // statement
    label = "UP";
    break;
  case 1:
    // statement
    label = "DOWN";
    break;
  case 2:
    // statement
    label = "CLEAR";
    break;
  case 3:
    // statement
    label = "BACK";
    break;
  case 4:
    // statement
    label = "LEFT";
    break;
  case 5:
    // statement
    label = "ENTER";
    break;
  case 6:
    // statement
    label = "RIGHT";
    break;
  default:
    // statement
    break;
  }
  Serial.print(label);
  //return label;
}

```

# Answer
> 1 votes
The solution is covered in my post here.

However, to avoid link-only answers, the brief explanation is that a class method has a hidden argument "this" which refers to the instance of the class. In your case the instance is `controlInterrupt`.

If you are only ever going to have one instance of the class you could replace it with a namespace. Otherwise, make yourself a glue routine as described in that post.

# Answer
> 0 votes
Member functions (here `pressInterrupt`) in C++ are special. You cannot use them where normal function is required (`atrachInterruot`). For explanations refer to any good book on C++.

In this particular case, just strip all C++ specific constructs off. They make your code neither shorter nor understandable. All the data for ISR is global anyway.

---
Tags: arduino-uno, arduino-ide, interrupt, compilation-errors
---


title: ESP8266 - server.write() doesn't work

text:
# Question
Title: ESP8266 - server.write() doesn't work
I have a few Arduinos with ESPs, one of which is set up to measure temperature, and I'd like to have it send commands to other connected Arduinos to perform various functions (open/close valves, etc.)

In other words, the only communication I need is for one server to hopefully broadcast to a few clients. I don't really need to discern which client is which.

I am struggling to make sense of some of the documentation, and this is new to me.

For starters, I'm just trying to get any communication at all functional before I do the next parts.

My server device is set up as a soft access point and a WiFiServer.

My server ESP code looks something like this:

```
#include <ESP8266WiFi.h>

const byte ON = 2;
const byte OFF = 1;
int message;

WiFiServer server(80);

void setup()
{
  Serial.begin(9600);
  WiFi.softAP("access", "point");
  server.begin();
}

void loop()
{
  server.write(ON);
  delay(1000);
  server.write(OFF);
  delay(1000);
}

```

My client code looks something like this:

```
#include <ESP8266WiFi.h>

const byte ON = 2;
const byte OFF = 1;
byte ip[] = {192, 168, 4, 1};
WiFiClient client;
int message;

void setup() {
  Serial.begin(9600);
  // connect to wifi.
  WiFi.mode(WIFI_STA);
  WiFi.begin("access", "point");

  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
  }

  // Now connect to server object
  if (client.connect(ip, 80) == 1) {
    Serial.println("Connected");
  } else {
    Serial.println("Not Connected");
  }
  delay(10000);
}

void loop() {
  if (client.connected()) {
    Serial.println("Waiting for Data");
    while(!client.available()) {}
    String message = client.read();
    Serial.println(message);
  }
}

```

The client code does connect to the server, and gets stuck at the "Waiting for Data" line, never getting out of the `while (!client.available)` loop, so it clearly isn't receiving anything.

I'm sure I must look super dumb here, can someone help me understand the proper way to do what I want to do? Stuff I have seen online is primarily client -\> server communication. Do I HAVE to ping the server with something in order to receive something back?

# Answer
> 1 votes
In esp8266 Arduino before version 3, `write` was not implemented in WiFiServer.

In version 3 WiFiServer doesn't have `write`, but class ArduinoWiFiServer implements the print-to-all-clients functionality.

Documentation link: https://arduino-esp8266.readthedocs.io/en/3.1.2/esp8266wifi/server-class.html

I am the author of `ArduinoWiFiServer` class.

---
Tags: arduino-uno, esp8266, wifi
---


# Question
Title: Is std::string (and ephemeral memory allocation in general) safe to use for ESP32 Arduino?
I know that with 8-bit AVR Arduinos, std::string is widely regarded as fundamentally unsafe due to problems with heap-fragmentation (partly due to limited ram, and partly because the WAY the underlying Arduino/AVR library *manages* memory is more simplistic than something constantly allocating and releasing a **lot** of ephemeral allocations **needs** to be).

Does the same caution apply to ESP32 (when used with Arduino framework), or does its underlying memory-management scheme (presumably based upon esp-idf) on a module with ~468k of RAM do a sufficiently better job of minimizing fragmentation and memory leaks to make std::string (and other similar classes) safe to use without having to constantly obsess about the problem and bend over backwards to avoid ephemeral memory allocations?

I'm particularly worried that the various libraries for making http requests (potentially involving the creation and destruction of multiple String objects for every request and response) might be more indifferent to the problem than they really *ought* to be.

I know there are a few Arduino libraries floating around that basically allocate one single big chunk of memory for a reusable String-like object (and additional space to give it "working room" to do things like regular expressions, substring replacements, etc), but it looks like the popular ESP32 libraries for making http requests just create and destroy new String objects for every request and act like it's totally fine. Am I being paranoid, or is ephemeral memory allocation still a real problem, even on platforms like ESP32?

By the same token... is there some way (on an ESP32 Arduino program) to periodically ask the library to *analyze* the current memory situation, and deliberately initiate a software-reboot (and re-initialization) if the memory appears to be getting overly fragmented (vs waiting for a slow memory leak to just fester until a memory allocation fails and the whole program crashes uncontrollably)?

# Answer
> 0 votes
The problem with randomly allocating and deallocating memory in embedded systems (ES) is that most are meant to be able to run error free, essentially forever. Since memory space is finite, an ES that employs the technique don't meet that constraint.

To the question in your last paragraph, apparently you are considering a system that can tolerate occasional resets with the consequent loss of context. In that case, perhaps resetting anytime an allocation fails would be acceptable. You'd want to vet each request especially carefully to to ensure it could usually be expected to succeed (define "usually"!), to avoid the large overhead of too-frequent resets or worse, a continuous reset loop resulting from an impossible-to-fill request.

Consider the difference between a human-driven question-and-answer system on one hand, and an implanted cardiac pacemaker or an jet fighter auto-landing system for night carrier-landings. An occasional timing hiccup might be annoy me in the Q&A system (if I can even notice it), but in either of the other two, it might spoil someone's whole day. :-(

# Answer
> 0 votes
If this is any interest to you, I did a web server library that doesn't use String class at all.

It decodes incoming HTTP requests an item at a time, passing the results to a callback routine.

That way it doesn't even have to hold the whole HTTP request in memory, it decodes the individual parameters "on the fly" using a state machine.

Since there is no dynamic memory allocation you should, in theory, be able to run indefinitely.

My library is at https://github.com/nickgammon/HTTPserver

Having said that, and to answer your actual question ...

---

Dynamic memory allocation doesn't *have* to be bad. In particular, if the amounts of memory being allocated are always the same (for example, your input to your server is predictable) then it shouldn't run out of memory. The allocator will just keep serving up the same bit of memory you used last time.

What can be particularly bad is this sort of stuff:

```
String buf = "";
while (Serial.available ())
  {
  buf += Serial.read ();  // append one byte
  }

```

That sort of code is likely to cause heap fragmentation because it keeps allocating larger and larger chunks. Whether the library you mention does that, I don't know.

---

Note, it isn't totally clear from your question if you are writing an HTTP *client* or *server*. If you are writing a client then my library won't be any help, but my remarks above should still apply.

---
Tags: esp32, memory, heap
---


# Question
Title: Arduino used as a multi-sensor controller, then sending results to a laptop to post on a web page
We are a non-profit group, we run a salmon rearing pond on Lake Michigan. We are upgrading our equipment that is over 50 years old with monitoring sensors.

We are adding 9 temperature, 5 oxygen, 2 air temperature, and several voltage and current sensors, hooked up to an Arduino. We need to have the results sent to a laptop to post them on a web page, and record values in a log.

We also want to do some simple calculations so that when a value drops below a preset level an output (valve) is activated until the level is corrected. We also want a built-in timer to control different equipment.

The high-voltage part (240 V, 480 V) is my area of expertise. Any help or ideas?

# Answer
Tackle this project as any other, divide and conquer.

First learn how to provide a static web site with the Arduino. There are multiple options, so please do your research and follow the inevitable tutorials. Experiment with a few to choose the best alternative for your case.

Then add dynamic values, for example with a simple time-controlled algorithm that alters a value. You can mock up monitored values, it does not matter. Make sure the dynamic web site works as you wish. You have also multiple options here.

Then add a first sensor to measure. First use the raw value to make sure you have the correct results. Only if you succeed, continue to scale the raw value to the physical value you want to use.

Finally extend with the next sensors, one after the other, in the same manner.

Doing "baby steps" helps you identify gaps in your knowledge (fill them) and avoids complex debugging. Never try to develop all at once. To accelerate the development, you can distribute the separate independent steps to multiple developers. This is how professionals do it, and there is no reason why it will not work for you.
> 2 votes

---
Tags: sensors, datalogging
---


# Question
Title: I can't get my interrupt to control my project
I'm building a programmable resin pump for my resin printer. The goal is to enable the ability to set a print volume and print time provided by the printer. The pump would then pump at full speed to cover the build plate, then continue at a lower speed to continue filling until the print is complete. Other features would be added as needed.

The problem that I'm having is that I can control everything by typing the commands on the keyboard, but the interrupt is not working as expected. Only the Left and Right commands work, but Up and Enter do nothing, and Down freezes the Arduino. The other buttons aren't connected yet.

When commands are issued via keyboard or button successfully, the command is printed to the serial terminal, e.g. `#CMD# UP`.

When using the buttons, Up and Enter (center) do nothing. Left and Right function properly, but Down begins to return the command and freezes the Arduino at `#C` so that even the keyboard commands do nothing.

I've tried changing the byte values of Up, Down, Left, Right, and Enter to a number of different values, but no change was noted.

What is going wrong?

**Sketch**

```
/*
 Sub Menu

 https://lcdmenu.forntoh.dev/examples/submenu

*/

#include <ItemSubMenu.h>
#include <ItemInput.h>
#include <LcdMenu.h>
#include <utils/commandProccesors.h>

// #define is an alternate way of declaring a const
#define LCD_ROWS 4
#define LCD_COLS 20

#define COMMONPIN 2
const byte BUTTONPINS[] = {4,5,6,7,8,9,10,11};

// Define commands as
#define UP 56        // NUMPAD 8
#define DOWN 50      // NUMPAD 2
#define LEFT 52      // NUMPAD 4
#define RIGHT 54     // NUMPAD 6
#define ENTER 53     // NUMPAD 5
#define BACK 55      // NUMPAD 7
#define BACKSPACE 8  // BACKSPACE
#define CLEAR 46     // NUMPAD .

// Assigned based on the hardware directional buttons
const byte commandOrder[8] = {UP,DOWN,LEFT,RIGHT,ENTER,BACK,BACKSPACE,CLEAR};

unsigned long lastFire = 0;

extern MenuItem* autofillMenu[];
extern MenuItem* semiautofillMenu[];
extern MenuItem* manualfillMenu[];
extern MenuItem* selfcleanMenu[];
extern MenuItem* settingsMenu[];

#define CHARSET_SIZE 10
// Charset used for entering values
char charset[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
// Active index of the charset
int8_t charsetPosition = -1;

// Declare the call back functions
void setVolumeCallback(char* value);
void setTimeCallback(char* value);

// Define the main menu
MAIN_MENU(
  ITEM_BASIC("   *Resin Pump*"),
  ITEM_SUBMENU("Auto Fill", autofillMenu),
  ITEM_SUBMENU("Semi-Auto Fill", semiautofillMenu),
  ITEM_SUBMENU("Manual Pump", manualfillMenu),
  ITEM_SUBMENU("Self-Clean", selfcleanMenu),
  ITEM_SUBMENU("Settings", settingsMenu));
// Auto-Fill
SUB_MENU(autofillMenu, mainMenu,
         ITEM_BASIC("   *Auto Fill*"),
         ITEM_INPUT("Set Vol. (ml)", "200", setVolumeCallback),  // defalt to 200 ml (create a variable)
         ITEM_INPUT("Set Time", "hh:mm", setTimeCallback),       // default to 0's (create a variable)
         ITEM_BASIC("Start")                                     // Execute the command to fill the vat over the specified time span.
);
// Semi-Auto Fill
SUB_MENU(semiautofillMenu, mainMenu,
         ITEM_BASIC(" *Semi-Auto Fill*"),
         ITEM_INPUT("Set Vol. (ml)", "200", setVolumeCallback),  // defalt to 200 ml (create a variable)
         ITEM_BASIC("Start")                                     // Execute the command to fill the vat immediately.
);
// Manual Control
SUB_MENU(manualfillMenu, mainMenu,
         ITEM_BASIC(" *Manual Control*"),
         ITEM_BASIC("Fill (out)"),  // Execute the command to pump out,
         ITEM_BASIC("Empty (in)"),  // Execute the command to pump in,
         ITEM_BASIC("Stop")         // Execute the command to stop pumping.
);
// Self-Clean
SUB_MENU(selfcleanMenu, mainMenu,
         ITEM_BASIC("   *Self-Clean*"),
         ITEM_BASIC("Start"));
// Settings
SUB_MENU(settingsMenu, mainMenu,
         ITEM_BASIC("    *Settings*"),
         ITEM_BASIC("Max Volume of Vat"),
         ITEM_BASIC("Default Volume"),
         ITEM_BASIC("Resin Amount"),
         ITEM_BASIC("Resin Overhead"),
         ITEM_BASIC("Pump Flow Rate"),
         ITEM_BASIC("Default Pump Speed"),
         ITEM_BASIC("Print Start Delay"));

LcdMenu menu(LCD_ROWS, LCD_COLS);

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

  menu.setupLcdWithMenu(0x27, mainMenu);
  attachInterrupt(digitalPinToInterrupt(COMMONPIN), pressInterrupt,  FALLING);
}

void loop() {
  // TODO: Remove check for serial and Serial.read, these will be replaced
  // by physical buttons and an interrupt.
  if (!Serial.available())
    return;
  char command = Serial.read();
  processMenuCommand(menu, command, charsetPosition, charset, CHARSET_SIZE, UP, DOWN, ENTER, BACK, CLEAR, BACKSPACE, LEFT, RIGHT);
}
/**
 * Define callbacks
 */
// setVolume is share with both Auto Fill and Semi-Auto Fill

void setVolumeCallback(char* value) {
  // Do stuff with value
  Serial.print(F("# "));
  Serial.println(value);
}

void setTimeCallback(char* value) {
  // Do stuff with value
  Serial.print(F("# "));
  Serial.println(value);
}

/*** Begin Button Interrupt Watching ***/

void pressInterrupt() { // ISR
  if (millis() - lastFire < 200) { // Debounce
    return;
  }
  lastFire = millis();

  configureDistinct(); // Setup pins for testing individual buttons

  for (int i = 0; i < sizeof(BUTTONPINS) / sizeof(int); i++) { // Test each button for press
    if (!digitalRead(BUTTONPINS[i])) {
      execCommand(i);
    }
  }
  configureCommon(); // Return to original state
}

void configureCommon() {
  pinMode(COMMONPIN, INPUT_PULLUP);
  for (int i = 0; i < sizeof(BUTTONPINS) / sizeof(int); i++) {
    pinMode(BUTTONPINS[i], OUTPUT);
    digitalWrite(BUTTONPINS[i], LOW);
  }
}

void configureDistinct() {
  pinMode(COMMONPIN, OUTPUT);
  digitalWrite(COMMONPIN, LOW);
  for (int i = 0; i < sizeof(BUTTONPINS) / sizeof(int); i++) {
    pinMode(BUTTONPINS[i], INPUT_PULLUP);
  }
}

void execCommand(int buttonIndex) {
  byte command =  commandOrder[buttonIndex];

  processMenuCommand(menu, command, charsetPosition, charset, CHARSET_SIZE, UP, DOWN, ENTER, BACK, CLEAR, BACKSPACE, LEFT, RIGHT);
}

```

Schematic

# Answer
> 1 votes
I resolved my problem by following the guidance that I linked to from digikey. My solution:

```
/*
 Sub Menu

 https://lcdmenu.forntoh.dev/examples/submenu

*/

#include <ItemSubMenu.h>
#include <ItemInput.h>
#include <LcdMenu.h>
#include <utils/commandProccesors.h>

// #define is an alternate way of declaring a const
#define LCD_ROWS 4
#define LCD_COLS 20
#define DELAY 100
#define COMMONPIN 2
const byte BUTTONPINS[] = {4,5,6,7,8,9,10,11};
// Used to indicate if the interrupt was triggered
// If not, set to -1.
int8_t interruptTrigger = -1;

// Define commands as
#define UP 56        // NUMPAD 8
#define DOWN 50      // NUMPAD 2
#define LEFT 52      // NUMPAD 4
#define RIGHT 54     // NUMPAD 6
#define ENTER 53     // NUMPAD 5
#define BACK 55      // NUMPAD 7
#define BACKSPACE 8  // BACKSPACE
#define CLEAR 46     // NUMPAD .

// Assigned based on the hardware directional buttons
const byte commandOrder[] = {UP,DOWN,LEFT,RIGHT,ENTER,BACK,CLEAR,BACKSPACE};

unsigned long lastFire = 0;

extern MenuItem* autofillMenu[];
extern MenuItem* semiautofillMenu[];
extern MenuItem* manualfillMenu[];
extern MenuItem* selfcleanMenu[];
extern MenuItem* settingsMenu[];

#define CHARSET_SIZE 10
// Charset used for entering values
char charset[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
// Active index of the charset
int8_t charsetPosition = -1;

// Declare the call back functions
void setVolumeCallback(char* value);
void setTimeCallback(char* value);

// Define the main menu
MAIN_MENU(
  ITEM_BASIC("   *Resin Pump*"),
  ITEM_SUBMENU("Auto Fill", autofillMenu),
  ITEM_SUBMENU("Semi-Auto Fill", semiautofillMenu),
  ITEM_SUBMENU("Manual Pump", manualfillMenu),
  ITEM_SUBMENU("Self-Clean", selfcleanMenu),
  ITEM_SUBMENU("Settings", settingsMenu));
// Auto-Fill
SUB_MENU(autofillMenu, mainMenu,
         ITEM_BASIC("   *Auto Fill*"),
         ITEM_INPUT("Set Vol. (ml)", "200", setVolumeCallback),  // defalt to 200 ml (create a variable)
         ITEM_INPUT("Set Time", "hh:mm", setTimeCallback),       // default to 0's (create a variable)
         ITEM_BASIC("Start")                                     // Execute the command to fill the vat over the specified time span.
);
// Semi-Auto Fill
SUB_MENU(semiautofillMenu, mainMenu,
         ITEM_BASIC(" *Semi-Auto Fill*"),
         ITEM_INPUT("Set Vol. (ml)", "200", setVolumeCallback),  // defalt to 200 ml (create a variable)
         ITEM_BASIC("Start")                                     // Execute the command to fill the vat immediately.
);
// Manual Control
SUB_MENU(manualfillMenu, mainMenu,
         ITEM_BASIC(" *Manual Control*"),
         ITEM_BASIC("Fill (out)"),  // Execute the command to pump out,
         ITEM_BASIC("Empty (in)"),  // Execute the command to pump in,
         ITEM_BASIC("Stop")         // Execute the command to stop pumping.
);
// Self-Clean
SUB_MENU(selfcleanMenu, mainMenu,
         ITEM_BASIC("   *Self-Clean*"),
         ITEM_BASIC("Start"));
// Settings
SUB_MENU(settingsMenu, mainMenu,
         ITEM_BASIC("    *Settings*"),
         ITEM_BASIC("Max Volume of Vat"),
         ITEM_BASIC("Default Volume"),
         ITEM_BASIC("Resin Amount"),
         ITEM_BASIC("Resin Overhead"),
         ITEM_BASIC("Pump Flow Rate"),
         ITEM_BASIC("Default Pump Speed"),
         ITEM_BASIC("Print Start Delay"));

LcdMenu menu(LCD_ROWS, LCD_COLS);

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

  menu.setupLcdWithMenu(0x27, mainMenu);
  attachInterrupt(digitalPinToInterrupt(COMMONPIN), pressInterrupt,  FALLING);
}

void loop() {
  // TODO: Remove check for serial and Serial.read, these will be replaced
  //       by physical buttons and an interrupt.
  //if (!Serial.available()) return;
  //char command = Serial.read();

  // If the trigger was set, ignore the serial command and redefine command to use the button command
  if (interruptTrigger > -1) {
    char command = commandOrder[interruptTrigger];
    processMenuCommand(menu, command, charsetPosition, charset, CHARSET_SIZE,
                      UP, DOWN, ENTER, BACK, CLEAR, BACKSPACE, LEFT, RIGHT);
    interruptTrigger = -1;  // Now that the trigger has been read, reset the trigger
  }

}
/**
 * Define callbacks
 */
// setVolume is share with both Auto Fill and Semi-Auto Fill
void setVolumeCallback(char* value) {
  // Do stuff with value
  Serial.print(F("# "));
  Serial.println(value);
}
void setTimeCallback(char* value) {
  // Do stuff with value
  Serial.print(F("# "));
  Serial.println(value);
}

/*** Begin Button Interrupt Watching ***/

void pressInterrupt()
{ // ISR
  if (millis() - lastFire < DELAY) { // Debounce
    return;
  }
  lastFire = millis();

  configureDistinct(); // Setup pins for testing individual buttons
  for (int i = 0; i < sizeof(BUTTONPINS); i++)
  { // Test each button for press
    if (!digitalRead(BUTTONPINS[i]))
    {
      interruptTrigger = i; // Set the interrupt trigger (reset elsewhere)
      /* NOT PICKING UP 4 (ENTER) */
    }
  }
  configureCommon(); // Return to original state
}

void configureCommon()
{
  pinMode(COMMONPIN, INPUT_PULLUP);
  for (int i = 0; i < sizeof(BUTTONPINS); i++)
  {
    pinMode(BUTTONPINS[i], OUTPUT);
    digitalWrite(BUTTONPINS[i], LOW);
  }
}

void configureDistinct()
{
  pinMode(COMMONPIN, OUTPUT);
  digitalWrite(COMMONPIN, LOW);
  for (int i = 0; i < sizeof(BUTTONPINS); i++)
  {
    pinMode(BUTTONPINS[i], INPUT_PULLUP);
  }
}

```

I also had to adjust my loops which were only allowing me to check 4 inputs. by excluding `/ sizeof(int)` I was able to detect all of the button presses. I'm sharing in case someone else runs into a similar situation.

# Answer
> 2 votes
You are probably doing too much in your interrupt routine (ISR). See my answer here about using interrupts.

I also have material on my web site which may be helpful.

A comment you linked to https://www.digikey.com/en/maker/tutorials/2022/the-dos-and-donts-of-using-arduino-interrupts which sensibly suggests merely setting a flag in the interrupt routine, and then doing something in your main loop if the flag is set.

Alternatively, as another comment suggested, just detect button presses in the main loop. You will iterate through it fast enough that this should be sufficient.

---
Tags: arduino-uno, interrupt
---


# Question
Title: ArduinoJson- unexplained '.size()' behaviour
I'm using an ESP8266, ArduinoJson v6.21.5. PlatformIO, and Vscode.

The config file is saved and read correctly to the ESP8266's flash:

```
{
    "gen_pubTopic": [
        "DvirHome/Messages",
        "DvirHome/log",
        "DvirHome/debug"
    ],
    "subTopic": [
        "DvirHome/Light/int/KitchenLEDs",
        "DvirHome/All",
        "DvirHome/Light/int",
        "DvirHome/Light"
    ],
    "pubTopic": [
        "DvirHome/Light/int/KitchenLEDs/Avail",
        "DvirHome/Light/int/KitchenLEDs/State"
    ]
}

```

In case the config file is missing, `t[]`, `t2[]`, and `t3[]` are hard-coded to avoid boot failure.

There are 3 `for` loops to read topics (from the config file or from hard-coded).

The unexplained part is:

1. The config file is saved and read as expected, and the code runs without any failure.
2. when the `for` loop contains `DOC["gen_pubTopic"].size()` topics are read as expected, but when it is defined as `DOC["gen_pubTopic"].size() |3` it crashes.
3. `x`, `x2`, and `x3` are defined before each `for` loop. Again, once as defined in 2), still no change.
4. At some part it happened at the 3rd `for` loop, and sometimes at the 2nd `for` loop.
5. Printing `xi` values shows the expected values.
6. To make it clear - without using "fallback annotation" `|2` the code runs without crashing.
7. In all stages above - the fallback stage is not tested (meaning- config file is saved and read), and crashes happens when "fallback annotation" is added.

Appreciate any lead why.

```
void start_iot2(JsonDocument &DOC)
{

  /* Default values */
  const char *t[] = {"DvirHome/Messages", "DvirHome/log", "DvirHome/debug"};
  const char *t2[] = {"DvirHome/Device", "DvirHome/All"};
  const char *t3[] = {"DvirHome/Device/Avail", "DvirHome/Device/State"};

  uint8_t x = DOC["gen_pubTopic"].size() | 3;
  for (uint8_t i = 0; i < x; i++)
  {
    /* Some Code */
    /* debug code */
    Serial.print("Gen_");
    Serial.println(i);
  }

  uint8_t x2 = DOC["subTopic"].size() | 2;
  for (uint8_t i = 0; i < x2; i++)
  {
    /* Some Code */
    /* debug code */
    Serial.print("Sub_");
    Serial.println(i);
  }

  uint8_t x3 = DOC["pubTopic"].size() | 2;
  for (uint8_t i = 0; i < x3; i++)
  {
    /* Some Code */
    /* debug code */
    Serial.print("Pub_");
    Serial.println(i);
  }

  iot.start_services(extMQTT);
}

```

**Edit\_1** Added `/*debug Code */` to for loops. At some point, the Serial Monitor showed:

```
Sub_0
Sub_1
Sub_2
Sub_3
Sub_4
Sub_5

```

while the size of the `SubTopic` array is **4** and `|2` as default.

# Answer
> 4 votes
The "fallback notation" you're referring to is `JsonVariant::operator|` and only works for `JsonVariant`.

In your program, you apply `|` to an `unsigned long`, so the built-in bitwise "or" operator applies. It performs a bit-by-bit "or" operation (for example `2 | 4` is `6`).

In other words, `doc["key"] | 4` behaves as you describe, but not `doc.size() | 4`.

Unlike many other languages, you cannot use logical "or" (`||`) either because it returns a boolean. So, if you want to make minimal changes to your code, you'll have to do something like this:

```
uint8_t x = DOC["gen_pubTopic"].size();
if (!x) x = 3;

```

---
Tags: json
---


title: RTC DS1307 module not working

text:
# Question
Title: RTC DS1307 module not working
I have a problem with an RTC: it doesn't work correctly. When I try to read date or time, I only see the characters "àààààààà" or or the program is blocked.

```
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RTClib.h>

DS1307 rtc;
DateTime rtcTime;

void setup() {
  //zainicjowanie generatora liczb losowych
  randomSeed(A0);
  Wire.begin();
  rtc.begin();
}

void loop() {
  rtcTime = rtc.now();
  int mm = rtcTime.minute();
  Serial.println(mm);

  delay(500);
}

```

This function blocks my program:

```
String timeRTC(){
  DateTime now = rtc.now();
  String Hour = "0" + String(now.hour());
  String Minutes = "0" + String(now.minute());
  String Seconds = "0" + String(now.second());

  if (Hour.length() == 3) {
    Hour = Hour.substring(1);
  }
  if (Minutes.length() == 3) {
    Minutes = Minutes.substring(1);
  }
  if (Seconds.length() == 3) {
    Seconds = Seconds.substring(1);
  }

  return Hour+":"+Minutes+":"+Seconds;
}

```

I can't find what is causing this problem. I use an Arduino Uno in the WOKWI emulator.

```
String dateRTC(){
  DateTime now = rtc.now();
  String Day = "0" + String(now.day());
  String Month = "0" + String(now.month());
  String Year = String(now.year());

  if (Day.length() == 3) {
    Day = Day.substring(1);
  }
  if (Month.length() == 3) {
    Month = Month.substring(1);
  }

  return Day+"-"+Month+"-"+Year;
}

String timeRTC(){
  DateTime now = rtc.now();
  String Hour = "0" + String(now.hour());
  String Minutes = "0" + String(now.minute());
  String Seconds = "0" + String(now.second());

  if (Hour.length() == 3) {
    Hour = Hour.substring(1);
  }
  if (Minutes.length() == 3) {
    Minutes = Minutes.substring(1);
  }
  if (Seconds.length() == 3) {
    Seconds = Seconds.substring(1);
  }

  return Hour+":"+Minutes+":"+Seconds;
}

```

# Answer
You don't have a problem with the RTC: if you call `Serial.begin()` in `setup()`, and remove everything in your `loop()` after `Serial.println(mm);`, the program works as expected. Your problem is elsewhere. Most likely you are corrupting memory, which leads to unpredictable behavior.

Side note: if you switch to Adafruit's RTClib (called simply “RTClib” in the library manager), you can simplify `timeRTC()` to this:

```
String timeRTC() {
    return rtc.now().timestamp(DateTime::TIMESTAMP_TIME);
}

```
> 2 votes

---
Tags: arduino-uno, rtc
---


# Question
Title: Arduino OTA - difference between Arduino IDE and PlatformIO
I used to OTA sketches and data files using the `ArduinoOTA` and `ESP32 sketch data Upload` tools and the ESP32 devkit, for example when using the Arduino IDE.

**Versions:**

* Arduino IDE- 1.8.19
* ESP32 on Arduino IDE - 1.0.6
* ArduinoOTA 1.0.9

**Moving to PlatformIO-**

Started a new project using the ESP32 devkit, `ArduinoOTA` was added from the registry to the project (also 1.0.9 as on Arduino IDE).

And now - when I tried to copy the `BasicOTA` example from the Arduino IDE's `ArduinoOTA` example, the compilation failed.

To make sure I don't have any library/version issues, I checked:

1. `WiFi`, `ESPmDNS` is part of `builtin`.
2. `WiFiUdp` was not found not in`builtin`,`installed` nor `registry` \- not shown in dependency graph during compilation -- ??
3. `ArduinoOTA` used from registry.

> ```
> #include <WiFi.h>
> #include <ESPmDNS.h>
> #include <WiFiUdp.h>
> #include <ArduinoOTA.h>
>
> const char* ssid = "dvirz_iot";
> const char* password = "GdSd13100301";
>
> void setup() {
>   Serial.begin(115200);
>   Serial.println("Booting");
>   WiFi.mode(WIFI_STA);
>   WiFi.begin(ssid, password);
>   while (WiFi.waitForConnectResult() != WL_CONNECTED) {
>     Serial.println("Connection Failed! Rebooting...");
>     delay(5000);
>     ESP.restart();
>   }
>
>   // Port defaults to 3232
>   // ArduinoOTA.setPort(3232);
>
>   // Hostname defaults to esp3232-[MAC]
>   // ArduinoOTA.setHostname("myesp32");
>
>   // No authentication by default
>   // ArduinoOTA.setPassword("admin");
>
>   // Password can be set with it's md5 value as well
>   // MD5(admin) = 21232f297a57a5a743894a0e4a801fc3
>   // ArduinoOTA.setPasswordHash("21232f297a57a5a743894a0e4a801fc3");
>
>   ArduinoOTA
>     .onStart([]() {
>       String type;
>       if (ArduinoOTA.getCommand() == U_FLASH)
>         type = "sketch";
>       else // U_SPIFFS
>         type = "filesystem";
>
>       // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
>       Serial.println("Start updating " + type);
>     })
>     .onEnd([]() {
>       Serial.println("\nEnd");
>     })
>     .onProgress([](unsigned int progress, unsigned int total) {
>       Serial.printf("Progress: %u%%\r", (progress / (total / 100)));
>     })
>     .onError([](ota_error_t error) {
>       Serial.printf("Error[%u]: ", error);
>       if (error == OTA_AUTH_ERROR) Serial.println("Auth Failed");
>       else if (error == OTA_BEGIN_ERROR) Serial.println("Begin Failed");
>       else if (error == OTA_CONNECT_ERROR) Serial.println("Connect Failed");
>       else if (error == OTA_RECEIVE_ERROR) Serial.println("Receive Failed");
>       else if (error == OTA_END_ERROR) Serial.println("End Failed");
>     });
>
>   ArduinoOTA.begin();
>
>   Serial.println("Ready");
>   Serial.print("IP address: ");
>   Serial.println(WiFi.localIP());
> }
>
> void loop() {
>   ArduinoOTA.handle();
> }
>
> ```

and got this compilation error:

```
 *  Executing task in folder esp32_OTA_from_scratch: platformio run --environment esp32dev

Processing esp32dev (platform: espressif32; board: esp32dev; framework: arduino)
-------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32dev.html
PLATFORM: Espressif 32 (6.1.0) > Espressif ESP32 Dev Module
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
DEBUG: Current (cmsis-dap) External (cmsis-dap, esp-bridge, esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
PACKAGES:
 - framework-arduinoespressif32 @ 3.20007.0 (2.0.7)
 - tool-esptoolpy @ 1.40500.0 (4.5.0)
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 34 compatible libraries
Scanning dependencies...
Dependency Graph
|-- ArduinoOTA @ 1.0.9
|-- ESPmDNS @ 2.0.0
|-- WiFi @ 2.0.0
Building in release mode
Compiling .pio/build/esp32dev/src/main.cpp.o
Building .pio/build/esp32dev/bootloader.bin
Generating partitions .pio/build/esp32dev/partitions.bin
esptool.py v4.5
Creating esp32 image...
Merged 1 ELF section
Successfully created esp32 image.
Compiling .pio/build/esp32dev/lib8f4/Update/HttpsOTAUpdate.cpp.o
Compiling .pio/build/esp32dev/lib8f4/Update/Updater.cpp.o
Compiling .pio/build/esp32dev/liba9d/WiFi/WiFi.cpp.o
Compiling .pio/build/esp32dev/liba9d/WiFi/WiFiAP.cpp.o
Compiling .pio/build/esp32dev/liba9d/WiFi/WiFiClient.cpp.o
Compiling .pio/build/esp32dev/liba9d/WiFi/WiFiGeneric.cpp.o
Archiving .pio/build/esp32dev/lib8f4/libUpdate.a
Indexing .pio/build/esp32dev/lib8f4/libUpdate.a
Compiling .pio/build/esp32dev/liba9d/WiFi/WiFiMulti.cpp.o
Compiling .pio/build/esp32dev/liba9d/WiFi/WiFiSTA.cpp.o
Compiling .pio/build/esp32dev/liba9d/WiFi/WiFiScan.cpp.o
Compiling .pio/build/esp32dev/liba9d/WiFi/WiFiServer.cpp.o
Compiling .pio/build/esp32dev/liba9d/WiFi/WiFiUdp.cpp.o
Compiling .pio/build/esp32dev/libc4b/FS/FS.cpp.o
Compiling .pio/build/esp32dev/libc4b/FS/vfs_api.cpp.o
Compiling .pio/build/esp32dev/lib263/SPI/SPI.cpp.o
Archiving .pio/build/esp32dev/liba9d/libWiFi.a
Indexing .pio/build/esp32dev/liba9d/libWiFi.a
Compiling .pio/build/esp32dev/lib494/SD/SD.cpp.o
Archiving .pio/build/esp32dev/lib263/libSPI.a
Indexing .pio/build/esp32dev/lib263/libSPI.a
Compiling .pio/build/esp32dev/lib494/SD/sd_diskio.cpp.o
Compiling .pio/build/esp32dev/lib494/SD/sd_diskio_crc.c.o
Compiling .pio/build/esp32dev/lib624/ArduinoOTA/InternalStorage.cpp.o
Archiving .pio/build/esp32dev/libc4b/libFS.a
Indexing .pio/build/esp32dev/libc4b/libFS.a
Compiling .pio/build/esp32dev/lib624/ArduinoOTA/InternalStorageAVR.cpp.o
Compiling .pio/build/esp32dev/lib624/ArduinoOTA/InternalStorageESP.cpp.o
Compiling .pio/build/esp32dev/lib624/ArduinoOTA/InternalStorageRP2.cpp.o
Archiving .pio/build/esp32dev/lib494/libSD.a
Indexing .pio/build/esp32dev/lib494/libSD.a
Compiling .pio/build/esp32dev/lib624/ArduinoOTA/InternalStorageSTM32.cpp.o
Compiling .pio/build/esp32dev/lib624/ArduinoOTA/OTAStorage.cpp.o
Compiling .pio/build/esp32dev/lib624/ArduinoOTA/WiFiOTA.cpp.o
Compiling .pio/build/esp32dev/lib624/ArduinoOTA/utility/rp2_flash_boot.c.o
Compiling .pio/build/esp32dev/lib624/ArduinoOTA/utility/stm32_flash_boot.c.o
Compiling .pio/build/esp32dev/lib6d4/ESPmDNS/ESPmDNS.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/Esp.cpp.o
Archiving .pio/build/esp32dev/lib624/libArduinoOTA.a
Compiling .pio/build/esp32dev/FrameworkArduino/FirmwareMSC.cpp.o
Indexing .pio/build/esp32dev/lib624/libArduinoOTA.a
Compiling .pio/build/esp32dev/FrameworkArduino/FunctionalInterrupt.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/HWCDC.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/HardwareSerial.cpp.o
Archiving .pio/build/esp32dev/lib6d4/libESPmDNS.a
Indexing .pio/build/esp32dev/lib6d4/libESPmDNS.a
Compiling .pio/build/esp32dev/FrameworkArduino/IPAddress.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/IPv6Address.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/MD5Builder.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/Print.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/Stream.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/StreamString.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/Tone.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/USB.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/USBCDC.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/USBMSC.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/WMath.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/WString.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/base64.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/cbuf.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-adc.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-bt.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-cpu.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-dac.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-gpio.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-i2c-slave.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-i2c.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-ledc.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-matrix.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-misc.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-psram.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-rgb-led.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-rmt.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-sigmadelta.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-spi.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-time.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-timer.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-tinyusb.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-touch.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/esp32-hal-uart.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/firmware_msc_fat.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/libb64/cdecode.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/libb64/cencode.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/main.cpp.o
Compiling .pio/build/esp32dev/FrameworkArduino/stdlib_noniso.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/wiring_pulse.c.o
Compiling .pio/build/esp32dev/FrameworkArduino/wiring_shift.c.o
Archiving .pio/build/esp32dev/libFrameworkArduino.a
Indexing .pio/build/esp32dev/libFrameworkArduino.a
Linking .pio/build/esp32dev/firmware.elf
Retrieving maximum program size .pio/build/esp32dev/firmware.elf
Checking size .pio/build/esp32dev/firmware.elf
Advanced Memory Usage is available via "PlatformIO Home > Project Inspect"
RAM:   [=         ]   8.0% (used 26164 bytes from 327680 bytes)
Flash: [==        ]  24.6% (used 322233 bytes from 1310720 bytes)
Building .pio/build/esp32dev/firmware.bin
esptool.py v4.5
Creating esp32 image...
Merged 2 ELF sections
Successfully created esp32 image.
========================================================= [SUCCESS] Took 13.72 seconds =========================================================
 *  Terminal will be reused by tasks, press any key to close it.

 *  Executing task in folder esp32_OTA_from_scratch: platformio run --environment esp32dev

Processing esp32dev (platform: espressif32; board: esp32dev; framework: arduino)
-------------------------------------------------------------------------------------------------------------------------------------------------
Verbose mode can be enabled via `-v, --verbose` option
CONFIGURATION: https://docs.platformio.org/page/boards/espressif32/esp32dev.html
PLATFORM: Espressif 32 (6.1.0) > Espressif ESP32 Dev Module
HARDWARE: ESP32 240MHz, 320KB RAM, 4MB Flash
DEBUG: Current (cmsis-dap) External (cmsis-dap, esp-bridge, esp-prog, iot-bus-jtag, jlink, minimodule, olimex-arm-usb-ocd, olimex-arm-usb-ocd-h, olimex-arm-usb-tiny-h, olimex-jtag-tiny, tumpa)
PACKAGES:
 - framework-arduinoespressif32 @ 3.20007.0 (2.0.7)
 - tool-esptoolpy @ 1.40500.0 (4.5.0)
 - toolchain-xtensa-esp32 @ 8.4.0+2021r2-patch5
LDF: Library Dependency Finder -> https://bit.ly/configure-pio-ldf
LDF Modes: Finder ~ chain, Compatibility ~ soft
Found 34 compatible libraries
Scanning dependencies...
Dependency Graph
|-- ArduinoOTA @ 1.0.9
|-- ESPmDNS @ 2.0.0
|-- WiFi @ 2.0.0
Building in release mode
Compiling .pio/build/esp32dev/src/main.cpp.o
src/main.cpp: In lambda function:
src/main.cpp:39:22: error: 'class ArduinoOTAMdnsClass<WiFiServer, WiFiClient, WiFiUDP>' has no member named 'getCommand'
       if (ArduinoOTA.getCommand() == U_FLASH)
                      ^~~~~~~~~~
src/main.cpp:39:38: error: 'U_FLASH' was not declared in this scope
       if (ArduinoOTA.getCommand() == U_FLASH)
                                      ^~~~~~~
src/main.cpp: In function 'void setup()':
src/main.cpp:37:13: error: invalid use of 'void'
   ArduinoOTA
   ~~~~~~~~~~
     .onStart([]() {
     ~~~~~~~~^~~~~~~
       String type;
       ~~~~~~~~~~~~
       if (ArduinoOTA.getCommand() == U_FLASH)
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         type = "sketch";
         ~~~~~~~~~~~~~~~~
       else // U_SPIFFS
       ~~~~~~~~~~~~~~~~
         type = "filesystem";
         ~~~~~~~~~~~~~~~~~~~~

       // NOTE: if updating SPIFFS this would be the place to unmount SPIFFS using SPIFFS.end()
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       Serial.println("Start updating " + type);
       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
     })
     ~~
src/main.cpp:49:6: error: expected primary-expression before ')' token
     })
      ^
src/main.cpp:52:6: error: expected primary-expression before ')' token
     })
      ^
src/main.cpp:60:6: error: expected primary-expression before ')' token
     });
      ^
src/main.cpp:62:20: error: no matching function for call to 'ArduinoOTAMdnsClass<WiFiServer, WiFiClient, WiFiUDP>::begin()'
   ArduinoOTA.begin();
                    ^
In file included from src/main.cpp:5:
.pio/libdeps/esp32dev/ArduinoOTA/src/ArduinoOTA.h:91:8: note: candidate: 'void ArduinoOTAMdnsClass<NetServer, NetClient, NetUDP>::begin(IPAddress, const char*, const char*, OTAStorage&) [with NetServer = WiFiServer; NetClient = WiFiClient; NetUDP = WiFiUDP]'
   void begin(IPAddress localIP, const char* name, const char* password, OTAStorage& storage) {
        ^~~~~
.pio/libdeps/esp32dev/ArduinoOTA/src/ArduinoOTA.h:91:8: note:   candidate expects 4 arguments, 0 provided
*** [.pio/build/esp32dev/src/main.cpp.o] Error 1
========================================================== [FAILED] Took 2.69 seconds ==========================================================

 *  The terminal process "platformio 'run', '--environment', 'esp32dev'" terminated with exit code: 1.
 *  Terminal will be reused by tasks, press any key to close it.

```

What could be the problem?

# Answer
> 1 votes
All I can see are three issues (plus possible some bonus ones)

1. You can't daisy chain callback setters like that, it returns void and it doesn't make any sense to have void.onProgress... and those callbacks doesn't seems to fit current library, so you should just drop them completely.
2. There is no `begin()` without parameters. See examples.
3. In the library I can see only callback setters: `beforeApply`, `onError` and `onStart`
4. You might be missing `#include <Arduino.h>` (it doesn't have any preprocessor like ArduinoIDE that adds this to the first line without you knowing it
5. You might be missing some defines (you can compare Arduino compile commands and Pio one)

Anyway you should take a look into the examples (at least begin is there). Callbacks looks like they are taken from who knows where (probably different library or so).

But they definitely must be complete like:

```
ArduinoOTA.onStart([](){ /* started */ });
ArduinoOTA.onBeforeApply([](){ /* before aply */ });

```

EDIT:

After some searching the ArduinoOTA library is directly in esp32 core (google: "arduinoOTA onProgress esp32"), so you don't have to add any lib\_deps for ArduinoOTA. Or more likely you shouldn't add any, and the correct library will be used then. If not, you'll have to copy (or download) correct library into lib/ subdirectory.

# Answer
> 0 votes
If you check the ArduinoOTA library in Github it states that this library has not been tested on platformIO Also note that not all functions are the same, parameters are different when you call in ArduinoIDE and on PlatformIO

---
Tags: arduino-ide, platformio, ota
---


# Question
Title: Problem converting a sketch from SPIFFS to LittleFS - no matching function for call error
I'm playing with a tutorial from this DroneBot Workshop YouTube (excellent video presentation by the way on WiFi manager for newbies). I am working with a NodeMCU ESP8266.

The example code was written for SPIFFS. If I use the LittleFS library instead, and whether I define SPIFFS to LittleFS at the start or manually replace all references to SPIFFS with LittleFS, this line will not compile:

```
if (LittleFS.begin(false) || LittleFS.begin(true))

```

It gives this error message:

```
no matching function for call to 'fs::FS::begin(bool)'

```

Any suggestions as to what I'm missing or need to change?

His original example code which won't compile is below. I'm guessing I'm missing something simple but I'mnot sure what.

I probably should have added that I have commented out the include SPIFFS, added include LittleFS.H

I still have FS.h included in libraries and not sure if that is contributing to the issue. Is there an equivalent replacement for FS.h that comes about with LitteFS that I should be including?

```
/*
  WiFiManager with saved textboxes Demo
  wfm-text-save-demo.ino
  Saves data in JSON file on ESP32
  Uses SPIFFS

  DroneBot Workshop 2022
  https://dronebotworkshop.com

  Functions based upon sketch by Brian Lough
  https://github.com/witnessmenow/ESP32-WiFi-Manager-Examples
*/

#define ESP_DRD_USE_SPIFFS true

// Include Libraries

// WiFi Library
#include <WiFi.h>
// File System Library
#include <FS.h>
// SPI Flash Syetem Library
#include <SPIFFS.h>
// WiFiManager Library
#include <WiFiManager.h>
// Arduino JSON library
#include <ArduinoJson.h>

// JSON configuration file
#define JSON_CONFIG_FILE "/test_config.json"

// Flag for saving data
bool shouldSaveConfig = false;

// Variables to hold data from custom textboxes
char testString[50] = "test value";
int testNumber = 1234;

// Define WiFiManager Object
WiFiManager wm;

void saveConfigFile()
// Save Config in JSON format
{
  Serial.println(F("Saving configuration..."));

  // Create a JSON document
  StaticJsonDocument<512> json;
  json["testString"] = testString;
  json["testNumber"] = testNumber;

  // Open config file
  File configFile = SPIFFS.open(JSON_CONFIG_FILE, "w");
  if (!configFile)
  {
    // Error, file did not open
    Serial.println("failed to open config file for writing");
  }

  // Serialize JSON data to write to file
  serializeJsonPretty(json, Serial);
  if (serializeJson(json, configFile) == 0)
  {
    // Error writing file
    Serial.println(F("Failed to write to file"));
  }
  // Close file
  configFile.close();
}

bool loadConfigFile()
// Load existing configuration file
{
  // Uncomment if we need to format filesystem
  // SPIFFS.format();

  // Read configuration from FS json
  Serial.println("Mounting File System...");

  // May need to make it begin(true) first time you are using SPIFFS
  if (SPIFFS.begin(false) || SPIFFS.begin(true))
  {
    Serial.println("mounted file system");
    if (SPIFFS.exists(JSON_CONFIG_FILE))
    {
      // The file exists, reading and loading
      Serial.println("reading config file");
      File configFile = SPIFFS.open(JSON_CONFIG_FILE, "r");
      if (configFile)
      {
        Serial.println("Opened configuration file");
        StaticJsonDocument<512> json;
        DeserializationError error = deserializeJson(json, configFile);
        serializeJsonPretty(json, Serial);
        if (!error)
        {
          Serial.println("Parsing JSON");

          strcpy(testString, json["testString"]);
          testNumber = json["testNumber"].as<int>();

          return true;
        }
        else
        {
          // Error loading JSON data
          Serial.println("Failed to load json config");
        }
      }
    }
  }
  else
  {
    // Error mounting file system
    Serial.println("Failed to mount FS");
  }

  return false;
}

void saveConfigCallback()
// Callback notifying us of the need to save configuration
{
  Serial.println("Should save config");
  shouldSaveConfig = true;
}

void configModeCallback(WiFiManager *myWiFiManager)
// Called when config mode launched
{
  Serial.println("Entered Configuration Mode");

  Serial.print("Config SSID: ");
  Serial.println(myWiFiManager->getConfigPortalSSID());

  Serial.print("Config IP Address: ");
  Serial.println(WiFi.softAPIP());
}

void setup()
{
  // Change to true when testing to force configuration every time we run
  bool forceConfig = false;

  bool spiffsSetup = loadConfigFile();
  if (!spiffsSetup)
  {
    Serial.println(F("Forcing config mode as there is no saved config"));
    forceConfig = true;
  }

  // Explicitly set WiFi mode
  WiFi.mode(WIFI_STA);

  // Setup Serial monitor
  Serial.begin(115200);
  delay(10);

  // Reset settings (only for development)
  wm.resetSettings();

  // Set config save notify callback
  wm.setSaveConfigCallback(saveConfigCallback);

  // Set callback that gets called when connecting to previous WiFi fails, and enters Access Point mode
  wm.setAPCallback(configModeCallback);

  // Custom elements

  // Text box (String) - 50 characters maximum
  WiFiManagerParameter custom_text_box("key_text", "Enter your string here", testString, 50);

  // Need to convert numerical input to string to display the default value.
  char convertedValue[6];
  sprintf(convertedValue, "%d", testNumber);

  // Text box (Number) - 7 characters maximum
  WiFiManagerParameter custom_text_box_num("key_num", "Enter your number here", convertedValue, 7);

  // Add all defined parameters
  wm.addParameter(&custom_text_box);
  wm.addParameter(&custom_text_box_num);

  if (forceConfig)
    // Run if we need a configuration
  {
    if (!wm.startConfigPortal("NEWTEST_AP", "password"))
    {
      Serial.println("failed to connect and hit timeout");
      delay(3000);
      //reset and try again, or maybe put it to deep sleep
      ESP.restart();
      delay(5000);
    }
  }
  else
  {
    if (!wm.autoConnect("NEWTEST_AP", "password"))
    {
      Serial.println("failed to connect and hit timeout");
      delay(3000);
      // if we still have not connected restart and try all over again
      ESP.restart();
      delay(5000);
    }
  }

  // If we get here, we are connected to the WiFi

  Serial.println("");
  Serial.println("WiFi connected");
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  // Lets deal with the user config values

  // Copy the string value
  strncpy(testString, custom_text_box.getValue(), sizeof(testString));
  Serial.print("testString: ");
  Serial.println(testString);

  //Convert the number value
  testNumber = atoi(custom_text_box_num.getValue());
  Serial.print("testNumber: ");
  Serial.println(testNumber);

  // Save the custom parameters to FS
  if (shouldSaveConfig)
  {
    saveConfigFile();
  }
}

void loop() {
  // put your main code here, to run repeatedly:
}

```

# Answer
> 1 votes
I encountered the same issue. I made the following changes to the code.

1. Commented out this line:

```
  //x  if (SPIFFS.begin(false) || SPIFFS.begin(true))

```
2. Replaced the commented out line with this line:

```
  SPIFFS.begin();

```
3. Then commented out the "else" portion of the code in that block:

```
  //x  else
  //x  {
  //x    // Error mounting file system
  //x    Serial.println("Failed to mount FS");
  //x  }

```

So basically you eliminate the true/false test and always execute the code, not really sure why you would not want execute the code anyway.

Note: I also used **LittleFS** to make it simple so I used the following include and define:

```
#include "LittleFS"
#define SPIFFS LittleFS

```

One final thing, I ran this on an ESP8266 mini D1 clone so I also changed the

```
#include <WiFi.h>

```

To

```
#include <ESP8266WiFi.h>

```

---
Tags: compile, error, spiffs, littlefs
---


title: HID keyboard: send modifier keys only

text:
# Question
Title: HID keyboard: send modifier keys only
I want to send modifier keys like shift, alt, ctrl, etc. without any other key. It should be theoretically possible due to the fact that it works for my normal keyboard (tested it with Xoutput and games).

For now I have to send dummy keystrokes like scroll lock (0xCF) to send the modifiers. I'm using *Arduino core for the ESP32* with the ESP32S3 Dev Module.

```
#include "USBHIDKeyboard.h"
USBHIDKeyboard Keyboard;
Keyboard.press(KEY_LEFT_CTRL); Keyboard.write(0xCF); Keyboard.write(0xCF);
delay(1000);
Keyboard.release(KEY_LEFT_CTRL); Keyboard.write(0xCF); Keyboard.write(0xCF);

```

This works, but it's realy ugly: sometimes the scroll lock LED flashes and in games the settings sometimes see the scroll lock and complain.

Any ideas?

# Answer
You can use `Keyboard.pressRaw()`, 0xE0 - 0xE8 should be the modifiers.

```
size_t USBHIDKeyboard::pressRaw(uint8_t k) {
  uint8_t i;
  if (k >= 0xE0 && k < 0xE8) {
    // it's a modifier key
    _keyReport.modifiers |= (1<<(k-0x80));
  } else if (k && k < 0xA5) {
    // Add k to the key report only if it's not already present
    // and if there is an empty slot.
    if (_keyReport.keys[0] != k && _keyReport.keys[1] != k && _keyReport.keys[2] != k && _keyReport.keys[3] != k && _keyReport.keys[4] != k && _keyReport.keys[5] != k) {
      for (i=0; i<6; i++) {
        if (_keyReport.keys[i] == 0x00) {
           _keyReport.keys[i] = k;
           break;
        }
      }
      if (i == 6) {
        return 0;
      }
    }
  } else {
    // not a modifier and not a key
    return 0;
  }
  sendReport(&_keyReport);
  return 1;
}

```

USBHIDKeyboard.cpp
> 1 votes

---
Tags: esp32, keyboard
---


# Question
Title: Watchdog on Arduino Nano does not recover
I'm trying to add a watchdog on an Arduino Nano, for my code, and I'm following the code in this manual exactly, but after the amount of time for not resetting the watchdog, it resets but does not reboot.

In the last paragraph it says that:

> However, I could not find something similar in Optiboot bootloader (If I'm wrong, correct me please!), used by Arduino UNO and other new boards. It seems that they sacrificed this check for size. So, it is possible that watchdog timer will be enabled after reset, so - if you set it's value too low, your board may never come out of bootloader stage. So, to be always on the safe side, set watchdog to longer periods (2S and more).

So I've tried for 8 s, but still same result.

What is wrong?

```
#include <avr/wdt.h>
int k=1;

void setup() {
  Serial.begin(9600);
  wdt_enable(WDTO_8S);
}

void loop() {
  //do stuff here
  k++;
  wdt_reset();
  Serial.println(1000 * k);
  delay(1000 * k);
}

```

# Answer
the "old" Nano bootloader doesn't clear the Watchdog flag and while the bootloader waits for the upload, the board resets.

flash the "new" bootloader to Nano

In IDE 1.8.6 you can select version of Nano in Tools menu. It is the 'new' bootloader if you select "Atmega328p" and 'old' if you select "Atmega328p (Old bootloader)". If you invoke "Burn bootloader" it will use the bootloader for the selected option.
> 3 votes

# Answer
This works on an Uno R3.

The sketch restarts every 4 seconds when wdt\_reset() is commented out.

Please copy the code and try it.

```
  #include <avr/wdt.h>

  void setup() {
    pinMode(13,OUTPUT);
    digitalWrite(13,LOW);

    delay(50);
    digitalWrite(13,HIGH);    // flicker the LED to identify when setup() is running
    delay(50);
    digitalWrite(13,LOW);
    delay(50);
    digitalWrite(13,HIGH);
    delay(50);
    digitalWrite(13,LOW);
    delay(50);
    digitalWrite(13,HIGH);
    delay(50);
    digitalWrite(13,LOW);
    delay(50);

    wdt_enable(WDTO_4S);

  }
  void loop() {
    digitalWrite(13,HIGH);
    delay(500);
    digitalWrite(13,LOW);
    delay(500);
    //wdt_reset();
   }

```
> 1 votes

# Answer
I got the WDT working in my Nano with the old bootloader using this code from an old post on Arduino Forum.

The code is added after the wdt\_reset();

```
  //reset watchdog
  wdt_reset();
  //set up WDT interrupt
  WDTCSR = (1 << WDCE) | (1 << WDE);
  //Start watchdog timer with 4s prescaller
  WDTCSR = (1 << WDIE) | (1 << WDE) | (1 << WDP3) | (1 << WDP0);

```
> 1 votes

# Answer
> After that leds on board start to flicker non stop, and data stop posting on serial monitor

Definitely have a read of the datasheet for the '328p, section 11.8. There are several modes of operation - it can interrupt; reset; or both. Even if it resets, it doesn't reload your initial RAM data, so, for instance, line 2 `int k=1;` looks like an executable statement but is an initializer and has no effect at reset, and `k` retains its current value. There are probably other, similar, anomalies that happen when the WDT fires.

You probably don't need to understand the WDT down to its last control-bit but definitely make sure you're using it in the mode you want to be in. It will probably be handy during your testing to have a tiny sketch that just disables the watchdog so you can quickly get your Arduino back for the next test.
> 0 votes

# Answer
The problem may be that the 328 defaults to a Watchdog time of 16mS after a reset- WDP3 = 0, WDP2 = 0, WDP1 = 0, WDP0 = 0 so when it resets and starts your program from the beginning of your code, the program never gets very far. I made a very short recovery program that just invokes noInterrupts(); and load it straight after a power off / on. Then you can load a new program that doesn't use the watchdog.
> 0 votes

---
Tags: arduino-nano, watchdog
---


# Question
Title: Is it safe to leave an ATmega328PB's RX and TX pins unconnected?
I am using an ATmega328PB and was looking at Arduino's and others' schematics that use it. All schematics I've found use a serial chip to communicate between USB and TX & RX. However, I do not need USB, and I plan to program the ATmega through ICSP.

Is it safe to leave these pins unconnected or connect them directly to LEDs like some Arduino boards do (like in the photo)? I wasn't able to find clear information about it.

I apologize if this is a silly question. Thank you!

# Answer
> 1 votes
Look at these pins as any other I/O pin. There is nothing special about them, if you do not enable their special function, in your case USART0.

This is what the data sheet says:

> **17.2.6 Unconnected Pins**
>
> If some pins are unused, it is recommended to ensure that these pins have a defined level. Even though most of the digital inputs are disabled in the deep sleep modes as described above, floating inputs should be avoided to reduce current consumption in all other modes where the digital inputs are enabled (Reset, Active mode and Idle mode).
>
> The simplest method to ensure a defined level of an unused pin, is to enable the internal pull-up. In this case, the pull-up will be disabled during reset. If low power consumption during reset is important, it is recommended to use an external pull-up or pull-down. Connecting unused pins directly to VCC or GND is not recommended, since this may cause excessive currents if the pin is accidentally configured as an output.

Therefore, if you do not use USART0 and do not need these LEDs, you can get away with no external circuitry. Just enable the internal pull-ups.

---
Tags: atmega328
---


title: PulseIN not recognised

text:
# Question
Title: PulseIN not recognised
I have included the library for the HCSR04 echo sounder, but `pulseIN` is not recognised; it never goes orange either.

I have tried multiple libraries, and closing down the IDE and opening it back up.

# Answer
There is no `pulseIN()` in the Arduino core. The function you are looking for is named `pulseIn()`. It's in the core library: you do not need to load anything extra. Beware that the language is case-sensitive.
> 2 votes

---
Tags: pulsein
---


# Question
Title: Problem saving "char" variable to EEPROM on ESP32
I am making a project, in which I need to store the WiFi credentials to the EEPROM. For now, I am able to store a bool array in the EEPROM, but I am not able to store the `char` variables as the ESP32 crashes. I can't share the complete code since it is REALLY long, but here are the problematic functions:

To save to the EEPROM:

```
//////////////////////////////////WORKING////////////////////////////////
void saveToEEPROM() {
  // Save the contents of the array in EEPROM memory
  for (int i = 0; i < NUM_PARAM_ITEMS; i++) {
    EEPROM.write(i, menu_items_param_bool[i]);
  }
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////NOT WORKING////////////////////////////////
  // Save WiFi credentials in EEPROM if not empty
  int offset = NUM_PARAM_ITEMS;

  if (strlen(ssid1) > 0) {
    for (int i = 0; i < strlen(ssid1); i++) {
      EEPROM.write(offset + i, ssid1[i]);
    }
    offset += strlen(ssid1);
    EEPROM.write(offset, '\0'); // Null-terminate the SSID1 string
  }
  offset++;
  if (strlen(password1) > 0) {
    for (int i = 0; i < strlen(password1); i++) {
      EEPROM.write(offset + i, password1[i]);
    }
    offset += strlen(password1);
    EEPROM.write(offset, '\0'); // Null-terminate the Password1 string
  }
  offset++;
  if (strlen(ssid2) > 0) {
    for (int i = 0; i < strlen(ssid2); i++) {
      EEPROM.write(offset + i, ssid2[i]);
    }
    offset += strlen(ssid2);
    EEPROM.write(offset, '\0');  // Null-terminate the SSID2 string
  }
  offset++;
  if (strlen(password2) > 0) {
    for (int i = 0; i < strlen(password2); i++) {
      EEPROM.write(offset + i, password2[i]);
    }
    offset += strlen(password2);
    EEPROM.write(offset, '\0'); // Null-terminate the Password2 string
  }

  EEPROM.commit(); // Don't forget to call the commit() function to save the data to EEPROM
}

```

To read from the EEPROM:

```
void readFromEEPROM() {
//////////////////////////////////WORKING////////////////////////////////
  // Read the contents of the EEPROM memory and restore it to the array
  for (int i = 0; i < NUM_PARAM_ITEMS; i++) {
    menu_items_param_bool[i] = EEPROM.read(i);
  }
//////////////////////////////////NOT WORKING//////////////////////////////////
  // Read WiFi credentials from EEPROM if not empty
  int offset = NUM_PARAM_ITEMS;

  // Read SSID1
  int ssid1Length = EEPROM.read(offset);
  if (ssid1Length > 0) {
    for (int i = 0; i < ssid1Length; i++) {
      ssid1[i] = (char)EEPROM.read(offset + i + 1);
    }
    ssid1[ssid1Length] = '\0'; // Null-terminate the SSID1 string
    Serial.print("SSID1: ");
    Serial.println(ssid1);
  }
  offset += ssid1Length + 1;

  // Read Password1
  int password1Length = EEPROM.read(offset);
  if (password1Length > 0) {
    for (int i = 0; i < password1Length; i++) {
      password1[i] = (char)EEPROM.read(offset + i + 1);
    }
    password1[password1Length] = '\0';  // Null-terminate the Password1 string
    Serial.print("PASSWORD1: ");
    Serial.println(password1);
  }

  offset += password1Length + 1;

  // Read SSID2
  int ssid2Length = EEPROM.read(offset);
  if (ssid2Length > 0) {
    for (int i = 0; i < ssid2Length; i++) {
      ssid2[i] = (char)EEPROM.read(offset + i + 1);
    }
    ssid2[ssid2Length] = '\0'; // Null-terminate the SSID2 string
    Serial.print("SSID2: ");
    Serial.println(ssid2);
  }

  offset += ssid2Length + 1;

  // Read Password2
  int password2Length = EEPROM.read(offset);
  if (password2Length > 0) {
    for (int i = 0; i < password2Length; i++) {
      password2[i] = (char)EEPROM.read(offset + i + 1);
    }
    password2[password2Length] = '\0'; // Null-terminate the Password2 string
    Serial.print("PASSWORD2: ");
    Serial.println(password2);
  }
}

```

And the variables to save and read are:

```
char ssid1[32] = "ssid1"; // SSID of the first WiFi network
char password1[64] = "password1"; // Password of the first WiFi network
char ssid2[32] = "ssid2"; // SSID of the second WiFi network
char password2[64] = "password2"; // Password of the second WiFi network

```

Can anybody help me?

# Answer
> 1 votes
There are multiple issues with your code.

1. If `strlen(anystring)` is zero, you write nothing, but increment `offset`.
2. You try to read the lengths of the strings from EEPROM, but you never write them there.

Some things are complicated and can be simplified.

1. You don't need to explicitly write `'\0'` (end-of-string marker), since each of your strings already includes it.
2. Increment `offset` directly in the course.

This is a possible corrected solution. It uses the length and the end-of-string marker for each stored string.

```
  // Write anystring
  int anystringLength = strlen(anystring);
  EEPROM.write(offset, anystringLength);
  offset++;
  for (int i = 0; i <= anystringLength; i++) {
    EEPROM.write(offset, anystring[i]);
    offset++;
  }

```

```
  // Read anystring
  int anystringLength = EEPROM.read(offset);
  offset++;
  for (int i = 0; i <= anystringLength; i++) {
    anystring[i] = (char)EEPROM.read(offset);
    offset++;
  }
  // start of debug output
  Serial.print("any String: ");
  Serial.println(anystring);
  // end of debug output

```

*Note: To make things explicit, using the `offset` and incrementing it are separated. You can combine them, though. But terse code tend to be misinterpreted.*

This is another possible solution. Instead of expecting the length written before a string, read it until you find the end-of-string marker.

The writing loop writes all characters including the final end-of-string marker. This works for empty strings, too.

```
  int i;
  // Write anystring
  i = 0;
  do {
    char c = anystring[i];
    EEPROM.write(offset, c);
    offset++;
    i++;
  } while (c != '\0');

```

The reading loop does the same in the opposite direction. Using the `do`-`while` loop ensures that the end-of-string marker is read in any case.

```
  int i;
  // Read anystring
  i = 0;
  do {
    char c = EEPROM.read(offset);
    anystring[i] = c;
    offset++;
    i++;
  } while (c != '\0');
  // start of debug output
  Serial.print("any String: ");
  Serial.println(anystring);
  // end of debug output

```

Like the original the solutions lack error checks, for example of too long strings, which are left as an exercise to the reader.

Another bonus point will be received, if you extract these loops into functions. It simplifies the code and removes duplicates. \[Edit\] I found that the `EEPROM` class already provides `writeString()` and `readString()`. You should read its documentation and consider the usage.

---
Tags: esp32, eeprom, char
---


# Question
Title: Is it possible to use analogWrite() using an external Power Supply Unit?
As far as I know, my Arduino Nano can perform `analogWrite()` ranging from 0 to 1023 for 0 to 5 V respectively, but I want to control a DC motor which requires 9 V for good performance. Is it possible to do so?

# Answer
Yes, that is possible.

`analogWrite()` sends out a 5 V PWM signal (3.3 V on some Arduinos) from a pin. You can't use this PWM signal to drive a motor directly; an Arduino IO pin can't deliver enough current for that.

You will have to get a separate 9 V power supply that can supply enough current for the motor, and use a BJT or MOSFET to switch that 9 V with the PWM signal from the Arduino, or use a dedicated motor driver IC or module.

There are plenty of schematics for this on the interwebs.
> 0 votes

---
Tags: arduino-nano, motor, analogwrite
---


# Question
Title: ATtiny25 - assembly - BOD disable for sleep mode - error
If I understood the AVR instruction manual correctly, the ATtiny 25 can disable the BODlevel2 fuse by software, but when compiling with Studio 7 (version 7.0.132), I get the error "Undefined symbol: BODS & BODSE".

* BODLEVEL fuses are set correctly. L:0x62 H:0xDD E:0xFF

```
; init brown out detection disable for sleep mode
        ldi     r23, (1<<BODS)|(1<<BODSE) ; En 2 fois 1- pour autoriser ecriture
        out     MCUCR, r23                ; Envoyer autorisation ecriture
        ldi     r23, (1<<BODS)|(0<<BODSE) ; Ecrire valeur a envoyer dans R23
        out     MCUCR, r23                ; Envoyer R23 dans MCUCR

; init WDT for interrupt mode
        ldi     r23, (1<<WDCE)|(1<<WDE)
        out     WDTCR, r23
        ldi     r23, (1<<WDIE)|(0<<WDE)|(0<<WDP0)|(1<<WDP1)|(0<<WDP2)|(0<<WDP3) ; sec time out WDT interrupt; no system reset
        out     WDTCR, r23
        sei                   ; global interrupt enable bit is active

L1_INTERIEUR:                 ; init sleep modes
        ldi     r23, (1<<SM1) ; Power-down mode
        out     MCUCR, r23
        in      r23, MCUCR
        ldi     r23, (1<<SE)  ; sleep mode enabled
        out     MCUCR, r23    ; put MCU into sleep

L1A_INTERIEUR:
        in R15,SREG
        wdr
        sleep

```

# Answer
> 2 votes
The Microchip Studio uses the GNU toolchain or the XC8 toolchain. Unfortunately you did not tell us, which you use for your project.

The common solution is to include the corresponding header file to let the preprocessor replace the symbols with the defined values:

```
#include <iotn25.h>

; init brown out detection disable for sleep mode
        ldi     r23, (1<<BODS)|(1<<BODSE) ; En 2 fois 1- pour autoriser ecriture

; ...

```

Or to add "iotn25.h" to the dependencies in the "Solution Explorer".

Now you need to make sure that the preprocessor runs before the assembler. Both toolchains behave identically, if you name the assembler source with an uppercase extension of ".S".

The GNU toolchain does this according to its documentation:

> You can use the GNU C compiler driver to get other “CPP” style preprocessing by giving the input file a ‘.S’ suffix.

For the XC8 toolchain, the documentation tells us explicitly about this and an interesting option in chapter 8.3.4.1:

> The `-x assembler-with-cpp` language option ensures assembly source files are preprocessed before they are assembled, thus allowing the use of preprocessor directives, such as #include and C-style comments with assembly code. By default, assembly files not using the .S or .sx extension are not preprocessed.

---
Tags: programming, power, compilation-errors
---


# Question
Title: Compressing grayscale image with ESP32-S3-WROOM Freenove
I have a ESP32-S3-WROOM module by Freenove which has a camera and a micro SD card module. I can take a snapshot as a PIXELFORMAT\_JPEG photo.

However, when I change the pixelformat to "PIXELFORMAT\_GRAYSCALE", the module captures the raw data (1 byte for each pixel) which is 480 kB constant for SVGA (600x800).

How can I compress this raw data to jpg? I also tried the way of saving the photo to a ".jpg" file, however, it did not work.

This is my capturing code for it:

```
/**********************************************************************
  Filename    : Camera and SDcard
  Description : Use the onboard buttons to take photo and save them to an SD card.
  Auther      : www.freenove.com
  Modification: 2022/11/02
**********************************************************************/
#include "esp_camera.h"
#define CAMERA_MODEL_ESP32S3_EYE
#include "camera_pins.h"
#include "ws2812.h"
#include "sd_read_write.h"

#define BUTTON_PIN  0

void setup() {
  Serial.begin(500000);
  Serial.setDebugOutput(false);
  Serial.println();
  pinMode(BUTTON_PIN, INPUT_PULLUP);
  ws2812Init();
  sdmmcInit();
  //removeDir(SD_MMC, "/camera");
  createDir(SD_MMC, "/camera");
  listDir(SD_MMC, "/camera", 0);
  if(cameraSetup()==1){
    ws2812SetColor(2);
  }
  else{
    ws2812SetColor(1);
    return;
  }
}

void loop() {
  if (digitalRead(BUTTON_PIN)==LOW) {
    delay(20);
    if (digitalRead(BUTTON_PIN)==LOW) {
      ws2812SetColor(3);
      while (digitalRead(BUTTON_PIN)==LOW);
      camera_fb_t * fb = NULL;
      fb = esp_camera_fb_get();
      if (fb != NULL) {
        int photo_index = readFileNum(SD_MMC, "/camera");
        if (photo_index!=-1) {
          String path = "/camera/" + String(photo_index) +".jpg";
          Serial.println(fb->len);
          writejpg(SD_MMC, path.c_str(), fb->buf, fb->len);
        }
        esp_camera_fb_return(fb);
      }
      else {
        Serial.println("Camera capture failed.");
      }
      ws2812SetColor(2);
    }
  }
}

int cameraSetup(void) {
  camera_config_t config;
  config.ledc_channel = LEDC_CHANNEL_0;
  config.ledc_timer = LEDC_TIMER_0;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.frame_size = FRAMESIZE_SVGA;
  config.pixel_format = PIXFORMAT_GRAYSCALE; // for streaming
  config.grab_mode = CAMERA_GRAB_WHEN_EMPTY;
  config.fb_location = CAMERA_FB_IN_PSRAM;
  config.jpeg_quality = 20;
  config.fb_count = 1;

  // if PSRAM IC present, init with UXGA resolution and higher JPEG quality
  // for larger pre-allocated frame buffer.
  if (psramFound()) {
    config.jpeg_quality = 10;
    config.fb_count = 2;
    config.grab_mode = CAMERA_GRAB_LATEST;
  } else {
    // Limit the frame size when PSRAM is not available
    config.frame_size = FRAMESIZE_SVGA;
    config.fb_location = CAMERA_FB_IN_DRAM;
  }

  // camera init
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed with error 0x%x", err);
    return 0;
  }

  sensor_t * s = esp_camera_sensor_get();
  // initial sensors are flipped vertically and colors are a bit saturated
  s->set_vflip(s, 1); // flip it back
  s->set_brightness(s, 1); // up the brightness just a bit
  s->set_saturation(s, 0); // lower the saturation

  Serial.println("Camera configuration complete!");
  return 1;
}

```

This is the `writejpg()` function that the code snippet used:

```
void writejpg(fs::FS &fs, const char * path, const uint8_t *buf, size_t size){
    File file = fs.open(path, FILE_WRITE);
    if (!file) {
        Serial.println("Failed to open file for writing");
        ws2812SetColor(1);
        delay(100);
        return;
    }
    file.write(buf, size);

    // Define the chunk size
    const size_t chunkSize = 2048;

    // Write to serial in chunks
    for (size_t i = 0; i < size; i += chunkSize) {
        // Calculate the size of the current chunk
        size_t currentChunkSize = ((i + chunkSize) <= size) ? chunkSize : (size - i);

        // Write the current chunk in hex format to serial
        for (size_t j = 0; j < currentChunkSize; ++j) {
            Serial.printf("%02X ", buf[i + j]);
//            if ((j + 1) % 128 == 0) { // Print a new line every 16 bytes for better readability
//                Serial.println();
//            }
        }
    }
    Serial.println();
    Serial.printf("Saved file to path: %s\r\n", path);
}

```

This function also prints out the hexadecimal values of the image for debugging.

So, do you guys have any ideas for compressing this image into a jpg file?

# Answer
> 0 votes
I just managed to solve this problem with using `frame2jpg()` function.

Hope it helps!

---
Tags: esp32, sd-card, esp32-cam, esp32-s3
---


# Question
Title: ESP8266 wakeup from deep sleep, both by timer and by button
In a project powered by batteries, the ESP8266 should go to deep sleep for a long time, having RF disabled. After some number of sleep cycles, it should restart with RF enabled, synchronize time via NTP, and again go to sleep. I think this could work with a counter saved in RTC Memory or SPIFFS.

Additionally, s.o. should be able to wake up via push button (at any time), which should result in different behaviour (running as WiFi AP, allowing for configuration of new WiFi credentials).

The question is: Is it possible to find out what did wake the ESP (timer or pushbutton)?

Using external circuitry this might be possible. To avoid this, we switch to ESP32, which is overkill for the project, but easy to find out the source of the wakeup (and with the possibility to use touch sensors). Additionally, ESP32 allows for easy use of "Preferences" in NVR and variables in RTC memory (when using the Arduino IDE), which will survive deep sleep (RTC) and even reset/power loss (NVR).

# Answer
Place a capacitor on the ESP side of the button to one power rail or the other (my preference is to ground, but either should work), and have the `setup()` routine read the state of the pushbutton switch as its first instruction. You probably don't even need the capacitor, but if you don't have it, you'll have to read the switch state multiple times to account for possible contact bounce.

If the ESP wakes up due to a timer, there will be no activity on the PB line; if it wakes up due to the PB being pressed, there will be activity on the PB line for several 10s of millisecinds no matter how quickly it is pressed and released.
> 1 votes

# Answer
The capacitor solution works, but I wanted to expand on what I did in particular and why I found it beneficial. Here's my circuit:

At wakeup, the first thing I do is read D1. After that, I then set D1 to an output and set it high to keep the capacitor charged. That prevents a second button press from resetting the ESP.

Then, when going to sleep, I set the pin low for a few ms to *discharge the capacitor*.

I picked the smallest capacitor possible because there's no downside to charging quickly.

Note that the ESP will reset as the button is pressed down - the pin will go low, and soon after go high as the capacitor charges, all before the button is released. This is ideal in circumstances where you want the act of pushing the button to cause the wake up, not the *release*.
> 0 votes

# Answer
If D0 is positive and D1 is negative, your shorting the power, BAD.... That will reset the chip all right, but burn the chip up... I would simply use an "and gate" like the MC74VHC1G08... I have not tried your Idea, But connecting 2 chip outputs together is never a good idea...
> 0 votes

---
Tags: esp8266, sleep
---