Test program to send MAX!-Messages with a RFM22-Module

Dependencies:   RF22 TextLCD TextLCDScroll mbed RF22Max

main.cpp

Committer:
charly
Date:
2013-08-20
Revision:
2:941c46d37d7e
Parent:
1:b71f9a293c54
Child:
3:4254b4c3557e

File content as of revision 2:941c46d37d7e:

// Testprogramm for RFM22B with RF22-Library to read ELV MAX! window Shutter-Contacts

#include "mbed.h"
#include <RF22.h>

#include "TextLCDScroll.h"


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

Serial pc(USBTX, USBRX);

//TextLCDScroll lcd(p30, p29, p28, p27, p26, p25, TextLCD::LCD16x2); // rs, e, d4-d7
TextLCDScroll lcd(p30, p29, p28, p27, p26, p25, TextLCD::LCD16x2); // rs, e, d4-d7

// mbed LEDs
DigitalOut led1(LED1);
DigitalOut led2(LED2);
DigitalOut led3(LED3);
DigitalOut led4(LED4);


// Singleton instance of the radio
//rf22(PinName slaveSelectPin , PinName mosi, PinName miso, PinName sclk, PinName interrupt );
RF22 rf22(p14,p11,p12,p13,p15);


const RF22::ModemConfig config = { // for MAX! protocol
    .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++) {
        pc.printf("%02X ",buf[i]);
    }
    if (nl)
        pc.printf("\n\r");
}

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;

    pc.printf("Until:         20");
    if (year < 10) pc.printf("0");
    pc.printf("%i",year);
    pc.printf(".");
    if (month < 10) pc.printf("0");
    pc.printf("%i",month);
    pc.printf(".");
    if (day < 10) pc.printf("0");
    pc.printf("%i",day);
    pc.printf(" ");
    if (time < 20) pc.printf("0");
    pc.printf("%i",time / 2);
    if (time % 2)
        pc.printf(":30");
    else
        pc.printf(":00");
    pc.printf("\n\r");
}


void max_rx_loop()
{
    uint8_t buf[RF22_MAX_MESSAGE_LEN];
    uint8_t len = sizeof(buf);

    if (rf22.recv(buf, &len)) {
        pc.printf("Recv: ");
        pc.printf("len: %i\n\r",len);
        len = 30;  // limit message to 30 Bytes as device receives all 255 Bytes

        //pc.printf("buf: >%s<\n\r",(char*)buf);
        printHex(buf, len, true);

        /* 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
        pc.printf("len: %i\n\r",len);

        if (len < 3 || len > lengthof(pn9)) {
            pc.printf("Packet length too short/long (%i)\n\r",len);
            return;
        }
        pc.printf("dewhiten: ");
        printHex(buf, len, true);

        /* 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)) {
            pc.printf("CRC error\n\r");
            return;
        }

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

        uint8_t type = buf[3];
#if 1
        pc.printf("Message count: ");
        printHex(buf + 1, 1, true);
        pc.printf("Flags:         ");
        printHex(buf + 2, 1, true);
        pc.printf("Packet type:   ");
        printHex(&type, 1, false);
        pc.printf(" (");
        pc.printf(type_str(type));
        pc.printf(")\n\r");
        pc.printf("Packet from:   ");
        printHex(buf + 4, 3, true);
        pc.printf("Packet to:     ");
        printHex(buf + 7, 3, true);
        pc.printf("GroupID:       ");
        printHex(buf + 10, 1, true);
        pc.printf("Payload:       ");
        printHex(buf + 11, len-11, true);

        if (type == 0x30 && len >= 11) { //ShutterContactState
            bool baterry_low = (buf[11] >> 7) & 0x1;
            bool state = (buf[11]>>1) & 0x1;
            pc.printf("State:      ");
            if (state) {
                pc.printf("open\n\r");
                lcd.setLine(1,"open   ");
            } else {
                pc.printf("closed\n\r");
                lcd.setLine(1,"closed ");
            }
            pc.printf("Battery:    ");
            if (baterry_low) {
                pc.printf("low\n\r");
            } else {
                pc.printf("good\n\r");
            }

        }

        /*
                else 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];

                    pc.printf("Mode:          ");
                    pc.printf(mode_str[mode]);

                    pc.printf("Valve pos:     %i%",100 * valve / 64);

                    pc.printf("Set temp:      %2.1i",set_temp / 2);

                    if (len > 15 && mode != MODE_TEMPORARY) {
                        // In tenths of degrees
                        uint8_t actual_temp = ((buf[14] & 0x1) << 8) + buf[15];
                        pc.printf("Actual temp:   ");
                        pc.printf(actual_temp / 10);
                        pc.printf(".");
                        pc.printf(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;

                    pc.printf("Mode:          ");
                    pc.printf(mode_str[mode]);

                    pc.print("Set temp:      ");
                    pc.printf(set_temp / 2);
                    pc.printf(set_temp % 2 ? ".5" : ".0");
                    if (len > 14) {
                        printUntil(buf + 12);
                    }
                }

                // 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)
                            pc.print("0"); // Sigh, pc.print does not know how to pad hex
                        pc.print(buf[i+j], HEX);
                        pc.print(" ");
                    }
                    // Padding on last block
                    while (j++ < 16)
                        pc.print("   ");

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

int main()
{

    pc.baud(115200);

    pc.printf("\n\rConnected to mbed\n\r");


    char version_str [80] = "RF22-MAX!-V1.1";
    lcd.cls();
    lcd.setLine(0,version_str);
    pc.printf("%s\n\r",version_str);

    pc.printf("Pre-init|");
    if (!rf22.init())
        pc.printf("RF22 init failed\n\r");
    pc.printf("Post-init\n\r");


    // try to detect Window-Shutter

    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);


    rf22.setModeRx();



    //wait forever and see if interrrupt  occurs(led1)
    while (1) {

        max_rx_loop();
    };
}