diff --git a/docs/source/Plugin/P000_commands.repl b/docs/source/Plugin/P000_commands.repl index cf850b393..18ea544b5 100644 --- a/docs/source/Plugin/P000_commands.repl +++ b/docs/source/Plugin/P000_commands.repl @@ -50,6 +50,14 @@ See also ``AccessInfo``. ``ClearAccessBlock``" + + " + ClearPassword"," + :red:`Internal`"," + Clear the password of the unit. + See also ``Password``. + + ``ClearPassword,``" " ClearRTCram"," :red:`Internal`"," @@ -287,7 +295,8 @@ " Password"," :red:`Internal`"," - Set the password of the unit + Set the password of the unit. + See also ``ClearPassword``. ``Password,``" " diff --git a/docs/source/Plugin/P124.rst b/docs/source/Plugin/P124.rst new file mode 100644 index 000000000..3d9ecd1e7 --- /dev/null +++ b/docs/source/Plugin/P124.rst @@ -0,0 +1,147 @@ +.. include:: ../Plugin/_plugin_substitutions_p12x.repl +.. _P124_page: + +|P124_typename| +================================================== + +|P124_shortinfo| + +Plugin details +-------------- + +Type: |P124_type| + +Name: |P124_name| + +Status: |P124_status| + +GitHub: |P124_github|_ + +Maintainer: |P124_maintainer| + +Used libraries: |P124_usedlibraries| + +Description +----------- + +This plugin is the interface to the Seeed Studio I2C multi relay boards (either solid state or mechanical). + +Each channel can be switched separately, or multiple relays at once, by sending the appropriate commands (see below). + +Supported hardware +------------------ + +* `Seeed Studio Grove SPDT 4 Channel Relay`_ +* `Seeed Studio Grove 2 Channel Solid State Relay`_ +* `Seeed Studio Grove 4 Channel Solid State Relay`_ +* `Seeed Studio Grove 8 Channel Solid State Relay`_ + +.. _Seeed Studio Grove SPDT 4 Channel Relay: https://wiki.seeedstudio.com/Grove-4-Channel_SPDT_Relay/ +.. _Seeed Studio Grove 2 Channel Solid State Relay: https://wiki.seeedstudio.com/Grove-2-Channel_Solid_State_Relay/ +.. _Seeed Studio Grove 4 Channel Solid State Relay: https://wiki.seeedstudio.com/Grove-4-Channel_Solid_State_Relay/ +.. _Seeed Studio Grove 8 Channel Solid State Relay: https://wiki.seeedstudio.com/Grove-8-Channel_Solid_State_Relay/ + +Configuration +------------- + +.. image:: P124_DeviceConfiguration.png + :alt: Device Configuration + +* **Name**: A unique name should be entered here. + +* **Enabled**: The device can be disabled or enabled. When not enabled the device should not use any resources. + +I2C Options +^^^^^^^^^^^^ + +The available settings here depend on the build used. At least the **Force Slow I2C speed** option is available, but selections for the I2C Multiplexer can also be shown. For details see the :ref:`Hardware_page` + +* **I2C Address**: The address the device is using. + +.. image:: P124_I2CAddresses.png + :alt: List of supported I2C addresses + +* **Change I2C address of board**: These boards allow to change their I2C address. Once changed the address will be retained in the board non-volatile memory, until changed again. Currently addresses ``0x11`` to ``0x18`` are supported. + +The procedure to change the address is: + +* Connect only 1 of these boards to the I2C bus (other I2C devices *can* stay connected) +* Select the current board address in the **I2C Address** field. Use the Tools / I2C Scanner feature to retrieve the current board address, if needed. + +.. .. comment + +* Enable the **Change I2C address of board** option. +* Submit the page (saving the settings). This will increment the board' I2C address to the next one in the list. +* Optional: Use the Tools / I2C Scanner feature to verify that the board now has the next address. +* The **I2C Address** of the task will be updated to the new address. +* Repeat above steps until the desired address is set. After reaching the highest address, the default address ``0x11`` will be set. +* Save the settings using the Submit button. + +After changing the address, a next board can be connected that uses another board address, most likely ``0x11``. Each board will require an additional task to be configured. + +Device Settings +^^^^^^^^^^^^^^^ + +* **Number of relays**: Select the number of relays on the board connected. + +.. image:: P124_NumberOfRelaysOptions.png + :alt: Number of relays selection + +When changing the number of relays, the page will be submitted and the settings saved. + +* **Initialize relays on startup**: To initialize the relay on/off state after startup of the plugin, this option can be set to Yes. When changing the setting the page will be submitted and the settings saved. + +.. image:: P124_InitializeRelaysYes.png + :alt: Initialize relays on startup enabled + +* **Apply initial state always**: Off by default, then the initialization state will be sent only once after a restart of the ESP unit. When Enabled the initialization state will be sent on every start of the plugin, like after a Submit of the settings page. + +* **Relay # initial state (on/off)**: The on or off state the relay should be set to on initialization of the plugin. Will show a checkbox for the number of relays configured (2, 4 or 8). + +.. .. comment + +* **Reset relays on exit**: To set the relay on/off state when the plugin is disabled, this option can be set to Yes. When changing the setting the page will be submitted and the settings saved. + +.. image:: P124_ResetRelaysYes.png + :alt: Reset relays on exit enabled + +* **Relay # exit-state (on/off)**: The on or off state the relay should be set to when the plugin is disabled. Will show a checkbox for the number of relays configured (2, 4 or 8). + +.. note:: This will also be applied when Submitting the device settings page! + +* **Loop Channel/Get on read**: When enabled, will update the Channel and Get values with the selected channel and its current state (0=off, 1=on). The channel will be incremented each interval (read action). + + +Data Acquisition +^^^^^^^^^^^^^^^^ + +The Data Acquisition, Send to Controller and Interval settings are standard available configuration items. Send to Controller only when one or more Controllers are configured. + +Values +^^^^^^ + +The Values available for this sensor, are ``State``, holding the decimal value of the current channel_bitmap, ``Channel`` and ``Get``, showing the channel and its on/off state (1/0) of the last ``multirelay,get,`` command or when **Loop Channel/Get on read** is checked. + +Commands available +------------------ + +.. include:: P124_commands.repl + +.. Events +.. ~~~~~~ + +.. .. include:: P124_events.repl + +Change log +---------- + +.. versionchanged:: 2.0 + ... + + |added| + 2021-11-19 Initial release version. + + + + + diff --git a/docs/source/Plugin/P124_DeviceConfiguration.png b/docs/source/Plugin/P124_DeviceConfiguration.png new file mode 100644 index 000000000..2a7ae2b2b Binary files /dev/null and b/docs/source/Plugin/P124_DeviceConfiguration.png differ diff --git a/docs/source/Plugin/P124_I2CAddresses.png b/docs/source/Plugin/P124_I2CAddresses.png new file mode 100644 index 000000000..7a43124e3 Binary files /dev/null and b/docs/source/Plugin/P124_I2CAddresses.png differ diff --git a/docs/source/Plugin/P124_InitializeRelaysYes.png b/docs/source/Plugin/P124_InitializeRelaysYes.png new file mode 100644 index 000000000..ea156edcc Binary files /dev/null and b/docs/source/Plugin/P124_InitializeRelaysYes.png differ diff --git a/docs/source/Plugin/P124_NumberOfRelaysOptions.png b/docs/source/Plugin/P124_NumberOfRelaysOptions.png new file mode 100644 index 000000000..d11d7a56c Binary files /dev/null and b/docs/source/Plugin/P124_NumberOfRelaysOptions.png differ diff --git a/docs/source/Plugin/P124_ResetRelaysYes.png b/docs/source/Plugin/P124_ResetRelaysYes.png new file mode 100644 index 000000000..ec0560021 Binary files /dev/null and b/docs/source/Plugin/P124_ResetRelaysYes.png differ diff --git a/docs/source/Plugin/P124_commands.repl b/docs/source/Plugin/P124_commands.repl new file mode 100644 index 000000000..c39dba848 --- /dev/null +++ b/docs/source/Plugin/P124_commands.repl @@ -0,0 +1,49 @@ +.. csv-table:: + :header: "Command Syntax", "Extra information" + :widths: 20, 30 + + " + + | ``multirelay,on,`` + + "," + + | Switch on the relay at the channel given. Range is 1..8, but limited to the number the plugin is configured for. + + " + " + + | ``multirelay,off,`` + + "," + + | Switch off the relay at the channel given. Range is 1..8, but limited to the number the plugin is configured for. + + " + " + + | ``multirelay,set,`` + + "," + + | Switch on and off according to the bitmap provided, in 8..1 order. The bitmap can be given in decimal (``nn``, 1-3 digits, ``0`` to ``9``), hexadecimal (``0xnn``, 1 or 2 digits, ``0`` to ``f``) or binary (``0bnnnnnnnn``, 1 to 8 digits, ``0`` or ``1``) notation. + + " + " + + | ``multirelay,get,`` + + "," + + | Get the current state of the requested channel (checked for being in range 1 to the number of relays configured), and put that in the Channel and Get values, so it will be available to rules or a configured controller. + + " + " + + | ``multirelay,loop,0|1`` + + "," + + | Disable or enable getting the next channel state on every Interval read. Corresponds to the **Loop Channel/Get on read** option in the device settings, but does not check/uncheck that setting. + + " diff --git a/docs/source/Plugin/_Plugin.rst b/docs/source/Plugin/_Plugin.rst index 5ffb5ab75..25d77e752 100644 --- a/docs/source/Plugin/_Plugin.rst +++ b/docs/source/Plugin/_Plugin.rst @@ -142,6 +142,7 @@ There are different released versions of ESP Easy: ":ref:`P115_page`","|P115_status|","P115" ":ref:`P116_page`","|P116_status|","P116" ":ref:`P117_page`","|P117_status|","P117" + ":ref:`P124_page`","|P124_status|","P124" Internal GPIO handling diff --git a/docs/source/Plugin/_plugin_categories.repl b/docs/source/Plugin/_plugin_categories.repl index 0766113ba..3c53231ff 100644 --- a/docs/source/Plugin/_plugin_categories.repl +++ b/docs/source/Plugin/_plugin_categories.repl @@ -19,7 +19,7 @@ .. |Plugin_Light_Lux| replace:: :ref:`P010_page`, :ref:`P015_page`, :ref:`P074_page` .. |Plugin_Motor| replace:: :ref:`P048_page`, :ref:`P079_page` .. |Plugin_Notify| replace:: :ref:`P055_page`, :ref:`P065_page` -.. |Plugin_Output| replace:: :ref:`P029_page`, :ref:`P038_page`, :ref:`P041_page`, :ref:`P042_page`, :ref:`P043_page`, :ref:`P070_page` +.. |Plugin_Output| replace:: :ref:`P029_page`, :ref:`P038_page`, :ref:`P041_page`, :ref:`P042_page`, :ref:`P043_page`, :ref:`P070_page`, :ref:`P124_page` .. |Plugin_Position| replace:: :ref:`P013_page`, :ref:`P082_page` .. |Plugin_Regulator| replace:: :ref:`P021_page` .. |Plugin_RFID| replace:: :ref:`P008_page`, :ref:`P017_page`, :ref:`P040_page`, :ref:`P111_page` diff --git a/docs/source/Plugin/_plugin_substitutions.repl b/docs/source/Plugin/_plugin_substitutions.repl index 5b21be98c..9f0022404 100644 --- a/docs/source/Plugin/_plugin_substitutions.repl +++ b/docs/source/Plugin/_plugin_substitutions.repl @@ -10,3 +10,4 @@ .. include:: ../Plugin/_plugin_substitutions_p09x.repl .. include:: ../Plugin/_plugin_substitutions_p10x.repl .. include:: ../Plugin/_plugin_substitutions_p11x.repl +.. include:: ../Plugin/_plugin_substitutions_p12x.repl diff --git a/docs/source/Plugin/_plugin_substitutions_p12x.repl b/docs/source/Plugin/_plugin_substitutions_p12x.repl new file mode 100644 index 000000000..5288de500 --- /dev/null +++ b/docs/source/Plugin/_plugin_substitutions_p12x.repl @@ -0,0 +1,12 @@ +.. |P124_name| replace:: :cyan:`MultiRelay` +.. |P124_type| replace:: :cyan:`Output` +.. |P124_typename| replace:: :cyan:`Output - I2C Multi Relay` +.. |P124_porttype| replace:: `.` +.. |P124_status| replace:: :yellow:`TESTING D` +.. |P124_github| replace:: P124_MultiTelay.ino +.. _P124_github: https://github.com/letscontrolit/ESPEasy/blob/mega/src/_P124_MyltiRelay.ino +.. |P124_usedby| replace:: `.` +.. |P124_shortinfo| replace:: `Seeed Studio I2C Multi Relay boards` +.. |P124_maintainer| replace:: `tonhuisman` +.. |P124_compileinfo| replace:: `.` +.. |P124_usedlibraries| replace:: `https://github.com/Seeed-Studio/Multi_Channel_Relay_Arduino_Library (modified local copy)` diff --git a/docs/source/Reference/Command.rst b/docs/source/Reference/Command.rst index f06384797..639f460ec 100644 --- a/docs/source/Reference/Command.rst +++ b/docs/source/Reference/Command.rst @@ -664,8 +664,12 @@ P115 :ref:`P115_page` .. include:: ../Plugin/P115_commands.repl - P117 :ref:`P117_page` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. include:: ../Plugin/P117_commands.repl + +P124 :ref:`P124_page` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +.. include:: ../Plugin/P124_commands.repl diff --git a/docs/source/Rules/Rules.rst b/docs/source/Rules/Rules.rst index d735938f1..6baf9fb11 100644 --- a/docs/source/Rules/Rules.rst +++ b/docs/source/Rules/Rules.rst @@ -7,6 +7,10 @@ Introduction Along with ESP Easy R108, a new feature was enabled, named Rules. Rules can be used to create very simple flows to control devices on your ESP. +.. note:: + To assist writing rules, one may prefer to use an editor like Notepad++ which supports user defined languages to colorize the text. + See the ``Misc/Notepad++`` folder for a Notepad++ language definition which can be used to colorize rules. + Enable Rules ------------ diff --git a/lib/Itho/CC1101.cpp b/lib/Itho/CC1101.cpp new file mode 100644 index 000000000..903a6cab7 --- /dev/null +++ b/lib/Itho/CC1101.cpp @@ -0,0 +1,276 @@ +/* + * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 + */ + +#include "CC1101.h" + +// default constructor +CC1101::CC1101() +{ + SPI.begin(); +#ifdef ESP8266 + pinMode(SS, OUTPUT); +#endif +} //CC1101 + +// default destructor +CC1101::~CC1101() +{ +} //~CC1101 + +/***********************/ +// SPI helper functions select() and deselect() +inline void CC1101::select(void) { + digitalWrite(SS, LOW); +} + +inline void CC1101::deselect(void) { + digitalWrite(SS, HIGH); +} + +void CC1101::spi_waitMiso() +{ + while(digitalRead(MISO) == HIGH) yield(); +} + +void CC1101::init() +{ + reset(); +} + +void CC1101::reset() +{ + deselect(); + delayMicroseconds(5); + select(); + delayMicroseconds(10); + deselect(); + delayMicroseconds(45); + select(); + + spi_waitMiso(); + SPI.transfer(CC1101_SRES); + delay(10); + spi_waitMiso(); + deselect(); +} + +uint8_t CC1101::writeCommand(uint8_t command) +{ + uint8_t result; + + select(); + spi_waitMiso(); + result = SPI.transfer(command); + deselect(); + + return result; +} + +void CC1101::writeRegister(uint8_t address, uint8_t data) +{ + select(); + spi_waitMiso(); + SPI.transfer(address); + SPI.transfer(data); + deselect(); +} + +uint8_t CC1101::readRegister(uint8_t address) +{ + uint8_t val; + + select(); + spi_waitMiso(); + SPI.transfer(address); + val = SPI.transfer(0); + deselect(); + + return val; +} + +uint8_t CC1101::readRegisterMedian3(uint8_t address) +{ + uint8_t val, val1, val2, val3; + + select(); + spi_waitMiso(); + SPI.transfer(address); + val1 = SPI.transfer(0); + SPI.transfer(address); + val2 = SPI.transfer(0); + SPI.transfer(address); + val3 = SPI.transfer(0); + deselect(); + // reverse sort (largest in val1) because this is te expected order for TX_BUFFER + if (val3 > val2) {val = val3; val3 = val2; val2 = val; } //Swap(val3,val2) + if (val2 > val1) {val = val2; val2 = val1, val1 = val; } //Swap(val2,val1) + if (val3 > val2) {val = val3; val3 = val2, val2 = val; } //Swap(val3,val2) + + return val2; +} + +/* Known SPI/26MHz synchronization bug (see CC1101 errata) +This issue affects the following registers: SPI status byte (fields STATE and FIFO_BYTES_AVAILABLE), +FREQEST or RSSI while the receiver is active, MARCSTATE at any time other than an IDLE radio state, +RXBYTES when receiving or TXBYTES when transmitting, and WORTIME1/WORTIME0 at any time.*/ +//uint8_t CC1101::readRegisterWithSyncProblem(uint8_t address, uint8_t registerType) +uint8_t /* ICACHE_RAM_ATTR */ CC1101::readRegisterWithSyncProblem(uint8_t address, uint8_t registerType) +{ + uint8_t value1, value2; + + value1 = readRegister(address | registerType); + + //if two consecutive reads gives us the same result then we know we are ok + do + { + value2 = value1; + value1 = readRegister(address | registerType); + } + while (value1 != value2); + + return value1; +} + +//registerType = CC1101_CONFIG_REGISTER or CC1101_STATUS_REGISTER +uint8_t CC1101::readRegister(uint8_t address, uint8_t registerType) +{ + switch (address) + { + case CC1101_FREQEST: + case CC1101_MARCSTATE: + case CC1101_RXBYTES: + case CC1101_TXBYTES: + case CC1101_WORTIME1: + case CC1101_WORTIME0: + return readRegisterWithSyncProblem(address, registerType); + + default: + return readRegister(address | registerType); + } +} + +void CC1101::writeBurstRegister(uint8_t address, uint8_t* data, uint8_t length) +{ + uint8_t i; + + select(); + spi_waitMiso(); + SPI.transfer(address | CC1101_WRITE_BURST); + for (i = 0; i < length; i++) { + SPI.transfer(data[i]); + } + deselect(); +} + +void CC1101::readBurstRegister(uint8_t* buffer, uint8_t address, uint8_t length) +{ + uint8_t i; + + select(); + spi_waitMiso(); + SPI.transfer(address | CC1101_READ_BURST); + + for (i = 0; i < length; i++) { + buffer[i] = SPI.transfer(0x00); + } + + deselect(); +} + +//wait for fixed length in rx fifo +uint8_t CC1101::receiveData(CC1101Packet* packet, uint8_t length) +{ + uint8_t rxBytes = readRegisterWithSyncProblem(CC1101_RXBYTES, CC1101_STATUS_REGISTER); + rxBytes = rxBytes & CC1101_BITS_RX_BYTES_IN_FIFO; + + //check for rx fifo overflow + if ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) & CC1101_BITS_MARCSTATE) == CC1101_MARCSTATE_RXFIFO_OVERFLOW) + { + writeCommand(CC1101_SIDLE); //idle + writeCommand(CC1101_SFRX); //flush RX buffer + writeCommand(CC1101_SRX); //switch to RX state + } + else if (rxBytes == length) + { + readBurstRegister(packet->data, CC1101_RXFIFO, rxBytes); + + //continue RX + writeCommand(CC1101_SIDLE); //idle + writeCommand(CC1101_SFRX); //flush RX buffer + writeCommand(CC1101_SRX); //switch to RX state + + packet->length = rxBytes; + } + else + { + //empty fifo + packet->length = 0; + writeCommand(CC1101_SIDLE); //idle + writeCommand(CC1101_SFRX); //flush RX buffer + writeCommand(CC1101_SRX); //switch to RX state + } + + return packet->length; +} + +//This function is able to send packets bigger then the FIFO size. +void CC1101::sendData(CC1101Packet *packet) +{ + uint8_t index = 0; + uint8_t txStatus, MarcState; + uint8_t length; + + writeCommand(CC1101_SIDLE); //idle + + txStatus = readRegisterWithSyncProblem(CC1101_TXBYTES, CC1101_STATUS_REGISTER); + + //clear TX fifo if needed + if (txStatus & CC1101_BITS_TX_FIFO_UNDERFLOW) + { + writeCommand(CC1101_SIDLE); //idle + writeCommand(CC1101_SFTX); //flush TX buffer + } + + writeCommand(CC1101_SIDLE); //idle + + //determine how many bytes to send + length = (packet->length <= CC1101_DATA_LEN ? packet->length : CC1101_DATA_LEN); + + writeBurstRegister(CC1101_TXFIFO, packet->data, length); + + writeCommand(CC1101_SIDLE); + //start sending packet + writeCommand(CC1101_STX); + + //continue sending when packet is bigger than 64 bytes + if (packet->length > CC1101_DATA_LEN) + { + index += length; + + //loop until all bytes are transmitted + while (index < packet->length) + { + //check if there is free space in the fifo + while ((txStatus = (readRegisterMedian3(CC1101_TXBYTES | CC1101_STATUS_REGISTER) & CC1101_BITS_RX_BYTES_IN_FIFO)) > (CC1101_DATA_LEN - 2)); + + //calculate how many bytes we can send + length = (CC1101_DATA_LEN - txStatus); + length = ((packet->length - index) < length ? (packet->length - index) : length); + + //send some more bytes + for (int i=0; idata[index+i]); + + index += length; + } + } + + //wait until transmission is finished (TXOFF_MODE is expected to be set to 0/IDLE or TXFIFO_UNDERFLOW) + do + { + MarcState = (readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) & CC1101_BITS_MARCSTATE); +// if (MarcState == CC1101_MARCSTATE_TXFIFO_UNDERFLOW) Serial.print(F("TXFIFO_UNDERFLOW occured in sendData() \n")); + } + while((MarcState != CC1101_MARCSTATE_IDLE) && (MarcState != CC1101_MARCSTATE_TXFIFO_UNDERFLOW)); +} diff --git a/lib/Itho/CC1101.h b/lib/Itho/CC1101.h new file mode 100644 index 000000000..b30e0162a --- /dev/null +++ b/lib/Itho/CC1101.h @@ -0,0 +1,207 @@ +/* + * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 + */ + +#ifndef __CC1101_H__ +#define __CC1101_H__ + +#include +#include "CC1101Packet.h" +#include +// On Arduino, SPI pins are predefined + +/* Type of transfers */ +#define CC1101_WRITE_BURST 0x40 +#define CC1101_READ_SINGLE 0x80 +#define CC1101_READ_BURST 0xC0 + +/* Type of register */ +#define CC1101_CONFIG_REGISTER CC1101_READ_SINGLE +#define CC1101_STATUS_REGISTER CC1101_READ_BURST + +/* PATABLE & FIFO's */ +#define CC1101_PATABLE 0x3E // PATABLE address +#define CC1101_TXFIFO 0x3F // TX FIFO address +#define CC1101_RXFIFO 0x3F // RX FIFO address +#define CC1101_PA_LowPower 0x60 +#define CC1101_PA_LongDistance 0xC0 + +/* Command strobes */ +#define CC1101_SRES 0x30 // Reset CC1101 chip +#define CC1101_SFSTXON 0x31 // Enable and calibrate frequency synthesizer (if MCSM0.FS_AUTOCAL=1). If in RX (with CCA): Go to a wait state where only the synthesizer is running (for quick RX / TX turnaround). +#define CC1101_SXOFF 0x32 // Turn off crystal oscillator +#define CC1101_SCAL 0x33 // Calibrate frequency synthesizer and turn it off. SCAL can be strobed from IDLE mode without setting manual calibration mode (MCSM0.FS_AUTOCAL=0) +#define CC1101_SRX 0x34 // Enable RX. Perform calibration first if coming from IDLE and MCSM0.FS_AUTOCAL=1 +#define CC1101_STX 0x35 // In IDLE state: Enable TX. Perform calibration first if MCSM0.FS_AUTOCAL=1. If in RX state and CCA is enabled: Only go to TX if channel is clear +#define CC1101_SIDLE 0x36 // Exit RX / TX, turn off frequency synthesizer and exit Wake-On-Radio mode if applicable +#define CC1101_SWOR 0x38 // Start automatic RX polling sequence (Wake-on-Radio) as described in Section 19.5 if WORCTRL.RC_PD=0 +#define CC1101_SPWD 0x39 // Enter power down mode when CSn goes high +#define CC1101_SFRX 0x3A // Flush the RX FIFO buffer. Only issue SFRX in IDLE or RXFIFO_OVERFLOW states +#define CC1101_SFTX 0x3B // Flush the TX FIFO buffer. Only issue SFTX in IDLE or TXFIFO_UNDERFLOW states +#define CC1101_SWORRST 0x3C // Reset real time clock to Event1 value +#define CC1101_SNOP 0x3D // No operation. May be used to get access to the chip status byte + +/* CC1101 configuration registers */ +#define CC1101_IOCFG2 0x00 // GDO2 Output Pin Configuration +#define CC1101_IOCFG1 0x01 // GDO1 Output Pin Configuration +#define CC1101_IOCFG0 0x02 // GDO0 Output Pin Configuration +#define CC1101_FIFOTHR 0x03 // RX FIFO and TX FIFO Thresholds +#define CC1101_SYNC1 0x04 // Sync Word, High Byte +#define CC1101_SYNC0 0x05 // Sync Word, Low Byte +#define CC1101_PKTLEN 0x06 // Packet Length +#define CC1101_PKTCTRL1 0x07 // Packet Automation Control +#define CC1101_PKTCTRL0 0x08 // Packet Automation Control +#define CC1101_ADDR 0x09 // Device Address +#define CC1101_CHANNR 0x0A // Channel Number +#define CC1101_FSCTRL1 0x0B // Frequency Synthesizer Control +#define CC1101_FSCTRL0 0x0C // Frequency Synthesizer Control +#define CC1101_FREQ2 0x0D // Frequency Control Word, High Byte +#define CC1101_FREQ1 0x0E // Frequency Control Word, Middle Byte +#define CC1101_FREQ0 0x0F // Frequency Control Word, Low Byte +#define CC1101_MDMCFG4 0x10 // Modem Configuration +#define CC1101_MDMCFG3 0x11 // Modem Configuration +#define CC1101_MDMCFG2 0x12 // Modem Configuration +#define CC1101_MDMCFG1 0x13 // Modem Configuration +#define CC1101_MDMCFG0 0x14 // Modem Configuration +#define CC1101_DEVIATN 0x15 // Modem Deviation Setting +#define CC1101_MCSM2 0x16 // Main Radio Control State Machine Configuration +#define CC1101_MCSM1 0x17 // Main Radio Control State Machine Configuration +#define CC1101_MCSM0 0x18 // Main Radio Control State Machine Configuration +#define CC1101_FOCCFG 0x19 // Frequency Offset Compensation Configuration +#define CC1101_BSCFG 0x1A // Bit Synchronization Configuration +#define CC1101_AGCCTRL2 0x1B // AGC Control +#define CC1101_AGCCTRL1 0x1C // AGC Control +#define CC1101_AGCCTRL0 0x1D // AGC Control +#define CC1101_WOREVT1 0x1E // High Byte Event0 Timeout +#define CC1101_WOREVT0 0x1F // Low Byte Event0 Timeout +#define CC1101_WORCTRL 0x20 // Wake On Radio Control +#define CC1101_FREND1 0x21 // Front End RX Configuration +#define CC1101_FREND0 0x22 // Front End TX Configuration +#define CC1101_FSCAL3 0x23 // Frequency Synthesizer Calibration +#define CC1101_FSCAL2 0x24 // Frequency Synthesizer Calibration +#define CC1101_FSCAL1 0x25 // Frequency Synthesizer Calibration +#define CC1101_FSCAL0 0x26 // Frequency Synthesizer Calibration +#define CC1101_RCCTRL1 0x27 // RC Oscillator Configuration +#define CC1101_RCCTRL0 0x28 // RC Oscillator Configuration +#define CC1101_FSTEST 0x29 // Frequency Synthesizer Calibration Control +#define CC1101_PTEST 0x2A // Production Test +#define CC1101_AGCTEST 0x2B // AGC Test +#define CC1101_TEST2 0x2C // Various Test Settings +#define CC1101_TEST1 0x2D // Various Test Settings +#define CC1101_TEST0 0x2E // Various Test Settings + +/* Status registers */ +#define CC1101_PARTNUM 0x30 // Chip ID +#define CC1101_VERSION 0x31 // Chip ID +#define CC1101_FREQEST 0x32 // Frequency Offset Estimate from Demodulator +#define CC1101_LQI 0x33 // Demodulator Estimate for Link Quality +#define CC1101_RSSI 0x34 // Received Signal Strength Indication +#define CC1101_MARCSTATE 0x35 // Main Radio Control State Machine State +#define CC1101_WORTIME1 0x36 // High Byte of WOR Time +#define CC1101_WORTIME0 0x37 // Low Byte of WOR Time +#define CC1101_PKTSTATUS 0x38 // Current GDOx Status and Packet Status +#define CC1101_VCO_VC_DAC 0x39 // Current Setting from PLL Calibration Module +#define CC1101_TXBYTES 0x3A // Underflow and Number of Bytes +#define CC1101_RXBYTES 0x3B // Overflow and Number of Bytes +#define CC1101_RCCTRL1_STATUS 0x3C // Last RC Oscillator Calibration Result +#define CC1101_RCCTRL0_STATUS 0x3D // Last RC Oscillator Calibration Result + +/* Bit fields in the chip status byte */ +#define CC1101_STATUS_CHIP_RDYn_BM 0x80 +#define CC1101_STATUS_STATE_BM 0x70 +#define CC1101_STATUS_FIFO_BYTES_AVAILABLE_BM 0x0F + +/* Masks to retrieve status bit */ +#define CC1101_BITS_TX_FIFO_UNDERFLOW 0x80 +#define CC1101_BITS_RX_BYTES_IN_FIFO 0x7F +#define CC1101_BITS_MARCSTATE 0x1F + + +/* Marc states */ +enum CC1101MarcStates +{ + CC1101_MARCSTATE_SLEEP = 0x00, + CC1101_MARCSTATE_IDLE = 0x01, + CC1101_MARCSTATE_XOFF = 0x02, + CC1101_MARCSTATE_VCOON_MC = 0x03, + CC1101_MARCSTATE_REGON_MC = 0x04, + CC1101_MARCSTATE_MANCAL = 0x05, + CC1101_MARCSTATE_VCOON = 0x06, + CC1101_MARCSTATE_REGON = 0x07, + CC1101_MARCSTATE_STARTCAL = 0x08, + CC1101_MARCSTATE_BWBOOST = 0x09, + CC1101_MARCSTATE_FS_LOCK = 0x0A, + CC1101_MARCSTATE_IFADCON = 0x0B, + CC1101_MARCSTATE_ENDCAL = 0x0C, + CC1101_MARCSTATE_RX = 0x0D, + CC1101_MARCSTATE_RX_END = 0x0E, + CC1101_MARCSTATE_RX_RST = 0x0F, + CC1101_MARCSTATE_TXRX_SWITCH = 0x10, + CC1101_MARCSTATE_RXFIFO_OVERFLOW = 0x11, + CC1101_MARCSTATE_FSTXON = 0x12, + CC1101_MARCSTATE_TX = 0x13, + CC1101_MARCSTATE_TX_END = 0x14, + CC1101_MARCSTATE_RXTX_SWITCH = 0x15, + CC1101_MARCSTATE_TXFIFO_UNDERFLOW = 0x16 +}; + + +/* Chip states */ +enum CC1101ChipStates +{ + CC1101_STATE_MASK = 0x70, + CC1101_STATE_IDLE = 0x00, + CC1101_STATE_RX = 0x10, + CC1101_STATE_TX = 0x20, + CC1101_STATE_FSTXON = 0x30, + CC1101_STATE_CALIBRATE = 0x40, + CC1101_STATE_SETTLING = 0x50, + CC1101_STATE_RX_OVERFLOW = 0x60, + CC1101_STATE_TX_UNDERFLOW = 0x70 +}; + + + +class CC1101 +{ + protected: + + //functions + public: + CC1101(); + ~CC1101(); + + //spi + void spi_waitMiso(); + + //cc1101 + void init(); + + uint8_t writeCommand(uint8_t command); + void writeRegister(uint8_t address, uint8_t data); + + uint8_t readRegister(uint8_t address, uint8_t registerType); + + void writeBurstRegister(uint8_t address, uint8_t* data, uint8_t length); + void readBurstRegister(uint8_t* buffer, uint8_t address, uint8_t length); + + void sendData(CC1101Packet *packet); + uint8_t receiveData(CC1101Packet* packet, uint8_t length); + + private: + CC1101( const CC1101 &c ); + CC1101& operator=( const CC1101 &c ); + // SPI helper functions + void select(void); + void deselect(void); + + protected: + uint8_t readRegister(uint8_t address); + uint8_t readRegisterMedian3(uint8_t address); + uint8_t readRegisterWithSyncProblem(uint8_t address, uint8_t registerType); + + void reset(); + +}; //CC1101 + +#endif //__CC1101_H__ diff --git a/lib/Itho/CC1101Packet.h b/lib/Itho/CC1101Packet.h new file mode 100644 index 000000000..250c63458 --- /dev/null +++ b/lib/Itho/CC1101Packet.h @@ -0,0 +1,23 @@ +/* + * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 + */ + +#ifndef CC1101PACKET_H_ +#define CC1101PACKET_H_ + +#include +#include + +#define CC1101_BUFFER_LEN 64 +#define CC1101_DATA_LEN CC1101_BUFFER_LEN - 3 + + +class CC1101Packet +{ + public: + uint8_t length = 0; + uint8_t data[128] = {0}; +}; + + +#endif /* CC1101PACKET_H_ */ diff --git a/lib/Itho/IthoCC1101.cpp b/lib/Itho/IthoCC1101.cpp new file mode 100644 index 000000000..9e2d0961f --- /dev/null +++ b/lib/Itho/IthoCC1101.cpp @@ -0,0 +1,850 @@ +/* + Author: Klusjesman, supersjimmie, modified and reworked by arjenhiemstra +*/ +//#define DEBUG 0 + +//#define BYTE_TO_BINARY_PATTERN "%c,%c,%c,%c,%c,%c,%c,%c," +/*#define BYTE_TO_BINARY(byte) \ + (byte & 0x80 ? '1' : '0'), \ + (byte & 0x40 ? '1' : '0'), \ + (byte & 0x20 ? '1' : '0'), \ + (byte & 0x10 ? '1' : '0'), \ + (byte & 0x08 ? '1' : '0'), \ + (byte & 0x04 ? '1' : '0'), \ + (byte & 0x02 ? '1' : '0'), \ + (byte & 0x01 ? '1' : '0')*/ + +#include "IthoCC1101.h" +#include +#include +#include + +//#define CRC_FILTER + +////original sync byte pattern +//#define STARTBYTE 6 //relevant data starts 6 bytes after the sync pattern bytes 170/171 +//#define SYNC1 170 +//#define SYNC0 171 +//#define MDMCFG2 0x02 //16bit sync word / 16bit specific + +////alternative sync byte pattern (filter much more non-itho messages out. Maybe too strict? Testing needed. +//#define STARTBYTE 0 //relevant data starts 0 bytes after the sync pattern bytes 179/42/171/42 +//#define SYNC1 187 //byte11 = 179, byte13 = 171 with SYNC1 = 163, 179 and 171 differ only by 1 bit +//#define SYNC0 42 +//#define MDMCFG2 0x03 //32bit sync word / 30bit specific + +//alternative sync byte pattern +#define STARTBYTE 2 //relevant data starts 2 bytes after the sync pattern bytes 179/42 +#define SYNC1 179 +#define SYNC0 42 +#define MDMCFG2 0x02 //16bit sync word / 16bit specific + +// default constructor +IthoCC1101::IthoCC1101(uint8_t counter, uint8_t sendTries) : CC1101() +{ + this->outIthoPacket.counter = counter; + this->sendTries = sendTries; + + this->outIthoPacket.deviceId[0] = 33; + this->outIthoPacket.deviceId[1] = 66; + this->outIthoPacket.deviceId[2] = 99; + + this->outIthoPacket.deviceType = 22; + +} //IthoCC1101 + +// default destructor +IthoCC1101::~IthoCC1101() +{ +} //~IthoCC1101 + +void IthoCC1101::initSendMessage(uint8_t len) +{ + //finishTransfer(); + writeCommand(CC1101_SIDLE); + delayMicroseconds(1); + writeRegister(CC1101_IOCFG0 , 0x2E); + delayMicroseconds(1); + writeRegister(CC1101_IOCFG1 , 0x2E); + delayMicroseconds(1); + writeCommand(CC1101_SIDLE); + writeCommand(CC1101_SPWD); + delayMicroseconds(2); + + /* + Configuration reverse engineered from remote print. The commands below are used by IthoDaalderop. + Base frequency 868.299866MHz + Channel 0 + Channel spacing 199.951172kHz + Carrier frequency 868.299866MHz + Xtal frequency 26.000000MHz + Data rate 38.3835kBaud + Manchester disabled + Modulation 2-FSK + Deviation 50.781250kHz + TX power ? + PA ramping enabled + Whitening disabled + */ + writeCommand(CC1101_SRES); + delayMicroseconds(1); + writeRegister(CC1101_IOCFG0 , 0x2E); //High impedance (3-state) + writeRegister(CC1101_FREQ2 , 0x21); //00100001 878MHz-927.8MHz + writeRegister(CC1101_FREQ1 , 0x65); //01100101 + writeRegister(CC1101_FREQ0 , 0x6A); //01101010 + writeRegister(CC1101_MDMCFG4 , 0x5A); //difference compared to message1 + writeRegister(CC1101_MDMCFG3 , 0x83); //difference compared to message1 + writeRegister(CC1101_MDMCFG2 , 0x00); //00000000 2-FSK, no manchester encoding/decoding, no preamble/sync + writeRegister(CC1101_MDMCFG1 , 0x22); //00100010 + writeRegister(CC1101_MDMCFG0 , 0xF8); //11111000 + writeRegister(CC1101_CHANNR , 0x00); //00000000 + writeRegister(CC1101_DEVIATN , 0x50); //difference compared to message1 + writeRegister(CC1101_FREND0 , 0x17); //00010111 use index 7 in PA table + writeRegister(CC1101_MCSM0 , 0x18); //00011000 PO timeout Approx. 146microseconds - 171microseconds, Auto calibrate When going from IDLE to RX or TX (or FSTXON) + writeRegister(CC1101_FSCAL3 , 0xA9); //10101001 + writeRegister(CC1101_FSCAL2 , 0x2A); //00101010 + writeRegister(CC1101_FSCAL1 , 0x00); //00000000 + writeRegister(CC1101_FSCAL0 , 0x11); //00010001 + writeRegister(CC1101_FSTEST , 0x59); //01011001 For test only. Do not write to this register. + writeRegister(CC1101_TEST2 , 0x81); //10000001 For test only. Do not write to this register. + writeRegister(CC1101_TEST1 , 0x35); //00110101 For test only. Do not write to this register. + writeRegister(CC1101_TEST0 , 0x0B); //00001011 For test only. Do not write to this register. + writeRegister(CC1101_PKTCTRL0 , 0x12); //00010010 Enable infinite length packets, CRC disabled, Turn data whitening off, Serial Synchronous mode + writeRegister(CC1101_ADDR , 0x00); //00000000 + writeRegister(CC1101_PKTLEN , 0xFF); //11111111 //Not used, no hardware packet handling + + //0x6F,0x26,0x2E,0x8C,0x87,0xCD,0xC7,0xC0 + writeBurstRegister(CC1101_PATABLE | CC1101_WRITE_BURST, (uint8_t*)ithoPaTableSend, 8); + + //difference, message1 sends a STX here + writeCommand(CC1101_SIDLE); + writeCommand(CC1101_SIDLE); + + writeRegister(CC1101_MDMCFG4 , 0x5A); //difference compared to message1 + writeRegister(CC1101_MDMCFG3 , 0x83); //difference compared to message1 + writeRegister(CC1101_DEVIATN , 0x50); //difference compared to message1 + writeRegister(CC1101_IOCFG0 , 0x2D); //GDO0_Z_EN_N. When this output is 0, GDO0 is configured as input (for serial TX data). + writeRegister(CC1101_IOCFG1 , 0x0B); //Serial Clock. Synchronous to the data in synchronous serial mode. + + writeCommand(CC1101_STX); + writeCommand(CC1101_SIDLE); + + writeRegister(CC1101_MDMCFG4 , 0x5A); //difference compared to message1 + writeRegister(CC1101_MDMCFG3 , 0x83); //difference compared to message1 + writeRegister(CC1101_DEVIATN , 0x50); //difference compared to message1 + //writeRegister(CC1101_IOCFG0 ,0x2D); //GDO0_Z_EN_N. When this output is 0, GDO0 is configured as input (for serial TX data). + //writeRegister(CC1101_IOCFG1 ,0x0B); //Serial Clock. Synchronous to the data in synchronous serial mode. + + //Itho is using serial mode for transmit. We want to use the TX FIFO with fixed packet length for simplicity. + writeRegister(CC1101_IOCFG0 , 0x2E); + writeRegister(CC1101_IOCFG1 , 0x2E); + writeRegister(CC1101_PKTCTRL0 , 0x00); + writeRegister(CC1101_PKTCTRL1 , 0x00); + + writeRegister(CC1101_PKTLEN , len); + +} + +void IthoCC1101::finishTransfer() +{ + writeCommand(CC1101_SIDLE); + delayMicroseconds(1); + + writeRegister(CC1101_IOCFG0 , 0x2E); + writeRegister(CC1101_IOCFG1 , 0x2E); + + writeCommand(CC1101_SIDLE); + writeCommand(CC1101_SPWD); +} + +void IthoCC1101::initReceive() +{ + /* + Configuration reverse engineered from RFT print. + + Base frequency 868.299866MHz + Channel 0 + Channel spacing 199.951172kHz + Carrier frequency 868.299866MHz + Xtal frequency 26.000000MHz + Data rate 38.3835kBaud + RX filter BW 325.000000kHz + Manchester disabled + Modulation 2-FSK + Deviation 50.781250kHz + TX power 0x6F,0x26,0x2E,0x7F,0x8A,0x84,0xCA,0xC4 + PA ramping enabled + Whitening disabled + */ + writeCommand(CC1101_SRES); + + writeRegister(CC1101_TEST0 , 0x09); + writeRegister(CC1101_FSCAL2 , 0x00); + + //0x6F,0x26,0x2E,0x7F,0x8A,0x84,0xCA,0xC4 + writeBurstRegister(CC1101_PATABLE | CC1101_WRITE_BURST, (uint8_t*)ithoPaTableReceive, 8); + + writeCommand(CC1101_SCAL); + + //wait for calibration to finish + while ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER)) != CC1101_MARCSTATE_IDLE) yield(); + + writeRegister(CC1101_FSCAL2 , 0x00); + writeRegister(CC1101_MCSM0 , 0x18); //no auto calibrate + writeRegister(CC1101_FREQ2 , 0x21); + writeRegister(CC1101_FREQ1 , 0x65); + writeRegister(CC1101_FREQ0 , 0x6A); + writeRegister(CC1101_IOCFG0 , 0x2E); //High impedance (3-state) + writeRegister(CC1101_IOCFG2 , 0x06); //0x06 Assert when sync word has been sent / received, and de-asserts at the end of the packet. + writeRegister(CC1101_FSCTRL1 , 0x06); + writeRegister(CC1101_FSCTRL0 , 0x00); + writeRegister(CC1101_MDMCFG4 , 0x5A); + writeRegister(CC1101_MDMCFG3 , 0x83); + writeRegister(CC1101_MDMCFG2 , 0x00); //Enable digital DC blocking filter before demodulator, 2-FSK, Disable Manchester encoding/decoding, No preamble/sync + writeRegister(CC1101_MDMCFG1 , 0x22); //Disable FEC + writeRegister(CC1101_MDMCFG0 , 0xF8); + writeRegister(CC1101_CHANNR , 0x00); + writeRegister(CC1101_DEVIATN , 0x50); + writeRegister(CC1101_FREND1 , 0x56); + writeRegister(CC1101_FREND0 , 0x17); + writeRegister(CC1101_MCSM0 , 0x18); //no auto calibrate + writeRegister(CC1101_FOCCFG , 0x16); + writeRegister(CC1101_BSCFG , 0x6C); + writeRegister(CC1101_AGCCTRL2 , 0x43); + writeRegister(CC1101_AGCCTRL1 , 0x40); + writeRegister(CC1101_AGCCTRL0 , 0x91); + writeRegister(CC1101_FSCAL3 , 0xE9); + writeRegister(CC1101_FSCAL2 , 0x2A); + writeRegister(CC1101_FSCAL1 , 0x00); + writeRegister(CC1101_FSCAL0 , 0x11); + writeRegister(CC1101_FSTEST , 0x59); + writeRegister(CC1101_TEST2 , 0x81); + writeRegister(CC1101_TEST1 , 0x35); + writeRegister(CC1101_TEST0 , 0x0B); + writeRegister(CC1101_PKTCTRL1 , 0x04); //No address check, Append two bytes with status RSSI/LQI/CRC OK, + writeRegister(CC1101_PKTCTRL0 , 0x32); //Infinite packet length mode, CRC disabled for TX and RX, No data whitening, Asynchronous serial mode, Data in on GDO0 and data out on either of the GDOx pins + writeRegister(CC1101_ADDR , 0x00); + writeRegister(CC1101_PKTLEN , 0xFF); + writeRegister(CC1101_TEST0 , 0x09); + + writeCommand(CC1101_SCAL); + + //wait for calibration to finish + while ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER)) != CC1101_MARCSTATE_IDLE) yield(); + + writeRegister(CC1101_MCSM0 , 0x18); //no auto calibrate + + writeCommand(CC1101_SIDLE); + writeCommand(CC1101_SIDLE); + + writeRegister(CC1101_MDMCFG2 , 0x00); //Enable digital DC blocking filter before demodulator, 2-FSK, Disable Manchester encoding/decoding, No preamble/sync + writeRegister(CC1101_IOCFG0 , 0x0D); //Serial Data Output. Used for asynchronous serial mode. + + writeCommand(CC1101_SRX); + + while ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER)) != CC1101_MARCSTATE_RX) yield(); + + initReceiveMessage(); +} + +void IthoCC1101::initReceiveMessage() +{ + uint8_t marcState; + + writeCommand(CC1101_SIDLE); //idle + + //set datarate + writeRegister(CC1101_MDMCFG4 , 0x5A); // set kBaud + writeRegister(CC1101_MDMCFG3 , 0x83); // set kBaud + writeRegister(CC1101_DEVIATN , 0x50); + + //set fifo mode with fixed packet length and sync bytes + writeRegister(CC1101_PKTLEN , 63); //63 bytes message (sync at beginning of message is removed by CC1101) + + //set fifo mode with fixed packet length and sync bytes + writeRegister(CC1101_PKTCTRL0 , 0x00); + writeRegister(CC1101_SYNC1 , SYNC1); + writeRegister(CC1101_SYNC0 , SYNC0); + writeRegister(CC1101_MDMCFG2 , MDMCFG2); + writeRegister(CC1101_PKTCTRL1 , 0x00); + + writeCommand(CC1101_SRX); //switch to RX state + + // Check that the RX state has been entered + while (((marcState = readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER)) & CC1101_BITS_MARCSTATE) != CC1101_MARCSTATE_RX) + { + if (marcState == CC1101_MARCSTATE_RXFIFO_OVERFLOW) // RX_OVERFLOW + writeCommand(CC1101_SFRX); //flush RX buffer + } +} + +bool IthoCC1101::checkForNewPacket() { + if (receiveData(&inMessage, 63) && parseMessageCommand()) { + initReceiveMessage(); + return true; + } + return false; +} + +bool IthoCC1101::parseMessageCommand() { + + messageDecode(&inMessage, &inIthoPacket); + + //deviceType of message type? + inIthoPacket.deviceType = inIthoPacket.dataDecoded[0]; + + //deviceID + inIthoPacket.deviceId[0] = inIthoPacket.dataDecoded[1]; + inIthoPacket.deviceId[1] = inIthoPacket.dataDecoded[2]; + inIthoPacket.deviceId[2] = inIthoPacket.dataDecoded[3]; + + //counter1 + inIthoPacket.counter = inIthoPacket.dataDecoded[4]; + + bool isHighCommand = checkIthoCommand(&inIthoPacket, ithoMessageHighCommandBytes); + bool isRVHighCommand = checkIthoCommand(&inIthoPacket, ithoMessageRVHighCommandBytes); + bool isMediumCommand = checkIthoCommand(&inIthoPacket, ithoMessageMediumCommandBytes); + bool isRVMediumCommand = checkIthoCommand(&inIthoPacket, ithoMessageRVMediumCommandBytes); + bool isLowCommand = checkIthoCommand(&inIthoPacket, ithoMessageLowCommandBytes); + bool isRVLowCommand = checkIthoCommand(&inIthoPacket, ithoMessageRVLowCommandBytes); + bool isRVAutoCommand = checkIthoCommand(&inIthoPacket, ithoMessageRVAutoCommandBytes); + bool isStandByCommand = checkIthoCommand(&inIthoPacket, ithoMessageStandByCommandBytes); + bool isTimer1Command = checkIthoCommand(&inIthoPacket, ithoMessageTimer1CommandBytes); + bool isTimer2Command = checkIthoCommand(&inIthoPacket, ithoMessageTimer2CommandBytes); + bool isTimer3Command = checkIthoCommand(&inIthoPacket, ithoMessageTimer3CommandBytes); + bool isJoinCommand = checkIthoCommand(&inIthoPacket, ithoMessageJoinCommandBytes); + bool isJoin2Command = checkIthoCommand(&inIthoPacket, ithoMessageJoin2CommandBytes); + bool isRVJoinCommand = checkIthoCommand(&inIthoPacket, ithoMessageRVJoinCommandBytes); + bool isLeaveCommand = checkIthoCommand(&inIthoPacket, ithoMessageLeaveCommandBytes); + + //determine command + inIthoPacket.command = IthoUnknown; + if (isHighCommand) inIthoPacket.command = IthoHigh; + if (isRVHighCommand) inIthoPacket.command = IthoHigh; + if (isMediumCommand) inIthoPacket.command = IthoMedium; + if (isRVMediumCommand) inIthoPacket.command = IthoMedium; + if (isLowCommand) inIthoPacket.command = IthoLow; + if (isRVLowCommand) inIthoPacket.command = IthoLow; + if (isRVAutoCommand) inIthoPacket.command = IthoStandby; + if (isStandByCommand) inIthoPacket.command = IthoStandby; + if (isTimer1Command) inIthoPacket.command = IthoTimer1; + if (isTimer2Command) inIthoPacket.command = IthoTimer2; + if (isTimer3Command) inIthoPacket.command = IthoTimer3; + if (isJoinCommand) inIthoPacket.command = IthoJoin; + if (isJoin2Command) inIthoPacket.command = IthoJoin; + if (isRVJoinCommand) inIthoPacket.command = IthoJoin; + if (isLeaveCommand) inIthoPacket.command = IthoLeave; + +#if defined (CRC_FILTER) + uint8_t mLen = 0; + if (isPowerCommand || isHighCommand || isMediumCommand || isLowCommand || isStandByCommand || isTimer1Command || isTimer2Command || isTimer3Command) { + mLen = 11; + } + else if (isJoinCommand || isJoin2Command) { + mLen = 20; + } + else if (isLeaveCommand) { + mLen = 14; + } + else { + return true; + } + if (getCounter2(&inIthoPacket, mLen) != inIthoPacket.dataDecoded[mLen]) { + inIthoPacket.command = IthoUnknown; + return false; + } +#endif + + return true; +} + +bool IthoCC1101::checkIthoCommand(IthoPacket *itho, const uint8_t commandBytes[]) { + uint8_t offset = 0; + if (itho->deviceType == 28 || itho->deviceType == 24) offset = 2; + for (int i = 4; i < 6; i++) + { + //if (i == 2 || i == 3) continue; //skip byte3 and byte4, rft-rv and co2-auto remote device seem to sometimes have a different number there + if ( (itho->dataDecoded[i + 5 + offset] != commandBytes[i]) && (itho->dataDecodedChk[i + 5 + offset] != commandBytes[i]) ) { + return false; + } + } + return true; +} + +void IthoCC1101::sendCommand(IthoCommand command) +{ + CC1101Packet outMessage; + uint8_t maxTries = sendTries; + uint8_t delaytime = 40; + + //update itho packet data + outIthoPacket.command = command; + outIthoPacket.counter += 1; + + //get message2 bytes + switch (command) + { + case IthoJoin: + createMessageJoin(&outIthoPacket, &outMessage); + break; + + case IthoLeave: + createMessageLeave(&outIthoPacket, &outMessage); + //the leave command needs to be transmitted for 1 second according the manual + maxTries = 30; + delaytime = 4; + break; + + default: + createMessageCommand(&outIthoPacket, &outMessage); + break; + } + + //send messages + for (int i = 0; i < maxTries; i++) + { + + //message2 + initSendMessage(outMessage.length); + sendData(&outMessage); + + finishTransfer(); + delay(delaytime); + } + //initReceive(); SV - I call this from the ESPEasy plugin to prevent crashes +} + + +void IthoCC1101::createMessageStart(IthoPacket *itho, CC1101Packet *packet) +{ + + //fixed, set start structure in data buffer manually + for (uint8_t i = 0; i < 7; i++) { + packet->data[i] = 170; + } + packet->data[7] = 171; + packet->data[8] = 254; + packet->data[9] = 0; + packet->data[10] = 179; + packet->data[11] = 42; + packet->data[12] = 171; + packet->data[13] = 42; + + //[start of command specific data] + +} + +void IthoCC1101::createMessageCommand(IthoPacket *itho, CC1101Packet *packet) +{ + + //set start message structure + createMessageStart(itho, packet); + + //set deviceType? (or messageType?), not sure what this is + itho->dataDecoded[0] = itho->deviceType; + + //set deviceID + itho->dataDecoded[1] = itho->deviceId[0]; + itho->dataDecoded[2] = itho->deviceId[1]; + itho->dataDecoded[3] = itho->deviceId[2]; + + //set counter1 + itho->dataDecoded[4] = itho->counter; + + //set command bytes on dataDecoded[5 - 10] + uint8_t *commandBytes = getMessageCommandBytes(itho->command); + for (uint8_t i = 0; i < 6; i++) { + itho->dataDecoded[i + 5] = commandBytes[i]; + } + + //set counter2 + itho->dataDecoded[11] = getCounter2(itho, 11); + + itho->length = 12; + + packet->length = messageEncode(itho, packet); + packet->length += 1; + + //set end byte + packet->data[packet->length] = 172; + packet->length += 1; + + //set end 'noise' + for (uint8_t i = packet->length; i < packet->length + 7; i++) { + packet->data[i] = 170; + } + packet->length += 7; + +} + +void IthoCC1101::createMessageJoin(IthoPacket *itho, CC1101Packet *packet) +{ + + //set start message structure + createMessageStart(itho, packet); + + //set deviceType? (or messageType?) + itho->dataDecoded[0] = itho->deviceType; + + //set deviceID + itho->dataDecoded[1] = itho->deviceId[0]; + itho->dataDecoded[2] = itho->deviceId[1]; + itho->dataDecoded[3] = itho->deviceId[2]; + + //set counter1 + itho->dataDecoded[4] = itho->counter; + + //set command bytes on dataDecoded[5 - ?] + uint8_t *commandBytes = getMessageCommandBytes(itho->command); + for (uint8_t i = 0; i < 6; i++) { + itho->dataDecoded[i + 5] = commandBytes[i]; + } + + //set deviceID + itho->dataDecoded[11] = itho->deviceId[0]; + itho->dataDecoded[12] = itho->deviceId[1]; + itho->dataDecoded[13] = itho->deviceId[2]; + + itho->dataDecoded[14] = 1; + itho->dataDecoded[15] = 16; + itho->dataDecoded[16] = 224; + + //set deviceID + itho->dataDecoded[17] = itho->deviceId[0]; + itho->dataDecoded[18] = itho->deviceId[1]; + itho->dataDecoded[19] = itho->deviceId[2]; + + //set counter2 + itho->dataDecoded[20] = getCounter2(itho, 20); + + itho->length = 21; + + packet->length = messageEncode(itho, packet); + packet->length += 1; + + //set end byte + packet->data[packet->length] = 202; + packet->length += 1; + + //set end 'noise' + for (uint8_t i = packet->length; i < packet->length + 7; i++) { + packet->data[i] = 170; + } + packet->length += 7; + +} + +void IthoCC1101::createMessageLeave(IthoPacket *itho, CC1101Packet *packet) +{ + + //set start message structure + createMessageStart(itho, packet); + + //set deviceType? (or messageType?) + itho->dataDecoded[0] = itho->deviceType; + + //set deviceID + itho->dataDecoded[1] = itho->deviceId[0]; + itho->dataDecoded[2] = itho->deviceId[1]; + itho->dataDecoded[3] = itho->deviceId[2]; + + //set counter1 + itho->dataDecoded[4] = itho->counter; + + //set command bytes on dataDecoded[5 - 10] + uint8_t *commandBytes = getMessageCommandBytes(itho->command); + for (uint8_t i = 0; i < 6; i++) { + itho->dataDecoded[i + 5] = commandBytes[i]; + } + + //set deviceID + itho->dataDecoded[11] = itho->deviceId[0]; + itho->dataDecoded[12] = itho->deviceId[1]; + itho->dataDecoded[13] = itho->deviceId[2]; + + //set counter2 + itho->dataDecoded[14] = getCounter2(itho, 14); + + itho->length = 15; + + packet->length = messageEncode(itho, packet); + packet->length += 1; + + //set end byte + packet->data[packet->length] = 202; + packet->length += 1; + + //set end 'noise' + for (uint8_t i = packet->length; i < packet->length + 7; i++) { + packet->data[i] = 170; + } + packet->length += 7; + +} + +uint8_t* IthoCC1101::getMessageCommandBytes(IthoCommand command) +{ + switch (command) + { + case IthoStandby: + return (uint8_t*)&ithoMessageStandByCommandBytes[0]; + case IthoHigh: + return (uint8_t*)&ithoMessageHighCommandBytes[0]; + case IthoFull: + return (uint8_t*)&ithoMessageFullCommandBytes[0]; + case IthoMedium: + return (uint8_t*)&ithoMessageMediumCommandBytes[0]; + case IthoLow: + return (uint8_t*)&ithoMessageLowCommandBytes[0]; + case IthoTimer1: + return (uint8_t*)&ithoMessageTimer1CommandBytes[0]; + case IthoTimer2: + return (uint8_t*)&ithoMessageTimer2CommandBytes[0]; + case IthoTimer3: + return (uint8_t*)&ithoMessageTimer3CommandBytes[0]; + case IthoJoin: + return (uint8_t*)&ithoMessageJoinCommandBytes[0]; + case IthoLeave: + return (uint8_t*)&ithoMessageLeaveCommandBytes[0]; + default: + return (uint8_t*)&ithoMessageLowCommandBytes[0]; + } +} + +/* + Counter2 is the decimal sum of all bytes in decoded form from + deviceType up to the last byte before counter2 subtracted + from zero. +*/ +uint8_t IthoCC1101::getCounter2(IthoPacket *itho, uint8_t len) { + + uint8_t val = 0; + + for (uint8_t i = 0; i < len; i++) { + val += itho->dataDecoded[i]; + } + + return 0 - val; +} + +uint8_t IthoCC1101::messageEncode(IthoPacket *itho, CC1101Packet *packet) { + + uint8_t lenOutbuf = 0; + + if ((itho->length * 20) % 8 == 0) { //inData len fits niecly in out buffer length + lenOutbuf = itho->length * 2.5; + } + else { //is this an issue? inData last byte does not fill out buffer length, add 1 out byte extra, padding is done after encode + lenOutbuf = (uint8_t)(itho->length * 2.5) + 0.5; + } + + uint8_t out_bytecounter = 14; //index of Outbuf, start at offset 14, first part of the message is set manually + uint8_t out_bitcounter = 0; //bit position of current outbuf byte + uint8_t out_patterncounter = 0; //bit counter to add 1 0 bit pattern after every 8 bits + uint8_t bitSelect = 4; //bit position of the inData byte (4 - 7, 0 - 3) + uint8_t out_shift = 7; //bit shift inData bit in position of outbuf byte + + //we need to zero the out buffer first cause we are using bitshifts + for (int i = out_bytecounter; i < sizeof(packet->data) / sizeof(packet->data[0]); i++) { + packet->data[i] = 0; + } + + //Serial.println(); + for (uint8_t dataByte = 0; dataByte < itho->length; dataByte++) { + for (uint8_t dataBit = 0; dataBit < 8; dataBit++) { //process a full dataByte at a time resulting in 20 output bits (2.5 bytes) with the pattern 7x6x5x4x 10 3x2x1x0x 10 7x6x5x4x 10 3x2x1x0x 10 etc + if (out_bitcounter == 8) { //check if new byte is needed + out_bytecounter++; + out_bitcounter = 0; + } + + if (out_patterncounter == 8) { //check if we have to start with a 1 0 pattern + out_patterncounter = 0; + packet->data[out_bytecounter] = packet->data[out_bytecounter] | 1 << out_shift; + out_shift--; + out_bitcounter++; + packet->data[out_bytecounter] = packet->data[out_bytecounter] | 0 << out_shift; + if (out_shift == 0) out_shift = 8; + out_shift--; + out_bitcounter++; + } + + if (out_bitcounter == 8) { //check if new byte is needed + out_bytecounter++; + out_bitcounter = 0; + } + + //set the even bit + uint8_t bit = (itho->dataDecoded[dataByte] & (1 << bitSelect)) >> bitSelect; //select bit and shift to bit pos 0 + bitSelect++; + if (bitSelect == 8) bitSelect = 0; + + packet->data[out_bytecounter] = packet->data[out_bytecounter] | bit << out_shift; //shift bit in corect pos of current outbuf byte + out_shift--; + out_bitcounter++; + out_patterncounter++; + + //set the odd bit (inverse of even bit) + bit = ~bit & 0b00000001; + packet->data[out_bytecounter] = packet->data[out_bytecounter] | bit << out_shift; + if (out_shift == 0) out_shift = 8; + out_shift--; + out_bitcounter++; + out_patterncounter++; + + } + + } + if (out_bitcounter < 8) { //add closing 1 0 pattern to fill last packet->data byte and ensure DC balance in the message + for (uint8_t i = out_bitcounter; i < 8; i += 2) { + packet->data[out_bytecounter] = packet->data[out_bytecounter] | 1 << out_shift; + out_shift--; + packet->data[out_bytecounter] = packet->data[out_bytecounter] | 0 << out_shift; + if (out_shift == 0) out_shift = 8; + out_shift--; + } + } + + return out_bytecounter; +} + + +void IthoCC1101::messageDecode(CC1101Packet *packet, IthoPacket *itho) { + + itho->length = 0; + int lenInbuf = packet->length; + + lenInbuf -= STARTBYTE; //correct for sync byte pos + + while (lenInbuf >= 5) { + lenInbuf -= 5; + itho->length += 2; + } + if (lenInbuf >= 3) { + itho->length++; + } + + for (int i = 0; i < sizeof(itho->dataDecoded) / sizeof(itho->dataDecoded[0]); i++) { + itho->dataDecoded[i] = 0; + } + for (int i = 0; i < sizeof(itho->dataDecodedChk) / sizeof(itho->dataDecodedChk[0]); i++) { + itho->dataDecodedChk[i] = 0; + } + + uint8_t out_i = 0; //byte index + uint8_t out_j = 4; //bit index + uint8_t out_i_chk = 0; //byte index + uint8_t out_j_chk = 4; //bit index + uint8_t in_bitcounter = 0; //process per 10 input bits + + for (int i = STARTBYTE; i < packet->length; i++) { + + for (int j = 7; j > -1; j--) { + if (in_bitcounter == 0 || in_bitcounter == 2 || in_bitcounter == 4 || in_bitcounter == 6) { //select input bits for output + uint8_t x = packet->data[i]; //select input byte + x = x >> j; //select input bit + x = x & 0b00000001; + x = x << out_j; //set value for output bit + itho->dataDecoded[out_i] = itho->dataDecoded[out_i] | x; + out_j += 1; //next output bit + if (out_j > 7) out_j = 0; + if (out_j == 4) out_i += 1; + } + if (in_bitcounter == 1 || in_bitcounter == 3 || in_bitcounter == 5 || in_bitcounter == 7) { //select input bits for check output + uint8_t x = packet->data[i]; //select input byte + x = x >> j; //select input bit + x = x & 0b00000001; + x = x << out_j_chk; //set value for output bit + itho->dataDecodedChk[out_i_chk] = itho->dataDecodedChk[out_i_chk] | x; + out_j_chk += 1; //next output bit + if (out_j_chk > 7) out_j_chk = 0; + if (out_j_chk == 4) { + itho->dataDecodedChk[out_i_chk] = ~itho->dataDecodedChk[out_i_chk]; //inverse bits + out_i_chk += 1; + } + } + in_bitcounter += 1; //continue cyling in groups of 10 bits + if (in_bitcounter > 9) in_bitcounter = 0; + } + } +} + +uint8_t IthoCC1101::ReadRSSI() +{ + uint8_t rssi = 0; + uint8_t value = 0; + + rssi = (readRegister(CC1101_RSSI, CC1101_STATUS_REGISTER)); + + if (rssi >= 128) + { + value = 255 - rssi; + value /= 2; + value += 74; + } + else + { + value = rssi / 2; + value += 74; + } + return (value); +} + +bool IthoCC1101::checkID(const uint8_t *id) +{ + for (uint8_t i = 0; i < 3; i++) + if (id[i] != inIthoPacket.deviceId[i]) + return false; + return true; +} + +String IthoCC1101::getLastIDstr(bool ashex) { + String str; + for (uint8_t i = 0; i < 3; i++) { + if (ashex) str += String(inIthoPacket.deviceId[i], HEX); + else str += String(inIthoPacket.deviceId[i]); + if (i < 2) str += ","; + } + return str; +} + +int * IthoCC1101::getLastID() { + static int id[3]; + for (uint8_t i = 0; i < 3; i++) { + id[i] = inIthoPacket.deviceId[i]; + } + return id; +} + +String IthoCC1101::getLastMessagestr(bool ashex) { + String str = "Length=" + String(inMessage.length) + "."; + for (uint8_t i = 0; i < inMessage.length; i++) { + if (ashex) str += String(inMessage.data[i], HEX); + else str += String(inMessage.data[i]); + if (i < inMessage.length - 1) str += ":"; + } + return str; +} + +String IthoCC1101::LastMessageDecoded() { + + String str; + if (inIthoPacket.length > 11) { + str += "Device type?: " + String(inIthoPacket.deviceType); + str += " - CMD: "; + for (int i = 4; i < inIthoPacket.length; i++) { + str += String(inIthoPacket.dataDecoded[i]); + if (i < inIthoPacket.length - 1) str += ","; + } + + } + else { + for (uint8_t i = 0; i < inIthoPacket.length; i++) { + str += String(inIthoPacket.dataDecoded[i]); + if (i < inIthoPacket.length - 1) str += ","; + } + + } + str += "\n"; + return str; + +} diff --git a/lib/Itho/IthoCC1101.h b/lib/Itho/IthoCC1101.h new file mode 100644 index 000000000..f4fd7b0da --- /dev/null +++ b/lib/Itho/IthoCC1101.h @@ -0,0 +1,116 @@ +/* + * Author: Klusjesman, supersjimmie, modified and reworked by arjenhiemstra + */ + +#ifndef __ITHOCC1101_H__ +#define __ITHOCC1101_H__ + +#include +#include "CC1101.h" +#include "IthoPacket.h" + + +//pa table settings +const uint8_t ithoPaTableSend[8] = {0x6F, 0x26, 0x2E, 0x8C, 0x87, 0xCD, 0xC7, 0xC0}; +const uint8_t ithoPaTableReceive[8] = {0x6F, 0x26, 0x2E, 0x7F, 0x8A, 0x84, 0xCA, 0xC4}; + +//message command bytes +const uint8_t ithoMessageRVHighCommandBytes[] = {49,224,4,0,0,200}; +const uint8_t ithoMessageHighCommandBytes[] = {34,241,3,0,4,4}; +const uint8_t ithoMessageFullCommandBytes[] = {34,241,3,0,4,4}; +const uint8_t ithoMessageMediumCommandBytes[] = {34,241,3,0,3,4}; +const uint8_t ithoMessageRVMediumCommandBytes[] = {34,241,3,0,3,7}; +const uint8_t ithoMessageLowCommandBytes[] = {34,241,3,0,2,4}; +const uint8_t ithoMessageRVLowCommandBytes[] = {49,224,4,0,0,1}; +const uint8_t ithoMessageRVAutoCommandBytes[] = {34,241,3,0,5,7}; +const uint8_t ithoMessageStandByCommandBytes[] = {0,0,0,0,0,0}; //unkown, tbd +const uint8_t ithoMessageTimer1CommandBytes[] = {34,243,3,0,0,10}; //10 minutes full speed +const uint8_t ithoMessageTimer2CommandBytes[] = {34,243,3,0,0,20}; //20 minutes full speed +const uint8_t ithoMessageTimer3CommandBytes[] = {34,243,3,0,0,30}; //30 minutes full speed +const uint8_t ithoMessageJoinCommandBytes[] = {31,201,12,0,34,241}; +const uint8_t ithoMessageJoin2CommandBytes[] = {31,201,12,99,34,248}; //join command of RFT AUTO Co2 remote +const uint8_t ithoMessageRVJoinCommandBytes[] = {31,201,24,0,49,224}; //join command of RFT-RV +const uint8_t ithoMessageLeaveCommandBytes[] = {31,201,6,0,31,201}; +//itho rft-rv +//unknown, high +//148,216,43,49,224,4,0,0,200,0,3,127,244,78,11,155,154,225,11,96,138 +//148,216,43,49,224,4,0,0,200,0,3,127,51,80,47,233,94,6,189,114,73 + +//low +//148,216,43,49,224,4,0,0,1,0,202,127,242,212,160,123,15,64,7,129,33 +//148,216,43,34,241,3,0,4,4,194,127,255,189,90,107,88,72,115,49,192,105 + +//join +//151,149,65,31,201,24,0,49,224,151,149,65,0,18,160,151,149,65,1,16,224 + + +class IthoCC1101 : protected CC1101 +{ + private: + //receive + CC1101Packet inMessage; //temp storage message2 + IthoPacket inIthoPacket; //stores last received message data + + //send + IthoPacket outIthoPacket; //stores state of "remote" + + //settings + uint8_t sendTries; //number of times a command is send at one button press + + //functions + public: + IthoCC1101(uint8_t counter = 0, uint8_t sendTries = 3); //set initial counter value + ~IthoCC1101(); + + //init + void init() { CC1101::init(); initReceive(); } //init,reset CC1101 + void initReceive(); + uint8_t getLastCounter() { return outIthoPacket.counter; } //counter is increased before sending a command + void setSendTries(uint8_t sendTries) { this->sendTries = sendTries; } + void setDeviceID(uint8_t byte0, uint8_t byte1, uint8_t byte2) { this->outIthoPacket.deviceId[0] = byte0; this->outIthoPacket.deviceId[1] = byte1; this->outIthoPacket.deviceId[2] = byte2;} + + //receive + bool checkForNewPacket(); //check RX fifo for new data + IthoPacket getLastPacket() { return inIthoPacket; } //retrieve last received/parsed packet from remote + IthoCommand getLastCommand() { return inIthoPacket.command; } //retrieve last received/parsed command from remote + uint8_t getLastInCounter() { return inIthoPacket.counter; } //retrieve last received/parsed command from remote + uint8_t ReadRSSI(); + bool checkID(const uint8_t *id); + int * getLastID(); + String getLastIDstr(bool ashex=true); + String getLastMessagestr(bool ashex=true); + String LastMessageDecoded(); + + //send + void sendCommand(IthoCommand command); + protected: + private: + IthoCC1101( const IthoCC1101 &c); + IthoCC1101& operator=( const IthoCC1101 &c); + + //init CC1101 for receiving + void initReceiveMessage(); + + //init CC1101 for sending + void initSendMessage(uint8_t len); + void finishTransfer(); + + //parse received message + bool parseMessageCommand(); + bool checkIthoCommand(IthoPacket *itho, const uint8_t commandBytes[]); + + //send + void createMessageStart(IthoPacket *itho, CC1101Packet *packet); + void createMessageCommand(IthoPacket *itho, CC1101Packet *packet); + void createMessageJoin(IthoPacket *itho, CC1101Packet *packet); + void createMessageLeave(IthoPacket *itho, CC1101Packet *packet); + uint8_t* getMessageCommandBytes(IthoCommand command); + uint8_t getCounter2(IthoPacket *itho, uint8_t len); + + uint8_t messageEncode(IthoPacket *itho, CC1101Packet *packet); + void messageDecode(CC1101Packet *packet, IthoPacket *itho); + + +}; //IthoCC1101 + +#endif //__ITHOCC1101_H__ \ No newline at end of file diff --git a/lib/Itho/IthoPacket.h b/lib/Itho/IthoPacket.h new file mode 100644 index 000000000..3a5c3e807 --- /dev/null +++ b/lib/Itho/IthoPacket.h @@ -0,0 +1,49 @@ +/* + * Author: Klusjesman, supersjimmie, modified and reworked by arjenhiemstra + */ + +#ifndef ITHOPACKET_H_ +#define ITHOPACKET_H_ + +enum IthoCommand +{ + IthoUnknown = 0, + + IthoJoin = 1, + IthoLeave = 2, + + IthoStandby = 3, + IthoLow = 4, + IthoMedium = 5, + IthoHigh = 6, + IthoFull = 7, + + IthoTimer1 = 8, + IthoTimer2 = 9, + IthoTimer3 = 10, + + //duco c system remote + DucoStandby = 11, + DucoLow = 12, + DucoMedium = 13, + DucoHigh = 14 +}; + + +class IthoPacket +{ + public: + IthoCommand command; + + uint8_t dataDecoded[32]; + uint8_t dataDecodedChk[32]; + uint8_t length; + + uint8_t deviceType; + uint8_t deviceId[3]; + + uint8_t counter; //0-255, counter is increased on every remote button press +}; + + +#endif /* ITHOPACKET_H_ */ \ No newline at end of file diff --git a/lib/Multi_Channel_Relay_Arduino_Library/.gitlab-ci.yml b/lib/Multi_Channel_Relay_Arduino_Library/.gitlab-ci.yml new file mode 100644 index 000000000..5e2b6aa0c --- /dev/null +++ b/lib/Multi_Channel_Relay_Arduino_Library/.gitlab-ci.yml @@ -0,0 +1,8 @@ +build: + tags: + - nas + script: + - wget -c https://files.seeedstudio.com/arduino/seeed-arduino-ci.sh + - chmod +x seeed-arduino-ci.sh + - bash $PWD/seeed-arduino-ci.sh test + diff --git a/lib/Multi_Channel_Relay_Arduino_Library/.travis.yml b/lib/Multi_Channel_Relay_Arduino_Library/.travis.yml new file mode 100644 index 000000000..202d2e079 --- /dev/null +++ b/lib/Multi_Channel_Relay_Arduino_Library/.travis.yml @@ -0,0 +1,21 @@ + +language: generic +dist: bionic +sudo: false +cache: + directories: + - ~/arduino_ide + - ~/.arduino15/packages/ + +before_install: + - wget -c https://files.seeedstudio.com/arduino/seeed-arduino-ci.sh + +script: + - chmod +x seeed-arduino-ci.sh + - cat $PWD/seeed-arduino-ci.sh + - bash $PWD/seeed-arduino-ci.sh test + +notifications: + email: + on_success: change + on_failure: change diff --git a/lib/Multi_Channel_Relay_Arduino_Library/LICENSE b/lib/Multi_Channel_Relay_Arduino_Library/LICENSE new file mode 100644 index 000000000..c78cc0bf3 --- /dev/null +++ b/lib/Multi_Channel_Relay_Arduino_Library/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Seeed Studio + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/lib/Multi_Channel_Relay_Arduino_Library/README.md b/lib/Multi_Channel_Relay_Arduino_Library/README.md new file mode 100644 index 000000000..f04230ae9 --- /dev/null +++ b/lib/Multi_Channel_Relay_Arduino_Library/README.md @@ -0,0 +1,84 @@ +# Multi_Channel_Relay_Arduino_Library [![Build Status](https://travis-ci.com/Seeed-Studio/Multi_Channel_Relay_Arduino_Library.svg?branch=master)](https://travis-ci.com/Seeed-Studio/Multi_Channel_Relay_Arduino_Library) +This is the Arduino library for Seeed multi channel relay. + + + + + + + +### How to use this library +you can download from Arduino Library Manager or directlly download from this repository. +Connect the 8-channel Solid state relay v1.0 to Arduino board's I2C port, compile and upload four_channel_relay_control.ino. Open the serial monitor the relay should go as expected some massage should show as below: + +``` +Channel 1 on +Channel 2 on +Channel 3 on +Channel 4 on +Turn all channels on, State: 1111 +Turn 1 3 channels on, State: 101 +Turn 2 4 channels on, State: 1010 +Turn off all channels, State: 0 +``` + +- Upload eight_channel_relay_control.ino, open the serial monitor and show the below logs: + +``` +Channel 1 on +Channel 2 on +Channel 3 on +Channel 4 on +Channel 5 on +Channel 6 on +Channel 7 on +Channel 8 on +Turn all channels on, State: 11111111 +Turn 1 3 5 7 channels on, State: 1010101 +Turn 2 4 6 8 channels on, State: 10101010 +Turn off all channels, State: 0 + +``` + +- This module can be set I2C address by software, open change_i2c_address.ino modify new_i2c_address as you want; compile and upload the sketch, open the serial monitor and show the below logs: + +``` +Scanning... +I2C device found at address 0x11 ! +Found 1 devices +Address 0x21 has been saved to flash. +``` + +- Read firmware version of the module by example read_firmware_version.ino. Serial logs as below: + +``` +firmware version: 0x1 +``` + +### Functionalities +- The relay board is an I2C device, device address is changeable, refer to **changeI2CAddress(uint8_t new_addr, uint8_t old_addr)** . +- Use **getChannelState()** to know the state of every channel. +- Use **getFirmwareVersion()** to recognize firmware burn in the on board MCU. +- Use **channelCtrl(uint8_t state)** to change all channel immediately, the **state** parameter represents channel 1 to 8. +- Use **turn_on_channel(uint8_t channel)** to turn on single channel. +- Use **turn_off_channel(uint8_t channel)** to turn off single channel. +- External functionality **scanI2CDevice()**, use for scan device address. + + + + +---- +This software is written by lambor for seeed studio and is licensed under The MIT License.
+ +Contributing to this software is warmly welcomed. You can do this basically by
+[forking](https://help.github.com/articles/fork-a-repo), committing modifications and then [pulling requests](https://help.github.com/articles/using-pull-requests) (follow the links above
+for operating guide). Adding change log and your contact into file header is encouraged.
+Thanks for your contribution. + +Seeed is a hardware innovation platform for makers to grow inspirations into differentiating products. By working closely with technology providers of all scale, Seeed provides accessible technologies with quality, speed and supply chain knowledge. When prototypes are ready to iterate, Seeed helps productize 1 to 1,000 pcs using in-house engineering, supply chain management and agile manufacture forces. Seeed also team up with incubators, Chinese tech ecosystem, investors and distribution channels to portal Maker startups beyond. + + +[1]:http://wiki.seeedstudio.com/Grove-OLED_Display_0.96inch/ + + + diff --git a/lib/Multi_Channel_Relay_Arduino_Library/examples/change_i2c_address/change_i2c_address.ino b/lib/Multi_Channel_Relay_Arduino_Library/examples/change_i2c_address/change_i2c_address.ino new file mode 100644 index 000000000..8c41134ca --- /dev/null +++ b/lib/Multi_Channel_Relay_Arduino_Library/examples/change_i2c_address/change_i2c_address.ino @@ -0,0 +1,45 @@ +/** + When running this sketch please make sure there is only one + Multi Channel Relay module connected to your board, otherwise + it may not work to change module address, or cause other + unpredictable issue. + + +*/ + +#include + + +int new_i2c_address = 0x21; + +Multi_Channel_Relay relay; + +void setup() { + uint8_t old_address = 0; + uint8_t retry = 0; + + DEBUG_PRINT.begin(9600); + while (!DEBUG_PRINT); + + // // Set I2C address by default and start relay + relay.begin(); + + /* Scan I2C device detect device address */ + while ((retry++ < 10) && (old_address == 0x00)) { + old_address = relay.scanI2CDevice(); + delay(100); + } + + if ((0x00 == old_address) || (0xff == old_address)) { + while (1); + } + + relay.changeI2CAddress(old_address, new_i2c_address); /* Set I2C address and save to Flash */ + DEBUG_PRINT.print("Address 0x"); + DEBUG_PRINT.print(new_i2c_address, HEX); + DEBUG_PRINT.println(" has been saved to flash."); +} + +void loop() { + +} diff --git a/lib/Multi_Channel_Relay_Arduino_Library/examples/eight_channel_relay_control/eight_channel_relay_control.ino b/lib/Multi_Channel_Relay_Arduino_Library/examples/eight_channel_relay_control/eight_channel_relay_control.ino new file mode 100644 index 000000000..d7f91ab5c --- /dev/null +++ b/lib/Multi_Channel_Relay_Arduino_Library/examples/eight_channel_relay_control/eight_channel_relay_control.ino @@ -0,0 +1,95 @@ +#include + +/** + channle: 8 7 6 5 4 3 2 1 + state: 0b00000000 -> 0x00 (all off) + state: 0b11111111 -> 0xff (all on) +*/ + +Multi_Channel_Relay relay; + +void setup() { + + DEBUG_PRINT.begin(9600); + while (!DEBUG_PRINT); + + // Set I2C address and start relay + relay.begin(0x11); + + /* Begin Controlling Relay */ + DEBUG_PRINT.println("Channel 1 on"); + relay.turn_on_channel(1); + delay(500); + DEBUG_PRINT.println("Channel 2 on"); + relay.turn_off_channel(1); + relay.turn_on_channel(2); + delay(500); + DEBUG_PRINT.println("Channel 3 on"); + relay.turn_off_channel(2); + relay.turn_on_channel(3); + delay(500); + DEBUG_PRINT.println("Channel 4 on"); + relay.turn_off_channel(3); + relay.turn_on_channel(4); + delay(500); + DEBUG_PRINT.println("Channel 5 on"); + relay.turn_off_channel(4); + relay.turn_on_channel(5); + delay(500); + DEBUG_PRINT.println("Channel 6 on"); + relay.turn_off_channel(5); + relay.turn_on_channel(6); + delay(500); + DEBUG_PRINT.println("Channel 7 on"); + relay.turn_off_channel(6); + relay.turn_on_channel(7); + delay(500); + DEBUG_PRINT.println("Channel 8 on"); + relay.turn_off_channel(7); + relay.turn_on_channel(8); + delay(500); + relay.turn_off_channel(8); + + relay.channelCtrl(CHANNLE1_BIT | + CHANNLE2_BIT | + CHANNLE3_BIT | + CHANNLE4_BIT | + CHANNLE5_BIT | + CHANNLE6_BIT | + CHANNLE7_BIT | + CHANNLE8_BIT); + DEBUG_PRINT.print("Turn all channels on, State: "); + DEBUG_PRINT.println(relay.getChannelState(), BIN); + + delay(2000); + + relay.channelCtrl(CHANNLE1_BIT | + CHANNLE3_BIT | + CHANNLE5_BIT | + CHANNLE7_BIT); + DEBUG_PRINT.print("Turn 1 3 5 7 channels on, State: "); + DEBUG_PRINT.println(relay.getChannelState(), BIN); + + delay(2000); + + relay.channelCtrl(CHANNLE2_BIT | + CHANNLE4_BIT | + CHANNLE6_BIT | + CHANNLE8_BIT); + DEBUG_PRINT.print("Turn 2 4 6 8 channels on, State: "); + DEBUG_PRINT.println(relay.getChannelState(), BIN); + + delay(2000); + + + relay.channelCtrl(0); + DEBUG_PRINT.print("Turn off all channels, State: "); + DEBUG_PRINT.println(relay.getChannelState(), BIN); + + delay(2000); +} + +void loop() { + + +} diff --git a/lib/Multi_Channel_Relay_Arduino_Library/examples/four_channel_relay_control/four_channel_relay_control.ino b/lib/Multi_Channel_Relay_Arduino_Library/examples/four_channel_relay_control/four_channel_relay_control.ino new file mode 100644 index 000000000..7c6b5c052 --- /dev/null +++ b/lib/Multi_Channel_Relay_Arduino_Library/examples/four_channel_relay_control/four_channel_relay_control.ino @@ -0,0 +1,70 @@ +#include + +/** + channle: 4 3 2 1 + state: 0b0000 -> 0x00 (all off) + state: 0b1111 -> 0x0f (all on) +*/ + +Multi_Channel_Relay relay; + +void setup() { + DEBUG_PRINT.begin(9600); + while (!DEBUG_PRINT); + + // Set I2C address and start relay + relay.begin(0x11); + + /* Begin Controlling Relay */ + DEBUG_PRINT.println("Channel 1 on"); + relay.turn_on_channel(1); + delay(500); + DEBUG_PRINT.println("Channel 2 on"); + relay.turn_off_channel(1); + relay.turn_on_channel(2); + delay(500); + DEBUG_PRINT.println("Channel 3 on"); + relay.turn_off_channel(2); + relay.turn_on_channel(3); + delay(500); + DEBUG_PRINT.println("Channel 4 on"); + relay.turn_off_channel(3); + relay.turn_on_channel(4); + delay(500); + relay.turn_off_channel(4); + + relay.channelCtrl(CHANNLE1_BIT | + CHANNLE2_BIT | + CHANNLE3_BIT | + CHANNLE4_BIT); + DEBUG_PRINT.print("Turn all channels on, State: "); + DEBUG_PRINT.println(relay.getChannelState(), BIN); + + delay(2000); + + relay.channelCtrl(CHANNLE1_BIT | + CHANNLE3_BIT); + DEBUG_PRINT.print("Turn 1 3 channels on, State: "); + DEBUG_PRINT.println(relay.getChannelState(), BIN); + + delay(2000); + + relay.channelCtrl(CHANNLE2_BIT | + CHANNLE4_BIT); + DEBUG_PRINT.print("Turn 2 4 channels on, State: "); + DEBUG_PRINT.println(relay.getChannelState(), BIN); + + delay(2000); + + + relay.channelCtrl(0); + DEBUG_PRINT.print("Turn off all channels, State: "); + DEBUG_PRINT.println(relay.getChannelState(), BIN); + + delay(2000); +} + +void loop() { + + +} diff --git a/lib/Multi_Channel_Relay_Arduino_Library/examples/read_firmware_version/read_firmware_version.ino b/lib/Multi_Channel_Relay_Arduino_Library/examples/read_firmware_version/read_firmware_version.ino new file mode 100644 index 000000000..b1fd29610 --- /dev/null +++ b/lib/Multi_Channel_Relay_Arduino_Library/examples/read_firmware_version/read_firmware_version.ino @@ -0,0 +1,23 @@ +#include + +#define USE_8_CHANNELS (1) + +Multi_Channel_Relay relay; + +void setup() { + DEBUG_PRINT.begin(9600); + while (!DEBUG_PRINT); + + // Set I2C address and start relay + relay.begin(0x11); + + /* Read firmware version */ + DEBUG_PRINT.print("firmware version: "); + DEBUG_PRINT.print("0x"); + DEBUG_PRINT.print(relay.getFirmwareVersion(), HEX); + DEBUG_PRINT.println(); +} + +void loop() { + +} diff --git a/lib/Multi_Channel_Relay_Arduino_Library/keywords.txt b/lib/Multi_Channel_Relay_Arduino_Library/keywords.txt new file mode 100644 index 000000000..16526100b --- /dev/null +++ b/lib/Multi_Channel_Relay_Arduino_Library/keywords.txt @@ -0,0 +1,6 @@ +changeI2CAddress KEYWORD2 +getChannelState KEYWORD2 +getFirmwareVersion KEYWORD2 +channelCtrl KEYWORD2 +turn_on_channel KEYWORD2 +turn_off_channel KEYWORD2 diff --git a/lib/Multi_Channel_Relay_Arduino_Library/library.properties b/lib/Multi_Channel_Relay_Arduino_Library/library.properties new file mode 100644 index 000000000..d8ab6cb97 --- /dev/null +++ b/lib/Multi_Channel_Relay_Arduino_Library/library.properties @@ -0,0 +1,9 @@ +name=Multi Channel Relay Arduino Library +version=1.1.0 +author=Seeed Studio +maintainer=Seeed Studio +sentence=Arduino library to control Multi Channel Rely. +paragraph=Arduino library to control Multi Channel Rely. +category=Device Control +url=https://github.com/Seeed-Studio/Multi_Channel_Relay_Arduino_Library +architectures=* \ No newline at end of file diff --git a/lib/Multi_Channel_Relay_Arduino_Library/multi_channel_relay.cpp b/lib/Multi_Channel_Relay_Arduino_Library/multi_channel_relay.cpp new file mode 100644 index 000000000..ecf5950e4 --- /dev/null +++ b/lib/Multi_Channel_Relay_Arduino_Library/multi_channel_relay.cpp @@ -0,0 +1,134 @@ +/* + multi_channel_relay.h + Seeed multi channel relay Arduino library + + Copyright (c) 2018 Seeed Technology Co., Ltd. + Author : lambor + Create Time : June 2018 + Change Log : + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include + +Multi_Channel_Relay::Multi_Channel_Relay() { + +} + +void Multi_Channel_Relay::begin(int address) { + // Wire.begin(); Handled by ESPEasy already + channel_state = 0; + _i2cAddr = address; + +} + +uint8_t Multi_Channel_Relay::getFirmwareVersion(void) { + Wire.beginTransmission(_i2cAddr); + Wire.write(CMD_READ_FIRMWARE_VER); + Wire.endTransmission(); + + Wire.requestFrom(_i2cAddr, 1); + //while(!Wire.available()); + return Wire.read(); +} + +void Multi_Channel_Relay::changeI2CAddress(uint8_t old_addr, uint8_t new_addr) { + Wire.beginTransmission(old_addr); + Wire.write(CMD_SAVE_I2C_ADDR); + Wire.write(new_addr); + Wire.endTransmission(); + + _i2cAddr = new_addr; +} + +uint8_t Multi_Channel_Relay::getChannelState(void) { + return channel_state; +} + +void Multi_Channel_Relay::channelCtrl(uint8_t state) { + channel_state = state; + + Wire.beginTransmission(_i2cAddr); + Wire.write(CMD_CHANNEL_CTRL); + Wire.write(channel_state); + Wire.endTransmission(); +} + +void Multi_Channel_Relay::turn_on_channel(uint8_t channel) { + channel_state |= (1 << (channel - 1)); + + Wire.beginTransmission(_i2cAddr); + Wire.write(CMD_CHANNEL_CTRL); + Wire.write(channel_state); + Wire.endTransmission(); +} + +void Multi_Channel_Relay::turn_off_channel(uint8_t channel) { + channel_state &= ~(1 << (channel - 1)); + + Wire.beginTransmission(_i2cAddr); + Wire.write(CMD_CHANNEL_CTRL); + Wire.write(channel_state); + Wire.endTransmission(); +} + +// uint8_t Multi_Channel_Relay::scanI2CDevice(void) { + // byte error = 0, address = 0, result = 0; + // int nDevices; + + // DEBUG_PRINT.println("Scanning..."); + + // nDevices = 0; + // for (address = 1; address <= 127; address++) { + // // The i2c_scanner uses the return value of + // // the Write.endTransmisstion to see if + // // a device did acknowledge to the address. + // Wire.beginTransmission(address); + // error = Wire.endTransmission(); + + // if (error == 0) { + // result = address; + // DEBUG_PRINT.print("I2C device found at address 0x"); + // if (address < 16) { + // DEBUG_PRINT.print("0"); + // } + // DEBUG_PRINT.print(address, HEX); + // DEBUG_PRINT.println(" !"); + + // nDevices++; + // } else if (error == 4) { + // DEBUG_PRINT.print("Unknown error at address 0x"); + // if (address < 16) { + // DEBUG_PRINT.print("0"); + // } + // DEBUG_PRINT.println(address, HEX); + // } + // } + // if (nDevices == 0) { + // DEBUG_PRINT.println("No I2C devices found\n"); + // result = 0x00; + // } else { + // DEBUG_PRINT.print("Found "); + // DEBUG_PRINT.print(nDevices); + // DEBUG_PRINT.print(" devices\n"); + // if (nDevices != 1) { + // result = 0x00; + // } + // } + + // return result; +// return 0; +// } diff --git a/lib/Multi_Channel_Relay_Arduino_Library/multi_channel_relay.h b/lib/Multi_Channel_Relay_Arduino_Library/multi_channel_relay.h new file mode 100644 index 000000000..df27d602b --- /dev/null +++ b/lib/Multi_Channel_Relay_Arduino_Library/multi_channel_relay.h @@ -0,0 +1,129 @@ +/* + multi_channel_relay.cpp + Seeed multi channel relay Arduino library + + Copyright (c) 2018 Seeed Technology Co., Ltd. + Author : lambor + Create Time : June 2018 + Change Log : + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library 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 + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#ifndef MULTI_CHANNEL_RELAY_H +#define MULTI_CHANNEL_RELAY_H + +// Architecture specific include +// #if defined(ARDUINO_ARCH_AVR) +// #define DEBUG_PRINT Serial +// #elif defined(ARDUINO_ARCH_SAM) +// #define DEBUG_PRINT SerialUSB +// #elif defined(ARDUINO_ARCH_SAMD) +// #define DEBUG_PRINT SerialUSB +// #elif defined(ARDUINO_ARCH_STM32F4) +// #define DEBUG_PRINT SerialUSB +// #else +// #pragma message("Not match any architecture.") +// #define DEBUG_PRINT Serial +// #endif + +#include +#include + + +#define CHANNLE1_BIT 0x01 +#define CHANNLE2_BIT 0x02 +#define CHANNLE3_BIT 0x04 +#define CHANNLE4_BIT 0x08 +#define CHANNLE5_BIT 0x10 +#define CHANNLE6_BIT 0x20 +#define CHANNLE7_BIT 0x40 +#define CHANNLE8_BIT 0x80 + +#define CMD_CHANNEL_CTRL 0x10 +#define CMD_SAVE_I2C_ADDR 0x11 +#define CMD_READ_I2C_ADDR 0x12 +#define CMD_READ_FIRMWARE_VER 0x13 + +class Multi_Channel_Relay { + public: + Multi_Channel_Relay(); + + /** + Begin Multi Channel Relay by a I2C address. + @param address can be changed by changeI2CAddress. Please refer to example change_i2c_address + */ + void begin(int address = 0x11); + + /** + @brief Change device address from old_addr to new_addr. + @param new_addr, the address to use. + old_addr, the original address + @return None + */ + void changeI2CAddress(uint8_t new_addr, uint8_t old_addr); + + /** + /brief Get channel state + /return One byte value to indicate channel state + the bits range from 0 to 7 represents channel 1 to 8 + */ + uint8_t getChannelState(void); + + /** + @brief Read firmware version from on board MCU + @param + @return Firmware version in byte + */ + uint8_t getFirmwareVersion(void); + + /** + @brief Control relay channels + @param state, use one Byte to represent 8 channel + @return None + */ + void channelCtrl(uint8_t state); + + /** + @brief Turn on one of 8 channels + @param channel, channel to control with (range form 1 to 8) + @return None + */ + void turn_on_channel(uint8_t channel); + + /** + @brief Turn off on of 8 channels + @param channel, channel to control with (range form 1 to 8) + @return None + */ + void turn_off_channel(uint8_t channel); + + // /** + // @brief Scan I2C device, return the address if there is only one i2c device + // @param + // @return device address + // */ + // uint8_t scanI2CDevice(void); + + private: + int _i2cAddr; // This is the I2C address you want to use + int channel_state; // Value to save channel state +}; + + + +#endif \ No newline at end of file diff --git a/misc/Notepad++/ESP_Easy__rules.xml b/misc/Notepad++/ESP_Easy__rules.xml new file mode 100644 index 000000000..5ebc41408 --- /dev/null +++ b/misc/Notepad++/ESP_Easy__rules.xml @@ -0,0 +1,64 @@ + + + + + + + + 00// 01 02 03// 04 + + + + + + + + > < = <= >= <> != + - * / % ! , # + and or + do + + endon + If + Else ElseIf + Endif + + + + AcessInfo Background Build ClearAccessBlock ClearPassword ClearRTCam Config ControllerDisable ControllerEnable DateTime Debug DeepSleep Delay DNS DST EraseSDKwifi ExecuteRules Gateway I2Cscanner IP Let Load Logentry LogPortStatus LoopTimerSet LoopTimerSet_ms MemInfo MemInfoDetail Name Password Publish Reboot Reset ResetFlashWriteCounter Save SendTo SendToHTTP SendToUDP Settings Subnet Subscribe TaskClear TaskClearAll TaskDisable TaskEnable TaskRun TaskValueSet TaskValueSetAndRun TimerPause TimerResume TimerSet TimerSet_ms TimeZone UdpPort UdpTest Unit UseNTP WdConfig WdRead WifiAPkey WifiAllowAP WifiAPMode WifiConnect WifiDisconnect WifiKey WifiKey2 WifiScan WifiSSID WifiSSID2 WifiSTAMode + Clock#Time= GPIO# Login#Failed MQTT#Connected MQTT#Disconnected MQTTimport#Connected MQTTimport#Disconnected Rules#Timer= System#Boot System#BootMode System#Sleep System#Wake TaskExit# TaskInit# Time#Initialized Time#Set WiFi#APmodeDisabled WiFi#APmodeEnabled WiFi#ChangedAccesspoint WiFi#ChangedWiFichannel WiFi#Connected + Event AsyncEvent + on + + /control?cmd= + GPIO GPIOToggle LongPulse LongPulse_mS Monitor UnMonitor Pulse PWM Servo Status MCPGPIO MCPGPIOToggle MCPLongPulse MCPLongPulse_ms MCPPulse Status,MCP Monitor,MCP UnMonitor,MCP MonitorRange;MCP UnMonitorRange,MCP MCPGPIORange MCPGPIOPattern MCPMode MCPModeRange PCFGPIO PCFGPIOToggle PCFLongPulse PCFLongPulse_ms PCFPulse Status,PCF Monitor,PCF UnMonitor,PCF MonitorRange,PCF UnMonitorRange,PCF PCFGPIORange PCFMode Tone RTTTL + ResetPulseCounter SetPulseCounterTotal LogPulseStatistic LCDCmd LCD OledFramedCmd OledFramedCmd,Display OledFramedCmd,Frame MotorShieldCmd,DCMotor MotorShieldCmd,Stepper Sensair_SetRelay PMSX003,Wake PMSX003,Sleep PMSX003,Reset Play Vol Eq Mode Repeat HLWCalibrate HLWReset WemosMotorShieldCMD LolinMotorShieldCMD HeatPumpir MitsubishiHP,temperature MitsubishiHP,power MitsubishiHP,mode MitsubishiHP,fan MitsubishiHP,vane MitsubishiHP,widevane Culreader_write TFTcmd TFT Touch,Rot WakeOnLan Max1704xclearalert + 00[ 01 02] 03{ 04 05} 06( 07 08) 09' 10 11' 12" 13 14" 15` 16 17` 18% 19 20% 21 22 23 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/platformio_esp32_envs.ini b/platformio_esp32_envs.ini index 3fdf954d3..ce7154e78 100644 --- a/platformio_esp32_envs.ini +++ b/platformio_esp32_envs.ini @@ -10,7 +10,7 @@ lib_ignore = ESP8266WiFi, ESP8266Ping, ESP8266WebServer, ESP8266H [esp32_common] extends = common, core_esp32_3_3_0 -lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Adafruit_ST77xx +lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ArduinoOTA, ESP32HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, Adafruit_ST77xx lib_ignore = ${esp32_always.lib_ignore}, ESP32_ping, IRremoteESP8266, HeatpumpIR board_build.f_flash = 80000000L board_build.flash_mode = dout diff --git a/platformio_esp82xx_base.ini b/platformio_esp82xx_base.ini index 7f383aff7..542a8b3e0 100644 --- a/platformio_esp82xx_base.ini +++ b/platformio_esp82xx_base.ini @@ -52,7 +52,7 @@ extends = common board_build.f_cpu = 80000000L build_flags = ${debug_flags.build_flags} ${mqtt_flags.build_flags} -DHTTPCLIENT_1_1_COMPATIBLE=0 build_unflags = -DDEBUG_ESP_PORT -lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, bblanchon/ArduinoJson @ ^6.17.2, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/RABurton ESP8266 Mutex @ ^1.0.2, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ESP8266HTTPUpdateServer, FrogmoreScd30, Adafruit_ST77xx +lib_deps = td-er/ESPeasySerial @ 2.0.8, adafruit/Adafruit ILI9341 @ ^1.5.6, Adafruit GFX Library, LOLIN_EPD, Adafruit BusIO, bblanchon/ArduinoJson @ ^6.17.2, VL53L0X @ 1.3.0, SparkFun VL53L1X 4m Laser Distance Sensor @ 1.2.9, td-er/RABurton ESP8266 Mutex @ ^1.0.2, td-er/SparkFun MAX1704x Fuel Gauge Arduino Library @ ^1.0.1, ESP8266HTTPUpdateServer, FrogmoreScd30, Multi Channel Relay Arduino Library, Adafruit_ST77xx lib_ignore = ${esp82xx_defaults.lib_ignore}, IRremoteESP8266, HeatpumpIR, LittleFS(esp8266), ServoESP32, TinyWireM board = esp12e monitor_filters = esp8266_exception_decoder diff --git a/src/Custom-sample.h b/src/Custom-sample.h index 045971cb3..7525d442d 100644 --- a/src/Custom-sample.h +++ b/src/Custom-sample.h @@ -385,6 +385,7 @@ static const char DATA_ESPEASY_DEFAULT_MIN_CSS[] PROGMEM = { // #define USES_P117 // SCD30 // #define USES_P119 // ITG3205 Gyro // #define USES_P120 // ADXL345 Acceleration / Gravity +// #define USES_P124 // I2C MultiRelay // Special plugins needing IR library diff --git a/src/_P118_Itho.ino b/src/_P118_Itho.ino new file mode 100644 index 000000000..f29d9bf24 --- /dev/null +++ b/src/_P118_Itho.ino @@ -0,0 +1,541 @@ +//####################################################################################################### +//############################## Plugin 118: Itho ventilation unit 868Mhz remote ######################## +//####################################################################################################### + +// author :jodur, 13-1-2018 +// changed :jeroen, 2-11-2019 +// changed :svollebregt, 30-1-2020 - changes to improve stability: volatile decleration of state, +// disable logging within interrupts unles enabled, removed some unused code, +// reduce noInterrupts() blockage of code fragments to prevent crashes +// svollebregt, 16-2-2020 - ISR now sets flag which is checked by 50 per seconds plugin call as +// receive ISR with Ticker was the cause of instability. Inspired by: https://github.com/arnemauer/Ducobox-ESPEasy-Plugin +// svollebregt, 11-04-2020 - Minor changes to make code compatible with latest mega 20200410, removed SYNC1 option for now; +// better to change this value in the Itho-lib code and compile it yourself +// svollebreggt, 13-2-2021 - Now uses rewirtten library made by arjenhiemstra: https://github.com/arjenhiemstra/IthoEcoFanRFT +// svollebregt, 11-2021 - Code improvements + +// Recommended to disable RF receive logging to minimize code execution within interrupts + +// List of commands: +// 1111 to join ESP8266 with Itho ventilation unit +// 9999 to leaveESP8266 with Itho ventilation unit +// 0 to set Itho ventilation unit to standby +// 1 - set Itho ventilation unit to low speed +// 2 - set Itho ventilation unit to medium speed +// 3 - set Itho ventilation unit to high speed +// 4 - set Itho ventilation unit to full speed +// 13 - set itho to high speed with hardware timer (10 min) +// 23 - set itho to high speed with hardware timer (20 min) +// 33 - set itho to high speed with hardware timer (30 min) + +//List of States: + +// 1 - Itho ventilation unit to lowest speed +// 2 - Itho ventilation unit to medium speed +// 3 - Itho ventilation unit to high speed +// 4 - Itho ventilation unit to full speed +// 13 -Itho to high speed with hardware timer (10 min) +// 23 -Itho to high speed with hardware timer (20 min) +// 33 -Itho to high speed with hardware timer (30 min) + +// Usage for http (not case sensitive): +// http://ip/control?cmd=STATE,1111 +// http://ip/control?cmd=STATE,1 +// http://ip/control?cmd=STATE,2 +// http://ip/control?cmd=STATE,3 + +// usage for example mosquito MQTT +// mosquitto_pub -t /Fan/cmd -m 'state 1111' +// mosquitto_pub -t /Fan/cmd -m 'state 1' +// mosquitto_pub -t /Fan/cmd -m 'state 2' +// mosquitto_pub -t /Fan/cmd -m 'state 3' + + +// This code needs the library made by 'arjenhiemstra': https://github.com/arjenhiemstra/IthoEcoFanRFT +// A CC1101 868Mhz transmitter is needed +// See https://gathering.tweakers.net/forum/list_messages/1690945 for more information +// code/idea was inspired by first release of code from 'Thinkpad' + +#ifdef USES_P118 + +#include +#include "IthoCC1101.h" +#include "IthoPacket.h" +#include "_Plugin_Helper.h" + +//This extra settings struct is needed because the default settingsstruct doesn't support strings +struct PLUGIN__ExtraSettingsStruct +{ char ID1[9]; + char ID2[9]; + char ID3[9]; +} PLUGIN_118_ExtraSettings; + +IthoCC1101 PLUGIN_118_rf; + +// extra for interrupt handling +bool PLUGIN_118_ITHOhasPacket = false; +int PLUGIN_118_State=1; // after startup it is assumed that the fan is running low +int PLUGIN_118_OldState=1; +int PLUGIN_118_Timer=0; +int PLUGIN_118_LastIDindex = 0; +int PLUGIN_118_OldLastIDindex = 0; +int8_t Plugin_118_IRQ_pin=-1; +bool PLUGIN_118_InitRunned = false; +bool PLUGIN_118_Log = false; + +// volatile for interrupt function +volatile bool PLUGIN_118_Int = false; +//volatile unsigned long PLUGIN_118_Int_time = 0; + +#define PLUGIN_118 +#define PLUGIN_ID_118 118 +#define PLUGIN_NAME_118 "Communication - Itho ventilation" +#define PLUGIN_VALUENAME1_118 "State" +#define PLUGIN_VALUENAME2_118 "Timer" +#define PLUGIN_VALUENAME3_118 "LastIDindex" + +// Timer values for hardware timer in Fan in seconds +#define PLUGIN_118_Time1 10*60 +#define PLUGIN_118_Time2 20*60 +#define PLUGIN_118_Time3 30*60 + +boolean Plugin_118(byte function, struct EventStruct *event, String &string) +{ + boolean success = false; + + switch (function) + { + + case PLUGIN_DEVICE_ADD: + { + Device[++deviceCount].Number = PLUGIN_ID_118; + Device[deviceCount].Type = DEVICE_TYPE_SINGLE; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_TRIPLE; + Device[deviceCount].Ports = 0; + Device[deviceCount].PullUpOption = false; + Device[deviceCount].InverseLogicOption = false; + Device[deviceCount].FormulaOption = false; + Device[deviceCount].ValueCount = 3; + Device[deviceCount].SendDataOption = true; + Device[deviceCount].TimerOption = false; + Device[deviceCount].TimerOptional = true; + Device[deviceCount].GlobalSyncOption = true; + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_118); + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_118)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_118)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_118)); + break; + } + + case PLUGIN_GET_DEVICEGPIONAMES: + { + event->String1 = formatGpioName_input(F("Interrupt pin (CC1101 GDO2)")); + break; + } + + case PLUGIN_SET_DEFAULTS: //Set defaults address to the one used in old versions of the library for backwards compatability + { + PCONFIG(0) = 1; + PCONFIG(1) = 10; + PCONFIG(2) = 87; + PCONFIG(3) = 81; + success = true; + break; + } + + case PLUGIN_INIT: + { + //If configured interrupt pin differs from configured, release old pin first + if ((Settings.TaskDevicePin1[event->TaskIndex]!=Plugin_118_IRQ_pin) && (Plugin_118_IRQ_pin!=-1)) + { + addLog(LOG_LEVEL_DEBUG, F("IO-PIN changed, deatachinterrupt old pin")); + detachInterrupt(Plugin_118_IRQ_pin); + } + LoadCustomTaskSettings(event->TaskIndex, (byte*)&PLUGIN_118_ExtraSettings, sizeof(PLUGIN_118_ExtraSettings)); + addLog(LOG_LEVEL_INFO, F("Extra Settings PLUGIN_118 loaded")); + PLUGIN_118_rf.setDeviceID(PCONFIG(1), PCONFIG(2), PCONFIG(3)); //DeviceID used to send commands, can also be changed on the fly for multi itho control, 10,87,81 corresponds with old library + PLUGIN_118_rf.init(); + Plugin_118_IRQ_pin = Settings.TaskDevicePin1[event->TaskIndex]; + pinMode(Plugin_118_IRQ_pin, INPUT); + attachInterrupt(Plugin_118_IRQ_pin, PLUGIN_118_ITHOinterrupt, FALLING); + addLog(LOG_LEVEL_INFO, F("CC1101 868Mhz transmitter initialized")); + PLUGIN_118_rf.initReceive(); + PLUGIN_118_InitRunned=true; + success = true; + break; + } + + case PLUGIN_EXIT: + { + addLog(LOG_LEVEL_INFO, F("EXIT PLUGIN_118")); + //remove interupt when plugin is removed + detachInterrupt(Plugin_118_IRQ_pin); + success = true; + break; + } + + case PLUGIN_ONCE_A_SECOND: + { + //decrement timer when timermode is running + if (PLUGIN_118_State>=10) PLUGIN_118_Timer--; + + //if timer has elapsed set Fan state to low + if ((PLUGIN_118_State>=10) && (PLUGIN_118_Timer<=0)) + { + PLUGIN_118_State=1; + PLUGIN_118_Timer=0; + } + + //Publish new data when vars are changed or init has runned or timer is running (update every 2 sec) + if ((PLUGIN_118_OldState!=PLUGIN_118_State) || ((PLUGIN_118_Timer>0) && (PLUGIN_118_Timer % 2==0)) || (PLUGIN_118_OldLastIDindex!=PLUGIN_118_LastIDindex)|| PLUGIN_118_InitRunned) + { + addLog(LOG_LEVEL_DEBUG, F("UPDATE by PLUGIN_ONCE_A_SECOND")); + PLUGIN_118_Publishdata(event); + sendData(event); + //reset flag set by init + PLUGIN_118_InitRunned=false; + } + //Remeber current state for next cycle + PLUGIN_118_OldState=PLUGIN_118_State; + PLUGIN_118_OldLastIDindex =PLUGIN_118_LastIDindex; + success = true; + break; + } + + case PLUGIN_FIFTY_PER_SECOND: + { + if (PLUGIN_118_Int) + { + PLUGIN_118_Int = false; // reset flag + PLUGIN_118_ITHOcheck(); + /* + unsigned long time_elapsed = millis() - PLUGIN_118_Int_time; //Disabled as it doesn't appear to be required + if (time_elapsed >= 10) + { + PLUGIN_118_ITHOcheck(); + } + else + { + delay(10-time_elapsed); + PLUGIN_118_ITHOcheck(); + }*/ + } + success = true; + break; + } + + + case PLUGIN_READ: { + // This ensures that even when Values are not changing, data is send at the configured interval for aquisition + addLog(LOG_LEVEL_DEBUG, F("UPDATE by PLUGIN_READ")); + PLUGIN_118_Publishdata(event); + //sendData(event); //SV - Added to send status every xx secnds as set within plugin + success = true; + break; + } + + case PLUGIN_WRITE: { + String tmpString = string; + String cmd = parseString(tmpString, 1); + if (cmd.equalsIgnoreCase(F("STATE"))) + { + //noInterrupts(); + switch(event->Par1) { + case 1111: //Join command + { + PLUGIN_118_rf.sendCommand(IthoJoin); + PLUGIN_118_rf.initReceive(); + PLUGIN_118_PluginWriteLog(F("join")); + success = true; + break; + } + case 9999: //Leave command + { + PLUGIN_118_rf.sendCommand(IthoLeave); + PLUGIN_118_rf.initReceive(); + PLUGIN_118_PluginWriteLog(F("leave")); + success = true; + break; + } + case 0: //Off command + { + PLUGIN_118_rf.sendCommand(IthoStandby); + PLUGIN_118_State=0; + PLUGIN_118_Timer=0; + PLUGIN_118_LastIDindex = 0; + PLUGIN_118_rf.initReceive(); + PLUGIN_118_PluginWriteLog(F("standby")); + success = true; + break; + } + case 1: //Fan low + { + PLUGIN_118_rf.sendCommand(IthoLow); + PLUGIN_118_State=1; + PLUGIN_118_Timer=0; + PLUGIN_118_LastIDindex = 0; + PLUGIN_118_rf.initReceive(); + PLUGIN_118_PluginWriteLog(F("low speed")); + success = true; + break; + } + case 2: //Fan medium + { + PLUGIN_118_rf.sendCommand(IthoMedium); + PLUGIN_118_State=2; + PLUGIN_118_Timer=0; + PLUGIN_118_LastIDindex = 0; + PLUGIN_118_rf.initReceive(); + PLUGIN_118_PluginWriteLog(F("medium speed")); + success = true; + break; + } + case 3: //Fan high + { + PLUGIN_118_rf.sendCommand(IthoHigh); + PLUGIN_118_State=3; + PLUGIN_118_Timer=0; + PLUGIN_118_LastIDindex = 0; + PLUGIN_118_rf.initReceive(); + PLUGIN_118_PluginWriteLog(F("high speed")); + success = true; + break; + } + case 4: //Fan full + { + PLUGIN_118_rf.sendCommand(IthoFull); + PLUGIN_118_State=4; + PLUGIN_118_Timer=0; + PLUGIN_118_LastIDindex = 0; + PLUGIN_118_rf.initReceive(); + PLUGIN_118_PluginWriteLog(F("full speed")); + success = true; + break; + } + case 13: //Timer1 - 10 min + { + PLUGIN_118_rf.sendCommand(IthoTimer1); + PLUGIN_118_State=13; + PLUGIN_118_Timer=PLUGIN_118_Time1; + PLUGIN_118_LastIDindex = 0; + PLUGIN_118_rf.initReceive(); + PLUGIN_118_PluginWriteLog(F("timer 1")); + success = true; + break; + } + case 23: //Timer2 - 20 min + { + PLUGIN_118_rf.sendCommand(IthoTimer2); + PLUGIN_118_State=23; + PLUGIN_118_Timer=PLUGIN_118_Time2; + PLUGIN_118_LastIDindex = 0; + PLUGIN_118_rf.initReceive(); + PLUGIN_118_PluginWriteLog(F("timer 2")); + success = true; + break; + } + case 33: //Timer3 - 30 min + { + PLUGIN_118_rf.sendCommand(IthoTimer3); + PLUGIN_118_State=33; + PLUGIN_118_Timer=PLUGIN_118_Time3; + PLUGIN_118_LastIDindex = 0; + PLUGIN_118_rf.initReceive(); + PLUGIN_118_PluginWriteLog(F("timer 3")); + success = true; + break; + } + default: + { + PLUGIN_118_PluginWriteLog(F("INVALID")); + success = true; + } + } + //interrupts(); + } + break; + } + + case PLUGIN_WEBFORM_LOAD: + { + addFormSubHeader(F("Remote RF Controls")); + addFormTextBox(F("Unit ID remote 1"), F("PLUGIN_118_ID1"), PLUGIN_118_ExtraSettings.ID1, 8); + addFormTextBox(F("Unit ID remote 2"), F("PLUGIN_118_ID2"), PLUGIN_118_ExtraSettings.ID2, 8); + addFormTextBox(F("Unit ID remote 3"), F("PLUGIN_118_ID3"), PLUGIN_118_ExtraSettings.ID3, 8); + addFormCheckBox(F("Enable RF receive log"), F("p118_log"), PCONFIG(0)); //Makes RF logging optional to reduce clutter in the lof file in RF noisy environments + addFormNumericBox(F("Device ID byte 1"), F("p118_deviceid1"), PCONFIG(1), 0, 255); + addFormNumericBox(F("Device ID byte 2"), F("p118_deviceid2"), PCONFIG(2), 0, 255); + addFormNumericBox(F("Device ID byte 3"), F("p118_deviceid3"), PCONFIG(3), 0, 255); + addFormNote(F("Device ID of your ESP, should not be the same as your neighbours ;-). Defaults to 10,87,81 which corresponds to the old Itho library")); + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + strcpy(PLUGIN_118_ExtraSettings.ID1, web_server.arg(F("PLUGIN_118_ID1")).c_str()); + strcpy(PLUGIN_118_ExtraSettings.ID2, web_server.arg(F("PLUGIN_118_ID2")).c_str()); + strcpy(PLUGIN_118_ExtraSettings.ID3, web_server.arg(F("PLUGIN_118_ID3")).c_str()); + SaveCustomTaskSettings(event->TaskIndex, (byte*)&PLUGIN_118_ExtraSettings, sizeof(PLUGIN_118_ExtraSettings)); + + PCONFIG(0) = isFormItemChecked(F("p118_log")); + PLUGIN_118_Log = PCONFIG(0); + PCONFIG(1) = getFormItemInt(F("p118_deviceid1"), 10); + PCONFIG(2) = getFormItemInt(F("p118_deviceid2"), 87); + PCONFIG(3) = getFormItemInt(F("p118_deviceid3"), 81); + success = true; + break; + } + } + return success; +} + +ICACHE_RAM_ATTR void PLUGIN_118_ITHOinterrupt() +{ + PLUGIN_118_Int = true; //flag + //PLUGIN_118_Int_time = millis(); //used to register time since interrupt, to make sure we don't read within 10 ms as the RX buffer needs some time to get ready. Update: Disabled as it appear not necessary +} + +void PLUGIN_118_ITHOcheck() +{ + if(PLUGIN_118_Log){addLog(LOG_LEVEL_DEBUG, "RF signal received");} //All logs statements contain if-statement to disable logging to reduce log clutter when many RF sources are present + if(PLUGIN_118_rf.checkForNewPacket()) + { + IthoCommand cmd = PLUGIN_118_rf.getLastCommand(); + String Id = PLUGIN_118_rf.getLastIDstr(); + + //Move check here to prevent function calling within ISR + byte index = 0; + if (Id == PLUGIN_118_ExtraSettings.ID1){ + index = 1; + } + else if (Id == PLUGIN_118_ExtraSettings.ID2){ + index = 2; + } + else if (Id == PLUGIN_118_ExtraSettings.ID3){ + index = 3; + } + + //int index = PLUGIN_118_RFRemoteIndex(Id); + // IF id is know index should be >0 + String log2 = ""; + if (index>0) + { + if(PLUGIN_118_Log){ + log2 += F("Command received from remote-ID: "); + log2 += Id; + log2 += F(", command: "); + //addLog(LOG_LEVEL_DEBUG, log); + } + switch (cmd) + { + case IthoUnknown: + if(PLUGIN_118_Log){log2 += F("unknown");} + break; + case IthoStandby: + case DucoStandby: + if(PLUGIN_118_Log){log2 += F("standby");} + PLUGIN_118_State = 0; + PLUGIN_118_Timer = 0; + PLUGIN_118_LastIDindex = index; + break; + case IthoLow: + case DucoLow: + if(PLUGIN_118_Log){log2 += F("low");} + PLUGIN_118_State = 1; + PLUGIN_118_Timer = 0; + PLUGIN_118_LastIDindex = index; + break; + case IthoMedium: + case DucoMedium: + if(PLUGIN_118_Log){log2 += F("medium");} + PLUGIN_118_State = 2; + PLUGIN_118_Timer = 0; + PLUGIN_118_LastIDindex = index; + break; + case IthoHigh: + case DucoHigh: + if(PLUGIN_118_Log){log2 += F("high");} + PLUGIN_118_State = 3; + PLUGIN_118_Timer = 0; + PLUGIN_118_LastIDindex = index; + break; + case IthoFull: + if(PLUGIN_118_Log){log2 += F("full");} + PLUGIN_118_State = 4; + PLUGIN_118_Timer = 0; + PLUGIN_118_LastIDindex = index; + break; + case IthoTimer1: + if(PLUGIN_118_Log){log2 += +F("timer1");} + PLUGIN_118_State = 13; + PLUGIN_118_Timer = PLUGIN_118_Time1; + PLUGIN_118_LastIDindex = index; + break; + case IthoTimer2: + if(PLUGIN_118_Log){log2 += F("timer2");} + PLUGIN_118_State = 23; + PLUGIN_118_Timer = PLUGIN_118_Time2; + PLUGIN_118_LastIDindex = index; + break; + case IthoTimer3: + if(PLUGIN_118_Log){log2 += F("timer3");} + PLUGIN_118_State = 33; + PLUGIN_118_Timer = PLUGIN_118_Time3; + PLUGIN_118_LastIDindex = index; + break; + case IthoJoin: + if(PLUGIN_118_Log){log2 += F("join");} + break; + case IthoLeave: + if(PLUGIN_118_Log){log2 += F("leave");} + break; + } + } + else { + if(PLUGIN_118_Log){ + log2 += F("Device-ID: "); + log2 += Id; + log2 += F(" IGNORED"); + } + } + if(PLUGIN_118_Log){addLog(LOG_LEVEL_DEBUG, log2);} + } +} + +void PLUGIN_118_Publishdata(struct EventStruct *event) +{ + UserVar[event->BaseVarIndex]=PLUGIN_118_State; + UserVar[event->BaseVarIndex+1]=PLUGIN_118_Timer; + UserVar[event->BaseVarIndex+2]=PLUGIN_118_LastIDindex; + + String log = F("State: "); + log += UserVar[event->BaseVarIndex]; + addLog(LOG_LEVEL_DEBUG, log); + log = F("Timer: "); + log += UserVar[event->BaseVarIndex+1]; + addLog(LOG_LEVEL_DEBUG, log); + log = F("LastIDindex: "); + log += UserVar[event->BaseVarIndex+2]; + addLog(LOG_LEVEL_DEBUG, log); +} + +void PLUGIN_118_PluginWriteLog(String command) +{ + String log = F("Send Itho command for: "); + log += command; + addLog(LOG_LEVEL_INFO, log); + printWebString += log; +} + +#endif // USES_P118 diff --git a/src/_P124_MultiRelay.ino b/src/_P124_MultiRelay.ino new file mode 100644 index 000000000..4e802ed84 --- /dev/null +++ b/src/_P124_MultiRelay.ino @@ -0,0 +1,334 @@ +#include "_Plugin_Helper.h" + +#ifdef USES_P124 + +// ####################################################################################################### +// ########################### Plugin 124 I2C Multi Relay module ############################### +// ####################################################################################################### + +/** Changelog: + * 2021-11-21 tonhuisman: Implement configurable I2C addresses, limited to 0x11..-x18 range (8 units) though + * the boards support any I2C address from 0x00 to 0x7F + * Add Relay state on exit/disabling of the plugin. + * WARNING: Is also triggered when the task is enabled and the settings are saved! + * 2021-11-19 tonhuisman: Add protection for single initialization of relays (per reset/boot/powercycle) + * 2021-11-18 tonhuisman: Implement settings, + * Implement write commands: + * multirelay,on, (channel 1..8, max. accepted as configured) + * multirelay,off, + * multirelay,set, (value in decimal, 0xnn hex or 0bnnnnnnnn binary) + * Add binary state to Devices screen, when plugin enabled. + * 2021-11-17 tonhuisman: Initial plugin development. + */ + +# define PLUGIN_124 +# define PLUGIN_ID_124 124 +# define PLUGIN_NAME_124 "Output - I2C Multi Relay [TESTING]" +# define PLUGIN_VALUENAME1_124 "State" +# define PLUGIN_VALUENAME2_124 "Channel" +# define PLUGIN_VALUENAME3_124 "Get" + +# include "./src/PluginStructs/P124_data_struct.h" + +static uint32_t P124_InitializedRelays = 0u; + +boolean Plugin_124(uint8_t function, struct EventStruct *event, String& string) +{ + boolean success = false; + + switch (function) + { + case PLUGIN_DEVICE_ADD: + { + Device[++deviceCount].Number = PLUGIN_ID_124; + Device[deviceCount].Type = DEVICE_TYPE_I2C; + Device[deviceCount].VType = Sensor_VType::SENSOR_TYPE_SINGLE; + Device[deviceCount].Ports = 0; + Device[deviceCount].PullUpOption = false; + Device[deviceCount].InverseLogicOption = false; + Device[deviceCount].FormulaOption = true; + Device[deviceCount].ValueCount = 3; + Device[deviceCount].SendDataOption = true; + Device[deviceCount].TimerOption = true; + Device[deviceCount].TimerOptional = true; + + break; + } + + case PLUGIN_GET_DEVICENAME: + { + string = F(PLUGIN_NAME_124); + break; + } + + case PLUGIN_GET_DEVICEVALUENAMES: + { + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[0], PSTR(PLUGIN_VALUENAME1_124)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[1], PSTR(PLUGIN_VALUENAME2_124)); + strcpy_P(ExtraTaskSettings.TaskDeviceValueNames[2], PSTR(PLUGIN_VALUENAME3_124)); + break; + } + + case PLUGIN_SET_DEFAULTS: + { + P124_CONFIG_I2C_ADDRESS = 0x11; // Default I2C address + ExtraTaskSettings.TaskDeviceValueDecimals[0] = 0; // No decimals needed + ExtraTaskSettings.TaskDeviceValueDecimals[1] = 0; // No decimals needed + ExtraTaskSettings.TaskDeviceValueDecimals[2] = 0; // No decimals needed + break; + } + + case PLUGIN_I2C_HAS_ADDRESS: + case PLUGIN_WEBFORM_SHOW_I2C_PARAMS: + { + // This range of I2C addresses is also maintained in the constructor of P124_data_struct, and must stay consecutive! + const uint8_t i2cAddressValues[] = { 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18 }; + + if (function == PLUGIN_WEBFORM_SHOW_I2C_PARAMS) { + addFormSelectorI2C(F("plugin_124_i2caddress"), 8, i2cAddressValues, P124_CONFIG_I2C_ADDRESS); + + addFormCheckBox(F("Change I2C address of board"), F("plugin_124_change_i2c"), false); + addFormNote( + F("Change of address will be stored in the board and retained until changed again. See documentation for change-procedure.")); + } else { + success = intArrayContains(8, i2cAddressValues, event->Par1); + } + break; + } + + case PLUGIN_WEBFORM_LOAD: + { + const __FlashStringHelper *optionsMode2[] = { + F("2 relays"), + F("4 relays"), + F("8 relays") }; + int optionValuesMode2[] { 2, 4, 8 }; + addFormSelector(F("Number of relays"), F("plugin_124_relays"), 3, optionsMode2, optionValuesMode2, P124_CONFIG_RELAY_COUNT, true); + + const __FlashStringHelper *noYesOptions[] = { + F("No"), + F("Yes") + }; + int noYesValues[] = { 0, 1 }; + addFormSelector(F("Initialize relays on startup"), + getPluginCustomArgName(P124_FLAGS_INIT_RELAYS), 2, noYesOptions, noYesValues, + bitRead(P124_CONFIG_FLAGS, P124_FLAGS_INIT_RELAYS) ? 1 : 0, true); + String label; + + if (bitRead(P124_CONFIG_FLAGS, P124_FLAGS_INIT_RELAYS)) { + addFormCheckBox(F("Apply initial state always"), + getPluginCustomArgName(P124_FLAGS_INIT_ALWAYS), + bitRead(P124_CONFIG_FLAGS, P124_FLAGS_INIT_ALWAYS)); + addFormNote(F("Disabled: Applied once per restart, Enabled: Applied on every plugin start, like on Submit of this page")); + + for (int i = 0; i < P124_CONFIG_RELAY_COUNT; i++) { + label = F("Relay "); + label += i + 1; + label += F(" initial state (on/off)"); + addFormCheckBox(label, getPluginCustomArgName(i), bitRead(P124_CONFIG_FLAGS, i)); + } + } + + addFormSelector(F("Reset relays on exit"), + getPluginCustomArgName(P124_FLAGS_EXIT_RELAYS), 2, noYesOptions, noYesValues, + bitRead(P124_CONFIG_FLAGS, P124_FLAGS_EXIT_RELAYS) ? 1 : 0, true); + + if (bitRead(P124_CONFIG_FLAGS, P124_FLAGS_EXIT_RELAYS)) { + for (int i = 0; i < P124_CONFIG_RELAY_COUNT; i++) { + label = F("Relay "); + label += i + 1; + label += F(" exit-state (on/off)"); + addFormCheckBox(label, getPluginCustomArgName(i + P124_FLAGS_EXIT_OFFSET), bitRead(P124_CONFIG_FLAGS, i + P124_FLAGS_EXIT_OFFSET)); + } + addFormNote(F("ATTENTION: These Relay states will be set when the task is enabled and the settings are saved!")); + } + + addFormCheckBox(F("Loop Channel/Get on read"), + getPluginCustomArgName(P124_FLAGS_LOOP_GET), + bitRead(P124_CONFIG_FLAGS, P124_FLAGS_LOOP_GET)); + + success = true; + break; + } + + case PLUGIN_WEBFORM_SAVE: + { + P124_CONFIG_RELAY_COUNT = getFormItemInt(F("plugin_124_relays")); + P124_CONFIG_I2C_ADDRESS = getFormItemInt(F("plugin_124_i2caddress")); + uint32_t lSettings = 0u; + bitWrite(lSettings, P124_FLAGS_INIT_RELAYS, getFormItemInt(getPluginCustomArgName(P124_FLAGS_INIT_RELAYS)) == 1); + bitWrite(lSettings, P124_FLAGS_INIT_ALWAYS, isFormItemChecked(getPluginCustomArgName(P124_FLAGS_INIT_ALWAYS))); + bitWrite(lSettings, P124_FLAGS_EXIT_RELAYS, getFormItemInt(getPluginCustomArgName(P124_FLAGS_EXIT_RELAYS)) == 1); + bitWrite(lSettings, P124_FLAGS_LOOP_GET, isFormItemChecked(getPluginCustomArgName(P124_FLAGS_LOOP_GET))); + + if (lSettings != 0) { + for (int i = 0; i < P124_CONFIG_RELAY_COUNT; i++) { // INIT and EXIT states + bitWrite(lSettings, i, isFormItemChecked(getPluginCustomArgName(i))); + bitWrite(lSettings, i + P124_FLAGS_EXIT_OFFSET, isFormItemChecked(getPluginCustomArgName(i + P124_FLAGS_EXIT_OFFSET))); + } + } + P124_CONFIG_FLAGS = lSettings; + + if (isFormItemChecked(F("plugin_124_change_i2c"))) { + P124_data_struct *P124_data = new (std::nothrow) P124_data_struct(P124_CONFIG_I2C_ADDRESS, P124_CONFIG_RELAY_COUNT, true); + + if (nullptr != P124_data) { + P124_CONFIG_I2C_ADDRESS++; // Increment, like the Change Address argument does. + + if (P124_CONFIG_I2C_ADDRESS > 0x18) { P124_CONFIG_I2C_ADDRESS = 0x11; } + + delete P124_data; + P124_data = nullptr; + } + } + + success = true; + break; + } + + case PLUGIN_INIT: + { + initPluginTaskData(event->TaskIndex, new (std::nothrow) P124_data_struct(P124_CONFIG_I2C_ADDRESS, P124_CONFIG_RELAY_COUNT)); + P124_data_struct *P124_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr == P124_data) { + return success; + } + + if (P124_data->isInitialized()) { + String log; + log.reserve(46); + log = F("MultiRelay: Initialized, firmware version: "); + log += P124_data->getFirmwareVersion(); + addLog(LOG_LEVEL_INFO, log); + + if (bitRead(P124_CONFIG_FLAGS, P124_FLAGS_INIT_RELAYS) && + (!bitRead(P124_InitializedRelays, event->TaskIndex) || + bitRead(P124_CONFIG_FLAGS, P124_FLAGS_INIT_ALWAYS))) { + P124_data->channelCtrl(get8BitFromUL(P124_CONFIG_FLAGS, P124_FLAGS_INIT_OFFSET)); // Set relays state + UserVar[event->BaseVarIndex] = P124_data->getChannelState(); // Get relays state + bitSet(P124_InitializedRelays, event->TaskIndex); // Update initialization status + } + P124_data->setLoopState(bitRead(P124_CONFIG_FLAGS, P124_FLAGS_LOOP_GET)); // Loop state + success = true; + } else { + addLog(LOG_LEVEL_ERROR, F("MultiRelay: Initialization error!")); + } + + break; + } + + case PLUGIN_EXIT: + { + P124_data_struct *P124_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr != P124_data) { + if (P124_data->isInitialized()) { + P124_data->channelCtrl(get8BitFromUL(P124_CONFIG_FLAGS, P124_FLAGS_EXIT_OFFSET)); // Set relays state + UserVar[event->BaseVarIndex] = P124_data->getChannelState(); // Get relays state + } + addLog(LOG_LEVEL_INFO, F("MultiRelay: Object still alive.")); + } + success = true; + break; + } + + case PLUGIN_READ: + { + P124_data_struct *P124_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr == P124_data) { + return success; + } + + if (P124_data->isInitialized()) { + UserVar[event->BaseVarIndex] = P124_data->getChannelState(); // Get relays state + + if (P124_data->isLoopEnabled()) { + uint8_t chan = P124_data->getNextLoop(); + uint8_t data = P124_data->getChannelState() & (1 << (chan - 1)); + UserVar[event->BaseVarIndex + 1] = chan; + UserVar[event->BaseVarIndex + 2] = (data ? 1 : 0); + } + success = true; + } + break; + } + + case PLUGIN_WEBFORM_SHOW_VALUES: + { + P124_data_struct *P124_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if ((nullptr != P124_data) && P124_data->isInitialized()) { + uint8_t varNr = 3; // VARS_PER_TASK; + String label = F("Relay state "); + label += P124_CONFIG_RELAY_COUNT; + label += F(".."); + label += 1; + String state = F("0b "); + uint32_t val = UserVar[event->BaseVarIndex]; + val &= 0xff; + val |= (0x1 << P124_CONFIG_RELAY_COUNT); + state += ull2String(val, 2); + state.remove(3, 1); // Delete leading 1 we added + pluginWebformShowValue(event->TaskIndex, varNr++, label, state, true); + + // success = true; + } + break; + } + + case PLUGIN_WRITE: + { + P124_data_struct *P124_data = static_cast(getPluginTaskData(event->TaskIndex)); + + if (nullptr == P124_data) { + return success; + } + + # ifdef P124_DEBUG_LOG + addLog(LOG_LEVEL_INFO, string); + String log = F("Par1..3:"); + log += event->Par1; + log += ','; + log += event->Par2; + log += ','; + log += event->Par3; + addLog(LOG_LEVEL_INFO, log); + # endif // ifdef P124_DEBUG_LOG + + String command = parseString(string, 1); + + if (P124_data->isInitialized() && + command.equals(F("multirelay"))) { + String subcommand = parseString(string, 2); + + if (subcommand.equals(F("on"))) { + success = P124_data->turn_on_channel(event->Par2); + } else if (subcommand.equals(F("off"))) { + success = P124_data->turn_off_channel(event->Par2); + } else if (subcommand.equals(F("set"))) { + success = P124_data->channelCtrl(event->Par2); + } else if (subcommand.equals(F("get")) && (event->Par2 > 0) && (event->Par2 <= P124_CONFIG_RELAY_COUNT)) { + uint8_t data = P124_data->getChannelState() & (1 << (event->Par2 - 1)); + UserVar[event->BaseVarIndex + 1] = event->Par2; + UserVar[event->BaseVarIndex + 2] = (data ? 1 : 0); + + success = true; + } else if (subcommand.equals(F("loop")) && (event->Par2 >= 0) && (event->Par2 <= 1)) { + P124_data->setLoopState(event->Par2 == 1); + + success = true; + } + + if (success) { + UserVar[event->BaseVarIndex] = P124_data->getChannelState(); // Get relays state + } + } + break; + } + } + return success; +} + +#endif // ifdef USES_P124 diff --git a/src/src/Commands/InternalCommands.cpp b/src/src/Commands/InternalCommands.cpp index a1584ca58..94fd6a5c4 100644 --- a/src/src/Commands/InternalCommands.cpp +++ b/src/src/Commands/InternalCommands.cpp @@ -268,6 +268,7 @@ bool executeInternalCommand(command_case_data & data) } case 'c': { COMMAND_CASE_R( "clearaccessblock", Command_AccessInfo_Clear, 0); // Network Command + COMMAND_CASE_R( "clearpassword", Command_Settings_Password_Clear, 1); // Settings.h COMMAND_CASE_R( "clearrtcram", Command_RTC_Clear, 0); // RTC.h COMMAND_CASE_R( "config", Command_Task_RemoteConfig, -1); // Tasks.h COMMAND_CASE_R("controllerdisable", Command_Controller_Disable, 1); // Controller.h diff --git a/src/src/Commands/Settings.cpp b/src/src/Commands/Settings.cpp index 61b2bf293..0e16e99b8 100644 --- a/src/src/Commands/Settings.cpp +++ b/src/src/Commands/Settings.cpp @@ -62,6 +62,20 @@ String Command_Settings_Password(struct EventStruct *event, const char* Line) ); } +String Command_Settings_Password_Clear(struct EventStruct *event, const char* Line) +{ + const String storedPassword = SecuritySettings.getPassword(); + if (storedPassword.length() > 0) { + // There is a password set, so we must check it. + const String password = parseStringKeepCase(Line, 2); + if (!storedPassword.equals(password)) { + return return_command_failed(); + } + ZERO_FILL(SecuritySettings.Password); + } + return return_command_success(); +} + const __FlashStringHelper * Command_Settings_Save(struct EventStruct *event, const char* Line) { SaveSettings(); diff --git a/src/src/Commands/Settings.h b/src/src/Commands/Settings.h index b6dc63a0c..bf39baf2a 100644 --- a/src/src/Commands/Settings.h +++ b/src/src/Commands/Settings.h @@ -7,6 +7,7 @@ String Command_Settings_Build(struct EventStruct *event, const char* Line); String Command_Settings_Unit(struct EventStruct *event, const char* Line); String Command_Settings_Name(struct EventStruct *event, const char* Line); String Command_Settings_Password(struct EventStruct *event, const char* Line); +String Command_Settings_Password_Clear(struct EventStruct *event, const char* Line); const __FlashStringHelper * Command_Settings_Save(struct EventStruct *event, const char* Line); const __FlashStringHelper * Command_Settings_Load(struct EventStruct *event, const char* Line); const __FlashStringHelper * Command_Settings_Print(struct EventStruct *event, const char* Line); diff --git a/src/src/CustomBuild/define_plugin_sets.h b/src/src/CustomBuild/define_plugin_sets.h index b13cbc5f3..bacceef9a 100644 --- a/src/src/CustomBuild/define_plugin_sets.h +++ b/src/src/CustomBuild/define_plugin_sets.h @@ -1138,6 +1138,8 @@ To create/register a plugin, you have to : #define USES_P114 // VEML6075 UVA/UVB sensor #define USES_P115 // Fuel Gauge MAX1704x #define USES_P117 // SCD30 + #define USES_P118 // Itho ventilation control + #define USES_P124 // I2C MultiRelay #endif @@ -1149,14 +1151,14 @@ To create/register a plugin, you have to : #ifndef USES_P027 #define USES_P027 // INA219 #endif - #ifndef USES_P076 + #ifndef USES_P076 #define USES_P076 // HWL8012 in POW r1 #endif - #ifndef USES_P077 + #ifndef USES_P077 // Needs CSE7766 Energy sensor, via Serial RXD 4800 baud 8E1 (GPIO1), TXD (GPIO3) #define USES_P077 // CSE7766 in POW R2 #endif - #ifndef USES_P078 + #ifndef USES_P078 #define USES_P078 // Eastron Modbus Energy meters #endif #ifndef USES_P085 @@ -1168,7 +1170,7 @@ To create/register a plugin, you have to : #ifndef USES_P102 #define USES_P102 // PZEM-004Tv30 #endif - #ifndef USES_P108 + #ifndef USES_P108 #define USES_P108 // DDS238-x ZN MODBUS energy meter (was P224 in the Playground) #endif #ifndef USES_P115 @@ -1190,31 +1192,31 @@ To create/register a plugin, you have to : #ifndef USES_P023 #define USES_P023 // OLED #endif - #ifndef USES_P036 + #ifndef USES_P036 #define USES_P036 // FrameOLED #endif - #ifndef USES_P038 + #ifndef USES_P038 #define USES_P038 // NeoPixel #endif - #ifndef USES_P041 + #ifndef USES_P041 #define USES_P041 // NeoClock #endif - #ifndef USES_P042 + #ifndef USES_P042 #define USES_P042 // Candle #endif - #ifndef USES_P057 + #ifndef USES_P057 #define USES_P057 // HT16K33_LED #endif - #ifndef USES_P070 + #ifndef USES_P070 #define USES_P070 // NeoPixel_Clock #endif - #ifndef USES_P075 + #ifndef USES_P075 #define USES_P075 // Nextion #endif - #ifndef USES_P095 + #ifndef USES_P095 #define USES_P095 // TFT ILI9341 #endif - #ifndef USES_P096 + #ifndef USES_P096 #define USES_P096 // eInk (Needs lib_deps = Adafruit GFX Library, LOLIN_EPD ) #endif #ifndef USES_P099 @@ -1296,7 +1298,7 @@ To create/register a plugin, you have to : #define USES_P142 // RGB-Strip #define USES_P143 // AnyonePresent #define USES_P144 // RC-Switch-TX - #define USES_P145 // Itho + #define USES_P145 // Itho - P118 in the main repo. #define USES_P149 // MHZ19 #define USES_P150 // SDM120C #define USES_P151 // CISA @@ -1429,7 +1431,7 @@ To create/register a plugin, you have to : #define USES_P117 // SCD30 #endif #ifndef USES_P118 - #define USES_P118 // + #define USES_P118 // Itho ventilation coontrol #endif #ifndef USES_P119 #define USES_P119 // ITG3205 Gyro @@ -1438,19 +1440,19 @@ To create/register a plugin, you have to : #define USES_P120 // ADXL345 Acceleration / Gravity #endif #ifndef USES_P121 - #define USES_P121 // + #define USES_P121 // #endif #ifndef USES_P122 - #define USES_P122 // + #define USES_P122 // #endif #ifndef USES_P123 - #define USES_P123 // + #define USES_P123 // #endif #ifndef USES_P124 - #define USES_P124 // + #define USES_P124 // #endif #ifndef USES_P125 - #define USES_P125 // + #define USES_P125 // #endif // Controllers diff --git a/src/src/DataStructs/SecurityStruct.cpp b/src/src/DataStructs/SecurityStruct.cpp index c9caba1d8..2a81ea4be 100644 --- a/src/src/DataStructs/SecurityStruct.cpp +++ b/src/src/DataStructs/SecurityStruct.cpp @@ -68,3 +68,13 @@ bool SecurityStruct::hasWiFiCredentials(SecurityStruct::WiFiCredentialsSlot slot } return false; } + +String SecurityStruct::getPassword() const { + String res; + const size_t passLength = strnlen(Password, sizeof(Password)); + res.reserve(passLength); + for (size_t i = 0; i < passLength; ++i) { + res += Password[i]; + } + return res; +} \ No newline at end of file diff --git a/src/src/DataStructs/SecurityStruct.h b/src/src/DataStructs/SecurityStruct.h index 49c66acc1..fca718e48 100644 --- a/src/src/DataStructs/SecurityStruct.h +++ b/src/src/DataStructs/SecurityStruct.h @@ -27,6 +27,8 @@ struct SecurityStruct bool hasWiFiCredentials(WiFiCredentialsSlot slot) const; + String getPassword() const; + char WifiSSID[32]; char WifiKey[64]; char WifiSSID2[32]; diff --git a/src/src/PluginStructs/P124_data_struct.cpp b/src/src/PluginStructs/P124_data_struct.cpp new file mode 100644 index 000000000..17670bc67 --- /dev/null +++ b/src/src/PluginStructs/P124_data_struct.cpp @@ -0,0 +1,92 @@ +#include "../PluginStructs/P124_data_struct.h" + +#ifdef USES_P124 + +// **************************************************************************/ +// Constructor +// **************************************************************************/ +P124_data_struct::P124_data_struct(int8_t i2c_address, + uint8_t relayCount, + bool changeAddress) + : _i2c_address(i2c_address), _relayCount(relayCount) { + relay = new (std::nothrow) Multi_Channel_Relay(); + + if (isInitialized()) { + relay->begin(_i2c_address); + + if (changeAddress) { + // This increment shpould match with the range of addresses in _P124_MultiRelay.ino PLUGIN_I2C_HAS_ADDRESS + uint8_t _new_address = _i2c_address == 0x18 ? 0x11 : _i2c_address + 1; // Set to next address + relay->changeI2CAddress(_new_address, _i2c_address); + # ifndef BUILD_NO_DEBUG + String log = F("MultiRelay: Change I2C address 0x"); + log += String(_i2c_address, HEX); + log += F(" to 0x"); + log += String(_new_address, HEX); + addLog(LOG_LEVEL_INFO, log); + # endif // ifndef BUILD_NO_DEBUG + } + } +} + +// **************************************************************************/ +// Destructor +// **************************************************************************/ +P124_data_struct::~P124_data_struct() { + if (isInitialized()) { + delete relay; + relay = nullptr; + } +} + +uint8_t P124_data_struct::getChannelState() { + if (isInitialized()) { + return relay->getChannelState(); + } + return 0u; +} + +uint8_t P124_data_struct::getFirmwareVersion() { + if (isInitialized()) { + return relay->getFirmwareVersion(); + } + return 0u; +} + +bool P124_data_struct::channelCtrl(uint8_t state) { + if (isInitialized()) { + relay->channelCtrl(state); + return true; + } + return false; +} + +bool P124_data_struct::turn_on_channel(uint8_t channel) { + if (isInitialized() && (validChannel(channel))) { + relay->turn_on_channel(channel); + return true; + } + return false; +} + +bool P124_data_struct::turn_off_channel(uint8_t channel) { + if (isInitialized() && (validChannel(channel))) { + relay->turn_off_channel(channel); + return true; + } + return false; +} + +uint8_t P124_data_struct::getNextLoop() { + if (_loopEnabled) { + _getLoop++; + + if (_getLoop > _relayCount) { + _getLoop = 1; + } + return _getLoop; + } + return 0; +} + +#endif // ifdef USES_P124 diff --git a/src/src/PluginStructs/P124_data_struct.h b/src/src/PluginStructs/P124_data_struct.h new file mode 100644 index 000000000..b55e3ce38 --- /dev/null +++ b/src/src/PluginStructs/P124_data_struct.h @@ -0,0 +1,64 @@ +#ifndef PLUGINSTRUCTS_P124_DATA_STRUCT_H +#define PLUGINSTRUCTS_P124_DATA_STRUCT_H + +#include "../../_Plugin_Helper.h" +#ifdef USES_P124 + +# include + +// # define P124_DEBUG_LOG // Enable for some (extra) logging + +# define P124_CONFIG_RELAY_COUNT PCONFIG(0) +# define P124_CONFIG_I2C_ADDRESS PCONFIG(7) // As P0 is already in use, pick the last of the range +# define P124_CONFIG_FLAGS PCONFIG_LONG(0) + +# define P124_FLAGS_INIT_OFFSET 0 // 0..7 hold the on/off state of each relay +# define P124_FLAGS_INIT_RELAYS 8 // Initialize relays at startup +# define P124_FLAGS_INIT_ALWAYS 9 // Apply relay initialization on every plugin restart +# define P124_FLAGS_EXIT_RELAYS 10 // Initialize relays when the plugin is disabled +# define P124_FLAGS_LOOP_GET 11 // Loop the state of the channels into Channel/Get values +# define P124_FLAGS_EXIT_OFFSET 16 // 16..23 hold the on/off exit-state of each relay + +struct P124_data_struct : public PluginTaskData_base { +public: + + P124_data_struct(int8_t i2c_address, + uint8_t relayCount, + bool changeAddress = false); + + P124_data_struct() = delete; + ~P124_data_struct(); + + bool isInitialized() { + return relay != nullptr; + } + + uint8_t getChannelState(); + uint8_t getFirmwareVersion(); + bool channelCtrl(uint8_t state); + bool turn_on_channel(uint8_t channel); + bool turn_off_channel(uint8_t channel); + uint8_t getNextLoop(); + bool isLoopEnabled() { + return _loopEnabled; + } + + void setLoopState(bool state) { + _loopEnabled = state; + } + +private: + + bool validChannel(uint channel) { + return channel > 0 && channel <= _relayCount; + } + + Multi_Channel_Relay *relay = nullptr; + + int8_t _i2c_address; + uint8_t _relayCount; + uint8_t _getLoop = 0; + bool _loopEnabled = false; +}; +#endif // ifdef USES_P124 +#endif // ifndef PLUGINSTRUCTS_P124_DATA_STRUCT_H diff --git a/src/src/WebServer/I2C_Scanner.cpp b/src/src/WebServer/I2C_Scanner.cpp index 8360cf68c..d85debf22 100644 --- a/src/src/WebServer/I2C_Scanner.cpp +++ b/src/src/WebServer/I2C_Scanner.cpp @@ -176,6 +176,21 @@ String getKnownI2Cdevice(uint8_t address) { switch (address) { + case 0x10: + result += F("VEML6075"); + break; + case 0x11: + result += F("VEML6075,I2C_MultiRelay"); + break; + case 0x12: + case 0x13: + case 0x14: + case 0x15: + case 0x16: + case 0x17: + case 0x18: + result += F("I2C_MultiRelay"); + break; case 0x20: case 0x21: case 0x22: