Arduino Yun Reading WH1080 using AUREL RX-4MM5

Here’s the sketch, it just reads and dumps to the console, the bridge can be used to send the data to the GNU/Linux side of the Yun.

See the other post on doing this with a Raspberry Pi for some code to turn the data into something useful.

I’m using the MCU of the Yun to do the RF stuff, and using the AUREL RX-4MM5 (a proper OOK receiver), it seems a lot more dependable than the Raspberry Pi + RFM01 (or RFM12B).

#include <Console.h>

#define SHORT_PULSE  500
#define LONG_PULSE  1500
#define SHORT_MARGIN 300
#define LONG_MARGIN 300

int val = 0;
unsigned long transition_t = micros();
unsigned long now, duration;

#ifdef DEBUG
unsigned long short_min = 9999, short_max = 0;
unsigned long long_min = 9999, long_max = 0;
unsigned int pulse_buffer[200];
unsigned int pb_idx = 0;
#endif

#define BUFFER_SIZE  16

byte byte_buffer[BUFFER_SIZE];
byte buffer_idx = 0;

byte sig_seen = 0;

unsigned short shift_register = 0;
byte bit_count = 0;

/*
* Function taken from Luc Small (http://lucsmall.com), itself
* derived from the OneWire Arduino library. Modifications to
* the polynomial according to Fine Offset's CRC8 calulations.
*/
uint8_t _crc8( uint8_t *addr, uint8_t len)
{
        uint8_t crc = 0;

        // Indicated changes are from reference CRC-8 function in OneWire library
        while (len--) {
                uint8_t inbyte = *addr++;
                uint8_t i;
                for (i = 8; i; i--) {
                        uint8_t mix = (crc ^ inbyte) & 0x80; // changed from & 0x01
                        crc <<= 1; // changed from right shift
                        if (mix) crc ^= 0x31;// changed from 0x8C;
                        inbyte <<= 1; // changed from right shift
                }
        }
        return crc;
}

void setup() {

  // put your setup code here, to run once:
  pinMode(2, INPUT);

  Bridge.begin();
  Console.begin();

  while (!Console) {
    ; // wait for Console port to connect.
  }

  // prints title with ending line break
  Console.println("WH1080RF Monitor");
  Console.flush();
}

void loop() {

  // put your main code here, to run repeatedly:

  int newVal;
  for(int i=0; i < 10; i++) {
    newVal += digitalRead(2) ? 1 : 0;
    delayMicroseconds(5);
  }
  newVal = (newVal + 5) / 10;

  /*
   * Handle situations where the clock has rolled over
   * between transitions (happens every ~70 mins).
   */
  now = micros();

  if(transition_t <= now)
    duration = now - transition_t;
  else
    duration = (~transition_t) + now;

  if(newVal != val) {  // then transitioning state

    /*
     *  We update the transition time for the pulse, and
     *  change the current state of the input value.
     */
    transition_t = now;
    val = newVal;

    /*
     *  If the pulse width (hi or low) is outside the
     *  range of the Fine Offset signal, then ignore them.
     */
    if(duration < (SHORT_PULSE - SHORT_MARGIN) 
        || duration > (LONG_PULSE + LONG_MARGIN)) {
      // Meaningless pulse
      return;
    }

    /*
     *  If we reach here, then we have seen a potentially
     *  valid pulse. Shift the bit into the register.
     */
    if(newVal == 1) {
      // rising edge of a pulse (0 -> 1)
    } else {
      // falling edge of a pulse (1 -> 0)
      if( duration >= (SHORT_PULSE - SHORT_MARGIN) && duration <= (SHORT_PULSE + SHORT_MARGIN) ) {
        // short pulse is binary '1'
        shift_register = (shift_register << 1) | 0x01;
        bit_count++;

        #ifdef DEBUG
        if(duration < short_min) short_min = duration;
        if(duration > short_max) short_max = duration;
        if(pb_idx < 200) pulse_buffer[pb_idx++] = duration;
        #endif

      } else if(duration >= (LONG_PULSE - LONG_MARGIN) && duration <= (LONG_PULSE + LONG_MARGIN)) {
        // long pulse is binary '0'
        shift_register = (shift_register << 1);
        bit_count++;

        #ifdef DEBUG
        if(duration < long_min) long_min = duration;
        if(duration > long_max) long_max = duration;
        if(pb_idx < 200) pulse_buffer[pb_idx++] = duration;
        #endif
      }
    }

    // Look for signature of 0xfa (4 bits 0xf0 pre-amble + 0xa)
    if((shift_register & 0xff) == 0xfa && buffer_idx == 0) {
      // Found signature - discard pre-amble and leave 0x0a.
      shift_register = 0x0a;
      bit_count = 4;
      sig_seen = 1;  // Flag that the signature has been seen.

      #ifdef DEBUG
      pb_idx = 0;
      #endif

    } else if(bit_count == 8 && sig_seen) {
      // Got a byte, so store it if we have room.
      if(buffer_idx < BUFFER_SIZE)
        byte_buffer[buffer_idx++] = (byte)(shift_register & 0xff);
      else
        Console.println("Overflow on byte");
      shift_register = 0;
      bit_count = 0;
    }

  } else {

      /*
       *  Have we reached timeout on duration? If so, process any
       *  bytes present in the buffer and then reset the state
       *  variables.
       */    
      if(duration > 5000) {

        if (buffer_idx > 0) {

          /*
           *  Dump the bytes to the console.
           */
          Console.print("Found ");
          Console.println(buffer_idx);

          for(int i = 0; i < buffer_idx; i++) {
            for (byte mask = 0x80; mask; mask >>= 1) {
              Console.print(mask & byte_buffer[i] ? '1' : '0');
            }
            Console.print(' ');
            Console.println(byte_buffer[i]);
          }

          /*
           *  If we have enough bytes, then verify the checksum.
           */
          if(buffer_idx >= 10 && _crc8(byte_buffer, 9) == byte_buffer[9]) {

            Console.println("CRC Passed");
          }

          buffer_idx = 0;
        }

        #ifdef DEBUG
          for(int i = 0; i < pb_idx; i++) {
            Console.print("Pulse ");
            Console.print(i);
            Console.print(' ');
            Console.println(pulse_buffer[i]);
          }

          Console.print("Short ");
          Console.print(short_min);
          Console.print(' ');
          Console.println(short_max);

          Console.print("Long ");
          Console.print(long_min);
          Console.print(' ');
          Console.println(long_max);

          short_min = long_min = 9999;
          short_max = long_max = 0;

        #endif

        shift_register = 0;
        bit_count = 0;
        sig_seen = 0;
      }

      // No transition on this iteration
      // Console.flush();
  }
}  // end loop()

So, it currently needs cobbling together by me or someone else, but still much nicer than the old Raspberry Pi code. I’ll update this as it progresses.

12 Replies to “Arduino Yun Reading WH1080 using AUREL RX-4MM5”

  1. Hello Kevin, would the Raspberry also be more stable with the Aurel receiver or why do you now prefer the Arduino Yun?
    Regards
    Markus

    1. Hi Markus,

      The Aurel receiver would work better than the RFM01/RFM12b, regardless of the platform – it has better sensitivity, and it’s designed specifically as an AM receiver. I don’t so much prefer the Yun over the Pi, but it works better for this application.

      Any microcontroller implementation will give much better results over a non-realtime operating system. The Arduino Yun has a microcontroller alongside a GNU/Linux based SoC, so I’d choose the Arduino Yun for anything other than a bit of experimenting. It’s just a cleaner, neater solution, and the SoC is free to do all the network stuff (web server, data-uploading, etc.) without any impact on signal reception.

      It would also be possible to decode signals from different devices, because the microcontroller is dedicated to reading RF, so it can examine anything it finds. In fact, it needn’t even do the decoding – it can simply provide pulse transition data to the Linux side (e.g. pulse + duration), which can then be decoded at leisure without any further real-time requirement.

      Any SoC board could be used simply by adding an MCU. The requirements are simple – just power and a UART (e,g. to the SoC), and a data input from the RF module. So a Carambola2, Dragino, Raspberry Pi, etc. could be used by adding your own MCU. Also, the BeagleBone Black has two MCUs built in which could be used (I’m sure my Twitter feed has a link to a tutorial on using C with the BBB MCUs, will try to dig out, otherwise search yourself).

      Lots of options, but in short – the RF reading bits are best done with a dedicated MCU, and the Arduino Yun already has one on board and it’s easily programmed.

  2. How did you interface the RX-4MM5 to the Ardunio Yun? I have read lots of problems with interfacing to other devices. I just need to clear up this interface requirement before buying the kit and having a go at programming. My background is software, I am a little weak on the electronics.

    1. I just wired the module’s Data-Out pin to a digital input on the Yun. The module operates at the same voltage as the Yun, so no level shifting was required. The wiring was very simple, it’s mostly software, so you should be right at home.

      1. Hi Kevin,

        Many thanks for posting this. I tried the Raspberry Pi example some time ago, but never got reliable results. However, with the RX-4MM5 and the Yun, it works brilliantly! It continues to receive data some distance from the transmitter.

        I am using this Arduino set-up to troubleshoot loss of sensor contact with the base station. This has been happening frequently over the last month or so, but only ever at night. I have been using the WH1080 attached to a Raspberry Pi running pywws for over 2 years without any major problems. Pywws uploads data to Weather Underground. With loss of sensor contact (dashes on base station display), pywws does not upload any data, and after 2 hours, an automated email from Wunderground is generated warning that the weather station is not working.

        Loss of sensor contact does not happen every night, and does not appear to be temperature related (we had -6.6°C a few nights ago, with no loss of sensors at all).

        I have replaced the transmitter with a spare unit. This has made no difference. Replacing batteries in the transmitter made no difference.

        I am using the Yun to log the received transmissions to SD card, along with a time-stamp. From this, I can see that the transmitter is still transmitting data even when the base station loses contact.

        Is there a way of determining signal strength using a RX-4MM5?

        By the way, I moved the input from pin 2 to pin 7 so I could add a BMP085 to the Yun. Pin 2 is used as SDA on the Yun.

        1. Interesting, thanks for sharing. From memory, the module has an RSSI output which could be used with an ADC input on the AVR to report signal strength. For completeness, I suppose it’s also worth checking the indoor temperature – weak batteries in the base station could drop out overnight.

          If the base station misses a signal, it keeps the receiver on until it gets the next valid signal which could exacerbate a weak battery problem, or cause cause the receiver to switch to a long polling interval (I forget how it handles this). I’d be interested to know what you learn from your logs.

          1. I connected the RSSI output to an ADC input on the Arduino, and added this reading to the log file. I also disabled Automatic Gain Control.
            When the base station loses contact with the transmitter, I see no associated loss of signal strength – see chart here: https://www.dropbox.com/sc/4kjen51rvc9h3bc/AADTx9VExgc0cvFWHpMJsZ0ya

            I have even tried a new base station, and that makes no difference. And having said it only happens at night – I had a period of missed transmission during the day!

            As the RX-4MM5 works so well, and hardly ever misses a transmission, I’ll work on using the Yun to upload the weather data to Weather Underground.

          2. Great data, thanks. As I recall, continuous logging via the base station USB is known to be buggy for these devices. If you dump the raw data over the Yun’s bridge, you should be able to re-use most of the existing Raspi C code to decode it on the AR9331.

  3. Hi Kevin,

    Great project, I am having a go at recreating your project. Do you have the completed code or know somebody who has completed the code.

    1. I never progressed with this, got sidetracked as often happens when the most interesting bit (to me, at least) has been done. The breadboard is still on my desk, so I might join in if you’re having a go, though I can’t make any promises at the moment. In any case, do feel free to email me for advice (assuming the fruits of your labour will be open-source, of course).

  4. Thanks for this, I’m wanting to make a wireless thermometer receiver that will pickup all sensors around it and display them. I’m using a mkr1000 and was trying with a cc1101 but couldn’t really find any libraries for it. Would it to better to use a RX-4MM5?

    1. I don’t have experience of the TI chip you mention. The RX-4MM5 is a simple AM receiver, whereas the TI chip supports various modulation formats, encoding formats, and packet formats. It’s much more complex, and will likely require extra pins for SPI, as well as software for configuration. If you don’t need all these features, then you’d be adding complexity for little to no gain. If all my transmitters were sending OOK signals, then I’d go with the RX-4MM5 – to start with, at least.

Leave a Reply

Your email address will not be published. Required fields are marked *