Decoder for Oregon Scientific Weather Sensor

This program receives Oregon Scientific Sensor V2 packets into mbed. The original version was created for Arduino by the folks at JetLabs

A few changed where needed:

  • The types of the internal variables needed some tweaking to work properly with the mbed compiler.
  • Implementing the interrupt service routine (ISR) using the InterruptIn facility was too slow and was skipping edges. Doing a "native" ISR implementation as described in this post worked properly.
  • A circular buffer was implemented to communicate the pulse width from the ISR to the foreground loop.

A SparkFun RF Link Receiver connected into the mbed pin 10 to receive the data was used in this project. Note that trying to power the RF Rceiver from the USB 5V Out provided by the mbed caused the module not to receive any data. It is probably a noise issue in the mbed hardware, since powering the same module using the 5V provided in the Arduino board worked fine. Powering the mbed and RF module from an external 5V supply worked fine.

#include "mbed.h"
#include "IOMacros.h"

//
//  Translated form Arduino code from the following page:
//
//  http://jeelabs.net/projects/11/wiki/Decoding_the_Oregon_Scientific_V2_protocol
//
class DecodeOOK {
protected:
    int total_bits, bits, flip, state, pos;
    char data[32];

    virtual int decode (int width) =0;
    
public:

    enum { UNKNOWN, T0, OK, DONE };

    DecodeOOK () 
        {
        resetDecoder();
        }
    
    bool nextPulse (int width) 
        {        
        if (state != DONE)
            {
            switch (decode(width)) 
                {
            case -1:
                resetDecoder();
                break;
            case 1:
                done();
                break;
                }
            }

        return isDone();
        }

    bool isDone () const 
        {
        return state == DONE;
        }

    const char* getData (int& count) const 
        {
        count = pos;
        return data;
        }

    void resetDecoder () 
        {
        total_bits = bits = pos = flip = 0;
        state = UNKNOWN;
        }

    // add one bit to the packet data buffer

    virtual void gotBit (int value) 
        {
        total_bits++;
        char *ptr = data + pos;
        *ptr = (*ptr >> 1) | (value << 7);

        if (++bits >= 8) {
            bits = 0;
            if (++pos >= sizeof data) {
                resetDecoder();
                return;
            }
        }
        state = OK;
        }

    // store a bit using Manchester encoding
    void manchester (char value) 
        {
        flip ^= value; // manchester code, long pulse flips the bit
        gotBit(flip);
        }

    void done () {
        while (bits)
            gotBit(0); // padding
        state = DONE;
    }
};


// 433 MHz decoders


class OregonDecoderV2 : public DecodeOOK {
public:
    OregonDecoderV2() {}

    // add one bit to the packet data buffer
    virtual void gotBit (int value) {
        if (!(total_bits & 0x01)) {
            data[pos] = (data[pos] >> 1) | (value ? 0x80 : 00);
        }
        total_bits++;
        pos = total_bits >> 4;
        if (pos >= sizeof data) {
            resetDecoder();
            return;
        }
        state = OK;
    }

    virtual int decode (int width) {
        if (200 <= width && width < 1200) {
            bool w = width >= 700;
            switch (state) {
                case UNKNOWN:
                    if (w) {
                        // Long pulse
                        ++flip;
                    } else if (32 <= flip) {
                        // Short pulse, start bit
                        flip = 0;
                        state = T0;
                    } else {
                        // Reset decoder
                        return -1;
                    }
                    break;
                case OK:
                    if (!w) {
                        // Short pulse
                        state = T0;
                    } else {
                        // Long pulse
                        manchester(1);
                    }
                    break;
                case T0:
                    if (!w) {
                        // Second short pulse
                        manchester(0);
                    } else {
                        // Reset decoder
                        return -1;
                    }
                    break;
            }
        } else {
            return -1;
        }
        return total_bits == 160 ? 1: 0;
    }
};

void reportSerial (const char* s, class DecodeOOK& decoder) 
    {
    int pos;
    const char* data = decoder.getData(pos);
    printf("%s ", s);

    for (int i = 0; i < pos; ++i) 
        {
        printf("%02X", data[i]);
        }

    printf("\r\n");

    decoder.resetDecoder();
    }

OregonDecoderV2 orscV2;

Timer t;

static volatile int irq_last = 0;
static volatile int irq_now = 0;

#define PB_SIZE 1024
static int pulse_buffer[PB_SIZE];
static volatile int pb_head = 0;
static volatile int pb_tail = 0;

void ext_int_1(void) 
    {
    irq_now = t.read_us();

    // determine the pulse length in microseconds, for either polarity

    pulse_buffer[pb_head] = irq_now - irq_last;
    pb_head = (pb_head + 1) % PB_SIZE;
       
    irq_last = irq_now;
    }

extern "C" void EINT3_IRQHandler /* __irq */ (void) 
    {
    // The "event" is connected to pin p10 which is LPC1768 P0_1
    // so lets trap that and ignore all other GPIO interrupts.    
    // Test for IRQ on Port0.
    if (LPC_GPIOINT->IntStatus & 0x1) 
        {
        // If P0_1/p10 rises, call atint()
        if (LPC_GPIOINT->IO0IntStatR & (1 << 1))  
            ext_int_1();
        else if (LPC_GPIOINT->IO0IntStatF & (1 << 1))  
            ext_int_1();
        }

    // Clear this and all other possible GPIO generated interrupts as they don't concern us.    
    LPC_GPIOINT->IO2IntClr = (LPC_GPIOINT->IO2IntStatR | LPC_GPIOINT->IO2IntStatF);
    LPC_GPIOINT->IO0IntClr = (LPC_GPIOINT->IO0IntStatR | LPC_GPIOINT->IO0IntStatF);
    }

void event_irq_init(void) 
    {
    // Use macro to set p10 as an input.
    p10_AS_INPUT; 

    // Enable P0_1/p10 for rising edge interrupt generation.
    LPC_GPIOINT->IO0IntEnR |= (1UL << 1);
    LPC_GPIOINT->IO0IntEnF |= (1UL << 1);

    // Enable the interrupt.
    NVIC_EnableIRQ(EINT3_IRQn);
    }


int main() {
    //
    //  Start the timer that counts microsonds
    //
    t.start();

    printf("\r\n");
    printf("[ookDecoder]\r\n");

    //
    //  Set up the pulse input
    //
    event_irq_init();

    while (1) 
        {
        int p = 0;
        if (pb_head != pb_tail)
            {
            p = pulse_buffer[pb_tail];
            pb_tail = (pb_tail + 1) % PB_SIZE;
            }

        if (p != 0) 
            {
            if (orscV2.nextPulse(p)) 
                {
                reportSerial("OSV2", orscV2);
                }
            }
        }
    }


4 comments on Decoder for Oregon Scientific Weather Sensor:

17 Nov 2013

Hi Jaime,

This looks really useful; just what I need. Would you also post the "IOMacros.h" file? I commented that out and also the line " p10_AS_INPUT; " and then it compiles successfully for the LP1768. How is the input port defined, in the Extern_C portion? I'm not able to understand that code. I would like to port this to the KL25Z platform, so I need to understand how to set the port in the lingo of that one. Thanks for your reply. Y.

18 Nov 2013

The IOMacros. header is SimpleIOMacros module by Andy Kirkham

http://mbed.org/users/AjK/code/SimpleIOMacros/

Here is the definition:

  1. define p10_AS_INPUT LPC_GPIO0->FIOMASK &= p10_CLR_MASK;

Regards, Jaime

18 Nov 2013

Hi Jaime, Thanks for the quick response. It compiles successfully now. Do you or someone else know where an equivalent hardware mapping contained in the IOMacros.h file for the LP1768 is located to perform the equivalent definitions for the KL25Z? Or is the IRQ initializer and IRQ event handler above done a different way on the KL25Z? Thanks for the help.

Y.

19 Nov 2013

Do not know. You may try to contact Andy Kirkham above and ask him since he wrote the IOMacros library.

Please log in to post comments.