NEXA Arduino Receiver module

Moderator: Telldus

Post Reply
4lk1s
Posts: 2
Joined: Fri Mar 17, 2023 9:45 am

NEXA Arduino Receiver module

Post by 4lk1s »

After reading several threads about how to build an Arduino device that can interpret Nexa Transmitter signals and be controlled by Telldus Live , I wrote this Arduino Nexa Receiver sketch.

Hardware needed:
One Arduino board.
One 1 USD 433MHz Receiver module. Bought on Ebay.
One 4 relay card connected to the Arduino I/O pins.
The code is commented so that even a beginner hopefully will understand it.

Im planning to write a "Self Learning" version, but time will tell if I will do it or not.

Enjoy! :D

_________________________________


/*
* Nexa Receiver
* Controls 4 relays connected to Aurduino I/O pins.
* Transmitter codes are hard-coded as a fixed number in the Sketch.
*
* The code has been validated to work with the following NEXA transmitters:
*
* WBT 912 2ch sender
* WT2 PRO 2ch Wall sender
* LMDT 810 Wireless Outdoor motion sensor
* LMST 606 Wireless Magnetic contact
* PB3 kit sender
* Telstick Net
*
* Please note that timing varies some between different senders.
* The Debug Serial port can be used to identifiy Sender codes and also for debug timing issues.
*
* Receiver Hardware is a 1 USD 433MHz wireless reciever module bought on Ebay. Search for "433Mhz RF transmitter and receiver arduino" to find a suitable reciever.
*
* Receiver functionality is based on original code from
* Barnaby Gray 12/2008
* Peter Mead 09/2008
* "Homeeasy protocol receiver for the new protocol."
*
* * The data is encoded on the wire (aerial) as a Manchester code.
*
* A latch of 275us high, 2675us low is sent before the data.
* There is a gap of 10ms between each message.
*
* 0 = holding the line high for 275us then low for 275us.
* 1 = holding the line high for 275us then low for 1225us.
*
* The timings seem to vary quite noticably between devices.
* If this script does not detect your signals try relaxing the timing
* conditions.
* *
* Each actual bit of data is encoded as two bits on the wire as:
* Data 0 = Wire 01
* Data 1 = Wire 10
*
* The actual message is 32 bits of data (64 wire bits):
* bits 0-25: the group code - a 26bit number assigned to controllers.
* bit 26: group flag
* bit 27: on/off flag
* bits 28-31: the device code - a 4bit number.
*
* The group flag just seems to be a separate set of addresses you can program devices
* to and doesn't trigger the dim cycle when sending two ONs.
*/

int rxPin = 12; // Input of 433 MHz receiver
int txPin0 = 8; // Output to relay 1
int txPin1 = 9; // Output to relay 2
int txPin2 = 10; // Output to relay 3
int txPin3 = 11; // Output to group relay


int t1 = 0; // Latch 1 time only needed for debugging purposes
int t2 = 0; //latch 2 time only needed for debugging purposes.

unsigned long Sendercode = 12712226; // Here is the unique Transmitter code. Use the Serial monitor to identify your Transmitter code.


void setup()
{ pinMode(rxPin, INPUT); // Input of 433 MHz receiver
pinMode(txPin0, OUTPUT); // Output to relay 1
pinMode(txPin1, OUTPUT); // Output to relay 2
pinMode(txPin2, OUTPUT); // Output to relay 3
pinMode(txPin3, OUTPUT); // Output to group relay

Serial.begin(9600);
Serial.println ("Nexa Receiver Hard Coded");
}


void loop()
{
int i = 0;
unsigned long t = 0;

byte prevBit = 0;
byte bit = 0;

unsigned long sender = 0;
bool group = false;
bool on = false;
unsigned int recipient = 0;

// latch 1
// Latch timing has been loosened to acommodate varieties in the Nexa transmitters.

while ((t < 8000 || t > 13000))
{ t = pulseIn(rxPin, LOW, 1000000);
t1 = t; // Save latch timing for debugging purposes
}

// Next line can be used to debug latch timing. Please note that this affects timing and that recieving the following data bits may fail.
//Serial.println (t);

// latch 2
// Latch timing has been loosened to acommodate varieties in the Nexa transmitters.
while (t < 2200 || t > 2900)
{ t = pulseIn(rxPin, LOW, 1000000);
}
t2 = t; // Save latch timing for debugging purposes
// Next line can be used to debug latch timing. Please note that this affects timing and that recieving the following data bits may fail.
//Serial.println (t);



// data collection from reciever circuit
while (i < 64)
{
t = pulseIn(rxPin, LOW, 1000000);

if (t > 200 && t < 400)
{ bit = 0;

}
else if (t > 1100 && t < 1560)
{ bit = 1;

}
else
{ i = 0;

break;
}

if (i % 2 == 1)
{
if ((prevBit ^ bit) == 0)
{ // must be either 01 or 10, cannot be 00 or 11
i = 0;
break;
}

if (i < 53)
{ // first 26 data bits
sender <<= 1;
sender |= prevBit;
}
else if (i == 53)
{ // 26th data bit
group = prevBit;
}
else if (i == 55)
{ // 27th data bit
on = prevBit;
}
else
{ // last 4 data bits
recipient <<= 1;
recipient |= prevBit;
}
}

prevBit = bit;
++i;
}

// interpret message
if (i > 0)
{ printResult(sender, group, on, recipient); // Print the result on Serial Monitor. Use this to identify your transmitter code.

if (Sendercode = sender) // This is the check for the correct transimtter code. If code is incorrect then go back to look for new code.
if (group) // Group command affects all relays, Either all on, or all off.
{

digitalWrite(txPin0, on); // Relay 1
digitalWrite(txPin1, on); // Relay 2
digitalWrite(txPin2, on); // Relay 3
digitalWrite(txPin3, on); // Group Relay

}

else
{
switch (recipient) // Check which channel should be activated
{
case 0:
digitalWrite(txPin0, on); // Relay 1
break;
case 1:
digitalWrite(txPin1, on); // Relay 2
break;
case 2:
digitalWrite(txPin2, on); // Relay 3
break;

break;
}
}
}

}



void printResult(unsigned long sender, bool group, bool on, unsigned int recipient)
{
Serial.print("sender ");
Serial.println(sender);

if (group)
{ Serial.println("group command");
}
else
{ Serial.println("no group");
}

if (on)
{ Serial.println("on");
}
else
{ Serial.println("off");
}

Serial.print("recipient ");
Serial.println(recipient);
Serial.println(t1); // Timing for latch 1
Serial.println(t2); // Timing for latch 2

Serial.println();


}
Aldermo
Posts: 2
Joined: Fri Mar 17, 2023 9:45 am

Re: NEXA Arduino Receiver module

Post by Aldermo »

Thanks for the post. Just tried and it works great with a Velleman RX433N (http://www.velleman.eu/products/view/?id=370780).

Any ideas on how to improve the range? I can't get it to work further away then 10 centimeters!
4lk1s
Posts: 2
Joined: Fri Mar 17, 2023 9:45 am

Re: NEXA Arduino Receiver module

Post by 4lk1s »

My guess is that you have no antenna soldered to the board.
fredrike
Posts: 18
Joined: Fri Mar 17, 2023 9:45 am

Re: NEXA Arduino Receiver module

Post by fredrike »

Thank you for the post and code!

I've made some modifications to implement a SelfLearning method. Now are the first ~30 secs after powering the Arduino a learning stage, supporting 3 different codes.

The learning stage acts as follows, if there are an available slot and a power on is received that remote+button is stored to EEPROM (so it will stick between power cycles). A group OFF will clear the EEPROM and memory, making room for new remotes. I will add the function of removing a button, but haven't had "time" for that yet.

The code have been developed and tested on an Ardunio Pro Micro so on other platforms some parts needs probably to be updated (i.e., the TXLED1/TXLED0 macros and such).

Code: Select all

/*
 * Nexa SelfLearning Receiver
 * Transmitter codes are hard-coded as a fixed number in the Sketch.
 * src: http://www.telldus.com/forum/viewtopic.php?f=12&t=4072
 *
 * The code has been validated to work with the following NEXA transmitters:
 *
 * WBT 912 2ch sender
 * WT2 PRO 2ch Wall sender
 * LMDT 810 Wireless Outdoor motion sensor
 * LMST 606 Wireless Magnetic contact
 * PB3 kit sender
 * Telstick Net
 *
 * Please note that timing varies some between different senders.
 * The Debug Serial port can be used to identifiy Sender codes and also for debug timing issues.
 *
 * Receiver Hardware is a 1 USD 433MHz wireless reciever module bought on Ebay. Search for "433Mhz RF transmitter and receiver arduino" to find a suitable reciever. 
 *
 * Receiver functionality is based on original code from
 * Barnaby Gray 12/2008
 * Peter Mead 09/2008
 * "Homeeasy protocol receiver for the new protocol."
 *
 * * The data is encoded on the wire (aerial) as a Manchester code.
 *
 * A latch of 275us high, 2675us low is sent before the data.
 * There is a gap of 10ms between each message.
 *
 * 0 = holding the line high for 275us then low for 275us.
 * 1 = holding the line high for 275us then low for 1225us.
 *
 * The timings seem to vary quite noticably between devices.
 * If this script does not detect your signals try relaxing the timing
 * conditions.
 * *
 * Each actual bit of data is encoded as two bits on the wire as:
 * Data 0 = Wire 01
 * Data 1 = Wire 10
 *
 * The actual message is 32 bits of data (64 wire bits):
 * bits 0-25: the group code - a 26bit number assigned to controllers.
 * bit 26: group flag
 * bit 27: on/off flag
 * bits 28-31: the device code - a 4bit number.
 *
 * The group flag just seems to be a separate set of addresses you can program devices
 * to and doesn't trigger the dim cycle when sending two ONs.
 */
#include <EEPROM.h>

#define rxPin 10 // Input of 433 MHz receiver
/*#define DEBUG*/

typedef struct {
  unsigned long sender;
  short channel;
} senderChannel;

senderChannel knownSenders[3] = {{0,0},{0,0},{0,0}};

unsigned long Sendercode = 0; // Here is the unique Transmitter code. Use the Serial monitor to identify your Transmitter code.

unsigned long NexaReceive(unsigned long &sender, bool &on, bool &group, short &channel) {
  int i = 0;
  unsigned long t = 0;

  byte prevBit = 0;
  byte bit = 0;
  unsigned long recievedData = 0;
#ifdef DEBUG
  int t1 = 0; // Latch 1 time only needed for debugging purposes
  int t2 = 0; //latch 2 time only needed for debugging purposes.
#endif

  int rotations = 0;
  // latch 1
  // Latch timing has been loosened to accommodate varieties in the Nexa transmitters.
  while(t < 8000 || t > 13000) {
    t = pulseIn(rxPin, LOW, 13000);
    if(rotations++ > 10000)
      return 0;
  }
#ifdef DEBUG
  t1 = t; // Save latch timing for debugging purposes
#endif

  rotations = 0;
  // latch 2
  // Latch timing has been loosened to accommodate varieties in the Nexa transmitters.
  while(t < 2200 || t > 2900) {
    t = pulseIn(rxPin, LOW, 2900);
    if(rotations++ > 10000)
      return 0;
  }
#ifdef DEBUG
  t2 = t; // Save latch timing for debugging purposes
#endif

  // data collection from receiver circuit
  while (i < 64)
  {
    t = pulseIn(rxPin, LOW, 1560);
    if (t > 200 && t < 400)
      bit = 0;
    else if (t > 1100 && t < 1560)
      bit = 1;
    else
     return 0;

    if (i % 2 == 1) {
      if ((prevBit ^ bit) == 0) // must be either 01 or 10, cannot be 00 or 11
        return 0;
      recievedData <<= 1;
      recievedData |= prevBit;
#ifdef OLD
      if (i < 53) { // first 26 data bits
        sender <<= 1;
        sender |= prevBit;
      }
      else if (i == 53)
      { // 26th data bit
        group = prevBit;
      }
      else if (i == 55)
      { // 27th data bit
        on = prevBit;
      }
      else
      { // last 4 data bits
        recipient <<= 1;
        recipient |= prevBit;
      }
#endif
    }
    prevBit = bit;
    ++i;
  }

#ifdef DEBUG
  Serial.println(recievedData,BIN);
  Serial.print("0x"); Serial.println(recievedData,HEX);
  Serial.print("Timings: "); 
  Serial.print(t1); // Timing for latch 1
  Serial.print(",");
  Serial.println(t2); // Timing for latch 2
#endif

  sender = recievedData >> 6;
  on = (recievedData >> 4) & 0x01;
  group = (recievedData >> 5) & 0x01;
  channel = (short)recievedData & 0xF;
  return recievedData;
}

void setup()
{
  TXLED1;
  pinMode(rxPin, INPUT); // Input of 433 MHz receiver
  Serial.begin(9600);
  bool on=false, group=false, exists=false;
  short channel;
  /* Read EEPROM */
#ifdef DEBUG
  delay(5000);
  for(int j=0; j<17; j++) {
    Serial.print("Reading from: "); Serial.print(j); Serial.print(" "); 
    Serial.print(" 0x"); Serial.println(EEPROM.read(j),HEX);}
#endif
  for(int i=0; i<3; i++) {
    knownSenders[i].sender = ((EEPROM.read((i*6)) << 0) & 0xFF) + ((EEPROM.read((i*6)+1) << 8) & 0xFFFF) + 
      ((long(EEPROM.read((i*6)+2)) << 16) & 0xFFFFFF) + ((long(EEPROM.read((i*6)+3)) << 24) & 0xFFFFFFFF);
    knownSenders[i].channel = EEPROM.read((i*6)+5);
#ifdef DEBUG
    Serial.print("Read senderCode: "); Serial.println(knownSenders[i].sender,HEX);
    Serial.print("Read channel from: "); Serial.print((i*6)+5); Serial.print(" 0x"); Serial.println(EEPROM.read((i*6)+5),HEX);
#endif
  }
  while(micros() < 30*1000000) {
    TXLED1;
    exists = false;
    if(NexaReceive(Sendercode,on,group,channel) == 0)
      continue;
    if(on && !group) {
      for(int i=0; i<3; i++) {
        if(knownSenders[i].sender == Sendercode && knownSenders[i].channel == channel) {
          Serial.print("Remote already registered, "); Serial.print(i); Serial.print(": ");
          Serial.print(Sendercode); Serial.print("|"); Serial.println(channel);
          exists = true;
          break;
        }
      }
      if(!exists) {
        for(int i=0; i<3; i++) {
          if(knownSenders[i].sender == 0 && knownSenders[i].channel == 0) {
            Serial.print("Will register remote, "); Serial.print(i); Serial.print(": ");
            Serial.print(Sendercode); Serial.print("|"); Serial.println(channel);
            knownSenders[i].sender = Sendercode; knownSenders[i].channel = channel;
            Serial.println(Sendercode,HEX);
            for(int j=0; j<4; j++) {
              EEPROM.write((i*6)+j, (Sendercode >> (j*8) & 0xFF));
            }
            EEPROM.write((i*6)+5, channel);
            break;
          }
        }
      }
    }
    if(!on && group) { //CLEAR!!
      Serial.println("Clearing stored remotes.");
      for(int i=0; i<19; i++)
        EEPROM.write(i,0);
      for(int i=0; i<3; i++) {
        knownSenders[i].sender = 0;
        knownSenders[i].channel = 0;
      }
    }
  }
  Serial.println("Nexa Receiver - SelfLearning ");
  Serial.print("Will use Sender|Code: ");// Serial.print(Sendercode); Serial.print(" "); Serial.println(channel);
  TXLED0;
  for(int i=0; i<3; i++) {
    Serial.print((unsigned long)knownSenders[i].sender);
    Serial.print("|");
    Serial.print(knownSenders[i].channel);
    Serial.print(" ");
  }
  Serial.println();
}


void loop()
{
  unsigned long sender;
  bool on, group;
  short channel;
  // interpret message
  if (NexaReceive(sender, on, group, channel) != 0) {
    printResult(sender, group, on, channel); // Print the result on Serial Monitor. Use this to identify your transmitter code.
    testResult(sender, group, on, channel);
  }
}


int testResult(unsigned long sender, bool group, bool on, short channel)
{
  for(int i=0; i<3; i++) {
    if(sender == knownSenders[i].sender && group) {
      Serial.print("MATCH "); Serial.print(sender);  Serial.print(" @ "); Serial.print(i);
      Serial.print(" action: ");
      on ? Serial.println("ON") : Serial.println("OFF");
      return 1;
    }
    else if(sender == knownSenders[i].sender && channel == knownSenders[i].channel) {
      Serial.print("MATCH "); Serial.print(sender);  Serial.print(" @ "); Serial.print(i);
      Serial.print(":"); Serial.print(channel);
      Serial.print(" action: ");
      on ? Serial.println("ON") : Serial.println("OFF");
      return 1;
    }
  }
  return 0;
}

void printResult(unsigned long sender, bool group, bool on, short channel)
{
  Serial.print("## Sender: ");
  Serial.print(sender);

  group ? Serial.print(" Group Cmd ") : Serial.print("");
  on ? Serial.print(" ON") :  Serial.print(" OFF");

  Serial.print(" Channel: ");
  Serial.println(channel);
}

BillCroan
Posts: 8
Joined: Fri Mar 17, 2023 9:45 am

Re: NEXA Arduino Receiver module

Post by BillCroan »

Hi,
like the self learning, however I like to use your earlier code as I like to "hardcode" the unsigned "long Sendercode = 12712226; // Here is the unique Transmitter code. Use the Serial monitor to identify your Transmitter code."

As I also wish to pickup not just the self learning codes but also the CODE Switch type like the old HomeEasy/Nexa house codes e.g.Housecode P unicode1 etc.
Using the RCSwitch library I can pick these up as a 7 character code e.g. P1 = 5570581 on my transmitter . . .so is there any way to get both into one Arduino sketch?
So if the return received code = 16499970 Unicode 1 then its a selflearn code . . if its only 5570581 using RCSwitch library then its a House Code.
Hope I have made clear but feel free to ask . .
geirgp
Posts: 22
Joined: Fri Mar 17, 2023 9:45 am

Re: NEXA Arduino Receiver module

Post by geirgp »

Self-learning is nice, but programming self-learning becomes almost impossible as soon as you start deploying NEXA motion detectors and/or door magnet switches in your home, unless you live alone. I have them in nearly every room for presence detection and they go off all the time. Lately I've had to wait until everyone else was either in bed our out of the house when programming new actuators, just to make sure that they also didn't respond to a random motion detector.
geirgp
Posts: 22
Joined: Fri Mar 17, 2023 9:45 am

Re: NEXA Arduino Receiver module

Post by geirgp »

As a follow-up question: has anyone implemented a NEXA-compatible tranceiver module?

I have the Tellstick Duo and use it for controlling switches/dimmers as well as listening to temperature sensors.

One of the issues I have, and I understand many others have too, relates to the fact that NEXA/Proove is one-way aka fire and forget messaging - you don't know if the message you sent was actually received. From time to time this causes random failures with my home automation, and for that reason I have started to migrate parts of my system to Z-Wave which is 2-way.

I think it would be really cool to build a Arduino Tranceiver which works like the Arduino Receiver described in this post, but also sends status updates. It could be as simple as the Arduino Tranceiver sending ON/OFF/DIM commands to a different house/unit (e.g house code + 1) which could be picked up by the Tellstick Duo. You would of course have to specifically handle this "reply" (or report) in whatever software you use since it's using different house/unit codes for command/report. I use OpenHab where that is easy.
Zaman
Posts: 243
Joined: Fri Mar 17, 2023 9:45 am

Re: NEXA Arduino Receiver module

Post by Zaman »

geirgp wrote:As a follow-up question: has anyone implemented a NEXA-compatible tranceiver module?

I have the Tellstick Duo and use it for controlling switches/dimmers as well as listening to temperature sensors.

One of the issues I have, and I understand many others have too, relates to the fact that NEXA/Proove is one-way aka fire and forget messaging - you don't know if the message you sent was actually received. From time to time this causes random failures with my home automation, and for that reason I have started to migrate parts of my system to Z-Wave which is 2-way.

I think it would be really cool to build a Arduino Tranceiver which works like the Arduino Receiver described in this post, but also sends status updates. It could be as simple as the Arduino Tranceiver sending ON/OFF/DIM commands to a different house/unit (e.g house code + 1) which could be picked up by the Tellstick Duo. You would of course have to specifically handle this "reply" (or report) in whatever software you use since it's using different house/unit codes for command/report. I use OpenHab where that is easy.
I suggest you take a look at the HomeEasy project at Arduino. It uses the same protocol as NEXA.
http://playground.arduino.cc/Code/HomeEasy
Post Reply