Marc Bax
/
EtonBreadboard1
Firmware for Eton breadboard
Embed:
(wiki syntax)
Show/hide line numbers
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 }
Generated on Thu Jul 14 2022 21:18:00 by 1.7.2