/* Author: Klusjesman, supersjimmie, modified and reworked by arjenhiemstra */ // #define DEBUG 0 // #define BYTE_TO_BINARY_PATTERN "%c,%c,%c,%c,%c,%c,%c,%c," /*#define BYTE_TO_BINARY(byte) \ (byte & 0x80 ? '1' : '0'), \ (byte & 0x40 ? '1' : '0'), \ (byte & 0x20 ? '1' : '0'), \ (byte & 0x10 ? '1' : '0'), \ (byte & 0x08 ? '1' : '0'), \ (byte & 0x04 ? '1' : '0'), \ (byte & 0x02 ? '1' : '0'), \ (byte & 0x01 ? '1' : '0')*/ #include "IthoCC1101.h" #include #include #include // #define CRC_FILTER ////original sync byte pattern // #define STARTBYTE 6 //relevant data starts 6 bytes after the sync pattern bytes 170/171 // #define SYNC1 170 // #define SYNC0 171 // #define MDMCFG2 0x02 //16bit sync word / 16bit specific ////alternative sync byte pattern (filter much more non-itho messages out. Maybe too strict? Testing needed. // #define STARTBYTE 0 //relevant data starts 0 bytes after the sync pattern bytes 179/42/171/42 // #define SYNC1 187 //byte11 = 179, byte13 = 171 with SYNC1 = 163, 179 and 171 differ only by 1 bit // #define SYNC0 42 // #define MDMCFG2 0x03 //32bit sync word / 30bit specific // alternative sync byte pattern #define STARTBYTE 2 // relevant data starts 2 bytes after the sync pattern bytes 179/42 #define SYNC1 179 #define SYNC0 42 #define MDMCFG2 0x02 // 16bit sync word / 16bit specific // default constructor IthoCC1101::IthoCC1101(int8_t CSpin, int8_t MISOpin, SPIClass& spi, uint8_t counter, uint8_t sendTries) : CC1101(CSpin, MISOpin, spi) { this->outIthoPacket.counter = counter; this->sendTries = sendTries; this->outIthoPacket.deviceId[0] = 33; this->outIthoPacket.deviceId[1] = 66; this->outIthoPacket.deviceId[2] = 99; this->outIthoPacket.deviceType = 22; } // IthoCC1101 // default destructor IthoCC1101::~IthoCC1101() {} // ~IthoCC1101 void IthoCC1101::enableOrcon(bool state) { _enableOrcon = state; } void IthoCC1101::initSendMessage(uint8_t len) { // finishTransfer(); writeCommand(CC1101_SIDLE); delayMicroseconds(1); writeRegister(CC1101_IOCFG0, 0x2E); delayMicroseconds(1); writeRegister(CC1101_IOCFG1, 0x2E); delayMicroseconds(1); writeCommand(CC1101_SIDLE); writeCommand(CC1101_SPWD); delayMicroseconds(2); /* Configuration reverse engineered from remote print. The commands below are used by IthoDaalderop. Base frequency 868.299866MHz Channel 0 Channel spacing 199.951172kHz Carrier frequency 868.299866MHz Xtal frequency 26.000000MHz Data rate 38.3835kBaud Manchester disabled Modulation 2-FSK Deviation 50.781250kHz TX power ? PA ramping enabled Whitening disabled */ writeCommand(CC1101_SRES); delayMicroseconds(1); writeRegister(CC1101_IOCFG0, 0x2E); // High impedance (3-state) writeRegister(CC1101_FREQ2, 0x21); // 00100001 878MHz-927.8MHz writeRegister(CC1101_FREQ1, 0x65); // 01100101 writeRegister(CC1101_FREQ0, 0x6A); // 01101010 writeRegister(CC1101_MDMCFG4, 0x5A); // difference compared to message1 writeRegister(CC1101_MDMCFG3, 0x83); // difference compared to message1 writeRegister(CC1101_MDMCFG2, 0x00); // 00000000 2-FSK, no manchester encoding/decoding, no preamble/sync writeRegister(CC1101_MDMCFG1, 0x22); // 00100010 writeRegister(CC1101_MDMCFG0, 0xF8); // 11111000 writeRegister(CC1101_CHANNR, 0x00); // 00000000 writeRegister(CC1101_DEVIATN, 0x50); // difference compared to message1 writeRegister(CC1101_FREND0, 0x17); // 00010111 use index 7 in PA table writeRegister(CC1101_MCSM0, 0x18); // 00011000 PO timeout Approx. 146microseconds - 171microseconds, Auto calibrate When going from // IDLE to RX or TX (or FSTXON) writeRegister(CC1101_FSCAL3, 0xA9); // 10101001 writeRegister(CC1101_FSCAL2, 0x2A); // 00101010 writeRegister(CC1101_FSCAL1, 0x00); // 00000000 writeRegister(CC1101_FSCAL0, 0x11); // 00010001 writeRegister(CC1101_FSTEST, 0x59); // 01011001 For test only. Do not write to this register. writeRegister(CC1101_TEST2, 0x81); // 10000001 For test only. Do not write to this register. writeRegister(CC1101_TEST1, 0x35); // 00110101 For test only. Do not write to this register. writeRegister(CC1101_TEST0, 0x0B); // 00001011 For test only. Do not write to this register. writeRegister(CC1101_PKTCTRL0, 0x12); // 00010010 Enable infinite length packets, CRC disabled, Turn data whitening off, Serial // Synchronous mode writeRegister(CC1101_ADDR, 0x00); // 00000000 writeRegister(CC1101_PKTLEN, 0xFF); // 11111111 //Not used, no hardware packet handling // 0x6F,0x26,0x2E,0x8C,0x87,0xCD,0xC7,0xC0 writeBurstRegister(CC1101_PATABLE | CC1101_WRITE_BURST, (uint8_t *)ithoPaTableSend, 8); // difference, message1 sends a STX here writeCommand(CC1101_SIDLE); writeCommand(CC1101_SIDLE); writeRegister(CC1101_MDMCFG4, 0x5A); // difference compared to message1 writeRegister(CC1101_MDMCFG3, 0x83); // difference compared to message1 writeRegister(CC1101_DEVIATN, 0x50); // difference compared to message1 writeRegister(CC1101_IOCFG0, 0x2D); // GDO0_Z_EN_N. When this output is 0, GDO0 is configured as input (for serial TX data). writeRegister(CC1101_IOCFG1, 0x0B); // Serial Clock. Synchronous to the data in synchronous serial mode. writeCommand(CC1101_STX); writeCommand(CC1101_SIDLE); writeRegister(CC1101_MDMCFG4, 0x5A); // difference compared to message1 writeRegister(CC1101_MDMCFG3, 0x83); // difference compared to message1 writeRegister(CC1101_DEVIATN, 0x50); // difference compared to message1 // writeRegister(CC1101_IOCFG0 ,0x2D); //GDO0_Z_EN_N. When this output is 0, GDO0 is configured as input (for serial TX data). // writeRegister(CC1101_IOCFG1 ,0x0B); //Serial Clock. Synchronous to the data in synchronous serial mode. // Itho is using serial mode for transmit. We want to use the TX FIFO with fixed packet length for simplicity. writeRegister(CC1101_IOCFG0, 0x2E); writeRegister(CC1101_IOCFG1, 0x2E); writeRegister(CC1101_PKTCTRL0, 0x00); writeRegister(CC1101_PKTCTRL1, 0x00); writeRegister(CC1101_PKTLEN, len); } void IthoCC1101::finishTransfer() { writeCommand(CC1101_SIDLE); delayMicroseconds(1); writeRegister(CC1101_IOCFG0, 0x2E); writeRegister(CC1101_IOCFG1, 0x2E); writeCommand(CC1101_SIDLE); writeCommand(CC1101_SPWD); } void IthoCC1101::initReceive() { /* Configuration reverse engineered from RFT print. Base frequency 868.299866MHz Channel 0 Channel spacing 199.951172kHz Carrier frequency 868.299866MHz Xtal frequency 26.000000MHz Data rate 38.3835kBaud RX filter BW 325.000000kHz Manchester disabled Modulation 2-FSK Deviation 50.781250kHz TX power 0x6F,0x26,0x2E,0x7F,0x8A,0x84,0xCA,0xC4 PA ramping enabled Whitening disabled */ writeCommand(CC1101_SRES); writeRegister(CC1101_TEST0, 0x09); writeRegister(CC1101_FSCAL2, 0x00); // 0x6F,0x26,0x2E,0x7F,0x8A,0x84,0xCA,0xC4 writeBurstRegister(CC1101_PATABLE | CC1101_WRITE_BURST, (uint8_t *)ithoPaTableReceive, 8); writeCommand(CC1101_SCAL); // wait for calibration to finish uint32_t maxWait = millis() + ITHO_MAX_WAIT; // Wait for max. x seconds while ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER)) != CC1101_MARCSTATE_IDLE && millis() < maxWait) { yield(); } writeRegister(CC1101_FSCAL2, 0x00); writeRegister(CC1101_MCSM0, 0x18); // no auto calibrate writeRegister(CC1101_FREQ2, 0x21); writeRegister(CC1101_FREQ1, 0x65); writeRegister(CC1101_FREQ0, 0x6A); writeRegister(CC1101_IOCFG0, 0x2E); // High impedance (3-state) writeRegister(CC1101_IOCFG2, 0x06); // 0x06 Assert when sync word has been sent / received, and de-asserts at the end of the packet. writeRegister(CC1101_FSCTRL1, 0x06); writeRegister(CC1101_FSCTRL0, 0x00); writeRegister(CC1101_MDMCFG4, 0x5A); writeRegister(CC1101_MDMCFG3, 0x83); writeRegister(CC1101_MDMCFG2, 0x00); // Enable digital DC blocking filter before demodulator, 2-FSK, Disable Manchester // encoding/decoding, No preamble/sync writeRegister(CC1101_MDMCFG1, 0x22); // Disable FEC writeRegister(CC1101_MDMCFG0, 0xF8); writeRegister(CC1101_CHANNR, 0x00); writeRegister(CC1101_DEVIATN, 0x50); writeRegister(CC1101_FREND1, 0x56); writeRegister(CC1101_FREND0, 0x17); writeRegister(CC1101_MCSM0, 0x18); // no auto calibrate writeRegister(CC1101_FOCCFG, 0x16); writeRegister(CC1101_BSCFG, 0x6C); writeRegister(CC1101_AGCCTRL2, 0x43); writeRegister(CC1101_AGCCTRL1, 0x40); writeRegister(CC1101_AGCCTRL0, 0x91); writeRegister(CC1101_FSCAL3, 0xE9); writeRegister(CC1101_FSCAL2, 0x2A); writeRegister(CC1101_FSCAL1, 0x00); writeRegister(CC1101_FSCAL0, 0x11); writeRegister(CC1101_FSTEST, 0x59); writeRegister(CC1101_TEST2, 0x81); writeRegister(CC1101_TEST1, 0x35); writeRegister(CC1101_TEST0, 0x0B); writeRegister(CC1101_PKTCTRL1, 0x04); // No address check, Append two bytes with status RSSI/LQI/CRC OK, writeRegister(CC1101_PKTCTRL0, 0x32); // Infinite packet length mode, CRC disabled for TX and RX, No data whitening, Asynchronous serial // mode, Data in on GDO0 and data out on either of the GDOx pins writeRegister(CC1101_ADDR, 0x00); writeRegister(CC1101_PKTLEN, 0xFF); writeRegister(CC1101_TEST0, 0x09); writeCommand(CC1101_SCAL); // wait for calibration to finish maxWait = millis() + ITHO_MAX_WAIT; // Wait for max. x seconds while ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER)) != CC1101_MARCSTATE_IDLE && millis() < maxWait) { yield(); } writeRegister(CC1101_MCSM0, 0x18); // no auto calibrate writeCommand(CC1101_SIDLE); writeCommand(CC1101_SIDLE); writeRegister(CC1101_MDMCFG2, 0x00); // Enable digital DC blocking filter before demodulator, 2-FSK, Disable Manchester encoding/decoding, // No preamble/sync writeRegister(CC1101_IOCFG0, 0x0D); // Serial Data Output. Used for asynchronous serial mode. writeCommand(CC1101_SRX); maxWait = millis() + ITHO_MAX_WAIT; // Wait for max. x seconds while ((readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER)) != CC1101_MARCSTATE_RX && millis() < maxWait) { yield(); } initReceiveMessage(); } void IthoCC1101::initReceiveMessage() { uint8_t marcState; writeCommand(CC1101_SIDLE); // idle // set datarate writeRegister(CC1101_MDMCFG4, 0x5A); // set kBaud writeRegister(CC1101_MDMCFG3, 0x83); // set kBaud writeRegister(CC1101_DEVIATN, 0x50); // set fifo mode with fixed packet length and sync bytes writeRegister(CC1101_PKTLEN, 63); // 63 bytes message (sync at beginning of message is removed by CC1101) // set fifo mode with fixed packet length and sync bytes writeRegister(CC1101_PKTCTRL0, 0x00); writeRegister(CC1101_SYNC1, SYNC1); writeRegister(CC1101_SYNC0, SYNC0); writeRegister(CC1101_MDMCFG2, MDMCFG2); writeRegister(CC1101_PKTCTRL1, 0x00); writeCommand(CC1101_SRX); // switch to RX state // Check that the RX state has been entered uint32_t maxWait = millis() + ITHO_MAX_WAIT; // Wait for max. x seconds while (((marcState = readRegisterWithSyncProblem(CC1101_MARCSTATE, CC1101_STATUS_REGISTER)) & CC1101_BITS_MARCSTATE) != CC1101_MARCSTATE_RX && millis() < maxWait) { if (marcState == CC1101_MARCSTATE_RXFIFO_OVERFLOW) { // RX_OVERFLOW writeCommand(CC1101_SFRX); // flush RX buffer } } } bool IthoCC1101::checkForNewPacket() { if (receiveData(&inMessage, 63) && parseMessageCommand()) { initReceiveMessage(); return true; } return false; } bool IthoCC1101::parseMessageCommand() { // TODO nl0pvm: make this orcon proof? #if defined(CRC_FILTER) uint8_t mLen = 0; # define SET_MLEN(n) mLen = n; #else // if defined(CRC_FILTER) # define SET_MLEN(n) #endif // if defined(CRC_FILTER) messageDecode(&inMessage, &inIthoPacket); // deviceType of message type? inIthoPacket.deviceType = inIthoPacket.dataDecoded[0]; // deviceID inIthoPacket.deviceId[0] = inIthoPacket.dataDecoded[1]; inIthoPacket.deviceId[1] = inIthoPacket.dataDecoded[2]; inIthoPacket.deviceId[2] = inIthoPacket.dataDecoded[3]; // counter1 inIthoPacket.counter = inIthoPacket.dataDecoded[4]; // determine command inIthoPacket.command = IthoUnknown; // TODO: When enabling CRC_FILTER, most likely commands without SET_MLEN() need that check too if (checkIthoCommand(&inIthoPacket, ithoMessageHighCommandBytes)) { inIthoPacket.command = IthoHigh; SET_MLEN(11) } else if (checkIthoCommand(&inIthoPacket, ithoMessageRVHighCommandBytes)) { inIthoPacket.command = IthoHigh; } else if (checkIthoCommand(&inIthoPacket, ithoMessageMediumCommandBytes)) { inIthoPacket.command = IthoMedium; SET_MLEN(11) } else if (checkIthoCommand(&inIthoPacket, ithoMessageRVMediumCommandBytes)) { inIthoPacket.command = IthoMedium; } else if (checkIthoCommand(&inIthoPacket, ithoMessageLowCommandBytes)) { inIthoPacket.command = IthoLow; SET_MLEN(11) } else if (checkIthoCommand(&inIthoPacket, ithoMessageRVLowCommandBytes)) { inIthoPacket.command = IthoLow; } else if (checkIthoCommand(&inIthoPacket, ithoMessageRVAutoCommandBytes)) { inIthoPacket.command = IthoStandby; } else if (checkIthoCommand(&inIthoPacket, ithoMessageStandByCommandBytes)) { inIthoPacket.command = IthoStandby; SET_MLEN(11) } else if (checkIthoCommand(&inIthoPacket, ithoMessageTimer1CommandBytes)) { inIthoPacket.command = IthoTimer1; SET_MLEN(11) } else if (checkIthoCommand(&inIthoPacket, ithoMessageTimer2CommandBytes)) { inIthoPacket.command = IthoTimer2; SET_MLEN(11) } else if (checkIthoCommand(&inIthoPacket, ithoMessageTimer3CommandBytes)) { inIthoPacket.command = IthoTimer3; SET_MLEN(11) } else if (checkIthoCommand(&inIthoPacket, ithoMessageJoinCommandBytes)) { inIthoPacket.command = IthoJoin; SET_MLEN(20) } else if (checkIthoCommand(&inIthoPacket, ithoMessageJoin2CommandBytes)) { inIthoPacket.command = IthoJoin; SET_MLEN(20) } else if (checkIthoCommand(&inIthoPacket, ithoMessageRVJoinCommandBytes)) { inIthoPacket.command = IthoJoin; } else if (checkIthoCommand(&inIthoPacket, ithoMessageLeaveCommandBytes)) { inIthoPacket.command = IthoLeave; SET_MLEN(14) } else if (_enableOrcon) { if (checkIthoCommand(&inIthoPacket, orconMessageStandByCommandBytes)) { inIthoPacket.command = OrconStandBy; } else if (checkIthoCommand(&inIthoPacket, orconMessageLowCommandBytes)) { inIthoPacket.command = OrconLow; } else if (checkIthoCommand(&inIthoPacket, orconMessageMediumCommandBytes)) { inIthoPacket.command = OrconMedium; } else if (checkIthoCommand(&inIthoPacket, orconMessageFullCommandBytes)) { inIthoPacket.command = OrconHigh; } else if (checkIthoCommand(&inIthoPacket, orconMessageAutoCommandBytes)) { inIthoPacket.command = OrconAuto; } else if (checkIthoCommand(&inIthoPacket, orconMessageTimer0CommandBytes)) { inIthoPacket.command = OrconTimer0; } else if (checkIthoCommand(&inIthoPacket, orconMessageTimer1CommandBytes)) { inIthoPacket.command = OrconTimer1; } else if (checkIthoCommand(&inIthoPacket, orconMessageTimer2CommandBytes)) { inIthoPacket.command = OrconTimer2; } else if (checkIthoCommand(&inIthoPacket, orconMessageTimer3CommandBytes)) { inIthoPacket.command = OrconTimer3; } else if (checkIthoCommand(&inIthoPacket, orconMessageAutoCO2CommandBytes)) { inIthoPacket.command = OrconAutoCO2; } } #undef SET_MLEN #if defined(CRC_FILTER) if ((mLen != 0) && (getCounter2(&inIthoPacket, mLen) != inIthoPacket.dataDecoded[mLen])) { inIthoPacket.command = IthoUnknown; return false; } #endif // if defined(CRC_FILTER) return true; } bool IthoCC1101::checkIthoCommand(IthoPacket *itho, const uint8_t commandBytes[]) { uint8_t offset = 0; // this is quite hacky as not even the opcode is checked for itho. Because of that orcon 31E0 messages are wrongly recognised as itho // standby messages. // TODO nl0pvm: FIX THIS :D // first byte is the header of the message, this determines the structure of the rest of the message // The bits are used as follows <00TTAAPP> // 00 - Unused // TT - Message type // AA - Present DeviceID fields // PP - Present Params if ((itho->deviceType == 28) || (itho->deviceType == 24)) { offset = 2; } // for (int i = 4; i < 6; i++) // for Orcon: the code above makes that only 3 bytes (byte 4, 5 and 6) are checked. That gives false positves for (int i = 0; i < 6; i++) { // this is required for differentiating between Orcon and Itho commands. However I don't know what the reason was to comment this out. // thus this needs to be verified by Itho users if ((i == 2) || (i == 3)) { continue; // skip byte3 and byte4, rft-rv and co2-auto remote device seem to sometimes have a different number there } if ((itho->dataDecoded[i + 5 + offset] != pgm_read_byte(&(commandBytes[i]))) && (itho->dataDecodedChk[i + 5 + offset] != pgm_read_byte(&(commandBytes[i])))) { return false; } } return true; } void IthoCC1101::sendCommand(IthoCommand command, uint8_t srcId[3], uint8_t destId[3]) { CC1101Packet outMessage; uint8_t maxTries = sendTries; uint8_t delaytime = 40; // update itho packet data outIthoPacket.command = command; outIthoPacket.counter += 1; // get message2 bytes switch (command) { case IthoJoin: createMessageJoin(&outIthoPacket, &outMessage); break; case IthoLeave: createMessageLeave(&outIthoPacket, &outMessage); // the leave command needs to be transmitted for 1 second according the manual maxTries = 30; delaytime = 4; break; case OrconStandBy: case OrconLow: case OrconMedium: case OrconHigh: case OrconAuto: case OrconTimer0: case OrconTimer1: case OrconTimer2: case OrconTimer3: case OrconAutoCO2: if (_enableOrcon) { maxTries = 1; createOrconMessageCommand(&outIthoPacket, &outMessage, srcId, destId); } break; default: createMessageCommand(&outIthoPacket, &outMessage); break; } // send messages for (int i = 0; i < maxTries; i++) { // message2 initSendMessage(outMessage.length); sendData(&outMessage); finishTransfer(); delay(delaytime); } // initReceive(); SV - I call this from the ESPEasy plugin to prevent crashes } void IthoCC1101::createMessageStart(IthoPacket *itho, CC1101Packet *packet) { // fixed, set start structure in data buffer manually for (uint8_t i = 0; i < 7; i++) { packet->data[i] = 170; } packet->data[7] = 171; packet->data[8] = 254; packet->data[9] = 0; packet->data[10] = 179; packet->data[11] = 42; packet->data[12] = 171; packet->data[13] = 42; // [start of command specific data] } void IthoCC1101::createOrconMessageCommand(IthoPacket *itho, CC1101Packet *packet, uint8_t srcId[3], uint8_t destId[3]) { // set start message structure createMessageStart(itho, packet); // first byte is the header of the message, this determines the structure of the rest of the message // The bits are used as follows <00TTAAPP> // 00 - Unused // TT - Message type // AA - Present DeviceID fields // PP - Present Params uint8_t header = 0b00011100; itho->dataDecoded[0] = header; // 00TTAAPP // set source deviceID itho->dataDecoded[1] = srcId[0]; itho->dataDecoded[2] = srcId[1]; itho->dataDecoded[3] = srcId[2]; // set destination deviceID itho->dataDecoded[4] = destId[0]; itho->dataDecoded[5] = destId[1]; itho->dataDecoded[6] = destId[2]; const uint8_t *commandBytes = getMessageCommandBytes(itho->command); const uint8_t commandLength = getMessageCommandLength(itho->command); for (uint8_t i = 0; i < commandLength; i++) { itho->dataDecoded[i + 7] = pgm_read_byte(&(commandBytes[i])); } itho->length = 7 + 1 + commandLength; itho->dataDecoded[itho->length - 1] = getCRC(itho, itho->length - 1); itho->length += 1; packet->length = messageEncode(itho, packet) - 2; // delete the last two itho bytes (0x55, 0x95) so we can reuse messageEncode() without // modifications // set compex orcon specific end bytes packet->data[packet->length] = 0xAC; packet->length += 1; packet->data[packet->length] = 0xAA; packet->length += 1; packet->data[packet->length] = 0xBF; packet->length += 1; packet->data[packet->length] = 0x0E; packet->length += 1; } uint8_t IthoCC1101::getCRC(IthoPacket *itho, uint8_t len) { uint8_t val = 0; for (uint8_t i = 0; i < len; i++) { val += itho->dataDecoded[i]; } return 0x100 - (val & 0xFF); } void IthoCC1101::createMessageCommand(IthoPacket *itho, CC1101Packet *packet) { // set start message structure createMessageStart(itho, packet); // set deviceType? (or messageType?), not sure what this is itho->dataDecoded[0] = itho->deviceType; // set deviceID itho->dataDecoded[1] = itho->deviceId[0]; itho->dataDecoded[2] = itho->deviceId[1]; itho->dataDecoded[3] = itho->deviceId[2]; // set counter1 itho->dataDecoded[4] = itho->counter; // set command bytes on dataDecoded[5 - 10] const uint8_t *commandBytes = getMessageCommandBytes(itho->command); for (uint8_t i = 0; i < 6; i++) { itho->dataDecoded[i + 5] = pgm_read_byte(&(commandBytes[i])); } // set counter2 itho->dataDecoded[11] = getCounter2(itho, 11); itho->length = 12; packet->length = messageEncode(itho, packet); packet->length += 1; // set end byte packet->data[packet->length] = 172; packet->length += 1; // set end 'noise' for (uint8_t i = packet->length; i < packet->length + 7; i++) { packet->data[i] = 170; } packet->length += 7; } void IthoCC1101::createMessageJoin(IthoPacket *itho, CC1101Packet *packet) { // set start message structure createMessageStart(itho, packet); // set deviceType? (or messageType?) itho->dataDecoded[0] = itho->deviceType; // set deviceID itho->dataDecoded[1] = itho->deviceId[0]; itho->dataDecoded[2] = itho->deviceId[1]; itho->dataDecoded[3] = itho->deviceId[2]; // set counter1 itho->dataDecoded[4] = itho->counter; // set command bytes on dataDecoded[5 - ?] const uint8_t *commandBytes = getMessageCommandBytes(itho->command); for (uint8_t i = 0; i < 6; i++) { itho->dataDecoded[i + 5] = pgm_read_byte(&(commandBytes[i])); } // set deviceID itho->dataDecoded[11] = itho->deviceId[0]; itho->dataDecoded[12] = itho->deviceId[1]; itho->dataDecoded[13] = itho->deviceId[2]; itho->dataDecoded[14] = 1; itho->dataDecoded[15] = 16; itho->dataDecoded[16] = 224; // set deviceID itho->dataDecoded[17] = itho->deviceId[0]; itho->dataDecoded[18] = itho->deviceId[1]; itho->dataDecoded[19] = itho->deviceId[2]; // set counter2 itho->dataDecoded[20] = getCounter2(itho, 20); itho->length = 21; packet->length = messageEncode(itho, packet); packet->length += 1; // set end byte packet->data[packet->length] = 202; packet->length += 1; // set end 'noise' for (uint8_t i = packet->length; i < packet->length + 7; i++) { packet->data[i] = 170; } packet->length += 7; } void IthoCC1101::createMessageLeave(IthoPacket *itho, CC1101Packet *packet) { // set start message structure createMessageStart(itho, packet); // set deviceType? (or messageType?) itho->dataDecoded[0] = itho->deviceType; // set deviceID itho->dataDecoded[1] = itho->deviceId[0]; itho->dataDecoded[2] = itho->deviceId[1]; itho->dataDecoded[3] = itho->deviceId[2]; // set counter1 itho->dataDecoded[4] = itho->counter; // set command bytes on dataDecoded[5 - 10] const uint8_t *commandBytes = getMessageCommandBytes(itho->command); for (uint8_t i = 0; i < 6; i++) { itho->dataDecoded[i + 5] = pgm_read_byte(&(commandBytes[i])); } // set deviceID itho->dataDecoded[11] = itho->deviceId[0]; itho->dataDecoded[12] = itho->deviceId[1]; itho->dataDecoded[13] = itho->deviceId[2]; // set counter2 itho->dataDecoded[14] = getCounter2(itho, 14); itho->length = 15; packet->length = messageEncode(itho, packet); packet->length += 1; // set end byte packet->data[packet->length] = 202; packet->length += 1; // set end 'noise' for (uint8_t i = packet->length; i < packet->length + 7; i++) { packet->data[i] = 170; } packet->length += 7; } const uint8_t * IthoCC1101::getMessageCommandBytes(IthoCommand command) { switch (command) { case IthoStandby: return &ithoMessageStandByCommandBytes[0]; case IthoHigh: return &ithoMessageHighCommandBytes[0]; case IthoFull: return &ithoMessageFullCommandBytes[0]; case IthoMedium: return &ithoMessageMediumCommandBytes[0]; case IthoLow: return &ithoMessageLowCommandBytes[0]; case IthoTimer1: return &ithoMessageTimer1CommandBytes[0]; case IthoTimer2: return &ithoMessageTimer2CommandBytes[0]; case IthoTimer3: return &ithoMessageTimer3CommandBytes[0]; case IthoJoin: return &ithoMessageJoinCommandBytes[0]; case IthoLeave: return &ithoMessageLeaveCommandBytes[0]; case OrconStandBy: return &orconMessageStandByCommandBytes[0]; case OrconLow: return &orconMessageLowCommandBytes[0]; case OrconMedium: return &orconMessageMediumCommandBytes[0]; case OrconHigh: return &orconMessageFullCommandBytes[0]; case OrconAuto: return &orconMessageAutoCommandBytes[0]; case OrconTimer0: return &orconMessageTimer0CommandBytes[0]; case OrconTimer1: return &orconMessageTimer1CommandBytes[0]; case OrconTimer2: return &orconMessageTimer2CommandBytes[0]; case OrconTimer3: return &orconMessageTimer3CommandBytes[0]; case OrconAutoCO2: return &orconMessageAutoCO2CommandBytes[0]; default: return &ithoMessageLowCommandBytes[0]; } } uint8_t IthoCC1101::getMessageCommandLength(IthoCommand command) { switch (command) { case IthoStandby: return sizeof(ithoMessageStandByCommandBytes) / sizeof(uint8_t); case IthoHigh: return sizeof(ithoMessageHighCommandBytes) / sizeof(uint8_t); case IthoFull: return sizeof(ithoMessageFullCommandBytes) / sizeof(uint8_t); case IthoMedium: return sizeof(ithoMessageMediumCommandBytes) / sizeof(uint8_t); case IthoLow: return sizeof(ithoMessageLowCommandBytes) / sizeof(uint8_t); case IthoTimer1: return sizeof(ithoMessageTimer1CommandBytes) / sizeof(uint8_t); case IthoTimer2: return sizeof(ithoMessageTimer2CommandBytes) / sizeof(uint8_t); case IthoTimer3: return sizeof(ithoMessageTimer3CommandBytes) / sizeof(uint8_t); case IthoJoin: return sizeof(ithoMessageJoinCommandBytes) / sizeof(uint8_t); case IthoLeave: return sizeof(ithoMessageLeaveCommandBytes) / sizeof(uint8_t); case OrconStandBy: return sizeof(orconMessageStandByCommandBytes) / sizeof(uint8_t); case OrconLow: return sizeof(orconMessageLowCommandBytes) / sizeof(uint8_t); case OrconMedium: return sizeof(orconMessageMediumCommandBytes) / sizeof(uint8_t); case OrconHigh: return sizeof(orconMessageFullCommandBytes) / sizeof(uint8_t); case OrconAuto: return sizeof(orconMessageAutoCommandBytes) / sizeof(uint8_t); case OrconTimer0: return sizeof(orconMessageTimer0CommandBytes) / sizeof(uint8_t); case OrconTimer1: return sizeof(orconMessageTimer1CommandBytes) / sizeof(uint8_t); case OrconTimer2: return sizeof(orconMessageTimer2CommandBytes) / sizeof(uint8_t); case OrconTimer3: return sizeof(orconMessageTimer3CommandBytes) / sizeof(uint8_t); case OrconAutoCO2: return sizeof(orconMessageAutoCO2CommandBytes) / sizeof(uint8_t); default: return sizeof(ithoMessageLowCommandBytes) / sizeof(uint8_t); } } /* Counter2 is the decimal sum of all bytes in decoded form from deviceType up to the last byte before counter2 subtracted from zero. */ uint8_t IthoCC1101::getCounter2(IthoPacket *itho, uint8_t len) { uint8_t val = 0; for (uint8_t i = 0; i < len; i++) { val += itho->dataDecoded[i]; } return 0 - val; } uint8_t IthoCC1101::messageEncode(IthoPacket *itho, CC1101Packet *packet) { // FIXME TD-er: lenOutbuf not used???? /* uint8_t lenOutbuf = 0; if ((itho->length * 20) % 8 == 0) { // inData len fits niecly in out buffer length lenOutbuf = itho->length * 2.5; } else { // is this an issue? inData last byte does not fill out buffer length, add 1 out byte extra, padding // is done after encode lenOutbuf = (uint8_t)(itho->length * 2.5) + 0.5; } */ uint8_t out_bytecounter = 14; // index of Outbuf, start at offset 14, first part of the message is set manually uint8_t out_bitcounter = 0; // bit position of current outbuf byte uint8_t out_patterncounter = 0; // bit counter to add 1 0 bit pattern after every 8 bits uint8_t bitSelect = 4; // bit position of the inData byte (4 - 7, 0 - 3) uint8_t out_shift = 7; // bit shift inData bit in position of outbuf byte // we need to zero the out buffer first cause we are using bitshifts for (unsigned int i = out_bytecounter; i < sizeof(packet->data) / sizeof(packet->data[0]); i++) { packet->data[i] = 0; } // Serial.println(); for (uint8_t dataByte = 0; dataByte < itho->length; dataByte++) { for (uint8_t dataBit = 0; dataBit < 8; dataBit++) { // process a full dataByte at a time resulting in 20 output bits (2.5 bytes) with // the pattern 7x6x5x4x 10 3x2x1x0x 10 7x6x5x4x 10 3x2x1x0x 10 etc if (out_bitcounter == 8) { // check if new byte is needed out_bytecounter++; out_bitcounter = 0; } if (out_patterncounter == 8) { // check if we have to start with a 1 0 pattern out_patterncounter = 0; packet->data[out_bytecounter] = packet->data[out_bytecounter] | 1 << out_shift; out_shift--; out_bitcounter++; packet->data[out_bytecounter] = packet->data[out_bytecounter] | 0 << out_shift; if (out_shift == 0) { out_shift = 8; } out_shift--; out_bitcounter++; } if (out_bitcounter == 8) { // check if new byte is needed out_bytecounter++; out_bitcounter = 0; } // set the even bit uint8_t bit = (itho->dataDecoded[dataByte] & (1 << bitSelect)) >> bitSelect; // select bit and shift to bit pos 0 bitSelect++; if (bitSelect == 8) { bitSelect = 0; } packet->data[out_bytecounter] = packet->data[out_bytecounter] | bit << out_shift; // shift bit in corect pos of current outbuf byte out_shift--; out_bitcounter++; out_patterncounter++; // set the odd bit (inverse of even bit) bit = ~bit & 0b00000001; packet->data[out_bytecounter] = packet->data[out_bytecounter] | bit << out_shift; if (out_shift == 0) { out_shift = 8; } out_shift--; out_bitcounter++; out_patterncounter++; } } if (out_bitcounter < 8) { // add closing 1 0 pattern to fill last packet->data byte and ensure DC balance in the message for (uint8_t i = out_bitcounter; i < 8; i += 2) { packet->data[out_bytecounter] = packet->data[out_bytecounter] | 1 << out_shift; out_shift--; packet->data[out_bytecounter] = packet->data[out_bytecounter] | 0 << out_shift; if (out_shift == 0) { out_shift = 8; } out_shift--; } } return out_bytecounter; } void IthoCC1101::messageDecode(CC1101Packet *packet, IthoPacket *itho) { itho->length = 0; int lenInbuf = packet->length; lenInbuf -= STARTBYTE; // correct for sync byte pos while (lenInbuf >= 5) { lenInbuf -= 5; itho->length += 2; } if (lenInbuf >= 3) { itho->length++; } for (unsigned int i = 0; i < sizeof(itho->dataDecoded) / sizeof(itho->dataDecoded[0]); i++) { itho->dataDecoded[i] = 0; } for (unsigned int i = 0; i < sizeof(itho->dataDecodedChk) / sizeof(itho->dataDecodedChk[0]); i++) { itho->dataDecodedChk[i] = 0; } uint8_t out_i = 0; // byte index uint8_t out_j = 4; // bit index uint8_t out_i_chk = 0; // byte index uint8_t out_j_chk = 4; // bit index uint8_t in_bitcounter = 0; // process per 10 input bits for (int i = STARTBYTE; i < packet->length; i++) { for (int j = 7; j > -1; j--) { if ((in_bitcounter == 0) || (in_bitcounter == 2) || (in_bitcounter == 4) || (in_bitcounter == 6)) { // select input bits for output uint8_t x = packet->data[i]; // select input byte x = x >> j; // select input bit x = x & 0b00000001; x = x << out_j; // set value for output bit itho->dataDecoded[out_i] = itho->dataDecoded[out_i] | x; out_j += 1; // next output bit if (out_j > 7) { out_j = 0; } if (out_j == 4) { out_i += 1; } } if ((in_bitcounter == 1) || (in_bitcounter == 3) || (in_bitcounter == 5) || (in_bitcounter == 7)) { // select input bits for check // output uint8_t x = packet->data[i]; // select input byte x = x >> j; // select input bit x = x & 0b00000001; x = x << out_j_chk; // set value for output bit itho->dataDecodedChk[out_i_chk] = itho->dataDecodedChk[out_i_chk] | x; out_j_chk += 1; // next output bit if (out_j_chk > 7) { out_j_chk = 0; } if (out_j_chk == 4) { itho->dataDecodedChk[out_i_chk] = ~itho->dataDecodedChk[out_i_chk]; // inverse bits out_i_chk += 1; } } in_bitcounter += 1; // continue cyling in groups of 10 bits if (in_bitcounter > 9) { in_bitcounter = 0; } } } } // uint8_t IthoCC1101::ReadRSSI() // { // uint8_t rssi = 0; // uint8_t value = 0; // rssi = (readRegister(CC1101_RSSI, CC1101_STATUS_REGISTER)); // if (rssi >= 128) // { // value = 255 - rssi; // value /= 2; // value += 74; // } // else // { // value = rssi / 2; // value += 74; // } // return value; // } // bool IthoCC1101::checkID(const uint8_t *id) const // { // for (uint8_t i = 0; i < 3; i++) { // if (id[i] != inIthoPacket.deviceId[i]) { // return false; // } // } // return true; // } String IthoCC1101::getLastIDstr(bool ashex) { String str; for (uint8_t i = 0; i < 3; i++) { if (ashex) { str += String(inIthoPacket.deviceId[i], HEX); } else { str += String(inIthoPacket.deviceId[i]); } if (i < 2) { str += ','; } } return str; } // int * IthoCC1101::getLastID() { // static int id[3]; // for (uint8_t i = 0; i < 3; i++) { // id[i] = inIthoPacket.deviceId[i]; // } // return id; // } // String IthoCC1101::getLastMessagestr(bool ashex) { // String str = F("Length="); // str += inMessage.length; // str += '.'; // for (uint8_t i = 0; i < inMessage.length; i++) { // if (ashex) { str += String(inMessage.data[i], HEX); } // else { str += String(inMessage.data[i]); } // if (i < inMessage.length - 1) { str += ':'; } // } // return str; // } // String IthoCC1101::LastMessageDecoded() const { // String str; // if (inIthoPacket.length > 11) { // str += F("Device type?: "); // str += String(inIthoPacket.deviceType); // str += F(" - CMD: "); // for (int i = 4; i < inIthoPacket.length; i++) { // str += String(inIthoPacket.dataDecoded[i]); // if (i < inIthoPacket.length - 1) { str += ','; } // } // } // else { // for (uint8_t i = 0; i < inIthoPacket.length; i++) { // str += String(inIthoPacket.dataDecoded[i]); // if (i < inIthoPacket.length - 1) { str += ','; } // } // } // str += '\n'; // return str; // }