Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(NetworkEvent lib) refactor network events handling #11115

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

vortigont
Copy link
Contributor

@vortigont vortigont commented Mar 13, 2025

the main idea here is to reuse ESP IDF's event bus to propagate notifications for Arduino network events too.
Currently NetworkEvents lib uses it's own instances of queue and Task to deliver event among componects, but ESP IDF already has a very similar implementation and what is currently happening is we resend messages from one queue to another.
Reusing IDF's event loop for Arduino networking events could give some benefits:
- a unified event exchange bus between arduino and IDF components
- save resources for extra Task stack and Queue to handle Arduino's own event bus

I've come further and refactored other code related to NetworkEvents to close some gaps and unify messaging.
Since code changes are quite large, would appreciate any feedbacks if this worth the efforts to continue.

@me-no-dev @P-R-O-C-H-Y

Relates to #10805

Copy link
Contributor

github-actions bot commented Mar 13, 2025

Warnings
⚠️

Some issues found for the commit messages in this PR:

  • the commit message "Network events handling improvements":
    • summary looks empty
    • type/action looks empty
  • the commit message "WiFiGeneric make _eventCallback accept events by id and optional event_info_t ptr":
    • summary looks empty
    • type/action looks empty
  • the commit message "WiFiGeneric refactor events handling":
    • summary looks empty
    • type/action looks empty
  • the commit message "feat(NetworkEvents lib) reuse ESP IDF's event bus notifications":
    • summary looks empty
    • type/action looks empty
  • the commit message "lib WiFi AP refactor event handling":
    • body's lines must not be longer than 100 characters
    • summary looks empty
    • type/action looks empty
  • the commit message "lib(NetworkEvents) refactor callback handlers":
    • body's lines must not be longer than 100 characters
    • summary looks empty
    • type/action looks empty
  • the commit message "refactor STAClass events handling":
    • body's lines must not be longer than 100 characters
    • summary looks empty
    • type/action looks empty

Please fix these commit messages - here are some basic tips:

  • follow Conventional Commits style
  • correct format of commit message should be: <type/action>(<scope/component>): <summary>, for example fix(esp32): Fixed startup timeout issue
  • allowed types are: change,ci,docs,feat,fix,refactor,remove,revert,test
  • sufficiently descriptive message summary should be between 10 to 72 characters and start with upper case letter
  • avoid Jira references in commit messages (unavailable/irrelevant for our customers)

TIP: Install pre-commit hooks and run this check when committing (uses the Conventional Precommit Linter).

Messages
📖 This PR seems to be quite large (total lines of code: 1113), you might consider splitting it into smaller PRs

👋 Hello vortigont, we appreciate your contribution to this project!


📘 Please review the project's Contributions Guide for key guidelines on code, documentation, testing, and more.

🖊️ Please also make sure you have read and signed the Contributor License Agreement for this project.

Click to see more instructions ...


This automated output is generated by the PR linter DangerJS, which checks if your Pull Request meets the project's requirements and helps you fix potential issues.

DangerJS is triggered with each push event to a Pull Request and modify the contents of this comment.

Please consider the following:
- Danger mainly focuses on the PR structure and formatting and can't understand the meaning behind your code or changes.
- Danger is not a substitute for human code reviews; it's still important to request a code review from your colleagues.
- Resolve all warnings (⚠️ ) before requesting a review from human reviewers - they will appreciate it.
- Addressing info messages (📖) is strongly recommended; they're less critical but valuable.
- To manually retry these Danger checks, please navigate to the Actions tab and re-run last Danger workflow.

Review and merge process you can expect ...


We do welcome contributions in the form of bug reports, feature requests and pull requests.

1. An internal issue has been created for the PR, we assign it to the relevant engineer.
2. They review the PR and either approve it or ask you for changes or clarifications.
3. Once the GitHub PR is approved we do the final review, collect approvals from core owners and make sure all the automated tests are passing.
- At this point we may do some adjustments to the proposed change, or extend it by adding tests or documentation.
4. If the change is approved and passes the tests it is merged into the default branch.

Generated by 🚫 dangerJS against 7fb791b

Copy link
Contributor

github-actions bot commented Mar 13, 2025

Test Results

 76 files   76 suites   12m 42s ⏱️
 38 tests  38 ✅ 0 💤 0 ❌
241 runs  241 ✅ 0 💤 0 ❌

Results for commit 7fb791b.

♻️ This comment has been updated with latest results.

ESP IDF has very similar implementation of event bus ti propagate messages to subscribers,
it consists of same set of componets - a queue and a task to execute subscriber's callbacks.
Reusing it for Arduino networking events could give some benefits:

 - a unified event exchange bus between arduino and IDF components
 - save resources for extra Task stack and Queue to handle Arduino's own event bus
 - use handlers to avoid dublicate event subscriptions
 - replace ARDUINO_EVENT_MAX with ARDUINO_EVENT_ANY to match with ESP event loop
 - replace legacy functions with handler instance based
 - replace if's with simplified switch/case
 - use post without arduino_event_t where appropriate, avoid useless mallocs on event bus
add a new functional callback type NetworkEventReceiver which accepts two argumnets: an event id and a pointer to
optional struct with trailer data. Using this callback would allow to propagate event messages more efficiently.
Mandatory allocation for arduino_event_info_t and arduino_event_t objects won't needed and simple events could
be passed with only (int32_t)arduino_event_id_t wich fits nice to ESP event loop

add NetworkEvents::postEvent() overload with optional arduino_event_info_t* struct

use std::variant for various types of callback handlers, event picker depending on callback variant will
provide an optimized calls avoiding extra copies when not needed.
 - make event handlers and callbacks as class members, remove static pointer to class instance
 - replace legacy esp_event_handler_unregister() with instance/hamdler via esp_event_handler_instance_unregister()
 - replace a bunch of if's with switch/case for better readability
 - removed mandatory struct arduino_event_t when sending arduino events, use post(event_id,*data)
- make _onApArduinoEvent event handler as class members, remove static pointer to APClass class instance
- replace legacy esp_event_handler_unregister() with instance/hamdler via esp_event_handler_instance_unregister()
- replace a bunch of if's with switch/case for better readability
- removed mandatory struct arduino_event_t when sending arduino events, use post(event_id,*data)
Copy link
Contributor

Memory usage test (comparing PR against master branch)

The table below shows the summary of memory usage change (decrease - increase) in bytes and percentage for each target.

MemoryFLASH [bytes]FLASH [%]RAM [bytes]RAM [%]
TargetDECINCDECINCDECINCDECINC
ESP32P40⚠️ +19680.00⚠️ +0.300⚠️ +240.00⚠️ +0.09
ESP32S30⚠️ +14760.00⚠️ +0.170⚠️ +160.00⚠️ +0.04
ESP32S20⚠️ +14880.00⚠️ +0.180⚠️ +160.00⚠️ +0.04
ESP32C30⚠️ +18200.00⚠️ +0.190⚠️ +320.00⚠️ +0.09
ESP32C60⚠️ +18220.00⚠️ +0.190⚠️ +320.00⚠️ +0.08
ESP32H20⚠️ +7580.00⚠️ +0.14000.000.00
ESP320⚠️ +15320.00⚠️ +0.170⚠️ +160.00⚠️ +0.04
Click to expand the detailed deltas report [usage change in BYTES]
TargetESP32P4ESP32S3ESP32S2ESP32C3ESP32C6ESP32H2ESP32
ExampleFLASHRAMFLASHRAMFLASHRAMFLASHRAMFLASHRAMFLASHRAMFLASHRAM
Ethernet/examples/ETH_TLK110⚠️ +5340----------⚠️ +1120
Ethernet/examples/ETH_W5500_Arduino_SPI⚠️ +6880⚠️ +3280⚠️ +3440⚠️ +3180⚠️ +3160⚠️ +6820⚠️ +3520
Ethernet/examples/ETH_W5500_IDF_SPI⚠️ +6900⚠️ +3240⚠️ +3520⚠️ +3180⚠️ +3180⚠️ +6840⚠️ +3600
Ethernet/examples/ETH_WIFI_BRIDGE⚠️ +1918⚠️ +16⚠️ +1400⚠️ +16⚠️ +1488⚠️ +16⚠️ +17700⚠️ +17660--⚠️ +1452⚠️ +16
NetworkClientSecure/examples/WiFiClientInsecure⚠️ +18060⚠️ +1312⚠️ +16⚠️ +1360⚠️ +16⚠️ +1644⚠️ +32⚠️ +1648⚠️ +16--⚠️ +1368⚠️ +16
NetworkClientSecure/examples/WiFiClientPSK⚠️ +1806⚠️ +16⚠️ +1312⚠️ +16⚠️ +1360⚠️ +16⚠️ +1644⚠️ +16⚠️ +1648⚠️ +16--⚠️ +1368⚠️ +16
NetworkClientSecure/examples/WiFiClientSecure⚠️ +17980⚠️ +1316⚠️ +16⚠️ +1360⚠️ +16⚠️ +1644⚠️ +32⚠️ +1648⚠️ +16--⚠️ +1368⚠️ +16
NetworkClientSecure/examples/WiFiClientSecureProtocolUpgrade⚠️ +18020⚠️ +1336⚠️ +16⚠️ +1320⚠️ +16⚠️ +1648⚠️ +32⚠️ +1644⚠️ +16--⚠️ +1380⚠️ +16
NetworkClientSecure/examples/WiFiClientShowPeerCredentials⚠️ +1802⚠️ +24⚠️ +1320⚠️ +16⚠️ +1356⚠️ +16⚠️ +16480⚠️ +16500--⚠️ +1388⚠️ +16
NetworkClientSecure/examples/WiFiClientTrustOnFirstUse⚠️ +18080⚠️ +1328⚠️ +16⚠️ +1340⚠️ +16⚠️ +16520⚠️ +16500--⚠️ +1356⚠️ +16
PPP/examples/PPP_Basic⚠️ +7560⚠️ +3960⚠️ +4080⚠️ +3880⚠️ +3880⚠️ +7580⚠️ +4160
PPP/examples/PPP_WIFI_BRIDGE⚠️ +19680⚠️ +1476⚠️ +16⚠️ +1468⚠️ +16⚠️ +1820⚠️ +16⚠️ +1822⚠️ +32--⚠️ +1532⚠️ +16
WebServer/examples/AdvancedWebServer⚠️ +1816⚠️ +24⚠️ +1340⚠️ +16⚠️ +1320⚠️ +16⚠️ +16620⚠️ +16640--⚠️ +1352⚠️ +16
WebServer/examples/FSBrowser⚠️ +18200⚠️ +1328⚠️ +16⚠️ +1308⚠️ +16⚠️ +16560⚠️ +16560--⚠️ +1404⚠️ +16
WebServer/examples/Filters⚠️ +1812⚠️ +24⚠️ +1344⚠️ +16⚠️ +1304⚠️ +16⚠️ +16700⚠️ +16720--⚠️ +1384⚠️ +16
WebServer/examples/HelloServer⚠️ +1812⚠️ +24⚠️ +1332⚠️ +16⚠️ +1332⚠️ +16⚠️ +16620⚠️ +16640--⚠️ +1356⚠️ +16
WebServer/examples/HttpAdvancedAuth⚠️ +17960⚠️ +1308⚠️ +16⚠️ +1364⚠️ +16⚠️ +1658⚠️ +32⚠️ +1648⚠️ +16--⚠️ +1404⚠️ +16
WebServer/examples/HttpAuthCallback⚠️ +18240⚠️ +1312⚠️ +16⚠️ +1348⚠️ +16⚠️ +1664⚠️ +32⚠️ +1664⚠️ +16--⚠️ +1396⚠️ +16
WebServer/examples/HttpAuthCallbackInline⚠️ +18060⚠️ +1312⚠️ +16⚠️ +1392⚠️ +16⚠️ +1664⚠️ +32⚠️ +1660⚠️ +16--⚠️ +1396⚠️ +16
WebServer/examples/HttpBasicAuth⚠️ +18080⚠️ +1320⚠️ +16⚠️ +1356⚠️ +16⚠️ +1654⚠️ +32⚠️ +1646⚠️ +16--⚠️ +1404⚠️ +16
WebServer/examples/HttpBasicAuthSHA1⚠️ +17960⚠️ +1308⚠️ +16⚠️ +1376⚠️ +16⚠️ +1664⚠️ +32⚠️ +1660⚠️ +16--⚠️ +1360⚠️ +16
WebServer/examples/HttpBasicAuthSHA1orBearerToken⚠️ +18240⚠️ +1304⚠️ +16⚠️ +1352⚠️ +16⚠️ +1668⚠️ +16⚠️ +1664⚠️ +32--⚠️ +1368⚠️ +16
WebServer/examples/MultiHomedServers⚠️ +18200⚠️ +1328⚠️ +16⚠️ +1356⚠️ +16⚠️ +1664⚠️ +32⚠️ +1664⚠️ +16--⚠️ +1360⚠️ +16
WebServer/examples/PathArgServer⚠️ +1808⚠️ +16⚠️ +1316⚠️ +16⚠️ +1404⚠️ +16⚠️ +16560⚠️ +16600--⚠️ +1376⚠️ +16
WebServer/examples/SDWebServer⚠️ +18160⚠️ +1340⚠️ +16⚠️ +1364⚠️ +16⚠️ +1654⚠️ +24⚠️ +16540--⚠️ +1372⚠️ +16
WebServer/examples/SimpleAuthentification⚠️ +18260⚠️ +1328⚠️ +16⚠️ +1440⚠️ +16⚠️ +1660⚠️ +16⚠️ +1668⚠️ +16--⚠️ +1336⚠️ +16
WebServer/examples/UploadHugeFile⚠️ +1824⚠️ +16⚠️ +1324⚠️ +16⚠️ +1360⚠️ +16⚠️ +16640⚠️ +16640--⚠️ +1376⚠️ +16
WebServer/examples/WebServer⚠️ +18120⚠️ +1344⚠️ +16⚠️ +1340⚠️ +16⚠️ +1660⚠️ +24⚠️ +16600--⚠️ +1420⚠️ +16
WebServer/examples/WebUpdate⚠️ +18200⚠️ +1340⚠️ +16⚠️ +1300⚠️ +16⚠️ +1666⚠️ +16⚠️ +1668⚠️ +16--⚠️ +1380⚠️ +16
WiFi/examples/FTM/FTM_Initiator⚠️ +1652⚠️ +16⚠️ +1068⚠️ +16⚠️ +1140⚠️ +16⚠️ +14900⚠️ +14920--⚠️ +1164⚠️ +16
WiFi/examples/FTM/FTM_Responder⚠️ +1806⚠️ +24⚠️ +1360⚠️ +16⚠️ +1332⚠️ +16⚠️ +1650⚠️ +16⚠️ +1654⚠️ +16--⚠️ +1364⚠️ +16
WiFi/examples/SimpleWiFiServer⚠️ +18080⚠️ +1316⚠️ +16⚠️ +1344⚠️ +16⚠️ +16540⚠️ +16580--⚠️ +1400⚠️ +16
WiFi/examples/WiFiAccessPoint⚠️ +18000⚠️ +1324⚠️ +16⚠️ +1360⚠️ +16⚠️ +16540⚠️ +16580--⚠️ +1404⚠️ +16
WiFi/examples/WiFiClient⚠️ +1812⚠️ +16⚠️ +1300⚠️ +16⚠️ +1356⚠️ +16⚠️ +16540⚠️ +16560--⚠️ +1388⚠️ +16
WiFi/examples/WiFiClientBasic⚠️ +18280⚠️ +1332⚠️ +16⚠️ +1316⚠️ +16⚠️ +1670⚠️ +24⚠️ +16720--⚠️ +1384⚠️ +16
WiFi/examples/WiFiClientConnect⚠️ +1800⚠️ +24⚠️ +1328⚠️ +16⚠️ +1396⚠️ +16⚠️ +1646⚠️ +16⚠️ +1652⚠️ +16--⚠️ +1388⚠️ +16
WiFi/examples/WiFiClientEvents⚠️ +1770⚠️ +24⚠️ +1192⚠️ +16⚠️ +1240⚠️ +16⚠️ +1620⚠️ +16⚠️ +1620⚠️ +16--⚠️ +1268⚠️ +16
WiFi/examples/WiFiClientStaticIP⚠️ +18140⚠️ +1296⚠️ +16⚠️ +1340⚠️ +16⚠️ +1662⚠️ +16⚠️ +1666⚠️ +16--⚠️ +1384⚠️ +16
WiFi/examples/WiFiExtender⚠️ +1938⚠️ +24⚠️ +1428⚠️ +16⚠️ +1476⚠️ +16⚠️ +1774⚠️ +16⚠️ +1778⚠️ +16--⚠️ +1496⚠️ +16
WiFi/examples/WiFiIPv6⚠️ +1634⚠️ +16⚠️ +1112⚠️ +16⚠️ +1144⚠️ +16⚠️ +14740⚠️ +14740--⚠️ +1128⚠️ +16
WiFi/examples/WiFiMulti⚠️ +17920⚠️ +1384⚠️ +16⚠️ +1428⚠️ +16⚠️ +1652⚠️ +24⚠️ +16480--⚠️ +1400⚠️ +16
WiFi/examples/WiFiMultiAdvanced⚠️ +18200⚠️ +1320⚠️ +16⚠️ +1376⚠️ +16⚠️ +1662⚠️ +32⚠️ +1664⚠️ +16--⚠️ +1368⚠️ +16
WiFi/examples/WiFiScan⚠️ +1794⚠️ +16⚠️ +1352⚠️ +16⚠️ +1400⚠️ +16⚠️ +16400⚠️ +16420--⚠️ +1380⚠️ +16
WiFi/examples/WiFiScanAsync⚠️ +1798⚠️ +16⚠️ +1352⚠️ +16⚠️ +1408⚠️ +16⚠️ +16320⚠️ +16320--⚠️ +1376⚠️ +16
WiFi/examples/WiFiScanDualAntenna⚠️ +1794⚠️ +16⚠️ +1332⚠️ +16⚠️ +1352⚠️ +16⚠️ +16460⚠️ +16460--⚠️ +1380⚠️ +16
WiFi/examples/WiFiScanTime⚠️ +1804⚠️ +16⚠️ +1352⚠️ +16⚠️ +1396⚠️ +16⚠️ +16380⚠️ +16380--⚠️ +1376⚠️ +16
WiFi/examples/WiFiTelnetToSerial⚠️ +18240⚠️ +1308⚠️ +16⚠️ +1352⚠️ +16⚠️ +1658⚠️ +32⚠️ +1662⚠️ +16--⚠️ +1404⚠️ +16
WiFi/examples/WiFiUDPClient⚠️ +16300⚠️ +1108⚠️ +16⚠️ +1124⚠️ +16⚠️ +14820⚠️ +14800--⚠️ +1168⚠️ +16
NetworkClientSecure/examples/WiFiClientSecureEnterprise--⚠️ +1324⚠️ +16⚠️ +1380⚠️ +16⚠️ +1656⚠️ +32⚠️ +1654⚠️ +16--⚠️ +1388⚠️ +16
WebServer/examples/Middleware--⚠️ +1332⚠️ +16⚠️ +1344⚠️ +16⚠️ +1662⚠️ +16⚠️ +1662⚠️ +32--⚠️ +1400⚠️ +16
WiFi/examples/WPS--⚠️ +1412⚠️ +16⚠️ +1440⚠️ +16⚠️ +1782⚠️ +16⚠️ +1778⚠️ +16--⚠️ +1476⚠️ +16
WiFi/examples/WiFiBlueToothSwitch--⚠️ +1120⚠️ +16--⚠️ +1468⚠️ +32⚠️ +1468⚠️ +16--⚠️ +1168⚠️ +16
WiFi/examples/WiFiClientEnterprise--⚠️ +1344⚠️ +16⚠️ +1376⚠️ +16⚠️ +1658⚠️ +16⚠️ +1658⚠️ +16--⚠️ +1372⚠️ +16
WiFi/examples/WiFiSmartConfig--⚠️ +1332⚠️ +16⚠️ +1308⚠️ +16⚠️ +16560⚠️ +16600--⚠️ +1368⚠️ +16
Ethernet/examples/ETH_LAN8720------------⚠️ +1120

@me-no-dev
Copy link
Member

@vortigont I understand your reasons for the changes, but I prefer to keep Arduino's events and handlers totally separate from IDF for a few reasons. Mainly because Arduino is used by all different kinds of people that do all different kinds of things in the callbacks. An Arduino callback should never interfere with the rest of the system at any point and even is user decides to block it, background tasks should always continue to run normally. That is why Arduino runs at very low priority as well. Using C++ standard mutex is also discouraged in FreeRTOS environment (we have discussed this in the Async Discord, maybe you have missed it).

Generally all code in Arduino is written with the presumption that the user will do something that is not supposed to and that should not break the rest of the chip, so we are OK with "wasting" a few KB of RAM to run own task to handle all events.

We are open for other improvements though :)

@me-no-dev
Copy link
Member

  • a unified event exchange bus between arduino and IDF components

Arduino events are in reality copies of already existing IDF events that span multiple components and that are just collected in on place, plus some internal handling to update states and such, because most do not listen to events and expect to get the states otherwise

@vortigont
Copy link
Contributor Author

yeah, users are tend to misbehave :) you point is reasonable. Although nothing prevents user to attach to the same default event bus in his code.
But if the question is to just have a separate event handling task IDF's API allows to spawn user queues with dedicated task but using same event bus API. Does that makes sense?

As for mutexes, I must have skipped it, sorry, will catch up.
But if I'm not mistaken in esp's toolchain cpp std mutexes are just wraps around same rtos semaphores under the hood, need to double check.

@Jason2866
Copy link
Collaborator

Jason2866 commented Mar 13, 2025

There is always a bell ringing when i see the chance to save some RAM :-)
Do you have an rough idea how much RAM would be "saved"?
There are cases where 1KB more makes the difference between working or not...
EDIT: I could imagine to do a branch in Tasmota framework fork with this change

@vortigont
Copy link
Contributor Author

@Jason2866 nah... I was not for the RAM saving here at all, it's just the thing everybody loves in MCU world, we do have that Memory usage test here for a reason I guess :) But straight answer is - it would save 8k for task's stack if reusing default event bus, which is not mandatory, we could have our own dedicated event task if that makes more sense.
My intention was to try to unify event messaging with it's origin - IDF's API, actually it is the source of network messages. Currently arduino's network event lib has 3 different types of callbacks - static by id, static by struct reference and functional by id + struct by value and none of those is matching the origin

using NetworkEventCb = void (*)(arduino_event_id_t event);
using NetworkEventFuncCb = std::function<void(arduino_event_id_t event, arduino_event_info_t info)>;
using NetworkEventSysCb = void (*)(arduino_event_t *event); 

and kind of not-so-nice scheme to register/unregister by function pointers while IDF is using instance handlers and keep by-ptr as legacy. OK, ok, NetworkEvent lib returns handlers too but under the hood most of the components are still not using it as I checked. This all definitely has some historic reasons and compatibility obligations. I was trying to move it all into some distinctive direction, if esp32 arduino's platform tends to reuse IDF's drivers and other components where possible. At least it makes a bit clear where to more forward if at all.

Well, anyway it was a nice exercise for me to understand the relation and exchange between Arduino's network events and IDF's origin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants