Questions receiving Oregon v1 data

Hello everybody.

First I must say I'm a total beginner with Arduino. I've done many tutorials but now I'm a little bit stumped.

I want to receive data from a Oregon wireless THN128 module. I've googled around and it should emit using the Oregon v1.0 protocol. I've bought a cheap wireless receiver of the same band (433Mhz).

I've done the circuit to record through the line-in of my computer what the receiver is getting and it looks good (see attached picture, received.png), but when I try to receive something from Arduino, I can't get anything.

I've searched a lot, and if I've understood it correctly, the data should be using a Manchester encoding. I've tried many libraries (some of them downloaded from here) like RadioHead, ManchesterInterrupts, Manchester, ... by running their examples of receving data, but none of them showed any data. Maybe it's because they can't calculate the checksum of an oregon v1 protocol message, but I don't know.

So, here are my questions:

  • Is there any library out there that decodes Oregon v1 protocol? I've seen some of them to receive and even transmit data using Oregon v2, but they didn't work with my module.
  • If not, is there any way I can get the raw data the receiver is getting in Arduino? (without checking crc or anything).

I've seen the protocol description in http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf and I could try to adapt the sketches I've found that use Oregon v2 to Oregon v1, but first I want to know if I've correctly set up everything.

Thanks in advance from your help!

Hi Kokopa,

I have spent some time decoding Oregon Scientific V3.0 format which I know is not really what you are after. However I have documented quite a bit on the Manchester format and can offer you some working V3.0 examples, probably not so useful directly, but maybe my debugging routines could be useful in gaining some insight into how you could begin cracking the codes, or at least use the debugging ideas on other people's code.

Manchester Decoding: ArduinoWeatherOS (+plus older weather Rx433 examples)

Send and receive 433MHz: 433MHz_Tx_Rx (make your own Tx/Rx pair).

OSV3.0 Debug and Final Decoding: Weather-Station-OS-Sensors (Current up to date versions, plus how to add your own sensors)

The first link has some good general Manchester encoding/decoding info, however the manchester decoding algorithm is more concise in the third link. Combining this with info from OregonScientific-RF-Protocols.pdf will be very useful. The OS products are a good balance between cost and performance.

Cheers, Rob

Thanks for the links, Rob. In fact, I've read some of your documents before and they have been extremely useful!

After understanding how the Manchester encoding works, I've had partial success hacking the OS v1.0, although I must say most of the work (if not all) had been done before by Alexander Yerezeyev. He explains the 1.0 protocol here (the original site seems down). Now, after a little bit of trial and error I can send data from the Arduino to the Oregon Station! :smiley:

That's interesting, but what I really want is to receive data from the THN128 module. I have problems filtering the noise and detecting the preamble of the stream. I've been quite busy these days, but I've read about your "DebugManchester.ino", and that's the next thing I'm going to try.

Edit: I've used your Debug Manchester sketch and it seems promising! I couldn't receive the whole stream yet, but at least it detects the preamble (for the time being I can't send my cloned stream through another Arduino, so I have to work with the original Oregon transmission, which means I have just two tries each minute :D). Your code is very well commented. I'll try to adapt it to this Oregon model.

The Oregon Scientific V1.0 protocol is described in detail here: http://wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf It is pretty easy to decode.

This project has a decoder for OSV1.0 http://www.osengr.org/WxShield/Web/WxShield.html
I have seen others, with some searching you should be able to find them. I use PIC processors for this purpose, and can post some very simple C code if you like.

I found Alexander Yerezeyev's original AVR code for receiving OSV1.0 and attached it to this post. It is quite a mess. Here is my simplification/translation of the core of it, for an ATtiny26 running at 1 MHz:

//FUSE: intrc_osc 1MHz

/*
 Oregon Scientific v 1.0 decoder routine for 433 MHz THR138, THN128 temp sensors
 THR138 sensors send temperature info about every 30 seconds

 This code is distributed and licensed according to the terms of the
 Creative Commons Attribution-Sharealike License 3.0 
   http://creativecommons.org/licenses/by-sa/3.0/
 
 Began with spaghetti interrupt code (the timing constants were very useful)
 by Alexander Yerezeyev 2007-2009
 URL: http://alyer.frihost.net

 V 1.1 
 S. James Remington sjames_remington at yahoo.com 
 Jan. 2012

MCU: ATtiny26 @ 1MHz
*/

#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#define F_CPU 1000000UL
#include <util/delay.h>

const char   StartStr[]   PROGMEM = "OS_V1 decoder";
char       TextBuffer[21];  //string, 20 chars max

// OS_V1 message structure. A long int is equivalenced to four bytes

union shared {
uint32_t l;
uint8_t b[4];
} OS_msg;  //OS_msg.b[3] is most significant byte of OS_msg.l


void Delay10ms(int count)  //long delays
{
   int i;
   for (i=0; i<count; i++) _delay_ms(10);
}

#define RF_IN   (1<<PB6)            //RF data in  PB0
#define LED      (1<<PB3)

// The following defines are for a receiver with RF ON = active low output
// switch them if RF ON = active high
// RF_OFF or RF_ON, if nonzero, must be equal to RF_IN
      
#define RF_ON  0
#define RF_OFF (1<<PB6)

/* io_init 
 *  Initialize timers, pins and software serial port
 */

void io_init(void) 
{
   DDRA=0;               //initially all inputs
   DDRB=0;

   TX_init(9600);         //baud rate for LCD output on PA0

   PORTA = 0xFE;         //pullups on, TX idle   

   DDRB |= LED;         //led output   
   PORTB = 0xFF;         //pullups, led on

   TCCR0 = (1<<CS01)|(1<<CS00);    //Timer0 counts at system clock/64 = 64 usec ticks
   TCCR1A = 0;                  //could use Timer1 instead, but clock divisor would
                                //necessarily change during decoding process

// Timer1 counts at system clock/16 = 16 usec tick
// Timer1 frequency = 1e6/16/(OCR1C+1) = 341.53 Hz (2928 usec),
// ~ Manchester bit clock period.


   TCCR1B = (1<<CTC1)|(1<<CS12)|(1<<CS10); //clear on compare match with OCR1C
   OCR1C = 0xB6;                           //182 
      
}

/*
** Main loop waits for RF transmissions, decodes OS V 1.0 packet, 
** displays decoded packet on serial output
*/
   int main (void)
{  
    unsigned char ont,offt;     // time between edges
    unsigned char ErrCode;
    unsigned char BitValue=0;
    unsigned char BitCnt;       // received bits counter
    unsigned char PreambleCnt=0;   // counts preamble pulses
    unsigned int crc;
    unsigned char Channel,Th1,Th2,Th3,LowBatt,Sign, HH, SensorID,unknown;

// the following define expected interval timing and allowed uncertainty for tick rate 64 uSec

#define deltaT 5
#define InRange(TVAL, TSETUP, dT) ((TVAL<=(TSETUP+dT))&&(TVAL>=(TSETUP-dT)))

#define TrfON 27                  // (1728us) RF ON time v1 protocol
#define TrfOFF 19                // (1216us) RF OFF time for v1 protocol

#define TsyncBitON 89            // (5696us) RF ON sync pulse for v1 protocol
#define TsyncBitOFF 66            //(4224us) First RF OFF sync pulse for v1 protocol
#define TsyncBitOFF2short 86       //(5504us) Second RF OFF sync pulse for v1 protocol
#define TsyncBitOFF2long 105       //(6720us) Second Long RF OFF sync pulse for v1 protocol

   io_init();

   cls();   //clear LCD screen

   TX_puts_f(StartStr);

   while (1)
   {
      
      PORTB &= ~LED;
      ErrCode=0;
      PreambleCnt=0;

// wait for RF on

   while ((PINB & RF_IN) == RF_OFF);      

// look for V1.0 preamble
      
   while(1)
   {

		TCNT0 = 0;							//zero counter
		while ((PINB & RF_IN) == RF_ON);		//wait for RF off
		ont=TCNT0;

		TCNT0 = 0;
		while ((PINB & RF_IN) == RF_OFF);
		offt = TCNT0;
		TCNT0 = 0;

		if( InRange(ont,TrfON,deltaT) && InRange(offt,TrfOFF,deltaT)) PreambleCnt++;

		if ( (PreambleCnt>8) && InRange(offt,TsyncBitOFF,deltaT)) break;  //long off sync?

    } //end while PreambleCnt
 
	PORTB |= LED; //got a candidate

// upon exit of above loop, RF is on

	while ((PINB & RF_IN) == RF_ON); //check for 5.7 ms high

	ont = TCNT0;
    TCNT0 = 0; //restart timer  

	if ( !InRange(ont, TsyncBitON, deltaT)) ErrCode |=1;  //1st high pulse not of proper length
         
    while ((PINB & RF_IN) == RF_OFF); //wait for RF on

    offt=TCNT0;

// Check for valid 2nd RF off sync (two possibilities)

    TCCR1B |= (1<<PSR1);             //clear Timer 1 prescaler
    TCNT1 = 0;                      //reset and start Timer1 in CTC mode (bit timer)

    if InRange(offt, TsyncBitOFF2long, deltaT)// T == TsyncBitOFFow2Long |____| ( Long syncBit2 is Detected)
    {                       
       BitValue=0;                   //long sync, the first message bit is a zero
    }
    
    else if InRange(offt, TsyncBitOFF2short, deltaT)// T == TsyncBitOFFow2short |__| ( Short syncBit2 is Detected)
    {                                              
       BitValue=1;                   //short sync, first message bit is a one

       while ((PINB & RF_IN) == RF_ON); //wait for falling edge to synchronize bit clock

       TCCR1B |= (1<<PSR1);             //clear Timer 1 prescaler
       TCNT1 = 0;                      //reset Timer1 in CTC mode as bit interval timer
     }
                           
     else ErrCode |= 2;  // Not a valid low sync bit

// if OK so far, get rest of message

   if(ErrCode == 0) {

/*
** Timer 1 bit clock is now synchronized to bit transition edges, assuming 342 Hz data rate
** store first bit of message
*/
     BitCnt = 1;
	 OS_msg.l = 0; //clear message buffer

// shift in data bit

     if (BitValue) OS_msg.b[3] |= 0x80; 

/*
** loop through next 31 bits, decoding Manchester code.
** This is done by synchronizing the bit clock to the first bit transition
** then sample RF before next bit transition. "0" = RF_off_to_on, "1" = RF_on_to_off 
*/
     while (BitCnt<32)
     {  

        if ((TCNT1 & 0x80) == 0x80)       //about 2/3 through bit period
        { 

// sample RF before next bit transition
// shift message right and add in new data bit

	    	OS_msg.l >>= 1; 
    	 	if ((PINB & RF_IN) == RF_ON) OS_msg.b[3] |= 0x80; //got a "1"
        
		    BitCnt++; 

		    while ((TCNT1 & 0x80) == 0x80); //wait for Timer1 rollover

         } //end if TCNT1

      } //end while BitCnt

// check message integrity

     crc = OS_msg.b[0] + OS_msg.b[1] + OS_msg.b[2];
     crc = (crc & 0xFF)+(crc>>8);       //sum in overflow
     if (crc != OS_msg.b[3]) ErrCode |=4;  //checksum not OK

     Channel  = (OS_msg.b[0] & 0xc0)>>6;      //0, 1, 2 = channels 1,2,3
     unknown  = (OS_msg.b[0] & 0x30)>>4;   //temp rising/falling flags
     SensorID = (OS_msg.b[0] & 0x0f);   //rolling code, assigned by sensor upon reset

     Th3 = (OS_msg.b[1] & 0x0f);      //BCD fraction digit of temp
     Th2 = (OS_msg.b[1] & 0xf0)>>4;   //BCD digit 2 of temp
     Th1 = (OS_msg.b[2] & 0x0f);      //BCD digit 1 of temp

     Sign    = (OS_msg.b[2] & 0x20);    //nonzero = negative temp
     HH       = (OS_msg.b[2] & 0x40);   //nonzero = HH.H malfunction?
     LowBatt   = (OS_msg.b[2] & 0x80);  //nonzero = low battery

// display message on LCD, hex except for temperature in C

     cls();

     TX_puts(ltoa(OS_msg.l,TextBuffer,16));

     TX_putc(' '); TX_puts(itoa(ErrCode,TextBuffer,16));
 
     crlf();

     if(Sign) TX_putc('-');  //display temp as XX.X (C)
     TX_putc(Th1+'0');
     TX_putc(Th2+'0');
     TX_putc('.');
     TX_putc(Th3+'0');

     TX_putc(' ');
     TX_puts(itoa(Channel,TextBuffer,16));  //channel #
     TX_putc(' ');
     TX_puts(itoa(Sign,TextBuffer,16));      //sign flag
     TX_putc(' ');
     TX_puts(itoa(LowBatt,TextBuffer,16));   //low battery indicator
     TX_putc(' ');
     TX_puts(itoa(SensorID,TextBuffer,16));   //sensor ID
     TX_putc(' ');
     TX_puts(itoa(unknown,TextBuffer,16));   //mystery bits

     PORTB &= ~LED;  //led off

      } //end if (ErrCode == 0)
    } //end while(1)
 }

RXOSv1.zip (62.8 KB)

Warning: I think the version of the ATtiny26 code posted above is an old one that may have a problem.
The correct version is posted below. I apologize for the mistake.

/*
 Oregon Scientific v 1.0 decoder routine for THR138 temperature sensor (and others)

 S. James Remington sjames_remington at yahoo.com

 Began with spaghetti interrupt code (the timing constants were handy)
 by Alexander Yerezeyev 2007-2009
 URL: http://alyer.frihost.net

useful documentation: wmrx00.sourceforge.net/Arduino/OregonScientific-RF-Protocols.pdf

  MCU: ATtiny26 @ 1MHz, output to TTL serial LCD display
*/
#include <inttypes.h>
#include <stdlib.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>

#define F_CPU 1000000UL
#include <util/delay.h>

const char   StartStr[]   PROGMEM = "OS_V1 decoder";
char       TextBuffer[21];  //string, 20 chars max

unsigned char RXdata[4]; //OS_V1 message buffer

void Delay10ms(int count)  //long delays
{
   int i;
   for (i=0; i<count; i++) _delay_ms(10);
}

// include software uart routines

#include "suart.c"


#define RF_IN   (1<<PB6)            //RF data in  PB6, ACTIVE LOW
#define LED      (1<<PB3)

// The following defines are for a receiver with RF ON = active low output
// switch them if RF ON = active high
// also, RF_OFF definition must match input bit of port connected to receiver

#define RF_ON  0
#define RF_OFF (1<<PB6)

/*** io_init
 *   Initialize timers, pins and software serial port
***/

void io_init(void)
{
   DDRA=0;               //initially all inputs
   DDRB=0;

   TX_init(2400);         //baud rate for LCD output on PA0

   PORTA = 0xFE;         //pullups on, TX idle

   DDRB |= LED;         //led output
   PORTB = 0xFF;         //pullups, led on

    TCCR0 = (1<<CS01)|(1<<CS00);    //Timer0 counts at system clock/64 = 64 usec ticks
    TCCR1A = 0;

   // Timer1 frequency = 1e6/16/(OCR1C+1) = 341.53 Hz (2928 usec, ~ bit clock period).
                                          //Timer1 counts at system clock/16 = 16 usec tick
    TCCR1B = (1<<CTC1)|(1<<CS12)|(1<<CS10); //clear on compare match with OCR1C
    OCR1C = 0xB6;                             //182


}

   int main (void)
{
    unsigned char TimerValue=0;     // time between edges
    unsigned char ErrCode;
    unsigned char BitValue=0;
   unsigned char ByteNum;
    unsigned char BitCnt;       // received bits counter
    unsigned char PreambleCnt=0;   // counts preamble pulses
   unsigned int crc;
   unsigned char Channel,Th1,Th2,Th3,LowBatt,Sign, HH;


//******************** Oregon Scientific Protocol V 1.0 decoder begins ****************************

// the following define interval timing and allowed uncertainty
#define deltaT 16
#define InRange(TVAL, TSETUP, dT) ((TVAL<=(TSETUP+dT))&&(TVAL>=(TSETUP-dT)))

#define TminON 0x1B         // (1750us) Tmin High Level for v1 protocol
#define TminOFF 0x13       // (1190us) Tmin Low Level for v1 protocol
#define TmaxON  0x31;      // (1500us) Tmax High Level for v1 protocol
#define TmaxOFF 0x2B;      // (1500us) Tmax Low Level for v1 protocol

#define TstartBitON 0x59         //High sync pulse for v1 protocol
#define TstartBitOFF 0x42         //First Low sync pulse for v1 protocol
#define TstartBitOFF2short 0x56    //Second Short Low sync pulse for v1 protocol
#define TstartBitOFF2long 0x69       //Second Long Low sync pulse for v1 protocol

   io_init();

   cls();   //clear LCD screen

   TX_puts_f(StartStr);

   while (1)
   {

      PORTB &= ~LED;
      ErrCode=0;
      PreambleCnt=0;

// wait for RF on

   while ((PINB & RF_IN) == RF_OFF);

// look for V1.0 preamble

      while(PreambleCnt<12)
      {
         while ((PINB & RF_IN) == RF_ON); //RF is on, wait for RF off

         TCNT0=0;             //Start T0

         while ((PINB & RF_IN) == RF_OFF); //wait for RF on

         TimerValue=TCNT0;     // Read timer value

         if (!InRange(TimerValue, TminOFF, 10)) break; // continue if T ~ Tmin |_| (Preamble off period is detected)

         PreambleCnt++;
      }

     if (PreambleCnt<10) ErrCode |= 1;  //probably not OS V1.0 protocol

     if (PreambleCnt>9) PORTB |= LED; //LED on, preamble detected

// RF is now on. Check for valid 1st RF off sync (from last timed interval)

      if (!InRange(TimerValue, TstartBitOFF, deltaT)) ErrCode |= 2; // T == Tstartbit Low |__| (Sync is Detected)

      while ((PINB & RF_IN) == RF_ON); //wait for RF off

       TCNT0 = 0; //restart timer

        while ((PINB & RF_IN) == RF_OFF); //wait for RF on

        TimerValue=TCNT0;

// RF is now on. Check for valid 2nd RF off sync (two possibilities)

         TCCR1B |= (1<<PSR1);          //clear Timer 1 prescaler
        TCNT1 = 0;                      //reset and start Timer1 in CTC mode (bit timer)

        if InRange(TimerValue, TstartBitOFF2long, deltaT)// T == TstartBitOFFow2Long |____| ( Long Startbit2 is Detected)
        {
            BitValue=0;                   //long sync, the first message bit is a zero
        }
        else if InRange(TimerValue, TstartBitOFF2short, deltaT)// T == TstartBitOFFow2short |__| ( Short Startbit2 is Detected)
        {
            BitValue=1;                   //short sync, first message bit is a one

            while ((PINB & RF_IN) == RF_ON); //wait for falling edge to synchronize bit clock

            TCCR1B |= (1<<PSR1);          //clear Timer 1 prescaler
           TCNT1 = 0;                      //reset Timer1 in CTC mode as bit interval timer
         }

         else ErrCode |= 4;  // Not a valid sync bit

// if OK so far, get rest of message

         if(ErrCode == 0)
       {

// Timer 1 bit clock is now synchronized to bit transition edges, assuming 342 Hz data rate
// store first bit of message

         BitCnt=0;
       ByteNum = (BitCnt>>3);

        if (BitValue == 0)   RXdata[ByteNum] &= ~(1<<(BitCnt & 7));
        else             RXdata[ByteNum] |=  (1<<(BitCnt & 7));

// loop through next 31 bits

         while (BitCnt<32)
            {
             if ((TCNT1 & 0x80) == 0x80)       //wait until about 2/3 through bit period
               {

 // sample RF before next bit transition (sample multiple times?)

                 if ((PINB & RF_IN) == RF_ON) BitValue = 1;
                 else BitValue = 0;

                 BitCnt++;
             ByteNum = (BitCnt>>3);

             if (BitValue == 0)    RXdata[ByteNum] &= ~(1<<(BitCnt & 7));
              else             RXdata[ByteNum] |=  (1<<(BitCnt & 7));

              while ((TCNT1 & 0x80) == 0x80); //wait for Timer1 rollover

            }

             } //end while (BitCnt<32)

         PORTB &= ~LED;  //led off

//  example valid message
//   RXdata[0]=0x23;
//   RXdata[1]=0x70;
//   RXdata[2]=0x01;
//   RXdata[3]=0x94;

         // message integrity check

         crc = RXdata[0]+RXdata[1]+RXdata[2];
         crc = (crc & 0xFF)+(crc>>8);       //sum in overflow
         if (crc != RXdata[3]) ErrCode |=8;  //checksum not OK

         Channel = RXdata[0] & 0xc0;     //0, 40h, 80h = channels 1,2,3

         Th3 = (RXdata[1] & 0x0f);     //BCD fraction digit of temp
         Th2 = (RXdata[1] & 0xf0)>>4;  //BCD digit 2 of temp
         Th1 = (RXdata[2] & 0x0f);     //BCD digit 1 of temp

         Sign    = (RXdata[2] & 0x20);  //nonzero = negative temp
         HH       = (RXdata[2] & 0x40);  //nonzero = HH.H overflow?
         LowBatt   = (RXdata[2] & 0x80);  //nonzero = low battery

// display message on LCD, hex except temperature in C

         cls();

         TX_puts(itoa(RXdata[0],TextBuffer,16));
         TX_puts(itoa(RXdata[1],TextBuffer,16));
         TX_puts(itoa(RXdata[2],TextBuffer,16));
         TX_puts(itoa(RXdata[3],TextBuffer,16));
         TX_putc(' '); TX_puts(itoa(ErrCode,TextBuffer,16));

         crlf();

         if(Sign) TX_putc('-');
         TX_putc(Th1+'0');
         TX_putc(Th2+'0');
         TX_putc('.');
         TX_putc(Th3+'0');

         TX_putc(' ');
         TX_puts(itoa(Channel,TextBuffer,16));
         TX_putc(' ');
         TX_puts(itoa(Sign,TextBuffer,16));
         TX_putc(' ');
         TX_puts(itoa(LowBatt,TextBuffer,16));
         Delay10ms(500);

      } //end if (ErrCode == 0)
    } //end while(1)
 }

Hi Folks,
Looking up Alexander's work mentioned above it appears that the Sync part of the packet goes for bits that are longer than the rest of the encoding.

It's major departure from standard Manchester encoding is the longer synchronising transitions do not appear to be direct multiples of the header bits or the data packet. My decoding program relies heavily on the stream of bits to have a waveform length for either a 0 or a 1 to be the same, including the synch bit ie the first 0.

I have cut and pasted a section in a copy of the waveform diagram from Alexander's work so some header waveforms line up underneath to check the durations of the timing.

Correct me if I am wrong but the synch waveforms do not easily form into a the right size???

Rob

I don't think there is any way (or any point in the attempt) to fit the preamble/sync part of an OS V 1.0 message into a Manchester scheme. V 1.0 was probably ad hoc, and was long ago abandoned.

Yes I agree, I think the engineers at OS missed a major point in the Manchester encoding, that is a constant bit rate was all that was required to decode the packets (as V3.0 shows quite successfully).

Not sure why they chose to do that sort of Synch waveform unless they were hoping throw hackers off the scent. It does make decoding the sensor though a lot more more than messy, but not impossible.

I would be interested in seeing an attachment of the Audio file that you made of the signal so I could look at it in detail. I am not offering some glorious solution, but I am intrigued by the design of the packet and would be interested to see how it was before V3.0 arrived.

Let us know how you go I would be interested to study your solution.

Cheers, Rob

PS My solution would be to buy an up to date V3.0 sensor. But that is not always possible, and who likes to duck a really good challenge :wink:

You may wish to have a look here.
http://wmrx00.sourceforge.net/

There are several versions for the Arduino which will Decode Oregon V1,V2 and V3.

Ive built 3 Arduino based weather station decoders using the Weather Shield SW and it works very well.
I cant comment on decoding V1 Sensors as I have only used it with V3.
The data output from the Arduino is a simple text based message which is easily understood.
It will also interface very easily with a PC program called Weather Station Data Logger which can decode a
variety of common Weather Stations.

@James:
Thanks! I've studied your code and it confirmed me many things. I'm trying to do something similar: detect the preamble, then the sync and then the message. After many, many tries I think I got it!

@Rob:
Yes, there is some kind of "sync" pulses between the preamble and the message itself (you can see the code James attached calling them startbits). I'm attaching to this post a wav with a transmission of my module (it's 21,9ºC on channel 2, which should translate to the code: 00111110 10011000 01000001 11101000). I've made a picture of it where you can see the pulses and how I decoded them.

@mauried:
Thanks, I've checked that site. I took many information from there, but I couldn't make the v1 work, so I tried to do it by myself.

By the way, while testing the transmissions I've found something funny about the polarity. The recorded wave shows the opposite values of what I read when I use digitalRead(). For example, originally, in the sync part Audacity shows high-low-high. If I use digitalRead() the values I get are low-high-low. Since I'm not sure about how this receiver, or my sound card line in works, I just inverted the wave. That's what I've attached here. That inverted wave follows the Manchester encoding with normal polarity (the bit value is the one before the change in the pulse). Does that make sense? I've read that v.1.0. should have reverse polarity, but in my case it looks like it doesn't (or maybe I've connected the receiver to the Arduino with the polarity inverted?).

About the sync part, you can't code it with a Manchester encoding, so I'm doing something similar to what James does in his code.

My approach is to detect the second pulse, then position myself at the beginning of the last third pulse and wait a little, till I'm at the beginning of the message. It looks like it's the only correct way to do this (the protocol says: "The first data sample point (clock edge) is not always marked by an RF transition and must be measured from the end of the long sync pulse"), so my problem was finding the exact frequency of that two pulses (they are written in the protocol, but I had to make many tries till it worked).

I've made lots of tests (I have just one Arduino at the moment, so I did everything with the original Oregon Scientific sensor) but finally it looks it's working! I have some false positives (just one or two each minute) detecting the preamble, but since later it detects the transmission is not valid it's not specially important. I may tweak the delays a little bit more to see if I can get more precision.

I probably end up building some TinyTx sensors for my home and discarding this sensor, but I must say it's been quite fun! Since it is my first project I tried to document everything. I put my code here if you want to check it.

Thanks everyone for your help!

Oregon_21_9_Canal_2_Inverted.zip (75.7 KB)

Receive_Oregon_v1.ino (8.1 KB)

I made my own Oregon Temp / Humidity Sensors which emulate a THGR810.
Simply used a DHT22 and a small PIC 12F688.
Would be fairly easy to adapt to something like a ATTiny.
Its quite easy if you simply want to decode the data yourself, but a lot harder if you want to display the data
on an Oregon Console.

Hi Kokopa,

I like your style of programming very neat and concise. Pretty easy to follow. You graphical documentation is excellent and very helpful. However if I may, I would like to suggest some things that you might like to consider in the processing of the waveform. I think you can improve its reliability with much more looping until data transitions occur. Advice is probably the last thing you feel like at this stage (after all your efforts), I hate being lectured so please forgive me if this offends you in anyway.

When you are processing the preamble after looping for a rising edge, you appear to set a minimum of 10 for the preamble bits, but process them in a set "for" loop, and if successful completed, then keep looking for the synch waveform (I presume here this part of the search just crunches and ignores any remaining preamble?).

I preferred to completely process all the preamble (well as much as the Rx may see, and this can vary of course) as a waveform until it no longer matches the preamble waveform. This could either be an error in the preamble (if error then exit) or the beginning of the synch pattern . This is then tested to see if at the right spots the waveform is instead conforming to the synch pattern (if synch waveform error then exit) and if so then gather the subsequent bit data (if data bit waveform error then exit). If all Ok process to human readable form.

One of the key aspects to the reliability of my program is the concept of predicting the next data transition from the start of the next Bit Waveform. It does this by looping for every data bit transition in the packet- if it does not conform, it flags an error and exits. This looping and realignment of the timing of the program on each bit to the waveform allows it to be very tolerant of small changes in timing (eg due to temp on the Tx or Rx, or processing calculations), but still able to very reliably detect any real errors in the bit waveform.

So in your case, I would begin detecting the preamble waveform, checking each 'bit', counting them and noting when more than MAXPREAMBLE is exceeded. When the synch sequence begins then it will appear as a possible "preamble error" or the "synch" waveform, and needs to be further examined to see if the waveform actually complies with the synch period. Personally I would immediately do a long delay that should land me about 1/4 the way into the next hi (0.050s), sample it again twice (eg 0.051 and 0.052s) and if both high, assume it was the synch bit (if not, flag error and exit). Then loop for the falling edge at about 0.054s. Then sample again at 0.56 and 0.58s, if both lo then continue (if not, flag error and exit) and loop for the rising edge of the first "0" waveform, and resynch it all in to the following data bit waveforms.

a5a4a3a2a1a0 -> Rolling code that changes each time a sensor is reset or changes batteries (?).

That is if "a0" is always a zero, then loop for a rising edge (at about 0.061s), then begin decoding the data packet proper. If not then this bit will need to be changed to handle finding a one first. Do you know the numbers for the rolling code, ie are the rolling codes always even for example ie A0=0? The V3.0 always includes the synch 0 in the data stream and it just means the upper nibble of the ID number is always even eg "0xA" in data, and 0x5 or B0101 in RF stream as it reverses nibble rather than bytes .

Either way at least aligning the program's timing on the falling edge in the synch waveform (0.054s) would be better than the first detected rising edge of the preamble (0.010s).

Why you might say do the "loop and wait for an edge" all the time? Well mainly because it continually resynchs the detector with the Tx, and this does allow for oscillator variations caused temp or humidity etc, but more importantly it allows for the differences in time to do the processing of the bits that come in. For example processing time for a Zero will be slightly different to a One etc. The delay(duration) commands can give the overall bigger delays required for waveform checking, but small errors can become significant if everything was taken from one part of the early waveform as the only synching point.

If you check my Transmitter example it should be very easy to adapt it to the above protocol and provide another sensor on another channel.

Sorry to drone on,
I hope you can glean something valuable from the above.

Cheers, Rob

PS In fact I reckon you could do away with detecting the preamble detection and just look for the synch waveform. The preamble is really only there to stabilise the Rx and in this case the synch waveform is so distinctive if it was found it could be assumed the preamble had successfully occurred.

robwlakes:
Advice is probably the last thing you feel like at this stage (after all your efforts), I hate being lectured so please forgive me if this offends you in anyway.

Not only it's not an offense, I'm grateful for your advice! It's the only way to learn :slight_smile:

robwlakes:
When you are processing the preamble after looping for a rising edge, you appear to set a minimum of 10 for the preamble bits, but process them in a set "for" loop, and if successful completed, then keep looking for the synch waveform (I presume here this part of the search just crunches and ignores any remaining preamble?).

As you say later, detecting the preamble is really not necessary. However, I did it this way because I wasn't sure that my cheap receiver could receive the whole preamble (12 ones) before it had adjusted the gain. So I put just a few as a first check and then wait for the sync (ignoring the rest of the preamble).

robwlakes:
So in your case, I would begin detecting the preamble waveform, checking each 'bit', counting them and noting when more than MAXPREAMBLE is exceeded. When the synch sequence begins then it will appear as a possible "preamble error" or the "synch" waveform, and needs to be further examined to see if the waveform actually complies with the synch period. Personally I would immediately do a long delay that should land me about 1/4 the way into the next hi (0.050s), sample it again twice (eg 0.051 and 0.052s) and if both high, assume it was the synch bit (if not, flag error and exit). Then loop for the falling edge at about 0.054s. Then sample again at 0.56 and 0.58s, if both lo then continue (if not, flag error and exit) and loop for the rising edge of the first "0" waveform, and resynch it all in to the following data bit waveforms.

That would be the best way, and extremely reliable. However, one of the things I like more about this Manchester encoding (which I didn't even know before this small project) is that, even if I make a false positive detecting a sync part, the message part will throw an error and safely discard the transmission. So I can make a simpler detection of the sync, because later the code will auto-detect if we have a valid message.

robwlakes:
Do you know the numbers for the rolling code, ie are the rolling codes always even for example ie A0=0?

At first, I tried to detect the third part of the sync (the low value), and then wait for a rising edge. I thought the a0 was always zero (at least I've always received a zero in my tests with my sensor). However the protocol says that that bit "is not always marked by an RF transition and must be measured from the end of the long sync pulse". So now my first positioning is at the falling edge between the second and third parts of the sync (and then wait a fixed duration that should be the length of that third part).

robwlakes:
Either way at least aligning the program's timing on the falling edge in the synch waveform (0.054s) would be better than the first detected rising edge of the preamble (0.010s).

That's what I did :wink: I aligned twice, one on the preamble part, and another when we leave the high pulse of the sync. Aligning on the preamble was redundant.

robwlakes:
Why you might say do the "loop and wait for an edge" all the time? Well mainly because it continually resynchs the detector with the Tx, and this does allow for oscillator variations caused temp or humidity etc, but more importantly it allows for the differences in time to do the processing of the bits that come in. For example processing time for a Zero will be slightly different to a One etc. The delay(duration) commands can give the overall bigger delays required for waveform checking, but small errors can become significant if everything was taken from one part of the early waveform as the only synching point.

You are right! I've found a potential problem in my code. I had my sketch aligned to the edge of the sync, but, after the sync part, through the message, it always moves in steps of one quarter of wave. That means that if the delays I'm using are not accurate enough (and they probably aren't), I could be out of sync after a few cycles. As the message is quite short (32 bits), the code worked and I got away with it, but if the conditions change then it could break.

I've changed my code to solve this. First, I don't look for the preamble at all, I just go for the sync. In fact, the code works if you only look for the second part (the high zone) of the sync, but I detect too the first low part because there were too many fake positives (although the message for them is invalid). Then align to the falling edge of the second zone, wait for what I think is the delay of the third pulse (5100 microseconds) and start a loop to read bits: wait one quarter, read possible value, wait for edge (this realigns the timings), wait one quarter, confirm the value has changed and then wait for another quarter to be at the beginning of the next bit. This should be much more robust than before.

Receive_Oregon_v1.ino (8.48 KB)

Away from home, so sorry can't enjoy your code just yet. However it sounds like things have moved along to your satisfaction. Pity my Google Tablet can"t read an 'arduino.ino' file.
I will try to have a look in a couple of days time.
Now running the "Arduino IDE" and slaves off a Google Tablet would be fun. Anybody done that?
Cheers
Rob

I had my sketch aligned to the edge of the sync, but, after the sync part, through the message, it always moves in steps of one quarter of wave. That means that if the delays I'm using are not accurate enough (and they probably aren't), I could be out of sync after a few cycles. As the message is quite short (32 bits), the code worked and I got away with it, but if the conditions change then it could break.

I've changed my code to solve this. First, I don't look for the preamble at all, I just go for the sync. In fact, the code works if you only look for the second part (the high zone) of the sync, but I detect too the first low part because there were too many fake positives (although the message for them is invalid). Then align to the falling edge of the second zone, wait for what I think is the delay of the third pulse (5100 microseconds) and start a loop to read bits: wait one quarter, read possible value, wait for edge (this realigns the timings), wait one quarter, confirm the value has changed and then wait for another quarter to be at the beginning of the next bit. This should be much more robust than before.

Have you improved the reliability?

Cheers, Rob

robwlakes:
Have you improved the reliability?

The message is quite sort and in the tests I did before the change the reliability was good. However, I've made much more tests after the change (realigning the timing each bit) and the reliability is extremely good. I didn't lose a single message after many hours.

If you remove the first part of the code (the one that says it's optional), the reliability is worse, because sometimes it confuses the end of the message with a sync part, so you may lose the second repeated message. It's still not bad (you get one message and sometimes even both of them) but since you don't need it, I wouldn't remove it.

I didn't lose a single message after many hours.

Well Kokopa that sounds really excellent!!! :wink: That's put a smile on my dial!!

I think you are right about the header's purpose and what you are saying about making sure it does not get confused by the data as header etc is good thinking. It does not require much code and the Arduino is not doing much apart from that anyway, so why not go for the more complete solution.

So how does this all fit in with the overall objective of the project? Are you now close to finishing it up?

Cheers, Rob

Thank! I know this small project is quite easy (specially thanks to the work and help from people like you!), but since I'm starting with Arduino, sensors and so on, I'm quite proud and I've learned a lot of things. About the project, one of my problems is the receiving range. I can't put a good antenna on the receiver (I know about the optimal length and now I've ordered one to see if I get better results).

In the meantime, I'm trying to do a TinyTx (following the tutorials from Nathan Chantrell). I have a raspberry and my final project would be to make it log data from several sensors around my home. Decoding the Oregon through the Raspberry may be cpu consuming, but with the TinyTx there are many solutions already available (like the RFM12Pi).

Those other projects featuring the Tiny are very interesting, however they do point to one thing, in my mind at least, is that is best to have a dedicated Arduino style CPU doing the receiving of the 433MHz. I have yet to see a good, simple project that clearly shows how to directly intercept 433MHz with a RPi.

Hence I think using a Tiny as a pre-processor so to speak is probably a good idea. I have found problems with signal strength as well. Most times though, the "Bad reception" has been because of flaky code, rather than RF problems. However to allay my suspicions I have experimented with what would be the "best" antenna and found this solution to be particularly effective. This is a photo of the setup before I went the next step. I have added to it graphically to explain the setup.

The first experiment was to have a length of TV coaxial cable with the shield stripped back for 17cm at the other end from the Tx or Rx (Yellow line). This was fairly good, but I found when I added a matching length of hookup wire to (where I had stripped off the shielding) the shielding, (Green) the performance really moved up a notch or two. The extra RF in/out helped me debug a few difficult problems as I the program was working OK with a strong signal, and it least gave me a lead where to correct the program.

The core of the coax (Yellow) and the balanced other half of it (Green) makes a simple dipole and connecting it to the earth (the one alongside the signal) and signal on the board made a really huge difference. The Arduino that I use 24/7 to gather the weather data off the Oregon Sci sensors is fairly well central to all three. However my development computer is tucked away in a small room with no windows and brick on two sides. Using this antenna was really helpful.

Cheers, Rob