32#ifndef M5_UNIT_UNIFIED_WIRING_HPP
33#define M5_UNIT_UNIFIED_WIRING_HPP
39#include <HardwareSerial.h>
40#elif defined(ESP_PLATFORM)
43#if __has_include(<driver/i2c_master.h>)
44#include <driver/i2c_master.h>
46#include <driver/i2c.h>
48#include <driver/uart.h>
49#include <driver/spi_master.h>
50#include <hal/gpio_types.h>
51#include <soc/soc_caps.h>
55#if defined(ESP_PLATFORM)
56#include <esp_idf_version.h>
58#if defined(SOC_HP_I2C_NUM)
59#define M5_UNIT_UNIFIED_WIRING_HP_I2C_NUM SOC_HP_I2C_NUM
61#define M5_UNIT_UNIFIED_WIRING_HP_I2C_NUM SOC_I2C_NUM
65#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0)
66#define M5_UNIT_UNIFIED_WIRING_HAS_WIRE1 (SOC_I2C_NUM > 1)
68#define M5_UNIT_UNIFIED_WIRING_HAS_WIRE1 (M5_UNIT_UNIFIED_WIRING_HP_I2C_NUM > 1)
72#if defined(SOC_UART_HP_NUM)
73#define M5_UNIT_UNIFIED_WIRING_HP_UART_NUM SOC_UART_HP_NUM
75#define M5_UNIT_UNIFIED_WIRING_HP_UART_NUM SOC_UART_NUM
158#if defined(__M5UNIFIED_HPP__)
168inline I2CPins i2cPins(const NessoPort nesso = NessoPort::PortB)
170 const auto board = M5.getBoard();
171 if (board == m5::board_t::board_ArduinoNessoN1) {
172 if (nesso == NessoPort::PortB) {
173 return {
static_cast<int8_t
>(M5.getPin(m5::pin_name_t::port_b_out)),
176 return {
static_cast<int8_t
>(M5.getPin(m5::pin_name_t::port_a_sda)),
179 if (board == m5::board_t::board_M5NanoC6 || board == m5::board_t::board_M5NanoH2) {
180 return {
static_cast<int8_t
>(M5.getPin(m5::pin_name_t::ex_i2c_sda)),
186 const int8_t ex_sda = M5.getPin(m5::pin_name_t::ex_i2c_sda);
187 const int8_t ex_scl = M5.getPin(m5::pin_name_t::ex_i2c_scl);
188 if (ex_sda >= 0 && ex_scl >= 0 && ex_sda == M5.getPin(m5::pin_name_t::in_i2c_sda) &&
189 ex_scl == M5.getPin(m5::pin_name_t::in_i2c_scl)) {
192 return {
static_cast<int8_t
>(M5.getPin(m5::pin_name_t::port_a_sda)),
203inline GpioPinPair gpioPins(
const GpioRole role = GpioRole::Both)
205 int rx = (role == GpioRole::OutOnly) ? -1 : M5.getPin(
m5::pin_name_t::port_b_in);
206 int tx = (role == GpioRole::InOnly) ? -1 : M5.getPin(
m5::pin_name_t::port_b_out);
207 const bool need_rx_fb = (role != GpioRole::OutOnly && rx < 0);
208 const bool need_tx_fb = (role != GpioRole::InOnly && tx < 0);
210 if (need_rx_fb || need_tx_fb) {
212 rx = (role == GpioRole::OutOnly) ? -1 : M5.getPin(
m5::pin_name_t::port_a_pin1);
213 tx = (role == GpioRole::InOnly) ? -1 : M5.getPin(
m5::pin_name_t::port_a_pin2);
215 return {
static_cast<int8_t
>(rx),
static_cast<int8_t
>(tx), fb};
224inline UartPinPair uartPins()
226 int rx = M5.getPin(m5::pin_name_t::port_c_rxd);
227 int tx = M5.getPin(m5::pin_name_t::port_c_txd);
229 if (rx < 0 || tx < 0) {
231 rx = M5.getPin(m5::pin_name_t::port_a_pin1);
232 tx = M5.getPin(m5::pin_name_t::port_a_pin2);
234 return {
static_cast<int8_t
>(rx),
static_cast<int8_t
>(tx), fb};
241inline SpiPins spiPins()
243 return {
static_cast<int8_t
>(M5.getPin(m5::pin_name_t::sd_spi_sclk)),
244 static_cast<int8_t
>(M5.getPin(m5::pin_name_t::sd_spi_miso)),
245 static_cast<int8_t
>(M5.getPin(m5::pin_name_t::sd_spi_mosi))};
252inline HatI2CPins hatI2CPins()
254 const auto board = M5.getBoard();
256 case m5::board_t::board_M5StickC:
257 case m5::board_t::board_M5StickCPlus:
258 case m5::board_t::board_M5StickCPlus2:
259 return {0, 26,
false};
260 case m5::board_t::board_M5StickS3:
263 return {8, 0,
false};
264 case m5::board_t::board_M5StackCoreInk:
265 return {25, 26,
false};
266 case m5::board_t::board_ArduinoNessoN1:
269 return {-1, -1,
false};
277inline HatPinPair hatGPIOPins()
279 const auto board = M5.getBoard();
281 case m5::board_t::board_M5StickC:
282 case m5::board_t::board_M5StickCPlus:
283 case m5::board_t::board_M5StickCPlus2:
285 case m5::board_t::board_M5StickS3:
287 case m5::board_t::board_M5StackCoreInk:
289 case m5::board_t::board_ArduinoNessoN1:
299inline HatPinPair hatUARTPins()
301 const auto board = M5.getBoard();
303 case m5::board_t::board_M5StickCPlus:
304 case m5::board_t::board_M5StickCPlus2:
306 case m5::board_t::board_M5StickS3:
308 case m5::board_t::board_M5StackCoreInk:
310 case m5::board_t::board_ArduinoNessoN1:
323inline bool i2cWire(UnitUnified& units, Component&
unit, TwoWire& wire,
const int sda,
const int scl,
324 const uint32_t clock)
326 M5_LIB_LOGI(
"wiring: I2C(Wire) sda=%d scl=%d clock=%lu", sda, scl, (
unsigned long)clock);
328 wire.begin(sda, scl, clock);
329 return units.add(
unit, wire);
333#if defined(__M5_I2C_CLASS_H__)
335inline bool i2cClass(UnitUnified& units, Component&
unit, m5::I2C_Class& i2c)
337 M5_LIB_LOGI(
"wiring: I2C(I2C_Class)");
339 return units.add(
unit, i2c);
343#if defined(M5_HAL_HPP) && defined(ESP_PLATFORM)
345inline bool i2cSoftware(UnitUnified& units, Component&
unit,
const int sda,
const int scl)
347 M5_LIB_LOGI(
"wiring: I2C(Software/M5HAL) sda=%d scl=%d", sda, scl);
348 m5::hal::bus::I2CBusConfig cfg;
349 cfg.pin_sda = m5::hal::gpio::getPin(sda);
350 cfg.pin_scl = m5::hal::gpio::getPin(scl);
351 auto bus = m5::hal::bus::i2c::getBus(cfg);
352 return units.add(
unit, bus ? bus.value() : nullptr);
365inline bool addI2C(UnitUnified& units, Component&
unit,
const uint32_t clock = 0,
366 const NessoPort nesso = NessoPort::PortB)
371 auto cfg =
unit.component_config();
373 unit.component_config(cfg);
375 const uint32_t eff_clock =
unit.component_config().clock;
376 const auto p = i2cPins(nesso);
377 M5_LIB_LOGI(
"wiring: addI2C board=0x%02x nesso=%d backend=%d sda=%d scl=%d clock=%lu", (
int)M5.getBoard(),
378 (
int)nesso, (
int)p.backend, (
int)p.sda, (
int)p.scl, (
unsigned long)eff_clock);
381 return i2cSoftware(units,
unit, p.sda, p.scl);
383 return i2cWire(units,
unit, Wire, p.sda, p.scl, eff_clock);
385 return i2cClass(units,
unit, M5.Ex_I2C);
396inline bool addGPIO(UnitUnified& units, Component&
unit,
const GpioRole role = GpioRole::Both)
398 const auto p = gpioPins(role);
402 M5_LIB_LOGI(
"wiring: GPIO rx=%d tx=%d role=%d fallback_a=%d", (
int)p.rx, (
int)p.tx, (
int)role, (
int)p.fallback_a);
403 return units.add(
unit, p.rx, p.tx);
407inline uint32_t toArduinoSerialConfig(
const UartConfig c)
410 case UartConfig::_8N2:
412 case UartConfig::_8E1:
414 case UartConfig::_8O1:
416 case UartConfig::_7N1:
418 case UartConfig::_7E1:
420 case UartConfig::_7O1:
422 case UartConfig::Default:
423 case UartConfig::_8N1:
430inline HardwareSerial& defaultUartSerial()
432#if M5_UNIT_UNIFIED_WIRING_HP_UART_NUM > 2
434#elif M5_UNIT_UNIFIED_WIRING_HP_UART_NUM > 1
437#error "Not enough Serial"
449inline bool addUART(UnitUnified& units, Component&
unit,
const uint32_t baud = 115200,
450 const UartConfig config = UartConfig::Default)
452 HardwareSerial& serial = defaultUartSerial();
453 const uint32_t cfg = toArduinoSerialConfig(config);
454 const auto p = uartPins();
456 const auto b = M5.getBoard();
457 if (b == m5::board_t::board_M5NanoC6 || b == m5::board_t::board_M5NanoH2) {
465 M5_LIB_LOGI(
"wiring: UART rx=%d tx=%d baud=%lu config=0x%lx fallback_a=%d", (
int)p.rx, (
int)p.tx,
466 (
unsigned long)baud, (
unsigned long)cfg, (
int)p.fallback_a);
468 serial.begin(baud, cfg, p.rx, p.tx);
469 return units.add(
unit, serial);
473inline bool spiBus(UnitUnified& units, Component&
unit, SPIClass& spi,
const SPISettings& settings)
475 M5_LIB_LOGI(
"wiring: SPI");
476 return units.add(
unit, spi, settings);
486inline bool addSPI(UnitUnified& units, Component&
unit,
const uint32_t clock_hz,
const uint8_t mode = 0,
487 const uint8_t bit_order = 0)
489 SPISettings settings{clock_hz,
static_cast<uint8_t
>((bit_order == 0) ? MSBFIRST : LSBFIRST), mode};
490 const auto p = spiPins();
491 M5_LIB_LOGI(
"wiring: addSPI sclk=%d miso=%d mosi=%d clock=%lu mode=%u bit_order=%u", (
int)p.sclk, (
int)p.miso,
492 (
int)p.mosi, (
unsigned long)clock_hz, (
unsigned)mode, (
unsigned)bit_order);
494 SPI.begin(p.sclk, p.miso, p.mosi);
496 return spiBus(units,
unit, SPI, settings);
506inline bool addHatI2C(UnitUnified& units, Component&
unit,
const uint32_t clock = 0)
508 const auto p = hatI2CPins();
509 if (p.sda < 0 || p.scl < 0) {
510 M5_LIB_LOGE(
"wiring: Hat I2C unsupported board=0x%02x", (
int)M5.getBoard());
515 auto cfg =
unit.component_config();
517 unit.component_config(cfg);
519 const uint32_t eff_clock =
unit.component_config().clock;
520#if M5_UNIT_UNIFIED_WIRING_HAS_WIRE1
521 TwoWire& wire = p.useWire1 ? Wire1 : Wire;
525 M5_LIB_LOGE(
"wiring: addHatI2C NessoN1 Hat needs Wire1, but Wire1 is not available");
528 TwoWire& wire = Wire;
530 M5_LIB_LOGI(
"wiring: addHatI2C board=0x%02x sda=%d scl=%d clock=%lu wire=%s", (
int)M5.getBoard(), (
int)p.sda,
531 (
int)p.scl, (
unsigned long)eff_clock, p.useWire1 ?
"Wire1" :
"Wire");
532 return i2cWire(units,
unit, wire, p.sda, p.scl, eff_clock);
539inline bool addHatGPIO(UnitUnified& units, Component&
unit,
const GpioRole role = GpioRole::Both)
541 auto p = hatGPIOPins();
542 if (p.rx < 0 || p.tx < 0) {
543 M5_LIB_LOGE(
"wiring: Hat GPIO unsupported board=0x%02x", (
int)M5.getBoard());
546 if (role == GpioRole::OutOnly) p.rx = -1;
547 if (role == GpioRole::InOnly) p.tx = -1;
548 M5_LIB_LOGI(
"wiring: addHatGPIO board=0x%02x rx=%d tx=%d role=%d", (
int)M5.getBoard(), (
int)p.rx, (
int)p.tx,
550 return units.add(
unit, p.rx, p.tx);
559inline bool addHatUART(UnitUnified& units, Component&
unit,
const uint32_t baud = 115200,
560 const UartConfig config = UartConfig::Default)
562 HardwareSerial& serial = defaultUartSerial();
563 const uint32_t cfg = toArduinoSerialConfig(config);
564 const auto p = hatUARTPins();
565 if (p.rx < 0 || p.tx < 0) {
566 M5_LIB_LOGE(
"wiring: Hat UART unsupported board=0x%02x", (
int)M5.getBoard());
569 M5_LIB_LOGI(
"wiring: addHatUART board=0x%02x rx=%d tx=%d baud=%lu config=0x%lx", (
int)M5.getBoard(), (
int)p.rx,
570 (
int)p.tx, (
unsigned long)baud, (
unsigned long)cfg);
572 serial.begin(baud, cfg, p.rx, p.tx);
573 return units.add(
unit, serial);
579#if !defined(ARDUINO) && defined(ESP_PLATFORM)
582inline uart_port_t defaultUartPort()
584#if M5_UNIT_UNIFIED_WIRING_HP_UART_NUM > 2
586#elif M5_UNIT_UNIFIED_WIRING_HP_UART_NUM > 1
589#error "Not enough UART"
594struct IdfUartParams {
595 uart_word_length_t word_length;
596 uart_parity_t parity;
597 uart_stop_bits_t stop_bits;
599inline IdfUartParams toIdfUartParams(
const UartConfig c)
602 case UartConfig::_8N2:
603 return {UART_DATA_8_BITS, UART_PARITY_DISABLE, UART_STOP_BITS_2};
604 case UartConfig::_8E1:
605 return {UART_DATA_8_BITS, UART_PARITY_EVEN, UART_STOP_BITS_1};
606 case UartConfig::_8O1:
607 return {UART_DATA_8_BITS, UART_PARITY_ODD, UART_STOP_BITS_1};
608 case UartConfig::_7N1:
609 return {UART_DATA_7_BITS, UART_PARITY_DISABLE, UART_STOP_BITS_1};
610 case UartConfig::_7E1:
611 return {UART_DATA_7_BITS, UART_PARITY_EVEN, UART_STOP_BITS_1};
612 case UartConfig::_7O1:
613 return {UART_DATA_7_BITS, UART_PARITY_ODD, UART_STOP_BITS_1};
614 case UartConfig::Default:
615 case UartConfig::_8N1:
617 return {UART_DATA_8_BITS, UART_PARITY_DISABLE, UART_STOP_BITS_1};
623constexpr size_t kI2CBusCacheSize = 4;
624constexpr size_t kUARTPortCacheSize = 4;
625constexpr size_t kSPIHostCacheSize = 2;
626constexpr size_t kSPIDevCacheSize = 4;
628#if __has_include(<driver/i2c_master.h>)
629struct I2CCacheEntry {
631 i2c_master_bus_handle_t handle;
635struct UARTCacheEntry {
641 spi_host_device_t host;
647 spi_device_handle_t handle;
650#if __has_include(<driver/i2c_master.h>)
652inline i2c_master_bus_handle_t ensureI2CBus(
const i2c_port_t port,
const gpio_num_t sda,
const gpio_num_t scl,
655 const uint32_t key = (
static_cast<uint32_t
>(
static_cast<uint8_t
>(port)) << 24) |
656 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(sda)) << 16) |
657 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(scl)) << 8);
658 static I2CCacheEntry cache[kI2CBusCacheSize]{};
659 static size_t count{};
660 for (
size_t i = 0; i < count; ++i) {
661 if (cache[i].key == key)
return cache[i].handle;
663 if (count >= kI2CBusCacheSize) {
664 M5_LIB_LOGE(
"wiring: I2C bus cache full (max %zu)", kI2CBusCacheSize);
667 i2c_master_bus_config_t cfg{};
669 cfg.sda_io_num = sda;
670 cfg.scl_io_num = scl;
671#if SOC_LP_I2C_SUPPORTED
672 if (port == LP_I2C_NUM_0) {
673#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
675 cfg.lp_source_clk = LP_I2C_SCLK_DEFAULT;
678 M5_LIB_LOGE(
"wiring: LP I2C needs ESP-IDF >= 5.3");
684 cfg.clk_source = I2C_CLK_SRC_DEFAULT;
686 cfg.glitch_ignore_cnt = 7;
687 cfg.flags.enable_internal_pullup =
true;
688 if (i2c_new_master_bus(&cfg, &cache[count].handle) != ESP_OK) {
689 M5_LIB_LOGE(
"wiring: i2c_new_master_bus failed port=%d sda=%d scl=%d", (
int)port, (
int)sda, (
int)scl);
692 cache[count].key = key;
693 return cache[count++].handle;
697inline bool ensureI2CLegacyDriver(const i2c_port_t port, const gpio_num_t sda, const gpio_num_t scl,
698 const uint32_t clock)
700 const uint32_t key = (
static_cast<uint32_t
>(
static_cast<uint8_t
>(port)) << 24) |
701 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(sda)) << 16) |
702 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(scl)) << 8);
703 static uint32_t cache[kI2CBusCacheSize]{};
704 static size_t count{};
705 for (
size_t i = 0; i < count; ++i) {
706 if (cache[i] == key)
return true;
708 if (count >= kI2CBusCacheSize) {
709 M5_LIB_LOGE(
"wiring: I2C bus cache full (max %zu)", kI2CBusCacheSize);
716 const esp_err_t install_err = i2c_driver_install(port, I2C_MODE_MASTER, 0, 0, 0);
717 if (install_err == ESP_OK) {
720 conf.mode = I2C_MODE_MASTER;
721 conf.sda_io_num = sda;
722 conf.scl_io_num = scl;
723 conf.sda_pullup_en =
true;
724 conf.scl_pullup_en =
true;
725 conf.master.clk_speed = clock;
726 if (i2c_param_config(port, &conf) != ESP_OK) {
727 M5_LIB_LOGE(
"wiring: legacy i2c param_config failed port=%d sda=%d scl=%d", (
int)port, (
int)sda, (
int)scl);
728 i2c_driver_delete(port);
731 M5_LIB_LOGI(
"wiring: legacy i2c INSTALLED(new) port=%d sda=%d scl=%d clock=%u", (
int)port, (
int)sda, (
int)scl,
733 }
else if (install_err == ESP_FAIL) {
736 int high = 0, low = 0;
737 i2c_get_period(port, &high, &low);
738 if (!(high > 0 && low > 0)) {
739 M5_LIB_LOGE(
"wiring: legacy i2c driver install failed port=%d sda=%d scl=%d", (
int)port, (
int)sda,
743 M5_LIB_LOGI(
"wiring: legacy i2c BORROW(already) port=%d sda=%d scl=%d high=%d low=%d", (
int)port, (
int)sda,
744 (
int)scl, high, low);
746 M5_LIB_LOGE(
"wiring: legacy i2c driver install error=0x%x port=%d sda=%d scl=%d", (
int)install_err, (
int)port,
750 cache[count++] = key;
756inline uart_port_t ensureUARTPort(
const uart_port_t port,
const gpio_num_t rx,
const gpio_num_t tx,
const uint32_t baud,
757 const UartConfig config)
759 const uint32_t key = (
static_cast<uint32_t
>(
static_cast<uint8_t
>(port)) << 24) |
760 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(rx)) << 16) |
761 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(tx)) << 8);
762 static UARTCacheEntry cache[kUARTPortCacheSize]{};
763 static size_t count{};
764 for (
size_t i = 0; i < count; ++i) {
765 if (cache[i].key == key)
return cache[i].port;
767 if (count >= kUARTPortCacheSize) {
768 M5_LIB_LOGE(
"wiring: UART port cache full (max %zu)", kUARTPortCacheSize);
771 if (uart_is_driver_installed(port)) {
772 cache[count] = {key, port};
773 return cache[count++].port;
775 const auto p = toIdfUartParams(config);
777 uc.baud_rate =
static_cast<int>(baud);
778 uc.data_bits = p.word_length;
779 uc.parity = p.parity;
780 uc.stop_bits = p.stop_bits;
781 uc.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
782 uc.source_clk = UART_SCLK_DEFAULT;
783 if (uart_param_config(port, &uc) != ESP_OK ||
784 uart_set_pin(port, tx, rx, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE) != ESP_OK ||
785 uart_driver_install(port, 256, 0, 0,
nullptr, 0) != ESP_OK) {
786 M5_LIB_LOGE(
"wiring: uart setup failed port=%d", (
int)port);
789 cache[count] = {key, port};
790 return cache[count++].port;
795inline spi_device_handle_t ensureSPIDevice(
const spi_host_device_t host,
const gpio_num_t mosi,
const gpio_num_t miso,
796 const gpio_num_t sck,
const uint32_t clock_hz,
const uint8_t mode,
797 const uint8_t bit_order)
799 static SPIHostEntry host_cache[kSPIHostCacheSize]{};
800 static SPIDevEntry dev_cache[kSPIDevCacheSize]{};
801 static size_t host_count{};
802 static size_t dev_count{};
804 bool host_init =
false;
805 for (
size_t i = 0; i < host_count; ++i) {
806 if (host_cache[i].host == host) {
807 host_init = host_cache[i].initialized;
812 spi_bus_config_t bc{};
813 bc.mosi_io_num = mosi;
814 bc.miso_io_num = miso;
815 bc.sclk_io_num = sck;
816 bc.quadwp_io_num = -1;
817 bc.quadhd_io_num = -1;
818 bc.max_transfer_sz = 4096;
819 if (spi_bus_initialize(host, &bc, SPI_DMA_CH_AUTO) != ESP_OK) {
820 M5_LIB_LOGE(
"wiring: spi_bus_initialize failed host=%d", (
int)host);
823 if (host_count < kSPIHostCacheSize) host_cache[host_count++] = {host,
true};
826 const uint32_t key = (
static_cast<uint32_t
>(
static_cast<uint8_t
>(host)) << 24) |
827 (
static_cast<uint32_t
>(clock_hz / 1000U) << 8) | (
static_cast<uint32_t
>(mode) << 4) |
828 static_cast<uint32_t
>(bit_order);
829 for (
size_t i = 0; i < dev_count; ++i) {
830 if (dev_cache[i].key == key)
return dev_cache[i].handle;
832 if (dev_count >= kSPIDevCacheSize) {
833 M5_LIB_LOGE(
"wiring: SPI device cache full (max %zu)", kSPIDevCacheSize);
836 spi_device_interface_config_t dc{};
837 dc.clock_speed_hz =
static_cast<int>(clock_hz);
839 dc.spics_io_num = GPIO_NUM_NC;
841 if (bit_order == 1) dc.flags |= SPI_DEVICE_BIT_LSBFIRST;
842 if (spi_bus_add_device(host, &dc, &dev_cache[dev_count].handle) != ESP_OK) {
843 M5_LIB_LOGE(
"wiring: spi_bus_add_device failed host=%d", (
int)host);
846 dev_cache[dev_count].key = key;
847 return dev_cache[dev_count++].handle;
852#if __has_include(<driver/i2c_master.h>)
858inline i2c_master_bus_handle_t i2cBusHandle(
const i2c_port_t port,
const gpio_num_t sda,
const gpio_num_t scl,
859 const uint32_t clock = 100000)
861 return detail::ensureI2CBus(port, sda, scl, clock);
869inline uart_port_t uartPortHandle(
const uart_port_t port,
const gpio_num_t rx,
const gpio_num_t tx,
const uint32_t baud,
870 const UartConfig config = UartConfig::Default)
872 return detail::ensureUARTPort(port, rx, tx, baud, config);
880inline spi_device_handle_t spiDeviceHandle(
const spi_host_device_t host,
const gpio_num_t mosi,
const gpio_num_t miso,
881 const gpio_num_t sck,
const uint32_t clock_hz,
const uint8_t mode = 0,
882 const uint8_t bit_order = 0)
884 return detail::ensureSPIDevice(host, mosi, miso, sck, clock_hz, mode, bit_order);
891#if defined(__M5UNIFIED_HPP__)
900inline bool addI2C(UnitUnified& units, Component&
unit,
const uint32_t clock = 0,
901 const NessoPort nesso = NessoPort::PortB)
906 auto cfg =
unit.component_config();
908 unit.component_config(cfg);
910 const uint32_t eff_clock =
unit.component_config().clock;
911 const auto pins = i2cPins(nesso);
912 const auto board = M5.getBoard();
913 M5_LIB_LOGI(
"wiring(ESP-IDF): addI2C board=0x%02x nesso=%d sda=%d scl=%d clock=%u backend=%d", (
int)board,
914 (
int)nesso, (
int)pins.sda, (
int)pins.scl, (
unsigned)eff_clock, (
int)pins.backend);
915 switch (pins.backend) {
917#if defined(M5_HAL_HPP)
918 return i2cSoftware(units,
unit, pins.sda, pins.scl);
920 M5_LIB_LOGE(
"wiring: SoftwareI2C backend requires M5HAL");
924 if (board == m5::board_t::board_ArduinoNessoN1) {
927 return i2cClass(units,
unit, M5.In_I2C);
931#if __has_include(<driver/i2c_master.h>)
932 auto bus = i2cBusHandle(I2C_NUM_0, (gpio_num_t)pins.sda, (gpio_num_t)pins.scl, eff_clock);
933 if (!bus)
return false;
934 return units.add(
unit, bus, I2C_NUM_0, (gpio_num_t)pins.sda, (gpio_num_t)pins.scl);
936 if (!detail::ensureI2CLegacyDriver(I2C_NUM_0, (gpio_num_t)pins.sda, (gpio_num_t)pins.scl, eff_clock)) {
939 return units.add(
unit, I2C_NUM_0, (gpio_num_t)pins.sda, (gpio_num_t)pins.scl);
944 return i2cClass(units,
unit, M5.Ex_I2C);
950inline bool addGPIO(UnitUnified& units, Component&
unit,
const GpioRole role = GpioRole::Both)
952 auto p = gpioPins(role);
953 if (role == GpioRole::OutOnly) p.rx = -1;
954 if (role == GpioRole::InOnly) p.tx = -1;
955 M5_LIB_LOGI(
"wiring(ESP-IDF): GPIO rx=%d tx=%d role=%d fallback_a=%d", (
int)p.rx, (
int)p.tx, (
int)role,
957 return units.add(
unit, p.rx, p.tx);
961inline bool addUART(UnitUnified& units, Component&
unit,
const uint32_t baud = 115200,
962 const UartConfig config = UartConfig::Default)
964 const auto pins = uartPins();
965 M5_LIB_LOGI(
"wiring(ESP-IDF): UART rx=%d tx=%d baud=%u fallback_a=%d", (
int)pins.rx, (
int)pins.tx, (
unsigned)baud,
966 (
int)pins.fallback_a);
967 auto port = uartPortHandle(defaultUartPort(), (gpio_num_t)pins.rx, (gpio_num_t)pins.tx, baud, config);
968 if (port == UART_NUM_MAX)
return false;
969 return units.add(
unit, port);
973inline bool addSPI(UnitUnified& units, Component&
unit,
const uint32_t clock_hz,
const uint8_t mode = 0,
974 const uint8_t bit_order = 0)
976 const auto pins = spiPins();
977 M5_LIB_LOGI(
"wiring(ESP-IDF): addSPI sclk=%d miso=%d mosi=%d clock=%u mode=%u bit_order=%u", (
int)pins.sclk,
978 (
int)pins.miso, (
int)pins.mosi, (
unsigned)clock_hz, (
unsigned)mode, (
unsigned)bit_order);
979 auto dev = spiDeviceHandle(SPI2_HOST, (gpio_num_t)pins.mosi, (gpio_num_t)pins.miso, (gpio_num_t)pins.sclk, clock_hz,
981 if (!dev)
return false;
982 return units.add(
unit, dev);
986inline bool addHatI2C(UnitUnified& units, Component&
unit,
const uint32_t clock = 0)
988 const auto p = hatI2CPins();
989 if (p.sda < 0 || p.scl < 0) {
990 M5_LIB_LOGE(
"wiring: Hat I2C unsupported board=0x%02x", (
int)M5.getBoard());
995 auto cfg =
unit.component_config();
997 unit.component_config(cfg);
999 const uint32_t eff_clock =
unit.component_config().clock;
1002#if M5_UNIT_UNIFIED_WIRING_HP_I2C_NUM >= 2
1004#elif SOC_LP_I2C_SUPPORTED
1005 port = LP_I2C_NUM_0;
1007 M5_LIB_LOGE(
"wiring: addHatI2C needs a 2nd I2C port, none available board=0x%02x", (
int)M5.getBoard());
1013 M5_LIB_LOGI(
"wiring(ESP-IDF): addHatI2C port=%d sda=%d scl=%d clock=%u", (
int)port, (
int)p.sda, (
int)p.scl,
1014 (
unsigned)eff_clock);
1015#if __has_include(<driver/i2c_master.h>)
1016 auto bus = i2cBusHandle(port, (gpio_num_t)p.sda, (gpio_num_t)p.scl, eff_clock);
1017 if (!bus)
return false;
1018 return units.add(
unit, bus, port, (gpio_num_t)p.sda, (gpio_num_t)p.scl);
1020 if (!detail::ensureI2CLegacyDriver(port, (gpio_num_t)p.sda, (gpio_num_t)p.scl, eff_clock))
return false;
1021 return units.add(
unit, port, (gpio_num_t)p.sda, (gpio_num_t)p.scl);
1026inline bool addHatGPIO(UnitUnified& units, Component&
unit,
const GpioRole role = GpioRole::Both)
1028 auto p = hatGPIOPins();
1029 if (p.rx < 0 || p.tx < 0) {
1030 M5_LIB_LOGE(
"wiring: Hat GPIO unsupported board=0x%02x", (
int)M5.getBoard());
1033 if (role == GpioRole::OutOnly) p.rx = -1;
1034 if (role == GpioRole::InOnly) p.tx = -1;
1035 M5_LIB_LOGI(
"wiring(ESP-IDF): addHatGPIO board=0x%02x rx=%d tx=%d role=%d", (
int)M5.getBoard(), (
int)p.rx,
1036 (
int)p.tx, (
int)role);
1037 return units.add(
unit, p.rx, p.tx);
1041inline bool addHatUART(UnitUnified& units, Component&
unit,
const uint32_t baud = 115200,
1042 const UartConfig config = UartConfig::Default)
1044 const auto p = hatUARTPins();
1045 if (p.rx < 0 || p.tx < 0) {
1046 M5_LIB_LOGE(
"wiring: Hat UART unsupported board=0x%02x", (
int)M5.getBoard());
1049 M5_LIB_LOGI(
"wiring(ESP-IDF): addHatUART board=0x%02x rx=%d tx=%d baud=%u", (
int)M5.getBoard(), (
int)p.rx,
1050 (
int)p.tx, (
unsigned)baud);
1051 auto port = uartPortHandle(defaultUartPort(), (gpio_num_t)p.rx, (gpio_num_t)p.tx, baud, config);
1052 if (port == UART_NUM_MAX)
return false;
1053 return units.add(
unit, port);
1062#if defined(__M5UNIFIED_HPP__)
1064[[noreturn]]
inline void failStop()
1066 M5.Display.fillScreen(0xF800 );
1068 m5::utility::delay(10000);
Main header of M5UnitUnified.
UartConfig
UART config (word length / parity / stop bits). Common across Arduino and ESP-IDF native.
Definition m5_unit_unified_wiring.hpp:96
GpioRole
GPIO usage role: how many pins of the port the unit actually uses.
Definition m5_unit_unified_wiring.hpp:89
@ Both
Both input and output pins (e.g., IR Tx+Rx)
@ OutOnly
Output only — rx = -1, tx = port_*_out (pin2) (e.g., Flashlight, RF433 Tx, IR Tx alone)
@ InOnly
Input only — rx = port_*_in (pin1), tx = -1 (e.g., RF433 Rx, IR Rx alone)
NessoPort
NessoN1 connection choice: PortB = direct GROVE (SoftwareI2C), PortA = QWIIC / PbHub (Wire)
Definition m5_unit_unified_wiring.hpp:86
Top level namespace of M5Stack.
Definition test_helper.hpp:20
GPIO rx/tx pin pair; fallback_a indicates port_a fallback was used.
Definition m5_unit_unified_wiring.hpp:122
bool fallback_a
True when PortB unavailable and PortA was chosen.
Definition m5_unit_unified_wiring.hpp:125
Hat I2C pin pair + bus selection; sda=scl=-1 if board has no Hat header.
Definition m5_unit_unified_wiring.hpp:143
bool useWire1
True if the Hat uses Wire1 (NessoN1); else use Wire.
Definition m5_unit_unified_wiring.hpp:146
Hat rx/tx pin pair (shared by Hat GPIO and Hat UART); rx=tx=-1 if board has no Hat header.
Definition m5_unit_unified_wiring.hpp:150
I2C backend selection + pin numbers (pure getter result, no side effects)
Definition m5_unit_unified_wiring.hpp:111
Backend
Definition m5_unit_unified_wiring.hpp:114
@ Wire
Arduino TwoWire on (sda, scl)
@ ExI2C
M5.Ex_I2C (pin numbers are for reference; M5Unified manages them)
@ SoftwareI2C
M5HAL SoftwareI2C on (sda, scl) — NessoN1 PortB.
SPI pin set (sclk / miso / mosi) on the board's shared SD/SPI bus.
Definition m5_unit_unified_wiring.hpp:136
UART rx/tx pin pair; fallback_a indicates port_a fallback was used.
Definition m5_unit_unified_wiring.hpp:129
bool fallback_a
True when PortC unavailable and PortA was chosen.
Definition m5_unit_unified_wiring.hpp:132