# M5UnitUnified [日本語](README.ja.md) **A new approach to connect and handle various M5 units in the M5Stack** Library for M5Stack Series and M5Unit Series ## Overview M5UnitUnified is a library for unified handling of various M5 units products. ### Unified APIs Each unit's external library has its own API design. Unify basic APIs so that all units can be handled in the same way. ### Unified connections and communications Each unit's external library requires its own communication functions and assumptions. Unify prerequisites and communication methods. In the future, we plan to work with [M5HAL (Hardware Abstraction Layer)](https://github.com/m5stack/M5HAL) to unified communication with each unit. ### Unified Licensing External library licenses for each unit are mixed. All M5UnitUnified and related libraries are under the [MIT license](LICENSE). ## How to install The library is registered with the library manager. ### Arduino IDE 1. Using library manager and select the library of the unit you want to use (e.g. M5Unit-GESTURE) Dependent M5UnitUnified related libraries will be downloaded automatically. ### PlatformIO 1. Write lib\_deps settings to platformio.ini ```ini lib_deps= m5stack/M5Unit-foo ; Unit to be used ``` Dependent M5UnitUnified related libraries will be downloaded automatically. ## How to use See also examples for each unit repository too. ### UnitComponent with UnitUnified (Standard usage) #### Unit using Wire ```cpp // If you use other units, change include files(*1), instances(*2), and get values(*3) #include #include #include // *1 Include the header of the unit to be used m5::unit::UnitUnified Units; m5::unit::UnitCO2 unit; // *2 Instance of the unit void setup() { M5.begin(); auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda); auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl); M5_LOGI("getPin: SDA:%u SCL:%u", pin_num_sda, pin_num_scl); Wire.end(); Wire.begin(pin_num_sda, pin_num_scl, 400 * 1000U); if (!Units.add(unit, Wire) // Add unit to UnitUnified manager || !Units.begin()) { // Begin each unit M5_LOGE("Failed to add/begin"); } } void loop() { M5.update(); Units.update(); if (unit.updated()) { // *3 Obtaining unit-specific measurements M5.Log.printf("CO2:%u Temp:%f Hum:%f\n", unit.co2(), unit.temperature(), unit.humidity()); } } ``` #### Unit using I2C_Class (M5Unified internal I2C) ```cpp // Example: Reading the SHT30 sensor built into M5Paper via M5Unified's In_I2C #include #include #include // *1 Include the header of the unit to be used m5::unit::UnitUnified Units; m5::unit::UnitSHT30 unit; // *2 Instance of the unit void setup() { M5.begin(); // Use M5Unified's In_I2C (internal I2C bus) // No manual pin/frequency configuration needed if (!Units.add(unit, M5.In_I2C) // Add unit using I2C_Class || !Units.begin()) { M5_LOGE("Failed to add/begin"); } } void loop() { M5.update(); Units.update(); if (unit.updated()) { // *3 Obtaining unit-specific measurements M5.Log.printf("Temp:%f Hum:%f\n", unit.temperature(), unit.humidity()); } } ``` #### Unit using M5HAL Bus (SoftwareI2C) ```cpp // If you use other units, change include files(*1), instances(*2), and get values(*3) #include #include #include // *1 Include the header of the unit to be used #include m5::unit::UnitUnified Units; m5::unit::UnitCO2 unit; // *2 Instance of the unit void setup() { M5.begin(); auto pin_num_sda = M5.getPin(m5::pin_name_t::port_a_sda); auto pin_num_scl = M5.getPin(m5::pin_name_t::port_a_scl); m5::hal::bus::I2CBusConfig i2c_cfg; i2c_cfg.pin_sda = m5::hal::gpio::getPin(pin_num_sda); i2c_cfg.pin_scl = m5::hal::gpio::getPin(pin_num_scl); auto i2c_bus = m5::hal::bus::i2c::getBus(i2c_cfg); if (!Units.add(unit, i2c_bus ? i2c_bus.value() : nullptr) // Add unit using M5HAL Bus || !Units.begin()) { M5_LOGE("Failed to add/begin"); } } void loop() { M5.update(); Units.update(); if (unit.updated()) { // *3 Obtaining unit-specific measurements M5.Log.printf("CO2:%u Temp:%f Hum:%f\n", unit.co2(), unit.temperature(), unit.humidity()); } } ``` #### Unit using GPIO ```cpp // If you use other units, change include files(*1), instances(*2), and get values(*3) #include #include #include // *1 Include the header of the unit to be used m5::unit::UnitUnified Units; m5::unit::UnitTubePressure unit; // *2 Instance of the unit void setup() { M5.begin(); // PortB if available, PortA if not auto pin_num_gpio_in = M5.getPin(m5::pin_name_t::port_b_in); auto pin_num_gpio_out = M5.getPin(m5::pin_name_t::port_b_out); if (pin_num_gpio_in < 0 || pin_num_gpio_out < 0) { M5_LOGW("PortB is not available"); Wire.end(); pin_num_gpio_in = M5.getPin(m5::pin_name_t::port_a_pin1); pin_num_gpio_out = M5.getPin(m5::pin_name_t::port_a_pin2); } if (!Units.add(unit, pin_num_gpio_in, pin_num_gpio_out) // Add unit to UnitUnified manager || !Units.begin()) { // Begin each unit M5_LOGE("Failed to add/begin"); } } void loop() { M5.update(); Units.update(); if (unit.updated()) { // *3 Obtaining unit-specific measurements M5.Log.printf("Pressure:%.2f\n", unit.pressure()); } } ``` #### Unit using UART(Serial) ```cpp // If you use other units, change include files(*1), instances(*2), and call any API(*3) #include #include #include // *1 Include the header of the unit to be used m5::unit::UnitUnified Units; m5::unit::UnitFinger unit; // *2 Instance of the unit void setup() { M5.begin(); // PortC if available, PortA if not auto pin_num_in = M5.getPin(m5::pin_name_t::port_c_rxd); auto pin_num_out = M5.getPin(m5::pin_name_t::port_c_txd); if (pin_num_in < 0 || pin_num_out < 0) { M5_LOGW("PortC is not available"); Wire.end(); pin_num_in = M5.getPin(m5::pin_name_t::port_a_pin1); pin_num_out = M5.getPin(m5::pin_name_t::port_a_pin2); } #if SOC_UART_NUM > 2 auto& s = Serial2; #elif SOC_UART_NUM > 1 auto& s = Serial1; #else #error "Not enough Serial" #endif s.end(); // Note that the argument varies depending on the target unit s.begin(19200, SERIAL_8N1, pin_num_in, pin_num_out); if (!Units.add(unit, s) // Add unit to UnitUnified manager || !Units.begin()) { // Begin each unit M5_LOGE("Failed to begin"); } } void loop() { M5.update(); Units.update(); // *3 Arbitrary API calls to the unit... } ``` #### Unit using SPI ```cpp // If you use other units, change include files(*1), instances(*2), and call any API(*3) #include #include #include // *1 Include the header of the unit to be used m5::unit::UnitUnified Units; m5::unit::UnitFoo unit; // *2 Instance of the unit void setup() { M5.begin(); if (!SPI.bus()) { auto spi_sclk = M5.getPin(m5::pin_name_t::sd_spi_sclk); auto spi_mosi = M5.getPin(m5::pin_name_t::sd_spi_mosi); auto spi_miso = M5.getPin(m5::pin_name_t::sd_spi_miso); M5_LOGI("getPin: %d,%d,%d", spi_sclk, spi_mosi, spi_miso); SPI.begin(spi_sclk, spi_miso, spi_mosi); } // Note that the depending on the target unit SPISettings settings = {10000000, MSBFIRST, SPI_MODE1}; if (!Units.add(cap, SPI, settings) || !Units.begin()) { M5_LOGE("Failed to begin"); lcd.fillScreen(TFT_RED); while (true) { m5::utility::delay(10000); } } } void loop() { M5.update(); Units.update(); // *3 Arbitrary API calls to the unit... } ``` ## Supported things ### Supported frameworks - Arduino In the future, all connections will be made through M5HAL, eliminating the need to use Wire class etc. directly. ### Supported connection - I2C with TwoWire class - I2C with I2C_Class (M5Unified In_I2C/Ex_I2C) - I2C with M5HAL Bus (including SoftwareI2C) - GPIO (Currently only functions required for the units are included) - UART with HardwareSerial class - SPI with SPI class ### Supported devices, units See also [Wiki](https://github.com/m5stack/M5UnitUnified/wiki/) ## Examples For examples, please refer to each unit's repository (e.g. M5Unit-ENV, M5Unit-HEART, etc.). ## Doxygen document [GitHub Pages](https://m5stack.github.io/M5UnitUnified/) If you want to generate documents on your local machine, execute the following command ``` bash docs/doxy.sh ``` It will output it under docs/html If you want to output Git commit hashes to html, do it for the git cloned folder. ### Required - [Doxygen](https://www.doxygen.nl/) - [pcregrep](https://formulae.brew.sh/formula/pcre2) - [Git](https://git-scm.com/) (Output commit hash to html)