So here's my first working Imp IR transmitter for Lego Power Functions.
Below I posted my code and schematic.
For more infomation about the Electric Imp click on the image below.
MY PROTO
THE HARDWARE
For this project I used:
1x Imp Dev Card
1x April breakout
1x bBreadboard (any will do)
2x 100k resistors
1x 1uF capacitor and some wires.
This is the schematic.
THE CODE
The Imp code consists of two parts. An Agent part and a Device part. You can copy/paste the code in your Imp Ide
The Device part
// IR Transmitter for Lego Power Functions Recieve
// version 0.4
//
// This transmitter sends to 4 channels RED and BLUE
// the motor speed can be varied.
// for more information on the Lego PF reciever
// http://www.philohome.com/pf/LEGO_Power_Functions_RC.pdf
//
// This code is based on the transmitter code from the Sana project:
// http://devwiki.electricimp.com/doku.php?id=sana
// the original code and explaining comments:
// https://github.com/eslectricimp/reference/blob/master/hardware/ir_transmitter/ir_transmitter.device.nut
//
imp.configure("IR Transmitter for Lego Power Functions",[],[]);
spi <- hardware.spi257;
pwm <- hardware.pin1;
function drive(code) {
channel <- code[0]-48;
color <- code[1]-48;
pwmcommand <- 0;
if (code[2] >= 48 && code[2] <= 58)
pwmcommand = code[2]-48;
else if (code[2] >= 97 && code[2] <= 102)
pwmcommand = code[2]-87;
server.log("code:" + channel+color+pwmcommand)
// Lego Reciever uses 38kHz
// All Values are in microseconds
local START_STOP_TIME_HIGH = 156.0;
local START_STOP_TIME_LOW = 1014.0;
// Pulse takes 6 cycles (6*1/38000=156us)
local PULSE_TIME = 156.0;
// 21 cycles to mark a "1" (in microseconds)
local TIME_LOW_1 = 546.0;
// 10 cycles to mark a "0" (in microseconds)
local TIME_LOW_0 = 260.0;
// PWM carrier frequency (typically 38 kHz in US, some devices use 56 kHz, especially in EU)
local CARRIER = 38000.0;
// The Lego transmitter repeats 6 times
local CODE_REPEATS = 6;
local toggle = [0,0,0,0];
/* Configure the SPI and PWM for each send.
* This ensures that they're not in an unknown state if reconfigured by other code between sends */
this.pwm.configure(PWM_OUT, 1.0/CARRIER, 0.0);
local clkrate = 1000.0 * spi.configure(SIMPLEX_TX,117);
local bytetime = 8 * (1000000.0/clkrate);
// ensure SPI lines are low
spi.write("\x00");
// calculate the number of bytes we need to send each signal
local start_stop_bytes_high = (START_STOP_TIME_HIGH / bytetime).tointeger();
local start_stop_bytes_low = (START_STOP_TIME_LOW / bytetime).tointeger();
local pulse_bytes = (PULSE_TIME / bytetime).tointeger();
local bytes_1 = (TIME_LOW_1 / bytetime).tointeger();
local bytes_0 = (TIME_LOW_0 / bytetime).tointeger();
local code_blob = blob(pulse_bytes); // blob will grow as it is written
// calculating the code
// for other outputs check http://www.philohome.com/pf/LEGO_Power_Functions_RC.pdf
local single_output = 0x4;
local nib1 = toggle[channel] | channel;
local nib2 = single_output | color;
local nib3 = pwmcommand;
local nib4 = 0xf ^ nib1 ^ nib2 ^ nib3;
local code = [0,0];
code[0] = nib1 << 4 | nib2;
code[1] = nib3 << 4 | nib4;
if(toggle[channel] == 0)
toggle[channel] = 8;
else
toggle[channel] = 0;
// Write the start sequence into the blob
for (local i = 0; i < start_stop_bytes_high; i++) {
code_blob.writen(0xFF, 'b');
}
for (local i = 0; i < start_stop_bytes_low; i++) {
code_blob.writen(0x00, 'b');
}
// now encode each bit in the code
foreach (bit in code){
local x = 128;
local low_bytes = 0;
while (x) {
// first, encode the pulse (same for both states)
for (local j = 0; j < pulse_bytes; j++) {
code_blob.writen(0xFF,'b');
}
// now, figure out if the bit is high or low
if (bit & x) //high bit
//server.log("Encoding 1");
low_bytes = bytes_1;
else //low bit
//server.log("Encoding 0");
low_bytes = bytes_0;
// write the correct number of low bytes to the blob, then check the next bit
for (local k = 0; k < low_bytes; k++) {
code_blob.writen(0x00,'b');
}
x = x >> 1; //next bit
}
}
// Write the stop sequence into the blob
for (local i = 0; i < start_stop_bytes_high; i++) {
code_blob.writen(0xFF, 'b');
}
for (local i = 0; i < start_stop_bytes_low; i++) {
code_blob.writen(0x00, 'b');
}
// calculate the pauses between the sends
// It starts with 6x a 0. For channel 1 this will be 5 5 5 7 7 0
local pauses = [0,0,0,0,0,0];
for (local i=0; i<6; i++) {
local a = 0;
if(i == 0)
a = 4 - channel + 1;
else if(i == 1 || i == 2)
a = 5;
else if(i == 3 || i == 4)
a = 5 + (channel + 1) * 2;
//
pauses[i]=a*0.000077;
}
// enable PWM carrier
pwm.write(0.5);
// send code 6 times a different intervals
for (local i = 0; i < CODE_REPEATS; i++) {
spi.write(code_blob);
// clear the SPI bus
spi.write("\x00");
imp.sleep(pauses[i]);
}
// disable pwm carrier
pwm.write(0.0);
// clear the SPI lines
spi.write("\x00");
server.log("Signal send");
}
// start
agent.on("PWMCODE", drive);
The Agent part
/*PWM speed steps
PWM float = 0
PWM forward speed 1 -> 7 = 1 -> 7
PWM break = 8
PWM reverse speed 9 -> F = 7 -> 1
*/
server.log("Start motor: " + http.agenturl() + "?chn=1&col=0&pwm=7");
function requestHandler(request, response) {
try {
code <- request.query.chn + request.query.col + request.query.pwm ;
device.send("PWMCODE", code);
response.send(200, "OK");
}
catch (ex) {
response.send(500, "Internal Server Error: " + ex);
}
}
// register the HTTP handler
http.onrequest(requestHandler);
To check if it works you should check out you Imp's URL.
(Imp ide screenshot)
Add the following to this URL: https://
your imp url?chn=0&?col=0&pwm=7 and press Enter
If your Lego reciever is set to channel 1 and your Power Functions motor is connected to the red port it should work.
To change channels and ports add different values to the URL. For instance full backwards on channel 3 on the blue port results in ?chn=2&col=1&pwm=9.
(note: channel 1 -> 4 is represented by 0 -> 3. Color RED = 0 and BLUE = 1. For the PWM codes check the comments add to the code above.)