I had the need for such driver and now I have one and it is working for me. Maybe it is helpful for others too and as I have the permission to publish it I am doing it here.
As mentioned in the subject it just implements the commands for the relays to work with Tasmota. I can not support it, so it is really as it is!
Cheers
Hm, file upload is not allowed?
2021-10-22 - firmware - MOD-IO2 (only relays).patch
CHANGELOG.md | 4 +
I2CDEVICES.md | 7 +-
tasmota/settings.h | 3 +-
tasmota/tasmota.h | 1 +
tasmota/xdrv_85_modio2.ino | 217 +++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 230 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2b819e733..d55a8c63e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,10 @@
# Changelog
All notable changes to this project will be documented in this file.
+## [9.6.0.1] 20211007
+### Added
+- Initial support for MOD-IO2 I2C I/O Expander (currently relay outputs only)
+
## [Released]
## [9.5.0] 20210617
diff --git a/I2CDEVICES.md b/I2CDEVICES.md
index 84dac460b..46b9dbf80 100644
--- a/I2CDEVICES.md
+++ b/I2CDEVICES.md
@@ -92,4 +92,9 @@ Index | Define | Driver | Device | Address(es) | Description
56 | USE_SEESAW_SOIL | xsns_81 | SEESOIL | 0x36 - 0x39 | Adafruit seesaw soil moisture sensor
57 | USE_TOF10120 | xsns_84 | TOF10120 | 0x52 | Time-of-flight (ToF) distance sensor
58 | USE_MPU_ACCEL | xsns_85 | MPU_ACCEL| 0x68 | MPU6886/MPU9250 6-axis MotionTracking sensor from M5Stack
- 59 | USE_BM8563 | xdrv_56 | BM8563 | 0x51 | BM8563 RTC from M5Stack
\ No newline at end of file
+ 59 | USE_BM8563 | xdrv_56 | BM8563 | 0x51 | BM8563 RTC from M5Stack
+ 60 | USE_AM2320 | xsns_88 | AM2320 | 0x5C | Temperature and Humidity sensor
+ 61 | USE_T67XX | xsns_89 | T67XX | 0x15 | CO2 sensor
+ 62 | USE_SCD40 | xsns_92 | SCD40 | 0x62 | CO2 sensor Sensirion SCD40/SCD41
+ 63 | USE_HM330X | xsns_93 | HM330X | 0x40 | Particule sensor
+ 64 | USE_MODIO2 | xdrv_85 | MOD-IO2 | Any | 2-channel relay expander
\ No newline at end of file
diff --git a/tasmota/settings.h b/tasmota/settings.h
index 075759b71..dc576adad 100644
--- a/tasmota/settings.h
+++ b/tasmota/settings.h
@@ -736,8 +736,9 @@ typedef struct {
uint8_t shd_leading_edge; // F5B
uint16_t shd_warmup_brightness; // F5C
uint8_t shd_warmup_time; // F5E
+ uint8_t modio2_config[MAX_MODIO2]; // F5F
- uint8_t free_f5f[65]; // F5F - Decrement if adding new Setting variables just above and below
+ uint8_t free_f6D[51]; // F6D - Decrement if adding new Setting variables just above and below
// Only 32 bit boundary variables below
diff --git a/tasmota/tasmota.h b/tasmota/tasmota.h
index a76403d7d..ca9833917 100644
--- a/tasmota/tasmota.h
+++ b/tasmota/tasmota.h
@@ -80,6 +80,7 @@ const uint8_t MAX_PCF8574 = 4; // Max number of PCF8574 devices
const uint8_t MAX_RULE_SETS = 3; // Max number of rule sets of size 512 characters
const uint16_t MAX_RULE_SIZE = 512; // Max number of characters in rules
const uint16_t VL53L0X_MAX_SENSORS = 8; // Max number of VL53L0X sensors
+const uint8_t MAX_MODIO2 = 14; // Max number of MOD-IO2 devices
#ifdef ESP32
const uint8_t MAX_I2C = 2; // Max number of I2C controllers (ESP32 = 2)
diff --git a/tasmota/xdrv_85_modio2.ino b/tasmota/xdrv_85_modio2.ino
new file mode 100644
index 000000000..0fdedb176
--- /dev/null
+++ b/tasmota/xdrv_85_modio2.ino
@@ -0,0 +1,217 @@
+/*
+ xdrv_85_modio2.ino - MOD-IO2 I2C support for Tasmota
+
+ Copyright (C) 2021 Power Void
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifdef USE_I2C
+#ifdef USE_MODIO2
+/*********************************************************************************************\
+ * MODIO2 - I2C 2-channel relay expander
+ *
+ * I2C Address: Any
+\*********************************************************************************************/
+
+#define XDRV_85 85
+#define XI2C_64 64 // See I2CDEVICES.md
+
+#define MODIO2_ADDR_1ST 0x00 /** 1st I2C address of board model **/
+#define MODIO2_I2C_ID_REG 0x20 /** Board model specific ID register **/
+#define MODIO2_I2C_ID_VAL 0x23 /** Board model specific ID register value **/
+#define MODIO2_I2C_FW_REG 0x21 /** Get firmware version **/
+#define MODIO2_I2C_STATE_REG 0x43 /** Get current relay state **/
+#define MODIO2_I2C_RONOFF_REG 0x40 /** Set relay(s) on/off **/
+#define MODIO2_I2C_RON_REG 0x41 /** Set relay(s) on **/
+#define MODIO2_I2C_ROFF_REG 0x42 /** Set relay(s) off **/
+
+struct MODIO2 {
+ int error;
+ uint8_t pin[64]; // max IC2 devices
+ uint8_t address[MAX_MODIO2];
+ uint8_t pin_mask[MAX_MODIO2] = { 0 };
+ uint8_t connected_ports = 0; // Number of ports coming from ModIO2 modules
+ uint8_t devices = 0; // Number of ModIO2 modules
+ char stype[9];
+ bool type = false;
+} ModIo2;
+
+uint8_t ModIo2Read(uint8_t address, uint8_t reg)
+{
+ return I2cRead8(address, reg);
+}
+
+bool ModIo2Write(uint8_t address, uint8_t reg, uint8_t val)
+{
+ return I2cWrite8(address, reg, val);
+}
+
+void ModIo2SwitchRelay(void)
+{
+ for (uint32_t i = 0; i < TasmotaGlobal.devices_present; i++) {
+ uint8_t relay_state = bitRead(XdrvMailbox.index, i);
+
+ if (ModIo2.devices > 0 && ModIo2.pin[i] < 99) {
+ uint8_t board = ModIo2.pin[i]>>3;
+ uint8_t pin = ModIo2.pin[i]&0x7;
+ uint8_t oldpinmask = ModIo2.pin_mask[board];
+
+ //AddLog(LOG_LEVEL_DEBUG, PSTR("MI2: SwitchRelay %d=%d => MODIO-%d.D%d=%d"), i, relay_state, board +1, pin, relay_state);
+ bitWrite(ModIo2.pin_mask[board], pin, relay_state);
+ if (oldpinmask != ModIo2.pin_mask[board]) {
+ ModIo2.error = SetRelay(ModIo2.address[board], ModIo2.pin_mask[board]);
+ }
+ //else AddLog(LOG_LEVEL_DEBUG, PSTR("MI2: SwitchRelay skipped"));
+ }
+ }
+}
+
+bool ModIo2IdCheck(uint8_t address)
+{
+ return MODIO2_I2C_ID_VAL == GetBoardId(address);
+}
+
+/*
+ Command code 0x20
+ Data range 0x23
+ Read the ID of the MOD-IO2. By default it must be 0x23.
+*/
+uint8_t GetBoardId(uint8_t address)
+{
+ return ModIo2Read(address, MODIO2_I2C_ID_REG);
+}
+
+/*
+ Command code 0x21
+ Data range 0x34
+ Read the firmware version of the MOD-IO2.
+*/
+uint8_t GetFirmwareVersion(uint8_t address)
+{
+ return ModIo2Read(address, MODIO2_I2C_FW_REG);
+}
+
+/*
+ Command code 0x40
+ Data range 0x00 - 0x03
+*/
+bool SetRelay(uint8_t address, uint8_t val)
+{
+ return ModIo2Write(address, MODIO2_I2C_RONOFF_REG, val);
+}
+
+/*
+ Command code 0x41
+ Data range 0x01 - 0x03
+*/
+bool SetRelayOn(uint8_t address, uint8_t val)
+{
+ return ModIo2Write(address, MODIO2_I2C_RON_REG, val);
+}
+
+/*
+ Command code 0x42
+ Data range 0x01 - 0x03
+*/
+bool SetRelayOff(uint8_t address, uint8_t val)
+{
+ return ModIo2Write(address, MODIO2_I2C_ROFF_REG, val);
+}
+
+/*
+ Command code 0x43
+ Data range 0x00 - 0x03
+ Read state of relays of the MOD-IO2. The read data is the state of the relays.
+ Bit0 is the state of RELAY1, and bit1 – RELAY2. If 1 is read the relay is turned
+ on, 0 means that it is off.
+*/
+uint8_t GetCurrentRelayState(uint8_t address)
+{
+ return ModIo2Read(address, MODIO2_I2C_STATE_REG);
+}
+
+void ModIo2Init(void)
+{
+ uint8_t modio2_address = MODIO2_ADDR_1ST;
+ while ((ModIo2.devices < MAX_MODIO2) && (modio2_address <= 0xFF)) {
+
+ // AddLog(LOG_LEVEL_DEBUG, PSTR("MI2: Probing addr: 0x%x for MODIO2"), modio2_address);
+
+ if (I2cSetDevice(modio2_address) && ModIo2IdCheck(modio2_address)) {
+ ModIo2.type = true;
+
+ ModIo2.address[ModIo2.devices] = modio2_address;
+ ModIo2.devices++;
+
+ strcpy(ModIo2.stype, "MODIO2");
+ I2cSetActiveFound(modio2_address, ModIo2.stype);
+ }
+
+ if(modio2_address == 0xFF) {
+ break;
+ }
+ modio2_address++;
+ }
+ if (ModIo2.type) {
+ for (uint32_t i = 0; i < sizeof(ModIo2.pin); i++) {
+ ModIo2.pin[i] = 99;
+ }
+ TasmotaGlobal.devices_present = TasmotaGlobal.devices_present - ModIo2.connected_ports; // reset no of devices to avoid duplicate ports on duplicate init.
+ ModIo2.connected_ports = 0; // reset no of devices to avoid duplicate ports on duplicate init.
+ for (uint32_t idx = 0; idx < ModIo2.devices; idx++) { // suport up to 14 boards MODIO2
+ uint8_t gpio = GetCurrentRelayState(ModIo2.address[idx]);
+ ModIo2.pin_mask[idx] = gpio;
+ //AddLog(LOG_LEVEL_DEBUG, PSTR("PCF: PCF-%d config=0x%02x, gpio=0x%02X"), idx +1, Settings->MODIO2_config[idx], gpio);
+
+ for (uint32_t i = 0; i < 2; i++, gpio>>=1) {
+ ModIo2.pin[TasmotaGlobal.devices_present] = i + 8 * idx;
+ if (!Settings->flag.save_state && !Settings->flag3.no_power_feedback) { // SetOption63 - Don't scan relay power state at restart - #5594 and #5663
+ uint8_t power_state = 1 & gpio;
+ bitWrite(TasmotaGlobal.power, TasmotaGlobal.devices_present, power_state);
+ bitWrite(Settings->power, TasmotaGlobal.devices_present, power_state);
+ }
+ TasmotaGlobal.devices_present++;
+ ModIo2.connected_ports++;
+ }
+ }
+ AddLog(LOG_LEVEL_INFO, PSTR("MI2: Total devices %d, MODIO2 output ports %d"), ModIo2.devices, ModIo2.connected_ports);
+ }
+}
+
+/*********************************************************************************************\
+ * Interface
+\*********************************************************************************************/
+
+bool Xdrv85(uint8_t function)
+{
+ if (!I2cEnabled(XI2C_64)) { return false; }
+
+ bool result = false;
+
+ if (FUNC_PRE_INIT == function) {
+ ModIo2Init();
+ }
+ else if (ModIo2.type) {
+ switch (function) {
+ case FUNC_SET_POWER:
+ ModIo2SwitchRelay();
+ break;
+ }
+ }
+ return result;
+}
+
+#endif // USE_MODIO2
+#endif // USE_I2C
Thank you for this, maybe somebody will find it useful indeed.
> Hm, file upload is not allowed?
Some bots always find a way trough our forums' defense layers and upload dangerous stuff.