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

Rebmon_main.cpp

Committer:
pegcjs
Date:
2012-08-07
Revision:
6:ab2d7d0a9b07
Parent:
5:35417986539a
Child:
7:f93b7eaab5f6

File content as of revision 6:ab2d7d0a9b07:

//lpc1124lcddemo
#include "ds1307.h"
#include "mbed.h"
#include "TextOLED.h"


#define METRE 0.02 // change in DEPin for 1m depth

//pin assignments and declarations
// LCD display
TextLCD g_lcd(p26, p25, p24, p23, p22, p21);  // RS, E, DB4, DB5, DB6, DB7
//backlight
DigitalOut backlight(p29);

//onboard leds
DigitalOut led1(LED1);
DigitalOut led2(LED2);

// warning leds
DigitalOut red(p34);
DigitalOut green(p33);
DigitalOut blue(p30);


// 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

// log data storage
LocalFileSystem local("local");

// adc inputs for sensors
AnalogIn PRESin(p20);
AnalogIn EG1(p19);
AnalogIn EG2(p18);
AnalogIn Vbatt(p17);

// realtime clock
DS1307 my1307(p28,p27); // start DS1307 class and give it pins for connections of the DS1307 device

// variables for realtime clock
int sec = 0;
int min = 0;
int hours = 0;
int day = 0;
int date = 0;
int month = 0;
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 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

// 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;
// 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

FILE *lp; // file pointer for log file

//===== sub to get time from ds1307 and create the 'seconds' which is a version of timestamp....
int getseconds() {
    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;
    //simple timestamp = # seconds since midnight jan 1st 2000 if all months were 30 days....
    // ie wrong but simpler than the real thing
    return(secondst);
}


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
    };
    int i=0;
// do stuff here to set cstom chars
    g_lcd.writeCommand(0x40); // set start address for CGRAM
    for (i=0; i<64; i++) {
        g_lcd.writeData(cgchar[i]);
    }

}

// stash cal values on local drive
void store() {
    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...
}


// 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;
    // 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;
        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));
        wait(1);
    }
    //average
    ppo1=ppo1/20;
    ppo2=ppo2/20;
    // set calibration variables
    eg1cal=ppo1;
    eg2cal=ppo2;
    pcal=pres/20; // surface pressure....
    scrubtime=0; // reset the scrubber timer to zero.
    // 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);
      fclose(fp); //NB file system locked on write so must make sure we close files in case want to reprogram etc...*/
    store();
}

// sub to test if a variable is an even number
int iseven(int g) {
    int test=0;
    if (g%2 ==0) test=1;
    return(test);
}


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
    } else {
        g_lcd.character(9,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

}

// 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

}

// pick maximum of two values
float maximum(float a,float b) {
    float maximum;
    if (a>b) maximum=a;
    else maximum=b;
    return(maximum);
}

// pick minimum  of two values
float minimum(float a,float b) {
    float minim;
    if (a<b) minim=a;
    else minim=b;
    return(minim);
}



void leds() {
// first turn everything off
    red=0;
    green=0;
    blue=0;
    float ppo;
    int mo=0;
    mo=MODE;
    ppo=maximum(ppo1,ppo2); // use max value to compute leds...
    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) {
            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) {
            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 (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 (depth< mod && depth >=0.8*mod) {
            green=1;
            blue=1;
        }
        if (depth >=mod && flash==1) blue=1;
    }

}



//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);
}

// 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;
//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);
//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
    // 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

    leds(); // this sets the leds according to the various warning conditions
    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,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

    };

    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
    g_lcd.writeCommand(120); // set start address for CGRAM
    for (i=0; i<8; i++) {
        g_lcd.writeData(cgchar[i+d*8]);
    }

    g_lcd.character(12,0,7); // put in appropriate custom character

}





// 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

    depth=(pressure-barometric)*0.1;   //100kPa=10m 1kPa=0.1m - this gives depth in m for freshwater.
    if (depth<0) depth=0;

    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);

    if (fo1<0) fo2=0;
    if (fo2<0) fo1=0;

    //with two sensors will calculate mod from the largest ppo2 reading
    mod1=(1.4/(fo1/100)-1)*10;
    mod2=(1.4/(fo2/100)-1)*10;

    mod=minimum(mod1,mod2); // pick the least value

}
// 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);
    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);
}


int main() {
// first some local variables
    int startuptime=getseconds();
    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



    set_custom_char(); // does what it says on the tin really
    g_lcd.cls();
    g_lcd.locate(0, 0);
    g_lcd.printf( "RebMon");
    g_lcd.locate(0,1);
    g_lcd.printf("CAL?");
    battery();
    j=0;
    // get cal values last used from local drive
    recall();
    // display the correct scrubber time
    scrubtime=scrubtime+scrubold;
// hang about waiting for the cal switch to be pressed in ccase it is
    while (seconds-startuptime<20) {
        seconds=getseconds();
        g_lcd.locate(5,1);
        g_lcd.printf("%.2d",21-(seconds-startuptime));
        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) {
            calibrate();

        }
        wait(0.2);
        j=(j+1) % 4;
    }
    g_lcd.cls();


    // ok there are three states in this system
//MAIN LOOP ONCE STARTUP PROTOCOLS ARE COMPLETED
    j=0;
    while (1) {
        wait(0.2); //stop screen flicker
        readsensors();
        seconds=getseconds();
        minutes=(int)(((float)seconds-(float)startuptime)/60);


        if (j>1) flash=1;
        else flash=0;

        display(); // write the display

        // 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) 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");
            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.
            // 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) {
                endclock=endclock+1;

                if (endclock>150) {
                    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...
                    fclose(lp);
                }
            }
            scrubtime=scrubold+divetime; //
        }


        j=(j+1) %4; // flash control variable = used to make the warnings flash for 0.4s duty cycle
    } // end while
} //end main