mardi 12 février 2013

HMC5883L Compass + Arduino + Processing


You will find below the Arduino and ProcessingCode. I used the arduino example code found into the library, and adaped it a bit.
The Processing code is from a code that I found on internet and adapted and changed some graphic details.
You will find also a link to download the library that I use.

Since the video, I have solved the angle issue, now performing 90 degrees angle with the compass will ouput correctly a +/- 90 degrees on the serial port.

HMC5883L Library:
http://dl.free.fr/q9O0J0cXe


Arduino code:


// Reference the I2C Library
#include <Wire.h>
// Reference the HMC5883L Compass Library
#include <HMC5883L.h>

// Store our compass as a variable.
HMC5883L compass;
// Record any errors that may occur in the compass.
int error = 0;

//rounding angle
int RoundDegreeInt;

int PreviousDegree = 0;

// Out setup routine, here we will configure the microcontroller and compass.
void setup()
{
  // Initialize the serial port.
  Serial.begin(9600);

  Wire.begin(); // Start the I2C interface.

  compass = HMC5883L(); // Construct a new HMC5883 compass.
 
  error = compass.SetScale(1.3); // Set the scale of the compass.
  if(error != 0) // If there is an error, print it out.
    Serial.println(compass.GetErrorText(error));

  error = compass.SetMeasurementMode(Measurement_Continuous); // Set the measurement mode to Continuous
  if(error != 0) // If there is an error, print it out.
    Serial.println(compass.GetErrorText(error));
}

// Our main program loop.
void loop()
{
  // Retrive the raw values from the compass (not scaled).
  MagnetometerRaw raw = compass.ReadRawAxis();
  // Retrived the scaled values from the compass (scaled to the configured scale).
  MagnetometerScaled scaled = compass.ReadScaledAxis();

  // Values are accessed like so:
  int MilliGauss_OnThe_XAxis = scaled.XAxis;// (or YAxis, or ZAxis)

  // Calculate heading when the magnetometer is level, then correct for signs of axis.
  float heading = atan2(scaled.YAxis, scaled.XAxis);

  // Once you have your heading, you must then add your 'Declination Angle', which is the 'Error' of the magnetic field in your location.
  // Find yours here: http://www.magnetic-declination.com/
  // Mine is: 2� 37' W, which is 2.617 Degrees, or (which we need) 0.0456752665 radians, I will use 0.0457
  // If you cannot find your Declination, comment out these two lines, your compass will be slightly off.
  float declinationAngle = 0.009 ;
  heading += declinationAngle;

  // Correct for when signs are reversed.
  if(heading < 0)
    heading += 2*PI;
 
  // Check for wrap due to addition of declination.
  if(heading > 2*PI)
    heading -= 2*PI;
 
  // Convert radians to degrees for readability.
  float headingDegrees = heading * 180/M_PI;

  //correcting the angle issue
  if (headingDegrees >= 1 && headingDegrees < 240)
  {
    headingDegrees = map(headingDegrees,0,239,0,179);
  }
  else if (headingDegrees >= 240)
  {
    headingDegrees =  map(headingDegrees,240,360,180,360);
  }

  //rounding the angle
  RoundDegreeInt =round(headingDegrees);

  //smoothing value
  if( RoundDegreeInt < (PreviousDegree + 3) && RoundDegreeInt > (PreviousDegree - 3) ) {
    RoundDegreeInt = PreviousDegree;
  }

  Output(RoundDegreeInt);

  PreviousDegree = RoundDegreeInt;

  // Normally we would delay the application by 66ms to allow the loop
  // to run at 15Hz (default bandwidth for the HMC5883L).
  // However since we have a long serial out (104ms at 9600) we will let
  // it run at its natural speed.
  // delay(66);
}

// Output the data down the serial port.
void Output(int RoundDegreeInt)
{
   //Serial.println();
   Serial.println(RoundDegreeInt);
 
   delay(150);
}

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Processing Code:

import processing.serial.*;

Serial myPort;
PFont b;

int lf = 10;    // Linefeed in ASCII
String myString = null;
float angle;

void setup(){
  size(600,400);
  b = loadFont("Arial-BoldMT-48.vlw");
  myPort = new Serial(this, "COM3", 9600);
}

void draw(){

background(255);

while (myPort.available() > 0) {
    myString = myPort.readStringUntil(lf);
    if (myString != null) {
  //print(myString);  // Prints String
    angle=float(myString);  // Converts and prints float
    println(angle);
    }
  }
  translate(160, 50);

  // draw the compass background
  ellipseMode(CENTER);
  fill(50);
  stroke(10);
  strokeWeight(2);
  ellipse(150,150,300,300);

  // draw the lines and dots
  translate(150,150);  // translate the lines and dots to the middle of the compass
  float CompassX = -angle;
  rotate(radians(CompassX));
  noStroke();
  fill(51, 255, 51);

  int radius = 120;

  for( int degC = 5; degC < 360; degC += 10) //Compass dots
  {
    float angleC = radians(degC);
    float xC = 0 + (cos(angleC)* radius);
    float yC = 0 + (sin(angleC)* radius);
    ellipse(xC,yC, 3, 3);
  }

  for( int degL = 10; degL < 370; degL += 10) //Compass lines
  {
    float angleL = radians(degL);
    float x = 0 + (cos(angleL)* 145);
    float y = 0 + (sin(angleL)* 145);
 
    if( degL==90 || degL==180 || degL==270 || degL==360) {
     stroke(51, 255, 51);
     strokeWeight(4);
    }
    else {
      stroke(234,144,7);
      strokeWeight(2);
    }
    line(0,0, x,y);
  }

  fill(102, 102, 102);
  noStroke();
  ellipseMode(CENTER);
  ellipse(0,0, 228,228); //draw a filled circle to hide the lines in the middle

  b = loadFont("Arial-BoldMT-48.vlw");
  textAlign(CENTER);

  // Draw the letters
  fill(250);
  textFont(b, 32);
  text("N", 1, -90);
  rotate(radians(90));
  text("E", 0, -90);
  rotate(radians(90));
  text("S", 0, -90);
  rotate(radians(90));
  text("W", 0, -90);
  rotate(radians(90));
 
  textFont(b,40);
  textAlign(CENTER);
  //text((angle), 20, 20);
  println(angle);

  //draw the needle

  rotate(radians(-CompassX)); //make it stationary
  stroke(234,144,7);
  strokeWeight(3);

  triangle(-10, 0, 10, 0, 0, -85);
  fill(234,144,7);
  triangle(-10, 0, 10, 0, 0, 60);

}











lundi 11 février 2013

Inside


Inside, we can find:

  • The ESC (Electronic Speed Control):

This is a KA-18 ESC Kyosho.
This one provides power (5V) to the radio receiver (which is missing in my case), and also control and vary the speed of the main motor according to the commands received and transmitted by the radio receiver.

This one consists of:
- The plug (which will connect to the battery, a 7.2V battery 2200 mha in my case)
- The output to power the main motor
- A pin with 3 wires, black, white, and red. Black and red normally allow powering the radio receiver (5V). The white wire is needed to send the data received by the receiver, which will be transmitted to the ESC.
- A switch, to turn on and off the car.

The steering servo (front wheels).
To control a servo, this one has 3 wires: The red and black wires will power the servo (5v). The white wire is the one used to control the servo, indicating the angle of the position.
The steering servo is directly connected to the receiver (which is itself powered by the ESC).


dimanche 10 février 2013

First step: Controling the steering servo

It is very simple to control a servo motor using an Arduino.
The servo position can vary from an angle of 0 to 180 degrees. It is interesting for me to know the maximum angle to turn on the right and on the left.

For this, I will use:
- An arduino
- A potentiometer
- Some simple wires
- A USB cable, to power the Arduino, and to get the value sends by the Arduino to the computer (with the serial port).

I will make an assembly, to change the angle of the servo, depending on the value of the potentiometer.

Here is the page that I used and which perfectly matched my case:

http://arduino.cc/en/Tutorial/Knob

And here's a picture of the assembly:


We will use:
- An analog input of the Arduino (to measure the value of the potentiometer)
- An output of the Arduino (to communicate to the servo the value of the angle to reach)
- The ground and the 5V output, to apply a voltage across the potentiometer, and also to power the servo.

Good to know:
Values which are ​​read from the analog input of the Arduino are always between 0 and 1023 (10 bits resolution).
Therefore, it is important, when making the code, to adapt this value to match the value expected by a servo, so between 0 and 179 (180 degrees).

To know this value, this one is then sent via the serial port (here USB) to the computer.

Controlling the steering servo with a Potentiometer: Code and video


To make the control of the servo motor easier, there is a special library made for: servo.h (http://www.arduino.cc/en/Reference/Servo)
I will use it here.

Here is the code:

#include <Servo.h>
Servo frontservo; // declare servo name as "frontservo"
int potpin = A0; // analog pin used to connect the potentiometer
int val; // variable to read the value from the analog pin
void setup()
{
frontservo.attach(9); // attach the servo on pin 9 to the servo object
Serial.begin(9600); // set the serial port rate to 9600 bauds
}
void loop()
{
val = analogRead(potpin); // reads the value of the potentiometer (value between 0 and 1023)
val = map(val, 0, 1023, 0, 179); // scale it to use it with the servo (value between 0 and 179 degres)
frontservo.write(val); // sets the servo position according to the scaled value
Serial.println(val); // send the value via the serial port
delay(15); // waits for the servo to get there
}

On the computer side, I will use the serial monitor of the Arduino programm (Ctrl Shift M).

The video:



So you can see thanks to this , that the wheels are centered when the value is around 100 ( between 90 and 100). The maximum movement on the is right about 130, on the left about 60. 

samedi 9 février 2013

Controlling the ESC: Arduino and potentiometer


Regarding the main motor control, I have to control the ESC. This one can be controlled as a servo, this the servo.h library.
Note that the ESC needs to be initialized. The boot sequence depends of the ESC brand and model. In my case, I need to send the value 100 during 1 second.
Also, as the ESC is already powered by the battery, I wont use the 5v pin of the Arduino.


Therefore we can use the same code.

Assembly:



Video:



vendredi 8 février 2013

Controlling the steering servo with a mouse


Instead of using a potentiometer to control a servo, i will use in this case a mouse.

In order to send a value to the Arduino, depending of the mouse movements, I have to make a short program which will run on the computer side.
To create this program, I will use a development platform called Processing. It is Open Source, easy to use, and specially made for interaction and animation. Actually, the programm Arduino is based on Processing.

For more info: http://processing.org/

So, I will have on code, running on Processing, sending the value to the Arduino, depending of the mouse position, and another code, running on the Arduino, to control the servo depending of the value received through the serial port.

I wil use:
- My computer (and the mouse)
- The Arduino
- The USB cable and some simple wires
- The steering servo of the Kyosho car

As we do not use any potentiometer anymore, the servo will be directly connecter to the Arduino. +5v pin to the red wire, GND to the black wire (here brown), and the pin to the white wire (here orange).

jeudi 7 février 2013

Controlling the steering servo with a mouse: the Processing code


As the Arduino program is based on Processing, the syntaxe used to write the code will be very similar.


To write the code, I used the examples below, which are matching perfertly what I intented to do.
and

After some changes, here the code that I will use:

import processing.serial.*;

int xpos=90; // set x servo's value to mid point (0-180);
Serial myport; // The serial port we will be using

void setup()
{
size(720, 360); // size of the main windows

myport = new Serial(this, "COM3", 9600); // declare the serial port on COM3 with a speed of 9600 bds)
}

void draw()
//Note that MouseX is referring to the X axis position of the mouse, from 0 on the left to 720 on the right
{
fill(175);
rect(0,0,720,360);
fill(255,0,0); // set the color RED for the next shapes
rect(360, 175, mouseX-360, 50); // draw a rectangle from the middle position to the x axis of the mouse position

update(mouseX);
}

void update(int x)
{
//Calculate servo postion from mouseX
xpos= x/4; // the middle will be 360/4=90

myport.write(xpos+"x"); //sends the position throught the serial port followed by the letter x, by that way we can if we want, control more servos with different letters
println(xpos); // display the value of the position into the processing monitor
delay(25);
}

mercredi 6 février 2013

Controlling the steering servo with a mouse: the Arduino code


On the Arduino side, I also adapted the sketch to fits with my purpose:

#include <Servo.h>

Servo xservo; // xservo is the direction servo

int xpos= 0;

void setup(){
xservo.attach(9); //attach the servo to pin 9
Serial.begin(9600); // 9600 is the rate of communication
}

void loop() {
static int v = 0; // value to be sent to the servo (0-180)
if ( Serial.available()) {
char ch = Serial.read(); // read in a character from the serial port and assign to ch
switch(ch) { // switch based on the value of ch, it will be only x in this case
case '0'...'9': // if it's numeric
v = v * 10 + ch - '0';
/*
so if the chars sent are 45x (turn x servo to 45 degs)..
v is the value we want to send to the servo and it is currently 0
The first char (ch) is 4 so
0*10 = 0 + 4 - 0 = 4;
Second char is 4;
4*10 = 40 + 5 = 45 - 0 = 45;
Third char is not a number(0-9) so we drop through...
*/
break;
case 'x': // if it's x
/*
....and land here
where we send the value of v which is now 45 to the x servo
and then reset v to 0
*/
v = constrain(v, 60, 130); // constrain the value to be between 60 and 130
xservo.write(v); // send the value to the servo
delay(25);
v = 0; // reset this value to 0
break;
}
}
}

lundi 4 février 2013

Pan tilt control with an Xbox controller: Intro


As we have seen, we can use Processing to send different values to the Arduino, depending in the previous case of the mouse, but we can also use many kind of devices.
As I have a Xbox 360, I decided to use the controller through Processing and the Arduino, to control a pan tilt.

The Pan tilt:
I have chose a very simple and cheap pan tilt that I have purchase on SparkFun.com
This one is made of 2 pieces of metal assembled with screws, and controlled with 2 servos.

A picture:



As demonstrated earlier, it is very easy to drive a servo with the Arduino, so driving 2 servos should be easy too.



dimanche 3 février 2013

Pan tilt control with an Xbox controller: the Processing code


To be able to use the buttons, joysticks and triggers of the xbox controler into processing easily, we need to use a librairy. There is a special librairy dedicated to jospads and controllers, it is called ProControll.

More info about Procontroll library: http://creativecomputing.cc/p5libs/procontroll/

With a controller like the Xbox 360 controller, acquiring a button change is quite easy. However, acquiring and using the value, for one or several axis, of the joystick, is a bit more difficult.
Actually, for the josticks, the value returned by the controller is a large floating decimal value.
This one needs to be converted into a value that we can directly use and send to the Arduino.
Also, as the joytick is not a perfect circle, and as it can be very sensitive, a function to control the tolerance needs to be used.

I have looked over internet, and I found this video on youtube:



The person described how to control a motor with a Xbox controller. There is below the video the description and also the code that the person used. I thank a lot Mr Mspinksosu because his video and code helped me a lot.

I adapted the code to fit in my project. The main changes are that I simplified the code a lot, I adapted the Processing side to get the value of the X and Y axis of the right joystick, and on the Arduino side, I removed the interuptions and I changed a bit the code in order to drive the servo with the servo.h library.