ublo
bogdan's (micro)blog

bogdanel » pinguino / manchester decoder / opentherm

09:28 pm on Jul 29, 2012 | #more | tags:

as a part of my PhD. studies i needed a simple interface to a central heating unit (CHU). searching the internet i found that most CHU are slave devices that use OpenTherm protocol for communication. i will bother you with that in a future post. for now, OpenTherm uses the Manchester code for sending and receiving information. as recently i've switched from arduino to pinguino (still using arduino for my work with students), here's a small library that i use to decode the OpenTherm messages, that is versatile enough to be used for any Manchester code codec. it worked with a PINGUINO MX220 from Olimex. the tolerance for the bit timing is quite good: it correctly received data with bit lengths from 880uS to 1320uS. for other ranges tweak the defined constants.

// the code bellow is provided "as-is" with no warranty whatsoever
// also, this is a sketch and should be treated likewise

/* half the bit length */
#define OTHRM_BIT_TX0    500
/* threshold in microseconds for bit detection.
   it should be larger than the fastest transition time,
   yet it should consider the clock tolerance and the
   speed of the CPU. for PINGUINO 32MX220 this worked */
#define OTHRM_BIT_RX0    300
/* maximum length of a bit, in microseconds */
#define OTHRM_BIT_RX1    1000

#include <delay.c>

/* the OpenTherm frame has 32 bits. for 8 bit processors, use an array */
volatile u32 OTHRM_FRAME;

/* this function sends the OTHRM_FRAME data through "pin" */
void othrm_tx (u8 pin) {
    u8 c = 0;

/* start bit "1" of OpenTherm */
    digitalWrite (pin, HIGH);
    Delayus (OTHRM_BIT_TX0);
    digitalWrite (pin, LOW);
    Delayus (OTHRM_BIT_TX0);

/* actual data */
    for (c = 0; c<32; c++) {
        if ((OTHRM_FRAME >> 31) == 1) {
            digitalWrite (pin, HIGH);
            Delayus (OTHRM_BIT_TX0);
            digitalWrite (pin, LOW);
            Delayus (OTHRM_BIT_TX0);
            }
        else {
            digitalWrite (pin, LOW);
            Delayus (OTHRM_BIT_TX0);
            digitalWrite (pin, HIGH);
            Delayus (OTHRM_BIT_TX0);
            }
        OTHRM_FRAME <<= 1;
        }

/* stop bit "1" of OpenTherm */
    digitalWrite (pin, HIGH);
    Delayus (OTHRM_BIT_TX0);
    digitalWrite (pin, LOW);
    Delayus (OTHRM_BIT_TX0);
    }

/* receiving data from pin "pin". the data will be found in
   OTHRM_FRAME */
void othrm_rx (u8 pin) {
    u8 s = 1, c = 0;
    u16 m = 0, t = 0;
    OTHRM_FRAME = 1;

/* wait for the begining of the start bit. i need this.
   for non-blocking operation you should include a timer
   with a timeout. */
    while (!digitalRead(pin));
    for (c = 0; c<33; c++) {
        OTHRM_FRAME <<= 1;

        t = 0;
        while ((s == digitalRead(pin)) && (t < OTHRM_BIT_RX0)) { t++; Delayus(1); }
        s = digitalRead(pin);
        OTHRM_FRAME |= !s;
        t = 0;
        while ((s == digitalRead(pin)) && (t < OTHRM_BIT_RX0)) { t++; Delayus(1); }
        s = digitalRead(pin);
        }
    }
Creampie