Firmware for Eton breadboard

Dependencies:   mbed Servo

Files at this revision

API Documentation at this revision

Comitter:
marcbax
Date:
Wed Mar 23 09:39:57 2011 +0000
Commit message:
v0.80, date 23/3/2011

Changed in this revision

Servo.lib Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
mbed.bld Show annotated file Show diff for this revision Revisions of this file
diff -r 000000000000 -r 2017f5924a22 Servo.lib
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Servo.lib	Wed Mar 23 09:39:57 2011 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/simon/code/Servo/#36b69a7ced07
diff -r 000000000000 -r 2017f5924a22 main.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp	Wed Mar 23 09:39:57 2011 +0000
@@ -0,0 +1,499 @@
+//Firmware to drive Eton bee trainer breadboard
+char* fwversion = "0.80";                                          //For published versions use format "n.nn"
+                                                                    //For development versions add "D" to end of string
+//This version published on 23/03/2011
+
+//Include libraries
+#include "mbed.h"
+#include "Servo.h"                                                  //need this library to control servos
+
+//Pin assignments and external functions
+DigitalOut led1(LED1), led2(LED2), led3(LED3), led4(LED4);          //for debugging purposes only
+DigitalOut buzzer(p5);                                              //pin high means buzzer on
+DigitalOut airswitcher(p7);                                         //pin high shorts coax for air switcher valve
+Serial display(p9,p10);                                             //serial display controller by ByVAC BV4618 controller
+AnalogOut irLED(p18);                                               //only one analog out controls both IR LEDS in parallel
+AnalogIn ptor1(p19), ptor2(p20);                                    //analog input for beeholder phototransistors
+Servo arm1(p21), elbow1(p22);                                       //servos for station 1 robot arm
+Servo arm2(p23), elbow2(p24);                                       //servos for station 2 robot arm
+I2C beeholder(p28,p27);                                             //set up I2C communication to read beeholder serial no
+DigitalOut bhsel1(p29), bhsel2(p30);                                //select lines for station 1 and station 2 - pull low to select holder
+DigitalIn upsw(p13), risw(p14), dnsw(p15), lesw(p16), oksw(p17);    //footswitch connected parallel to OK switch
+LocalFileSystem local("local");                                     //allow access to the mbed "thumbdrive" as a file system
+FILE *logfile = fopen("/local/etonlog.csv","w");                    //creates new file etonlog.csv and set for writing. Overwrites existing file!
+
+//Definition of global constants
+    //station 1 (left) robot arm positions
+    float arm1basepos = 0.4;
+    float elbow1basepos = 0.07;
+    float arm1dippos = 0.2;
+    float elbow1dippos = 0.6;
+    float arm1ticklepos = 0.68;
+    float elbow1ticklepos = 0.07;
+    float arm1feedpos = 0.73;
+    float elbow1feedpos = 0.4;
+
+    //station 2 (right) robot arm positions
+    float arm2basepos = 0.4;
+    float elbow2basepos = 0.07;
+    float arm2dippos = 0.2;
+    float elbow2dippos = 0.07;
+    float arm2ticklepos = 0.2;
+    float elbow2ticklepos = 0.07;
+    float arm2feedpos = 0.2;
+    float elbow2feedpos = 0.07;
+    
+    //robot arm hold times                                            all times are in seconds
+    float diptime = 1;                                              //time that brush stays down dipped in sugar water
+    float tickletimeout = 1.5;                                      //maximum time to tickle if no PER is detected
+    float feedtime = 3;                                             //feeding time
+    
+    //PER detection parameters
+    float calsetval = 1.4;                                          //voltage to which phototransistor output will be calibrated  
+    float PERsetval = 2.1;                                          //phototransistor output needs to be above this voltage for PER
+    float PERtime = 0.2;                                            //phototransistor output needs to stay high this long in seconds
+    
+    //I2C address
+    int addr = 0xA2;                                                //address is actually 0x51, but mbed expects left-shifted address
+
+//Definition of global variables
+
+struct holderrecord {                                               //used to hold data on beeholder
+    int serialno;                                                   //beeholder serial number 0-9999
+    float calvalue;                                                 //IR-LED calibration value
+    char cycleno;                                                   //number of cycles gone through in this session
+    char reslastcycle;                                              //result of last cycle, 1 for PER
+    char ticklenextcycle;                                           //whether tickling was done for last cycle
+    time_t tstamp;                                                  //timestamp when last cycle was run
+};
+
+holderrecord currentholder;                                         //struct record for the current beeholder
+holderrecord sessionrecord[30];                                     //sessionrecord contains holderrecords for up to 30 beeholders
+
+int numbeeholders;                                                  //number of beeholders used in current session
+int station;                                                        //which station is used in this session    
+
+
+
+//Function declarations
+
+//Short beep
+void beeponce() {
+    buzzer = 1;
+    wait(0.1);
+    buzzer = 0;
+}
+
+//beep beep beep
+void multibeeps(int number, float interval) {
+    for(int i=0;i<number;i++){
+        beeponce();
+        wait(interval);
+    }
+}
+
+//Clears and homes the display
+void cleardisplay() {
+    display.printf("\e[2J\e[H");
+}
+
+//Returns both robot arms to base position
+void armstobase() {
+    elbow1 = elbow1basepos;
+    elbow2 = elbow2basepos;
+    wait(0.5);                                                      //need this delay to avoid snagging brush on IR LED
+    arm1 = arm1basepos;
+    arm2 = arm2basepos;
+}
+
+//Initialises sessionrecords
+void sessionrecordinit() {
+    for (int i=0; i<30; i++) {                                      //set base values for all possible beeholders
+        sessionrecord[i].serialno = 0;                              //set serialno to 0 so we can detect a unresponsive holder
+        sessionrecord[i].calvalue = 0;
+        sessionrecord[i].cycleno = 0;
+        sessionrecord[i].reslastcycle = 0;
+        sessionrecord[i].ticklenextcycle = 1;                       //default is to tickle on each cycle
+    }
+}
+
+//Initialisation on power-on
+void initialise() {
+
+    //Set pins to a defined state
+    led1 = led2 = led3 = led4 =0;                                   //internal LEDs only used for debugging purposes
+    buzzer = 0;                                                     //buzzer off
+    airswitcher = 0;                                                //relay contacts open, clean air position
+    bhsel1 = bhsel2 = 1;                                            //I2C select lines high to deselect both beeholders
+    upsw.mode(PullUp);                                              //set pull-up on all frontpanel switches
+    risw.mode(PullUp);
+    dnsw.mode(PullUp);
+    lesw.mode(PullUp);
+    oksw.mode(PullUp);
+    
+    //Set robot arms to base positions
+    armstobase();
+    
+    //initialise the display
+    display.baud(19200);                                            //set display baud rate to 19200
+    display.putc(0x0d);                                             //send CR to display - this needs to be first character sent after power-up
+    wait(0.5);                                                      //wait for display to adjust to serial baud rate
+    display.printf("\e[4L\e[20c");                                  //set display to 4 lines of 20 characters
+    cleardisplay();                                                 //clear screen and home cursor
+    
+    //display splash screen with time
+    display.printf("Eton trainer v%s\r", fwversion);                //show firmware release version
+    time_t seconds = time(NULL);                                    //creates timestamp
+    char bufferd[20], buffert[20];
+    strftime(bufferd, 20, "%d/%m/%Y", localtime(&seconds));         //formats date part of timestamp
+    display.printf("Date: %s\r", bufferd);                          //displays date
+    strftime(buffert, 20, "%X", localtime(&seconds));               //formats time part of timestamp
+    display.printf("Time: %s\r", buffert);                          //displays time
+    wait(5);                                                        //how long splash screen will be displayed
+    cleardisplay();                                                 //clear screen and home cursor
+    
+    //initialise sessionrecord
+    sessionrecordinit();
+    
+    //enter info in logfile.csv
+    fprintf(logfile, "%s\r", bufferd);                              //writes date, time and firmware version
+    fprintf(logfile, "%s\r", buffert);
+    fprintf(logfile, "v%s\r", fwversion);
+    
+    multibeeps(3, 0.4);                                              //three beeps to signal initialisation ready
+}
+
+//Converts the last 4 digits in the serial number string into a integer 0-9999
+int serialstring2int(char bser[8]) {
+    int tempserial = 0;    
+    tempserial = tempserial + (bser[4]-0x30)*1000;                  //5th digit is thousands
+    tempserial = tempserial + (bser[5]-0x30)*100;                   //6th digit is hundreds
+    tempserial = tempserial + (bser[6]-0x30)*10;                    //7th digit is tens
+    tempserial = tempserial + (bser[7]-0x30);                       //8th digit is units
+    return tempserial;
+}
+
+//beeholder resets on rising edge of select line
+void resetbeeholder() {                                             //need this as mounting beeholder causes undefined start of beeholder firmware
+    bhsel1 = bhsel2 = 0;
+    wait(0.1);
+    bhsel1 = bhsel2 = 1;
+    wait(0.3);
+}
+
+//Reads beeholder serial number from station 1 or 2
+int getserialno(int station) {
+    char bser[8];                                                   //define 8-byte serial number string to read
+    resetbeeholder();                                               //does not work without this!!
+    for (int i=0;i<8;i++) bser[i]=0x30;
+    if (station==1) bhsel1=0;                                       //pull select line station 1 low
+    if (station==2) bhsel2=0;                                       //pull select line station 2 low
+    wait(0.2);
+    beeholder.stop();                                               //I2C stop condition
+    wait(0.2);                                                      //delay for beeholder to respond
+    beeholder.write(addr,0x0,1);                                    //initial write before read
+    beeholder.read(addr,bser,8);                                    //read 8 byte serial number
+    bhsel1 = bhsel2= 1;                                             //pull both select lines high
+    int serialno = serialstring2int(bser);                          //translate serial number string to integer
+    return serialno;
+}    
+
+//Returns 1 if a PER is detected on this beeholder within timeout seconds
+int detectPER(int station, float timeout) {
+    Timer ttotal, tper;
+    ttotal.start();                                                 //start timers for time-out and PER-detect
+    ttotal.reset();
+    tper.start();
+    tper.reset();
+    if (station == 1) {
+        while ((ttotal.read() < timeout) && (tper.read() < PERtime)) {        //loop until timeout or PER detected
+            wait_ms(10);
+            if (ptor1 * 3.3 < PERsetval) tper.reset();              //if phototransistor voltage below treshold keep PER timer in reset
+                                                                    //if above treshold let timer run until it reaches PERtime
+        }
+    }
+    if (station == 2) {
+         while ((ttotal.read() < timeout) && (tper.read() < PERtime)) {
+            if (ptor2 * 3.3 < PERsetval) tper.reset();
+        }
+    }
+    ttotal.stop();
+    tper.stop();
+    return (tper.read() >= PERtime);                                //if the loop exit condition was a PER, return a TRUE value
+}
+
+//Checks if bee shows PER when stimulated with odour but without feeding
+int checktrained(int station) {
+    airswitcher = 1;
+    int responded = detectPER(station, 6);                          //PER needs to be detected within 6 seconds
+    airswitcher = 0;
+    return responded;
+}
+
+//Function performs beeholder/IR-led calibration
+float calibrate(int station, int runs) {
+    float tempcal;
+    tempcal=0.5;                                                    //start calibration at 50% voltage
+    irLED=tempcal;
+    float calstep;                                                  //start calstep at 25% voltage
+    calstep = tempcal/2;
+    for (int i=0; i<10; i++) {                                      //does a "10-bit binary search" for the correct voltage to get a good response
+        irLED = tempcal;
+        wait(0.1);                                                  //important to allow AD converter to settle
+        if (ptor1 < calsetval/3.3) {                                //check phototransistor voltage against desired value
+            tempcal = tempcal - calstep;                            //if phototransistor voltage is too low then reduce brightness
+        }
+        else {
+            tempcal = tempcal + calstep;                            //if phototransistor voltage is too high then increase brightness
+        }
+        calstep = calstep/2;                                        //on each loop of the for-cycle make smaller changes to IR LED voltage
+    }
+    float calib;
+    calib = tempcal;                                                //set preliminary calibration to the value just measured
+    for (int j=1; j<runs; j++) {                                    //run another j-1 runs, this corrects for antennae-movement as
+        tempcal=0.5;                                                //we use the lowest calibration value from j runs
+        irLED=tempcal;                                              //this is similar to what we do in the cassettes
+        calstep = tempcal/2;
+        for (int i=0;i<10;i++) {
+            irLED = tempcal;
+            wait(0.1);
+            if (ptor1 < calsetval/3.3) {
+                tempcal = tempcal - calstep;
+            }
+            else {
+                tempcal = tempcal + calstep;
+            }
+            calstep = calstep/2;
+        }
+        if (tempcal < calib) calib = tempcal;                       //use the lowest of j calibration values
+    }
+    return calib;
+}
+
+//switches the IR LED on at the right brightness level for the beeholder
+void IRledON(int i) {
+    irLED = sessionrecord[i].calvalue;
+ }
+
+//Performs a conditioning cycle. if tickle==0, no tickling takes place. Return==1 if a PER has been detected
+int condcycle(int station, int tickle) {
+    int perseen;
+    perseen = 0;
+    //for station 1
+    if (station == 1) {
+        //dip brush
+        arm1 = arm1dippos;
+        wait(0.5);
+        elbow1 = elbow1dippos;
+        wait (diptime);
+        elbow1 = elbow1basepos;
+        wait(0.3);
+        arm1 = arm1basepos;
+        //switch air to supply trace vapour
+        airswitcher = 1;                                            //air contains target vapour
+        display.printf("Vapour ON");
+        wait (0.5);
+        //tickle
+        if (tickle) {                                               //if tickling, first tickle then wait for PER or timeout
+            elbow1 = elbow1ticklepos;
+            arm1 = arm1ticklepos;
+            perseen = detectPER(1, tickletimeout);                  //tickle until timeout or PER detected
+        }
+        //or not tickle
+        else {
+            perseen = detectPER(1, tickletimeout);                  //if not tickling, wait for PER or timeout then move to pre-feeding position
+            arm1 = arm1ticklepos - 0.05;                            //move to position between LED and holder
+            wait(0.3);                                              //this is needed to avoid the brush snagging on the phototransistor
+            elbow1 = elbow1ticklepos;
+        }
+        //feeding only if you have tickled or a PER has been detected
+        if (tickle || perseen) {                                    //only feed if a PER has been detector, or "tickle" is true
+            elbow1 = elbow1feedpos;
+            arm1 = arm1feedpos;
+            wait(feedtime);
+            arm1 = arm1ticklepos -0.05;
+            elbow1 = elbow1ticklepos;
+        }
+        //move back to base position
+        arm1 = arm1basepos;
+        elbow1 = elbow1basepos;
+        airswitcher = 0;                                            //air valve to clean air
+        display.printf("\e[DFF\r");                                 //rewrite "ON" to "OFF" on display
+    }
+    //for station 2
+    if (station == 2) {
+        //dip brush
+        arm2 = arm2dippos;
+        wait(0.5);
+        elbow2 = elbow2dippos;
+        wait (diptime);
+        elbow2 = elbow2basepos;
+        wait(0.3);
+        arm2 = arm2basepos;
+        //switch air to supply trace vapour
+        //airswitcher = 1;                                          //no air switching for station 2
+        wait (0.5);
+        //tickle
+        if (tickle) {                                               //if tickling, first tickle then wait for PER or timeout
+            elbow2 = elbow2ticklepos;
+            arm2 = arm2ticklepos;
+            perseen = detectPER(2, tickletimeout);
+        }
+        else {
+            perseen = detectPER(2, tickletimeout);                  //if not tickling, wait for PER or timeout then move to pre-feeding position
+            arm2 = arm2ticklepos - 0.05;
+            wait(0.3);                                              //this is needed to avoid the brush snagging on the phototransistor
+            elbow2 = elbow2ticklepos;
+        }
+        //feeding only if tickling takes place or a PER has been detected
+        if (tickle || perseen) {
+            elbow2 = elbow2feedpos;
+            arm2 = arm2feedpos;
+            wait(feedtime);
+            arm2 = arm2ticklepos -0.05;
+            elbow2 = elbow2ticklepos;
+        }
+        //move back to base position
+        arm2 = arm2basepos;
+        elbow2 = elbow2basepos;
+    }
+    return perseen;
+}
+
+//User to input choice between station 1 and 2 for this session
+int stationchoice() {
+    int station;
+    station = 1;
+    cleardisplay();                                                 //clear screen and home cursor
+    display.printf(" Station selection  ");
+    display.printf("Left=1       2=Right");
+    while (lesw && risw) {                                          //lesw and risw become false when pressed!
+        wait(0.02);
+        if (!lesw) station=1;                                       //on LEFT select station 1
+        if (!risw) station=2;                                       //on RIGHT select station 2
+    }
+    display.printf("\rStation %1u selected", station);
+    fprintf(logfile, "%3u, station selected\r", station);
+    return station;
+}
+
+//Registers and calibrates all beeholders used in this session
+int registerbeeholders() {
+    int i;
+    bool done;
+    char buffert[30];
+    i = done = 0;
+    cleardisplay();                                                 //clear screen and home cursor
+    fprintf(logfile, "calibration record:\r");
+    fprintf(logfile, "i, serialno, LED V, time\r");
+    while (i<30 && !done) {                                         //register and calibrate a maximum of 30 beeholders
+        display.printf("calibrating %u\r",i+1);
+        sessionrecord[i].serialno = getserialno(station);           //read serial number
+        if (sessionrecord[i].serialno != 0) {                       //check if serial number correctly read - if not it will be 0000
+            sessionrecord[i].calvalue = calibrate(station, 5);      //5 calibration cycles
+            sessionrecord[i].tstamp = time(NULL);                   //create timestamp NOW
+            strftime(buffert, 20, "%X", localtime(&sessionrecord[i].tstamp));               //formats time part of timestamp
+            cleardisplay();
+            display.printf("SN %4u - cal %4.2fV\r", sessionrecord[i].serialno, sessionrecord[i].calvalue*3.3);
+            display.printf("OK for next\r");
+            display.printf("DOWN for training");
+            fprintf(logfile, "%4u,%6u,%6.2f,  %s,  calibrate\r", i, sessionrecord[i].serialno, sessionrecord[i].calvalue*3.3, buffert);
+            i++;
+            while (!done && oksw) {                                 //loop until OK or DOWN are pressed
+                wait(0.02);
+                done = !dnsw;                                       //DOWN exits registration cycle and moves to training
+            }
+        }
+        else {                                                     //retry when serialno can't be read (is 0000)
+            cleardisplay();
+            display.printf("invalid serial no\r");
+            display.printf("reseat beeholder\r");
+            display.printf("press OK");
+            while (oksw) wait(0.02);                                //loop until OK is pressed to start calibration loop again
+        }
+        cleardisplay();
+    }
+    return i;                                                       //upon done condition, i== number of beeholders calibrated
+}
+
+//gets holderrecord for the holder with a certain serial number
+int getholderindex(int serialno) {
+    for (int i=0; i<numbeeholders; i++) {                           //reverse lookup of index for a certain serial number
+        if (sessionrecord[i].serialno == serialno) return i;
+    }
+    return 29;                                                      //if the record is not found, returns i==29
+}
+
+//all the elements making up a single training cycle with one beeholder
+void trainingcycle() {
+    wait(0.2);
+    time_t tstamp = time(NULL);                                     //create timestamp NOW
+    char buffert[30];
+    strftime(buffert, 20, "%X", localtime(&tstamp));                //format timestamp to time string
+    int serialno = getserialno(station);                            //read serial number
+    int i = getholderindex(serialno);                               //get index i for serial number
+    IRledON(i);                                                     //switch IR LED on at correct brightness
+    sessionrecord[i].cycleno++;                                     //increment cycle number for this beeholder
+    cleardisplay();
+    display.printf("SN: %4u, cycle %u\r", serialno, sessionrecord[i].cycleno);
+    sessionrecord[i].reslastcycle = condcycle(station, sessionrecord[i].ticklenextcycle);       //do a conditioning cycle
+    fprintf(logfile, "%s,",buffert);
+    fprintf(logfile, "  %4u,",serialno);
+    fprintf(logfile, "  %2u,", sessionrecord[i].cycleno);
+    if (sessionrecord[i].reslastcycle) {                            //log PER or TimeOut
+        fprintf(logfile, "  PER,");
+    }
+    else {
+        fprintf(logfile, "   TO,");
+    }
+    fprintf(logfile, " training\r");
+    if (sessionrecord[i].reslastcycle) {
+        display.printf("PER detected\r");
+    }
+    else {
+        display.printf("PER time-out\r");
+    }
+    display.printf("mount holder + OK\r");
+    display.printf("DOWN to finish");
+}
+
+//main program
+int main() {
+    initialise();
+    
+    //Choose which station this session will use
+    station = stationchoice ();
+    cleardisplay();
+    
+    //Register and calibrate beeholders used in this session
+    display.printf("Now register holders\r");
+    display.printf("seat first holder\rpress OK\r");
+    while (oksw) wait(0.02);                                        //loop so first beeholder can be mounted, then OK pressed
+    numbeeholders = registerbeeholders();
+    display.printf("%2u holders entered\r", numbeeholders);
+    wait(3);
+    cleardisplay();
+    
+    //Conduct training
+    display.printf("Start training cycle\r");
+    display.printf("mount holder + OK\r");
+    display.printf("DOWN to finish");
+    fprintf(logfile, "Training started\r");
+    fprintf(logfile, "time, serial, cycleno, result\r");
+    wait(1);
+    InterruptIn oksw(p17);                                          //redefine OK and footswitch as interrupts
+    oksw.mode(PullUp);
+    bool finishsession;
+    finishsession = 0;
+    oksw.fall(&trainingcycle);                                      //call subroutine "trainingcycle" on OK/footswitch interrupt
+    while (!finishsession) {                                        //loop while servicing interrupts
+        wait(0.02);
+        finishsession = !dnsw;                                      //until DOWN button is pressed, which exits to program close
+    }
+    
+    //Close logfile
+    fprintf(logfile, "session closed");
+    fclose(logfile);                                                //close logfile for reading
+    cleardisplay();
+    display.printf("Session finished\r\rdownload etonlog.csvand rename");
+}
diff -r 000000000000 -r 2017f5924a22 mbed.bld
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mbed.bld	Wed Mar 23 09:39:57 2011 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/mbed_official/code/mbed/builds/63bcd7ba4912