A code to drive a 3sensor reading unit for monitoring the operation opf a closed circuit rebreather (CCR) with 3 electrogalvanic sensors. Also uses a DS1307 for realtime clock and an MPX5700 to read the depth (mounted inside the breathing loop to keep it 'dry'). circuit diagrams available on rebreather world.

Dependencies:   DS1307 TextOLED mbed

Revision:
7:f93b7eaab5f6
Parent:
6:ab2d7d0a9b07
Child:
8:f45e654b47d0
--- a/Rebmon_main.cpp	Tue Aug 07 16:25:18 2012 +0000
+++ b/Rebmon_main.cpp	Mon Jan 14 12:52:45 2013 +0000
@@ -1,10 +1,30 @@
 //lpc1124lcddemo
+// driver for mbed based ppo2 monitoring system for closed circuit rebreather
+// reading 3 electrogalvanic oxygen sensors and one pressure sensor
 #include "ds1307.h"
 #include "mbed.h"
 #include "TextOLED.h"
 
+//TODO - MAKE A VERSION THAT DRIVES THE HUD, CHECKS THE 5V SUPPLY AND COMPENSATES THE READINGS IF ITS VARYING (LINEARLY)
+// AND PREVENT A HANG IF THE DS1307 FAILS TO RESPOND.
 
-#define METRE 0.02 // change in DEPin for 1m depth
+#define DRATIO 0.6420066 // ratio of voltage at pin20 to voltage actually generated by the sensor
+
+//hud LINES
+DigitalOut AB(p7); //pins AB for data bits
+DigitalOut CP(p5); // clock to shift reg
+//DigitalOut MR(p8); // reset to shift reg (low for clear)#
+DigitalOut btest(p6); // pin to  drive lastblue led
+
+// offsets for lm324 amp in terms of reading values on adc
+#define coff1 -0.013375
+#define coff2 -0.00936
+#define coff3 -0.0212136
+
+
+
+Serial pc(USBTX, USBRX); // tx, rx  for debug and usb pc comunications
+
 
 //pin assignments and declarations
 // LCD display
@@ -25,8 +45,8 @@
 // switches and buttons - these are pulled up by resistors so are active low
 DigitalIn CAL(p36);
 DigitalIn SW1(p35); // reed switch in display unit
-DigitalIn SW2(p10); // reed switch in dispaly unit
-DigitalIn MODE(p11);// a switchn on the mbed pcb to select between SCR and CCR modes for the LEDs
+DigitalIn SW2(p10); // reed switch in dispaly unit - NONE FUNCIONAL IN CURRENT HEAD - SWITCH FAILED DURING POTTING
+//DigitalIn MODE(p11);// a switchn on the mbed pcb to select between SCR and CCR modes for the LEDs NOT USED ANYMORE
 
 // log data storage
 LocalFileSystem local("local");
@@ -35,7 +55,11 @@
 AnalogIn PRESin(p20);
 AnalogIn EG1(p19);
 AnalogIn EG2(p18);
-AnalogIn Vbatt(p17);
+AnalogIn EG3(p16); 
+AnalogIn Vbatt(p17); // battery voltage divided down by 3
+AnalogIn V5V(p15); // sense the '5V' output from the max1724 unit - divided down by 2.  Nominally 2.5V===0.757575757' in 3.3V ADC
+
+
 
 // realtime clock
 DS1307 my1307(p28,p27); // start DS1307 class and give it pins for connections of the DS1307 device
@@ -50,43 +74,134 @@
 int year = 0;
 int seconds=0; // general number of seconds since 2000 etc timestamp variable
 
-int scrubtime=0,scrubold=0;; // these are expressed in minutes
+int scrubtime=0,scrubold=0; // these are expressed in minutes
 int divetime=0;
 
 int flash=0; // variable used top control flashing icons
 int state=0; // IMPORTANT - VARIABLE THAT DRIVES HNTE STATE MACHINE STATE=0 = STARTUP, STATE=1=SURFACE  STATE=2= DIVING
+float lowsetpoint=0.7,highsetpoint=1.2,switchdepth=10; // variables to determine HUD led states 
+//switchdepth is centre of switch region 1m deep if switchdepth=10 then will go to high as descebnd 
+//through 10.5 and go back to low when ascending through 9.5
+int setpoint=0; // 0=low 1 = high
 
 // variables for the eg cells and pressure sensor eg1calamd eg2cal ar reading when the sensor is in 0.21bar O2 and
 //dcal is the reading whe the pressure sensor is at the surface
-float eg1cal=0.09,eg2cal=0.09,pcal=0.1136;
+float eg1cal=0.09,eg2cal=0.09,eg3cal=0.09,pcal=0.1136;
 // NB these are updated from /local/cal.dat so values not so important.... eventually
 
-float depth=0,ppo1=0,ppo2=0,  Vb=0,pressure=0; // depth, 1st o2 sensor second o2 sensor battery voltage,,Pressure
-float fo1=0,fo2=0,mod=55; //%f values,mod
+float depth=0,ppo1=0,ppo2=0,ppo3=0  ,Vb=0,pressure=0; // depth, 1st o2 sensor second o2 sensor battery voltage,,Pressure
+float fo1=0,fo2=0,fo3=0,mod=55; //%f values,mod
 
 FILE *lp; // file pointer for log file
 
+bool nostop=1, deco=0; // variables to define state for deco
+
+
+
+//HUD codes
+// make a HUD clock pulse
+int clk()
+{
+    wait_us(1);
+    CP=0;
+    wait_us(1);
+    CP=1;
+    return(0);
+}
+
+// write 8 bits to the HUD shift register
+int HUD_write(char d)
+{
+    int i=0;
+    for(i=7; i>=0; i--) {
+        AB=d & (1 << i);
+        AB=!AB;
+        clk();
+    }
+    return(0);
+}
+
+// make all HUD leds white - useful for warnings etc
+int HUD_white()
+{
+    // set all white;
+    HUD_write(255);
+    btest=0;
+
+    return(0);
+}
+// clear the HUD - make al black
+int HUD_clr()
+{
+    HUD_write(0);
+    btest=1;
+    return(0);
+}
+
+
+
+// code to detect leap years
+int LeapYear(int year) {
+    int leap=0;
+
+    if (year % 400==0) leap=1;
+    else if (year %100 ==0) leap=0;
+    else if (year % 4 ==0) leap=1;
+    else leap=0;
+    return(leap);
+}
+
+
 //===== sub to get time from ds1307 and create the 'seconds' which is a version of timestamp....
 int getseconds() {
+    int leap=0,dayofyear=0,timestamp=0;
+    int y=0,byear=0;
+    int days[12]={0,31,59,90,120,151,181,212,243,273,304,334};
     my1307.gettime( &sec, &min, &hours, &day, &date, &month, &year);
     //simple timestamp = # seconds since midnight jan 1st 2000 if all months were 30 days.
-    int secondst=year*365*24*60*60+month*30*24*60*60+day*24*60*60+hours*60*60+min*60+sec;
+    //int secondst=year*365*24*60*60+month*30*24*60*60+day*24*60*60+hours*60*60+min*60+sec;
     //simple timestamp = # seconds since midnight jan 1st 2000 if all months were 30 days....
     // ie wrong but simpler than the real thing
-    return(secondst);
+
+
+    // sort out ds1307 definiteion of year
+    year=year+2000;
+    leap=LeapYear(year);
+
+    // now decide dayofyear
+    dayofyear=days[month-1]+date-1;
+    if (leap==1 && month >2) dayofyear++; // deal with extra february day in leap year
+
+    // now find number of days since 1970
+    for (y=1970; y<year; y++) {
+        if (LeapYear(y) == 1) {
+            byear += 366*24*60*60;
+        } else {
+            byear += 365*24*60*60;
+        }
+    }
+
+    // finally get the seconds right and construct timestamp in seconds since beginning of 1970
+    timestamp=(byear)+dayofyear*24*3600+hours*3600+min*60+sec;
+
+    //DEBUG====================================
+    // printf("secondst =  %d\t timestamp = %d\t%.2d : %.2d : %d - %.2d:%.2d:%.2d\r",secondst,timestamp,date,month,year,hours,min,sec);
+
+    return(timestamp);
+
 }
 
 
 void set_custom_char() {
     char cgchar[64]={
-        6,9,9,9,9,9,9,15, // battery empty symbol         0
-        6,9,9,9,9,15,15,15, // battery 50% symbol         1
-        6,9,9,15,15,15,15,15, // battery 75% symbol       2
-        6,15,15,15,15,15,15,15, // battery 100% symbol    3
-        31,19,21,21,21,21,19,31,  // diving symbol        4 inverse D
-        6,6,6,6,6,0,0,6,             // warning symbol    5
-        31,17,23,17,29,17,31,0, // surface symbol         6 inverse S
-        2,6,2,2,2,2,23 // defined to handle dec point in depth          7
+        6,9,9,9,9,9,9,15, // battery  symbol                    0 address 64 = 0x40
+        28,20,20,20,20,20,29,0, //  0. symbol for ppo2          1 address 72 = 0x48
+        8,24,8,8,8,8,29,0, // 1. symbol for ppo2                2 address 80 =0x50
+        6,15,15,15,15,15,15,15, // unused                       3 address 88 = 0x58
+        31,19,21,21,21,21,19,31,  // unused                     4 address 96 = 0x60
+        6,6,6,6,6,0,0,6,             // top char Vmessg         5 address 104 =0x68 - used for Vmessage
+        31,17,23,17,29,17,31,0, // bottom char Vmessg           6 address 112 =0x70 -used for Vmessg
+        2,6,2,2,2,2,23 // for dec point in depth                7 address 120 =0x78
     };
     int i=0;
 // do stuff here to set cstom chars
@@ -99,36 +214,45 @@
 
 // stash cal values on local drive
 void store() {
+    int timestamp=0;
+    timestamp=getseconds();
+    wait(0.1);
     FILE *fp=fopen("/local/CAL.dat","w");
-    fprintf(fp,"%e\n%e\n%e\n%d",eg1cal,eg2cal,pcal,scrubtime);
+    fprintf(fp,"%e\n%e\n%e\n%e\n%d\n%d\n",eg1cal,eg2cal,eg3cal,pcal,scrubtime,timestamp);
+
     fclose(fp); //NB file system locked on write so must make sure we close files in case want to reprogram etc...
+    wait(0.1);
 }
 
 
 // subroutine to calibreate o2 sesnors and store ca data in /local/CAL.dat
 void calibrate() {
     int count=1;
-    float ppo1=0,ppo2=0,pres=0;
+    float s1=0,s2=0,s3=0,pres=0;
     // average 20 readings for noise reduction
     g_lcd.cls();
     for (count=20; count>0; count--) {
         g_lcd.locate(0,0);
         g_lcd.printf("Calibrate 21%% %.2d",count);
-        ppo1=ppo1+EG1;
-        ppo2=ppo2+EG2;
+        s1=s1+EG1;
+        s2=s2+EG2;
+        s3=s3+EG3;
         pres=pres+PRESin;
         g_lcd.locate(0,1);
-        g_lcd.printf("%1.2f: %1.2f: %1.2f",ppo1/(20-count+1),ppo2/(20-count+1),pres/(20-count+1));
+        g_lcd.printf("%1.2f: %1.2f: %1.2f",s1/(20-count+1),s2/(20-count+1),s3/(20-count+1));
         wait(1);
     }
     //average
-    ppo1=ppo1/20;
-    ppo2=ppo2/20;
+    s1=s1/20-coff1;
+    s2=s2/20-coff2;
+    s3=s3/20-coff3;
     // set calibration variables
-    eg1cal=ppo1;
-    eg2cal=ppo2;
-    pcal=pres/20; // surface pressure....
+    eg1cal=s1;
+    eg2cal=s2;
+    eg3cal=s3;
+    pcal=pres/20/DRATIO; // surface pressure output voltage from sensor
     scrubtime=0; // reset the scrubber timer to zero.
+    scrubold=0; // set stored scrub time to zero too.
     // write cal data NB overwites previous
     /*  FILE *fp=fopen("/local/CAL.dat","w");
       fprintf(fp,"%e\n%e\n%e\n%d",eg1cal,eg2cal,pcal,scrubtime);
@@ -145,78 +269,85 @@
 
 
 void status() {
-    if (state==0) {
-        g_lcd.character(9,0,5); // warning icon until 1 min up
-        g_lcd.character(8,0,6); // surface icon
+   /* if (state==0) {
+        g_lcd.character(7,0,33); // warning icon until 1 min up
+        g_lcd.character(8,0,83); // surface icon - letter S
     } else {
-        g_lcd.character(9,0,32);
+        g_lcd.character(7,0,32);
     }
-    if (state==1) g_lcd.character(8,0,6); // surface icon
-    if (state==2 && iseven(seconds)==1) g_lcd.character(8,0,4); // diving icon
-    if (state==2 && iseven(seconds)==0) g_lcd.character(8,0,68); // diving icon
-
+    if (state==1) g_lcd.character(8,0,83); // surface icon
+    if (state==2 && iseven(seconds)==1) g_lcd.character(8,0,4); // diving icon - inverse D
+    if (state==2 && iseven(seconds)==0) g_lcd.character(8,0,68); // diving icon - normal D
+    */
+// yes - this does nothing as all this is now done by vmessage
 }
 
 // warning and LED conditions
 
 void warning() {
-    if (depth>=mod && flash==1) g_lcd.character(13,0,5);
-    else g_lcd.character(13,0,32); // blank sapce
+    if (depth>=mod && flash==1) g_lcd.character(11,0,33);
+    else g_lcd.character(11,0,32); // blank sapce
 
 }
 
 // pick maximum of two values
-float maximum(float a,float b) {
+float maximum(float a,float b,float c) {
     float maximum;
-    if (a>b) maximum=a;
-    else maximum=b;
+    if(a>b && a>c) maximum=a;
+    if(b>a && b>c) maximum=b;
+    if(c>a && c>b) maximum=c;
     return(maximum);
 }
 
-// pick minimum  of two values
-float minimum(float a,float b) {
+// pick minimum  of three values
+float minimum(float a,float b,float c) {
     float minim;
-    if (a<b) minim=a;
-    else minim=b;
+    if (a<b && a < c) minim=a;
+    if(b<a && b<c) minim=b;
+    if(c<a && c <b) minim=c;
+
     return(minim);
 }
 
 
-
+// handset led control
 void leds() {
 // first turn everything off
     red=0;
     green=0;
     blue=0;
-    float ppo;
+    float ppox,ppom,sp;
     int mo=0;
-    mo=MODE;
-    ppo=maximum(ppo1,ppo2); // use max value to compute leds...
+    //mo=MODE;
+    if(setpoint==0) sp=lowsetpoint;
+    else sp=highsetpoint;
+    ppox=maximum(ppo1,ppo2,ppo3); // use max value to compute leds...
+    ppom=minimum(ppo1,ppo2,ppo3); // unless we want minimum
     if (mo==0) { // CCR mode
-        if (ppo<0.2 && flash==1) red=1; // flashing red means very bad things - getting low on oxygen!!!
-        if (ppo>0.2 && ppo < 1) red=1; // non-flashing red
-        if (ppo>=1.0 && ppo <1.2) {
+        if (ppom<0.2 && flash==1) {red=1;} // flashing red means very bad things - getting very --low on oxygen!!!
+        if (ppom>0.2 && ppox < (sp-0.15)) red=1; // non-flashing red
+        if (ppox>=(sp-0.15) && ppox <(sp-0.5)) {
             red=1;    // red-green
             green=1;
         }
-        if (ppo<1.3 && ppo >=1.2) green=1; // green - optimal range in ccr mode
-        if (ppo<1.4 && ppo >=1.3) {
+        if (ppox<(sp+0.05) && ppox >=(sp-0.05)) green=1; // green - optimal range in ccr mode
+        if (ppox<(sp+0.15) && ppox >=(sp+0.05)) {
             green=1;    // green-blue - high ppo2 be careful of spiking
             blue=1;
         }
-        if (ppo2<1.6 && ppo2>=1.4) blue=1; // DANGE ble high ppo2
-        if (ppo2>=1.6 && flash==1) blue=1;
+        if (ppox<1.6 && ppox>=(sp+0.15)) blue=1; // DANGER blue high ppo2
+        if (ppox>=1.6 && flash==1) blue=1;
     }
-    if (mo==1) { // SCR mode
-        if (ppo<0.2 && flash==1) red=1;
-        if(ppo2>=0.2 && ppo2 <0.26) red=1; // will give green red for low but not lethal ppo2s
-        if (depth < 0.8*mod && ppo>0.2) green=1;
+   /*if (mo==1) { // SCR mode
+        if (ppom<0.2 && flash==1) red=1;
+        if (ppom>=0.2 && ppox <0.26) red=1; // will give green red for low but not lethal ppo2s
+        if (depth < 0.8*mod && ppom>0.2) green=1;
         if (depth< mod && depth >=0.8*mod) {
             green=1;
             blue=1;
         }
         if (depth >=mod && flash==1) blue=1;
-    }
+    }*/
 
 }
 
@@ -224,64 +355,137 @@
 
 //read battery state and insert the battery symbol
 void battery() {
-    int batsym=0;
-    Vb=Vbatt; // read adc connected to battery via a 1/3 potential divider
-    if (Vb>0.606) batsym=1;
-    if (Vb>0.707) batsym=2;
-    if (Vb>0.808) batsym=3;
-    if (batsym >0) g_lcd.character(8,1,batsym);
-    if (batsym ==0 && flash==1) g_lcd.character(8,1,batsym);
-    if (batsym ==0 && flash==0) g_lcd.character(8,1,32);
+ char cgchar[32]={
+        6,9,9,9,9,9,9,15, // battery empty symbol         
+        6,9,9,9,9,15,15,15, // battery 50% symbol         
+        6,9,9,15,15,15,15,15, // battery 75% symbol       
+        6,15,15,15,15,15,15,15, // battery 100% symbol    
+        };
+
+
+    int batsym=0,i=0; // battery < 3.85V
+
+
+    // idea to build in 6 levels of battery indicator by on the fly reprogramming character 2 as required.
+    Vb=0;
+    for (i=0; i<4; i++) {
+        Vb=Vb+Vbatt; // read adc connected to battery via a 1/3 potential divider
+        wait(0.05);
+    }
+    Vb=Vb/4; // average 4 readings to reduce noise
+
+    if (Vb>0.388) batsym=8; //3.85-3.92V
+    if (Vb>0.395) batsym=16; // battery 3.92-4V
+    if (Vb>0.404) batsym=24; // battery . >4V
+
+
+// write the appropriate battery symbol into the first custom character
+    g_lcd.writeCommand(0x40); // set start address for CGRAM
+    for (i=0; i<8; i++) {
+        g_lcd.writeData(cgchar[i+batsym]);
+    }
+
+g_lcd.character(11,1,0); // battery symbol
+ if (batsym ==0 && flash==0) g_lcd.character(11,1,32); // bung in space if flashing
+
+
 }
 
+// sub to make the nice stop or no stop message work in locations 9,0 and 9,1
+void vmessage() {
+int i,d,cpos=0;
+// "INITSURFDIVE" in vertical chas - 1 custom char per two symbols
+   char mesg[48]={  17,31,17,0,31,6,12,31, 0,17,31,17,0,1,31,1,  19,21,25,0,31,16,31,0,    31,13,23,0,31,5,1,0,   31,17,14,0,17,31,17,0,  15,16,15,0,31,21,17,0};
+                  
+   
+ 
+   g_lcd.writeCommand(104); // set start address for CGRAM characrter #6 out of 8
+
+    for (i=0; i<16; i++) { // write 2 chars worth into this segment NO DECO
+   
+
+       g_lcd.writeData(mesg[i+state*16]);
+    } // endfor
+
+
+    
+g_lcd.character(12,0,5); // custom char 5
+
+g_lcd.character(12,1,6); // custom char 6
+
+}
+
+
 // subroutine to write the main display data
 //0123456789abcdef
 
 //x.xx:xx D XX  xx
 //x.xx:xx B XX xxx NB the warning, staus and battery icons are driven by separate subroutines.
 void display() {
-    int mo=0;
-    mo=MODE;
+    int mo=0,tmp=0;
+    //mo=MODE;
+    g_lcd.character(3,1,32);
 //1st line
-    g_lcd.locate(0,0);
-    g_lcd.printf("%1.2f:%.2d",ppo1,(int)fo1);
-    g_lcd.locate(10,0);
-    g_lcd.printf("%.2d",(int)depth);
-    g_lcd.locate(14,0);
-    g_lcd.printf("%.2d",(int)mod);
+
+ //ppo1
+    if(ppo1<1) tmp=(int)(ppo1*100); else tmp=(int)(ppo1*100-100);
+    g_lcd.locate(1,0);
+    g_lcd.printf("%02d ",tmp);
+    if(ppo1>=1) g_lcd.character(0,0,2);
+    if(ppo1<1) g_lcd.character(0,0,1);
+    
+    if(ppo2<1) tmp=(int)(ppo2*100); else tmp=(int)(ppo2*100-100);
+    g_lcd.locate(5,0);
+    g_lcd.printf("%02d ",tmp);
+    if(ppo2>=1) g_lcd.character(4,0,2);
+    if(ppo2<1) g_lcd.character(4,0,1);
+    
+    if(ppo3<1) tmp=(int)(ppo3*100); else tmp=(int)(ppo3*100-100);
+    g_lcd.locate(9,0);
+    g_lcd.printf("%02d ",tmp);
+    if(ppo3>=1) g_lcd.character(8,0,2);
+    if(ppo3<1) g_lcd.character(8,0,1);
+    
+    g_lcd.locate(13,0);
+    g_lcd.printf("%.2d",(int)depth); // depth
+    g_lcd.locate(4,1);
+    g_lcd.printf("%2dm",(int)mod); // mod
 //2nd line
     g_lcd.locate(0,1);
-    g_lcd.printf("%1.2f:%.2d",ppo2,(int)fo2);
-    g_lcd.locate(10,1);
-    g_lcd.printf("%.2d %.3d",divetime % 100 ,scrubtime % 1000); // modulo to avoid digits conflict - means divetime is always less than 100
+    tmp=minimum((float)fo1,(float)fo2,(float)fo3); // get min fraction of oxygen to display lowest for deco use
+    g_lcd.printf("%2d%%",tmp);
+    g_lcd.locate(13,1);
+    g_lcd.printf("%03d",scrubtime % 1000); // modulo to avoid digits conflict - means divetime is always less than 100
+    g_lcd.locate(8,1);
+    g_lcd.printf("%2d",(int)(divetime/60) % 100 ); // rolls back to zero if go over 99
     // bung in battery icon
     battery();
     status(); // this will set the diving / suface mode icon
-    warning(); // this will set the warning icon assuming that max ppo2 is exceeded
-
+ //   warning(); // this will set the warning ic on assuming that max ppo2 is exceeded
+    g_lcd.character(7,1,32); // space to cover any write error on top line
     leds(); // this sets the leds according to the various warning conditions
-    if (mo==0) {
+   /* if (mo==0) {
         g_lcd.character(7,1,99);    //'c' = ccr
     } else {
         g_lcd.character(7,1,115);    //'s' = scr
-    }
+    }*/
     // custom character setting to sort out dp in depths
 
 
     char cgchar[80]={
         7,5,5,5,23,0,0,0, // .0
         2,2,2,2,18,0,0,0, //  .1
-        7,1,7,4,23,0,0,0, // 0.2
-        7,1,3,1,23,0,0,0, // 0.3
-        5,5,7,1,17,0,0,0, //0.4
-        7,4,7,1,23,0,0,0, //0.5
-        7,4,7,5,23,0,0,0, //0.6
+        7,1,7,4,23,0,0,0, // .2
+        7,1,3,1,23,0,0,0, // .3
+        5,5,7,1,17,0,0,0, //.4
+        7,4,7,1,23,0,0,0, //.5
+        7,4,7,5,23,0,0,0, //.6
         7,1,2,2,18,0,0,0, //.7
         7,5,7,5,23,0,0,0, //.8
         7,5,7,1,17,0,0,0 //.9
 
     };
-
+// special dp digit for depth
     int i=0,d=0;
     d=(int)((depth-(int)depth)*10); // should be size of the 1st decimal place
 // do stuff here to set cstom chars
@@ -290,54 +494,181 @@
         g_lcd.writeData(cgchar[i+d*8]);
     }
 
-    g_lcd.character(12,0,7); // put in appropriate custom character
+    g_lcd.character(15,0,7); // put in appropriate custom character
+    
+    // display the current setpoint information
+    if(setpoint==0)    g_lcd.character(7,1,218); // letter down arrow for low setpoint
+    if(setpoint==1)    g_lcd.character(7,1,217); // Letter 'H'
+    if(flash==1)  g_lcd.character(7,1,115); // Letter ':'
 
 }
 
 
 
 
+// read sensors and generate calibrated outputs NB battery is read elsewhere
 
-// read sensors and generate calibrated outputs NB battery is read elsewhere
 void readsensors() {
-    float barometric=0,mod1,mod2;
-    ppo1=EG1*0.21/eg1cal; // eg1cal is 0.21bar ppO2
-    ppo2=EG2*0.21/eg2cal; // second oxygen cell ppO2
-    // NB this assumes that the calibration is done at exactly 1 bar.... - not always the case but ok for sea level diving
-    pressure=(PRESin*3.3-0.024)/(0.0038574); // pressure in kPa assuming standard cal for mpx5700 sensor SUSPECT
-    barometric=(pcal*3.3-0.024)/(0.0038574); // sealevel in kPa assuming standard cal for mpx5700 sensor
+    float barometric=0,mod1,mod2,mod3,temp,Vdepth=0,s1,s2,s3,MPXref=0;
+    int tc=0;
+  //  ppo1=EG1*0.21/eg1cal; // eg1cal is 0.21bar ppO2
+  //  ppo2=EG2*0.21/eg2cal; // second oxygen cell ppO2
+  //  ppo3=EG3*0.21/eg3cal;
+   
+    s1=0;
+    s2=0;
+    s3=0;
+    for(tc=0;tc<20;tc++) // noise on Vdepth so average readings to compensate
+    { 
+    Vdepth=Vdepth+(PRESin/DRATIO); // read the depth sensor and calculate the real value rather using the dividing ratio
+    wait_ms(10); // slows stuff down a bit but not a big problem
+    s1=s1+EG1; // read o2 sensors
+    s2=s2+EG2;
+    s3=s3+EG3;
+    MPXref=MPXref+V5V; // read 5V
+    wait_ms(10); // slows stuff down a bit but not a big problem
+    }
+    Vdepth=Vdepth/20; // now have the average
+    s1=s1/20-coff1;
+    s2=s2/20-coff2;
+    s3=s3/20-coff3;
+    MPXref=MPXref/20*3.3*2; // should be 5V
 
+    
+    // compute ppO2s
+    ppo1=s1*0.21/eg1cal;
+    ppo2=s2*0.21/eg2cal;
+    ppo3=s3*0.21/eg3cal;
+    
+    pc.printf("EG1=%f\tEG2=%f\tEG3=%f   \tMPXref=%f                             \r",s1,s2,s3,MPXref);
+    pressure=(Vdepth*3.3/MPXref-0.04)/0.0012858; // pressure in kpa NB - assums that the 5V is correct
+    //pressure=(PRESin*3.3/0.65006-0.04)/(0.0012858); // pressure in kPa assuming standard cal for mpx5700 sensor SUSPECT
+    // NB the mpx5700 runs off 5v so would be better to divide its output down by 3/5 to get full range measurement
+    //outputs. with no division the max mbed adc of 3.3V coresponds to 480kpa or about 38m depth.....
+    // standard spec on mpx5700 should be 5V*(P*0.0012858+0.04)
+    // new sensor has 3/5 divider built into sensor wiring.
+    //barometric=(pcal*3.3/0.65006-0.004)/(0.0012858); // sealevel in kPa assuming standard cal for mpx5700 sensor
+    barometric=(pcal*3.3/MPXref-0.04)/0.0012858; // barometric pressure (kpa) evaluated from calibration which we assume is baseline
     depth=(pressure-barometric)*0.1;   //100kPa=10m 1kPa=0.1m - this gives depth in m for freshwater.
-    if (depth<0) depth=0;
+    
+    if (depth<0) depth=0; // deals wtih noise that may lead to small variation in values
 
+// THESE SHOULD BE JUST 100*ppox/(pressure/100);
     fo1=100*ppo1/((pressure-barometric)/100+1); // pressure in bar = pressure /100 and want a % so multiply by 100 as well
     fo2=100*ppo2/((pressure-barometric)/100+1);
+    fo3=100*ppo3/((pressure-barometric)/100+1); // maybe these should be ppox/(depth/10+1)*100....?
 
-    if (fo1<0) fo2=0;
+    /*if (fo1<0) fo2=0;
     if (fo2<0) fo1=0;
-
-    //with two sensors will calculate mod from the largest ppo2 reading
+*/
+    //with three sensors will calculate mod from the largest ppo2 reading
     mod1=(1.4/(fo1/100)-1)*10;
     mod2=(1.4/(fo2/100)-1)*10;
+    mod3=(1.4/(fo3/100)-1)*10;
 
-    mod=minimum(mod1,mod2); // pick the least value
+    mod=minimum(mod1,mod2,mod3); // pick the least value
+    
+   // output for debugging to pc via usb line.
+  //  pc.printf("VDepth %f\tPressure %f\tbarometric %f\tdepth %f\t  \n\r",Vdepth,pressure,barometric,depth);
+    //NB - problem - we really need to monitor the 5V power line to ensure that it's driving the depth sensor ok.
+    // it may need thicker cables as it currently only manages 4.8V on the board when everything is running.
 
 }
 // get values back from cal file on local drive
 void recall() {
     FILE *fp=fopen("/local/CAL.dat","r");
-    fscanf(fp,"%e\n%e\n%e\n%d",&eg1cal,&eg2cal,&pcal,&scrubold);
+    fscanf(fp,"%e\n%e\n%e\n%e\n%d",&eg1cal,&eg2cal,&eg3cal,&pcal,&scrubold);
     fclose(fp); //NB file system locked on write so must make sure we close files in case want to reprogram etc...
 }
 
 // write the logfile opened and closed by start and end of dive
 void store_log() {
- Vb=Vbatt;
+
     //FILE *fp=fopen("/local/divelog.dat","a");
-    fprintf(lp,"%d\t%e\t%e\t%e\t%e\t%d\n",seconds,depth,ppo1,ppo2,Vb,scrubtime);
-   // fclose(fp);
+    float v5=0;
+    v5=V5V;
+    fprintf(lp,"%d\t%e\t%e\t%e\t%e\t%e\t%e\t%d\n",seconds,depth,ppo1,ppo2,ppo3,Vb,v5,scrubtime);
+
+    if (divetime % 60==0) fflush(lp);
+    // fclose(fp);
+}
+
+// read switches and report state
+int switches() {
+    int ss=0;
+    if (SW1==1 && SW2==1) ss=3;
+    if (SW2==1 && SW1==0) ss=2;
+    if (SW1==1 && SW2==0) ss=1;
+    return(ss);
 }
 
+// interpret the ppo2 data into a simple set of hud codes.
+int HUD_display()
+{ 
+    int i,j3,h1,h2,h3;
+    float cset=0;
+      char gcode[6]={0,1,3,2,6,4}; // grey code for HUD reading 'red,amber,green,cyan,blue'
+    
+    HUD_clr(); // clear the HUID ready for write
+   
+    
+    if(setpoint==0)  // low setpoint
+    {
+    cset=lowsetpoint;
+    }
+    
+    if(setpoint==1) // hgh setpoint
+    {
+    cset=highsetpoint;
+    }
+
+    h1=(int)((ppo1-cset)/0.1+3.5);
+    if(h1<1) h1=1;
+    if(h1>5) h1=5;
+    h2=(int)((ppo2-cset)/0.1+3.5);
+    if(h2<1) h2=1;
+    if(h2>5) h2=5;
+    h3=(int)((ppo3-cset)/0.1+3.5);
+    if(h3<1) h3=1;
+    if(h3>5) h3=5;
+    
+    if(h3>3) btest=0; // handle extra blue connected to btest setting btest low lights blue led
+    
+    
+    i=gcode[h1]+8*gcode[h2]+64*gcode[h3]; // this is possible bigger than a char so have to typeconvert
+    HUD_write(i);
+       
+}
+
+// sub to flash HUD n times as a warning of setpoint shift
+int HUD_flash(int n)
+{
+    int i;
+    for(i=0;i<n;i++)
+    {
+    HUD_clr();
+    wait(0.2);
+    HUD_white();
+    wait(0.05);
+    }
+}
+
+int setswitch()
+{
+    if(setpoint==0 && depth >(switchdepth+0.5)) 
+    { 
+        setpoint=1; // handle switch from low to high
+        HUD_flash(4);
+        // flash the hud here
+    }
+    
+    if(setpoint==1 && depth < (switchdepth -0.5))
+    {
+        setpoint=0;  // swtich to low setpoint
+        HUD_flash(2);
+        // flash the HUD here
+    }
+}
 
 int main() {
 // first some local variables
@@ -345,9 +676,11 @@
     int startdive=0,endclock=0; // value of seconds when dive starts and counter to decide if dive complete...
 
     int minutes=0; // minutes is elapsed minutes since start of prog
-    int j=0; // general loop counting variable
+    int j=0,scount=1;; // general loop counting variable
+    int sw=0; // status of the switches in the handset
+    char schars[4]={32,0xd9,0xda,0xfb}; // up arrow, down arrow and both arrows;
 
-
+    bool mdir=0;
 
     set_custom_char(); // does what it says on the tin really
     g_lcd.cls();
@@ -357,74 +690,103 @@
     g_lcd.printf("CAL?");
     battery();
     j=0;
+wait(0.2);
+
     // get cal values last used from local drive
     recall();
     // display the correct scrubber time
-    scrubtime=scrubtime+scrubold;
+    scrubtime=scrubold;
 // hang about waiting for the cal switch to be pressed in ccase it is
-    while (seconds-startuptime<20) {
-        seconds=getseconds();
+    while (scount<30) {
+        seconds=getseconds(); // NB if this hangs then nothing works :( - usually means 5V is dodgy
+        red=1;
+        green=1;
+        blue=1; // light all leds
+
         g_lcd.locate(5,1);
-        g_lcd.printf("%.2d",21-(seconds-startuptime));
+        g_lcd.printf("%.2d ",30-scount);
         if (j>1) flash=1;
         else flash=0;
         battery(); // bung in battery symbol.
         g_lcd.locate(7,0);
         g_lcd.printf( "%.2d:%.2d:%.2d", hours,min,sec);
-        if (CAL==0) {
+      //  if (CAL==0) {
+        if(SW1==0) {
             calibrate();
-
+            scount=31; // make sure it goes to next frame ok
         }
-        wait(0.2);
+        wait(0.05);
+
+
         j=(j+1) % 4;
-    }
+        if(flash==0) HUD_white();
+        else HUD_clr();
+        scount++;
+    } 
     g_lcd.cls();
 
 
     // ok there are three states in this system
 //MAIN LOOP ONCE STARTUP PROTOCOLS ARE COMPLETED
     j=0;
+    g_lcd.cls();
     while (1) {
-        wait(0.2); //stop screen flicker
+        wait(0.1); //stop screen flicker
         readsensors();
+        setswitch(); // check the setpoint and adjust if crossing the swtich depth
+        HUD_display(); // write the HUD codes
         seconds=getseconds();
         minutes=(int)(((float)seconds-(float)startuptime)/60);
-
+        led1=seconds % 2; // flash the onboard led to make it clear stuff is running
 
         if (j>1) flash=1;
         else flash=0;
 
         display(); // write the display
+        HUD_display(); // write the HUD
+      // sw=switches(); // read the switches and report their state
+      // if(SW1==0) g_lcd.character(11,0,0xEF); else g_lcd.character(11,0,32); // NB here is possible alternate display underwater switching point
+        // HERE do deco calcs - update tissues and compute desat , nostop or ascent times as required.
 
         // setup state variable
-        if (minutes<1) state=0; // startup mode - do nothing just wait to allow sensor readings to settle.
+      if (minutes<1) state=0; // startup mode - do nothing just wait to allow sensor readings to settle.
         if (minutes>=1 && state==0) state=1; // surface mode - ok to go for a dive now
         if (minutes>=1 && depth>0.8 && state==1) {
             state=2; // enter dive mode
             lp=fopen("/local/divelog.dat","a");
+            fprintf(lp,"#startdive = %d\n#seconds\tdepth\tppo1\tppo2\tppo3\tVb\t\tV5V\tscrubtime\n",seconds); // bung in a header here in case one needs it
             if (startdive==0) startdive=seconds; // set start of divetime. don't do this twice
             endclock=0; // reset end of dive clock
         }
-        if (state==2) {
-            divetime=(int)(((float)seconds-(float)startdive)/60); // time since start of dive in minutes.
+        // in dive mode - things to do, 1 update divetime and scrubber time, 2 write log data 3 check for end of dive...
+       if (state==2) {
+            // divetime=(int)(((float)seconds-(float)startdive)/60.0); // time since start of dive in minutes.
+            divetime=(seconds-startdive); // divetime no recorded in seconds since start of dive
+
             // do deco calcs here when implemented
-            if ((seconds-startdive) %15 ==0) store_log(); // this saves the dive profile and sensor optputs in a file called divelog.dat every 15s
-            if (depth<=0.3) {
+            if (divetime %15 ==0) store_log(); // this saves the dive profile and sensor optputs in a file called divelog.dat every 15s
+            if (depth<=0.5) {
                 endclock=endclock+1;
 
                 if (endclock>150) {
-                    state=1; // 30s at shallower than 0.3m and we return to surface mode.
+                    state=1; // 30s at shallower than 0.3m and we return to surface mode. */
                     FILE *fp=fopen("/local/CAL.dat","w");
                     fprintf(fp,"%e\n%e\n%e\n%d",eg1cal,eg2cal,pcal,scrubtime);
                     fclose(fp); //NB file system locked on write so must make sure we close files in case want to reprogram etc...
+                    
+                    store();
                     fclose(lp);
-                }
-            }
-            scrubtime=scrubold+divetime; //
-        }
+                } // endif endclock
+                   
+            } // end if depth
+            scrubtime=scrubold+(divetime/60); //
+        } // end state 2
 
 
         j=(j+1) %4; // flash control variable = used to make the warnings flash for 0.4s duty cycle
+
+
+        vmessage(); // call to generate status message in vertical segment
     } // end while
 } //end main