NXT/EV3 Arduino I2C Ultimate Guide

This is my ULTIMATE guide for connecting The NXT or EV3 with the Arduino Uno. I have worked on NXT and EV3 using the LEGO Mindstorms graphical programming and RobotC for some years, but you always feel like you need more motors and sensors without using another brick, or you want to extend the functionality of your project. Well, this is the answer to your prayers. In this guide, I will give you a step-by-step guide to master this, whether you are using NXT or EV3, whether you are using LEGO Mindstorms graphical programming or RobotC. I will update this article in the future to add the LabView programming.

Before you go further, you must know that this is not a “Master the I2C between NXT/EV3 and Arduino in 5 minutes Guide”. This needs TIME. I would say about 2-3 days of reading, understanding and testing before you can apply it to your own project. Of course it depends on how fast you get bored, and how much time you spend on this in a day.

 

Introduction:

To be honest, this guide is developed from other simpler guides. The first and the second guides are maybe the first guides ever written on the internet about connecting the NXT or the EV3 with the Arduino Uno. These guides are made by Dexter Industries and they are a very good start, but you will feel like you still need more to complete your project. Anything quoted from these guides will be noted between [DI] ….. [/DI]. The third guide was written by Justin Eng, a college student who relied on Dexter Industries’ guide to make a project and shared his work on a blog. Anything quoted from his guide will be noted between [JE] ….. [/JE].

That brings us to me and my guide. This guide summarizes all the information from those guides, and includes my special adding that makes the Arduino Uno works with the NXT/EV3 as if they are one entity.

One more thing to say, The Dexter Industries guide uses Arduino Uno, but they say that it can be done using almost any Arduino board. After trying to use the Arduino Mega 2560, I can say that they’re not totally right. A special section in this guide will talk about that.

So, here we go….

 

The I2C communication protocol:

This section is for the geeks who like to understand everything happening between NXT/EV3 and the Arduino Uno. If you’re not that geek, you can skip this section, but I suggest you read at least the last paragraph after the diagram.

[DI] The NXT/EV3 and the Arduino can be made to talk over I2C.  In this tutorial, we setup the Arduino as an I2C slave, and the NXT/EV3 as an I2C master. The NXT/EV3 must always be a master and can never be a slave. Although it’s beyond this tutorial, with clever coding and polling, the Arduino can be setup to control the NXT/EV3. [/DI] It’s a great idea to be done, and my guide includes something of that.

You may ask why I2C? Well, long story short, this is the only way supported by NXT/EV3 other than Bluetooth, which has many disadvantages if used. You can read the full explanation on Justin Eng’s blog.

http://nxtarduino.blogspot.com.es/2013/12/week-1-system-overview-aka-it-begins.html

http://nxtarduino.blogspot.com.es/2013/12/week-2-functional-requirements-for.html

 

 

A little background on I2C:

[JE] I2C (Inter Integrated Circuit) is a bus protocol that facilitates communication between embedded electronic components. The hardware side specifies a two-wire system, consisting of an SDA line (Serial Data) and an SCL line (Serial Clock), both pulled up to whatever logic level is used. All devices on the bus are connected to these two lines, and all communication occur when a designated ‘master’ device initiates communications. As the protocol (and our implementation) has it, the master is the only device that can initiate communications, the slaves are entirely reactive. [/JE]

I2C

As you can see, you can add multiple slaves to the bus. Each slave must have an address. When the master wants to send a message to a slave, it sends that message to the bus. The message contains a header that includes the address of the chosen slave. All slaves actually receive that message, but only the slave which has the same address as the one included in the message header will read the data of that message. This is pretty interesting because you can add multiple Arduinos as slaves to the same NXT/EV3 master. These Arduinos can be connected to a very large number of sensors and motors. I tried connecting two Arduinos as slaves to NXT and it worked very well. You can add up to 128 slaves to the NXT/EV3 Master. The slaves’ addresses are usually in hexadecimal. They are like 0x01, 0x0A….

The Hardware:

There are two options:

  1. The easy option that costs a little:
    You can buy the Dexter Industries Breadboard Adapter for the LEGO MINDSTORMS NXT. (image from the Dexter Industries Guide)
    socket
  2. The harder option that is almost free:
    You just need to buy two 82KΩ resistors. You also need to, get your heart ready to be broken, cut one LEGO Mindstorms cable that you usually use to connect the NXT/EV3 brick with a motor or a sensor. The good thing is that you get two half cables so you can use the other half with another brick. I guess you can always buy more cables from LEGO or other third party sellers. It is better to solder the wires from the LEGO cable to header pins.
    (images from the Justin Eng Guide)

    wires1wires2

 

Here is the schematic of the connection (image from the Dexter Industries Guide): (You can find the full image in the downloadable package in the end)

wires3

If you use the Dexter Industries Breadboard Adapter for the LEGO MINDSTORMS NXT, the pull-up resistors are already built into the board. So you only need to connect the pins with jump wires.

This schematic provides the necessary power to power the Arduino, but be sure not to overload the NXT/EV3 (recommended 50 mA current consumption as a maximum by LEGO). If you will keep the Aruino connected to the computer by USB or you will provide an external power source for the Arduino, you don’t need to connect the green line to Vin, but the GND lines should be always connected.

Make sure to always connect the Arduino to sensor port 1. I don’t know why, but it seems like other ports don’t handle I2C functions properly, although it’s known that all LEGO Mindstorms sensors use I2C to communications with the NXT/EV3 Brick.

 

The Software:

This is the most important section of this guide. I will try to break into simple parts. Whether you are using NXT or the EV3, and whether you are using RobotC or LEGO Mindstorms Graphical Programming, the code for the Arduino is exactly the same. I suggest a little warm up before you dive into the next sections in order to understand them. My code is a little more complex than what I want you to read first.

If you are using RobotC or LabView to program your brick, then read this before:
http://www.dexterindustries.com/howto/connect-the-arduino-and-the-lego-mindstorms-together/

And if you are using EV3 graphical programming, then read this before:
http://www.dexterindustries.com/howto/connecting-ev3-arduino/

The concept of my code:

After you have read the simple guides of Dexter Industries, and after applying their code, you must now know that you can’t do some stuff out of the box. So this our job now to do. We want to make some advanced programming blocks. Now I will start by explaining the idea using the EV3 GRAPHICAL PROGRAMMING (EV3_GP) because it is easier to understand using blocks. Then, I will go further with the RobotC. Please read the next section regardless of what programming language you use, because they all share the same concept. Even if you find yourself not understanding something, just skip it and you will do in the RobotC section.

EV3 Graphical Programming:

As you saw in Dexter Industries’ guide, their I2C blocks provide you with some simple actions:

  • Write 1 Byte: Writes one byte to an Arduino slave.
  • Read 1 Byte: Reads one byte from an Arduino slave.
  • Write 8 Bytes: Writes 8 bytes in one message to an Arduino slave.
  • Read 8 Bytes: Reads 8 bytes from an Arduino slave.
  • Read 8 Bytes ASCII: Reads 8 bytes as characters from an Arduino slave. I guess that this block needs to be developed a little, because you would read a word rather than reading a set of letters. You can use the “My Block Builder” to create such a block.
    read string
  • Analog Read: This block reads the value from an Analog pin on an Arduino slave.
  • Digital Write: This block writes a digital state to a Digital pin in an Arduino slave.

 

You must add a “Wait” block for 50 mille seconds (0.05 seconds) after every message sent, that is to make sure that the message was received and processed.

Now here’s the shock, according to Dexter Industries, for each one of these block there’s a different Arduino code. It’s very crazy!!!

That is why you need to make custom blocks using basic I2C blocks in order to have one Arduino Code. What we need to have as a result are these blocks:

  • Move Arduino Servo Motor( Arduino_Address, motor_pin, angle )
  • Move Arduino DC Motor (Arduino_Address, motor_pin, speed )
  • Set Arduino LED (Arduino_Address, LED_pin, state )
  • Read Arduino Sensor (Arduino_Address, sensor_pin, analog/digital )

You can add whatever you want depending on your project, like reading from a keypad or sending a value to an external screen.

I only want to point out one more thing. When using the EV3 brick with these blocks, you can’t read/write a full byte. You can only send/receive values between 0 and 127. Don’t ask me why!!

 

Now finally, the FUN will start.

We will use the (Write 8 Bytes) block and “encode” all the parameters of our instructions in its bytes. Here is how:

Write_8_Bytes( Arduino_Address, Device_code, pin_num, special_value , optional_byte1, optional_byte2 , 0 , 0 ,0 )

  • Arduino_Address: The slave address for the Arduino we want to make a communication with.
  • Device_code: Let’s set this numbe to (1: led, 2: servo_motor, 3: dc_motor, 4: sensor). You can add whatever devices and codes you like.
  • Pin_num: We can use this parameter in two ways. The first obvious one is to choose the direct pin that the device is connected to. The other choice is to use it as a device number. Let’s suppose your project has 5 motors, 3 LEDs and 3 sensors that should be connected to the Arduino. Maybe you haven’t planned the connection pins yet, and they might be changed during the tests. We can say like “Move motor number 2” or “Read sensor number 3” and then in the Arduino Code we map those numbers to their pins.
  • Special_value: This value varies depending on the device. ( servo_motor -> angle, dc_motor -> speed,  led -> state, sensor -> analog/digital ). For the sensors, we can drop the use of the special value if we used the pin_num parameter as a device number. I will discuss that further in a special section.

Note: To have the Arduino Icons on the “My Block” that are in the attached ev3 file, you need to copy the contents of the “Icons” folder to this folder if you use the Education Editition:
C:\Program Files\LEGO Software\LEGO MINDSTORMS Edu EV3\Resources\MyBlocks\images
or to this folder if use the Home Edition:
C:\Program Files\LEGO Software\LEGO MINDSTORMS EV3 Home Edition\Resources\MyBlocks\images

So now let’s write our own instructions depending on that:

  • Set Arduino LED (Arduino_Address, LED_pin, state ) ->
    Write_8_Bytes( Arduino_Address, 1, pin_num, state ,0 ,0 ,0 ,0 ,0 )
    This is very simple. The state will be a true/false (1/0) parameter.LEDFor people who are not familiar with EV3_GP:
    -The light blue block in the first image is like a function call in programming language. It combines some basic blocks.
    -The second image is the function itself. The grey block holds the function’s parameters. If there’s another grey block at the end of the function, it would be the output of the function (the returned value).

     

  • Move Arduino Servo Motor( Arduino_Address, motor_pin, angle ) ->
    Write_8_Bytes( Arduino_Address, 2, pin_num, angle ,0 ,0 ,0 ,0 ,0 )
    Too bad that EV3 only allows values under 127, so the motor will not respond to bigger angles. Don’t worry, I have a work around. Since the values of the angle on normal servo motors are ±1 degree, we can always send angle/2. So if the real angle must be 100 degrees, we send 50, and in the Arduino code we double it back. This means that the range of [0,180] will be sent as [0,90] which is smaller than 127. As I said before, you will lose 1 degree of accuracy sometimes
    ( if you want 51 , 51/2= 25, so the angle rotated by the motor will be 50 ). If your project requires accuracy, you will have to use another byte to indicate that ( real_value = sent_value + 1 ) when the angle is an odd number. So our final instruction will be:Write_8_Bytes( Arduino_Address, 2, pin_num, angle/2 , angle%2 ,0 ,0 ,0 ,0 ) // %: modulo
    // 50%2 = 0 , 51%2 = 1servoThe second “red” block is “Round down”. It rounds the input to an integer value. (51.5 rounded down equals 51).
  • Move Arduino DC Motor (Arduino_Address, motor_pin1, motor_pin2 , speed ) ->
    Write_8_Bytes( Arduino_Address, 3, pin1_num, speed1 , pin2_num , speed2 ,0 ,0 ,0 )The DC motor requires two PWM pins, so we need to send both pin numbers.
    If we want to move the motor forward (positive speed), we must give the speed to the first pin and a zero value to the other pin. If we want to move the motor backwards (negative speed), we must give the absolute value of speed to the second pin and a zero value to the first pin.Since we are used with LEGO motors that full speed is 100, we will stick with that in the final block, but in the Arduino code we will multiply that value by 2.55 because the full speed of DC motors is represented as 255.

    dc.png

  • Read Arduino Sensor (Arduino_Address, sensor_pin, digital\analog ) ->
    Write_8_Bytes( Arduino_Address, 4, pin_num, analog/digital ,0 ,0 ,0 ,0 ,0 )
    Read_1_Byte ( Arduino_address, [output] value )
    analog/digital value will be a true/false (1/0) parameter. It just says that the sensor is connected to a digital pin or an analog pin.
    sensor

 

Now here’s an example:

Let’s suppose you have a vehicle with two wheels connected to DC motors, a third balancing wheel, a servo motor that holds an ultrasonic sensor and an indicator (LED).

robot

Our goal for this robot is to avoid obstacles. Let’s say that the program should be like this:

Loop Start

Set servo to 90 degrees

Wait 1 second

If ( ultrasonic_value > 25 )

                Move forward

Else

                Stop the robot

                Turn LED on

                Set servo to 45 degrees

                Wait 1 second

                X = ultrasonic_value

                Set servo to 135 degrees

                Wait 1 second

                Y = ultrasonic_value

                If ( X > Y )

                                Turn to the right a little

                Else

                                Turn to the left a little

                Turn LED off

Loop End

 

And let’s say that the hardware connections are like this:

DC motor 1 pins: 5,6

DC motor 2 pins: 9,10

Servo motor pin: 11

LED motor pin: 2

Ultrasonic Sensor pin: A0

Actually most ultrasonic sensor modules require being connected to two digital pins, but now let’s say that our hardware setup is correct. I will discuss that further in the Arduino code section.

robot_prg

That’s it for the EV3 GRAPHICAL PROGRAMMING. In the package you will download, you will find Dexter Industries’ I2C blocks , my custom Arduino blocks and the previous example.

First open your EV3 Application. Then, import the Dexter Industries’ I2C blocks. After that, open the i2c.ev3 project file which already has the I2C custom blocks built in, and create your program.

Reminder: To have the Arduino Icons on the “My Block” that are in the attached ev3 file, you need to copy the contents of the “Icons” folder to this folder if you use the EV3 Education Editition:
C:\Program Files\LEGO Software\LEGO MINDSTORMS Edu EV3\Resources\MyBlocks\images

or to this folder if use the Ev3 Home Edition:
C:\Program Files\LEGO Software\LEGO MINDSTORMS EV3 Home Edition\Resources\MyBlocks\images

 

You can skip the next section about RobotC and move straight to the Arduino code section.

((Downloadable files are in the end of this guide))

RobotC programming:

As you saw in Dexter Industries guide, things are not that easy. Just like we did in the EV3 Graphical Programming, we will use the same method here.

Read this code, and the explanation is after it.

wp-1455150184370.png

  1. I2C Basics:
    1. The slave address in RobotC must be bit-shifted like you read in the code, because the slave address is just 7 bits long.bit 0..bit 6 (coding for 0…127).
      BUT:
      when addressing a slave, some environments are use  8 bits coding, where
      bit 0 = 0 => read slave
      bit 0 = 1 => write to slave
      bits 1-7 : address, coding for 0…127
      As you may observe, the address is now shifted from bit 0-6 to 1-7.
      RobotC and Lego NXT use the 8 bits coding, and EV3 and Arduino use the genuine 7-bit address coding.
      This explanation was provided to me from Helmut Wunder.
    2. The bytes in RobotC I2C are full, which means they are from 0 to 255.
    3. The master/slave communication is “send and request” by default. You don’t have “write” and “read” instructions.
    4. The parameter “return_size” represents the number of bytes that you request from the slave as a reply from it to your sent message. If you just want to send a message to the slave and you don’t need a reply, you can set it to 0.
    5. The parameter “message_size” represents the number of “data” bytes you want to send to the slave. Inside the “i2c_msg” function, we add 3 to message_size. These 3 bytes are the message’s header. They are ( slave_address, message_size, return_size ).
    6. The data bytes are 5 bytes. You can encode whatever you want in them. The last byte must have a maximum value of 99. I don’t know why the message is only 5 bytes and why the last one can’t be more than 99, but that is what I found after many tests and tries.
  2. Devices Functions:
    These are almost exactly like in EV3_GP. If you skipped the previous section, please go back and read it even if you are not familiar with EV3_GP. It’s simple and easy to understand.
  • set_light( address, pin_num , state) ->
    i2c_msg(address, 2, 0, 1, pin_num, state, 0, 0)
    This is just like the EV3_GP.
    message_size is 2 because we are sending only pin_num and state.
    return_size is 0 because we are setting a light’s state. We don’t need a reply.
    byte0 is 1 as it is the device_code.

     

  • set_servo_motor( address, pin_num, angle) ->
    i2c_msg(address, 3, 0, 2, port, angle, 0, 0)
    message_size is 2 because we are sending only pin_num and angle.
    return_size is 0 because we are setting a servo’s angle. We don’t need a reply.
    byte0 is 2 as it is the device_code.Notice here that we didn’t need to send angle/2 nor angle%2, because we can send values between 0 and 255 in RobotC.
  • set_dc_motor( address, pin1_num, pin2_num, speed) ->
    i2c_msg(address, 3, 0, 3, pin1_num, speed1, pin2_num, speed2)
    message_size is 4 because we are sending pin_num, speed and sign.
    return_size is 0 because we are setting a motor’s speed. We don’t need a reply.
    byte0 is 3 as it is the device_code.Just like in EV3_GP, we want the speed value to be only from -100 to 100. Since the last byte of the I2C message in RobotC can’t be more than 99, we will make 99 the max value of speed because it’s almost a 100. It won’t make much difference.
  • read_sensor(address, pin_num, analog_digital) ->
    i2c_msg(address, 2, 1, 4, pin_num, analog_digital , 0, 0)
    message_size is 2 because we are sending pin_num and analog_digital.
    return_size is 1 because we are reading a sensor’s value. We need a reply to be returned.
    byte0 is 4 as it is the device_code.Notice that here we are sending and receiving using one instruction, unlike the EV3_GP. This function returns the sensor’s value.
  1. Example:
    The example is the same of the previous section.

 

In the downloadable package, you will find a file “i2c_robot_C.c”. Just open it, and edit the main function to adapt it to your project.

((Downloadable files are in the end of this guide))

 

LabView Programming:

I will keep this for the future. If you understood the EV3_GP section, I am sure that you can do the same on your own after reading the Dexter Industries Basics guide:
http://www.dexterindustries.com/howto/connect-the-arduino-and-the-lego-mindstorms-together/

 

The Arduino Code:

Now that you are familiar with the idea of I2C messaging and the concept of my “encoding” system, you must expect what the slave code should do.

The slave is mostly interruption-driven. The master sends messages and the slave processes them and reply if needed.

When a message is received, we need to “decode” its bytes and do what the master wants. We will store its bytes in an array (instruction array), and then we will process that “instruction”.

When an I2C message is received, the slave calls the function that handles the (onReceive) event. Then, if the message requires a reply, it calls the function that handles the (onRequest) event.

You are expected to know about Arduino programming before reading this code. You must know the basics of working with LEDs, servo motors, DC motors and sensors. If you don’t, please learn those things then come back here.

If you know those Arduino basics, you will not find anything hard while reading the code.

// I2C Slave Send / Receive
// How to send data from the LEGO Mindstorms NXT/EV3 to the Arduino.
// For LEGO Mindstorms
// Demonstrates how to connect a LEGO MINDSTORMS to an Arduino and Send commands,
// receive data.
// A4 – SDA
// A5 – SCL
// See www.dexterindustries.com/howto for more information on the physical setup.
//________________________________________________________________________________
//________________________________________________________________________________
//________________________________________________________________________________

#include<Wire.h> // I2C library
#include <Servo.h>

///----------------------------------------------
///----------------------------------------------

int instruction[5] = {5,0,0,0,0};

/// instruction[0] = 1 (LED), 2 (servo motor} ,3 (DC motor), 4 (sensor)
///
///
/// instruction [0] = 1 ==>  instruction [1] is port (LED digital pin)
///                          instruction [2] is: 0 (LED off) or 1 (LED on)
///
/// instruction [0] = 2 ==>  instruction [1] is port (servo motor number)
///                          instruction [2] is angle
///
/// instruction [0] = 3 ==>  instruction [1] is pin1 number 
///                          instruction [2] is pin1 speed (0-99)
///                          instruction [3] is pin2 number 
///                          instruction [4] is pin2 speed (0-99)  
///
/// instruction [0] = 4 ==>  instruction [1] is pin number
///                          instruction [2] is: 0 (analog pin) or 1 (digital pin)
///                    

//________________________________________________________________________________
//________________________________________________________________________________
//________________________________________________________________________________

Servo temp_servo;
int temp_sensor = 0;

void setup()
{
  Wire.begin(0x04); // set the slave address
  Wire.onRequest(requestEvent); // Sending information back to the NXT/EV3
  Wire.onReceive(receiveI2C); // Receiving information!
  
  // Debugging
  Serial.begin(9600);
    
}
//________________________________________________________________________________
void loop()
{
  delay(500);      
}

//________________________________________________________________________________
//________________________________________________________________________________
//________________________________________________________________________________

byte read_byte = 0x00;
int byte_count = 0;


// When data is received from NXT/EV3, this function is called.
void receiveI2C(int bytesIn)
{
  read_byte = bytesIn;
  byte_count = 0;
  while(1 < Wire.available()) // loop through all but the last
  {
    read_byte = Wire.read(); 
    
    instruction[byte_count] = read_byte;
    
    byte_count++;
  }
  int x = Wire.read(); 
  // Read the last dummy byte (has no meaning, but must read it)



  if( instruction[0] == 1 )  
  {
    Serial.println("  Light ");
    
    Serial.print("Pin: "); 
    Serial.println(instruction[1]);
    pinMode(instruction[1], OUTPUT);
    
    Serial.print("State: ");
    if(instruction[2] == 0) 
    {
      Serial.print("off");
      digitalWrite(instruction[1], LOW);
    }
    else                    
    {
      Serial.println("on");
      digitalWrite(instruction[1], HIGH);
    }
            
    
  }
  else if( instruction[0] == 2 )  
  {
    Serial.println("  Servo Motor ");
    
    Serial.print("Pin: "); 
    Serial.println(instruction[1]);
    temp_servo.attach(instruction[1]);
    
    
    //Uncomment the next line if you are using EV3 Graphical Programming
    //instruction[2] = instruction[2]*2 + instruction[3];
    
    Serial.print("Angle: ");
    Serial.println(instruction[2]);
    temp_servo.write(instruction[2]);
    
  }
  else if( instruction[0] == 3 )  
  {
    Serial.println("  DC Motor ");
    
    Serial.print("Pin1: "); 
    Serial.print(instruction[1]);
    pinMode(instruction[1], OUTPUT);
    
    Serial.print("  Speed1: "); 
    Serial.println(instruction[2]);
    analogWrite(instruction[1], instruction[2]*2.55);
    
    Serial.print("Pin2: "); 
    Serial.print(instruction[3]);
    pinMode(instruction[3], OUTPUT);
    
    Serial.print("  Speed2: "); 
    Serial.println(instruction[4]);
    analogWrite(instruction[3], instruction[4]*2.55);
    
    Serial.print("Result movement: "); 
    if (instruction[2] !=0)
      Serial.println("Forward");
    else
      Serial.println("Backwards");    
  }
  else if( instruction[0] == 4 )  
  {
    Serial.println("  Sensor "); 
    
    Serial.print("Pin: "); 
    Serial.print(instruction[1]);
    
    if ( instruction[2] == true )
    {
      Serial.println("Analog");
      
      int temp_pin = A0;
      if (instruction[1] !=0 )  temp_pin += instruction[1];
      
      temp_sensor = analogRead(temp_pin);
    }
    else
    {
      Serial.println("Digital");
      pinMode(instruction[1], INPUT);
      temp_sensor = digitalRead(instruction[1]);
    }
  }
   
}//end recieveI2C

//________________________________________________________________________________

void requestEvent()
{  
  if (instruction[0] == 4)
  {
    Wire.write(temp_sensor); // respond with message
    Serial.print("Value: ");
    Serial.println(temp_sensor);
  } 
}//end requestEvent
//________________________________________________________________________________

 

If you worked with sensors and Arduino before, then you know that most sensors don’t just give you an understandable output. You always need some calculations before getting the final output, and these calculations are different for each sensor. That is why we shouldn’t read the pin value using the I2C instructions. We will use a workaround.

You first need to know all the sensors in your project and which pin each sensor will be connected to.

I will explain this using an example. Let’s say we have a robot that has 3 sensors:

  • Sensor 1 (Light Sensor “LDR”): connected to A0.
  • Sensor 2 (Ultrasonic Sensor): The module I have to be connected to any two digital pins. “trig” and “echo”. I will say they are connected to digital pins 7 and 8.
  • Sensor 3 (Temperature Sensor): connected to A1.

Now when I want to read the value of the ultrasonic sensor in the RobotC code, I will write:
ultrasonic_value = read_sensor( ARDUINO_ADDRESS, 2, true);
considering 2 is “Sensor 2”, and the analog_digital value now will have no meaning.

If I want to read the value of the light sensor in the RobotC code, I will write:
light_sensor_value = read_sensor( ARDUINO_ADDRESS, 1, true);
considering 2 is “Sensor 1”, and so on…

Now we need to modify the Arduino Code to adapt to this. We will add a separate function for each sensor. In this function we will read the value from the pins, make the appropriate calculations to this sensor and then return the final value.

Here is an example of the temperature sensor function that is for the LM35 sensor:

float LM35() 
{
  float t=analogRead(A1);
  t=t*5.0/1024; 
  t*=100;
  return t;
}

 

Now when we read the sensor number in the Arduino code, we call the corresponding function and send the value it returns.

Here is the edited code after adding the example functions.

// I2C Slave Send / Receive
// How to send data from the LEGO Mindstorms NXT/EV3 to the Arduino.
// For LEGO Mindstorms
// Demonstrates how to connect a LEGO MINDSTORMS to an Arduino and Send commands,
// receive data.
// A4 – SDA
// A5 – SCL
// See www.dexterindustries.com/howto for more information on the physical setup.
//________________________________________________________________________________
//________________________________________________________________________________
//________________________________________________________________________________

#include<Wire.h> // I2C library
#include <Servo.h>

///----------------------------------------------
///----------------------------------------------

int instruction[5] = {5,0,0,0,0};

/// instruction[0] = 1 (LED), 2 (servo motor} ,3 (DC motor), 4 (sensor)
///
///
/// instruction [0] = 1 ==>  instruction [1] is port (LED digital pin)
///                          instruction [2] is: 0 (LED off) or 1 (LED on)
///
/// instruction [0] = 2 ==>  instruction [1] is port (servo motor number)
///                          instruction [2] is angle
///
/// instruction [0] = 3 ==>  instruction [1] is pin1 number 
///                          instruction [2] is pin1 speed (0-99)
///                          instruction [3] is pin2 number 
///                          instruction [4] is pin2 speed (0-99)  
///
/// instruction [0] = 4 ==>  instruction [1] is pin number
///                          instruction [2] is: 0 (analog pin) or 1 (digital pin)
///                    

//________________________________________________________________________________
//________________________________________________________________________________
//________________________________________________________________________________

float LM35() 
{
  float t=analogRead(A1);
  t=t*5.0/1024; 
  t*=100;
  return t;
}

//________________________________________________________________________________

const int trig = 7;
const int echo = 8;

float ultrasonic() 
{
  digitalWrite(trig,LOW);
  delayMicroseconds(2);
  digitalWrite(trig,HIGH);
  delayMicroseconds(10);
  digitalWrite(trig,LOW);
  float dis=pulseIn(echo,HIGH)/58.2;
  return dis;
  
}

//________________________________________________________________________________

int ldr()
{
 int lightl = analogRead(A0);
 lightl = map(lightl,0,900,255);
 lightl = constrain(lightl,0,255);
 return lightl;
}

//________________________________________________________________________________

Servo temp_servo;
int temp_sensor = 0;

void setup()
{
  Wire.begin(0x04); // set the slave address
  Wire.onRequest(requestEvent); // Sending information back to the NXT/EV3
  Wire.onReceive(receiveI2C); // Receiving information!
  
  // Debugging
  Serial.begin(9600);
    
}
//________________________________________________________________________________
void loop()
{
  delay(500);      
}

//________________________________________________________________________________
//________________________________________________________________________________
//________________________________________________________________________________

byte read_byte = 0x00;
int byte_count = 0;


// When data is received from NXT/EV3, this function is called.
void receiveI2C(int bytesIn)
{
  read_byte = bytesIn;
  byte_count = 0;
  while(1 < Wire.available()) // loop through all but the last
  {
    read_byte = Wire.read(); 
    
    instruction[byte_count] = read_byte;
    
    byte_count++;
  }
  int x = Wire.read(); 
  // Read the last dummy byte (has no meaning, but must read it)



  if( instruction[0] == 1 )  
  {
    Serial.println("  Light ");
    
    Serial.print("Pin: "); 
    Serial.println(instruction[1]);
    pinMode(instruction[1], OUTPUT);
    
    Serial.print("State: ");
    if(instruction[2] == 0) 
    {
      Serial.print("off");
      digitalWrite(instruction[1], LOW);
    }
    else                    
    {
      Serial.println("on");
      digitalWrite(instruction[1], HIGH);
    }
            
    
  }
  else if( instruction[0] == 2 )  
  {
    Serial.println("  Servo Motor ");
    
    Serial.print("Pin: "); 
    Serial.println(instruction[1]);
    temp_servo.attach(instruction[1]);
    
    //Uncomment the next line if you are using EV3 Graphical Programming
    //instruction[2] = instruction[2]*2 + instruction[3];
    
    Serial.print("Angle: ");
    Serial.println(instruction[2]);
    temp_servo.write(instruction[2]);
    
  }
  else if( instruction[0] == 3 )  
  {
    Serial.println("  DC Motor ");
    
    Serial.print("Pin1: "); 
    Serial.print(instruction[1]);
    pinMode(instruction[1], OUTPUT);
    
    Serial.print("  Speed1: "); 
    Serial.println(instruction[2]);
    analogWrite(instruction[1], instruction[2]*2.55);
    
    Serial.print("Pin2: "); 
    Serial.print(instruction[3]);
    pinMode(instruction[3], OUTPUT);
    
    Serial.print("  Speed2: "); 
    Serial.println(instruction[4]);
    analogWrite(instruction[3], instruction[4]*2.55);
    
    Serial.print("Result movement: "); 
    if (instruction[2] !=0)
      Serial.println("Forward");
    else
      Serial.println("Backwards");    
  }
  else if( instruction[0] == 4 )  
  {
    Serial.println("  Sensor "); 
    
    Serial.print(": "); 
    Serial.print(instruction[1]);
    
    if ( instruction[1] == 1 )
    {
      Serial.println("Light");
      temp_sensor = ldr();
    }
    else if ( instruction[1] == 2 )
    {
      Serial.println("Ultrasonic");
      temp_sensor = ultrasonic();
    }
    else if ( instruction[1] == 3 )
    {
      Serial.println("Temperature");
      temp_sensor = LM35();
    }
  }
   
}//end recieveI2C

//________________________________________________________________________________

void requestEvent()
{  
  if (instruction[0] == 4)
  {
    Wire.write(temp_sensor); // respond with message
    Serial.print("Value: ");
    Serial.println(temp_sensor);
  } 
}//end requestEvent
//________________________________________________________________________________

 

In the downloadable package, you will find a folder “NXT_EV3_I2C_ultimate_guide” which contains the first Arduino Code, and a folder “NXT_EV3_I2C_ultimate_guide_edit” which contains the second Arduino Code.

You need to change the functions of the sensors to adapt to your project.

((Downloadable files are in the end of this guide))

 

Using Arduino boards other than Uno:

As I mentioned in the beginning of my guide, The Dexter Industries guide says that the same thing can be done using almost any Arduino board. After trying to use the Arduino Mega 2560, I can say that they’re not totally right.

Arduino Uno doesn’t have on-board I2C pull-up resistors. That is why we added the 82KΩ resistors. Arduino Mega 2560 on the other hand, has 10KΩ pull-up resistors on-board. You can’t connect any resistors on parallel nor serial to make the result resistor 82kΩ. The only way to work it out is to remove these on-board resistors which requires special techniques and has risks. I have not tried it. I didn’t want to risk losing the Mega I have, but I read this on two different places on the internet. One of them said that it should work, theoretically. The other one said he did it and it worked.

That is just for the Mega 2560. Other boards might have different things. You must search to know about the pull-up resistors of your board.

 

Downloadable Files:

Here are links to the full package download. They are the same, but just using different hosting services:

http://1drv.ms/1PAQt9V

https://goo.gl/C5R44J

To download this guide, you can use many ways. One of them is to search for “convert a webpage to pdf” and you can find many sites that allow you to do that.

 

Finally…

Just for the record, I started to work on this idea as a trainer for a team “The A-Team” that participated in the Open Category competition of World Robot Olympiad – WRO Syria 2015, representing the Robotic Club of the Faculty of Mechanical and Electrical Engineering of Damascus University (RoboTech Club).  This team got the third place in the national competition, and we were about to participate in the worldwide event of WRO 2015, but unfortunately all Syrian teams were not allowed to enter Qatar due to the political situations between both countries, which was really unfair. I will post about the project details later in my blog. I started working on this on July and continued until November, yes it took me long time. It went through many edits and developments until it became what you see here.

I hope you enjoyed my guide, and if you have any questions, feel free to ask in the comment section.

I hope this benefits you all…

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s