2010-12-29

Using HM55B Compass Module from Parallax with Arduino and Processing

In this post, you'll find code samples for using HM55B Compass Module from Parallax with Arduino and Processing.

The Parallax Compass is a dual-axis magnetic field sensor built around the Hitachi HM55B. Parallax makes this compass accessible by providing their surface-mount sensor chip with a 3V onboard voltage regulator and resistor protection in a 0.3 wide 6-pin DIP module. The HM55B is compatible with the BASIC Stamps 5V supply and signal levels. Compass date is read using a simple synchronous serial interface (the BASIC Stamps SHIFTIN and SHIFTOUT commands).


You can buy the Thumb Joystick Module from: http://www.parallax.com/StoreSearchResults/tabid/768/txtSearch/hm55b/List/0/SortField/4/ProductID/98/Default.aspx or http://www.zerko.ch/parallaxshop/robotzubehoer/hm55b-digital-compass.php

Arduino part of this post is based on Arduino Playground sample from http://www.arduino.cc/playground/Main/HM55B

Pin connections are:
Compass Pin 1and Pin 2 => Arduino Pin 10
Compass Pin 3 => Arduino GND
Compass Pin 4 => Arduino Pin 8
Compass Pin 5 => Arduino Pin 9
Compass Pin 6 => Arduino 5V

The Processing code block is getting the miscellaneous (delivered from arduino) values from Serial protocol and using these values for rotating a box in a circle.


Arduino Code (from http://www.arduino.cc/playground/Main/HM55B):
/*
/////////////////////////////////
 Htachi HM55B Compass
 parallax (#)
 
 AUTHOR:   kiilo kiilo@kiilo.org
 License:  http://creativecommons.org/licenses/by-nc-sa/2.5/ch/
 
 http://parallax.com/Store/Microcontrollers/BASICStampModules/tabid/134/txtSearch/hm55b/List/1/ProductID/98/Default.aspx?SortField=ProductName%2cProductName
 http://sage.medienkunst.ch/tiki-index.php?page=HowTo_Arduino_Parallax_HM55B_Kompass
 http://arduino.cc/playground/HM55B
 
 /////////////////////////////////
 */
#include <math.h> // (no semicolon)
//// VARS
byte CLK_pin = 8;
byte EN_pin = 9;
byte DIO_pin = 10;

int X_Data = 0;
int Y_Data = 0;
int angle;

//// FUNCTIONS

void ShiftOut(int Value, int BitsCount) {
  for(int i = BitsCount; i >= 0; i--) {
    digitalWrite(CLK_pin, LOW);
    if ((Value & 1 << i) == ( 1 << i)) {
      digitalWrite(DIO_pin, HIGH);
      //Serial.print("1");
    }
    else {
      digitalWrite(DIO_pin, LOW);
      //Serial.print("0");
    }
    digitalWrite(CLK_pin, HIGH);
    delayMicroseconds(1);
  }
  //Serial.print(" ");
}

int ShiftIn(int BitsCount) {
  int ShiftIn_result;
  ShiftIn_result = 0;
  pinMode(DIO_pin, INPUT);
  for(int i = BitsCount; i >= 0; i--) {
    digitalWrite(CLK_pin, HIGH);
    delayMicroseconds(1);
    if (digitalRead(DIO_pin) == HIGH) {
      ShiftIn_result = (ShiftIn_result << 1) + 1; 
      //Serial.print("x");
    }
    else {
      ShiftIn_result = (ShiftIn_result << 1) + 0;
      //Serial.print("_");
    }
    digitalWrite(CLK_pin, LOW);
    delayMicroseconds(1);
  }
  //Serial.print(":");

  // below is difficult to understand:
  // if bit 11 is Set the value is negative
  // the representation of negative values you
  // have to add B11111000 in the upper Byte of
  // the integer.
  // see: http://en.wikipedia.org/wiki/Two%27s_complement
  if ((ShiftIn_result & 1 << 11) == 1 << 11) {
    ShiftIn_result = (B11111000 << 8) | ShiftIn_result; 
  }


  return ShiftIn_result;
}

void HM55B_Reset() {
  pinMode(DIO_pin, OUTPUT);
  digitalWrite(EN_pin, LOW);
  ShiftOut(B0000, 3);
  digitalWrite(EN_pin, HIGH);
}

void HM55B_StartMeasurementCommand() {
  pinMode(DIO_pin, OUTPUT);
  digitalWrite(EN_pin, LOW);
  ShiftOut(B1000, 3);
  digitalWrite(EN_pin, HIGH);
}

int HM55B_ReadCommand() {
  int result = 0;
  pinMode(DIO_pin, OUTPUT);
  digitalWrite(EN_pin, LOW);
  ShiftOut(B1100, 3);
  result = ShiftIn(3);
  return result;
}


void setup() {
  Serial.begin(115200);
  pinMode(EN_pin, OUTPUT);
  pinMode(CLK_pin, OUTPUT);
  pinMode(DIO_pin, INPUT);

  HM55B_Reset();
}

void loop() {
  HM55B_StartMeasurementCommand(); // necessary!!
  delay(40); // the data is 40ms later ready

  Serial.print(HM55B_ReadCommand()); // read data and print Status
  Serial.print(" ");  

  X_Data = ShiftIn(11); // Field strength in X
  Y_Data = ShiftIn(11); // and Y direction

  Serial.print(X_Data); // print X strength
  Serial.print(" ");
  Serial.print(Y_Data); // print Y strength
  Serial.print(" ");
  digitalWrite(EN_pin, HIGH); // ok deselect chip
  angle = 180 * (atan2(-1 * Y_Data , X_Data) / M_PI); // angle is atan( -y/x) !!!
  Serial.print(angle); // print angle
  Serial.println("");
  Hm55bCompassModuleProcessingHm55bCompassModuleProcessing
}

Processing Code:
// Author:  Özgür Aytekin - http://ozguraytekin.blogspot.com
// License: http://creativecommons.org/licenses/by-nc-sa/2.5/ch/

// The Processing serial library allows for easily reading and writing data to and
// from external machines.
// It allows two computers to send and receive data and gives you the flexibility
// to communicate with custom microcontroller devices, using them as the input or
// output to Processing programs. 
import processing.serial.*;

// Define variable for receiving data using the communication protocol
Serial serialPort;

// Define variables for storing miscellaneous variables (delivered from arduino)
float hm55bStatus, fieldStrengthInX, fieldStrengthInY, angleRawValue, angleRotateValue = 0;

// Define a variable for storing angle between 0 and 360 degree
float angleDisplayValue = 0;

// Called once when the program is started.
void setup()
{
  // Defines the dimension of the display window in units of pixels.
  // The size() function must be the first line in setup().
  size(640, 480);

  // Specifies the number of frames to be displayed every second.
  // If the processor is not fast enough to maintain the specified rate,
  // it will not be achieved.
  // For example, the function call frameRate(24) will attempt to refresh 24 times a second.
  // It is recommended to set the frame rate within setup().
  // The default rate is 60 frames per second.
  frameRate(24);

  // Gets a list of all available serial ports.
  // Use println() to write the information to the text window.
  println(Serial.list());

  // Class for sending and receiving data using the serial communication protocol.
  // Constructor Serial(parent, name, rate)
  // parent = PApplet: typically use "this", name = String: name of the port (COM1 is the default),
  // rate = int: 115200 
  serialPort = new Serial(this, Serial.list()[1], 115200);

  // Sets a specific byte to buffer until before calling serialEvent().
  // Don't generate a serialEvent() until get a newline character
  serialPort.bufferUntil('\n');
}

// Called when data is available.
void serialEvent(Serial serialPort)
{
  // Reads from the port into a buffer of bytes up to and including a particular character.
  // If the character isn't in the buffer, 'null' is returned.
  String inputString = serialPort.readStringUntil('\n');

  if (inputString != null)
  {
    // Removes whitespace characters from the beginning and end of a String.
    // In addition to standard whitespace characters such as space, carriage return,
    // and tab, this function also removes the Unicode "nbsp" character.
    inputString = trim(inputString);

    // The split() function breaks a string into pieces using a character or string as the divider.
    // filling substrings into a float array
    float[] values = float(split(inputString, " "));

    // we are waiting for four elements
    // put the numbers in the values array-variable
    if(values.length >= 4)
    {
      hm55bStatus = values[0];
      fieldStrengthInX  = values[1];
      fieldStrengthInY = values[2];
      angleRawValue = values[3];

      // hm55b is delivering values between 0 and 180 and 0 and -179
      // when the angle value is less than 0, adding 360 to angle
      // is delivering the degree between 0 and 359
      if(angleRawValue >= 0 && angleRawValue <= 180)
      {
        angleDisplayValue = angleRawValue;
      }
      else if(angleRawValue < 0)
      {
        angleDisplayValue = 360 + angleRawValue;
      }
    }
  }
}

// Called directly after setup() and continuously executes the lines of code contained inside
// its block until the program is stopped or noLoop() is called.
// The draw() function is called automatically and should never be called explicitly. 
void draw()
{
  // The background() function sets the color used for the background of the Processing window. 
  background(200);
  // Sets the color used to fill shapes.
  fill(153);

  print("angleRawValue: ");
  print(angleRawValue);
  print('\t');
  
  print("angleDisplayValue: ");
  println(angleDisplayValue);

  // Re-maps a number from one range to another.
  angleRotateValue = map(angleRawValue, 0, 359, 0, PI * 2);

  noFill();
  ellipseMode(CENTER);
  ellipse(width/2, height/2, 200, 200);
  ellipse(width/2, height/2, 20, 20);

  text("North", width/2 - 16, 130);
  text("South", width/2 - 16, 360);

  text("West", 180, height/2);
  text("East", 430, height/2);

  // Specifies an amount to displace objects within the display window.
  // The x parameter specifies left/right translation,
  // the y parameter specifies up/down translation,
  // and the z parameter specifies translations toward/away from the screen.
  translate(width/2, height/2);

  // Rotates a shape the amount specified by the angle parameter.
  rotate(angleRotateValue);

  // Draws a rectangle to the screen.
  rect(-6, - 100, 12, 100);

  // Forces the program to stop running for a specified time. 
  delay(500);
}

1 comment:

solagul forum said...

Paylaşim için Teşekkürler. Güzel makalelerin varmış hepsini elimden Geldigindece okumaya çalicagım.