Modification in routine to select substance

Dependencies:   mbed Servo

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers main.cpp Source File

main.cpp

00001 //Firmware to drive Eton bee trainer breadboard
00002 char* fwversion = "0.91";                                          //For published versions use format "n.nn"
00003                                                                     //For development versions add "D" to end of string
00004 //This version published on 31/03/2011
00005 
00006 //Include libraries
00007 #include "mbed.h"
00008 #include "Servo.h"                                                  //need this library to control servos
00009 
00010 //Pin assignments and external functions
00011 DigitalOut led1(LED1), led2(LED2), led3(LED3), led4(LED4);          //for debugging purposes only
00012 DigitalOut buzzer(p5);                                              //pin high means buzzer on
00013 DigitalOut airswitcher(p7);                                         //pin high shorts coax for air switcher valve
00014 Serial display(p9,p10);                                             //serial display controller by ByVAC BV4618 controller
00015 AnalogOut irLED(p18);                                               //only one analog out controls both IR LEDS in parallel
00016 AnalogIn ptor1(p19), ptor2(p20);                                    //analog input for beeholder phototransistors
00017 Servo arm1(p21), elbow1(p22);                                       //servos for station 1 robot arm
00018 Servo arm2(p23), elbow2(p24);                                       //servos for station 2 robot arm
00019 I2C beeholder(p28,p27);                                             //set up I2C communication to read beeholder serial no
00020 DigitalOut bhsel1(p29), bhsel2(p30);                                //select lines for station 1 and station 2 - pull low to select holder
00021 DigitalIn upsw(p13), risw(p14), dnsw(p15), lesw(p16), oksw(p17);    //footswitch connected parallel to OK switch
00022 LocalFileSystem local("local");                                     //allow access to the mbed "thumbdrive" as a file system
00023 FILE *logfile = fopen("/local/etonlog.csv","w");                    //creates new file etonlog.csv and set for writing. Overwrites existing file!
00024 
00025 //Definition of global constants
00026     //station 1 (left) robot arm positions
00027     float arm1basepos = 0.4;
00028     float elbow1basepos = 0.07;
00029     float arm1dippos = 0.2;
00030     float elbow1dippos = 0.6;
00031     float arm1ticklepos = 0.65;
00032     float elbow1ticklepos = 0.07;
00033     float arm1feedpos = 0.7;
00034     float elbow1feedpos = 0.4;
00035 
00036     //station 2 (right) robot arm positions
00037     float arm2basepos = 0.4;
00038     float elbow2basepos = 0.07;
00039     float arm2dippos = 0.2;
00040     float elbow2dippos = 0.07;
00041     float arm2ticklepos = 0.2;
00042     float elbow2ticklepos = 0.07;
00043     float arm2feedpos = 0.2;
00044     float elbow2feedpos = 0.07;
00045     
00046     //robot arm hold and move times                                   all times are in seconds
00047     float diptime = 1;                                              //time that brush stays down dipped in sugar water
00048     float tickletimeout = 3;                                        //maximum time to tickle if no PER is detected
00049     float feedtime = 3;                                             //feeding time
00050     
00051     //PER detection parameters
00052     float calsetval = 1.4;                                          //voltage to which phototransistor output will be calibrated  
00053     float PERsetval = 2.1;                                          //phototransistor output needs to be above this voltage for PER
00054     float PERtime = 0.2;                                            //phototransistor output needs to stay high this long in seconds
00055     
00056     //I2C address
00057     int addr = 0xA2;                                                //address is actually 0x51, but mbed expects left-shifted address   
00058 
00059 //Definition of global variables
00060 
00061 struct holderrecord {                                               //used to hold data on beeholder
00062     int serialno;                                                   //beeholder serial number 0-9999
00063     float calvalue;                                                 //IR-LED calibration value
00064     char cycleno;                                                   //number of cycles gone through in this session
00065     char reslastcycle;                                              //result of last cycle, 1 for PER
00066     char ticklenextcycle;                                           //whether tickling was done for last cycle
00067     time_t tstamp;                                                  //timestamp when last cycle was run
00068 };
00069 
00070 holderrecord currentholder;                                         //struct record for the current beeholder
00071 holderrecord sessionrecord[30];                                     //sessionrecord contains holderrecords for up to 30 beeholders
00072 
00073 int numbeeholders;                                                  //number of beeholders used in current session
00074 int station;                                                        //which station is used in this session
00075 
00076 //Substance strings
00077 
00078 struct submenuitem {
00079     char* displaystring;                                            //keep display string to 19 characters or less, otherwise display screws up
00080     char* logstring;                                                //this is the string that gets printed in the logfile, and can be longer then 19 characters
00081 };
00082 
00083 submenuitem submenu[11];
00084 int substanceindex;                                                 //this variable contains the index number (in the list) of the selected substance
00085 
00086 //Function declarations
00087 
00088 //Short beep
00089 void beeponce() {
00090     buzzer = 1;
00091     wait(0.1);
00092     buzzer = 0;
00093 }
00094 
00095 //beep beep beep
00096 void multibeeps(int number, float interval) {
00097     for(int i=0;i<number;i++){
00098         beeponce();
00099         wait(interval);
00100     }
00101 }
00102 
00103 //Clears and homes the display
00104 void cleardisplay() {
00105     display.printf("\e[2J\e[H");
00106 }
00107 
00108 //Returns both robot arms to base position
00109 void armstobase() {
00110     elbow1 = elbow1basepos;
00111     elbow2 = elbow2basepos;
00112     wait(0.5);                                                      //need this delay to avoid snagging brush on IR LED
00113     arm1 = arm1basepos;
00114     arm2 = arm2basepos;
00115 }
00116 
00117 //Fills the substance menu array with text
00118 void fillsubstancemenu() {
00119     submenu[0].displaystring = "No substance";
00120     submenu[0].logstring = "Lack of substance";
00121     
00122     submenu[1].displaystring = "Coffee";
00123     submenu[1].logstring = "Espresso macchiato";
00124     
00125     submenu[2].displaystring = "Earl Grey";
00126     submenu[2].logstring = "Nice cup of tea";
00127     
00128     submenu[3].displaystring = "Apple Pie";
00129     submenu[3].logstring = "Mom's homemade apple pie - longer then 19 characters";
00130     
00131     submenu[4].displaystring = "Substance 04";
00132     submenu[4].logstring = "Substance 4";
00133     
00134     submenu[5].displaystring = "Substance 05";
00135     submenu[5].logstring = "Substance 5";
00136     
00137     submenu[6].displaystring = "Substance 06";
00138     submenu[6].logstring = "Substance 6";
00139     
00140     submenu[7].displaystring = "Substance 07";
00141     submenu[7].logstring = "Substance 7";
00142     
00143     submenu[8].displaystring = "Substance 08";
00144     submenu[8].logstring = "Substance 8";
00145     
00146     submenu[9].displaystring = "Substance 09";
00147     submenu[9].logstring = "Substance 9";
00148     
00149     submenu[10].displaystring = "Red Roses";
00150     submenu[10].logstring = "I never promised you a rose garden";
00151 }
00152 
00153 //Initialises sessionrecords
00154 void sessionrecordinit() {
00155     for (int i=0; i<30; i++) {                                      //set base values for all possible beeholders
00156         sessionrecord[i].serialno = 0;                              //set serialno to 0 so we can detect a unresponsive holder
00157         sessionrecord[i].calvalue = 0;
00158         sessionrecord[i].cycleno = 0;
00159         sessionrecord[i].reslastcycle = 0;
00160         sessionrecord[i].ticklenextcycle = 1;                       //default is to tickle on each cycle
00161     }
00162 }
00163 
00164 //Initialisation on power-on
00165 void initialise() {
00166 
00167     //Set pins to a defined state
00168     led1 = led2 = led3 = led4 =0;                                   //internal LEDs only used for debugging purposes
00169     buzzer = 0;                                                     //buzzer off
00170     airswitcher = 0;                                                //relay contacts open, clean air position
00171     bhsel1 = bhsel2 = 1;                                            //I2C select lines high to deselect both beeholders
00172     upsw.mode(PullUp);                                              //set pull-up on all frontpanel switches
00173     risw.mode(PullUp);
00174     dnsw.mode(PullUp);
00175     lesw.mode(PullUp);
00176     oksw.mode(PullUp);
00177     
00178     //Set robot arms to base positions
00179     armstobase();
00180     
00181     //initialise the display
00182     display.baud(19200);                                            //set display baud rate to 19200
00183     display.putc(0x0d);                                             //send CR to display - this needs to be first character sent after power-up
00184     wait(0.5);                                                      //wait for display to adjust to serial baud rate
00185     display.printf("\e[4L\e[20c");                                  //set display to 4 lines of 20 characters
00186     cleardisplay();                                                 //clear screen and home cursor
00187     
00188     //display splash screen with time
00189     display.printf("Eton trainer v%s\r", fwversion);                //show firmware release version
00190     time_t seconds = time(NULL);                                    //creates timestamp
00191     char bufferd[20], buffert[20];
00192     strftime(bufferd, 20, "%d/%m/%Y", localtime(&seconds));         //formats date part of timestamp
00193     display.printf("Date: %s\r", bufferd);                          //displays date
00194     strftime(buffert, 20, "%X", localtime(&seconds));               //formats time part of timestamp
00195     display.printf("Time: %s\r", buffert);                          //displays time
00196     wait(3);                                                        //display splash screen
00197     cleardisplay();                                                 //clear screen and home cursor
00198     
00199     //initialise sessionrecord
00200     sessionrecordinit();
00201     
00202     //initialise substance menu
00203     fillsubstancemenu();
00204     
00205     //enter info in logfile.csv
00206     fprintf(logfile, "%s\r", bufferd);                              //writes date, time and firmware version
00207     fprintf(logfile, "%s\r", buffert);
00208     fprintf(logfile, "v%s\r", fwversion);
00209     
00210     beeponce();                                                       //three beeps to signal initialisation ready
00211 }
00212 
00213 //Returns the index of the selected substance menu item
00214 int subchoice(submenuitem submenu[11]) {
00215     bool up, down, done;
00216     int i;
00217     up = down = done = 0;
00218     i = 0;
00219     cleardisplay();
00220     display.printf("Substance selection");
00221     wait(3);
00222     cleardisplay();
00223     while (!done) {
00224         display.printf("%s\r", submenu[i].displaystring);
00225         display.printf("\rUP/DOWN to scroll\rOK to select");
00226         wait(1);                                                  //delay required to avoid very rapid cycling of the menu
00227         while (!up && !down && !done) {
00228             up = !upsw;
00229             down = !dnsw;
00230             done = !oksw;
00231         }
00232         if (down) {
00233             i++;
00234             if (i==11) i = 0;
00235         }
00236         if (up) {
00237             if (i==0) i = 11;
00238             i--;
00239         }
00240         cleardisplay();
00241     }
00242     return i;
00243 }
00244 
00245 //Converts the last 4 digits in the serial number string into a integer 0-9999
00246 int serialstring2int(char bser[8]) {
00247     int tempserial = 0;    
00248     tempserial = tempserial + (bser[4]-0x30)*1000;                  //5th digit is thousands
00249     tempserial = tempserial + (bser[5]-0x30)*100;                   //6th digit is hundreds
00250     tempserial = tempserial + (bser[6]-0x30)*10;                    //7th digit is tens
00251     tempserial = tempserial + (bser[7]-0x30);                       //8th digit is units
00252     return tempserial;
00253 }
00254 
00255 //beeholder resets on rising edge of select line
00256 void resetbeeholder() {                                             //need this as mounting beeholder causes undefined start of beeholder firmware
00257     bhsel1 = bhsel2 = 0;
00258     wait(0.1);
00259     bhsel1 = bhsel2 = 1;
00260     wait(0.3);
00261 }
00262 
00263 //Reads beeholder serial number from station 1 or 2
00264 int getserialno(int station) {
00265     char bser[8];                                                   //define 8-byte serial number string to read
00266     resetbeeholder();                                               //does not work without this!!
00267     for (int i=0;i<8;i++) bser[i]=0x30;
00268     if (station==1) bhsel1=0;                                       //pull select line station 1 low
00269     if (station==2) bhsel2=0;                                       //pull select line station 2 low
00270     wait(0.2);
00271     beeholder.stop();                                               //I2C stop condition
00272     wait(0.2);                                                      //delay for beeholder to respond
00273     beeholder.write(addr,0x0,1);                                    //initial write before read
00274     beeholder.read(addr,bser,8);                                    //read 8 byte serial number
00275     bhsel1 = bhsel2= 1;                                             //pull both select lines high
00276     int serialno = serialstring2int(bser);                          //translate serial number string to integer
00277     return serialno;
00278 }    
00279 
00280 //Returns 1 if a PER is detected on this beeholder within timeout seconds
00281 int detectPER(int station, float timeout) {
00282     Timer ttotal, tper;
00283     ttotal.start();                                                 //start timers for time-out and PER-detect
00284     ttotal.reset();
00285     tper.start();
00286     tper.reset();
00287     if (station == 1) {
00288         while ((ttotal.read() < timeout) && (tper.read() < PERtime)) {        //loop until timeout or PER detected
00289             wait_ms(10);
00290             if (ptor1 * 3.3 < PERsetval) tper.reset();              //if phototransistor voltage below treshold keep PER timer in reset
00291                                                                     //if above treshold let timer run until it reaches PERtime
00292         }
00293     }
00294     if (station == 2) {
00295          while ((ttotal.read() < timeout) && (tper.read() < PERtime)) {
00296             if (ptor2 * 3.3 < PERsetval) tper.reset();
00297         }
00298     }
00299     ttotal.stop();
00300     tper.stop();
00301     return (tper.read() >= PERtime);                                //if the loop exit condition was a PER, return a TRUE value
00302 }
00303 
00304 //Checks if bee shows PER when stimulated with odour but without feeding
00305 int checktrained(int station) {
00306     airswitcher = 1;
00307     int responded = detectPER(station, 6);                          //PER needs to be detected within 6 seconds
00308     airswitcher = 0;
00309     return responded;
00310 }
00311 
00312 //Function performs beeholder/IR-led calibration
00313 float calibrate(int station, int runs) {
00314     float tempcal;
00315     tempcal=0.5;                                                    //start calibration at 50% voltage
00316     irLED=tempcal;
00317     float calstep;                                                  //start calstep at 25% voltage
00318     calstep = tempcal/2;
00319     for (int i=0; i<10; i++) {                                      //does a "10-bit binary search" for the correct voltage to get a good response
00320         irLED = tempcal;
00321         wait(0.1);                                                  //important to allow AD converter to settle
00322         if (ptor1 < calsetval/3.3) {                                //check phototransistor voltage against desired value
00323             tempcal = tempcal - calstep;                            //if phototransistor voltage is too low then reduce brightness
00324         }
00325         else {
00326             tempcal = tempcal + calstep;                            //if phototransistor voltage is too high then increase brightness
00327         }
00328         calstep = calstep/2;                                        //on each loop of the for-cycle make smaller changes to IR LED voltage
00329     }
00330     float calib;
00331     calib = tempcal;                                                //set preliminary calibration to the value just measured
00332     for (int j=1; j<runs; j++) {                                    //run another j-1 runs, this corrects for antennae-movement as
00333         tempcal=0.5;                                                //we use the lowest calibration value from j runs
00334         irLED=tempcal;                                              //this is similar to what we do in the cassettes
00335         calstep = tempcal/2;
00336         for (int i=0;i<10;i++) {
00337             irLED = tempcal;
00338             wait(0.1);
00339             if (ptor1 < calsetval/3.3) {
00340                 tempcal = tempcal - calstep;
00341             }
00342             else {
00343                 tempcal = tempcal + calstep;
00344             }
00345             calstep = calstep/2;
00346         }
00347         if (tempcal < calib) calib = tempcal;                       //use the lowest of j calibration values
00348     }
00349     return calib;
00350 }
00351 
00352 //switches the IR LED on at the right brightness level for the beeholder
00353 void IRledON(int i) {
00354     irLED = sessionrecord[i].calvalue;
00355  }
00356  
00357 //moves arm1 to a position in a specified time, allowing speed control
00358 void arm1move (float endpos, float movetime) {
00359     float startpos = arm1.read();
00360     int numsteps = (movetime * 50);                                 //50 pulses/second, so each step is 1 servo pulse
00361     for (int i=0; i<numsteps; i++) {
00362         arm1 = startpos + i * (endpos - startpos)/numsteps;
00363         wait(movetime/numsteps);
00364     }
00365     arm1 = endpos;
00366 }
00367 
00368 //moves arm2 to a position in a specified time, allowing speed control
00369 void arm2move (float endpos, float movetime) {
00370     float startpos = arm2.read();
00371     int numsteps = (movetime * 50);                                 //50 pulses/second, so each step is 1 servo pulse
00372     for (int i=0; i<numsteps; i++) {
00373         arm2 = startpos + i * (endpos - startpos)/numsteps;
00374         wait(movetime/numsteps);
00375     }
00376     arm2 = endpos;
00377 }
00378 
00379 //moves elbow1 to a position in a specified time, allowing speed control
00380 void elbow1move (float endpos, float movetime) {
00381     float startpos = elbow1.read();
00382     int numsteps = (movetime * 50);                                 //50 pulses/second, so each step is 1 servo pulse
00383     for (int i=0; i<numsteps; i++) {
00384         elbow1 = startpos + i * (endpos - startpos)/numsteps;
00385         wait(movetime/numsteps);
00386     }
00387     elbow1 = endpos;
00388 }
00389 
00390 //moves elbow2 to a position in a specified time, allowing speed control
00391 void elbow2move (float endpos, float movetime) {
00392     float startpos = elbow2.read();
00393     int numsteps = (movetime * 50);                                 //50 pulses/second, so each step is 1 servo pulse
00394     for (int i=0; i<numsteps; i++) {
00395         elbow2 = startpos + i * (endpos - startpos)/numsteps;
00396         wait(movetime/numsteps);
00397     }
00398     elbow2 = endpos;
00399 }
00400 
00401 //Performs a conditioning cycle. if tickle==0, no tickling takes place. Return==1 if a PER has been detected
00402 int condcycle(int station, int tickle) {
00403     int perseen;
00404     perseen = 0;
00405     //for station 1
00406     if (station == 1) {
00407         //dip brush
00408         arm1move(arm1dippos, 0.5);
00409         elbow1move(elbow1dippos, 0.3);
00410         wait(diptime);
00411         elbow1move(elbow1basepos, 0.3);
00412         arm1move(arm1basepos, 0.5);
00413         //switch air to supply trace vapour
00414         airswitcher = 1;                                            //air contains target vapour
00415         display.printf("Vapour ON");
00416         wait (0.5);
00417         //tickle
00418         if (tickle) {                                               //if tickling, first tickle then wait for PER or timeout
00419             elbow1move(elbow1ticklepos, 0.2);
00420             arm1move(arm1ticklepos, 1.5);                           //slower move of arm towards bee
00421             perseen = detectPER(1, tickletimeout);                  //tickle until timeout or PER detected
00422         }
00423         //or not tickle
00424         else {
00425             perseen = detectPER(1, tickletimeout);                  //if not tickling, wait for PER or timeout then move to pre-feeding position
00426             arm1move(arm1ticklepos-0.05, 1);                        //move to position between LED and holder
00427             elbow1move(elbow1ticklepos, 0.3);
00428         }
00429         //feeding only if you have tickled or a PER has been detected
00430         if (tickle || perseen) {                                    //only feed if a PER has been detector, or "tickle" is true
00431             elbow1move(elbow1feedpos, 0.3);
00432             arm1move(arm1feedpos, 0.3);
00433             wait(feedtime);
00434             arm1move(arm1ticklepos -0.05, 0.3);
00435             elbow1move(elbow1ticklepos, 0.3);
00436         }
00437         //move back to base position
00438         arm1 = arm1basepos;                                         //use fast move here
00439         elbow1 = elbow1basepos;                                     //use fast move here
00440         airswitcher = 0;                                            //air valve to clean air
00441         display.printf("\e[DFF\r");                                 //rewrite "ON" to "OFF" on display
00442     }
00443     //for station 2
00444     if (station == 2) {
00445         //dip brush
00446         arm2move(arm2dippos, 0.5);
00447         elbow2move(elbow2dippos, 0.3);
00448         wait(diptime);
00449         elbow2move(elbow2basepos, 0.3);
00450         arm2move(arm2basepos, 0.5);
00451         //switch air to supply trace vapour
00452         //airswitcher = 1;                                          //no air switching for station 2
00453         wait (0.5);
00454         //tickle
00455         if (tickle) {                                               //if tickling, first tickle then wait for PER or timeout
00456             elbow2move(elbow2ticklepos, 0.2);
00457             arm2move(arm2ticklepos, 1.5);                           //slower move of arm towards bee
00458             perseen = detectPER(1, tickletimeout);                  //tickle until timeout or PER detected
00459         }
00460         //or not tickle
00461         else {
00462             perseen = detectPER(1, tickletimeout);                  //if not tickling, wait for PER or timeout then move to pre-feeding position
00463             arm2move(arm2ticklepos-0.05, 1);                        //move to position between LED and holder
00464             elbow2move(elbow2ticklepos, 0.3);
00465         }
00466        //feeding only if you have tickled or a PER has been detected
00467        if (tickle || perseen) {                                    //only feed if a PER has been detector, or "tickle" is true
00468             elbow2move(elbow2feedpos, 0.3);
00469             arm2move(arm2feedpos, 0.3);
00470             wait(feedtime);
00471             arm2move(arm2ticklepos -0.05, 0.3);
00472             elbow2move(elbow2ticklepos, 0.3);
00473         }
00474         //move back to base position
00475         arm2 = arm2basepos;
00476         elbow2 = elbow2basepos;
00477     }
00478     return perseen;
00479 }
00480 
00481 //User to input choice between station 1 and 2 for this session
00482 int stationchoice() {
00483     int station;
00484     station = 1;
00485     cleardisplay();                                                 //clear screen and home cursor
00486     display.printf(" Station selection  ");
00487     display.printf("Left=1       2=Right");
00488     while (lesw && risw) {                                          //lesw and risw become false when pressed!
00489         wait(0.02);
00490         if (!lesw) station=1;                                       //on LEFT select station 1
00491         if (!risw) station=2;                                       //on RIGHT select station 2
00492     }
00493     display.printf("\rStation %1u selected", station);
00494     fprintf(logfile, "%3u, station selected\r", station);
00495     return station;
00496 }
00497 
00498 //Registers and calibrates all beeholders used in this session
00499 int registerbeeholders() {
00500     int i;
00501     bool done;
00502     char buffert[30];
00503     i = done = 0;
00504     cleardisplay();                                                 //clear screen and home cursor
00505     fprintf(logfile, "calibration record:\r");
00506     fprintf(logfile, "i, serialno, LED V, time\r");
00507     while (i<30 && !done) {                                         //register and calibrate a maximum of 30 beeholders
00508         display.printf("calibrating %u\r",i+1);
00509         sessionrecord[i].serialno = getserialno(station);           //read serial number
00510         if (sessionrecord[i].serialno != 0) {                       //check if serial number correctly read - if not it will be 0000
00511             sessionrecord[i].calvalue = calibrate(station, 5);      //5 calibration cycles
00512             if ((sessionrecord[i].calvalue > 0.25) && (sessionrecord[i].calvalue < 0.97)) {     //check that calvalue is in expected range
00513                 sessionrecord[i].tstamp = time(NULL);                   //create timestamp NOW
00514                 strftime(buffert, 20, "%X", localtime(&sessionrecord[i].tstamp));               //formats time part of timestamp
00515                 cleardisplay();
00516                 display.printf("SN %4u - cal %4.2fV\r", sessionrecord[i].serialno, sessionrecord[i].calvalue*3.3);
00517                 display.printf("OK for next\r");
00518                 display.printf("DOWN for training");
00519                 fprintf(logfile, "%4u,%6u,%6.2f,  %s,  calibrated\r", i, sessionrecord[i].serialno, sessionrecord[i].calvalue*3.3, buffert);
00520                 i++;
00521             }
00522             else {
00523                 cleardisplay();
00524                 display.printf("SN %4u - cal %4.2fV\r", sessionrecord[i].serialno, sessionrecord[i].calvalue*3.3);
00525                 display.printf("Cal out of range!\r");
00526                 multibeeps(2,0.5);
00527                 display.printf("OK to recalibrate\r");
00528                 fprintf(logfile, "%4u,%6u,%6.2f,  %s,  out of range\r", i, sessionrecord[i].serialno, sessionrecord[i].calvalue*3.3, buffert);
00529             }
00530             while (!done && oksw) {                                 //loop until OK or DOWN are pressed
00531                 wait(0.02);
00532                 done = !dnsw;                                       //DOWN exits registration cycle and moves to training
00533             }
00534         }
00535         else {                                                      //retry when serialno can't be read (is 0000)
00536             cleardisplay();
00537             multibeeps(3,0.3);                                      //beep-beep-beep when beeholder not correctly read
00538             display.printf("invalid serial no\r");
00539             display.printf("reseat beeholder\r");
00540             display.printf("press OK");
00541             while (oksw) wait(0.02);                                //loop until OK is pressed to start calibration loop again
00542         }
00543         cleardisplay();
00544     }
00545     return i;                                                       //upon done condition, i== number of beeholders calibrated
00546 }
00547 
00548 //gets holderrecord for the holder with a certain serial number
00549 int getholderindex(int serialno) {
00550     for (int i=0; i<numbeeholders; i++) {                           //reverse lookup of index for a certain serial number
00551         if (sessionrecord[i].serialno == serialno) return i;
00552     }
00553     return 29;                                                      //if the record is not found, returns i==29
00554 }
00555 
00556 //all the elements making up a single training cycle with one beeholder
00557 void trainingcycle() {
00558     wait(0.2);
00559     time_t tstamp = time(NULL);                                     //create timestamp NOW
00560     char buffert[30];
00561     strftime(buffert, 20, "%X", localtime(&tstamp));                //format timestamp to time string
00562     bool invalidserial;
00563     invalidserial = 1;
00564     int serialno;
00565     serialno = 0;
00566     while (invalidserial) {                                         //loop until a serial number is read correctly
00567         cleardisplay();
00568         int serialno = getserialno(station);                        //read serial number
00569         invalidserial = (serialno == 0);                            //if the serialno is 0, then it's invalid
00570         if (invalidserial) {
00571             display.printf("Invalid serialno\rReseat holder\rUP to retry");
00572             while (upsw) wait(0.02);                                //loop until UP switch is pressed
00573         }
00574     }
00575     int i = getholderindex(serialno);                               //get index i for serial number
00576     IRledON(i);                                                     //switch IR LED on at correct brightness
00577     sessionrecord[i].cycleno++;                                     //increment cycle number for this beeholder
00578     cleardisplay();
00579     display.printf("SN: %4u, cycle %u\r", serialno, sessionrecord[i].cycleno);
00580     sessionrecord[i].reslastcycle = condcycle(station, sessionrecord[i].ticklenextcycle);       //do a conditioning cycle
00581     fprintf(logfile, "%s,",buffert);
00582     fprintf(logfile, "  %4u,",serialno);
00583     fprintf(logfile, "  %2u,", sessionrecord[i].cycleno);
00584     if (sessionrecord[i].reslastcycle) {                            //log PER or TimeOut
00585         fprintf(logfile, "  PER,");
00586     }
00587     else {
00588         fprintf(logfile, "   TO,");
00589     }
00590     fprintf(logfile, " training\r");
00591     if (sessionrecord[i].reslastcycle) {
00592         display.printf("PER detected\r");
00593     }
00594     else {
00595         display.printf("PER time-out\r");
00596     }
00597     display.printf("mount holder + OK\r");
00598     display.printf("DOWN to finish");
00599 }
00600 
00601 //main program
00602 int main() {
00603     initialise();                                                   //initialise a new session
00604     
00605     //Choose which station this session will use
00606     station = stationchoice ();                                     //menu to let user select station 1 or 2
00607     cleardisplay();
00608     
00609     //Choose substance string
00610     substanceindex = subchoice(submenu);                            //menu to let use chose substance
00611     fprintf(logfile, "%s\r", submenu[substanceindex].logstring);    //prints the substance log string to the logfile
00612     
00613     //Register and calibrate beeholders used in this session
00614     display.printf("Now register holders\r");
00615     display.printf("seat first holder\rpress OK\r");
00616     while (oksw) wait(0.02);                                        //loop so first beeholder can be mounted, then OK pressed
00617     numbeeholders = registerbeeholders();
00618     display.printf("%2u holders entered\r", numbeeholders);
00619     wait(3);
00620     cleardisplay();
00621     
00622     //Conduct training
00623     display.printf("Start training cycle\r");
00624     display.printf("mount holder + OK\r");
00625     display.printf("DOWN to finish");
00626     fprintf(logfile, "Training started\r");
00627     fprintf(logfile, "time, serial, cycleno, result\r");
00628     wait(1);
00629     InterruptIn oksw(p17);                                          //redefine OK and footswitch as interrupts
00630     oksw.mode(PullUp);
00631     bool finishsession;
00632     finishsession = 0;
00633     oksw.fall(&trainingcycle);                                      //call subroutine "trainingcycle" on OK/footswitch interrupt
00634     while (!finishsession) {                                        //loop while servicing interrupts
00635         wait(0.02);
00636         finishsession = !dnsw;                                      //until DOWN button is pressed, which exits to program close
00637     }
00638     
00639     //Close logfile
00640     fprintf(logfile, "session closed");
00641     fclose(logfile);                                                //close logfile for reading
00642     cleardisplay();
00643     display.printf("Session finished\r\rdownload etonlog.csvand rename");
00644 }