32#ifndef M5_UNIT_UNIFIED_WIRING_HPP
33#define M5_UNIT_UNIFIED_WIRING_HPP
39#include <HardwareSerial.h>
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>
134#if defined(__M5UNIFIED_HPP__)
143inline I2CPins i2cPins(const NessoPort nesso = NessoPort::PortB)
145 const auto board = M5.getBoard();
146 if (board == m5::board_t::board_ArduinoNessoN1) {
147 if (nesso == NessoPort::PortB) {
148 return {
static_cast<int8_t
>(M5.getPin(m5::pin_name_t::port_b_out)),
151 return {
static_cast<int8_t
>(M5.getPin(m5::pin_name_t::port_a_sda)),
154 if (board == m5::board_t::board_M5NanoC6 || board == m5::board_t::board_M5NanoH2) {
155 return {
static_cast<int8_t
>(M5.getPin(m5::pin_name_t::ex_i2c_sda)),
158 if (board == m5::board_t::board_M5Stack) {
162 return {
static_cast<int8_t
>(M5.getPin(m5::pin_name_t::ex_i2c_sda)),
165 return {
static_cast<int8_t
>(M5.getPin(m5::pin_name_t::port_a_sda)),
176inline GpioPinPair gpioPins(
const GpioRole role = GpioRole::Both)
178 int rx = (role == GpioRole::OutOnly) ? -1 : M5.getPin(
m5::pin_name_t::port_b_in);
179 int tx = (role == GpioRole::InOnly) ? -1 : M5.getPin(
m5::pin_name_t::port_b_out);
180 const bool need_rx_fb = (role != GpioRole::OutOnly && rx < 0);
181 const bool need_tx_fb = (role != GpioRole::InOnly && tx < 0);
183 if (need_rx_fb || need_tx_fb) {
185 rx = (role == GpioRole::OutOnly) ? -1 : M5.getPin(
m5::pin_name_t::port_a_pin1);
186 tx = (role == GpioRole::InOnly) ? -1 : M5.getPin(
m5::pin_name_t::port_a_pin2);
188 return {
static_cast<int8_t
>(rx),
static_cast<int8_t
>(tx), fb};
197inline UartPinPair uartPins()
199 int rx = M5.getPin(m5::pin_name_t::port_c_rxd);
200 int tx = M5.getPin(m5::pin_name_t::port_c_txd);
202 if (rx < 0 || tx < 0) {
204 rx = M5.getPin(m5::pin_name_t::port_a_pin1);
205 tx = M5.getPin(m5::pin_name_t::port_a_pin2);
207 return {
static_cast<int8_t
>(rx),
static_cast<int8_t
>(tx), fb};
214inline SpiPins spiPins()
216 return {
static_cast<int8_t
>(M5.getPin(m5::pin_name_t::sd_spi_sclk)),
217 static_cast<int8_t
>(M5.getPin(m5::pin_name_t::sd_spi_miso)),
218 static_cast<int8_t
>(M5.getPin(m5::pin_name_t::sd_spi_mosi))};
225inline HatI2CPins hatI2CPins()
227 const auto board = M5.getBoard();
229 case m5::board_t::board_M5StickC:
230 case m5::board_t::board_M5StickCPlus:
231 case m5::board_t::board_M5StickCPlus2:
232 return {0, 26,
false};
233 case m5::board_t::board_M5StickS3:
234 return {8, 0,
false};
235 case m5::board_t::board_M5StackCoreInk:
236 return {25, 26,
false};
237 case m5::board_t::board_ArduinoNessoN1:
240 return {-1, -1,
false};
248inline HatPinPair hatGPIOPins()
250 const auto board = M5.getBoard();
252 case m5::board_t::board_M5StickC:
253 case m5::board_t::board_M5StickCPlus:
254 case m5::board_t::board_M5StickCPlus2:
256 case m5::board_t::board_M5StickS3:
258 case m5::board_t::board_M5StackCoreInk:
260 case m5::board_t::board_ArduinoNessoN1:
270inline HatPinPair hatUARTPins()
272 const auto board = M5.getBoard();
274 case m5::board_t::board_M5StickCPlus:
275 case m5::board_t::board_M5StickCPlus2:
277 case m5::board_t::board_M5StickS3:
279 case m5::board_t::board_M5StackCoreInk:
281 case m5::board_t::board_ArduinoNessoN1:
294inline bool i2cWire(UnitUnified& units, Component&
unit, TwoWire& wire,
const int sda,
const int scl,
295 const uint32_t clock)
297 M5_LIB_LOGI(
"wiring: I2C(Wire) sda=%d scl=%d clock=%lu", sda, scl, (
unsigned long)clock);
299 wire.begin(sda, scl, clock);
300 return units.add(
unit, wire);
304#if defined(__M5_I2C_CLASS_H__)
306inline bool i2cClass(UnitUnified& units, Component&
unit, m5::I2C_Class& i2c)
308 M5_LIB_LOGI(
"wiring: I2C(I2C_Class)");
310 return units.add(
unit, i2c);
314#if defined(M5_HAL_HPP)
316inline bool i2cSoftware(UnitUnified& units, Component&
unit,
const int sda,
const int scl)
318 M5_LIB_LOGI(
"wiring: I2C(Software/M5HAL) sda=%d scl=%d", sda, scl);
319 m5::hal::bus::I2CBusConfig cfg;
320 cfg.pin_sda = m5::hal::gpio::getPin(sda);
321 cfg.pin_scl = m5::hal::gpio::getPin(scl);
322 auto bus = m5::hal::bus::i2c::getBus(cfg);
323 return units.add(
unit, bus ? bus.value() : nullptr);
336inline bool addI2C(UnitUnified& units, Component&
unit,
const uint32_t clock = 0,
337 const NessoPort nesso = NessoPort::PortB)
342 auto cfg =
unit.component_config();
344 unit.component_config(cfg);
346 const uint32_t eff_clock =
unit.component_config().clock;
347 const auto p = i2cPins(nesso);
348 M5_LIB_LOGI(
"wiring: addI2C board=0x%02x nesso=%d backend=%d sda=%d scl=%d clock=%lu", (
int)M5.getBoard(),
349 (
int)nesso, (
int)p.backend, (
int)p.sda, (
int)p.scl, (
unsigned long)eff_clock);
352 return i2cSoftware(units,
unit, p.sda, p.scl);
354 return i2cWire(units,
unit, Wire, p.sda, p.scl, eff_clock);
356 return i2cClass(units,
unit, M5.Ex_I2C);
367inline bool addGPIO(UnitUnified& units, Component&
unit,
const GpioRole role = GpioRole::Both)
369 const auto p = gpioPins(role);
373 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);
374 return units.add(
unit, p.rx, p.tx);
378inline uint32_t toArduinoSerialConfig(
const UartConfig c)
381 case UartConfig::_8N2:
383 case UartConfig::_8E1:
385 case UartConfig::_8O1:
387 case UartConfig::_7N1:
389 case UartConfig::_7E1:
391 case UartConfig::_7O1:
393 case UartConfig::Default:
394 case UartConfig::_8N1:
401inline HardwareSerial& defaultUartSerial()
403#if defined(CONFIG_IDF_TARGET_ESP32C6)
405#elif SOC_UART_NUM > 2
407#elif SOC_UART_NUM > 1
410#error "Not enough Serial"
422inline bool addUART(UnitUnified& units, Component&
unit,
const uint32_t baud = 115200,
423 const UartConfig config = UartConfig::Default)
425 HardwareSerial& serial = defaultUartSerial();
426 const uint32_t cfg = toArduinoSerialConfig(config);
427 const auto p = uartPins();
429 const auto b = M5.getBoard();
430 if (b == m5::board_t::board_M5NanoC6 || b == m5::board_t::board_M5NanoH2) {
438 M5_LIB_LOGI(
"wiring: UART rx=%d tx=%d baud=%lu config=0x%lx fallback_a=%d", (
int)p.rx, (
int)p.tx,
439 (
unsigned long)baud, (
unsigned long)cfg, (
int)p.fallback_a);
441 serial.begin(baud, cfg, p.rx, p.tx);
442 return units.add(
unit, serial);
446inline bool spiBus(UnitUnified& units, Component&
unit, SPIClass& spi,
const SPISettings& settings)
448 M5_LIB_LOGI(
"wiring: SPI");
449 return units.add(
unit, spi, settings);
459inline bool addSPI(UnitUnified& units, Component&
unit,
const uint32_t clock_hz,
const uint8_t mode = 0,
460 const uint8_t bit_order = 0)
462 SPISettings settings{clock_hz,
static_cast<uint8_t
>((bit_order == 0) ? MSBFIRST : LSBFIRST), mode};
463 const auto p = spiPins();
464 M5_LIB_LOGI(
"wiring: addSPI sclk=%d miso=%d mosi=%d clock=%lu mode=%u bit_order=%u", (
int)p.sclk, (
int)p.miso,
465 (
int)p.mosi, (
unsigned long)clock_hz, (
unsigned)mode, (
unsigned)bit_order);
467 SPI.begin(p.sclk, p.miso, p.mosi);
469 return spiBus(units,
unit, SPI, settings);
479inline bool addHatI2C(UnitUnified& units, Component&
unit,
const uint32_t clock = 0)
481 const auto p = hatI2CPins();
482 if (p.sda < 0 || p.scl < 0) {
483 M5_LIB_LOGE(
"wiring: Hat I2C unsupported board=0x%02x", (
int)M5.getBoard());
488 auto cfg =
unit.component_config();
490 unit.component_config(cfg);
492 const uint32_t eff_clock =
unit.component_config().clock;
494 TwoWire& wire = p.useWire1 ? Wire1 : Wire;
498 M5_LIB_LOGE(
"wiring: addHatI2C NessoN1 Hat needs Wire1, but SOC_I2C_NUM==1");
501 TwoWire& wire = Wire;
503 M5_LIB_LOGI(
"wiring: addHatI2C board=0x%02x sda=%d scl=%d clock=%lu wire=%s", (
int)M5.getBoard(), (
int)p.sda,
504 (
int)p.scl, (
unsigned long)eff_clock, p.useWire1 ?
"Wire1" :
"Wire");
505 return i2cWire(units,
unit, wire, p.sda, p.scl, eff_clock);
512inline bool addHatGPIO(UnitUnified& units, Component&
unit,
const GpioRole role = GpioRole::Both)
514 auto p = hatGPIOPins();
515 if (p.rx < 0 || p.tx < 0) {
516 M5_LIB_LOGE(
"wiring: Hat GPIO unsupported board=0x%02x", (
int)M5.getBoard());
519 if (role == GpioRole::OutOnly) p.rx = -1;
520 if (role == GpioRole::InOnly) p.tx = -1;
521 M5_LIB_LOGI(
"wiring: addHatGPIO board=0x%02x rx=%d tx=%d role=%d", (
int)M5.getBoard(), (
int)p.rx, (
int)p.tx,
523 return units.add(
unit, p.rx, p.tx);
532inline bool addHatUART(UnitUnified& units, Component&
unit,
const uint32_t baud = 115200,
533 const UartConfig config = UartConfig::Default)
535 HardwareSerial& serial = defaultUartSerial();
536 const uint32_t cfg = toArduinoSerialConfig(config);
537 const auto p = hatUARTPins();
538 if (p.rx < 0 || p.tx < 0) {
539 M5_LIB_LOGE(
"wiring: Hat UART unsupported board=0x%02x", (
int)M5.getBoard());
542 M5_LIB_LOGI(
"wiring: addHatUART board=0x%02x rx=%d tx=%d baud=%lu config=0x%lx", (
int)M5.getBoard(), (
int)p.rx,
543 (
int)p.tx, (
unsigned long)baud, (
unsigned long)cfg);
545 serial.begin(baud, cfg, p.rx, p.tx);
546 return units.add(
unit, serial);
557#if defined(CONFIG_IDF_TARGET_ESP32C6)
559#elif SOC_UART_NUM > 2
561#elif SOC_UART_NUM > 1
564#error "Not enough UART"
570 uart_word_length_t word_length;
571 uart_parity_t parity;
572 uart_stop_bits_t stop_bits;
577 case UartConfig::_8N2:
578 return {UART_DATA_8_BITS, UART_PARITY_DISABLE, UART_STOP_BITS_2};
579 case UartConfig::_8E1:
580 return {UART_DATA_8_BITS, UART_PARITY_EVEN, UART_STOP_BITS_1};
581 case UartConfig::_8O1:
582 return {UART_DATA_8_BITS, UART_PARITY_ODD, UART_STOP_BITS_1};
583 case UartConfig::_7N1:
584 return {UART_DATA_7_BITS, UART_PARITY_DISABLE, UART_STOP_BITS_1};
585 case UartConfig::_7E1:
586 return {UART_DATA_7_BITS, UART_PARITY_EVEN, UART_STOP_BITS_1};
587 case UartConfig::_7O1:
588 return {UART_DATA_7_BITS, UART_PARITY_ODD, UART_STOP_BITS_1};
589 case UartConfig::Default:
590 case UartConfig::_8N1:
592 return {UART_DATA_8_BITS, UART_PARITY_DISABLE, UART_STOP_BITS_1};
598constexpr size_t kI2CBusCacheSize = 4;
599constexpr size_t kUARTPortCacheSize = 4;
600constexpr size_t kSPIHostCacheSize = 2;
601constexpr size_t kSPIDevCacheSize = 4;
603#if __has_include(<driver/i2c_master.h>)
604struct I2CCacheEntry {
606 i2c_master_bus_handle_t handle;
616 spi_host_device_t host;
622 spi_device_handle_t handle;
625#if __has_include(<driver/i2c_master.h>)
627inline i2c_master_bus_handle_t ensureI2CBus(
const i2c_port_t port,
const gpio_num_t sda,
const gpio_num_t scl,
630 const uint32_t key = (
static_cast<uint32_t
>(
static_cast<uint8_t
>(port)) << 24) |
631 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(sda)) << 16) |
632 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(scl)) << 8);
633 static I2CCacheEntry cache[kI2CBusCacheSize]{};
634 static size_t count{};
635 for (
size_t i = 0; i < count; ++i) {
636 if (cache[i].key == key)
return cache[i].handle;
638 if (count >= kI2CBusCacheSize) {
639 M5_LIB_LOGE(
"wiring: I2C bus cache full (max %zu)", kI2CBusCacheSize);
642 i2c_master_bus_config_t cfg{};
644 cfg.sda_io_num = sda;
645 cfg.scl_io_num = scl;
646#if SOC_LP_I2C_SUPPORTED
647 if (port == LP_I2C_NUM_0) {
649 cfg.lp_source_clk = LP_I2C_SCLK_DEFAULT;
653 cfg.clk_source = I2C_CLK_SRC_DEFAULT;
655 cfg.glitch_ignore_cnt = 7;
656 cfg.flags.enable_internal_pullup =
true;
657 if (i2c_new_master_bus(&cfg, &cache[count].handle) != ESP_OK) {
658 M5_LIB_LOGE(
"wiring: i2c_new_master_bus failed port=%d sda=%d scl=%d", (
int)port, (
int)sda, (
int)scl);
661 cache[count].key = key;
662 return cache[count++].handle;
666inline bool ensureI2CLegacyDriver(const i2c_port_t port, const gpio_num_t sda, const gpio_num_t scl,
667 const uint32_t clock)
669 const uint32_t key = (
static_cast<uint32_t
>(
static_cast<uint8_t
>(port)) << 24) |
670 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(sda)) << 16) |
671 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(scl)) << 8);
672 static uint32_t cache[kI2CBusCacheSize]{};
673 static size_t count{};
674 for (
size_t i = 0; i < count; ++i) {
675 if (cache[i] == key)
return true;
677 if (count >= kI2CBusCacheSize) {
678 M5_LIB_LOGE(
"wiring: I2C bus cache full (max %zu)", kI2CBusCacheSize);
685 const esp_err_t install_err = i2c_driver_install(port, I2C_MODE_MASTER, 0, 0, 0);
686 if (install_err == ESP_OK) {
689 conf.mode = I2C_MODE_MASTER;
690 conf.sda_io_num = sda;
691 conf.scl_io_num = scl;
692 conf.sda_pullup_en =
true;
693 conf.scl_pullup_en =
true;
694 conf.master.clk_speed = clock;
695 if (i2c_param_config(port, &conf) != ESP_OK) {
696 M5_LIB_LOGE(
"wiring: legacy i2c param_config failed port=%d sda=%d scl=%d", (
int)port, (
int)sda, (
int)scl);
697 i2c_driver_delete(port);
700 M5_LIB_LOGI(
"wiring: legacy i2c INSTALLED(new) port=%d sda=%d scl=%d clock=%u", (
int)port, (
int)sda, (
int)scl,
702 }
else if (install_err == ESP_FAIL) {
705 int high = 0, low = 0;
706 i2c_get_period(port, &high, &low);
707 if (!(high > 0 && low > 0)) {
708 M5_LIB_LOGE(
"wiring: legacy i2c driver install failed port=%d sda=%d scl=%d", (
int)port, (
int)sda,
712 M5_LIB_LOGI(
"wiring: legacy i2c BORROW(already) port=%d sda=%d scl=%d high=%d low=%d", (
int)port, (
int)sda,
713 (
int)scl, high, low);
715 M5_LIB_LOGE(
"wiring: legacy i2c driver install error=0x%x port=%d sda=%d scl=%d", (
int)install_err, (
int)port,
719 cache[count++] = key;
725inline uart_port_t
ensureUARTPort(
const uart_port_t port,
const gpio_num_t rx,
const gpio_num_t tx,
const uint32_t baud,
728 const uint32_t key = (
static_cast<uint32_t
>(
static_cast<uint8_t
>(port)) << 24) |
729 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(rx)) << 16) |
730 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(tx)) << 8);
732 static size_t count{};
733 for (
size_t i = 0; i < count; ++i) {
734 if (cache[i].key == key)
return cache[i].port;
736 if (count >= kUARTPortCacheSize) {
737 M5_LIB_LOGE(
"wiring: UART port cache full (max %zu)", kUARTPortCacheSize);
740 if (uart_is_driver_installed(port)) {
741 cache[count] = {key, port};
742 return cache[count++].port;
746 uc.baud_rate =
static_cast<int>(baud);
747 uc.data_bits = p.word_length;
748 uc.parity = p.parity;
749 uc.stop_bits = p.stop_bits;
750 uc.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
751 uc.source_clk = UART_SCLK_DEFAULT;
752 if (uart_param_config(port, &uc) != ESP_OK ||
753 uart_set_pin(port, tx, rx, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE) != ESP_OK ||
754 uart_driver_install(port, 256, 0, 0,
nullptr, 0) != ESP_OK) {
755 M5_LIB_LOGE(
"wiring: uart setup failed port=%d", (
int)port);
758 cache[count] = {key, port};
759 return cache[count++].port;
764inline spi_device_handle_t
ensureSPIDevice(
const spi_host_device_t host,
const gpio_num_t mosi,
const gpio_num_t miso,
765 const gpio_num_t sck,
const uint32_t clock_hz,
const uint8_t mode,
766 const uint8_t bit_order)
770 static size_t host_count{};
771 static size_t dev_count{};
773 bool host_init =
false;
774 for (
size_t i = 0; i < host_count; ++i) {
775 if (host_cache[i].host == host) {
776 host_init = host_cache[i].initialized;
781 spi_bus_config_t bc{};
782 bc.mosi_io_num = mosi;
783 bc.miso_io_num = miso;
784 bc.sclk_io_num = sck;
785 bc.quadwp_io_num = -1;
786 bc.quadhd_io_num = -1;
787 bc.max_transfer_sz = 4096;
788 if (spi_bus_initialize(host, &bc, SPI_DMA_CH_AUTO) != ESP_OK) {
789 M5_LIB_LOGE(
"wiring: spi_bus_initialize failed host=%d", (
int)host);
792 if (host_count < kSPIHostCacheSize) host_cache[host_count++] = {host,
true};
795 const uint32_t key = (
static_cast<uint32_t
>(
static_cast<uint8_t
>(host)) << 24) |
796 (
static_cast<uint32_t
>(clock_hz / 1000U) << 8) | (
static_cast<uint32_t
>(mode) << 4) |
797 static_cast<uint32_t
>(bit_order);
798 for (
size_t i = 0; i < dev_count; ++i) {
799 if (dev_cache[i].key == key)
return dev_cache[i].handle;
801 if (dev_count >= kSPIDevCacheSize) {
802 M5_LIB_LOGE(
"wiring: SPI device cache full (max %zu)", kSPIDevCacheSize);
805 spi_device_interface_config_t dc{};
806 dc.clock_speed_hz =
static_cast<int>(clock_hz);
808 dc.spics_io_num = GPIO_NUM_NC;
810 if (bit_order == 1) dc.flags |= SPI_DEVICE_BIT_LSBFIRST;
811 if (spi_bus_add_device(host, &dc, &dev_cache[dev_count].handle) != ESP_OK) {
812 M5_LIB_LOGE(
"wiring: spi_bus_add_device failed host=%d", (
int)host);
815 dev_cache[dev_count].key = key;
816 return dev_cache[dev_count++].handle;
821#if __has_include(<driver/i2c_master.h>)
827inline i2c_master_bus_handle_t i2cBusHandle(
const i2c_port_t port,
const gpio_num_t sda,
const gpio_num_t scl,
828 const uint32_t clock = 100000)
830 return detail::ensureI2CBus(port, sda, scl, clock);
838inline uart_port_t
uartPortHandle(
const uart_port_t port,
const gpio_num_t rx,
const gpio_num_t tx,
const uint32_t baud,
839 const UartConfig config = UartConfig::Default)
841 return detail::ensureUARTPort(port, rx, tx, baud, config);
849inline spi_device_handle_t
spiDeviceHandle(
const spi_host_device_t host,
const gpio_num_t mosi,
const gpio_num_t miso,
850 const gpio_num_t sck,
const uint32_t clock_hz,
const uint8_t mode = 0,
851 const uint8_t bit_order = 0)
853 return detail::ensureSPIDevice(host, mosi, miso, sck, clock_hz, mode, bit_order);
860#if defined(__M5UNIFIED_HPP__)
870 const NessoPort nesso = NessoPort::PortB)
875 auto cfg =
unit.component_config();
877 unit.component_config(cfg);
879 const uint32_t eff_clock =
unit.component_config().clock;
880 const auto pins = i2cPins(nesso);
881 const auto board = M5.getBoard();
882 M5_LIB_LOGI(
"wiring(ESP-IDF): addI2C board=0x%02x nesso=%d sda=%d scl=%d clock=%u backend=%d", (
int)board,
883 (
int)nesso, (
int)pins.sda, (
int)pins.scl, (
unsigned)eff_clock, (
int)pins.backend);
884 switch (pins.backend) {
886#if defined(M5_HAL_HPP)
887 return i2cSoftware(units,
unit, pins.sda, pins.scl);
889 M5_LIB_LOGE(
"wiring: SoftwareI2C backend requires M5HAL");
893 if (board == m5::board_t::board_ArduinoNessoN1) {
896 return i2cClass(units,
unit, M5.In_I2C);
900#if __has_include(<driver/i2c_master.h>)
901 auto bus = i2cBusHandle(I2C_NUM_0, (gpio_num_t)pins.sda, (gpio_num_t)pins.scl, eff_clock);
902 if (!bus)
return false;
905 if (!detail::ensureI2CLegacyDriver(I2C_NUM_0, (gpio_num_t)pins.sda, (gpio_num_t)pins.scl, eff_clock)) {
908 return units.
add(
unit, I2C_NUM_0, (gpio_num_t)pins.sda, (gpio_num_t)pins.scl);
913 return i2cClass(units,
unit, M5.Ex_I2C);
919inline bool addGPIO(UnitUnified& units, Component&
unit,
const GpioRole role = GpioRole::Both)
921 auto p = gpioPins(role);
922 if (role == GpioRole::OutOnly) p.rx = -1;
923 if (role == GpioRole::InOnly) p.tx = -1;
924 M5_LIB_LOGI(
"wiring(ESP-IDF): GPIO rx=%d tx=%d role=%d fallback_a=%d", (
int)p.rx, (
int)p.tx, (
int)role,
926 return units.add(
unit, p.rx, p.tx);
930inline bool addUART(UnitUnified& units, Component&
unit,
const uint32_t baud = 115200,
931 const UartConfig config = UartConfig::Default)
933 const auto pins = uartPins();
934 M5_LIB_LOGI(
"wiring(ESP-IDF): UART rx=%d tx=%d baud=%u fallback_a=%d", (
int)pins.rx, (
int)pins.tx, (
unsigned)baud,
935 (
int)pins.fallback_a);
937 if (port == UART_NUM_MAX)
return false;
938 return units.add(
unit, port);
942inline bool addSPI(UnitUnified& units, Component&
unit,
const uint32_t clock_hz,
const uint8_t mode = 0,
943 const uint8_t bit_order = 0)
945 const auto pins = spiPins();
946 M5_LIB_LOGI(
"wiring(ESP-IDF): addSPI sclk=%d miso=%d mosi=%d clock=%u mode=%u bit_order=%u", (
int)pins.sclk,
947 (
int)pins.miso, (
int)pins.mosi, (
unsigned)clock_hz, (
unsigned)mode, (
unsigned)bit_order);
948 auto dev =
spiDeviceHandle(SPI2_HOST, (gpio_num_t)pins.mosi, (gpio_num_t)pins.miso, (gpio_num_t)pins.sclk, clock_hz,
950 if (!dev)
return false;
951 return units.add(
unit, dev);
955inline bool addHatI2C(UnitUnified& units, Component&
unit,
const uint32_t clock = 0)
957 const auto p = hatI2CPins();
958 if (p.sda < 0 || p.scl < 0) {
959 M5_LIB_LOGE(
"wiring: Hat I2C unsupported board=0x%02x", (
int)M5.getBoard());
964 auto cfg =
unit.component_config();
966 unit.component_config(cfg);
968 const uint32_t eff_clock =
unit.component_config().clock;
971#if SOC_HP_I2C_NUM >= 2
973#elif SOC_LP_I2C_SUPPORTED
976 M5_LIB_LOGE(
"wiring: addHatI2C needs a 2nd I2C port, none available board=0x%02x", (
int)M5.getBoard());
982 M5_LIB_LOGI(
"wiring(ESP-IDF): addHatI2C port=%d sda=%d scl=%d clock=%u", (
int)port, (
int)p.sda, (
int)p.scl,
983 (
unsigned)eff_clock);
984#if __has_include(<driver/i2c_master.h>)
985 auto bus = i2cBusHandle(port, (gpio_num_t)p.sda, (gpio_num_t)p.scl, eff_clock);
986 if (!bus)
return false;
987 return units.add(
unit, bus);
989 if (!detail::ensureI2CLegacyDriver(port, (gpio_num_t)p.sda, (gpio_num_t)p.scl, eff_clock))
return false;
990 return units.add(
unit, port, (gpio_num_t)p.sda, (gpio_num_t)p.scl);
995inline bool addHatGPIO(UnitUnified& units, Component&
unit,
const GpioRole role = GpioRole::Both)
997 auto p = hatGPIOPins();
998 if (p.rx < 0 || p.tx < 0) {
999 M5_LIB_LOGE(
"wiring: Hat GPIO unsupported board=0x%02x", (
int)M5.getBoard());
1002 if (role == GpioRole::OutOnly) p.rx = -1;
1003 if (role == GpioRole::InOnly) p.tx = -1;
1004 M5_LIB_LOGI(
"wiring(ESP-IDF): addHatGPIO board=0x%02x rx=%d tx=%d role=%d", (
int)M5.getBoard(), (
int)p.rx,
1005 (
int)p.tx, (
int)role);
1006 return units.add(
unit, p.rx, p.tx);
1010inline bool addHatUART(UnitUnified& units, Component&
unit,
const uint32_t baud = 115200,
1011 const UartConfig config = UartConfig::Default)
1013 const auto p = hatUARTPins();
1014 if (p.rx < 0 || p.tx < 0) {
1015 M5_LIB_LOGE(
"wiring: Hat UART unsupported board=0x%02x", (
int)M5.getBoard());
1018 M5_LIB_LOGI(
"wiring(ESP-IDF): addHatUART board=0x%02x rx=%d tx=%d baud=%u", (
int)M5.getBoard(), (
int)p.rx,
1019 (
int)p.tx, (
unsigned)baud);
1021 if (port == UART_NUM_MAX)
return false;
1022 return units.add(
unit, port);
1031#if defined(__M5UNIFIED_HPP__)
1033[[noreturn]]
inline void failStop()
1035 M5.Display.fillScreen(0xF800 );
1037 m5::utility::delay(10000);
Main header of M5UnitUnified.
Base class of unit component.
For managing and leading units.
bool add(Component &u, TwoWire &wire)
Add unit to be managed (I2C via TwoWire)
UartConfig
UART config (word length / parity / stop bits). Common across Arduino and ESP-IDF native.
Definition m5_unit_unified_wiring.hpp:72
spi_device_handle_t spiDeviceHandle(const spi_host_device_t host, const gpio_num_t mosi, const gpio_num_t miso, const gpio_num_t sck, const uint32_t clock_hz, const uint8_t mode=0, const uint8_t bit_order=0)
Get or initialize a SPI device on the given host (cached by {host, clock_hz, mode,...
Definition m5_unit_unified_wiring.hpp:849
spi_device_handle_t ensureSPIDevice(const spi_host_device_t host, const gpio_num_t mosi, const gpio_num_t miso, const gpio_num_t sck, const uint32_t clock_hz, const uint8_t mode, const uint8_t bit_order)
Get or initialize a SPI device, cached by {host, clock_hz, mode, bit_order}.
Definition m5_unit_unified_wiring.hpp:764
GpioRole
GPIO usage role: how many pins of the port the unit actually uses.
Definition m5_unit_unified_wiring.hpp:65
@ 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:62
uart_port_t defaultUartPort()
The board's default UART port for a Port unit, chosen by SoC UART count (ESP-IDF native)
Definition m5_unit_unified_wiring.hpp:555
uart_port_t uartPortHandle(const uart_port_t port, const gpio_num_t rx, const gpio_num_t tx, const uint32_t baud, const UartConfig config=UartConfig::Default)
Get or install a UART port (cached by {port, rx, tx})
Definition m5_unit_unified_wiring.hpp:838
uart_port_t ensureUARTPort(const uart_port_t port, const gpio_num_t rx, const gpio_num_t tx, const uint32_t baud, const UartConfig config)
Get or install a UART port, cached by {port, rx, tx}.
Definition m5_unit_unified_wiring.hpp:725
IdfUartParams toIdfUartParams(const UartConfig c)
The board's default UART port for a Port unit, chosen by SoC UART count (ESP-IDF native)
Definition m5_unit_unified_wiring.hpp:574
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:98
bool fallback_a
True when PortB unavailable and PortA was chosen.
Definition m5_unit_unified_wiring.hpp:101
Hat I2C pin pair + bus selection; sda=scl=-1 if board has no Hat header.
Definition m5_unit_unified_wiring.hpp:119
bool useWire1
True if the Hat uses Wire1 (NessoN1); else use Wire.
Definition m5_unit_unified_wiring.hpp:122
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:126
I2C backend selection + pin numbers (pure getter result, no side effects)
Definition m5_unit_unified_wiring.hpp:87
Backend
Definition m5_unit_unified_wiring.hpp:90
@ 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.
Map UartConfig to ESP-IDF native uart_config_t fields.
Definition m5_unit_unified_wiring.hpp:569
SPI pin set (sclk / miso / mosi) on the board's shared SD/SPI bus.
Definition m5_unit_unified_wiring.hpp:112
UART rx/tx pin pair; fallback_a indicates port_a fallback was used.
Definition m5_unit_unified_wiring.hpp:105
bool fallback_a
True when PortC unavailable and PortA was chosen.
Definition m5_unit_unified_wiring.hpp:108
Definition m5_unit_unified_wiring.hpp:620
Definition m5_unit_unified_wiring.hpp:615
Definition m5_unit_unified_wiring.hpp:610