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,38 @@ 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 || i2c_driver_install (port, I2C_MODE_MASTER , 0 , 0 , 0 ) != ESP_OK ) {
666+ M5_LIB_LOGE (" wiring: legacy i2c driver install failed port=%d sda=%d scl=%d" , (int )port, (int )sda, (int )scl);
667+ return false ;
668+ }
669+ cache[count++] = key;
670+ return true ;
671+ }
672+ #endif
632673
633674// ! @brief Get or install a UART port, cached by {port, rx, tx}.
634675inline 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 +768,18 @@ inline spi_device_handle_t ensureSPIDevice(const spi_host_device_t host, const g
727768
728769} // namespace detail
729770
771+ #if __has_include(<driver/i2c_master.h>)
730772/* !
731773 @brief Get or create an I2C master bus handle (cached by {port, sda, scl})
732774 @note Intended for unit-specific wiring in M5Unit-* libraries; examples should use addI2C() instead.
775+ @note Available only on IDF >= 5.2 (new I2C master driver). On IDF 5.0/5.1 use addI2C() instead.
733776*/
734777inline i2c_master_bus_handle_t i2cBusHandle (const i2c_port_t port, const gpio_num_t sda, const gpio_num_t scl,
735778 const uint32_t clock = 100000 )
736779{
737780 return detail::ensureI2CBus (port, sda, scl, clock);
738781}
782+ #endif
739783
740784/* !
741785 @brief Get or install a UART port (cached by {port, rx, tx})
@@ -793,9 +837,16 @@ inline bool addI2C(UnitUnified& units, Component& unit, const uint32_t clock = 1
793837 return i2cClass (units, unit, M5 .In_I2C );
794838 } else {
795839 // Core / Stick / S3 etc: M5Unified does not call Wire.begin(), so install ourselves
840+ #if __has_include(<driver/i2c_master.h>)
796841 auto bus = i2cBusHandle (I2C_NUM_0 , (gpio_num_t )pins.sda , (gpio_num_t )pins.scl , clock);
797842 if (!bus) return false ;
798843 return units.add (unit, bus);
844+ #else // legacy driver (IDF 5.0 / 5.1): install legacy driver, then add by port
845+ if (!detail::ensureI2CLegacyDriver (I2C_NUM_0 , (gpio_num_t )pins.sda , (gpio_num_t )pins.scl , clock)) {
846+ return false ;
847+ }
848+ return units.add (unit, I2C_NUM_0 , (gpio_num_t )pins.sda , (gpio_num_t )pins.scl );
849+ #endif
799850 }
800851 case I2CPins::Backend::ExI2C:
801852 // NanoC6 / NanoH2: M5Unified manages Ex_I2C -> borrow
@@ -863,9 +914,14 @@ inline bool addHatI2C(UnitUnified& units, Component& unit, const uint32_t clock
863914 }
864915 M5_LIB_LOGI (" wiring(ESP-IDF): addHatI2C port=%d sda=%d scl=%d clock=%u" , (int )port, (int )p.sda , (int )p.scl ,
865916 (unsigned )clock);
917+ #if __has_include(<driver/i2c_master.h>)
866918 auto bus = i2cBusHandle (port, (gpio_num_t )p.sda , (gpio_num_t )p.scl , clock);
867919 if (!bus) return false ;
868920 return units.add (unit, bus);
921+ #else // legacy driver (IDF 5.0 / 5.1)
922+ if (!detail::ensureI2CLegacyDriver (port, (gpio_num_t )p.sda , (gpio_num_t )p.scl , clock)) return false ;
923+ return units.add (unit, port, (gpio_num_t )p.sda , (gpio_num_t )p.scl );
924+ #endif
869925}
870926
871927// ! @brief Add a unit on the board's Hat GPIO header (ESP-IDF native)
0 commit comments