ELV Max! Radio Signal Protocol 868.3MHz

Forum about the home automation suites by ELV etc

Moderator: jrkalf

sfischer
Starting Member
Starting Member
Posts: 13
Joined: Tue Dec 20, 2011 1:30 pm

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by sfischer »

I already tried to interpret the reg settings, which are posted in that thread, but in the datasheet there are formulas which are referring to the osciallator frequency. But I don't know the schematic of the cul. It's getting very difficult to find out the needed info.

Thanks,
SF
7x Thermostat / 1x WindowContact / 1x Max! Cube
Digit
Global Moderator
Global Moderator
Posts: 3388
Joined: Sat Mar 25, 2006 10:23 am
Location: Netherlands
Contact:

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by Digit »

Here maybe: http://busware.de/tiki-index.php?page=CUL
But I think the best option would be to just ask on the mikrocontroller forum, can save you a lot of work ;)
sfischer
Starting Member
Starting Member
Posts: 13
Joined: Tue Dec 20, 2011 1:30 pm

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by sfischer »

Hi,

now I have it working with RF22B module.

I get this data every 3-5 minutes from my MAX!. What is that?

2940A0251221002814A809204005A53011013601C00108008328020600281010448104088A48800000C0400BC0840102
7x Thermostat / 1x WindowContact / 1x Max! Cube
sfischer
Starting Member
Starting Member
Posts: 13
Joined: Tue Dec 20, 2011 1:30 pm

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by sfischer »

Hi,
I've gone over the forums, but still have now idea what this string means:
2912A0051221006028033320019001A004058000010000A0864008403011224036005252204024030080
I have received it over wireless
It was the reaction, after I set a temperature (21Deg) on my iPhone. Zhe cube has communicated with the thermostat.
7x Thermostat / 1x WindowContact / 1x Max! Cube
Digit
Global Moderator
Global Moderator
Posts: 3388
Joined: Sat Mar 25, 2006 10:23 am
Location: Netherlands
Contact:

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by Digit »

I've been looking at it more than once, but can't find anything useful in there...
Are you sure this string is OK? Can you reproduce it time after time?
lesender
Starting Member
Starting Member
Posts: 3
Joined: Mon Dec 05, 2011 10:04 pm

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by lesender »

I think you received some garbage...
Setting a temperatur of 25.0°C you should see this

2012-10-17-21-01-59.937 receive: 0B 6F 05 40 00 AA C4 03 4E 13 02 72
2012-10-17-21-01-59.984 Send: 0E 6F 02 02 03 4E 13 00 AA C4 00 01 19 02 32
2012-10-17-21-03-21.078 Send: 0F 00 04 60 03 4E 13 00 00 00 00 19 40 32 00 E2

it´s seen from the thermostat side: receive means the data, that the cube sent
(the packets were captured by sniffing the spi-bus)

sfischer wrote:Hi,
I've gone over the forums, but still have now idea what this string means:
2912A0051221006028033320019001A004058000010000A0864008403011224036005252204024030080
I have received it over wireless
It was the reaction, after I set a temperature (21Deg) on my iPhone. Zhe cube has communicated with the thermostat.
matthijskooijman
Member
Member
Posts: 62
Joined: Tue Oct 18, 2011 10:07 am

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by matthijskooijman »

Stefan, I'm also interested in getting the same receiver you have working with Max! (though I ordered one for an Arduino, not a raspberry pi). Did you get it to work already? Which settings are you using / did you try?
sfischer
Starting Member
Starting Member
Posts: 13
Joined: Tue Dec 20, 2011 1:30 pm

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by sfischer »

Hi Matthijs,
no I couldn't move any step forward. I've contacted silabs support, but still no progress. They mentioned that the hw based data whitening algorithm is not compatible to eachother. I've checked it and couldn't see any difference. So I'm stucked.
7x Thermostat / 1x WindowContact / 1x Max! Cube
matthijskooijman
Member
Member
Posts: 62
Joined: Tue Oct 18, 2011 10:07 am

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by matthijskooijman »

Hmm, sucky. Is there any documentation or info you did find out, that I could use as a starting point for giving it a try?

Also, did you consider buying a CUL device, which should be compatible in any case?
sfischer
Starting Member
Starting Member
Posts: 13
Joined: Tue Dec 20, 2011 1:30 pm

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by sfischer »

pls. look at the Appnote from silabs AN592 and the Design Note DN509 from TI. These notes are describing the hw based data whitening algorithm. hope this helps. pls. inform me, if you find a way that the CC1xxx and the RFM22B can talk to each other.
7x Thermostat / 1x WindowContact / 1x Max! Cube
matthijskooijman
Member
Member
Posts: 62
Joined: Tue Oct 18, 2011 10:07 am

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by matthijskooijman »

I've been struggling with this today and yesterday and made some progress.

I started out spending more than a day getting any reception at all working, when I found out there was a typo in the CULFW source comments and the data rate was really 10Kbit, not 1Kbit, which explained why I couldn't receive anything until this afternoon :-)

For reference, here's the data I've found from the CULFW source:

Modulation 2-FSK
Center frequency 868.3Mhz
Frequence deviation 19Khz
Datarate 10Kbit
Preamble: 4 bytes (1010...)
Syncword: 0xc626c626

With those settings, the RF22B chip was able to pick up packets from my cube and radiator thermostats reliably. However, it still fails to properly dewhiten the data, like you suggested. I agree with you that the whitening algorithms described in the two documents you referenced are identical, however in practice they seem to describe the CC1101 whitener only, not the RF22 one. Also interesting is that the shematic of whitening sequence in the silabs document is actually short one bits (there are only 7 data bits and 8 whitening bits), but the examples do suggest that the extra bit is there in the implementation.

If I do the dewhitening manually, I can actually get some sensical data out of the packet. For example, when I disable dewhitening in the RF22B and send a change temperature command to my thermostat, I receive F4 C1 18 DA ED 3D 16 25 DB CE D3 64 B4 69 (I'm using a fixed packet length and manually trimmed the packet and disabled CRC, since the length byte is also whitened). When I manually xor this with (every 8th byte of) a PN9 sequence, as described in DN509, I get: 0b 20 05 40 00 b8 25 01 31 b4 01 5d c4 fe. Here 0b is probably packet length excl last two CRC bytes, 40 packet type (or something, as suggested in previous posts) and the 00b825 and 0131b4 RF address of my cube and thermostat.

However, if I enable data whitening and then apply the same temperature change, I get the following packet: FB 8D AB B5 AE A5 5E 8B 67 59 EB 79 64 B7, which is incorrectly dewhitened. Xoring the raw data with the dewhitened data suggests that the RF22B chip is using the following sequence for dewhitening: 0f 4c b3 6f 43 98 48 ae bc 97 38 1d d0 de (I've confirmed this by doing this for three pairs of identical packets, all of which resulted in the same sequence).

However, I can't produce this sequence using the PN9 generator, even when I choose a different starting value (since there is only one "hidden" bit in the PN9 generator, there are only two starting values that generate 0x0f first: 0x00f and 0x10f). So, I'm not sure how the RF22 does its whitening exactly... We could do the dewhitening manually afterwards, but since the length byte is also whitened, that would mean the RF22 should be set to a fixed (high) packet size and we'll need to trim off the random garbage off the end afterwards.

As for the data you received, I'm not quite sure what to make of that yet. I tried undoing the wrong whitening applied by the RF22, and got: 26 5e 13 6a 51 b9 48 ce 94 94 0b 3d d1 4e. The first byte, 26 is the same as the last byte of the sync word. Perhaps you misconfigured the number of sync word bytes? Applying proper dewhitening to this sequence gives d9 bf 0e f0 bc 3c 7b ea 7e ee d9 04 a1 d9. Is we ignore the d9 (assuming it's part of the sync word), the next bf byte doesn't make sense (way too long for the packet length). The 0e could be the packet length, but then I'm unsure where the bf comes from. Is the RF address of your cube in there somewhere? Also, in your (I think) microcontroller.net posting, you mention this is the data after manchester decoding. However, CULFW doesn't use manchestor decoding, nor have I used it in my testing. Is this perhaps your problem?

For reference, here's my prototyping python script:

Code: Select all

from binascii import hexlify, unhexlify
from itertools import islice

def xor(a, b):
    return [a ^ b for a,b in zip(a,b)]

def unhex(s):
    return [ord(c) for c in unhexlify(s.replace(' ', ''))]

def pn9(state):
    while True:
        yield (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)

def ph(h):
    print ' '.join([hexlify(chr(c)) for c in h])

def pb(l):
    print '\n'.join( [''.join([str( a >> i & 1 ) for i in range( 7, -1, -1) ]) for a in l] )



# Left are packets dewhitened by RF22, right are identical packets
# captured with dewhitening turned off. Xoring them gives identical
# sequences.
ph(xor(unhex("FB 8D AB B5 AE A5 5E 8B 67 59 EB 79 64 B7"), unhex("F4 C1 18 DA ED 3D 16 25 DB CE D3 64 B4 69")))
ph(xor(unhex("FB 8C AB B5 AE A5 5E 8B 67 59 EB 7A F1 FE"), unhex("F4 C0 18 DA ED 3D 16 25 DB CE D3 67 21 20")))
ph(xor(unhex("FB 8E AB B5 AE A5 5E 8B 67 59 EB 4C 5B C9"), unhex("F4 C2 18 DA ED 3D 16 25 DB CE D3 51 8B 17")))

# Seems no starting value of PN9 will produce the dewhitening sequence
# used by RF22
ph(islice(pn9(0x0f), 14))
ph(islice(pn9(0x10f), 14))

# "Wrong" dewhitening sequence as used by RF22, taken from the above xors
wrongw = unhex("0f 4c b3 6f 43 98 48 ae bc 97 38 1d d0 de")
# Data received by sfischer. Probably wrongly dewhitened by RF22, so try
# to undo that.
dewhite = xor(unhex("2912A0051221006028033320019001A004058000010000A0864008403011224036005252204024030080"), wrongw)
ph(dewhite)
# Now, apply correct dewhitening
ph(xor(dewhite, pn9(0x1ff)))

# Dewhiten one of the raw packets with the correct pn9 sequence
ph(xor(unhex("F4 C1 18 DA ED 3D 16 25 DB CE D3 64 B4 69"), pn9(0x1ff)))

# This is the correct dewhitening sequence
ph(islice(pn9(0x1ff), 14))
Which produces the following output:

Code: Select all

0f 4c b3 6f 43 98 48 ae bc 97 38 1d d0 de
0f 4c b3 6f 43 98 48 ae bc 97 38 1d d0 de
0f 4c b3 6f 43 98 48 ae bc 97 38 1d d0 de
0f fe c3 3b 34 db 0b 67 48 d4 f5 a4 73 e0
0f ef d0 6c 2f 9c 21 51 d7 93 ce 81 bb bc
26 5e 13 6a 51 b9 48 ce 94 94 0b 3d d1 4e
d9 bf 0e f0 bc 3c 7b ea 7e ee d9 04 a1 d9
0b 20 05 40 00 b8 25 01 31 b4 01 5d c4 fe
ff e1 1d 9a ed 85 33 24 ea 7a d2 39 70 97
sfischer
Starting Member
Starting Member
Posts: 13
Joined: Tue Dec 20, 2011 1:30 pm

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by sfischer »

Hi Matthijs,

this is good News, that you figuerd out that the DataWhitening (DW) algos are different. Therefore we have to implement the DW according to the CC1101 in software.
I set the Temp to 22.5C and get the following Raw-Data (C64DE3D07E63B4198125604D238D). Below you will also find the complete Registersettings (regdump) of my RFM22B module.
Can you post yours please?
For testing reasons, I've set the packet length to 14.
To cope with the crypted Length field, I suggest the following strategy.
Set the packet length to 255. Set the lower FIFO Threshold to eg. 4 Bytes and the higher FIFO Threshold to 48 Bytes. Wait until the lower FIFO Threshold Interrrupt occurs. Read the 5 Byte. Dewhitening the 5 Bytes. Get the Data Length and write the length to the packet length register. If you get higher Threshold Interrupts, read 48 Bytes per Interrupt as long as you get an packet received Interrupt. Read the last few bytes (package length - already received bytes).
Dewhitening all. Check CRC ...

Stefan



2013-02-06 09:19:06 INF RFM22B Stat[1]: 0x000AB188 struct size: 0x0118
2013-02-06 09:19:06 INF .typ (DTC/DVC) 0x08 (RX/TRX) 0x06 Chip-Type: SI443x_B1 (RFM22B) supported: TRUE
2013-02-06 09:19:06 INF .state: RFM22_POWERUP .next_state: RFM22_IDLE
2013-02-06 09:19:06 INF .rx_state: RFM22_RX_IDLE .tx_state: RFM22_TX_IDLE
2013-02-06 09:19:06 INF .last_rssi: 0x00 (-120.0 dBm) .spi_devnum: 0
2013-02-06 09:19:06 INF .status: 0x20 0010 0000 rxffem idle-state
2013-02-06 09:19:06 INF .isr1: 0x20 0010 0000 ixtffaem
2013-02-06 09:19:06 INF .isr2: 0x00 0000 0000
2013-02-06 09:19:06 INF .ier1: 0x00 0000 0000
2013-02-06 09:19:06 INF .ier2: 0x00 0000 0000
2013-02-06 09:19:06 INF .reg31: 0x00 0000 0000
2013-02-06 09:19:06 INF .reg62: 0x04 0000 0100 LP enamp2x
2013-02-06 09:19:06 INF .rx_frames: 0 .tx_frames: 0 .crc_error: 0
2013-02-06 09:19:06 INF .tx_bytes: 0 .tx_bytes_net: 0 (Ratio: - %)
2013-02-06 09:19:06 INF .rx_bytes: 0 .rx_bytes_net: 0 (Ratio: - %)
2013-02-06 09:19:06 INF .Proto: RFM_Protocol_MAX_Heating .protoidx: 33
2013-02-06 09:19:06 INF .Desc: FSK_Rb10_0Fd45 (FSK, No HW Manchester, Fd = 45kHz, MAX! 10kcps)
2013-02-06 09:19:06 INF .rfm_freq: 868.300 .cps: 10000
2013-02-06 09:19:06 INF .codeing: none .complemented: FALSE
2013-02-06 09:19:06 INF .syncword: 0xC626 .tx_header: <notset> .check_header_mask: 00 .rx_mask: <notset>
2013-02-06 09:19:06 INF .pkglen_max: 14 .pkglen: 14 .initsize: 14

regdump (start): devnum 1
0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F
Reg[0x00..0x0F]: 0x08 0x06 0x20 0x20 0x00 0x00 0x00 0x40 0x10 0xD7 0x06 0x12 0x15 0x03 0x00 0x00
Reg[0x10..0x1F]: 0x00 0x00 0x20 0x00 0x03 0x00 0x01 0x00 0x00 0x01 0x14 0x00 0x1E 0x44 0x0A 0x03
Reg[0x20..0x2F]: 0xD0 0x00 0x9D 0x49 0x00 0x45 0x00 0x90 0x00 0x00 0x0F 0x00 0x00 0x0A 0x20 0x08
Reg[0x30..0x3F]: 0x88 0x00 0x00 0x0A 0x08 0x28 0xC6 0x26 0x33 0x30 0x32 0x30 0x33 0x30 0x33 0x30
Reg[0x40..0x4F]: 0x32 0x30 0x33 0x30 0x33 0x30 0x32 0x00 0x00 0x00 0x00 0xFF 0x08 0x08 0x08 0x10
Reg[0x50..0x5F]: 0x00 0x00 0xDF 0x52 0x20 0x64 0x00 0x01 0x87 0x00 0x01 0x00 0x0E 0x00 0x00 0x00
Reg[0x60..0x6F]: 0xA0 0x00 0x04 0x48 0x43 0x81 0x12 0x1F 0x03 0x60 0x9D 0x00 0x01 0x1B 0x51 0xEC
Reg[0x70..0x7F]: 0x20 0x22 0x48 0x00 0x00 0x73 0x67 0xC0 0x19 0x05 0x00 0x03 0x36 0x12 0x36 0xC6
regdump (end): devnum 1

2013-02-06 09:19:06 DBG rfm22_go_ready
2013-02-06 09:19:06 DBG rfm22_ResetFifo
##Start READ TEST ################################
2013-02-06 09:19:06 DBG rfm22_go_rx
...
IntSeq#18791 2013-02-06 09:20:28.320 FIFORead:C64DE3D07E63B4198125604D238D lgt: 14 0x0E
...

Recv 2013-02-06 09:20:30 C64DE3D07E63B4198125604D238D Length: 14 LastRSSI: -120.0 dBm
7x Thermostat / 1x WindowContact / 1x Max! Cube
matthijskooijman
Member
Member
Posts: 62
Joined: Tue Oct 18, 2011 10:07 am

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by matthijskooijman »

I thought I had come up with a better strategy last night than setting a big packet length. We can misuse the fact that the first byte in PN9 is 0xff. If we enable data inversion in FSK, all data will be put into the FIFO inverted. This will correctly dewhiten the length byte, because it is always the first byte, and allow the RF22 hardware to read the correct number of bytes exactly. However, while I writing this I'm realizing the packet length does not include the CRC bytes, so this will mean the CRC bytes will not be received by the RF22, which is not really useful I'm afraid...

So, I guess you're suggestion make sense (only I wonder how the RF22 handles changing the packet length halfway a packet, but that's something we can simply try).

I'm wrapping up my code now, I'll post the Arduino code (including the register defs) later today.
sfischer
Starting Member
Starting Member
Posts: 13
Joined: Tue Dec 20, 2011 1:30 pm

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by sfischer »

Hi Matthihs,

>>> ... changing the packet length halfway a packet, but that's something we can simply try).
This works. I'm using it since many months.
7x Thermostat / 1x WindowContact / 1x Max! Cube
matthijskooijman
Member
Member
Posts: 62
Joined: Tue Oct 18, 2011 10:07 am

Re: ELV Max! Radio Signal Protocol 868.3MHz

Post by matthijskooijman »

Ok, I implemented this now and it works perfect. However, I could not find the low and high FIFO threshold you were referring to, it seems the RX FIFO only has a single threshold. So I set this threshold to a low value first and then raise it when its interrupt triggers for the first time. I did have to set this to a fairly high value, below 7 it seemed to cause problems where the RF22 would remain stuck in RX mode / filling the FIFO (even after the pkvalid interrupt). I wasn't able to properly find out what was going wrong, but now the FIFO receives 10 bytes before finding out the real length, which seems to work fine (I'm not sure what happens for packets shorter than 10 packets, though).

Below is my Arduino program, which receives packets and decodes a few of them. I also have a patch against the Arduino RF22 library (version 1.25), since the "first read 10 bytes and then set the packet size"-stuff needed changes inside that library. Finally, here's a dump of all the register values I'm writing:

Code: Select all

05      0xB7
06      0x40
07      0x5
0B      0x12
0C      0x15
1C      0x1
1F      0x3
20      0x90
21      0x20
22      0x51
23      0xEA
24      0x0
25      0x58
2A      0x1C
2C      0x0
2D      0x0
2E      0x0
30      0x80
32      0x0
33      0xE
34      0x8
35      0x20
36      0xC6
37      0xD4
38      0xC6
39      0x26
3A      0x0
3B      0x0
3C      0x0
3D      0x0
3E      0x32
3F      0x0
43      0xFF
58      0x80
69      0x60
6D      0x3
6E      0x8
6F      0x31
70      0x24
71      0x22
72      0x1E
73      0x0
74      0x0
75      0x73
76      0x67
77      0xC0
7D      0x4
7E      0xA

Code: Select all

#include <SPI.h>
#include <RF22.h>

#define lengthof(x) (sizeof(x) / sizeof(*x))

const RF22::ModemConfig config =
{
	.reg_1c = 0x01,
	.reg_1f = 0x03,
	.reg_20 = 0x90,
	.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,
	.reg_6f = 0x31,
	.reg_70 = 0x24,
	.reg_71 = RF22_DTMOD_FIFO | RF22_MODTYP_FSK,
	.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 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};

/**
 * CRC code based on example from Texas Instruments DN502, matches
 * CC1101 implementation 
 */
#define CRC16_POLY 0x8005
uint16_t 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 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;
}

void printHex(uint8_t *buf, size_t len, bool nl) {
  for (size_t i = 0; i < len; ++i) {
    if (buf[i] < 0x10)
      Serial.print("0");
    Serial.print(buf[i], HEX);
  }
  if (nl)
    Serial.println("");
}

// Singleton instance of the radio
RF22 rf22;

void setup()
{
  Serial.begin(115200);          //  setup serial
  if (rf22.init())
    Serial.println("Initialized");
  else
    Serial.println("RF22 init failed");
  rf22.setModemRegisters(&config);
  rf22.setFrequency(868.3, 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. */
  rf22.spiWrite(RF22_REG_30_DATA_ACCESS_CONTROL, RF22_MSBFRST | RF22_ENPACRX);
  /* No packet headers, 4 sync words, fixed packet length */
  rf22.spiWrite(RF22_REG_32_HEADER_CONTROL1, RF22_BCEN_NONE | RF22_HDCH_NONE);
  rf22.spiWrite(RF22_REG_33_HEADER_CONTROL2, RF22_HDLEN_0 | RF22_FIXPKLEN | RF22_SYNCLEN_4);
  rf22.setSyncWords(sync_words, lengthof(sync_words));
  /* Detect preamble after 4 nibbles */
  rf22.spiWrite(RF22_REG_35_PREAMBLE_DETECTION_CONTROL1, (0x4 << 3));
  /* Send 8 bytes of preamble */
  rf22.setPreambleLength(8); // in nibbles
  rf22.spiWrite(RF22_REG_3E_PACKET_LENGTH, 20);
}

void printUntil(uint8_t *buf) {
  uint8_t year = buf[1] & 0x3f;
  uint8_t month = ((buf[0] & 0xE0) >> 4) | (buf[1] >> 7);
  uint8_t day = buf[0] & 0x1f;
  /* In 30-minute increments */
  uint8_t time = buf[2] & 0x3f;

  Serial.print("Until:         20");
  if (year < 10) Serial.print("0");
  Serial.print(year);
  Serial.print(".");
  if (month < 10) Serial.print("0");
  Serial.print(month);
  Serial.print(".");
  if (day < 10) Serial.print("0");
  Serial.print(day);
  Serial.print(" ");
  if (time < 20) Serial.print("0");
  Serial.print(time / 2);
  if (time % 2)
    Serial.print(":30");
  else
    Serial.print(":00");
  Serial.println("");
}

void loop()
{
  uint8_t buf[RF22_MAX_MESSAGE_LEN];
  uint8_t len = sizeof(buf);
  if (Serial.read() != -1)
    Serial.println("OK");

  if (rf22.recv(buf, &len))
  {
    Serial.print("Recv: ");
    Serial.println(len, HEX);

    if (len < 3 || len > lengthof(pn9)) {
      Serial.print("Packet length too long (");
      Serial.print(len);
      Serial.println(")");
      return;
    }

    /* Dewhiten data */
    for (int i = 0; i < len; ++i)
      buf[i] ^= pn9[i];

    /* 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)) {
      Serial.println("CRC error");
      return;
    }

    /* Don't use the CRC as data */
    len -= 2;

    uint8_t type = buf[3];

    Serial.print("Packet from:   ");
    printHex(buf + 4, 3, true);
    Serial.print("Packet to:     ");
    printHex(buf + 7, 3, true);
    Serial.print("Packet type:   ");
    printHex(&type, 1, false);
    Serial.print(" (");
    Serial.print(type_str(type));
    Serial.println(")");
    Serial.print("Message count: ");
    printHex(buf + 1, 1, true);
    Serial.print("Flags:         ");
    printHex(buf + 2, 1, true);
    if (type == 0x60 && len >= 13) { /* ThermostatState */
      uint8_t mode = buf[11] & 0x3;
      bool dst = (buf[11] >> 2) & 0x1;
      bool locked = (buf[11] >> 5) & 0x1;
      bool baterry_low = (buf[11] >> 7) & 0x1;
      /* 0 - 64 */
      uint8_t valve = buf[12];
      uint8_t set_temp = buf[13];

      Serial.print("Mode:          ");
      Serial.println(mode_str[mode]);

      Serial.print("Valve pos:     ");
      Serial.print(100 * valve / 64, DEC);
      Serial.println("%");

      Serial.print("Set temp:      ");
      Serial.print(set_temp / 2);
      Serial.println(set_temp % 2 ? ".5" : ".0");

      if (len > 15 && mode != MODE_TEMPORARY) {
        /* In tenths of degrees */
        uint8_t actual_temp = ((buf[14] & 0x1) << 8) + buf[15];
        Serial.print("Actual temp:   ");
        Serial.print(actual_temp / 10);
        Serial.print(".");
        Serial.println(actual_temp % 10);
      }
      if (len > 16 && mode == MODE_TEMPORARY) {
        printUntil(buf + 14);
      }
    } else if (type == 0x40 && len >= 11) { /* SetTemperature */
      uint8_t set_temp = buf[11] & 0x3f;
      uint8_t mode = buf[11] >> 6;

      Serial.print("Mode:          ");
      Serial.println(mode_str[mode]);

      Serial.print("Set temp:      ");
      Serial.print(set_temp / 2);
      Serial.println(set_temp % 2 ? ".5" : ".0");
      if (len > 14) {
        printUntil(buf + 12);
      }
    }
    #if 1
    // Print the data
    int i, j;
    for (i = 0; i < len; i += 16)
    {
      // Hex
      for (j = 0; j < 16 && i+j < len; j++)
      {
        if (buf[i+j] < 16)
          Serial.print("0"); // Sigh, Serial.print does not know how to pad hex
        Serial.print(buf[i+j], HEX);
        Serial.print(" ");
      }
      // Padding on last block
      while (j++ < 16)
        Serial.print("   ");

      Serial.print("   ");
      // ASCII
      for (j = 0; j < 16 && i+j < len; j++)
        Serial.write(isprint(buf[i+j]) ? buf[i+j] : '.');
      Serial.println("");
    }
    Serial.println("");
    #endif
  }
}

/* vim: set sw=2 sts=2 expandtab filetype=cpp: */

Code: Select all

commit 840a881cce98543ee09c97e9880c5e4d4eb3a51a
Author: Matthijs Kooijman <matthijs@stdin.nl>
Date:   Thu Feb 7 10:10:05 2013 +0100

    Changes needed for MAX communication

diff --git a/RF22.cpp b/RF22.cpp
index f17d7d7..916eb5d 100644
--- a/RF22.cpp
+++ b/RF22.cpp
@@ -239,7 +239,8 @@ void RF22::handleInterrupt()
     }
     if (_lastInterruptFlags[0] & RF22_IPKVALID)
     {
-	uint8_t len = spiRead(RF22_REG_4B_RECEIVED_PACKET_LENGTH);
+	//uint8_t len = spiRead(RF22_REG_4B_RECEIVED_PACKET_LENGTH);
+	uint8_t len = spiRead(RF22_REG_3E_PACKET_LENGTH);
 //	Serial.println("IPKVALID");   
 //	Serial.println(len);   
 //	Serial.println(_bufLen);   
@@ -463,6 +464,17 @@ uint8_t RF22::ezmacStatusRead()
 
 void RF22::setMode(uint8_t mode)
 {
+    if (mode & RF22_RXON) {
+	/* In RX mode, set the packet length to the maximum. Once one
+	 * byte is received, readNextFragment below will get the real
+	 * packet length and set that into the length register. */
+	spiWrite(RF22_REG_3E_PACKET_LENGTH, RF22_MAX_MESSAGE_LEN);
+	/* Start by reading a few bytes, so we can find out the length.
+	 * Don't make this lower, since it seems the FIFO gets confused
+	 * when you read only a few bytes and/or set this too low.. */
+	_rxFifoThreshold = 10;
+	spiWrite(RF22_REG_7E_RX_FIFO_CONTROL, _rxFifoThreshold);
+    }
     spiWrite(RF22_REG_07_OPERATING_MODE1, mode);
 }
 
@@ -700,12 +712,22 @@ void RF22::sendNextFragment()
 // That means it should only be called after a RXFFAFULL interrupt
 void RF22::readNextFragment()
 {
-    if (((uint16_t)_bufLen + RF22_RXFFAFULL_THRESHOLD) > RF22_MAX_MESSAGE_LEN)
+    if (((uint16_t)_bufLen + _rxFifoThreshold) > RF22_MAX_MESSAGE_LEN)
 	return; // Hmmm receiver overflow. Should never occur
 
-    // Read the RF22_RXFFAFULL_THRESHOLD octets that should be there
-    spiBurstRead(RF22_REG_7F_FIFO_ACCESS, _buf + _bufLen, RF22_RXFFAFULL_THRESHOLD);
-    _bufLen += RF22_RXFFAFULL_THRESHOLD;
+    // Read the _rxFifoThreshold octets that should be there
+    spiBurstRead(RF22_REG_7F_FIFO_ACCESS, _buf + _bufLen, _rxFifoThreshold);
+    _bufLen += _rxFifoThreshold;
+
+    if (_bufLen == _rxFifoThreshold) {
+	/* This was the first read, raise the threshold and find out the
+	 * packet size. */
+	_rxFifoThreshold = RF22_RXFFAFULL_THRESHOLD;
+	spiWrite(RF22_REG_7E_RX_FIFO_CONTROL, _rxFifoThreshold);
+	uint8_t len = (_buf[0] ^ 0xff) + 3; /* Add length and two CRC bytes */
+
+	spiWrite(RF22_REG_3E_PACKET_LENGTH, len);
+    }
 }
 
 // Clear the FIFOs
diff --git a/RF22.h b/RF22.h
index 46d0b46..00b42f6 100644
--- a/RF22.h
+++ b/RF22.h
@@ -1147,6 +1147,8 @@ protected:
     volatile uint16_t   _txGood;
 
     volatile uint8_t    _lastRssi;
+
+    volatile uint8_t	_rxFifoThreshold;
 };
 
 /// @example rf22_client.pde
Post Reply

Return to “Homematic, FS20, FHT, ESA and ELV”