Code for a Portable Weather Station built on a PCB.

Dependencies:   BMP180 N5110 PowerControl mbed

main.cpp

Committer:
Volcano_498
Date:
2015-05-06
Revision:
14:054e6faf0ca8
Parent:
12:1d4b5465ecc1

File content as of revision 14:054e6faf0ca8:

/**
@file main.cpp
@brief Code for a Portable Weather Station to be built on PCB.
@brief Design Specs: *A small, portable battery-powered data logger that records sensor data at regular intervals.
@brief -- Minimises mbed power consumption to the greatest extent possible.
@brief -- Displays the information to the user.
@brief -- Features a BMP180 pressure and temperature sensor. (TMP102 is unnecessary as BMP180 also takes temp. readings)
@brief -- Features a Nokia 5110 display to print the sensor readings over and display them to the user.
@brief -- Sensor information is recorded on Flash memory on the mbed.
@brief -- Graph to display data to the user.
@brief -- mbed is powered by a Buck regulator (5V output) that is in turn supplied by a 9V PP3 battery which powers the PCB.
@brief -- Audible and visual alert when a threshold reading is obtained.
@brief -- Celsius-to-Fahrenheit and Celsius-to-Kelvin conversions of temperature readings and displaying them.
@brief -- Adjusting the unit the pressure is displayed in: millibars(mb), Pascals(Pa) and Atmospheres (atm).
@brief -- Displaying a brief splash screen to show changes in the unit display settings.
@author Volkan Esendag (SID:200795870)
@date 11 March 2015 (created) / 6 May 2015(last modified)
*/


#include "mbed.h"
#include "N5110.h"
#include "BMP180.h"
//import the PowerControl/EthernetPowerControl header files from the Power Control library
//to enable the power down features for the microprocessor and the Ethernet feature.
#include "PowerControl/PowerControl.h"
#include "PowerControl/EthernetPowerControl.h"

#ifndef USR_POWERDOWN
#define USR_POWERDOWN (0x104)    //defines USB interface powerdown.
#endif

#ifndef PNought_DEFAULT
#define PNought_DEFAULT  1013.25  //Po = 101325 Pa or 1 atm or 1013.25 mb.
#endif

/**
Custom struct for logging intervals. Fetch state number and select recording interval via the use of a button.
*/

struct RecState {
    int recordState;
    float time;
};

//once struct has been declared, define a pointer type with it.
typedef const struct RecState RecS;

/**
@namespace recInterval
@brief Structed RecS object type that holds information on different logging interval settings for a given state.
@brief Has an array size of 10 to accomodate 10 different logging settings.
*/

RecS recInterval[10] = {
    {0,600.0},    //state 0, 10 minutes
    {1,1200.0},   //state 1, 20 minutes
    {2,1800.0},   //state 2, 30 minutes
    {3,3600.0},   //state 3, 1 hour. Default State.
    {4,7200.0},   //state 4, 2 hours
    {5,10800.0},  //state 5, 3 hours
    {6,14400.0},  //state 6, 4 hours
    {7,21600.0},  //state 7, 6 hours
    {8,28800.0}, //state 8, 8 hours
    {9,43200.0}  //state 9, 12 hours
};

/**
@namespace dispInterval
@brief Another RecS pointer type object that holds information on device readings update intervals.
@brief Has an array size of 6 to accomodate 6 different display interval settings.
*/

RecS dispInterval [6] = {
    {0,1.0},    //state 0, 1 second. Default state.
    {1,2.0},   //state 1, 2 seconds
    {2,3.0},   //state 2, 3 seconds
    {3,4.0},   //state 3, 4 seconds
    {4,5.0},   //state 4, 5 seconds
    {5,0.5},  //state 5, 500 milliseconds
};

/*!<Struct to set the temperature threshold after which point the device gives off a warning.*/
struct TempThreshold {
    int tempState;
    float thresTemp;
};

//define the TempTyp pointer type using the TempThreshold struct.
typedef const struct TempThreshold TempTyp;

/**
@namespace tempThres
@brief Structed TempTyp pointer type object to hold information on threshold temperature settings for the device.
@brief Should the temperature reading exceed a set value, visual and audible feedback is generated.
@brief Has 5 different settings to accomodate 5 threshold temperature settings.
*/

TempTyp tempThres[5] = {
    {0,30.0},   //state 0, 30'C
    {1,40.0},   //state 1, 40'C
    {2,50.0},   //state 2, 50'C. Default threshold.
    {3,60.0},   //state 3, 60'C
    {4,70.0}    //state 4, 70'C
};

/**
@namespace lowerTemp
@brief Structed TempTyp pointer type object to hold information on lower end temperature settings for the device.
@brief This is instead used for the Temperature Plotter Menu to set a bottom floor for the plot space.
@brief Has 5 different settings to accomodate 5 minimum display temperature settings.
*/

TempTyp lowerTemp[5] = {
    {0,-10.0},   //state 0, -10'C
    {1,-5.0},   //state 1, -5'C
    {2,0.0},   //state 2, 0'C. Default lower threshold.
    {3,10.0},   //state 3, 10'C
    {4,20.0}    //state 4, 20'C
};

/**
An array of characters to represent temperature unit settings (C - Degrees Celsius F - Degrees Fahrenheit K - Kelvins R - Degrees Rankine).
*/

char tempUnit [4] = {'C','F','K','R'}; //character that stores temperature unit type.

/**
An array of characters to represent pressure unit settings (M - millibars P - Pascals A - Atmospheres)
*/

char pressUnit[3] = {'M','P','A'}; //character that stores pressure units. M - millibars P - Pascals A - atmospheres

/**
An array of characters to represent unit settings (m - metres f - feet y - yards) for altitude.
*/

char AltUnit[3] = {'m','f','y'}; //character that stores altitude units. m - metres f - feet y - yards

/**
An array of characters to represent the column chart and the dot graph mode of the Temperature Plotter.
*/

char tempGraphMode [2] = {'c','d'}; //column chart, dot graph

/**
An array of characters to represent the column chart and the dot graph mode of the Pressure Plotter.
*/
char pressGraphMode [2] = {'c','d'}; //column chart, dot graph

/**
An array of PNought values to be used to calibrate the device for different weather fronts for altitude compensation.
float could work as well, but it is better to use double as pow(double x,double y) may not accept *arrays* with float pointer type!
*/
double PNought [13] = {983.25,988.25,993.25,998.25,1003.25,1008.25,PNought_DEFAULT,1018.25,1023.25,1028.25,1033.25,1038.25,1043.25};


/**
@namespace bmp180
@brief A special structed type of Inter-integrated Circuit object created for the BMP180. For more info, see the BMP180 library by Dr. Craig Evans.
@see http://developer.mbed.org/users/eencae/code/BMP180/
*/
BMP180 bmp180(p28,p27);
//Pins are declared in the public domain and the sensor itself acts on private variables.

/**
@namespace buzzerPwm
@brief PwmOut object to apply a PWM signal with a duty ratio of 50% to the buzzer as an improvised square wave.
@brief Used for an audible feedback should the temperature reading exceed a certain value.
*/
PwmOut buzzerPwm(p24);

/**
@namespace redLED
@brief PwmOut object to apply a PWM signal to the red visual feedback LED via pin 22.
@brief Used for visual feedback to tell the user a certain temperature threshold has been reached.
*/
PwmOut redLED(p22);

/**
@namespace greenLED
@brief PwmOut object to apply a PWM signal to the green visual feedback LED via pin 23.
@brief Used to let the user know the device is operating normally.
*/
PwmOut greenLED(p23);

/**
@namespace serial
@brief Serial object to print readings over a USB cable and display them on a terminal.
*/
Serial serial(USBTX,USBRX); //serial object to print readings for debugging WHILE the USB cable is connected.

/**
@namespace menuButton
@brief Interrupt object to call ISR for the designated menu button when an input to p15 is applied.
@namespace buttonOne
@brief Interrupt object to call ISR for Button 1 when an input to p16 is applied.
@namespace buttonTwo
@brief Interrupt object to call ISR for Button 2 when an input to p17 is applied.
@namespace buttonThree
@brief Interrupt object to call ISR for Button 3 when an input to p18 is applied.
*/

InterruptIn menuButton(p15);  //Interrupt object for the designated menu button.
InterruptIn buttonOne(p16);   //Interrupt objects for the other buttons.
InterruptIn buttonTwo(p17);
InterruptIn buttonThree(p18);

/**
@namespace potAin
@brief Analogue input from potentiometer whose Vout is connected to pin 20.
*/

AnalogIn potAin(p20); //Potentiometer feedback pin to the mbed.

/**
@namespace logTimer
@brief Ticker object to record/log readings with a specified interval. Can be varied with Interrupt Service Routines.
*/

Ticker logTimer;   //Ticker object to call ISR for a specified period of time.

/**
@namespace displayTimer
@brief Ticker object to display readings on screen with a specified interval. Can be varied with Interrupt Service Routines.
*/
Ticker displayTimer; //ticker object to display readings

/*
@namespace leds
@brief GPIO output for status LEDs - used to display error message or as a flash memory overwrite operation feedback.
*/

BusOut leds(LED1,LED2,LED3,LED4);  //BusOut object for error feedback LEDs.
//configure pins of the LCD display...

/**
@namespace splashFlip
@brief Calls an ISR only once when attached. Used to delay splash screen for a few seconds before commencing with the program flow.
*/
Timeout splashFlip;

/**
@namespace lcd
@brief Object that belongs to the N5110 class. Set up pin outputs for the Nokia 5110 display. Defined in N5110.h.
@see https://developer.mbed.org/users/eencae/code/N5110/
*/
void printReadings();  // declare function to print readings here, which is then defined after the main() function.
void clearCells();   //declare function to clear all cells should there be a need for it. Even though lcd.clear() also does the job this can be handy in case of failure.

float temp = 0.0;  /*!< declare the temp variable for temperature universally so that it can be shared across functions to represent temperature. */
float press = 0.0; /*!< do the same for pressure using press. */
float altitude = 0.0;  /*!< and altitude using, well, altitude :P */

int i = 0;  /*!< represents the column number (horizontal pixel number) of the display. */
int j = 0;  /*!< represents the row number of the display. */

int dispSetting = 0; /*!< set display setting to default. */
int recSetting = 3;  /*!< set log setting to default. */
int tempSetting = 2; /*!< set temperature threshold to default. */

int tempUnitSetting = 0; /*!< set temperature unit setting to default. */
int pressUnitSetting = 0; /*!< set pressure unit setting to default. */
int altUnitSetting = 0; /*!< and do the same for altitude. */
int tempGraphSetting = 1; /*!< set the default to be dot chart. */
int pressGraphSetting = 1; /*!< set the default to be dot chart. */
int PNoughtSetting = 6; /*!< set the default PNought to be PNought_DEFAULT. */
int subMenuId = 0; /*!< int used to store sub-menu number. For instance pressing the menu button once and then Button One gives Sub-Menu 1. */

int logCounter = 0; /*!< int pointer used to store second count for logging. Initialised to 0.*/
int lowerTempSetting = 2; /*!< set lower temperature display to default.*/

N5110 lcd(p7,p8,p9,p10,p11,p13,p21);    //VCC,SCE,RST,DC,MOSI,SCLK,BACKLIGHT

///////////The following pieces of code are to configure real-time clock for the data logger.*************

char rxString[16]; /*!< buffer to store received string. Each character is a byte long - hence the char pointer type. */

int setTimeFlag = 0; /*!< set time flag set in serial ISR */

/**
Reads string input via serial interrupt and converts it into real time
@param rxString - string received from serial
@param time - integer that represents time, converted from input string (rxString).

*/
void setTime()
{
// print time for debugging
    serial.printf("set_time - %s",rxString);
// atoi() converts a string to an integer
    int time = atoi(rxString);
// update the time
    set_time(time);
}

/**
Interrupt Service Routine performed by Serial Interrupts - by using a serial terminal program such as CoolTerm.
Sets time flag in order to set and overwrite the current time onto the mbed. This only needs to be done once.
*/

void serialISR()
{
// when a serial interrupt occurs, read rx string into buffer. Holds 16 characters. gets implies fetching a string from serial port.
    serial.gets(rxString,16);
// set flag
    setTimeFlag = 1;
}

/**
Disables USB interface when the mbed's USB cable isn't attached.
Acknowledgements to Michael Wei's PowerControl library.
@param arg - argument function (pointer type unsigned int/uint32_t)
@returns __semihost(USR_POWERDOWN, &arg)
@see https://developer.mbed.org/users/JST2011/code/PowerControl/
*/

int semihost_powerdown()
{

    uint32_t arg;  //variable for return function
    return __semihost(USR_POWERDOWN, &arg);    //return semihost state...

} //...to power down the USB interface when the USB cable is detached.


LocalFileSystem local("local"); // create local filesystem

/**
Pretty self-explanatory: Using the local file system created, overwrites the mbed's Flash Memory and saves data on it.
@param data - placeholder for temperature data to be overwritten.
@param dataTwo - placeholder for pressure data to be overwritten.
@param dataThree - placeholder for time string to be stored - hence has an array size of 30 and a data type of char.
*/

void writeDataToFile(float data, float dataTwo, char dataThree[30])
{
    time_t seconds = time(NULL); // get current time
    strftime(dataThree, 30 , "%R %x", localtime(&seconds)); //convert it into a string, from an array size of 30.

    leds = 15; // turn on LEDs for feedback
    FILE *fp = fopen("/local/tempLog.csv", "a"); // open 'log.txt' for appending. Instance of class FILE.
// if the file doesn't exist it is created, if it exists, data is appended to the end
    fprintf(fp,"%s, %.2f , %.2f \n",dataThree,dataTwo,data); // print string to file
    fclose(fp); // close file
    leds = 0; // turn off LEDs to signify file access has finished
}

int menuButtonFlag = 0; /*!< menu button flag set in ISR */

int buttonOneFlag = 0; /*!< Button One flag set in ISR */
int buttonOneAltFlag = 0;  /*!< Button One Alternate flag set in ISR */

/**
Interrupt Service Routine to toggle buttonOneFlag/buttonOneAltFlag when not in a settings main menu and when in one respectively.
No parameters to be entered by, or values to be returned to, the user.
*/

void buttonOnePressed()
{
    if(menuButtonFlag > 0) { //if menu button has been pressed and main menu entered
        buttonOneAltFlag = !buttonOneAltFlag;  //set/reset-if-set alternate flag and proceed to next menu
    } 
    else {
        buttonOneFlag = !buttonOneFlag;  //set/reset-if-set flag if not navigated to a menu
    }
}

int buttonTwoFlag = 0; /*!< Button Two flag set in ISR */
int buttonTwoAltFlag = 0; /*!< Button Two Alternate flag set in ISR */

/**
Interrupt Service Routine to toggle buttonTwoFlag/buttonTwoAltFlag.
No parameters to be entered by, or values to be returned to, the user.
*/

void buttonTwoPressed()
{
    if(menuButtonFlag > 0) {
        buttonTwoAltFlag = !buttonTwoAltFlag;
    } 
    else {
        buttonTwoFlag = !buttonTwoFlag;
    }
}

int buttonThreeFlag = 0; /*!< Button Three flag set in ISR */
int buttonThreeAltFlag = 0; /*!< Button Three Alternate flag set in ISR */

/**
Interrupt Service Routine to toggle buttonThreeFlag/buttonThreeAltFlag.
No parameters to be entered by, or values to be returned to, the user.
*/

void buttonThreePressed()
{
    if(menuButtonFlag > 0) {
        buttonThreeAltFlag = !buttonThreeAltFlag;
    } 
    else
        buttonThreeFlag = !buttonThreeFlag;
}

/**
Interrupt Service Routine to increment menuButtonFlag when Menu Button is pressed to navigate to settings menus.
No parameters to be entered by, or values to be returned to, the user.
*/

void menuButtonPressed()
{
    if(buttonOneAltFlag == 0 && buttonTwoAltFlag == 0 && buttonThreeAltFlag == 0 || buttonOneFlag || buttonTwoFlag){ //if no flag is set and therefore no menu accessed (except buttonOneFlag and buttonTwoFlag which are set in the measurement menu)
        menuButtonFlag++; //increment the flag to access different menu states.

        if(menuButtonFlag > 3) { //if menu button has been clicked three times
            menuButtonFlag = 0; //go back to the measurements menu
        }
    }
}

int splashFlag = 1;  /*!< Splash flag set to continue with program flow for the main function before proceeding with program flow */

/**
Interrupt Service Routine to reset splashFlag when Timeout has been performed.
No parameters to be entered by, or values to be returned to, the user.
*/

void splashDelay()
{
    splashFlag = 0;
}

int dispTimerFlag = 0; /*!< Display flag set to 0 initially. Used to update values on the display when the ISR is called.*/

/**
Interrupt Service Routine to set dispTimerFlag when Ticker duration has elapsed.
No parameters to be entered by, or values to be returned to, the user.
*/
void timerExpiDisplay()
{
    dispTimerFlag = 1;
}

int logTimerFlag = 0; /*!< Log flag set to 0 initially. Used to overwrite the Flash Memory when the ISR is called, by using a log counter.*/

/**
Interrupt Service Routine to set logTimerFlag when Ticker duration has elapsed.
No parameters to be entered by, or values to be returned to, the user.
*/
void timerExpiLog()
{
    logTimerFlag = 1;
}

/**
Displays the initial splash screen when the Weather Station is turned on from rest.
*/

void displayInitSplash()  //!display splash screen
{
    lcd.printString("Welcome to",15,1);
    lcd.printString("Portable Weat.",3,2);
    lcd.printString("Station",20,3);
    lcd.printString("by Volkan",20,4);
    lcd.printString("Esendag",20,5);
}

/**
Displays the first settings main menu.
*/

void displayMenuOne(){  //!settings main menu 1!
    lcd.printString("Settings Menu",0,0);
    lcd.printString("One",0,1);
    lcd.printString("Use Buttons",0,2);
    lcd.printString("To change",0,3);
    lcd.printString("Settings",0,4);
    lcd.refresh();
}

/**
Displays the second settings main menu.
*/

void displayMenuTwo(){
    lcd.printString("Settings Menu",0,0);
    lcd.printString("Two",0,1);
    lcd.printString("Use menuButton",0,2);
    lcd.printString("To Advance To",0,3);
    lcd.printString("Next menu",0,4);
    lcd.refresh();
}

/**
Displays the third settings main menu.
*/

void displayMenuThree(){
    lcd.printString("Settings Menu",0,0);
    lcd.printString("Three",0,1);
    lcd.printString("Use menuButton",0,2);
    lcd.printString("To Go Back To",0,3);
    lcd.printString("Display menu",0,4);
    lcd.refresh();
}

/**
Displays the Temperature Unit Display Sub-Menu. In this menu one can determine whether the temperature will be displayed...
...in degrees Celsius(default), degrees Fahrenheit, Kelvins or degrees Rankine.
*/

void displayTempUnit(){  //!temperature unit display sub-menu
    lcd.printString("Use Button 2",0,0);
    lcd.printString("To decrease",0,1);
    lcd.printString("Temp. Setting;",0,2);
    lcd.printString("Button 3",0,3);
    lcd.printString("To increase.",0,4);
    
    char bufferSt[14];  //buffer to store string
    sprintf(bufferSt,"current:%c",tempUnit[tempUnitSetting]); //write the typed string and the current unit setting onto the buffer
    
    lcd.printString(bufferSt,0,5); //print the buffer
    lcd.refresh();  //needs to refresh to write the string buffers to the display
}

/**
Displays the Pressure Unit Display Sub-Menu. In this menu one can determine whether the pressure will be displayed...
...in millibars(default), Pascals or Atmospheres.
*/

void displayPressUnit(){  //!pressure unit display sub menu
    lcd.printString("Use Button 1",0,0);
    lcd.printString("To decrease",0,1);
    lcd.printString("Press Setting;",0,2);
    lcd.printString("Button 3",0,3);
    lcd.printString("To increase.",0,4);
    
    char bufferSt[14];  //buffer to store string
    sprintf(bufferSt,"current:%c",pressUnit[pressUnitSetting]);
    
    lcd.printString(bufferSt,0,5);
    lcd.refresh();
}

/**
Displays the Log Interval Menu for the Weather Station. In this menu one can determine how often the device will log...
...or more correctly the number of counters it takes for the device to log data on and reset the counter.
*/

void displayLogMenu(){ //!displays the log interval change sub-menu.
    lcd.printString("Use Button 1",0,0);
    lcd.printString("To decrease",0,1);
    lcd.printString("Log Interval;",0,2);
    lcd.printString("Button 2",0,3);
    lcd.printString("To increase.",0,4);
    
    char bufferSt[14];  //buffer to store string
    //just a universally applicable if-else if statement to create a string out of current time setting should more time settings be added...
    if(recInterval[recSetting].time >= 3600.0){ //if log interval is an hour (3600 seconds) or more
        float tempTime = recInterval[recSetting].time/3600.0; //convert time in seconds into hours
        sprintf(bufferSt,"set:%.1f hr",tempTime);
    }
    else if(recInterval[recSetting].time < 3600.0){ //if log interval is in minutes
        float tempTime = recInterval[recSetting].time/60.0; //convert time to minutes
        sprintf(bufferSt,"set:%.1f min",tempTime);
    }    
    lcd.printString(bufferSt,0,5);
    lcd.refresh();
}

/**
Displays the Display Interval Sub-Menu. Intervals of a few seconds or 500 ms.
In other words, determines how often the device will update temperature/pressure/altitude readings.
*/

void displayDispMenu(){
    lcd.printString("Use Button 2",0,0);
    lcd.printString("To decrease",0,1);
    lcd.printString("Time Setting;",0,2);
    lcd.printString("Button 3",0,3);
    lcd.printString("To increase.",0,4);
    
    char bufferSt[14];  //buffer to store string
    //just a universally applicable if-else if statement to create a string out of current time setting should more time settings be added...
    if(dispInterval[dispSetting].time >= 1.0){ //if time setting is a second or more
        sprintf(bufferSt,"set:%.1f s",dispInterval[dispSetting].time);
    }
    else if(dispInterval[dispSetting].time < 1.0){ //if time setting is less than a second
        float tempTime = dispInterval[dispSetting].time * 1000.0; //convert time to milliseconds
        sprintf(bufferSt,"set:%.1f ms",tempTime);
    }    
    lcd.printString(bufferSt,0,5);
    lcd.refresh();
}

/**
Displays the Threshold Temperature Change menu. Above this threshold temperature set here a visual and audible alert will be generated.
This threshold temperature also determines the maximum value for the temperature graph menu.
*/

void displayThresholdTemp(){  //!displays the threshold temperature change sub-menu.
    lcd.printString("Use Button 1",0,0);
    lcd.printString("To decrease",0,1);
    lcd.printString("Temp Thresh;",0,2);
    lcd.printString("Button 3",0,3);
    lcd.printString("To increase.",0,4);
    
    char bufferSt[14];  //buffer to store string
    sprintf(bufferSt,"current:%.1f",tempThres[tempSetting].thresTemp);
    
    lcd.printString(bufferSt,0,5);
    lcd.refresh();
}

/**
Displays the Altitude Change Sub-Menu for the Weather Station. Determines whether altitude data will be displayed in metres, feet or yards.
*/

void displayAltitudeUnit(){  //!displays the altitude unit change sub-menu.
    lcd.printString("Use Button 1",0,0);
    lcd.printString("To decrease",0,1);
    lcd.printString("Alti. Setting",0,2);
    lcd.printString("Button 2",0,3);
    lcd.printString("To increase.",0,4);
    
    char bufferSt[14];  //buffer to store string
    sprintf(bufferSt,"current:%c",AltUnit[altUnitSetting]);
    
    lcd.printString(bufferSt,0,5);
    lcd.refresh();
}

/**
Displays the Temperature Graph Sub-Menu for the Weather Station. This Sub-Menu determines whether the temperature value will be displayed as a...
...column chart or a dot chart (just a mark at where the pressure would be located for a given point in time).
Also displays (and used to change) the lower end value for the temperature graph.
*/

void displayTempMode(){  //!temperature graph display mode
    lcd.printString("Graph mode",0,0);
    lcd.printString("for Temp.",0,1);
    lcd.printString("B2:change gr.",0,2);
    
    char bufferSt[14];  //buffer to store string
    sprintf(bufferSt,"current:%c",tempGraphMode[tempGraphSetting]);
    lcd.printString(bufferSt,0,3);
    
    lcd.printString("B3:LowThresho.",0,4);
    sprintf(bufferSt,"current:%.1f",lowerTemp[lowerTempSetting].thresTemp);
    lcd.printString(bufferSt,0,5);
    lcd.refresh();
}

/**
Displays the Pressure Graph Sub-Menu for the Weather Station. This Sub-Menu determines whether the pressure value will be displayed as a column chart or...
...a dot chart (just a mark at where the pressure would be located for a given point in time)
*/

void displayPressMode(){
    lcd.printString("Graph mode",0,0);
    lcd.printString("for Press.",0,1);
    lcd.printString("c: column ch",0,2);
    lcd.printString("d: dot chart",0,3);
    
    char bufferSt[14];  //buffer to store string
    sprintf(bufferSt,"current:%c",pressGraphMode[pressGraphSetting]);
    
    lcd.printString(bufferSt,0,5);
    lcd.refresh();
}

/**
Displays the PNought value setting sub-menu. PNought indicates pressure at sea level and changing it allows to compensate for altitude...
...at a certain weather front.
*/

void displayPNoughtMenu(){
    lcd.printString("Alter P0 val",0,0);
    lcd.printString("to compensate",0,1);
    lcd.printString("for altitude",0,2);
    lcd.printString("atWeatherfront",0,3);
    lcd.printString("CurrentValue:",0,4);
    
    char bufferSt[14];  //buffer to store string
    sprintf(bufferSt,"%.2f",PNought[PNoughtSetting]); //.2f works just as well for doubles as floats as doubles are essentially high-precision floats!
    
    lcd.printString(bufferSt,0,5);
    lcd.refresh();
}

/**
Displays the "measurement title" which is just a collection of shapes to make the measurements menu look nicer...
*/
void displayMeasurementTitle(){
    //          origin x,y,width,height,type
            lcd.drawRect(0,0,83,6,0);    // transparent, just outline
            lcd.drawCircle(10,3,2,0);  // x,y,radius,transparent with outline
            lcd.drawCircle(14,3,2,1);  // x,y,radius,black fill
            lcd.drawCircle(18,3,2,0);  // x,y,radius,transparent with outline
            lcd.drawCircle(22,3,2,1);  // x,y,radius,black fill
            lcd.drawCircle(26,3,2,0);  // x,y,radius,transparent with outline
            lcd.drawCircle(30,3,2,1);  // x,y,radius,black fill
            lcd.drawCircle(34,3,2,0);  // x,y,radius,transparent with outline
            lcd.drawCircle(38,3,2,1);  // x,y,radius,black fill
            lcd.drawCircle(34,3,2,0);  // x,y,radius,transparent with outline
            lcd.drawCircle(46,3,2,1);  // x,y,radius,black fill
            lcd.drawCircle(50,3,2,0);  // x,y,radius,transparent with outline
            lcd.drawCircle(54,3,2,1);  // x,y,radius,black fill
            lcd.drawCircle(58,3,2,0);  // x,y,radius,transparent with outline
            lcd.drawCircle(62,3,2,1);  // x,y,radius,black fill
            lcd.drawCircle(66,3,2,0);  // x,y,radius,transparent with outline
            lcd.drawCircle(70,3,2,1);  // x,y,radius,black fill
            lcd.drawCircle(74,3,2,0);  // x,y,radius,transparent with outline
            
            // x0,y0,x1,y1,type 0-white,1-black,2-dotted
            lcd.drawLine(0,0,6,6,1);
            lcd.drawLine(0,6,6,0,1);
            /*
            *           *
              *       *
                *   *
                  *          Black line with intercept; (3,3).
                *   *
              *       *
            *           *
            */
            lcd.drawLine(77,0,83,6,1);
            lcd.drawLine(77,6,83,0,1);
}

/**
Prints current time on the screen whenever the measurements menu is updated. Time is first converted into strings to be held in buffers.
@param hourBuffer
@param dateBuffer
Displays time and date on two different lines.
*/    

void printCurrentTime(char hourBuffer[14], char dateBuffer[14]){
    
    lcd.printString(hourBuffer,0,1);  //print hour:minute:second on row 1, column 0.
    lcd.printString(dateBuffer,0,2);  //print dd:mm:yyyy on row 2, column 0. 
}    

//1 bar is 100000 Pa. An atm is 101325 Pa. Therefore an mb is 100 Pa.
//http://www.cplusplus.com/reference/ctime/strftime/
//has information on time string formatting.

int main()
{

    splashFlip.attach(&splashDelay,3.0); //attach timer and wait for ISR to be called after 3 seconds.

    displayTimer.attach(&timerExpiDisplay,dispInterval[dispSetting].time); //do the same for display dispInterval[dispSetting].time

    logTimer.attach(&timerExpiLog,1.0);  //initiate log timer with an interval of a second.

    menuButton.rise(&menuButtonPressed); //event generated on rising edge (a positive spike in voltage), indicated by .rise

    buttonOne.rise(&buttonOnePressed);
    
    buttonTwo.rise(&buttonTwoPressed);
    
    buttonThree.rise(&buttonThreePressed);
    
    serial.attach(&serialISR); // attach serial ISR, which is to be used to set time.
    
    char buffer[30]; // buffer used to store time string for logging.
    char bufferTwo[14]; //buffer used to store time string for hour/minute/second display.
    char bufferThree[14]; //buffer used to store time string for day/month/year display.
    char tempBuffer[14]; //buffer used to store temperature graph display chunks.
    char pressBuffer[14]; //buffer used to store pressure graph display chunks.

    // first need to initialise display
    lcd.init();

    displayInitSplash();

    //initialise barometer
    bmp180.init();

    PHY_PowerDown();    //powers down the Ethernet feature.
    
    /*
    int result = semihost_powerdown();   //as a result, power down the USB connection
    //unless the cable is connected.
    //but this also prevents the flash memory from being overwritten when the cable is disconnected.
    //so one should uncomment this block of code only if the device is to be used with the cable attached.
    */
    Measurement measurement; // object created for pressure & temperature using the structure declared in BMP180 class

    while(1) {
        if(setTimeFlag){  //if serial interrupt is performed using a serial terminal program (e.g: CoolTerm) and serial interrupt performed (e.g: by pressing Enter)
            set_time(1430180280); // initialise time to 28th of April, 00:18:00 using this method.
            //if not used to initialise time, software will not be able to keep counting up in time, as it will reset every time the mbed is reset!
            //http://www.epochconverter.com/ is a site which converts real time into UNIX time.
            setTimeFlag = 0;  //reset flag in order to prevent it from re-setting the time in an infinite loop once this happens!
        }
        
        if(splashFlag == 0) { //if splash screen ISR has been called proceed with program flow
            splashFlip.detach();    //detach Timeout object
            lcd.clear();
            clearCells();
            lcd.refresh(); //need to refresh to write buffer on lcd
            lcd.normalMode(); //set the LCD's black-on-white standard colour layout. Especially in case it has been inverted before this can be handy.
            
            
            if(buttonThreeFlag){
                lcd.inverseMode();  //if buttonTwoFlag is set (just in the measurements menu, Button 2 has been pressed), invert colours.
            }        

            if(dispTimerFlag) {
                //read values (T in degrees Celsius and P in mb).
                displayMeasurementTitle();
                measurement = bmp180.readValues();
                temp = measurement.temperature;
                press = measurement.pressure;
                /*formula for calculating altitude from sea level by using atmospheric pressure. Unit in metres.
                Use pow(double a,double b) for indices, not the ^ sign. Just a reminder! Also check out this site:
                http://www.mide.com/products/slamstick/air-pressure-altitude-calculator.php
                */
                double PNoughtCurrent = PNought[PNoughtSetting];
                altitude = 44330.0*(1.0-(pow((press/PNoughtCurrent),(1.0/5.255)))); //pow(10,5) = 10^5
                dispTimerFlag = 0;
                
                printCurrentTime(bufferTwo,bufferThree);
                
                if(buttonOneFlag){  //if Button 1 has been pressed whilst in the measurements menu
                    lcd.clear(); //clear lcd once
                    lcd.printString("Temp Graph",0,0);  //string, column #, row #
                    sprintf(tempBuffer,"%.0f",tempThres[tempSetting].thresTemp);
                    lcd.printString(tempBuffer,0,1);  //display max. temperature value for graph. Keep in mind each string is 6 pixels wide and 8 pixels tall.
                    sprintf(tempBuffer,"%.0f",lowerTemp[lowerTempSetting].thresTemp);
                    lcd.printString(tempBuffer,0,5);   //display min. temperature value for graph.
                    int i = 18; //set the initial x-coordinate of the graph plotter.
                    while(buttonOneFlag){  //then enter infinite buttonOneFlag loop
                        if(dispTimerFlag){  //if this flag has been set for display setting (since the outer if statement will not be able to affect the while loop)
                            dispTimerFlag = 0;  //set flag to 0 and wait for it to be set again
                            measurement = bmp180.readValues();
                            temp = measurement.temperature;
                            int tempHeight = (temp/(tempThres[tempSetting].thresTemp + lowerTemp[lowerTempSetting].thresTemp))*40;  //Set graph vertical point by dividing current temperature by (threshold temperature plus lower temperature). Which is the height of the plot space.
                            if(tempGraphSetting == 0){  //column chart selected
                                // x0,y0,x1,y1,type 0-white,1-black,2-dotted
                                lcd.drawLine(i,47,i,(47-tempHeight),1);
                            }
                            else if(tempGraphSetting == 1){ //dot chart selected
                                lcd.setPixel(i,(47-tempHeight)); //x, y. 47-tempHeight gives the y point as, 47 is the bottom of the display and nominally set to the lower threshold.
                            }
                            i++;
                            if(i > 83){  //if the 84th pixel has been exceeded
                                i = 18; //set i back to 18.
                                lcd.clear();
                                lcd.printString("Temp Graph",0,0);  //string, column #, row #
                                sprintf(tempBuffer,"%.0f",tempThres[tempSetting].thresTemp);
                                lcd.printString(tempBuffer,0,1);  //display max. temperature value for graph. Keep in mind each string is 6 pixels wide and 8 pixels tall.
                                sprintf(tempBuffer,"%.0f",lowerTemp[lowerTempSetting].thresTemp);
                                lcd.printString(tempBuffer,0,5);   //display min. temperature value for graph.
                            }
                        } //terminate dispTimerFlag
                        
                        lcd.refresh();  
                    }  //break while if button is pressed again
                }  //terminate if
                
                else if(buttonTwoFlag){  //if otherwise Button 2 has been pressed whilst in the measurements menu
                    lcd.clear(); //clear lcd once
                    lcd.printString("Press Graph",0,0);  //string, column #, row #
                    lcd.printString("1.5",0,1);  //display max. pressure value (set for atm) for graph. Keep in mind each string is 6 pixels wide and 8 pixels tall.
                    lcd.printString("0",0,5);   //display min. pressure value for graph.
                    int i = 18; //set the initial x-coordinate of the graph plotter.
                    while(buttonTwoFlag){  //then enter infinite buttonOneFlag loop
                        if(dispTimerFlag){  //if this flag has been set for display setting (since the outer if statement will not be able to affect the while loop)
                            dispTimerFlag = 0;  //set flag to 0 and wait for it to be set again
                            measurement = bmp180.readValues();
                            press = measurement.pressure;  //1 atm is 1013.25 mb. 1519.875 mb equals 1.5 atm.
                            int pressHeight = (press/(1519.875))*40;  //Set graph vertical point by dividing current pressure by the plot space height. Then multiply by 40 as it is the number of pixels for the plot space.
                            if(pressGraphSetting == 0){  //column chart selected
                                // x0,y0,x1,y1,type 0-white,1-black,2-dotted
                                lcd.drawLine(i,47,i,(47-pressHeight),1);
                            }
                            else if(tempGraphSetting == 1){ //dot chart selected
                                lcd.setPixel(i,(47-pressHeight)); //x, y. 47-tempHeight gives the y point as, 47 is the bottom of the display and nominally set to the lower threshold.
                            }
                            i++;
                            if(i > 83){  //if the 84th pixel has been exceeded
                                i = 18; //set i back to 18.
                                lcd.clear();
                                lcd.printString("Temp Graph",0,0);  //string, column #, row #
                                lcd.printString("1.5",0,1);  //display max. pressure value (set for atm) for graph. Keep in mind each string is 6 pixels wide and 8 pixels tall.
                                lcd.printString("0",0,5);   //display min. pressure value for graph.
                            }
                        } //terminate dispTimerFlag
                        
                        lcd.refresh();  
                    }  //break while if button is pressed again
                }
                
                else{
                    printReadings();
                }
                lcd.refresh();
                //currentSessionArray[] = [temp,press];
                
                if(temp > tempThres[tempSetting].thresTemp){ //if temperature exceeds set threshold
                    redLED = 0.95;   //apply a PWM signal and light up the red LED
                    buzzerPwm = 0.5;   //apply a PWM signal to buzzer with 50% duty ratio to imitate a square wave.
                    buzzerPwm.period_us(500);  //period of 500 us or 0.5 ms equals a frequency of 2000 Hz.
                    greenLED = 0.0; //if green LED has been set, reset it.
                }    
                else{  //if not, light up green LED to signify that the device is operating normally.
                    greenLED = 1.0;
                    buzzerPwm = 0.0; //if PWM signal is set and threshold exceeded once, stop the buzzer until threshold is reached again.
                    redLED = 0.0; //if red LED has been lit up previously, turn it off.
                }
            } //terminate dispTimerFlag
            
            if(logTimerFlag){  //if a second has elapsed and the flag set
                logTimerFlag = 0;  //reset the flag
                logCounter++;  //increment Log Counter
                
                time_t seconds = time(NULL); // get current time
                // format time into a string (time and date)//
                

                strftime(buffer, 30 , "%R, %x", localtime(&seconds));  //%X --> Time representation in HH:MM:SS.  %D --> %m/%d/%y
                    //replace %X,%D by %c for date and time representation like "23 Aug 2001 14:55:02".
                    //add %A to show day  e.g: Thursday or lowercase for abbreviation (Thu).
                    //%R, %x gives time format as 09:06 7/8/14, for easy logging.
                    
                strftime(bufferTwo, 14 , "%X", localtime(&seconds));
                strftime(bufferThree, 14 , "%x", localtime(&seconds));
                
                printCurrentTime(bufferTwo,bufferThree);
                printReadings();
                    
                if(logCounter >= recInterval[recSetting].time){  //if counter reaches time setting or exceeds it
                    writeDataToFile(temp,press,buffer);  //write temperature, pressure and time setting on Flash Memory.
                    logCounter = 0;   //reset counter so the cycle starts again.
                }    
            }            
            
            if(menuButtonFlag == 1){  //if menu button has been pressed once
                
                lcd.clear();  //clear the lcd display
                displayMenuOne();  //display the menu strings function
                subMenuId = 0; //initially set subMenuId to zero in case it is not
                while(buttonOneAltFlag){ //while Button One is pressed AND the menu button is; 
                //(because using if will affect other settings as well, even though we're only interested in this menu while this flag has been set.)
                    if(subMenuId == 0){  //if initially the UI wasn't in a sub-menu
                        lcd.clear(); //clear lcd
                    }
                    displayTempUnit();  //display unit change menu
                    lcd.refresh();
                    //this helps avoid vertical swipe blurs and re-overwrites on the display while in sub-menu.
                    subMenuId = 1; //set sub-menu number to avoid confusions for the processor as it might change other settings!

                    if(buttonTwoAltFlag && subMenuId == 1){  //if in addition to the above conditions button 2 is pressed
                        tempUnitSetting--;  //decrease the unit setting
                        buttonTwoAltFlag = 0;  //reset flag to avoid repeated unit changes on loop
                        if(tempUnitSetting < 0)  //if it goes below 0
                            tempUnitSetting = 3; //go back to the highest value
                    }
                    if(buttonThreeAltFlag && subMenuId == 1){  //if otherwise button 3 has been pressed
                        tempUnitSetting++;  //increase temp setting
                        buttonThreeAltFlag = 0;
                        if(tempUnitSetting > 3)  //if the upper limit has been exceeded (3)
                            tempUnitSetting = 0;  //reset it to zero
                    }
                } //close button one alt
                buttonOneAltFlag = 0; //reset flag in case it is not after the while loop                                                                                                                                                                 
                lcd.clear();
                displayMenuOne();  //display the menu strings function
                subMenuId = 0; //reset sub-menu ID when loop has finished executing so it can be applied to other sub-menus.
                
                while(buttonTwoAltFlag){ //if Button Two flag is set AND the menu button is;
                    if(subMenuId == 0){
                        lcd.clear(); //clear lcd
                    }
                    displayPressUnit();  //display unit change menu
                    lcd.refresh();    
                    subMenuId = 2;
                    
                    if(buttonOneAltFlag && subMenuId == 2){  //if in  to the above conditions button 1 is pressed
                        pressUnitSetting--;  //decrease the unit setting
                        buttonOneAltFlag = 0;  //reset flag to avoid repeated unit changes on loop
                        if(pressUnitSetting < 0)  //if it goes below 0
                            pressUnitSetting = 2; //go back to the highest value
                    }
                    if(buttonThreeAltFlag && subMenuId == 2){  //if otherwise button 3 has been pressed
                        pressUnitSetting++;  //increase pressure setting
                        buttonThreeAltFlag = 0;
                        if(pressUnitSetting > 2)  //if the upper limit has been exceeded (2)
                            pressUnitSetting = 0;  //reset it to zero
                    }                                   
                } //close button two alt
                buttonTwoAltFlag = 0;
                lcd.clear();
                displayMenuOne();  //display the menu strings function
                subMenuId = 0;
                while(buttonThreeAltFlag){ //while Button Three flag is set AND the menu button is;
                    if(subMenuId == 0){
                        lcd.clear(); //clear lcd
                    }
                    displayLogMenu();  //display log interval change menu
                    lcd.refresh();  //refresh to write buffer on display.   
                    subMenuId = 3;
                    
                    if(buttonOneAltFlag && subMenuId == 3){  //if added to the above conditions button 1 is pressed
                        lcd.clear();   //clear lcd
                        recSetting--;  //decrease the unit setting
                        buttonOneAltFlag = 0;  //reset flag to avoid repeated unit changes on loop
                        if(recSetting < 0){  //if it goes below 0
                            recSetting = 9; //go back to the highest value
                        }
                        displayLogMenu();  //overwrite the display interval menu to avoid having "ms" and "s" on the same menu as ms might remain even if the setting has been altered!
                    }
                    if(buttonTwoAltFlag && subMenuId == 3){  //if otherwise button 3 has been pressed
                        lcd.clear();   //clear lcd
                        recSetting++;  //increase pressure setting
                        buttonTwoAltFlag = 0;
                        if(recSetting > 9){  //if the upper limit has been exceeded (2)
                            recSetting = 0;  //reset it to zero
                        }
                        displayLogMenu();  //overwrite the display interval menu to avoid having "ms" and "s" on the same menu as ms might remain even if the setting has been altered!    
                    }
                } //close button two alt
                buttonThreeAltFlag = 0;
                lcd.clear();
                displayMenuOne();  //display the menu strings function
                subMenuId = 0;
            } //close menu button flag
            
            subMenuId = 0; //reset sub-menu ID after going through first menu in case it is not already
            if(menuButtonFlag == 2){  //if menu button has been pressed twice
                
                lcd.clear();  //clear the lcd display
                displayMenuTwo();  //display the menu strings function
                while(buttonOneAltFlag){ //if Button One is pressed AND the menu button is;
                    if(subMenuId == 0){
                        lcd.clear(); //clear lcd
                    }
                    displayDispMenu();  //display unit change menu
                    lcd.refresh();
                    subMenuId = 4;
                    lcd.refresh();
                    if(buttonTwoAltFlag && subMenuId == 4){  //if added to the above conditions button 2 is pressed
                        lcd.clear();   //clear lcd
                        dispSetting--;  //decrease setting
                        buttonTwoAltFlag = 0;  //reset flag to avoid repeated unit changes on loop
                        if(dispSetting < 0){  //if it goes below 0
                            dispSetting = 5; //go back to the highest value
                        }
                        displayDispMenu();  //overwrite the display interval menu to avoid having "ms" and "s" on the same menu as ms might remain even if the setting has been altered!
                        displayTimer.detach();  //detach the timer to disable it to overwrite the new setting
                        displayTimer.attach(&timerExpiDisplay,dispInterval[dispSetting].time); //re-attach the timer.    
                    }
                    if(buttonThreeAltFlag && subMenuId == 4){  //if otherwise button 3 has been pressed
                        lcd.clear();   //clear lcd
                        dispSetting++;  //increase setting
                        buttonThreeAltFlag = 0;
                        if(dispSetting > 5){  //if the upper limit has been exceeded (3)
                            dispSetting = 0;  //reset it to zero
                        }
                        displayDispMenu();  //overwrite the display interval menu to avoid having "ms" and "s" on the same menu as ms might remain even if the setting has been altered!
                        displayTimer.detach();  //detach the timer to disable it to overwrite the new setting
                        displayTimer.attach(&timerExpiDisplay,dispInterval[dispSetting].time); //re-attach the timer.    
                    }
                } //close button one alt
                buttonOneAltFlag = 0;
                lcd.clear();  //clear the lcd display
                displayMenuTwo();  //display the menu strings function
                subMenuId = 0;
                
                while(buttonTwoAltFlag){ //if Button Two flag is set AND the menu button is;
                    if(subMenuId == 0){
                        lcd.clear(); //clear lcd
                    }
                    displayThresholdTemp();  //display unit change menu
                    lcd.refresh();
                    subMenuId = 5;
                    if(buttonOneAltFlag && subMenuId == 5){  //if added to the above conditions button 1 is pressed
                        tempSetting--;  //decrease setting
                        buttonOneAltFlag = 0;  //reset flag to avoid repeated unit changes on loop
                        if(tempSetting < 0)  //if it goes below 0
                            tempSetting = 4; //go back to the highest value
                    }
                    if(buttonThreeAltFlag && subMenuId == 5){  //if otherwise button 3 has been pressed
                        tempSetting++;  //increase setting
                        buttonThreeAltFlag = 0;
                        if(tempSetting > 4)  //if the upper limit has been exceeded (2)
                            tempSetting = 0;  //reset it to zero
                    }
                } //close button two alt
                buttonTwoAltFlag = 0;
                lcd.clear();  //clear the lcd display
                displayMenuTwo();  //display the menu strings function
                subMenuId = 0;
                
                while(buttonThreeAltFlag){ //if Button Three flag is set AND the menu button is;
                    if(subMenuId == 0){
                        lcd.clear(); //clear lcd
                    }
                    displayAltitudeUnit();  //display unit change menu
                    lcd.refresh();
                    subMenuId = 6;
                    if(buttonOneAltFlag && subMenuId == 6){  //if added to the above conditions button 1 is pressed
                        altUnitSetting--;  //decrease the unit setting
                        buttonOneAltFlag = 0;  //reset flag to avoid repeated unit changes on loop
                        if(altUnitSetting < 0)  //if it goes below 0
                            altUnitSetting = 2; //go back to the highest value
                    }
                    if(buttonTwoAltFlag && subMenuId == 6){  //if otherwise button 2 has been pressed
                        altUnitSetting++;  //increase pressure setting
                        buttonTwoAltFlag = 0;
                        if(altUnitSetting > 2)  //if the upper limit has been exceeded (2)
                            altUnitSetting = 0;  //reset it to zero
                    }
                } //close button three alt
                buttonThreeAltFlag = 0;
                lcd.clear();  //clear the lcd display
                displayMenuTwo();  //display the menu strings function
                subMenuId = 0;
            } //close menu button flag
            
            subMenuId = 0;
            if(menuButtonFlag == 3){  //if menu button has been pressed twice
                
                lcd.clear();  //clear the lcd display
                displayMenuThree();  //display the menu strings function
                while(buttonOneAltFlag){ //while Button One is pressed AND the menu button is;
                    if(subMenuId == 0){
                        lcd.clear(); //clear lcd
                    }
                    displayTempMode();  //display temperature graph model change menu
                    lcd.refresh();
                    subMenuId = 7;
                    if(buttonTwoAltFlag && subMenuId == 7){  //if added to the above conditions button 2 is pressed
                        lcd.clear();   //clear lcd
                        tempGraphSetting = !tempGraphSetting;  //change setting to its opposite (if 1, reset; if 0, set to 1). Increasing doesn't help when the setting can only have two settings as it can pop up with unintended characters before the flag resets!
                        buttonTwoAltFlag = 0;  //reset flag to avoid repeated unit changes on loop
                        displayTempMode();  //overwrite the temperature graph menu    
                    }
                    if(buttonThreeAltFlag && subMenuId == 7){  //if otherwise button 3 has been pressed
                        lcd.clear();   //clear lcd
                        lowerTempSetting++;  //increase setting
                        buttonThreeAltFlag = 0;
                        if(lowerTempSetting > 4){  //if the upper limit has been exceeded (3)
                            lowerTempSetting = 0;  //reset it to zero
                        }
                        displayTempMode();  //overwrite the temperature graph menu    
                    }
                } //close button one alt
                buttonOneAltFlag = 0;
                lcd.clear();  //clear the lcd display
                displayMenuThree();  //display the menu strings function
                subMenuId = 0;
                
                while(buttonTwoAltFlag){ //if Button Two flag is set AND the menu button is;
                    if(logTimerFlag){
                        lcd.clear(); //clear lcd
                        displayPressMode();  //display unit change menu
                        lcd.refresh();
                        logTimerFlag = 0;
                    }
                    subMenuId = 8;
                    if(buttonOneAltFlag && subMenuId == 8){  //if added to the above conditions button 1 is pressed
                        pressGraphSetting = !pressGraphSetting;  //invert the setting so it becomes a 1 when it previously used to be a 0 and vice versa.
                        buttonOneAltFlag = 0;  //reset flag to avoid repeated unit changes on loop
                    }
                    //no ButtonThree option here as it has no use... or is there not?
                    while(buttonThreeAltFlag && subMenuId == 8){  //if added to the above conditions button 1 is pressed
                        if(logTimerFlag){
                            lcd.clear();
                            lcd.printString("*Easter Egg*",0,0);
                            lcd.printString("No Age of",0,1);
                            lcd.printString("Empires puns",0,2);
                            lcd.printString("To be Found",0,3);
                            lcd.printString("Here!",0,4);
                            lcd.printString("B3 to go back",0,5);
                            lcd.refresh();
                            logTimerFlag = 0;
                        }
                    }
                } //close button two alt
                buttonTwoAltFlag = 0;
                lcd.clear();  //clear the lcd display
                displayMenuThree();  //display the menu strings function
                subMenuId = 0;
                
                while(buttonThreeAltFlag){ //if Button Three flag is set AND the menu button is;
                    if(subMenuId == 0){
                        lcd.clear(); //clear lcd
                    }
                    displayPNoughtMenu();  //display Altitude Compensation change menu
                    lcd.refresh();
                    subMenuId = 9;
                    if(buttonOneAltFlag && subMenuId == 9){  //if added to the above conditions button 1 is pressed
                        PNoughtSetting--;  //decrease the unit setting
                        buttonOneAltFlag = 0;  //reset flag to avoid repeated unit changes on loop
                        if(PNoughtSetting < 0)  //if it goes below 0
                            PNoughtSetting = 12; //go back to the highest value
                    }
                    if(buttonTwoAltFlag && subMenuId == 9){  //if otherwise button 2 has been pressed
                        PNoughtSetting++;  //increase pressure setting
                        buttonTwoAltFlag = 0;
                        if(PNoughtSetting > 12)  //if the upper limit has been exceeded (12)
                            PNoughtSetting = 0;  //reset it to zero
                    }
                } //close button three alt
                buttonThreeAltFlag = 0;
                lcd.clear();  //clear the lcd display
                displayMenuThree();  //display the menu strings function
                subMenuId = 0;
            } //close menu button flag


            Sleep();  //put the mbed to sleep once the program flow has been completed.
            lcd.setBrightness(potAin.read()); //read potentiometer ratio and set display brightness according to it. Function inside a function...


        } //close if
    } //close while
} //terminate main()

/**
Fetches readings from the sensor via the main() function and prints them on the display.
Or converts them into their respective units depending on temperature/pressure unit settings.
@param temp - temperature reading from the BMP180(˚C).
@param press - pressure reading from the BMP180(mb).
@param altitude - altitude calculated from the pressure reading (m).
*/

void printReadings()
{
    char buffer[14];  // each character is 6 pixels wide, screen is 84 pixels (84/6 = 14)
    // so can display a string of a maximum 14 characters in length
    // or create formatted strings - ensure they aren't more than 14 characters long
    
    if(tempUnitSetting == 0){ //if Celsius has been selected as the temperature unit
        int length = sprintf(buffer,"T = %.2f 'C",temp); // print formatted data to buffer
    // it is important the format specifier ensures the length will fit in the buffer
        if (length <= 14) { // if string will fit on display
            lcd.printString(buffer,0,3);  // display on screen. Column 0, row 3.
        }
    } //close unit setting 0
    else if(tempUnitSetting == 1){ //Fahrenheit
        float tempTemp = (temp*1.8) + 32;
        int length = sprintf(buffer,"T = %.2f 'F",tempTemp); // print formatted data to buffer
    // it is important the format specifier ensures the length will fit in the buffer
        if (length <= 14) { // if string will fit on display
            lcd.printString(buffer,0,3);  // display on screen. Column 0, row 3.
        }
    } //close unit setting 1
    else if(tempUnitSetting == 2){ //Kelvin
        float tempTemp = temp + 273.15;
        int length = sprintf(buffer,"T = %.1f K",tempTemp); // print formatted data to buffer
    // it is important the format specifier ensures the length will fit in the buffer
        if (length <= 14) { // if string will fit on display
            lcd.printString(buffer,0,3);  // display on screen. Column 0, row 3.
        }
    } //close unit setting 2
    
    else if(tempUnitSetting == 3){ //Rankine
        float tempTemp = (temp + 273.15)*1.8;
        int length = sprintf(buffer,"T = %.1f 'Ra",tempTemp); // print formatted data to buffer
    // it is important the format specifier ensures the length will fit in the buffer
        if (length <= 14) { // if string will fit on display
            lcd.printString(buffer,0,3);  // display on screen. Column 0, row 3.
        }
    } //close unit setting 3
    
    if(pressUnitSetting == 0){ //if pressure is to be displayed in mb
        int length = sprintf(buffer,"P = %.2f mb",press); //use single letters to represent parameters or string may not fit in the banks!
        if (length <= 14) {
            lcd.printString(buffer,0,4); // Column 0, row 4.
        }
    } //close unit setting 0
    
    else if(pressUnitSetting == 1){ //Pa
        float tempPress = press*100; //convert from mb to Pa
        int length = sprintf(buffer,"P = %.0f Pa",tempPress); //use single letters to represent parameters or string may not fit in the banks!
        if (length <= 14) {
            lcd.printString(buffer,0,4); // Column 0, row 4.
        }
    } //close unit setting 1
    else if(pressUnitSetting == 2){ //atm
        float tempPress = press/1013.25; //an atm is 1013.25 mb; therefore dividing press by that gives pressure in atm.
        int length = sprintf(buffer,"P = %.4f atm",tempPress); //use single letters to represent parameters or string may not fit in the banks!
        if (length <= 14) {
            lcd.printString(buffer,0,4); // Column 0, row 4.
        }
    } //close unit setting 2
    
    //Now for the altitude display settings. Bear in mind that a metre is 3.2808399 feet; or 1.0936133 yards. Three feet equals a yard.
    if(altUnitSetting == 0){ //if metres have been selected
        int length = sprintf(buffer,"A = %.1f m",altitude);
        if (length <= 14) {
            lcd.printString(buffer,0,5);  //Column 0, row 3.
        }
    } // close unit setting 0
    else if(altUnitSetting == 1){ //feet
        float tempAlt = altitude*3.2808399; //convert to feet
        int length = sprintf(buffer,"A = %.1f ft",tempAlt);
        if (length <= 14) {
            lcd.printString(buffer,0,5);  //Column 0, row 5.
        }
    } //close unit setting 1
    else if(altUnitSetting == 2){ //yards
        float tempAlt = altitude*1.0936133; //convert to yards
        int length = sprintf(buffer,"A = %.1f yd",tempAlt);
        if (length <= 14) {
            lcd.printString(buffer,0,5);  //Column 0, row 5.
        }
    } //close unit setting 2
}

/**
Function to "kill" all cells after looping through i-j coordinates. Used as a back-up to lcd.clear() should that fail.
Void type, so it returns no values.
*/
void clearCells()
{
    //loop through cells, and clear them
    for (int i = 0; i < 83 ; i++) {
        for (int j = 0; j < 47; j++) {
            lcd.clearPixel(i,j);         //function to "kill" all cells after looping through.
        }
    }
    lcd.refresh(); //must refresh to write buffer to display
}