'Sensor-Matic!' ELEC2645 Jack Berriman - 200836573

Dependencies:   N5110-JDB SRF02 SoftPWM-JDB TMP102 mbed

main.cpp

Committer:
ll13jdb
Date:
2016-05-05
Revision:
0:e8ca5d36a1e7

File content as of revision 0:e8ca5d36a1e7:

/*

ELEC2645 - Embedded Systems Project - "Sensor-matic!"

Distance and Temperature Sensors Displayed on Nokia 5110 Screen


Week 19 - Testing SRF02 Distance Sensor

Week 20 - Creating Splash Screens

Week 21 - Diagrams for Temperature and Distance

Easter - Navigation Between Menus

Week 22 - Piezo/LEDs Interfaced

Week 23 - DOxygen Commenting

Week 24 - Cleaning Up & Submission


(c) Jack Berriman, University of Leeds, Summer 2016

*/

/**
@file main.cpp
@brief Program Implementation
*/


#include "main.h"


// ------------------------- MAIN FUNCTION ------------------------- //

int main() // Main Function
{

    init_K64F(); // Initialise FRDM K64F Micrcontroller
    init_buzzer(); // Initialise Piezo Buzzer
    init_program(); // Initialise Other Program Requirements (Screen/Ticker/Baud Rate)

    welcomeScreen(); // Display Welcome Screen
    screen.clear(); // Clear Screen

    while (1) { // Infinite Program While Loop

        if (screenNumber == 0) { // Main Menu = 0
            mainMenu();
        } else if (screenNumber == 1) { // Sensor Menu = 1
            sensorMenu();
        } else if (screenNumber == 2) { // Options Menu = 2
            optionsMenu();
        } else if (screenNumber == 3) { // Distance Display = 3
            distanceDisplay();
        } else { // Temperature Display = 4
            temperatureDisplay();
        }

        screen.refresh(); // Refresh Screen AFTER Each Iteration
        sleep(); // Put Peripherals To Sleep (Due to InterruptIn)

    }
}


// ------------------------- INITIALISE FUNCTIONS ------------------------- //

void clearAll() // Clears Current Buffer (Text/Pixels) -- ADAPTED FROM ELEC1620 (GAME OF LIFE PROJECT)
{
    for (int xPixel = 0; xPixel < 84; xPixel++) { // Loops Through X-Pixels
        for (int yPixel = 0; yPixel < 48; yPixel++) { // Loops Through Y-Pixels
            screen.clearPixel(xPixel,yPixel); // Clears
        }
    }
    screen.refresh(); // Refreshes Screen AFTER Clearing
}


void init_K64F() // Initialise K64F Function
{
    // On-Board LEDs are Active-Low
    r_led = 1;
    g_led = 1; // Set to 1 (High) to Turn OFF
    b_led = 1;

    allLEDOff(); // Turn off All LEDs

    forward.rise(&forward_isr); // Interrupts Fire on Rising Clock Edge
    backward.rise(&backward_isr);

    forward.mode(PullDown); // Push Buttons are Connected to +3V3 - Pull-Down to Ground when Pressed
    backward.mode(PullDown);
}


void init_buzzer() // Initialise Piezo Buzzer Function
{
    piezo.period(1.0/2000.0); // Set Frequency to 2 kHz
    piezo.write(0.0); // Turn OFF (Duty Cycle = 0)
}


void init_program() // Initialise Other Program Requirements
{
    screen.init(); // Initialise Screen
    screen.clear(); // Clear Buffer
    systemTicker.attach(&system_isr,0.5); // Attach Ticker
    pc.baud(115200); // Set Serial Port Baud Rate to 115200 Hz
}


void welcomeScreen()    // Prints Welcome Screen on Start-Up
{

    // printString is a function from N5110.h
    screen.printString("ELEC2645", 20, 0); // Format: screen.printString("Text", Number of Pixels 'in' (X-Position), Bank Number (Y-Position))
    screen.printString("Sensor-matic!", 5, 2); // Project Title
    screen.printString("By", 35, 4);
    screen.printString("Jack Berriman", 3, 5);

    wait(2);    // Delay (To Read Text)

}


// ------------------------- STRUCTS ------------------------- //

AverageValues getAverageValues() // Struct for Calculating Average Values of Distance and Temperature
{
    AverageValues values; // Access Struct
    int temporaryDistanceSum = 0;
    int temporaryTemperatureSum = 0; // Initialise Temporary 'Sum' Variables Equal to Zero

    for (int i = 0; i <= 10; i++) { // Loop Through 10 Samples

        int distanceRead = srf02.getDistanceCm(); // Each Iteration: Get Distance and Write to Variable 'distanceRead'
        temporaryDistanceSum += distanceRead; // Add to Sum

        int temperatureRead = tmp102.get_temperature(); // Each Iteration: Get Temperature and Write to Variable 'distanceRead'
        temporaryTemperatureSum += temperatureRead; // Add to Sum
    }

    values.averageDistance = temporaryDistanceSum/10; // Divide by Number of Samples (10)
    values.averageTemperature = temporaryTemperatureSum/10;

    return values; // Return 'values' to Struct
}


// ------------------------- CURSOR POSITION (POTENTIOMETER) FUNCTIONS ------------------------- //

int getCursorPosition() // Read Potentiometer Value & Convert to Bank Value
{
    int bank = floor(controller * 5) + 1; // Read Potentioementer & Multiply by 5
    // 'Floor' = Rounds Down

    if (bank == 6) { // If Bank value us greater that 5...
        bank = 5;  // A value of 5 is returned
    }

    return bank; // Return Bank Value
}


void printCursorPositionMain(int cursor) // Print Cursor as Rectangle Next to Option in Main/Sensor Menu (Used with 'getCursorPosition')
{
    // Prevents Cursor Position From Being Unselectable
    if (cursor != 1 && cursor != 3 && cursor != 5) { // If Cursor in Banks 2 or 4 - Print Rectangle

        y_axis_rect_main = (cursor * 8) + 3; // Multiply Cursor by 8 to Return Y-Position & Add 3 to Align in Bank

        screen.drawRect(75,y_axis_rect_main,2,2,0); // Print Rectangle (Depending on Potentiometer/Cursor)
    } else {
        screen.drawRect(75,y_axis_rect_main,2,2,0); // If Bank is 1, 3 or 5 - Still Print Rectangle
    }
}


void printCursorPositionOptions(int cursor, int stateOfLED, int stateOfSound) // Print Cursor as Rectangle Next to Option in Options Menu (Used with 'getCursorPosition')
{
    // stateOfLed & stateOfSound Return 1 or 0
    // 1 or 0 Passed into 'drawRect' - Fills or Unfills Rectangle

    y_axis_rect_options = (cursor * 8) + 3; // Multiply Cursor by 8 to Return Y-Position & Add 3 to Align in Bank
    cursor == 1 ? screen.drawRect(81,y_axis_rect_options,2,2,stateOfLED):screen.drawRect(81,y_axis_rect_options,2,2,stateOfSound);
    // If Cursor is 1 = Draw Rectangle OR If Cursor is 3 = Draw Rectangle
}


// ------------------------- MENU FUNCTIONS (MAIN/SENSORS/OPTIONS ------------------------- //

void mainMenu() // Function for Main Menu
{
    y_axis_rect_main = 19; // Set Initial Y-Position to 19th Pixel
    
    while (screenNumber == 0) { // While Main Menu Screen On...

        int cursor = getCursorPosition(); // Read Position of Cursor

        if (g_system_flag) {
            g_system_flag = 0; // Reset System Flag to 0

            clearAll();   // Clears Previous Text String (Welcome Screen)

            screen.printString("MAIN MENU", 16, 0); // Print String 'Main Menu'
            screen.drawLine(5,11,79,11,1); // Draw Line Above Sensors String
            screen.printString("Sensors", 3, 2); // Print String 'Sensors'
            screen.printString("Options", 3, 4); // Print String 'Options'
            screen.drawLine(5,44,79,44,1); // Draw Line Underneath Options String

            // Used to Ensure Cursor is Set in Bank
            if (cursor <= 2) { // If Position of Cursor is 2 or Less
                cursor = 2; // Set Cursor to Equal 2
            }
            if (cursor >= 3) { // If Position of Cursor is 3 or Greater
                cursor = 4; // Set Cursor to Equal 4
            }

            printCursorPositionMain(cursor); // Print Cursor to Screen
            pc.printf("Controller = %d \n",cursor); // Debug in 'CoolTerm.exe'

            // Check Flag IF 'forward' Button Interrupt Pressed
            if (g_forward_flag == 1) {
                g_forward_flag = 0;  // Clear Flag

                // Determine Next Screen
                if (cursor == 2) {
                    screenNumber = 1; // Go to Sensor Menu
                }
                if (cursor == 4) {
                    screenNumber = 2; // Go to Options Menu
                }
            }
        }
        returnToMenu(); // Back Button Function
        screen.refresh(); // Refresh Screen
    }
}

void sensorMenu() // Function for Sensor Menu
{
    while (screenNumber == 1) { // While Sensor Menu is On...

        int cursor = getCursorPosition(); // Read Position of Cursor

        if (g_system_flag) {
            g_system_flag = 0; // Reset System Flag to 0

            clearAll(); // Clears Previous Text String (Main Menu Screen)

            screen.printString("SENSOR MENU", 10, 0); // Prints String 'SensorMenu'
            screen.drawLine(5,11,79,11,1); // Draw Line Above Dist Sensor
            screen.printString("Dist Sensor", 3, 2); // Prints String 'Dist Sensor'
            screen.printString("Temp Sensor", 3, 4); // Prints String 'Temper Sensor'
            screen.drawLine(5,44,79,44,1); // Draw Line Below Temp Sensor

            // Used to Ensure Cursor is Set in Bank
            if (cursor <= 2) { // If Position of Cursor is 2 or Less
                cursor = 2; // Set Cursor to Equal 2
            }
            if (cursor >= 3) { // If Position of Cursor is 3 or Greater
                cursor = 4; // Set Cursor to Equal 4
            }

            printCursorPositionMain(cursor); // Print Cursor to Screen
            pc.printf("Controller = %d \n",cursor); // Debug in 'CoolTerm.exe'

            // Check Flag IF 'forward' Button Interrupt Pressed
            if (g_forward_flag == 1) {
                g_forward_flag = 0;  // Clear Flag

                // Determine Next Screen
                if (cursor == 2) {
                    screenNumber = 3; // Go to Distance Display
                }
                if (cursor == 4) {
                    screenNumber = 4; // Go to Temperature Display
                }
            }
        }
        returnToMenu(); // Back Button Function
        screen.refresh(); // Refresh Screen
    }
}

void optionsMenu() // Function for Options Menu
{
    y_axis_rect_options = 27; // Set Initial Y-Position to 27th Pixel

    while (screenNumber == 2) { // While Options Menu is On...

        int cursor = getCursorPosition(); // Read Position of Cursor

        if (g_system_flag) {
            g_system_flag = 0; // Reset System Flag to 0

            clearAll();   // Clears Previous Text String (Main Menu Screen)

            screen.printString("Toggle LEDs", 1, 1); // Print String 'Toggle LEDs'
            screen.printString("Toggle Sound", 1, 3); // Print String 'Toggle Sound'
            screen.printString("Toggle Colour", 1, 5); // Print String 'Toggle Colour'

            // Used to Ensure Cursor is Set in Bank
            if (cursor <= 2) { // If Position of Cursor is 2 or Less
                cursor = 1; // Set Cursor to Equal 1
            }
            if (cursor > 2 && cursor <= 4) { // If Position of Cursor is Between 2 or 4
                cursor = 3; // Set Cursor to Equal 3
            }
            if (cursor > 4) { // If Position of Cursor is Greater than 4
                cursor = 5; // Set Cursor to Equal 5
            }

            if (triggerLEDs == 1) { // Read Trigger of LED
                allLEDOn(); // If Trigger = 1, Turn ON LEDs
            }

            printCursorPositionOptions(cursor, stateOfLED, stateOfSound); // Print Position of Cursor

            // Check Flag IF 'forward' Button Interrupt Pressed
            if (g_forward_flag == 1) {
                g_forward_flag = 0;  // Clear Flag

                // Determine Options to Edit
                if (cursor == 1) {
                    stateOfLED = toggleLEDs(); // Toggle LEDs & Write 1 or 0 to 'stateOfLED'
                }
                if (cursor == 3) {
                    stateOfSound = toggleSound(); // Toggle Sound & Write 1 or 0 to 'stateOfLED'
                }
                if (cursor == 5) {
                    toggleColour(); // Toggle Colour
                }
            }
        }
        returnToMenu(); // Back Button Function
        screen.refresh(); // Refresh Screen
    }
}

void distanceDisplay() // Function to Display Distance
{
    AverageValues values; // Call Struct For Average Values

    while(screenNumber == 3) { // While Temperature Display is On...

        values = getAverageValues();
        int Distance = values.averageDistance; // Access Struct Value

        if (g_system_flag) {
            g_system_flag = 0; // Reset System Flag to 0

            clearAll();

            if (triggerLEDs == 1) {
                LEDs_Distance(Distance); // Call Distance LED Function
            }

            char buffer[14];  // Each Character = 6 pixels wide & Screen = 84 pixels (84/6 = 14)
            int length = sprintf(buffer,"Dist = %d CM",Distance); // Print Distance Value to Buffer
            pc.printf("DistAv = %f \n DistNorm = %f \n", Distance, srf02.getDistanceCm()); // Debug in 'CoolTerm.exe'

            if (length <= 14)  // If String Fits on Display
                screen.printString(buffer,3,2); // Display on Screen

            if (Distance <= 14 || Distance >= 250) { // If Sensor is Out of Range
                clearAll(); // Clear Buffer
                screen.drawLine(4,2,80,2,2); // Draw Dotted Line Above Text
                screen.printString("DISTANCE OUT", 6, 1);
                screen.printString("OF RANGE", 18, 2);
                screen.drawLine(4,27,80,27,2); // Draw Dotted Line Below Text
                screen.printString("MIN = 15 CM", 10, 4);
                screen.printString("MAX = 250 CM", 7, 5);

                if (triggerSound == 1) { // Play Continuous Buzz When Out of Range
                    buzzerOn();
                }
            } else { // If Sensor is In Range

                screen.printString("DIST SENSOR", 10, 0); // Print String 'Distance Sensor' (Title)

                screen.drawRect(5,26,74,12,0); // Draw Rectangle Outline
                int fillLength = floor(PIXEL_PER_CM * Distance); // Update Fill Length Depending on Distance Value

                screen.drawRect(6,27,fillLength,11,1); // Fill the Distance Bar (Left to Right)

                screen.drawRect(17,26,2,12,0); // Puts in Dividers at 50 cm Intervals
                screen.drawRect(33,26,2,12,0);
                screen.drawRect(48,26,2,12,0);
                screen.drawRect(63,26,2,12,0);

                screen.printString("15",0,5); // Prints Lower Bound Marker (15 cm)
                screen.printString("250",67,5); // Prints Upper Bound Marker (250 cm)

                if (Distance <= 50) {
                    if (triggerSound == 1) { // Check Trigger Value of Buzzer
                        buzzerClose(); // Play Highly Freuquent Buzz
                    }
                } else if (Distance <= 100) {
                    if (triggerSound == 1) { // Check Trigger Value of Buzzer
                        buzzerNormal(); // Play Moderately Freuquent Buzz
                    }
                } else if (Distance <= 200) {
                    if (triggerSound == 1) { // Check Trigger Value of Buzzer
                        buzzerFar(); // Play Lowly Freuquent Buzz
                    }
                }
            }


            returnToMenu(); // Function for Back Button
            screen.refresh(); // Refreshes Screen
        }
    }
}

void temperatureDisplay() // Function to Display Temperature
{
    AverageValues values; // Call Struct For Average Values

    while(screenNumber == 4) { // While Temperature Display is On...

        values = getAverageValues();
        int Temperature = values.averageTemperature; // Access Struct Value

        if (g_system_flag) {
            g_system_flag = 0; // Reset System Flag to 0

            clearAll(); // Clears Previous Text String (Sensor Menu Screen)

            screen.printString("TEMP SENSOR", 10, 0); // Print String 'Temp Sensor' (Title)

            LEDs_Temperature(Temperature); // Call Temperature LED Function

            char buffer[14];  // Each Character = 6 pixels wide & Screen = 84 pixels (84/6 = 14)
            int length = sprintf(buffer,"Temp = %d -C",Temperature); // Print Temperature Value to Buffer
            pc.printf("TempAv = %f \n TempNorm = %f \n", Temperature, tmp102.get_temperature()); // Debug in 'CoolTerm.exe'

            if (length <= 14)  // If String Fits on Display
                screen.printString(buffer,6,2); // Display on Screen

            screen.drawCircle(15,36,7,1); // Circular End of Thermometer
            screen.drawRect(22,32,6,8,1); // Start of Thermometer
            screen.drawRect(26,32,48,8,0); // Outline of the Thermometer

            int fillLength = floor(PIXEL_PER_DEGREE * Temperature); // Update Fill Length Depending on Thermometer Value

            screen.drawRect(27,33,fillLength,6,1); // Fill the Thermometer (Left to Right)
            screen.refresh(); // Refresh Screen
        }
        returnToMenu(); // Function for Back Button
    }
}


// ------------------------- OPTIONS MENU (TOGGLE) FUNCTIONS ------------------------- //

bool toggleLEDs() // Toggles LED (Enable/Disable)
{
    if (triggerLEDs == 0) {
        allLEDOn(); // Turn ON LEDs (While on Options Screen)
        triggerLEDs = 1; // 'Flip' Trigger
    } else {
        allLEDOff(); // Turn OFF LEDs (While on Options Screen
        triggerLEDs = 0; // 'Flip' Trigger
    }

    bool stateOfLED = triggerLEDs; // Set Trigger Value (1 or 0) into Bool
    return stateOfLED; // Return Bool (Used in Printing Rectangle)
}


bool toggleSound() // Toggles Sound (Enable/Disable)
{
    if (triggerSound == 0) {
        piezo.write(0.5); // Play Bleep Noise
        wait(0.1);
        piezo.write(0.0);
        triggerSound = 1; // 'Flip' Trigger
    } else {
        triggerSound = 0; // 'Flip' Trigger
    }

    bool stateOfSound = triggerSound; // Set Trigger Value (1 or 0) into Bool
    return stateOfSound; // Return Bool (Used in Printing Rectangle)
}


void toggleColour() // Toggles Colour (Black-on-White/White-on-Black)
{
    if (triggerColour == 0) {
        screen.inverseMode(); // Set Colour to Inverse Mode (White-on-Black)
        triggerColour = 1; // 'Flip' Trigger
    } else {
        screen.normalMode(); // Set Colour to Normal Mode (Black-on-White)
        triggerColour = 0; // 'Flip' Trigger
    }
}


// ------------------------- LED & SOFT-PWM FUNCTIONS ------------------------- //

void allLEDOn() // Function to Turn ON All LEDs
{
    red_led = 0;
    yellow_led = 0;
    green_led = 0;
}


void allLEDOff() // Function to Turn OFF All LEDs
{
    red_led = 1;
    yellow_led = 1;
    green_led = 1;
}


void LEDs_Flashing()
{
    redPWM = 0.5; // Turn ON Soft PWM LEDs
    yellowPWM = 0.5;
    greenPWM = 0.5;

    redPWM.period(0.25); // Set Period = 0.25 seconds
    yellowPWM.period(0.25);
    greenPWM.period(0.25);
}


void cancel_LEDs_Flashing()
{
    redPWM = 0; // Turn OFF Soft PWM LEDs
    yellowPWM = 0;
    greenPWM = 0;
    
    redPWM.stop(); // Stop Soft PWM
    yellowPWM.stop();
    greenPWM.stop();
}


void LEDs_Distance(int d)   // LEDs Illuminate (Depending on Distance)
{

    if (d < 15 || d > 250) { // All ON
        LEDs_Flashing(); // Enable Soft PWM Flashing LEDs
    } else if (d >= 15 && d < 50) { // Red ON
        cancel_LEDs_Flashing(); // Prevent Soft PWM Overwritting Digital LEDs
        red_led = 0;
        yellow_led = 1;
        green_led = 1;
    } else if (d >= 50 && d < 100) { // Red & Yellow ON
        cancel_LEDs_Flashing();
        red_led = 0;
        yellow_led = 0;
        green_led = 1;
    } else if (d >= 100 && d < 150) { // Yellow ON
        cancel_LEDs_Flashing();
        red_led = 1;
        yellow_led = 0;
        green_led = 1;
    } else if (d >= 150 && d < 200) { // Yellow & Green ON
        cancel_LEDs_Flashing();
        red_led = 1;
        yellow_led = 0;
        green_led = 0;
    } else if (d >= 200 && d < 250) { // Green ON
        cancel_LEDs_Flashing();
        red_led = 1;
        yellow_led = 1;
        green_led = 0;
    }
}


void LEDs_Temperature(int t)   // LEDs Illuminate (Depending on Temperature)
{

    if (t < 10 || t >= 40) { // All ON
        allLEDOn();
    } else if (t >= 10 && t < 15) { // Red ON
        red_led = 0;
        yellow_led = 1;
        green_led = 1;
    } else if (t >= 15 && t < 20) { // Red & Yellow ON
        red_led = 0;
        yellow_led = 0;
        green_led = 1;
    } else if (t >= 20 && t < 25) { // Green ON
        red_led = 0;
        yellow_led = 0;
        green_led = 1;
    } else if (t >= 25 && t < 30) { // Red & Yellow ON
        red_led = 0;
        yellow_led = 0;
        green_led = 1;
    } else if (t >= 30 && t < 40) { // Red ON
        red_led = 0;
        yellow_led = 1;
        green_led = 1;
    }
}


// ------------------------- BUZZER FUNCTIONS ------------------------- //

void buzzerOn() // Buzz Continuously
{
    piezo.write(0.5); // Set Duty Cycle - 50%
    wait_ms(10); // Wait 10 Milli-Seconds
}


void buzzerClose() // Buzz Every 0.1 Seconds
{
    piezo.write(0.5);
    wait(0.1); // Wait 0.1 Seconds
    piezo.write(0.0);
}


void buzzerNormal() // Buzz Every 0.3 Seconds
{
    piezo.write(0.5);
    wait(0.3);
    piezo.write(0.0);
}


void buzzerFar() // Buzz Every 0.5 Seconds
{
    piezo.write(0.5);
    wait(0.5);
    piezo.write(0.0);
}


// ------------------------- ISR & INTERRUPT FUNCTIONS ------------------------- //

void returnToMenu() // Function for Returning to Main Menu (if 'backward' Button Pressed)
{
    // Check Flag IF 'forward' Button Interrupt Pressed
    if (g_backward_flag) {
        g_backward_flag = 0;  // Clear Flag

        screenNumber = 0; // Go to Screen Number = 0 (Main Menu)
        cancel_LEDs_Flashing();
        allLEDOff(); // Turn OFF LEDs
        piezo.write(0.0); // Write 0% Duty Cycle to Piezo (Turn OFF)
    }
    
}


void system_isr() // ISR For 'systemTicker'
{
    g_system_flag = 1;   // Set 'systemTicker' Flag
}


void forward_isr() // ISR For 'forward' Buttom
{
    g_forward_flag = 1;   // Set 'forward' Button Flag
}


void backward_isr() // ISR For 'backward' Button
{
    g_backward_flag = 1;   // Set 'backward' Button Flag
}