Help with HM55B Compass

As of right now my HM55B compass is printing constant data to processing from -180 to 0N to 180. How would I modify the following code to only output 8 directions. N, NE, E, SE, S, SW, W, NW?

#include <math.h> // (no semicolon)
//// VARS
byte CLK_pin = 8;
byte EN_pin = 9;
byte DIO_pin = 10;
byte LED = 13;

int ZeroOffset = 0;        // To calibrate the compass, run in uncalibrated mode [Section 1], 
                           // point the sensor due North, and record the value sensed.  
                           // Enter that value here and then run in calibrated mode [Section 2]
int X_Data = 0;
int Y_Data = 0;
int angle, status;
float Delta, xPrime, yPrime;


//// 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);
  }
}

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(9600);
  pinMode(EN_pin, OUTPUT);
  pinMode(CLK_pin, OUTPUT);
  pinMode(DIO_pin, INPUT);
  Serial.println();
  HM55B_Reset();
}

void loop() {
  HM55B_StartMeasurementCommand(); // necessary!!
  delay(40); // the data is 40ms later ready
  status = HM55B_ReadCommand(); // read data
  X_Data = ShiftIn(11); // Field strength in X
  Y_Data = ShiftIn(11); // and Y direction
  digitalWrite(EN_pin, HIGH); // Deselect chip

//  [Section 1: Simple Angle calculation]
//  To determine the angle using the raw sensor data, we can simply calculate
//  the Inverse Tangent of Y_Data/X_Data.  Uncomment the following line and comment
//  all of [Section 2]

//  angle = (180 * (atan2(-1 * Y_Data , X_Data) / M_PI)); // angle is atan( -y/x) !!!

//  [End Section 1]


//  [Section 2: Offset-Compensated Angle Calculation]
//  To better match the compass readings with well known values of true north,
//  you can use the following calculations to rotate the value of the tangent
//  before determining the angle.  To do this, we first need to run the program
//  with the code in [Section 1] uncommented and note the angle value when we
//  point the sensor to true north.  Enter the value recorded at the top of 
//  this program in the variable "ZeroOffset", comment the code in [Section 1],
//  and uncomment the code below.

  Delta = (ZeroOffset/180.0)*M_PI;                      // Calculate radians from degrees
  xPrime = X_Data*cos(Delta) - Y_Data*sin(Delta);       // Translate the X_Data value based on 
                                                        // rotating the original angle by "Delta" rads.
  yPrime = Y_Data*cos(Delta) + X_Data*sin(Delta);       // Translate the Y_Data value based on 
                                                        // rotating the original angle by "Delta" rads.
  angle = (180 * (atan2(-1 * yPrime , xPrime) / M_PI)); // angle is still atan(-y/x)

//  [End Section 2]

  if (abs(angle) < 10) {
    digitalWrite (LED, HIGH);
  } else {
    digitalWrite (LED, LOW);
  }    
  
  Serial.println(angle); // print angle
  delay(100);

}

Assuming angle contains a value between 0 and 359:

char* directions[]={"N", "NE", "E", "SE", "S", "SW", "W", "NW"};

Serial.println(directions[((angle+22)/45)%8]);

Beware, though, I haven't been able to actually try it.

Well it partially works. The angle contains a value of -180 to 0 North to 180. Negative numbers would represent SW, W, NW.

N = N NE = NE E = E SE = SE S = S SW = E W = E_? NW = N

maybe try "map (angle, -180, 180, 0, 8)" ? http://arduino.cc/en/Reference/Map And rearrange the strings?

It may not be pretty, but I got this to work.

 if(angle > -22.5 && angle < 22.5){
  Serial.println('N');
  }
  else if(angle > 22.5 && angle < 67.5){
  Serial.println("NE");
  }
  else if(angle > 67.5 && angle< 112.5){
  Serial.println('E');
  }
  else if(angle > 112.5 && angle < 157.5){
  Serial.println("SE");
  }
  else if(angle > 157.5 || angle < -157.5){   
  Serial.println('S');
  }
  else if(angle < -22.5 && angle > -67.5){
  Serial.println("NW");
  }
  else if(angle < -67.5 && angle > -112.5){
  Serial.println('W');
  }
  else if(angle < -112.5 && angle > -157.5){
  Serial.println("SW");
  }

Equally unpretty:

char* directions[]={"s", "sw", "w", "nw", "n", "ne", "e", "se", "s"};

void printDirection (int angle)
{
  Serial.println (directions [map (angle, -180, 180, 0, 8)]);  
}

Forgot about map(). Definitely the way to go.

Very Nice.

I am now trying to get Processing to read Arduino's serial output and assign an image to each direction. Here is what I have.

import processing.serial.*; 
PImage img;


Serial myPort;    // The serial port: 
PFont myFont;     // The display font: 
String inString;  // Input string from serial port: 
int lf = 10;      // ASCII linefeed 

void setup() {
  img = loadImage("arrow2.png");
  size(800,600); 
  // Make your own font. It's fun! 
  myFont = loadFont("Serif-48.vlw"); 
  textFont(myFont, 35); 
  // List all the available serial ports: 
  println(Serial.list()); 
  myPort = new Serial(this, Serial.list()[3], 2400); 
  myPort.bufferUntil(lf); 
} 

void draw() {
  
  background(0);
  if (inString == 'N' )   
  {                      
    set(200,200,img);               
  } 
   
} 

void serialEvent(Serial p) { 
  inString = (myPort.readString()); 
}

What am I doing wrong?

inString == 'N'

I'm not a Processing programmer, but "inString" is a string, whilst 'N' is a single character.