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

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers RF22Max.cpp Source File

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 }