Home > Arduino, Development boards, Guides, TKJ Electronics > NXT Shield Version 2

NXT Shield Version 2

Update
The NXT Shield is for sale in our shop: http://shop.tkjelectronics.dk/product_info.php?products_id=29. A easy to use library is also provided: https://github.com/TKJElectronics/NXTShield.
Three examples that demonstrates reading the encoders, turning the motors and using the ultrasonic sensor is found in the library as well: https://github.com/TKJElectronics/NXTShield/tree/master/examples.

More pictures of the NXT Shield can be found at the following blog post: http://blog.tkjelectronics.dk/2012/04/nxt-shield-library/.


I finally made a new version of my NXT Shield. The big news is that it now supports the Lego Ultrasonic Sensor and it has NXT Compatible Sockets from mindsensors.


I won’t go much in detail on how the motors and encoders work, as I did that in the old post. So if you want to know how they work. Se my previous post. Instead I will focus on the Lego Ultrasonic Sensor.

Ultrasonic Sensor
Below is the pinout for the Ultrasonic Sensor:

NXT Input Port

  • Pin 1 = 9V supply
  • Pin 2 = GND
  • Pin 3 = GND
  • Pin 4 = IPOWERA – 4.3V supply
  • Pin 5 = DIGIAI0 – I2C Clock Line (SCL) – needs a extra wiggle between read and write commands
  • Pin 6 = DIGIAI1 – I2C Data Line (SDA)


  • As there is already pullup resistors inside the Lego Ultrasonic Sensor (see the schematic for more details), there is no need to add those on the data and clock lines.
    The I2C frequency has to be adjusted – the datasheet says 9600Hz, but I have adjusted it to 11494,253Hz, as it is the same as the one used by the NXT (measured with my logic analyzer).
    For more information see this pdf, at “Input ports” and “I2C Communication”.
    Furthermore if you have a look at the Appendix 7-LEGO MINDSTORMS NXT Ultrasonic Sensor I2C communication protocol, it says byte 2 is R + 0×03, this means that the user have to send a repeated start and read from the device straight away.
    So for example if you want to read the product version. The official Arduino Library (Wire.h) doesn’t support repeated start. So instead I decided to use a modified version of Peter Fleury excellent library: I2Cmaster, as it can be used for any AVR devices.
    The library is modified to work with the Lego Ultrasonic Sensor (I2C frequency set to 11494,253Hz), and the CPU frequency has been set to the one used by Arduino (16MHz). Keywords has also been added, so the commands will colored orange.

    The modified library can be found here: i2cmaster library.

    But that’s not all! For some reason Lego decided to use a non-offical I2C firmware, therefore the clock line needs an extra wiggle between the write and read commands as explained at this post. Therefore pin 4 is connected to the clock line (SCL) on the shield.

    Below is a example sketch which will print out the following on the terminal:


    The first one is the version number (V1.0), the second one the product ID (LEGO), then the sensor type (Sonar), then measurements units (10E-2m). After that it starts printing out the distance measured by the sensor.
    All the I2C commands supported by the Ultrasonic can be found here:
    Appendix 7-LEGO MINDSTORMS NXT Ultrasonic Sensor I2C communication protocol.

    #include <i2cmaster.h>

    byte clockPin = 4;
    byte buf[9];//Buffer to store the received valeus
    byte addr = 0x02;//address 0x02 in a 8-bit context - 0x01 in a 7-bit context
    byte distance;

    void setup()
    {  
      i2c_init();//I2C frequency = 11494,253Hz
      Serial.begin(115200);
      printUltrasonicCommand(0x00);//Read Version
      printUltrasonicCommand(0x08);//Read Product ID
      printUltrasonicCommand(0x10);//Read Sensor Type
      printUltrasonicCommand(0x14);//Read Measurement Units  
    }

    void loop()
    {    
    //  printUltrasonicCommand(0x42);//Read Measurement Byte 0
      distance = readDistance();
      if(distance == 0xFF)
        Serial.println("Error Reading Distance");
      else
        Serial.println(distance, DEC);    
    }
    byte readDistance()
    {  
      delay(100);//There has to be a delay between commands
      byte cmd = 0x42;//Read Measurement Byte 0
       
      pinMode(clockPin, INPUT);//Needed for writing to work
      digitalWrite(clockPin, HIGH);  
     
      if(i2c_start(addr+I2C_WRITE))//Check if there is an error
      {
        Serial.println("ERROR i2c_start");
        i2c_stop();
        return 0xFF;
      }    
      if(i2c_write(cmd))//Check if there is an error
      {
        Serial.println("ERROR i2c_write");
        i2c_stop();
        return 0xFF;
      }  
      i2c_stop();
       
      delayMicroseconds(60);//Needed for receiving to work
      pinMode(clockPin, OUTPUT);
      digitalWrite(clockPin, LOW);
      delayMicroseconds(34);
      pinMode(clockPin, INPUT);
      digitalWrite(clockPin, HIGH);
      delayMicroseconds(60);  
       
      if(i2c_rep_start(addr+I2C_READ))//Check if there is an error
      {
        Serial.println("ERROR i2c_rep_start");
        i2c_stop();    
        return 0xFF;
      }  
      for(int i = 0; i < 8; i++)
        buf[i] = i2c_readAck();
      buf[8] = i2c_readNak();  
      i2c_stop();
     
      return buf[0];  
    }

    void printUltrasonicCommand(byte cmd)
    {
      delay(100);//There has to be a delay between commands
       
      pinMode(clockPin, INPUT);//Needed for writing to work
      digitalWrite(clockPin, HIGH);
     
      if(i2c_start(addr+I2C_WRITE))//Check if there is an error
      {
        Serial.println("ERROR i2c_start");
        i2c_stop();
        return;
      }
      if(i2c_write(cmd))//Check if there is an error
      {
        Serial.println("ERROR i2c_write");
        i2c_stop();
        return;
      }
      i2c_stop();
       
      delayMicroseconds(60);//Needed for receiving to work
      pinMode(clockPin, OUTPUT);
      digitalWrite(clockPin, LOW);
      delayMicroseconds(34);
      pinMode(clockPin, INPUT);
      digitalWrite(clockPin, HIGH);
      delayMicroseconds(60);  

      if(i2c_rep_start(addr+I2C_READ))//Check if there is an error
      {
        Serial.println("ERROR i2c_rep_start");
        i2c_stop();
        return;
      }
      for(int i = 0; i < 8; i++)
        buf[i] = i2c_readAck();
      buf[8] = i2c_readNak();
      i2c_stop();  
     
      if(cmd == 0x00 || cmd == 0x08 || cmd == 0x10 || cmd == 0x14)
      {
          for(int i = 0; i < 9; i++)
          {
            if(buf[i] != 0xFF && buf[i] != 0x00)
              Serial.print(buf[i]);
            else
              break;
          }              
      }
      else
        Serial.print(buf[0], DEC);  

      Serial.println("");      
    }
    /*
    ' Wires on NXT jack plug.
    ' Wire colours may vary. Pin 1 is always end nearest latch.
    ' 1 White +9V
    ' 2 Black GND
    ' 3 Red GND
    ' 4 Green +5V
    ' 5 Yellow SCL - also connect clockpin to give a extra low impuls
    ' 6 Blue SDA
    ' Do not use i2c pullup resistor - already provided within sensor.
    */

    NXT Shield
    Below are some pictures of the shield, mounted on top of an Arduino, a FEZ Panda II, an overview of the soldered PCB, the bare PCB, and a picture of the top silkscreen.

    NXT Shield mounted on top of an Arduino


    NXT Shield mounted on top of a FEZ Panda II


    NXT Shield PCB Soldered


    NXT Shield PCB


    NXT Shield PCB


    NXT Shield top silkscreen

    The schematic can be seen below:

    NXT Shield Schematic

    Buy it as a kit
    If you do not want to make the board yourself, then we are going to sell this shield to you as a kit in the beginning of 2012 from our store.
    You are also free to make it yourself. All the gerber files ready for manufacturing at http://www.dorkbotpdx.org/wiki/pcb_order and schematic can be found here: NXT Shield Gerber files and PCB. The NXT Socket eagle part can be downloaded here.

    Example code
    An example code for controlling the motors can be seen below. I read and write directly to the registers in the interrupts, to reduce the amount of time. See this page for more information.

    //Encoders
    #define TachLeft1 2
    #define TachLeft2 5
    #define TachRight1 3
    #define TachRight2 6

    volatile int leftTachPos = 0;
    volatile int rightTachPos = 0;

    //Left motor
    #define leftPWM 9
    #define logicleft1 7
    #define logicleft2 11
    //Right motor
    #define rightPWM 10
    #define logicright1 12
    #define logicright2 8

    //Constatens for move functions
    #define left 0
    #define right 1
    #define backward 0
    #define forward 1

    #define block 2
    #define brake 1
    #define coast 0

    //Variabels for moving using encoders
    int leftPos = 0;
    int rightPos = 0;
    boolean blockLeft;
    boolean blockRight;

    void setup(){
      pinMode(TachLeft1, INPUT);
      pinMode(TachLeft2, INPUT);
      pinMode(TachRight1, INPUT);
      pinMode(TachRight2, INPUT);

      pinMode(leftPWM, OUTPUT);
      pinMode(logicleft1, OUTPUT);
      pinMode(logicleft2, OUTPUT);

      pinMode(rightPWM, OUTPUT);
      pinMode(logicright1, OUTPUT);
      pinMode(logicright2, OUTPUT);

      attachInterrupt(0, leftencoder, CHANGE); //pin 2
      attachInterrupt(1, rigthencoder, CHANGE); //pin 3

      Serial.begin(115200);
    }

    void loop(){
      //Forward at full speed for 3 seconds
      Serial.println("Forward for 3 seconds");
      move(left, forward, 255);
      move(right, forward, 255);
      delay(3000);

      //Backward at full speed for 3 seconds
      Serial.println("Backward for 3 seconds");
      move(left, backward, 255);
      move(right, backward, 255);
      delay(3000);

      //Rotate 360 degrees (one resolution) and coast motors
      Serial.println("Coast");
      move(left,forward, 255, 360, coast);
      delay(3000);  

      //Rotate 360 degrees (one resolution) and brake motors
      Serial.println("Brake");
      move(left,forward, 255, 360, brake);
      delay(3000);

      //Rotate 360 degrees (one resolution) and block motors
      Serial.println("Block");
      move(left,forward, 255, 360, block);
      delay(3000);  
    }

    void move(int motor, int way, int torque)
    {
      int pwm;
      int logic1;
      int logic2;

      if(motor == left)
      {
        pwm = leftPWM;
        logic1 = logicleft1;
        logic2 = logicleft2;
      }
      else if(motor == right)
      {
        pwm = rightPWM;
        logic1 = logicright1;
        logic2 = logicright2;
      }
      analogWrite(pwm, torque);

      if(way == backward)
      {
        digitalWrite(logic1, LOW);
        digitalWrite(logic2, HIGH);
      }
      else if(way == forward)
      {
        digitalWrite(logic1, HIGH);
        digitalWrite(logic2, LOW);
      }
    }
    void move(int motor, int way, int torque, int rotation, int halt)
    {
      if(motor == left)
      {
        analogWrite(leftPWM, torque);
        if(way == backward)    
        {
          leftPos = leftTachPos-rotation;
          digitalWrite(logicleft1, LOW);
          digitalWrite(logicleft2, HIGH);    
          while(leftTachPos > leftPos);
        }
        else if(way == forward)    
        {
          leftPos = leftTachPos+rotation;  
          digitalWrite(logicleft1, HIGH);
          digitalWrite(logicleft2, LOW);    
          while(leftTachPos < leftPos);
        }
      }
      else if(motor == right)
      {
        analogWrite(rightPWM, torque);
        if(way == backward)
        {
          rightPos = rightTachPos-rotation;
          digitalWrite(logicright1, LOW);
          digitalWrite(logicright2, HIGH);    
          while(rightTachPos > rightPos);
        }
        else if(way == forward)
        {
          rightPos = rightTachPos+rotation;
          digitalWrite(logicright1, HIGH);
          digitalWrite(logicright2, LOW);
          while(rightTachPos < rightPos);
        }
      }
      if(halt == block)
      {
        stop(motor);
        if(motor == left)
        {
          //digitalWrite(leftPWM, HIGH);
          analogWrite(leftPWM, 100);//Might have to adjusted depending on the battery voltage
          blockLeft = true;
        }
        else if(motor == right)
        {
          //digitalWrite(rightPWM, HIGH);
          analogWrite(rightPWM, 100);//Might have to adjusted depending on the battery voltage
          blockRight = true;
        }
      }
      else if(halt == brake)
      {
        stop(motor);
        if(motor == left)
          blockLeft = false;
        else if(motor == right)
          blockRight = false;    
      }
      else if(halt == coast)
      {
        if(motor == left)
        {
          digitalWrite(leftPWM, LOW);
          blockLeft = false;
        }
        else if(motor == right)
        {
          digitalWrite(rightPWM, LOW);
          blockRight = false;
        }        
      }    
    }

    void stop(int motor)
    {
      int pwm;
      int logic1;
      int logic2;
      if(motor == left)
      {
        pwm = leftPWM;
        logic1 = logicleft1;
        logic2 = logicleft2;
      }
      else if(motor == right)
      {
        pwm = rightPWM;
        logic1 = logicright1;
        logic2 = logicright2;
      }
      digitalWrite(pwm, HIGH);
      digitalWrite(logic1, HIGH);
      digitalWrite(logic2, HIGH);
    }

    void leftencoder()
    {
      if((PIND & B00100100) == 0 || (PIND & B00100100) == 36)//pin 2 == pin 5
        leftTachPos--;
      else
        leftTachPos++;

      if(blockLeft)
      {
        if(leftTachPos < leftPos)
        {
          //Turn on logicleft1 (pin 7)
          PORTD |= B10000000;

          //Turn off logicleft2 (pin 11)
          if((PORTB & B00001000) != 0)//Check if it's already off
            PORTB ^= B00001000;//If not turn it off

        }
        else if (leftTachPos > leftPos)
        {
          //Turn on logicleft2 (pin 11)
          PORTB |= B00001000;

          //Turn off logicleft1 (pin 7)
          if((PORTD & B10000000) != 0)//Check if it's already off
            PORTD ^= B10000000;//If not turn it off  
        }
      }
    }
    void rigthencoder()
    {
      if((PIND & B01001000) == 0 || (PIND & B01001000) == 72)//pin 3 == pin 6
        rightTachPos--;
      else  
        rightTachPos++;

      if(blockRight)
      {
        if(rightTachPos < rightPos)
        {
          //Turn on logicright1 (pin 12)
          PORTB |= B00010000;

          //Turn off logicright2 (pin 8)
          if((PORTB & B00000001) != 0)//Check if it's already off
            PORTB ^= B00000001;//If not turn it off  
        }
        else if (rightTachPos > rightPos)
        {
          //Turn on logicright2 (pin 8)      
          PORTB |= B00000001;

          //Turn off logicright1 (pin 12)
          if((PORTB & B00010000) != 0)//Check if it's already off
            PORTB ^= B00010000;//If not turn it off
        }
      }
    }

    Features

  • Full motor control, including three different breaking functionality
  • Encoder support on both motors – 360 counts per revolution
  • Supports the NXT Ultrasonic sensor as well
  • Custom NXT Sockets – no hacking needed
  • Easy to solder – All parts is through hole, no SMD components
  • Reset button mounted on top
  • Power LED
  • Status LED on pin 13
  • Nice silkscreen
  • Supports Arduino, FEZ Panda, FEZ Panda II, and FEZ Domino


  • To do

  • Make an Arduino Library, so people can get started right away
  • Record a video, to show everything in action


  • NB
    The shield and code is licensed under a Creative Commons Attribution Noncommercial Share-Alike license.

    1. Jakub
      January 7th, 2012 at 22:02 | #1

      I’m testing the ultrasonic sensor but Arduino always return :ERROR i2c_rep_start
      What and where is wrong?
      Thanks

    2. Lauszus
      January 7th, 2012 at 22:32 | #2

      @Jakub
      Have you downloaded the modified library that I provided: http://blog.tkjelectronics.dk/wp-content/uploads/i2cmaster.zip ?

    3. Jakub
      January 7th, 2012 at 23:06 | #3

      Yes I download it and copy “i2cmaster.h” , “keywords.txt” and “twimaster.cpp” to “/usr/share/arduino/libraries/i2c” (I use Linux Fedora 16)
      I have Arduino Uno and I’m using Arduino 1.0 IDE. I use 2x Li-ion battery -- 8,4V (instead of 9V).
      (sorry for my bad english and thank you for answer!)

    4. Lauszus
      January 7th, 2012 at 23:11 | #4

      @Jakub
      Okay, ti seems correct. Have you made your own shield or are you just using a breadboard? Have you remembered to connect pin 4 to the clockline?
      Your grammar is not a problem, I’m not a native speaker either :)

    5. Jakub
      January 7th, 2012 at 23:16 | #5

      I’m using breadboard and i have connect 6pin of NXT conector to 4pin of arduino IO

    6. Jakub
      January 7th, 2012 at 23:27 | #6

      Oh I’m noob! It shall be 5pin NXT to 4pin IO. Now it works
      Very thanks for you for your advice, a library and build schemes
      !!

    7. Lauszus
      January 8th, 2012 at 00:40 | #7

      @Jakub
      Your welcome :)

    8. LeeJaeSeong
      March 5th, 2012 at 17:32 | #8

      when i saw your wrting “NXT Sheld Vession2 , i want to ask you something.
      By using an Arduino UNO ,i want to controll a NXT ultrasonic waves sensor
      so i did download i2cmaster library from your website but
      it didn’t work and it’s differnt from your result.
      It changes into infinite loop on unsigned char i2c_start(unsigned char address)’s line
      i don’t know the reason.
      My H/W is using an Arduino UNO atmega328.
      ps. if you tell me your e-mail address, i will send a screenshot.

    9. March 5th, 2012 at 18:04 | #9

      @LeeJaeSeong
      Hi.
      Have you connected your Ultrasonic sensor properly to the Arduino I2C port? Have you also remembered to connect the extra clock wire?
      Please write us an email at: mail@tkjelectronics.dk

      Thomas

    10. July 11th, 2012 at 19:42 | #10

      Hi,
      I was reading through this, having made many futile attempts to learn about the I2C protocol, and used your code for the sensor. I managed to get the error messages down to one, after connecting NXT pin 5 to Arduino I/O 4: “Error reading distance”, but now I do not know how to progress from here!
      Any help would be greatly appreciated!

    11. Lauszus
      July 11th, 2012 at 22:57 | #11

      @Nicolas Weninger
      You should check out my newest post: http://blog.tkjelectronics.dk/2012/04/nxt-shield-library/ As I have created a library as well: https://github.com/TKJElectronics/NXTShield

      Btw: you should connect pin 4, not pin 5. But you could of course just change the code to reflect your hardware.

      You can also buy the NXT shield directly from our shop: http://shop.tkjelectronics.dk/product_info.php?products_id=29 if you don’t want to create it yourself.

      Regards
      Lauszus

    12. July 16th, 2012 at 18:20 | #12

      @Lauszus
      Thank you, but the problem still persists! (I am using a breadboard, but am planning to buy the shield).

    13. Elliot
      July 23rd, 2012 at 03:14 | #13

      Will it still work if I just cut the cable and put each wire on an arduino pin (for the ultrasonic sensor)? Or do I have to buy the shield?

    14. Lauszus
      July 23rd, 2012 at 14:52 | #14

      @Elliot
      Yes. I originally just breadboarded everything. But it was quite a mess, but it is possible :)
      But I would recommend buying the shield if you want to build a robot or so.

    15. November 6th, 2012 at 04:15 | #15

      @Elliot
      I tried cutting an NXT cable with the intent of making a breadboard compatible connector. My advice: don’t do it. Don’t waste your time, energy, or the cable. Stripping the black jacket is without damaging the wires inside is very difficult. Even if you succeed in removing the outer jacket, stripping the insulation off the internal wires without damaging the hair-thin stranded copper is very very difficult. And even if you succeed at all of that, working with or soldering the delicate stranded copper is very very very difficult.

      And even IF you invested the time to overcome all of these obstacles, the end result is: you put a new connector on a cable. Not a very rewarding end result. Just buy this NXT shield or find a “Breadboard Connector Kit for NXT” for about $3.

    16. Arjen
      November 11th, 2012 at 03:28 | #16

      This page states that “Encoder support on both motors – 360 counts per revolution”. Does this mean that I can turn each motor with a precision of 1 degree?

    17. November 11th, 2012 at 15:12 | #17

      @Arjen
      Yes, and with the help of the library we wrote it’s actually really easy!
      For instance this command will turn the motor forward 1 degree at full speed and then brake the motor:

      rightMotor.move(forward, 255, 1, brake);

      For more information see the example sketch: https://github.com/TKJElectronics/NXTShield/blob/master/examples/Motors/Motors.ino

    18. Arjen
      November 11th, 2012 at 21:55 | #18

      Thanks for the quick reply. Last question: after having a look at the code, I see that when make a motor move, the program waits until the motor is at the right position before the program continues:

      if(way == backward)
      {
      leftPos = leftTachPos-rotation;
      digitalWrite(logicleft1, LOW);
      digitalWrite(logicleft2, HIGH);
      while(leftTachPos > leftPos);
      }

      This indicates that you cannot activate motor 1 and motor 2 at the same time, but you have to wait until the first motor has reached the final position before you can activate the second motor. Is this correct? If so, is there a way to get around this: I’d like to turn both motors at the same time…

    19. November 11th, 2012 at 21:59 | #19

      @Arjen
      This has been fixed in the library. So you can simply call the function and then it is handled in the interrupt rutine instead. See: https://github.com/TKJElectronics/NXTShield/blob/master/NXTShield.cpp#L122-147

    1. No trackbacks yet.