diff --git a/examples/Modules/StamPLC_AC/Relay/Relay.ino b/examples/Modules/StamPLC_AC/Relay/Relay.ino new file mode 100644 index 0000000..8de2885 --- /dev/null +++ b/examples/Modules/StamPLC_AC/Relay/Relay.ino @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include +#include + +M5StamPLC_AC stamplc_ac; + +void setup() +{ + /* Init M5StamPLC */ + M5StamPLC.begin(); + + /* Init M5StamPLC-AC */ + while (!stamplc_ac.begin()) { + printf("M5StamPLC-AC init failed, retry in 1s...\n"); + delay(1000); + } +} + +void loop() +{ + static bool relay_state = false; + + /* Toggle M5StamPLC-AC relay state */ + relay_state = !relay_state; + stamplc_ac.writeRelay(relay_state); + printf("Write M5StamPLC-AC Relay to %s\n", stamplc_ac.readRelay() ? "ON" : "OFF"); + + delay(1000); +} diff --git a/examples/Modules/StamPLC_AC/StatusLight/StatusLight.ino b/examples/Modules/StamPLC_AC/StatusLight/StatusLight.ino new file mode 100644 index 0000000..7ffce42 --- /dev/null +++ b/examples/Modules/StamPLC_AC/StatusLight/StatusLight.ino @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include +#include + +M5StamPLC_AC stamplc_ac; + +void setup() +{ + /* Init M5StamPLC */ + M5StamPLC.begin(); + + /* Init M5StamPLC-AC */ + while (!stamplc_ac.begin()) { + printf("M5StamPLC-AC init failed, retry in 1s...\n"); + delay(1000); + } +} + +void loop() +{ + /* Set status light to red */ + stamplc_ac.setStatusLight(1, 0, 0); + printf("Set M5StamPLC-AC status light to red\n"); + delay(1000); + + /* Set status light to green */ + stamplc_ac.setStatusLight(0, 1, 0); + printf("Set M5StamPLC-AC status light to green\n"); + delay(1000); + + /* Set status light to blue */ + stamplc_ac.setStatusLight(0, 0, 1); + printf("Set M5StamPLC-AC status light to blue\n"); + delay(1000); + + /* Set status light to white */ + stamplc_ac.setStatusLight(1, 1, 1); + printf("Set M5StamPLC-AC status light to white\n"); + delay(1000); + + /* Set status light to black */ + stamplc_ac.setStatusLight(0, 0, 0); + printf("Set M5StamPLC-AC status light to black\n"); + delay(1000); +} diff --git a/library.json b/library.json index 0beb511..8c51e55 100644 --- a/library.json +++ b/library.json @@ -13,7 +13,7 @@ "M5Unified": "*", "M5GFX": "*" }, - "version": "1.1.0", + "version": "1.2.0", "frameworks": "arduino", "platforms": "espressif32" } \ No newline at end of file diff --git a/library.properties b/library.properties index badfdc4..87c2f0f 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=M5StamPLC -version=1.1.0 +version=1.2.0 author=M5Stack maintainer=M5Stack sentence=M5StamPLC is a library for M5StamPLC diff --git a/src/M5StamPLC.h b/src/M5StamPLC.h index d743d9d..7ad30c9 100644 --- a/src/M5StamPLC.h +++ b/src/M5StamPLC.h @@ -8,6 +8,7 @@ #include "utils/aw9523/aw9523.h" #include "utils/lm75b/lm75b.h" #include "utils/rx8130/rx8130.h" +#include "modules/M5StamPLC_AC.h" #include #include #include diff --git a/src/modules/M5StamPLC_AC.cpp b/src/modules/M5StamPLC_AC.cpp new file mode 100644 index 0000000..2d70426 --- /dev/null +++ b/src/modules/M5StamPLC_AC.cpp @@ -0,0 +1,98 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#include "M5StamPLC_AC.h" +#include "utils/make_unique_compat.h" +#include +#include + +static const char* _tag = "M5StamPLC_AC"; + +static const uint8_t _ioe_address = 0x44; +static const uint8_t _pin_relay = 2; +static const uint8_t _pin_status_light_r = 5; +static const uint8_t _pin_status_light_g = 6; +static const uint8_t _pin_status_light_b = 7; + +bool M5StamPLC_AC::begin() +{ + if (_ioe) { + ESP_LOGW(_tag, "IOExpander already initialized"); + return true; + } + + _ioe = std::make_unique(_ioe_address, 400000, &m5::In_I2C); + if (!_ioe->begin()) { + _ioe.reset(); + ESP_LOGE(_tag, "IOExpander initialization failed"); + return false; + } + + // AC relay + _ioe->setDirection(_pin_relay, true); + _ioe->setPullMode(_pin_relay, false); + _ioe->setHighImpedance(_pin_relay, false); + + // Status light + auto setup_status_light_pin = [](m5::PI4IOE5V6408_Class* ioe, uint8_t pin) { + ioe->setDirection(pin, true); + ioe->setPullMode(pin, true); + ioe->setHighImpedance(pin, false); + ioe->digitalWrite(pin, true); + }; + + setup_status_light_pin(_ioe.get(), _pin_status_light_r); + setup_status_light_pin(_ioe.get(), _pin_status_light_g); + setup_status_light_pin(_ioe.get(), _pin_status_light_b); + + return true; +} + +bool M5StamPLC_AC::readRelay() +{ + if (!is_ioe_valid()) { + return false; + } + + return _ioe->getWriteValue(_pin_relay); +} + +void M5StamPLC_AC::writeRelay(const bool& state) +{ + if (!is_ioe_valid()) { + return; + } + + _ioe->digitalWrite(_pin_relay, state); +} + +void M5StamPLC_AC::setStatusLight(const uint8_t& r, const uint8_t& g, const uint8_t& b) +{ + if (!is_ioe_valid()) { + return; + } + + auto set_channel = [](m5::PI4IOE5V6408_Class* ioe, uint8_t pin, uint8_t value) { + if (value == 0) { + ioe->setHighImpedance(pin, true); + } else { + ioe->setHighImpedance(pin, false); + ioe->digitalWrite(pin, false); + } + }; + + set_channel(_ioe.get(), _pin_status_light_r, r); + set_channel(_ioe.get(), _pin_status_light_g, g); + set_channel(_ioe.get(), _pin_status_light_b, b); +} + +bool M5StamPLC_AC::is_ioe_valid() +{ + if (!_ioe) { + ESP_LOGE(_tag, "IOExpander not initialized"); + return false; + } + return true; +} diff --git a/src/modules/M5StamPLC_AC.h b/src/modules/M5StamPLC_AC.h new file mode 100644 index 0000000..3ed7b24 --- /dev/null +++ b/src/modules/M5StamPLC_AC.h @@ -0,0 +1,49 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#pragma once +#include +#include +#include +#include + +class M5StamPLC_AC { +public: + /** + * @brief Initialize M5StamPLC-AC + * + * @return true + * @return false + */ + bool begin(); + + /** + * @brief Read Relay state + * + * @return true if ON, false if OFF + */ + bool readRelay(); + + /** + * @brief Write AC Relay state + * + * @param state true if ON, false if OFF + */ + void writeRelay(const bool& state); + + /** + * @brief Set Status Light + * + * @param r + * @param g + * @param b + */ + void setStatusLight(const uint8_t& r, const uint8_t& g, const uint8_t& b); + +protected: + std::unique_ptr _ioe; + + bool is_ioe_valid(); +}; diff --git a/src/utils/make_unique_compat.h b/src/utils/make_unique_compat.h new file mode 100644 index 0000000..aefe4fe --- /dev/null +++ b/src/utils/make_unique_compat.h @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ +#pragma once +#include +#include +#include +#include + +#if __cplusplus < 201402L +namespace std { + +template +typename std::enable_if::value, std::unique_ptr>::type make_unique(Args&&... args) +{ + return std::unique_ptr(new T(std::forward(args)...)); +} + +template +typename std::enable_if::value && std::extent::value == 0, std::unique_ptr>::type make_unique( + std::size_t n) +{ + using U = typename std::remove_extent::type; + return std::unique_ptr(new U[n]()); +} + +template +typename std::enable_if<(std::extent::value != 0), void>::type make_unique(Args&&...) = delete; + +} // namespace std +#endif diff --git a/src/utils/pi4ioe5v6408/pi4ioe5v6408.cpp b/src/utils/pi4ioe5v6408/pi4ioe5v6408.cpp deleted file mode 100644 index a2cfb0e..0000000 --- a/src/utils/pi4ioe5v6408/pi4ioe5v6408.cpp +++ /dev/null @@ -1,108 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - * SPDX-License-Identifier: MIT - */ -#include "pi4ioe5v6408.h" - -bool PI4IOE5V6408_Class::begin() -{ - auto id = readRegister8(0x01); - // printf("id: %d\n", id); - if (id == 0) { - return false; - } - return true; -} - -// false input, true output -void PI4IOE5V6408_Class::setDirection(uint8_t pin, bool direction) -{ - // Input, set 0 - if (!direction) { - auto data = readRegister8(0x03); - data &= ~(1 << pin); - writeRegister8(0x03, data); - } - // Output, set 1 - else { - auto data = readRegister8(0x03); - data |= (1 << pin); - writeRegister8(0x03, data); - } -} - -void PI4IOE5V6408_Class::enablePull(uint8_t pin, bool enablePull) -{ - if (enablePull) { - auto data = readRegister8(0x0B); - data |= (1 << pin); - writeRegister8(0x0B, data); - } else { - auto data = readRegister8(0x0B); - data &= ~(1 << pin); - writeRegister8(0x0B, data); - } -} - -// false down, true up -void PI4IOE5V6408_Class::setPullMode(uint8_t pin, bool mode) -{ - if (mode) { - auto data = readRegister8(0x0D); - data |= (1 << pin); - writeRegister8(0x0D, data); - } else { - auto data = readRegister8(0x0D); - data &= ~(1 << pin); - writeRegister8(0x0D, data); - } -} - -void PI4IOE5V6408_Class::setHighImpedance(uint8_t pin, bool enable) -{ - auto data = readRegister8(0x07); - - if (enable) { - data |= (1 << pin); - } else { - data &= ~(1 << pin); - } - - writeRegister8(0x07, data); -} - -void PI4IOE5V6408_Class::digitalWrite(uint8_t pin, bool level) -{ - auto data = readRegister8(0x05); - // spdlog::info("data: {}", data); - - if (level) { - data |= (1 << pin); - } else { - data &= ~(1 << pin); - } - - writeRegister8(0x05, data); -} - -bool PI4IOE5V6408_Class::digitalRead(uint8_t pin) -{ - auto data = readRegister8(0x0F); - return (data & (1 << pin)) != 0; -} - -void PI4IOE5V6408_Class::resetIrq() -{ - readRegister8(0x13); -} - -void PI4IOE5V6408_Class::disableIrq() -{ - writeRegister8(0x11, 0B11111111); -} - -void PI4IOE5V6408_Class::enableIrq() -{ - writeRegister8(0x11, 0x0); -} diff --git a/src/utils/pi4ioe5v6408/pi4ioe5v6408.h b/src/utils/pi4ioe5v6408/pi4ioe5v6408.h deleted file mode 100644 index ba37853..0000000 --- a/src/utils/pi4ioe5v6408/pi4ioe5v6408.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - * SPDX-License-Identifier: MIT - */ -#pragma once -#include - -// https://www.diodes.com/assets/Datasheets/PI4IOE5V6408.pdf -class PI4IOE5V6408_Class : public m5::I2C_Device { -public: - PI4IOE5V6408_Class(std::uint8_t i2c_addr = 0x43, std::uint32_t freq = 400000, m5::I2C_Class* i2c = &m5::In_I2C) - : I2C_Device(i2c_addr, freq, i2c) - { - } - - bool begin(); - - // false input, true output - void setDirection(uint8_t pin, bool direction); - - void enablePull(uint8_t pin, bool enablePull); - - // false down, true up - void setPullMode(uint8_t pin, bool mode); - - void setHighImpedance(uint8_t pin, bool enable); - - void digitalWrite(uint8_t pin, bool level); - - bool digitalRead(uint8_t pin); - - void resetIrq(); - - void disableIrq(); - - void enableIrq(); -};