├── .gitignore ├── Master ├── Itho │ ├── CC1101Packet.h │ ├── IthoCC1101.h │ ├── CC1101.cpp │ ├── CC1101.h │ ├── IthoPacket.h │ └── IthoCC1101.cpp └── IthoEcoFanRFT │ └── IthoEcoFanRFT.ino ├── IthoEcoFanRft.atsln └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | *.d 2 | Master/Debug/suart/BBuart.o 3 | *.o 4 | Master/Debug/Master.srec 5 | *.atsuo 6 | Master/Debug/Makefile 7 | *.map 8 | Master/Debug/Master.lss 9 | *.eep 10 | *.hex 11 | Master/Debug/Master.elf 12 | Master/Debug/makedep.mk -------------------------------------------------------------------------------- /Master/Itho/CC1101Packet.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #define CC1101_BUFFER_LEN 64 11 | #define CC1101_DATA_LEN CC1101_BUFFER_LEN - 3 12 | #define MAX_RAW 162 13 | 14 | class CC1101Packet 15 | { 16 | public: 17 | uint8_t length = 0; 18 | uint8_t data[MAX_RAW]{}; 19 | }; 20 | -------------------------------------------------------------------------------- /IthoEcoFanRft.atsln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Atmel Studio Solution File, Format Version 11.00 4 | VisualStudioVersion = 14.0.23107.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{E66E83B9-2572-4076-B26E-6BE79FF3018A}") = "Master", "Master\Master.cppproj", "{FDBDDBF9-5449-4629-93BB-1AD1B03C10C3}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|AVR = Debug|AVR 11 | Release|AVR = Release|AVR 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {FDBDDBF9-5449-4629-93BB-1AD1B03C10C3}.Debug|AVR.ActiveCfg = Debug|AVR 15 | {FDBDDBF9-5449-4629-93BB-1AD1B03C10C3}.Debug|AVR.Build.0 = Debug|AVR 16 | {FDBDDBF9-5449-4629-93BB-1AD1B03C10C3}.Release|AVR.ActiveCfg = Release|AVR 17 | {FDBDDBF9-5449-4629-93BB-1AD1B03C10C3}.Release|AVR.Build.0 = Release|AVR 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IthoEcoFanRFT 2 | Cloned from supersjimmie which cloned from Klusjesman, 3 | 4 | Complete rework of the itho packet section, cleanup and easier to understand, improved stability 5 | ``` 6 | * Library structure is preserved, should be a drop in replacement (apart from device id) 7 | * Decode incoming messages to direct usable decimals without further bit-shifting 8 | * DeviceID is now 3 bytes long and can be set during runtime 9 | * Counter2 is now the decimal sum of all bytes in decoded form from deviceType up to the last byte before counter2 subtracted from zero. 10 | * Encode outgoing messages in itho compatible format 11 | * Added ICACHE_RAM_ATTR to 'void ITHOcheck()' for ESP8266/ESP32 compatibility 12 | * Trigger on the falling edge and simplified ISR routine for more robust packet handling 13 | * Move SYNC word from 171,170 further down the message to 179,42,163,42 to filter out more non-itho messages in CC1101 hardware 14 | * Support for RFT-CO2 and RFT-RV devices (monitor values and commands) 15 | 16 | Tested on ESP8266 & ESP32, compiles on Arduino Uno but might be memory heavy 17 | ``` 18 | 19 | Will work with a 868MHz CC1101 module. 20 | The CC1150 may also work, except for receiving (which is not required for controlling an Itho EcoFan). 21 | A 433MHz CC1101/CC1150 might also work, because it has the same chip. But a 433MHz CC11xx board has a different RF filter, causing a lot less transmission power (and reception). 22 | ``` 23 | Connections between the CC1101 and the ESP8266 or Arduino: 24 | CC11xx pins ESP pins Arduino pins Description 25 | * 1 - VCC VCC VCC 3v3 26 | * 2 - GND GND GND Ground 27 | * 3 - MOSI 13=D7 Pin 11 Data input to CC11xx 28 | * 4 - SCK 14=D5 Pin 13 Clock pin 29 | * 5 - MISO/GDO1 12=D6 Pin 12 Data output from CC11xx / serial clock from CC11xx 30 | * 6 - GDO2 04=D2 Pin 2 Programmable output 31 | * 7 - GDO0 ? Pin ? Programmable output 32 | * 8 - CSN 15=D8 Pin 10 Chip select / (SPI_SS) 33 | ``` 34 | Note that CC11xx pin GDO0 is not used. 35 | Also note that the GDO2 pin connected to pin 2 on an Arduino. Change ```#define ITHO_IRQ_PIN``` in the example ino accordingly. 36 | 37 | You should keep the wires to the CC11xx module as short as possible. 38 | 39 | Beware that the CC11xx modules are 3.3V (3.6V max) on all pins! 40 | This won't be a problem with an ESP8266, but for Arduino you either need to use a 3.3V Arduino or use levelshifters and a separate 3.3V power source. 41 | 42 | For use with an ESP8266, you will need the ESP8266 core for Arduino from https://github.com/esp8266/Arduino 43 | 44 | For SPI, pins 12-15 (aka D5-D8) are used. 45 | A larger ESP8266 model like (but not only) the ESP-03, ESP-07, ESP-12(D, E) or a NodeMCU board is required. 46 | -------------------------------------------------------------------------------- /Master/IthoEcoFanRFT/IthoEcoFanRFT.ino: -------------------------------------------------------------------------------- 1 | /* 2 | Original Author: Klusjesman & supersjimmie 3 | 4 | Tested with STK500 + ATMega328P 5 | GCC-AVR compiler 6 | 7 | Modified by arjenhiemstra: 8 | Complete rework of the itho packet section, cleanup and easier to understand 9 | Library structure is preserved, should be a drop in replacement (apart from device id) 10 | Decode incoming messages to direct usable decimals without further bit-shifting 11 | DeviceID is now 3 bytes long and can be set during runtime 12 | Counter2 is the decimal sum of all bytes in decoded form from deviceType up to the last byte before counter2 subtracted from zero. 13 | Encode outgoing messages in itho compatible format 14 | Added ICACHE_RAM_ATTR to 'void ITHOcheck()' for ESP8266/ESP32 compatibility 15 | Trigger on the falling edge and simplified ISR routine for more robust packet handling 16 | Move SYNC word from 171,170 further down the message to 179,42,163,42 to filter out more non-itho messages in CC1101 hardware 17 | Check validity of incoming message 18 | 19 | Tested on ESP8266 & ESP32 20 | */ 21 | 22 | /* 23 | CC11xx pins ESP pins Arduino pins Description 24 | 1 - VCC VCC VCC 3v3 25 | 2 - GND GND GND Ground 26 | 3 - MOSI 13=D7 Pin 11 Data input to CC11xx 27 | 4 - SCK 14=D5 Pin 13 Clock pin 28 | 5 - MISO/GDO1 12=D6 Pin 12 Data output from CC11xx / serial clock from CC11xx 29 | 6 - GDO2 04=D2 Pin 2? Serial data to CC11xx 30 | 7 - GDO0 ? Pin ? output as a symbol of receiving or sending data 31 | 8 - CSN 15=D8 Pin 10 Chip select / (SPI_SS) 32 | */ 33 | 34 | #include 35 | #include "IthoCC1101.h" 36 | #include "IthoPacket.h" 37 | 38 | #define ITHO_IRQ_PIN 4 //D2(GPIO4) on NodeMCU 39 | 40 | IthoCC1101 rf; 41 | 42 | const uint8_t RFTid[] = {11, 22, 33}; // my ID 43 | 44 | bool ITHOhasPacket = false; 45 | 46 | void setup(void) { 47 | Serial.begin(115200); 48 | delay(500); 49 | Serial.println("setup begin"); 50 | rf.updateRFDeviceID(0x12,0x34,0x56, 0); //to set device ID for send for first remote (ID0, ID1, ID3, remote index) 51 | rf.updateRFDeviceType(RFTAUTO, 0); //sets device type to RFT Auto remote instead of default RFT CVE, for first remote (remote type, remote index) 52 | //rf.setRemoteType(RemoteTypes::RFTCVE); //DeviceType used to send commands 53 | rf.init(); 54 | Serial.println("setup done"); 55 | rf.sendCommand(IthoJoin); //use this instead of sendRegister(), sends a join commando with first remote (remote index=0) 56 | //rf.sendRFCommand(0, join); (remote index=0, command=join) to send a command with a specifiek configured remote 57 | Serial.println("join command sent"); 58 | pinMode(ITHO_IRQ_PIN, INPUT); 59 | attachInterrupt(ITHO_IRQ_PIN, ITHOcheck, FALLING); 60 | } 61 | 62 | void loop(void) { 63 | if (ITHOhasPacket) { 64 | ITHOhasPacket = false; // clear the interrupt flag 65 | if (rf.checkForNewPacket()) { 66 | int *lastID = rf.getLastID(); 67 | IthoCommand cmd = rf.getLastCommand(); 68 | RemoteTypes remtype = rf.getLastRemType(); 69 | if (cmd != IthoUnknown) { 70 | showPacket(); 71 | } 72 | 73 | } 74 | } 75 | } 76 | 77 | #if defined (ESP8266) || defined (ESP32) 78 | IRAM_ATTR void ITHOcheck() { 79 | #else 80 | void ITHOcheck() { 81 | #endif 82 | ITHOhasPacket = true; 83 | } 84 | 85 | void showPacket() { 86 | // example: 87 | // H:16 _I P0:72 P1:-- --,--,-- --,--,-- 12,34,56 22F3 03:63,00,0A 88 | // 89 | // H=header hex byte, 0x16 in this example is an itho remote 90 | // _I = message type 91 | // P0: param0 (0x72 in this example is a sequence number) 92 | // P1: param1 (not present in this example) 93 | // --,--,-- = source ID 94 | // --,--,-- = destination ID 95 | // 12,34,56 = broadcast ID 96 | // 22F3 = message class (RFT Auto in this case) 97 | // 03 = command length 98 | // 63,00,0A = command bytes 99 | Serial.print(rf.LastMessageDecoded()); 100 | } 101 | 102 | 103 | void sendRegister() { 104 | Serial.println("sending join..."); 105 | rf.sendCommand(IthoJoin); 106 | Serial.println("sending join done."); 107 | } 108 | 109 | void sendStandbySpeed() { 110 | Serial.println("sending away..."); 111 | rf.sendCommand(IthoAway); 112 | Serial.println("sending away done."); 113 | } 114 | 115 | void sendLowSpeed() { 116 | Serial.println("sending low..."); 117 | rf.sendCommand(IthoLow); 118 | Serial.println("sending low done."); 119 | } 120 | 121 | void sendMediumSpeed() { 122 | Serial.println("sending medium..."); 123 | rf.sendCommand(IthoMedium); 124 | Serial.println("sending medium done."); 125 | } 126 | 127 | void sendHighSpeed() { 128 | Serial.println("sending high..."); 129 | rf.sendCommand(IthoHigh); 130 | Serial.println("sending high done."); 131 | } 132 | 133 | void sendFullSpeed() { 134 | Serial.println("sending FullSpeed..."); 135 | rf.sendCommand(IthoFull); 136 | Serial.println("sending FullSpeed done."); 137 | } 138 | 139 | void sendTimer() { 140 | Serial.println("sending timer..."); 141 | rf.sendCommand(IthoTimer1); 142 | Serial.println("sending timer done."); 143 | } 144 | -------------------------------------------------------------------------------- /Master/Itho/IthoCC1101.h: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Klusjesman, supersjimmie, modified and reworked by arjenhiemstra 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | #include "CC1101.h" 11 | #include "IthoPacket.h" 12 | 13 | #define MAX_NUM_OF_REMOTES 10 14 | 15 | struct ithoRFDevice 16 | { 17 | uint32_t deviceId{0}; 18 | RemoteTypes remType{RemoteTypes::RFTCVE}; 19 | // char name[16]; 20 | IthoCommand lastCommand{IthoUnknown}; 21 | time_t timestamp; 22 | uint8_t counter; 23 | bool bidirectional{false}; 24 | int32_t co2{0xEFFF}; 25 | int32_t temp{0xEFFF}; 26 | int32_t hum{0xEFFF}; 27 | uint8_t pir{0xEF}; 28 | int32_t dewpoint{0xEFFF}; 29 | int32_t battery{0xEFFF}; 30 | }; 31 | 32 | struct RFmessage 33 | { 34 | uint8_t header{0xC0}; 35 | uint8_t deviceid0[3]{0}; 36 | uint8_t deviceid1[3]{0}; 37 | uint8_t deviceid2[3]{0}; 38 | uint8_t opt0{0}; 39 | uint8_t opt1{0}; 40 | uint16_t opcode{0}; 41 | uint8_t len{0}; 42 | const uint8_t *command{nullptr}; 43 | // uint8_t checksum; 44 | }; 45 | 46 | struct ithoRFDevices 47 | { 48 | uint8_t count{0}; 49 | ithoRFDevice device[MAX_NUM_OF_REMOTES]; 50 | }; 51 | 52 | // pa table settings 53 | const uint8_t ithoPaTableSend[8] = {0x6F, 0x26, 0x2E, 0x8C, 0x87, 0xCD, 0xC7, 0xC0}; 54 | const uint8_t ithoPaTableReceive[8] = {0x6F, 0x26, 0x2E, 0x7F, 0x8A, 0x84, 0xCA, 0xC4}; 55 | 56 | class IthoPacket; 57 | 58 | class IthoCC1101 : protected CC1101 59 | { 60 | private: 61 | // receive 62 | CC1101Packet inMessage; // temp storage message2 63 | IthoPacket inIthoPacket; // stores last received message data 64 | 65 | // send 66 | uint8_t deviceIDsend[3]{33, 66, 99}; 67 | IthoPacket outIthoPacket; // stores state of "remote" 68 | int8_t joinreply_test{-1}; 69 | 70 | // settings 71 | uint8_t sendTries; // number of times a command is send at one button press 72 | uint8_t cc_freq[3]; // FREQ0, FREQ1, FREQ2 73 | 74 | // Itho remotes 75 | bool bindAllowed; 76 | bool allowAll; 77 | ithoRFDevices ithoRF; 78 | 79 | typedef struct 80 | { 81 | IthoCommand code; 82 | const char *msg; 83 | } remote_command_char; 84 | 85 | const remote_command_char remote_command_msg_table[15]{ 86 | {IthoUnknown, "IthoUnknown"}, 87 | {IthoJoin, "IthoJoin"}, 88 | {IthoLeave, "IthoLeave"}, 89 | {IthoAway, "IthoAway"}, 90 | {IthoLow, "IthoLow"}, 91 | {IthoMedium, "IthoMedium"}, 92 | {IthoHigh, "IthoHigh"}, 93 | {IthoFull, "IthoFull"}, 94 | {IthoTimer1, "IthoTimer1"}, 95 | {IthoTimer2, "IthoTimer2"}, 96 | {IthoTimer3, "IthoTimer3"}, 97 | {IthoAuto, "IthoAuto"}, 98 | {IthoAutoNight, "IthoAutoNight"}, 99 | {IthoCook30, "IthoCook30"}, 100 | {IthoCook60, "IthoCook60"}}; 101 | 102 | const char *remote_unknown_msg = "CMD UNKNOWN ERROR"; 103 | // functions 104 | public: 105 | IthoCC1101(uint8_t counter = 0, uint8_t sendTries = 3); // set initial counter value 106 | ~IthoCC1101(); 107 | uint8_t CC1101MessageLen{}; 108 | uint8_t IthoPacketLen{}; 109 | // init 110 | void init() 111 | { 112 | CC1101::init(); // init,reset CC1101 113 | initReceive(); 114 | } 115 | void initReceive(); 116 | 117 | void setSendTries(uint8_t sendTries) 118 | { 119 | this->sendTries = sendTries; 120 | } 121 | void setDeviceIDsend(uint8_t byte0, uint8_t byte1, uint8_t byte2) 122 | { 123 | this->deviceIDsend[0] = byte0; 124 | this->deviceIDsend[1] = byte1; 125 | this->deviceIDsend[2] = byte2; 126 | } 127 | bool addRFDevice(uint8_t byte0, uint8_t byte1, uint8_t byte2, RemoteTypes deviceType, bool bidirectional = false); 128 | bool addRFDevice(uint32_t ID, RemoteTypes deviceType, bool bidirectional = false); 129 | bool updateRFDeviceID(uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t remote_index); 130 | bool updateRFDeviceID(uint32_t ID, uint8_t remote_index); 131 | bool updateRFDeviceType(RemoteTypes deviceType, uint8_t remote_index); 132 | bool removeRFDevice(uint8_t byte0, uint8_t byte1, uint8_t byte2); 133 | bool removeRFDevice(uint32_t ID); 134 | bool checkRFDevice(uint8_t byte0, uint8_t byte1, uint8_t byte2); 135 | bool checkRFDevice(uint32_t ID); 136 | void setRFDeviceBidirectional(uint8_t remote_index, bool bidirectional); 137 | bool getRFDeviceBidirectional(uint8_t remote_index); 138 | bool getRFDeviceBidirectionalByID(int32_t ID); 139 | int8_t getRemoteIndexByID(int32_t ID); 140 | 141 | void setBindAllowed(bool input) 142 | { 143 | bindAllowed = input; 144 | } 145 | bool getBindAllowed() 146 | { 147 | return bindAllowed; 148 | } 149 | void setAllowAll(bool input) 150 | { 151 | allowAll = input; 152 | } 153 | bool getAllowAll() 154 | { 155 | return allowAll; 156 | } 157 | const struct ithoRFDevices &getRFdevices() const 158 | { 159 | return ithoRF; 160 | } 161 | // receive 162 | uint8_t receivePacket(); // read RX fifo 163 | bool checkForNewPacket(); 164 | IthoPacket getLastPacket() 165 | { 166 | return inIthoPacket; // retrieve last received/parsed packet from remote 167 | } 168 | IthoCommand getLastCommand() 169 | { 170 | return inIthoPacket.command; // retrieve last received/parsed command from remote 171 | } 172 | RemoteTypes getLastRemType() 173 | { 174 | return inIthoPacket.remType; // retrieve last received/parsed rf device type 175 | } 176 | uint32_t getFrequency() 177 | { 178 | return ((uint32_t)cc_freq[2] << 16) | ((uint32_t)cc_freq[1] << 8) | ((uint32_t)cc_freq[0] << 0); 179 | } 180 | 181 | uint8_t ReadRSSI(); 182 | // bool checkID(const uint8_t *id); 183 | int *getLastID(); 184 | String getLastIDstr(bool ashex = true); 185 | String getLastMessagestr(bool ashex = true); 186 | String LastMessageDecoded(); 187 | 188 | // send 189 | const uint8_t *getRemoteCmd(const RemoteTypes type, const IthoCommand command); 190 | void sendRFCommand(uint8_t remote_index, IthoCommand command); 191 | void sendJoinReply(uint8_t byte0, uint8_t byte1, uint8_t byte2); 192 | void sendJoinReply(uint32_t ID); 193 | void send10E0(); 194 | void send2E10(uint8_t remote_index, IthoCommand command); 195 | void sendRFMessage(RFmessage *message); 196 | void sendCommand(IthoCommand command); 197 | 198 | void handleBind(); 199 | void handleLevel(); 200 | void handleTimer(); 201 | void handleStatus(); 202 | void handleRemotestatus(); 203 | void handleTemphum(); 204 | void handleCo2(); 205 | void handleBattery(); 206 | void handlePIR(); 207 | const char *rem_cmd_to_name(IthoCommand code); 208 | 209 | protected: 210 | private: 211 | IthoCC1101(const IthoCC1101 &c); 212 | IthoCC1101 &operator=(const IthoCC1101 &c); 213 | 214 | // init CC1101 for receiving 215 | void initReceiveMessage(); 216 | 217 | // init CC1101 for sending 218 | void initSendMessage(uint8_t len); 219 | void finishTransfer(); 220 | 221 | // parse received message 222 | bool parseMessageCommand(); 223 | bool checkIthoCommand(IthoPacket *itho, const uint8_t commandBytes[]); 224 | 225 | // send 226 | void createMessageStart(IthoPacket *itho, CC1101Packet *packet); 227 | uint8_t checksum(IthoPacket *itho, uint8_t len); 228 | 229 | uint8_t messageEncode(IthoPacket *itho, CC1101Packet *packet); 230 | void messageDecode(CC1101Packet *packet, IthoPacket *itho); 231 | }; // IthoCC1101 232 | -------------------------------------------------------------------------------- /Master/Itho/CC1101.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 3 | */ 4 | 5 | #include "CC1101.h" 6 | 7 | // default constructor 8 | CC1101::CC1101() 9 | { 10 | SPI.begin(); 11 | pinMode(SS, OUTPUT); 12 | } // CC1101 13 | 14 | // default destructor 15 | CC1101::~CC1101() 16 | { 17 | } //~CC1101 18 | 19 | /***********************/ 20 | // SPI helper functions select() and deselect() 21 | inline void CC1101::select(void) 22 | { 23 | digitalWrite(SS, LOW); 24 | } 25 | 26 | inline void CC1101::deselect(void) 27 | { 28 | digitalWrite(SS, HIGH); 29 | } 30 | 31 | void CC1101::spi_waitMiso() 32 | { 33 | while (digitalRead(MISO) == HIGH) 34 | yield(); 35 | } 36 | 37 | void CC1101::init() 38 | { 39 | reset(); 40 | } 41 | 42 | void CC1101::reset() 43 | { 44 | deselect(); 45 | delayMicroseconds(5); 46 | select(); 47 | delayMicroseconds(10); 48 | deselect(); 49 | delayMicroseconds(45); 50 | select(); 51 | 52 | spi_waitMiso(); 53 | SPI.transfer(CC1101_SRES); 54 | delay(10); 55 | spi_waitMiso(); 56 | deselect(); 57 | } 58 | 59 | uint8_t CC1101::writeCommand(uint8_t command) 60 | { 61 | uint8_t result; 62 | 63 | select(); 64 | spi_waitMiso(); 65 | result = SPI.transfer(command); 66 | deselect(); 67 | 68 | return result; 69 | } 70 | 71 | void CC1101::writeRegister(uint8_t address, uint8_t data) 72 | { 73 | select(); 74 | spi_waitMiso(); 75 | SPI.transfer(address); 76 | SPI.transfer(data); 77 | deselect(); 78 | } 79 | 80 | uint8_t CC1101::readRegister(uint8_t address) 81 | { 82 | uint8_t val; 83 | 84 | select(); 85 | spi_waitMiso(); 86 | SPI.transfer(address); 87 | val = SPI.transfer(0); 88 | deselect(); 89 | 90 | return val; 91 | } 92 | 93 | uint8_t CC1101::readRegisterMedian3(uint8_t address) 94 | { 95 | uint8_t val, val1, val2, val3; 96 | 97 | select(); 98 | spi_waitMiso(); 99 | SPI.transfer(address); 100 | val1 = SPI.transfer(0); 101 | SPI.transfer(address); 102 | val2 = SPI.transfer(0); 103 | SPI.transfer(address); 104 | val3 = SPI.transfer(0); 105 | deselect(); 106 | // reverse sort (largest in val1) because this is te expected order for TX_BUFFER 107 | if (val3 > val2) 108 | { 109 | val = val3; // Swap(val3,val2) 110 | val3 = val2; 111 | val2 = val; 112 | } 113 | if (val2 > val1) 114 | { 115 | val = val2; // Swap(val2,val1) 116 | val2 = val1, val1 = val; 117 | } 118 | if (val3 > val2) 119 | { 120 | val = val3; // Swap(val3,val2) 121 | val3 = val2, val2 = val; 122 | } 123 | 124 | return val2; 125 | } 126 | 127 | /* Known SPI/26MHz synchronization bug (see CC1101 errata) 128 | This issue affects the following registers: SPI status byte (fields STATE and FIFO_BYTES_AVAILABLE), 129 | FREQEST or RSSI while the receiver is active, MARCSTATE at any time other than an IDLE radio state, 130 | RXBYTES when receiving or TXBYTES when transmitting, and WORTIME1/WORTIME0 at any time.*/ 131 | // uint8_t CC1101::readRegisterWithSyncProblem(uint8_t address, uint8_t registerType) 132 | uint8_t /* ICACHE_RAM_ATTR */ CC1101::readRegisterWithSyncProblem(uint8_t address, uint8_t registerType) 133 | { 134 | uint8_t value1, value2; 135 | value1 = readRegister(address | registerType); 136 | value2 = readRegister(address | registerType); 137 | 138 | if (value1 == value2) 139 | return value1; 140 | 141 | // if two consecutive reads gives us the same result then we know we are ok 142 | do 143 | { 144 | value2 = value1; 145 | value1 = readRegister(address | registerType); 146 | } while (value1 != value2); 147 | 148 | return value1; 149 | } 150 | 151 | // registerType = CC1101_CONFIG_REGISTER or CC1101_STATUS_REGISTER 152 | uint8_t CC1101::readRegister(uint8_t address, uint8_t registerType) 153 | { 154 | switch (address) 155 | { 156 | case CC1101_FREQEST: 157 | case CC1101_MARCSTATE: 158 | case CC1101_RXBYTES: 159 | case CC1101_TXBYTES: 160 | case CC1101_WORTIME1: 161 | case CC1101_WORTIME0: 162 | return readRegisterWithSyncProblem(address, registerType); 163 | 164 | default: 165 | return readRegister(address | registerType); 166 | } 167 | } 168 | 169 | void CC1101::writeBurstRegister(const uint8_t address, const uint8_t *data, const uint8_t length) 170 | { 171 | uint8_t i; 172 | 173 | select(); 174 | spi_waitMiso(); 175 | SPI.transfer(address | CC1101_WRITE_BURST); 176 | for (i = 0; i < length; i++) 177 | { 178 | SPI.transfer(data[i]); 179 | } 180 | deselect(); 181 | } 182 | 183 | void CC1101::readBurstRegister(uint8_t *buffer, const uint8_t address, const uint8_t length) 184 | { 185 | uint8_t i; 186 | 187 | select(); 188 | spi_waitMiso(); 189 | SPI.transfer(address | CC1101_READ_BURST); 190 | 191 | for (i = 0; i < length; i++) 192 | { 193 | buffer[i] = SPI.transfer(0x00); 194 | } 195 | 196 | deselect(); 197 | } 198 | 199 | size_t CC1101::readData(CC1101Packet *packet, size_t maxLen) 200 | { 201 | packet->length = 0; 202 | size_t length = maxLen; 203 | uint8_t bytesInFIFO = 0; 204 | 205 | bytesInFIFO = readRegisterWithSyncProblem(CC1101_RXBYTES, CC1101_READ_BURST) & CC1101_BITS_RX_BYTES_IN_FIFO; 206 | 207 | size_t readBytes = 0; 208 | uint32_t lastFIFORead = millis(); 209 | 210 | // keep reading from FIFO until we get all the packet. 211 | while (readBytes < length) 212 | { 213 | if (bytesInFIFO == 0) 214 | { 215 | if (millis() - lastFIFORead > 5) 216 | { 217 | break; 218 | } 219 | else 220 | { 221 | bytesInFIFO = readRegisterWithSyncProblem(CC1101_RXBYTES, CC1101_READ_BURST) & CC1101_BITS_RX_BYTES_IN_FIFO; 222 | continue; 223 | } 224 | } 225 | 226 | uint8_t bytesToRead = min((uint8_t)(length - readBytes), bytesInFIFO); // only read from bytesInFIFO if there is buffer length available 227 | if (bytesToRead > 1) 228 | { // CC1101 errata: RX FIFO pointer is sometimes not properly updated and the last read byte is duplicated, leave on byte in buffer until only one byte is left 229 | bytesToRead -= 1; 230 | } 231 | readBurstRegister(&(packet->data[readBytes]), CC1101_RXFIFO, bytesToRead); 232 | readBytes += bytesToRead; 233 | packet->length = readBytes; 234 | lastFIFORead = millis(); 235 | 236 | // Get how many bytes are left in FIFO. 237 | bytesInFIFO = readRegisterWithSyncProblem(CC1101_RXBYTES, CC1101_READ_BURST) & CC1101_BITS_RX_BYTES_IN_FIFO; 238 | } 239 | 240 | writeCommand(CC1101_SIDLE); // idle 241 | writeCommand(CC1101_SFRX); // flush RX buffer 242 | writeCommand(CC1101_SRX); // switch to RX state 243 | 244 | return readBytes; 245 | } 246 | 247 | // This function is able to send packets bigger then the FIFO size. 248 | void CC1101::sendData(CC1101Packet *packet) 249 | { 250 | uint8_t index = 0; 251 | uint8_t txStatus, MarcState; 252 | uint8_t length; 253 | 254 | writeCommand(CC1101_SIDLE); // idle 255 | 256 | txStatus = readRegisterWithSyncProblem(CC1101_TXBYTES, CC1101_STATUS_REGISTER); 257 | 258 | // clear TX fifo if needed 259 | if (txStatus & CC1101_BITS_TX_FIFO_UNDERFLOW) 260 | { 261 | writeCommand(CC1101_SIDLE); // idle 262 | writeCommand(CC1101_SFTX); // flush TX buffer 263 | } 264 | 265 | writeCommand(CC1101_SIDLE); // idle 266 | 267 | // determine how many bytes to send 268 | length = (packet->length <= CC1101_DATA_LEN ? packet->length : CC1101_DATA_LEN); 269 | 270 | writeBurstRegister(CC1101_TXFIFO, packet->data, length); 271 | 272 | writeCommand(CC1101_SIDLE); 273 | // start sending packet 274 | writeCommand(CC1101_STX); 275 | 276 | // continue sending when packet is bigger than 64 bytes 277 | if (packet->length > CC1101_DATA_LEN) 278 | { 279 | index += length; 280 | // loop until all bytes are transmitted 281 | while (index < packet->length) 282 | { 283 | // check if there is free space in the fifo 284 | while ((txStatus = (readRegisterMedian3(CC1101_TXBYTES | CC1101_STATUS_REGISTER) & CC1101_BITS_RX_BYTES_IN_FIFO)) > (CC1101_DATA_LEN - 2)) 285 | ; 286 | 287 | // calculate how many bytes we can send 288 | length = (CC1101_DATA_LEN - txStatus); 289 | length = ((packet->length - index) < length ? (packet->length - index) : length); 290 | 291 | // send some more bytes 292 | for (int i = 0; i < length; i++) 293 | writeRegister(CC1101_TXFIFO, packet->data[index + i]); 294 | 295 | index += length; 296 | } 297 | } 298 | // wait until transmission is finished (TXOFF_MODE is expected to be set to 0/IDLE or TXFIFO_UNDERFLOW) 299 | do 300 | { 301 | MarcState = (readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER) & CC1101_BITS_MARCSTATE); 302 | // if (MarcState == CC1101_MARCSTATE_TXFIFO_UNDERFLOW) Serial.print("TXFIFO_UNDERFLOW occured in sendData() \n"); 303 | } while ((MarcState != CC1101_MARCSTATE_IDLE) && (MarcState != CC1101_MARCSTATE_TXFIFO_UNDERFLOW)); 304 | } 305 | -------------------------------------------------------------------------------- /Master/Itho/CC1101.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Author: Klusjesman, modified bij supersjimmie for Arduino/ESP8266 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include "CC1101Packet.h" 9 | #include 10 | 11 | // On Arduino, SPI pins are predefined 12 | 13 | /* Type of transfers */ 14 | #define CC1101_WRITE_BURST 0x40 15 | #define CC1101_READ_SINGLE 0x80 16 | #define CC1101_READ_BURST 0xC0 17 | 18 | /* Type of register */ 19 | #define CC1101_CONFIG_REGISTER CC1101_READ_SINGLE 20 | #define CC1101_STATUS_REGISTER CC1101_READ_BURST 21 | 22 | /* PATABLE & FIFO's */ 23 | #define CC1101_PATABLE 0x3E // PATABLE address 24 | #define CC1101_TXFIFO 0x3F // TX FIFO address 25 | #define CC1101_RXFIFO 0x3F // RX FIFO address 26 | #define CC1101_PA_LowPower 0x60 27 | #define CC1101_PA_LongDistance 0xC0 28 | 29 | /* Command strobes */ 30 | #define CC1101_SRES 0x30 // Reset CC1101 chip 31 | #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). 32 | #define CC1101_SXOFF 0x32 // Turn off crystal oscillator 33 | #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) 34 | #define CC1101_SRX 0x34 // Enable RX. Perform calibration first if coming from IDLE and MCSM0.FS_AUTOCAL=1 35 | #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 36 | #define CC1101_SIDLE 0x36 // Exit RX / TX, turn off frequency synthesizer and exit Wake-On-Radio mode if applicable 37 | #define CC1101_SWOR 0x38 // Start automatic RX polling sequence (Wake-on-Radio) as described in Section 19.5 if WORCTRL.RC_PD=0 38 | #define CC1101_SPWD 0x39 // Enter power down mode when CSn goes high 39 | #define CC1101_SFRX 0x3A // Flush the RX FIFO buffer. Only issue SFRX in IDLE or RXFIFO_OVERFLOW states 40 | #define CC1101_SFTX 0x3B // Flush the TX FIFO buffer. Only issue SFTX in IDLE or TXFIFO_UNDERFLOW states 41 | #define CC1101_SWORRST 0x3C // Reset real time clock to Event1 value 42 | #define CC1101_SNOP 0x3D // No operation. May be used to get access to the chip status byte 43 | 44 | /* CC1101 configuration registers */ 45 | #define CC1101_IOCFG2 0x00 // GDO2 Output Pin Configuration 46 | #define CC1101_IOCFG1 0x01 // GDO1 Output Pin Configuration 47 | #define CC1101_IOCFG0 0x02 // GDO0 Output Pin Configuration 48 | #define CC1101_FIFOTHR 0x03 // RX FIFO and TX FIFO Thresholds 49 | #define CC1101_SYNC1 0x04 // Sync Word, High Byte 50 | #define CC1101_SYNC0 0x05 // Sync Word, Low Byte 51 | #define CC1101_PKTLEN 0x06 // Packet Length 52 | #define CC1101_PKTCTRL1 0x07 // Packet Automation Control 53 | #define CC1101_PKTCTRL0 0x08 // Packet Automation Control 54 | #define CC1101_ADDR 0x09 // Device Address 55 | #define CC1101_CHANNR 0x0A // Channel Number 56 | #define CC1101_FSCTRL1 0x0B // Frequency Synthesizer Control 57 | #define CC1101_FSCTRL0 0x0C // Frequency Synthesizer Control 58 | #define CC1101_FREQ2 0x0D // Frequency Control Word, High Byte 59 | #define CC1101_FREQ1 0x0E // Frequency Control Word, Middle Byte 60 | #define CC1101_FREQ0 0x0F // Frequency Control Word, Low Byte 61 | #define CC1101_MDMCFG4 0x10 // Modem Configuration 62 | #define CC1101_MDMCFG3 0x11 // Modem Configuration 63 | #define CC1101_MDMCFG2 0x12 // Modem Configuration 64 | #define CC1101_MDMCFG1 0x13 // Modem Configuration 65 | #define CC1101_MDMCFG0 0x14 // Modem Configuration 66 | #define CC1101_DEVIATN 0x15 // Modem Deviation Setting 67 | #define CC1101_MCSM2 0x16 // Main Radio Control State Machine Configuration 68 | #define CC1101_MCSM1 0x17 // Main Radio Control State Machine Configuration 69 | #define CC1101_MCSM0 0x18 // Main Radio Control State Machine Configuration 70 | #define CC1101_FOCCFG 0x19 // Frequency Offset Compensation Configuration 71 | #define CC1101_BSCFG 0x1A // Bit Synchronization Configuration 72 | #define CC1101_AGCCTRL2 0x1B // AGC Control 73 | #define CC1101_AGCCTRL1 0x1C // AGC Control 74 | #define CC1101_AGCCTRL0 0x1D // AGC Control 75 | #define CC1101_WOREVT1 0x1E // High Byte Event0 Timeout 76 | #define CC1101_WOREVT0 0x1F // Low Byte Event0 Timeout 77 | #define CC1101_WORCTRL 0x20 // Wake On Radio Control 78 | #define CC1101_FREND1 0x21 // Front End RX Configuration 79 | #define CC1101_FREND0 0x22 // Front End TX Configuration 80 | #define CC1101_FSCAL3 0x23 // Frequency Synthesizer Calibration 81 | #define CC1101_FSCAL2 0x24 // Frequency Synthesizer Calibration 82 | #define CC1101_FSCAL1 0x25 // Frequency Synthesizer Calibration 83 | #define CC1101_FSCAL0 0x26 // Frequency Synthesizer Calibration 84 | #define CC1101_RCCTRL1 0x27 // RC Oscillator Configuration 85 | #define CC1101_RCCTRL0 0x28 // RC Oscillator Configuration 86 | #define CC1101_FSTEST 0x29 // Frequency Synthesizer Calibration Control 87 | #define CC1101_PTEST 0x2A // Production Test 88 | #define CC1101_AGCTEST 0x2B // AGC Test 89 | #define CC1101_TEST2 0x2C // Various Test Settings 90 | #define CC1101_TEST1 0x2D // Various Test Settings 91 | #define CC1101_TEST0 0x2E // Various Test Settings 92 | 93 | /* Status registers */ 94 | #define CC1101_PARTNUM 0x30 // Chip ID 95 | #define CC1101_VERSION 0x31 // Chip ID 96 | #define CC1101_FREQEST 0x32 // Frequency Offset Estimate from Demodulator 97 | #define CC1101_LQI 0x33 // Demodulator Estimate for Link Quality 98 | #define CC1101_RSSI 0x34 // Received Signal Strength Indication 99 | #define CC1101_MARCSTATE 0x35 // Main Radio Control State Machine State 100 | #define CC1101_WORTIME1 0x36 // High Byte of WOR Time 101 | #define CC1101_WORTIME0 0x37 // Low Byte of WOR Time 102 | #define CC1101_PKTSTATUS 0x38 // Current GDOx Status and Packet Status 103 | #define CC1101_VCO_VC_DAC 0x39 // Current Setting from PLL Calibration Module 104 | #define CC1101_TXBYTES 0x3A // Underflow and Number of Bytes 105 | #define CC1101_RXBYTES 0x3B // Overflow and Number of Bytes 106 | #define CC1101_RCCTRL1_STATUS 0x3C // Last RC Oscillator Calibration Result 107 | #define CC1101_RCCTRL0_STATUS 0x3D // Last RC Oscillator Calibration Result 108 | 109 | /* Bit fields in the chip status byte */ 110 | #define CC1101_STATUS_CHIP_RDYn_BM 0x80 111 | #define CC1101_STATUS_STATE_BM 0x70 112 | #define CC1101_STATUS_FIFO_BYTES_AVAILABLE_BM 0x0F 113 | 114 | /* Masks to retrieve status bit */ 115 | #define CC1101_BITS_TX_FIFO_UNDERFLOW 0x80 116 | #define CC1101_BITS_RX_BYTES_IN_FIFO 0x7F 117 | #define CC1101_BITS_MARCSTATE 0x1F 118 | 119 | /* Marc states */ 120 | enum CC1101MarcStates 121 | { 122 | CC1101_MARCSTATE_SLEEP = 0x00, 123 | CC1101_MARCSTATE_IDLE = 0x01, 124 | CC1101_MARCSTATE_XOFF = 0x02, 125 | CC1101_MARCSTATE_VCOON_MC = 0x03, 126 | CC1101_MARCSTATE_REGON_MC = 0x04, 127 | CC1101_MARCSTATE_MANCAL = 0x05, 128 | CC1101_MARCSTATE_VCOON = 0x06, 129 | CC1101_MARCSTATE_REGON = 0x07, 130 | CC1101_MARCSTATE_STARTCAL = 0x08, 131 | CC1101_MARCSTATE_BWBOOST = 0x09, 132 | CC1101_MARCSTATE_FS_LOCK = 0x0A, 133 | CC1101_MARCSTATE_IFADCON = 0x0B, 134 | CC1101_MARCSTATE_ENDCAL = 0x0C, 135 | CC1101_MARCSTATE_RX = 0x0D, 136 | CC1101_MARCSTATE_RX_END = 0x0E, 137 | CC1101_MARCSTATE_RX_RST = 0x0F, 138 | CC1101_MARCSTATE_TXRX_SWITCH = 0x10, 139 | CC1101_MARCSTATE_RXFIFO_OVERFLOW = 0x11, 140 | CC1101_MARCSTATE_FSTXON = 0x12, 141 | CC1101_MARCSTATE_TX = 0x13, 142 | CC1101_MARCSTATE_TX_END = 0x14, 143 | CC1101_MARCSTATE_RXTX_SWITCH = 0x15, 144 | CC1101_MARCSTATE_TXFIFO_UNDERFLOW = 0x16 145 | }; 146 | 147 | /* Chip states */ 148 | enum CC1101ChipStates 149 | { 150 | CC1101_STATE_MASK = 0x70, 151 | CC1101_STATE_IDLE = 0x00, 152 | CC1101_STATE_RX = 0x10, 153 | CC1101_STATE_TX = 0x20, 154 | CC1101_STATE_FSTXON = 0x30, 155 | CC1101_STATE_CALIBRATE = 0x40, 156 | CC1101_STATE_SETTLING = 0x50, 157 | CC1101_STATE_RX_OVERFLOW = 0x60, 158 | CC1101_STATE_TX_UNDERFLOW = 0x70 159 | }; 160 | 161 | class CC1101 162 | { 163 | protected: 164 | // functions 165 | public: 166 | CC1101(); 167 | ~CC1101(); 168 | 169 | // spi 170 | void spi_waitMiso(); 171 | 172 | // cc1101 173 | void init(); 174 | 175 | uint8_t writeCommand(uint8_t command); 176 | void writeRegister(uint8_t address, uint8_t data); 177 | 178 | uint8_t readRegister(uint8_t address, uint8_t registerType); 179 | 180 | void writeBurstRegister(const uint8_t address, const uint8_t *data, const uint8_t length); 181 | void readBurstRegister(uint8_t *buffer, const uint8_t address, const uint8_t length); 182 | 183 | void sendData(CC1101Packet *packet); 184 | size_t readData(CC1101Packet *packet, size_t len); 185 | 186 | private: 187 | CC1101(const CC1101 &c); 188 | CC1101 &operator=(const CC1101 &c); 189 | // SPI helper functions 190 | void select(void); 191 | void deselect(void); 192 | 193 | protected: 194 | uint8_t readRegister(uint8_t address); 195 | uint8_t readRegisterMedian3(uint8_t address); 196 | uint8_t readRegisterWithSyncProblem(uint8_t address, uint8_t registerType); 197 | 198 | void reset(); 199 | 200 | }; // CC1101 201 | -------------------------------------------------------------------------------- /Master/Itho/IthoPacket.h: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Klusjesman, supersjimmie, modified and reworked by arjenhiemstra 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | 9 | enum IthoCommand 10 | { 11 | IthoUnknown = 0, 12 | 13 | IthoJoin = 1, 14 | IthoLeave = 2, 15 | 16 | IthoAway = 3, 17 | IthoLow = 4, 18 | IthoMedium = 5, 19 | IthoHigh = 6, 20 | IthoFull = 7, 21 | 22 | IthoTimer1 = 8, 23 | IthoTimer2 = 9, 24 | IthoTimer3 = 10, 25 | 26 | IthoAuto = 11, 27 | IthoAutoNight = 12, 28 | 29 | IthoCook30 = 13, 30 | IthoCook60 = 14, 31 | 32 | IthoTimerUser = 15, 33 | 34 | IthoJoinReply = 16, 35 | 36 | IthoPIRmotionOn = 17, 37 | IthoPIRmotionOff = 18, 38 | 39 | }; 40 | 41 | enum RemoteTypes : uint16_t 42 | { 43 | UNSETTYPE = 0x0000, 44 | RFTCVE = 0x22F1, 45 | RFTAUTO = 0x22F3, 46 | RFTAUTON = 0x22F4, 47 | DEMANDFLOW = 0x22F8, 48 | RFTRV = 0x12A0, 49 | RFTCO2 = 0x1298, 50 | RFTPIR = 0x2E10, 51 | ORCON15LF01 = 0x6710 52 | }; 53 | 54 | #define F_MASK 0x03 55 | #define F_RQ 0x00 56 | #define F_I 0x01 57 | #define F_W 0x02 58 | #define F_RP 0x03 59 | 60 | #define F_ADDR0 0x10 61 | #define F_ADDR1 0x20 62 | #define F_ADDR2 0x40 63 | 64 | #define F_PARAM0 0x04 65 | #define F_PARAM1 0x08 66 | #define F_RSSI 0x80 67 | 68 | // Only used for received fields 69 | #define F_OPCODE 0x01 70 | #define F_LEN 0x02 71 | 72 | #define MAX_PAYLOAD 64 73 | #define MAX_DECODED MAX_PAYLOAD + 18 74 | 75 | static char const *const MsgType[4] = {"RQ", "_I", "_W", "RP"}; 76 | 77 | // General command structure: 78 | // < opcode 2 bytes >< len 1 byte >< command len bytes > 79 | 80 | // message command bytes for CVE/HRU remote (536-0124) 81 | const uint8_t ithoMessageAwayCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x01, 0x04}; 82 | const uint8_t ithoMessageLowCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x02, 0x04}; 83 | const uint8_t ithoMessageMediumCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x03, 0x04}; 84 | const uint8_t ithoMessageHighCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x04, 0x04}; 85 | const uint8_t ithoMessageFullCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x04, 0x04}; 86 | const uint8_t ithoMessageTimer1CommandBytes[] = {0x22, 0xF3, 0x03, 0x00, 0x00, 0x0A}; // 10 minutes full speed 87 | const uint8_t ithoMessageTimer2CommandBytes[] = {0x22, 0xF3, 0x03, 0x00, 0x00, 0x14}; // 20 minutes full speed 88 | const uint8_t ithoMessageTimer3CommandBytes[] = {0x22, 0xF3, 0x03, 0x00, 0x00, 0x1E}; // 30 minutes full speed 89 | 90 | // message command bytes specific for AUTO RFT (536-0150) 91 | const uint8_t ithoMessageAUTORFTLowCommandBytes[] = {0x22, 0xF1, 0x03, 0x63, 0x02, 0x04}; 92 | const uint8_t ithoMessageAUTORFTAutoCommandBytes[] = {0x22, 0xF1, 0x03, 0x63, 0x03, 0x04}; 93 | const uint8_t ithoMessageAUTORFTAutoNightCommandBytes[] = {0x22, 0xF8, 0x03, 0x63, 0x02, 0x03}; 94 | const uint8_t ithoMessageAUTORFTHighCommandBytes[] = {0x22, 0xF1, 0x03, 0x63, 0x04, 0x04}; 95 | const uint8_t ithoMessageAUTORFTTimer1CommandBytes[] = {0x22, 0xF3, 0x03, 0x63, 0x00, 0x0A}; 96 | const uint8_t ithoMessageAUTORFTTimer2CommandBytes[] = {0x22, 0xF3, 0x03, 0x63, 0x00, 0x14}; 97 | const uint8_t ithoMessageAUTORFTTimer3CommandBytes[] = {0x22, 0xF3, 0x03, 0x63, 0x00, 0x1E}; 98 | 99 | // message command bytes specific for RFT-RV (04-00046) and RFT-CO2 (04-00045) 100 | const uint8_t ithoMessageRV_CO2MediumCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x03, 0x07}; 101 | const uint8_t ithoMessageRV_CO2AutoCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x05, 0x07}; 102 | const uint8_t ithoMessageRV_CO2AutoNightCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x0B, 0x0B}; 103 | const uint8_t ithoMessageRV_CO2Timer1CommandBytes[] = {0x22, 0xF3, 0x07, 0x00, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00}; 104 | const uint8_t ithoMessageRV_CO2Timer2CommandBytes[] = {0x22, 0xF3, 0x07, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00}; 105 | const uint8_t ithoMessageRV_CO2Timer3CommandBytes[] = {0x22, 0xF3, 0x07, 0x00, 0x00, 0x1E, 0x00, 0x00, 0x00, 0x00}; 106 | 107 | // message command bytes specific for DemandFlow remote (536-0146) 108 | const uint8_t ithoMessageDFLowCommandBytes[] = {0x22, 0xF8, 0x03, 0x00, 0x01, 0x02}; 109 | const uint8_t ithoMessageDFHighCommandBytes[] = {0x22, 0xF8, 0x03, 0x00, 0x02, 0x02}; 110 | const uint8_t ithoMessageDFTimer1CommandBytes[] = {0x22, 0xF3, 0x05, 0x00, 0x42, 0x03, 0x03, 0x03}; 111 | const uint8_t ithoMessageDFTimer2CommandBytes[] = {0x22, 0xF3, 0x05, 0x00, 0x42, 0x06, 0x03, 0x03}; 112 | const uint8_t ithoMessageDFTimer3CommandBytes[] = {0x22, 0xF3, 0x05, 0x00, 0x42, 0x09, 0x03, 0x03}; 113 | const uint8_t ithoMessageDFCook30CommandBytes[] = {0x22, 0xF3, 0x05, 0x00, 0x02, 0x1E, 0x02, 0x03}; 114 | const uint8_t ithoMessageDFCook60CommandBytes[] = {0x22, 0xF3, 0x05, 0x00, 0x02, 0x3C, 0x02, 0x03}; 115 | 116 | // message command bytes specific for RFT PIR remote 117 | const uint8_t ithoMessageRFTPIRonCommandBytes[] = {0x2E, 0x10, 0x03, 0x00, 0x01, 0x00}; 118 | const uint8_t ithoMessageRFTPIRoffCommandBytes[] = {0x2E, 0x10, 0x03, 0x00, 0x00, 0x00}; 119 | 120 | // Join/Leave command structure: 121 | // < opcode 2 bytes >< len 1 byte >[next command + device ID block repeats len/6 times]< command 3 bytes >< device ID 3 bytes > 122 | 123 | // Join/Leave commands: 124 | const uint8_t ithoMessageCVERFTJoinCommandBytes[] = {0x1F, 0xC9, 0x0C, 0x00, 0x22, 0xF1, 0x00, 0x00, 0x00, 0x01, 0x10, 0xE0, 0x00, 0x00, 0x00}; // join command of CVE/HRU remote (536-0124) 125 | const uint8_t ithoMessageAUTORFTJoinCommandBytes[] = {0x1F, 0xC9, 0x0C, 0x63, 0x22, 0xF8, 0x00, 0x00, 0x00, 0x01, 0x10, 0xE0, 0x00, 0x00, 0x00}; // join command of AUTO RFT (536-0150) 126 | const uint8_t ithoMessageDFJoinCommandBytes[] = {0x1F, 0xC9, 0x0C, 0x00, 0x22, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x10, 0xE0, 0x00, 0x00, 0x00}; // join command of DemandFlow remote (536-0146) 127 | const uint8_t ithoMessageRVJoinCommandBytes[] = {0x1F, 0xC9, 0x18, 0x00, 0x31, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x12, 0xA0, 0x00, 0x00, 0x00, 0x01, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC9, 0x00, 0x00, 0x00}; // join command of RFT-RV (04-00046) 128 | const uint8_t ithoMessageCO2JoinCommandBytes[] = {0x1F, 0xC9, 0x1E, 0x00, 0x31, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x12, 0x98, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x10, 0x00, 0x00, 0x00, 0x01, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC9, 0x00, 0x00, 0x00}; // join command of RFT-CO2 (04-00045) 129 | const uint8_t ithoMessagePIRJoinCommandBytes[] = {0x1F, 0xC9, 0x06, 0x00, 0x2E, 0x10, 0x00, 0x00, 0x00}; // join command of RF PIR 130 | const uint8_t ithoMessageLeaveCommandBytes[] = {0x1F, 0xC9, 0x06, 0x00, 0x1F, 0xC9, 0x00, 0x00, 0x00}; // standard leave command 131 | const uint8_t ithoMessageAUTORFTLeaveCommandBytes[] = {0x1F, 0xC9, 0x06, 0x63, 0x1F, 0xC9, 0x00, 0x00, 0x00}; // leave command of AUTO RFT (536-0150) 132 | const uint8_t ithoMessageAUTORFTNJoinCommandBytes[] = {0x1F, 0xC9, 0x12, 0x00, 0x22, 0xF8, 0x00, 0x00, 0x00, 0x01, 0x10, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x1F, 0xC9, 0x00, 0x00, 0x00}; // join command of Auto RFT-N (04-00161) (bi-directioal) 133 | 134 | // Join/Leave reply commands: 135 | const uint8_t ithoMessageJoinReplyCommandBytes[] = {0x1F, 0xC9, 0x0C, 0x00, 0x31, 0xD9, 0x00, 0x00, 0x00, 0x00, 0x31, 0xDA, 0x00, 0x00, 0x00}; // leave command of AUTO RFT (536-0150) 136 | 137 | // Orcon remote VMN-15LF01 138 | const uint8_t orconMessageAwayCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x00, 0x04}; 139 | const uint8_t orconMessageAutoCommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x04, 0x04}; 140 | const uint8_t orconMessageButton1CommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x01, 0x04}; 141 | const uint8_t orconMessageButton2CommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x02, 0x04}; 142 | const uint8_t orconMessageButton3CommandBytes[] = {0x22, 0xF1, 0x03, 0x00, 0x03, 0x04}; 143 | const uint8_t orconMessageTimer1CommandBytes[] = {0x22, 0xF3, 0x07, 0x00, 0x02, 0x0F, 0x03, 0x04, 0x00, 0x00}; 144 | const uint8_t orconMessageTimer2CommandBytes[] = {0x22, 0xF3, 0x07, 0x00, 0x02, 0x1E, 0x03, 0x04, 0x00, 0x00}; 145 | const uint8_t orconMessageTimer3CommandBytes[] = {0x22, 0xF3, 0x07, 0x00, 0x02, 0x3C, 0x03, 0x04, 0x00, 0x00}; 146 | const uint8_t orconMessageFilterCleanCommandBytes[] = {0x10, 0xD0, 0x02, 0x00, 0xFF}; 147 | const uint8_t orconMessageJoinCommandBytes[] = {0x1F, 0xC9, 0x12, 0x00, 0x22, 0xF1, 0x00, 0x00, 0x00, 0x00, 0x22, 0xF3, 0x00, 0x00, 0x00, 0x67, 0x10, 0xE0, 0x00, 0x00, 0x00}; 148 | const uint8_t orconMessageBatteryStatusCommandBytes[] = {0x10, 0x60, 0x03, 0x00, 0xFF, 0x01}; 149 | 150 | class IthoPacket 151 | { 152 | public: 153 | enum Type : uint32_t 154 | { 155 | BIND = 0x1FC9, 156 | LEVEL = 0x22F1, 157 | TIMER = 0x22F3, 158 | SETPOINT = 0x22F8, 159 | STATUS = 0x31DA, 160 | FANSTATUS = 0x31D9, 161 | REMOTESTATUS = 0x31E0, 162 | TEMPHUM = 0x12A0, 163 | CO2 = 0x1298, 164 | PIR = 0x2E10, 165 | BATTERY = 0x1060 166 | }; 167 | 168 | RemoteTypes remType; 169 | IthoCommand command; 170 | 171 | int8_t state; 172 | 173 | // Message Fields 174 | //
175 | //< 1 > < 3 > < 3 > < 3 > < 1 > < 1 > < 2 > < 1 > < 1 > 176 | 177 | uint8_t header{0}; 178 | // uint8_t type; 179 | uint32_t deviceId0{0}; 180 | uint32_t deviceId1{0}; 181 | uint32_t deviceId2{0}; 182 | uint8_t param0{0}; 183 | uint8_t param1{0}; 184 | uint16_t opcode{0}; 185 | uint8_t len{0}; 186 | 187 | uint8_t error{0}; 188 | 189 | uint8_t payloadPos{0}; 190 | // uint8_t payload[MAX_PAYLOAD]; 191 | 192 | uint8_t dataDecoded[MAX_DECODED]{}; 193 | uint8_t length{0}; 194 | 195 | // uint8_t deviceType{0}; 196 | // uint8_t deviceId[3]; 197 | 198 | // uint8_t counter{0}; // 0-255, counter is increased on every remote button press 199 | 200 | // Type getType(uint16_t opcode) const; 201 | }; 202 | -------------------------------------------------------------------------------- /Master/Itho/IthoCC1101.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Author: Klusjesman, supersjimmie, modified and reworked by arjenhiemstra 3 | */ 4 | 5 | #include "IthoCC1101.h" 6 | #include "IthoPacket.h" 7 | #include 8 | #include 9 | #include 10 | 11 | // #define CRC_FILTER 12 | 13 | ////original sync byte pattern 14 | // #define STARTBYTE 6 //relevant data starts 6 bytes after the sync pattern bytes 170/171 15 | // #define SYNC1 170 16 | // #define SYNC0 171 17 | // #define MDMCFG2 0x02 //16bit sync word / 16bit specific 18 | 19 | // alternative sync byte pattern (filter much more non-itho messages out. Maybe too strict? Testing needed. 20 | #define STARTBYTE 0 // relevant data starts 0 bytes after the sync pattern bytes 179/42/171/42 21 | #define SYNC1 187 // byte11 = 179, byte13 = 171 with SYNC1 = 187, 179 and 171 differ only by 1 bit 22 | #define SYNC0 42 23 | #define MDMCFG2 0x03 // 32bit sync word / 30bit specific 24 | 25 | // alternative sync byte pattern 26 | // #define STARTBYTE 2 //relevant data starts 2 bytes after the sync pattern bytes 179/42 27 | // #define SYNC1 179 28 | // #define SYNC0 42 29 | // #define MDMCFG2 0x02 //16bit sync word / 16bit specific 30 | 31 | // alternative sync byte pattern 32 | // #define STARTBYTE 1 //relevant data starts 1 byte after the sync pattern bytes 89/149/85/149 33 | // #define SYNC1 93 //byte11 = 89, byte13 = 85 with SYNC1 = 94, 89 and 85 differ only by 1 bit and line up with the start bit 34 | // #define SYNC0 149 35 | // #define MDMCFG2 0x03 //32bit sync word / 30bit specific 36 | 37 | #define DEVICEID_MASK 0xC 38 | 39 | #define MESSAGE_TYPE_RQ_MASK 0x0 40 | #define MESSAGE_TYPE_I_MASK 0x1 41 | #define MESSAGE_TYPE_W_MASK 0x2 42 | #define MESSAGE_TYPE_RP_MASK 0x3 43 | 44 | #define OPT0_MASK 0x2 45 | #define OPT1_MASK 0x1 46 | 47 | #define HEADER_REMOTE_CMD 0x16 // message type: I, addr2, param0 48 | #define HEADER_JOIN_REPLY 0x2C // message type: W, addr0+addr1 49 | #define HEADER_10E0 0x18 // message type: 1, addr0+addr2 50 | #define HEADER_31D9 0x1A // message type: I, addr0+addr2, param0 51 | #define HEADER_31DA 0x18 // message type: I, addr0+addr2 52 | #define HEADER_2E10 0x14 // message type: I, addr2 53 | 54 | // default constructor 55 | IthoCC1101::IthoCC1101(uint8_t counter, uint8_t sendTries) : CC1101() 56 | { 57 | uint8_t default_deviceId[3] = {10, 87, 81}; 58 | 59 | this->ithoRF.device[0].counter = counter; 60 | this->ithoRF.device[0].deviceId = default_deviceId[0] << 16 | default_deviceId[1] << 8 | default_deviceId[2]; 61 | 62 | this->sendTries = sendTries; 63 | 64 | this->cc_freq[0] = 0x6A; 65 | this->cc_freq[1] = 0x65; 66 | this->cc_freq[2] = 0x21; 67 | 68 | this->bindAllowed = false; 69 | this->allowAll = true; 70 | 71 | // this->outIthoPacket.counter = counter; 72 | // this->sendTries = sendTries; 73 | 74 | // this->outIthoPacket.deviceId[0] = 33; 75 | // this->outIthoPacket.deviceId[1] = 66; 76 | // this->outIthoPacket.deviceId[2] = 99; 77 | 78 | // this->outIthoPacket.deviceType = 22; 79 | 80 | // cc_freq[0] = 0x6A; 81 | // cc_freq[1] = 0x65; 82 | // cc_freq[2] = 0x21; 83 | 84 | // bindAllowed = false; 85 | // allowAll = true; 86 | 87 | } // IthoCC1101 88 | 89 | // default destructor 90 | IthoCC1101::~IthoCC1101() 91 | { 92 | } //~IthoCC1101 93 | 94 | // { IthoUnknown, IthoJoin, IthoLeave, IthoAway, IthoLow, IthoMedium, IthoHigh, IthoFull, IthoTimer1, IthoTimer2, IthoTimer3, IthoAuto, IthoAutoNight, IthoCook30, IthoCook60, IthoTimerUser, IthoJoinReply, IthoPIRmotionOn, IthoPIRmotionOff } 95 | const uint8_t *RFTCVE_Remote_Map[] = {nullptr, ithoMessageCVERFTJoinCommandBytes, ithoMessageLeaveCommandBytes, ithoMessageAwayCommandBytes, ithoMessageLowCommandBytes, ithoMessageMediumCommandBytes, ithoMessageHighCommandBytes, nullptr, ithoMessageTimer1CommandBytes, ithoMessageTimer2CommandBytes, ithoMessageTimer3CommandBytes, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; 96 | const uint8_t *RFTAUTO_Remote_Map[] = {nullptr, ithoMessageAUTORFTJoinCommandBytes, ithoMessageAUTORFTLeaveCommandBytes, nullptr, ithoMessageAUTORFTLowCommandBytes, nullptr, ithoMessageAUTORFTHighCommandBytes, nullptr, ithoMessageAUTORFTTimer1CommandBytes, ithoMessageAUTORFTTimer2CommandBytes, ithoMessageAUTORFTTimer3CommandBytes, ithoMessageAUTORFTAutoCommandBytes, ithoMessageAUTORFTAutoNightCommandBytes, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr}; 97 | const uint8_t *RFTAUTON_Remote_Map[] = {nullptr, ithoMessageAUTORFTNJoinCommandBytes, ithoMessageAUTORFTLeaveCommandBytes, nullptr, ithoMessageAUTORFTLowCommandBytes, nullptr, ithoMessageAUTORFTHighCommandBytes, nullptr, ithoMessageAUTORFTTimer1CommandBytes, ithoMessageAUTORFTTimer2CommandBytes, ithoMessageAUTORFTTimer3CommandBytes, ithoMessageAUTORFTAutoCommandBytes, ithoMessageAUTORFTAutoNightCommandBytes, nullptr, nullptr, nullptr, ithoMessageJoinReplyCommandBytes, nullptr, nullptr}; 98 | const uint8_t *DEMANDFLOW_Remote_Map[] = {nullptr, ithoMessageDFJoinCommandBytes, ithoMessageLeaveCommandBytes, nullptr, ithoMessageDFLowCommandBytes, nullptr, ithoMessageDFHighCommandBytes, nullptr, ithoMessageDFTimer1CommandBytes, ithoMessageDFTimer2CommandBytes, ithoMessageDFTimer3CommandBytes, nullptr, nullptr, ithoMessageDFCook30CommandBytes, ithoMessageDFCook60CommandBytes, nullptr, nullptr, nullptr, nullptr}; 99 | const uint8_t *RFTRV_Remote_Map[] = {nullptr, ithoMessageRVJoinCommandBytes, ithoMessageLeaveCommandBytes, nullptr, ithoMessageLowCommandBytes, ithoMessageRV_CO2MediumCommandBytes, ithoMessageHighCommandBytes, nullptr, ithoMessageRV_CO2Timer1CommandBytes, ithoMessageRV_CO2Timer2CommandBytes, ithoMessageRV_CO2Timer3CommandBytes, ithoMessageRV_CO2AutoCommandBytes, ithoMessageRV_CO2AutoNightCommandBytes, nullptr, nullptr, nullptr, ithoMessageJoinReplyCommandBytes, nullptr, nullptr}; 100 | const uint8_t *RFTCO2_Remote_Map[] = {nullptr, ithoMessageCO2JoinCommandBytes, ithoMessageLeaveCommandBytes, nullptr, ithoMessageLowCommandBytes, ithoMessageRV_CO2MediumCommandBytes, ithoMessageHighCommandBytes, nullptr, ithoMessageRV_CO2Timer1CommandBytes, ithoMessageRV_CO2Timer2CommandBytes, ithoMessageRV_CO2Timer3CommandBytes, ithoMessageRV_CO2AutoCommandBytes, ithoMessageRV_CO2AutoNightCommandBytes, nullptr, nullptr, nullptr, ithoMessageJoinReplyCommandBytes, nullptr, nullptr}; 101 | const uint8_t *RFTPIR_Remote_Map[] = {nullptr, ithoMessagePIRJoinCommandBytes, ithoMessageLeaveCommandBytes, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, ithoMessageRFTPIRonCommandBytes, ithoMessageRFTPIRoffCommandBytes}; 102 | 103 | struct ihtoRemoteCmdMap 104 | { 105 | RemoteTypes type; 106 | const uint8_t **commandMapping; 107 | }; 108 | 109 | const struct ihtoRemoteCmdMap ihtoRemoteCmdMapping[]{ 110 | {RFTCVE, RFTCVE_Remote_Map}, 111 | {RFTAUTO, RFTAUTO_Remote_Map}, 112 | {RFTAUTON, RFTAUTON_Remote_Map}, 113 | {DEMANDFLOW, DEMANDFLOW_Remote_Map}, 114 | {RFTRV, RFTRV_Remote_Map}, 115 | {RFTCO2, RFTCO2_Remote_Map}, 116 | {RFTPIR, RFTPIR_Remote_Map}}; 117 | 118 | void IthoCC1101::initSendMessage(uint8_t len) 119 | { 120 | // finishTransfer(); 121 | writeCommand(CC1101_SIDLE); 122 | delayMicroseconds(1); 123 | writeRegister(CC1101_IOCFG0, 0x2E); 124 | delayMicroseconds(1); 125 | writeRegister(CC1101_IOCFG1, 0x2E); 126 | delayMicroseconds(1); 127 | writeCommand(CC1101_SIDLE); 128 | writeCommand(CC1101_SPWD); 129 | delayMicroseconds(2); 130 | 131 | /* 132 | Configuration reverse engineered from remote print. The commands below are used by IthoDaalderop. 133 | Base frequency 868.299866MHz 134 | Channel 0 135 | Channel spacing 199.951172kHz 136 | Carrier frequency 868.299866MHz 137 | Xtal frequency 26.000000MHz 138 | Data rate 38.3835kBaud 139 | Manchester disabled 140 | Modulation 2-FSK 141 | Deviation 50.781250kHz 142 | TX power ? 143 | PA ramping enabled 144 | Whitening disabled 145 | */ 146 | writeCommand(CC1101_SRES); 147 | delayMicroseconds(1); 148 | writeRegister(CC1101_IOCFG0, 0x2E); // High impedance (3-state) 149 | writeRegister(CC1101_FREQ2, cc_freq[2]); // 00100001 878MHz-927.8MHz 150 | writeRegister(CC1101_FREQ1, cc_freq[1]); // 01100101 151 | writeRegister(CC1101_FREQ0, cc_freq[0]); // 01101010 152 | writeRegister(CC1101_MDMCFG4, 0x5A); // difference compared to message1 153 | writeRegister(CC1101_MDMCFG3, 0x83); // difference compared to message1 154 | writeRegister(CC1101_MDMCFG2, 0x00); // 00000000 2-FSK, no manchester encoding/decoding, no preamble/sync 155 | writeRegister(CC1101_MDMCFG1, 0x22); // 00100010 156 | writeRegister(CC1101_MDMCFG0, 0xF8); // 11111000 157 | writeRegister(CC1101_CHANNR, 0x00); // 00000000 158 | writeRegister(CC1101_DEVIATN, 0x50); // difference compared to message1 159 | writeRegister(CC1101_FREND0, 0x17); // 00010111 use index 7 in PA table 160 | writeRegister(CC1101_MCSM0, 0x18); // 00011000 PO timeout Approx. 146microseconds - 171microseconds, Auto calibrate When going from IDLE to RX or TX (or FSTXON) 161 | writeRegister(CC1101_FSCAL3, 0xA9); // 10101001 162 | writeRegister(CC1101_FSCAL2, 0x2A); // 00101010 163 | writeRegister(CC1101_FSCAL1, 0x00); // 00000000 164 | writeRegister(CC1101_FSCAL0, 0x11); // 00010001 165 | writeRegister(CC1101_FSTEST, 0x59); // 01011001 For test only. Do not write to this register. 166 | writeRegister(CC1101_TEST2, 0x81); // 10000001 For test only. Do not write to this register. 167 | writeRegister(CC1101_TEST1, 0x35); // 00110101 For test only. Do not write to this register. 168 | writeRegister(CC1101_TEST0, 0x0B); // 00001011 For test only. Do not write to this register. 169 | writeRegister(CC1101_PKTCTRL0, 0x12); // 00010010 Enable infinite length packets, CRC disabled, Turn data whitening off, Serial Synchronous mode 170 | writeRegister(CC1101_ADDR, 0x00); // 00000000 171 | writeRegister(CC1101_PKTLEN, 0xFF); // 11111111 //Not used, no hardware packet handling 172 | 173 | // 0x6F,0x26,0x2E,0x8C,0x87,0xCD,0xC7,0xC0 174 | writeBurstRegister(CC1101_PATABLE | CC1101_WRITE_BURST, ithoPaTableSend, 8); 175 | 176 | // difference, message1 sends a STX here 177 | writeCommand(CC1101_SIDLE); 178 | writeCommand(CC1101_SIDLE); 179 | 180 | writeRegister(CC1101_MDMCFG4, 0x5A); // difference compared to message1 181 | writeRegister(CC1101_MDMCFG3, 0x83); // difference compared to message1 182 | writeRegister(CC1101_DEVIATN, 0x50); // difference compared to message1 183 | writeRegister(CC1101_IOCFG0, 0x2D); // GDO0_Z_EN_N. When this output is 0, GDO0 is configured as input (for serial TX data). 184 | writeRegister(CC1101_IOCFG1, 0x0B); // Serial Clock. Synchronous to the data in synchronous serial mode. 185 | 186 | writeCommand(CC1101_STX); 187 | writeCommand(CC1101_SIDLE); 188 | 189 | writeRegister(CC1101_MDMCFG4, 0x5A); // difference compared to message1 190 | writeRegister(CC1101_MDMCFG3, 0x83); // difference compared to message1 191 | writeRegister(CC1101_DEVIATN, 0x50); // difference compared to message1 192 | // writeRegister(CC1101_IOCFG0 ,0x2D); //GDO0_Z_EN_N. When this output is 0, GDO0 is configured as input (for serial TX data). 193 | // writeRegister(CC1101_IOCFG1 ,0x0B); //Serial Clock. Synchronous to the data in synchronous serial mode. 194 | 195 | // Itho is using serial mode for transmit. We want to use the TX FIFO with fixed packet length for simplicity. 196 | writeRegister(CC1101_IOCFG0, 0x2E); 197 | writeRegister(CC1101_IOCFG1, 0x2E); 198 | writeRegister(CC1101_PKTCTRL0, 0x00); 199 | writeRegister(CC1101_PKTCTRL1, 0x00); 200 | 201 | writeRegister(CC1101_PKTLEN, len); 202 | } 203 | 204 | void IthoCC1101::finishTransfer() 205 | { 206 | writeCommand(CC1101_SIDLE); 207 | delayMicroseconds(1); 208 | 209 | writeRegister(CC1101_IOCFG0, 0x2E); 210 | writeRegister(CC1101_IOCFG1, 0x2E); 211 | 212 | writeCommand(CC1101_SIDLE); 213 | writeCommand(CC1101_SPWD); 214 | } 215 | 216 | void IthoCC1101::initReceive() 217 | { 218 | /* 219 | Configuration reverse engineered from RFT print. 220 | 221 | Base frequency 868.299866MHz 222 | Channel 0 223 | Channel spacing 199.951172kHz 224 | Carrier frequency 868.299866MHz 225 | Xtal frequency 26.000000MHz 226 | Data rate 38.3835kBaud 227 | RX filter BW 325.000000kHz 228 | Manchester disabled 229 | Modulation 2-FSK 230 | Deviation 50.781250kHz 231 | TX power 0x6F,0x26,0x2E,0x7F,0x8A,0x84,0xCA,0xC4 232 | PA ramping enabled 233 | Whitening disabled 234 | */ 235 | writeCommand(CC1101_SRES); 236 | 237 | writeRegister(CC1101_TEST0, 0x09); 238 | writeRegister(CC1101_FSCAL2, 0x00); 239 | 240 | // 0x6F,0x26,0x2E,0x7F,0x8A,0x84,0xCA,0xC4 241 | writeBurstRegister(CC1101_PATABLE | CC1101_WRITE_BURST, ithoPaTableReceive, 8); 242 | 243 | writeCommand(CC1101_SCAL); 244 | 245 | // wait for calibration to finish 246 | while ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER)) != CC1101_MARCSTATE_IDLE) 247 | yield(); 248 | 249 | writeRegister(CC1101_FSCAL2, 0x00); 250 | writeRegister(CC1101_MCSM0, 0x18); // no auto calibrate 251 | writeRegister(CC1101_FREQ2, cc_freq[2]); 252 | writeRegister(CC1101_FREQ1, cc_freq[1]); 253 | writeRegister(CC1101_FREQ0, cc_freq[0]); 254 | writeRegister(CC1101_IOCFG0, 0x2E); // High impedance (3-state) 255 | writeRegister(CC1101_IOCFG2, 0x06); // 0x06 Assert when sync word has been sent / received, and de-asserts at the end of the packet. 256 | writeRegister(CC1101_FSCTRL1, 0x0F); // change 06 257 | writeRegister(CC1101_FSCTRL0, 0x00); 258 | writeRegister(CC1101_MDMCFG4, 0x6A); 259 | writeRegister(CC1101_MDMCFG3, 0x83); 260 | writeRegister(CC1101_MDMCFG2, 0x10); // Enable digital DC blocking filter before demodulator, 2-FSK, Disable Manchester encoding/decoding, No preamble/sync 261 | writeRegister(CC1101_MDMCFG1, 0x22); // Disable FEC 262 | writeRegister(CC1101_MDMCFG0, 0xF8); 263 | writeRegister(CC1101_CHANNR, 0x00); 264 | writeRegister(CC1101_DEVIATN, 0x50); 265 | writeRegister(CC1101_FREND1, 0x56); 266 | writeRegister(CC1101_FREND0, 0x10); 267 | writeRegister(CC1101_MCSM0, 0x18); // no auto calibrate 268 | writeRegister(CC1101_FOCCFG, 0x16); 269 | writeRegister(CC1101_BSCFG, 0x6C); 270 | writeRegister(CC1101_AGCCTRL2, 0x43); 271 | writeRegister(CC1101_AGCCTRL1, 0x40); 272 | writeRegister(CC1101_AGCCTRL0, 0x91); 273 | writeRegister(CC1101_FSCAL3, 0xE9); 274 | writeRegister(CC1101_FSCAL2, 0x21); 275 | writeRegister(CC1101_FSCAL1, 0x00); 276 | writeRegister(CC1101_FSCAL0, 0x1F); 277 | writeRegister(CC1101_FSTEST, 0x59); 278 | writeRegister(CC1101_TEST2, 0x81); 279 | writeRegister(CC1101_TEST1, 0x35); 280 | writeRegister(CC1101_TEST0, 0x09); 281 | writeRegister(CC1101_PKTCTRL1, 0x04); // No address check, Append two bytes with status RSSI/LQI/CRC OK, 282 | 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 283 | writeRegister(CC1101_ADDR, 0x00); 284 | writeRegister(CC1101_PKTLEN, 0xFF); 285 | 286 | writeCommand(CC1101_SCAL); 287 | 288 | // wait for calibration to finish 289 | while ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER)) != CC1101_MARCSTATE_IDLE) 290 | yield(); 291 | 292 | writeRegister(CC1101_MCSM0, 0x18); // no auto calibrate 293 | 294 | writeCommand(CC1101_SIDLE); 295 | writeCommand(CC1101_SIDLE); 296 | 297 | writeRegister(CC1101_MDMCFG2, 0x00); // Enable digital DC blocking filter before demodulator, 2-FSK, Disable Manchester encoding/decoding, No preamble/sync 298 | writeRegister(CC1101_IOCFG0, 0x0D); // Serial Data Output. Used for asynchronous serial mode. 299 | 300 | writeCommand(CC1101_SRX); 301 | 302 | while ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER)) != CC1101_MARCSTATE_RX) 303 | yield(); 304 | 305 | initReceiveMessage(); 306 | } 307 | 308 | void IthoCC1101::initReceiveMessage() 309 | { 310 | uint8_t marcState; 311 | 312 | writeCommand(CC1101_SIDLE); // idle 313 | 314 | // set datarate 315 | writeRegister(CC1101_MDMCFG4, 0x6A); // set kBaud 316 | writeRegister(CC1101_MDMCFG3, 0x83); // set kBaud 317 | writeRegister(CC1101_DEVIATN, 0x50); 318 | 319 | // set fifo mode with fixed packet length and sync bytes 320 | // writeRegister(CC1101_PKTLEN , 63); //63 bytes message (sync at beginning of message is removed by CC1101) 321 | 322 | // set fifo mode with fixed packet length and sync bytes 323 | writeRegister(CC1101_PKTCTRL0, 0x02); 324 | writeRegister(CC1101_SYNC1, SYNC1); 325 | writeRegister(CC1101_SYNC0, SYNC0); 326 | writeRegister(CC1101_MDMCFG2, MDMCFG2); 327 | writeRegister(CC1101_PKTCTRL1, 0x00); 328 | 329 | writeCommand(CC1101_SRX); // switch to RX state 330 | 331 | // Check that the RX state has been entered 332 | while (((marcState = readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER)) & CC1101_BITS_MARCSTATE) != CC1101_MARCSTATE_RX) 333 | { 334 | if (marcState == CC1101_MARCSTATE_RXFIFO_OVERFLOW) // RX_OVERFLOW 335 | writeCommand(CC1101_SFRX); // flush RX buffer 336 | } 337 | } 338 | 339 | uint8_t IthoCC1101::receivePacket() 340 | { 341 | return readData(&inMessage, MAX_RAW); 342 | } 343 | 344 | bool IthoCC1101::checkForNewPacket() 345 | { 346 | receivePacket(); 347 | if (parseMessageCommand()) 348 | { 349 | initReceiveMessage(); 350 | return true; 351 | } 352 | return false; 353 | } 354 | 355 | bool IthoCC1101::parseMessageCommand() 356 | { 357 | 358 | inIthoPacket.header = 0; 359 | inIthoPacket.deviceId0 = 0; 360 | inIthoPacket.deviceId1 = 0; 361 | inIthoPacket.deviceId2 = 0; 362 | inIthoPacket.param0 = 0; 363 | inIthoPacket.param1 = 0; 364 | inIthoPacket.opcode = 0; 365 | inIthoPacket.len = 0; 366 | inIthoPacket.error = 0; 367 | inIthoPacket.payloadPos = 0; 368 | inIthoPacket.length = 0; 369 | 370 | messageDecode(&inMessage, &inIthoPacket); 371 | 372 | uint8_t dataPos = 0; 373 | inIthoPacket.error = 0; 374 | inIthoPacket.remType = RemoteTypes::UNSETTYPE; 375 | inIthoPacket.command = IthoUnknown; 376 | 377 | // first byte is the header of the message, this determines the structure of the rest of the message 378 | // The bits are used as follows <00TTAAPP> 379 | // 00 - Unused 380 | // TT - Message type 381 | // AA - Present DeviceID fields 382 | // PP - Present Params 383 | inIthoPacket.header = inIthoPacket.dataDecoded[0]; 384 | dataPos++; 385 | 386 | // packet type: RQ-Request:00, I-Inform:01, W-Write:10, RP-Response:11 387 | if ((inIthoPacket.dataDecoded[0] >> 4) > 3) 388 | { 389 | inIthoPacket.error = 1; 390 | return false; 391 | } 392 | 393 | inIthoPacket.deviceId0 = 0; 394 | inIthoPacket.deviceId1 = 0; 395 | inIthoPacket.deviceId2 = 0; 396 | 397 | // get DeviceID fields 398 | uint8_t idfield = (inIthoPacket.dataDecoded[0] >> 2) & 0x03; 399 | 400 | if (idfield == 0x00 || idfield == 0x02 || idfield == 0x03) 401 | { 402 | inIthoPacket.deviceId0 = inIthoPacket.dataDecoded[dataPos] << 16 | inIthoPacket.dataDecoded[dataPos + 1] << 8 | inIthoPacket.dataDecoded[dataPos + 2]; 403 | dataPos += 3; 404 | if (idfield == 0x00 || idfield == 0x03) 405 | { 406 | inIthoPacket.deviceId1 = inIthoPacket.dataDecoded[dataPos] << 16 | inIthoPacket.dataDecoded[dataPos + 1] << 8 | inIthoPacket.dataDecoded[dataPos + 2]; 407 | dataPos += 3; 408 | } 409 | if (idfield == 0x00 || idfield == 0x02) 410 | { 411 | inIthoPacket.deviceId2 = inIthoPacket.dataDecoded[dataPos] << 16 | inIthoPacket.dataDecoded[dataPos + 1] << 8 | inIthoPacket.dataDecoded[dataPos + 2]; 412 | dataPos += 3; 413 | } 414 | } 415 | else 416 | { 417 | inIthoPacket.deviceId2 = inIthoPacket.dataDecoded[dataPos] << 16 | inIthoPacket.dataDecoded[dataPos + 1] << 8 | inIthoPacket.dataDecoded[dataPos + 2]; 418 | dataPos += 3; 419 | } 420 | 421 | if (inIthoPacket.deviceId0 == 0 && inIthoPacket.deviceId1 == 0 && inIthoPacket.deviceId2 == 0) 422 | return false; 423 | 424 | // determine param0 present 425 | if (inIthoPacket.header & OPT0_MASK) 426 | { 427 | inIthoPacket.param0 = inIthoPacket.dataDecoded[dataPos]; 428 | 429 | dataPos++; 430 | } 431 | 432 | // determine param1 present 433 | if (inIthoPacket.header & OPT1_MASK) 434 | { 435 | inIthoPacket.param1 = inIthoPacket.dataDecoded[dataPos]; 436 | dataPos++; 437 | } 438 | 439 | // Get the two bytes of the opcode 440 | inIthoPacket.opcode = inIthoPacket.dataDecoded[dataPos] << 8 | inIthoPacket.dataDecoded[dataPos + 1]; 441 | dataPos += 2; 442 | 443 | // Payload length 444 | inIthoPacket.len = inIthoPacket.dataDecoded[dataPos]; 445 | if (inIthoPacket.len > MAX_PAYLOAD) 446 | { 447 | inIthoPacket.error = 1; 448 | return false; 449 | } 450 | 451 | dataPos++; 452 | inIthoPacket.payloadPos = dataPos; 453 | 454 | // Now we have parsed all the variable fields and know the total lenth of the message 455 | // with that we can determine if the message CRC is correct 456 | uint8_t mLen = inIthoPacket.payloadPos + inIthoPacket.len; 457 | 458 | if (checksum(&inIthoPacket, mLen) != inIthoPacket.dataDecoded[mLen]) 459 | { 460 | inIthoPacket.error = 2; 461 | #if defined(CRC_FILTER) 462 | inIthoPacket.command = IthoUnknown; 463 | return false; 464 | #endif 465 | } 466 | 467 | // 468 | // old message parse code below 469 | // 470 | 471 | // deviceType of message type? 472 | // inIthoPacket.deviceType = inIthoPacket.dataDecoded[0]; 473 | 474 | // // deviceID 475 | // inIthoPacket.deviceId[0] = inIthoPacket.dataDecoded[1]; 476 | // inIthoPacket.deviceId[1] = inIthoPacket.dataDecoded[2]; 477 | // inIthoPacket.deviceId[2] = inIthoPacket.dataDecoded[3]; 478 | 479 | // // counter1 480 | // inIthoPacket.counter = inIthoPacket.dataDecoded[4]; 481 | 482 | // handle command 483 | switch (inIthoPacket.opcode) 484 | { 485 | case IthoPacket::Type::BIND: 486 | handleBind(); 487 | break; 488 | case IthoPacket::Type::LEVEL: 489 | handleLevel(); 490 | break; 491 | case IthoPacket::Type::SETPOINT: 492 | handleLevel(); 493 | break; 494 | case IthoPacket::Type::TIMER: 495 | handleTimer(); 496 | break; 497 | case IthoPacket::Type::STATUS: 498 | handleStatus(); 499 | break; 500 | case IthoPacket::Type::REMOTESTATUS: 501 | handleRemotestatus(); 502 | break; 503 | case IthoPacket::Type::TEMPHUM: 504 | handleTemphum(); 505 | break; 506 | case IthoPacket::Type::CO2: 507 | handleCo2(); 508 | break; 509 | case IthoPacket::Type::BATTERY: 510 | handleBattery(); 511 | break; 512 | case IthoPacket::Type::PIR: 513 | handlePIR(); 514 | break; 515 | // default: 516 | // return false; 517 | } 518 | 519 | return true; 520 | } 521 | 522 | bool IthoCC1101::checkIthoCommand(IthoPacket *itho, const uint8_t commandBytes[]) 523 | { 524 | for (int i = 0; i < 6; i++) 525 | { 526 | if (itho->dataDecoded[i + (itho->payloadPos - 3)] != commandBytes[i]) 527 | { 528 | return false; 529 | } 530 | } 531 | return true; 532 | } 533 | 534 | // sendCommand -> sendRFCommand(0, command) results in backwards compatible behaviour 535 | void IthoCC1101::sendCommand(IthoCommand command) 536 | { 537 | sendRFCommand(0, command); 538 | } 539 | 540 | const uint8_t *IthoCC1101::getRemoteCmd(const RemoteTypes type, const IthoCommand command) 541 | { 542 | 543 | const struct ihtoRemoteCmdMap *ihtoRemoteCmdMapPtr = ihtoRemoteCmdMapping; 544 | const struct ihtoRemoteCmdMap *ihtoRemoteCmdMapEndPtr = ihtoRemoteCmdMapping + sizeof(ihtoRemoteCmdMapping) / sizeof(ihtoRemoteCmdMapping[0]); 545 | while (ihtoRemoteCmdMapPtr < ihtoRemoteCmdMapEndPtr) 546 | { 547 | if (ihtoRemoteCmdMapPtr->type == type) 548 | { 549 | return *(ihtoRemoteCmdMapPtr->commandMapping + command); 550 | } 551 | ihtoRemoteCmdMapPtr++; 552 | } 553 | return nullptr; 554 | } 555 | 556 | void IthoCC1101::createMessageStart(IthoPacket *itho, CC1101Packet *packet) 557 | { 558 | 559 | // fixed, set start structure in data buffer manually 560 | for (uint8_t i = 0; i < 7; i++) 561 | { 562 | packet->data[i] = 170; 563 | } 564 | packet->data[7] = 171; 565 | packet->data[8] = 254; 566 | packet->data[9] = 0; 567 | packet->data[10] = 179; 568 | packet->data[11] = 42; 569 | packet->data[12] = 171; 570 | packet->data[13] = 42; 571 | 572 | //[start of command specific data] 573 | } 574 | 575 | void IthoCC1101::sendRFCommand(uint8_t remote_index, IthoCommand command) 576 | { 577 | RFmessage message; 578 | message.header = HEADER_REMOTE_CMD; // header of regular itho remote commands seems always to be 0x16 (message type: I, addr2, opt0) 579 | 580 | message.deviceid2[0] = static_cast((ithoRF.device[remote_index].deviceId >> 16) & 0xFF); 581 | message.deviceid2[1] = static_cast((ithoRF.device[remote_index].deviceId >> 8) & 0xFF); 582 | message.deviceid2[2] = static_cast(ithoRF.device[remote_index].deviceId & 0xFF); 583 | 584 | ithoRF.device[remote_index].counter += 1; 585 | message.opt0 = ithoRF.device[remote_index].counter; 586 | 587 | message.command = getRemoteCmd(ithoRF.device[remote_index].remType, command); 588 | 589 | sendRFMessage(&message); 590 | } 591 | 592 | void IthoCC1101::sendJoinReply(uint8_t byte0, uint8_t byte1, uint8_t byte2) 593 | { 594 | uint32_t tempID = byte0 << 16 | byte1 << 8 | byte2; 595 | sendJoinReply(tempID); 596 | } 597 | 598 | void IthoCC1101::sendJoinReply(uint32_t ID) 599 | { 600 | int8_t remote_index = getRemoteIndexByID(ID); 601 | 602 | if (remote_index == -1) 603 | return; 604 | 605 | RFmessage message; 606 | 607 | message.header = HEADER_JOIN_REPLY; 608 | 609 | message.deviceid0[0] = deviceIDsend[0]; 610 | message.deviceid0[1] = deviceIDsend[1]; 611 | message.deviceid0[2] = deviceIDsend[2]; 612 | 613 | message.deviceid1[0] = static_cast((ithoRF.device[remote_index].deviceId >> 16) & 0xFF); 614 | message.deviceid1[1] = static_cast((ithoRF.device[remote_index].deviceId >> 8) & 0xFF); 615 | message.deviceid1[2] = static_cast(ithoRF.device[remote_index].deviceId & 0xFF); 616 | 617 | message.command = getRemoteCmd(ithoRF.device[remote_index].remType, IthoCommand::IthoJoinReply); 618 | 619 | sendRFMessage(&message); 620 | } 621 | 622 | void IthoCC1101::send2E10(uint8_t remote_index, IthoCommand command) 623 | { 624 | RFmessage message; 625 | message.header = HEADER_2E10; // header of regular itho remote commands seems always to be 0x16 (message type: I, addr2, opt0) 626 | 627 | message.deviceid2[0] = static_cast((ithoRF.device[remote_index].deviceId >> 16) & 0xFF); 628 | message.deviceid2[1] = static_cast((ithoRF.device[remote_index].deviceId >> 8) & 0xFF); 629 | message.deviceid2[2] = static_cast(ithoRF.device[remote_index].deviceId & 0xFF); 630 | 631 | ithoRF.device[remote_index].counter += 1; 632 | message.opt0 = ithoRF.device[remote_index].counter; 633 | 634 | message.command = getRemoteCmd(ithoRF.device[remote_index].remType, command); 635 | 636 | sendRFMessage(&message); 637 | } 638 | 639 | void IthoCC1101::send10E0() 640 | { 641 | // H:18 RQ P0:-- P1:-- 96,A4,3B --,--,-- 96,A4,3B 10E0 26:00,0x00,0x01,0x00,0x1B,0x31,0x19,0x01,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0x0E,0x05,0x07,0xE2,0x43,0x56,0x45,0x2D,0x52,0x46,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 642 | 643 | RFmessage message; 644 | 645 | message.header = HEADER_10E0; 646 | 647 | message.deviceid0[0] = deviceIDsend[0]; 648 | message.deviceid0[1] = deviceIDsend[1]; 649 | message.deviceid0[2] = deviceIDsend[2]; 650 | 651 | message.deviceid2[0] = deviceIDsend[0]; 652 | message.deviceid2[1] = deviceIDsend[1]; 653 | message.deviceid2[2] = deviceIDsend[2]; 654 | 655 | // 4E 52 47 2D 57 41 54 43 48 656 | // const uint8_t command[] = {0x10, 0xE0, 0x26, 0x00, 0x00, 0x01, 0x00, 0x1B, 0x31, 0x19, 0x01, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0E, 0x05, 0x07, 0xE2, 0x43, 0x56, 0x45, 0x2D, 0x52, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 657 | const uint8_t command[] = {0x10, 0xE0, 0x26, 0x00, 0x00, 0x01, 0x00, 0x1B, 0x31, 0x19, 0x01, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xAA, 0xBB, 0xCC, 0xE2, 0x43, 0x56, 0x45, 0x2D, 0x52, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 658 | 659 | message.command = &command[0]; 660 | 661 | sendRFMessage(&message); 662 | } 663 | 664 | //
665 | // rewrite: uint8_t header, uint32_t deviceid0, uint32_t deviceid1, uint32_t deviceid2, uint8_t opt0, uint8_t opt1, uint8_t opcode*, uint8_t len*, uint8_t* command 666 | void IthoCC1101::sendRFMessage(RFmessage *message) 667 | { 668 | 669 | if (message->header == 0xC0) // header not set 670 | return; 671 | if (message->command == nullptr) 672 | return; 673 | 674 | CC1101Packet CC1101Message; 675 | IthoPacket ithoPacket; 676 | 677 | uint8_t maxTries = sendTries; 678 | uint8_t delaytime = 40; 679 | 680 | // itho = ithoPacket 681 | // packet = CC1101Message 682 | // set start message structure 683 | createMessageStart(&ithoPacket, &CC1101Message); 684 | 685 | uint8_t messagePos = 0; 686 | // set message structure, for itho RF remotes this seems to be always 0x16 687 | ithoPacket.dataDecoded[messagePos] = message->header; 688 | 689 | // set deviceIDs based on header bits xxxxAAxx 690 | if ((message->header & DEVICEID_MASK) ^ 0x4) // true if AA bits are 00, 10 or 11 691 | { 692 | ithoPacket.dataDecoded[++messagePos] = message->deviceid0[0]; 693 | ithoPacket.dataDecoded[++messagePos] = message->deviceid0[1]; 694 | ithoPacket.dataDecoded[++messagePos] = message->deviceid0[2]; 695 | } 696 | if (((message->header & DEVICEID_MASK) == 0) || ((message->header & DEVICEID_MASK) == DEVICEID_MASK)) // true if AA bits are 00 or 11 697 | { 698 | ithoPacket.dataDecoded[++messagePos] = message->deviceid1[0]; 699 | ithoPacket.dataDecoded[++messagePos] = message->deviceid1[1]; 700 | ithoPacket.dataDecoded[++messagePos] = message->deviceid1[2]; 701 | } 702 | if (((message->header & DEVICEID_MASK)) != DEVICEID_MASK) // true if AA bits are 00, 01 or 10 703 | { 704 | ithoPacket.dataDecoded[++messagePos] = message->deviceid2[0]; 705 | ithoPacket.dataDecoded[++messagePos] = message->deviceid2[1]; 706 | ithoPacket.dataDecoded[++messagePos] = message->deviceid2[2]; 707 | } 708 | 709 | // set option fields 710 | if (message->header & OPT0_MASK) 711 | { 712 | ithoPacket.dataDecoded[++messagePos] = message->opt0; 713 | } 714 | if (message->header & OPT1_MASK) 715 | { 716 | ithoPacket.dataDecoded[++messagePos] = message->opt1; 717 | } 718 | 719 | // determine command length 720 | const uint8_t command_len = message->command[2]; 721 | const uint16_t opcode = message->command[0] << 8 | message->command[1]; 722 | 723 | uint8_t deviceID[3]{0}; 724 | if (opcode == 0x1FC9) 725 | { 726 | if (message->header == HEADER_REMOTE_CMD) 727 | { 728 | deviceID[0] = message->deviceid2[0]; 729 | deviceID[1] = message->deviceid2[1]; 730 | deviceID[2] = message->deviceid2[2]; 731 | } 732 | else if (message->header == HEADER_JOIN_REPLY) 733 | { 734 | deviceID[0] = message->deviceid0[0]; 735 | deviceID[1] = message->deviceid0[1]; 736 | deviceID[2] = message->deviceid0[2]; 737 | } 738 | } 739 | for (int i = 0; i < 2 + command_len + 1; i++) 740 | { 741 | ithoPacket.dataDecoded[++messagePos] = message->command[i]; 742 | if (i > 5 && opcode == 0x1FC9) 743 | { 744 | // command bytes locations with ID in Join/Leave messages: 6/7/8 12/13/14 18/19/20 24/25/26 30/31/32 36/37/38 etc 745 | if (i % 6 == 0 || i % 6 == 1 || i % 6 == 2) 746 | { 747 | ithoPacket.dataDecoded[messagePos] = deviceID[i % 6]; 748 | } 749 | } 750 | } 751 | 752 | // timer could be made configurable 753 | // if (command == IthoTimerUser) 754 | // { 755 | // ithoPacket.dataDecoded[10] = timer_value; 756 | // } 757 | 758 | // set checksum (used to be called counter2) 759 | ++messagePos; 760 | ithoPacket.dataDecoded[messagePos] = checksum(&ithoPacket, messagePos); 761 | 762 | ithoPacket.length = messagePos + 1; 763 | 764 | CC1101Message.length = messageEncode(&ithoPacket, &CC1101Message); 765 | CC1101Message.length += 1; 766 | 767 | // set end byte - even/uneven cmd length determines last byte? 768 | if (opcode == 0x1FC9) 769 | { 770 | CC1101Message.data[CC1101Message.length] = 0xCA; 771 | } 772 | else 773 | { 774 | CC1101Message.data[CC1101Message.length] = 0xAC; 775 | } 776 | 777 | CC1101Message.length += 1; 778 | 779 | // set end 'noise' 780 | for (uint8_t i = CC1101Message.length; i < CC1101Message.length + 7; i++) 781 | { 782 | CC1101Message.data[i] = 0xAA; 783 | } 784 | CC1101Message.length += 7; 785 | 786 | CC1101MessageLen = CC1101Message.length; 787 | IthoPacketLen = ithoPacket.length; 788 | 789 | // send messages 790 | for (int i = 0; i < maxTries; i++) 791 | { 792 | 793 | // message2 794 | initSendMessage(CC1101Message.length); 795 | sendData(&CC1101Message); 796 | 797 | finishTransfer(); 798 | delay(delaytime); 799 | } 800 | initReceive(); 801 | } 802 | 803 | /* 804 | Counter2 is the decimal sum of all bytes in decoded form from 805 | deviceType up to the last byte before counter2 subtracted 806 | from zero. 807 | */ 808 | uint8_t IthoCC1101::checksum(IthoPacket *itho, uint8_t len) 809 | { 810 | 811 | uint8_t val = 0; 812 | 813 | for (uint8_t i = 0; i < len; i++) 814 | { 815 | val += itho->dataDecoded[i]; 816 | } 817 | 818 | return 0 - val; 819 | } 820 | 821 | uint8_t IthoCC1101::messageEncode(IthoPacket *itho, CC1101Packet *packet) 822 | { 823 | 824 | uint8_t out_bytecounter = 14; // index of Outbuf, start at offset 14, first part of the message is set manually 825 | uint8_t out_bitcounter = 0; // bit position of current outbuf byte 826 | uint8_t out_patterncounter = 0; // bit counter to add 1 0 bit pattern after every 8 bits 827 | uint8_t bitSelect = 4; // bit position of the inData byte (4 - 7, 0 - 3) 828 | uint8_t out_shift = 7; // bit shift inData bit in position of outbuf byte 829 | 830 | // we need to zero the out buffer first cause we are using bitshifts 831 | for (unsigned int i = out_bytecounter; i < sizeof(packet->data) / sizeof(packet->data[0]); i++) 832 | { 833 | packet->data[i] = 0; 834 | } 835 | 836 | for (uint8_t dataByte = 0; dataByte < itho->length; dataByte++) 837 | { 838 | for (uint8_t dataBit = 0; dataBit < 8; dataBit++) 839 | { // 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 840 | if (out_bitcounter == 8) 841 | { // check if new byte is needed 842 | out_bytecounter++; 843 | out_bitcounter = 0; 844 | } 845 | 846 | if (out_patterncounter == 8) 847 | { // check if we have to start with a 1 0 pattern 848 | out_patterncounter = 0; 849 | packet->data[out_bytecounter] = packet->data[out_bytecounter] | 1 << out_shift; 850 | out_shift--; 851 | out_bitcounter++; 852 | packet->data[out_bytecounter] = packet->data[out_bytecounter] | 0 << out_shift; 853 | if (out_shift == 0) 854 | out_shift = 8; 855 | out_shift--; 856 | out_bitcounter++; 857 | } 858 | 859 | if (out_bitcounter == 8) 860 | { // check if new byte is needed 861 | out_bytecounter++; 862 | out_bitcounter = 0; 863 | } 864 | 865 | // set the even bit 866 | uint8_t bit = (itho->dataDecoded[dataByte] & (1 << bitSelect)) >> bitSelect; // select bit and shift to bit pos 0 867 | bitSelect++; 868 | if (bitSelect == 8) 869 | bitSelect = 0; 870 | 871 | packet->data[out_bytecounter] = packet->data[out_bytecounter] | bit << out_shift; // shift bit in corect pos of current outbuf byte 872 | out_shift--; 873 | out_bitcounter++; 874 | out_patterncounter++; 875 | 876 | // set the odd bit (inverse of even bit) 877 | bit = ~bit & 0b00000001; 878 | packet->data[out_bytecounter] = packet->data[out_bytecounter] | bit << out_shift; 879 | if (out_shift == 0) 880 | out_shift = 8; 881 | out_shift--; 882 | out_bitcounter++; 883 | out_patterncounter++; 884 | } 885 | } 886 | if (out_bitcounter < 8) 887 | { // add closing 1 0 pattern to fill last packet->data byte and ensure DC balance in the message 888 | for (uint8_t i = out_bitcounter; i < 8; i += 2) 889 | { 890 | packet->data[out_bytecounter] = packet->data[out_bytecounter] | 1 << out_shift; 891 | out_shift--; 892 | packet->data[out_bytecounter] = packet->data[out_bytecounter] | 0 << out_shift; 893 | if (out_shift == 0) 894 | out_shift = 8; 895 | out_shift--; 896 | } 897 | } 898 | 899 | return out_bytecounter; 900 | } 901 | 902 | void IthoCC1101::messageDecode(CC1101Packet *packet, IthoPacket *itho) 903 | { 904 | 905 | itho->length = 0; 906 | int lenInbuf = packet->length; 907 | 908 | lenInbuf -= STARTBYTE; // correct for sync byte pos 909 | 910 | while (lenInbuf >= 5) 911 | { 912 | lenInbuf -= 5; 913 | itho->length += 2; 914 | } 915 | if (lenInbuf >= 3) 916 | { 917 | itho->length++; 918 | } 919 | 920 | for (unsigned int i = 0; i < sizeof(itho->dataDecoded) / sizeof(itho->dataDecoded[0]); i++) 921 | { 922 | itho->dataDecoded[i] = 0; 923 | } 924 | // for (unsigned int i = 0; i < sizeof(itho->dataDecodedChk) / sizeof(itho->dataDecodedChk[0]); i++) 925 | // { 926 | // itho->dataDecodedChk[i] = 0; 927 | // } 928 | 929 | uint8_t out_i = 0; // byte index 930 | uint8_t out_j = 4; // bit index 931 | uint8_t out_i_chk = 0; // byte index 932 | uint8_t out_j_chk = 4; // bit index 933 | uint8_t in_bitcounter = 0; // process per 10 input bits 934 | 935 | for (int i = STARTBYTE; i < packet->length; i++) 936 | { 937 | 938 | for (int j = 7; j > -1; j--) 939 | { 940 | if (in_bitcounter == 0 || in_bitcounter == 2 || in_bitcounter == 4 || in_bitcounter == 6) 941 | { // select input bits for output 942 | uint8_t x = packet->data[i]; // select input byte 943 | x = x >> j; // select input bit 944 | x = x & 0b00000001; 945 | x = x << out_j; // set value for output bit 946 | itho->dataDecoded[out_i] = itho->dataDecoded[out_i] | x; 947 | out_j += 1; // next output bit 948 | if (out_j > 7) 949 | out_j = 0; 950 | if (out_j == 4) 951 | out_i += 1; 952 | } 953 | if (in_bitcounter == 1 || in_bitcounter == 3 || in_bitcounter == 5 || in_bitcounter == 7) 954 | { // select input bits for check output 955 | uint8_t x = packet->data[i]; // select input byte 956 | x = x >> j; // select input bit 957 | x = x & 0b00000001; 958 | x = x << out_j_chk; // set value for output bit 959 | // itho->dataDecodedChk[out_i_chk] = itho->dataDecodedChk[out_i_chk] | x; 960 | out_j_chk += 1; // next output bit 961 | if (out_j_chk > 7) 962 | out_j_chk = 0; 963 | if (out_j_chk == 4) 964 | { 965 | // itho->dataDecodedChk[out_i_chk] = ~itho->dataDecodedChk[out_i_chk]; // inverse bits 966 | out_i_chk += 1; 967 | } 968 | } 969 | in_bitcounter += 1; // continue cyling in groups of 10 bits 970 | if (in_bitcounter > 9) 971 | in_bitcounter = 0; 972 | } 973 | } 974 | // clear packet data 975 | for (unsigned int i = 0; i < sizeof(packet->data) / sizeof(packet->data[0]); i++) 976 | { 977 | packet->data[i] = 0; 978 | } 979 | } 980 | 981 | uint8_t IthoCC1101::ReadRSSI() 982 | { 983 | uint8_t rssi = 0; 984 | uint8_t value = 0; 985 | 986 | rssi = (readRegister(CC1101_RSSI, CC1101_STATUS_REGISTER)); 987 | 988 | if (rssi >= 128) 989 | { 990 | value = 255 - rssi; 991 | value /= 2; 992 | value += 74; 993 | } 994 | else 995 | { 996 | value = rssi / 2; 997 | value += 74; 998 | } 999 | return (value); 1000 | } 1001 | 1002 | // bool IthoCC1101::checkID(const uint8_t *id) 1003 | // { 1004 | // for (uint8_t i = 0; i < 3; i++) 1005 | // if (id[i] != inIthoPacket.deviceId2[i]) 1006 | // return false; 1007 | // return true; 1008 | // } 1009 | 1010 | String IthoCC1101::getLastIDstr(bool ashex) 1011 | { 1012 | String str; 1013 | static int id[3]; 1014 | id[0] = inIthoPacket.deviceId2 >> 16 & 0xFF; 1015 | id[1] = inIthoPacket.deviceId2 >> 8 & 0xFF; 1016 | id[2] = inIthoPacket.deviceId2 & 0xFF; 1017 | 1018 | for (uint8_t i = 0; i < 3; i++) 1019 | { 1020 | if (ashex) 1021 | str += String(id[i], HEX); 1022 | else 1023 | str += String(id[i]); 1024 | if (i < 2) 1025 | str += ","; 1026 | } 1027 | return str; 1028 | } 1029 | 1030 | int *IthoCC1101::getLastID() 1031 | { 1032 | static int id[3]; 1033 | id[0] = inIthoPacket.deviceId2 >> 16 & 0xFF; 1034 | id[1] = inIthoPacket.deviceId2 >> 8 & 0xFF; 1035 | id[2] = inIthoPacket.deviceId2 & 0xFF; 1036 | 1037 | return id; 1038 | } 1039 | 1040 | String IthoCC1101::getLastMessagestr(bool ashex) 1041 | { 1042 | String str = "Length=" + String(inMessage.length) + "."; 1043 | for (uint8_t i = 0; i < inMessage.length; i++) 1044 | { 1045 | if (ashex) 1046 | str += String(inMessage.data[i], HEX); 1047 | else 1048 | str += String(inMessage.data[i]); 1049 | if (i < inMessage.length - 1) 1050 | str += ":"; 1051 | } 1052 | return str; 1053 | } 1054 | 1055 | String IthoCC1101::LastMessageDecoded() 1056 | { 1057 | 1058 | String str; 1059 | 1060 | if (inIthoPacket.length > 11) 1061 | { 1062 | 1063 | char bufhead[10]{}; 1064 | snprintf(bufhead, sizeof(bufhead), "H:%02X ", inIthoPacket.header); 1065 | str += String(bufhead); 1066 | 1067 | str += String(MsgType[(inIthoPacket.header >> 4) & 0x3]); 1068 | 1069 | str += " P0:"; 1070 | if (inIthoPacket.header & 2) 1071 | { 1072 | char buf[10]{}; 1073 | snprintf(buf, sizeof(buf), "%02X ", inIthoPacket.param0); 1074 | str += String(buf); 1075 | } 1076 | else 1077 | { 1078 | str += "--"; 1079 | } 1080 | 1081 | str += " P1:"; 1082 | if (inIthoPacket.header & 1) 1083 | { 1084 | char buf[10]{}; 1085 | snprintf(buf, sizeof(buf), "%02X ", inIthoPacket.param1); 1086 | str += String(buf); 1087 | } 1088 | else 1089 | { 1090 | str += "--"; 1091 | } 1092 | 1093 | if (inIthoPacket.deviceId0 == 0) 1094 | { 1095 | str += " --,--,--"; 1096 | } 1097 | else 1098 | { 1099 | str += " "; 1100 | char buf[10]{}; 1101 | snprintf(buf, sizeof(buf), "%02X,%02X,%02X", inIthoPacket.deviceId0 >> 16 & 0xFF, inIthoPacket.deviceId0 >> 8 & 0xFF, inIthoPacket.deviceId0 & 0xFF); 1102 | str += String(buf); 1103 | } 1104 | if (inIthoPacket.deviceId1 == 0) 1105 | { 1106 | str += " --,--,--"; 1107 | } 1108 | else 1109 | { 1110 | str += " "; 1111 | char buf[10]{}; 1112 | snprintf(buf, sizeof(buf), "%02X,%02X,%02X", inIthoPacket.deviceId1 >> 16 & 0xFF, inIthoPacket.deviceId1 >> 8 & 0xFF, inIthoPacket.deviceId1 & 0xFF); 1113 | str += String(buf); 1114 | } 1115 | if (inIthoPacket.deviceId2 == 0) 1116 | { 1117 | str += " --,--,--"; 1118 | } 1119 | else 1120 | { 1121 | str += " "; 1122 | char buf[10]{}; 1123 | snprintf(buf, sizeof(buf), "%02X,%02X,%02X", inIthoPacket.deviceId2 >> 16 & 0xFF, inIthoPacket.deviceId2 >> 8 & 0xFF, inIthoPacket.deviceId2 & 0xFF); 1124 | str += String(buf); 1125 | } 1126 | 1127 | str += " "; 1128 | 1129 | char buf[10]{}; 1130 | snprintf(buf, sizeof(buf), "%04X", inIthoPacket.opcode); 1131 | str += String(buf); 1132 | 1133 | str += " "; 1134 | snprintf(buf, sizeof(buf), "%02X", inIthoPacket.len); 1135 | str += String(buf); 1136 | str += ":"; 1137 | 1138 | for (int i = inIthoPacket.payloadPos; i < inIthoPacket.payloadPos + inIthoPacket.len; i++) 1139 | { 1140 | snprintf(buf, sizeof(buf), "%02X", inIthoPacket.dataDecoded[i]); 1141 | str += String(buf); 1142 | if (i < inIthoPacket.payloadPos + inIthoPacket.len - 1) 1143 | { 1144 | str += ","; 1145 | } 1146 | } 1147 | str += "\n"; 1148 | } 1149 | else 1150 | { 1151 | for (uint8_t i = 0; i < inIthoPacket.length; i++) 1152 | { 1153 | char buf[10]{}; 1154 | snprintf(buf, sizeof(buf), "%02X", inIthoPacket.dataDecoded[i]); 1155 | str += String(buf); 1156 | if (i < inIthoPacket.length - 1) 1157 | str += ","; 1158 | } 1159 | } 1160 | str += "\n"; 1161 | return str; 1162 | } 1163 | 1164 | bool IthoCC1101::addRFDevice(uint8_t byte0, uint8_t byte1, uint8_t byte2, RemoteTypes remType, bool bidirectional) 1165 | { 1166 | uint32_t tempID = byte0 << 16 | byte1 << 8 | byte2; 1167 | return addRFDevice(tempID, remType, bidirectional); 1168 | } 1169 | bool IthoCC1101::addRFDevice(uint32_t ID, RemoteTypes remType, bool bidirectional) 1170 | { 1171 | if (!bindAllowed) 1172 | return false; 1173 | if (checkRFDevice(ID)) 1174 | return false; // device already registered 1175 | 1176 | for (auto &item : ithoRF.device) 1177 | { 1178 | if (item.deviceId == 0) 1179 | { // pick the first available slot 1180 | item.deviceId = ID; 1181 | item.remType = remType; 1182 | item.bidirectional = bidirectional; 1183 | ithoRF.count++; 1184 | return true; 1185 | } 1186 | } 1187 | return false; 1188 | } 1189 | 1190 | bool IthoCC1101::updateRFDeviceID(uint8_t byte0, uint8_t byte1, uint8_t byte2, uint8_t remote_index) 1191 | { 1192 | uint32_t tempID = byte0 << 16 | byte1 << 8 | byte2; 1193 | return updateRFDeviceID(tempID, remote_index); 1194 | } 1195 | 1196 | bool IthoCC1101::updateRFDeviceID(uint32_t ID, uint8_t remote_index) 1197 | { 1198 | if (remote_index > MAX_NUM_OF_REMOTES - 1) 1199 | return false; 1200 | 1201 | ithoRF.device[remote_index].deviceId = ID; 1202 | 1203 | return true; 1204 | } 1205 | bool IthoCC1101::updateRFDeviceType(RemoteTypes deviceType, uint8_t remote_index) 1206 | { 1207 | if (remote_index > MAX_NUM_OF_REMOTES - 1) 1208 | return false; 1209 | 1210 | ithoRF.device[remote_index].remType = deviceType; 1211 | 1212 | return true; 1213 | } 1214 | 1215 | bool IthoCC1101::removeRFDevice(uint8_t byte0, uint8_t byte1, uint8_t byte2) 1216 | { 1217 | uint32_t tempID = byte0 << 16 | byte1 << 8 | byte2; 1218 | return removeRFDevice(tempID); 1219 | } 1220 | 1221 | bool IthoCC1101::removeRFDevice(uint32_t ID) 1222 | { 1223 | if (!bindAllowed) 1224 | return false; 1225 | if (!checkRFDevice(ID)) 1226 | return false; // device not registered 1227 | 1228 | for (auto &item : ithoRF.device) 1229 | { 1230 | if (item.deviceId == ID) 1231 | { 1232 | item.deviceId = 0; 1233 | // strlcpy(item.name, "", sizeof(item.name)); 1234 | item.remType = RemoteTypes::UNSETTYPE; 1235 | item.lastCommand = IthoUnknown; 1236 | item.timestamp = (time_t)0; 1237 | item.co2 = 0xEFFF; 1238 | item.temp = 0xEFFF; 1239 | item.hum = 0xEFFF; 1240 | item.dewpoint = 0xEFFF; 1241 | item.battery = 0xEFFF; 1242 | ithoRF.count--; 1243 | return true; 1244 | } 1245 | } 1246 | 1247 | return false; // not found 1248 | } 1249 | 1250 | bool IthoCC1101::checkRFDevice(uint8_t byte0, uint8_t byte1, uint8_t byte2) 1251 | { 1252 | uint32_t tempID = byte0 << 16 | byte1 << 8 | byte2; 1253 | return checkRFDevice(tempID); 1254 | } 1255 | 1256 | bool IthoCC1101::checkRFDevice(uint32_t ID) 1257 | { 1258 | for (auto &item : ithoRF.device) 1259 | { 1260 | if (item.deviceId == ID) 1261 | { 1262 | return true; 1263 | } 1264 | } 1265 | return false; 1266 | } 1267 | void IthoCC1101::setRFDeviceBidirectional(uint8_t remote_index, bool bidirectional) 1268 | { 1269 | if (remote_index > MAX_NUM_OF_REMOTES - 1) 1270 | return; 1271 | 1272 | ithoRF.device[remote_index].bidirectional = bidirectional; 1273 | } 1274 | bool IthoCC1101::getRFDeviceBidirectional(uint8_t remote_index) 1275 | { 1276 | if (remote_index > MAX_NUM_OF_REMOTES - 1) 1277 | return false; 1278 | 1279 | return ithoRF.device[remote_index].bidirectional; 1280 | } 1281 | bool IthoCC1101::getRFDeviceBidirectionalByID(int32_t ID) 1282 | { 1283 | int8_t remote_index = getRemoteIndexByID(ID); 1284 | 1285 | if (remote_index >= 0) 1286 | { 1287 | return ithoRF.device[remote_index].bidirectional; 1288 | } 1289 | return false; 1290 | } 1291 | 1292 | int8_t IthoCC1101::getRemoteIndexByID(int32_t ID) 1293 | { 1294 | int8_t index = 0; 1295 | for (auto &item : ithoRF.device) 1296 | { 1297 | if (item.deviceId == ID) 1298 | { 1299 | return index; 1300 | } 1301 | index++; 1302 | } 1303 | return -1; 1304 | } 1305 | 1306 | void IthoCC1101::handleBind() 1307 | { 1308 | uint32_t tempID = 0; 1309 | if (inIthoPacket.deviceId0 != 0) 1310 | tempID = inIthoPacket.deviceId0; 1311 | else if (inIthoPacket.deviceId2 != 0) 1312 | tempID = inIthoPacket.deviceId2; 1313 | else 1314 | return; 1315 | if (checkIthoCommand(&inIthoPacket, ithoMessageLeaveCommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageAUTORFTLeaveCommandBytes)) 1316 | { 1317 | inIthoPacket.command = IthoLeave; 1318 | if (bindAllowed) 1319 | { 1320 | removeRFDevice(tempID); 1321 | } 1322 | } 1323 | else if (checkIthoCommand(&inIthoPacket, ithoMessageCVERFTJoinCommandBytes)) 1324 | { 1325 | inIthoPacket.command = IthoJoin; 1326 | inIthoPacket.remType = RemoteTypes::RFTCVE; 1327 | if (bindAllowed) 1328 | { 1329 | addRFDevice(tempID, RemoteTypes::RFTCVE); 1330 | } 1331 | } 1332 | else if (checkIthoCommand(&inIthoPacket, ithoMessageAUTORFTJoinCommandBytes)) 1333 | { 1334 | inIthoPacket.command = IthoJoin; 1335 | inIthoPacket.remType = RemoteTypes::RFTAUTO; 1336 | if (bindAllowed) 1337 | { 1338 | addRFDevice(tempID, RemoteTypes::RFTAUTO); 1339 | } 1340 | } 1341 | else if (checkIthoCommand(&inIthoPacket, ithoMessageAUTORFTNJoinCommandBytes)) 1342 | { 1343 | inIthoPacket.command = IthoJoin; 1344 | inIthoPacket.remType = RemoteTypes::RFTAUTON; 1345 | if (bindAllowed) 1346 | { 1347 | addRFDevice(tempID, RemoteTypes::RFTAUTON); 1348 | } 1349 | // if (getRFDeviceBidirectionalByID(tempID)) 1350 | // { 1351 | // joinreply_test = 1; 1352 | // sendTries = 1; 1353 | // delay(10); 1354 | // sendJoinReply(tempID); 1355 | // delay(10); 1356 | // send10E0(); 1357 | // sendTries = 3; 1358 | // } 1359 | // else 1360 | // { 1361 | // joinreply_test = 0; 1362 | // } 1363 | } 1364 | else if (checkIthoCommand(&inIthoPacket, ithoMessageDFJoinCommandBytes)) 1365 | { 1366 | inIthoPacket.command = IthoJoin; 1367 | inIthoPacket.remType = RemoteTypes::DEMANDFLOW; 1368 | if (bindAllowed) 1369 | { 1370 | addRFDevice(tempID, RemoteTypes::DEMANDFLOW); 1371 | } 1372 | } 1373 | else if (checkIthoCommand(&inIthoPacket, ithoMessageCO2JoinCommandBytes)) 1374 | { 1375 | inIthoPacket.command = IthoJoin; 1376 | inIthoPacket.remType = RemoteTypes::RFTCO2; 1377 | if (bindAllowed) 1378 | { 1379 | addRFDevice(tempID, RemoteTypes::RFTCO2); 1380 | } 1381 | // TODO: handle join handshake 1382 | } 1383 | else if (checkIthoCommand(&inIthoPacket, ithoMessageRVJoinCommandBytes)) 1384 | { 1385 | inIthoPacket.command = IthoJoin; 1386 | inIthoPacket.remType = RemoteTypes::RFTRV; 1387 | if (bindAllowed) 1388 | { 1389 | addRFDevice(tempID, RemoteTypes::RFTRV); 1390 | } 1391 | // TODO: handle join handshake 1392 | } 1393 | else if (checkIthoCommand(&inIthoPacket, ithoMessagePIRJoinCommandBytes)) 1394 | { 1395 | inIthoPacket.command = IthoJoin; 1396 | inIthoPacket.remType = RemoteTypes::RFTPIR; 1397 | if (bindAllowed) 1398 | { 1399 | addRFDevice(tempID, RemoteTypes::RFTPIR); 1400 | } 1401 | } 1402 | else if (checkIthoCommand(&inIthoPacket, orconMessageJoinCommandBytes)) 1403 | { 1404 | inIthoPacket.command = IthoJoin; 1405 | inIthoPacket.remType = RemoteTypes::ORCON15LF01; 1406 | if (bindAllowed) 1407 | { 1408 | addRFDevice(tempID, RemoteTypes::ORCON15LF01); 1409 | } 1410 | // TODO: handle join handshake 1411 | } 1412 | 1413 | for (auto &item : ithoRF.device) 1414 | { 1415 | if (item.deviceId == tempID) 1416 | { 1417 | item.lastCommand = inIthoPacket.command; 1418 | } 1419 | } 1420 | } 1421 | 1422 | void IthoCC1101::handleLevel() 1423 | { 1424 | RemoteTypes remtype = RemoteTypes::UNSETTYPE; 1425 | uint32_t tempID = 0; 1426 | if (inIthoPacket.deviceId0 != 0) 1427 | tempID = inIthoPacket.deviceId0; 1428 | else if (inIthoPacket.deviceId2 != 0) 1429 | tempID = inIthoPacket.deviceId2; 1430 | else 1431 | return; 1432 | 1433 | if (!allowAll) 1434 | { 1435 | if (!checkRFDevice(tempID)) 1436 | return; 1437 | } 1438 | for (auto &item : ithoRF.device) 1439 | { 1440 | if (item.deviceId == tempID) 1441 | { 1442 | remtype = item.remType; 1443 | } 1444 | } 1445 | 1446 | if (remtype == RemoteTypes::ORCON15LF01) 1447 | { 1448 | if (checkIthoCommand(&inIthoPacket, orconMessageAwayCommandBytes)) 1449 | { 1450 | inIthoPacket.command = IthoAway; 1451 | } 1452 | else if (checkIthoCommand(&inIthoPacket, orconMessageAutoCommandBytes)) 1453 | { 1454 | inIthoPacket.command = IthoAuto; 1455 | } 1456 | else if (checkIthoCommand(&inIthoPacket, orconMessageButton1CommandBytes)) 1457 | { 1458 | inIthoPacket.command = IthoLow; 1459 | } 1460 | else if (checkIthoCommand(&inIthoPacket, orconMessageButton2CommandBytes)) 1461 | { 1462 | inIthoPacket.command = IthoMedium; 1463 | } 1464 | else if (checkIthoCommand(&inIthoPacket, orconMessageButton3CommandBytes)) 1465 | { 1466 | inIthoPacket.command = IthoHigh; 1467 | } 1468 | } 1469 | else 1470 | { 1471 | if (checkIthoCommand(&inIthoPacket, ithoMessageLowCommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageAUTORFTLowCommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageDFLowCommandBytes)) 1472 | { 1473 | inIthoPacket.command = IthoLow; 1474 | } 1475 | else if (checkIthoCommand(&inIthoPacket, ithoMessageMediumCommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageRV_CO2MediumCommandBytes)) 1476 | { 1477 | inIthoPacket.command = IthoMedium; 1478 | } 1479 | else if (checkIthoCommand(&inIthoPacket, ithoMessageHighCommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageAUTORFTHighCommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageDFHighCommandBytes)) 1480 | { 1481 | inIthoPacket.command = IthoHigh; 1482 | } 1483 | else if (checkIthoCommand(&inIthoPacket, ithoMessageAwayCommandBytes)) 1484 | { 1485 | inIthoPacket.command = IthoAway; 1486 | } 1487 | else if (checkIthoCommand(&inIthoPacket, ithoMessageRV_CO2AutoCommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageAUTORFTAutoCommandBytes)) 1488 | { 1489 | inIthoPacket.command = IthoAuto; 1490 | } 1491 | else if (checkIthoCommand(&inIthoPacket, ithoMessageRV_CO2AutoNightCommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageAUTORFTAutoNightCommandBytes)) 1492 | { 1493 | inIthoPacket.command = IthoAutoNight; 1494 | } 1495 | } 1496 | 1497 | time_t now; 1498 | time(&now); 1499 | 1500 | for (auto &item : ithoRF.device) 1501 | { 1502 | if (item.deviceId == tempID) 1503 | { 1504 | item.lastCommand = inIthoPacket.command; 1505 | item.timestamp = now; 1506 | } 1507 | } 1508 | } 1509 | 1510 | void IthoCC1101::handleTimer() 1511 | { 1512 | RemoteTypes remtype = RemoteTypes::UNSETTYPE; 1513 | uint32_t tempID = 0; 1514 | if (inIthoPacket.deviceId0 != 0) 1515 | tempID = inIthoPacket.deviceId0; 1516 | else if (inIthoPacket.deviceId2 != 0) 1517 | tempID = inIthoPacket.deviceId2; 1518 | else 1519 | return; 1520 | 1521 | if (!allowAll) 1522 | { 1523 | if (!checkRFDevice(tempID)) 1524 | return; 1525 | } 1526 | for (auto &item : ithoRF.device) 1527 | { 1528 | if (item.deviceId == tempID) 1529 | { 1530 | remtype = item.remType; 1531 | } 1532 | } 1533 | if (remtype == RemoteTypes::ORCON15LF01) 1534 | { 1535 | if (checkIthoCommand(&inIthoPacket, orconMessageTimer1CommandBytes)) 1536 | { 1537 | inIthoPacket.command = IthoTimer1; 1538 | } 1539 | else if (checkIthoCommand(&inIthoPacket, orconMessageTimer2CommandBytes)) 1540 | { 1541 | inIthoPacket.command = IthoTimer2; 1542 | } 1543 | else if (checkIthoCommand(&inIthoPacket, orconMessageTimer3CommandBytes)) 1544 | { 1545 | inIthoPacket.command = IthoTimer3; 1546 | } 1547 | } 1548 | else 1549 | { 1550 | if (checkIthoCommand(&inIthoPacket, ithoMessageTimer1CommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageAUTORFTTimer1CommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageRV_CO2Timer1CommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageDFTimer1CommandBytes)) 1551 | { 1552 | inIthoPacket.command = IthoTimer1; 1553 | } 1554 | else if (checkIthoCommand(&inIthoPacket, ithoMessageTimer2CommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageAUTORFTTimer2CommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageRV_CO2Timer2CommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageDFTimer2CommandBytes)) 1555 | { 1556 | inIthoPacket.command = IthoTimer2; 1557 | } 1558 | else if (checkIthoCommand(&inIthoPacket, ithoMessageTimer3CommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageAUTORFTTimer3CommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageRV_CO2Timer3CommandBytes) || checkIthoCommand(&inIthoPacket, ithoMessageDFTimer3CommandBytes)) 1559 | { 1560 | inIthoPacket.command = IthoTimer3; 1561 | } 1562 | else if (checkIthoCommand(&inIthoPacket, ithoMessageDFCook30CommandBytes)) 1563 | { 1564 | inIthoPacket.command = IthoCook30; 1565 | } 1566 | else if (checkIthoCommand(&inIthoPacket, ithoMessageDFCook60CommandBytes)) 1567 | { 1568 | inIthoPacket.command = IthoCook60; 1569 | } 1570 | } 1571 | 1572 | time_t now; 1573 | time(&now); 1574 | 1575 | for (auto &item : ithoRF.device) 1576 | { 1577 | if (item.deviceId == tempID) 1578 | { 1579 | item.lastCommand = inIthoPacket.command; 1580 | item.timestamp = now; 1581 | } 1582 | } 1583 | } 1584 | void IthoCC1101::handleStatus() 1585 | { 1586 | uint32_t tempID = 0; 1587 | if (inIthoPacket.deviceId0 != 0) 1588 | tempID = inIthoPacket.deviceId0; 1589 | else if (inIthoPacket.deviceId2 != 0) 1590 | tempID = inIthoPacket.deviceId2; 1591 | else 1592 | return; 1593 | 1594 | if (!checkRFDevice(tempID)) 1595 | return; 1596 | 1597 | for (auto &item : ithoRF.device) 1598 | { 1599 | if (item.deviceId == tempID) 1600 | { 1601 | // store last command 1602 | } 1603 | } 1604 | } 1605 | void IthoCC1101::handleRemotestatus() 1606 | { 1607 | uint32_t tempID = 0; 1608 | if (inIthoPacket.deviceId0 != 0) 1609 | tempID = inIthoPacket.deviceId0; 1610 | else if (inIthoPacket.deviceId2 != 0) 1611 | tempID = inIthoPacket.deviceId2; 1612 | else 1613 | return; 1614 | 1615 | if (!checkRFDevice(tempID)) 1616 | return; 1617 | 1618 | for (auto &item : ithoRF.device) 1619 | { 1620 | if (item.deviceId == tempID) 1621 | { 1622 | // store last command 1623 | } 1624 | } 1625 | } 1626 | void IthoCC1101::handleTemphum() 1627 | { 1628 | /* 1629 | message length: 6 1630 | message opcode: 0x12A0 1631 | byte[0] : unknown 1632 | byte[1] : humidity 1633 | bytes[2:3] : temperature 1634 | bytes[4:5] : dewpoint temperature 1635 | 1636 | */ 1637 | if (inIthoPacket.error > 0) 1638 | return; 1639 | uint32_t tempID = 0; 1640 | if (inIthoPacket.deviceId0 != 0) 1641 | tempID = inIthoPacket.deviceId0; 1642 | else if (inIthoPacket.deviceId2 != 0) 1643 | tempID = inIthoPacket.deviceId2; 1644 | else 1645 | return; 1646 | 1647 | if (!checkRFDevice(tempID)) 1648 | return; 1649 | int32_t tempHum = inIthoPacket.dataDecoded[inIthoPacket.payloadPos + 1]; 1650 | int32_t tempTemp = inIthoPacket.dataDecoded[inIthoPacket.payloadPos + 2] << 8 | inIthoPacket.dataDecoded[inIthoPacket.payloadPos + 3]; 1651 | int32_t tempDewp = inIthoPacket.dataDecoded[inIthoPacket.payloadPos + 4] << 8 | inIthoPacket.dataDecoded[inIthoPacket.payloadPos + 5]; 1652 | 1653 | for (auto &item : ithoRF.device) 1654 | { 1655 | if (item.deviceId == tempID) 1656 | { 1657 | item.temp = tempTemp; 1658 | item.hum = tempHum; 1659 | item.dewpoint = tempDewp; 1660 | } 1661 | } 1662 | } 1663 | 1664 | void IthoCC1101::handleCo2() 1665 | { 1666 | /* 1667 | message length: 3 1668 | message opcode: 0x1298 1669 | byte[0] : unknown 1670 | bytes[1:2] : CO2 level 1671 | 1672 | */ 1673 | if (inIthoPacket.error > 0) 1674 | return; 1675 | uint32_t tempID = 0; 1676 | if (inIthoPacket.deviceId0 != 0) 1677 | tempID = inIthoPacket.deviceId0; 1678 | else if (inIthoPacket.deviceId2 != 0) 1679 | tempID = inIthoPacket.deviceId2; 1680 | else 1681 | return; 1682 | 1683 | if (!checkRFDevice(tempID)) 1684 | return; 1685 | int32_t tempVal = inIthoPacket.dataDecoded[inIthoPacket.payloadPos + 1] << 8 | inIthoPacket.dataDecoded[inIthoPacket.payloadPos + 2]; 1686 | 1687 | for (auto &item : ithoRF.device) 1688 | { 1689 | if (item.deviceId == tempID) 1690 | { 1691 | item.co2 = tempVal; 1692 | } 1693 | } 1694 | } 1695 | void IthoCC1101::handleBattery() 1696 | { 1697 | /* 1698 | message length: 3 1699 | message opcode: 0x1060 1700 | byte[0] : zone_id 1701 | byte[1] : battery level percentage (0xFF = no percentage present) 1702 | byte[2] : battery state (0x01 OK, 0x00 LOW) 1703 | 1704 | */ 1705 | if (inIthoPacket.error > 0) 1706 | return; 1707 | uint32_t tempID = 0; 1708 | if (inIthoPacket.deviceId0 != 0) 1709 | tempID = inIthoPacket.deviceId0; 1710 | else if (inIthoPacket.deviceId2 != 0) 1711 | tempID = inIthoPacket.deviceId2; 1712 | else 1713 | return; 1714 | 1715 | if (!checkRFDevice(tempID)) 1716 | return; 1717 | int32_t tempVal = 10; 1718 | if (inIthoPacket.dataDecoded[inIthoPacket.payloadPos + 1] == 0xFF) 1719 | { 1720 | if (inIthoPacket.dataDecoded[inIthoPacket.payloadPos + 2] == 0x01) 1721 | tempVal = 100; 1722 | } 1723 | else 1724 | { 1725 | tempVal = (int32_t)inIthoPacket.dataDecoded[inIthoPacket.payloadPos + 1] / 2; 1726 | } 1727 | for (auto &item : ithoRF.device) 1728 | { 1729 | if (item.deviceId == tempID) 1730 | { 1731 | item.battery = tempVal; 1732 | } 1733 | } 1734 | } 1735 | 1736 | void IthoCC1101::handlePIR() 1737 | { 1738 | /* 1739 | message length: 3 1740 | message opcode: 0x2E10 1741 | byte[0] : unknown 1742 | byte[1] : movement (1) / no movement (0) 1743 | byte[2] : unknown 1744 | 1745 | */ 1746 | if (inIthoPacket.error > 0) 1747 | return; 1748 | uint32_t tempID = 0; 1749 | if (inIthoPacket.deviceId0 != 0) 1750 | tempID = inIthoPacket.deviceId0; 1751 | else if (inIthoPacket.deviceId2 != 0) 1752 | tempID = inIthoPacket.deviceId2; 1753 | else 1754 | return; 1755 | 1756 | if (!checkRFDevice(tempID)) 1757 | return; 1758 | int32_t tempVal = inIthoPacket.dataDecoded[inIthoPacket.payloadPos + 1]; 1759 | 1760 | for (auto &item : ithoRF.device) 1761 | { 1762 | if (item.deviceId == tempID) 1763 | { 1764 | item.pir = tempVal; 1765 | } 1766 | } 1767 | } 1768 | 1769 | const char *IthoCC1101::rem_cmd_to_name(IthoCommand code) 1770 | { 1771 | size_t i; 1772 | 1773 | for (i = 0; i < sizeof(remote_command_msg_table) / sizeof(remote_command_msg_table[0]); ++i) 1774 | { 1775 | if (remote_command_msg_table[i].code == code) 1776 | { 1777 | return remote_command_msg_table[i].msg; 1778 | } 1779 | } 1780 | return remote_unknown_msg; 1781 | } --------------------------------------------------------------------------------