Firmware for Eton breadboard

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.80";                                          //For published versions use format "n.nn"
00003                                                                     //For development versions add "D" to end of string
00004 //This version published on 23/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.68;
00032     float elbow1ticklepos = 0.07;
00033     float arm1feedpos = 0.73;
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 times                                            all times are in seconds
00047     float diptime = 1;                                              //time that brush stays down dipped in sugar water
00048     float tickletimeout = 1.5;                                      //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 
00077 
00078 //Function declarations
00079 
00080 //Short beep
00081 void beeponce() {
00082     buzzer = 1;
00083     wait(0.1);
00084     buzzer = 0;
00085 }
00086 
00087 //beep beep beep
00088 void multibeeps(int number, float interval) {
00089     for(int i=0;i<number;i++){
00090         beeponce();
00091         wait(interval);
00092     }
00093 }
00094 
00095 //Clears and homes the display
00096 void cleardisplay() {
00097     display.printf("\e[2J\e[H");
00098 }
00099 
00100 //Returns both robot arms to base position
00101 void armstobase() {
00102     elbow1 = elbow1basepos;
00103     elbow2 = elbow2basepos;
00104     wait(0.5);                                                      //need this delay to avoid snagging brush on IR LED
00105     arm1 = arm1basepos;
00106     arm2 = arm2basepos;
00107 }
00108 
00109 //Initialises sessionrecords
00110 void sessionrecordinit() {
00111     for (int i=0; i<30; i++) {                                      //set base values for all possible beeholders
00112         sessionrecord[i].serialno = 0;                              //set serialno to 0 so we can detect a unresponsive holder
00113         sessionrecord[i].calvalue = 0;
00114         sessionrecord[i].cycleno = 0;
00115         sessionrecord[i].reslastcycle = 0;
00116         sessionrecord[i].ticklenextcycle = 1;                       //default is to tickle on each cycle
00117     }
00118 }
00119 
00120 //Initialisation on power-on
00121 void initialise() {
00122 
00123     //Set pins to a defined state
00124     led1 = led2 = led3 = led4 =0;                                   //internal LEDs only used for debugging purposes
00125     buzzer = 0;                                                     //buzzer off
00126     airswitcher = 0;                                                //relay contacts open, clean air position
00127     bhsel1 = bhsel2 = 1;                                            //I2C select lines high to deselect both beeholders
00128     upsw.mode(PullUp);                                              //set pull-up on all frontpanel switches
00129     risw.mode(PullUp);
00130     dnsw.mode(PullUp);
00131     lesw.mode(PullUp);
00132     oksw.mode(PullUp);
00133     
00134     //Set robot arms to base positions
00135     armstobase();
00136     
00137     //initialise the display
00138     display.baud(19200);                                            //set display baud rate to 19200
00139     display.putc(0x0d);                                             //send CR to display - this needs to be first character sent after power-up
00140     wait(0.5);                                                      //wait for display to adjust to serial baud rate
00141     display.printf("\e[4L\e[20c");                                  //set display to 4 lines of 20 characters
00142     cleardisplay();                                                 //clear screen and home cursor
00143     
00144     //display splash screen with time
00145     display.printf("Eton trainer v%s\r", fwversion);                //show firmware release version
00146     time_t seconds = time(NULL);                                    //creates timestamp
00147     char bufferd[20], buffert[20];
00148     strftime(bufferd, 20, "%d/%m/%Y", localtime(&seconds));         //formats date part of timestamp
00149     display.printf("Date: %s\r", bufferd);                          //displays date
00150     strftime(buffert, 20, "%X", localtime(&seconds));               //formats time part of timestamp
00151     display.printf("Time: %s\r", buffert);                          //displays time
00152     wait(5);                                                        //how long splash screen will be displayed
00153     cleardisplay();                                                 //clear screen and home cursor
00154     
00155     //initialise sessionrecord
00156     sessionrecordinit();
00157     
00158     //enter info in logfile.csv
00159     fprintf(logfile, "%s\r", bufferd);                              //writes date, time and firmware version
00160     fprintf(logfile, "%s\r", buffert);
00161     fprintf(logfile, "v%s\r", fwversion);
00162     
00163     multibeeps(3, 0.4);                                              //three beeps to signal initialisation ready
00164 }
00165 
00166 //Converts the last 4 digits in the serial number string into a integer 0-9999
00167 int serialstring2int(char bser[8]) {
00168     int tempserial = 0;    
00169     tempserial = tempserial + (bser[4]-0x30)*1000;                  //5th digit is thousands
00170     tempserial = tempserial + (bser[5]-0x30)*100;                   //6th digit is hundreds
00171     tempserial = tempserial + (bser[6]-0x30)*10;                    //7th digit is tens
00172     tempserial = tempserial + (bser[7]-0x30);                       //8th digit is units
00173     return tempserial;
00174 }
00175 
00176 //beeholder resets on rising edge of select line
00177 void resetbeeholder() {                                             //need this as mounting beeholder causes undefined start of beeholder firmware
00178     bhsel1 = bhsel2 = 0;
00179     wait(0.1);
00180     bhsel1 = bhsel2 = 1;
00181     wait(0.3);
00182 }
00183 
00184 //Reads beeholder serial number from station 1 or 2
00185 int getserialno(int station) {
00186     char bser[8];                                                   //define 8-byte serial number string to read
00187     resetbeeholder();                                               //does not work without this!!
00188     for (int i=0;i<8;i++) bser[i]=0x30;
00189     if (station==1) bhsel1=0;                                       //pull select line station 1 low
00190     if (station==2) bhsel2=0;                                       //pull select line station 2 low
00191     wait(0.2);
00192     beeholder.stop();                                               //I2C stop condition
00193     wait(0.2);                                                      //delay for beeholder to respond
00194     beeholder.write(addr,0x0,1);                                    //initial write before read
00195     beeholder.read(addr,bser,8);                                    //read 8 byte serial number
00196     bhsel1 = bhsel2= 1;                                             //pull both select lines high
00197     int serialno = serialstring2int(bser);                          //translate serial number string to integer
00198     return serialno;
00199 }    
00200 
00201 //Returns 1 if a PER is detected on this beeholder within timeout seconds
00202 int detectPER(int station, float timeout) {
00203     Timer ttotal, tper;
00204     ttotal.start();                                                 //start timers for time-out and PER-detect
00205     ttotal.reset();
00206     tper.start();
00207     tper.reset();
00208     if (station == 1) {
00209         while ((ttotal.read() < timeout) && (tper.read() < PERtime)) {        //loop until timeout or PER detected
00210             wait_ms(10);
00211             if (ptor1 * 3.3 < PERsetval) tper.reset();              //if phototransistor voltage below treshold keep PER timer in reset
00212                                                                     //if above treshold let timer run until it reaches PERtime
00213         }
00214     }
00215     if (station == 2) {
00216          while ((ttotal.read() < timeout) && (tper.read() < PERtime)) {
00217             if (ptor2 * 3.3 < PERsetval) tper.reset();
00218         }
00219     }
00220     ttotal.stop();
00221     tper.stop();
00222     return (tper.read() >= PERtime);                                //if the loop exit condition was a PER, return a TRUE value
00223 }
00224 
00225 //Checks if bee shows PER when stimulated with odour but without feeding
00226 int checktrained(int station) {
00227     airswitcher = 1;
00228     int responded = detectPER(station, 6);                          //PER needs to be detected within 6 seconds
00229     airswitcher = 0;
00230     return responded;
00231 }
00232 
00233 //Function performs beeholder/IR-led calibration
00234 float calibrate(int station, int runs) {
00235     float tempcal;
00236     tempcal=0.5;                                                    //start calibration at 50% voltage
00237     irLED=tempcal;
00238     float calstep;                                                  //start calstep at 25% voltage
00239     calstep = tempcal/2;
00240     for (int i=0; i<10; i++) {                                      //does a "10-bit binary search" for the correct voltage to get a good response
00241         irLED = tempcal;
00242         wait(0.1);                                                  //important to allow AD converter to settle
00243         if (ptor1 < calsetval/3.3) {                                //check phototransistor voltage against desired value
00244             tempcal = tempcal - calstep;                            //if phototransistor voltage is too low then reduce brightness
00245         }
00246         else {
00247             tempcal = tempcal + calstep;                            //if phototransistor voltage is too high then increase brightness
00248         }
00249         calstep = calstep/2;                                        //on each loop of the for-cycle make smaller changes to IR LED voltage
00250     }
00251     float calib;
00252     calib = tempcal;                                                //set preliminary calibration to the value just measured
00253     for (int j=1; j<runs; j++) {                                    //run another j-1 runs, this corrects for antennae-movement as
00254         tempcal=0.5;                                                //we use the lowest calibration value from j runs
00255         irLED=tempcal;                                              //this is similar to what we do in the cassettes
00256         calstep = tempcal/2;
00257         for (int i=0;i<10;i++) {
00258             irLED = tempcal;
00259             wait(0.1);
00260             if (ptor1 < calsetval/3.3) {
00261                 tempcal = tempcal - calstep;
00262             }
00263             else {
00264                 tempcal = tempcal + calstep;
00265             }
00266             calstep = calstep/2;
00267         }
00268         if (tempcal < calib) calib = tempcal;                       //use the lowest of j calibration values
00269     }
00270     return calib;
00271 }
00272 
00273 //switches the IR LED on at the right brightness level for the beeholder
00274 void IRledON(int i) {
00275     irLED = sessionrecord[i].calvalue;
00276  }
00277 
00278 //Performs a conditioning cycle. if tickle==0, no tickling takes place. Return==1 if a PER has been detected
00279 int condcycle(int station, int tickle) {
00280     int perseen;
00281     perseen = 0;
00282     //for station 1
00283     if (station == 1) {
00284         //dip brush
00285         arm1 = arm1dippos;
00286         wait(0.5);
00287         elbow1 = elbow1dippos;
00288         wait (diptime);
00289         elbow1 = elbow1basepos;
00290         wait(0.3);
00291         arm1 = arm1basepos;
00292         //switch air to supply trace vapour
00293         airswitcher = 1;                                            //air contains target vapour
00294         display.printf("Vapour ON");
00295         wait (0.5);
00296         //tickle
00297         if (tickle) {                                               //if tickling, first tickle then wait for PER or timeout
00298             elbow1 = elbow1ticklepos;
00299             arm1 = arm1ticklepos;
00300             perseen = detectPER(1, tickletimeout);                  //tickle until timeout or PER detected
00301         }
00302         //or not tickle
00303         else {
00304             perseen = detectPER(1, tickletimeout);                  //if not tickling, wait for PER or timeout then move to pre-feeding position
00305             arm1 = arm1ticklepos - 0.05;                            //move to position between LED and holder
00306             wait(0.3);                                              //this is needed to avoid the brush snagging on the phototransistor
00307             elbow1 = elbow1ticklepos;
00308         }
00309         //feeding only if you have tickled or a PER has been detected
00310         if (tickle || perseen) {                                    //only feed if a PER has been detector, or "tickle" is true
00311             elbow1 = elbow1feedpos;
00312             arm1 = arm1feedpos;
00313             wait(feedtime);
00314             arm1 = arm1ticklepos -0.05;
00315             elbow1 = elbow1ticklepos;
00316         }
00317         //move back to base position
00318         arm1 = arm1basepos;
00319         elbow1 = elbow1basepos;
00320         airswitcher = 0;                                            //air valve to clean air
00321         display.printf("\e[DFF\r");                                 //rewrite "ON" to "OFF" on display
00322     }
00323     //for station 2
00324     if (station == 2) {
00325         //dip brush
00326         arm2 = arm2dippos;
00327         wait(0.5);
00328         elbow2 = elbow2dippos;
00329         wait (diptime);
00330         elbow2 = elbow2basepos;
00331         wait(0.3);
00332         arm2 = arm2basepos;
00333         //switch air to supply trace vapour
00334         //airswitcher = 1;                                          //no air switching for station 2
00335         wait (0.5);
00336         //tickle
00337         if (tickle) {                                               //if tickling, first tickle then wait for PER or timeout
00338             elbow2 = elbow2ticklepos;
00339             arm2 = arm2ticklepos;
00340             perseen = detectPER(2, tickletimeout);
00341         }
00342         else {
00343             perseen = detectPER(2, tickletimeout);                  //if not tickling, wait for PER or timeout then move to pre-feeding position
00344             arm2 = arm2ticklepos - 0.05;
00345             wait(0.3);                                              //this is needed to avoid the brush snagging on the phototransistor
00346             elbow2 = elbow2ticklepos;
00347         }
00348         //feeding only if tickling takes place or a PER has been detected
00349         if (tickle || perseen) {
00350             elbow2 = elbow2feedpos;
00351             arm2 = arm2feedpos;
00352             wait(feedtime);
00353             arm2 = arm2ticklepos -0.05;
00354             elbow2 = elbow2ticklepos;
00355         }
00356         //move back to base position
00357         arm2 = arm2basepos;
00358         elbow2 = elbow2basepos;
00359     }
00360     return perseen;
00361 }
00362 
00363 //User to input choice between station 1 and 2 for this session
00364 int stationchoice() {
00365     int station;
00366     station = 1;
00367     cleardisplay();                                                 //clear screen and home cursor
00368     display.printf(" Station selection  ");
00369     display.printf("Left=1       2=Right");
00370     while (lesw && risw) {                                          //lesw and risw become false when pressed!
00371         wait(0.02);
00372         if (!lesw) station=1;                                       //on LEFT select station 1
00373         if (!risw) station=2;                                       //on RIGHT select station 2
00374     }
00375     display.printf("\rStation %1u selected", station);
00376     fprintf(logfile, "%3u, station selected\r", station);
00377     return station;
00378 }
00379 
00380 //Registers and calibrates all beeholders used in this session
00381 int registerbeeholders() {
00382     int i;
00383     bool done;
00384     char buffert[30];
00385     i = done = 0;
00386     cleardisplay();                                                 //clear screen and home cursor
00387     fprintf(logfile, "calibration record:\r");
00388     fprintf(logfile, "i, serialno, LED V, time\r");
00389     while (i<30 && !done) {                                         //register and calibrate a maximum of 30 beeholders
00390         display.printf("calibrating %u\r",i+1);
00391         sessionrecord[i].serialno = getserialno(station);           //read serial number
00392         if (sessionrecord[i].serialno != 0) {                       //check if serial number correctly read - if not it will be 0000
00393             sessionrecord[i].calvalue = calibrate(station, 5);      //5 calibration cycles
00394             sessionrecord[i].tstamp = time(NULL);                   //create timestamp NOW
00395             strftime(buffert, 20, "%X", localtime(&sessionrecord[i].tstamp));               //formats time part of timestamp
00396             cleardisplay();
00397             display.printf("SN %4u - cal %4.2fV\r", sessionrecord[i].serialno, sessionrecord[i].calvalue*3.3);
00398             display.printf("OK for next\r");
00399             display.printf("DOWN for training");
00400             fprintf(logfile, "%4u,%6u,%6.2f,  %s,  calibrate\r", i, sessionrecord[i].serialno, sessionrecord[i].calvalue*3.3, buffert);
00401             i++;
00402             while (!done && oksw) {                                 //loop until OK or DOWN are pressed
00403                 wait(0.02);
00404                 done = !dnsw;                                       //DOWN exits registration cycle and moves to training
00405             }
00406         }
00407         else {                                                     //retry when serialno can't be read (is 0000)
00408             cleardisplay();
00409             display.printf("invalid serial no\r");
00410             display.printf("reseat beeholder\r");
00411             display.printf("press OK");
00412             while (oksw) wait(0.02);                                //loop until OK is pressed to start calibration loop again
00413         }
00414         cleardisplay();
00415     }
00416     return i;                                                       //upon done condition, i== number of beeholders calibrated
00417 }
00418 
00419 //gets holderrecord for the holder with a certain serial number
00420 int getholderindex(int serialno) {
00421     for (int i=0; i<numbeeholders; i++) {                           //reverse lookup of index for a certain serial number
00422         if (sessionrecord[i].serialno == serialno) return i;
00423     }
00424     return 29;                                                      //if the record is not found, returns i==29
00425 }
00426 
00427 //all the elements making up a single training cycle with one beeholder
00428 void trainingcycle() {
00429     wait(0.2);
00430     time_t tstamp = time(NULL);                                     //create timestamp NOW
00431     char buffert[30];
00432     strftime(buffert, 20, "%X", localtime(&tstamp));                //format timestamp to time string
00433     int serialno = getserialno(station);                            //read serial number
00434     int i = getholderindex(serialno);                               //get index i for serial number
00435     IRledON(i);                                                     //switch IR LED on at correct brightness
00436     sessionrecord[i].cycleno++;                                     //increment cycle number for this beeholder
00437     cleardisplay();
00438     display.printf("SN: %4u, cycle %u\r", serialno, sessionrecord[i].cycleno);
00439     sessionrecord[i].reslastcycle = condcycle(station, sessionrecord[i].ticklenextcycle);       //do a conditioning cycle
00440     fprintf(logfile, "%s,",buffert);
00441     fprintf(logfile, "  %4u,",serialno);
00442     fprintf(logfile, "  %2u,", sessionrecord[i].cycleno);
00443     if (sessionrecord[i].reslastcycle) {                            //log PER or TimeOut
00444         fprintf(logfile, "  PER,");
00445     }
00446     else {
00447         fprintf(logfile, "   TO,");
00448     }
00449     fprintf(logfile, " training\r");
00450     if (sessionrecord[i].reslastcycle) {
00451         display.printf("PER detected\r");
00452     }
00453     else {
00454         display.printf("PER time-out\r");
00455     }
00456     display.printf("mount holder + OK\r");
00457     display.printf("DOWN to finish");
00458 }
00459 
00460 //main program
00461 int main() {
00462     initialise();
00463     
00464     //Choose which station this session will use
00465     station = stationchoice ();
00466     cleardisplay();
00467     
00468     //Register and calibrate beeholders used in this session
00469     display.printf("Now register holders\r");
00470     display.printf("seat first holder\rpress OK\r");
00471     while (oksw) wait(0.02);                                        //loop so first beeholder can be mounted, then OK pressed
00472     numbeeholders = registerbeeholders();
00473     display.printf("%2u holders entered\r", numbeeholders);
00474     wait(3);
00475     cleardisplay();
00476     
00477     //Conduct training
00478     display.printf("Start training cycle\r");
00479     display.printf("mount holder + OK\r");
00480     display.printf("DOWN to finish");
00481     fprintf(logfile, "Training started\r");
00482     fprintf(logfile, "time, serial, cycleno, result\r");
00483     wait(1);
00484     InterruptIn oksw(p17);                                          //redefine OK and footswitch as interrupts
00485     oksw.mode(PullUp);
00486     bool finishsession;
00487     finishsession = 0;
00488     oksw.fall(&trainingcycle);                                      //call subroutine "trainingcycle" on OK/footswitch interrupt
00489     while (!finishsession) {                                        //loop while servicing interrupts
00490         wait(0.02);
00491         finishsession = !dnsw;                                      //until DOWN button is pressed, which exits to program close
00492     }
00493     
00494     //Close logfile
00495     fprintf(logfile, "session closed");
00496     fclose(logfile);                                                //close logfile for reading
00497     cleardisplay();
00498     display.printf("Session finished\r\rdownload etonlog.csvand rename");
00499 }