Tuesday, December 17, 2013

Project 1 - Lego IR transmitter with Electric Imp

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.)

No comments :

Post a Comment