Lego IR Remote (Power Functions)

Hi All,

I recieved my Imp yesterday. And I love it! I can put my Arduino on eBay now. I am trying to translate all Arduino c++ codes to my Imp. But I am a complete Squirrrel newbie. I have only some basic C++ knowledge.

One of my code doesn’t want to be translated.
I tried to make a remote IR control for my kids to control their Technic Lego models.
For instance http://www.youtube.com/watch?v=hjmaHYJ3NY0

I have this code running perfectly on my Arduino Uno.

This is my Squirrel code:
http://pastebin.com/KJQjT1M9

or

`
// configure the imp (best practice)
imp.configure(“Lego IR Remote”, [], []);

// create a global variabled called led,
// and assign pin9 to it
led <- hardware.pin9;

toggle <- [0,0,0,0];
// create a global variable to store current
// state of the LED
state <- 0;
s2ms <- 0.000001;

//The used Global variabels
PWM_FWD7 <- 0x7;
PWM_FLT <- 0x0;

//channel
CH1 <- 0x0;

//output
RED <- 0x0;

// configure led to be a digital output
led.configure(DIGITAL_OUT);

// create a global variable to store current
// state of the LED
state <- 0;

function blink() {
// invert the value of state:
// when state = 1, 1-1 = 0
// when state = 0, 1-0 = 1
state = 1-state;

// sWITCHING IR COMMANDS

server.log("Starting motor");
SingleOutput(PWM_FWD7,RED,CH1);


// schedule imp to wakeup in .5 seconds and do it again.
imp.sleep(5);

server.log("Stopping motor");
SingleOutput(PWM_FLT, RED,CH1);



// schedule imp to wakeup in .5 seconds and do it again.
imp.wakeup(5, blink);

}

function SingleOutput(pwm,output,channel) {

server.log(“Sending IR Commands…”)

SINGLE_OUTPUT <- 0x4;

nib1 <- toggle[channel] | channel;
nib2 <- SINGLE_OUTPUT | output;
nib3 <- pwm;
nib4 <- 0xf ^ nib1 ^ nib2 ^ nib3;

for (local i=0; i<6; i++)
{
message_pause(channel, i);
pf_send(nib1 << 4 | nib2, nib3 << 4 | nib4);

}

if(toggle[channel] == 0)
toggle[channel] = 8;
else
toggle[channel] = 0;

server.log(“ready”)

}

function start_pause()
{
imp.sleep(1014*s2ms);
}

function high_pause()
{
imp.sleep(546*s2ms);
}

function low_pause()
{
imp.sleep(260*s2ms);
}

function tx_pause()
{
imp.sleep(156*s2ms);
}

function message_pause(channel, count)
{
a <- 0;

if(count == 0)
a = 4 - channel + 1;
else if(count == 1 || count == 2)
a = 5;
else if(count == 3 || count == 4)
a = 5 + (channel + 1) * 2;

imp.sleep(a * 77 * s2ms);

}

function pf_send(code1, code2)
{
x <- 128;

start_stop_bit();

while (x)
{
oscillationWrite(156 * s2ms);

if (code1 & x) //high bit
  high_pause();
else //low bit
low_pause();

x = x >> 1;  //next bit

}

x = 128;
while (x)
{
oscillationWrite(156*s2ms);

if (code2 & x) // high bit
  high_pause();
else //low bit
low_pause();

x = x >> 1;  //next bit

}

start_stop_bit();

}

function start_stop_bit()
{
oscillationWrite(156*s2ms);
start_pause();
}

function oscillationWrite(time) {
for( local i=0; i <= time/26; i++) {

led.write(1)
imp.sleep(13*s2ms);
led.write(0)
imp.sleep(13*s2ms);

}
}

// start the loop
blink();
`

Here is my Arduino code:
http://pastebin.com/E2N8KW2j

or

`//mode
#define SINGLE_OUTPUT 0x4

//PWM speed steps
#define PWM_FLT 0x0
#define PWM_FWD7 0x7

//channel
#define CH1 0x0

//output
#define RED 0x0

int IRPin = 13;
int toggle[4] = {0,0,0,0};

void setup()
{
pinMode(IRPin, OUTPUT);
digitalWrite(IRPin, LOW);

}

void loop()
{
SingleOutput(PWM_FWD7, RED, CH1);
delay(2000);
SingleOutput(PWM_FLT, RED, CH1);
delay(2000);

}

void pf_send(int code1, int code2)
{
int x = 128;

start_stop_bit();

while (x)
{
oscillationWrite(IRPin, 156);

if (code1 & x) //high bit
  high_pause();
else //low bit
  low_pause();

x >>= 1;  //next bit

}

x = 128;
while (x)
{
oscillationWrite(IRPin, 156);

if (code2 & x) // high bit
  high_pause();
else //low bit
  low_pause();

x >>= 1;  //next bit

}

start_stop_bit();
delay(10);
}

void SingleOutput(int pwm, int output, int channel)
{
int nib1, nib2, nib3, nib4, i;

//set nibs
nib1 = toggle[channel] | channel;
nib2 = SINGLE_OUTPUT | output;
nib3 = pwm;
nib4 = 0xf ^ nib1 ^ nib2 ^ nib3;

for(i = 0; i < 6; i++)
{
message_pause(channel, i);
pf_send(nib1 << 4 | nib2, nib3 << 4 | nib4);
}

if(toggle[channel] == 0)
toggle[channel] = 8;
else
toggle[channel] = 0;
}

void start_pause()
{
delayMicroseconds(1014);
}

void high_pause()
{
delayMicroseconds(546);
}

void low_pause()
{
delayMicroseconds(260);
}

void tx_pause()
{
delayMicroseconds(156);
}

void message_pause(int channel, int count)
{
unsigned char a = 0;

if(count == 0)
a = 4 - channel + 1;
else if(count == 1 || count == 2)
a = 5;
else if(count == 3 || count == 4)
a = 5 + (channel + 1) * 2;

delayMicroseconds(a * 77);
}

void start_stop_bit()
{
oscillationWrite(IRPin, 156);
//digitalWrite(IRPin, HIGH);
//tx_pause();
//digitalWrite(IRPin, LOW);
start_pause();
}

void oscillationWrite(int pin, int time) {
for(int i = 0; i <= time/26; i++) {
digitalWrite(pin, HIGH);
delayMicroseconds(13);
digitalWrite(pin, LOW);
delayMicroseconds(13);
}
}`

I don’t know what I have done wrong. The Arduino code is working great. The Squirrel/Imp code doesn’t react at all.

Please help. I am lost here.

Thanks

Jeroen :-*

Some extra information.

I checked all functions. It isn’t the squirrel translation. I monitored the Arduino variables and the Imp variables. All functions are returning the same values.
So there is something with the imp.sleep(). I attached a normal LED to my Arduino and at the same time to my Imp. The LED from my Arduino flickers the LED from my Imp just turns on and off. The timing is not the same at all! How can this be?

I filmed it but my digital camera has a frame rate that is too slow. It doesn’t show the flickering difference. This would have helped to show the flickering difference.

I think this is interesting. I got the same thing with my television remote. It works with my Arduino and not with my Imp.

Is there a known issue with the imp.sleep(). Am I using it wrong?

Again. Still lost. Please help.

Thanks for your time!

The imp runs an interpreted language; you can’t bit-bang microsecond level sequences in code because it doesn’t run fast enough. The arduino code is compiled to assembler before download, the imp compiles squirrel to bytecode and then interprets it.

For example, every time you are doing an imp.sleep with a multiplication in there, the multiplication is happening at runtime, looking up the s2ms variable, checking the value, doing a float multiply, etc.

Have a look at the Sana reference design and code to see how to use an imp to send IR.

If you have a website to use, you could have your kids use any browser, tablet, ipod, smartphone, or laptop to control your imp/legos. Ditch the tv remote because an actual webpage would allow all kinds of programming and creativity.

@Hugo. I understand the difference now. (I’m a newbie so please forgive me for my ignorance.)
I used the multiply and variable because I was a bit lazy. I’ll test the code with the correct values and make it easier on my Imp.
I’ll check the Sana project code. I didn’t know of the project.

@ miseim. You misunderstood. I used my tv to test a remote control code. My kids and I can’t use the remote control at all because my wife is using it all the time :frowning: .
My main intention is to use any browser, tablet, ipod, smartphone, or laptop to control your imp/legos.

Thanks guys for your quick reply!

Please one more question guys,

If I understand the Sana code correctly you create a string and write the string to the SPI257 port.
So looking at my Arduino code I create a string of 1’s and 0’s accordingly to the Lego receiver specs and write the string to the SPI port. The string will contain between 600 and 750 characters. I hope this is not too long. I guess not. I split the string in smaller pieces and use for loops I lose the speed like in my first code.

I took closer look at the Sana scheme and don’t understand how my IR Led will exactly be connected to my Imp.

The site says:

The PWM output is configured to produce a carrier signal at the carrier frequency for a given device. Given a binary string and timing definitions for a given receiving device, the imp then generates a pulse train and sends it over the SPI bus. A simple AND gate modulates the carrier with the IR pulse train, and uses the resulting signal to drive a FET, gating power to an external IR LED.

I’m an architect so for me this is like Chinese. I found in the scheme of the Sana the SN74AUP1G08DCKR AND gate. The FET is a NXP - 2N7002P - MOSFET if I get the part list right. The Led, if figure can also an internal led and doesn’t have to be attached to a audio plug like in the Sana.
Is this the easiest way to do this? I thought I hook up a Led to PinX and GND, upload some code and start playing. I come to realize this might be a bit too difficult for me.

Sorry for bothering you with this question but I really wanted to learn this stuff. Thank you for any help you can provide in this situation.

Jeroen

Yep, you need the AND gate here as you are transmitting modulated IR; the PWM provides the modulation and the SPI provides the “envelope”. The FET is there to increase the current to blast the IR… you could likely do without.

You will need an AND gate, though - and yes the LED could be on board (or on a breadboard or whatever).

Hi Hugo,

I ordered the missing parts (AND gate, resistors, etc.) and can’t wait to start building my remote!

I’m making a string (the blob) containing the 0’s and 1’s accordingly to the Lego receiver’s specs.

The lego receiver uses a 38kHZ signal. It uses an up and down in 1 cycle.
One pulse is 1/38000 = 26 microseconds. The only thing is the Lego receiver expects a 1 and 0 within 26 microseconds so 13 microseconds up 13 microseconds down.(like in my oscillationWrite function in my first post).

How do I use the PWM + SPI257 in this case?

Do I speed it up to 77KHZ (1/13µs) and put a 1 and 0 for every pulse. The duty cycle is in this case 100% I think.
So if the string is (for example) “1010101010” every 1 is full current to the AND gate, every 0 is no current to the AND gate every 0.000013 seconds.

Does the PWM and the SPI257 operate at this speed?

Or can I set the PWM period to 38kHZ (26µs) like in the Sana project and set the duty cycle of the pulse to 50%? The pulse takes 26µs. So every 1 in the blob is a 1 (for 13µs) and a 0 (for 13µs) and every 0 in the blob string consists of a 0 (for 13µs) and again a 0 (for 13µs).
The blob string is “1111000” and results in 10101010000000.

What is the right way of doing this?

Thank you very much for helping me out.

Jeroen

This is the scheme of the Lego Receiver.
http://www.philohome.com/pf/LEGO_Power_Functions_RC_v120.pdf (page 11).

You’d set 38kHz, 50% PWM. This gives a 38kHz square wave, which is the carrier you want (like sana).

Then, you work out the nearest SPI clock rate that will allow you to send the correct “envelope” for the carrier, and work out the 1’s and 0’s that you’ll need to send. This is the bit you’re missing right now. There will be multiple 1’s and 0’s in the SPI data in order to form the envelope.

Jeroen,

You don’t need to worry about manually modulating the signal at all; just set the PWM output to 38kHz and 50% duty cycle as Hugo says, and the AND gate takes care of modulating your output signal.

The IR transmitter class (https://github.com/electricimp/reference/tree/master/hardware/ir_transmitter) takes care of working out the clock timing for you, so you don’t even have to worry about that provided the Lego module uses the commonly-used pulse-position-modulation scheme that most IR remote equipment uses.

Take a look at that class and at the protocol your device uses. You might need to make some changes, but it should get you a good head start, and it’s pretty heavily commented.

It’s alive!!! Issue solved.

How could I overlooked the Sana transmitter code? I adapted the code a little. Then build the Sana transmitter diagram and it worked right away!
I’ll post the code and will make a Fritzing schematic for other newbies as soon as possible. Maybe other Lego lovers also want to build it.

Thanks again guys!

This library has much better range then that code
https://github.com/dwc56/LegoIrRemote.git

@dwc56

This thread and posts are like 3 years old.
A lot has changed since then.