Help with esp32 sketch

Sral

Valuable Member
View Badges
Joined
May 2, 2022
Messages
1,006
Reaction score
976
Location
Germany
Rating - 0%
0   0   0
On any embedded device, repeated allocations and frees are obviously a bad idea. `operator+=` and `.append` on std::string are synonymous, so they only cause allocations at 50% marks.

If you control the std::string you're providing, its a good idea to `.reserve` a reasonable buffer size for the operation you'll be doing. Of course, you should also be checking it repeatedly for being over a threshold or its a classic DoS situation.
I guess that’s only strictly true for array-type data structures. If you were to repeatedly allocate 4 byte structures like a single integer that doesn‘t change in size I imagine this would be no problem at all in comparison.

thanks for the info, I’ll probably have to try and fork the original library and make the appropriate changes. Currently it looks like it’s using the Arduino String implementation ( String(); ), so I’ll likely have to modify that.
 

theatrus

Valuable Member
View Badges
Joined
Mar 26, 2016
Messages
1,961
Reaction score
3,358
Location
Sacramento, CA area
Rating - 0%
0   0   0
I guess that’s only strictly true for array-type data structures. If you were to repeatedly allocate 4 byte structures like a single integer that doesn‘t change in size I imagine this would be no problem at all in comparison.

thanks for the info, I’ll probably have to try and fork the original library and make the appropriate changes. Currently it looks like it’s using the Arduino String implementation ( String(); ), so I’ll likely have to modify that.


Correct. Fixed size allocations, and only that size allocation, are fine. Even if you made all allocations some multiple of each other (say, powers of two), you'll still run into fragmentation if there is any concurrency or threading. The little malloc impl in newlib is designed to be small, not good. Something like web requests that run-to-completion would be well suited to an arena allocation.
 

Sral

Valuable Member
View Badges
Joined
May 2, 2022
Messages
1,006
Reaction score
976
Location
Germany
Rating - 0%
0   0   0
Correct. Fixed size allocations, and only that size allocation, are fine. Even if you made all allocations some multiple of each other (say, powers of two), you'll still run into fragmentation if there is any concurrency or threading. The little malloc impl in newlib is designed to be small, not good. Something like web requests that run-to-completion would be well suited to an arena allocation.
Hm, true. Since I’m not that familiar with these kind of things yet, I’ll probably try to start with reserving a fixed buffer for the string.
 

theatrus

Valuable Member
View Badges
Joined
Mar 26, 2016
Messages
1,961
Reaction score
3,358
Location
Sacramento, CA area
Rating - 0%
0   0   0
Hm, true. Since I’m not that familiar with these kind of things yet, I’ll probably try to start with reserving a fixed buffer for the string.
Good first step.

Arena allocation = grab one block of memory at the start of some series of operations, use it without free, and then dispose. A simple example is just allocating one string buffer, on the stack, and using it for the entirety of the function. As it’s on the stack, it will also be freed and suffer zero fragmentation.

I don’t have much experience with what the Arduino ecosystem here is, but the HTTP frameworks in esp-idf are pretty reasonable.
 

Sral

Valuable Member
View Badges
Joined
May 2, 2022
Messages
1,006
Reaction score
976
Location
Germany
Rating - 0%
0   0   0
Good first step.

Arena allocation = grab one block of memory at the start of some series of operations, use it without free, and then dispose. A simple example is just allocating one string buffer, on the stack, and using it for the entirety of the function. As it’s on the stack, it will also be freed and suffer zero fragmentation.

I don’t have much experience with what the Arduino ecosystem here is, but the HTTP frameworks in esp-idf are pretty reasonable.
Yeah, arena allocation sounds very reasonable, although I’m not sure if I will find the time to implement that with my limited knowledge on somebody else’s library :grinning-face-with-sweat:

My next step would likely be to switch to another library if possible.
 

miran2782

Well-Known Member
View Badges
Joined
Feb 15, 2015
Messages
504
Reaction score
327
Rating - 0%
0   0   0
Finally !

Had a huge problem getting the ESP32 ready, because I could not debug properly. As it turns out, the standard pinDefinitions from @Ranjib use GPIO 1 as an INLET. Sadly, GPIO1 is also the UART Serial Pin, which means that as soon as you define GPIO1 as INLET, the Serial Connections I was using for debug no longer worked. Took me AGES to find that out :grinning-face-with-sweat:

I built a small demonstration circuit that uses all features. Since the ESP32 is rather large for a prototyping breadboard I had to hide a few components underneath it:
ReefPi_ESP32_Guide_Demo.jpg


All used Pins are labeled in Red on the translucent ESP32 board. I can try to get a better picture. Here is the circuit drawing:
ReefPi_ESP32_Guide_schem.png

3V3 and GND connected to the rails with red and black

On the right side:
  • One Wire Pin 4 is connected with purple wire
  • One Wire Pin 4 is pulled up to 3V3 with a 5k resistor
  • One Wire Signal, 3V3 and GND are connected to 2 JST connectors

  • OUTLET Pin 5 powers a LED through a 220 Ohm resistor
On the Bottom side of the breadboard and the left side of the drawing:
  • INLET Pin 36, fed with an analog 0 to 3.3V signal
    • (one could also connect an ATO signal here)

  • Analog Input Pin 32, fed with an analog 0 to 3.3V signal
    • (one could also connect a sensor signal here)

  • Jack Pin 12 and 27, feeding a 10µF capacitor over a 10k resistor each, generating an analog voltage from 0 to 3.3V depending on the PWM duty cycle from 0 to 100%
    • (one could also directly connect a lamp input to the Pin with a 200 Ohm resistor)

The GPIO Pins can be configured in the ESP32 script:
ReefPi_ESP32_Guide_PinDef.png


  • Modify the code as such:
    • First enter the number of respective Pins that you want. Make sure that this is the right number, as errors will result in crashes or non-functioning Pins !
    • Enter your WiFi info, e.g. WiFi name and password
    • Enter the GPIO-Numbers (example Pinout diagram) that you want to use for each purpose. Watch out for the following caveats:
      • if you want to use the Serial interface (UART) for debugging, don't use GPIO pins 1 and 3, as this will disable the Serial interface
      • GPIO 34-39 can only be used for INLET
        • they also have no internal pull-up or pull-down resistors
      • ADC2 Pins can NOT be used as Analog Inputs
        • only available when WiFi is off
  • Upload the code to your ESP32 (don't worry, compiling takes quite a while)
  • Start the Serial Monitor in the top right corner:
    • 1672675893169.png
  • In the bottom panel, klick "Serial Monitor" and set the Baudrate to 115200:
    • 1672675960581.png
    • restart the ESP32 by pressing the "EN" button on the front
  • if everything works, you should see something like this after a while:
    • 1672676004835.png
    • When using windows you can then open the command console with WIN+R and typing in "cmd" and pressing enter
    • type "ping" and afterwards the IP you just read and press enter:
    • 1672676102242.png
    • you should see some pings working with a time in the 100s of milliseconds
    • then type "arp -a" and press enter
    • You should see something like this:
    • 1672676187551.png
    • Note the "physical address" behind the previously encountered IP
    • You can set your router to give this physical address a constant IP if you like, in my case it looks something like this:
    • 1672676388996.png
    • I entered the physical address we noted before and the IP it should get
    • restart the ESP32 again by presing the "EN" button on the front
    • now I get this in the Serial Monitor:
    • 1672676583075.png
  • We now know the IP of the ESP32 and can continue to ReefPi
    • Go to Configuration->Drivers and add an ESP32 driver:
      • 1672676898170.png
      • Give it a nice name
      • enter the IP-address of the ESP32
      • enter the COUNT of OUTLET pins that you have entered in the code before
        • in my case I have the standard number of 6 outlets
        • proceed the same with INLET, PWM and Analog Input
    • go to Configuration->Admin and press reload
    • go to Configuration->Connectors and define your connections:
      • 1672677289451.png
      • Note that the "Pins" 0 to (Count-1) correspond to the order in the GPIO List we have defined in the ESP32 code:
      • 1672677454437.png
      • Since I connected my example to GPIO 36 I have to use Inlet "Pin" 2
      • Analog Inputs are special, since the Analog Input "Pin" 0 is always redirected to the DS18B20 OneWire protocol:
      • 1672677662940.png
      • That is why we had to increase the number of Analog Input in the driver by 1
      • The other Analog Inputs can then be accessed with "Pins" 1, 2, etc. once @Ranjib updated the code:
      • 1672677789975.png
      • Jacks work like OUTPUTS and INPUTS again, even though you have to manually type the pin number:
      • 1672677972263.png
    • Go to Configuration->Admin and Reload again
I am a little confused about how to assign pwm jacks/channels.
In your example you have
jackPins[JACK_COUNT] = { 12, 13, 14, 27 }
pwmChannels[JACK_COUNT] = { 0, 1, 2, 3 }
but in your example you have pins 12 and 27 wired for pwm
 

Sral

Valuable Member
View Badges
Joined
May 2, 2022
Messages
1,006
Reaction score
976
Location
Germany
Rating - 0%
0   0   0
I am a little confused about how to assign pwm jacks/channels.
In your example you have
jackPins[JACK_COUNT] = { 12, 13, 14, 27 }
pwmChannels[JACK_COUNT] = { 0, 1, 2, 3 }
but in your example you have pins 12 and 27 wired for pwm
Yeah, that’s a little confusing. As far as I understand:
- JackPins are the physical pins that output the PWM
- pwmChannels are the background units that generate the PWM, that’s then routed to the individul Pins.

So in short:
- set „jackPins“ to the pins you want to use.
- set „pwmChannels“ to an incrementing series from 0 to JACK_COUNT-1

If you like I believe you can also chain several pins to the same channel, so that they get the same PWM signal. In that case simply change the associated channel like so:

Code:
jackPins[JACK_COUNT] = { 12, 13, 14, 27 }
pwmChannels[JACK_COUNT] = { 0, 1, 2, 0 }
If I’m not mistaken this should result in pins 12 and 27 having the same PWM signal. If you change either of them, the other will follow.
 

Ranjib

7500 Club Member
View Badges
Joined
Apr 16, 2016
Messages
9,843
Reaction score
17,058
Location
Pleasant Hill, Concord
Rating - 0%
0   0   0
Yeah, that’s a little confusing. As far as I understand:
- JackPins are the physical pins that output the PWM
- pwmChannels are the background units that generate the PWM, that’s then routed to the individul Pins.

So in short:
- set „jackPins“ to the pins you want to use.
- set „pwmChannels“ to an incrementing series from 0 to JACK_COUNT-1

If you like I believe you can also chain several pins to the same channel, so that they get the same PWM signal. In that case simply change the associated channel like so:

Code:
jackPins[JACK_COUNT] = { 12, 13, 14, 27 }
pwmChannels[JACK_COUNT] = { 0, 1, 2, 0 }
If I’m not mistaken this should result in pins 12 and 27 having the same PWM signal. If you change either of them, the other will follow.
This is due to the way esp32 pwm /led library works. It decouples pwm pin with led channels, and allows declaring them separately and then attach a channel with pin . I have not tested it, but I assume this style allows multiple pins to be used as a single led channel or vice versa
 

miran2782

Well-Known Member
View Badges
Joined
Feb 15, 2015
Messages
504
Reaction score
327
Rating - 0%
0   0   0
Yeah, that’s a little confusing. As far as I understand:
- JackPins are the physical pins that output the PWM
- pwmChannels are the background units that generate the PWM, that’s then routed to the individul Pins.

So in short:
- set „jackPins“ to the pins you want to use.
- set „pwmChannels“ to an incrementing series from 0 to JACK_COUNT-1

If you like I believe you can also chain several pins to the same channel, so that they get the same PWM signal. In that case simply change the associated channel like so:

Code:
jackPins[JACK_COUNT] = { 12, 13, 14, 27 }
pwmChannels[JACK_COUNT] = { 0, 1, 2, 0 }
If I’m not mistaken this should result in pins 12 and 27 having the same PWM signal. If you change either of them, the other will follow.
Thanks that is what I was thinking. I setup the below for 6 channels. I haven't built the light yet so have not tried it out.

const int jackPins[JACK_COUNT] = { 13, 12, 14, 27, 26, 25 };
const int pwmChannels[JACK_COUNT] = { 0, 1, 2, 3, 4, 5 };

I have the code uploaded to the esp32 and started programming the reef-pi interface, but haven't started any wiring to the esp32 yet, just the bare devkit powered from the usb port.
 

Sral

Valuable Member
View Badges
Joined
May 2, 2022
Messages
1,006
Reaction score
976
Location
Germany
Rating - 0%
0   0   0
On any embedded device, repeated allocations and frees are obviously a bad idea. `operator+=` and `.append` on std::string are synonymous, so they only cause allocations at 50% marks.

If you control the std::string you're providing, its a good idea to `.reserve` a reasonable buffer size for the operation you'll be doing. Of course, you should also be checking it repeatedly for being over a threshold or its a classic DoS situation.
I rechecked the code in the "ESPAsyncTCPWebserver" and it does in fact reserve buffer for the String _temp every time there is new data to append to it. This might also lead to fragmentation, but I guess that's unavoidable in the Asynchronous Implementation.

I'll try a dry run of only the ESPAsyncTCPWebserver without the additional code to see if the problem persists by itself or is caused by the interaction with the rest.
 

Sral

Valuable Member
View Badges
Joined
May 2, 2022
Messages
1,006
Reaction score
976
Location
Germany
Rating - 0%
0   0   0
My first test looked a bit different though. I compared a standard ESP32 driver setup with one Inlet/ATO, 2 Jacks/Lights and 2 pH/AnalogInputs.

I tested it once with a 60s query interval and had 2 crashes after 30 min and 60 min runtime and no further crashes for the hour that followed.

Another setup I tested was to limit requests to a single pH one, but request every 1 second. That's much more often than my initial setup, but also saw less crashes (3 in 30 min), which is less than the maximum I saw, 12 in 60 minutes.
I switched this to 2 pH submodules that request every 2 seconds, but then I strangely got no crashes in 30 minutes.

I went back to test the original setup with one Inlet/ATO, 2 Jacks/Lights and 2 pH/AnalogInputs, but this time made the period 10s again, the setup where I had a lot of crashes in the past. Now however, it runs stable for the last 45 minutes.

Looks almost as if the crashed become more infrequent the longer the ESP32 runs ? I'm slightly confused :grinning-face-with-sweat:
 

Sral

Valuable Member
View Badges
Joined
May 2, 2022
Messages
1,006
Reaction score
976
Location
Germany
Rating - 0%
0   0   0
I think I found another angle on this.

Code:
Guru Meditation Error: Core  1 panic'ed (LoadProhibited). Exception was unhandled.

Core  1 register dump:
PC      : 0x400da7e3  PS      : 0x00060930  A0      : 0x800d37c9  A1      : 0x3ffd0f20 
A2      : 0x3ffd0f50  A3      : 0x3f401418  A4      : 0x89a05220  A5      : 0x00000000 
A6      : 0x0000003a  A7      : 0x706e695f  A8      : 0x800da7e3  A9      : 0x3ffd0f00 
A10     : 0x00000000  A11     : 0x3ffd0ed4  A12     : 0x3ffd0ee8  A13     : 0x0000002f 
A14     : 0x00ff0000  A15     : 0xff000000  SAR     : 0x00000008  EXCCAUSE: 0x0000001c 
EXCVADDR: 0x00000000  LBEG    : 0x40089f69  LEND    : 0x40089f79  LCOUNT  : 0xfffffffb 


Backtrace: 0x400da7e0:0x3ffd0f20 0x400d37c6:0x3ffd0f40 0x401536e3:0x3ffd0fc0 0x400d9c35:0x3ffd0fe0 0x400d880d:0x3ffd1030 0x400d891d:0x3ffd1080 0x400d8b05:0x3ffd10d0 0x400d5435:0x3ffd10f0 0x400d54b1:0x3ffd1120 0x400d5aba:0x3ffd1140

In the Arduino-ESP32 Documentation under Fatal Errors under "Load prohibited" it is mentioned that this happends when the ESP32 tries to:
read from an invalid memory location. The address which has been written/read is found in the EXCVADDR register in the register dump. If this address is zero, it usually means that the application has attempted to dereference a NULL pointer.

As you see above, the EXCVADDR is exactly zero. Looks like a Null-Pointer. When I analyse the backtrace (for my modified code that creates more errors as it seems) it says:
Code:
_ZNK16UrlTokenBindings3getEPKc
h:\Projekte\Arduino Projects\libraries\PathVariableHandlers\src/UrlTokenBindings.cpp:28
_Z15readAnalogInputP21AsyncWebServerRequest
H:\Projekte\Arduino Projects\ReefPi_ESP32/ReefPi_ESP32.ino:216
_ZNSt17_Function_handlerIFvP21AsyncWebServerRequestEPS2_E9_M_invokeERKSt9_Any_dataOS1_
c:\users\chris\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\esp-2021r2-patch5-8.4.0\xtensa-esp32-elf\include\c++\8.4.0\bits/std_function.h:297
_ZNKSt8functionIFvP21AsyncWebServerRequestEEclES1_
c:\users\chris\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\esp-2021r2-patch5-8.4.0\xtensa-esp32-elf\include\c++\8.4.0\bits/std_function.h:687
_ZN21AsyncWebServerRequest10_parseLineEv
h:\Projekte\Arduino Projects\libraries\ESPAsyncWebServer-master\src/WebRequest.cpp:581 (discriminator 1)
_ZN21AsyncWebServerRequest7_onDataEPvj
h:\Projekte\Arduino Projects\libraries\ESPAsyncWebServer-master\src/WebRequest.cpp:123
_ZNSt17_Function_handlerIFvPvP11AsyncClientS0_jEZN21AsyncWebServerRequestC4EP14AsyncWebServerS2_EUlS0_S2_S0_jE6_E9_M_invokeERKSt9_Any_dataOS0_OS2_SC_Oj
h:\Projekte\Arduino Projects\libraries\ESPAsyncWebServer-master\src/WebRequest.cpp:76
_ZNKSt8functionIFvPvP11AsyncClientS0_jEEclES0_S2_S0_j
c:\users\chris\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\esp-2021r2-patch5-8.4.0\xtensa-esp32-elf\include\c++\8.4.0\bits/std_function.h:687
_ZN11AsyncClient7_s_recvEPvP7tcp_pcbP4pbufa
h:\Projekte\Arduino Projects\libraries\AsyncTCP\src/AsyncTCP.cpp:1210
_ZL19_async_service_taskPv
h:\Projekte\Arduino Projects\libraries\AsyncTCP\src/AsyncTCP.cpp:162
The first Element is the current heap element during the error, as far as I think to understand. This is the URLTokenBindings.cpp on line 28. Incidentally, the URLTokenBindings Library extensively uses pointers on the handling of c_strings.
 

Sral

Valuable Member
View Badges
Joined
May 2, 2022
Messages
1,006
Reaction score
976
Location
Germany
Rating - 0%
0   0   0
Just for completeness: I also looked at the Strings that the Function gets just before the crashes and they look fine, no corruption or similar, just plain correct URLs:
Code:
/analog_inputs/:id
/analog_inputs/0
They are also below the 30byte size of the char buffer.
 

Sral

Valuable Member
View Badges
Joined
May 2, 2022
Messages
1,006
Reaction score
976
Location
Germany
Rating - 0%
0   0   0
Found our culprit: the TokenItterator spuriously returns NULL pointers. This is partially intended, but also partially, not. Both cause isses, the first when we use the returned pointer to get the binding data from the URL and the second, when the URLTokenBindings class doesn't expect a NULL pointer internally.

Here is an example:
1678440790814.png

You can see several http requests, the first and the last are identical (/analog_inputs/3) but once we get the propper id and the measurement value and the second time we get a NULL pointer.

Simply checking for this NULL pointer, as I did above, removes one source of the crashes.
Sadly, that doesn't solve the errors within the PathVariableHandler-library.
 
Last edited:

Sral

Valuable Member
View Badges
Joined
May 2, 2022
Messages
1,006
Reaction score
976
Location
Germany
Rating - 0%
0   0   0
I believe I have found some solutions. Some crashes are still happening, but they are much less for me.

@Ranjib would be great if you could test this with your Prometheus/Grafana setup. You can find the ESP32 code as a fork on my Github (still the AnalogInputUpdate branch) together with the adapted PathVariableHandlers.
 
Last edited:

amdreef74

New Member
View Badges
Joined
Oct 12, 2022
Messages
2
Reaction score
6
Location
Mexico
Rating - 0%
0   0   0
Hello everyone, first thanks to @Ranjib and all collaborators for the excellent work with reef pi.

I'm completely new in the forum, so I don't know if I'm posting this topic correctly.

I have a reef aquarium with Reef Pi 5.3 it works amazing, and the new possibilities with ESP32 blew my mind.

So I'm testing with Reef Pi 6.0 and ESP32 on a standalone system before migrating, I got everything working and I've been doing tests, the biggest problem I've seen is that when the ESP32 unit reboots for example due to a power loss, all the outputs are set to high, and never synchronize with Reep Pi, I don't know if this issue can be corrected in this first version. Or if I miss somethink in the ESP32 code upload, or reef pi configuration.

Thanks for your support.
 

Ranjib

7500 Club Member
View Badges
Joined
Apr 16, 2016
Messages
9,843
Reaction score
17,058
Location
Pleasant Hill, Concord
Rating - 0%
0   0   0
Hello everyone, first thanks to @Ranjib and all collaborators for the excellent work with reef pi.

I'm completely new in the forum, so I don't know if I'm posting this topic correctly.

I have a reef aquarium with Reef Pi 5.3 it works amazing, and the new possibilities with ESP32 blew my mind.

So I'm testing with Reef Pi 6.0 and ESP32 on a standalone system before migrating, I got everything working and I've been doing tests, the biggest problem I've seen is that when the ESP32 unit reboots for example due to a power loss, all the outputs are set to high, and never synchronize with Reep Pi, I don't know if this issue can be corrected in this first version. Or if I miss somethink in the ESP32 code upload, or reef pi configuration.

Thanks for your support.
I was not aware of it. Let me do some research on this and get back to you
 

Dave's Reef

Active Member
View Badges
Joined
Nov 4, 2018
Messages
102
Reaction score
150
Rating - 0%
0   0   0
Sral. Nice writeup I'm am by no means even good at this kind of stuff but you made it east. I do have a question if anyone can help me. I hooked some LEDs to the outputs and they seem to be working perfectly however I can't seem to get inlets to work at all? Any help would be greatly appreciated.

Thank you
 

Sral

Valuable Member
View Badges
Joined
May 2, 2022
Messages
1,006
Reaction score
976
Location
Germany
Rating - 0%
0   0   0
Sral. Nice writeup I'm am by no means even good at this kind of stuff but you made it east. I do have a question if anyone can help me. I hooked some LEDs to the outputs and they seem to be working perfectly however I can't seem to get inlets to work at all? Any help would be greatly appreciated.

Thank you
Glad to hear that my posts are helping :)

Can you share a picture of your circuit and screenshots of the settings in the Arduino code, the driver in ReefPi and the Connections in ReefPi ?
 

Reefing threads: Do you wear gear from reef brands?

  • I wear reef gear everywhere.

    Votes: 46 16.5%
  • I wear reef gear primarily at fish events and my LFS.

    Votes: 18 6.5%
  • I wear reef gear primarily for water changes and tank maintenance.

    Votes: 1 0.4%
  • I wear reef gear primarily to relax where I live.

    Votes: 35 12.6%
  • I don’t wear gear from reef brands.

    Votes: 159 57.2%
  • Other.

    Votes: 19 6.8%
Back
Top