The "press button " is standard, but the B is double size (10x16 pixels)--we can only get 2 rows of about 12 characters at that size.
For further readability, I also made the entire battery status and heading messages double size. Another option that I did not choose would have been to setTextColor(INVERSE)--I used WHITE.
/********************************
2016-1011 VM
Code sample from Adafruit web site, FeatherWing OLED example
Combined with HMC5883L example as described below
Added power management, interrupts, and battery status display, plus a function
******************/
/***************************************************************************
This is a library example for the HMC5883 magnentometer/compass
Designed specifically to work with the Adafruit HMC5883 Breakout
http://www.adafruit.com/products/1746
*** You will also need to install the Adafruit_Sensor library! ***
These displays use I2C to communicate, 2 pins are required to interface.
Adafruit invests time and resources providing this open source code,
please support Adafruit andopen-source hardware by purchasing products
from Adafruit!
Written by Kevin Townsend for Adafruit Industries with some heading example from
Love Electronics (loveelectronics.co.uk)
This program is free software: you can redistribute it and/or modify
it under the terms of the version 3 GNU General Public License as
published by the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see
.
***************************************************************************/
#include
#include
#include
#include
#include
Adafruit_SSD1306 display = Adafruit_SSD1306();
#if defined(ESP8266)
#define BUTTON_A 0
#define BUTTON_B 16
#define BUTTON_C 2
#define LED 0
#elif defined(ARDUINO_STM32F2_FEATHER)
#define BUTTON_A PA15
#define BUTTON_B PC7
#define BUTTON_C PC5
#define LED PB5
#elif defined(TEENSYDUINO)
#define BUTTON_A 4
#define BUTTON_B 3
#define BUTTON_C 8
#define LED 13
#else
//these are the values for the Feather M0
#define BUTTON_A 9
#define BUTTON_B 6
#define BUTTON_C 5
#define LED 13
#endif
#define VBATPIN A7 //aka D9, which BUTTON_A uses on the M0--don't use BUTTON_A for interrupts if you are also trying to display battery status
#if (SSD1306_LCDHEIGHT != 32)
#error("Height incorrect, please fix Adafruit_SSD1306.h!");
#endif
/* Assign a unique ID to this sensor at the same time */
Adafruit_HMC5883_Unified mag = Adafruit_HMC5883_Unified(12345);
String directionArray[] = {"N","NNE","NE","ENE","E","ESE", "SE","SSE","S","SSW","SW","WSW","W","WNW","NW","NNW" }; //array for map function 16 compass directions
int Dir=0; //index for directionArray
const float declinationAngle = 0.261799; //angle for Orleans, MA
float heading = 0; //this and next for comparison to see if we've been interrupted
volatile boolean headingRead = false; //variables in ISR need to be volatile
volatile boolean batteryRead = false;
#if defined(ARDUINO_SAMD_ZERO) && defined(SERIAL_PORT_USBVIRTUAL)
// Required for Serial on Zero based boards
#define Serial SERIAL_PORT_USBVIRTUAL
#endif
void headingISR() //ISR for button B press
{
headingRead = true;
}
void batteryISR() //ISR for button A press
{
batteryRead = true;
}
String mapDirection (float headDeg)
{
//function to return direction in text based on heading in degrees
//N is 11.25 degrees each side of 0, so that doesn't work too well with the map function
//so if it's N, we say so
//this is a nice way of taking the procedural stuff out of line, so the loop code can just ask for the direction
if (headDeg > 348.75 | headDeg <11 .26="" 0="" around="" case="" direction="North," nbsp="" p="" special="" wrapped=""> {
Dir = 0;
} else {
//otherwise we elimiate the decimals and map degrees to text in 22.5 degree increments
Dir = map(headDeg*100,1126,34875,1,15);
}
//and return the selected text compass direction
return directionArray[Dir];
}
void setup() {
// by default, we'll generate the high voltage from the 3.3v line internally! (neat!)
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // initialize with the I2C addr 0x3C (for the 128x32)
// init done
// Show image buffer on the display hardware.
// Since the buffer is intialized with an Adafruit splashscreen
// internally, this will display the splashscreen.
display.display();
delay(1000);
pinMode(BUTTON_A, INPUT_PULLUP);
pinMode(BUTTON_B, INPUT_PULLUP);
pinMode(BUTTON_C, INPUT_PULLUP);
// Clear the buffer and display the press button message
display.clearDisplay();
display.setCursor(0,0);
display.print("Press Button ");display.setTextSize(2); display.print("B");
display.display();
display.setTextSize(1); //reset size
/* Initialise the sensor */
if(!mag.begin())
{
/* There was a problem detecting the HMC5883 ... check your connections */
display.println("Ooops, no HMC5883 detected ... Check your wiring!"); display.display();
while(1);
}
interrupts(); //enable interrupts (should not need to do this, but just for drill...)
//on the zero (as in Feather M0), interrup#=pin#; we use digitalPintToInterrupt here to provide some portability
//if we change to a different board AND that board allows interrups on the same pins, we don't have to change anything to get the interrupt number
//if we're using the battery function, VBATPIN is A7, also D9, and button A uses D9, so we avoid conflict
attachInterrupt(digitalPinToInterrupt(BUTTON_B), headingISR, LOW); // when button B is pressed display compass heading; use LOW because FALLING does not work in sleep mode--needs a timer
attachInterrupt(digitalPinToInterrupt(BUTTON_C), batteryISR, LOW); // when button C is pressed display battery status
//set System Control Register (SCR) sleep bit to deep sleep (do once so wfi (wait for intyerrupt) in loop waits)
//There are 2 sleep modes, idle and standby (deep) Sleep bit is SCR bit 2, 0 is idle, 1 is standby
// SCB->SCR |= 0b00000100; //just a test to see how to code binary--this works
// SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // set to deep sleep (bit-1) by ORing with 100
SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk; //set to idle (bit=0) by ANDing with the inverse of above--leaves all bita alone except sleep, which ANDs to 0
// SCB->SCR &= 0b11111011; //another test
//There are 3 idle modes, 0 (CPU),1 (CPU,AHB clock),2 (CPU,AHB,APB). Set this if using IDLE
PM->SLEEP.reg |= PM_SLEEP_IDLE_APB; //idle turn off CPU, AFB, APB //Advanced Peripheral Bus (APB) Advanced High-performance Bus (AHB)
// display instructions at startup
//set size and color of text, tell the user we're waiting 10s, then say to press the button
display.setTextSize(1); //parameter is scale of 5X8 pixels
display.setTextColor(WHITE); //options are BLACK | WHITE | INVERSE
display.clearDisplay();
display.setCursor(0,0);
display.println("initializing for 10s");
display.display();
// delay(10000); //see if sensor will settle down before taking reading
display.setCursor(0,0);
display.clearDisplay();
display.print("Press Button ");display.setTextSize(2); display.print("B"); //button letter is double size
display.display();
display.setTextSize(1); //reset size
}
void loop() {
//the wfi() means we only progress in loop on an interrupt, either button B or C pressed invoking headingISR or batteryISR
//which set the corresponding booleans
//If it was C, we display the battery status for 3 seconds and go on
//in either case, we display the compass heading, complete the loop, and wait for the next button press
//wait-for-interrupt has no effect unless the sleep bit is set in the
//System Control Register (SCR)(see setup, in the attachInterrupt area)
while (!(batteryRead || headingRead)) { //if an ISR has not set one of the booleans, wait (they're both initalized to false)
//if the sleep bit is set, we wait after this instruction for an interrupt
__WFI(); //Double underscore!! (took me a few looks to see that)
}
if (batteryRead) //if we got here because operator wants battery info (button C pressed--see batteryISR)
{
batteryRead=!batteryRead; //reset for next pass
float measuredvbat = analogRead(VBATPIN);
measuredvbat *= 2; // we divided by 2, so multiply back
measuredvbat *= 3.3; // Multiply by 3.3V, our reference voltage
measuredvbat /= 1024; // convert to voltage
display.clearDisplay();
display.setCursor(0,0);
display.setTextSize(2); //set size
display.print("VBat: " ); display.println(measuredvbat);
display.setTextSize(1); //reset size
display.print("Press Button ");display.setTextSize(2); display.println("B");
display.display();
display.setTextSize(1); //reset size
delay(3000); //hold for 3 sec, then go on and display heading
}
//however we got here (BUTTON_B or BUTTON_C), read the compass and display
/* Get a new sensor event */
headingRead = false; //set headingRead to false whether we need to or not
sensors_event_t event;
mag.getEvent(&event);
float heading = atan2(event.magnetic.y, event.magnetic.x);
heading+=declinationAngle; //add declination for location--initialized as a constant
// 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;
// use degrees to determine text Direction in map function
display.clearDisplay();
display.setCursor(0,0);
display.setTextSize(2); display.print(headingDegrees);display.print("="); display.println(mapDirection(headingDegrees));
display.setTextSize(1);display.print("Press Button ");display.setTextSize(2); display.print("B");
display.display();
display.setTextSize(1); //reset size
//end of loop--back to top to wait for interrupt (next button press)
}11>