Class to communicate with ELV(R) MAX! wireless devices with RFM22B-Modules. Based on Library RF22. Initial version unable to send! Only receive! See http://mbed.org/users/charly/notebook/reading-a-max-wireless-window-sensor-with-rfm22-an/
Dependents: RF22_MAX_test_Send
RF22Max.cpp
- Committer:
- charly
- Date:
- 2013-10-22
- Revision:
- 2:f75e51ce001b
- Parent:
- 0:565a81d6f278
File content as of revision 2:f75e51ce001b:
//show debug output on Serial pc #define DEBUG #include "mbed.h" #include <RF22Max.h> RF22Max::RF22Max(PinName slaveSelectPin, PinName mosi, PinName miso, PinName sclk, PinName interrupt) : RF22(slaveSelectPin , mosi, miso, sclk, interrupt ) { } static const RF22::ModemConfig max_config = { // for MAX! protocol .reg_1c = 0x01, //POR-Value : 75KHzBW? .reg_1f = 0x03, //POR-Value: .reg_20 = 0x90, //?? Oversampling Rate .reg_21 = 0x20, .reg_22 = 0x51, .reg_23 = 0xea, .reg_24 = 0x00, .reg_25 = 0x58, // 2c - 2e are only for OOK .reg_2c = 0x00, .reg_2d = 0x00, .reg_2e = 0x00, .reg_58 = 0x80, // Copied from RF22 defaults .reg_69 = 0x60, // Copied from RF22 defaults //.reg_6e = 0x08, // TX Data Rate 1 //.reg_6f = 0x31, // TX Data Rate 0 .reg_6e = 0x51, // TX Data Rate 1 .reg_6f = 0xDC, // TX Data Rate 0 .reg_70 = 0x20, //<30kps,no manchaster, no dewithening .reg_71 = RF22_DTMOD_FIFO | RF22_MODTYP_FSK, //=0x22 .reg_72 = 0x1e, }; /* Sync words to send / check for. Don't forget to update RF22_SYNCLEN * below if changing the length of this array. */ const uint8_t max_sync_words[] = { 0xc6, 0x26, 0xc6, 0x26, }; enum modes {MODE_AUTO, MODE_MANUAL, MODE_TEMPORARY, MODE_BOOST}; const char *mode_str[] = { [MODE_AUTO] = "auto", [MODE_MANUAL] = "manual", [MODE_TEMPORARY] = "temporary", [MODE_BOOST] = "boost" }; char *type_str(uint8_t type) { switch(type) { case 0x00: return "PairPing"; case 0x01: return "PairPong"; case 0x02: return "Ack"; case 0x03: return "TimeInformation"; case 0x10: return "ConfigWeekProfile"; case 0x11: return "ConfigTemperatures"; case 0x12: return "ConfigValve"; case 0x20: return "AddLinkPartner"; case 0x21: return "RemoveLinkPartner"; case 0x22: return "SetGroupId"; case 0x23: return "RemoveGroupId"; case 0x30: return "ShutterContactState"; case 0x40: return "SetTemperature"; case 0x42: return "WallThermostatState"; case 0x43: return "SetComfortTemperature"; case 0x44: return "SetEcoTemperature"; case 0x50: return "PushButtonState"; case 0x60: return "ThermostatState"; case 0x82: return "SetDisplayActualTemperature"; case 0xF1: return "WakeUp"; case 0xF0: return "Reset"; } return "Unknown"; }; /* First 255 bytes of PN9 sequence used for data whitening by the CC1101 chip. The RF22 chip is documented to support the same data whitening algorithm, but in practice seems to use a different sequence. Data was generated using the following python snippet: import itertools def pn9(state): while True: yield hex(state & 0xff) # The pn9 generator is clocked 8 times while shifting in the # next data byte for i in range(8): state = (state >> 1) + (((state & 1) ^ (state >> 5) & 1) << 8) print(list(itertools.islice(pn9(0x1ff), 255))) */ const uint8_t pn9[] = { 0xff, 0xe1, 0x1d, 0x9a, 0xed, 0x85, 0x33, 0x24, 0xea, 0x7a, 0xd2, 0x39, 0x70, 0x97, 0x57, 0x0a, 0x54, 0x7d, 0x2d, 0xd8, 0x6d, 0x0d, 0xba, 0x8f, 0x67, 0x59, 0xc7, 0xa2, 0xbf, 0x34, 0xca, 0x18, 0x30, 0x53, 0x93, 0xdf, 0x92, 0xec, 0xa7, 0x15, 0x8a, 0xdc, 0xf4, 0x86, 0x55, 0x4e, 0x18, 0x21, 0x40, 0xc4, 0xc4, 0xd5, 0xc6, 0x91, 0x8a, 0xcd, 0xe7, 0xd1, 0x4e, 0x09, 0x32, 0x17, 0xdf, 0x83, 0xff, 0xf0, 0x0e, 0xcd, 0xf6, 0xc2, 0x19, 0x12, 0x75, 0x3d, 0xe9, 0x1c, 0xb8, 0xcb, 0x2b, 0x05, 0xaa, 0xbe, 0x16, 0xec, 0xb6, 0x06, 0xdd, 0xc7, 0xb3, 0xac, 0x63, 0xd1, 0x5f, 0x1a, 0x65, 0x0c, 0x98, 0xa9, 0xc9, 0x6f, 0x49, 0xf6, 0xd3, 0x0a, 0x45, 0x6e, 0x7a, 0xc3, 0x2a, 0x27, 0x8c, 0x10, 0x20, 0x62, 0xe2, 0x6a, 0xe3, 0x48, 0xc5, 0xe6, 0xf3, 0x68, 0xa7, 0x04, 0x99, 0x8b, 0xef, 0xc1, 0x7f, 0x78, 0x87, 0x66, 0x7b, 0xe1, 0x0c, 0x89, 0xba, 0x9e, 0x74, 0x0e, 0xdc, 0xe5, 0x95, 0x02, 0x55, 0x5f, 0x0b, 0x76, 0x5b, 0x83, 0xee, 0xe3, 0x59, 0xd6, 0xb1, 0xe8, 0x2f, 0x8d, 0x32, 0x06, 0xcc, 0xd4, 0xe4, 0xb7, 0x24, 0xfb, 0x69, 0x85, 0x22, 0x37, 0xbd, 0x61, 0x95, 0x13, 0x46, 0x08, 0x10, 0x31, 0x71, 0xb5, 0x71, 0xa4, 0x62, 0xf3, 0x79, 0xb4, 0x53, 0x82, 0xcc, 0xc5, 0xf7, 0xe0, 0x3f, 0xbc, 0x43, 0xb3, 0xbd, 0x70, 0x86, 0x44, 0x5d, 0x4f, 0x3a, 0x07, 0xee, 0xf2, 0x4a, 0x81, 0xaa, 0xaf, 0x05, 0xbb, 0xad, 0x41, 0xf7, 0xf1, 0x2c, 0xeb, 0x58, 0xf4, 0x97, 0x46, 0x19, 0x03, 0x66, 0x6a, 0xf2, 0x5b, 0x92, 0xfd, 0xb4, 0x42, 0x91, 0x9b, 0xde, 0xb0, 0xca, 0x09, 0x23, 0x04, 0x88, 0x98, 0xb8, 0xda, 0x38, 0x52, 0xb1, 0xf9, 0x3c, 0xda, 0x29, 0x41, 0xe6, 0xe2, 0x7b }; #ifdef DEBUG void RF22Max::printHex(uint8_t *buf, size_t len, bool nl) { for (size_t i = 0; i < len; i++) { pc.printf("%02X ",buf[i]); } if (nl) pc.printf("\n\r"); } #endif boolean RF22Max::init() { boolean ret = this->RF22::init(); if (ret) { //Max! specific settings this->RF22::setModemRegisters(&max_config); this->RF22::setFrequency(868.299866, 0.035); /* Disable TX packet control, since the RF22 doesn't do proper * whitening so can't read the length header or CRC. We need RX packet * control so the RF22 actually sends pkvalid interrupts when the * manually set packet length is reached. */ this->RF22::spiWrite(RF22_REG_30_DATA_ACCESS_CONTROL, RF22_MSBFRST | RF22_ENPACRX); /* No packet headers, 4 sync words, fixed packet length */ this->RF22::spiWrite(RF22_REG_32_HEADER_CONTROL1, RF22_BCEN_NONE | RF22_HDCH_NONE); this->RF22::spiWrite(RF22_REG_33_HEADER_CONTROL2, RF22_HDLEN_0 | RF22_FIXPKLEN | RF22_SYNCLEN_4); this->RF22::setSyncWords(max_sync_words, lengthof(max_sync_words)); /* Detect preamble after 4 nibbles */ this->RF22::spiWrite(RF22_REG_35_PREAMBLE_DETECTION_CONTROL1, (0x4 << 3)); /* Send 4 nibbles of preamble */ this->RF22::setPreambleLength(4); // in nibbles this->RF22::spiWrite(RF22_REG_3E_PACKET_LENGTH, 30); // maximum length of a MAX!-packet } return ret; } /* CRC code based on example from Texas Instruments DN502, matches CC1101 implementation */ #define CRC16_POLY 0x8005 uint16_t RF22Max::calc_crc_step(uint8_t crcData, uint16_t crcReg) { uint8_t i; for (i = 0; i < 8; i++) { if (((crcReg & 0x8000) >> 8) ^ (crcData & 0x80)) crcReg = (crcReg << 1) ^ CRC16_POLY; else crcReg = (crcReg << 1); crcData <<= 1; } return crcReg; } // culCalcCRC #define CRC_INIT 0xFFFF uint16_t RF22Max::calc_crc(uint8_t *buf, size_t len) { uint16_t checksum; checksum = CRC_INIT; // Init value for CRC calculation for (size_t i = 0; i < len; i++) checksum = calc_crc_step(buf[i], checksum); return checksum; } boolean RF22Max::recv(uint8_t* buf, uint8_t* len) { if (RF22::recv(buf, len)) { *len = 50; // limit message to 50 Bytes as device receives all 255 Bytes /* Dewhiten data */ for (int i = 0; i < *len; i++) buf[i] ^= pn9[i]; // now read the real length *len = buf[0]+3; // 1 length-Byte + 2 CRC if (*len < 3 || *len > lengthof(pn9)) { #ifdef DEBUG printHex(buf, *len, true); #endif return false; } #ifdef DEBUG printHex(buf, *len, true); #endif return true; } else return false; } boolean RF22Max::recv_max(RF22Max::max_message* message) { uint8_t buf[RF22_MAX_MESSAGE_LEN]; uint8_t len = sizeof(buf); message->len = 0; if (recv(buf,&len)) { /* Calculate CRC (but don't include the CRC itself) */ uint16_t crc = calc_crc(buf, len - 2); if (buf[len - 1] != (crc & 0xff) || buf[len - 2] != (crc >> 8)) { return false; } /* Don't use the CRC as data */ len -= 2; message->len = len; //message-length message->cnt = buf[1]; //message-counter message->flags = buf[2]; message->type = buf[3]; strcpy(message->type_str,type_str(message->type)); message->frm_adr = buf[4]<<16 | buf[5]<<8| buf[6]; // unique address of device message->to_adr = buf[7]<<16 | buf[8]<<8| buf[9]; ; // unique address of device message->groupid = buf[10]; //groupid memcpy( (void *) message->payload, (void *) buf[11],len-11); // data message->crc = buf[len-2]<<8 | buf[len-1]; // crc for the message if (message->type == 0x30 && len >= 11) { //ShutterContactState bool baterry_low = (buf[11] >> 7) & 0x1; bool state = (buf[11]>>1) & 0x1; if (state) { strcpy(message->state,"open"); } else { strcpy(message->state,"closed"); } if (baterry_low) { strcpy(message->battery_state,"low"); } else { strcpy(message->battery_state,"good"); } } if (message->type == 0x50 && len >= 11) { //PushButtonState bool baterry_low = (buf[11] >> 7) & 0x1; // to validate!!! bool state = (buf[12]) & 0x1; if (state) { strcpy(message->state,"auto"); } else { strcpy(message->state,"eco"); } if (baterry_low) { strcpy(message->battery_state,"low"); } else { strcpy(message->battery_state,"good"); } } #if 0 if (message->type == 0x00 && len >= 11) { //PairPing char serial[20]=""; uint8_t sbuf[RF22_MAX_MESSAGE_LEN]; uint8_t slen =0; strncpy(serial,(char*)buf+14,10); //10 Characters for Seial Number serial[11] = '\0'; #ifdef DEBUG pc.printf("Serial: %s\n\r",serial); #endif // try to send PairPong // wait some time wait_ms(20); // seen at moritz-code sbuf[0] = 11; // MsgLen sbuf[1] = buf[1]+1 &0xFF; // MsgCount ?? sbuf[2] = 0x00; // Flag sbuf[3] = 0x01; // Type = Cmd = PairPong sbuf[4] = 0x11; // From Fake Address sbuf[5] = 0x11; // From sbuf[6] = 0x11; // From sbuf[7] = buf[4] ; // To Address = From address of Windowcontact sbuf[8] = buf[5] ; sbuf[9] = buf[6] ; sbuf[10] = 0x00; // GroupId sbuf[11] = 0x00; //Payload is 0x00 for pairpong? slen = 12+2; //+2Byte CRC???? /* Calculate CRC */ uint16_t scrc = calc_crc(sbuf, slen - 2); sbuf[12] = crc >> 8; sbuf[13] = crc & 0xff; if (RF22::send(sbuf,slen)) { #ifdef DEBUG pc.printf("Send PairPong OK\n\r"); #endif } else { #ifdef DEBUG pc.printf("Send PairPong NOT OK\n\r"); #endif } } #endif return true; } else return false; }