message:
ESP8266 soft resets when using a GPIO Expander

In my project I'm trying to use a PCF8574 to drive some peripherals, since the ESP8266 doesn't offer all the GPIO pins I need, however I seem to be having problems at the most basic level of driving the pins of an e-paper display.

The e-paper display is connected to MOSI, MISO and SCL to the "native" pins GPIO12, 13 and 14, and I wanted to rely on the PCF to drive the other four pins (CS, DC, RST, BUSY).

I modified [the original library][1] so that it could accept an arbitrary `Pin` abstraction that looks like the following:

```cpp
class Pin {
public:
  Pin(void){};
  ~Pin(void){};

  virtual uint8_t read() = 0;
  virtual void write(uint8_t value) = 0;
};
```

and at first implemented it as `ArduinoPin`, which all it does is setting up the pin mode and calling `digitalRead` or `digitalWrite` when needed.

While connecting the EPD with the `ArduinoPin` implementation, using D0, D1, D2, D4 as pins, the e-paper display works correctly, so I proceeded to make another implementation of Pin with the GPIO Expander.

Following the GPIO Expander and relative Pin implementations

`gpio-expander.h`
```cpp
class GpioExpander {
public:
  GpioExpander(uint16_t address);
  GpioExpander();
  uint8_t read(uint8_t pin);
  void write(uint8_t pin, uint8_t value);

private:
  uint8_t buffer[1] = {0xFF};
  uint16_t address;
};

class GpioExpanderPin : public Pin {
public:
  GpioExpanderPin(GpioExpander *expander, uint8_t pinNumber);
  ~GpioExpanderPin(void);
  virtual uint8_t read();
  virtual void write(uint8_t value);

private:
  uint8_t myPin;
  GpioExpander *expander;
};
```

`gpio-expander.cpp`
```cpp
GpioExpander::GpioExpander(uint16_t address) {
  this->address = address;
  buffer[0] = 0xFF;
  Wire.beginTransmission(address);
  Wire.write(buffer[0]);
  Wire.endTransmission();
}

GpioExpander::GpioExpander() : GpioExpander(0x20) {}

uint8_t GpioExpander::read(uint8_t pin) {
  write(pin, 1);
  Wire.requestFrom(address, 1);
  buffer[0] = Wire.read();
  return (buffer[0] >> pin) & 1;
}

void GpioExpander::write(uint8_t pin, uint8_t value) {
  if (value == 1)
    buffer[0] |= (1 << pin);
  else
    buffer[0] &= ~(1 << pin);
  Wire.beginTransmission(address);
  Wire.write(buffer[0]);
  Wire.endTransmission();
}
```

`gpio-expander-pin.cpp`
```cpp
GpioExpanderPin::GpioExpanderPin(GpioExpander *expander, uint8_t pinNumber) {
  this->expander = expander;
  myPin = pinNumber;
}

GpioExpanderPin::~GpioExpanderPin(void) {}

uint8_t GpioExpanderPin::read() { return this->expander->read(myPin); }

void GpioExpanderPin::write(uint8_t value) {
  this->expander->write(myPin, value);
}
```

These implementations work fine, I tested everything with a multimeter and I can arbitrarily write 1s and 0s on all 8 pins of the PCF, but when I try to drive the four pins of the e-paper, the ESP8266 resets and I have no clue on why. I tried with the exception decoder and the following stacktrace appears:

```
Exception Cause: Not found

0x40202984: Twi::read_bit() at ??:?
0x402029db: Twi::write_byte(unsigned char) at ??:?
0x40202b94: Twi::writeTo(unsigned char, unsigned char*, unsigned int, unsigned char) at ??:?
0x40202bb9: Twi::writeTo(unsigned char, unsigned char*, unsigned int, unsigned char) at ??:?
0x40202cc0: twi_writeTo at ??:?
0x40201720: TwoWire::endTransmission(unsigned char) at ??:?
0x40105cfe: os_printf_plus at ??:?
0x40201748: TwoWire::endTransmission() at ??:?
0x402017f5: GpioExpander::write(unsigned char, unsigned char) at ??:?
0x40201794: GpioExpanderPin::write(unsigned char) at ??:?
0x402011f9: Epd::spiTransfer(unsigned char) at ??:?
0x40201238: Epd::sendData(unsigned char) at ??:?
0x40201385: Epd::clear() at ??:?
0x402010b6: setup at ??:?
0x4020583c: std::_Function_handler<bool (), settimeofday::{lambda()#1}>::_M_manager(std::_Any_data&, std::_Function_handler<bool (), settimeofday::{lambda()#1}> const&, std::_Manager_operation) at time.cpp:?
0x4020583c: std::_Function_handler<bool (), settimeofday::{lambda()#1}>::_M_manager(std::_Any_data&, std::_Function_handler<bool (), settimeofday::{lambda()#1}> const&, std::_Manager_operation) at time.cpp:?
0x4020583c: std::_Function_handler<bool (), settimeofday::{lambda()#1}>::_M_manager(std::_Any_data&, std::_Function_handler<bool (), settimeofday::{lambda()#1}> const&, std::_Manager_operation) at time.cpp:?
0x4020583c: std::_Function_handler<bool (), settimeofday::{lambda()#1}>::_M_manager(std::_Any_data&, std::_Function_handler<bool (), settimeofday::{lambda()#1}> const&, std::_Manager_operation) at time.cpp:?
0x40202158: loop_wrapper() at core_esp8266_main.cpp:?
0x40100e85: cont_wrapper at ??:?
```
(Not sure why I can't get the source lines to appear, any suggestion in that is most welcome.)

Anyway apparently the problem appears when ending the I2C transmission, at the lowest level possible, at Twi::read_bit, which I include for the sake of all of you trying to help me out

`core_esp8266_si2c.cpp`
```cpp
bool Twi::read_bit(void)
{
    SCL_LOW(twi_scl);
    SDA_HIGH(twi_sda);
    busywait(twi_dcount + 2);
    SCL_HIGH(twi_scl);
    WAIT_CLOCK_STRETCH();
    bool bit = SDA_READ(twi_sda);
    busywait(twi_dcount);
    return bit;
}
```

The most upsetting thing is that this same setup, when programmed in Micropython works just fine (I can't use Micropython though, because I need all the RAM I can get from the ESP to draw pictures, and I'm much interested in the overall performance I can get with C++).

Anyone had this same problem? Any advice on how to fix this behavior?

**EDIT**

With some experimentation, I managed to replicate the problem in a smaller scale. The following code resets the ESP8266 as well:

```cpp
void setup() {
  Wire.begin();
  GpioExpander expander;
  GpioExpanderPin pin(&expander, 0);
  while(true) {
    pin.write(0);
    pin.write(1);
  }
}

```

It looks like fast cycles of on-off crash the system (which is what the EPD library does when sending data, turning on and off the pins relatively fast to switch between modes etc.). Not sure how to address this though, but I believe it might be something related to the Twi libraries at this point.

**EDIT 2**

Adding a `yield()` to the `write` function of the gpio expander class makes the issue go away, but the time to execute a simple display clear grows to unacceptable levels (~3 seconds to perform a full display refresh).

Any suggestion is appreciated at this point.

**EDIT 3**

I did some more experimenting and benchmarking, and reached the following conclusions:

- the time that it takes for the Wire library to send data to the GPIO expander (the `write` function) is ~50µs
- when using the gpio expander, the time to write data to the SPI device (DC write + enable CS + SPI transfer + disable CS) takes a total of ~170µs (which checks out, 3 times calling the gpio expander makes for ~150µs and the rest is SPI transaction)
- when using the arduino pins, the time to write data to the SPI device takes ~10µs (an order of magnitude less than the GPIO expander).

To send a total of 150,000 bytes to the e-paper display, then, with the GPIO expander takes roughly 3 seconds, while with the normal pins it would take 10 times less (0.3 seconds).
The reset issue is surely due to the time spent in the send loop: the watchdog thinks the program is stuck in an infinite loop and thus resets the board. This is why the calls to `yield` remove the reset issues.

I believe I must either find a faster implementation of wire (?) or try to minimize usage of CS / DC pins

[1]: https://github.com/waveshare/e-Paper/tree/master/Arduino/epd4in2

body:
In the end the answer to the problem is the update frequency of the pins that drive the e-paper display:

The original waveshare library (as linked in the post) does the following to send data to the EPD:

- enable DC (to tell the EPD that it's receiving DATA)
- enable CS (to tell the EPD to listen to the incoming SPI data)
- send SPI data
- disable CS (to tell the EPD that anything received after that is not its competence)

Normally with arduino's `digitalWrite` this happens in a matter of ~10µs, and even if we repeat this process 15k times (that's how many bytes a 4.2 inch display supports) we spend 0.15 seconds to update **half** of the display's content (because the EPD has 2 memory banks which must be written).

When using the GPIO expander, instead, the mean time to send data via I2C is something around 50µs, which means that to enable / disable the pins three times we increase tenfold the time that the normal digital write takes (~150µs) which translates to 1.5 seconds to update half of the display and finally roughly 3 seconds for a full update.

During this loop, the watchdog thinks that the program is stalling and resets the chip. This is why using `yield` the update runs its course correctly.

To fix this issue, I worked on the EPD library and reduced the use of the digital writes, by creating a "session" of sorts: whenever I start writing data in a loop, I enable the CS pin, and disable it only at the end of the loop, so that the 100µs are only expended around the data transfer loop.

This is actually what happened in the Micropython driver for the e-paper display: the SPI connection sent data in chunks, enabling and disabling the chip select pin only at the beginning and end of the "transaction".

I'm planning to publish the updated library at some point, once everything is smoothed out, if this can interest anyone leave a message :)


sender: user

message:
SIM800L stays in 1sec-blink-state

I've bought such on Amazon. My main problem is that it stays in 1sec-blink, meaning it can't registrer cellular network.

First I tried with all anntennas possiblities (one of each, both, none) - failed.

Using NodeMCU (for 3.3 TxRx) and `SoftwareSerial.h` to communicate. It answers all AT command as expected.

I tried several powering possibilities (as noted extensively about 2A on the internet), including 18650 , 2000mAh battery connected only to SIM800L module with no MCU connected, still same state.

What Can be done else?

SEE PELEPHONE:
[![Carrier support - "PELEPHONE"][1]][1]

  [1]: https://i.stack.imgur.com/mWCzC.png

sender: assistant

body:
Your mobile network provider does not provide a 2G (GPRS / GSM) network.

2G is graduallly being phased out around the globe, so switching to an LTE modem is really the way forward.

If you still want to use the SIM800L then you need a network provider that gives 2G services. Fortunately the SIM800L is a "quad band" modem, so it will work with pretty much any 2G provider (it works on 850/900/1800/1900MHz, hence the "quad" of "quad band").


message:
Wrong use of memory?

Probably I'm doing something wrong.
I'm writing an Arduino sketch for ESP32.
Among the others I have this function:

    #define HDR_MAX_LENGHT 4
    #define CMD_MAX_LENGHT 5
    #define ARG_MAX_LENGHT 5
    #define ARG_MAX_NUMBER 8

    void ProcessLine(HardwareSerial *serial, char *line)
    {
      int ret;
      int argc;
      char header[HDR_MAX_LENGHT + 1];
      char cmd[CMD_MAX_LENGHT + 1];
      char argv[ARG_MAX_NUMBER][ARG_MAX_LENGHT + 1];

      return; // for debugging
    }

Usage example:

    void loop()
    {
        static char inBuf[64];

        if (Serial.available())
        {
          size_t size = Serial.readBytes(inBuf, 64);
          inBuf[size] = '\0';
          ProcessLine(&Serial, inBuf);
        }
    }

In the last days all worked fine.
After adding other code, I noticed that sometimes my program "hangs": nothing runs. Perhaps it goes in an exception trap.

Commenting out the different sections of the code, I discovered that if I remove the declarations of the variables in `ProcessLine()` no more hangs happen.
The current RAM consumption is negligible: 0.5%.

During the tests NO serial data was received! So the function `ProcessLine()` was *never* called.
Hence, only the declaration of the variables lead to the "hang".

I wonder if there are some mistakes in the code above.
Otherwise, how I can further debug my code to understand what I've done wrong?

body:
As I said in a comment, there is nothing wrong with these declarations.
Now, here are some debugging strategies you may try:

1. You can rebuild your program with all compiler warnings enabled. Look
   carefully at the compiler messages and investigate any warning you
   see.

2. You can continue with your current strategy: put back those
   declarations, then try to remove something else. You may find that
   there are multiple pieces of code that, when removed, make your
   program work again. Chances are the problem resides in one of those
   pieces. But then, it may be that the problem is in the interaction
   between multiple pieces rather than in one specific piece.

3. You may just read the code carefully and see if you spot something
   suspicious. This can work sometimes: see how @Juraj spotted a buffer
   overflow in your code snippet just by reading it.

4. You can add debug messages across your program, to check what
   branches are executed, the values of variables...

5. I don't know the ESP32 dev environment, but you can check whether the
   compiler supports adding “sanitizers”. These are pieces of code added
   by the compiler that do run-time checks on your program, and report
   problems as they happen. You may want to enable at least an address
   sanitizer and an undefined behavior sanitizer.

6. If the environment supports it, you can try to run the program in a
   debugger. No success guaranteed: if the program corrupts its memory,
   it may crash in some place unrelated to the actual problem. The
   debugger will tell you where it crashed, which may not be very
   helpful in this case.

7. If the ESP dev environment supports neither 5 nor 6, you may try to
   run your program on your PC. There are a couple of Arduino-compatible
   cores that you can download for running on a PC.


message:
Problem with messuring capacitance with 50 Arduinos connected by I²C

I want to measure changes in capacitance over time. One Arduino is used per capacitance sensor. There is one anode per Arduino and a total of one common cathode (see the picture)

With four Arduinos connected in series via an I²C master-slave connection, successful results were obtained.
With more than four Arduinos, all measurement results were 0, because no voltage is measured at the input pin anymore.

I then lowered the reference voltage for analogRead. So 10 Arduinos in series were possible. However, the measured values are clearly lower. However, no absolute values are to be measured, but any difference in the capacitance is sufficient. Finally 50 Arduinos should be used.

I have thought of the following possibilities:

1. reduce the reference voltage further, but this may reduce the measurement results further, so that no deflection is measured.
2. increase the output voltage at the Arduino via an operational amplifier to compensate the voltage drop.

How could I proceed to get a usable result with all 50 Arduinos? The code I used is to be found below.

[![Enter image description here][1]][1]

[![enter image description here][2]][2]

### Master

```
#include <Wire.h>

volatile byte x1, x2;
volatile int val_slave;

const int OUT_PIN = A0;
const int IN_PIN = A2;

#define nodeMax 10
#define nodeStart 2

void setup() {
  analogReference(DEFAULT);
  Wire.begin();

  pinMode(OUT_PIN,OUTPUT);
  pinMode(IN_PIN,OUTPUT);

  Serial.begin(9600);
  // put your setup code here, to run once:

}

  void loop() {

    pinMode(IN_PIN,INPUT);
    digitalWrite(OUT_PIN,HIGH);

    int val = analogRead(IN_PIN);

    digitalWrite(OUT_PIN,LOW);
    pinMode(IN_PIN,OUTPUT);

    Serial.print(val);
    Serial.print(",");

      delay(10);

    for (int node = nodeStart; node <= nodeMax; node++) {

        // Ask the slave to release the input pin.
        Wire.beginTransmission(node);
        Wire.write(0);
        Wire.endTransmission();
        delay(10);  // give it time to do so

        // Charge the capacitors.
        digitalWrite(OUT_PIN, HIGH);

        // Ask the slave to read val.
        Wire.requestFrom(node, sizeof(val));

        // Discharge.
        x1 = Wire.read();
 x2 = Wire.read();

 val_salve = (int)x1 << 8 | (int)x2;

        Serial.print(val_slave);
        Serial.print(",");

        digitalWrite(OUT_PIN, LOW);
        delay(10);

    }
    Serial.println();
    delay(50);
}
```

### Slave

```
const int IN_PIN=A2;

#include <Wire.h>
#define node 10

void setup() {
    // ...
    analogReference(DEFAULT);
    Wire.begin(node);
    Serial.begin(9600);

    pinMode(IN_PIN,OUTPUT);

    Wire.onReceive(receiveEvent);
    Wire.onRequest(requestEvent);

}

// Release IN_PIN when the master sends a message.
void receiveEvent(int byte_count) {
    pinMode(IN_PIN, INPUT);
    while (byte_count--)
        Wire.read();  // discard the received message
}

void loop ()
{
}

void requestEvent() {
    int val = analogRead(IN_PIN);
    // transmit the result... then

    Wire.write(highByte(val));
    Wire.write(lowByte(val));

    pinMode(IN_PIN, OUTPUT);  // discharge
}

```

  [1]: https://i.stack.imgur.com/5KHSo.png
  [2]: https://i.stack.imgur.com/JGqCJ.png

body:
The cathode line is being shared by all the Arduinos' `OUT_PIN`. When
one of them tries to set the pin `HIGH`, all the others are trying to
hold it `LOW`. This is a short-circuit condition that can damage the
Arduino's outputs. Even if they do survive, the voltage on the line will
be significantly below 5 V because of those Arduinos pulling the
line `LOW`. The more of them pull the line `LOW`, the lower the voltage
will be.

The simple solution would be to change the protocol such that only the
Arduino that is currently making a measurement can pull that line, while
the others keep their `OUT_PIN` in high-impedance mode (i.e. `INPUT`).
This is fragile because... bugs happen, and you may still be making a
short-circuit. A more robust solution would be to connect _only_ the
master to the cathode, and give it the responsibility to drive the line
`HIGH` and `LOW`.

Now, a coupe of unrelated comments:

* A `float` is four bytes: there is no point in transmitting ten bytes.

* You can measure up to four capacitors with each Arduino (pins `A0`
  through `A3`, you can use a digital pin for `OUT_PIN`), it is wasteful
  to use one Arduino per capacitor.

**Edit**: If only the master controls `OUT_PIN`, then some communication
protocol is needed in order to synchronize it with the slaves, so that
they can release `IN_PIN` at the right time. The simplest protocol I can
think of is the following: when the master sends something (_anything_)
to a slave, this message means “please release `IN_PIN` now”.

Here is some untested code implementing this idea:

**master**:

```c++
void loop() {
    // ...
    for (int node = nodeStart; node <= nodeMax; node++) {

        // Ask the slave to release the input pin.
        Wire.beginTransmission(node);
        Wrire.write(0);
        Wire.endTransmission();
        delay(10);  // give it time to do so

        // Charge the capacitors.
        digitalWrite(OUT_PIN, HIGH);

        // Ask the slave to read the capacitance.
        Wire.requestFrom(node, sizeof(capacitanceSlave));

        // Discharge.
        digitalWrite(OUT_PIN, LOW);

        // ...
    }
}
```

**slave**:

```c++
void setup() {
    // ...
    Wire.onReceive(receiveEvent);
    Wire.onRequest(requestEvent);
}

// Release IN_PIN when the master sends a message.
void receiveEvent(int byte_count) {
    pinMode(IN_PIN, INPUT);
    while (byte_count--)
        Wire.read();  // discard the received message
}

void requestEvent() {
    int val = analogRead(IN_PIN);
    // transmit the result... then
    pinMode(IN_PIN, OUTPUT);  // discharge
}
```


body:
The I²C bus was never intended as a distribution bus between many computers. The outputs are not designed to drive long wires. With a lot of kludging you may get this to work, but it is definitely not repeatable or reliable.

Consider something else. [CAN][1] can do maybe 60 nodes, depending on the transceiver chosen. It is relatively inexpensive.

  [1]: https://en.wikipedia.org/wiki/CAN_bus


body:
You might want to consider using a RS485 protocol. Unlike I2C this uses "balanced" wiring so is more suitable for longer cable runs.

I described a way of coding a number of Arduinos using a "rolling master" protocol [here](https://gammon.com.au/forum/?id=11428) - this lets you connect a considerable number of Arduinos to the two wires, and they take turns to send the data.

However in your case, since you are measuring capacitance (not capacity?) then each of your Arduinos can measure 6 (or possibly more) analog readings, so you don't need 50, you need 50/6 = 8.33 Arduinos (OK, nine).

The [Arduino Micro](https://store-usa.arduino.cc/products/arduino-micro?selectedStore=us), for example, has 12 analog input channels, so you would only need 4 (to get 48 inputs) or 5 (to get 60 inputs). Then you could stick to I2C.


message:
''Wire1' was not declared in this scope' in I2C_MPU6886 library

I recently purchased a 6-axis IMU MCP6886 module by M5Stack. Product link: https://github.com/m5stack/M5-ProductExampleCodes/tree/master/Unit/IMU_Unit. The example code provided by the manufacturer is as follows:

```
#include "I2C_MPU6886.h"

I2C_MPU6886 imu(I2C_MPU6886_DEFAULT_ADDRESS, Wire1);

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

  Wire1.begin(21, 22);

  imu.begin();
  Serial.print("whoAmI() = 0xx\n", imu.whoAmI());
}

void loop() {
  float ax;
  float ay;
  float az;
  float gx;
  float gy;
  float gz;
  float t;

  imu.getAccel(&ax, &ay, &az);
  imu.getGyro(&gx, &gy, &gz);
  imu.getTemp(&t);

  Serial.print("%f,%f,%f,%f,%f,%f,%f\n", ax, ay, az, gx, gy, gz, t);

  delay(100);
}
```
When I run this code on Arduino IDE 1.8.19, I get the following error: **'Wire1' was not declared in this scope**. I believe this issue is with the I2C_MPU6886 library itself, but I am not able to troubleshoot it. I am using an Arduino Uno R3 board and running the IDE on Windows 11.

body:
"M5Stack" is an ecosystem of ESP32 based boards and peripherals.

All code for M5Stack peripherals is written for the M5Stack system. Not for the Uno.

You have to find code for the Uno, not for the M5Stack. M5Stack code **will not work on the Uno since it is a completely different system.**


message:
Create a library when using PlatformIO

**(1)** I'm trying to work with libraries in PlatformIO format", for [![example][1]][1], of a library, located in Arduino's `libraries`, where code is llocated under `\src` directry.

**(2)** Any other library, not it "PlatformIO format" as I call it, has to contain inside `libraries/EXMPLE` lib a `EXMPLE.h`, and `EXMPLE.cpp` file.

**(3)** Now, when I try to create a "PlatromIO format" library (since I want to move to PlatfromIO)
compilation fails, saying `.h` file was not found (an error you would get since `.h` `.cpp` was not located in their parent directory), as noted in **2** . see library `ABCD` in [![snapshot][2]][2] (which contains only library structures).

Appreciate any help

Guy

  [1]: https://i.stack.imgur.com/lq1ad.png
  [2]: https://i.stack.imgur.com/uFoBt.png

body:
You're describing "1.5.x" format libraries, not "PlatformIO". This arrangement for a library was created for Arduino IDE version 1.5.0.

The critical part of this library format is the `library.properties` file which describes the library, and also identifies it as being a library (and which you seem to be lacking). The format of the file is described [here](https://arduino.github.io/arduino-cli/0.20/library-specification/).


message:
Problem with "converting a string constant to 'char*'" in the WiFi library

error:
```
C:\Users\mrtas\Desktop\sketch_may23a\sketch_may23a.ino: In function 'void setup()':

C:\Users\mrtas\Desktop\sketch_may23a\sketch_may23a.ino:41:24:
warning: ISO C++ forbids converting a string constant to 'char*' [-Wwrite-strings]

   WiFi.begin(SSID, PASS);   //Begin WiFi
              ^     ^
```

code:

    #include <DHT.h>
    #include <WiFi.h>
    #define DHTTYPE DHT11
    #define DHTPIN 5

    DHT dht(DHTPIN, DHTTYPE);

    String msg = "GET /update?key=TWE505JXFTXDPNB3";
    float temp;
    char SSID;
    char Pass;

    int hum;

    String tempC;

    int error;
    void updateTemp();
    #define SSID "wifi name"

    #define PASS "wifi pass"

    #define IP "184.106.153.149"

    void setup()
    {
      dht.begin();
      Serial.begin(9600);
      delay(10);

      // Connect to WiFi network
      Serial.println();
      Serial.print("Connecting to ");
      Serial.println(SSID);
      WiFi.begin(SSID, PASS);     //Begin WiFi

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

    void loop() {
    start:
      error = 0;

      temp = dht.readTemperature();
      hum = dht.readHumidity();

      char buffer[10];
      tempC = dtostrf(temp, 4, 1, buffer);
      updateTemp();

      if (error == 1) {
        goto start;
      }
      delay(5000);
      String cmd = "AT+CIPSTART=\"TCP\",\"";
      cmd += IP;
      cmd += "\",80";
      Serial.println(cmd);
      delay(2000);

      if (Serial.find("Error")) {
        return;
      }
      cmd = msg ;
      cmd += "&field1=";
      cmd += tempC;
      cmd += "&field2=";
      cmd += String(hum);
      cmd += "\r\n";
      Serial.print("AT+CIPSEND=");
      Serial.println(cmd.length());

      if (Serial.find(">")) {
        Serial.print(cmd);
      }
      else {
        Serial.println("AT+CIPCLOSE");
        //Resend...
        error = 1;
      }

      Serial.println("AT+CWMODE=1");
      delay(2000);
    }

    boolean connectWiFi()
    {
      String cmd = "AT+CWJAP=\"";
      cmd += SSID;
      cmd += "\",\"";
      cmd += PASS;
      cmd += "\"";
      Serial.println(cmd);
      delay(5000);

      if (Serial.find("OK")) {
        return true;
      }
      return false;
    }

body:
It's basically a bug in the library which is expecting "char *" when you are supplying "const char *.

You could work around it by changing:

    #define SSID "wifi name"
    #define PASS "wifi pass"

to:

    char SSID [] =  "wifi name";
    char PASS [] =  "wifi pass";

That makes them non-const so the error message should go away.

Not tested. But that is the general idea.


message:
Error while uploading the sketch to Arduino UNO using CLI

I have connected my Arduino UNO through USB to Raspberry pi 3B. I am using CLI for editing, compiling and uploading sketches >  `arduino-cli upload --fqbn arduino:avr:uno cli-test`. However, after attempting to upload the sketch, it throws the error, `Error during Upload: Failed uploading: no upload port provided` I even checked if the Arduino is detected on the respective port using `arduino-cli board list` and it shows

    /dev/ttyACM0 serial   Serial Port (USB) Arduino Uno arduino:avr:uno arduino:avr
    /dev/ttyAMA0 serial   Serial Port       Unknown

The [support forum][1] of Arduino says to 'select' the port. Nevertheless the support forum resolves the error for GUI application and not for CLI. I searched for how to select the port in CLI, but in vain. Any help is appreciated.

  [1]: https://support.arduino.cc/hc/en-us/articles/360020831120-Error-2-UNKNOWN-uploading-error-no-upload-port-provided

body:
You specify the port with the `--port` flag to the upload command:

```bash
$ arduino-cli upload --fqbn arduino:avr:uno --port /dev/ttyACM0 cli-test
```

You can find all these things out for yourself by using `--help` at any time. For example:

```bash
$ arduino-cli upload --help
Upload Arduino sketches. This does NOT compile the sketch prior to upload.

Usage:
  arduino-cli upload [flags]

Examples:
  arduino-cli upload /home/user/Arduino/MySketch

Flags:
      --board-options strings        List of board options separated by commas. Or can be used multiple times for multiple options.
      --discovery-timeout duration   Max time to wait for port discovery, e.g.: 30s, 1m (default 1s)
  -b, --fqbn string                  Fully Qualified Board Name, e.g.: arduino:avr:uno
  -h, --help                         help for upload
      --input-dir string             Directory containing binaries to upload.
  -i, --input-file string            Binary file to upload.
  -p, --port string                  Upload port address, e.g.: COM3 or /dev/ttyACM2
  -P, --programmer string            Programmer to use, e.g: atmel_ice
  -l, --protocol string              Upload port protocol, e.g: serial
  -t, --verify                       Verify uploaded binary after the upload.

Global Flags:
      --additional-urls strings   Comma-separated list of additional URLs for the Boards Manager.
      --config-file string        The custom config file (if not specified the default will be used).
      --format string             The output format for the logs, can be: text, json, jsonmini, yaml (default "text")
      --log-file string           Path to the file where logs will be written.
      --log-format string         The output format for the logs, can be: text, json
      --log-level string          Messages with this level and above will be logged. Valid levels are: trace, debug, info, warn, error, fatal, panic
      --no-color                  Disable colored output.
  -v, --verbose                   Print the logs on the standard output.
```


message:
A bug with multiple IR detectors

I have a device power by arduino nano atmega328.
The idea of this HW is to detects dropped seed (corn, oil bean etc.) and to count it.

I wrote initial code, but id does not work in an expected way. it increments one count "channel" (I have 8 pairs of IR diodes 3mm) automatically, and passing it does not detect.

Here is the code maybe, someone has some similar experience and maybe help me to refactor the code.

```
#include<wire.h>
#include<Adafruit_SSD1306.h>

int irPin1 = 1;
int irPin2 = 2;
int irPin3 = 3;
int irPin4 = 4;
int irPin5 = 5;
int irPin6 = 6;
int irPin7 = 7;
int irPin8 = 8;

int count1,count2,count3,count4,count5,count6,count7,count8, count = 0;
boolean state = true;

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

void loop(){
if(!digitalRead(irPin1) && state)
 {
   count1++;
   state = false;
   Serial.print("count1: ");
   Serial.println(count1);
   delay(200);
 }

if(digitalRead(irPin1))
  {
    state=true;
    delay(100);
  }

if(!digitalRead(irPin2) && state)
 {
   count2++;
   state = false;
   Serial.print("count2: ");
   Serial.println(count2);
   delay(200);
 }

if(digitalRead(irPin2))
  {
    state=true;
    delay(100);
  }

  if(!digitalRead(irPin3) && state)
 {
   count3++;
   state = false;
   Serial.print("count3: ");
   Serial.println(count3);
   delay(200);
 }

if(digitalRead(irPin3))
  {
    state=true;
    delay(100);
  }

    if(!digitalRead(irPin4) && state)
 {
   count4++;
   state = false;
   Serial.print("count4: ");
   Serial.println(count4);
   delay(200);
 }

if(digitalRead(irPin4))
  {
    state=true;
    delay(100);
  }

 if(!digitalRead(irPin5) && state)
 {
   count5++;
   state = false;
   Serial.print("count5: ");
   Serial.println(count5);
   delay(200);
 }

if(digitalRead(irPin5))
  {
    state=true;
    delay(100);
  }
 if(!digitalRead(irPin6) && state)
 {
   count6++;
   state = false;
   Serial.print("count6: ");
   Serial.println(count6);
   delay(200);
 }

if(digitalRead(irPin6))
  {
    state=true;
    delay(100);
  }
 if(!digitalRead(irPin7) && state)
 {
   count7++;
   state = false;
   Serial.print("count7: ");
   Serial.println(count7);
   delay(200);
 }

if(digitalRead(irPin7))
  {
    state=true;
    delay(100);
  }

 if(!digitalRead(irPin8) && state)
 {
   count8++;
   state = false;
   Serial.print("count8: ");
   Serial.println(count8);
   delay(200);
 }

if(digitalRead(irPin8))
  {
    state=true;
    delay(100);
  }
count= count1+count2+count3+count4+count5+count6+count7+count8;
}
```

I work in platformIO.

body:
Use arrays instead of a copy/paste coding style.
Use millis() instead of delay() statements to eliminate blocking code.
Explicitly configure input pins.

compiles, but untested:
```
uint8_t irPin[ 8 ] = { 2, 3, 4, 5, 6, 7, 8, 9 } ; // set the sensor pins here . Avoid 0 and 1.
uint16_t seedCount[ 8 ] = { 0 } ;
uint32_t lastSeedAtMs[ 8 ] = { 0 } ;
const uint32_t lockOutTimeMs = 200UL ; // milliseconds - prevent 1 seed generating multiple counts

void setup() {
  Serial.begin(9600);
    for ( uint8_t channel = 0; channel < 8 ; channel++ ) {
      pinMode( irPin[ channel ] , INPUT_PULLUP ) ;  // configure all input pins
    }
}

void loop() {
  for ( uint8_t channel = 0; channel < 8 ; channel++ ) {
    if ( digitalRead( irPin[ channel ] ) == LOW ) {  // sensor active low
      if ( millis() - lastSeedAtMs[ channel ] > lockOutTimeMs )  {  // if X ms passed since last detection on this channel
        seedCount[ channel ] ++ ;
        lastSeedAtMs[channel ] = millis() ;
        Serial.print("count");
        Serial.print(channel);
        Serial.print(": ");
        Serial.println( seedCount[ channel ] );
      }
    }
  }
}
```


message:
Input Oscillating Wildly

I'm trying to read an input from a switch but instead of getting 0 and 1 for closed/open, I'm getting 0 and a rapid series of 1s and 0s. Originally I tried (and failed) to simply read a rotary encoder but when I swap it for a standard rocker switch or even connect wires directly then it's the same.

This is my first time trying to get an input into an Arduino so I'm probably missing something quite straightforward.

I've got ground going to one pin of the switch and pin 2 to the other.
Here's the code I'm using to see if the switch is on or off.

    void setup() {
      pinMode (2, INPUT);
      Serial.begin(9600);
    }

    void loop() {
     Serial.println(digitalRead(2));
    }

body:
Pins configured as INPUT are in a high impedance state and very sensitive to electrical noise from the environment, especially with wires connected to them [See Arduino Tutorial][1]

  [1]: https://www.arduino.cc/en/Tutorial/Foundations/DigitalPins

Try using INPUT_PULLUP in your pinMode statement.

Additionally you might want to investigate switch debouncing methods for when your switch is operated.


message:
esp32, platformio A fatal error occurred: Packet content transfer stopped (received 8 bytes) *** [upload] Error 2

When i try to upload code to the esp32 the following error shows up in the command line:

__A fatal error occurred: Packet content transfer stopped (received 8 bytes).__

I use platformio in VScode the platform.ini file looks like this:

        [env:esp-wrover-kit]
        platform = espressif32
        board = esp-wrover-kit
        framework = arduino
        upload_port = COM4
        monitor_speed = 115200

Here's the code i'm trying to upload:

    #include <Arduino.h>

    void setup() {
        Serial.begin(115200);
        Serial.println("init");
    }

    void loop() {
    }

It's an esp32-wrover-dev from freenove. When i unplug it and plug it back in it starts to write random stuff to the serial monitor, when i attempt an upload it stops but it also gives me the error above.

body: For anyone wondering, i found out it means that there's some kind of disconnect between the computer from which your uploading and the file system. Essentially, the esp32 is broken.


body:
I was able to resolve this issue by detaching jumpers that i had connected to the GPIO 12/13 pins during upload.

After uploading they can be attached again.
See this github issue: [https://github.com/espressif/arduino-esp32/issues/2415][1]

  [1]: https://github.com/espressif/arduino-esp32/issues/2415


body:
Hi all I found I got this error because my FTDI adapter was in 3.3V mode, not 5V as soon as I set it to 5V the issue was resolved and it programmed fine. One more thing to check before you scrap your ESP32.

Mick


body:
I had this error when trying to upload a sketch from Arduino IDE to a TTGO T-Display, found that I could resolve this by changing the upload speed from the default of 921600 to 115200.

After doing that it uploaded just fine with no issues.


message:
ESP32 AsyncWebServer with softAP does not serve pages

I'm trying to create a `AsyncWebServer` after enabling the `SoftAP`:

    #include <Arduino.h>
    #include <SPIFFS.h>
    #include <WiFi.h>
    #include <WiFiAP.h>
    #include <AsyncTCP.h>
    #include <ESPAsyncWebServer.h>

    WebApp webapp;

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

      File f = SPIFFS.open("/index.html");
      String s = f.readString();
      Serial.println(s);
      f.close();

      WiFi.softAP("ssid", NULL);
      WiFi.softAPsetHostname("ssid");
      Serial.println(WiFi.softAPIP());

      webapp.Begin();
    }

    void loop()
    {
    }

    class WebApp
    {
    public:
        WebApp();
        void Begin();

    private:
        AsyncWebServer _server;
    };

    #include "webapp.h"

    WebApp::WebApp() : _server(80)
    {
    }

    void WebApp::Begin()
    {
      _server.on("/", HTTP_GET, [this](AsyncWebServerRequest *request)
      {
        Serial.println("GET request");
        _server.serveStatic("/", SPIFFS, "/").setDefaultFile("/index.html");
      });

      _server.onNotFound([](AsyncWebServerRequest *request){
        request->send(404);
      });

      _server.begin();
      Serial.println("[WEB] HTTP server started");
    }

The `SoftAP` brings up the network and I can connect to it (`softAPgetStationNum()` tells me that 1 client is connected). It answers to pings.
The `index.html` is stored into the flash (the contents is printed out correctly).

But when I try to open the root page (192.168.4.1) it prints out "GET request" but the browser request timeouts and nothing is received.

Am I missing anything in the code?

body:
I don't have enough rep to make a comment on your post, but is it possible that when you give the parameter for the function `setDefaultFile()` that the forward slash on `/index.html` is causing the path of the file to be `//index.html` instead of `/index.html`.

Because serveStatic is probably appending the filename of `setDefaultFile()` to the root directory of `/` (the third parameter of `serveStatic()`), you're probably getting an extra character you don't want in your filepath.

**TL;DR** you don't need the `/` in `index.html` for `setDefaultFile()`.

See https://github.com/me-no-dev/ESPAsyncWebServer#serving-files-in-directory for the example.


body:
The usage of `serveStatic()` is wrong. It's not intended to be used inside a callback. Just place the call in the setup of the webserver, i.e.:

    void WebApp::Begin()
    {
      _server.serveStatic("/", SPIFFS, "/").setDefaultFile("/index.html");

      _server.onNotFound([](AsyncWebServerRequest *request)
      {
        request->send(404);
      });

      _server.begin();
      Serial.println("[WEB] HTTP server started");
    }


message:
How to make the compiler ensure that local variables memory allocation won't cause any RAM overflow during execution?

Upon compiling a sketch using the Arduino IDE, a message like the following gets displayed in the console:

    Global variables use 1540 bytes (75%) of dynamic memory, leaving 508 bytes for local variables. Maximum is 2048 bytes.

In addition, it seems that if the global variables use exceeds around 75% of total dynamic memory, the following message gets displayed in addition:

    Low memory available, stability problems may occur.

So, it seems that by using the IDE in a regular way, the compiler does not ensure that local variables memory allocation won't cause any RAM overflow during program execution. Instead, it just displays a warning message when little RAM is left for local variables.

This can lead to program malfunctions at execution time, if local variables happen to take up more space than the remaining RAM.

How can one have the compiler detect whether local variables memory allocation will cause RAM overflow during execution or not, and warn the user at compile time only in those specific cases?

body:
You are describing "memory profiling" and is not typically a feature of C or C++.  It is more common in higher level languages, such as Java, but C is a considered a much lower level language.

Typically such profiling is done at runtime using external tools such as `valgrind` and `massif`.

It's not really possible for the compiler to work out what your program will be doing when it is using external data and inputs to control how it works. Any such tests at compile time would be pretty much meaningless since any that pass in testing may still cause problems at runtime depending on what else may be happening at the same time, or even what may have happened in the past (see [heap fragmentation](https://cpp4arduino.com/2018/11/06/what-is-heap-fragmentation.html)).


message:
Serial ports grayed out when Arduino plugged in (MS Surface Pro X, Windows 10 Pro 21H2). How do I get Arduino IDE to recognize them?

I am helping a Windows user program an Arduino application. The computer is a Surface Pro X running Windows 10 Pro 21H2. It only has USB-C ports (which cannot be used to power or program Arduinos) so he's using [this USB-C hub][1] and a USB-type A to USB-C cable to program it. The Arduino is a Sparkfun Artemis Redboard (Cortex M4).

When he connects the Arduino to the Surface Pro, the Arduino powers up as normal, but no serial ports are visible in the Arduino IDE, and the serial port line remains grayed out.

By contrast, the same USB-C hub, cable, and Arduino can be programmed by a Mac. I run Linux and don't know anything about USB port access on either Mac or Windows, but I got the impression that manually installing drivers should not be necessary on either.

Any advice on how to get the Arduino IDE to recognize this as a serial port when plugged in to a Surface Pro?

  [1]: https://www.mokinglobal.com/product/usb-c-hub-5-in-1-21.html

body:
Consider running through [this Sparkfun Hookup Guide][1] for the [SparkFun RedBoard Artemis][2].  There is a section dealing with this problem:

> My Nano is not showing up as a COM port?!
> Most Artemis carrier boards use the CH340C IC which is not affected by
> this issue. This issue is only for the Nano that uses the much smaller
> CH340E.

  [1]: https://learn.sparkfun.com/tutorials/hookup-guide-for-the-sparkfun-redboard-artemis-nano/troubleshooting
  [2]: https://www.sparkfun.com/products/15444https://


message:
ESP8266 - Can't upload or use Serial Monitor - Ubuntu 22.04 LTS

When trying to use the ESP8266 Node MCU or Multiple Feather Huzzah's I get the following error. I can program Arduino Uno & Mega without issue.
```
warning: espcomm_sync failed
error: espcomm_open failed
error: espcomm_upload_mem failed
error: espcomm_upload_mem failed
```
I have tried all the many fixes on the web, the real issue is that all of these devices work fine with a windows machine. Also I use VMM and QEMU, I built a Windows 10 VM and Ubuntu 20.04 - When I share ttyS0 with either of these VM's I can program the 8266 just fine, but I can not directly from my host running 22.04. I can work with the VM's but I would rather not.

Any help would be appreciated.

Thank you

body:
I found this solution in a separate post - it fixed my issue and I was able to replicate and validate this worked on a second 22.04 machine.

```
systemctl stop brltty-udev.service
sudo systemctl mask brltty-udev.service
systemctl stop brltty.service
systemctl disable brltty.service
```


message:
How to convert a reading to percentage in Arduino

I am using [this example][1] on how to use a photoresistor to detect the value of light that the sensor is getting but I want to convert the reading of the resistor to a percentage but I can't use the `map()` function from the Arduino [docs][2] but I can implement something similar to it

so I created this simple function
```
float photo_resistor(int pinNum)
{
    float reading = analogRead(pinNum);
    if (reading == 54)
    {
     return 0.0;
    }
    return (reading - 54)/(100/920);

}
```
as in the docs I want to use an output range from 0 to 100 and the minimum reading i got is 54 and the maximum is 974
```
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
// this becomes (x - 54)*(100 - 0 ) / ( 974 - 54) + 0;
// (x-54)/(100/920);
```
but this does not output a value from on the scale from 0-100

[![enter image description here][3]][3]

  [1]: http://developer.wildernesslabs.co/Hardware/Tutorials/Electronics/Part5/Resistive_Sensor_Lab/
  [2]: https://www.arduino.cc/reference/en/language/functions/math/map/
  [3]: https://i.stack.imgur.com/DnoNl.png

body:
You wrote:
> ```c++
> return (reading - 54)/(100/920);
> ```

In C++, `100/920` is zero. This is because, when both operands are
integers, the division operator performs an integer division. You can
overcome this problem by making sure that at least one of the operands
is a floating point number. For instance, `100.0/920` is roughly 0.1087.

However, your division is backwards. You probably mean

```c++
return (reading - 54) * (100.0/920);
```


message:
TVout characters don't line up

I am messing around with the Arduino TVout library and I have created some multi-character tiles (I don't know what else to call them). Just to start out, I created a simple X that spans across 4 characters but when I print them to the screen they align correctly.

How they should align:

How they really align:

Here is the code I have so far:

Main file:
```cpp
#include "expressions.h"
#include <TVout.h>

TVout win;
unsigned char x = 0, y = 0;

void setup() {
  win.begin(NTSC);
  win.select_font(expr8x8);
}

void loop() {
  win.clear_screen();
  x = 2, y = 5;
  print_char(0, x, y);

  win.delay(1);
}

void print_char(char c, int startx, int starty) {
  int tmpx = startx, tmpy = starty;
  for (char i = c; i < c + 4; i++) {
    win.print_char(tmpx * 8, tmpy * 8, i);
    if (++tmpx == c + 2) {
      tmpy++;
      tmpx = startx;
    }
  }
}
```

expressions.h
```
#define EXPRESSIONS8x8_H

#include <avr/pgmspace.h>
extern const unsigned char expr8x8[];
```

expressions.cpp
```cpp
#include "expressions.h"

PROGMEM const unsigned char expr8x8[] = {
  8, 8, 0, // width, height, start

  // broken
  0x00, 0x00, 0x30, 0x38, 0x1c, 0x1e, 0x07, 0x03,
  0x00, 0x00, 0x0c, 0x1c, 0x38, 0x70, 0xe0, 0xc0,
  0x03, 0x07, 0x0e, 0x1c, 0x38, 0x38, 0x00, 0x00,
  0xc0, 0xc0, 0x70, 0x38, 0x1c, 0x0c, 0x00, 0x00,

  // happy
  0x00, 0x00, 0x00, 0x00, 0x07, 0x1f, 0x38, 0x70,
  0x00, 0x00, 0x00, 0x00, 0xe0, 0xf8, 0x1c, 0x0e,
  0x60, 0x60, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00
};
```

Is there something I am doing wrong? How do I fix this bug/issue?

body:
I believe your problem is in this line:

```c++
if (++tmpx == c + 2) {
```

Here you are mixing two different things:

* `c` is a character code, i.e. the index of a glyph within the
  `expr8x8` font
* `tmpx` is the index of a character cell (a 8×8 block of pixels) within
  a line of the screen

Comparing those makes little sense. And in fact, the comparison will
always be false, as `c` is zero, and `tmpx` starts at 2 and increases
from there.

I guess what you meant was:

```c++
if (++tmpx == startx + 2) {
```


message:
Why are raw data bytes not converting to ASCII, but only in one part of my program?

I am writing a program to read from the Atlas Scientific EZO-HUM, -O2, and -CO2 sensors simultaneously via the I2C protocol with an Arduino Uno. The sensors all work, but for some reason, just the O2 sensor data does not convert to ASCII text. Below is my program:

    #include <Ezo_i2c.h>
    #include <Ezo_i2c_util.h>
    #include <iot_cmd.h>
    #include <sequencer1.h>
    #include <sequencer2.h>
    #include <sequencer3.h>
    #include <sequencer4.h>
    #include <Wire.h>                //enable I2C.

    #define HUM_address 111          //default I2C ID number for EZO Humidity sensor.
    #define O2_address 108           //default I2C ID number for EZO O2 sensor.
    #define CO2_address 105          //default I2C ID number for EZO CO2 sensor.

    //EZO-HUM vars
    //   | |
    //   V V
    char computerdata[32];           //we make a 32 byte character array to hold incoming data from a pc/mac/other.
    byte received_from_computer = 0; //we need to know how many characters have been received.
    byte serial_event = false;       //a flag to signal when data has been received from the pc/mac/other.
    byte code = 0;                   //used to hold the I2C response code.
    char Humidity_data[22];          //we make a 22-byte character array to hold incoming data from the Humidity sensor.
    byte in_char = 0;                //used as a 1 byte buffer to store inbound bytes from the Humidity  sensor.
    byte i = 0;                      //counter used for Humidity_data array.
    int time_ = 1000;                 //used to set the delay needed to process the command sent to the EZO Humidity sensor.

    //EZO-CO2 vars
    //   | |
    //   V V
    //char CO2_computerdata[20];           //we make a 20 byte character array to hold incoming data from a pc/mac/other.
    byte CO2_received_from_computer = 0; //we need to know how many characters have been received.
    byte CO2_serial_event = false;       //a flag to signal when data has been received from the pc/mac/other.
    byte CO2_code = 0;                   //used to hold the I2C response code.
    char CO2_data[20];               //we make a 20-byte character array to hold incoming data from the Co2 sensor.
    byte CO2_in_char = 0;                //used as a 1 byte buffer to store inbound bytes from the Co2 sensor.
    byte CO2_i = 0;                      //counter used for Co2_data array.
    //int CO2_time_ = 1000;                 //used to set the delay needed to process the command sent to the EZO Co2 sensor.
    int Co2_int;                     //int var used to hold the value of the Co2.

    //EZO-O2 vars
    //   | |
    //   V V
    //char O2_computerdata[20];           //we make a 20 byte character array to hold incoming data from a pc/mac/other.
    byte O2_received_from_computer = 0; //we need to know how many characters have been received.
    byte O2_serial_event = false;       //a flag to signal when data has been received from the pc/mac/other.
    byte O2_code = 0;                   //used to hold the I2C response code.
    char O2_data[20];                //we make a 20-byte character array to hold incoming data from the o2 sensor.
    byte O2_in_char = 0;                //used as a 1 byte buffer to store inbound bytes from the o2 sensor.
    byte O2_i = 0;                      //counter used for o2_data array.
    //int O2_time_ = 600;                 //used to set the delay needed to process the command sent to the EZO o2 sensor.

    char *HUM;                       //char pointer used in string parsing.
    char *TMP;                       //char pointer used in string parsing.
    char *NUL;                       //char pointer used in string parsing (the sensor outputs some text that we don't need).
    char *DEW;                       //char pointer used in string parsing.
    char *O2_reading;
    char *O2_reading_2;

    float HUM_float;                 //float var used to hold the float value of the humidity.
    float TMP_float;                 //float var used to hold the float value of the temperatur.
    float DEW_float;                 //float var used to hold the float value of the dew point.
    float O2_float;
    float CO2Con_float;

    void setup()                    //hardware initialization.
    {
      Serial.begin(9600);           //enable serial port.
      Wire.begin();                 //enable I2C port.
      while(!Serial);            //wait for serial monitor to be open
    }

    void serialEvent() {                                                              //this interrupt will trigger when the data coming from the serial monitor(pc/mac/other) is received.
      received_from_computer = Serial.readBytesUntil(13, computerdata, 22);           //we read the data sent from the serial monitor(pc/mac/other) until we see a <CR>. We also count how many characters have been received.
      computerdata[received_from_computer] = 0;                                       //stop the buffer from transmitting leftovers or garbage.
      serial_event = true;                                                            //set the serial event flag.
    }

    void loop() {                                                                     //the main loop.
      if (serial_event == true) {                                                     //if a command was sent to the EZO device.
        for (i = 0; i <= received_from_computer; i++) {                               //set all char to lower case, this is just so this exact sample code can recognize the "sleep" command.
          computerdata[i] = tolower(computerdata[i]);                                 //"Sleep" ≠ "sleep"
        }
        i=0;                                                                          //reset i, we will need it later

     //---------------------------------------------------------EZO-HUM Sensor-----------------------------------------------------------------------------//
        Wire.beginTransmission(HUM_address);                                              //call the circuit by its ID number.
        Wire.write(computerdata);                                                     //transmit the command that was sent through the serial port.
        Wire.endTransmission();                                                       //end the I2C data transmission.

    // Need to talk to each sensor separately. Because they're all Atlas Scientific EZO sensors, they all respond to the same commands

        if (strcmp(computerdata, "sleep") != 0) {                                     //if the command that has been sent is NOT the sleep command, wait the correct amount of time and request data.
                                                                                //if it is the sleep command, we do nothing. Issuing a sleep command and then requesting data will wake the Humidity  sensor.

          delay(time_);                                                               //wait the correct amount of time for the circuit to complete its instruction.

          Wire.requestFrom(HUM_address, 22, 1);                                           //call the circuit and request 22 bytes.
          code = Wire.read();                                                         //the first byte is the response code, we read this separately.

          switch (code) {                       //switch case based on what the response code is.
            case 1:                             //decimal 1.
              Serial.println("HUM Success");        //means the command was successful.
              break;                            //exits the switch case.

            case 2:                             //decimal 2.
              Serial.println("HUM Failed");         //means the command has failed.
              break;                            //exits the switch case.

            case 254:                           //decimal 254.
              Serial.println("HUM Pending");        //means the command has not yet been finished calculating.
              break;                            //exits the switch case.

            case 255:                           //decimal 255.
              Serial.println("HUM No Data");        //means there is no further data to send.
              break;                            //exits the switch case.
        }

        while (Wire.available()) {              //are there bytes to receive.
          in_char = Wire.read();                //receive a byte.
          Humidity_data[i] = in_char;           //load this byte into our array.
          //Serial.println((int)in_char);
          i += 1;                               //incur the counter for the array element.
          if (in_char == 0) {                   //if we see that we have been sent a null command.
            i = 0;                              //reset the counter i to 0.
            break;                              //exit the while loop.
          }
        }
        if (computerdata[0] == 'r') string_pars();    //uncomment this function if you would like to break up the comma separated string into its individual parts.
      }

     //---------------------------------------------------------EZO-O2 Sensor-----------------------------------------------------------------------------//

        Wire.beginTransmission(O2_address);                                           //call the circuit by its ID number.
        Wire.write(computerdata);                                                     //transmit the command that was sent through the serial port.
        Wire.endTransmission();                                                       //end the I2C data transmission.

        if (strcmp(computerdata, "sleep") != 0) {                                     //if the command that has been sent is NOT the sleep command, wait the correct amount of time and request data.
                                                                                //if it is the sleep command, we do nothing. Issuing a sleep command and then requesting data will wake the Humidity  sensor.

          delay(time_);                                                               //wait the correct amount of time for the circuit to complete its instruction.

          Wire.requestFrom(O2_address, 22, 1);                                           //call the circuit and request 22 bytes.
          O2_code = Wire.read();                                                         //the first byte is the response code, we read this separately.

          switch (O2_code) {                       //switch case based on what the response code is.
            case 1:                             //decimal 1.
              Serial.println("O2 Sensor Success");        //means the command was successful.
              break;                            //exits the switch case.

            case 2:                             //decimal 2.
              Serial.println("O2 Sensor Failed");         //means the command has failed.
              break;                            //exits the switch case.

            case 254:                           //decimal 254.
              Serial.println("O2 Sensor Pending");        //means the command has not yet been finished calculating.
              break;                            //exits the switch case.

            case 255:                           //decimal 255.
              Serial.println("O2 Sensor No Data");        //means there is no further data to send.
              break;                            //exits the switch case.
        }

        while (Wire.available()) {              //are there bytes to receive.
          O2_in_char = Wire.read();                //receive a byte.
          O2_data[i] = O2_in_char;           //load this byte into our array.
          Serial.println((int)O2_in_char);
          Serial.println(O2_data);
          i += 1;                               //incur the counter for the array element.
          if (O2_in_char == 0) {                   //if we see that we have been sent a null command.
            i = 0;                              //reset the counter i to 0.
            break;                              //exit the while loop.
          }
        }
    //    Serial.print("O2 Concentration:");                      //This code doesn't output any information, although the program receives bytes which display in the while loop.
    //    Serial.println(O2_data);
    //    Serial.println();

        if (computerdata[0] == 'r') O2_string_pars();             //This code also doesn't output any information.
      }

     //---------------------------------------------------------EZO-CO2 Sensor-----------------------------------------------------------------------------//

        Wire.beginTransmission(CO2_address);                                          //call the circuit by its ID number.
        Wire.write(computerdata);                                                     //transmit the command that was sent through the serial port.
        Wire.endTransmission();                                                       //end the I2C data transmission.

        if (strcmp(computerdata, "sleep") != 0) {                                     //if the command that has been sent is NOT the sleep command, wait the correct amount of time and request data.
                                                                                //if it is the sleep command, we do nothing. Issuing a sleep command and then requesting data will wake the Humidity  sensor.

          delay(time_);                                                               //wait the correct amount of time for the circuit to complete its instruction.

          Wire.requestFrom(CO2_address, 22, 1);                                           //call the circuit and request 22 bytes.
          CO2_code = Wire.read();                                                         //the first byte is the response code, we read this separately.

          switch (CO2_code) {                       //switch case based on what the response code is.
            case 1:                             //decimal 1.
              Serial.println("CO2 Sensor Success");        //means the command was successful.
              break;                            //exits the switch case.

            case 2:                             //decimal 2.
              Serial.println("CO2 Sensor Failed");         //means the command has failed.
              break;                            //exits the switch case.

            case 254:                           //decimal 254.
              Serial.println("CO2 Sensor Pending");        //means the command has not yet been finished calculating.
              break;                            //exits the switch case.

            case 255:                           //decimal 255.
              Serial.println("CO2 Sensor No Data");        //means there is no further data to send.
              break;                            //exits the switch case.
        }

        while (Wire.available()) {              //are there bytes to receive.
          CO2_in_char = Wire.read();                //receive a byte.
          CO2_data[i] = CO2_in_char;           //load this byte into our array.
          //Serial.println((int)CO2_in_char);
          i += 1;                               //incur the counter for the array element.
          if (CO2_in_char == 0) {                   //if we see that we have been sent a null command.
            i = 0;                              //reset the counter i to 0.
            break;                              //exit the while loop.
          }
        }
        Serial.print("CO2 Concentration:");
        Serial.println(CO2_data);
      }

      serial_event = false;                     //reset the serial event flag

    //  if (computerdata[0] == 'r') string_pars();    //uncomment this function if you would like to break up the comma separated string into its individual parts.
    }
       }

    void string_pars() {                        //this function will break up the CSV string into its 3 individual parts. HUM|TMP|DEW.
                                                //this is done using the C command “strtok”.

      HUM = strtok(Humidity_data, ",");         //let's pars the string at each comma.
      TMP = strtok(NULL, ",");                  //let's pars the string at each comma.
      NUL = strtok(NULL, ",");                  //let's pars the string at each comma (the sensor outputs the word "DEW" in the string, we dont need it.
      DEW = strtok(NULL, ",");                  //let's pars the string at each comma.

      Serial.println();                          //this just makes the output easier to read by adding an extra blank line.

      Serial.print("HUM:");                      //we now print each value we parsed separately.
      Serial.println(HUM);                       //this is the humidity value.

      Serial.print("Air TMP:");                  //we now print each value we parsed separately.
      Serial.println(TMP);                       //this is the air temperatur value.

      Serial.print("DEW:");                      //we now print each value we parsed separately.
      Serial.println(DEW);                       //this is the dew point.
      Serial.println();

      //uncomment this section if you want to take the values and convert them into floating point number.
      /*
        HUM_float=atof(HUM);
        TMP_float=atof(TMP);
        DEW_float=atof(DEW);
      */
    }

    void O2_string_pars() {                        //this function will break up the CSV string
                                                //this is done using the C command “strtok”.

      O2_reading = strtok(O2_data, ",");         //let's pars the string at each comma.
    //  O2_reading_2 = strtok(NULL, ",");

      Serial.println();                          //this just makes the output easier to read by adding an extra blank line.

      Serial.print("O2 Concentration: ");
      Serial.println(O2_reading);
    //  Serial.println("O2 extra info: ");
    //  Serial.print(O2_reading_2);

    }

The following is the output from the program:

> HUM Success
>
> HUM:45.39
>
> Air TMP:23.61
>
> DEW:11.16
>
> O2 Sensor Success
>
> 50
>
> 48
>
> 46
>
> 56
>
> 53
>
> 0
>
>
> O2 Concentration:
>
> CO2 Sensor Success
>
> CO2 Concentration:599

body:
You are relying on a NULL byte being received to reset your `i` pointer variable. If that NULL never arrives for whatever reason you won't be writing your new data to the start of your next array.

You should *always* reset the counter before receiving new data regardless of what you have just received.


message:
How often should we poll the 74HC165 if we wish to detect 'button' presses?

I am going to either chain 4 shift registers, or connect 4 shift registers to an ESP32 that is connected to a computer with bluetooth.

I understand at 3.3 volts, the 74HC165 can run at a clock speed of up to 18mhz. (5mhz at 2v, 25mhz at 4v).

What is normal for monitoring events on the shift registers? Is it normal to endlessly poll the shift registers? Or is a short delay between each read possible and/or desirable?

I cant imagine a reason for wanting to check for "events" (button presses, etc...) 1 million times per second. We could I guess, I just don't know if we should.

(I am new to the ESP32, so I am not sure how maintaining a bluetooth connection impacts things)

body:
For this you can employ the *nyquist-shannon* theory for signal sampling.

In short it states that "to sample a signal frequency of X you need at least 2x X samples per second".

So if you know how long a button will be pressed for (or released for, whichever is faster, i.e., the "highest frequency component of interest") then you know you need to sample with an interval of *at most half* that time.

So for a button that you may press for 100ms you know you need to sample at most every 50ms, preferably faster.


body:
Yes, it's pretty normal to endlessly poll the buttons. Even the original NES controller did it exactly that way, its protocol works exactly like you'd talk to a shift register. [Majenko alredy gave an answer on a good polling frequency][1], here's the additional hardware you'd need to work with interrupts instead of polling:

You'd need to add a bunch of OR gates (or AND gates if your buttons pull down instead of up) in "parallel" to your shift registers, so that you've got one signal with the semantics "(at least) one of the buttons have been pressed".

This signal then can be connected to the SH/LD input of your shift registers, triggering the shift registers to load (i.e. sample and hold) the inputs. In addition, it needs to be connected to one GPIO of your Arduino/ESP, to which you connect an interrupt handler on the software side. That interrupt handler then polls the shift register once (or sets a flag that causes your main loop to poll it). You may need an additional gate and GPIO to allow the polling routine to keep SH/LD HIGH while polling, depending on your use case and how bouncy your buttons are.

You could even use that signal to wake the ESP from deep sleep, poll the buttons once, do whatever needs to be done as a reaction to that button, then go back to deep sleep.

In conclusion: Endless polling is mostly the way to go, because of the hardware overhead to implement an interrupt solution.

  [1]: https://arduino.stackexchange.com/a/89881/55698


message:
Assign a list of char *

Explain in a simple example. How to pass a `char*` to a function?

    #define name1 "ABCD"
    #define name2 "EFGH"
    #define name3 "HIJK"

    char *list[3] = {};

    void printList(char *l, int x) {
      for (int i = 0; i < x; i++) {
        Serial.println(l[i]);
      }

    }

    void setup() {
      // put your setup code here, to run once:
      Serial.begin(115200);
      Serial.println("start");
      list[0] = name1;
      list[1] = name2;
      list[2] = name3;

      printList(list, 3);

    }

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

    }

I get an error:

    /home/guy/Documents/Dropbox/Arduino/sketch_jun12a/sketch_jun12a.ino: In function 'void setup()':
    sketch_jun12a:24:13: error: cannot convert 'char**' to 'char*'
       24 |   printList(list, 3);
          |             ^~~~
          |             |
          |             char**
    /home/guy/Documents/Dropbox/Arduino/sketch_jun12a/sketch_jun12a.ino:7:22: note:   initializing argument 1 of 'void printList(char*, int)'
        7 | void printList(char *l, int x) {
          |                ~~~~~~^
    exit status 1
    cannot convert 'char**' to 'char*'

body:
Just change this:

```c++
void printList(char *l, int x) {
```

to this:

```c++
void printList(char **l, int x) {
```

OK, this deserves some explanation...

In C and C++, when you **use** (not when you _define_) an array
identifier, it [decays to a pointer to its first element][decay]. In
this case,

```c++
char *list[3] = {};
```

is an array of pointers to `char`. When you use it here:

```c++
printList(list, 3);
```

it decays to a pointer to pointer to `char`. Thus the double `*` needed
in the parameter declaration.

**Side note**: to get really correct C++, you should replace every
instance of `char` by `const char`. This is because the characters in
question belong to string literals, and you are not allowed to modify a
string literal at run time.

[decay]: https://stackoverflow.com/questions/1461432/what-is-array-to-pointer-decay


message:
Enable full float-capable snprintf() library with PlatformIO

I want to use snprintf() to format floats to a string.

I know the "normal" version of Arduino's avrlibc had all the float-formatting code removed to make it smaller.

I also know that at some point in the fairly recent past, it became possible to force gcc to use an alternate (forked?) larger Arduino avrlibc that had all the float-formatting functionality that was originally removed put back in.

However... I'm still trying to find an actual explanation of *how to **do** that* for an Arduino project using **PlatformIO**.

I found a clue someplace where someone said, "change linker rules in platform.txt file to enable floating point xxprintf()" (which hopefully enables floating point support in other float-to-string functions as well).

Problem #1: `platform.txt` appears to be global rather than local to this one project. There's no file named "platform.txt" anywhere in the project's directory.

Problem #2: I **do** have *several dozen* files on my computer named `platform.txt`... I don't know which one is the one that actually matters to PlatformIO, and I don't know enough about PlatformIO's internals to know how to figure out which `platform.txt` is the one that *matters*.

Problem #3: Even if I manage to figure out which `platform.txt` is the one I need to modify, I still don't know what exact modifications actually need to be *made*.

---

Update: I stumbled over a thread in Arduino's Issue discussion (["Sprintf for floats on Arduino Mega 293"][1]) that said to append the text quoted further down to boards.txt. The relevant line appears to be `mega.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt`

So... I'm guessing that what I need to put somewhere in `platform.txt` (once I figure out which `platform.txt` is the one that matters) is `-Wl,-u,vfprintf -lprintf_flt`

~~~
##############################################################

menu.printf=AVR printf Version
menu.scanf=AVR scanf Version

yun.menu.printf.default=Default printf
yun.menu.printf.default.avr_printf_flags=
yun.menu.printf.full=Full printf
yun.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
yun.menu.printf.minimal=Minimal printf
yun.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

yun.menu.scanf.default=Default scanf
yun.menu.scanf.default.avr_scanf_flags=
yun.menu.scanf.full=Full scanf
yun.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
yun.menu.scanf.minimal=Minimal scanf
yun.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

yun.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

uno.menu.printf.default=Default printf
uno.menu.printf.default.avr_printf_flags=
uno.menu.printf.full=Full printf
uno.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
uno.menu.printf.minimal=Minimal printf
uno.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

uno.menu.scanf.default=Default scanf
uno.menu.scanf.default.avr_scanf_flags=
uno.menu.scanf.full=Full scanf
uno.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
uno.menu.scanf.minimal=Minimal scanf
uno.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

uno.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

diecimila.menu.printf.default=Default printf
diecimila.menu.printf.default.avr_printf_flags=
diecimila.menu.printf.full=Full printf
diecimila.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
diecimila.menu.printf.minimal=Minimal printf
diecimila.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

diecimila.menu.scanf.default=Default scanf
diecimila.menu.scanf.default.avr_scanf_flags=
diecimila.menu.scanf.full=Full scanf
diecimila.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
diecimila.menu.scanf.minimal=Minimal scanf
diecimila.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

diecimila.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

nano.menu.printf.default=Default printf
nano.menu.printf.default.avr_printf_flags=
nano.menu.printf.full=Full printf
nano.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
nano.menu.printf.minimal=Minimal printf
nano.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

nano.menu.scanf.default=Default scanf
nano.menu.scanf.default.avr_scanf_flags=
nano.menu.scanf.full=Full scanf
nano.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
nano.menu.scanf.minimal=Minimal scanf
nano.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

nano.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

mega.menu.printf.default=Default printf
mega.menu.printf.default.avr_printf_flags=
mega.menu.printf.full=Full printf
mega.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
mega.menu.printf.minimal=Minimal printf
mega.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

mega.menu.scanf.default=Default scanf
mega.menu.scanf.default.avr_scanf_flags=
mega.menu.scanf.full=Full scanf
mega.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
mega.menu.scanf.minimal=Minimal scanf
mega.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

mega.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

megaADK.menu.printf.default=Default printf
megaADK.menu.printf.default.avr_printf_flags=
megaADK.menu.printf.full=Full printf
megaADK.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
megaADK.menu.printf.minimal=Minimal printf
megaADK.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

megaADK.menu.scanf.default=Default scanf
megaADK.menu.scanf.default.avr_scanf_flags=
megaADK.menu.scanf.full=Full scanf
megaADK.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
megaADK.menu.scanf.minimal=Minimal scanf
megaADK.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

megaADK.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

leonardo.menu.printf.default=Default printf
leonardo.menu.printf.default.avr_printf_flags=
leonardo.menu.printf.full=Full printf
leonardo.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
leonardo.menu.printf.minimal=Minimal printf
leonardo.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

leonardo.menu.scanf.default=Default scanf
leonardo.menu.scanf.default.avr_scanf_flags=
leonardo.menu.scanf.full=Full scanf
leonardo.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
leonardo.menu.scanf.minimal=Minimal scanf
leonardo.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

leonardo.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

micro.menu.printf.default=Default printf
micro.menu.printf.default.avr_printf_flags=
micro.menu.printf.full=Full printf
micro.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
micro.menu.printf.minimal=Minimal printf
micro.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

micro.menu.scanf.default=Default scanf
micro.menu.scanf.default.avr_scanf_flags=
micro.menu.scanf.full=Full scanf
micro.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
micro.menu.scanf.minimal=Minimal scanf
micro.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

micro.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

esplora.menu.printf.default=Default printf
esplora.menu.printf.default.avr_printf_flags=
esplora.menu.printf.full=Full printf
esplora.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
esplora.menu.printf.minimal=Minimal printf
esplora.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

esplora.menu.scanf.default=Default scanf
esplora.menu.scanf.default.avr_scanf_flags=
esplora.menu.scanf.full=Full scanf
esplora.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
esplora.menu.scanf.minimal=Minimal scanf
esplora.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

esplora.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

mini.menu.printf.default=Default printf
mini.menu.printf.default.avr_printf_flags=
mini.menu.printf.full=Full printf
mini.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
mini.menu.printf.minimal=Minimal printf
mini.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

mini.menu.scanf.default=Default scanf
mini.menu.scanf.default.avr_scanf_flags=
mini.menu.scanf.full=Full scanf
mini.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
mini.menu.scanf.minimal=Minimal scanf
mini.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

mini.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

ethernet.menu.printf.default=Default printf
ethernet.menu.printf.default.avr_printf_flags=
ethernet.menu.printf.full=Full printf
ethernet.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
ethernet.menu.printf.minimal=Minimal printf
ethernet.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

ethernet.menu.scanf.default=Default scanf
ethernet.menu.scanf.default.avr_scanf_flags=
ethernet.menu.scanf.full=Full scanf
ethernet.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
ethernet.menu.scanf.minimal=Minimal scanf
ethernet.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

ethernet.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

fio.menu.printf.default=Default printf
fio.menu.printf.default.avr_printf_flags=
fio.menu.printf.full=Full printf
fio.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
fio.menu.printf.minimal=Minimal printf
fio.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

fio.menu.scanf.default=Default scanf
fio.menu.scanf.default.avr_scanf_flags=
fio.menu.scanf.full=Full scanf
fio.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
fio.menu.scanf.minimal=Minimal scanf
fio.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

fio.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

bt.menu.printf.default=Default printf
bt.menu.printf.default.avr_printf_flags=
bt.menu.printf.full=Full printf
bt.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
bt.menu.printf.minimal=Minimal printf
bt.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

bt.menu.scanf.default=Default scanf
bt.menu.scanf.default.avr_scanf_flags=
bt.menu.scanf.full=Full scanf
bt.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
bt.menu.scanf.minimal=Minimal scanf
bt.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

bt.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

LilyPadUSB.menu.printf.default=Default printf
LilyPadUSB.menu.printf.default.avr_printf_flags=
LilyPadUSB.menu.printf.full=Full printf
LilyPadUSB.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
LilyPadUSB.menu.printf.minimal=Minimal printf
LilyPadUSB.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

LilyPadUSB.menu.scanf.default=Default scanf
LilyPadUSB.menu.scanf.default.avr_scanf_flags=
LilyPadUSB.menu.scanf.full=Full scanf
LilyPadUSB.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
LilyPadUSB.menu.scanf.minimal=Minimal scanf
LilyPadUSB.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

LilyPadUSB.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

lilypad.menu.printf.default=Default printf
lilypad.menu.printf.default.avr_printf_flags=
lilypad.menu.printf.full=Full printf
lilypad.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
lilypad.menu.printf.minimal=Minimal printf
lilypad.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

lilypad.menu.scanf.default=Default scanf
lilypad.menu.scanf.default.avr_scanf_flags=
lilypad.menu.scanf.full=Full scanf
lilypad.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
lilypad.menu.scanf.minimal=Minimal scanf
lilypad.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

lilypad.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

pro.menu.printf.default=Default printf
pro.menu.printf.default.avr_printf_flags=
pro.menu.printf.full=Full printf
pro.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
pro.menu.printf.minimal=Minimal printf
pro.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

pro.menu.scanf.default=Default scanf
pro.menu.scanf.default.avr_scanf_flags=
pro.menu.scanf.full=Full scanf
pro.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
pro.menu.scanf.minimal=Minimal scanf
pro.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

pro.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

atmegang.menu.printf.default=Default printf
atmegang.menu.printf.default.avr_printf_flags=
atmegang.menu.printf.full=Full printf
atmegang.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
atmegang.menu.printf.minimal=Minimal printf
atmegang.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

atmegang.menu.scanf.default=Default scanf
atmegang.menu.scanf.default.avr_scanf_flags=
atmegang.menu.scanf.full=Full scanf
atmegang.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
atmegang.menu.scanf.minimal=Minimal scanf
atmegang.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

atmegang.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

robotControl.menu.printf.default=Default printf
robotControl.menu.printf.default.avr_printf_flags=
robotControl.menu.printf.full=Full printf
robotControl.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
robotControl.menu.printf.minimal=Minimal printf
robotControl.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

robotControl.menu.scanf.default=Default scanf
robotControl.menu.scanf.default.avr_scanf_flags=
robotControl.menu.scanf.full=Full scanf
robotControl.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
robotControl.menu.scanf.minimal=Minimal scanf
robotControl.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

robotControl.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

robotMotor.menu.printf.default=Default printf
robotMotor.menu.printf.default.avr_printf_flags=
robotMotor.menu.printf.full=Full printf
robotMotor.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
robotMotor.menu.printf.minimal=Minimal printf
robotMotor.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

robotMotor.menu.scanf.default=Default scanf
robotMotor.menu.scanf.default.avr_scanf_flags=
robotMotor.menu.scanf.full=Full scanf
robotMotor.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
robotMotor.menu.scanf.minimal=Minimal scanf
robotMotor.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

robotMotor.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

gemma.menu.printf.default=Default printf
gemma.menu.printf.default.avr_printf_flags=
gemma.menu.printf.full=Full printf
gemma.menu.printf.full.avr_printf_flags=-Wl,-u,vfprintf -lprintf_flt
gemma.menu.printf.minimal=Minimal printf
gemma.menu.printf.minimal.avr_printf_flags=-Wl,-u,vfprintf -lprintf_min

gemma.menu.scanf.default=Default scanf
gemma.menu.scanf.default.avr_scanf_flags=
gemma.menu.scanf.full=Full scanf
gemma.menu.scanf.full.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_flt
gemma.menu.scanf.minimal=Minimal scanf
gemma.menu.scanf.minimal.avr_scanf_flags=-Wl,-u,vfscanf -lscanf_min

gemma.compiler.c.elf.extra_flags={avr_printf_flags} {avr_scanf_flags}

##############################################################
~~~

### Update 2:
I found what appears to be the solution & posted it as my own answer, but I'm going to hold off for a day or two to test it thoroughly and make sure it actually works.

  [1]: https://github.com/arduino/ArduinoCore-avr/issues/293

body:
I finally found the answer at https://github.com/sstaub/LCDi2c

When using PlatformIO, add the following line to `platformio.ini` to enable full support for floats to printf():

`build_flags = -Wl,-u,vfprintf -lprintf_flt -lm`

### Caution:

This solution increases the amount of required flash by at least 2k. If you're using a Mega2560, you probably have flash to burn and really don't care. If you're using an Uno, Leonardo, Nano, or some other smaller AVR-based variant... it might matter, and you might *have* to care.

Tip: if the Nano/Leonardo/Uno doesn't have enough flash or SRAM, but you don't have space for a full-blown Mega2560, consider the "Arduino Nano Every". It uses the ATMega4809, with 48k flash and 6k SRAM.


message:
Fluctuation in 4 half bridge load cell via combinator

I'm currently using a Sparkfun 4 Half-Bridge load sensor (50 Kg) with combinator and amplifier both are from the Sparkfun. Link:https://www.sparkfun.com/products/10245 (For Load Sensors)https://www.sparkfun.com/products/13878 (Combinator)
https://www.sparkfun.com/products/13879 (HX711)
Our application requires long-term use i.e 8 to 10 hours of load application and monitoring the weight change during this time. Is this possible to achieve the above requirement using these chips/amplifiers? As we have experienced a drift in the readings if a weight is placed for more than approx. 3 minutes.
We are using 4 half-bridge sensors, a combinator, and an amplifier.The issues I am tackling right now are,
1) Zero balance: Load sensor value exponentially increases or decreases after calibration(this is a cyclic thing and keeps happening). The error ranges usually from -20g to +40g.
2) Drift in reading: When the load is placed and kept for more than 3 minutes a drift is observed which increases with time and rises up to 50 to 60g.
Can you please assist in this matter?
have used different libraries. Results are the same.

body:
Consider a change in temperature either the cause or partially the cause for weight drift.  [This supplier web page][1] talks about load cell temperature compensation.  There they consider a 2000lb load cell.  Their chart infers a 20 degree Fahrenheit change can cause an temperature uncompensated load cell to change about 1.5lb.

[![enter image description here][2]][2]

  [1]: https://www.interfaceforce.com/understanding-load-cell-temperature-compensation/
  [2]: https://i.stack.imgur.com/5PuRH.jpg


message:
NodeMCU ESP8266 - Which pins can I use?

I'm trying to connect 3 LEDs to a LoLin NodeMCU ESP8266. It seems that several or "all" of the digital pins have some special use that prevents me from using them.

 - If I connect the LEDs (and set pinmode to output) to D0, D1 and D2, then the blue bootloading LED will glow constantly, and the code does not seem to run at all (nothing in serial output).
 - D3 seems to constantly have high power, regardless what I code it to.
 - If I connect the LEDs (and set pinmode to output) to D1, D2 and D4, then the blue bootloading LED will glow constantly, and the serial output is garbled, but I guess the code is running.
 - If I connect the LEDs (and set pinmode to output) to D5, D6 and D7, then the blue bootloading LED will glow constantly, and the serial output is garbled, but I guess the code is running.

Yes, I have checked the baud rate, that's not the problem.

What's up with this?

body:
This link at [projecthub][1] says:

> In order to use digital pins, you should select GPIO numbers. For example, the D7 pin is defined as GPIO13. So you should set up the pin number 13 whenever you want to use D7 in your program. Also, you can use pin D2(GPIO4) as SDA and pin D1(GPIO5) as SCL

  [1]: https://create.arduino.cc/projecthub/electropeak/getting-started-w-nodemcu-esp8266-on-arduino-ide-28184f


message:
How to do 'for' loop over inline anonymous array

I have a bunch of pins given names in the header:

~~~
#define DIP5 53
#define DIP4 52
#define DIP3 51
#define DIP2 50
#define DIP1 49
#define DIP0 48
~~~

If it's not obvious, I have a bunch of DIP switches connected to digital pins 48..53 on an Arduino Mega2560

In setup(), I need to set them all to INPUT_PULLUP. I know it would probably be less total effort to just do a series of pinMode commands, like:

~~~
pinMode(DIP5, INPUT_PULLUP);
pinMode(DIP4, INPUT_PULLUP);
// ... and so on
~~~

... but I'm trying to enrich my C++ skills & be fancy, so I'm trying to figure out how to declare an anonymous array and foreach through it... something like:

~~~
for (int pin : {DIP5, DIP4, DIP3, DIP2, DIP1, DIP0}) {
    pinMode(pin, INPUT_PULLUP);
}
~~~

... which, presumably, the compiler will optimize away into 6 inline pinMode commands anyway, but let me feel good about having pretty source code ;-)

except... when I try it, I get the following error:

`error: deducing from brace-enclosed initializer list requires #include <initializer_list>`

... but when I add `#include <initializer_list>`, I get a different error:

`fatal error: initializer_list: No such file or directory`

**Any ideas what's wrong?**

also... *can* I safely assume that the compiler will just unroll the whole thing behind the scenes, or is there a real risk it will *actually* declare a *real* array in SRAM just to iterate through one time?

body:
> I'm trying to enrich my C++ skills & be fancy

The Arduino Mega is probably not the right platform to learn fancy C++.
The development environment doesn't support all the modern C++ stuff. In
particular...

> `fatal error: initializer_list: No such file or directory`

...it rests on avr-libc, which is a C library with only limited support
for C++.

> _can_ I safely assume that the compiler will just unroll the whole
> thing behind the scenes

No, you can't. The Arduino IDE, by default, calls avr-gcc with the `-Os`
flag, which means “optimize for code size”. Loop unrolling is the kind
of space vs. speed trade-off the compiler is unlikely to do with this
optimization setting. You can disassemble the .elf file if you want to
know for sure.

> is there a real risk it will _actually_ declare a _real_ array in SRAM
> just to iterate through one time?

I suggest you follow jsotola's advice and declare the array yourself.
You can declare the array `PROGMEM` if you want to avoid wasting RAM.
But given that it need not take more than 6 bytes (as `uint8_t[]`),
I would personally not even bother.


body:
Edgar Bonet [answers](https://arduino.stackexchange.com/a/89904/70020) why `for (int pin : {DIP5, DIP4, DIP3, DIP2, DIP1, DIP0}) {` isn't working. What you would get (where supported) with this syntax is an `initializer_list` which itself is not an *array type*, although it is *backed* by an array.

If you really did want to do something *like* that with an AVR-based Arduino you could do:

```c++
for (int pin : (const int []){DIP5, DIP4, DIP3, DIP2, DIP1, DIP0}) {
    pinMode(pin, INPUT_PULLUP);
}
```

This is technically not standard C++. It is the use of something called a [compound-literal](https://en.cppreference.com/w/c/language/compound_literal), which is being used to make an array typed value. This is a feature of C language feature (from C99 onward), not C++. However, the compiler and its configuration for Arduino allows its use as a language extension.

Should you do this? *Perhaps* not, for the reasons that Edgar, 6v6gt, and jsotola point out.  However, this much *can* be done if you really wanted. With respect to what it's doing efficiency-wise, you should test it.


message:
Is it possible to use assembly with arduino?

I want to learn assembly, but would like to learn by doing, is it possible to learn and make something useful with arduino hardware? I dont know where or how to start something like this.

body:
I suggest the [ARV libC](https://www.nongnu.org/avr-libc/user-manual/assembler.html) project as a starting point to HOW to get assmbler code running. And the [AVR Instruction Set Manual](https://ww1.microchip.com/downloads/en/DeviceDoc/AVR-Instruction-Set-Manual-DS40002198A.pdf) to get hold of the actual assembler mnemonics.

Maybe also my own [AVR Assembly Project](https://kwasi-ich.de/blog/2019/11/15/pwm_rider/) might be a helpful starting point with its code available on GitHub.