Description of the NEXA protocol
|
One burst of data from a NEXA remote. |
The "new" NEXAprotocol are described
here. Basically it is like this: The signal for start TX is a pulse (0.2 ms) followed by a delay of 2.7 ms. A pulse followed by a short delay (0.4 ms) reads as "0". A pulse followed by a longer delay (1.4 ms) reads as "1". The data are then combined into a 32 bit string by assigning "01" as '0' and "10" as '1'.
00000000001000000000000010010010
The sequence above were generated from a
tellstick using "house=32770" and "unit=3".
The first 26 bits defines the ID of the remote as a binary number. If set, the 27th bit defines the command active for all recievers with the ID ( group command). The 28th bit are the command, '1' for "ON", '0' for "OFF". The last four bits are the unit code. For my NEXA remote control "unit 1" are "0000", "unit 2" are "0001" and "unit 3" are "0010". Every push on the remote generates a train of four pulse bursts, the tellstick generated five.
Decoding
|
It is noisy in the 433 MHz range and hard to get the first burst of data. |
It appears that the 433 MHz reciever used are doing level adjustment based on the signal level in the 433 MHz range so the strategy for initiating the decoding were to wait until some data forced the reciever to turn down the gain.
An Atmega 328P available at hand were chosen to do the decoding. The 433 MHz RX data pin were connected to one of the PCINT22 interrupt pins, PD6. The 16 bit timer1 were used to time pulse widths and delays. Power to the 433 MHz RX unit were provided from PD5 (GND) and PD7 (5V). PC5 were used for debugging by outputting pulses to a logic analyzer. Data were outputted to a LCD screen using code from
extremeelectronics.
The 328P were run at 8 MHz. A check for how slow it can run and still decode the data are left for later.
/*
* WirelessReader.c
*
* Sample signals from 433 MHz reciver and decode in a state machine.
*
* PCB: SousVide V.1
* Using Using port 11, 12 and 13 (PD5, PD6 and PD7) for 433 MHz RX, drawing ~4 mA
*
*
*
*
*
*/
#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include "lcd.h"
#define T01 1
#define T10 0
// shortest and longest accepted pulsewidth
#define kMinPulseWidth 140
#define kMaxPulseWidth 300 //210
// Minimum time between two pulsetrains
#define kPulseTrainDelay 9700
// Minimum time between init pulse and data stream
#define kMinInitDelay 2200 //2600
#define kMaxInitDelay 2800
// Maximum time between two data pulses
#define kMaxDataDelay 1450 //1363
// A delay longer than reads as a '1'
#define kZeroDelay 687 //346
// The data arrives in pairs, '01' or '10', ministate
// keeps track of these pairs
// -1 undefined, waiting for '0' or '1'
// 0 '0' read, waiting for '1'
// 1 '1' read, waiting for '0'
volatile int8_t ministate = -1;
//#state
//# 0 - wait for 9.3 ms (kPulseTrainDelay) silence
//# 1 - wait for and measure init pulse width
//# 2 - measure init delay
//# 3 - check data pulse width and start measuring data delay
//# 4 - read data delay and determine '0', '1'. n time out store data
volatile uint8_t state = 0;
volatile uint32_t rawdata, data;
volatile uint8_t dataready;
//PD5, ground for 433 MHz RX
#define RX_GND_PIN PD5
#define RX_DTA_PIN PD6
#define RX_VCC_PIN PD7
int main(void)
{
// Set up interrupts
PCMSK2 |= (1<<PCINT22);
PCICR |= (1<<PCIE2);
// Set up timer, do not start it
TCCR1B |= (1 << WGM12); //CTC mode
TIMSK1 |= (1<<OCIE1A);
state = 0;
rawdata = 0;
data = 0;
dataready = 0;
// Set up 433MHz RX
DDRD |= (1 << RX_GND_PIN) | (1 << RX_VCC_PIN);
DDRD &= ~(1<<RX_DTA_PIN);
PORTD &= ~(1 << RX_GND_PIN); // set to 0V
PORTD |= (1 << RX_VCC_PIN); // set to 5V
// PORTD |= (1 << RX_DTA_PIN); // pullup
// For debugging
DDRC |= (1 << PC5);
//Initialize LCD module
LCDInit(LS_NONE);//(LS_BLINK|LS_ULINE);
LCDClear();
sei();
while(1)
{
if (dataready==1)
{
cli();
char buff[8];
uint8_t tmp = data &0x000000FF;
sprintf(buff,"%04X", (data>>(16+6))&0x0000FFFF);
LCDWriteStringXY(0,0,buff);
sprintf(buff,"%04X", (data>>6)&0xFFFF);
LCDWriteStringXY(4,0,buff);
if (tmp & 0x10)
{
LCDWriteStringXY(0,1,"ON");
}
else
{
LCDWriteStringXY(0,1,"OFF");
}
// LCDWriteStringXY(4,1,"U ");
// LCDWriteIntXY(6,1,(tmp&0x0f),3);
if ((tmp & 0x0F) == 0x00) LCDWriteStringXY(4,1,"U 1");
if ((tmp & 0x0F) == 0x01) LCDWriteStringXY(4,1,"U 2");
if ((tmp & 0x0F) == 0x02) LCDWriteStringXY(4,1,"U 3");
if (tmp & 0x20) LCDWriteStringXY(4,1,"Grp");
sei();
for (int ii=0;ii<100;ii++)
{
_delay_ms(30);
}
dataready = 0;
cli();
LCDClear();
sei();
}
}
}
//reset state to initial
void inline resetstate()
{
unsigned char sreg;
state=0;
ministate=-1;
data = 0;
rawdata = 0;
TCCR1B &= ~(1 << CS11); //stop timer 1
sreg = SREG; // save global interrupt flag
cli();
TCNT1 = 0;
SREG = sreg; // restore interrrupt flag
}
// start timer
void inline starttimer(int v)
{
unsigned char sreg;
sreg = SREG; // save global interrupt flag
cli();
TCNT1 = 0;
OCR1A = v;
SREG = sreg; // restore interrrupt flag
TCCR1B |= (1 << CS11); //prescale 8
}
// This interrupt is called when the 433 MHz RX pin changes state
ISR(PCINT2_vect)
{
char transition;
// determine the state change direction, 0->1 or 1->0
if (PIND & (1<<PIND6))transition = T01;
else transition = T10;
//state 0: should time out, we need 9.3 ms silence, otherwise try again
if (state == 0)
{
starttimer(kPulseTrainDelay-100);
return;
}
// state 1: we had a 9.3 ms delay, now we are recieving the init pulse
// start the pulsewidth timer
if ((state==1) && (transition == T01))
{
starttimer(kMaxPulseWidth);
return;
}
if ((state==1) && (transition == T10))
{
if (TCNT1<kMinPulseWidth)
{
resetstate();
return;
}
else // pulse OK, too long and it will time out in the timer part
{
state = 2;
starttimer(kMaxInitDelay);
return;
}
}
// state 2: measure the init delay
if (state == 2)// && (transition == T01))
{
if (transition == T10)
{
resetstate();
return;
}
if (TCNT1<kMinInitDelay) // to short
{
resetstate();
return;
}
else // delay OK, too long and it times out
{
starttimer(kMaxPulseWidth);
rawdata = 0;
state = 3;
return;
}
}
//state 3: check pulsewidth in data transmission
if ((state == 3))// && (transition==T10))
{
if (TCNT1<kMinPulseWidth)
{
resetstate();
return;
}
else // pulse OK, too long and it will time out from the timer
{
starttimer(kMaxDataDelay);
state = 4;
return;
}
}
//state 4: read data, '0' or '1'
if (state == 4)
{
int t = TCNT1;
//observing 1310 or 320
if (t > kZeroDelay) // we read '1'
{
if (ministate==1) // error, two '1' in a row
{
resetstate();
return();
}
else if (ministate==-1) // OK, next bit should be '0'
{
ministate = 1;
}
else if (ministate==0) // OK, bit pair completed
{
ministate = -1;
rawdata = (rawdata<<1);
// no need to add '0'
}
}
else // we read '0'
{
if (ministate==0) // error, two '0' in a row
{
resetstate();
return();
}
else if (ministate==-1) // OK, next bit should be '1'
{
ministate = 0;
}
else if (ministate==1) // OK, bit pair completed
{
ministate = -1;
rawdata = (rawdata<<1);
rawdata +=1;
}
}
state = 3;
starttimer(kMaxPulseWidth);
return;
}
}
ISR(TIMER1_COMPA_vect)
{
if (state==0)
{
state = 1;
TCCR1B &= ~(1 << CS11); //stop the timer
return;
}
// pulsewidth/delay were to long
if ((state == 1) || (state == 2) || (state == 3))
{
PORTC = 0b00100000;
PORTC = 0b00000000;
_delay_us(5);
PORTC = 0b00100000;
PORTC = 0b00000000;
resetstate();
return;
}
// end of data burst, wait for next burst
if (state==4)
{
data = rawdata;
rawdata = 0;
dataready=1;
starttimer(kPulseTrainDelay>>1);
state = 0;
}
}
This extracts the data in the pulsetrain, but it does not do any accumulation of the data in the pulse trains. Usaully there are at least four bursts that contains the same data.