Greg Folker / Mbed OS Hexi_Sensor_System

Dependencies:   Hexi_KW40Z images

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers driver.cpp Source File

driver.cpp

00001 #include "mbed.h"
00002 #include "pwm_tone.h"   /* Tone library for Buzzer Click */
00003 #include "Hexi_OLED_SSD1351.h" /* Header file for OLED Display */
00004 #include "string.h"
00005 #include "Hexi_KW40Z.h"   /* Driver for button presses and BLE */
00006 #include <math.h>       /* Used for ADC conversion algorithm */
00007 #include "images.h"     /* BMPs of the images drawn to the OLED */
00008 
00009 /*****Function Prototypes*****/
00010 void readSensors();
00011 void ledColor();
00012 void led_out();
00013 void buzzer();
00014 void CalculatePPM();
00015 void dataQueue();
00016 void initModules();
00017 void StartHaptic(void);
00018 void StopHaptic(void const *n);
00019 void txTask(void);
00020 
00021 PwmOut Buzzer(PTA10);   /* Buzzer is attached to docking station port 1 */
00022 AnalogIn AQSensor(PTB3);   /* Air Quality sensor is attached to docking station port 2 */
00023 AnalogIn COSensor(PTB6);   /* Carbon Monoxide sensor is attached to docking station port 3 */
00024 
00025 /* Instantiate the SSD1351 OLED Driver */ 
00026 SSD1351 oled(PTB22,PTB21,PTC13,PTB20,PTE6, PTD15);
00027 
00028 BusOut led(PTC8, PTD0, PTC9);   /* RGB Port Configurations */
00029 DigitalOut haptic(PTB9);        /* Port Configuration for vibrations */
00030 /* Define timer for haptic feedback */
00031 RtosTimer hapticTimer(StopHaptic, osTimerOnce);
00032 
00033 int screen = 1;         //detects which screen the user is currently on
00034 bool is_drawn1 = 0;      //detects if the screen is already drawn to the screen
00035 bool is_drawn2 = 0;
00036 bool is_drawn3 = 0;
00037 bool is_drawn4 = 0;
00038 bool is_ble = 0;        //detects if the device is currently running BLE mode
00039 
00040 /* Screens */
00041 const uint8_t *image1;          //homescreen
00042     
00043 const uint8_t *image2;         //air quality screen
00044 
00045 const uint8_t *image3;         //co screen
00046 
00047 const int green = 5,
00048           red = 6,          /* LED Colors */
00049           black = 7,
00050           yellow = 4;
00051           
00052 bool is_sounding = false;       /* Set to high if the buzzer is going off */
00053           
00054 int led_color;      /* Variable to hold the current LED color */
00055 
00056 Ticker sensor_read,      /* Used for the read sensor subroutine call */
00057        buzzer_sound,     /* Used for the sound buzzer subroutine call */
00058        led_flash,        /* Used to determine the flash rate */
00059        ledcolor,        /* Used to determine the LED color */
00060        A2D_Convert,    /* Used to convert the analog signal to digital */
00061        push;            /* Used to push values into the data array */
00062 
00063 float B_4 = 1000000/Ti4,    /* Tones that will play on the buzzer click */
00064       B_5 = 1000000/Ti5;
00065       
00066 char text[20];  /* Static Text Buffer for OLED Display */
00067 char AQ_level[20]; /* Dynamic Text Buffer for AQ total */
00068 char CO_level[20]; /* Dynamic Text Buffer for CO total */
00069 char average[20]; /* Dynamic Text Buffer for the Average total */
00070 char total_total[20]; /* Dynamic Text Buffer for the Total total */
00071 char ble_status[20];  /* Informs the user if BLE is activated or disabled */
00072 
00073 /* Either unit16_t or double */
00074 double     total,    /* Variable to store the total total */
00075            aq_ppm,   /* Variable used to store the AQ PPM */
00076            co_ppm,   /* Variable used to store the CO PPM */ 
00077            prev_value;  /* Used for ARC Calculation */
00078 double ARC;
00079          
00080 uint16_t aq_total, /* Variable to store the AQ total */
00081          co_total; /* Variable to store the CO total */  
00082          
00083 /*Create a Thread to handle sending BLE Sensor Data */ 
00084 Thread txThread;
00085 
00086 /* Variables to handle the circular array algorithm */
00087 const int SIZE = 500;
00088 double dataSet [SIZE];
00089 int pushIndex = 0;
00090 bool calc = false;  
00091 double avg = 0, sum = 0;     
00092 
00093 /* Instantiate the Hexi KW40Z Driver (UART TX, UART RX) */ 
00094 KW40Z kw40z_device(PTE24, PTE25);
00095 
00096 /* Below are the functions to handle button presses and screens */
00097 void ButtonRight(void)//CO screen button
00098 {   
00099     StartHaptic();
00100     screen = 3;
00101 }
00102 
00103 void ButtonLeft(void)//AQ screen button
00104 {
00105     StartHaptic();
00106     screen = 2;
00107 }
00108 
00109 void ButtonDown(void)//home screen button
00110 {
00111     StartHaptic();
00112     screen = 1;
00113 }
00114 
00115 void ButtonUp(void) {//toggles bluetooth mode
00116     StartHaptic();
00117     screen = 4;
00118     kw40z_device.ToggleAdvertisementMode();
00119     is_ble = !is_ble;
00120 }
00121 
00122 void PassKey(void)
00123 {
00124     StartHaptic();
00125     strcpy((char *) text,"PAIR CODE");
00126     oled.TextBox((uint8_t *)text,0,25,95,18);
00127   
00128     /* Display Bond Pass Key in a 95px by 18px textbox at x=0,y=40 */
00129     sprintf(text,"%d", kw40z_device.GetPassKey());
00130     oled.TextBox((uint8_t *)text,0,40,95,18);
00131 } 
00132       
00133 int main(){
00134     
00135     initModules();
00136     
00137     /* Subroutine Calls */
00138     sensor_read.attach(&readSensors, 1);   /* Read the sensor on a time interval */
00139     A2D_Convert.attach(&CalculatePPM, 1);  /* Convert the values read from the sensors to floating point ppm values */
00140     push.attach(&dataQueue, 1);            /* Push the value into the set and compute the average */
00141     ledcolor.attach(&ledColor, 0.5);       /* Determine the LED color */
00142     led_flash.attach(&led_out, 0.25);      /* Flash LED based on sensor data */
00143     buzzer_sound.attach(&buzzer, 0.25);    /* Sensor values are sent to buzzer function */
00144     
00145     /* Register callbacks to button presses */
00146     kw40z_device.attach_buttonDown(&ButtonDown);
00147     kw40z_device.attach_buttonLeft(&ButtonLeft);
00148     kw40z_device.attach_buttonRight(&ButtonRight);
00149     kw40z_device.attach_buttonUp(&ButtonUp);
00150     kw40z_device.attach_passkey(&PassKey);
00151     
00152     txThread.start(txTask); /*Start transmitting Sensor Tag Data */
00153     
00154     while (1) { /* Loop to process and display data to the OLED */
00155     
00156         /* Get OLED Class Default Text Properties */
00157         oled_text_properties_t textProperties = {0};
00158         oled.GetTextProperties(&textProperties);
00159         
00160         /* Set text properties to green and right aligned for the dynamic text */
00161         textProperties.fontColor = COLOR_GREEN;
00162         textProperties.alignParam = OLED_TEXT_ALIGN_RIGHT;
00163         oled.SetTextProperties(&textProperties);
00164         
00165         if (screen == 2){   //Air Quality Screen
00166             if(!is_drawn2){
00167                 is_drawn1 = 0;
00168                 is_drawn3 = 0;
00169                 is_drawn4 = 0;
00170                 oled.DrawImage(image2,0,0);
00171                 is_drawn2 = 1;
00172             }
00173             if (aq_ppm >= 50 and aq_ppm < 100){
00174                 textProperties.fontColor = COLOR_YELLOW;  //color the font yellow
00175                 oled.SetTextProperties(&textProperties);
00176             }
00177             else if (aq_ppm >= 100){
00178                 textProperties.fontColor = COLOR_RED;   //color the font red
00179                 oled.SetTextProperties(&textProperties);
00180             }
00181             sprintf(AQ_level,"%.2f",aq_ppm);    /* Print the AQ PPM to the screen */
00182             oled.TextBox((uint8_t *)AQ_level,35,76,35,15);
00183         }
00184         else if (screen == 3){  //Carbon Monoxide Screen
00185             if (!is_drawn3){
00186                 is_drawn2 = 0;
00187                 is_drawn1 = 0;
00188                 is_drawn4 = 0;
00189                 oled.DrawImage(image3,0,0);
00190                 is_drawn3 = 1;
00191             }
00192             if (co_ppm >= 50 and co_ppm < 100){
00193                 textProperties.fontColor = COLOR_YELLOW;  //color the font yellow
00194                 oled.SetTextProperties(&textProperties);
00195             }
00196             
00197             else if (co_ppm >= 100) {
00198                 textProperties.fontColor = COLOR_RED;  //color the font red
00199                 oled.SetTextProperties(&textProperties);
00200             }
00201             sprintf(CO_level,"%.2f",co_ppm);    /* Print the CO PPM to the screen */        
00202             oled.TextBox((uint8_t *)CO_level,35,76,35,15);
00203             
00204         }
00205         else if (screen == 1) {   //Home Screen
00206             if (!is_drawn1){
00207                 is_drawn3 = 0;
00208                 is_drawn2 = 0;
00209                 is_drawn4 = 0;
00210                 oled.DrawImage(image1,0,0);
00211                 is_drawn1 = 1;
00212             }
00213             if (avg >= 50 and avg <100) {
00214                 textProperties.fontColor = COLOR_YELLOW;  //color the font yellow
00215                 oled.SetTextProperties(&textProperties);
00216             }
00217             else if (avg >=100){
00218                 textProperties.fontColor = COLOR_RED;  //color the font red
00219                 oled.SetTextProperties(&textProperties); 
00220             }
00221             
00222             sprintf(average, "%.2f", avg);
00223             oled.TextBox((uint8_t *)average,53,23,35,15);
00224             
00225             if (total >=50 and total < 100){
00226                 textProperties.fontColor = COLOR_YELLOW;  //color the font yellow
00227                 oled.SetTextProperties(&textProperties); 
00228             }
00229             else if (total >= 100) {
00230                 textProperties.fontColor = COLOR_RED;  //color the font red
00231                 oled.SetTextProperties(&textProperties);
00232             }
00233             else {
00234                 textProperties.fontColor = COLOR_GREEN; //color the font green
00235                 oled.SetTextProperties(&textProperties);
00236             }
00237             sprintf(total_total, "%.2f", total);   /* Print the total to the screen */
00238             oled.TextBox((uint8_t *)total_total,53,39,35,15);
00239         }
00240         
00241         else if (screen == 4){   //BLE Pairing Screen
00242             if (!is_drawn4){
00243                 is_drawn3 = 0;
00244                 is_drawn2 = 0;
00245                 is_drawn1 = 0; 
00246                 oled.FillScreen(COLOR_BLACK);
00247                 is_drawn4 = 1;  
00248             }
00249             textProperties.alignParam = OLED_TEXT_ALIGN_CENTER;
00250             textProperties.fontColor = COLOR_BLUE;
00251             oled.SetTextProperties(&textProperties);
00252             strcpy((char *) text,"BLUETOOTH");
00253             oled.TextBox((uint8_t *)text,0,25,95,18);  
00254             if (is_ble) {
00255                 textProperties.fontColor = COLOR_GREEN;
00256                 oled.SetTextProperties(&textProperties);
00257                 strcpy((char *) ble_status, "On");
00258                 oled.TextBox((uint8_t *)ble_status,0,55,95,18);
00259             }
00260             else {
00261                 textProperties.fontColor = COLOR_RED;
00262                 oled.SetTextProperties(&textProperties);
00263                 strcpy((char *) ble_status, "Off");
00264                 oled.TextBox((uint8_t *)ble_status,0,55,95,18);
00265             }
00266         }
00267         Thread::wait(500);
00268     }
00269     
00270     return 0;
00271 }
00272 void txTask(void){
00273     while (1){
00274         /*Notify Hexiwear App that it is running Sensor Tag mode*/
00275         kw40z_device.SendSetApplicationMode(GUI_CURRENT_APP_SENSOR_TAG);   
00276         kw40z_device.SendTemperature(100*aq_ppm); //send ppm Air Quality Click value
00277         kw40z_device.SendHumidity(100*co_ppm);  //send ppm CO click value
00278         kw40z_device.SendPressure(100*total);     //send total
00279         Thread::wait(1000);
00280     }
00281 }
00282 void initModules() {    /* Function to initialize the system */
00283 
00284     /* Turns on the OLED Display*/
00285     oled.PowerON();
00286     
00287     /* Sets the pointers to their respective images */
00288     image1 = homescreen_bmp;
00289     image2 = airquality_bmp;
00290     image3  = co_bmp;
00291 }
00292 
00293 void readSensors(){  /* Function to read sensors */
00294     
00295     /* Grab the analog signal from the sensors as 16 bit unsigned integers */
00296     aq_total = AQSensor.read_u16();
00297     co_total = COSensor.read_u16();
00298     /* Grab the analog signal from the sensors as floats */
00299     //aq_total = AQSensor.read();
00300     //co_total = AQSensor.read();
00301   
00302 }
00303 
00304 void ledColor(){    /* Function to determine the LED color */
00305     
00306     if ((total - avg) <= 10 and total < 50 and ARC < 10 ) {
00307         led = green;
00308         led_color = led;     /* Store the LED color for the flash function */
00309     }
00310     else if ((total - avg) > 10 and (total - avg) < 50 and total < 100 or (ARC >= 10 and ARC < 20)) {
00311         led = yellow;
00312         led_color = led;     /* Store the LED color for the flash function */
00313     }
00314     else if ((total - avg >= 50) or total >= 100 or avg >= 100 or ARC >= 20) {
00315         led = red;
00316         led_color = led;     /* Store the LED color for the flash function */
00317     }
00318 }
00319 
00320 void led_out() { /* Function to flash LEDs */
00321 
00322     if (led == green) {return;}   /* If green, don't blink */
00323     else if (led == black) {led = led_color;}
00324     else {led = black;}
00325      
00326 }
00327 
00328 void buzzer() { /* Function to handle the buzzer sound */
00329 
00330     if ((total - avg) <=25 and ARC < 10){            /* No buzzer sound if PPM is under 10 */
00331         Buzzer.period_us(0);
00332         is_sounding = false;  
00333     }
00334 
00335     else if ((total - avg) > 25 and (total - avg) < 50 and total < 100 or (ARC >=10 and ARC < 20)) {
00336         if (is_sounding == false){      /* If the buzzer is not triggered, trigger */
00337             Buzzer.period_us(B_4);      /* Period of B4 */
00338             Buzzer.write(0.50f);        /* 50% Duty Cycle on the Tone */
00339             is_sounding = true;         /* Signal that the buzzer is currently triggering */
00340         }
00341         else {   
00342             Buzzer.period_us(0);        /* Turn off the buzzer (Period = 0us)*/
00343             is_sounding = false;        /* Signal that the buzzer is no longer triggering */
00344         }        
00345     }
00346     
00347     else if ((total - avg) >= 50 or total >= 100 or avg >= 100 or ARC >= 20){
00348         if (is_sounding == false){
00349             Buzzer.period_us(B_5);      /* Period of B5 */
00350             Buzzer.write(0.50f);        /* 50% Duty Cycle on the Tone */
00351             is_sounding = true;
00352         }
00353         else {   
00354             Buzzer.period_us(0);        /* Turn off the buzzer */
00355             is_sounding = false;
00356         }
00357     }   
00358 }
00359 
00360 void CalculatePPM(){ /* Function to convert the analog signals to digital based on 16 bit adc steps */
00361     
00362     const double AQ_Rl = 20000.0;               // AQ_Rl (20kOhm) - Load resistance for AQ sensor
00363     const double CO_Rl = 18500.0;               // CO_Rl (18.5kOhm) - Load resistance for CO sensor
00364     const double Vadc_33 = 0.0000503548;         // ADC step 3,3V/65535 0.00503mV (16bit ADC)
00365     //const double Vadc_5 = 5.0/65535;          // ADC step 5.0V/2^16 (65535, 16bit ADC)
00366     double Vrl;                                  // Output voltage
00367     double Rs;                                   // Rs (Ohm) - Sensor resistance
00368     double ratio;                                // Rs/Rl ratio
00369     double lgPPM;
00370 
00371     if (aq_total > 65533)       //prevents NAN error from overflow of 16 bits
00372         aq_total = 65530;
00373     Vrl = (double)aq_total * Vadc_33;            // For 3.3V Vcc use Vadc_33
00374     Rs = AQ_Rl * (3.3 - Vrl)/Vrl;                 // Calculate sensor resistance
00375     ratio = Rs/AQ_Rl;                             // Calculate ratio
00376     lgPPM = (log10(ratio) * -0.8) + 0.9;      
00377     aq_ppm = 6* pow(10,lgPPM) - 12;                 // Calculate air quality ppm
00378     
00379     if (co_total > 65533)   //prevents NAN error from overflow of 16 bits
00380         co_total = 65530;
00381     Vrl = (double)co_total * Vadc_33;            // For 3.3V Vcc use Vadc_33
00382     Rs = CO_Rl * (3.3 - Vrl)/Vrl;                 // Calculate sensor resistance
00383     ratio = Rs/CO_Rl;                             // Calculate ratio
00384     lgPPM = (log10(ratio) * -0.8) + 0.9;       
00385     co_ppm = 6* pow(10,lgPPM) - 12;                 // Calculate carbon monoxide ppm
00386     
00387     if (aq_ppm < 0)
00388         aq_ppm = 0;             //prevents underflow
00389     else if (aq_ppm >= 1000)
00390         aq_ppm = 999.99;        //prevents overflow
00391     
00392     if (co_ppm <0)
00393         co_ppm = 0;
00394     else if (co_ppm >= 1000)
00395         co_ppm = 999.99;
00396       
00397     /* Calculate the total */
00398     total = co_ppm + aq_ppm;    
00399     if (total < 0)
00400         total = 0;
00401     else if (total >= 1000)
00402         total = 999.99; 
00403 }
00404 
00405 void dataQueue()
00406 {   /* Beginning of function */
00407     if (pushIndex != SIZE and !calc){    /* Initially pushing values into the set */
00408         dataSet[pushIndex] = total;          //push value into the queue
00409         sum += dataSet[pushIndex];  //add the value to the sum
00410         pushIndex++;                    //increment the push index
00411         
00412         if (pushIndex == SIZE){
00413             avg = sum / SIZE;       //compute the average once the queue is full
00414             calc = true;                //flag calc
00415             ARC = dataSet[SIZE - 1] - dataSet[SIZE - 2];
00416             pushIndex = 0;              //reset the push index back to 0
00417         }
00418     }
00419     
00420     else if (pushIndex != SIZE and calc){   /* Pushing values into the set once the set is full */
00421         sum -= dataSet[pushIndex];          //subtract the value to be overriden from the sum
00422         dataSet[pushIndex] = total;           //push the value into the set
00423         sum += dataSet[pushIndex];          //add the value to the sum
00424         avg = sum / SIZE;                   //compute average
00425         if (pushIndex == 0){
00426             prev_value = dataSet[SIZE - 1];
00427             ARC = dataSet[pushIndex] - prev_value;
00428         }
00429         else {
00430             prev_value = dataSet[pushIndex - 1];
00431             ARC = dataSet[pushIndex] - prev_value;
00432         }
00433         pushIndex++;                        //increment the push index
00434         
00435         if (pushIndex == SIZE)
00436             pushIndex = 0;    //reset the push index back to 0
00437     }
00438 
00439 } /* End of function */
00440 
00441 void StartHaptic(void)
00442 {
00443     hapticTimer.start(50);
00444     haptic = 1;
00445 }
00446 
00447 void StopHaptic(void const *n) {
00448     haptic = 0;
00449     hapticTimer.stop();
00450 }