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
Diff: Rebmon_main.cpp
- Revision:
- 7:f93b7eaab5f6
- Parent:
- 6:ab2d7d0a9b07
- Child:
- 8:f45e654b47d0
diff -r ab2d7d0a9b07 -r f93b7eaab5f6 Rebmon_main.cpp --- 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