From ed577991a3bf2bb2c6dae50b0ea54fdaeee5dc29 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 13:10:23 +0000 Subject: [PATCH 1/3] Bump version to 1.0.2 to publish BMI270 i2c_port_t fix The invalid int -> i2c_port_t conversion in BMI270.h (dev_info::i2c_port) was already fixed on main by commit 1aaf0d6, but the library version was never bumped, so PlatformIO's Library Registry still serves the old 1.0.1 release without the fix, and users pulling M5Module-GNSS as a dependency still hit the -fpermissive warning. Bump to 1.0.2 so a new release ships the fix. --- library.json | 2 +- library.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library.json b/library.json index 28c8a99..12ac498 100644 --- a/library.json +++ b/library.json @@ -10,7 +10,7 @@ "type": "git", "url": "https://github.com/m5stack/M5Module-GNSS.git" }, - "version": "1.0.1", + "version": "1.0.2", "frameworks": "arduino", "platforms": "espressif32", "headers": "M5Module_GNSS.h" diff --git a/library.properties b/library.properties index 78fafce..4361872 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=M5Module-GNSS -version=1.0.1 +version=1.0.2 author=M5Stack maintainer=M5Stack sentence=Library for M5Stack GNSS Module From 6e49ffe878e201c12d63e200638e018f3a9b8467 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 29 Aug 2026 13:40:02 +0000 Subject: [PATCH 2/3] BMI270: port I2C access to Wire to avoid driver_ng conflict abort On Arduino core 3.x / pioarduino platform-espressif32 (ESP-IDF 5.x), BMI270 called the legacy driver/i2c.h API (i2c_master_write_to_device / i2c_master_write_read_device) directly. When Arduino's own Wire also links the new i2c_master driver, ESP-IDF's old i2c.c driver aborts at startup via a global constructor: E (1057) i2c: CONFLICT! driver_ng is not allowed to be used with this old driver abort() was called at PC 0x4204ef83 on core 0 firmware.map confirmed libdriver.a(i2c.c.obj) was pulled in solely by BMI270.cpp.o, while libesp_driver_i2c.a(i2c_master.c.obj) was pulled in by the Arduino core's own Wire (esp32-hal-i2c-ng.c.o), which cannot be avoided. Port BMI270's I2C access to Arduino's TwoWire (Wire) so it no longer touches driver/i2c.h at all: - dev_info::i2c_port (i2c_port_t) -> dev_info::wire (TwoWire*) - BMI270::_i2c_port (i2c_port_t) -> BMI270::_wire (TwoWire*) - bmi2_i2c_read/bmi2_i2c_write reimplemented on top of beginTransmission/write/endTransmission/requestFrom/read instead of i2c_master_write_read_device/i2c_master_write_to_device - drop the file-scope static _data_buffer/_i2c_ret/_dev_info (shared mutable state unsafe across instances/tasks) in favor of locals in each callback - add int init(TwoWire&, uint8_t); keep int init(i2c_port_t, uint8_t) as a deprecated back-compat overload (I2C_NUM_0 -> Wire, I2C_NUM_1 -> Wire1), now only needing the header-only for the i2c_port_t type instead of , so it does not reintroduce the i2c.c.obj link - update examples/getSensorData to use the new Wire-based init() - document the fix and new init() usage in README - bump library version to 1.0.3 Not independently re-verified against a real firmware.map in this environment (no ESP32/PlatformIO toolchain available here); please confirm libdriver.a(i2c.c.obj) is absent from the link in your build. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_01KRT6bgzHXTPERGjx5tEHQj --- README.md | 26 +++++++++++ examples/getSensorData/getSensorData.ino | 2 +- library.json | 2 +- library.properties | 2 +- src/BMI270.cpp | 58 +++++++++++++++--------- src/BMI270.h | 14 ++++-- 6 files changed, 76 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index 479cee8..8b7fdc7 100644 --- a/README.md +++ b/README.md @@ -4,6 +4,32 @@ [![Clang Format](https://github.com/m5stack/M5Module-GNSS/actions/workflows/clang-format-check.yml/badge.svg)](https://github.com/m5stack/M5Module-GNSS/actions/workflows/clang-format-check.yml) +## BMI270 I2C access (Arduino core 3.x / ESP-IDF 5.x) + +`BMI270` now talks I2C through the Arduino `TwoWire` (`Wire`) API instead of calling the +ESP-IDF legacy `driver/i2c.h` functions directly. On Arduino core 3.x (ESP-IDF 5.x, +`i2c_master`/`driver_ng`), linking any code against the old `driver/i2c.h` API alongside the +Arduino core's own I2C driver aborts at startup with: + +``` +E (1057) i2c: CONFLICT! driver_ng is not allowed to be used with this old driver +abort() was called at PC 0x4204ef83 on core 0 +``` + +Since `BMI270` no longer references the legacy driver, this conflict can no longer occur. + +`BMI270::init()` now takes a `TwoWire&`: + +```cpp +Wire.begin(21, 22, 100000); // the library does not call Wire.begin() itself +bmi270.init(Wire, BIM270_SENSOR_ADDR); +``` + +The old `init(i2c_port_t, uint8_t)` signature is kept for source compatibility +(`I2C_NUM_0` maps to `Wire`, `I2C_NUM_1` maps to `Wire1`) but is deprecated — prefer the +`TwoWire&` overload in new code. + + ## License - [M5Module-GNSS - MIT](LICENSE) diff --git a/examples/getSensorData/getSensorData.ino b/examples/getSensorData/getSensorData.ino index 93c9e44..3ff4cd2 100644 --- a/examples/getSensorData/getSensorData.ino +++ b/examples/getSensorData/getSensorData.ino @@ -40,7 +40,7 @@ void setup() { while (1) delay(10); } - bmi270.init(I2C_NUM_0, BIM270_SENSOR_ADDR); + bmi270.init(Wire, BIM270_SENSOR_ADDR); } void loop() { diff --git a/library.json b/library.json index 12ac498..6275bfe 100644 --- a/library.json +++ b/library.json @@ -10,7 +10,7 @@ "type": "git", "url": "https://github.com/m5stack/M5Module-GNSS.git" }, - "version": "1.0.2", + "version": "1.0.3", "frameworks": "arduino", "platforms": "espressif32", "headers": "M5Module_GNSS.h" diff --git a/library.properties b/library.properties index 4361872..6747fb1 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=M5Module-GNSS -version=1.0.2 +version=1.0.3 author=M5Stack maintainer=M5Stack sentence=Library for M5Stack GNSS Module diff --git a/src/BMI270.cpp b/src/BMI270.cpp index 529a885..050c926 100644 --- a/src/BMI270.cpp +++ b/src/BMI270.cpp @@ -52,7 +52,14 @@ namespace BMI270 { int BMI270::init(i2c_port_t i2c_port, uint8_t bmiAddr) { - _i2c_port = i2c_port; + TwoWire& wire = (i2c_port == I2C_NUM_1) ? Wire1 : Wire; + return init(wire, bmiAddr); + } + + + int BMI270::init(TwoWire& wire, uint8_t bmiAddr) + { + _wire = &wire; /* BMI270 init */ @@ -67,7 +74,7 @@ namespace BMI270 { bmi2.read_write_len = 30; // Limitation of the Wire library bmi2.config_file_ptr = NULL; // Use the default BMI270 config file - accel_gyro_dev_info.i2c_port = _i2c_port; + accel_gyro_dev_info.wire = _wire; accel_gyro_dev_info.dev_addr = bmi2.chip_id; int8_t rslt = bmi270_init(&bmi2); @@ -143,7 +150,7 @@ namespace BMI270 { bmm1.intf = BMM150_I2C_INTF; bmm1.intf_ptr = &mag_dev_info; - mag_dev_info.i2c_port = _i2c_port; + mag_dev_info.wire = _wire; mag_dev_info.dev_addr = bmm1.chip_id; @@ -629,26 +636,34 @@ namespace BMI270 { - /* I2C W/R buffer */ - static uint8_t _data_buffer[32]; - static esp_err_t _i2c_ret; - struct dev_info* _dev_info; - - + /* I2C register R/W, 32 bytes max per transaction (bmi2.read_write_len above) */ int8_t BMI270::bmi2_i2c_read(uint8_t reg_addr, uint8_t *reg_data, uint32_t len, void *intf_ptr) { if ((reg_data == NULL) || (len == 0) || (len > 32)) { return -1; } - _dev_info = (struct dev_info*)intf_ptr; + struct dev_info* dev_info = (struct dev_info*)intf_ptr; + TwoWire* wire = dev_info->wire; - _i2c_ret = i2c_master_write_read_device(_dev_info->i2c_port, _dev_info->dev_addr, ®_addr, 1, reg_data, len, portMAX_DELAY); - if (_i2c_ret != ESP_OK) { - ESP_LOGE(TAG, "P:%d R %02X %02X E:%d", _dev_info->i2c_port, _dev_info->dev_addr, reg_addr, _i2c_ret); + wire->beginTransmission(dev_info->dev_addr); + wire->write(reg_addr); + uint8_t tx_ret = wire->endTransmission(false); // repeated start, no stop before the read + if (tx_ret != 0) { + ESP_LOGE(TAG, "A:%02X R %02X endTransmission:%d", dev_info->dev_addr, reg_addr, tx_ret); return -1; } + size_t received = wire->requestFrom((uint8_t)dev_info->dev_addr, (uint8_t)len); + if (received != len) { + ESP_LOGE(TAG, "A:%02X R %02X requestFrom got:%u want:%lu", dev_info->dev_addr, reg_addr, (unsigned)received, (unsigned long)len); + return -1; + } + + for (uint32_t i = 0; i < len; i++) { + reg_data[i] = (uint8_t)wire->read(); + } + return 0; } @@ -659,16 +674,15 @@ namespace BMI270 { return -1; } - _dev_info = (struct dev_info *)intf_ptr; - - _data_buffer[0] = reg_addr; - for (int i = 0; i < len; i++) { - _data_buffer[i + 1] = reg_data[i]; - } + struct dev_info* dev_info = (struct dev_info*)intf_ptr; + TwoWire* wire = dev_info->wire; - _i2c_ret = i2c_master_write_to_device(_dev_info->i2c_port, _dev_info->dev_addr, _data_buffer, (len + 1), portMAX_DELAY); - if (_i2c_ret != ESP_OK) { - ESP_LOGE(TAG, "P:%d W %02X %02X E:%d", _dev_info->i2c_port, _dev_info->dev_addr, reg_addr, _i2c_ret); + wire->beginTransmission(dev_info->dev_addr); + wire->write(reg_addr); + wire->write(reg_data, len); + uint8_t tx_ret = wire->endTransmission(true); + if (tx_ret != 0) { + ESP_LOGE(TAG, "A:%02X W %02X endTransmission:%d", dev_info->dev_addr, reg_addr, tx_ret); return -1; } diff --git a/src/BMI270.h b/src/BMI270.h index a1b49b0..72ca9bd 100644 --- a/src/BMI270.h +++ b/src/BMI270.h @@ -28,7 +28,8 @@ */ #include "utilities/BMI270-Sensor-API/bmi270.h" #include "utilities/BMM150-Sensor-API/bmm150.h" -#include +#include +#include // header-only, only needed for the deprecated i2c_port_t overload below #include #include @@ -36,14 +37,14 @@ namespace BMI270 { struct dev_info { - i2c_port_t i2c_port = I2C_NUM_0; + TwoWire* wire = &Wire; uint8_t dev_addr; }; class BMI270 { private: - i2c_port_t _i2c_port; + TwoWire* _wire = &Wire; bool _initialized = false; int _interrupts = 0; @@ -81,6 +82,13 @@ namespace BMI270 { void setContinuousMode(); void oneShotMode(); + int init(TwoWire& wire, uint8_t bmiAddr); + + // Deprecated: kept for source compatibility with the old ESP-IDF driver/i2c.h based + // API. I2C_NUM_0 maps to Wire, I2C_NUM_1 maps to Wire1. Only (a + // header-only type definition) is needed for i2c_port_t here, so this overload does + // not pull in the legacy driver/i2c.c object file and its Arduino core 3.x / + // ESP-IDF 5.x driver_ng conflict. int init(i2c_port_t i2c_port, uint8_t bmiAddr); From 339ffa53e140801825a4327012845a25f47df6e1 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 14 Oct 2026 11:23:49 +0000 Subject: [PATCH 3/3] BMI270: keep init(i2c_port_t) compiling on core 2.x and single-I2C chips The compatibility overload added with the Wire port referenced I2C_NUM_1 and Wire1 unconditionally, and the header used the global Wire as a default member initializer. Checked against the real arduino-esp32 and ESP-IDF headers, that breaks the build in several configurations: - Arduino core 2.x (ESP-IDF 4.4): I2C_NUM_0/I2C_NUM_1 are macros in driver/i2c.h, not in hal/i2c_types.h, so the library itself failed to compile and existing init(I2C_NUM_0, ...) sketches lost the constant. - ESP32-C3/C6 and other chips with one HP I2C controller: Wire1 (and I2C_NUM_1 on IDF 5.5) is not declared. - Arduino core 3.3 with NO_GLOBAL_WIRE: Wire is not declared at all. Include driver/i2c.h only on IDF 4.x, where no driver_ng exists to conflict with; keep the header-only hal/i2c_types.h on IDF 5.x. Map port 1 to Wire1 only on chips with a second HP I2C controller (which implies Wire1 in every core release), guard the global instances, and log an error otherwise. Default the TwoWire pointers to nullptr. README: the conflict starts with Arduino core 3.2.0, where Wire moved to the i2c_master driver (esp32-hal-i2c-ng.c, IDF >= 5.4). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01KRT6bgzHXTPERGjx5tEHQj --- README.md | 15 ++++++++------- src/BMI270.cpp | 25 +++++++++++++++++++++++-- src/BMI270.h | 20 ++++++++++++-------- 3 files changed, 43 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 8b7fdc7..885361e 100644 --- a/README.md +++ b/README.md @@ -4,19 +4,20 @@ [![Clang Format](https://github.com/m5stack/M5Module-GNSS/actions/workflows/clang-format-check.yml/badge.svg)](https://github.com/m5stack/M5Module-GNSS/actions/workflows/clang-format-check.yml) -## BMI270 I2C access (Arduino core 3.x / ESP-IDF 5.x) +## BMI270 I2C access (Arduino core 3.2+ / ESP-IDF 5.4+) `BMI270` now talks I2C through the Arduino `TwoWire` (`Wire`) API instead of calling the -ESP-IDF legacy `driver/i2c.h` functions directly. On Arduino core 3.x (ESP-IDF 5.x, -`i2c_master`/`driver_ng`), linking any code against the old `driver/i2c.h` API alongside the -Arduino core's own I2C driver aborts at startup with: +ESP-IDF legacy `driver/i2c.h` functions directly. Since Arduino core 3.2.0, `Wire` is built on +the new `i2c_master` driver (`driver_ng`), and linking the legacy driver into the same firmware +aborts at startup with: ``` E (1057) i2c: CONFLICT! driver_ng is not allowed to be used with this old driver abort() was called at PC 0x4204ef83 on core 0 ``` -Since `BMI270` no longer references the legacy driver, this conflict can no longer occur. +Since `BMI270` no longer references the legacy driver, this conflict can no longer occur. Arduino +core 2.x and 3.0/3.1 keep working as before. `BMI270::init()` now takes a `TwoWire&`: @@ -26,8 +27,8 @@ bmi270.init(Wire, BIM270_SENSOR_ADDR); ``` The old `init(i2c_port_t, uint8_t)` signature is kept for source compatibility -(`I2C_NUM_0` maps to `Wire`, `I2C_NUM_1` maps to `Wire1`) but is deprecated — prefer the -`TwoWire&` overload in new code. +(`I2C_NUM_0` maps to `Wire`, `I2C_NUM_1` maps to `Wire1` on chips with a second I2C controller +such as ESP32 / ESP32-S3) but is deprecated — prefer the `TwoWire&` overload in new code. ## License diff --git a/src/BMI270.cpp b/src/BMI270.cpp index 050c926..f06abbf 100644 --- a/src/BMI270.cpp +++ b/src/BMI270.cpp @@ -48,12 +48,33 @@ static bmi2_dev* _bmi2_dev_ptr = nullptr; +// arduino-esp32 declares Wire1 on every 2.x target, but on 3.x only where it exists (the condition +// differs per 3.x release); a second HP I2C controller implies Wire1 in every release. +#if ESP_IDF_VERSION_MAJOR < 5 +#define BMI270_HAS_WIRE1 1 +#elif defined(SOC_HP_I2C_NUM) +#define BMI270_HAS_WIRE1 (SOC_HP_I2C_NUM > 1) +#else +#define BMI270_HAS_WIRE1 (SOC_I2C_NUM > 1) +#endif + + namespace BMI270 { int BMI270::init(i2c_port_t i2c_port, uint8_t bmiAddr) { - TwoWire& wire = (i2c_port == I2C_NUM_1) ? Wire1 : Wire; - return init(wire, bmiAddr); +#if !defined(NO_GLOBAL_INSTANCES) && !defined(NO_GLOBAL_WIRE) + if (i2c_port == 0) { + return init(Wire, bmiAddr); + } +#if BMI270_HAS_WIRE1 + if (i2c_port == 1) { + return init(Wire1, bmiAddr); + } +#endif +#endif + ESP_LOGE(TAG, "No global TwoWire for I2C port %d (addr 0x%02X), use init(TwoWire&, uint8_t)", (int)i2c_port, bmiAddr); + return false; } diff --git a/src/BMI270.h b/src/BMI270.h index 72ca9bd..1191276 100644 --- a/src/BMI270.h +++ b/src/BMI270.h @@ -29,7 +29,14 @@ #include "utilities/BMI270-Sensor-API/bmi270.h" #include "utilities/BMM150-Sensor-API/bmm150.h" #include -#include // header-only, only needed for the deprecated i2c_port_t overload below +#include +// i2c_port_t / I2C_NUM_x for the deprecated init(i2c_port_t, ...) overload only. Nothing from the +// legacy I2C driver is called, so its i2c.c object (and the driver_ng conflict check) is not linked. +#if ESP_IDF_VERSION_MAJOR >= 5 +#include +#else +#include // IDF 4.x defines I2C_NUM_x here; it has no driver_ng to conflict with +#endif #include #include @@ -37,14 +44,14 @@ namespace BMI270 { struct dev_info { - TwoWire* wire = &Wire; + TwoWire* wire = nullptr; uint8_t dev_addr; }; class BMI270 { private: - TwoWire* _wire = &Wire; + TwoWire* _wire = nullptr; bool _initialized = false; int _interrupts = 0; @@ -84,11 +91,8 @@ namespace BMI270 { int init(TwoWire& wire, uint8_t bmiAddr); - // Deprecated: kept for source compatibility with the old ESP-IDF driver/i2c.h based - // API. I2C_NUM_0 maps to Wire, I2C_NUM_1 maps to Wire1. Only (a - // header-only type definition) is needed for i2c_port_t here, so this overload does - // not pull in the legacy driver/i2c.c object file and its Arduino core 3.x / - // ESP-IDF 5.x driver_ng conflict. + // Deprecated, kept for source compatibility: I2C_NUM_0 -> Wire, I2C_NUM_1 -> Wire1 + // (Wire1 only on chips with two HP I2C controllers). Prefer init(TwoWire&, uint8_t). int init(i2c_port_t i2c_port, uint8_t bmiAddr);