text:
Which WiFi module is able to host a custom web UI and can act as a station and AP?
I am an Arduino beginner. I am searching for a WiFi module that can act as a station and access point. It should have a external antenna.
I need to upload a script that starts a web server, so I can host my custom UI where the user can enter his WLAN credentials. After the credentials are set, it should try to connect to that WLAN like in the example script below, from a YouTube video.
I've tried the ESP8266, but uploading my script also overwrote the firmware.
Someone suggested the "Wemos D1 mini", but it does not have a big antenna and the one that I found with an antenna on Amazon has really really bad reviews.
#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <EEPROM.h>
ESP8266WebServer server(80);

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

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

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

void handlePortal() {
  if (server.method() == HTTP_POST) {
    strncpy(user_wifi.ssid, server.arg("ssid").c_str(), sizeof(user_wifi.ssid));
    strncpy(user_wifi.password, server.arg("password").c_str(), sizeof(user_wifi.password));
    user_wifi.ssid[server.arg("ssid").length()] = user_wifi.password[server.arg("password").length()] = '\0';
    EEPROM.put(0, user_wifi);
    EEPROM.commit();
    server.send(200, "text/html", "<!doctype html><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'><title>Wifi Setup</title>
Wifi Setup

Your settings have been saved successfully!
Please restart the device.
" );
  } else {
    server.send(200, "text/html", "<!doctype html><meta charset='utf-8'><meta name='viewport' content='width=device-width, initial-scale=1'><title>Wifi Setup</title>    <form action='/' method='post'>
Wifi Setup

SSID<input type='text' class='form-control' name='ssid'>

Password<input type='password' class='form-control' name='password'>

<button type='submit'>Save</button>
mrdiy.ca
</form> " );
  }
}

why is overwritten firmware a problem? why do you want a big antenna?

If I override the firmware, then my device does not respond to AT commands and the wifi does not work at all.
To be able to transmit and receive data over a bigger distance, at least 20 meters.

the wifi not working isn't related to the firmware. but that is a separate question.

Thx, I will ask another question then.

About the bad reviews: A lot of them complain about the necessity to desolder a 0Ohm SMD resistor for using the external antenna. Maybe you can find a similar kit with higher quality and without that desoldering job at a more reputable seller. As you seem to be located in germany, maybe reichelt or conrad, or internationally mouser. Haven't searched them for such a kit, but the quality on amazon is often the same as buying clones directly from china over aliexpress or similar.

@chrisl, yes and not only that, they also replaced a diode with a 0 Ohm resistor which can destroy the USB slot of your PC. I also made the experience that amazon more and more becomes like wish. The sellers also buy 5 star reviews by trying to bribe users with money / gift cards. I will search on another shop, thx

20 meters doesn't require any external antenna.

@Delta_G well, obviously It depends how many obstacles are in the way and how far the router is away. In my case the device will be put a few meters underground inside of a well, so I need a external antenna which I can place more at the top.

What do you mean depends on how far the router is away.  You said it was 20 meters.  You didn't mention anything about any well.

"uploading my script also overwrote the firmware" well, yeah... You're replacing the firmware, so obviously you can't have AT commands anymore.

@Delta_G so you automatically assumed that there are no obstacles in the way at all, only because I mentioned the distance is 20 meters? Thats a weird conclusion.

@dda is there a way to upload a sketch without also replacing the firmware?

I think the thing that you are not understanding is that the sketch and firmware are the same thing.  You only get one program on there.  The firmware is like a sketch that is preloaded and responds to AT commands.  You can write a program that also responds to AT commands or does any other thing you want.  But you cannot replace the program on the chip and then expect to still be able to use it.

@Black - I assumed nothing.  You didn't say there were any obstacles.  It's foolish to ask for help with something and then hide information about it.

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

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

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

As described above, you have created and uploaded your own firmware, by writing a sketch for the ESP in the Arduino IDE and uploading it. That program overwrites the one that was previously saved on the ESP. Thus the AT firmware is gone now. If you want to restore it, I'm sure you can find a fitting one for your board online and upload that again.
Though from what you have written so far it seems to me that you don't really need an AT firmware. Programming a custom portal for Wifi credentials is something where you need your own firmware anyway. You are now sitting at the place of the AT firmware with your own program. You can tell it what to do by writing the corresponding code.

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

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

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

Most ESP boards are not made with an external antenna as a priority. Even the ones with a socket for an external antenna might require some work to set them from internal to external antenna (like desoldering a resistor). You might be able to find one that doesn't require this. The Wemos D1 mini pro unfortunately requires the desoldering of the 0Ohm resistor. That is set by the manufacturer, so any reseller would do the work themselves. Not many will do that.
I cannot help you with finding such a board, as I don't know one (didn't need an external antenna yet). I would also just Google it.
And for better quality you need to go to reputable sources. Amazon is often just as a bad gamble as directly buying from China, AliExpress or similar. If you buy there, expect the quality to be rather low and some boards might even be broken. Sometimes that is OK, sometimes not.
Note: I'm really not an expert in RF, but honestly I would be surprised if connecting the antenna via a multiple meter-long cable wouldn't impact signal strength significantly. The ESP Wifi hardware is probably not built for driving the charge into such a long antenna. If this is the case it might be better to rethink your project design. You could put the ESP on top of the well (easily in reach of the Wifi network) and connect whatever you have down in the well from there. Or you could use another microcontroller handling the things in the well, connecting to the ESP via cable. Or if you absolutely need Wifi to connect down there, you might use an extender at the top of the well (can also be done by an ESP, if you don't want a full Wifi extender there). This very much depends on your exact requirements, so only a few ideas here.

Also I've noticed that in your handlePortal() function you are copying the SSID and password into the user_Wifi struct, but you are not actually changing the Wifi status. I would expect the code to do a change from AP to Station mode or reconnecting to the new Wifi.
You are saving the credentials into EEPROM (which is just flash on an ESP) and retrieving it on the next startup. I've made that error many times myself: when the expected Wifi network from the ESP doesn't show up, are you sure that there are no valid Wifi credentials saved from your previous run?

I hope that this helps you a bit.

"The ESP wifi hardware is probably not build for driving the charge into such a long antenna".  This is what I found with the ESP-07.  The losses in the cable far outweighed the gain from the antenna.  In the end it worked much better with the built in ceramic antenna, even when the antenna lead was kept to just a few cm.

So the SDK is permanent on the ESP and can't get overridden by flashing the flash memory?

No, the SDK is a software component on your PC, that is referenced by your own sketch (whenever you use Wifi in there for example). When you compile your own sketch,  the compiler also compiles the relevant parts of the SDK and puts both into one single binary, which then can be uploaded to the ESP. The SDK is needed when compiling a program for the ESP to access its special functions (like Wifi), so that the compiler knows how the calls to them should look like.

Thank you for the helpful informations @chrisl, I appreciate it


text:
ESP8266 I2C doesn't respond
I've got a huge problem using an ESP8266 from a Wemos D1 mini.
I am using an STM32L073RZ to send a request to an ESP8266 to send me the time it has acquired from a WiFi connection.
ESP code:
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
#include <Wire.h>

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

#define Status D4

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

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

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

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

  timeClient.begin();

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

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

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

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

STM simple code :
xD = HAL_I2C_Master_Receive(&hi2c2, 0x08<<1, czas, 8, 1000);

I tried everything. Adding pull ups, changing the GPIO speeds, etc.
Here is what my logic analyser gave me:

Still nothing. I've also checked which pins are the SDA and SCL which are D2 and D1. I've checked is the connection was correct and it should be, but still no response.
I don't know anymore what is wrong or what is not.
Schematic :

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

you must have a common ground

I've mentioned in the Edit, that I've connected G with GND of the STM32, didn't work

Is D4 high - to confirm you initialized ok?

Yup D4 is high.

What are the voltage levels? The STM32L073RZ seems to be a 3.3V device, and the ESP8266 also, however you mentioned 5V. I'm no expert on either, but that could be an issue.

Oh I have mentioned that I only connected 5V STM32 to 5V ESP8266 a lot later after so many attempts to connect with I2C. But welp still sitting 5h fixing it.

But welp still sitting 5h fixing it. - I don't understand that.

nothing just saying that it is exhausting. But I've burned now the WiFi connected it  accidently GND from STM32 to 5V ESP8266. But still I don't understand why my I2C didn't work

Are you powering both devices from 3.3V or 5V?

Both of them I power using simple USB port. Which is as I know 5V and is lowered to 3.3V.

From this: https://github.com/esp8266/Arduino/issues/5762 it appears that the esp8266 is not a good I2C slave device.

Hmmmm interesting. I thought it was an old post so maybe they've fixed it.
I saw some tutorials that they have used the I2C on ESP8266 so I thought maybe it works.

@Jakob2001 The esp8266 works as a master and, if you are lucky, works as a slave but only with another esp8266 as master. You could try reversing the roles. The esp8266 as master sending a time stamp occasionally to your STM device as an I2C slave.

I2C slave doesn't work on esp8266 because it doesn't have hardware support for I2C.
From the esp8266 Wire library documentation:

Wire library currently supports master mode ...

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

Even though the wire library has this "onRequest" the ESP8266 cannot use it ?

the code for slave is there. it just doesn't work.

The devices must share a common ground, and also you may need 4.7k pull-up resistors on both SDA and SCL.
See http://gammon.com.au/i2c for a whole lot more detail.

I've mentioned in the post that I've added pull ups in STM32 using software and in the edit I've mentioned I have connected G pin with GND pin of the STM32, still didn't work.

I was looking at your circuit diagram. You can't expect to post incorrect information and get a good answer.

Okey, my bad. But still the solution to add pull ups didn't work as well as connecting G pin to GND pin of stm32.

Both other answers are correct, but there is yet another problem with your design.
As can be clearly seen from the logic analyzer output, the master runs its clock at 100kb/sec (the standard i2c rate). That leaves the slave ca. 5us to react. For a 80 MHz chip this might be quite a tight timing by itself and requires thorough inspection and optimization of code. But a UDP request into the big net? Seriously?!
The only way it might work is if both parties could agree on a clock stretching. However, the i2c spec never defines precise timings for the stretched clock, so, the chances are, any master would probably break the session assuming the slave hung up long before the UDP request could be served.
Therefore, such requests need to be split into two parts. First, the master asks the slave to initiate the process of measurement/exchange/computation/whatever. And second, after an appropriate delay, the master issues another request for the buffered result from slave.

I am not that good at speeds etc. So 80 MHz and 100kb/s doesn't say a lot to me if context of if it makes in time etc.
I just wanted to get time, so I checked some tutorials how to do it. I've checked the I2C frequency speed and it was 100 kHz.
Let's say I don't know how to imagine yet these speeds and timings because 5 us itself is so small for me. Sorry for my incompetence. Although I would like to learn something how to judge whether the clock is enough or not, or it must be always the same or it can be a bit different.

@Jackob2001 The timings are written on the chart. 100khz is the smallest i2c speed by spec and by h/w implementations. 100000 cycles per second gives 10/1000000 sec per whole cycle and 5/1000000 for one half of it (when clock is low). Pretty trivial.

Sorry for pinging. h/w ? 10/100000 ? I just wondered how timing works here though because as you said there are two now. One is with 100kHz and the other one is with 80Mhz. Shouldn't the timing be the same for them to work ? If one of the clock is faster than the other then after one cycle the same bit can change mamy time ??? I don't know. Just saying after reading your comment about timing I want to learn more though

@Jackob2001 10/1000000 = 1/100000. Divide by two and get 5/1000000. If mcu runs at 80 mhz then it has ca. 400 internal clocks per 5 us (1/2 of i2c clock). This is at most 400 machine instructions even for RISC arch.

@Jackob2001 You probably need to start with master mode. Don't use libraries but implement it in software. This is pretty easy as master runs the scl clock the way it wants. Then you'll understand how i2c works completely.

I still don't quite get the calculations. Maybe this is not the best topic, but with timing things I don't quite get what you've tried to say. And implement it in software ? Hmmm I am quite new just happened to learn some stuff so I don't know how to get to it. WiFi code was made from yt videos :D

source: Stack Exchange

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

sort: votes


text:
Inaccurate and changed results with ultrasonic sensor
I have an issue using an ultrasonic sensor. The problem is that when I connect the ultrasonic sensor to my Arduino Nano, it always gives some random values, usually 130 cm, I don't know why.
I bought a second ultrasonic sensor and the problem reoccured. One I bought last year, and the other one I bought yesterday.
I tried several programs like the traditional one:
const int trigPin = 9;
const int echoPin = 10;
long duration;
int distance;

void setup() {
  pinMode(trigPin, OUTPUT);
  pinMode(echoPin, INPUT);
  Serial.begin(9600);
}

void loop() {
  digitalWrite(trigPin, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin, LOW);

  duration = pulseIn(echoPin, HIGH);
  distance = duration * 0.034 / 2;

  Serial.print("Distance: ");
  Serial.println(distance);
}

This didn't work, so I searched for useful libraries such as NewPing.h:
#include <NewPing.h>

const int trigPin = 9 ;
const int echoPin = 10;
const int maxDistance = 2000;
unsigned int distance;

NewPing sonar(trigPin, echoPin, maxDistance);

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

void loop() {
  delay(1000) ;
  distance = sonar.ping_cm();

  Serial.print("Distance : ");
  Serial.print(distance);
  Serial.println(" cm !");
}

This also didn't work. This is a simple screenshot of what happens in the monitor: (the 0 cm ! value is caused because I turned off the ultrasonic, and the object was actually 4 cm away):

Any help would be appreciated.

What are you using as object to test the sensors? Ultrasonic distance measurement is not really accurate and has the big flaw, that it depends on the reflectivity of the object. Soft objects can dampen the reflection significantly until you don't get a reliable measurement anymore and hard flat objects, that are placed at an angle, can reflect the sound wave in a direction away from the sensor, again impacting the measurement.

my hand is the object I was using, also a black plastic, and sometimes, thr wall

Please try with something like a book at right angle to the sensors line of view. That should at least rule out that problem for now. I'm not sure, if that solves your problem. I don't see any error in your code. Please also check your wiring (you didn't show it here). It should be rather simple, if you have one of the standard ultrasonic sensor modules for Arduino. Vcc and ground connections between sensor and Arduino and then trigger and echo to the corresponding digital pins (9 and 10).

I tried, everything is well connected, nothing is happening, same error

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


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

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

ControlInterrupt controlInterrupt(commonPin, buttonPins);

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

void loop() {
  // Empty
}

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

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

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

unsigned long lastFire;

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

  lastFire = 0;
}

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

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

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

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

  configureCommon(); // Return to original  state
}

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

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

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

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

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

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

It's all based on the code that I found here: https://projecthub.arduino.cc/Svizel_pritula/10-buttons-using-1-interrupt-6e0896

Please edit your post to show the error message you get.

The solution is covered in my post here.
However, to avoid link-only answers, the brief explanation is that a class method has a hidden argument "this" which refers to the instance of the class. In your case the instance is controlInterrupt.
If you are only ever going to have one instance of the class you could replace it with a namespace. Otherwise, make yourself a glue routine as described in that post.

Member functions (here pressInterrupt) in C++ are special. You cannot use them where normal function is required (atrachInterruot). For explanations refer to any good book on C++.
In this particular case, just strip all C++ specific constructs off. They make your code neither shorter nor understandable. All the data for ISR is global anyway.

Hi Matt, could you please explain what you mean by "just strip all C++ specific constructs off"?

@TheCodeGeek Don't use class at all. As an it pro you should be able to figure out how to rewrite the code in plain C, aren't you?


text:
Arduino + NRF24L01 mesh network implementation
TL;DR

I want to produce a large self configuring wireless mesh network using Arduinos and NRF24L01s transceivers. These have been chosen because they are capable of achieving the requirement and the application is cost sensitive.

I cannot find any "plug it in and run" system , although the RF24Mesh, RF24, RF24Network libraries produced by  TMRh20 / Avamander have the capabilities to produce such a system.

Information on suitable software is sought.

I'm using Arduino / C++ on a project that requires mesh radio connection of, ideally hundreds of sensors*. If required the network can be partitioned to overcome addressing or traffic capacity issues, but this will probably not be necessary.
I am using NRF24L01 transceivers due to acceptable performance, energy use, range** and cost issues. There will also be one or more cellphone-network gateways per mesh network. I am also investigating the use of LORA in some situations.
I can currently achieve NRF24L01 + Arduino capability over a few nodes.
I could extend this to a semi-indefinite number of nodes but not in a sensible manner
Requirement:

I wish to implement a "many node" wireless mesh network* with automatic inclusion of new nodes, and the ability mainly to transfer data from nodes to a master, but with some master to node traffic. Average data rate per node can be as low as is required to ensure operation with whatever number of nodes happen to be connected. (Nodes do not 'move about' physically, but new nodes may be added and others may occasionally be disabled.)

I am capable, needs must, of wading through what is available and 'learning the hard way' but hope that a reasonably complete solution is available. I have a large amount of experience in software development on microprocessors / microcontrollers over about 45 years, but much of this is 'bare metal' in assembler.
I was hoping that something close to 'it just works' may be available.
I have spent "many hours" internet searching to find what is available, and have been surprised at the extremely small number of sites reporting availability of anything close to this specification. The libraries implemented by TMRh20 / Avamander (RF24Mesh, RF24, RF24Network) as commented on in his blog here seem fully capable of this task but still seem to require me to know rather more than I do - so far :-)
I have not found any posts in this group that address the requirement.
Any sites that I have found propose multiple node networks are useful for small tasks but limited in extensibility and lack mesh addressing automation features.

Example only. This site offers Arduino Wireless Network with Multiple NRF24L01 Modules but requires a very 'manual' network construction process.

This probably comes closest of what I found  - Cheap Arduino mesh using RF24 radio modules. It utilises RF24, RF24Network and RF24Mesh libraries but is also relatively incomplete.

Any advice on how to achieve my aim would be welcome.

*The application is the management of solar lighting, and usage and availability / status reporting for women's toilets in refugee camps. The ability to remotely reconfigure energy use profiles in low sun periods may be useful. Additional features such as alarm provision may be considered.
Information can include lighting operation statistics and current state, energy use and battery state, toilet usage and availability, ... .
**I am aware that some "clone" versions have range issues.

Note:  The NRF24L01 was chosen due to a combination of cost and low current when continuously receiving. The latter removes the need for time-crucial synchronisation and greatly simplifies multi hop mesh implementation. Energy budget is extremely limited and a far easier eg ESP8266 based solution is, at least so far, untenable.


text:
Can't upload Blinky on CH32V003F4P6 using Arduino IDE
Warn : Transport "sdi" was already selected
Ready for Remote Connections
Error: open failed

*** [upload] Error 1

I have bought a development board (CH32V003F4P6) and I cannot upload a blinky example to the thing. The Chinese company I got it from doesn't seem to have English support, so I'm struggling to figure out what is wrong. I've got Platform IO, Ardunio, MounRiver, and gcc all installed for CH32V003 and they all have the same problem, they cannot upload to the thing.
I read this hackaday article and it seems there is both SPI and Serial transfers.
I checked my USB (I'm using Linux) and it shows up.
Bus 003 Device 048: ID 1a86:7523 QinHeng Electronics CH340 serial converter
I have absolutely no idea where to go from here in order to upload to this thing.

Do you have rights for accessing serial ports (added correct group, udev rules ...)?

https://hackaday.com/2023/03/02/a-ch32v003-toolchain-if-you-can-get-one-to-try-it-on/

google official WCH Link-E Programmer

Yes I have rights to the serial ports. It doesn't work even if I use sudo. The people who sold it to me said I didn't need the WCH Link-E Programmer because this was built in.

if the programmer is built-in then use the correct software for that programmer

I have. It is Eclipse, but the error message is the same regardless of the software.


text:
Is std::string (and ephemeral memory allocation in general) safe to use for ESP32 Arduino?
I know that with 8-bit AVR Arduinos, std::string is widely regarded as fundamentally unsafe due to problems with heap-fragmentation (partly due to limited ram, and partly because the WAY the underlying Arduino/AVR library manages memory is more simplistic than something constantly allocating and releasing a lot of ephemeral allocations needs to be).
Does the same caution apply to ESP32 (when used with Arduino framework), or does its underlying memory-management scheme (presumably based upon esp-idf) on a module with ~468k of RAM do a sufficiently better job of minimizing fragmentation and memory leaks to make std::string (and other similar classes) safe to use without having to constantly obsess about the problem and bend over backwards to avoid ephemeral memory allocations?
I'm particularly worried that the various libraries for making http requests (potentially involving the creation and destruction of multiple String objects for every request and response) might be more indifferent to the problem than they really ought to be.
I know there are a few Arduino libraries floating around that basically allocate one single big chunk of memory for a reusable String-like object (and additional space to give it "working room" to do things like regular expressions, substring replacements, etc), but it looks like the popular ESP32 libraries for making http requests just create and destroy new String objects for every request and act like it's totally fine. Am I being paranoid, or is ephemeral memory allocation still a real problem, even on platforms like ESP32?
By the same token... is there some way (on an ESP32 Arduino program) to periodically ask the library to analyze the current memory situation, and deliberately initiate a software-reboot (and re-initialization) if the memory appears to be getting overly fragmented (vs waiting for a slow memory leak to just fester until a memory allocation fails and the whole program crashes uncontrollably)?

The problem with randomly allocating and deallocating memory in embedded systems (ES) is that most are meant to be able to run error free, essentially forever. Since memory space is finite, an ES that employs the technique don't meet that constraint.
To the question in your last paragraph, apparently you are considering a system that can tolerate occasional resets with the consequent loss of context. In that case, perhaps resetting anytime an allocation fails would be acceptable. You'd want to vet each request especially carefully to to ensure it could usually be expected to succeed (define "usually"!), to avoid the large overhead of too-frequent resets or worse, a continuous reset loop resulting from an impossible-to-fill request.
Consider the difference between a human-driven question-and-answer system on one hand, and an implanted cardiac pacemaker or an jet fighter auto-landing system for night carrier-landings. An occasional timing hiccup might be annoy me in the Q&A system (if I can even notice it), but in either of the other two, it might spoil someone's whole day. :-(

The system could definitely tolerate periodic reboots without incident, as long as they happen when I decide they're necessary (say, upon checking as the final act in loop(), and triggering a reboot if the largest chunk of free memory is smaller than 100k, or less than 200k remains available). The general assumption is that it wouldn't happen very often... but from what I've seen, Arduino-based stuff in general that uses wifi for almost anything tends to reliably crap out after a few weeks unless it gets periodically rebooted... so I'm just starting from that assumption and embracing it :)

For what it's worth, I have a fan controller on an ATmega328p -> ESP-01 (AT firmware) that runs continuously until a power glitch or some other event takes down my WiFi network. If it were more critical to keep the controller running, it could have been made to "don't care" if the WiFi is down and just keep going without it. The WiFi is only to allow me to view what the controller is doing and isn't a necessary function.

If this is any interest to you, I did a web server library that doesn't use String class at all.
It decodes incoming HTTP requests an item at a time, passing the results to a callback routine.
That way it doesn't even have to hold the whole HTTP request in memory, it decodes the individual parameters "on the fly" using a state machine.
Since there is no dynamic memory allocation you should, in theory, be able to run indefinitely.
My library is at https://github.com/nickgammon/HTTPserver
Having said that, and to answer your actual question ...

Dynamic memory allocation doesn't have to be bad. In particular, if the amounts of memory being allocated are always the same (for example, your input to your server is predictable) then it shouldn't run out of memory. The allocator will just keep serving up the same bit of memory you used last time.
What can be particularly bad is this sort of stuff:
String buf = "";
while (Serial.available ())
  {
  buf += Serial.read ();  // append one byte
  }

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

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

Thanks, but my program is purely a http client. The main thing I'm wringing my hands over is that the circuit's primary mission is, "monitor moisture sensors, and trigger the piezo buzzer if water is detected". I have it sending BLE advertisements for my debugging convenience, and making occasional http POST requests to log the data & make it available to me from outside the house... but my big concern is making sure that failure with Bluetooth or WiFi won't hang or crash the whole program and derail it from its primary mission (of watching the sensors & sounding the piezo if necessary).

(continued)... I think I might need to eventually implement something involving watchdog timers... but I'm not sure whether ESP32 even has AVR-like watchdog timers capable of automatically rebooting the chip if the software fails to "check in" within some specified time limit. In any case, that would be a topic for another question ;-)

According to a Google search, it has in fact got watchdog timers.


text:
Arduino used as a multi-sensor controller, then sending results to a laptop to post on a web page
We are a non-profit group, we run a salmon rearing pond on Lake Michigan. We are upgrading our equipment that is over 50 years old with monitoring sensors.
We are adding 9 temperature, 5 oxygen, 2 air temperature, and several voltage and current sensors, hooked up to an Arduino. We need to have the results sent to a laptop to post them on a web page, and record values in a log.
We also want to do some simple calculations so that when a value drops below a preset level an output (valve) is activated until the level is corrected. We also want a built-in timer to control different equipment.
The high-voltage part (240 V, 480 V) is my area of expertise. Any help or ideas?

yes .. start with one sensor

Instead of developing your own solution from scratch (which would be a learning experience that might or might not be enjoyable), you might also want to look at a project like Home Assistant.

Tackle this project as any other, divide and conquer.
First learn how to provide a static web site with the Arduino. There are multiple options, so please do your research and follow the inevitable tutorials. Experiment with a few to choose the best alternative for your case.
Then add dynamic values, for example with a simple time-controlled algorithm that alters a value. You can mock up monitored values, it does not matter. Make sure the dynamic web site works as you wish. You have also multiple options here.
Then add a first sensor to measure. First use the raw value to make sure you have the correct results. Only if you succeed, continue to scale the raw value to the physical value you want to use.
Finally extend with the next sensors, one after the other, in the same manner.
Doing "baby steps" helps you identify gaps in your knowledge (fill them) and avoids complex debugging. Never try to develop all at once. To accelerate the development, you can distribute the separate independent steps to multiple developers. This is how professionals do it, and there is no reason why it will not work for you.


text:
I can't get my interrupt to control my project
I'm building a programmable resin pump for my resin printer. The goal is to enable the ability to set a print volume and print time provided by the printer. The pump would then pump at full speed to cover the build plate, then continue at a lower speed to continue filling until the print is complete. Other features would be added as needed.
The problem that I'm having is that I can control everything by typing the commands on the keyboard, but the interrupt is not working as expected. Only the Left and Right commands work, but Up and Enter do nothing, and Down freezes the Arduino. The other buttons aren't connected yet.
When commands are issued via keyboard or button successfully, the command is printed to the serial terminal, e.g. #CMD# UP.
When using the buttons, Up and Enter (center) do nothing. Left and Right function properly, but Down begins to return the command and freezes the Arduino at #C so that even the keyboard commands do nothing.
I've tried changing the byte values of Up, Down, Left, Right, and Enter to a number of different values, but no change was noted.
What is going wrong?
Sketch
/*
 Sub Menu

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

*/

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

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

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

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

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

unsigned long lastFire = 0;

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

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

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

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

LcdMenu menu(LCD_ROWS, LCD_COLS);

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

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

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

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

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

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

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

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

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

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

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

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

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

Schematic

Welcome to SE/Arduino! Please take the [tour] to learn how this site works, and you might want to read "[ask]" (again). You forgot to ask an answerable specific question... Please [edit] your post to add that, the schematic of your design, and what you already tried to debug. You might want to reduce your code to a minimal, complete and reproducible source, too. My next step would be to use some output pin as debug aid and toggle it in the ISR, to verify the assumptions.

What does processMenuCommand do? I suspect it does too much to be handled in an interrupt.

Human input is slow.  Trying to use an interrupt to catch human input is adding unnecessary complication to your project.  It would be better to ditch the interrupts and just read the buttons normally.

@Nick Gammon I was unaware of limitations in this sense. I did some brief research in responding to your comment and found the following which may be helpful to me. https://www.digikey.com/en/maker/tutorials/2022/the-dos-and-donts-of-using-arduino-interrupts

The BUTTONPINS contains bytes but in the setup loop you are dividing it by sizeof(int). So half of the buttons are left floating and catching radio... Btw you can use for (byte pin : BUTTONPINS) {

I resolved my problem by following the guidance that I linked to from digikey. My solution:
/*
 Sub Menu

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

*/

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

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

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

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

unsigned long lastFire = 0;

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

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

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

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

LcdMenu menu(LCD_ROWS, LCD_COLS);

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

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

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

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

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

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

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

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

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

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

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

interruptTrigger should be declared volatile. Otherwise the compiler may optimize away any changes to it.

Thank you! I missed this!

You are probably doing too much in your interrupt routine (ISR). See my answer here about using interrupts.
I also have material on my web site which may be helpful.
A comment you linked to https://www.digikey.com/en/maker/tutorials/2022/the-dos-and-donts-of-using-arduino-interrupts which sensibly suggests merely setting a flag in the interrupt routine, and then doing something in your main loop if the flag is set.
Alternatively, as another comment suggested, just detect button presses in the main loop. You will iterate through it fast enough that this should be sufficient.


text:
ArduinoJson- unexplained '.size()' behaviour
I'm using an ESP8266, ArduinoJson v6.21.5. PlatformIO, and Vscode.
The config file is saved and read correctly to the ESP8266's flash:
{
    "gen_pubTopic": [
        "DvirHome/Messages",
        "DvirHome/log",
        "DvirHome/debug"
    ],
    "subTopic": [
        "DvirHome/Light/int/KitchenLEDs",
        "DvirHome/All",
        "DvirHome/Light/int",
        "DvirHome/Light"
    ],
    "pubTopic": [
        "DvirHome/Light/int/KitchenLEDs/Avail",
        "DvirHome/Light/int/KitchenLEDs/State"
    ]
}

In case the config file is missing, t[], t2[], and t3[] are hard-coded to avoid boot failure.
There are 3 for loops to read topics (from the config file or from hard-coded).
The unexplained part is:

The config file is saved and read as expected, and the code runs without any failure.

when the for loop contains DOC["gen_pubTopic"].size() topics are read as expected, but when it is defined as DOC["gen_pubTopic"].size() |3 it crashes.

x, x2, and x3 are defined before each for loop. Again, once as defined in 2), still no change.

At some part it happened at the 3rd for loop, and sometimes at the 2nd for loop.

Printing xi values shows the expected values.

To make it clear - without using "fallback annotation" |2 the code runs without crashing.

In all stages above - the fallback stage is not tested (meaning- config file is saved and read), and crashes happens when "fallback annotation" is added.

Appreciate any lead why.
void start_iot2(JsonDocument &DOC)
{

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

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

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

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

  iot.start_services(extMQTT);
}

Edit_1
Added /*debug Code */ to for loops.
At some point, the Serial Monitor showed:
Sub_0
Sub_1
Sub_2
Sub_3
Sub_4
Sub_5

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

I don't know what you expected | 3 to do.

|3 is set as a value in case config file does not exist

I'm afraid that you don't know what | value actually means. Please [edit] your question and describe what you think it does in detail.

|3 is set as a value in case config file does not exist - that isn't what it is doing.

The "fallback notation" you're referring to is JsonVariant::operator| and only works for JsonVariant.
In your program, you apply | to an unsigned long, so the built-in bitwise "or" operator applies. It performs a bit-by-bit "or" operation (for example 2 | 4 is 6).
In other words, doc["key"] | 4 behaves as you describe, but not doc.size() | 4.
Unlike many other languages, you cannot use logical "or" (||) either because it returns a boolean.
So, if you want to make minimal changes to your code, you'll have to do something like this:
uint8_t x = DOC["gen_pubTopic"].size();
if (!x) x = 3;

I prefer explicit conditions instead of hidden C features: if (x == 0) // ... ;-) It does not irritate the casual reader of the source and shows the intention much better.

@Benoit - thank you for a concise answer

I wish to make sure that using iot.add_pubTopic(DOC["pubTopic"][i] | t3[i]); is correct (using t3[i] as "fallback")

Nope, I think not. It looks as if the argument to add_pubTopic is still a unsigned long, for which | means 'bitwise or'. This probably should not even compile, because t3[i] is a string.


text:
RTC DS1307 module not working
I have a problem with an RTC: it doesn't work correctly. When I try to read date or time, I only see the characters "àààààààà" or or the program is blocked.
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <RTClib.h>

DS1307 rtc;
DateTime rtcTime;

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

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

  delay(500);
}

This function blocks my program:
String timeRTC(){
  DateTime now = rtc.now();
  String Hour = "0" + String(now.hour());
  String Minutes = "0" + String(now.minute());
  String Seconds = "0" + String(now.second());

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

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

I can't find what is causing this problem. I use an Arduino Uno in the WOKWI emulator.
String dateRTC(){
  DateTime now = rtc.now();
  String Day = "0" + String(now.day());
  String Month = "0" + String(now.month());
  String Year = String(now.year());

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

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

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

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

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

Could you provide a link to your WOKWI project?

I use free version of WOKWI. This is link for my project: https://file.io/y1XjwyqHjhNk

The link says “The transfer you requested has been deleted.”

I shared the file again:  https://easyupload.io/b0e4w8

What do you mean by "blocked my program"?

Where is the Serial.begin() specifying the used baud rate? And it seems, that the function  timeRTC() is not used in your program, so how does it block the program?

Unresponsive/frozen program.

I modified it many times trying to solve the problem, hence the shortcomings in the code. I have included two RTC functions in the main post

You don't have a problem with the RTC: if you call Serial.begin() in
setup(), and remove everything in your loop() after
Serial.println(mm);, the program works as expected. Your problem is
elsewhere. Most likely you are corrupting memory, which leads to
unpredictable behavior.
Side note: if you switch to Adafruit's RTClib (called simply “RTClib” in
the library manager), you can simplify timeRTC() to this:
String timeRTC() {
    return rtc.now().timestamp(DateTime::TIMESTAMP_TIME);
}


text:
HID keyboard: send modifier keys only
I want to send modifier keys like shift, alt, ctrl, etc. without any other key.
It should be theoretically possible due to the fact that it works for my normal keyboard (tested it with Xoutput and games).
For now I have to send dummy keystrokes like scroll lock (0xCF) to send the modifiers.
I'm using Arduino core for the ESP32 with the ESP32S3 Dev Module.
#include "USBHIDKeyboard.h"
USBHIDKeyboard Keyboard;
Keyboard.press(KEY_LEFT_CTRL); Keyboard.write(0xCF); Keyboard.write(0xCF);
delay(1000);
Keyboard.release(KEY_LEFT_CTRL); Keyboard.write(0xCF); Keyboard.write(0xCF);

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

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

USBHIDKeyboard.cpp


text:
Is it safe to leave an ATmega328PB's RX and TX pins unconnected?
I am using an ATmega328PB and was looking at Arduino's and others' schematics that use it. All schematics I've found use a serial chip to communicate between USB and TX & RX. However, I do not need USB, and I plan to program the ATmega through ICSP.
Is it safe to leave these pins unconnected or connect them directly to LEDs like some Arduino boards do (like in the photo)? I wasn't able to find clear information about it.

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

With a 120 ohm resistor those leds will be quite bright.

VCC will be connected to 3.3V, and the LED is rated for 2.7V with a forward current of 5mA. Should my resistors still have a greater resistance?

Look at these pins as any other I/O pin. There is nothing special about them, if you do not enable their special function, in your case USART0.
This is what the data sheet says:

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

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

Thank you so much! I was worried as other posts I have seen showed issues with floating TX and RX pins that interfere with their script, but as you stated, I can enable pull-ups to avoid this. Have a great day!

@Acmek Better use the correct sources of information, for example data sheets and application notes. Forums posts are competent only in rare cases.


text:
PulseIN not recognised
I have included the library for the HCSR04 echo sounder, but pulseIN is not recognised; it never goes orange either.
I have tried multiple libraries, and closing down the IDE and opening it back up.

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

What a simple mistake to make thankyou for giving me the answer.


text:
Problem saving "char" variable to EEPROM on ESP32
I am making a project, in which I need to store the WiFi credentials to the EEPROM. For now, I am able to store a bool array in the EEPROM, but I am not able to store the char variables as the ESP32 crashes. I can't share the complete code since it is REALLY long, but here are the problematic functions:
To save to the EEPROM:
//////////////////////////////////WORKING////////////////////////////////
void saveToEEPROM() {
  // Save the contents of the array in EEPROM memory
  for (int i = 0; i < NUM_PARAM_ITEMS; i++) {
    EEPROM.write(i, menu_items_param_bool[i]);
  }
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////NOT WORKING////////////////////////////////
  // Save WiFi credentials in EEPROM if not empty
  int offset = NUM_PARAM_ITEMS;

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

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

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

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

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

  offset += password1Length + 1;

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

  offset += ssid2Length + 1;

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

And the variables to save and read are:
char ssid1[32] = "ssid1"; // SSID of the first WiFi network
char password1[64] = "password1"; // Password of the first WiFi network
char ssid2[32] = "ssid2"; // SSID of the second WiFi network
char password2[64] = "password2"; // Password of the second WiFi network

Can anybody help me?

what is the specific problem? ... but not the Char variables does not desctibe what actually  happens ... please update your question above ... do not add a comment below

In your read routine you are trying to read the length of the string from EEPROM, but you never saved the length there, just the null terminated strings themselves. For that to work you would need to write strlen() to EEPROM directly before the string

There are multiple issues with your code.

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

Some things are complicated and can be simplified.

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

This is a possible corrected solution. It uses the length and the end-of-string marker for each stored string.
  // Write anystring
  int anystringLength = strlen(anystring);
  EEPROM.write(offset, anystringLength);
  offset++;
  for (int i = 0; i <= anystringLength; i++) {
    EEPROM.write(offset, anystring[i]);
    offset++;
  }

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

Note: To make things explicit, using the offset and incrementing it are separated. You can combine them, though. But terse code tend to be misinterpreted.
This is another possible solution. Instead of expecting the length written before a string, read it until you find the end-of-string marker.
The writing loop writes all characters including the final end-of-string marker. This works for empty strings, too.
  int i;
  // Write anystring
  i = 0;
  do {
    char c = anystring[i];
    EEPROM.write(offset, c);
    offset++;
    i++;
  } while (c != '\0');

The reading loop does the same in the opposite direction. Using the do-while loop ensures that the end-of-string marker is read in any case.
  int i;
  // Read anystring
  i = 0;
  do {
    char c = EEPROM.read(offset);
    anystring[i] = c;
    offset++;
    i++;
  } while (c != '\0');
  // start of debug output
  Serial.print("any String: ");
  Serial.println(anystring);
  // end of debug output

Like the original the solutions lack error checks, for example of too long strings, which are left as an exercise to the reader.
Another bonus point will be received, if you extract these loops into functions. It simplifies the code and removes duplicates. [Edit] I found that the EEPROM class already provides writeString() and readString(). You should read its documentation and consider the usage.


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

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

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

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

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

L1A_INTERIEUR:
        in R15,SREG
        wdr
        sleep

which AVR instruction manual are you referring to? ... which section?

So the assembler doesn't recognize "BODS" symbol, as those "bit names", unlike registers or instructions, are the part of user manual existing for human convenience only. What's so surprising? Use numbers or define symbolic constants yourself...

run cat iotn25.h | grep BODS in where the toolchain is stalled give me this #define BODS    7, and #define BODSE   2.

@hcheung What does some C include file do with assembler?

@Matt, it define the bit position of BODS an BODSE bit in the MCU Control Register of the ATtiny25 that is used in the assembly.

@hcheung And so? Assemblers can not read C include files. While humans can read user manuals as well.

@Matt Of course this is possible. The GCC frontend pipes the assembler source through the preprocessor before giving it to the actual assembler, if the file extension is upper-case "S". Been there, did it.

@matt, we are all here to help OP who don't know the answer. I provided the value of what those values should be and where to find it. It is way better than "What's so surprising, define yourself"...  Sometime one have to admitted you don't know what you don't know... +1 for me as per the busybee, been there, done that.

The Microchip Studio uses the GNU toolchain or the XC8 toolchain. Unfortunately you did not tell us, which you use for your project.
The common solution is to include the corresponding header file to let the preprocessor replace the symbols with the defined values:
#include <iotn25.h>

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

; ...

Or to add "iotn25.h" to the dependencies in the "Solution Explorer".
Now you need to make sure that the preprocessor runs before the assembler. Both toolchains behave identically, if you name the assembler source with an uppercase extension of ".S".
The GNU toolchain does this according to its documentation:

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

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

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


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

Yes, that is possible.
analogWrite() sends out a 5 V PWM signal (3.3 V on some Arduinos) from a pin. You can't use this PWM signal to drive a motor directly; an Arduino IO pin can't deliver enough current for that.
You will have to get a separate 9 V power supply that can supply enough current for the motor, and use a BJT or MOSFET to switch that 9 V with the PWM signal from the Arduino, or use a dedicated motor driver IC or module.
There are plenty of schematics for this on the interwebs.


text:
Compressing grayscale image with ESP32-S3-WROOM Freenove
I have a ESP32-S3-WROOM module by Freenove which has a camera and a micro SD card module. I can take a snapshot as a PIXELFORMAT_JPEG photo.
However, when I change the pixelformat to "PIXELFORMAT_GRAYSCALE", the module captures the raw data (1 byte for each pixel) which is 480 kB constant for SVGA (600x800).
How can I compress this raw data to jpg? I also tried the way of saving the photo to a ".jpg" file, however, it did not work.
This is my capturing code for it:
/**********************************************************************
  Filename    : Camera and SDcard
  Description : Use the onboard buttons to take photo and save them to an SD card.
  Auther      : www.freenove.com
  Modification: 2022/11/02
**********************************************************************/
#include "esp_camera.h"
#define CAMERA_MODEL_ESP32S3_EYE
#include "camera_pins.h"
#include "ws2812.h"
#include "sd_read_write.h"

#define BUTTON_PIN  0

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

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

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

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

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

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

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

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

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

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

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

This function also prints out the hexadecimal values of the image for debugging.
So, do you guys have any ideas for compressing this image into a jpg file?

I just managed to solve this problem with using frame2jpg() function.
Hope it helps!

Perhaps you would like to expand this answer to include a little more detail?

Of course! There is a code that to_jpg() which located in esp32 github repo. You can find the "frame2jpg()" function in it. Also the usage will be like "frame2jpg(fb, quality, &jpg_buf, &jpg_buf_len)" it will return a boolean value which indicating the success situation. If you dont understand, I'm here to help!


text:
How to change the direction of a stepper motor using the AccelStepper library?
I'm using a Nema 17 stepper motor (200 steps) and a DRV8825 motor driver. This is the configuration I am using:

I am using a 9 V power supply. The dirPin is connected to pin 3 on the Arduino board, and my stepPin to pin 2.
What I'm trying to do is move the motor 100 steps clockwise and then move it 100 steps anti-clockwise, so that it returns to the starting position. This is the code I am using:
#include <AccelStepper.h>

#define dirPin 3
#define stepPin 2
#define motorInterfaceType 1

int SPR = 200;

AccelStepper stepper(motorInterfaceType, stepPin, dirPin);

void setup() {
  pinMode(dirPin,OUTPUT);
  pinMode(stepPin,OUTPUT);
  // put your setup code here, to run once:
  stepper.setMaxSpeed(200);
  stepper.setAcceleration(30);
}

void loop() {
  // put your main code here, to run repeatedly:
  stepper.moveTo(100);

  stepper.runToPosition();
  delay(1000);

  stepper.moveTo(-100);

  stepper.runToPosition();
  delay(1000);
}

I have two issues:

Before moving the motor 100 steps, it moves a couple of steps (like 15°), and then it starts moving, first 100 steps, and then 200 steps. That would be the first iteration, and I don't understand why it moves those 200 steps. And then, in the following iterations, the motor just moves 200 steps.

It only moves in one direction. I can't get it to move anti-clockwise.

the wiring diagram shows connections different from the code

The moveTo() function takes absolute positions, so if you want to move in both directions, you should adjust the target positions accordingly.
You can modify your loop() function as follows:
void loop() {
  stepper.moveTo(100);
  stepper.runToPosition();
  delay(1000);

  stepper.moveTo(0); // Move back to the starting position
  stepper.runToPosition();
  delay(1000);
}


text:
Code for ST7789 NO CS pin - Using XIAO ESP32C3
I am trying to get anything out of this screen except for blackness. The CS pin is present based on the coupling or resistors and capacitors on the back of the display.
I have tried using the Arduino ST7789 fast library, but I am wondering if anyone has gotten that to work in recent times. I first get an error saying no such library for avr/pgmspace. I found a fix to that issue by adding:
 #if (defined(__AVR__))
 #include <avr\pgmspace.h>
 #else
 #include <pgmspace.h>
 #endif

but then I move right to another error saying:
In member function 'void Arduino_ST7789::commonST7789Init(const uint8_t*)':
Arduino15/packages/esp32/hardware/esp32/2.0.11/cores/esp32/Arduino.h:114:70: error: cannot convert 'volatile uint32_t*' {aka 'volatile long unsigned int*'} to 'volatile uint8_t*' {aka 'volatile unsigned char*'} in assignment
 #define portOutputRegister(port)    ((volatile uint32_t*)GPIO_OUT_REG)

I have found nobody else with this issue.
Here is my pinout:

Esp32c3
St7789

GPIO 2
DC

GPIO 3
RES

GPIO 6
SDA

GPIO 7
SCL

3v3
Vcc/Lite

Here is the screen in question and
here is the microcontroller in question
I have also used the normal ST7789 library and substituted -1 for the CS pin, but still black on my display. If anyone can provide any raw code to get this to work or just let me know where to begin on trying new code please let me know.

The library is optimised for AVR chip using some AVR register and assembly code, it is therefore not compatible with ESP32 in the default mode. Add #define COMPATIBILITY_MODE to the first line of your code (before any #include <file> macro to see if it is working. If not, use other library such Adafruit ST7789 library.


text:
Increase in power consumption after brown-out detection (BOD)
I'm working on a low-power energy harvesting project, where I'm considering using an ATtiny85 to perform some minor tasks. The ATtiny is put in Power Down mode most of the time and does not consume much power in this mode.
Since the power source is unstable, and/or could be absent from time to time, I've been enabling the internal brown-out detection (BOD) by setting the fuses. In this way, the ATtiny is reset when the voltage gets too low. This is to ensure that the microcontroller starts working properly again when sufficient power returns. This also seems to work, so that the chip starts running again when the voltage is above a certain threshold.
However, when the voltage drops below the BOD level, I notice a considerably larger power surge through the ATtiny, even when all peripherals are disconnected. It seems to me that some sort of gate is opened inside the chip when the BOD fires, and the current is limited by some sort of internal resistance. When the voltage increases somewhat above the BOD level, the large power surge stops.
I've been searching through the documentation/datasheet and forums online to find out why this happens but can't seem to find some sort of explanation or ways to prevent this from happening. Since this is a low-power energy harvesting project, it is important that the internal protection from low voltages does not contribute to the problem it detects.
Any useful input would be appreciated.

When the Vcc level is below the BOD activation voltage, the chip is held in a reset state...

...which uses significantly more current than the chip does in sleep state...

You are probably better off disabling the BOD and instead using an external Voltage Supervisor chip to turn the power on to the ATTINY when there is sufficient voltage and turn it off when there is not. There are many of these chips and they can use less than a microwatt when quiescent.

Thank you for replying. The graph seems to offer some explanation to the matter. However, I'm observing ten times the current consumption, running at 1 MHz and BOD level at 4.3 V. According to the graph that should give a current consumption starting at ~0.1 mA? I'm observing >1 mA.
As you write, I might be better off disabeling the BOD and using an external supervisor, but I'm stil wondering if there is anything I could do to reach the 0.1 mA level.

Are you testing the ATTINY in isolation or in your circuit? If you are testing in circuit, I'd bet the additional drain is coming from other components and possibly the way they are reacting to the floating outputs on the ATTINY. Better to test the ATTINY alone, connected only to a variable power supply. Start at a voltage above the BOD threshold and slowly lower the voltage to below the threshold and watch what happens to the current. My guess is that it will match the chart above.

I've been testing in isolation, as you described. I found some useful information in section 10.2.6 "Unconnected Pins" in the datasheet: "... the [internal] pull-up will be disabled during reset. If low power consumption during reset is important, it is recommended to use an external pull-up or pull-down."
I've tested only a little with 1 MΩ external pull-up resistors, which seemed to bring the current consumption further down. Still not quite matching the chart yet. I haven't progressed further with testing, but it seems this might be at least part of the issue."

The chart in section 22.11 also states that it's "Excluding Current Through The Reset Pull-up". So there's also that. :)

Did some more testing now. External pull-up resistors on all floating pins. Current consumption down to ~0.22 mA right after BOD. Still a lot compared to ~26 µA in Power Down mode, but I guess that's more what is to be expected with BOD level at 2.7 V and taking into account an internal Reset Pull-up Resistor rated at 30-60 kΩ (according to "21.2 DC Characteristics" in the datasheet). Worst case that should give a current leak of ~90 µA + ~60 µA ≈ 0.15 mA. I guess this is as far as I get without an external Voltage Supervisor. :)


text:
How to connect an Arduino Uno with an ESP-01 to the Arduino IoT cloud?
I have been trying to connect to the Arduino IoT cloud using my Arduino Uno and an ESP-01.
The Arduino IoT cloud does not recognize my device as genuine. Hence, I've been trying to create a thing and connecting it as a third-party device. I have an ESP-01 WiFi module that I want to use to connect to the IoT cloud.
I tried the following connection:

Arduino ----> ESP-01
3.3V  ------> 3v3
3.3V  ------> EN(CH_PD)
TX(1) ------> TX
RX(0) ------> RX
GND   ------> GND

I also tried to reverse the connections of the RX and TX pins. Still no success. I want to connect to the cloud and use some sensors to update the cloud variables. How do I do it? Any kind of help is highly appreciated.
Edit: I tried to upload the code that thd cloud editor provided and it throws and error saying:
fatal error: list: No such file or directory
 #include <list>

Arduino IoT Cloud doesn't support Arduino Uno, but it does support esp8266.
You have to do the communication with the IoT Cloud from a ESP-01 sketch with esp8266 Arduino Core. Then you can pass the values from/to a sketch in Uno over Serial.
There are ESP8266 development boards which can replace the Uno in many projects. Search for "Wemos D1 mini" or "NodeMCU" ESP8266 modules.
The ESP32 is even more capable MCU with WiFi and Arduino Core is available. It is too supported in Arduino Cloud and Arduino even has a new Nano ESP32 board.

I was able to connect to Blynk using the same devices. It turns out that it is low-code platform and since I have a programming background, I was looking for a way to connect to the IoT cloud with the same configurations so that I could have more customization and flexibility. Thank you for your kind response. I will though give it a try with NodeMCU sometime soon.

@SauravGhimire Blynk has binary protocol and allows unsecure access

Can you suggest me with a better alternative? I am actually thinking of using the ESP 32 in my final project but till then, I will be using this setup with blynk(while learning and as a hobby). Thankyou!


text:
LED button alarm with DS3231 RTC problems
I am a PHP web programmer, but I am a beginner studying Arduino. I would like to inquire about a problem with the following function that I am creating while looking for various references. (This article was written through Google Translator.)
I would appreciate it if you could see what the problem is and give me some advice.
I also posted this question on the arduino forum.
▶ Program function: To save battery, in sleep mode, when the button is low, it wakes up with a button interrupt and pressing and holding the button sets the alarm. After setting the alarm, it goes back into sleep mode and when the time comes, the LED blinks through an interrupt via RTC. When the button is set low, the alarm is disabled and you must press and hold it to reset the alarm.
▶ Problem: The first run goes well, but if you turn off the alarm and set the alarm again, the next alarm does not run. (When the alarm time comes, you have to turn it off and then set the alarm again after a certain time.)
▶ Hardware used: Arduino Nano (CH340), DS3231 RTC, Ywrobot LED button switch module
▶ Program source:
#include "RTClib.h"  // DS3231 library
#include <avr/sleep.h>

const int alarmPin = 2;  // The number of the pin for monitoring alarm status on DS3231
const int btnPin = 3; // button interrupt
const int ledPin = 4;

int countBlink;
unsigned long buttonPressTime;
bool buttonState;
bool ledState;
long current_millis;
unsigned long previousMillis = 0;
bool alarmActive = false;
const int check_interval = 1000;

RTC_DS3231 rtc;
DateTime alarmTime;

void setup() {
  Serial.begin(9600);               // Start serial port for monitoring
  pinMode(alarmPin, INPUT_PULLUP);  // Set alarm pin as pullup
  pinMode(btnPin, INPUT_PULLUP);
  pinMode(ledPin, OUTPUT);

  if (!rtc.begin()) {
    Serial.println("Couldn't find RTC");
    Serial.flush();
    abort();
  }
  if (rtc.lostPower()) {
    // this will adjust to the date and time at compilation
    rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
  }
  rtc.disableAlarm(1);
  rtc.clearAlarm(1);

  rtc.writeSqwPinMode(DS3231_OFF);  // Place SQW pin into alarm interrupt mode
  DateTime now = rtc.now();
  char buff1[] = "Now: YYYY MM DD hh:mm:ss";
  Serial.println(now.toString(buff1));
  delay(1000);
}

void loop() {
  current_millis = millis();
  buttonState = digitalRead(btnPin);
  if (buttonState == LOW) {
    alarmActive = false;
    buttonPressTime = millis();
    while (digitalRead(btnPin) == LOW)
      ;
    if (millis() - buttonPressTime > 1500) {
      Serial.println("Long press detected");
      settingAlarm();
    }
  }

  if (alarmActive) {
    blink_led();
  }
  if (!alarmActive) {
    digitalWrite(ledPin, LOW);
    Serial.println("...Zzz....");
    Serial.println("====================");
    enterSleep();
  }

  //delay(check_interval);
}

void blink_led() {
  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= 1000) {
    previousMillis = currentMillis;
    if (ledState == LOW) {
      ledState = HIGH;
    } else {
      ledState = LOW;
    }
    digitalWrite(ledPin, ledState);
  }
}

void sleeping_led() {
  for (countBlink = 0; countBlink < 5; countBlink++) {
    digitalWrite(ledPin, HIGH);
    delay(200);
    digitalWrite(ledPin, LOW);
    delay(200);
  }
}

void settingAlarm() {
  DateTime now = rtc.now();  // Get current time
  alarmTime = now + TimeSpan(0, 0, 0, 20);
  rtc.setAlarm1(alarmTime, DS3231_A1_Second);  // In 20 seconds
  char buff4[] = " YYYY  MM DD hh:mm:ss";
  Serial.println(alarmTime.toString(buff4));
  Serial.println("===================");
  sleeping_led();
}

void enterSleep() {
  set_sleep_mode(SLEEP_MODE_PWR_DOWN);  // Setting the sleep mode, in this case full sleep
  noInterrupts();                       // Disable interrupts
  sleep_enable();                       // Enabling sleep mode
  attachInterrupt(digitalPinToInterrupt(alarmPin), wakeUp, FALLING);
  attachInterrupt(digitalPinToInterrupt(btnPin), checkUp, FALLING);
  Serial.flush();  // Ensure all characters are sent to the serial monitor
  interrupts();    // Allow interrupts again
  sleep_cpu();     // Enter sleep mode
  rtc.disableAlarm(1);
  rtc.clearAlarm(1);
}

void wakeUp() {
  sleep_disable();                                   // Disable sleep mode
  detachInterrupt(digitalPinToInterrupt(alarmPin));  // Detach the interrupt to stop it firing
  detachInterrupt(digitalPinToInterrupt(btnPin));    // Detach the interrupt to stop it firing
  alarmActive = true;
}

void checkUp() {
  sleep_disable();                                   // Disable sleep mode
  detachInterrupt(digitalPinToInterrupt(alarmPin));  // Detach the interrupt to stop it firing
  detachInterrupt(digitalPinToInterrupt(btnPin));    // Detach the interrupt to stop it firing
}

we'll wait to see what results you get at the arduino forum

You can use the "uRTCLib.h" library instead. Here is a detailed tutorial:
https://lastminuteengineers.com/ds3231-rtc-arduino-tutorial/
Here is also an Arduino-based project using DS3231: https://www.pcbway.com/project/shareproject/Arduino_based_weather_station_with_the_atmega328p.html I hope these tutorials will help you.