Marc Bax
/
EtonBreadboard_091
Modification in routine to select substance
main.cpp@0:73e3587b65ec, 2011-03-31 (annotated)
- Committer:
- marcbax
- Date:
- Thu Mar 31 07:14:17 2011 +0000
- Revision:
- 0:73e3587b65ec
v0.91
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
marcbax | 0:73e3587b65ec | 1 | //Firmware to drive Eton bee trainer breadboard |
marcbax | 0:73e3587b65ec | 2 | char* fwversion = "0.91"; //For published versions use format "n.nn" |
marcbax | 0:73e3587b65ec | 3 | //For development versions add "D" to end of string |
marcbax | 0:73e3587b65ec | 4 | //This version published on 31/03/2011 |
marcbax | 0:73e3587b65ec | 5 | |
marcbax | 0:73e3587b65ec | 6 | //Include libraries |
marcbax | 0:73e3587b65ec | 7 | #include "mbed.h" |
marcbax | 0:73e3587b65ec | 8 | #include "Servo.h" //need this library to control servos |
marcbax | 0:73e3587b65ec | 9 | |
marcbax | 0:73e3587b65ec | 10 | //Pin assignments and external functions |
marcbax | 0:73e3587b65ec | 11 | DigitalOut led1(LED1), led2(LED2), led3(LED3), led4(LED4); //for debugging purposes only |
marcbax | 0:73e3587b65ec | 12 | DigitalOut buzzer(p5); //pin high means buzzer on |
marcbax | 0:73e3587b65ec | 13 | DigitalOut airswitcher(p7); //pin high shorts coax for air switcher valve |
marcbax | 0:73e3587b65ec | 14 | Serial display(p9,p10); //serial display controller by ByVAC BV4618 controller |
marcbax | 0:73e3587b65ec | 15 | AnalogOut irLED(p18); //only one analog out controls both IR LEDS in parallel |
marcbax | 0:73e3587b65ec | 16 | AnalogIn ptor1(p19), ptor2(p20); //analog input for beeholder phototransistors |
marcbax | 0:73e3587b65ec | 17 | Servo arm1(p21), elbow1(p22); //servos for station 1 robot arm |
marcbax | 0:73e3587b65ec | 18 | Servo arm2(p23), elbow2(p24); //servos for station 2 robot arm |
marcbax | 0:73e3587b65ec | 19 | I2C beeholder(p28,p27); //set up I2C communication to read beeholder serial no |
marcbax | 0:73e3587b65ec | 20 | DigitalOut bhsel1(p29), bhsel2(p30); //select lines for station 1 and station 2 - pull low to select holder |
marcbax | 0:73e3587b65ec | 21 | DigitalIn upsw(p13), risw(p14), dnsw(p15), lesw(p16), oksw(p17); //footswitch connected parallel to OK switch |
marcbax | 0:73e3587b65ec | 22 | LocalFileSystem local("local"); //allow access to the mbed "thumbdrive" as a file system |
marcbax | 0:73e3587b65ec | 23 | FILE *logfile = fopen("/local/etonlog.csv","w"); //creates new file etonlog.csv and set for writing. Overwrites existing file! |
marcbax | 0:73e3587b65ec | 24 | |
marcbax | 0:73e3587b65ec | 25 | //Definition of global constants |
marcbax | 0:73e3587b65ec | 26 | //station 1 (left) robot arm positions |
marcbax | 0:73e3587b65ec | 27 | float arm1basepos = 0.4; |
marcbax | 0:73e3587b65ec | 28 | float elbow1basepos = 0.07; |
marcbax | 0:73e3587b65ec | 29 | float arm1dippos = 0.2; |
marcbax | 0:73e3587b65ec | 30 | float elbow1dippos = 0.6; |
marcbax | 0:73e3587b65ec | 31 | float arm1ticklepos = 0.65; |
marcbax | 0:73e3587b65ec | 32 | float elbow1ticklepos = 0.07; |
marcbax | 0:73e3587b65ec | 33 | float arm1feedpos = 0.7; |
marcbax | 0:73e3587b65ec | 34 | float elbow1feedpos = 0.4; |
marcbax | 0:73e3587b65ec | 35 | |
marcbax | 0:73e3587b65ec | 36 | //station 2 (right) robot arm positions |
marcbax | 0:73e3587b65ec | 37 | float arm2basepos = 0.4; |
marcbax | 0:73e3587b65ec | 38 | float elbow2basepos = 0.07; |
marcbax | 0:73e3587b65ec | 39 | float arm2dippos = 0.2; |
marcbax | 0:73e3587b65ec | 40 | float elbow2dippos = 0.07; |
marcbax | 0:73e3587b65ec | 41 | float arm2ticklepos = 0.2; |
marcbax | 0:73e3587b65ec | 42 | float elbow2ticklepos = 0.07; |
marcbax | 0:73e3587b65ec | 43 | float arm2feedpos = 0.2; |
marcbax | 0:73e3587b65ec | 44 | float elbow2feedpos = 0.07; |
marcbax | 0:73e3587b65ec | 45 | |
marcbax | 0:73e3587b65ec | 46 | //robot arm hold and move times all times are in seconds |
marcbax | 0:73e3587b65ec | 47 | float diptime = 1; //time that brush stays down dipped in sugar water |
marcbax | 0:73e3587b65ec | 48 | float tickletimeout = 3; //maximum time to tickle if no PER is detected |
marcbax | 0:73e3587b65ec | 49 | float feedtime = 3; //feeding time |
marcbax | 0:73e3587b65ec | 50 | |
marcbax | 0:73e3587b65ec | 51 | //PER detection parameters |
marcbax | 0:73e3587b65ec | 52 | float calsetval = 1.4; //voltage to which phototransistor output will be calibrated |
marcbax | 0:73e3587b65ec | 53 | float PERsetval = 2.1; //phototransistor output needs to be above this voltage for PER |
marcbax | 0:73e3587b65ec | 54 | float PERtime = 0.2; //phototransistor output needs to stay high this long in seconds |
marcbax | 0:73e3587b65ec | 55 | |
marcbax | 0:73e3587b65ec | 56 | //I2C address |
marcbax | 0:73e3587b65ec | 57 | int addr = 0xA2; //address is actually 0x51, but mbed expects left-shifted address |
marcbax | 0:73e3587b65ec | 58 | |
marcbax | 0:73e3587b65ec | 59 | //Definition of global variables |
marcbax | 0:73e3587b65ec | 60 | |
marcbax | 0:73e3587b65ec | 61 | struct holderrecord { //used to hold data on beeholder |
marcbax | 0:73e3587b65ec | 62 | int serialno; //beeholder serial number 0-9999 |
marcbax | 0:73e3587b65ec | 63 | float calvalue; //IR-LED calibration value |
marcbax | 0:73e3587b65ec | 64 | char cycleno; //number of cycles gone through in this session |
marcbax | 0:73e3587b65ec | 65 | char reslastcycle; //result of last cycle, 1 for PER |
marcbax | 0:73e3587b65ec | 66 | char ticklenextcycle; //whether tickling was done for last cycle |
marcbax | 0:73e3587b65ec | 67 | time_t tstamp; //timestamp when last cycle was run |
marcbax | 0:73e3587b65ec | 68 | }; |
marcbax | 0:73e3587b65ec | 69 | |
marcbax | 0:73e3587b65ec | 70 | holderrecord currentholder; //struct record for the current beeholder |
marcbax | 0:73e3587b65ec | 71 | holderrecord sessionrecord[30]; //sessionrecord contains holderrecords for up to 30 beeholders |
marcbax | 0:73e3587b65ec | 72 | |
marcbax | 0:73e3587b65ec | 73 | int numbeeholders; //number of beeholders used in current session |
marcbax | 0:73e3587b65ec | 74 | int station; //which station is used in this session |
marcbax | 0:73e3587b65ec | 75 | |
marcbax | 0:73e3587b65ec | 76 | //Substance strings |
marcbax | 0:73e3587b65ec | 77 | |
marcbax | 0:73e3587b65ec | 78 | struct submenuitem { |
marcbax | 0:73e3587b65ec | 79 | char* displaystring; //keep display string to 19 characters or less, otherwise display screws up |
marcbax | 0:73e3587b65ec | 80 | char* logstring; //this is the string that gets printed in the logfile, and can be longer then 19 characters |
marcbax | 0:73e3587b65ec | 81 | }; |
marcbax | 0:73e3587b65ec | 82 | |
marcbax | 0:73e3587b65ec | 83 | submenuitem submenu[11]; |
marcbax | 0:73e3587b65ec | 84 | int substanceindex; //this variable contains the index number (in the list) of the selected substance |
marcbax | 0:73e3587b65ec | 85 | |
marcbax | 0:73e3587b65ec | 86 | //Function declarations |
marcbax | 0:73e3587b65ec | 87 | |
marcbax | 0:73e3587b65ec | 88 | //Short beep |
marcbax | 0:73e3587b65ec | 89 | void beeponce() { |
marcbax | 0:73e3587b65ec | 90 | buzzer = 1; |
marcbax | 0:73e3587b65ec | 91 | wait(0.1); |
marcbax | 0:73e3587b65ec | 92 | buzzer = 0; |
marcbax | 0:73e3587b65ec | 93 | } |
marcbax | 0:73e3587b65ec | 94 | |
marcbax | 0:73e3587b65ec | 95 | //beep beep beep |
marcbax | 0:73e3587b65ec | 96 | void multibeeps(int number, float interval) { |
marcbax | 0:73e3587b65ec | 97 | for(int i=0;i<number;i++){ |
marcbax | 0:73e3587b65ec | 98 | beeponce(); |
marcbax | 0:73e3587b65ec | 99 | wait(interval); |
marcbax | 0:73e3587b65ec | 100 | } |
marcbax | 0:73e3587b65ec | 101 | } |
marcbax | 0:73e3587b65ec | 102 | |
marcbax | 0:73e3587b65ec | 103 | //Clears and homes the display |
marcbax | 0:73e3587b65ec | 104 | void cleardisplay() { |
marcbax | 0:73e3587b65ec | 105 | display.printf("\e[2J\e[H"); |
marcbax | 0:73e3587b65ec | 106 | } |
marcbax | 0:73e3587b65ec | 107 | |
marcbax | 0:73e3587b65ec | 108 | //Returns both robot arms to base position |
marcbax | 0:73e3587b65ec | 109 | void armstobase() { |
marcbax | 0:73e3587b65ec | 110 | elbow1 = elbow1basepos; |
marcbax | 0:73e3587b65ec | 111 | elbow2 = elbow2basepos; |
marcbax | 0:73e3587b65ec | 112 | wait(0.5); //need this delay to avoid snagging brush on IR LED |
marcbax | 0:73e3587b65ec | 113 | arm1 = arm1basepos; |
marcbax | 0:73e3587b65ec | 114 | arm2 = arm2basepos; |
marcbax | 0:73e3587b65ec | 115 | } |
marcbax | 0:73e3587b65ec | 116 | |
marcbax | 0:73e3587b65ec | 117 | //Fills the substance menu array with text |
marcbax | 0:73e3587b65ec | 118 | void fillsubstancemenu() { |
marcbax | 0:73e3587b65ec | 119 | submenu[0].displaystring = "No substance"; |
marcbax | 0:73e3587b65ec | 120 | submenu[0].logstring = "Lack of substance"; |
marcbax | 0:73e3587b65ec | 121 | |
marcbax | 0:73e3587b65ec | 122 | submenu[1].displaystring = "Coffee"; |
marcbax | 0:73e3587b65ec | 123 | submenu[1].logstring = "Espresso macchiato"; |
marcbax | 0:73e3587b65ec | 124 | |
marcbax | 0:73e3587b65ec | 125 | submenu[2].displaystring = "Earl Grey"; |
marcbax | 0:73e3587b65ec | 126 | submenu[2].logstring = "Nice cup of tea"; |
marcbax | 0:73e3587b65ec | 127 | |
marcbax | 0:73e3587b65ec | 128 | submenu[3].displaystring = "Apple Pie"; |
marcbax | 0:73e3587b65ec | 129 | submenu[3].logstring = "Mom's homemade apple pie - longer then 19 characters"; |
marcbax | 0:73e3587b65ec | 130 | |
marcbax | 0:73e3587b65ec | 131 | submenu[4].displaystring = "Substance 04"; |
marcbax | 0:73e3587b65ec | 132 | submenu[4].logstring = "Substance 4"; |
marcbax | 0:73e3587b65ec | 133 | |
marcbax | 0:73e3587b65ec | 134 | submenu[5].displaystring = "Substance 05"; |
marcbax | 0:73e3587b65ec | 135 | submenu[5].logstring = "Substance 5"; |
marcbax | 0:73e3587b65ec | 136 | |
marcbax | 0:73e3587b65ec | 137 | submenu[6].displaystring = "Substance 06"; |
marcbax | 0:73e3587b65ec | 138 | submenu[6].logstring = "Substance 6"; |
marcbax | 0:73e3587b65ec | 139 | |
marcbax | 0:73e3587b65ec | 140 | submenu[7].displaystring = "Substance 07"; |
marcbax | 0:73e3587b65ec | 141 | submenu[7].logstring = "Substance 7"; |
marcbax | 0:73e3587b65ec | 142 | |
marcbax | 0:73e3587b65ec | 143 | submenu[8].displaystring = "Substance 08"; |
marcbax | 0:73e3587b65ec | 144 | submenu[8].logstring = "Substance 8"; |
marcbax | 0:73e3587b65ec | 145 | |
marcbax | 0:73e3587b65ec | 146 | submenu[9].displaystring = "Substance 09"; |
marcbax | 0:73e3587b65ec | 147 | submenu[9].logstring = "Substance 9"; |
marcbax | 0:73e3587b65ec | 148 | |
marcbax | 0:73e3587b65ec | 149 | submenu[10].displaystring = "Red Roses"; |
marcbax | 0:73e3587b65ec | 150 | submenu[10].logstring = "I never promised you a rose garden"; |
marcbax | 0:73e3587b65ec | 151 | } |
marcbax | 0:73e3587b65ec | 152 | |
marcbax | 0:73e3587b65ec | 153 | //Initialises sessionrecords |
marcbax | 0:73e3587b65ec | 154 | void sessionrecordinit() { |
marcbax | 0:73e3587b65ec | 155 | for (int i=0; i<30; i++) { //set base values for all possible beeholders |
marcbax | 0:73e3587b65ec | 156 | sessionrecord[i].serialno = 0; //set serialno to 0 so we can detect a unresponsive holder |
marcbax | 0:73e3587b65ec | 157 | sessionrecord[i].calvalue = 0; |
marcbax | 0:73e3587b65ec | 158 | sessionrecord[i].cycleno = 0; |
marcbax | 0:73e3587b65ec | 159 | sessionrecord[i].reslastcycle = 0; |
marcbax | 0:73e3587b65ec | 160 | sessionrecord[i].ticklenextcycle = 1; //default is to tickle on each cycle |
marcbax | 0:73e3587b65ec | 161 | } |
marcbax | 0:73e3587b65ec | 162 | } |
marcbax | 0:73e3587b65ec | 163 | |
marcbax | 0:73e3587b65ec | 164 | //Initialisation on power-on |
marcbax | 0:73e3587b65ec | 165 | void initialise() { |
marcbax | 0:73e3587b65ec | 166 | |
marcbax | 0:73e3587b65ec | 167 | //Set pins to a defined state |
marcbax | 0:73e3587b65ec | 168 | led1 = led2 = led3 = led4 =0; //internal LEDs only used for debugging purposes |
marcbax | 0:73e3587b65ec | 169 | buzzer = 0; //buzzer off |
marcbax | 0:73e3587b65ec | 170 | airswitcher = 0; //relay contacts open, clean air position |
marcbax | 0:73e3587b65ec | 171 | bhsel1 = bhsel2 = 1; //I2C select lines high to deselect both beeholders |
marcbax | 0:73e3587b65ec | 172 | upsw.mode(PullUp); //set pull-up on all frontpanel switches |
marcbax | 0:73e3587b65ec | 173 | risw.mode(PullUp); |
marcbax | 0:73e3587b65ec | 174 | dnsw.mode(PullUp); |
marcbax | 0:73e3587b65ec | 175 | lesw.mode(PullUp); |
marcbax | 0:73e3587b65ec | 176 | oksw.mode(PullUp); |
marcbax | 0:73e3587b65ec | 177 | |
marcbax | 0:73e3587b65ec | 178 | //Set robot arms to base positions |
marcbax | 0:73e3587b65ec | 179 | armstobase(); |
marcbax | 0:73e3587b65ec | 180 | |
marcbax | 0:73e3587b65ec | 181 | //initialise the display |
marcbax | 0:73e3587b65ec | 182 | display.baud(19200); //set display baud rate to 19200 |
marcbax | 0:73e3587b65ec | 183 | display.putc(0x0d); //send CR to display - this needs to be first character sent after power-up |
marcbax | 0:73e3587b65ec | 184 | wait(0.5); //wait for display to adjust to serial baud rate |
marcbax | 0:73e3587b65ec | 185 | display.printf("\e[4L\e[20c"); //set display to 4 lines of 20 characters |
marcbax | 0:73e3587b65ec | 186 | cleardisplay(); //clear screen and home cursor |
marcbax | 0:73e3587b65ec | 187 | |
marcbax | 0:73e3587b65ec | 188 | //display splash screen with time |
marcbax | 0:73e3587b65ec | 189 | display.printf("Eton trainer v%s\r", fwversion); //show firmware release version |
marcbax | 0:73e3587b65ec | 190 | time_t seconds = time(NULL); //creates timestamp |
marcbax | 0:73e3587b65ec | 191 | char bufferd[20], buffert[20]; |
marcbax | 0:73e3587b65ec | 192 | strftime(bufferd, 20, "%d/%m/%Y", localtime(&seconds)); //formats date part of timestamp |
marcbax | 0:73e3587b65ec | 193 | display.printf("Date: %s\r", bufferd); //displays date |
marcbax | 0:73e3587b65ec | 194 | strftime(buffert, 20, "%X", localtime(&seconds)); //formats time part of timestamp |
marcbax | 0:73e3587b65ec | 195 | display.printf("Time: %s\r", buffert); //displays time |
marcbax | 0:73e3587b65ec | 196 | wait(3); //display splash screen |
marcbax | 0:73e3587b65ec | 197 | cleardisplay(); //clear screen and home cursor |
marcbax | 0:73e3587b65ec | 198 | |
marcbax | 0:73e3587b65ec | 199 | //initialise sessionrecord |
marcbax | 0:73e3587b65ec | 200 | sessionrecordinit(); |
marcbax | 0:73e3587b65ec | 201 | |
marcbax | 0:73e3587b65ec | 202 | //initialise substance menu |
marcbax | 0:73e3587b65ec | 203 | fillsubstancemenu(); |
marcbax | 0:73e3587b65ec | 204 | |
marcbax | 0:73e3587b65ec | 205 | //enter info in logfile.csv |
marcbax | 0:73e3587b65ec | 206 | fprintf(logfile, "%s\r", bufferd); //writes date, time and firmware version |
marcbax | 0:73e3587b65ec | 207 | fprintf(logfile, "%s\r", buffert); |
marcbax | 0:73e3587b65ec | 208 | fprintf(logfile, "v%s\r", fwversion); |
marcbax | 0:73e3587b65ec | 209 | |
marcbax | 0:73e3587b65ec | 210 | beeponce(); //three beeps to signal initialisation ready |
marcbax | 0:73e3587b65ec | 211 | } |
marcbax | 0:73e3587b65ec | 212 | |
marcbax | 0:73e3587b65ec | 213 | //Returns the index of the selected substance menu item |
marcbax | 0:73e3587b65ec | 214 | int subchoice(submenuitem submenu[11]) { |
marcbax | 0:73e3587b65ec | 215 | bool up, down, done; |
marcbax | 0:73e3587b65ec | 216 | int i; |
marcbax | 0:73e3587b65ec | 217 | up = down = done = 0; |
marcbax | 0:73e3587b65ec | 218 | i = 0; |
marcbax | 0:73e3587b65ec | 219 | cleardisplay(); |
marcbax | 0:73e3587b65ec | 220 | display.printf("Substance selection"); |
marcbax | 0:73e3587b65ec | 221 | wait(3); |
marcbax | 0:73e3587b65ec | 222 | cleardisplay(); |
marcbax | 0:73e3587b65ec | 223 | while (!done) { |
marcbax | 0:73e3587b65ec | 224 | display.printf("%s\r", submenu[i].displaystring); |
marcbax | 0:73e3587b65ec | 225 | display.printf("\rUP/DOWN to scroll\rOK to select"); |
marcbax | 0:73e3587b65ec | 226 | wait(1); //delay required to avoid very rapid cycling of the menu |
marcbax | 0:73e3587b65ec | 227 | while (!up && !down && !done) { |
marcbax | 0:73e3587b65ec | 228 | up = !upsw; |
marcbax | 0:73e3587b65ec | 229 | down = !dnsw; |
marcbax | 0:73e3587b65ec | 230 | done = !oksw; |
marcbax | 0:73e3587b65ec | 231 | } |
marcbax | 0:73e3587b65ec | 232 | if (down) { |
marcbax | 0:73e3587b65ec | 233 | i++; |
marcbax | 0:73e3587b65ec | 234 | if (i==11) i = 0; |
marcbax | 0:73e3587b65ec | 235 | } |
marcbax | 0:73e3587b65ec | 236 | if (up) { |
marcbax | 0:73e3587b65ec | 237 | if (i==0) i = 11; |
marcbax | 0:73e3587b65ec | 238 | i--; |
marcbax | 0:73e3587b65ec | 239 | } |
marcbax | 0:73e3587b65ec | 240 | cleardisplay(); |
marcbax | 0:73e3587b65ec | 241 | } |
marcbax | 0:73e3587b65ec | 242 | return i; |
marcbax | 0:73e3587b65ec | 243 | } |
marcbax | 0:73e3587b65ec | 244 | |
marcbax | 0:73e3587b65ec | 245 | //Converts the last 4 digits in the serial number string into a integer 0-9999 |
marcbax | 0:73e3587b65ec | 246 | int serialstring2int(char bser[8]) { |
marcbax | 0:73e3587b65ec | 247 | int tempserial = 0; |
marcbax | 0:73e3587b65ec | 248 | tempserial = tempserial + (bser[4]-0x30)*1000; //5th digit is thousands |
marcbax | 0:73e3587b65ec | 249 | tempserial = tempserial + (bser[5]-0x30)*100; //6th digit is hundreds |
marcbax | 0:73e3587b65ec | 250 | tempserial = tempserial + (bser[6]-0x30)*10; //7th digit is tens |
marcbax | 0:73e3587b65ec | 251 | tempserial = tempserial + (bser[7]-0x30); //8th digit is units |
marcbax | 0:73e3587b65ec | 252 | return tempserial; |
marcbax | 0:73e3587b65ec | 253 | } |
marcbax | 0:73e3587b65ec | 254 | |
marcbax | 0:73e3587b65ec | 255 | //beeholder resets on rising edge of select line |
marcbax | 0:73e3587b65ec | 256 | void resetbeeholder() { //need this as mounting beeholder causes undefined start of beeholder firmware |
marcbax | 0:73e3587b65ec | 257 | bhsel1 = bhsel2 = 0; |
marcbax | 0:73e3587b65ec | 258 | wait(0.1); |
marcbax | 0:73e3587b65ec | 259 | bhsel1 = bhsel2 = 1; |
marcbax | 0:73e3587b65ec | 260 | wait(0.3); |
marcbax | 0:73e3587b65ec | 261 | } |
marcbax | 0:73e3587b65ec | 262 | |
marcbax | 0:73e3587b65ec | 263 | //Reads beeholder serial number from station 1 or 2 |
marcbax | 0:73e3587b65ec | 264 | int getserialno(int station) { |
marcbax | 0:73e3587b65ec | 265 | char bser[8]; //define 8-byte serial number string to read |
marcbax | 0:73e3587b65ec | 266 | resetbeeholder(); //does not work without this!! |
marcbax | 0:73e3587b65ec | 267 | for (int i=0;i<8;i++) bser[i]=0x30; |
marcbax | 0:73e3587b65ec | 268 | if (station==1) bhsel1=0; //pull select line station 1 low |
marcbax | 0:73e3587b65ec | 269 | if (station==2) bhsel2=0; //pull select line station 2 low |
marcbax | 0:73e3587b65ec | 270 | wait(0.2); |
marcbax | 0:73e3587b65ec | 271 | beeholder.stop(); //I2C stop condition |
marcbax | 0:73e3587b65ec | 272 | wait(0.2); //delay for beeholder to respond |
marcbax | 0:73e3587b65ec | 273 | beeholder.write(addr,0x0,1); //initial write before read |
marcbax | 0:73e3587b65ec | 274 | beeholder.read(addr,bser,8); //read 8 byte serial number |
marcbax | 0:73e3587b65ec | 275 | bhsel1 = bhsel2= 1; //pull both select lines high |
marcbax | 0:73e3587b65ec | 276 | int serialno = serialstring2int(bser); //translate serial number string to integer |
marcbax | 0:73e3587b65ec | 277 | return serialno; |
marcbax | 0:73e3587b65ec | 278 | } |
marcbax | 0:73e3587b65ec | 279 | |
marcbax | 0:73e3587b65ec | 280 | //Returns 1 if a PER is detected on this beeholder within timeout seconds |
marcbax | 0:73e3587b65ec | 281 | int detectPER(int station, float timeout) { |
marcbax | 0:73e3587b65ec | 282 | Timer ttotal, tper; |
marcbax | 0:73e3587b65ec | 283 | ttotal.start(); //start timers for time-out and PER-detect |
marcbax | 0:73e3587b65ec | 284 | ttotal.reset(); |
marcbax | 0:73e3587b65ec | 285 | tper.start(); |
marcbax | 0:73e3587b65ec | 286 | tper.reset(); |
marcbax | 0:73e3587b65ec | 287 | if (station == 1) { |
marcbax | 0:73e3587b65ec | 288 | while ((ttotal.read() < timeout) && (tper.read() < PERtime)) { //loop until timeout or PER detected |
marcbax | 0:73e3587b65ec | 289 | wait_ms(10); |
marcbax | 0:73e3587b65ec | 290 | if (ptor1 * 3.3 < PERsetval) tper.reset(); //if phototransistor voltage below treshold keep PER timer in reset |
marcbax | 0:73e3587b65ec | 291 | //if above treshold let timer run until it reaches PERtime |
marcbax | 0:73e3587b65ec | 292 | } |
marcbax | 0:73e3587b65ec | 293 | } |
marcbax | 0:73e3587b65ec | 294 | if (station == 2) { |
marcbax | 0:73e3587b65ec | 295 | while ((ttotal.read() < timeout) && (tper.read() < PERtime)) { |
marcbax | 0:73e3587b65ec | 296 | if (ptor2 * 3.3 < PERsetval) tper.reset(); |
marcbax | 0:73e3587b65ec | 297 | } |
marcbax | 0:73e3587b65ec | 298 | } |
marcbax | 0:73e3587b65ec | 299 | ttotal.stop(); |
marcbax | 0:73e3587b65ec | 300 | tper.stop(); |
marcbax | 0:73e3587b65ec | 301 | return (tper.read() >= PERtime); //if the loop exit condition was a PER, return a TRUE value |
marcbax | 0:73e3587b65ec | 302 | } |
marcbax | 0:73e3587b65ec | 303 | |
marcbax | 0:73e3587b65ec | 304 | //Checks if bee shows PER when stimulated with odour but without feeding |
marcbax | 0:73e3587b65ec | 305 | int checktrained(int station) { |
marcbax | 0:73e3587b65ec | 306 | airswitcher = 1; |
marcbax | 0:73e3587b65ec | 307 | int responded = detectPER(station, 6); //PER needs to be detected within 6 seconds |
marcbax | 0:73e3587b65ec | 308 | airswitcher = 0; |
marcbax | 0:73e3587b65ec | 309 | return responded; |
marcbax | 0:73e3587b65ec | 310 | } |
marcbax | 0:73e3587b65ec | 311 | |
marcbax | 0:73e3587b65ec | 312 | //Function performs beeholder/IR-led calibration |
marcbax | 0:73e3587b65ec | 313 | float calibrate(int station, int runs) { |
marcbax | 0:73e3587b65ec | 314 | float tempcal; |
marcbax | 0:73e3587b65ec | 315 | tempcal=0.5; //start calibration at 50% voltage |
marcbax | 0:73e3587b65ec | 316 | irLED=tempcal; |
marcbax | 0:73e3587b65ec | 317 | float calstep; //start calstep at 25% voltage |
marcbax | 0:73e3587b65ec | 318 | calstep = tempcal/2; |
marcbax | 0:73e3587b65ec | 319 | for (int i=0; i<10; i++) { //does a "10-bit binary search" for the correct voltage to get a good response |
marcbax | 0:73e3587b65ec | 320 | irLED = tempcal; |
marcbax | 0:73e3587b65ec | 321 | wait(0.1); //important to allow AD converter to settle |
marcbax | 0:73e3587b65ec | 322 | if (ptor1 < calsetval/3.3) { //check phototransistor voltage against desired value |
marcbax | 0:73e3587b65ec | 323 | tempcal = tempcal - calstep; //if phototransistor voltage is too low then reduce brightness |
marcbax | 0:73e3587b65ec | 324 | } |
marcbax | 0:73e3587b65ec | 325 | else { |
marcbax | 0:73e3587b65ec | 326 | tempcal = tempcal + calstep; //if phototransistor voltage is too high then increase brightness |
marcbax | 0:73e3587b65ec | 327 | } |
marcbax | 0:73e3587b65ec | 328 | calstep = calstep/2; //on each loop of the for-cycle make smaller changes to IR LED voltage |
marcbax | 0:73e3587b65ec | 329 | } |
marcbax | 0:73e3587b65ec | 330 | float calib; |
marcbax | 0:73e3587b65ec | 331 | calib = tempcal; //set preliminary calibration to the value just measured |
marcbax | 0:73e3587b65ec | 332 | for (int j=1; j<runs; j++) { //run another j-1 runs, this corrects for antennae-movement as |
marcbax | 0:73e3587b65ec | 333 | tempcal=0.5; //we use the lowest calibration value from j runs |
marcbax | 0:73e3587b65ec | 334 | irLED=tempcal; //this is similar to what we do in the cassettes |
marcbax | 0:73e3587b65ec | 335 | calstep = tempcal/2; |
marcbax | 0:73e3587b65ec | 336 | for (int i=0;i<10;i++) { |
marcbax | 0:73e3587b65ec | 337 | irLED = tempcal; |
marcbax | 0:73e3587b65ec | 338 | wait(0.1); |
marcbax | 0:73e3587b65ec | 339 | if (ptor1 < calsetval/3.3) { |
marcbax | 0:73e3587b65ec | 340 | tempcal = tempcal - calstep; |
marcbax | 0:73e3587b65ec | 341 | } |
marcbax | 0:73e3587b65ec | 342 | else { |
marcbax | 0:73e3587b65ec | 343 | tempcal = tempcal + calstep; |
marcbax | 0:73e3587b65ec | 344 | } |
marcbax | 0:73e3587b65ec | 345 | calstep = calstep/2; |
marcbax | 0:73e3587b65ec | 346 | } |
marcbax | 0:73e3587b65ec | 347 | if (tempcal < calib) calib = tempcal; //use the lowest of j calibration values |
marcbax | 0:73e3587b65ec | 348 | } |
marcbax | 0:73e3587b65ec | 349 | return calib; |
marcbax | 0:73e3587b65ec | 350 | } |
marcbax | 0:73e3587b65ec | 351 | |
marcbax | 0:73e3587b65ec | 352 | //switches the IR LED on at the right brightness level for the beeholder |
marcbax | 0:73e3587b65ec | 353 | void IRledON(int i) { |
marcbax | 0:73e3587b65ec | 354 | irLED = sessionrecord[i].calvalue; |
marcbax | 0:73e3587b65ec | 355 | } |
marcbax | 0:73e3587b65ec | 356 | |
marcbax | 0:73e3587b65ec | 357 | //moves arm1 to a position in a specified time, allowing speed control |
marcbax | 0:73e3587b65ec | 358 | void arm1move (float endpos, float movetime) { |
marcbax | 0:73e3587b65ec | 359 | float startpos = arm1.read(); |
marcbax | 0:73e3587b65ec | 360 | int numsteps = (movetime * 50); //50 pulses/second, so each step is 1 servo pulse |
marcbax | 0:73e3587b65ec | 361 | for (int i=0; i<numsteps; i++) { |
marcbax | 0:73e3587b65ec | 362 | arm1 = startpos + i * (endpos - startpos)/numsteps; |
marcbax | 0:73e3587b65ec | 363 | wait(movetime/numsteps); |
marcbax | 0:73e3587b65ec | 364 | } |
marcbax | 0:73e3587b65ec | 365 | arm1 = endpos; |
marcbax | 0:73e3587b65ec | 366 | } |
marcbax | 0:73e3587b65ec | 367 | |
marcbax | 0:73e3587b65ec | 368 | //moves arm2 to a position in a specified time, allowing speed control |
marcbax | 0:73e3587b65ec | 369 | void arm2move (float endpos, float movetime) { |
marcbax | 0:73e3587b65ec | 370 | float startpos = arm2.read(); |
marcbax | 0:73e3587b65ec | 371 | int numsteps = (movetime * 50); //50 pulses/second, so each step is 1 servo pulse |
marcbax | 0:73e3587b65ec | 372 | for (int i=0; i<numsteps; i++) { |
marcbax | 0:73e3587b65ec | 373 | arm2 = startpos + i * (endpos - startpos)/numsteps; |
marcbax | 0:73e3587b65ec | 374 | wait(movetime/numsteps); |
marcbax | 0:73e3587b65ec | 375 | } |
marcbax | 0:73e3587b65ec | 376 | arm2 = endpos; |
marcbax | 0:73e3587b65ec | 377 | } |
marcbax | 0:73e3587b65ec | 378 | |
marcbax | 0:73e3587b65ec | 379 | //moves elbow1 to a position in a specified time, allowing speed control |
marcbax | 0:73e3587b65ec | 380 | void elbow1move (float endpos, float movetime) { |
marcbax | 0:73e3587b65ec | 381 | float startpos = elbow1.read(); |
marcbax | 0:73e3587b65ec | 382 | int numsteps = (movetime * 50); //50 pulses/second, so each step is 1 servo pulse |
marcbax | 0:73e3587b65ec | 383 | for (int i=0; i<numsteps; i++) { |
marcbax | 0:73e3587b65ec | 384 | elbow1 = startpos + i * (endpos - startpos)/numsteps; |
marcbax | 0:73e3587b65ec | 385 | wait(movetime/numsteps); |
marcbax | 0:73e3587b65ec | 386 | } |
marcbax | 0:73e3587b65ec | 387 | elbow1 = endpos; |
marcbax | 0:73e3587b65ec | 388 | } |
marcbax | 0:73e3587b65ec | 389 | |
marcbax | 0:73e3587b65ec | 390 | //moves elbow2 to a position in a specified time, allowing speed control |
marcbax | 0:73e3587b65ec | 391 | void elbow2move (float endpos, float movetime) { |
marcbax | 0:73e3587b65ec | 392 | float startpos = elbow2.read(); |
marcbax | 0:73e3587b65ec | 393 | int numsteps = (movetime * 50); //50 pulses/second, so each step is 1 servo pulse |
marcbax | 0:73e3587b65ec | 394 | for (int i=0; i<numsteps; i++) { |
marcbax | 0:73e3587b65ec | 395 | elbow2 = startpos + i * (endpos - startpos)/numsteps; |
marcbax | 0:73e3587b65ec | 396 | wait(movetime/numsteps); |
marcbax | 0:73e3587b65ec | 397 | } |
marcbax | 0:73e3587b65ec | 398 | elbow2 = endpos; |
marcbax | 0:73e3587b65ec | 399 | } |
marcbax | 0:73e3587b65ec | 400 | |
marcbax | 0:73e3587b65ec | 401 | //Performs a conditioning cycle. if tickle==0, no tickling takes place. Return==1 if a PER has been detected |
marcbax | 0:73e3587b65ec | 402 | int condcycle(int station, int tickle) { |
marcbax | 0:73e3587b65ec | 403 | int perseen; |
marcbax | 0:73e3587b65ec | 404 | perseen = 0; |
marcbax | 0:73e3587b65ec | 405 | //for station 1 |
marcbax | 0:73e3587b65ec | 406 | if (station == 1) { |
marcbax | 0:73e3587b65ec | 407 | //dip brush |
marcbax | 0:73e3587b65ec | 408 | arm1move(arm1dippos, 0.5); |
marcbax | 0:73e3587b65ec | 409 | elbow1move(elbow1dippos, 0.3); |
marcbax | 0:73e3587b65ec | 410 | wait(diptime); |
marcbax | 0:73e3587b65ec | 411 | elbow1move(elbow1basepos, 0.3); |
marcbax | 0:73e3587b65ec | 412 | arm1move(arm1basepos, 0.5); |
marcbax | 0:73e3587b65ec | 413 | //switch air to supply trace vapour |
marcbax | 0:73e3587b65ec | 414 | airswitcher = 1; //air contains target vapour |
marcbax | 0:73e3587b65ec | 415 | display.printf("Vapour ON"); |
marcbax | 0:73e3587b65ec | 416 | wait (0.5); |
marcbax | 0:73e3587b65ec | 417 | //tickle |
marcbax | 0:73e3587b65ec | 418 | if (tickle) { //if tickling, first tickle then wait for PER or timeout |
marcbax | 0:73e3587b65ec | 419 | elbow1move(elbow1ticklepos, 0.2); |
marcbax | 0:73e3587b65ec | 420 | arm1move(arm1ticklepos, 1.5); //slower move of arm towards bee |
marcbax | 0:73e3587b65ec | 421 | perseen = detectPER(1, tickletimeout); //tickle until timeout or PER detected |
marcbax | 0:73e3587b65ec | 422 | } |
marcbax | 0:73e3587b65ec | 423 | //or not tickle |
marcbax | 0:73e3587b65ec | 424 | else { |
marcbax | 0:73e3587b65ec | 425 | perseen = detectPER(1, tickletimeout); //if not tickling, wait for PER or timeout then move to pre-feeding position |
marcbax | 0:73e3587b65ec | 426 | arm1move(arm1ticklepos-0.05, 1); //move to position between LED and holder |
marcbax | 0:73e3587b65ec | 427 | elbow1move(elbow1ticklepos, 0.3); |
marcbax | 0:73e3587b65ec | 428 | } |
marcbax | 0:73e3587b65ec | 429 | //feeding only if you have tickled or a PER has been detected |
marcbax | 0:73e3587b65ec | 430 | if (tickle || perseen) { //only feed if a PER has been detector, or "tickle" is true |
marcbax | 0:73e3587b65ec | 431 | elbow1move(elbow1feedpos, 0.3); |
marcbax | 0:73e3587b65ec | 432 | arm1move(arm1feedpos, 0.3); |
marcbax | 0:73e3587b65ec | 433 | wait(feedtime); |
marcbax | 0:73e3587b65ec | 434 | arm1move(arm1ticklepos -0.05, 0.3); |
marcbax | 0:73e3587b65ec | 435 | elbow1move(elbow1ticklepos, 0.3); |
marcbax | 0:73e3587b65ec | 436 | } |
marcbax | 0:73e3587b65ec | 437 | //move back to base position |
marcbax | 0:73e3587b65ec | 438 | arm1 = arm1basepos; //use fast move here |
marcbax | 0:73e3587b65ec | 439 | elbow1 = elbow1basepos; //use fast move here |
marcbax | 0:73e3587b65ec | 440 | airswitcher = 0; //air valve to clean air |
marcbax | 0:73e3587b65ec | 441 | display.printf("\e[DFF\r"); //rewrite "ON" to "OFF" on display |
marcbax | 0:73e3587b65ec | 442 | } |
marcbax | 0:73e3587b65ec | 443 | //for station 2 |
marcbax | 0:73e3587b65ec | 444 | if (station == 2) { |
marcbax | 0:73e3587b65ec | 445 | //dip brush |
marcbax | 0:73e3587b65ec | 446 | arm2move(arm2dippos, 0.5); |
marcbax | 0:73e3587b65ec | 447 | elbow2move(elbow2dippos, 0.3); |
marcbax | 0:73e3587b65ec | 448 | wait(diptime); |
marcbax | 0:73e3587b65ec | 449 | elbow2move(elbow2basepos, 0.3); |
marcbax | 0:73e3587b65ec | 450 | arm2move(arm2basepos, 0.5); |
marcbax | 0:73e3587b65ec | 451 | //switch air to supply trace vapour |
marcbax | 0:73e3587b65ec | 452 | //airswitcher = 1; //no air switching for station 2 |
marcbax | 0:73e3587b65ec | 453 | wait (0.5); |
marcbax | 0:73e3587b65ec | 454 | //tickle |
marcbax | 0:73e3587b65ec | 455 | if (tickle) { //if tickling, first tickle then wait for PER or timeout |
marcbax | 0:73e3587b65ec | 456 | elbow2move(elbow2ticklepos, 0.2); |
marcbax | 0:73e3587b65ec | 457 | arm2move(arm2ticklepos, 1.5); //slower move of arm towards bee |
marcbax | 0:73e3587b65ec | 458 | perseen = detectPER(1, tickletimeout); //tickle until timeout or PER detected |
marcbax | 0:73e3587b65ec | 459 | } |
marcbax | 0:73e3587b65ec | 460 | //or not tickle |
marcbax | 0:73e3587b65ec | 461 | else { |
marcbax | 0:73e3587b65ec | 462 | perseen = detectPER(1, tickletimeout); //if not tickling, wait for PER or timeout then move to pre-feeding position |
marcbax | 0:73e3587b65ec | 463 | arm2move(arm2ticklepos-0.05, 1); //move to position between LED and holder |
marcbax | 0:73e3587b65ec | 464 | elbow2move(elbow2ticklepos, 0.3); |
marcbax | 0:73e3587b65ec | 465 | } |
marcbax | 0:73e3587b65ec | 466 | //feeding only if you have tickled or a PER has been detected |
marcbax | 0:73e3587b65ec | 467 | if (tickle || perseen) { //only feed if a PER has been detector, or "tickle" is true |
marcbax | 0:73e3587b65ec | 468 | elbow2move(elbow2feedpos, 0.3); |
marcbax | 0:73e3587b65ec | 469 | arm2move(arm2feedpos, 0.3); |
marcbax | 0:73e3587b65ec | 470 | wait(feedtime); |
marcbax | 0:73e3587b65ec | 471 | arm2move(arm2ticklepos -0.05, 0.3); |
marcbax | 0:73e3587b65ec | 472 | elbow2move(elbow2ticklepos, 0.3); |
marcbax | 0:73e3587b65ec | 473 | } |
marcbax | 0:73e3587b65ec | 474 | //move back to base position |
marcbax | 0:73e3587b65ec | 475 | arm2 = arm2basepos; |
marcbax | 0:73e3587b65ec | 476 | elbow2 = elbow2basepos; |
marcbax | 0:73e3587b65ec | 477 | } |
marcbax | 0:73e3587b65ec | 478 | return perseen; |
marcbax | 0:73e3587b65ec | 479 | } |
marcbax | 0:73e3587b65ec | 480 | |
marcbax | 0:73e3587b65ec | 481 | //User to input choice between station 1 and 2 for this session |
marcbax | 0:73e3587b65ec | 482 | int stationchoice() { |
marcbax | 0:73e3587b65ec | 483 | int station; |
marcbax | 0:73e3587b65ec | 484 | station = 1; |
marcbax | 0:73e3587b65ec | 485 | cleardisplay(); //clear screen and home cursor |
marcbax | 0:73e3587b65ec | 486 | display.printf(" Station selection "); |
marcbax | 0:73e3587b65ec | 487 | display.printf("Left=1 2=Right"); |
marcbax | 0:73e3587b65ec | 488 | while (lesw && risw) { //lesw and risw become false when pressed! |
marcbax | 0:73e3587b65ec | 489 | wait(0.02); |
marcbax | 0:73e3587b65ec | 490 | if (!lesw) station=1; //on LEFT select station 1 |
marcbax | 0:73e3587b65ec | 491 | if (!risw) station=2; //on RIGHT select station 2 |
marcbax | 0:73e3587b65ec | 492 | } |
marcbax | 0:73e3587b65ec | 493 | display.printf("\rStation %1u selected", station); |
marcbax | 0:73e3587b65ec | 494 | fprintf(logfile, "%3u, station selected\r", station); |
marcbax | 0:73e3587b65ec | 495 | return station; |
marcbax | 0:73e3587b65ec | 496 | } |
marcbax | 0:73e3587b65ec | 497 | |
marcbax | 0:73e3587b65ec | 498 | //Registers and calibrates all beeholders used in this session |
marcbax | 0:73e3587b65ec | 499 | int registerbeeholders() { |
marcbax | 0:73e3587b65ec | 500 | int i; |
marcbax | 0:73e3587b65ec | 501 | bool done; |
marcbax | 0:73e3587b65ec | 502 | char buffert[30]; |
marcbax | 0:73e3587b65ec | 503 | i = done = 0; |
marcbax | 0:73e3587b65ec | 504 | cleardisplay(); //clear screen and home cursor |
marcbax | 0:73e3587b65ec | 505 | fprintf(logfile, "calibration record:\r"); |
marcbax | 0:73e3587b65ec | 506 | fprintf(logfile, "i, serialno, LED V, time\r"); |
marcbax | 0:73e3587b65ec | 507 | while (i<30 && !done) { //register and calibrate a maximum of 30 beeholders |
marcbax | 0:73e3587b65ec | 508 | display.printf("calibrating %u\r",i+1); |
marcbax | 0:73e3587b65ec | 509 | sessionrecord[i].serialno = getserialno(station); //read serial number |
marcbax | 0:73e3587b65ec | 510 | if (sessionrecord[i].serialno != 0) { //check if serial number correctly read - if not it will be 0000 |
marcbax | 0:73e3587b65ec | 511 | sessionrecord[i].calvalue = calibrate(station, 5); //5 calibration cycles |
marcbax | 0:73e3587b65ec | 512 | if ((sessionrecord[i].calvalue > 0.25) && (sessionrecord[i].calvalue < 0.97)) { //check that calvalue is in expected range |
marcbax | 0:73e3587b65ec | 513 | sessionrecord[i].tstamp = time(NULL); //create timestamp NOW |
marcbax | 0:73e3587b65ec | 514 | strftime(buffert, 20, "%X", localtime(&sessionrecord[i].tstamp)); //formats time part of timestamp |
marcbax | 0:73e3587b65ec | 515 | cleardisplay(); |
marcbax | 0:73e3587b65ec | 516 | display.printf("SN %4u - cal %4.2fV\r", sessionrecord[i].serialno, sessionrecord[i].calvalue*3.3); |
marcbax | 0:73e3587b65ec | 517 | display.printf("OK for next\r"); |
marcbax | 0:73e3587b65ec | 518 | display.printf("DOWN for training"); |
marcbax | 0:73e3587b65ec | 519 | fprintf(logfile, "%4u,%6u,%6.2f, %s, calibrated\r", i, sessionrecord[i].serialno, sessionrecord[i].calvalue*3.3, buffert); |
marcbax | 0:73e3587b65ec | 520 | i++; |
marcbax | 0:73e3587b65ec | 521 | } |
marcbax | 0:73e3587b65ec | 522 | else { |
marcbax | 0:73e3587b65ec | 523 | cleardisplay(); |
marcbax | 0:73e3587b65ec | 524 | display.printf("SN %4u - cal %4.2fV\r", sessionrecord[i].serialno, sessionrecord[i].calvalue*3.3); |
marcbax | 0:73e3587b65ec | 525 | display.printf("Cal out of range!\r"); |
marcbax | 0:73e3587b65ec | 526 | multibeeps(2,0.5); |
marcbax | 0:73e3587b65ec | 527 | display.printf("OK to recalibrate\r"); |
marcbax | 0:73e3587b65ec | 528 | fprintf(logfile, "%4u,%6u,%6.2f, %s, out of range\r", i, sessionrecord[i].serialno, sessionrecord[i].calvalue*3.3, buffert); |
marcbax | 0:73e3587b65ec | 529 | } |
marcbax | 0:73e3587b65ec | 530 | while (!done && oksw) { //loop until OK or DOWN are pressed |
marcbax | 0:73e3587b65ec | 531 | wait(0.02); |
marcbax | 0:73e3587b65ec | 532 | done = !dnsw; //DOWN exits registration cycle and moves to training |
marcbax | 0:73e3587b65ec | 533 | } |
marcbax | 0:73e3587b65ec | 534 | } |
marcbax | 0:73e3587b65ec | 535 | else { //retry when serialno can't be read (is 0000) |
marcbax | 0:73e3587b65ec | 536 | cleardisplay(); |
marcbax | 0:73e3587b65ec | 537 | multibeeps(3,0.3); //beep-beep-beep when beeholder not correctly read |
marcbax | 0:73e3587b65ec | 538 | display.printf("invalid serial no\r"); |
marcbax | 0:73e3587b65ec | 539 | display.printf("reseat beeholder\r"); |
marcbax | 0:73e3587b65ec | 540 | display.printf("press OK"); |
marcbax | 0:73e3587b65ec | 541 | while (oksw) wait(0.02); //loop until OK is pressed to start calibration loop again |
marcbax | 0:73e3587b65ec | 542 | } |
marcbax | 0:73e3587b65ec | 543 | cleardisplay(); |
marcbax | 0:73e3587b65ec | 544 | } |
marcbax | 0:73e3587b65ec | 545 | return i; //upon done condition, i== number of beeholders calibrated |
marcbax | 0:73e3587b65ec | 546 | } |
marcbax | 0:73e3587b65ec | 547 | |
marcbax | 0:73e3587b65ec | 548 | //gets holderrecord for the holder with a certain serial number |
marcbax | 0:73e3587b65ec | 549 | int getholderindex(int serialno) { |
marcbax | 0:73e3587b65ec | 550 | for (int i=0; i<numbeeholders; i++) { //reverse lookup of index for a certain serial number |
marcbax | 0:73e3587b65ec | 551 | if (sessionrecord[i].serialno == serialno) return i; |
marcbax | 0:73e3587b65ec | 552 | } |
marcbax | 0:73e3587b65ec | 553 | return 29; //if the record is not found, returns i==29 |
marcbax | 0:73e3587b65ec | 554 | } |
marcbax | 0:73e3587b65ec | 555 | |
marcbax | 0:73e3587b65ec | 556 | //all the elements making up a single training cycle with one beeholder |
marcbax | 0:73e3587b65ec | 557 | void trainingcycle() { |
marcbax | 0:73e3587b65ec | 558 | wait(0.2); |
marcbax | 0:73e3587b65ec | 559 | time_t tstamp = time(NULL); //create timestamp NOW |
marcbax | 0:73e3587b65ec | 560 | char buffert[30]; |
marcbax | 0:73e3587b65ec | 561 | strftime(buffert, 20, "%X", localtime(&tstamp)); //format timestamp to time string |
marcbax | 0:73e3587b65ec | 562 | bool invalidserial; |
marcbax | 0:73e3587b65ec | 563 | invalidserial = 1; |
marcbax | 0:73e3587b65ec | 564 | int serialno; |
marcbax | 0:73e3587b65ec | 565 | serialno = 0; |
marcbax | 0:73e3587b65ec | 566 | while (invalidserial) { //loop until a serial number is read correctly |
marcbax | 0:73e3587b65ec | 567 | cleardisplay(); |
marcbax | 0:73e3587b65ec | 568 | int serialno = getserialno(station); //read serial number |
marcbax | 0:73e3587b65ec | 569 | invalidserial = (serialno == 0); //if the serialno is 0, then it's invalid |
marcbax | 0:73e3587b65ec | 570 | if (invalidserial) { |
marcbax | 0:73e3587b65ec | 571 | display.printf("Invalid serialno\rReseat holder\rUP to retry"); |
marcbax | 0:73e3587b65ec | 572 | while (upsw) wait(0.02); //loop until UP switch is pressed |
marcbax | 0:73e3587b65ec | 573 | } |
marcbax | 0:73e3587b65ec | 574 | } |
marcbax | 0:73e3587b65ec | 575 | int i = getholderindex(serialno); //get index i for serial number |
marcbax | 0:73e3587b65ec | 576 | IRledON(i); //switch IR LED on at correct brightness |
marcbax | 0:73e3587b65ec | 577 | sessionrecord[i].cycleno++; //increment cycle number for this beeholder |
marcbax | 0:73e3587b65ec | 578 | cleardisplay(); |
marcbax | 0:73e3587b65ec | 579 | display.printf("SN: %4u, cycle %u\r", serialno, sessionrecord[i].cycleno); |
marcbax | 0:73e3587b65ec | 580 | sessionrecord[i].reslastcycle = condcycle(station, sessionrecord[i].ticklenextcycle); //do a conditioning cycle |
marcbax | 0:73e3587b65ec | 581 | fprintf(logfile, "%s,",buffert); |
marcbax | 0:73e3587b65ec | 582 | fprintf(logfile, " %4u,",serialno); |
marcbax | 0:73e3587b65ec | 583 | fprintf(logfile, " %2u,", sessionrecord[i].cycleno); |
marcbax | 0:73e3587b65ec | 584 | if (sessionrecord[i].reslastcycle) { //log PER or TimeOut |
marcbax | 0:73e3587b65ec | 585 | fprintf(logfile, " PER,"); |
marcbax | 0:73e3587b65ec | 586 | } |
marcbax | 0:73e3587b65ec | 587 | else { |
marcbax | 0:73e3587b65ec | 588 | fprintf(logfile, " TO,"); |
marcbax | 0:73e3587b65ec | 589 | } |
marcbax | 0:73e3587b65ec | 590 | fprintf(logfile, " training\r"); |
marcbax | 0:73e3587b65ec | 591 | if (sessionrecord[i].reslastcycle) { |
marcbax | 0:73e3587b65ec | 592 | display.printf("PER detected\r"); |
marcbax | 0:73e3587b65ec | 593 | } |
marcbax | 0:73e3587b65ec | 594 | else { |
marcbax | 0:73e3587b65ec | 595 | display.printf("PER time-out\r"); |
marcbax | 0:73e3587b65ec | 596 | } |
marcbax | 0:73e3587b65ec | 597 | display.printf("mount holder + OK\r"); |
marcbax | 0:73e3587b65ec | 598 | display.printf("DOWN to finish"); |
marcbax | 0:73e3587b65ec | 599 | } |
marcbax | 0:73e3587b65ec | 600 | |
marcbax | 0:73e3587b65ec | 601 | //main program |
marcbax | 0:73e3587b65ec | 602 | int main() { |
marcbax | 0:73e3587b65ec | 603 | initialise(); //initialise a new session |
marcbax | 0:73e3587b65ec | 604 | |
marcbax | 0:73e3587b65ec | 605 | //Choose which station this session will use |
marcbax | 0:73e3587b65ec | 606 | station = stationchoice (); //menu to let user select station 1 or 2 |
marcbax | 0:73e3587b65ec | 607 | cleardisplay(); |
marcbax | 0:73e3587b65ec | 608 | |
marcbax | 0:73e3587b65ec | 609 | //Choose substance string |
marcbax | 0:73e3587b65ec | 610 | substanceindex = subchoice(submenu); //menu to let use chose substance |
marcbax | 0:73e3587b65ec | 611 | fprintf(logfile, "%s\r", submenu[substanceindex].logstring); //prints the substance log string to the logfile |
marcbax | 0:73e3587b65ec | 612 | |
marcbax | 0:73e3587b65ec | 613 | //Register and calibrate beeholders used in this session |
marcbax | 0:73e3587b65ec | 614 | display.printf("Now register holders\r"); |
marcbax | 0:73e3587b65ec | 615 | display.printf("seat first holder\rpress OK\r"); |
marcbax | 0:73e3587b65ec | 616 | while (oksw) wait(0.02); //loop so first beeholder can be mounted, then OK pressed |
marcbax | 0:73e3587b65ec | 617 | numbeeholders = registerbeeholders(); |
marcbax | 0:73e3587b65ec | 618 | display.printf("%2u holders entered\r", numbeeholders); |
marcbax | 0:73e3587b65ec | 619 | wait(3); |
marcbax | 0:73e3587b65ec | 620 | cleardisplay(); |
marcbax | 0:73e3587b65ec | 621 | |
marcbax | 0:73e3587b65ec | 622 | //Conduct training |
marcbax | 0:73e3587b65ec | 623 | display.printf("Start training cycle\r"); |
marcbax | 0:73e3587b65ec | 624 | display.printf("mount holder + OK\r"); |
marcbax | 0:73e3587b65ec | 625 | display.printf("DOWN to finish"); |
marcbax | 0:73e3587b65ec | 626 | fprintf(logfile, "Training started\r"); |
marcbax | 0:73e3587b65ec | 627 | fprintf(logfile, "time, serial, cycleno, result\r"); |
marcbax | 0:73e3587b65ec | 628 | wait(1); |
marcbax | 0:73e3587b65ec | 629 | InterruptIn oksw(p17); //redefine OK and footswitch as interrupts |
marcbax | 0:73e3587b65ec | 630 | oksw.mode(PullUp); |
marcbax | 0:73e3587b65ec | 631 | bool finishsession; |
marcbax | 0:73e3587b65ec | 632 | finishsession = 0; |
marcbax | 0:73e3587b65ec | 633 | oksw.fall(&trainingcycle); //call subroutine "trainingcycle" on OK/footswitch interrupt |
marcbax | 0:73e3587b65ec | 634 | while (!finishsession) { //loop while servicing interrupts |
marcbax | 0:73e3587b65ec | 635 | wait(0.02); |
marcbax | 0:73e3587b65ec | 636 | finishsession = !dnsw; //until DOWN button is pressed, which exits to program close |
marcbax | 0:73e3587b65ec | 637 | } |
marcbax | 0:73e3587b65ec | 638 | |
marcbax | 0:73e3587b65ec | 639 | //Close logfile |
marcbax | 0:73e3587b65ec | 640 | fprintf(logfile, "session closed"); |
marcbax | 0:73e3587b65ec | 641 | fclose(logfile); //close logfile for reading |
marcbax | 0:73e3587b65ec | 642 | cleardisplay(); |
marcbax | 0:73e3587b65ec | 643 | display.printf("Session finished\r\rdownload etonlog.csvand rename"); |
marcbax | 0:73e3587b65ec | 644 | } |