LEGO power functions is a system of electronic components, motors,
lights, remote controls to power various LEGO models.
The remote control works with IR, but works surprisingly well even
when there is no direct visual contact. The handheld unit is small,
has two joystick-buttons, and allows 4 different channels to be selected.
Much cooler than a TV remote!
Interesting for hacking, LEGO has made the protocol open source, so
anybody can play with it, as long as not making it commercial.
The protocol can be found here:
So I decoded the powerfuctions protocol with the launchpad/energia:
Apart from the lauchpad, I only needed a IR receiver. I tested with
a Vishay TSOP38338, other sensors might work as well.
The processor sleeps when doing nothing, wake up only when there is
something to do. It is low power, but not ultra low, since the IR
receiver needs around 0.4mA.
//example program für Lego Power Functions Remote
//2014-07-04
//
//tested with Energia, Launchpad, MSP430G2553, 16Mhz
//IR receiver Vishay TSOP38338:
//Pin1 to Pin 3 on Launchpad Signal
//Pin2 to Pin 20 on Launchpad GND
//Pin3 to Pin 1 on Launchpad +3.3V
volatile byte out;
void setup() {
pinMode(2, OUTPUT); //red led
blinky (3); //just checking
pinMode(3, INPUT); //remote sensor
attachInterrupt(3, legosig, FALLING);
}
void loop(){
// this if for channel 1,
// add 16 for channel 2
// add 32 for channel 3
// add 48 for channel 4
// see page 7 on Lego spec: "Combo direct Mode"
LPM3;
if (out){
if (out==1 ) blinky(1); //left up
if (out==2 ) blinky(2); //left down
if (out==4 ) blinky(3); //right up
if (out==8 ) blinky(4); //right down
if (out==5 ) blinky(5); //both up
if (out==10) blinky(6); //both down
if (out==9 ) blinky(7); //left up right down
if (out==6 ) blinky(8); //left down right up
out=0;
}
}
void blinky(byte a){
for(int i=0;i<a;i++){
digitalWrite(2,1);
delay(200);
digitalWrite(2,0);
delay(200);
}
}
void legosig(){ //interrupt service routine reads powerfunctions protocol
//global variable "out" contains remote code when succesful,
//otherwise 0
int p,q;
word telegram;
if (!out){
telegram = 0;
if (checkonebit()==0) return; //check for start bit
for (q = 0; q < 16; q++){ //16 check 16 data bits
if ((p=checkonebit())==0) return;
telegram<<=1;if(p>18) telegram|=1; //18:threshold, 421us<(18*25us+overhead)<711us
}
out=lowByte(telegram)^highByte(telegram); //xor for parity check
if(((out/16^out)&B00001111)!=15) out=0; //do the parity check
out=lowByte(telegram)/16+(highByte(telegram)&B00110000); //reduce to 6 bit
}
}
int checkonebit(){ //check one bit of remote protocol
int loops=0; //returns lenght when succesful / 0 when too long
while (!(P1IN & B00000010)) {
delayMicroseconds(25);
loops++; if (loops>64) return 0;
}
loops=0;
while (P1IN & B00000010) {
delayMicroseconds(25);
loops++; if (loops>64) return 0;
}
return loops;
}