This post was originally published on September 16, 2017 at https://sparklechicken.net/channel/jakessbc
Building on what I mashed together a couple of days ago, I’ve got an updated version of the Little Dipper. And I just named it. Like just now, as I was typing that. Good grief, I’m an idiot. Anyways, I’ve added a virtual bubble level, the Vubble Level, to the mix to help isolate the pitch from the roll. It is three LEDs, two red with a green one in the middle. Yes, I named this, too.
Software wise, I cleaned up the code a bit and found a lovely function in the 9DOF library, fusionGetOrientation(), that integrates the positional data with the magnetometer reading to give a true north reading regardless of the orientation of the IMU.
Since I live in Illinois and am several hundred kilometers from the nearest rock outcropping, I had to run my tests on my windshield and my wife’s. W00t! My pitch readings are about 2° off from the transit compass. This is within the current design parameters since it reads 2° of dip while sitting on a flat surface due to a less than perpendicular solder job on the headers and the fact that this is just breadboarded. Directional readings were likewise within 2° of the transit compass, but since I was measuring a curved surface this was within my margins of error.
This build requires:
Arduino Uno
ST7735 1.4″ TFT
9-DOF IMU L3GD20 + LSM303
3 x 260𝛺 Resistors
2 x Red LEDs
1 x Green LED
#include <Wire.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_LSM303_U.h>
#include <Adafruit_L3GD20_U.h>
#include <Adafruit_9DOF.h>
#include <Adafruit_GFX.h> // Core graphics library
#include <Adafruit_ST7735.h> // Hardware-specific library
#include <SPI.h>
#define TFT_CS 10
#define TFT_RST 9 // you can also connect this to the Arduino reset
// in which case, set this #define pin to 0!
#define TFT_DC 8
#define TFT_SCLK 13 // set these to be whatever pins you like!
#define TFT_MOSI 11 // set these to be whatever pins you like!
/* Assign a unique ID to the sensors */
Adafruit_9DOF dof = Adafruit_9DOF();
Adafruit_LSM303_Accel_Unified accel = Adafruit_LSM303_Accel_Unified(30301);
Adafruit_LSM303_Mag_Unified mag = Adafruit_LSM303_Mag_Unified(30302);
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);
/* Update this with the correct SLP for accurate altitude measurements */
float seaLevelPressure = SENSORS_PRESSURE_SEALEVELHPA;
float p = 3.1415926;
float adjHeading = 0;
/*set pins for Vubble Level */
int left = 7;
int level = 6;
int right = 5;
/**************************************************************************/
/*!
@brief Initialises all the sensors used by this example
*/
/**************************************************************************/
void initSensors()
{
if(!accel.begin())
{
/* There was a problem detecting the LSM303 ... check your connections */
Serial.println(F("Ooops, no LSM303 detected ... Check your wiring!"));
while(1);
}
if(!mag.begin())
{
/* There was a problem detecting the LSM303 ... check your connections */
Serial.println("Ooops, no LSM303 detected ... Check your wiring!");
while(1);
}
}
/**************************************************************************/
/*!
*/
/**************************************************************************/
void setup(void)
{
Serial.begin(115200);
Serial.println(F("Adafruit 9 DOF Pitch/Roll/Heading Example")); Serial.println("");
tft.initR(INITR_BLACKTAB);
/* Initialise the sensors */
initSensors();
uint16_t time = millis();
tft.fillScreen(ST7735_BLACK);
time = millis() - time;
/*Initialize the Vubble Level */
pinMode(left, OUTPUT);
pinMode(level, OUTPUT);
pinMode(right, OUTPUT);
}
/**************************************************************************/
/*!
@brief Constantly check the roll/pitch/heading/altitude/temperature
*/
/**************************************************************************/
void loop(void)
{
sensors_event_t accel_event;
sensors_event_t mag_event;
sensors_vec_t orientation;
/* Calculate pitch and roll from the raw accelerometer data
and calculate heading from magnetometer*/
accel.getEvent(&accel_event);
mag.getEvent(&mag_event);
if (dof.fusionGetOrientation(&accel_event, &mag_event, &orientation))
{
if (orientation.heading < 0)
{
adjHeading = 360 + orientation.heading;
}
else
{
adjHeading = orientation.heading;
}
/* 'orientation' should have valid .roll field
pitch will be represented by Vubble Level*/
adjHeading = abs(360-adjHeading);
tft.fillScreen(ST7735_BLACK);
tft.setCursor(0, 30);
tft.setTextColor(ST7735_YELLOW);
tft.setTextSize(2);
tft.println("Dip: ");
tft.println(abs(orientation.roll));
tft.println(" ");
tft.setTextColor(ST7735_YELLOW);
tft.setTextSize(2);
tft.println("Strike: ");
tft.println(adjHeading);
tft.println(" ");
delay(10);
}
/*This series of if statements run the Vubble Level
* There is probably a vastly better way to do this
* using elif, but need to look up syntax first.
*/
if (orientation.pitch < -3)
{
digitalWrite(left,HIGH);
analogWrite(level,0);
analogWrite(right,0);
}
if (orientation.pitch > 3)
{
analogWrite(left,0);
analogWrite(level,0);
analogWrite(right,255);
}
if (orientation.pitch >= -3 && orientation.pitch <= 3)
{
analogWrite(left,0);
analogWrite(level,255);
analogWrite(right,0);
}
tft.println("");
delay(100);
}
void testdrawtext(char *text, uint16_t color) {
tft.setCursor(0, 0);
tft.setTextColor(color);
tft.setTextWrap(true);
tft.print(text);
}