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"
442inline bool beginUartSerial(HardwareSerial& serial,
const uint32_t baud,
const uint32_t cfg,
const int8_t rx,
443 const int8_t tx,
const size_t rx_buffer_size)
447 if (rx_buffer_size && serial.setRxBufferSize(rx_buffer_size) != rx_buffer_size) {
448 M5_LIB_LOGE(
"wiring: UART rx_buffer_size=%u rejected", (
unsigned)rx_buffer_size);
451 serial.begin(baud, cfg, rx, tx);
465inline bool addUART(UnitUnified& units, Component&
unit,
const uint32_t baud = 115200,
466 const UartConfig config = UartConfig::Default,
const size_t rx_buffer_size = 0)
468 HardwareSerial& serial = defaultUartSerial();
469 const uint32_t cfg = toArduinoSerialConfig(config);
470 const auto p = uartPins();
472 const auto b = M5.getBoard();
473 if (b == m5::board_t::board_M5NanoC6 || b == m5::board_t::board_M5NanoH2) {
481 M5_LIB_LOGI(
"wiring: UART rx=%d tx=%d baud=%lu config=0x%lx fallback_a=%d", (
int)p.rx, (
int)p.tx,
482 (
unsigned long)baud, (
unsigned long)cfg, (
int)p.fallback_a);
483 return beginUartSerial(serial, baud, cfg, p.rx, p.tx, rx_buffer_size) && units.add(
unit, serial);
487inline bool spiBus(UnitUnified& units, Component&
unit, SPIClass& spi,
const SPISettings& settings)
489 M5_LIB_LOGI(
"wiring: SPI");
490 return units.add(
unit, spi, settings);
500inline bool addSPI(UnitUnified& units, Component&
unit,
const uint32_t clock_hz,
const uint8_t mode = 0,
501 const uint8_t bit_order = 0)
503 SPISettings settings{clock_hz,
static_cast<uint8_t
>((bit_order == 0) ? MSBFIRST : LSBFIRST), mode};
504 const auto p = spiPins();
505 M5_LIB_LOGI(
"wiring: addSPI sclk=%d miso=%d mosi=%d clock=%lu mode=%u bit_order=%u", (
int)p.sclk, (
int)p.miso,
506 (
int)p.mosi, (
unsigned long)clock_hz, (
unsigned)mode, (
unsigned)bit_order);
508 SPI.begin(p.sclk, p.miso, p.mosi);
510 return spiBus(units,
unit, SPI, settings);
520inline bool addHatI2C(UnitUnified& units, Component&
unit,
const uint32_t clock = 0)
522 const auto p = hatI2CPins();
523 if (p.sda < 0 || p.scl < 0) {
524 M5_LIB_LOGE(
"wiring: Hat I2C unsupported board=0x%02x", (
int)M5.getBoard());
529 auto cfg =
unit.component_config();
531 unit.component_config(cfg);
533 const uint32_t eff_clock =
unit.component_config().clock;
534#if M5_UNIT_UNIFIED_WIRING_HAS_WIRE1
535 TwoWire& wire = p.useWire1 ? Wire1 : Wire;
539 M5_LIB_LOGE(
"wiring: addHatI2C NessoN1 Hat needs Wire1, but Wire1 is not available");
542 TwoWire& wire = Wire;
544 M5_LIB_LOGI(
"wiring: addHatI2C board=0x%02x sda=%d scl=%d clock=%lu wire=%s", (
int)M5.getBoard(), (
int)p.sda,
545 (
int)p.scl, (
unsigned long)eff_clock, p.useWire1 ?
"Wire1" :
"Wire");
546 return i2cWire(units,
unit, wire, p.sda, p.scl, eff_clock);
553inline bool addHatGPIO(UnitUnified& units, Component&
unit,
const GpioRole role = GpioRole::Both)
555 auto p = hatGPIOPins();
556 if (p.rx < 0 || p.tx < 0) {
557 M5_LIB_LOGE(
"wiring: Hat GPIO unsupported board=0x%02x", (
int)M5.getBoard());
560 if (role == GpioRole::OutOnly) p.rx = -1;
561 if (role == GpioRole::InOnly) p.tx = -1;
562 M5_LIB_LOGI(
"wiring: addHatGPIO board=0x%02x rx=%d tx=%d role=%d", (
int)M5.getBoard(), (
int)p.rx, (
int)p.tx,
564 return units.add(
unit, p.rx, p.tx);
574inline bool addHatUART(UnitUnified& units, Component&
unit,
const uint32_t baud = 115200,
575 const UartConfig config = UartConfig::Default,
const size_t rx_buffer_size = 0)
577 HardwareSerial& serial = defaultUartSerial();
578 const uint32_t cfg = toArduinoSerialConfig(config);
579 const auto p = hatUARTPins();
580 if (p.rx < 0 || p.tx < 0) {
581 M5_LIB_LOGE(
"wiring: Hat UART unsupported board=0x%02x", (
int)M5.getBoard());
584 M5_LIB_LOGI(
"wiring: addHatUART board=0x%02x rx=%d tx=%d baud=%lu config=0x%lx", (
int)M5.getBoard(), (
int)p.rx,
585 (
int)p.tx, (
unsigned long)baud, (
unsigned long)cfg);
586 return beginUartSerial(serial, baud, cfg, p.rx, p.tx, rx_buffer_size) && units.add(
unit, serial);
592#if !defined(ARDUINO) && defined(ESP_PLATFORM)
595inline uart_port_t defaultUartPort()
597#if M5_UNIT_UNIFIED_WIRING_HP_UART_NUM > 2
599#elif M5_UNIT_UNIFIED_WIRING_HP_UART_NUM > 1
602#error "Not enough UART"
607struct IdfUartParams {
608 uart_word_length_t word_length;
609 uart_parity_t parity;
610 uart_stop_bits_t stop_bits;
612inline IdfUartParams toIdfUartParams(
const UartConfig c)
615 case UartConfig::_8N2:
616 return {UART_DATA_8_BITS, UART_PARITY_DISABLE, UART_STOP_BITS_2};
617 case UartConfig::_8E1:
618 return {UART_DATA_8_BITS, UART_PARITY_EVEN, UART_STOP_BITS_1};
619 case UartConfig::_8O1:
620 return {UART_DATA_8_BITS, UART_PARITY_ODD, UART_STOP_BITS_1};
621 case UartConfig::_7N1:
622 return {UART_DATA_7_BITS, UART_PARITY_DISABLE, UART_STOP_BITS_1};
623 case UartConfig::_7E1:
624 return {UART_DATA_7_BITS, UART_PARITY_EVEN, UART_STOP_BITS_1};
625 case UartConfig::_7O1:
626 return {UART_DATA_7_BITS, UART_PARITY_ODD, UART_STOP_BITS_1};
627 case UartConfig::Default:
628 case UartConfig::_8N1:
630 return {UART_DATA_8_BITS, UART_PARITY_DISABLE, UART_STOP_BITS_1};
636constexpr size_t kI2CBusCacheSize = 4;
637constexpr size_t kUARTPortCacheSize = 4;
638constexpr size_t kSPIHostCacheSize = 2;
639constexpr size_t kSPIDevCacheSize = 4;
641#if __has_include(<driver/i2c_master.h>)
642struct I2CCacheEntry {
644 i2c_master_bus_handle_t handle;
648constexpr size_t uart_default_rx_buffer_size = 256;
650struct UARTCacheEntry {
653 size_t rx_buffer_size;
657 spi_host_device_t host;
663 spi_device_handle_t handle;
666#if __has_include(<driver/i2c_master.h>)
668inline i2c_master_bus_handle_t ensureI2CBus(
const i2c_port_t port,
const gpio_num_t sda,
const gpio_num_t scl,
671 const uint32_t key = (
static_cast<uint32_t
>(
static_cast<uint8_t
>(port)) << 24) |
672 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(sda)) << 16) |
673 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(scl)) << 8);
674 static I2CCacheEntry cache[kI2CBusCacheSize]{};
675 static size_t count{};
676 for (
size_t i = 0; i < count; ++i) {
677 if (cache[i].key == key)
return cache[i].handle;
679 if (count >= kI2CBusCacheSize) {
680 M5_LIB_LOGE(
"wiring: I2C bus cache full (max %zu)", kI2CBusCacheSize);
683 i2c_master_bus_config_t cfg{};
685 cfg.sda_io_num = sda;
686 cfg.scl_io_num = scl;
687#if SOC_LP_I2C_SUPPORTED
688 if (port == LP_I2C_NUM_0) {
689#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
691 cfg.lp_source_clk = LP_I2C_SCLK_DEFAULT;
694 M5_LIB_LOGE(
"wiring: LP I2C needs ESP-IDF >= 5.3");
700 cfg.clk_source = I2C_CLK_SRC_DEFAULT;
702 cfg.glitch_ignore_cnt = 7;
703 cfg.flags.enable_internal_pullup =
true;
704 if (i2c_new_master_bus(&cfg, &cache[count].handle) != ESP_OK) {
705 M5_LIB_LOGE(
"wiring: i2c_new_master_bus failed port=%d sda=%d scl=%d", (
int)port, (
int)sda, (
int)scl);
708 cache[count].key = key;
709 return cache[count++].handle;
713inline bool ensureI2CLegacyDriver(const i2c_port_t port, const gpio_num_t sda, const gpio_num_t scl,
714 const uint32_t clock)
716 const uint32_t key = (
static_cast<uint32_t
>(
static_cast<uint8_t
>(port)) << 24) |
717 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(sda)) << 16) |
718 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(scl)) << 8);
719 static uint32_t cache[kI2CBusCacheSize]{};
720 static size_t count{};
721 for (
size_t i = 0; i < count; ++i) {
722 if (cache[i] == key)
return true;
724 if (count >= kI2CBusCacheSize) {
725 M5_LIB_LOGE(
"wiring: I2C bus cache full (max %zu)", kI2CBusCacheSize);
732 const esp_err_t install_err = i2c_driver_install(port, I2C_MODE_MASTER, 0, 0, 0);
733 if (install_err == ESP_OK) {
736 conf.mode = I2C_MODE_MASTER;
737 conf.sda_io_num = sda;
738 conf.scl_io_num = scl;
739 conf.sda_pullup_en =
true;
740 conf.scl_pullup_en =
true;
741 conf.master.clk_speed = clock;
742 if (i2c_param_config(port, &conf) != ESP_OK) {
743 M5_LIB_LOGE(
"wiring: legacy i2c param_config failed port=%d sda=%d scl=%d", (
int)port, (
int)sda, (
int)scl);
744 i2c_driver_delete(port);
747 M5_LIB_LOGI(
"wiring: legacy i2c INSTALLED(new) port=%d sda=%d scl=%d clock=%u", (
int)port, (
int)sda, (
int)scl,
749 }
else if (install_err == ESP_FAIL) {
752 int high = 0, low = 0;
753 i2c_get_period(port, &high, &low);
754 if (!(high > 0 && low > 0)) {
755 M5_LIB_LOGE(
"wiring: legacy i2c driver install failed port=%d sda=%d scl=%d", (
int)port, (
int)sda,
759 M5_LIB_LOGI(
"wiring: legacy i2c BORROW(already) port=%d sda=%d scl=%d high=%d low=%d", (
int)port, (
int)sda,
760 (
int)scl, high, low);
762 M5_LIB_LOGE(
"wiring: legacy i2c driver install error=0x%x port=%d sda=%d scl=%d", (
int)install_err, (
int)port,
766 cache[count++] = key;
772inline uart_port_t ensureUARTPort(
const uart_port_t port,
const gpio_num_t rx,
const gpio_num_t tx,
const uint32_t baud,
773 const UartConfig config,
const size_t rx_buffer_size)
775 const size_t rx_size = rx_buffer_size ? rx_buffer_size : uart_default_rx_buffer_size;
776 const uint32_t key = (
static_cast<uint32_t
>(
static_cast<uint8_t
>(port)) << 24) |
777 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(rx)) << 16) |
778 (
static_cast<uint32_t
>(
static_cast<uint8_t
>(tx)) << 8);
779 static UARTCacheEntry cache[kUARTPortCacheSize]{};
780 static size_t count{};
781 for (
size_t i = 0; i < count; ++i) {
782 if (cache[i].key == key) {
784 if (rx_buffer_size && cache[i].rx_buffer_size && cache[i].rx_buffer_size != rx_size) {
785 M5_LIB_LOGW(
"wiring: UART port=%d already installed with rx_buffer_size=%u (requested %u)", (
int)port,
786 (
unsigned)cache[i].rx_buffer_size, (
unsigned)rx_size);
788 return cache[i].port;
791 if (count >= kUARTPortCacheSize) {
792 M5_LIB_LOGE(
"wiring: UART port cache full (max %zu)", kUARTPortCacheSize);
795 if (uart_is_driver_installed(port)) {
796 cache[count] = {key, port, 0};
797 return cache[count++].port;
799 const auto p = toIdfUartParams(config);
801 uc.baud_rate =
static_cast<int>(baud);
802 uc.data_bits = p.word_length;
803 uc.parity = p.parity;
804 uc.stop_bits = p.stop_bits;
805 uc.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
806 uc.source_clk = UART_SCLK_DEFAULT;
807 if (uart_param_config(port, &uc) != ESP_OK ||
808 uart_set_pin(port, tx, rx, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE) != ESP_OK ||
809 uart_driver_install(port,
static_cast<int>(rx_size), 0, 0,
nullptr, 0) != ESP_OK) {
810 M5_LIB_LOGE(
"wiring: uart setup failed port=%d rx_buffer_size=%u", (
int)port, (
unsigned)rx_size);
813 cache[count] = {key, port, rx_size};
814 return cache[count++].port;
819inline spi_device_handle_t ensureSPIDevice(
const spi_host_device_t host,
const gpio_num_t mosi,
const gpio_num_t miso,
820 const gpio_num_t sck,
const uint32_t clock_hz,
const uint8_t mode,
821 const uint8_t bit_order)
823 static SPIHostEntry host_cache[kSPIHostCacheSize]{};
824 static SPIDevEntry dev_cache[kSPIDevCacheSize]{};
825 static size_t host_count{};
826 static size_t dev_count{};
828 bool host_init =
false;
829 for (
size_t i = 0; i < host_count; ++i) {
830 if (host_cache[i].host == host) {
831 host_init = host_cache[i].initialized;
836 spi_bus_config_t bc{};
837 bc.mosi_io_num = mosi;
838 bc.miso_io_num = miso;
839 bc.sclk_io_num = sck;
840 bc.quadwp_io_num = -1;
841 bc.quadhd_io_num = -1;
842 bc.max_transfer_sz = 4096;
843 if (spi_bus_initialize(host, &bc, SPI_DMA_CH_AUTO) != ESP_OK) {
844 M5_LIB_LOGE(
"wiring: spi_bus_initialize failed host=%d", (
int)host);
847 if (host_count < kSPIHostCacheSize) host_cache[host_count++] = {host,
true};
850 const uint32_t key = (
static_cast<uint32_t
>(
static_cast<uint8_t
>(host)) << 24) |
851 (
static_cast<uint32_t
>(clock_hz / 1000U) << 8) | (
static_cast<uint32_t
>(mode) << 4) |
852 static_cast<uint32_t
>(bit_order);
853 for (
size_t i = 0; i < dev_count; ++i) {
854 if (dev_cache[i].key == key)
return dev_cache[i].handle;
856 if (dev_count >= kSPIDevCacheSize) {
857 M5_LIB_LOGE(
"wiring: SPI device cache full (max %zu)", kSPIDevCacheSize);
860 spi_device_interface_config_t dc{};
861 dc.clock_speed_hz =
static_cast<int>(clock_hz);
863 dc.spics_io_num = GPIO_NUM_NC;
865 if (bit_order == 1) dc.flags |= SPI_DEVICE_BIT_LSBFIRST;
866 if (spi_bus_add_device(host, &dc, &dev_cache[dev_count].handle) != ESP_OK) {
867 M5_LIB_LOGE(
"wiring: spi_bus_add_device failed host=%d", (
int)host);
870 dev_cache[dev_count].key = key;
871 return dev_cache[dev_count++].handle;
876#if __has_include(<driver/i2c_master.h>)
882inline i2c_master_bus_handle_t i2cBusHandle(
const i2c_port_t port,
const gpio_num_t sda,
const gpio_num_t scl,
883 const uint32_t clock = 100000)
885 return detail::ensureI2CBus(port, sda, scl, clock);
895inline uart_port_t uartPortHandle(
const uart_port_t port,
const gpio_num_t rx,
const gpio_num_t tx,
const uint32_t baud,
896 const UartConfig config = UartConfig::Default,
const size_t rx_buffer_size = 0)
898 return detail::ensureUARTPort(port, rx, tx, baud, config, rx_buffer_size);
906inline spi_device_handle_t spiDeviceHandle(
const spi_host_device_t host,
const gpio_num_t mosi,
const gpio_num_t miso,
907 const gpio_num_t sck,
const uint32_t clock_hz,
const uint8_t mode = 0,
908 const uint8_t bit_order = 0)
910 return detail::ensureSPIDevice(host, mosi, miso, sck, clock_hz, mode, bit_order);
917#if defined(__M5UNIFIED_HPP__)
926inline bool addI2C(UnitUnified& units, Component&
unit,
const uint32_t clock = 0,
927 const NessoPort nesso = NessoPort::PortB)
932 auto cfg =
unit.component_config();
934 unit.component_config(cfg);
936 const uint32_t eff_clock =
unit.component_config().clock;
937 const auto pins = i2cPins(nesso);
938 const auto board = M5.getBoard();
939 M5_LIB_LOGI(
"wiring(ESP-IDF): addI2C board=0x%02x nesso=%d sda=%d scl=%d clock=%u backend=%d", (
int)board,
940 (
int)nesso, (
int)pins.sda, (
int)pins.scl, (
unsigned)eff_clock, (
int)pins.backend);
941 switch (pins.backend) {
943#if defined(M5_HAL_HPP)
944 return i2cSoftware(units,
unit, pins.sda, pins.scl);
946 M5_LIB_LOGE(
"wiring: SoftwareI2C backend requires M5HAL");
950 if (board == m5::board_t::board_ArduinoNessoN1) {
953 return i2cClass(units,
unit, M5.In_I2C);
957#if __has_include(<driver/i2c_master.h>)
958 auto bus = i2cBusHandle(I2C_NUM_0, (gpio_num_t)pins.sda, (gpio_num_t)pins.scl, eff_clock);
959 if (!bus)
return false;
960 return units.add(
unit, bus, I2C_NUM_0, (gpio_num_t)pins.sda, (gpio_num_t)pins.scl);
962 if (!detail::ensureI2CLegacyDriver(I2C_NUM_0, (gpio_num_t)pins.sda, (gpio_num_t)pins.scl, eff_clock)) {
965 return units.add(
unit, I2C_NUM_0, (gpio_num_t)pins.sda, (gpio_num_t)pins.scl);
970 return i2cClass(units,
unit, M5.Ex_I2C);
976inline bool addGPIO(UnitUnified& units, Component&
unit,
const GpioRole role = GpioRole::Both)
978 auto p = gpioPins(role);
979 if (role == GpioRole::OutOnly) p.rx = -1;
980 if (role == GpioRole::InOnly) p.tx = -1;
981 M5_LIB_LOGI(
"wiring(ESP-IDF): GPIO rx=%d tx=%d role=%d fallback_a=%d", (
int)p.rx, (
int)p.tx, (
int)role,
983 return units.add(
unit, p.rx, p.tx);
991inline bool addUART(UnitUnified& units, Component&
unit,
const uint32_t baud = 115200,
992 const UartConfig config = UartConfig::Default,
const size_t rx_buffer_size = 0)
994 const auto pins = uartPins();
995 M5_LIB_LOGI(
"wiring(ESP-IDF): UART rx=%d tx=%d baud=%u fallback_a=%d", (
int)pins.rx, (
int)pins.tx, (
unsigned)baud,
996 (
int)pins.fallback_a);
998 uartPortHandle(defaultUartPort(), (gpio_num_t)pins.rx, (gpio_num_t)pins.tx, baud, config, rx_buffer_size);
999 if (port == UART_NUM_MAX)
return false;
1000 return units.add(
unit, port);
1004inline bool addSPI(UnitUnified& units, Component&
unit,
const uint32_t clock_hz,
const uint8_t mode = 0,
1005 const uint8_t bit_order = 0)
1007 const auto pins = spiPins();
1008 M5_LIB_LOGI(
"wiring(ESP-IDF): addSPI sclk=%d miso=%d mosi=%d clock=%u mode=%u bit_order=%u", (
int)pins.sclk,
1009 (
int)pins.miso, (
int)pins.mosi, (
unsigned)clock_hz, (
unsigned)mode, (
unsigned)bit_order);
1010 auto dev = spiDeviceHandle(SPI2_HOST, (gpio_num_t)pins.mosi, (gpio_num_t)pins.miso, (gpio_num_t)pins.sclk, clock_hz,
1012 if (!dev)
return false;
1013 return units.add(
unit, dev);
1017inline bool addHatI2C(UnitUnified& units, Component&
unit,
const uint32_t clock = 0)
1019 const auto p = hatI2CPins();
1020 if (p.sda < 0 || p.scl < 0) {
1021 M5_LIB_LOGE(
"wiring: Hat I2C unsupported board=0x%02x", (
int)M5.getBoard());
1026 auto cfg =
unit.component_config();
1028 unit.component_config(cfg);
1030 const uint32_t eff_clock =
unit.component_config().clock;
1033#if M5_UNIT_UNIFIED_WIRING_HP_I2C_NUM >= 2
1035#elif SOC_LP_I2C_SUPPORTED
1036 port = LP_I2C_NUM_0;
1038 M5_LIB_LOGE(
"wiring: addHatI2C needs a 2nd I2C port, none available board=0x%02x", (
int)M5.getBoard());
1044 M5_LIB_LOGI(
"wiring(ESP-IDF): addHatI2C port=%d sda=%d scl=%d clock=%u", (
int)port, (
int)p.sda, (
int)p.scl,
1045 (
unsigned)eff_clock);
1046#if __has_include(<driver/i2c_master.h>)
1047 auto bus = i2cBusHandle(port, (gpio_num_t)p.sda, (gpio_num_t)p.scl, eff_clock);
1048 if (!bus)
return false;
1049 return units.add(
unit, bus, port, (gpio_num_t)p.sda, (gpio_num_t)p.scl);
1051 if (!detail::ensureI2CLegacyDriver(port, (gpio_num_t)p.sda, (gpio_num_t)p.scl, eff_clock))
return false;
1052 return units.add(
unit, port, (gpio_num_t)p.sda, (gpio_num_t)p.scl);
1057inline bool addHatGPIO(UnitUnified& units, Component&
unit,
const GpioRole role = GpioRole::Both)
1059 auto p = hatGPIOPins();
1060 if (p.rx < 0 || p.tx < 0) {
1061 M5_LIB_LOGE(
"wiring: Hat GPIO unsupported board=0x%02x", (
int)M5.getBoard());
1064 if (role == GpioRole::OutOnly) p.rx = -1;
1065 if (role == GpioRole::InOnly) p.tx = -1;
1066 M5_LIB_LOGI(
"wiring(ESP-IDF): addHatGPIO board=0x%02x rx=%d tx=%d role=%d", (
int)M5.getBoard(), (
int)p.rx,
1067 (
int)p.tx, (
int)role);
1068 return units.add(
unit, p.rx, p.tx);
1075inline bool addHatUART(UnitUnified& units, Component&
unit,
const uint32_t baud = 115200,
1076 const UartConfig config = UartConfig::Default,
const size_t rx_buffer_size = 0)
1078 const auto p = hatUARTPins();
1079 if (p.rx < 0 || p.tx < 0) {
1080 M5_LIB_LOGE(
"wiring: Hat UART unsupported board=0x%02x", (
int)M5.getBoard());
1083 M5_LIB_LOGI(
"wiring(ESP-IDF): addHatUART board=0x%02x rx=%d tx=%d baud=%u", (
int)M5.getBoard(), (
int)p.rx,
1084 (
int)p.tx, (
unsigned)baud);
1085 auto port = uartPortHandle(defaultUartPort(), (gpio_num_t)p.rx, (gpio_num_t)p.tx, baud, config, rx_buffer_size);
1086 if (port == UART_NUM_MAX)
return false;
1087 return units.add(
unit, port);
1096#if defined(__M5UNIFIED_HPP__)
1098[[noreturn]]
inline void failStop()
1100 M5.Display.fillScreen(0xF800 );
1102 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