Software to drive a monitor unit for a closed circuit rebreather using 3 electrogalvanic oxygen sensor cells run through an amplifier (lm324) . Uses a separate ds1307 clock IC to get timestamp values for logged data.

Dependencies:   DS1307 TextOLED_custom mbed

The main electornics is housed in another pod mounted on the back of the unit. I'm using an mbed lpc11u24 to drive everything which comes with a flash drive for data logging built in. It has an external ds1307 clock chip added and a very cheapo lm324 quad op-amp to amplify the o2 sensor signals from the 10s of mV range by 30x so that ppo2=0.21 corresponds to about 0.3V. I still have to do some ADC averaging with this amplifier and do have to calibrate out the individual offsets of the chip but this works ok now that I've worked out which amp is on which adc...

Files at this revision

API Documentation at this revision

Comitter:
pegcjs
Date:
Mon Jan 14 12:52:45 2013 +0000
Parent:
6:ab2d7d0a9b07
Child:
8:f45e654b47d0
Commit message:
Fixed bug in setswitch prevennting switch back to lowsetpoint on ascent; added hud flashes 4 flashes = going to high sp , 2 flashes = going to low sp.

Changed in this revision

DS1307.lib Show annotated file Show diff for this revision Revisions of this file
Rebmon_main.cpp Show annotated file Show diff for this revision Revisions of this file
TextOLED.lib Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DS1307.lib	Mon Jan 14 12:52:45 2013 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/harrypowers/code/DS1307/#c3e4da8feb10
--- 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
 
--- a/TextOLED.lib	Tue Aug 07 16:25:18 2012 +0000
+++ b/TextOLED.lib	Mon Jan 14 12:52:45 2013 +0000
@@ -1,1 +1,1 @@
-http://mbed.org/users/simon/code/TextLCD/#8e272a434581
+http://mbed.org/users/simon/code/TextLCD/#25a700bce989