Sunday, December 22, 2013

Project 0 - My own wireless Lego Power Functions Hub

I bought a Lego Crawler and wanted to connect it to my phone, tablet and PC. Now Lego has deleted my Cuusoo entry because It doesn't meet the new Cuusoo guidelines.


So I wondered if I could create my own Wireless Lego PF HUB. Three months ago I had no programming nor any electrical experience. So forgive for inefficient code and clumsy electric schematics.

I have 3 wishes for the Hub:

  • it has to be Wireless and able to connect to the internet
  • it as to small enough to fit into most models
  • it has to be affordable and even better be cheap.
  • it has to be Open Source. Front end and back end.
  • While searching the web for a cheap Arduino Wifi shield I bumped into the Electric Imp (or Impee).


    The Electric Imp Website

    The Imp is great. It is small, cheap and wireless!.
    During my searches around the net I bumped into the Electic Imp. I use this instead of my beloved Arduino now.

    PROS
  • pretty small
  • cheap
  • open source for the front end as well as the back end
  • control up to 8 motors separately
  • Use as many tablets, Iphones and Desktops as you want at the same time
  • use one Imp to control multiple models. For instance if you have 3 models. 3 people can use there smartphone to control their models individually. The hub doesn't have to be inside a model.

  • CONS
  • not a lego original part
  • still uses the chunky ir receivers
  • You need a internet connection and Wifi router.
  • It took me some time but I managed to get the Imp + IR Lego Reciever to work.
    See my next project for the schematic and the code.

    Project 1 - Lego IR transmitter with Electric Imp

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