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

Committer:
charly
Date:
Sun Sep 15 19:33:56 2013 +0000
Revision:
0:565a81d6f278
Child:
2:f75e51ce001b
Initial version of RF22Max. Library for communicating with ELV(R) MAX! wireless modules. Based on RF22-Library.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
charly 0:565a81d6f278 1 #include "mbed.h"
charly 0:565a81d6f278 2 #include <RF22Max.h>
charly 0:565a81d6f278 3
charly 0:565a81d6f278 4
charly 0:565a81d6f278 5 RF22Max::RF22Max(PinName slaveSelectPin, PinName mosi, PinName miso, PinName sclk, PinName interrupt)
charly 0:565a81d6f278 6 : RF22(slaveSelectPin , mosi, miso, sclk, interrupt )
charly 0:565a81d6f278 7 {
charly 0:565a81d6f278 8 }
charly 0:565a81d6f278 9
charly 0:565a81d6f278 10 static const RF22::ModemConfig max_config = { // for MAX! protocol
charly 0:565a81d6f278 11 .reg_1c = 0x01,
charly 0:565a81d6f278 12 .reg_1f = 0x03,
charly 0:565a81d6f278 13 .reg_20 = 0x90,
charly 0:565a81d6f278 14 .reg_21 = 0x20,
charly 0:565a81d6f278 15 .reg_22 = 0x51,
charly 0:565a81d6f278 16 .reg_23 = 0xea,
charly 0:565a81d6f278 17 .reg_24 = 0x00,
charly 0:565a81d6f278 18 .reg_25 = 0x58,
charly 0:565a81d6f278 19 // 2c - 2e are only for OOK
charly 0:565a81d6f278 20 .reg_2c = 0x00,
charly 0:565a81d6f278 21 .reg_2d = 0x00,
charly 0:565a81d6f278 22 .reg_2e = 0x00,
charly 0:565a81d6f278 23 .reg_58 = 0x80, // Copied from RF22 defaults
charly 0:565a81d6f278 24 .reg_69 = 0x60, // Copied from RF22 defaults
charly 0:565a81d6f278 25 .reg_6e = 0x08,
charly 0:565a81d6f278 26 .reg_6f = 0x31,
charly 0:565a81d6f278 27 .reg_70 = 0x24,
charly 0:565a81d6f278 28 .reg_71 = RF22_DTMOD_FIFO | RF22_MODTYP_FSK,
charly 0:565a81d6f278 29 .reg_72 = 0x1e,
charly 0:565a81d6f278 30 };
charly 0:565a81d6f278 31
charly 0:565a81d6f278 32
charly 0:565a81d6f278 33 /* Sync words to send / check for. Don't forget to update RF22_SYNCLEN
charly 0:565a81d6f278 34 * below if changing the length of this array. */
charly 0:565a81d6f278 35
charly 0:565a81d6f278 36 const uint8_t max_sync_words[] = {
charly 0:565a81d6f278 37 0xc6,
charly 0:565a81d6f278 38 0x26,
charly 0:565a81d6f278 39 0xc6,
charly 0:565a81d6f278 40 0x26,
charly 0:565a81d6f278 41 };
charly 0:565a81d6f278 42
charly 0:565a81d6f278 43
charly 0:565a81d6f278 44 enum modes {MODE_AUTO, MODE_MANUAL, MODE_TEMPORARY, MODE_BOOST};
charly 0:565a81d6f278 45
charly 0:565a81d6f278 46 const char *mode_str[] = {
charly 0:565a81d6f278 47 [MODE_AUTO] = "auto",
charly 0:565a81d6f278 48 [MODE_MANUAL] = "manual",
charly 0:565a81d6f278 49 [MODE_TEMPORARY] = "temporary",
charly 0:565a81d6f278 50 [MODE_BOOST] = "boost"
charly 0:565a81d6f278 51 };
charly 0:565a81d6f278 52
charly 0:565a81d6f278 53 char *type_str(uint8_t type)
charly 0:565a81d6f278 54 {
charly 0:565a81d6f278 55 switch(type) {
charly 0:565a81d6f278 56 case 0x00:
charly 0:565a81d6f278 57 return "PairPing";
charly 0:565a81d6f278 58 case 0x01:
charly 0:565a81d6f278 59 return "PairPong";
charly 0:565a81d6f278 60 case 0x02:
charly 0:565a81d6f278 61 return "Ack";
charly 0:565a81d6f278 62 case 0x03:
charly 0:565a81d6f278 63 return "TimeInformation";
charly 0:565a81d6f278 64 case 0x10:
charly 0:565a81d6f278 65 return "ConfigWeekProfile";
charly 0:565a81d6f278 66 case 0x11:
charly 0:565a81d6f278 67 return "ConfigTemperatures";
charly 0:565a81d6f278 68 case 0x12:
charly 0:565a81d6f278 69 return "ConfigValve";
charly 0:565a81d6f278 70 case 0x20:
charly 0:565a81d6f278 71 return "AddLinkPartner";
charly 0:565a81d6f278 72 case 0x21:
charly 0:565a81d6f278 73 return "RemoveLinkPartner";
charly 0:565a81d6f278 74 case 0x22:
charly 0:565a81d6f278 75 return "SetGroupId";
charly 0:565a81d6f278 76 case 0x23:
charly 0:565a81d6f278 77 return "RemoveGroupId";
charly 0:565a81d6f278 78 case 0x30:
charly 0:565a81d6f278 79 return "ShutterContactState";
charly 0:565a81d6f278 80 case 0x40:
charly 0:565a81d6f278 81 return "SetTemperature";
charly 0:565a81d6f278 82 case 0x42:
charly 0:565a81d6f278 83 return "WallThermostatState";
charly 0:565a81d6f278 84 case 0x43:
charly 0:565a81d6f278 85 return "SetComfortTemperature";
charly 0:565a81d6f278 86 case 0x44:
charly 0:565a81d6f278 87 return "SetEcoTemperature";
charly 0:565a81d6f278 88 case 0x50:
charly 0:565a81d6f278 89 return "PushButtonState";
charly 0:565a81d6f278 90 case 0x60:
charly 0:565a81d6f278 91 return "ThermostatState";
charly 0:565a81d6f278 92 case 0x82:
charly 0:565a81d6f278 93 return "SetDisplayActualTemperature";
charly 0:565a81d6f278 94 case 0xF1:
charly 0:565a81d6f278 95 return "WakeUp";
charly 0:565a81d6f278 96 case 0xF0:
charly 0:565a81d6f278 97 return "Reset";
charly 0:565a81d6f278 98 }
charly 0:565a81d6f278 99 return "Unknown";
charly 0:565a81d6f278 100 };
charly 0:565a81d6f278 101
charly 0:565a81d6f278 102
charly 0:565a81d6f278 103 /* First 255 bytes of PN9 sequence used for data whitening by the CC1101
charly 0:565a81d6f278 104 chip. The RF22 chip is documented to support the same data whitening
charly 0:565a81d6f278 105 algorithm, but in practice seems to use a different sequence.
charly 0:565a81d6f278 106
charly 0:565a81d6f278 107 Data was generated using the following python snippet:
charly 0:565a81d6f278 108
charly 0:565a81d6f278 109 import itertools
charly 0:565a81d6f278 110 def pn9(state):
charly 0:565a81d6f278 111 while True:
charly 0:565a81d6f278 112 yield hex(state & 0xff)
charly 0:565a81d6f278 113 # The pn9 generator is clocked 8 times while shifting in the
charly 0:565a81d6f278 114 # next data byte
charly 0:565a81d6f278 115 for i in range(8):
charly 0:565a81d6f278 116 state = (state >> 1) + (((state & 1) ^ (state >> 5) & 1) << 8)
charly 0:565a81d6f278 117 print(list(itertools.islice(pn9(0x1ff), 255)))
charly 0:565a81d6f278 118 */
charly 0:565a81d6f278 119
charly 0:565a81d6f278 120 const uint8_t pn9[] = {
charly 0:565a81d6f278 121 0xff, 0xe1, 0x1d, 0x9a, 0xed, 0x85, 0x33, 0x24,
charly 0:565a81d6f278 122 0xea, 0x7a, 0xd2, 0x39, 0x70, 0x97, 0x57, 0x0a,
charly 0:565a81d6f278 123 0x54, 0x7d, 0x2d, 0xd8, 0x6d, 0x0d, 0xba, 0x8f,
charly 0:565a81d6f278 124 0x67, 0x59, 0xc7, 0xa2, 0xbf, 0x34, 0xca, 0x18,
charly 0:565a81d6f278 125 0x30, 0x53, 0x93, 0xdf, 0x92, 0xec, 0xa7, 0x15,
charly 0:565a81d6f278 126 0x8a, 0xdc, 0xf4, 0x86, 0x55, 0x4e, 0x18, 0x21,
charly 0:565a81d6f278 127 0x40, 0xc4, 0xc4, 0xd5, 0xc6, 0x91, 0x8a, 0xcd,
charly 0:565a81d6f278 128 0xe7, 0xd1, 0x4e, 0x09, 0x32, 0x17, 0xdf, 0x83,
charly 0:565a81d6f278 129 0xff, 0xf0, 0x0e, 0xcd, 0xf6, 0xc2, 0x19, 0x12,
charly 0:565a81d6f278 130 0x75, 0x3d, 0xe9, 0x1c, 0xb8, 0xcb, 0x2b, 0x05,
charly 0:565a81d6f278 131 0xaa, 0xbe, 0x16, 0xec, 0xb6, 0x06, 0xdd, 0xc7,
charly 0:565a81d6f278 132 0xb3, 0xac, 0x63, 0xd1, 0x5f, 0x1a, 0x65, 0x0c,
charly 0:565a81d6f278 133 0x98, 0xa9, 0xc9, 0x6f, 0x49, 0xf6, 0xd3, 0x0a,
charly 0:565a81d6f278 134 0x45, 0x6e, 0x7a, 0xc3, 0x2a, 0x27, 0x8c, 0x10,
charly 0:565a81d6f278 135 0x20, 0x62, 0xe2, 0x6a, 0xe3, 0x48, 0xc5, 0xe6,
charly 0:565a81d6f278 136 0xf3, 0x68, 0xa7, 0x04, 0x99, 0x8b, 0xef, 0xc1,
charly 0:565a81d6f278 137 0x7f, 0x78, 0x87, 0x66, 0x7b, 0xe1, 0x0c, 0x89,
charly 0:565a81d6f278 138 0xba, 0x9e, 0x74, 0x0e, 0xdc, 0xe5, 0x95, 0x02,
charly 0:565a81d6f278 139 0x55, 0x5f, 0x0b, 0x76, 0x5b, 0x83, 0xee, 0xe3,
charly 0:565a81d6f278 140 0x59, 0xd6, 0xb1, 0xe8, 0x2f, 0x8d, 0x32, 0x06,
charly 0:565a81d6f278 141 0xcc, 0xd4, 0xe4, 0xb7, 0x24, 0xfb, 0x69, 0x85,
charly 0:565a81d6f278 142 0x22, 0x37, 0xbd, 0x61, 0x95, 0x13, 0x46, 0x08,
charly 0:565a81d6f278 143 0x10, 0x31, 0x71, 0xb5, 0x71, 0xa4, 0x62, 0xf3,
charly 0:565a81d6f278 144 0x79, 0xb4, 0x53, 0x82, 0xcc, 0xc5, 0xf7, 0xe0,
charly 0:565a81d6f278 145 0x3f, 0xbc, 0x43, 0xb3, 0xbd, 0x70, 0x86, 0x44,
charly 0:565a81d6f278 146 0x5d, 0x4f, 0x3a, 0x07, 0xee, 0xf2, 0x4a, 0x81,
charly 0:565a81d6f278 147 0xaa, 0xaf, 0x05, 0xbb, 0xad, 0x41, 0xf7, 0xf1,
charly 0:565a81d6f278 148 0x2c, 0xeb, 0x58, 0xf4, 0x97, 0x46, 0x19, 0x03,
charly 0:565a81d6f278 149 0x66, 0x6a, 0xf2, 0x5b, 0x92, 0xfd, 0xb4, 0x42,
charly 0:565a81d6f278 150 0x91, 0x9b, 0xde, 0xb0, 0xca, 0x09, 0x23, 0x04,
charly 0:565a81d6f278 151 0x88, 0x98, 0xb8, 0xda, 0x38, 0x52, 0xb1, 0xf9,
charly 0:565a81d6f278 152 0x3c, 0xda, 0x29, 0x41, 0xe6, 0xe2, 0x7b
charly 0:565a81d6f278 153 };
charly 0:565a81d6f278 154
charly 0:565a81d6f278 155 #ifdef DEBUG
charly 0:565a81d6f278 156 void RF22Max::printHex(uint8_t *buf, size_t len, bool nl)
charly 0:565a81d6f278 157 {
charly 0:565a81d6f278 158 for (size_t i = 0; i < len; i++) {
charly 0:565a81d6f278 159 pc.printf("%02X ",buf[i]);
charly 0:565a81d6f278 160 }
charly 0:565a81d6f278 161 if (nl)
charly 0:565a81d6f278 162 pc.printf("\n\r");
charly 0:565a81d6f278 163 }
charly 0:565a81d6f278 164 #endif
charly 0:565a81d6f278 165
charly 0:565a81d6f278 166 boolean RF22Max::init()
charly 0:565a81d6f278 167 {
charly 0:565a81d6f278 168 boolean ret = this->RF22::init();
charly 0:565a81d6f278 169 if (ret) {
charly 0:565a81d6f278 170 //Max! specific settings
charly 0:565a81d6f278 171 this->RF22::setModemRegisters(&max_config);
charly 0:565a81d6f278 172 this->RF22::setFrequency(868.3, 0.035);
charly 0:565a81d6f278 173 /* Disable TX packet control, since the RF22 doesn't do proper
charly 0:565a81d6f278 174 * whitening so can't read the length header or CRC. We need RX packet
charly 0:565a81d6f278 175 * control so the RF22 actually sends pkvalid interrupts when the
charly 0:565a81d6f278 176 * manually set packet length is reached. */
charly 0:565a81d6f278 177 this->RF22::spiWrite(RF22_REG_30_DATA_ACCESS_CONTROL, RF22_MSBFRST | RF22_ENPACRX);
charly 0:565a81d6f278 178 /* No packet headers, 4 sync words, fixed packet length */
charly 0:565a81d6f278 179 this->RF22::spiWrite(RF22_REG_32_HEADER_CONTROL1, RF22_BCEN_NONE | RF22_HDCH_NONE);
charly 0:565a81d6f278 180 this->RF22::spiWrite(RF22_REG_33_HEADER_CONTROL2, RF22_HDLEN_0 | RF22_FIXPKLEN | RF22_SYNCLEN_4);
charly 0:565a81d6f278 181 this->RF22::setSyncWords(max_sync_words, lengthof(max_sync_words));
charly 0:565a81d6f278 182 /* Detect preamble after 4 nibbles */
charly 0:565a81d6f278 183 this->RF22::spiWrite(RF22_REG_35_PREAMBLE_DETECTION_CONTROL1, (0x4 << 3));
charly 0:565a81d6f278 184 /* Send 8 bytes of preamble */
charly 0:565a81d6f278 185 this->RF22::setPreambleLength(8); // in nibbles
charly 0:565a81d6f278 186 this->RF22::spiWrite(RF22_REG_3E_PACKET_LENGTH, 30); // maximum length of a MAX!-packet
charly 0:565a81d6f278 187 }
charly 0:565a81d6f278 188 return ret;
charly 0:565a81d6f278 189 }
charly 0:565a81d6f278 190
charly 0:565a81d6f278 191 /*
charly 0:565a81d6f278 192 CRC code based on example from Texas Instruments DN502, matches
charly 0:565a81d6f278 193 CC1101 implementation
charly 0:565a81d6f278 194 */
charly 0:565a81d6f278 195 #define CRC16_POLY 0x8005
charly 0:565a81d6f278 196 uint16_t RF22Max::calc_crc_step(uint8_t crcData, uint16_t crcReg)
charly 0:565a81d6f278 197 {
charly 0:565a81d6f278 198 uint8_t i;
charly 0:565a81d6f278 199 for (i = 0; i < 8; i++) {
charly 0:565a81d6f278 200 if (((crcReg & 0x8000) >> 8) ^ (crcData & 0x80))
charly 0:565a81d6f278 201 crcReg = (crcReg << 1) ^ CRC16_POLY;
charly 0:565a81d6f278 202 else
charly 0:565a81d6f278 203 crcReg = (crcReg << 1);
charly 0:565a81d6f278 204 crcData <<= 1;
charly 0:565a81d6f278 205 }
charly 0:565a81d6f278 206 return crcReg;
charly 0:565a81d6f278 207 } // culCalcCRC
charly 0:565a81d6f278 208
charly 0:565a81d6f278 209 #define CRC_INIT 0xFFFF
charly 0:565a81d6f278 210 uint16_t RF22Max::calc_crc(uint8_t *buf, size_t len)
charly 0:565a81d6f278 211 {
charly 0:565a81d6f278 212 uint16_t checksum;
charly 0:565a81d6f278 213 checksum = CRC_INIT;
charly 0:565a81d6f278 214 // Init value for CRC calculation
charly 0:565a81d6f278 215 for (size_t i = 0; i < len; i++)
charly 0:565a81d6f278 216 checksum = calc_crc_step(buf[i], checksum);
charly 0:565a81d6f278 217 return checksum;
charly 0:565a81d6f278 218 }
charly 0:565a81d6f278 219
charly 0:565a81d6f278 220
charly 0:565a81d6f278 221 boolean RF22Max::recv(uint8_t* buf, uint8_t* len)
charly 0:565a81d6f278 222 {
charly 0:565a81d6f278 223
charly 0:565a81d6f278 224 if (RF22::recv(buf, len)) {
charly 0:565a81d6f278 225 *len = 50; // limit message to 50 Bytes as device receives all 255 Bytes
charly 0:565a81d6f278 226
charly 0:565a81d6f278 227 /* Dewhiten data */
charly 0:565a81d6f278 228 for (int i = 0; i < *len; i++)
charly 0:565a81d6f278 229 buf[i] ^= pn9[i];
charly 0:565a81d6f278 230
charly 0:565a81d6f278 231 // now read the real length
charly 0:565a81d6f278 232 *len = buf[0]+3; // 1 length-Byte + 2 CRC
charly 0:565a81d6f278 233
charly 0:565a81d6f278 234 if (*len < 3 || *len > lengthof(pn9)) {
charly 0:565a81d6f278 235 #ifdef DEBUG
charly 0:565a81d6f278 236 printHex(buf, *len, true);
charly 0:565a81d6f278 237 #endif
charly 0:565a81d6f278 238 return false;
charly 0:565a81d6f278 239 }
charly 0:565a81d6f278 240 #ifdef DEBUG
charly 0:565a81d6f278 241 printHex(buf, *len, true);
charly 0:565a81d6f278 242 #endif
charly 0:565a81d6f278 243 return true;
charly 0:565a81d6f278 244 } else
charly 0:565a81d6f278 245 return false;
charly 0:565a81d6f278 246 }
charly 0:565a81d6f278 247
charly 0:565a81d6f278 248 boolean RF22Max::recv_max(RF22Max::max_message* message)
charly 0:565a81d6f278 249 {
charly 0:565a81d6f278 250 uint8_t buf[RF22_MAX_MESSAGE_LEN];
charly 0:565a81d6f278 251
charly 0:565a81d6f278 252 uint8_t len = sizeof(buf);
charly 0:565a81d6f278 253
charly 0:565a81d6f278 254 message->len = 0;
charly 0:565a81d6f278 255
charly 0:565a81d6f278 256 if (recv(buf,&len)) {
charly 0:565a81d6f278 257
charly 0:565a81d6f278 258 /* Calculate CRC (but don't include the CRC itself) */
charly 0:565a81d6f278 259 uint16_t crc = calc_crc(buf, len - 2);
charly 0:565a81d6f278 260 if (buf[len - 1] != (crc & 0xff) || buf[len - 2] != (crc >> 8)) {
charly 0:565a81d6f278 261
charly 0:565a81d6f278 262 return false;
charly 0:565a81d6f278 263 }
charly 0:565a81d6f278 264
charly 0:565a81d6f278 265 /* Don't use the CRC as data */
charly 0:565a81d6f278 266 len -= 2;
charly 0:565a81d6f278 267
charly 0:565a81d6f278 268 message->len = len; //message-length
charly 0:565a81d6f278 269 message->cnt = buf[1]; //message-counter
charly 0:565a81d6f278 270 message->flags = buf[2];
charly 0:565a81d6f278 271 message->type = buf[3];
charly 0:565a81d6f278 272 strcpy(message->type_str,type_str(message->type));
charly 0:565a81d6f278 273 message->frm_adr = buf[4]<<16 | buf[5]<<8| buf[6]; // unique address of device
charly 0:565a81d6f278 274 message->to_adr = buf[7]<<16 | buf[8]<<8| buf[9]; ; // unique address of device
charly 0:565a81d6f278 275 message->groupid = buf[10]; //groupid
charly 0:565a81d6f278 276 memcpy( (void *) message->payload, (void *) buf[11],len-11); // data
charly 0:565a81d6f278 277 message->crc = buf[len-2]<<8 | buf[len-1]; // crc for the message
charly 0:565a81d6f278 278
charly 0:565a81d6f278 279 if (message->type == 0x30 && len >= 11) { //ShutterContactState
charly 0:565a81d6f278 280 bool baterry_low = (buf[11] >> 7) & 0x1;
charly 0:565a81d6f278 281 bool state = (buf[11]>>1) & 0x1;
charly 0:565a81d6f278 282
charly 0:565a81d6f278 283 if (state) {
charly 0:565a81d6f278 284 strcpy(message->state,"open");
charly 0:565a81d6f278 285 } else {
charly 0:565a81d6f278 286 strcpy(message->state,"closed");
charly 0:565a81d6f278 287 }
charly 0:565a81d6f278 288
charly 0:565a81d6f278 289 if (baterry_low) {
charly 0:565a81d6f278 290 strcpy(message->battery_state,"low");
charly 0:565a81d6f278 291 } else {
charly 0:565a81d6f278 292 strcpy(message->battery_state,"good");
charly 0:565a81d6f278 293 }
charly 0:565a81d6f278 294 }
charly 0:565a81d6f278 295
charly 0:565a81d6f278 296 if (message->type == 0x50 && len >= 11) { //PushButtonState
charly 0:565a81d6f278 297 bool baterry_low = (buf[11] >> 7) & 0x1; // to validate!!!
charly 0:565a81d6f278 298 bool state = (buf[12]) & 0x1;
charly 0:565a81d6f278 299
charly 0:565a81d6f278 300 if (state) {
charly 0:565a81d6f278 301 strcpy(message->state,"auto");
charly 0:565a81d6f278 302 } else {
charly 0:565a81d6f278 303 strcpy(message->state,"eco");
charly 0:565a81d6f278 304 }
charly 0:565a81d6f278 305
charly 0:565a81d6f278 306 if (baterry_low) {
charly 0:565a81d6f278 307 strcpy(message->battery_state,"low");
charly 0:565a81d6f278 308 } else {
charly 0:565a81d6f278 309 strcpy(message->battery_state,"good");
charly 0:565a81d6f278 310 }
charly 0:565a81d6f278 311
charly 0:565a81d6f278 312 }
charly 0:565a81d6f278 313 return true;
charly 0:565a81d6f278 314 } else
charly 0:565a81d6f278 315 return false;
charly 0:565a81d6f278 316 }