Monday, May 6, 2013

More RF433 Wireless: Home Automation

The RF433 transmitter and receiver modules can be used with Home Automation equipment. This blog post presents the Cosa support for sending and receiving wireless commands for NEXA/HomeEasy equipment. With sensor modules and supporting drivers it is possible to build advanced Home Automation applications that both receive and send commands to monitor and control lighting, alarms, door-bells, heaters, fans, etc. The Cosa NEXA/HomeEasy may be used with ATtiny.

Fig.1: CosaNEXAreceiver on ATtiny85 with RF433 Receiver and NEXA Remote

The Cosa NEXA::Receiver allows Arduino sketches to listen for wireless commands. These may be transmitted by a standard remote as in the figure(1) above or from another Arduino running the NEXA::Transmitter as in the figure(2) below. The receiver supports message decoding and address matching hiding much of the protocol complexity. Below is an example sketch (CosaNEXAreceiver.ino). The command data type (NEXA::code_t) simplifies the access of the command members. It also supports address matching.  

OutputPin led(Board::LED);
NEXA::Receiver receiver(Board::EXT0);
NEXA::code_t device = 0;
...
void setup()
{
  ...
  // Use polling version to receive the remote button for device
  receiver.recv(device);
  trace << PSTR("learning: ") << device << endl;
  ...
  // Enable the interrupt driven version
  receiver.enable();
}
...
void loop()
{
  // Wait for the next event
  Event event;
  Event::queue.await(&event);
  ...
  // Get the received command code and check if it is for this device
  NEXA::code_t code = receiver.get_code();
  if (code == device) {
    trace << PSTR("matched: ") << code << endl;
    led << code.onoff;
  }
}


The NEXA::Receiver uses an external interrupt pin, Board::EXT0. Both a polling (busy-wait) and interrupt driven version are provided. In the sketch above the polled version, receiver.revc(), is used to receive the first command code. This code is used as the device address for matching command codes received from the interrupt driven version in the loop() function.

Fig.2: Arduino Nano with RF433 Transmitter and NEXA Receiver

An event is pushed by the interrupt handler when a command code has been received and decoded. The loop() simply waits for the next event, retrieves the received command code, get_code(), and matches it with the operator==. If the unit address matches the received code is printed to the trace stream and the LED is turned on according to the onoff member of the received command code. The Cosa NEXA class also contains a command code transmitter, NEXA::Transmitter. The current version of send() is delay-based. The below example sketch (CosaNEXAsender.ino) is the wireless version of the Blink sketch.

OutputPin led(Board::LED);
NEXA::Transmitter transmitter(Board::D9, 0xc05a01L);
...
void setup()
{
  ...
  // First code will be used by receiver as address (learning mode)
  transmitter.send(0, 1);
}
...
void loop()
{
  // Blink the transmitter and receiver leds
  led.on();
  transmitter.send(0, 1);
  SLEEP(1);
  transmitter.send(0, 0);
  led.off();
  SLEEP(5);
}


The NEXA::Transmitter constructor requires an output pin and the house address (26-bit). The unit number is given as a parameter to the send() method. There is also a broadcast() method to send a group-message.

  union code_t {
    int32_t as_long;
    struct {
      uint8_t device:4;       /** device number, group:unit<2,2> */
      uint8_t onoff:1;        /** device mode, off(0), on(1) */
      uint8_t group:1;        /** group command */
      uint32_t house:26;      /** house code number */
    };
    code_t(int32_t value = 0L);
    code_t(int32_t h, uint8_t g, uint8_t d, uint8_t f);
    bool operator==(const NEXA::code_t &other) const;
    friend IOStream& operator<<(IOStream& outs, code_t code);
  };


The last example sketch in this post integrates the Virtual Wire Interface (VWI) and NEXA Transmitter to form a wireless Thermostat. The sketch receives humidity and temperature readings from the wireless DHT11 sketch (CosaTinyDHT11.ino) running on a ATtiny85. When temperature drops too low a relay/heater is turned on and when the humidity is too high a relay/fan is turned on and a command is send to a NEXA receiver (vent). When the readings drop below a lower threshold the heater/fan/vent are turned off again.

Fig.3: CosaTinyDHT11 running on an ATtiny85 and receiver

Two support classes, Relay/RemoteRelay, are defined to make the setup() and loop() easier to read. Below is the setup() of CosaVWIthermostat.ino. The setup() starts the Virtual Wire Interface (VWI) and receiver, and sends a NEXA command to the vent.

Relay heater(Board::D2);
Relay fan(Board::D3);
RemoteRelay vent(Board::D4, 0xc05a01L, 0);
...
void setup()
{
  ...
  // Start virtual wire interface and receiver
  VWI::begin(SPEED);
  rx.begin();
  ...
  // Close the remote vent
  vent.close();
}

...
// Message type to receive
struct msg_t {
  uint16_t nr;
  int16_t humidity;
  int16_t temperature;
};
...
void loop()
{
  // Wait for a message
  msg_t msg;
  int8_t len = rx.recv(&msg, sizeof(msg));
  if (len != sizeof(msg)) return;
  int16_t humidity = msg.humidity;
  int16_t temperature = msg.temperature;
  ...
  // Check if heater should be turned on @ 22 C and off @ 26 C
  static const uint8_t TEMP_MIN = 22;
  static const uint8_t TEMP_MAX = 26;
  if (heater.is_on()) {
    if (temperature > TEMP_MAX) heater.off();
  }
  else if (temperature < TEMP_MIN) heater.on();
  ...
  // Check if fan/vent should be turned on @ 70 %RH and off @ 50 %RH.
  static const uint8_t RH_MIN = 50;
  static const uint8_t RH_MAX = 70;
  if (fan.is_on()) {
    if (humidity < RH_MIN) {
      fan.off();
      vent.close();
    }
  }
  else if (humidity > RH_MAX) {
    vent.open();
    fan.on();
  }
}


Please see the Cosa examples folder for the full listing of the example sketches.

[Update 2013-06-02]

Since the first publishing of this post the NEXA device driver has been improved to make it even easier to handle Remote Control device actions. A NEXA::Receiver::Device class has been added. Below is an example of usage:

class LED : public NEXA::Receiver::Device {
private:
  OutputPin m_pin;
public:
  LED(Board::DigitalPin pin) : NEXA::Receiver::Device(0L), m_pin(pin) {}
  virtual void on_event(uint8_t type, uint16_t value) { m_pin << value; }
};
...
NEXA::Receiver receiver(Board::EXT0);
LED device(Board::LED);
...
void setup()
{
  ...
  // Use polling version to receive the remote button to attach
  NEXA::code_t cmd;
  receiver.recv(cmd);
  device.set_key(cmd);
  receiver.attach(&device);
  ...
  // Enable the interrupt driven version of the receiver
  receiver.enable();
}


The NEXA::Receiver::Device class is used to create a LED output pin that is controlled by the NEXA Remote. The incoming command is filtered by the receiver and the matching device will receive event and the value (on/off/dim level). In the example above the event value is written to the output pin (m_pin). To bind the device to the receiver we need to 1) set the key (address) of the device, 2) attach the device to the receiver. That is all. Once the interrupt handler for the receiver is enabled all incoming commands are compared to the attached devices and events passed on a match. The loop() function looks like this:

void loop()
{
  // Wait for the next event and dispatch
  Event event;
  Event::queue.await(&event);
  event.dispatch();
}


This is the standard event loop for Cosa. Typically in an application there would be several devices and a special "learning" mode where the devices would be bound to buttons on the Remote. The keys would be stored in EEPROM and read on startup. The LED (NEXA::Receiver::Device) class can actually be used for any control that would be the toggling of an output pin and not just LEDs.

The NEXA classes may be used with both ATtinyX4 and ATtinyX5.

[Update 2015-08-22]
The new support library Cosa Domotica makes it really easy to develop RF433 based sensor nodes.  The library performs common operations such as battery status measurement, message sequencing, low power sleep modes, etc.

14 comments:

  1. Hi Mikael,

    is your RF433 Transmitter powered by 3.3V ?

    How many meter the transmission link works ?

    thx

    Guillaume

    ReplyDelete
  2. Hi Guillaume. I have tried a number of different variants (both VWI and NEXA/HomeEasy protocol over RF433). With ATtiny and Arduino Pro Mini I have successfully got the RF433 transmitters to work on 3.3-4.4 V. The best indoors transmission length is about 5 meters but this is very dependent on the antenna and ground plane. There is a large variation between the cheap RF433 modules and with the baud-rate. All my tests are at 4 Kbit air bit rate which in some cases is pushing the modules too hard. The construction of the antenna is also important. Without an antenna the range is 10-20 cm max.

    The Cosa VWI Codecs give an additional dimension of tuning and the selection can improve transmission length and handling of noise. The RF433 receiver module use dynamic gain and the preamble can be improved. The NEXA/HomeEasy protocol does some of this as it contains both a long presence pulse and easy package is transmitted four times.

    I should add that the RF433 transmitter need to run at higher voltage (up to 12V) to "cover a house". With 3.3-4.4V the application is basically "a room".

    I need to get my hands on higher quality RF433 modules before excluding low voltage applications without voltage boosting.

    Cheers!

    ReplyDelete
  3. hey !

    I'm really interested in what you did using the home easy protocol and I would like to use it using a Raspberry pi and my own receptor made with an ATtiny 45.

    I already have a program for the raspberry to send Home-easy frames but I'm not pretty sure about how to implement your code to my ATtiny (NEXA:receiver ) ?

    Should I download any library first ? ( I gess yes but which one ?)

    Thanks a lot for sharing your knowledge.

    ReplyDelete
    Replies
    1. Hi Charles! To use Cosa please see the install description. It is really easy. You will need to install Arduino software first. Cosa is stand-alone so you will not need any other Arduino core or library. Modify the CosaNEXAreceiver sketch after your needs. There is about 200 bytes program memory left on an ATtiny45. If the device is to be battery powered you will have to add some code for low power mode.

      Best regards, Mikael

      Delete
    2. Hi,
      Thanks for the awnser I'll try this today !

      Delete
    3. ok , I have the core installed ( I can pick cosa in the board list )

      But I steel have two question :
      - I can't see any mention of ATtiny 45 ( only 85 ) , is it ok if I take this one ?
      - I'ts written : ATtiny 85, 8MHz . Is it the internal clock or should I put an external oscillator with it to work ?

      Thanks again for helping me :) !

      Delete
  4. The board Cosa Tiny (ATtiny85, 8 Mhz) handles ATtiny25, -45,and -85 and it uses the internal clock at 8 Mhz. The difference between the processors is that amount of memory. Best regards, Mikael

    ReplyDelete
  5. BW don't forget to "Burn Bootloader". This is set the fuse bits correctly. Otherwise the clock will be wrong.

    ReplyDelete
  6. Hi Mikael, I've been trying to use this library of yours and it works with one of my two NEXA remotes.
    Works: PET-910 (a simple 3 channel + all off remote)
    Does not work: NEYCT-705 (4+group x 4 channel remote, looks just like in your picture, fig 1)

    My receiver also looks like the one in your picture, although I have not yet connected any additional antenna. This remote works to turn on/off a light 3 meters away while being right next to the receiver connected to my Arduino Uno, however it does not receive/decode anything. I modified CosaNEXAreceiver sketch to print each time a code was received and decoded from the remote.

    Is this remote you use of the same type NEYCT-705? My NEYCT-705 has date code 1106, and the 3-channel remote has date code 1208.
    Any ideas what could be the reason for one of these remotes not working?

    I checked inside and neither of these remotes have any writing on their internal ICs.

    I hope you can help.

    ReplyDelete
    Replies
    1. Hi zxinn. It is difficult to say without more details. I would recommend that you start by adding an antenna and checking the batteries in the remote. Also note that the example sketch uses the address of the first received message as the address to monitor in further communication. You may need to change this logic if you want to use too different remotes at the same time. For testing purpose you can only use one at a time with the sketch. You need to reset before testing another remote.

      There may be a slight difference between remotes and the timing in the decoder.

      Cheers!

      Delete
    2. Is your remote the same model type?

      The remote and it's battery is working well enough for the NEXA breakers, it can turn on/off a breaker-connected lamp which is 3 meters away easily.

      And as I mentioned I have modified the example sketch to print the received codes each time a button is pressed. It no longer stores or locks on to the first code it receives, nor use the on-board LED.

      I did an experiment with two Arduino (one Uno, one Mini Pro) and two pairs of 433 MHz sender+receiver. I used VirtualWire and I was sending and replying up to 5 times per second. While this was running it caused enough interference that this NEYCT-705 remote could not turn on or off the light breaker unless I moved very close to the NEXA breaker (which also means I was 3 meters away from the Arduinos and their transmitters).
      Right away once I turned off the Arduino's sending, the remote was working also at a distance again.

      And the most interesting observation: While holding each remote very close to the 433 MHz receivers and senders connected to the Arduino I could get one of them, the PET-910, to interfere with the signal enough that the Arduinos did not detect the data sent from its counterpart. NEYCT-705 however was not able to cause this type of interference.

      I did not have a breaker paired with the PET-910 remote but I can test that scenario while Arduinos are talking also.

      Perhaps the transmitter in the NEYCT-705 is weaker, or the battery is delivering a lower voltage.

      I'll try swapping the batteries, and also add an antenna to the Arduino's receiver module.

      Any ideas how I can check if the timing is different in the two remotes?

      Delete
    3. Yes. The remote model in the picture and the one I have used for this demo is NEYCT-705 (Date code: 1212). There is a probe sketch in the Cosa Sandbox that you can modify to get pulse sequences. This is what I use to re-engineer some devices. Best regards, Mikael

      Delete
  7. Nice blog. I'm planning using nexa products to control my lights. I have thought also using arduinos to control servos/motors to control curtains. Problem has been what kind of system should I build to control these from one place.

    Do I understand right that I could have maybe have one "gateway" arduino with some chip? And then receiving nexas controlling lights and "slave" arduinos with RFM69HW 433MHz receiving orders to open/close curtains?

    ReplyDelete
  8. I wondered upon your blog and wanted to say that I have really enjoyed reading your blog posts. Any way I’ll be subscribing to your feed and I hope you post again soon.
    Home Automation in Chennai
    smart home in Chennai
    Home security in Chennai
    Burglar alarm in Chennai
    Door sensors Chennai

    ReplyDelete