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
00001 //show debug output on Serial pc 00002 #define DEBUG 00003 00004 #include "mbed.h" 00005 #include <RF22Max.h> 00006 00007 00008 RF22Max::RF22Max(PinName slaveSelectPin, PinName mosi, PinName miso, PinName sclk, PinName interrupt) 00009 : RF22(slaveSelectPin , mosi, miso, sclk, interrupt ) 00010 { 00011 } 00012 00013 static const RF22::ModemConfig max_config = { // for MAX! protocol 00014 .reg_1c = 0x01, //POR-Value : 75KHzBW? 00015 .reg_1f = 0x03, //POR-Value: 00016 .reg_20 = 0x90, //?? Oversampling Rate 00017 .reg_21 = 0x20, 00018 .reg_22 = 0x51, 00019 .reg_23 = 0xea, 00020 .reg_24 = 0x00, 00021 .reg_25 = 0x58, 00022 // 2c - 2e are only for OOK 00023 .reg_2c = 0x00, 00024 .reg_2d = 0x00, 00025 .reg_2e = 0x00, 00026 .reg_58 = 0x80, // Copied from RF22 defaults 00027 .reg_69 = 0x60, // Copied from RF22 defaults 00028 //.reg_6e = 0x08, // TX Data Rate 1 00029 //.reg_6f = 0x31, // TX Data Rate 0 00030 .reg_6e = 0x51, // TX Data Rate 1 00031 .reg_6f = 0xDC, // TX Data Rate 0 00032 .reg_70 = 0x20, //<30kps,no manchaster, no dewithening 00033 .reg_71 = RF22_DTMOD_FIFO | RF22_MODTYP_FSK, //=0x22 00034 .reg_72 = 0x1e, 00035 }; 00036 00037 00038 /* Sync words to send / check for. Don't forget to update RF22_SYNCLEN 00039 * below if changing the length of this array. */ 00040 00041 const uint8_t max_sync_words[] = { 00042 0xc6, 00043 0x26, 00044 0xc6, 00045 0x26, 00046 }; 00047 00048 00049 enum modes {MODE_AUTO, MODE_MANUAL, MODE_TEMPORARY, MODE_BOOST}; 00050 00051 const char *mode_str[] = { 00052 [MODE_AUTO] = "auto", 00053 [MODE_MANUAL] = "manual", 00054 [MODE_TEMPORARY] = "temporary", 00055 [MODE_BOOST] = "boost" 00056 }; 00057 00058 char *type_str(uint8_t type) 00059 { 00060 switch(type) { 00061 case 0x00: 00062 return "PairPing"; 00063 case 0x01: 00064 return "PairPong"; 00065 case 0x02: 00066 return "Ack"; 00067 case 0x03: 00068 return "TimeInformation"; 00069 case 0x10: 00070 return "ConfigWeekProfile"; 00071 case 0x11: 00072 return "ConfigTemperatures"; 00073 case 0x12: 00074 return "ConfigValve"; 00075 case 0x20: 00076 return "AddLinkPartner"; 00077 case 0x21: 00078 return "RemoveLinkPartner"; 00079 case 0x22: 00080 return "SetGroupId"; 00081 case 0x23: 00082 return "RemoveGroupId"; 00083 case 0x30: 00084 return "ShutterContactState"; 00085 case 0x40: 00086 return "SetTemperature"; 00087 case 0x42: 00088 return "WallThermostatState"; 00089 case 0x43: 00090 return "SetComfortTemperature"; 00091 case 0x44: 00092 return "SetEcoTemperature"; 00093 case 0x50: 00094 return "PushButtonState"; 00095 case 0x60: 00096 return "ThermostatState"; 00097 case 0x82: 00098 return "SetDisplayActualTemperature"; 00099 case 0xF1: 00100 return "WakeUp"; 00101 case 0xF0: 00102 return "Reset"; 00103 } 00104 return "Unknown"; 00105 }; 00106 00107 00108 /* First 255 bytes of PN9 sequence used for data whitening by the CC1101 00109 chip. The RF22 chip is documented to support the same data whitening 00110 algorithm, but in practice seems to use a different sequence. 00111 00112 Data was generated using the following python snippet: 00113 00114 import itertools 00115 def pn9(state): 00116 while True: 00117 yield hex(state & 0xff) 00118 # The pn9 generator is clocked 8 times while shifting in the 00119 # next data byte 00120 for i in range(8): 00121 state = (state >> 1) + (((state & 1) ^ (state >> 5) & 1) << 8) 00122 print(list(itertools.islice(pn9(0x1ff), 255))) 00123 */ 00124 00125 const uint8_t pn9[] = { 00126 0xff, 0xe1, 0x1d, 0x9a, 0xed, 0x85, 0x33, 0x24, 00127 0xea, 0x7a, 0xd2, 0x39, 0x70, 0x97, 0x57, 0x0a, 00128 0x54, 0x7d, 0x2d, 0xd8, 0x6d, 0x0d, 0xba, 0x8f, 00129 0x67, 0x59, 0xc7, 0xa2, 0xbf, 0x34, 0xca, 0x18, 00130 0x30, 0x53, 0x93, 0xdf, 0x92, 0xec, 0xa7, 0x15, 00131 0x8a, 0xdc, 0xf4, 0x86, 0x55, 0x4e, 0x18, 0x21, 00132 0x40, 0xc4, 0xc4, 0xd5, 0xc6, 0x91, 0x8a, 0xcd, 00133 0xe7, 0xd1, 0x4e, 0x09, 0x32, 0x17, 0xdf, 0x83, 00134 0xff, 0xf0, 0x0e, 0xcd, 0xf6, 0xc2, 0x19, 0x12, 00135 0x75, 0x3d, 0xe9, 0x1c, 0xb8, 0xcb, 0x2b, 0x05, 00136 0xaa, 0xbe, 0x16, 0xec, 0xb6, 0x06, 0xdd, 0xc7, 00137 0xb3, 0xac, 0x63, 0xd1, 0x5f, 0x1a, 0x65, 0x0c, 00138 0x98, 0xa9, 0xc9, 0x6f, 0x49, 0xf6, 0xd3, 0x0a, 00139 0x45, 0x6e, 0x7a, 0xc3, 0x2a, 0x27, 0x8c, 0x10, 00140 0x20, 0x62, 0xe2, 0x6a, 0xe3, 0x48, 0xc5, 0xe6, 00141 0xf3, 0x68, 0xa7, 0x04, 0x99, 0x8b, 0xef, 0xc1, 00142 0x7f, 0x78, 0x87, 0x66, 0x7b, 0xe1, 0x0c, 0x89, 00143 0xba, 0x9e, 0x74, 0x0e, 0xdc, 0xe5, 0x95, 0x02, 00144 0x55, 0x5f, 0x0b, 0x76, 0x5b, 0x83, 0xee, 0xe3, 00145 0x59, 0xd6, 0xb1, 0xe8, 0x2f, 0x8d, 0x32, 0x06, 00146 0xcc, 0xd4, 0xe4, 0xb7, 0x24, 0xfb, 0x69, 0x85, 00147 0x22, 0x37, 0xbd, 0x61, 0x95, 0x13, 0x46, 0x08, 00148 0x10, 0x31, 0x71, 0xb5, 0x71, 0xa4, 0x62, 0xf3, 00149 0x79, 0xb4, 0x53, 0x82, 0xcc, 0xc5, 0xf7, 0xe0, 00150 0x3f, 0xbc, 0x43, 0xb3, 0xbd, 0x70, 0x86, 0x44, 00151 0x5d, 0x4f, 0x3a, 0x07, 0xee, 0xf2, 0x4a, 0x81, 00152 0xaa, 0xaf, 0x05, 0xbb, 0xad, 0x41, 0xf7, 0xf1, 00153 0x2c, 0xeb, 0x58, 0xf4, 0x97, 0x46, 0x19, 0x03, 00154 0x66, 0x6a, 0xf2, 0x5b, 0x92, 0xfd, 0xb4, 0x42, 00155 0x91, 0x9b, 0xde, 0xb0, 0xca, 0x09, 0x23, 0x04, 00156 0x88, 0x98, 0xb8, 0xda, 0x38, 0x52, 0xb1, 0xf9, 00157 0x3c, 0xda, 0x29, 0x41, 0xe6, 0xe2, 0x7b 00158 }; 00159 00160 #ifdef DEBUG 00161 void RF22Max::printHex(uint8_t *buf, size_t len, bool nl) 00162 { 00163 for (size_t i = 0; i < len; i++) { 00164 pc.printf("%02X ",buf[i]); 00165 } 00166 if (nl) 00167 pc.printf("\n\r"); 00168 } 00169 #endif 00170 00171 boolean RF22Max::init() 00172 { 00173 boolean ret = this->RF22::init(); 00174 if (ret) { 00175 //Max! specific settings 00176 this->RF22::setModemRegisters(&max_config); 00177 this->RF22::setFrequency(868.299866, 0.035); 00178 /* Disable TX packet control, since the RF22 doesn't do proper 00179 * whitening so can't read the length header or CRC. We need RX packet 00180 * control so the RF22 actually sends pkvalid interrupts when the 00181 * manually set packet length is reached. */ 00182 this->RF22::spiWrite(RF22_REG_30_DATA_ACCESS_CONTROL, RF22_MSBFRST | RF22_ENPACRX); 00183 /* No packet headers, 4 sync words, fixed packet length */ 00184 this->RF22::spiWrite(RF22_REG_32_HEADER_CONTROL1, RF22_BCEN_NONE | RF22_HDCH_NONE); 00185 this->RF22::spiWrite(RF22_REG_33_HEADER_CONTROL2, RF22_HDLEN_0 | RF22_FIXPKLEN | RF22_SYNCLEN_4); 00186 this->RF22::setSyncWords(max_sync_words, lengthof(max_sync_words)); 00187 /* Detect preamble after 4 nibbles */ 00188 this->RF22::spiWrite(RF22_REG_35_PREAMBLE_DETECTION_CONTROL1, (0x4 << 3)); 00189 /* Send 4 nibbles of preamble */ 00190 this->RF22::setPreambleLength(4); // in nibbles 00191 this->RF22::spiWrite(RF22_REG_3E_PACKET_LENGTH, 30); // maximum length of a MAX!-packet 00192 } 00193 return ret; 00194 } 00195 00196 /* 00197 CRC code based on example from Texas Instruments DN502, matches 00198 CC1101 implementation 00199 */ 00200 #define CRC16_POLY 0x8005 00201 uint16_t RF22Max::calc_crc_step(uint8_t crcData, uint16_t crcReg) 00202 { 00203 uint8_t i; 00204 for (i = 0; i < 8; i++) { 00205 if (((crcReg & 0x8000) >> 8) ^ (crcData & 0x80)) 00206 crcReg = (crcReg << 1) ^ CRC16_POLY; 00207 else 00208 crcReg = (crcReg << 1); 00209 crcData <<= 1; 00210 } 00211 return crcReg; 00212 } // culCalcCRC 00213 00214 #define CRC_INIT 0xFFFF 00215 uint16_t RF22Max::calc_crc(uint8_t *buf, size_t len) 00216 { 00217 uint16_t checksum; 00218 checksum = CRC_INIT; 00219 // Init value for CRC calculation 00220 for (size_t i = 0; i < len; i++) 00221 checksum = calc_crc_step(buf[i], checksum); 00222 return checksum; 00223 } 00224 00225 00226 boolean RF22Max::recv(uint8_t* buf, uint8_t* len) 00227 { 00228 00229 if (RF22::recv(buf, len)) { 00230 *len = 50; // limit message to 50 Bytes as device receives all 255 Bytes 00231 00232 /* Dewhiten data */ 00233 for (int i = 0; i < *len; i++) 00234 buf[i] ^= pn9[i]; 00235 00236 // now read the real length 00237 *len = buf[0]+3; // 1 length-Byte + 2 CRC 00238 00239 if (*len < 3 || *len > lengthof(pn9)) { 00240 #ifdef DEBUG 00241 printHex(buf, *len, true); 00242 #endif 00243 return false; 00244 } 00245 #ifdef DEBUG 00246 printHex(buf, *len, true); 00247 #endif 00248 return true; 00249 } else 00250 return false; 00251 } 00252 00253 boolean RF22Max::recv_max(RF22Max::max_message* message) 00254 { 00255 uint8_t buf[RF22_MAX_MESSAGE_LEN]; 00256 00257 uint8_t len = sizeof(buf); 00258 00259 message->len = 0; 00260 00261 if (recv(buf,&len)) { 00262 00263 /* Calculate CRC (but don't include the CRC itself) */ 00264 uint16_t crc = calc_crc(buf, len - 2); 00265 if (buf[len - 1] != (crc & 0xff) || buf[len - 2] != (crc >> 8)) { 00266 00267 return false; 00268 } 00269 00270 /* Don't use the CRC as data */ 00271 len -= 2; 00272 00273 message->len = len; //message-length 00274 message->cnt = buf[1]; //message-counter 00275 message->flags = buf[2]; 00276 message->type = buf[3]; 00277 strcpy(message->type_str,type_str(message->type)); 00278 message->frm_adr = buf[4]<<16 | buf[5]<<8| buf[6]; // unique address of device 00279 message->to_adr = buf[7]<<16 | buf[8]<<8| buf[9]; ; // unique address of device 00280 message->groupid = buf[10]; //groupid 00281 memcpy( (void *) message->payload, (void *) buf[11],len-11); // data 00282 message->crc = buf[len-2]<<8 | buf[len-1]; // crc for the message 00283 00284 if (message->type == 0x30 && len >= 11) { //ShutterContactState 00285 bool baterry_low = (buf[11] >> 7) & 0x1; 00286 bool state = (buf[11]>>1) & 0x1; 00287 00288 if (state) { 00289 strcpy(message->state,"open"); 00290 } else { 00291 strcpy(message->state,"closed"); 00292 } 00293 00294 if (baterry_low) { 00295 strcpy(message->battery_state,"low"); 00296 } else { 00297 strcpy(message->battery_state,"good"); 00298 } 00299 } 00300 00301 if (message->type == 0x50 && len >= 11) { //PushButtonState 00302 bool baterry_low = (buf[11] >> 7) & 0x1; // to validate!!! 00303 bool state = (buf[12]) & 0x1; 00304 00305 if (state) { 00306 strcpy(message->state,"auto"); 00307 } else { 00308 strcpy(message->state,"eco"); 00309 } 00310 00311 if (baterry_low) { 00312 strcpy(message->battery_state,"low"); 00313 } else { 00314 strcpy(message->battery_state,"good"); 00315 } 00316 00317 } 00318 #if 0 00319 if (message->type == 0x00 && len >= 11) { //PairPing 00320 char serial[20]=""; 00321 uint8_t sbuf[RF22_MAX_MESSAGE_LEN]; 00322 uint8_t slen =0; 00323 00324 strncpy(serial,(char*)buf+14,10); //10 Characters for Seial Number 00325 serial[11] = '\0'; 00326 #ifdef DEBUG 00327 pc.printf("Serial: %s\n\r",serial); 00328 #endif 00329 00330 // try to send PairPong 00331 // wait some time 00332 wait_ms(20); // seen at moritz-code 00333 sbuf[0] = 11; // MsgLen 00334 sbuf[1] = buf[1]+1 &0xFF; // MsgCount ?? 00335 sbuf[2] = 0x00; // Flag 00336 sbuf[3] = 0x01; // Type = Cmd = PairPong 00337 sbuf[4] = 0x11; // From Fake Address 00338 sbuf[5] = 0x11; // From 00339 sbuf[6] = 0x11; // From 00340 sbuf[7] = buf[4] ; // To Address = From address of Windowcontact 00341 sbuf[8] = buf[5] ; 00342 sbuf[9] = buf[6] ; 00343 sbuf[10] = 0x00; // GroupId 00344 sbuf[11] = 0x00; //Payload is 0x00 for pairpong? 00345 slen = 12+2; //+2Byte CRC???? 00346 /* Calculate CRC */ 00347 uint16_t scrc = calc_crc(sbuf, slen - 2); 00348 sbuf[12] = crc >> 8; 00349 sbuf[13] = crc & 0xff; 00350 00351 00352 if (RF22::send(sbuf,slen)) { 00353 #ifdef DEBUG 00354 pc.printf("Send PairPong OK\n\r"); 00355 #endif 00356 } else { 00357 #ifdef DEBUG 00358 pc.printf("Send PairPong NOT OK\n\r"); 00359 #endif 00360 } 00361 00362 } 00363 #endif 00364 return true; 00365 } else 00366 return false; 00367 }
Generated on Mon Jul 18 2022 04:16:30 by 1.7.2