text:
FastLED web client, clicked on page to control on and off for ws2812
FastLED web client, when clicked on, all RGB on in red color. Clicked off, all RGB off/black. but not work.
Here is the full code, i don't know why it's loop always and web client stuck when i clicked on button on webpage
I can not turn on all LED, or turn off all LED.
#include <ESP8266WiFi.h>

#include <FastLED.h>
#define DATA_PIN 5
#define NUM_LEDS 300
CRGB leds[NUM_LEDS];
#define COLOR_ORDER GRB
#define BRIGHTNESS 254
#define FRAMES_PER_SECOND 30

// Enter your wifi network name and Wifi Password
const char* ssid = "";
const char* password = "";

// Set web server port number to 80
WiFiServer server(80);

// Variable to store the HTTP request
String header;

// These variables store current output state of LED
String outputRedState = "off";

// Assign output variables to GPIO pins
const int redLED = 5;

// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;

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

  //FastLED.addLeds<WS2812, DATA_PIN, GRB>(leds, NUM_LEDS);
  LEDS.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);  // 初始化光带
  FastLED.setBrightness(BRIGHTNESS);                     // 设置光带亮度
  set_max_power_in_volts_and_milliamps(5, 2000);         // 光带电源管理（设置光带5伏特，500mA）

  // Connect to Wi-Fi network with SSID and password
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  // Print local IP address and start web server
  Serial.println("");
  Serial.println("WiFi connected.");
  Serial.println("IP address: ");
  Serial.println(WiFi.localIP());
  server.begin();
}

void loop() {
  WiFiClient client = server.available();  // Listen for incoming clients

  if (client) {                     // If a new client connects,
    Serial.println("New Client.");  // print a message out in the serial port
    String currentLine = "";        // make a String to hold incoming data from the client
    currentTime = millis();
    previousTime = currentTime;
    while (client.connected() && currentTime - previousTime <= timeoutTime) {  // loop while the client's connected
      currentTime = millis();
      if (client.available()) {  // if there's bytes to read from the client,
        char c = client.read();  // read a byte, then
        Serial.write(c);         // print it out the serial monitor
        header += c;
        if (c == '\n') {  // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
            // and a content-type so the client knows what's coming, then a blank line:
            client.println("HTTP/1.1 200 OK");
            client.println("Content-type:text/html");
            client.println("Connection: close");
            client.println();

            // turns the GPIOs on and off
            if (header.indexOf("GET /2/on") >= 0) {
              Serial.println("RED LED is on");
              outputRedState = "on";

              fill_solid(leds, 300, CRGB::Red);
              FastLED.show();

            } else if (header.indexOf("GET /2/off") >= 0) {
              Serial.println("RED LED is off");
              outputRedState = "off";

              fill_solid(leds, 300, 0);
              FastLED.show();
            }

            // Display the HTML web page
            client.println("<!DOCTYPE html>");
            client.println("<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
            client.println("<link rel=\"icon\" href=\"data:,\">");
            // CSS to style the on/off buttons
            client.println("");

            // Web Page Heading
            client.println("
My LED Control Server
");

            // Display current state, and ON/OFF buttons for GPIO 2 Red LED
            client.println("
Red LED is " + outputRedState + "
");
            // If the outputRedState is off, it displays the OFF button
            if (outputRedState == "off") {
              client.println("
<button class=\"button buttonOff\">OFF</button>
");
            } else {
              client.println("
<button class=\"button buttonRed\">ON</button>
");
            }

            client.println("");

            // The HTTP response ends with another blank line
            client.println();
            // Break out of the while loop
            break;
          } else {  // if you got a newline, then clear currentLine
            currentLine = "";
          }
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }
      }
    }
    // Clear the header variable
    header = "";
    // Close the connection
    client.stop();
    Serial.println("Client disconnected.");
    Serial.println("");
  }
}

but not work ... what does this mean?

all RGB on in red color ... what color are the supposed to be?

please clean up your code ... it is not indented properly, which makes errors very difficult to identify ... do yourself a favor, and spare yourself a world of hurt by learning how to format your code properly

look at the code at https://arduino.stackexchange.com/questions/91113/improve-sd-logger-0-02-of-data-isnt-logged-at-desired-rate .... that is what properly formatted code looks like

What i want to do is, access web link, http://x.x.x.x/2/on, LED strip of 300 NUM_LED on, and then if click button off, all LED strip off.
But i encounter device stuck when i clicked on, sometime LED not all switch on, and the web seems no response, then wait few seconds, try to click on again, sometime the LED can all on. But off LED totally not work.

people will not spend time looking at your code when it is presented unformatted ... it should be easy to read here, on this page ... people should not have to copy your code and paste it into an editor, format it and then read it

this may help ... https://www.martyncurrey.com/esp8266-and-the-arduino-ide-part-6-javascript-and-ajax .... look at the last sketch at AJAX With Acknowledgement   ................... here is another https://startingelectronics.org/tutorials/arduino/ethernet-shield-web-server-tutorial/web-server-read-switch-using-AJAX/

just re-format. thanks. Sorry about my mistake

My expectation is all 300 Leds turn-on when i clicked button, but sometimes, only Led[0] on,  then i need to refresh "x.x.x.x/2/on" few times, then finally all Led on.


text:
How to program Attiny12 with Arduino?
I bought a bunch of Attiny12's from Digikey without checking if I can even program them with an Arduino. I've looked around a bunch, and I've only ever found ways to program Attiny13 and Attiny10.
Strangely, one of the board packages I downloaded had board information for the Attiny12 in the avrdude.conf file, but I can't select it in the Arduino IDE. I've tried to change the Attiny13 info in the config file to the Attin12 and just selecting the Attiny13, but I'm getting the following error:
         Using Port                    : COM3
         Using Programmer              : stk500v1
         Overriding Baud Rate          : 19200
         AVR Part                      : ATtiny13
         Chip Erase delay              : 20000 us
         PAGEL                         : P00
         BS2                           : P00
         RESET disposition             : dedicated
         RETRY pulse                   : SCK
         serial program mode           : yes
         parallel program mode         : yes
         Timeout                       : 200
         StabDelay                     : 100
         CmdexeDelay                   : 25
         SyncLoops                     : 32
         ByteDelay                     : 0
         PollIndex                     : 3
         PollValue                     : 0x53
         Memory Detail                 :

                                  Block Poll               Page                       Polled
           Memory Type Mode Delay Size  Indx Paged  Size   Size #Pages MinW  MaxW   ReadBack
           ----------- ---- ----- ----- ---- ------ ------ ---- ------ ----- ----- ---------
           eeprom         4     8    64    0 no         64    0      0  9000 20000 0xff 0xff
           flash          4     5   128    0 no       1024    0      0  4500 20000 0xff 0xff
           signature      0     0     0    0 no          3    0      0     0     0 0x00 0x00
           lock           0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           calibration    0     0     0    0 no          1    0      0     0     0 0x00 0x00
           hfuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00
           lfuse          0     0     0    0 no          1    0      0  9000  9000 0x00 0x00

         Programmer Type : STK500
         Description     : Atmel STK500 Version 1.x firmware
         Hardware Version: 2
         Firmware Version: 1.18
         Topcard         : Unknown
         Vtarget         : 0.0 V
         Varef           : 0.0 V
         Oscillator      : Off
         SCK period      : 0.1 us

avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.03s

avrdude: Device signature = 0x000000 (retrying)

Reading | ################################################## | 100% 0.02s

avrdude: Device signature = 0x000000 (retrying)

Error while burning bootloader.
Reading | ################################################## | 100% 0.03s

avrdude: Device signature = 0x000000
avrdude: Yikes!  Invalid device signature.
         Double check connections and try again, or use -F to override
         this check.

Anyway I can program the Attiny12 without resorting to a dedicated programmer?

are you asking about programming with Arduino or with Arduino IDE?

info about programming is almost non-existent ... maybe info here can be used to write a programming sketch ... http://web.archive.org/web/20090823113448/http://www.xs4all.nl:80/~sbolt/e-spider_prog.html

here is the datasheet for Attiny12 ... it's the 94 page one labeled ATtiny11/12 ... https://web.archive.org/web/20090417081256/http://www.atmel.com/dyn/products/alldatasheets.asp?family_id=607

LOL. Well, in some ways I love the question, but you're not going to run an Arduino sketch on this thing. And it's for the same reason. The ATTiny12 has no RAM. I don't mean little RAM, I mean literally no RAM and no way to add RAM to its internal address space. Basically, all you have inside the chip for mutable state is the general purpose registers and whatever peripheral registers you can cram stuff into. gcc/g++ do not support it as a C or C++ target. So, if you want to know about ardunoisp.ino to program an assembly program, okay. But, you're not going to code this chip with Arduino.

@timemage Huh, I completely missed that. So what's the purpose of this microcontroller?

Whatever purpose you can dream up with assembly code. It is theoretically possible to make C run on such a thing with extraordinary limitations that might technically violate even the non-hosted standard. But, gcc does not do this. The attiny13 is very similar but has some RAM and can be coded be coded via an Arduino core in a very limited way. If you want to stick with this tiny series, use the the ATTiny85, but really, still don't exactly recommend it. If you're new to all this stuff, you really should not go smaller the an Arduino UNO/Nano/Promini in terms of memories.

If "program Attiny12 with Arduino" was intended to mean, "use an Ardunio board to put code onto a Attiny12", then your question in still unanswered and on topic (so far as I'm concerned anyway) though may need clarification.  If it was intended to mean "use Arduino code/core/ecosystem on an Attiny12" then it's basically answered. You can delete the question or you can clarify in the question that that's what you meant and I'll convent the essence of the comment into an answer.

@timemage The former - I wanted to use an Arduino Uno as a programmer for Attiny, so I don't have to buy a dedicated programmer.

source: Stack Exchange

license: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/

sort: votes


text:
Duinotech ATTINY85 Pinout
I'm sure this is a super nooby question but,
what is the pinout of the Digispark ATTINY85 Development Board
The board looks like this:

Now the reason why I'm asking this question, when I do digitalWrite(1, HIGH);, the test light will come on. My question is then, what is the virtual number for PB1 (P1 in photo).
Also, does that arduino output + or -?
Any help is appreciated. Thank You!

This seems to show an obvious pinout with an LED connected to p1 (digital pin 1).
With "virtual number" I guess you mean the Arduino pin nr. As your code shows you, it's Arduino pin 1.

when I do digitalWrite(1, HIGH);, the test light will come on.

nvm, got what you mean now.

all_licenses: Creative Commons - Attribution Share-Alike - https://creativecommons.org/licenses/by-sa/4.0/


text:
avrdude: ser_open(): can't open device "/dev/ttyACM0": Permission denied - can't get out of that
I have an Arduino Pro Micro ATmega32U4 and I'm facing this error message on uploading:
         Copyright (c) Brian Dean, http://www.bdmicro.com/
         Copyright (c) Joerg Wunsch

         System wide configuration file is "//etc/avrdude.conf"
         User configuration file is "/home/lucas/.avrduderc"
         User configuration file does not exist or is not a regular file, skipping

         Using Port                    : /dev/ttyACM0
         Using Programmer              : avr109
         Overriding Baud Rate          : 57600
avrdude: ser_open(): can't open device "/dev/ttyACM0": Permission denied
avrdude: opening programmer "avr109" on port "/dev/ttyACM0" failed

avrdude done.  Thank you.

Problem uploading to board.  See https://support.arduino.cc/hc/en-us/sections/360003198300 for suggestions.

I can upload code if I open Arduino IDE through terminal with sudo, but I don't know if there is any problem with this.
the stats for ttyACM0 shows up like this
~ >>> stat /dev/ttyACM0
    Arquivo: /dev/ttyACM0
    Tamanho: 0          Blocos: 0          bloco de E/S: 4096   arquivo especial de caractere
Dispositivo: 0,5        Inode: 1319        Ligações: 1     Tipo de dispositivo: 166,0
     Acesso: (0660/crw-rw----)  Uid: (    0/    root)   Gid: (  987/    uucp)
     Acesso: 2022-10-23 17:13:33.897457466 -0300
Modificação: 2022-10-23 17:13:33.897457466 -0300
  Alteração: 2022-10-23 17:13:33.897457466 -0300
    Criação: 2022-10-23 17:13:33.307457434 -0300

I've tried:
sudo chmod a+rw /dev/ttyACM0
sudo usermod -a -G uucp myUser
sudo udevadm trigger
And none of these worked, this error keeps showing when I use the IDE as my regular 'non root' user (Before someone ask, yes, I logged off and entered again, to make sure that the groups were updated).
So, is there any other step that I'm missing?

every time that I try to flash anything to it I get this message ... that is not true ... you said I can upload code opening Arduino IDE .... please edit your post to describe the problem exactly

It doesn't normally manifest in a permission error, but I might as well mention while you're at this to make sure you have ModemManager disabled. Otherwise it may not be as obvious when you've fixed your current problem if it's there creating a similar looking one.

Is this Linux?  Did you check the default permissions and ownership of the /dev/ttyACM0 device (when it is newly created / before you alter it)? How's about your self - did you check what groups your login belongs to? Ultimately you can edit the Linux UDEV rules, but that's a bit drastic and we should look for a simpler solution.

At the risk of annoying you, check id -Gn to be sure that effective group membership really has propagated. This something that hasn't been shown. sudo udevadm trigger isn't going to anything for you if you haven't been messing with udev rules. "Permission denied" the string version of EACCES coming from the open call in avrdude's ser_posix.c. If everything said is correct, the only other pain points are things like SELINUX, AppArmor, snap, or other security/sandbox/container/virtualization environment.

well I checked a few stuff, my user belongs to the same group (uucp) as the device file, I've searched a little bit more about udev rules and added the file /etc/udev/rules.d/50-embedded_devices.rules and the only line I've added is SUBSYSTEMS="usb", ATTRS{product}== "Arduino Micro", GROUP="users", MODE="0666" but nothing changed apparently


text:
Attiny timer interrupt does not make an interrupt every 1000ms
I'm making a stopwatch using an attiny85, the idea was to use the timer interrupts to count the seconds
my theory was: since I set the Attiny85 to run at 1Mhz, I can use a presale of 1024; 1000000 / 1024 = 976.5625 hz, 976.5625hz = 1.024ms, 125ms / 1.024ms = 122.07, so that I need to count to 122 to get a 125ms interrupt, then in the ISR function I can count to 8 to get around 1 second, ( 122 * 1.024 ) * 8 = 999.424ms
What is the problem? well, the problem is that I'm not getting something close to 999.424ms, I compere it with my phone's stopwatch and the attiny seems to start well, but then it gets about 2-3 slower than my phone's stopwatch
#include <TM1637Display.h>

#define CLK 4
#define DIO 3
#define button 1

TM1637Display display (CLK, DIO);
int intr_count = 0;
byte dots = 0b01000000;

volatile int seg;
volatile int min;
volatile bool start = false;

void setup() {
  display.setBrightness(5);
  display.clear();
  pinMode(button, INPUT);

  cli();

  TCCR0A  = 0;
  TCCR0B  = 0;
  TCCR0B |= B00000101;
  TIMSK  |= B00010000;

  OCR0A   = 122;

  sei();
}

void start_counting(){
  if (digitalRead(button) == HIGH){
    start = !start;
    if(start == false){
//    display.clear();
      seg = 0;
      min = 0;
    }
    while(digitalRead(button) == HIGH);
  }
}

void loop() {
  start_counting();
  display.showNumberDecEx(min, dots, true, 2, 0);
  display.showNumberDec(seg, true, 2, 2);

}

ISR(TIMER0_COMPA_vect) {
  TCNT0 = 0;
  if(start == true){

    if (intr_count == 8) {

      seg++;

      if(seg == 60){
        min++;
        min = min % 60;
      }

      seg = seg % 60;

      intr_count = 0;
    } else {
      intr_count++;
    }
  }
}

Are you running this from the internal oscillator? "2-3 slower" 2 to 3 what slower? Milliseconds? And this much slower over the course of what amount of real time?

@timemage yes I'm running the attiny from the internal oscillator . "2 - 3 " seconds  slower, time drift I should say , for example starting both stopwatches at the same time when my phone is 00:10 the attiny stopwatch is 00:08, when my phone is 1:00 the attiny stopwatch is 00:57, on the first seconds the stopwatches seem to be synchronized, but after couple of seconds my phone seems to "count the seconds" much faster

display.showNumberDec(seg, true, 2, 2); This, for example, is a non-atomic read operation on an int16_t. It could have been half updated by the ISR at the time of the call. Best is to suspend interrupts while making a copy of seg and display that.

" ( 122 * 1.024 ) * 8 = 999.424ms" . I guess then OCR0A should be 121 since it starts from 0.

@6v6gt Yeah. I noticed that also, but it's likely under the accuracy of the RC osc itself. It also makes sense to use a mode of the counter that automatically reset TCNT rather than doing that in software, but I don't know how important that will be ultimately either. I probably won't be looking at seriously again for another 10+ hours.

I uploaded the code on an Attiny45 using an external oscillator of 16Mhz, the time is also drifting, although less than with the internal 1Mhz of the Attiny85

It sounds like it is time for you to buy a RTC (Real Time Clock) module. That will be accurate.

You need to have an external 32.768kHz Low-Frequency Crystal Oscillator in order to produce accurate 1Hz clock and use it to configure the timer to generate a 1s clock that you need. Read the datasheet about Low-Frequency Crystal Oscillator and how to configure it.

If Edgar's answer doesn't just answer it, what sort of drift are you observing with the external 16Mhz and is it a crystal, resonator, etc?

I see three issues with this approach.
The first is that you are using a very low quality, uncalibrated time
source. The frequency of the internal RC oscillator is good to within a
few percent only. It is also very unstable, and hugely dependent on
temperature. Using an external 16 Mhz ceramic oscillator should
give you a frequency that is no worse than 0.5% off, with a typical
error about 0.1%. It is still somewhat unstable though. If you instead
are using a crystal oscillator, then you can expect an error about ten
times smaller (in the range of 100 ppm), and a very good stability.
Depending on you quality requirements, 100 ppm may still not be
good enough, in which case you still have the option to measure the
drift rate and calibrate it out (see below).
The second problem, which has already been raised by 6v6g, is that your
timer is counting from zero to 122 inclusive, which gives a period of
123 timer clock cycles. Take a look at the timing diagrams in the
datasheet if you need to convince yourself of this. If you want a period
of 122, you should set OCR0A to 121.
The third problem is that you are resetting the timer with the ISR. This
may work in this program, but it is a very fragile approach worth
avoiding. The problem is that everything the microcontroller does takes
time, and your ISR may even get delayed by another interrupt. If the
timer is incremented after rising the interrupt flag but before you
reset it, you are missing one count. If you want to have a continuous
time scale, never reset your timer. You can either:

let it run continuously, undisturbed (and do OCR0A += 122 within the
ISR),

or let it reset itself by using the appropriate waveform generation
mode (CTC or fast PWM).

Lastly, here is a trick you may use to calibrate your stop watch.
Instead of counting the interrupts until the counter reaches 8
(which assumes an interrupt period of exactly 1/8 s), increment a
nanosecond counter within the ISR, and count one second once you have
one billion nanoseconds:
const uint32_t nanoseconds_per_interrupt = 124928000;

ISR(TIMER0_COMPA_vect) {
    static uint32_t nanoseconds;
    nanoseconds += nanoseconds_per_interrupt;
    if (nanoseconds >= 1000000) {
        nanoseconds -= 1000000;
        seconds++;
        // then update minutes, hours... if needed
    }
}

This will increment the seconds every eight interrupts... most of the
time. From time to time, however, it will take nine interrupts. This
adds a little bit of jitter, but prevents the timing errors from
accumulating and making the clock drift. If the jitter is visible, you
can reduce it by shortening the interrupt period.
The value 124928000 I wrote above assumes the ISR is called every
122×1024 cycles of a 1 MHz clock. You can change it to fit a
different clock frequency or a different OCR0A setting. The trick is:
once you measure the drift rate of your stop watch, you can tweak this
number to remove that drift. For example, if you measure the stopwatch
and find it is running 0.04% too slow, then you increase that number by
0.04% (that would give 124977971) and the drift is gone.
Edit: In a comment, 6v6gt pointed out a fourth issue in your code,
which is likely the biggest issue. Your logic for counting interrupts
is:
if (intr_count == 8) {
    intr_count = 0;
} else {
    intr_count++;
}

This is counting from 0 to 8 inclusive, and looping with a period of
9 interrupts. A safer idiom for looping with a period of 8
would be:
if (++intr_count >= 8) {
    intr_count = 0;
}

Although I would still recommend counting nanoseconds if you want the
ability to calibrate your clock.

( 122 * 1.024 ) * 8 = 999.424ms Maybe the following from the OP is also wrong for the same reason the OCR0A was wrong:  if (intr_count == 8) { . .  It is call 7 (calls running from 0) of the ISR that indicates the end of a full second, not call 8

@6v6gt: Indeed you are right, and this looks like it should introduce a huge error. Added that to the answer.

Thank you, Edgar, for an extremely comprehensive answer. :)


text:
Code not checking for updated sensor values after connecting to firebase?
I am using a project with an MQ138 sensor and want to push the real time sensor reads to firebase. Currently, the wifi/firebase part and sensor part work fine separately. The code connects to wifi and will update a temporary variable (just an int that gets 1 added to it every time the loop runs) and put that on firebase. On a separate arduino file, the code is also able to read the sensor, do math with it, and consistently update. However, when I put the two together and try to print the sensor output, the value does not change at all (both the raw analogRead AND the raw value after being put through some math)--even when it does change on the separate code I ran without any firebase/wifi involved.
Why is this? Could there be some sort of cache that saves the old sensor value? Something with the way the code loops? The only different between the firebase code and the sensor test code (without use of firebase) is that on the Arduino software the board has to be selected as ESP8266 for firebase/wifi connection to work, but the test code uses Arduino Uno as a board. Could there be something with this that is not letting AnalogRead (for the sensor) work properly?
The circuit is wired so that the ESP8266 and MQ138 sensor are both connected to an Arduino Uno.
My code is below (currently, the sensor output is only getting printed in serial, and the integer "n" is consistently getting updated and pushed on firebase, but even the serial output of the sensor won't change):
#include <Firebase.h>
#include <FirebaseArduino.h>
#include <FirebaseCloudMessaging.h>
#include <FirebaseError.h>
#include <FirebaseHttpClient.h>
#include <FirebaseObject.h>
#include <dummy.h>

#include <ESP8266WiFi.h>
#include <FirebaseArduino.h>
#include <SPI.h>
#include <Wire.h>
#include <DHT.h>
#include <Adafruit_BMP085.h>

//passwords have been removed, but are in the actual code
#define FIREBASE_HOST ""
#define FIREBASE_AUTH ""

#define WIFI_SSID ""
#define WIFI_PASSWORD ""

//Hardware Macros for sensor
#define MQ138 (5) //Analog 5 input
#define RL_VALUE (10000) //Load resistance in ohms
//Software macros
#define         CALIBRATION_SAMPLE_TIMES     (40)    //define how many samples you are going to take in the calibration phase
#define         CALIBRATION_SAMPLE_INTERVAL  (500)   //define the time interal(in milisecond) between each samples in the
//cablibration phase
#define         READ_SAMPLE_INTERVAL         (50)    //define how many samples you are going to take in normal operation
#define         READ_SAMPLE_TIMES            (5)     //define the time interal(in milisecond) between each samples in

//Globals for math with the sensor
float AcetoneCurve[2] = {3.720, -2.222}; //A, B
float R0 = 10000; //Initialized to 10k ohms
unsigned long SLEEP_TIME = 600; // Sleep time between reads (in seconds)
int val = 0;          // variable to store the value coming from the sensor
float calcVoltage = 0;
float dustDensity = 0;
boolean metric = true;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  Serial.print("help");
  WiFi.disconnect();
  Serial.setDebugOutput(true);
  /*IPAddress ip(192,168,3,34);
  IPAddress gateway(192,168,3,1);
  IPAddress subnet(255,255,255,0);*/
  WiFi.setAutoReconnect(true);

  //connect to Wifi
  WiFi.mode(WIFI_STA);
  WiFi.hostname("ESP-host");
  //WiFi.config(ip, gateway, subnet);
  WiFi.enableInsecureWEP(true);
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print("connecting");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    Serial.print("Wifi status: ");
    Serial.println(WiFi.status());
    delay(500);
  }
  Serial.println();
  Serial.print("connected: ");
  Serial.println(WiFi.localIP());
  Firebase.begin(FIREBASE_HOST, FIREBASE_AUTH);

  //define pin
  pinMode(MQ138, INPUT);
}

int n = 0;

void loop() {
  // put your main code here, to run repeatedly:
  //MQ138 code
  int raw = analogRead(MQ138);
  static float Ro = MQCalibration(MQ138, 25, RL_VALUE, AcetoneCurve);    //Assuming 25ppm of acetone in clean air?
  float Rs = MQRead(MQ138, RL_VALUE);
  float ratio = Rs / Ro;
  int ppm = MQGetPercentage(ratio, Ro, AcetoneCurve);

 //This prints the same output every time, even when it shouldn't
  Serial.println(raw);
  Serial.print("PPM: ");
  Serial.println(ppm);

//Firebase code
  // set value
  Firebase.setFloat("PPM", n);
  //error catching
  if (Firebase.failed()) {
    Serial.print("setting /number failed:");
    Serial.println(Firebase.error());
    return;
  }
  delay(1000);
  // appending a new value to /logs, CHANGE INT
  String name = Firebase.pushInt("logs", n++);
  //handle error
  if (Firebase.failed()) {
    Serial.print("pushing /logs failed: ");
    Serial.println(Firebase.error());
    return;
  }
  Serial.print("pushed: /logs/");
  Serial.println(name);
  delay(1000);

  if (Firebase.failed()) {
    return;
  }

}

//Functions - for calculating the PPM from a raw sensor reading, can be ignored
/****************** MQResistanceCalculation ****************************************
  Input:   raw_adc - raw value read from adc, which represents the voltage
  Output:  the calculated sensor resistance
  Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage across the load resistor and its resistance, the resistance of the sensor could be derived.
************************************************************************************/
float MQResistanceCalculation(int raw_adc, float rl_value)
{
  return  (long)((long)(1024 * 1000 * (long)rl_value) / raw_adc - (long)rl_value);
  ;
}

/***************************** MQCalibration (R0) ****************************************
  Input:   mq_pin - analog channel
  Output:  Ro of the sensor
  Remarks: This function assumes that the sensor is in clean air. It use
         MQResistanceCalculation to calculates the sensor resistance in clean air.        .
************************************************************************************/
float MQCalibration(int mq_pin, double ppm, double rl_value, float *pcurve )
{
  int i;
  float val = 0;

  for (i = 0; i < CALIBRATION_SAMPLE_TIMES; i++) {      //take multiple samples
    val += MQResistanceCalculation(analogRead(mq_pin), rl_value);
    delay(CALIBRATION_SAMPLE_INTERVAL);
  }
  val = val / CALIBRATION_SAMPLE_TIMES;                 //calculate the average value
  //Ro = Rs * sqrt(a/ppm, b) = Rs * exp( ln(a/ppm) / b )

  return  (long)val * exp((log(pcurve[0] / ppm) / pcurve[1]));

}

/*****************************  MQRead (Rs) *********************************************
  Input:   mq_pin - analog channel
  Output:  Rs of the sensor
  Remarks: This function use MQResistanceCalculation to caculate the sensor resistenc (Rs).
         The Rs changes as the sensor is in the different consentration of the target
         gas. The sample times and the time interval between samples could be configured
         by changing the definition of the macros.
************************************************************************************/
float MQRead(int mq_pin, float rl_value)
{
  int i;
  float rs = 0;

  for (i = 0; i < READ_SAMPLE_TIMES; i++) {
    rs += MQResistanceCalculation(analogRead(mq_pin), rl_value);
    delay(READ_SAMPLE_INTERVAL);
  }

  rs = rs / READ_SAMPLE_TIMES;

  return rs;
}

/*****************************  MQGetPercentage **********************************
  Input:   rs_ro_ratio - Rs divided by Ro
         pcurve      - pointer to the curve of the target gas
  Output:  ppm of the target gas
  Remarks: By using the slope and a point of the line. The x(logarithmic value of ppm)
         of the line could be derived if y(rs_ro_ratio) is provided. As it is a
         logarithmic coordinate, power of 10 is used to convert the result to non-logarithmic
         value.
************************************************************************************/
int  MQGetPercentage(float rs_ro_ratio, float ro, float *pcurve)
{
  return (double)(pcurve[0] * pow(((double)rs_ro_ratio), pcurve[1]));
}

The code to test the sensor, without integrating firebase and ESP8266, is as follows:

//Source: https://github.com/empierre/arduino-mysensors-contribs/blob/master/AirQuality-Multiple_Gas_Sensor1_4.ino

// Libraries
#include <SPI.h>
#include <Wire.h>
#include <DHT.h>
#include <Adafruit_BMP085.h>

//Hardware Macros
#define MQ138 (5) //Analog 5 input
#define RL_VALUE (10000) //Load resistance in ohms
//Software macros
#define         CALIBRATION_SAMPLE_TIMES     (40)    //define how many samples you are going to take in the calibration phase
#define         CALIBRATION_SAMPLE_INTERVAL  (500)   //define the time interal(in milisecond) between each samples in the
                                                     //cablibration phase
#define         READ_SAMPLE_INTERVAL         (50)    //define how many samples you are going to take in normal operation
#define         READ_SAMPLE_TIMES            (5)     //define the time interal(in milisecond) between each samples in

//Globals
float AcetoneCurve[2] = {3.720, -2.222}; //A, B
float R0 = 10000; //Initialized to 10k ohms
unsigned long SLEEP_TIME = 600; // Sleep time between reads (in seconds)
int val = 0;          // variable to store the value coming from the sensor
//idfk what this is
float calcVoltage = 0;
float dustDensity = 0;
boolean metric = true;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  pinMode(MQ138, INPUT);
}

void loop() {
  // put your main code here, to run repeatedly:
  int raw = analogRead(MQ138);
  static float Ro = MQCalibration(MQ138, 25, RL_VALUE, AcetoneCurve);    //Assuming 25ppm of acetone in clean air?
  //https://www.health.ny.gov/environmental/indoors/air/contaminants/acetone.htm#:~:text=Levels%20of%20acetone%20are%20typically,common%20ingredient%20in%20many%20products.
  float Rs = MQRead(MQ138, RL_VALUE);
  float ratio = Rs / Ro;
  int ppm = MQGetPercentage(ratio, Ro, AcetoneCurve);
  Serial.println(raw);
  Serial.print("PPM: ");
  Serial.println(ppm);
}

//Functions
/****************** MQResistanceCalculation ****************************************
Input:   raw_adc - raw value read from adc, which represents the voltage
Output:  the calculated sensor resistance
Remarks: The sensor and the load resistor forms a voltage divider. Given the voltage across the load resistor and its resistance, the resistance of the sensor could be derived.
************************************************************************************/
float MQResistanceCalculation(int raw_adc,float rl_value)
{
  return  (long)((long)(1024*1000*(long)rl_value)/raw_adc-(long)rl_value);
;
}

/***************************** MQCalibration (R0) ****************************************
Input:   mq_pin - analog channel
Output:  Ro of the sensor
Remarks: This function assumes that the sensor is in clean air. It use
         MQResistanceCalculation to calculates the sensor resistance in clean air.        .
************************************************************************************/
float MQCalibration(int mq_pin, double ppm, double rl_value,float *pcurve )
{
  int i;
  float val=0;

  for (i=0;i<CALIBRATION_SAMPLE_TIMES;i++) {            //take multiple samples
    val += MQResistanceCalculation(analogRead(mq_pin),rl_value);
    delay(CALIBRATION_SAMPLE_INTERVAL);
  }
  val = val/CALIBRATION_SAMPLE_TIMES;                   //calculate the average value
  //Ro = Rs * sqrt(a/ppm, b) = Rs * exp( ln(a/ppm) / b )

  return  (long)val*exp((log(pcurve[0]/ppm)/pcurve[1]));

}

/*****************************  MQRead (Rs) *********************************************
Input:   mq_pin - analog channel
Output:  Rs of the sensor
Remarks: This function use MQResistanceCalculation to caculate the sensor resistenc (Rs).
         The Rs changes as the sensor is in the different consentration of the target
         gas. The sample times and the time interval between samples could be configured
         by changing the definition of the macros.
************************************************************************************/
float MQRead(int mq_pin,float rl_value)
{
  int i;
  float rs=0;

  for (i=0;i<READ_SAMPLE_TIMES;i++) {
    rs += MQResistanceCalculation(analogRead(mq_pin),rl_value);
    delay(READ_SAMPLE_INTERVAL);
  }

  rs = rs/READ_SAMPLE_TIMES;

  return rs;
}

/*****************************  MQGetPercentage **********************************
Input:   rs_ro_ratio - Rs divided by Ro
         pcurve      - pointer to the curve of the target gas
Output:  ppm of the target gas
Remarks: By using the slope and a point of the line. The x(logarithmic value of ppm)
         of the line could be derived if y(rs_ro_ratio) is provided. As it is a
         logarithmic coordinate, power of 10 is used to convert the result to non-logarithmic
         value.
************************************************************************************/
int  MQGetPercentage(float rs_ro_ratio, float ro, float *pcurve)
{
  return (double)(pcurve[0] * pow(((double)rs_ro_ratio), pcurve[1]));
}

Any help would be much appreciated!


text:
ESP32-CAM rst:0x8 TG1WDT_SYS_RESET bootloop
I have ESP32-CAM that I plan to use as a simple security camera. I tested the software and it was working perfectly. I programmed it using FTDI programming board with 5V. After testing everything I decided to power it with an old adapter (5V 160mA) I had for some time but when I powered with adapter the power started flickering. When I noticed it I immediately unplugged the adapter but since then I started getting stuck in bootloop. Here is the output I get:
12:56:10.663 -> ets Jul 29 2019 12:21:46
12:56:10.667 ->
12:56:10.667 -> rst:0x1 (POWERON_RESET),boot:0x3 (DOWNLOAD_BOOT(UART0/UART1/SDIO_REI_REO_V2))
12:56:10.667 -> waiting for download
12:56:49.327 -> ets Jul 29 2019 12:21:46
12:56:49.327 ->
12:56:49.327 -> rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
12:56:49.327 -> configsip: 0, SPIWP:0xee
12:56:49.327 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
12:56:49.327 -> mode:DIO, clock div:2
12:56:49.327 -> load:0x3fff0030,len:1344
12:56:49.327 -> load:0x40078000,len:13836
12:56:49.327 -> load:0x40080400,len:3608
12:56:49.327 -> entry 0x400805f0
12:56:50.707 -> ets Jul 29 2019 12:21:46
12:56:50.707 ->
12:56:50.707 -> rst:0x8 (TG1WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
12:56:50.707 -> configsip: 0, SPIWP:0xee
12:56:50.707 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
12:56:50.707 -> mode:DIO, clock div:2
12:56:50.707 -> load:0x3fff0030,len:1344
12:56:50.707 -> load:0x40078000,len:13836
12:56:50.707 -> load:0x40080400,len:3608
12:56:50.707 -> entry 0x400805f0
12:56:52.086 -> ets Jul 29 2019 12:21:46
12:56:52.086 ->
12:56:52.086 -> rst:0x8 (TG1WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)
12:56:52.086 -> configsip: 0, SPIWP:0xee
12:56:52.086 -> clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
12:56:52.086 -> mode:DIO, clock div:2
12:56:52.086 -> load:0x3fff0030,len:1344
12:56:52.086 -> load:0x40078000,len:13836
12:56:52.086 -> load:0x40080400,len:3608
12:56:52.086 -> entry 0x400805f0

It keeps going like this. I can upload new code without any problems but every code I upload gets stuck in this exact bootloop. Load and entry adresses does not change. I also tried programming with arduino nano instead of ftdi but nothing changed.
I am using Arduino IDE 2.0.0 on Linux Mint 21 OS. I also tried it on Windows 10 but the results didn't change. I am quite new to ESP32 so I don't know how to debug it either.
Thanks in advance.
##############
UPDATE 25/10/2022
After timemage's comment I tried a few more codes and one of arduino ide's example codes ran once with error. I couldn't get the error log but there was something like "PSRAM ID Read error 0xffffff" and another log pointing to a corrupted address.
After that I tried uploading another sketch without PSRAM with following settings:

Board: ESP32 Dev Module
CPU frequency: 240MHz(WiFi/BT)
Events run on: Core 1
Flash frequency: 80MHz
Flash mode: QIO
Flash size: 4MB(32Mb)
Arduino runs on: Core 1
PSRAM: Disabled
Partition scheme: Huge
APP(3MB No OTA/1MB SPIFFS)
Upload speed: 115200

and it worked. But I need PSRAM for my project. So my question is: can it be a hardware problem ? If not, how can I fix it ?
Thanks again.

"every code I upload gets stuck in this exact bootloop" This holds true if that new code is just a blink sketch or hello world on serial?

@timemage It holded true for every sketch I tried so far. But after your comment I tried more sketches and made some more progress and edited my question, so thanks.

Selecting "AI Thinker ESP32-CAM" doesn't expose the menu option called "PSRAM". You say "tried uploading another sketch without PSRAM"  This makes me wonder: What board have you been selecting? If not the AI Thinker ESP32-CAM, what happens when you switch to it? Were you using AI Thinker board and then switched to a generic option just so that you'd be able to test with PSRAM set to "Disabled"?

@timemage I selected "ESP32 Dev Module". Added details about configurations to question.

It is starting to look like damage. I took your custom settings and tested them on a ESP32-CAM with nothing attached and did not get your boot loop. The PSRAM chip is not under the RF shield, which makes it comparatively replaceable.  If it were me, I might consider probing the PSRAM chip from another board, but it's not clear how to do that with the the ESP32 in reset, because the SPI FLASH chip on the same bus is not pulled to inactive and that contact is not exposed. Hopefully someone else has a better idea of what if anything to try next.

Is your esp32_cam comes with PSRAM?  I had a program running on esp32 wrover with PSRAM, the same code could not run on esp32 wroom.  After checking, the esp32 wroom does not come with PSRAM.

Say that the esp32_cam does come with PSRAM, how do you initilize it?

My code has ISR to force shutdown after 15min of wakeup from deep sleep, I will get the same error with reset when there is a while loop or for loop, and i missed these 2 line of codes.
vTaskDelay( 3 / portTICK_PERIOD_MS );
esp_task_wdt_reset();

hopefully, this helps.

I got the same "rst:0x8 (TG1WDT_SYS_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT)" and crash when using the M5stack-Timer-Camera after doing a "SerialM.setDebugOutput(true);" then starting the camera with "camera_init()" and then doing an "EEPROM.commit".
When I remove the "SerialM.setDebugOutput(true);" the error/crash goes away and instead I just get a (Serial-Terminal) warning of "cam_hal: EV-EOF-OVF" when doing an "EEPROM.commit".
So in my case I suspect that the "SerialM.setDebugOutput(true)" was causing the "cam_hal: EV-EOF-OVF" error message to crash during the "EEPROM.commit".

From my personal experience working with the ESP-CAM's ESP32-s3 chip, the particular error is the result of an invalid line of code.
The best way to address this is to look very closely at the code that you've written, for some of the errors you may introduce through the arduino ide may not be caught in the compilation process.
For example, I was building out a UART shell using the same processor.
My code was producing the same error because of this code here:
private:
    size_t commandsLen = 1;
    string commands[1]{
        "lcdcam"
    };

I was able to fix the reset loop by correcting the syntax in my string array. Which was missing an equals sign.
The following modification fixed my particular issue, and odds are that your problem is syntax related like mine.
private:
    size_t commandsLen = 1;
    string commands[1] = {
        "lcdcam"
    };

Edit:
Upon further inspection, there was an additional issue that also caused this error.
Take the following code into consideration:
public:
    bool commandSwitch(Uart uart, BosCommand cmd){
        if(cmd.getCommandLength() <= 0){
            sendOutput(uart, "Failed to find commands.\n\r");
            return false;
        }
        string target = cmd.getCommandArg(0);

        if(target == "lcdcam"){
            BosCommandSwitch targetCmd;
            targetCmd.init(uart, cmd);
            return targetCmd.process();
        }else{
           // removed
        }
        return false;
    }

The issue is that the processor doesn't like it when I declare the BosCommandSwitch class inside the if logic. Not only did fixing the string array correct the reset bug, but moving the declaration of the class object up into the private section of my class allowed all my code to run without the automated reset.
private:
    BosCommandSwitch targetCmd;
public:
    bool commandSwitch(Uart uart, BosCommand cmd){
        if(cmd.getCommandLength() <= 0){
            sendOutput(uart, "Failed to find commands.\n\r");
            return false;
        }
        string target = cmd.getCommandArg(0);

        if(target == "lcdcam"){
            targetCmd.init(uart, cmd);
            return targetCmd.process();
        }else{
           // removed
        }
        return false;
    }

I believe that the issue is related to memory allocation. Once you get deep into logic, you reduce the amount of available memory in a particular region. Though this is just speculation on my part.

I had a similar problem when running test code for a data input module based on the NodeMCU-32s board. The board would reset about once per second with that same error. That turned out to be my error. I had confused the physical pin number of the board with the GPIO number. I am assuming that trying to read GPIO pins that are not routed to the chip pins was still accessing internal registers, and was enough to initialize the watchdog timer. Since I was otherwise doing nothing to the watchdog, (like resetting it frequently,) the watchdog was properly doing its job and resetting the part.
This wasn't obvious to me. In the Arduino environment, setting Tools->Core Debug Level to Verbose gave some clues. Turning off some of my use of GPIO inputs also got me to a point where the watchdog error stopped, and from there I was able to proceed. So long story shorter, I recommend checking your pin assignments for IO!


text:
Does the bootloader use some SRAM?
I want to use all SRAM of an Arduino by writing the code in assembly. But does the bootloader use some SRAM while the program is running, and if the answer is yes would changing that data lead to failure or does the data stored there get corrupted? Thanks for reaching out.

You did not specify what type of Arduino you are using. My answer is for
the AVR-based ones.
The bootloader does use RAM while it is running. However, once it
handles control to your program, the microcontroller is all yours: you
are free to use all the RAM.
Id did not check with optiboot but, it is possible that the bootloader
leaves some stuff in the stack. The C runtime of the avr-libc takes care
of this: one of the first things it does is to reset the stack pointer.
If you link with the avr-libc, you will find the stack empty. If you
don't, I suggest you set the stack pointer to RAMEND at the beginning of
your program, just in case.

I expecting that it didn't, but apparently optiboot as it was compiled for UNO leaves 2 bytes on the stack, entering application code with SP = 0x8FD. It doesn't matter for the reason you pointed out, but it's fun useless trivia I guess. =)

Whether you need those two bytes depends on an architecture detail: is the stack pointer written, then decremented or the other way round on the avr cpu?

@PMF: On AVR, the stack pointer is post-decremented when writing to the stack.


text:
Array of Functions
I'm new to C++. How to make a menu without if() {} else {} & switch() case?
I made a menu on an array, but for some reason it doesn't compile. How to correct it?
typedef void (*cbd)(uint8_t, uint8_t);
typedef void (*cbt)(uint8_t, uint8_t);
typedef void (*cba)(uint8_t, uint8_t);
typedef void (*MenuFunction) (int8_t);

class Menu {
public:
    Menu() {
        updownFns[5] = {updownNone, updownAlarmHours, updownAlarmMinutes, updownTimeHours, updownTimeMinutes};
    }

    static void Setup(cbd display, cbt setTime, cba setAlarm, uint8_t alarmHours, uint8_t alarmMinutes) {
        cbDisplay = display;
        cbSetTime = setTime;
        cbSetAlarms = setAlarm;
        alarmHours = alarmHours;
        alarmMinutes = alarmMinutes;
        cbSetAlarms(alarmHours, alarmMinutes);
    }
    // Used for menu.UpDown(dir) callback
    static void UpDown(int8_t direction) {
        updownFn(direction);
    }
    // Used for menu.Tab() callback
    static void Tab() {
        updownFnNum++;
        updownFn = updownFns[updownFnNum % 5];
    }

private:

    static void updownNone(int8_t direction) { }

    static void updownAlarmHours(int8_t direction) {
        alarmHours += direction;
        cbDisplay(alarmHours, alarmMinutes);
        cbSetAlarms(alarmHours, alarmMinutes);
    }

    static void updownAlarmMinutes(int8_t direction) {
        alarmMinutes += direction;
        cbDisplay(alarmHours, alarmMinutes);
        cbSetAlarms(alarmHours, alarmMinutes);
    }

    static void updownTimeHours(int8_t direction) {
        timeHours += direction;
        cbSetTime(timeHours, timeMinutes);
        cbDisplay(timeHours, timeMinutes);
    }

    static void updownTimeMinutes(int8_t direction) {
        timeMinutes += direction;
        cbSetTime(timeHours, timeMinutes);
        cbDisplay(timeHours, timeMinutes);
    }

    static uint8_t alarmHours, alarmMinutes, timeHours, timeMinutes;
    static uint8_t updownFnNum; // 0-None, 1-AlarmH, 2-AlarmM, 3-TimeH, 4-TimeM
    static cbd cbDisplay;
    static cbt cbSetTime;
    static cba cbSetAlarms;
    static MenuFunction updownFns[5];
    static MenuFunction updownFn;
};

uint8_t Menu::alarmHours = 0;
uint8_t Menu::alarmMinutes = 0;
uint8_t Menu::timeHours = 0;
uint8_t Menu::timeMinutes = 0;
uint8_t Menu::updownFnNum = 0;
cbd Menu::cbDisplay = NULL;
cbt Menu::cbSetTime = NULL;
cba Menu::cbSetAlarms = NULL;
MenuFunction Menu::updownFn = NULL;

Output:
In file included from app.ino:7:0:
menu.h: In constructor 'Menu::Menu()':
menu.h:9:109: error: cannot convert '<brace-enclosed initializer list>' to 'MenuFunction {aka void (*)(signed char)}' in assignment
updownFns[5] = {updownNone, updownAlarmHours, updownAlarmMinutes, updownTimeHours, updownTimeMinutes};

Thank you!

The C++ language makes a distinction between an initialization and an
assignment. An initialization is the initial value you give to a
variable at the point where you are defining it. An assignment is the
use of the = operator anywhere else. Also, the language does not allow
assigning the contents of an array as a whole. You can initialize an
array though.
This:
Menu() {
    updownFns[5] = {updownNone, updownAlarmHours, updownAlarmMinutes,
        updownTimeHours, updownTimeMinutes};
}

is an attempt to assign a whole array. It cannot work. If you want to
initialize the array, do it at the point where it is being defined,
outside of the class definition:
MenuFunction Menu::updownFns[5] = {updownNone, updownAlarmHours,
    updownAlarmMinutes, updownTimeHours, updownTimeMinutes};

First, your updownFns[5] can be contain 5 variable. And the last variable index can be 4 not 5 because of array index starts from 0.
Here is the code :
typedef void (*cbd)(uint8_t, uint8_t);
typedef void (*cbt)(uint8_t, uint8_t);
typedef void (*cba)(uint8_t, uint8_t, uint8_t);
typedef void (*MenuFunction) (int8_t);

class Menu {
public:
    Menu() {
        updownFns[0] = updownNone;
        updownFns[1] = updownAlarmHours;
        updownFns[2] = updownAlarmMinutes;
        updownFns[3] = updownTimeHours;
        updownFns[4] = updownTimeMinutes;
    }

    static void Setup(cbd display, cbt setTime, cba setAlarm, uint8_t alarmHours, uint8_t alarmMinutes) {
        cbDisplay = display;
        cbSetTime = setTime;
        cbSetAlarms = setAlarm;
        alarmHours = alarmHours;
        alarmMinutes = alarmMinutes;
        cbSetAlarms(alarmHours, alarmMinutes);
    }
    // Used for menu.UpDown(dir) callback
    static void UpDown(int8_t direction) {
        updownFn(direction);
    }
    // Used for menu.Tab() callback
    static void Tab() {
        updownFnNum++;
        updownFn = updownFns[updownFnNum % 5];
    }

private:

    static void updownNone(int8_t direction) { }

    static void updownAlarmHours(int8_t direction) {
        alarmHours += direction;
        cbDisplay(alarmHours, alarmMinutes);
        cbSetAlarms(alarmHours, alarmMinutes);
    }

    static void updownAlarmMinutes(int8_t direction) {
        alarmMinutes += direction;
        cbDisplay(alarmHours, alarmMinutes);
        cbSetAlarms(alarmHours, alarmMinutes);
    }

    static void updownTimeHours(int8_t direction) {
        timeHours += direction;
        cbSetTime(timeHours, timeMinutes);
        cbDisplay(timeHours, timeMinutes);
    }

    static void updownTimeMinutes(int8_t direction) {
        timeMinutes += direction;
        cbSetTime(timeHours, timeMinutes);
        cbDisplay(timeHours, timeMinutes);
    }

    static uint8_t alarmHours, alarmMinutes, timeHours, timeMinutes;
    static uint8_t updownFnNum; // 0-None, 1-AlarmH, 2-AlarmM, 3-TimeH, 4-TimeM
    static cbd cbDisplay;
    static cbt cbSetTime;
    static cba cbSetAlarms;
    static MenuFunction updownFns[5];
    static MenuFunction updownFn;
};

uint8_t Menu::alarmHours = 0;
uint8_t Menu::alarmMinutes = 0;
uint8_t Menu::timeHours = 0;
uint8_t Menu::timeMinutes = 0;
uint8_t Menu::updownFnNum = 0;
cbd Menu::cbDisplay = NULL;
cbt Menu::cbSetTime = NULL;
cba Menu::cbSetAlarms = NULL;
MenuFunction Menu::updownFn = NULL;
// MenuFunction Menu::updownFns = {};

But still code is not correct. if you look at the signature of this definition
typedef void (*cba)(uint8_t, uint8_t, uint8_t);

it takes 3 parameters. But inside the code, you give 2 parameter to the function
cbSetAlarms(alarmHours, alarmMinutes)

It's a typo, I've corrected it. But the error is the same. typedef void (*cba)(uint8_t, uint8_t);

It's not typo, typo means something different.
if you change typedef void (*cba)(uint8_t, uint8_t, uint8_t); to typedef void (*cba)(uint8_t, uint8_t);
Code wil compile without errors

updownFns[0] = updownNone; It gave:
/tmp/cc3MHoSt.ltrans0.ltrans.o: In function global constructors keyed to 65535_0_app.ino.cpp.o.4201': <artificial>:(.text.startup+0x144): undefined reference to Menu::updownFns'
:(.text.startup+0x148): undefined reference to `Menu::updownFns'
collect2: error: ld returned 1 exit status
exit status 1
Error compiling for board Arduino Pro or Pro Mini.

There are several issue with your code. You can use like this
Remove initilization from ctor and init static variable outside.
MenuFunction Menu::updownFns[] = {updownNone, updownAlarmHours, updownAlarmMinutes, updownTimeHours, updownTimeMinutes };
But when you want to use updownFns it will give errors because of the function is private within this context also.

I initialized the array outside of the class definition. No errors. I then used updownFns and no errors.

Yes, i already said about it :)


text:
Problem with character concatenation algorithm in matrix led
good afternoon, I am making a 7x10 led matrix in which I use a CD4017 to handle the 7 rows and 2 cascaded shift registers to handle the 10 columns.
I first tried a programming to turn on my entire led matrix and it worked fine. Then, I tried to make a character appear statically and it worked fine. Next, I tried to make that character sweep from right to left and it worked fine. But, what I am having a hard time with is sweeping the message from right to left smoothly with a column of character to character spacing.
What I originally thought is that if I wanted to move a character from right to left, I have to shift the column values for all the rows by an appropriate amount (shift step) and once the character has been shifted enough, I start feeding the column values of the next character in the message for which, the formula that would create the right to left shift effect is:
(current_char<scroll) | (next_char>>(8-shift_step)-scroll)
But I don't get the expected result, the message scrolls from right to left but it doesn't do it with a column of separation between character and character and it doesn't do it in a fluid way either. In addition to that at the end of sweeping the message appears something strange that should not be there.
I recorded a video showing what I am saying: https://drive.google.com/file/d/1ebO5zassWY060mvWACPhcSLeOi5JmZAC/view
Code:
/*
 char_data is a two dimensional constant array that holds the 5-bit column values
 of individual rows for ASCII characters that are to be displayed on a 7x10 matrix.
*/
const byte char_data[95][7]={
    {0, 0, 0, 0, 0, 0, 0}, // space
    {0b100, 0b100, 0b100, 0b100, 0b100, 0, 0b100}, // !
    {0b1010, 0b1010, 0b1010, 0, 0, 0, 0}, // "
    {0b1010, 0b1010, 0b11111, 0b1010, 0b11111, 0b1010, 0b1010}, // #
    {0b100, 0b1111, 0b10100, 0b1110, 0b101, 0b11110, 0b100}, // $
    {0b11000, 0b11001, 0b10, 0b100, 0b1000, 0b10011, 0b11}, // %
    {0b1000, 0b10100, 0b10100, 0b1000, 0b10101, 0b10010, 0b1101}, // &
    {0b100, 0b100, 0b100, 0, 0, 0, 0}, // '
    {0b100, 0b1000, 0b10000, 0b10000, 0b10000, 0b1000, 0b100}, // (
    {0b100, 0b10, 0b1, 0b1, 0b1, 0b10, 0b100}, // )
    {0b100, 0b10101, 0b1110, 0b100, 0b1110, 0b10101, 0b100}, // *
    {0, 0b100, 0b100, 0b11111, 0b100, 0b100, 0}, // +
    {0, 0, 0, 0, 0b100, 0b100, 0b1000}, // ,
    {0, 0, 0, 0b11111, 0, 0, 0}, // -
    {0, 0, 0, 0, 0, 0, 0b100}, // .
    {0, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0}, // /
    {0b1110, 0b10001, 0b10011, 0b10101, 0b11001, 0b10001, 0b1110}, // 0
    {0b110, 0b1100, 0b100, 0b100, 0b100, 0b100, 0b1110}, // 1
    {0b1110, 0b10001, 0b1, 0b110, 0b1000, 0b10000, 0b11111}, // 2
    {0b11111, 0b1, 0b10, 0b110, 0b1, 0b10001, 0b1111}, // 3
    {0b10, 0b110, 0b1010, 0b10010, 0b11111, 0b10, 0b10}, // 4
    {0b11111, 0b10000, 0b11110, 0b1, 0b1, 0b10001, 0b1110}, // 5
    {0b01110, 0b10001, 0b10000, 0b11110, 0b10001, 0b10001, 0b1110}, // 6
    {0b11111, 0b1, 0b10, 0b100, 0b1000, 0b1000, 0b1000}, // 7
    {0b01110, 0b10001, 0b10001, 0b1110, 0b10001, 0b10001, 0b1110}, // 8
    {0b01110, 0b10001, 0b10001, 0b1111, 0b1, 0b10001, 0b1110}, // 9
    {0, 0, 0b100, 0, 0b100, 0, 0}, // :
    {0, 0, 0b100, 0, 0b100, 0b100, 0b1000}, // ;
    {0b10, 0b100, 0b1000, 0b10000, 0b1000, 0b100, 0b10}, // <
    {0, 0, 0b11111, 0, 0b11111, 0, 0}, // =
    {0b1000, 0b100, 0b10, 0b1, 0b10, 0b100, 0b1000}, // >
    {0b1110, 0b10001, 0b1, 0b10, 0b100, 0, 0b100}, // ?
    {0b1110, 0b10001, 0b10111, 0b10101, 0b10110, 0b10000, 0b1111}, // @
    {0b1110, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001}, // A
    {0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b11110}, // B
    {0b1110, 0b10001, 0b10000, 0b10000, 0b10000, 0b10001, 0b1110}, // C
    {0b11110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b11110}, // D
    {0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b11111}, // E
    {0b11111, 0b10000, 0b10000, 0b11110, 0b10000, 0b10000, 0b10000}, // F
    {0b1110, 0b10001, 0b10000, 0b10111, 0b10101, 0b10001, 0b1110}, // G
    {0b10001, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b10001}, // H
    {0b11111, 0b100, 0b100, 0b100, 0b100, 0b100, 0b11111}, // I
    {0b1, 0b1, 0b1, 0b1, 0b1, 0b10001, 0b1110}, // J
    {0b10001, 0b10001, 0b10010, 0b11100, 0b10010, 0b10001, 0b10001}, // K
    {0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b10000, 0b11111}, // L
    {0b10001, 0b11011, 0b10101, 0b10101, 0b10001, 0b10001, 0b10001}, // M
    {0b10001, 0b10001, 0b11001, 0b10101, 0b10011, 0b10001, 0b10001}, // N
    {0b1110, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b1110}, // O
    {0b11110, 0b10001, 0b10001, 0b11110, 0b10000, 0b10000, 0b10000}, // P
    {0b1110, 0b10001, 0b10001, 0b10001, 0b10101, 0b10011, 0b1111}, // Q
    {0b11110, 0b10001, 0b10001, 0b11110, 0b10001, 0b10001, 0b10001}, // R
    {0b1111, 0b10000, 0b10000, 0b1110, 0b1, 0b1, 0b11110}, // S
    {0b11111, 0b100, 0b100, 0b100, 0b100, 0b100, 0b100}, // T
    {0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b1110}, // U
    {0b10001, 0b10001, 0b10001, 0b10001, 0b10001, 0b1010, 0b100}, // V
    {0b10001, 0b10001, 0b10001, 0b10101, 0b10101, 0b11011, 0b10001}, // W
    {0b10001, 0b10001, 0b1010, 0b100, 0b1010, 0b10001, 0b10001}, // X
    {0b10001, 0b10001, 0b1010, 0b100, 0b100, 0b100, 0b100}, // Y
    {0b11111, 0b1, 0b10, 0b100, 0b1000, 0b10000, 0b11111}, // Z
    {0b1110, 0b1000, 0b1000, 0b1000, 0b1000, 0b1000, 0b1110}, // [
    {0, 0b10000, 0b1000, 0b100, 0b10, 0b1, 0}, // backward slash
    {0b1110, 0b10, 0b10, 0b10, 0b10, 0b10, 0b1110}, // ]
    {0, 0, 0b100, 0b1010, 0b10001, 0, 0}, // ^
    {0, 0, 0, 0, 0, 0, 0b11111}, // _
    {0b1000, 0b100, 0b10, 0, 0, 0, 0}, // `
    {0, 0, 0b1110, 0b1, 0b1111, 0b10001, 0b1111}, // a
    {0b10000, 0b10000, 0b10000, 0b11110, 0b10001, 0b10001, 0b11110}, // b
    {0, 0, 0b1110, 0b10001, 0b10000, 0b10001, 0b1110}, // c
    {0b1, 0b1, 0b1, 0b1111, 0b10001, 0b10001, 0b1111}, // d
    {0, 0, 0b1110, 0b10001, 0b11111, 0b10000, 0b1111}, // e
    {0b1110, 0b1001, 0b11100, 0b1000, 0b1000, 0b1000, 0b1000}, // f
    {0, 0, 0b1110, 0b10001, 0b1111, 0b1, 0b1110}, // g
    {0b10000, 0b10000, 0b10000, 0b11110, 0b10001, 0b10001, 0b10001}, // h
    {0b100, 0, 0b100, 0b100, 0b100, 0b100, 0b1110}, // i
    {0b1, 0, 0b11, 0b1, 0b1, 0b10001, 0b1110}, // j
    {0b10000, 0b10000, 0b10001, 0b10010, 0b11100, 0b10010, 0b10001}, // k
    {0b1100, 0b100, 0b100, 0b100, 0b100, 0b100, 0b1110}, // l
    {0, 0, 0b11110, 0b10101, 0b10101, 0b10101, 0b10101}, // m
    {0, 0, 0b11110, 0b10001, 0b10001, 0b10001, 0b10001}, // n
    {0, 0, 0b1110, 0b10001, 0b10001, 0b10001, 0b1110}, // o
    {0, 0, 0b1111, 0b1001, 0b1110, 0b1000, 0b1000}, // p
    {0, 0, 0b1111, 0b10001, 0b1111, 0b1, 0b1}, // q
    {0, 0, 0b10111, 0b11000, 0b10000, 0b10000, 0b10000}, // r
    {0, 0, 0b1111, 0b10000, 0b1110, 0b1, 0b11110}, // s
    {0b100, 0b100, 0b1110, 0b100, 0b100, 0b100, 0b11}, // t
    {0, 0, 0b10001, 0b10001, 0b10001, 0b10011, 0b1101}, // u
    {0, 0, 0b10001, 0b10001, 0b10001, 0b1010, 0b100}, // v
    {0, 0, 0b10001, 0b10001, 0b10101, 0b11111, 0b10101}, // w
    {0, 0, 0b10001, 0b1010, 0b100, 0b1010, 0b10001}, // x
    {0, 0, 0b10001, 0b10001, 0b1111, 0b1, 0b11110}, // y
    {0, 0, 0b11111, 0b10, 0b100, 0b1000, 0b11111}, // z
    {0b10, 0b100, 0b100, 0b1000, 0b100, 0b100, 0b10}, // {
    {0b100, 0b100, 0b100, 0b100, 0b100, 0b100, 0b100}, // |
    {0b1000, 0b100, 0b100, 0b10, 0b100, 0b100, 0b1000}, // }
    {0, 0, 0, 0b1010, 0b10101, 0, 0} // ~
};
char message[] = "MATRIX LED 7X10 WITH ATMEGA328p";
int string_length = strlen(message);
byte shift_step = 1;

void setup(){
    // PORTB as output.
    // Pin 13: ClockRegister
    // Pin 12: Latch
    // Pin 11: Data
    // Pin 10: Reset
    // Pin 9: Clock4017
    DDRB = 0b111111;
    // Makes sure the 4017 value is 0.
    PORTB |= (1 << PB2);
    PORTB &= ~(1 << PB2);
    Serial.begin(9600);
}

void send_data(uint16_t data){
    uint16_t mask = 1, flag = 0;
    for (byte i=0; i<10; i++){
        flag = data & mask;
        if (flag) PORTB |= (1 << PB3);
        else PORTB &= ~(1 << PB3);
        PORTB |= (1 << PB5);
        PORTB &= ~(1 << PB5);
        mask <<= 1;
    }
    PORTB |= (1 << PB4);
    PORTB &= ~(1 << PB4);
}

void display_pattern(byte loops){
    for (byte c=0; c<string_length; c++){ // For each character
        for (byte scroll=0; scroll<10; scroll++){ // For each column.
            for (byte t=0; t<loops; t++){ // The delay we get with loops.
                for (byte z=0; z<7; z++){ // For each row.
                    // To obtain the position of the current character in the char_data array, subtract 32 from the ASCII value of the character itself.
                    byte index = message[c];
                    byte temp = char_data[index-32][z];
                    byte index_2 = message[c+1];
                    byte temp_2 = char_data[index_2-32][z];
                    send_data((temp<<scroll) | (temp_2>>(8-shift_step)-scroll));
                    delayMicroseconds(800);
                    // Clear the row so we can go on to the next row without smearing.
                    send_data(0);
                    // On to the next row.
                    PORTB |= (1 << PB1);
                    PORTB &= ~(1 << PB1);
                }
                // Select the first row.
                PORTB |= (1 << PB2);
                PORTB &= ~(1 << PB2);
            }
        }
    }
}

void loop(){
    display_pattern(10);
    delay(100000);
}

I appreciate the help.

For the strange char at the end of the display, I think your index_2 is accessing beyond the strlen(message) toward the end of the message.

Slow down your Youtube video to playback rate of 0.25, you will see why it is not display the char smoothly...

Here are a few issues that I noticed in this code. I think that, if you
manage to address them, you should get a smooth display:

In the expression (temp_2>>(8-shift_step)-scroll), the amount of
shift will be negative when scroll gets larger than 7. A righ
shift by a negative amount is not a left shift: it is undefined
behavior. Judging by the video, it seems the compiler does no shit
at all in this case.

If you want a one pixel space between the characters, you should
move to the next character in the string every six pixels, thus the
scroll variable should count from 0 to 5.

Be aware that there could be up to three characters simultaneously
(partially) displayed on the screen: when one character is centered,
you should be able to see the edges of both its neighbors.

As noted by hcheung in a comment, when you get to the last character
in the string, the expression message[c+1] is reading past the end
of that string.

You are right about the scrolling, I reduced the range of the scroll for to scroll<7 and it doesn't get stuck but the character fades earlier. I can't figure out the correct formula for when the scroll is greater than 6. Video: https://drive.google.com/file/d/1bkDzEiGrz1c6avpHSnl6ox7D34MD-mp9/view?ts=635b02e3

I tried to do this:
if (scroll < 7) send_data((temp<<scroll) + (temp_2 >> 6-scroll));
else send_data((temp<<scroll) + (temp_2 << (scroll-6)));
But it behaves strangely, the first character goes through, then it blinks, goes back and goes on again.

@LucioMazzini: Hard to help debugging without seeing the whole code. That being said, I think the right formula should be send_data(temp<<scroll+6|temp_2<<scroll|temp_3>>6-scroll);.

Where temp would be the current character, temp_2 the next character and temp_3 the character that comes after temp_2 right?

@LucioMazzini: temp_2 is the only character that fits completely in the display (there can only be one). temp is the character (if any) that is partially displayed to the left of temp_2, only visible when scroll≤3. temp_3 is the character (if any) that is partially displayed to the right of temp_2, only visible when scroll≥2. Note that you will have to pad your message with spaces on both ends.

Hello, well, I have been able to give a twist to my programming and the sweeping of the characters is working correctly but I am having a little problem... The characters are displayed in mirror.

Video: https://drive.google.com/file/d/1qM7w7Zx73k8dR_xCpCwLOzvuMJoXU7vZ/view

Code: https://pastebin.com/seL3RTch

Forget it, I already solved it. I started the mask at 0b10000 and scroll the mask to the right and the binary offset is 4-column. It works fine now, thanks.


text:
Connect OLED SSD1306 to Elegoo Uno R3
I'm trying to connect the OLED SSD1306 screen to my Elegoo Uno R3 and display something using Arduino IDE. However, nothing shows up on the screen for some reason.
I have connected every pin in the following way:

VCC: 3.3V
GND: GND
SCL: A5
SDA: A4

You can see the setup in the following pictures:

Then, I'm using the code from this tutorial, that is, this code, to see if the setup is correct, but nothing shows up on the screen. My question is: does anybody know if this is right way to do it? Even though I'm using analog pins and the screen needs digital ones, I read online that it doesn't matter:

The analog input pins can be used as digital pins, referred to as A0, A1, etc. The exception is the Arduino Nano, Pro Mini, and Mini’s A6 and A7 pins, which can only be used as analog inputs.

I also saw in Elegoo Uno R3's guide that the A4 and A5 pins are used for the I2C protocol, which is the one that this OLED screen uses:

So as far as I know, from the hardware point of view, everything seems to be correct, right?

Did you check that your code uses the correct I2C address for your particular OLED display? If you're not sure about the address, you could run the I2C scan example sketch to find it. You can find it in the Arduino IDE in Examples->Wire->WireScan.

@StarCat Yes, it's the 0x3C address: Scanning... I2C device found at address 0x3C! done

Did you need to change any parameters in the sample code to match your display e.g. SCREEN_HEIGHT ?

Many of these modules have their own onboard 3.3V regulator. You appear to power it from 3.3V while the tutorial you cited is powering it from 5V. Perhaps it "works" enough with 3.3V minus regulator-dropout to respond to I2C but not enough to actually light the display.

Well, I found one of my own displays with similar (not same) looking design, which I can see has a regulator. It didn't seem to care whether it was powered it from 5V or 3.3V. Worked with your code in both cases.  I don't know how definitive that is though. If yours is a type designed to operate with 5V into VCC I would still try it. I wouldn't have used the colors where you did, but I don't see anything technically wrong with your wiring.  My display also responds to 3C.

You are close. Looks like you have a run-of-the-mill, clone [not Adafruit], 128x64 LCD with / I2C interface.

Determine the I2C address by flipping it over and seeing which side a 0 ohm resistor or solder blob is on - either 0 or 1. If it is on the '0' side, the address is 0x3C if it is on the '1' side, it is 0x3D.

Here is a photo of the back and with the address of 0x3c.

If the back of yours looks very different, locate and run an I2C device scanner to find the address of your display.

Do not use the code that you linked.

From the Arduino IDE, install or update the the Adafruit SSD1306 library, Version 2.57 is what I used to test this.
Once the library is in place, load the example SSD1306_128X64_I2C
Locate this line (near the beginning of the code):
#define SCREEN_ADDRESS 0x3D ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32
Change 0x3D to 0x3C if you need to (as determined earlier). I don't think it is an Adafruit screen, so you can ignore the comment.

Triple check your 4 connections

Run demo and audibly gasp - "cool".
PS: Mine works on either 3.3V or 5V


text:
How to determine the minimum time for a servo to reach its destination?
We all know servos don't move instantly, which is why it is common to put a delay or a millis() loop whenever you issue a servo.write() command to give it time to reach its target before moving on to the next thing.
What irks me though is that this could be inefficient in situations where the last servo position might be close to its target - you might only need a very short delay in those cases, whereas if it has to sweep the full 359 degrees, you might want more time.
I was disappointed that servo.read() is completely useless for this task (why even include it in the library?) as the following code illustrates:
#include <Adafruit_TiCoServo.h>
Adafruit_TiCoServo servo;
const uint8_t PIN_SERVO = 9;

void setup() {
  servo.attach(PIN_SERVO);
  Serial.begin(9600);

  Serial.println("start");
  servo.write(180);
  delay(1000);

  int pos;
  while(pos != 0){
    pos = servo.read();
    Serial.println(pos); // because it only returns what the last write() operation
                         // was sent, it immediately thinks it's at pos 0 and exits.
    servo.write(0);
  }

  Serial.println("Done");
}

So that sucks. It seems that my options are:

just use a long delay that accommodates the worst-case scenario. I hate this.
add a limit switch at the final target position and just read the state of that switch
build some kind of elaborate encoder that you can read an absolute angle from or buy a more expensive servo that can tell you this info

For reference the servo I'm playing with is a hobby servo: MG90S
Specifically my question is to confirm that there are no other options available for the MG90S other than what I've listed above.

What exactly are you trying to achieve? Which solution is the best depends on the actual target of your project. If you only want to wait for the servo to finish moving before continuing with your code, how about scaling the delay linearly with the angle difference? You can find the factor by testing it yourself. Also keep in mind, that this is also dependent on the load on the servo axle.

@jsotola sure fair enough, but I'll leave it to you to skip the parts you consider offensive and focus on an answer. And to you other comment, fair point - I have attempted to make it more obvious what I'm asking. You know how these questions go - you start to write them and end up writing more of a solution than you expected to. But I still want to know from others whether there are other approaches.

I regard servo.read() as a convenience function. I do not need to remember the last target position, I can ask the object, across other functions and methods. No hobby servo I know feeds back the actual position, because they are usually used in radio-controlled models, which have no feedback path.

in this case there is mostly one simple solution, and there are some better ones.
the problem you faced with the method:
First of all, the read() function indeed only returns what you send to it, it mostly is useful for if you want to know what position you send it to in some other part of code(for example can be used to plan what way to go first if you need to go to many points, or if you already are going to the desired position).
but since the rc servo has only one signal wire and that wire sends no signals back you don't know what state they are in, rc servo's are mostly for ease of use and not for accuracy and speed, if you need that you can look into a real servo(stepper motor).
so yes that function doesn't return the actual position, it returns the desired position.
Solution1: the easy software based method: to do this you can either read the datasheet to read the speed in duration per degree or measure it, and then use that to calculate the time it would take.
actually here the Servo.read() function really comes in use, so this is a perfect example of why you would actually sometimes need or want it.

save the duration in miliseconds per degree in a variable.
next just before you write to the servo use Servo.read() to get the current theoretical position and use that combined with the position you want to go to calculate the difference in dergees (for example 100 and 40 results in a difference of 60), then multiply that with the duration per step. and you will have the answer of how long a change would take.

Example1 warning, untested, might contain spelling mistakes and such
    /* tuned delay Sweep
  This example code is in the public domain.
  No warranty

  modified 27 March 2023
  by Daan V.Loon
*/

#include <Servo.h>
#define DELAYPERDEGREE 2 //this is the value for the sg90 servo from the datasheet(0,12s(120ms)per 60degrees.
#define SAFETYMARGIN 110 //this is to add 10% extra time to wait, to make sure it finishes in time even under load.
//# define is used kind of like a variable that doesn't need to change, purely in code and not in ram
unsigned long waitDuration=0;//variable that shows how long to wait.

Servo myservo;

unsigned long CalcWait(Servo s,int desiredPosition)//method to calculate and return duration to wait in ms 1000ms is 1 s.
{
  int spos=s.read();
  if(spos>desiredPosition){return (((spos-desiredPosition)*DELAYPERDEGREE)*SAFETYMARGIN)/100;}
  else{ return(((desiredPosition-spos)*DELAYPERDEGREE)*SAFETYMARGIN)/100;}
}//end of method

void ServoAndWait(int pos){
  //insert range limiting code here if needed, warning this method doesn't check if you try to use numbers bigger or smaller than your servo or the servo library can actually handle.
  waitDuration=CalcWait(myservo,pos);//call method to update how long to wait, needs to be done before telling the servo to move.
  myservo.write(pos);//tell servo to rotate
  delay(waitDuration);//blocking wait for servo to complete
}
void setup() {
  myservo.attach(2);
  ServoAndWait(180);//rotate to 180 degrees and wait until it should be there+10% with the safetymargin of 110
}

void loop() {
    ServoAndWait(0);
    ServoAndWait(180);
    //this should reult in the famous sweep example, however then at the max speed of your servo, this way you can test it.
    //to optimize speed use a safetymargin far over 100(like 200 or such) then reduce it until you see it just stop or just not stop anymore at the edges.
    //also make sure to first test if your servo can actually handle 0 and 180 degrees, for example first test if it moves if you tell it to go from 10 to 0 and from 170 to 180, not all servos reach the full 180 degree range.

  }

Example2:(no comments)
/* tuned delay Sweep
  This example code is in the public domain.
  No Warranty

  modified 27 March 2023
  by Daan V.Loon
*/

#include <Servo.h>
#define DELAYPERDEGREE 2
#define SAFETYMARGIN 110
unsigned long waitDuration = 0;
Servo myservo;

unsigned long CalcWait(Servo s, int desiredPosition)
{
  int spos = s.read();
  if (spos > desiredPosition) {
    return (((spos - desiredPosition) * DELAYPERDEGREE) * SAFETYMARGIN) / 100;
  }
  else {
    return (((desiredPosition - spos) * DELAYPERDEGREE) * SAFETYMARGIN) / 100;
  }
}

void ServoAndWait(int pos) {
  waitDuration = CalcWait(myservo, pos);
  myservo.write(pos);
  delay(waitDuration);
}
void setup() {
  myservo.attach(2);
  ServoAndWait(180);
}

void loop() {
  ServoAndWait(0);
  ServoAndWait(180);
}

Example3: Shorter and more stack overflow alien themed for people who just copy paste or find this clearer
/* tuned delay Sweep
  This example code is in the public domain.
  No Warranty

  modified 27 March 2023
  by Daan V.Loon
*/

#include <Servo.h>
#define DELAYPERDEGREE 2
#define SAFETYMARGIN 110

Servo myservo;

unsigned long CalcWait(Servo s, int desiredPosition)
{
  int spos = s.read();
  return (spos>desiredPosition?((spos - desiredPosition) * DELAYPERDEGREE * SAFETYMARGIN) / 100 : ((desiredPosition - spos) * DELAYPERDEGREE * SAFETYMARGIN) / 100);
}

unsigned long waitDuration = 0;
void ServoAndWait(int pos) {
  waitDuration = CalcWait(myservo, pos);
  myservo.write(pos);
  delay(waitDuration);
}

////---code you would see below, headers and functions and variables above---////
void setup() {
  myservo.attach(2);
}

void loop() {
  ServoAndWait(0);
  ServoAndWait(180);
}

These above should be fully functional codes that should work for you, they all work the same. I didn't test it with a servo however, but the logic worked when I tested that. also make sure to change the delay per degree to whatever your datasheet says, in this case the datasheet said 0.12s/60 degrees sg90(very common servo for arduino) so I turned seconds into miliseconds and divided it by 60, so 120/60=2, if you have a much faster servo or need more accurate results for different servos you can use the safety margin to tune, or you can use microseconds in the calculations or such, or just multiply the numbers by 10 and then at the end of the calculations divide it by 10 before returning.
Then here is also the more difficult hardware version
to do this you can either try to use the servo's buildin hardware and modify the servo with a 4th wire, either add it to the rotation sensor/potentiometer, or add it to the chip if it has a place where you can read it directly, with this you are on your own however since it is very speciffic for every servo.
a more easy way to do this is to use a compactor(or something else that gives you one digital output(typical rc servos will always keep the motor on,and just very rapidly change direction once they are on position, if it actually stops you might just want to read it like a normal digital signal(perhaps do something to protect against motor spikes), and read the wires both standalone instead of comparing them to each other) to compare the voltages on the wires to the motor in the servo, since when it moves one side has a higher voltage than the other side, when it stops, they either are equal or rapidly change to keep position. so you just check for whenever it changes. or if you know the direction you can be even more accurate and fast. this ofcource requires you opening up the servo.
or you can indeed add something like a rotary encoder, no need for a complex one, since you can just count the stripes and if you suddenly don't get changes anymore the servo is also stopped in case you missed some, or you can use something like a small microphone or vibration meter since the servo when it moves, this however may become inaccurate under higher load.
just try to avoid reading it with analog signals, since for what you want to use it for analog signals can be really slow.
also
you might want to look into non blocking delays, it won't make the servo's faster, but it allows you to do other things while the servo is moving so you won't have to wait until it is finished.

Re solution 1: I would expect the servo speed to depend on the load, which would invalidate any computation based on a fixed speed.

Thanks for this detailed answer. The project is long over (I can't remember which workaround I ended up using), but I'll definitely be looking this up the next time I'm playing around with RC servos!

@EdgarBonet partly yes, depends on the servos partly how much so, but in typical loads where the servo isn't really overloaded it should behave similar to what is stated in the datasheet, when it is overloaded you might want to measure it instead(also mentioned that), but I also speciffically added the SAFETYMARGIN to the code for hat speciffic thing, it allows you to set a safety margin or finetune code so that it works well. most cases where a servo needs to move and all else needs to stop then would be a quite controlled proces, so less expectations for strange behaviour.

but indeed when a servo is constantly under different load it might have slightly different speeds, so you will need one of those hardware modifications like the ServoMotorWireReadWires method I added below the code, but software is almost always a tradeoff compared to hardware. but as long as the servo isn't overloaded or close to it it should typically be around the speed in the datasheet, or faster. since often for the speed in the datasheet they take the speed under load(even though that differs per manufacturer and the amount of load as well.
reading motor wires instantly shows changes.

Yes, normally servo.read() is pretty useless.
First, check your library with a sketch like this
void setup() {
  servo.attach(PIN_SERVO);
  Serial.begin(9600);

  Serial.println("start");
  servo.write(180);
  delay(1000);
  unsigned long start = millis();
  servo.write(0);
  unsigned long duration = millis() - start;
  if (duration <= 1)
     Serial.println("servo.write() returns immediately");
  else {
     Serial.print("servo.write() takes ");
     Serial.print(duration);
     Serial.println(" ms for a 180° move");
  }
}

If it returns immediately, you have to really measure somehow the actual speed and eventually follow chrisl's comment.


text:
Conditional assignment of array
I have some really long global variable arrays filled with data.
There is a single function that processes the data. The function works on only one array everytime. This arrays value changes every iteration and gets the value from the global variable arrays.
Since i declare lots of global variable arrays, in order to pass them to the array_to_be_processed_by_the function, the memory is filled up really quickly.
What is the best way to conditionally give values to the array?
For example, there could be a counter in the program, and if the counter is a particular value, the array would be initialized with certain data. Of course, i would also have to call the function, and i would have to keep track of the counter and increase it and zero it at the beginning of the loop, to start from the beginning.
In this approach, only one variable would always be initialized.
PSEUDOCODE:
int looper = 0;

void loop()
{
  switch(looper)
  {
     case 0:
        my_array[] = {DATA HERE};
        exec_func(myarray);
        looper++;
        continue;
     case 1:
         ......
     case 'last_case':
         ...
         looper = 0;
         break;
  }
}

However, i am not sure if this approach is the correct one.
Especially for a microcontroller.
One obvious issue, is that the array values are not necessarily the same size.
Therefore, the base array should be destroyed at each case?
If this is good approach, should new/delete be used on an arduino?
What is the best approach to conditionally initialize an array, so that i don't get out of memory

Are your base arrays constant? If yes, you could put them in flash/program memory instead of RAM. As I've understood you would copy the data into an extra arasy anyway to work on the data there

In the example pseudocode, my_array[] defined in case 1 would be automatically destroyed at the end of case 1. Incidentally,  if you define variables within a case, the case block should be enclosed in brackets { } or you get compiler warnings.

In order to answer your question, we need to know how you define the exec_func(myarray)? What is the data type of my_array[]? Does the function take in a my_array[] or does it take in a pointer to the array?  Whether the my_array[] only be used within the case 0 or does it uses somewhere else? Meanwhile I'd suggest you read Learn C++. BTW, the new/delete is used for dynamic allocated memory using command like malloc(), it does not applicable to static array creation.

Would you mind to [edit] your question and provide a minimal, reproducible, and complete example, please? It should show your use case, even if it is not optimized as you like.

@chrisl Yes they are constant. I am learning about PROGMEM right now! Thank you for the tip!

@hcheung My arrays are type int with many values. The functions receive the pointers directly, not a pointer to them. Which is the same thing actually, since the name of an array is a pointer to the first element. In my theoretical implementation (it was for solving the full memory problem), it would be used only in the case 0 situation.

@thebusybee It really isn't anything special. Just long int arrays with lots of data. Then they get assigned to a working array, that is passed to a function for processing. Add many long global vars and you quickly get out of memory. Nothing fancy. If you want though, i can still provide code, if you are interested.

Sure. For example, we need to know whether these arrays contain constant values. Or whether the called function changes elements of the arrays. And so on. Make sure that any important aspect is covered for a good answer. Only good questions receive good answers, that's the GIGO principle.

Let's consider this:
case 0:
    my_array[] = {DATA HERE};
    exec_func(myarray);

This is not quite valid C++, but let's pretend you get the syntax right
to make it work. The problem is: doing this will not help you save
memory. Where do you think the compiler is going to store {DATA HERE}?
In memory, right. It will be stored as an anonymous array. Then the
initialization of my_array will make a second copy of the data in
memory, this time in a named array.
You can avoid this second copy by naming your arrays of constants, and
passing the correct one to your function:
// At global scope:
const int my_array_0[] = {...};
const int my_array_1[] = {...};

// Within the switch/case:
case 0:
    exec_func(my_array_0);

You can even avoid the switch/case altogether by using an array of
pointers, but that is irrelevant to your current problem.
Your idea of initializing only the array you actually need can work if
you manage to do it procedurally, i.e. implementing some recipe with
instructions, rather than copying a set of constant:
case 0: {
    int my_array[array_size];
    for (int i = 0; i < array_size; i++) {
        my_array[i] = some_expression_to_compute_this_array_item;
    }
    exec_func(myarray);
}

This is not always feasible though.
If you really have to store the constants in the program, and you are
using an AVR-based Arduino (like the Uno, Mega, Micro...), then you can
save RAM by storing the arrays of constants in flash memory, and copying
only the one you need to RAM:
// At global scope:
const int my_array_0[] PROGMEM = {...};
const int array_size_0 = sizeof my_array_0 / sizeof my_array_0[0];
const int my_array_1[] PROGMEM = {...};
const int array_size_1 = sizeof my_array_1 / sizeof my_array_1[0];

// Within the switch/case:
case 0: {
    int my_array[array_size_0];  // array in RAM
    memcpy_P(my_array, my_array_0, sizeof my_array);
    exec_func(my_array);
}
case 1: {
    int my_array[array_size_1];  // array in RAM
    memcpy_P(my_array, my_array_1, sizeof my_array);
    exec_func(my_array);
}

Check the documentations of PROGMEM and memcpy_P() for
the details.
If you go this route, you may consider modifying the function
exec_func() so that it expects its parameter to be a pointer to flash
instead of a pointer to RAM. Then you will completely avoid the copy in
RAM.

Edit: expanding on the idea of passing a pointer to flash.
The C++ compiler doesn't really know the difference between a pointer to
RAM and a pointer to flash. If you want to pass a pointer to flash to a
function, you have to write the function in such a way that it expects a
pointer to flash. For example, this function prints out the contents of
a flash-based array:
// The argument should be a pointer to flash.
void exec_func(const int *data)
{
    for (int i = 0; i < array_size; i++) {
        int number = pgm_read_word(&data[i]);
        Serial.println(number);
    }
}

Note that the array is not accessed directly (evaluating data[i] would
give garbage). Instead, the address of the element you want (&data[i],
a flash address) is passed to the macro pgm_read_word(), which uses
inline assembly to get the relevant data from the flash.
Now you can call this function passing it the address of a PROGMEM
array, as in exec_func(my_array_0);.
Just for completeness, I will show you how to use an array of pointers
to avoid the switch/case construct:
const int my_array_0[] PROGMEM = {...};
const int my_array_1[] PROGMEM = {...};
...

const int *arrays[] = {my_array_0, my_array_1, ...};

int looper = 0;

void loop()
{
    exec_func(arrays[looper]);
    if (++looper == number_of_arrays) looper = 0;
}

Note that here arrays is a RAM-based array. That's why you can access
the elements directly as arrays[looper]. These elements, however, are
pointers to flash-based arrays. If you have so many arrays that arrays
gets too big, you might consider putting it also in flash.

Thank you very much for this detailed answer! The problem is: doing this will not help you save memory. Where do you think the compiler is going to store {DATA HERE}?  Well, my logic behind this thought, was that I have seen much larger codebases in arduino. I thought that the CODE part was different than the variable assignment. I am reading about PROGMEM right now. One more question. If i pass a pointer to flash location, then i don't need the my_array[] at all? I just give a pointer to the my_array_0[] (for example)?

@user1584421 That depends on if you want to change the data. exec_func() can be written to read the data from flash, but you cannot change it there. If you need to do transformations to the raw array data, then you still need to copy it to RAM like Edgar did in his last code snippet.

@chrisl No they arrays would be constants. So i don't even need my_array[]. Just pointers for the const arrays in Flash memory. Thanks for the tip!

LOL! You just edited it in the same time i managed to get it to work! I will give it a read and mark it as accepted it later! Thank you so much for your answer!

I tried the pointer to array to flash before but it didnt work. Of course, maybe i did something wrong. However, i managed to get it to work with the code snippet you provided just before the edit. With the extra work of having to deduce the number of elements in the array in the flash. int ArrayElements = sizeof(my_array_0) / sizeof(int); And then i would create the empty array, just like your example, passing the arrayelements as its size. Yes, maybe its not as fast and memory efficient, but its three lines of code. And only one array in memory. I can get away with it.

I am facing another problem. While i am able to assign an array from PROGMEM and pass it to a function, the program would have to move from case 0 to other cases. Thus, the base array would have to be reinitialized. Considering i have not used new, can i somehow or reassign the array? The arrays in PROGMEM are not of the same size.

@user1584421: I amended the answer to cope with arrays of different sizes. Note that there are now multiple variables that share the name my_array, but they are different variables, each local to a {} block in the switch/case.


text:
Changing Frequency of PWM Pin on Arduino Uno
I am trying to change the frequency of the PWM output from an Arduino Uno R3 (Been using Pin 9) to 200 Hz with a duty cycle of 20%. This is for an ESC that is connected to a 12V motor, and I know the ESC works as if I input a 200 HZ wave from a waveform generator the motor runs. However, I have been finding it difficult to do the same thing with an Arduino Uno and I believe it is due to the frequency being a default of 490 Hz, so I need a way to change that frequency to 200 Hz. Are there any libraries or specific ways to do so?
Thanks!

please add your code to the post ... format as "code"

Haven't used a library for this yet, but you can find promising results in a web search. Like this library

You can also use my newly-created AVR_PWM library to shield you from dealing with complex hardware registers
For example
#include "AVR_PWM.h"

#if ( PWM_USING_ATMEGA2560 )
  // Pins tested OK in Mega
  //#define pinToUse      12            // Timer1B on Mega
  //#define pinToUse      11            // Timer1A on Mega
  //#define pinToUse       9            // Timer2B on Mega
  //#define pinToUse       2            // Timer3B on Mega
  //#define pinToUse       3            // Timer3C on Mega
  //#define pinToUse       5            // Timer3A on Mega
  //#define pinToUse       6            // Timer4A on Mega
  //#define pinToUse       7            // Timer4B on Mega
  #define pinToUse       8            // Timer4C on Mega
  //#define pinToUse      46            // Timer5A on Mega
  //#define pinToUse      45            // Timer5B on Mega
  //#define pinToUse      44            // Timer5C on Mega

#elif ( PWM_USING_ATMEGA_32U4  )
  // Pins tested OK on 32u4
  //#define pinToUse      5            // Timer3A on 32u4
  #define pinToUse      9            // Timer1A on 32u4
  //#define pinToUse      10            // Timer1B on 32u4

#else

  // Pins tested OK on Nano / UNO
  #define pinToUse      9            // Timer1A on UNO, Nano, etc
  //#define pinToUse     10            // Timer1B on UNO, Nano, etc
  //#define pinToUse      5               // Timer0B on UNO, Nano, e
  //#define pinToUse       3            // Timer2B on UNO, Nano, etc
#endif

//creates pwm instance
AVR_PWM* PWM_Instance;

float frequency;
float dutyCycle;

void setup()
{
  Serial.print(F("\nStarting PWM_Basic on "));
  Serial.println(BOARD_NAME);
  Serial.println(AVR_PWM_VERSION);

  //assigns PWM frequency of 200 Hz and a duty cycle of 0%
  PWM_Instance = new AVR_PWM(pinToUse, 200, 0);
}

void loop()
{
  frequency = 200;
  dutyCycle = 20;

  PWM_Instance->setPWM(pinToUse, frequency, dutyCycle);

  delay(10000);
  dutyCycle = 90;

  PWM_Instance->setPWM(pinToUse, frequency, dutyCycle);

  delay(10000);
}

This works for me, thank you!

If you are prepared to use Timer 2 (Pin D3) this code will do it:
void setup()
 {
  pinMode (3, OUTPUT);

  TCCR2A = bit (WGM20) | bit (WGM21) | bit (COM2B1); // fast PWM, clear OC2B on compare
  TCCR2B = bit (WGM22) | bit (CS20) | bit (CS21) | bit (CS22); // prescaler of 1024
  OCR2A =  77;    // zero relative
  OCR2B =  15;    // 20% duty cycle
  }  // end of setup

void loop()
  {
  // do other stuff here
  }

More information on the Atmega328P timers on my page about timers.
The figure 77 is obtained from:
16000000 / 1024 / 78 = 200.32

In other words, the prescaler of 1024 is divided into the clock of 16 MHz, and then we count up to 78, giving a result of just over 200. I actually use 77 because the timer is zero-relative (it counts from 0 to 77, giving 78 counts). This means the frequency is slightly out (it will be 200.32 Hz rather than 200 but that could be close enough).

You could also do it with Timer 1 (pin D10) to get slightly more accurate results, like this:
void setup()
 {
  pinMode (10, OUTPUT);

  // mode 15
  TCCR1A = bit (WGM10) | bit (WGM11) | bit (COM1B1); // fast PWM, clear OC1B on compare
  TCCR1B = bit (WGM12) | bit (WGM13) | bit (CS11);   // fast PWM, prescaler of 8
  OCR1A = 10000 - 1;   // what to count to
  OCR1B = 2000 - 1;    // duty cycle
  }  // end of setup

void loop()
  {
  // do other stuff here
  }

In this case I have used a prescaler of 8 and a count-up value of 10000, as follows:
16000000 / 8 / 10000 = 200

In this case the answer is exactly 200, which will be more accurate than the 200.32 obtained from using Timer 2. That is because Timer 1 is a 16-bit timer and has higher resolution.