Created
March 29, 2012 23:11
-
-
Save dccourt/2244790 to your computer and use it in GitHub Desktop.
Receive from Maplin RF transmitter using Arduino
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// See http://www.fanjita.org/serendipity/archives/51-Interfacing-with-radio-controlled-mains-sockets-part-1.html | |
#define STATE_NOISE 0 | |
#define STATE_COUNT_PRE 1 | |
#define STATE_COUNT_RISE 2 | |
#define STATE_DECODE_MSG_HI 3 | |
#define STATE_DECODE_MSG_LO 4 | |
// payload size in bits | |
#define PAYLOAD_SIZE 48 | |
#define SAMPLES_PER_MILLI 20 | |
// minimum preamble (zero) length in ms | |
#define MINIMUM_PREAMBLE 13L | |
#define MAXIMUM_PREAMBLE 15L | |
// Maximum first pulse (one) length in ms. Also defines the max length that we | |
// will consider to be a 'short' pulse. | |
#define MAXIMUM_FIRST 0.7f | |
#define MINIMUM_FIRST 0.4f | |
#ifdef __AVR_ATtiny85__ | |
#define DEBUGLN(A) | |
#define DEBUG(A) | |
#define DEBUGINIT | |
// PB3 is DIP pin 2. PB0 is DIP pin 5 | |
#define INPUTPIN 3 | |
#define OUTPUTPIN 0 | |
#define BITDEBUGPIN 1 | |
#define STATEDEBUGPIN 2 | |
#else | |
#define DEBUGLN(A) Serial.println(A) | |
#define DEBUG(A) Serial.print(A) | |
#define DEBUGINIT Serial.begin(115200) | |
#define INPUTPIN 9 | |
#define OUTPUTPIN 13 | |
#define BITDEBUGPIN 10 | |
#define STATEDEBUGPIN 11 | |
#endif | |
// Now works to identify all buttons and channels on Arduino Uno. | |
// Less solid on ATTiny85 @ 8MHz, but still differentiates channels which is good enough. | |
int state; | |
long timer_start; | |
long pulse_width; | |
int preamble_width; | |
int num_bits; | |
long payload[4]; | |
void setup() { | |
pinMode(INPUTPIN, INPUT); | |
pinMode(OUTPUTPIN, OUTPUT); | |
DEBUGINIT; | |
DEBUGLN("Begin"); | |
// DEBUGLN(sizeof(payload)); | |
state = STATE_NOISE; | |
timer_start = 0; | |
// Send centre spring voltage high for 20ms or so to trigger the monkey! | |
digitalWrite(OUTPUTPIN, LOW); | |
} | |
void print_long(long x) | |
{ | |
DEBUGLN(x); | |
for (int ii = 0; ii < PAYLOAD_SIZE; ii++) | |
{ | |
if (ii % 4 == 0) | |
{ | |
DEBUG(" "); | |
} | |
if (x & (1L << ii)) | |
{ | |
DEBUG("1"); | |
} | |
else | |
{ | |
DEBUG("0"); | |
} | |
} | |
} | |
void print_payload() | |
{ | |
// DEBUGLN(preamble_width); | |
// DEBUGLN(pulse_width); | |
for (int ii = 0; ii <= (PAYLOAD_SIZE / 32); ii++) | |
{ | |
// print_long(payload[ii]); | |
} | |
// DEBUGLN("."); | |
// Attempt to identify the payload bytes | |
int chan = -1; | |
int button = -1; | |
switch (payload[0]) | |
{ | |
case 859124533L: | |
chan = 1; | |
button = 1; | |
break; | |
case 861090613L: | |
chan = 1; | |
button = 2; | |
break; | |
case 892547893L: | |
chan = 1; | |
button = 3; | |
break; | |
case 1395864373L: | |
chan = 1; | |
button = 4; | |
break; | |
case 859124563L: | |
chan = 2; | |
button = 1; | |
break; | |
case 861090643L: | |
chan = 2; | |
button = 2; | |
break; | |
case 892547923L: | |
chan = 2; | |
button = 3; | |
break; | |
case 1395864403L: | |
chan = 2; | |
button = 4; | |
break; | |
case 859125043L: | |
chan = 3; | |
button = 1; | |
break; | |
case 861091123L: | |
chan = 3; | |
button = 2; | |
break; | |
case 892548403L: | |
chan = 3; | |
button = 3; | |
break; | |
case 1395864883L: | |
chan = 3; | |
button = 4; | |
break; | |
case 859132723L: | |
chan = 4; | |
button = 1; | |
break; | |
case 861098803L: | |
chan = 4; | |
button = 2; | |
break; | |
case 892556083L: | |
chan = 4; | |
button = 3; | |
break; | |
case 1395872563L: | |
chan = 4; | |
button = 4; | |
break; | |
} | |
// on or off status is seen in the second payload word. | |
int on = -1; | |
switch (payload[1]) | |
{ | |
case 13107L: | |
{ | |
on = 1; | |
} | |
break; | |
case 21299L: | |
{ | |
on = 0; | |
} | |
break; | |
} | |
if ((chan != -1) && (on != -1)) | |
{ | |
DEBUG(chan); | |
DEBUG(" "); | |
DEBUG(button); | |
DEBUG(" "); | |
DEBUGLN(on); | |
if ((chan == 1) && (button = 1) && (on == 1)) | |
{ | |
// This is our special button. Trigger the output. | |
digitalWrite(OUTPUTPIN, HIGH); | |
delay(20); | |
digitalWrite(OUTPUTPIN, LOW); | |
} | |
} | |
} | |
void record_bit(int val) | |
{ | |
if (val) | |
{ | |
payload[num_bits / 32] |= (1L << (num_bits % 32)); | |
} | |
num_bits ++; | |
if (num_bits == PAYLOAD_SIZE) | |
{ | |
// XXX message complete. | |
print_payload(); | |
state = STATE_NOISE; | |
} | |
} | |
void loop() { | |
// digitalWrite(13, (state == STATE_COUNT_RISE)); | |
// Run a simple FSM to react to changes in input voltage. | |
if (digitalRead(INPUTPIN) == LOW) | |
{ | |
switch (state) | |
{ | |
case STATE_NOISE: | |
{ | |
// This is our first low transition. Start counting. | |
state = STATE_COUNT_PRE; | |
timer_start = micros(); | |
} | |
break; | |
case STATE_COUNT_PRE: | |
{ | |
// still in the preamble pulse - NOP. | |
} | |
break; | |
case STATE_COUNT_RISE: | |
{ | |
// We've dropped back to zero after the first rise. How long was the pulse? | |
long now = micros(); | |
pulse_width = now - timer_start; | |
// We expect the pulse to be less than 2ms. | |
if ((pulse_width < (MAXIMUM_FIRST * 1000L)) && | |
(pulse_width > (MINIMUM_FIRST * 1000L))) | |
{ | |
// init decoding state | |
timer_start = now; | |
digitalWrite(STATEDEBUGPIN, 1); | |
state = STATE_DECODE_MSG_LO; | |
num_bits = 0; | |
payload[0] = 0; | |
payload[1] = 0; | |
payload[2] = 0; | |
payload[3] = 0; | |
// DEBUGLN(pulse_width); | |
digitalWrite(STATEDEBUGPIN, 0); | |
} | |
else | |
{ | |
/* DEBUG('.'); | |
DEBUGLN(pulse_width); */ | |
// too long, must be noise. reset state. | |
state = STATE_NOISE; | |
} | |
} | |
break; | |
case STATE_DECODE_MSG_LO: | |
{ | |
// NOP, still in a low pulse | |
} | |
break; | |
case STATE_DECODE_MSG_HI: | |
{ | |
// We were high and are now low. Figure out the length of the pulse. | |
long now = micros(); | |
digitalWrite(BITDEBUGPIN, 1); | |
long diff = now - timer_start; | |
// Was this pulse longer than our definition of a 'short' pulse? | |
record_bit(diff > (MAXIMUM_FIRST * 1000L)); | |
timer_start = now; | |
// record_bit may have reset our state | |
if (state != STATE_NOISE) | |
{ | |
state = STATE_DECODE_MSG_LO; | |
} | |
digitalWrite(BITDEBUGPIN, 0); | |
} | |
break; | |
default: | |
{ | |
DEBUGLN("Bad state"); | |
state = STATE_NOISE; | |
} | |
break; | |
} | |
} | |
else | |
{ | |
switch (state) | |
{ | |
case STATE_NOISE: | |
{ | |
// Nothing to do here. We're looking for low pulses in NOISE state | |
} | |
break; | |
case STATE_COUNT_PRE: | |
{ | |
// We reached the end of a low pulse. Was it long enough? We're looking | |
// for at least 10ms. | |
long now = micros(); | |
long diff = now - timer_start; | |
if ((diff > (MINIMUM_PREAMBLE * 1000L)) && | |
(diff < (MAXIMUM_PREAMBLE * 1000L))) | |
{ | |
// yes, long enough. | |
preamble_width = diff; | |
state = STATE_COUNT_RISE; | |
timer_start = now; | |
// DEBUG('+'); | |
// DEBUGLN(preamble_width); | |
} | |
else | |
{ | |
// not long enough. Reset state. | |
state = STATE_NOISE; | |
/* DEBUG('*'); | |
DEBUG(MINIMUM_PREAMBLE * 1000); | |
DEBUG(':'); | |
DEBUG(MAXIMUM_PREAMBLE * 1000); | |
DEBUG(':'); | |
DEBUGLN(diff); | |
*/ | |
} | |
} | |
break; | |
case STATE_COUNT_RISE: | |
{ | |
// We're in the middle of a high pulse, and still high : NOP. | |
} | |
break; | |
case STATE_DECODE_MSG_HI: | |
{ | |
// NOP, still in a high pulse | |
} | |
break; | |
case STATE_DECODE_MSG_LO: | |
{ | |
// We were low and are now high. Figure out the length of the pulse. | |
long now = micros(); | |
digitalWrite(BITDEBUGPIN, 1); | |
long diff = now - timer_start; | |
// Was this pulse longer than our definition of a 'short' pulse? | |
record_bit(diff > (MAXIMUM_FIRST * 1000L)); | |
timer_start = now; | |
// record_bit may have reset our state | |
if (state != STATE_NOISE) | |
{ | |
state = STATE_DECODE_MSG_HI; | |
} | |
digitalWrite(BITDEBUGPIN, 0); | |
} | |
break; | |
default: | |
{ | |
DEBUGLN("Bad state2"); | |
state = STATE_NOISE; | |
} | |
break; | |
} | |
} | |
delayMicroseconds((1000 / SAMPLES_PER_MILLI)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment