Skip to content

Instantly share code, notes, and snippets.

@dccourt
Created March 29, 2012 23:11
Show Gist options
  • Save dccourt/2244790 to your computer and use it in GitHub Desktop.
Save dccourt/2244790 to your computer and use it in GitHub Desktop.
Receive from Maplin RF transmitter using Arduino
// 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