3838#include < SPI.h>
3939#include < HardwareSerial.h>
4040#else // ESP-IDF native
41+ // driver/i2c_master.h (new I2C master driver) exists only on IDF >= 5.2. On IDF 5.0/5.1 fall back to
42+ // the legacy driver/i2c.h so the I2C wiring helpers keep working across the whole IDF >= 5.0 range.
43+ #if __has_include(<driver/i2c_master.h>)
4144#include < driver/i2c_master.h>
45+ #else
46+ #include < driver/i2c.h>
47+ #endif
4248#include < driver/uart.h>
4349#include < driver/spi_master.h>
4450#include < hal/gpio_types.h>
@@ -571,10 +577,12 @@ constexpr size_t kUARTPortCacheSize = 4;
571577constexpr size_t kSPIHostCacheSize = 2 ; // SPI2_HOST / SPI3_HOST
572578constexpr size_t kSPIDevCacheSize = 4 ;
573579
580+ #if __has_include(<driver/i2c_master.h>)
574581struct I2CCacheEntry {
575582 uint32_t key;
576583 i2c_master_bus_handle_t handle;
577584};
585+ #endif
578586
579587struct UARTCacheEntry {
580588 uint32_t key;
@@ -591,6 +599,7 @@ struct SPIDevEntry {
591599 spi_device_handle_t handle;
592600};
593601
602+ #if __has_include(<driver/i2c_master.h>)
594603// ! @brief Get or create an I2C master bus, cached by {port, sda, scl}.
595604inline i2c_master_bus_handle_t ensureI2CBus (const i2c_port_t port, const gpio_num_t sda, const gpio_num_t scl,
596605 const uint32_t /* clock - not used to key, single bus per pins*/ )
@@ -629,6 +638,39 @@ inline i2c_master_bus_handle_t ensureI2CBus(const i2c_port_t port, const gpio_nu
629638 cache[count].key = key;
630639 return cache[count++].handle ;
631640}
641+ #else // legacy driver/i2c.h (IDF 5.0 / 5.1)
642+ // ! @brief Install the legacy I2C master driver once per {port, sda, scl}; cached so re-adds are no-ops.
643+ inline bool ensureI2CLegacyDriver (const i2c_port_t port, const gpio_num_t sda, const gpio_num_t scl,
644+ const uint32_t clock)
645+ {
646+ const uint32_t key = (static_cast <uint32_t >(static_cast <uint8_t >(port)) << 24 ) |
647+ (static_cast <uint32_t >(static_cast <uint8_t >(sda)) << 16 ) |
648+ (static_cast <uint32_t >(static_cast <uint8_t >(scl)) << 8 );
649+ static uint32_t cache[kI2CBusCacheSize ]{};
650+ static size_t count{};
651+ for (size_t i = 0 ; i < count; ++i) {
652+ if (cache[i] == key) return true ;
653+ }
654+ if (count >= kI2CBusCacheSize ) {
655+ M5_LIB_LOGE (" wiring: I2C bus cache full (max %zu)" , kI2CBusCacheSize );
656+ return false ;
657+ }
658+ i2c_config_t conf{};
659+ conf.mode = I2C_MODE_MASTER ;
660+ conf.sda_io_num = sda;
661+ conf.scl_io_num = scl;
662+ conf.sda_pullup_en = true ;
663+ conf.scl_pullup_en = true ;
664+ conf.master .clk_speed = clock;
665+ if (i2c_param_config (port, &conf) != ESP_OK ||
666+ i2c_driver_install (port, I2C_MODE_MASTER , 0 , 0 , 0 ) != ESP_OK ) {
667+ M5_LIB_LOGE (" wiring: legacy i2c driver install failed port=%d sda=%d scl=%d" , (int )port, (int )sda, (int )scl);
668+ return false ;
669+ }
670+ cache[count++] = key;
671+ return true ;
672+ }
673+ #endif
632674
633675// ! @brief Get or install a UART port, cached by {port, rx, tx}.
634676inline uart_port_t ensureUARTPort (const uart_port_t port, const gpio_num_t rx, const gpio_num_t tx, const uint32_t baud,
@@ -727,15 +769,18 @@ inline spi_device_handle_t ensureSPIDevice(const spi_host_device_t host, const g
727769
728770} // namespace detail
729771
772+ #if __has_include(<driver/i2c_master.h>)
730773/* !
731774 @brief Get or create an I2C master bus handle (cached by {port, sda, scl})
732775 @note Intended for unit-specific wiring in M5Unit-* libraries; examples should use addI2C() instead.
776+ @note Available only on IDF >= 5.2 (new I2C master driver). On IDF 5.0/5.1 use addI2C() instead.
733777*/
734778inline i2c_master_bus_handle_t i2cBusHandle (const i2c_port_t port, const gpio_num_t sda, const gpio_num_t scl,
735779 const uint32_t clock = 100000 )
736780{
737781 return detail::ensureI2CBus (port, sda, scl, clock);
738782}
783+ #endif
739784
740785/* !
741786 @brief Get or install a UART port (cached by {port, rx, tx})
@@ -793,9 +838,16 @@ inline bool addI2C(UnitUnified& units, Component& unit, const uint32_t clock = 1
793838 return i2cClass (units, unit, M5 .In_I2C );
794839 } else {
795840 // Core / Stick / S3 etc: M5Unified does not call Wire.begin(), so install ourselves
841+ #if __has_include(<driver/i2c_master.h>)
796842 auto bus = i2cBusHandle (I2C_NUM_0 , (gpio_num_t )pins.sda , (gpio_num_t )pins.scl , clock);
797843 if (!bus) return false ;
798844 return units.add (unit, bus);
845+ #else // legacy driver (IDF 5.0 / 5.1): install legacy driver, then add by port
846+ if (!detail::ensureI2CLegacyDriver (I2C_NUM_0 , (gpio_num_t )pins.sda , (gpio_num_t )pins.scl , clock)) {
847+ return false ;
848+ }
849+ return units.add (unit, I2C_NUM_0 , (gpio_num_t )pins.sda , (gpio_num_t )pins.scl );
850+ #endif
799851 }
800852 case I2CPins::Backend::ExI2C:
801853 // NanoC6 / NanoH2: M5Unified manages Ex_I2C -> borrow
@@ -863,9 +915,14 @@ inline bool addHatI2C(UnitUnified& units, Component& unit, const uint32_t clock
863915 }
864916 M5_LIB_LOGI (" wiring(ESP-IDF): addHatI2C port=%d sda=%d scl=%d clock=%u" , (int )port, (int )p.sda , (int )p.scl ,
865917 (unsigned )clock);
918+ #if __has_include(<driver/i2c_master.h>)
866919 auto bus = i2cBusHandle (port, (gpio_num_t )p.sda , (gpio_num_t )p.scl , clock);
867920 if (!bus) return false ;
868921 return units.add (unit, bus);
922+ #else // legacy driver (IDF 5.0 / 5.1)
923+ if (!detail::ensureI2CLegacyDriver (port, (gpio_num_t )p.sda , (gpio_num_t )p.scl , clock)) return false ;
924+ return units.add (unit, port, (gpio_num_t )p.sda , (gpio_num_t )p.scl );
925+ #endif
869926}
870927
871928// ! @brief Add a unit on the board's Hat GPIO header (ESP-IDF native)
0 commit comments