Skip to content

Add Arduino-ESP32 3.3.11 compatibility - #6

Open
LuoIsHere wants to merge 15 commits into
m5stack:mainfrom
LuoIsHere:main
Open

LuoIsHere wants to merge 15 commits into
m5stack:mainfrom
LuoIsHere:main

Conversation

@LuoIsHere

@LuoIsHere LuoIsHere commented Sep 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

This PR adds support for Arduino-ESP32 3.3.11 while preserving the existing Arduino-ESP32 2.x build environment and runtime behavior.

The update addresses API and driver changes introduced in Arduino-ESP32 3.x, including I2C, hardware timers, LEDC/PWM, USB CDC, ESP-NOW callbacks, and Wi-Fi station MAC retrieval. It also adds CI coverage for both framework generations.

Motivation

When running the original firmware on Arduino-ESP32 3.x, several compatibility problems were observed:

  • Mixing the new and legacy ESP-IDF I2C drivers caused a runtime abort.
  • VL53LX I2C transfer errors were not propagated to the upper layers, which could leave ToF initialization waiting indefinitely.
  • WiFi.macAddress() could return an all-zero address during startup, causing the controller to save an invalid ESP-NOW peer address.
  • Timer, LEDC, USB CDC, and ESP-NOW callback APIs have changed between Arduino-ESP32 2.x and 3.x.

Changes

Arduino-ESP32 compatibility

  • Added a dedicated PlatformIO environment for Arduino-ESP32 3.3.11.
  • Kept the original Arduino-ESP32 2.x environment available for backward compatibility.
  • Added shared compatibility macros based on the official Arduino-ESP32 version definitions.
  • Added a startup log showing the active Arduino-ESP32 version and compatibility branch.
  • Adapted hardware timer APIs for Arduino-ESP32 3.x.
  • Adapted motor and buzzer LEDC/PWM APIs while preserving the original channel, frequency, resolution, and motor mapping.
  • Adapted ESP-NOW send and receive callback signatures.
  • Preserved the existing 2.x implementation through conditional compilation.

ToF and I2C

  • Added an Arduino-ESP32 3.x I2C compatibility layer.
  • Avoided mixing the new ESP-IDF I2C driver with the legacy command-link driver.
  • Added argument, transfer-length, and result validation.
  • Increased the I2C transaction timeout to accommodate the actual sensor initialization flow.
  • Propagated I2C transfer failures through the VL53LX platform layer instead of reporting false success.
  • Added an I2C scan after changing the front ToF address to verify the transition from 0x29 to 0x2A.
  • Preserved the existing VL53LX address semantics and the Arduino-ESP32 2.x implementation.

ESP-NOW

  • Added esp_mac.h support for Arduino-ESP32 3.x.
  • Read the station MAC using esp_read_mac(..., ESP_MAC_WIFI_STA).
  • Checked both the API result and whether the returned MAC address is all zero.
  • Prevented pairing broadcasts when the local MAC address is invalid, avoiding invalid peer addresses being stored by the controller.
  • Kept the original WiFi.macAddress() path for Arduino-ESP32 2.x.

CI and repository cleanup

  • Added a PlatformIO build matrix covering:
    • The existing Arduino-ESP32 2.x environment.
    • The new Arduino-ESP32 3.3.11 environment.
  • Updated the Arduino Lint workflow to support repositories using either main or master.
  • Updated GitHub Actions dependencies and added manual workflow triggers.
  • Fixed the duplicated exclude key in the clang-format workflow.
  • Restored source and header file permissions from 100755 to 100644.
  • Removed unintended CRLF and trailing-whitespace changes from the affected source files.

Backward Compatibility

The existing Arduino-ESP32 2.x environment and implementation paths remain available. Version-specific behavior is isolated through conditional compilation.

This PR does not intentionally change:

  • Flight-control algorithms or tuning parameters.
  • ESP-NOW packet formats.
  • Motor channel or GPIO mappings.
  • ToF sensor address semantics.
  • Existing Arduino-ESP32 2.x initialization behavior.

Validation

  • Hardware tested on M5StampFly.
  • Hardware tested on M5StampFly V1.1.
  • Verified complete sensor initialization.
  • Verified front ToF address reassignment and ToF ranging.
  • Verified ESP-NOW pairing and remote-control operation.
  • Verified motor/PWM operation and takeoff behavior.
  • git diff --check passes.
  • GitHub Actions workflow YAML files parse successfully.
  • CI is configured to build both Arduino-ESP32 environments.

Notes

The configured I2C timeout is a maximum wait time, not an unconditional delay. Successful transfers return as soon as the underlying transaction completes.

@imliubo

imliubo commented Oct 27, 2026

Copy link
Copy Markdown
Member

Hi @kouhei1970 , could you help to review this MR :)

@kouhei1970 kouhei1970 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for this careful work — supporting Arduino-ESP32 3.x while keeping the 2.x path intact is very welcome, and isolating the I2C access through esp32-hal-i2c (instead of mixing the new and legacy IDF drivers) looks like the right approach. I reviewed the code and built both environments locally (PlatformIO 6.1.19): esp32-s3-devkitc-1 (Arduino-ESP32 2.0.17) and esp32-s3-devkitc-1-arduino3311 both build successfully. I have not flashed them on hardware myself yet. A few points before merging:

Should fix

  1. ESP-NOW send callback guard (src/rc.cpp:57, src/rc.cpp:166)
    The guard is ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0), but esp_now_send_info_t was introduced in ESP-IDF 5.5. In IDF 5.1–5.4 (Arduino-ESP32 3.0–3.2) esp_now_send_cb_t still takes const uint8_t *mac_addr, so the build fails there. I confirmed this with Arduino-ESP32 3.1.3 (pioarduino 53.03.13, IDF 5.3):

    src/rc.cpp:58:28: error: 'esp_now_send_info_t' does not name a type; did you mean 'esp_now_recv_info_t'?
    

    Suggest ESP_IDF_VERSION_VAL(5, 5, 0) for the send callback (the receive callback change to esp_now_recv_info_t in IDF 5.0 is fine as is).

  2. I2C scan in tof_init() (src/tof.cpp:80-82)
    scan_i2c() and the log line are not inside a version guard, so they also run on the 2.x path. This looks like debug code; could you remove it or put it behind a debug flag, so that the 2.x behavior stays unchanged as described?

  3. CI workflows

    • Arduino Lint: this repository is a PlatformIO project, not an Arduino library/sketch, so arduino-lint fails with "No projects found". It never ran before because it was only triggered on master. Adding main to the triggers turns it into a check that cannot pass. I would suggest reverting the trigger change in this PR (whether to delete or reconfigure the workflow is up to the maintainers).
    • clang-format: thanks for fixing the duplicated exclude key — the workflow was invalid YAML on main and never actually ran. Now it runs and reports many pre-existing files, which is out of scope here, but could you at least format the new file src/arduino_esp32_i2c_compat.c?

Questions

  1. I2C timeout inside the 400 Hz loop (src/arduino_esp32_i2c_compat.c:8)
    The ToF range is read from sensor_read(), which is called from loop_400Hz(). With a 50 ms timeout, an I2C fault in flight would block the control loop for up to 20 control periods (2.5 ms each), compared with about 1 tick in the original code. I agree it is only an upper bound, but have you considered a shorter timeout for runtime reads (keeping the longer one only for initialization)?

  2. Hardware testing of the 2.x environment
    The PR also changes the 2.x path (the scan above, and digitalWrite(buzzerPin, 0) in src/buzzer.cpp:39 — the latter fixes an obvious bug, since the original wrote GPIO5 = front-left motor pin). Were the hardware tests in the checklist done with both environments, or only with 3.3.11?

Nits

  • The three new files are added as 100755 (the description says permissions were restored to 100644).
  • Please write the new comments in src/rc.cpp in English (the rest of the code uses Japanese/English).
  • platformio.ini has no newline at end of file.

P.S. A note mainly for the M5Stack team: honestly, I have been thinking that the firmware shipped on StampFly (this repository) should eventually be replaced with the vehicle firmware from stampfly_ecosystem. That is a separate discussion from this PR, though. Until then, this PR is still valuable for everyone who uses the current firmware with newer Arduino-ESP32 versions.

- Switch the esp_now_send_info_t callback guard from IDF 5.0.0 to 5.5.0
- Fix build on Arduino-ESP32 3.0–3.2 (IDF 5.1–5.4), which still use const uint8_t *mac_addr
- Restore 100644 mode on the three new compat source/header files
- Drop the scan_i2c() call and log after the front ToF address change
- Drop the sensor.hpp include that was only needed for it
- Keep 2.x ToF init behavior unchanged
- Restore Arduino-Lint-Check.yml to the upstream version
- Avoid an always-failing check on main, since this is a PlatformIO project
- Apply the repository .clang-format with clang-format 13 (same as CI)
- Whitespace-only change
- Replace the two Chinese comments added around the ESP-NOW receive handler
- Keep a 50 ms I2C timeout during sensor initialization
- Switch to 5 ms at the end of sensor_init() before the 400 Hz loop starts
- Add stampfly_i2c_set_timeout_ms() to the I2C compat layer
- Leave the Arduino 2.x path unchanged
- Add a "Recommended firmware" section linking to M5Fly-kanazawa/stampfly_ecosystem
@LuoIsHere

LuoIsHere commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor Author

@kouhei1970 Thank you for the detailed review and for building both environments locally. I also apologize for the careless mistakes in the previous revision. All review points have been addressed as follows.

Should fix

  1. ESP-NOW send callback guard: Changed both guards in src/rc.cpp to ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 5, 0), so IDF 5.1–5.4 now uses the const uint8_t *mac_addr signature. The receive callback is unchanged.
  2. I2C scan in tof_init(): Removed, together with the related include. The 2.x path no longer runs it.
  3. CI workflows
    • Arduino Lint: Reverted the trigger change, so the workflow is the same as on main.
    • clang-format: Formatted src/arduino_esp32_i2c_compat.c with clang-format 13 (the same version as the CI). The remaining failures come from pre-existing files, so I left them out of scope.

Questions

  1. I2C timeout inside the 400 Hz loop: Thank you for pointing this out. The timeout is now configurable. It stays at 50 ms during sensor initialization, and sensor_init() switches it to 5 ms right before entering the 400 Hz loop (stampfly_i2c_set_timeout_ms(), 3.x only).

    The original 1-tick value could not be reused directly:

    • Arduino-ESP32 2.0.17 is based on ESP-IDF v4.4.7. There, vl53lx_platform.c passes 1 / portTICK_RATE_MS to i2c_master_cmd_begin(), but the legacy driver only applies this value when taking the port mutex (mutex). While waiting for the transfer itself, it raises each wait to at least I2C_CMD_ALIVE_INTERVAL_TICK (1000 ms) and only times out if no I2C event arrives within that period, so the 1-tick value does not actually limit the transfer time (definition, wait loop).
    • Arduino-ESP32 3.3.11 is based on ESP-IDF 5.5.5. The new i2c_master driver converts the timeout with pdMS_TO_TICKS() and uses it directly for its blocking waits (conversion, command wait, event wait). With CONFIG_FREERTOS_HZ=1000, a 1-tick wait can expire anywhere between 0 and 1 ms, while a normal VL53LX histogram read (83 bytes at 400 kHz) takes about 2 ms, so 1 tick would abort valid transfers.

    On the bench, with temporary I2C statistics enabled, all bottom ToF transfers succeeded with no timeouts, and the longest single transfer was about 2.3 ms, so 5 ms leaves enough margin.

  2. Hardware testing of the 2.x environment: In the previous revision, I tested both environments (2.0.17 and 3.3.11) on M5Stack StampFly and StampFly v1.1, and both looked fine. As you pointed out, the original digitalWrite(channel, 0) in src/buzzer.cpp was a bug, since it wrote to GPIO5 (the front-left motor pin), so I changed it to digitalWrite(buzzerPin, 0). Apart from that, the only change visible on 2.x is an additional Environment: ... line printed at boot.

Nits

  • The three new files are now 100644.
  • The new comments in src/rc.cpp are now in English.
  • Added the missing newline at the end of platformio.ini.

Regarding the P.S., I have also added a note to the README recommending the vehicle firmware in stampfly_ecosystem.

Update on the front ToF issue

Please disregard the front ToF issue I described earlier, if you have already read it. It turned out to be a power supply issue: during that test, the board was powered only over USB, without the battery connected. With the battery connected, the original code works fine: the front ToF stays at 0x2A, there are no I2C errors, and both the bottom and front ranges are valid. So no code change is needed for this, and this PR does not include one.

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