iBreathe Breathalyzer can now talk thanks to the Text to Speech Click Board

Dependencies:   Hexi_KW40Z Hexi_OLED_SSD1351 text_to_speak_mbed

Fork of iBreathe_Breathalyzer by Dave Clarke

main.cpp

Committer:
daveyclk
Date:
2016-09-28
Revision:
7:5d272a0e250b
Parent:
6:7f4ba36b025f
Child:
9:fe5114551ec3

File content as of revision 7:5d272a0e250b:

/****************************************************************************
* Title                 :   iBreathe Breathalyzer
* Filename              :   breathalyzer
* Author                :   Dave Clarke
* Origin Date           :   27/09/2016
* Notes                 :   Breathalyzer utilizing Hexiware, Alcohol click and Wolksense
*****************************************************************************/
/**************************CHANGE LIST **************************************
*
*    Date    Software Version    Initials       Description
*  27/09/16       1.0.0            DC        Interface Created.
*
*****************************************************************************/

/**
 * @page TEST_CFG Test Configurations
 * <h3> Test configuration : </h3>
 * @par
 * <ul>
 * <li><b> MCU           </b> :      MK64FN1M0XXX12               </li>
 * <li><b> Dev. Board    </b> :      HEXIWEAR                     </li>
 * <li><b> Oscillator    </b> :      12 MHz external              </li>
 * <li><b> Ext. Modules  </b> :      Alcohol Click on mikroBUS 1  </li>
 * <li><b> SW            </b> :      mBed OS5    </li>
 * </ul>
 */

/**
 * @mainpage
 * <h3> Breathalyser created with HEXIWEAR and mBed OS 5 </h3>
 * @par This will show you how much you've drunk and tell you with an Emoticon if
 * you're too hammered to even consider driving. Using the Hexiware app the readings
 * are transmitted to the cloud via bluetooth. Is it time to give up drinking yet?!
 
 * <h3> Alcohol Features </h3>
 * @par Alcohol click, Hexiwear docking station, Hexiware
 */

/******************************************************************************
* Includes
*******************************************************************************/

#include "mbed.h"
#include "Hexi_KW40Z.h"
#include "Hexi_OLED_SSD1351.h"
#include "OLED_types.h"
#include "OpenSans_Font.h"
#include "string.h"
#include "iBreatheImages.h"

/******************************************************************************
* Module Variable Definitions
*******************************************************************************/

#define LED_ON      0
#define LED_OFF     1

                          // Pointers to:  
const uint8_t   *welcome, // Welcome screen image
                *blank,   // blank image
                *blow,    // Start Blowing Image
                *drink,   // You've been drinking image
                *drive,   // Don't drive image
                *hang,    // You'll have a hangover image
                *ini,     // Initialising image
                *sober;   // Sober as a judge image

const float     Vadc_3V3  = 0.00005035;    // 16-Bit ADC step 3V3/65535   = 0.05035 mV
       
float           Vrl = 0,                   // Output voltage
                ambientAlc = 0,            // Abmient Output voltage from sensor
                SensorRes = 0,             // SensorRes (Ohm) - Sensor resistance
                SensorRes1 = 0,            // SensorRes (Ohm) - Sensor resistance
                ppm = 0,                   // ppm
                ppm_1 = 0,                 // Ambient ppm variable  
                ratio = 0;                 // SensorRes/LoadRes ratio
              
unsigned short  adc_rd = 0; //Initialise anologue read variable

const uint8_t   ppmText[] = "ppm:"; // text for PPM label

char            text[20],           // Text array variables
                text2[20],
                text3[20];

float           value[20],  // initial sensor set up values
                value1[20]; // initial sensor set up values

bool            isFirstBoot = true;                   
                

/******************************************************************************
* Function Prototypes
*******************************************************************************/

void sysinit(void);
void ReadSensor();
void CalculatePPM(int times, bool amb);
void ambient(int times);
void StartHaptic(void);
void StopHaptic(void const *n);
void ButtonUp(void);
void txTask(void);

/******************************************************************************
* Instance setups
*******************************************************************************/
/* Define timer for haptic feedback */
RtosTimer hapticTimer(StopHaptic, osTimerOnce);

//set up Analog read pin for alcohol sensor
AnalogIn Alcohol(PTB2);

/* Instantiate the SSD1351 OLED Driver */ 
SSD1351 oled(PTB22,PTB21,PTC13,PTB20,PTE6, PTD15); /* (MOSI,SCLK,POWER,CS,RST,DC) */
      
/* Get OLED Class Default Text Properties */
oled_text_properties_t textProperties = {0};

/* Instantiate the Hexi KW40Z Driver (UART TX, UART RX) */ 
KW40Z kw40z_device(PTE24, PTE25);

/* LED and Haptic Set ups */
DigitalOut redLed(LED1);
DigitalOut greenLed(LED2);
DigitalOut blueLed(LED3);
DigitalOut haptic(PTB9);

/******************************************************************************
* Bluetooth button functions and passkey function
*******************************************************************************/

void ButtonRight(void)
{
    StartHaptic();
    kw40z_device.ToggleAdvertisementMode();
    blueLed = kw40z_device.GetAdvertisementMode(); /*Indicate BLE Advertisment Mode*/
    redLed = !kw40z_device.GetAdvertisementMode(); /*Indicate BLE Advertisment Mode*/
    greenLed = !kw40z_device.GetAdvertisementMode(); /*Indicate BLE Advertisment Mode*/
}

void ButtonLeft(void)
{
    StartHaptic();
    kw40z_device.ToggleAdvertisementMode();
    blueLed = kw40z_device.GetAdvertisementMode(); /*Indicate BLE Advertisment Mode*/
    redLed = !kw40z_device.GetAdvertisementMode(); /*Indicate BLE Advertisment Mode*/
    greenLed = !kw40z_device.GetAdvertisementMode(); /*Indicate BLE Advertisment Mode*/
}

void PassKey(void)
{
    StartHaptic();
    strcpy((char *) text,"PAIR CODE");
    oled.TextBox((uint8_t *)text,0,25,95,18);
  
    /* Display Bond Pass Key in a 95px by 18px textbox at x=0,y=40 */
    sprintf(text3,"%d", kw40z_device.GetPassKey());
    oled.TextBox((uint8_t *)text3,0,40,95,18);
}

/******************************************************************************
* Main
*******************************************************************************/
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
int main()
{
     /* Set pointers to the BMPs stored in  iBreatheImages.c */
     welcome = iBreatheWS_bmp;    // Welcome screen image 
     blank = iBreatheBlank_bmp;   // blank image
     blow = iBreatheBlow_bmp;    // Start Blowing Image
     drink = iBreatheDrink_bmp;   // You've been drinking image
     drive = iBreatheDrive_bmp;   // Don't drive image
     hang = iBreatheHang_bmp;    // You'll have a hangover image
     ini = iBreatheini_bmp;     // Initialising image
     sober = iBreatheSober_bmp;   // Sober as a judge image
       
     /* Set initial Values */
     sysinit();
}

/******************************************************************************
* Public Function Definitions
*******************************************************************************/
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**************************************************************************************************
* Function readSensor(void)
* -------------------------------------------------------------------------------------------------
* Overview: Read sensor
* Input: None
* Output: None
**************************************************************************************************/

void ReadSensor()
{
    /* Read 16 Bit Analog value */
    adc_rd = Alcohol.read_u16();

    // pause 200ms 
    Thread::wait(200);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**************************************************************************************************
* Function CalculatePPM(Int times, bool amb)
* -------------------------------------------------------------------------------------------------
* Overview: Calculation of PPM
* Input: times = Number of samples to take, amb sets either ambient reading or test reading (true for test)
* Output: None
**************************************************************************************************/

void CalculatePPM(int times, bool amb)
{
        float   lgPPM;
        
        /* Read values x times */
        for(int x = 0; x < times; x++)
        {
            ReadSensor();    
            value[x] = ((float)adc_rd  * Vadc_3V3);
            StartHaptic();
            Thread::wait(50);
        }    
        
        /* Calculate the average value for accuratcy */
        for(int y = 0; y < times; y++)
        {
            Vrl += value[y];
        }
        Vrl = Vrl / times;       

        /* Set SensorRes reference value */
        SensorRes =    (Vrl / 3.3);
        
        /* Set ratio */
        ratio     = SensorRes1 / SensorRes;

        /* Convert to PPM */
        lgPPM = ( log10( ratio ) * -1.5512 ) + 2.5911;
        
        /* If true create test result, flase creates reference result */
        if (amb == true)
        {
            ppm = pow( 10, lgPPM );
        }
        else
        {
            ppm_1 = pow( 10, lgPPM );
        }    
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**************************************************************************************************
* Function void ambient(int times)
* -------------------------------------------------------------------------------------------------
* Overview: Reading of the Ambient Voltgage of the sensor for reference
* Input: times = Number of samples to take
* Output: None
**************************************************************************************************/

void ambient(int times)
{
    /* Read ambient values x times flashing green led*/
    for(int x = 0; x < times; x++)
    {
        redLed      = LED_OFF;
        greenLed    = LED_OFF;
        blueLed     = LED_OFF;
        
        ReadSensor();
        value1[x] = (float)adc_rd  * Vadc_3V3; 
        
        redLed      = LED_OFF;
        greenLed    = LED_ON;
        blueLed     = LED_OFF;
        Thread::wait(48);   
    }
    
    /* Calculate the average value for accuratcy */
    for(int y = 0; y < times; y++)
    {
        ambientAlc+=value1[y];            
    }
    ambientAlc = ambientAlc / times;
  
    /* Set SensorRes1 reference value */
    SensorRes1 =   (ambientAlc / 3.3);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**************************************************************************************************
* Function void StartHaptic(void)
* -------------------------------------------------------------------------------------------------
* Overview: Start Buzzing haptic motor
* Input:  None
* Output: None
**************************************************************************************************/

void StartHaptic(void)
{
    hapticTimer.start(50);
    haptic = 1;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**************************************************************************************************
* Function void StopHaptic(void)
* -------------------------------------------------------------------------------------------------
* Overview: Stop Buzzing haptic motor
* Input:  None
* Output: None
**************************************************************************************************/

void StopHaptic(void const *n) {
    haptic = 0;
    hapticTimer.stop();
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**************************************************************************************************
* Function void ButtonUp(void)
* -------------------------------------------------------------------------------------------------
* Overview: Function called whe up button pressed, Begins Breathilyzer testing procedure
* Input:  None
* Output: None
**************************************************************************************************/

void ButtonUp(void)
{
    StartHaptic();
    
    bool Ref = false;
    bool Test = true;
        
    /* LED set to green for test beginning*/
    redLed      = LED_OFF;
    greenLed    = LED_ON;
    blueLed     = LED_OFF;
    
    /* Fill 96px by 96px Screen with 96px by 96px Initialising Image starting at x=0,y=0 */   
    oled.DrawImage(ini,0,0);
    
    /* first boot bug work around to stop junk values */
    if (isFirstBoot == true)
    {
        /*read ambient atmosphere levels with 10 samples and set flag to show it's ambient basline figure*/
        ambient(1);
        CalculatePPM(1,Ref);
        isFirstBoot = false;
    }
    
    /*read ambient atmosphere levels with 10 samples and set flag to show it's ambient basline figure*/
    ambient(10);
    CalculatePPM(10,Ref);
    
    
    /* Fill 96px by 96px Screen with 96px by 96px Blowing Image starting at x=0,y=0 */  
    oled.DrawImage(blow,0,0);
    
    /*read breathe alcohol levels with 10 samples and set flag to show it's breathilyzer test figure*/
    CalculatePPM(10,Test);
    
    /*Calculate the difference in Alcohol level based on Ambient and test sample*/
    ppm = ppm - ppm_1;
    
    /*Throw away any values less than 0*/
    if (ppm < 0)
    {
         ppm = 0;
    }     
    
    /* Fill 96px by 96px Screen with 96px by 96px Blank Background Image starting at x=0,y=0 */
    oled.DrawImage(blank,0,0);     
    
    /* Show Calculated alcohol level in PPM and send data via bluetooth to the cloud */
    oled.SetTextProperties(&textProperties);
    oled.Label(ppmText,20,36);
    sprintf(text,"%.2f",ppm);
    oled.Label((uint8_t *)text,50,36);
    Thread::wait(1000);
    
    /* Currently sending to the Pressure variable as a temp place holder */
    kw40z_device.SendiBreathe(ppm); // this is used for custom Hexiware app
    kw40z_device.SendPressure(ppm * 10); // using this to record on Wolksense Cloud
    
    
    /* You've got a Hangover coming!*/
    if ( ppm > 200)
    {
        redLed      = LED_ON;
        greenLed    = LED_OFF;
        blueLed     = LED_OFF;
        
        StartHaptic();
        
        /* Fill 96px by 96px Screen with 96px by 96px Hangover Image starting at x=0,y=0 */
        oled.DrawImage(hang,0,0);
    }   
    
    /* You Shouldn't drive */
    else if (ppm < 200 && ppm > 150)
    {
        redLed      = LED_ON;
        greenLed    = LED_OFF;
        blueLed     = LED_ON;
        
        StartHaptic();
      
        /* Fill 96px by 96px Screen with 96px by 96px Don't Drive Image starting at x=0,y=0 */      
        oled.DrawImage(drive,0,0);
    } 
    
    /* You've had a drink */
    else if (ppm < 150 && ppm > 50)
    {
        redLed      = LED_OFF;
        greenLed    = LED_ON;
        blueLed     = LED_ON;
        
        StartHaptic();
    
        /* Fill 96px by 96px Screen with 96px by 96px Had a drink Image starting at x=0,y=0 */     
        oled.DrawImage(drink,0,0);
    }
    
    /* Sober as a judge*/   
    else
    {
        redLed      = LED_OFF;
        greenLed    = LED_ON;
        blueLed     = LED_OFF;
        
        StartHaptic();
        oled.DrawImage(sober,0,0);    
    }
    Thread::wait(5000);
    
    /* Go back to start screen */
    sysinit();   
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**************************************************************************************************
* Function void sysint(void)
* -------------------------------------------------------------------------------------------------
* Overview: System Initial Values Set up
* Input: None
* Output: None
**************************************************************************************************/
void sysinit(void)
{
    /* Set LED to Blue by default*/      
    redLed      = LED_OFF;
    greenLed    = LED_OFF;
    blueLed     = LED_ON;
    
    /* Turn on the backlight of the OLED Display */
    oled.DimScreenON();
    
    /* Fill 96px by 96px Screen with 96px by 96px Welcome Image starting at x=0,y=0 */
    oled.DrawImage(welcome,0,0);
    
    /* Register callbacks to application functions */
    kw40z_device.attach_buttonUp(&ButtonUp);
    kw40z_device.attach_buttonLeft(&ButtonLeft);
    kw40z_device.attach_buttonRight(&ButtonRight);
    kw40z_device.attach_passkey(&PassKey);
    
    /* Send sensor data to bluetooth */
    kw40z_device.SendSetApplicationMode(GUI_CURRENT_APP_SENSOR_TAG);
    
    /* Change font color to White */
    oled.GetTextProperties(&textProperties); 
    textProperties.fontColor   = COLOR_WHITE;
    oled.SetTextProperties(&textProperties);
}