Skip to content

Instantly share code, notes, and snippets.

@DaAwesomeP
Last active October 11, 2022 23:22
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DaAwesomeP/4e7a8f56e25d2cd9694c to your computer and use it in GitHub Desktop.
Save DaAwesomeP/4e7a8f56e25d2cd9694c to your computer and use it in GitHub Desktop.
DIY Digital Compass Sketch (for your car) http://www.instructables.com/id/DIY-Digital-Compass-for-your-car
/******************************************************************************
Compass.ino
For the project here: http://www.instructables.com/id/DIY-Digital-Compass-for-your-car
Originally Created: August 10, 2015
Written by Perry Naseck (https://perrynaseck.com/)
Dependencies:
- Cardinal (in Library Manager and at https://github.com/DaAwesomeP/arduino-cardinal )
- Sparkfun Micro OLED Breakout (in Library Manager and at https://github.com/sparkfun/SparkFun_Micro_OLED_Arduino_Library )
- Wire (included with IDE)
- SPI (included with IDE)
This sketch interfaces with the HMC5883 magentometer of I2C, converts degrees
to a heading with Cardinal, and then outputs the heading on the Sparkfun
Micro OLED screen.
Copyright 2015-present Perry Naseck (DaAwesomeP)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
******************************************************************************/
#include <Wire.h> // I2C Arduino Library for the sensor
#include <SPI.h> // Include SPI if you're using SPI for the screen
#include <SFE_MicroOLED.h> // Include the SFE_MicroOLED library
#include <Cardinal.h> // Cardinal library
// ----------------------------------------------------------
// | Configuration area (edit stuff below here) |
// ----------------------------------------------------------
/*
Precision types (see https://github.com/DaAwesomeP/arduino-cardinal/wiki/Types
for more info):
0: N, S, E, W
1: N, NE, E, SE, S, SW, W, NW
2: N, NNE, NE, ENE, E, ESE, SE, SSE, S, SSW, SW, WSW, W, WNW, NW, NNW
3: all 32 headings (N, NbE, NNE, NEbN, NE, NEbE, etc.)
*/
int type = 1;
/*
Your declination angle is an offset that depends on your location. To find
out yours, go to http://www.magnetic-declination.com/
Convert the degrees to radians and use it below.
If you don't bother to adjust this, then your compass will be a little
bit off. Set it to 0.0 if you don't care.
*/
float declinationAngle = 0.0;
/*
The floatingX and floatingY values determine the 3D orientation of
the sensor. Shift the following values through x, y, and z until
the compass gices an accurate change. Note that it may not
actually give the right reading at this point. The Rotation offset
will help with that. With this settings you are ensuring that the
compass changes properly when it is rotated on the correct axis.
The default values (x, y) assumes that the commpass is flat and
face up. Mine ended up being (x,z).
*/
String floatingX = "x",
floatingY = "y";
/*
The rotation offset will adjust the problem where the compass gives
the wrong reading when your device is facing a different direction.
You should change this in 90 degree increments until North is North,
etc. Test all four cardinal directions and not just one. I needed a
90 degree increase.
This field is in radians and not degrees (so don't use 90). I've
included some handy measurements here:
0 degrees = 0.0 radians
90 degrees = 1.57079633 radians
180 degrees = 3.14159265 radians (sound familiar?)
270 degrees = 4.71238898 radians
*/
float rotationOffset = 0;
/*
If you follow the same design that I had, then you'll
need to flip the screen virtically and horizontally.
*/
boolean flip = true;
/*
Precision of PI. You porbably don't need to change this. Don't overdo it.
*/
float pi = 3.1415926535897932384626433832;
/*
Zombie Mode will launch when the direction changes
however many times in zmodeStartAfter. Set it to 0
to disable it. It will last for however many direction
changes as set in zmodeLength. Set zmodeLength to 0
to stay on permanently. Set zmodeOnStart to true for
zombie mode to start immeadietly at startup.
*/
int zmodeStartAfter = 20,
zmodeLength = 5;
boolean zmodeOnStart = false;
/*
By uncommenting the follwing line, you will enable
DEVMODE, which turns off the display output and
enable Serial output of the sensor.
*/
//#define DEVMODE 1
// ----------------------------------------------------------
// | You probably don't need to edit anything below here... |
// ----------------------------------------------------------
Cardinal cardinal; // init the Cardinal library to "cardinal"
#define address 0x1E //0011110b, I2C 7bit address of HMC5883
// MicroOLED Definition
#define PIN_RESET 9 // Connect RST to pin 9
#define PIN_DC 8 // Connect DC to pin 8
#define PIN_CS 10 // Connect CS to pin 10
#define DC_JUMPER 0
MicroOLED oled(PIN_RESET, PIN_DC, PIN_CS); // SPI declaration
void setup(){
oled.begin(); // Initialize the OLED
oled.flipVertical(flip);
oled.flipHorizontal(flip);
oled.clear(ALL); // Clear the display's internal memory
oled.clear(PAGE);
oled.display(); // Display what's in the buffer (splashscreen)
//delay(1000); // Delay 1000 ms
// Initialize Serial and I2C communications
Serial.begin(9600);
Wire.begin();
// Put the HMC5883 IC into the correct operating mode
Wire.beginTransmission(address); // open communication with HMC5883
Wire.write(0x02); // select mode register
Wire.write(0x00); // continuous measurement mode
Wire.endTransmission();
}
void loop(){
float direction, directionDegrees; // Init the direction data
String dirString, // Init the direction string
dirStringPrev;
int x,y,z, // Init triple axis data
zmodeStartCount = 0,
zmodeCount = 0;
boolean zmodeOn = zmodeOnStart;
// Tell the HMC5883L where to begin reading data
Wire.beginTransmission(address);
Wire.write(0x03); // select register 3, X MSB register
Wire.endTransmission();
// Read data from each axis, 2 registers per axis
Wire.requestFrom(address, 6);
if(6<=Wire.available()){
x = Wire.read()<<8; // X msb
x |= Wire.read(); // X lsb
z = Wire.read()<<8; // Z msb
z |= Wire.read(); // Z lsb
y = Wire.read()<<8; // Y msb
y |= Wire.read(); // Y lsb
}
if (floatingX == "x" && floatingY == "y")
direction = atan2(y, x);
if (floatingX == "y" && floatingY == "x")
direction = atan2(x, y);
if (floatingX == "z" && floatingY == "y")
direction = atan2(y, z);
if (floatingX == "z" && floatingY == "x")
direction = atan2(x, z);
if (floatingX == "x" && floatingY == "z")
direction = atan2(z, x);
if (floatingX == "y" && floatingY == "z")
direction = atan2(z, y);
direction += declinationAngle + rotationOffset;
if(direction < 0)
direction += 2*pi;
// Check for wrap due to addition of declination and rotation offset;
if(direction > 2*pi)
direction -= 2*pi;
// Convert radians to degrees for readability.
directionDegrees = 360 - (direction * 180/pi);
dirString = cardinal.getString(type, directionDegrees);
#if defined(DEVMODE)
// Print out values of each axis with 4 decimal places(to Serial, not the screen)
Serial.print("x: ");
Serial.print(x, 4); // X
Serial.print(" y: ");
Serial.print(y, 4); // y
Serial.print(" z: ");
Serial.print(z, 4); // z
Serial.print(" dRad: ");
Serial.print(direction, 8); // direction in Radians
Serial.print(" dDeg: ");
Serial.print(directionDegrees, 8); // direction in Degrees
Serial.print(" dInt: ");
Serial.print(cardinal.getInteger(type, directionDegrees)); // heading integer (1, 2, 3, etc...)
Serial.print(" dStr: ");
Serial.println(dirString); // heading string (N, NE, E, etc...)
#else
if ((zmodeStartCount == zmodeStartAfter) || (zmodeOn && zmodeOnStart)) {
zmodeOn = true;
zmodeStartCount = 0;
zmodeOnStart = false;
zmodeStart();
}
if (dirString != dirStringPrev && !zmodeOn) {
zmodeStartCount++;
}
if (zmodeCount <= zmodeLength && zmodeOn){
zombieModeDirection(dirString);
zmodeCount++;
}
else {
if (zmodeCount > zmodeLength && zmodeLength != 0) {
zmodeCount = 0;
zmodeOn = false;
zmodeStop();
}
clearScreen();
if (dirString.length() > 2) {
oled.setFontType(1); // Use font size 1
}
else {
oled.setFontType(4); // Use font size 4
}
oled.print(dirString); // Get the heading string and put it on screen
oled.display(); // Write to the screen
}
dirStringPrev = dirString;
#endif
delay(250); // Delay of 250 ms to reduce sensitivity and allow screen and sensor to refresh
}
void zmodeStart() {
oled.setFontType(1);
int c = 0;
boolean d = true;
String str = "ZOMBIES FOUND";
while (c < 4) {
clearScreen();
oled.invert(d);
oled.print(str);
oled.display();
delay(250);
c++;
d = !d;
if (c == 2) { str = "ZOMBIE\n MODE\nENABLED"; }
}
}
void zombieModeDirection(String zdir) {
oled.setFontType(1);
int c = 0;
boolean d = true;
while (c < 2) {
clearScreen();
oled.invert(d);
oled.print("ZOMBIESHEADING");
oled.display();
delay(250);
c++;
d = !d;
}
oled.setFontType(4);
clearScreen();
oled.print(zdir);
oled.display();
}
void zmodeStop() {
oled.setFontType(1);
int c = 0;
boolean d = true;
String str = "ZOMBIES TOO\n CLOSE!";
while (c < 4) {
clearScreen();
oled.invert(d);
oled.print(str);
oled.display();
delay(250);
c++;
d = !d;
if (c == 2) { str = "GOING OFF!"; }
}
}
void clearScreen() {
oled.clear(PAGE);
oled.setCursor(0, 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment