Agian a version that works for me .

Fixed a lot of bugs ...
This commit is contained in:
Fenoglio 2018-07-23 18:28:40 +02:00
parent b84b19b740
commit 46319a972a
2 changed files with 352 additions and 208 deletions

View file

@ -10,7 +10,7 @@
typedef enum {BASESTATION = 0, TOPSTATION} radio_type_e; typedef enum {BASESTATION = 0, TOPSTATION} radio_type_e;
#define RF24_CNS 7 // this is 7 for the Nano, D4 for the ESP #define RF24_CNS 7 // this is 7 for the Nano, D4 for the ESP
#define RF24_CE 8 // this is 8 for the Nano, D3 for the ESP #define RF24_CE 8 // this is 8 for the Nano, D3 for the ESP
#define RF24_PA_LEVEL RF24_PA_LOW // sending power level RF24_PA_LOW, ???? #define RF24_PA_LEVEL RF24_PA_LOW // sending power level RF24_PA_LOW, RF24_PA_HIGH????
//--------------- defines for the I2C //--------------- defines for the I2C
//#define SCL A5 // I2C clock pin //#define SCL A5 // I2C clock pin
@ -24,7 +24,7 @@ typedef struct transcv_struct{
}transcv_s; }transcv_s;
#define MIN_DELAY_BETWEEN_SEND_MS 1000 // this defines the time in milliseconds before the next set of data will be send to the base station - except the button was pressed. #define MIN_DELAY_BETWEEN_SEND_MS 1000 // this defines the time in milliseconds before the next set of data will be send to the base station - except the button was pressed.
#define CONN_TIMEOUT 10000 // if there was no data received from the TOPSTATION for that amount of time - the connection is flagged as lost #define CONN_TIMEOUT 5000 // if there was no data received from the TOPSTATION for that amount of time - the connection is flagged as lost
#define KEY_BOUNCE_MS 10 // the time we use to avoid keybouncing ... #define KEY_BOUNCE_MS 10 // the time we use to avoid keybouncing ...
@ -41,42 +41,42 @@ typedef struct transcv_struct{
#define PIEZO_PIN 6 // piezo speaker #define PIEZO_PIN 6 // piezo speaker
#define WARN_LED A1 // yellow warn LED
#define WARN_LED_ON HIGH
#define WARN_LED_OFF LOW
#define FAIL_LED A3 // red fail LED
#define FAIL_LED_ON HIGH
#define FAIL_LED_OFF LOW
#define READY_LED A2 // green ready LED
#define READY_LED_ON HIGH
#define READY_LED_OFF LOW
#define RUN_LED A0 // blue run LED
#define RUN_LED_ON HIGH
#define RUN_LED_OFF LOW
#define DISPLAY_I2C_ADDRESS 0x3C //Adress of the Display #define DISPLAY_I2C_ADDRESS 0x3C //Adress of the Display
typedef enum {TIMER_INIT = 0, TIMER_NOCONNECTION, TIMER_IDLE, TIMER_READY, TIMER_STARTED, TIMER_RUNNING , TIMER_CANCELLED, TIMER_STOPPED, TIMER_TIMEDOUT, TIMER_FAIL, TIMER_WAIT} timer_state_e; typedef enum {TIMER_INIT = 0, TIMER_NOCONNECTION, TIMER_IDLE, TIMER_READY, TIMER_STARTED, TIMER_RUNNING , TIMER_CANCELLED, TIMER_STOPPED, TIMER_TIMEDOUT, TIMER_FAIL, TIMER_WAIT} timer_state_e;
typedef enum {MODE_COMPETE = 0, MODE_TRAINING, MODE_CALIBRATION} timer_mode_e; // compete - full mode with false start detector, training - no false start detection, calibration - parellel wired connection between top and base to kalibrate the offset calculation of the wireless connection typedef enum {MODE_COMPETE = 0, MODE_TRAINING, MODE_CALIBRATION} timer_mode_e; // compete - full mode with false start detector, training - no false start detection, calibration - parellel wired connection between top and base to kalibrate the offset calculation of the wireless connection
#define LED_BLINK_ALL_MS 500 // LED set to BLINK will change there state every number of milli seconds specified here
// READY_LED, WARN_LED, RUN_LED, FAIL_LED // READY_LED, WARN_LED, RUN_LED, FAIL_LED
const float LEDStates[][3] = typedef enum {READY_LED = 0, RUN_LED, FAIL_LED, WARN_LED ,NO_LAST_LED} led_number_e; // leave NO_LAST_LED as last element - its our marker ...
const uint8_t LEDPins[NO_LAST_LED] = {
[READY_LED] =A2, // green ready LED
[RUN_LED] =A0, // blue run LED
[FAIL_LED] =A3, // red fail LED
[WARN_LED] =A1 // yellow warn LED
};
typedef enum {LED_OFF = 0, LED_ON, LED_BLINK } led_state_e;
const uint8_t LEDStates[][NO_LAST_LED] =
{ {
[TIMER_INIT] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_OFF}, [TIMER_INIT] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_BLINK ,[WARN_LED]=LED_OFF}, // 0
[TIMER_NOCONNECTION] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_ON}, [TIMER_NOCONNECTION] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_ON, [WARN_LED]=LED_OFF}, // 1
[TIMER_IDLE] = {READY_LED_ON, RUN_LED_OFF, FAIL_LED_OFF}, [TIMER_IDLE] = {[READY_LED]=LED_ON, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 2
[TIMER_READY] = {READY_LED_ON, RUN_LED_OFF, FAIL_LED_OFF}, [TIMER_READY] = {[READY_LED]=LED_BLINK, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 3
[TIMER_STARTED] = {READY_LED_ON, RUN_LED_ON, FAIL_LED_OFF}, [TIMER_STARTED] = {[READY_LED]=LED_ON, [RUN_LED]=LED_ON, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 4
[TIMER_RUNNING] = {READY_LED_OFF, RUN_LED_ON, FAIL_LED_OFF}, [TIMER_RUNNING] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_ON, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 5
[TIMER_CANCELLED] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_ON}, [TIMER_CANCELLED] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_ON, [WARN_LED]=LED_OFF}, // 6
[TIMER_STOPPED] = {READY_LED_ON, RUN_LED_ON, FAIL_LED_OFF}, [TIMER_STOPPED] = {[READY_LED]=LED_ON, [RUN_LED]=LED_ON, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 7
[TIMER_TIMEDOUT] = {READY_LED_OFF, RUN_LED_ON, FAIL_LED_ON}, [TIMER_TIMEDOUT] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_ON, [FAIL_LED]=LED_ON, [WARN_LED]=LED_OFF}, // 8
[TIMER_FAIL] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_ON} [TIMER_FAIL] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_BLINK, [FAIL_LED]=LED_ON, [WARN_LED]=LED_OFF}, // 9
[TIMER_WAIT] = {[READY_LED]=LED_ON, [RUN_LED]=LED_ON, [FAIL_LED]=LED_ON, [WARN_LED]=LED_ON} // 10
}; };
#define MAX_DIFFERENCE_OFFSET_MS 100 // 0,001sec is the maximum offset we allow between the current offset and the mean offset. if it is more - restart offset calculation #define MAX_DIFFERENCE_OFFSET_MS 100 // 0,001sec is the maximum offset we allow between the current offset and the mean offset. if it is more - restart offset calculation
#define REQUIRED_NUMBER_MEANVALS 100 // we need at least this number of meanvalues to be ready to start a run #define REQUIRED_NUMBER_MEANVALS 100 // we need at least this number of meanvalues to be ready to start a run
#define MAX_ALLOWED_FAILED_OFFSETS 3 // if more than this number of offsets are out of the specified MAX_DIFFERENCE_OFFSET_MS value, offset calcultion will be restarted #define MAX_ALLOWED_FAILED_OFFSETS 10 // if more than this number of offsets are out of the specified MAX_DIFFERENCE_OFFSET_MS value, offset calcultion will be restarted
#define STARTSEQ_STEPS 4 #define STARTSEQ_STEPS 4
const uint8_t STARTSEQ_NOTES[] = {0,392,392,1047}; // tone frequence const uint8_t STARTSEQ_NOTES[] = {0,392,392,1047}; // tone frequence
@ -84,9 +84,10 @@ const uint16_t STARTSEQ_DURATION[] = {0,200,200,100}; // tone duration i
const unsigned long STARTSEQ_PAUSE[] = {1000000,1000000,1000000,100000}; // pause between tones in microseconds const unsigned long STARTSEQ_PAUSE[] = {1000000,1000000,1000000,100000}; // pause between tones in microseconds
#define STARTSEQ_LENGTH_MS 3100 // the length of the start sequence from the time the button was pressed ... includes the 3 tones #define STARTSEQ_LENGTH_MS 3100 // the length of the start sequence from the time the button was pressed ... includes the 3 tones
#define FAILSEQ_TONEPAUSE_MS 400 #define FAILSEQ_STEPS 2
#define FAILSEQ_TON_LENGTH_MS 300 const uint8_t FAILSEQ_NOTES[] = {49,49}; // tone frequence
#define FAILSEQ_TON_FREQUENCY 49 //NOTE_G1 const uint16_t FAILSEQ_DURATION[] = {300,300}; // tone duration in milliseconds
const unsigned long FAILSEQ_PAUSE[] = {400000,400000}; // pause between tones in microseconds
#define TIMER_MAX_TIME 99999 #define TIMER_MAX_TIME 99999
#define TIMER_TIMEOUT 20000 #define TIMER_TIMEOUT 20000

View file

@ -18,8 +18,16 @@ RF24 radio(RF24_CNS,RF24_CE);
/**********************************************************/ /**********************************************************/
byte addresses[][12] = {"top_station","basestation"}; // Radio pipe addresses for the 2 nodes to communicate. byte addresses[][12] = {"top_station","basestation"}; // Radio pipe addresses for the 2 nodes to communicate.
unsigned long startloop_ms = 0;
boolean offset_sync_sequence = true; // set to true as the offset sync / calibration is not done - sending data to the basestation is more often as after the sync is done ...
boolean blink_on = false; // set to TRUE if the system clock cycle signals to set LEDs in LED_BLINK mode to be active - means top be switched on
unsigned long blink_on_swiched_at = 0; // the last system time (in milliseconds) the blink_on was switched
uint8_t *leds_states = LEDStates[TIMER_INIT];
boolean time_offset_ok = false; // true as long as the offset is correctly calculated boolean time_offset_ok = false; // true as long as the offset is correctly calculated
uint8_t counter_time_offset = 0; // number of used values for the mean value calculation uint16_t counter_time_offset = 0; // number of used values for the mean value calculation
signed long sum_time_offset = 0; // sum of offset values signed long sum_time_offset = 0; // sum of offset values
signed long mean_time_offset = 0; // mean value for the offset signed long mean_time_offset = 0; // mean value for the offset
signed long running_time_offset = 0; // offset that will be used for this run ... signed long running_time_offset = 0; // offset that will be used for this run ...
@ -29,10 +37,14 @@ unsigned long run_time = 0; // if the timer is running this is
boolean warn_during_run = false; // will be set to true if there is a warning during the run - usually an offset sync error boolean warn_during_run = false; // will be set to true if there is a warning during the run - usually an offset sync error
unsigned long connection_last_established_at_ms = 0; // time the last active connection was established unsigned long connection_last_established_at_ms = 0; // time the last active connection was established
boolean connection_available = false; // if there were no data for longer then CONN_TIMEOUT the connection will be flaged as lost ... boolean connection_available = false; // if there were no data for longer then CONN_TIMEOUT the connection will be flaged as lost ...
boolean keep_connection_off = true; // if sett to true the connection to the top station will be kept off for a timeout time to signal that we are in the init sequnce again ...
uint8_t failed_offsets = MAX_ALLOWED_FAILED_OFFSETS; // number of offset values that did not fullfill the MAX_DIFFERENCE_OFFSET_MS criterion uint8_t failed_offsets = MAX_ALLOWED_FAILED_OFFSETS; // number of offset values that did not fullfill the MAX_DIFFERENCE_OFFSET_MS criterion
volatile boolean false_start = false; // set to true if a false start occurs (use volatile for all shared variables that deals with hardware) volatile boolean false_start = false; // set to true if a false start occurs (use volatile for all shared variables that deals with hardware)
volatile uint8_t startsequence_count = 0; // shows thze actual step in the startsquence. Number of steps is defined in STARTSEQ_STEPS (use volatile for all shared variables that deals with hardware) volatile uint8_t startsequence_count = 0; // shows thze actual step in the startsquence. Number of steps is defined in STARTSEQ_STEPS (use volatile for all shared variables that deals with hardware)
volatile boolean startsequence_done = false; volatile boolean startsequence_done = false; // set to TRUE if the startsequnce was completed successfully (without a false start)
volatile boolean failsequence_done = false;
volatile uint8_t failsequence_count = 0;
boolean topbuttonwaspressed = false; // set to true if the stop button was pressed boolean topbuttonwaspressed = false; // set to true if the stop button was pressed
timer_state_e timer_state = TIMER_IDLE; // current state needs to be initialized to somethin different then new_state due to the fact that some pieces of the code check for differnt values of state and _new_state to detect an update... timer_state_e timer_state = TIMER_IDLE; // current state needs to be initialized to somethin different then new_state due to the fact that some pieces of the code check for differnt values of state and _new_state to detect an update...
@ -44,6 +56,8 @@ transcv_s radio_data;
void setup(){ void setup(){
PCICR = 0; // disable all pin change interrupts
Serial.begin(115200); Serial.begin(115200);
// this is the top button - will be pressed by the speed climber as soon she/he reaches the top ... // this is the top button - will be pressed by the speed climber as soon she/he reaches the top ...
@ -52,9 +66,10 @@ void setup(){
pinMode(CANCELBUTTON_IN, INPUT_PULLUP); pinMode(CANCELBUTTON_IN, INPUT_PULLUP);
pinMode(FAILSTARTBUTTON_IN, INPUT_PULLUP); pinMode(FAILSTARTBUTTON_IN, INPUT_PULLUP);
pinMode(WARN_LED, OUTPUT); // set the LED pins as putput pins ...
pinMode(FAIL_LED, OUTPUT); for(uint8_t led = 0; led<NO_LAST_LED-1;led++){
pinMode(READY_LED, OUTPUT); pinMode(LEDPins[led], OUTPUT);
}
// Get the station type (base or top) as set by the station select pin - BASESTATION is default // Get the station type (base or top) as set by the station select pin - BASESTATION is default
@ -98,11 +113,18 @@ void setup(){
void loop(void) { void loop(void) {
/****************** Shared code for all stations ********************************************************************************/
if(millis() - blink_on_swiched_at > LED_BLINK_ALL_MS){
blink_on_swiched_at = millis();
blink_on = !blink_on;
}
/****************** Code for the TOPSTATION is here - the stop button is connected to the top station ***************************/ /****************** Code for the TOPSTATION is here - the stop button is connected to the top station ***************************/
if (stationNumber == TOPSTATION){ // Radio is the top station and sends continously its time and the time the stop button was pressed. if (stationNumber == TOPSTATION){ // Radio is the top station and sends continously its time and the time the stop button was pressed.
startloop_ms = millis();
if(false == offset_sync_sequence){
// check for pressed button ... // check for pressed button ...
if(topbuttonwaspressed == false){ if(topbuttonwaspressed == false){
if( (millis() - radio_data.topbuttonpressedtime) > MIN_DELAY_BETWEEN_PRESSED_MS){ if( (millis() - radio_data.topbuttonpressedtime) > MIN_DELAY_BETWEEN_PRESSED_MS){
@ -111,46 +133,84 @@ void loop(void) {
// button was pressed - store the time // button was pressed - store the time
radio_data.topbuttonpressedtime = millis(); radio_data.topbuttonpressedtime = millis();
topbuttonwaspressed = true; topbuttonwaspressed = true;
digitalWrite(RUN_LED, RUN_LED_ON); digitalWrite(LEDPins[RUN_LED], LED_ON);
} }
} }
} else { } else {
if(digitalRead(STOPBUTTON_IN) != STOPBUTTON_PRESSED){ if(digitalRead(STOPBUTTON_IN) != STOPBUTTON_PRESSED){
topbuttonwaspressed = false; topbuttonwaspressed = false;
digitalWrite(RUN_LED, RUN_LED_OFF); digitalWrite(LEDPins[RUN_LED], LED_OFF);
} }
} }
} else {
topbuttonwaspressed = false;
digitalWrite(LEDPins[RUN_LED], LED_OFF);
}
// if the button was not pressed only each few second data will be send to BASESTATION ... // if the button was not pressed only each few second data will be send to BASESTATION ...
if(topbuttonwaspressed || ((millis()-radio_data.topstationtime) >= MIN_DELAY_BETWEEN_SEND_MS)){ if(offset_sync_sequence || topbuttonwaspressed || ((millis()-radio_data.topstationtime) >= MIN_DELAY_BETWEEN_SEND_MS)){
// store current millis to be send as reference ... // store current millis to be send as reference ...
radio_data.topstationtime = millis(); // set the current milli second count radio_data.topstationtime = millis(); // set the current milli second count
Serial.print("ms - Send data to bottom station: topstationtime: "); Serial.print("senddate_to_base ");
Serial.print( radio_data.topstationtime ); Serial.println(millis());
Serial.print(" stoppressedtime: "); //Serial.print("ms - topstationtime: ");
Serial.print( radio_data.topbuttonpressedtime ); //Serial.print( radio_data.topstationtime );
//Serial.print(" stoppressedtime: ");
//Serial.print( radio_data.topbuttonpressedtime );
Serial.print(" offset counter value : ");
Serial.println(counter_time_offset);
// send data ... // send data ...
if (!radio.write(&radio_data,sizeof(radio_data))){ // Send the counter variable to the other radio if (!radio.write(&radio_data,sizeof(radio_data))){ // Send the counter variable to the other radio
if(((millis() - connection_last_established_at_ms) >= (CONN_TIMEOUT-100)) || (connection_last_established_at_ms == 0)){
connection_available = false;
Serial.println(F("Failed to send data to BASESSTATION ... will retry")); Serial.println(F("Failed to send data to BASESSTATION ... will retry"));
digitalWrite(FAIL_LED, FAIL_LED_ON); digitalWrite(LEDPins[FAIL_LED], LED_ON);
digitalWrite(READY_LED, READY_LED_OFF); digitalWrite(LEDPins[READY_LED], LED_OFF);
offset_sync_sequence = true;
counter_time_offset = 0;
} else {
if(offset_sync_sequence){
if(counter_time_offset > 0){
counter_time_offset--;
}
}
}
} }
else else
{ {
Serial.println("Data sent to BASESSTATION"); //Serial.println("Data sent to BASESSTATION");
digitalWrite(FAIL_LED, FAIL_LED_OFF); digitalWrite(LEDPins[FAIL_LED], LED_OFF);
digitalWrite(READY_LED, READY_LED_ON); if(offset_sync_sequence){
digitalWrite(LEDPins[FAIL_LED], blink_on);
} else {
digitalWrite(LEDPins[READY_LED], LED_ON);
}
connection_last_established_at_ms = millis();
connection_available = true;
// check offset sync counter ...
if(counter_time_offset < (4*REQUIRED_NUMBER_MEANVALS)){
counter_time_offset++;
} else {
// offset sync done
offset_sync_sequence = false;
} }
} }
} }
Serial.print("looptime_top ");
Serial.println(millis()-startloop_ms);
}
/****************** Code for the BASESTATION is here - the display and the start button is connected here. All caclulation will be done here ***************************/ /****************** Code for the BASESTATION is here - the display and the start button is connected here. All caclulation will be done here ***************************/
if ( stationNumber == BASESTATION ) { if ( stationNumber == BASESTATION ) {
startloop_ms = millis();
// receive data from top_station, calculate offset and set 'last connection' time stamp // receive data from top_station, calculate offset and set 'last connection' time stamp
receive_values(); receive_values();
@ -158,6 +218,13 @@ void loop(void) {
update_screen(timer_new_state); update_screen(timer_new_state);
// set state to new_state // set state to new_state
if(timer_state != timer_new_state){
Serial.print(millis());
Serial.print("ms : current state: ");
Serial.print(timer_state);
Serial.print(" new state: ");
Serial.println(timer_new_state);
}
timer_state = timer_new_state; timer_state = timer_new_state;
// set LEDs // set LEDs
@ -174,15 +241,21 @@ void loop(void) {
if(connection_available == false){ if(connection_available == false){
// if the connection was lost ... switch to noconnection state // if the connection was lost ... switch to noconnection state
timer_new_state = TIMER_NOCONNECTION; timer_new_state = TIMER_NOCONNECTION;
//Serial.println("No connection state will be next");
} }
else{ else{
// if the offset is claculated, cancel not pressed and failstart not pressed switch to IDLE mode ... // if the offset is claculated, cancel not pressed and failstart not pressed switch to IDLE mode ...
if((time_offset_ok == true) && if((time_offset_ok == true) &&
(digitalRead(CANCELBUTTON_IN != CANCELBUTTON_PRESSED)) && (digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED) &&
(digitalRead(FAILSTARTBUTTON_IN != FAILSTARTBUTTON_PRESSED)) ) (digitalRead(FAILSTARTBUTTON_IN) != FAILSTARTBUTTON_PRESSED) )
{ {
// check if offset is OK - if not .. set state back to INIT // check if offset is OK - if not .. set state back to INIT
//Serial.println("idle state will be next");
timer_new_state = TIMER_IDLE; timer_new_state = TIMER_IDLE;
} else {
//Serial.println("init state will be next");
//Serial.print("offset_ok :");
//Serial.println(time_offset_ok);
} }
} }
break; break;
@ -193,9 +266,11 @@ void loop(void) {
timer_new_state = TIMER_NOCONNECTION; timer_new_state = TIMER_NOCONNECTION;
} }
else{ else{
if(time_offset_ok == true){ if(time_offset_ok == false){
// check if offset is OK - if not .. set state back to INIT // check if offset is OK - if not .. set state back to INIT
timer_new_state = TIMER_INIT; timer_new_state = TIMER_INIT;
keep_connection_off = true;
connection_last_established_at_ms = millis();
} }
else{ else{
// check if the FALSESTATE button is pressed OR we are in trainingsmode - somebody is ready to run, but STARTBUTTON is NOT pressed ... // check if the FALSESTATE button is pressed OR we are in trainingsmode - somebody is ready to run, but STARTBUTTON is NOT pressed ...
@ -213,11 +288,12 @@ void loop(void) {
} }
break; break;
case TIMER_READY: case TIMER_READY:
delay(10);
if((digitalRead(FAILSTARTBUTTON_IN) != FAILSTARTBUTTON_PRESSED) && if((digitalRead(FAILSTARTBUTTON_IN) != FAILSTARTBUTTON_PRESSED) &&
(timer_mode != MODE_TRAINING)) (timer_mode != MODE_TRAINING))
{ {
// false start was released again - go back to INIT ... so far this is not a false start - run was not started yet // false start was released again - go back to IDLE ... so far this is not a false start - run was not started yet
timer_new_state = TIMER_INIT; timer_new_state = TIMER_IDLE;
} else { } else {
// check if the start button was pressed ... there is at least still someone waiting for the run . // check if the start button was pressed ... there is at least still someone waiting for the run .
if(digitalRead(STARTBUTTON_IN) == STARTBUTTON_PRESSED){ if(digitalRead(STARTBUTTON_IN) == STARTBUTTON_PRESSED){
@ -226,12 +302,16 @@ void loop(void) {
startsequence_done = false; startsequence_done = false;
running_time_offset = mean_time_offset; running_time_offset = mean_time_offset;
false_start = false; false_start = false;
attachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN), false_start_isr, CHANGE); attachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN), false_start_isr, RISING );
Timer1.initialize();
timer_new_state = TIMER_STARTED;
// set the startime - this is the current time plus the length of this sequence // set the startime - this is the current time plus the length of this sequence
start_time = millis() + STARTSEQ_LENGTH_MS; start_time = millis() + STARTSEQ_LENGTH_MS;
timer_new_state = TIMER_STARTED; //Serial.print(millis());
//Serial.print(" <- current time ; starttime -> ");
//Serial.println(start_time);
// call the start sequence interrupt routine ... // call the start sequence interrupt routine ...
start_isr(); Timer1.attachInterrupt(start_isr,STARTSEQ_PAUSE[startsequence_count]); // startISR to run every given microseconds
} }
} }
break; break;
@ -239,7 +319,10 @@ void loop(void) {
//initialize the start ISR and the timer interrupt ... //initialize the start ISR and the timer interrupt ...
//----> to be removed : startSequence(); //----> to be removed : startSequence();
if( false_start == true) { if( false_start == true) {
failsequence_done = false;
failsequence_count = 0;
timer_new_state = TIMER_FAIL; timer_new_state = TIMER_FAIL;
Timer1.attachInterrupt(failSequence,FAILSEQ_PAUSE[failsequence_count]);
} else { } else {
if(startsequence_done == true){ if(startsequence_done == true){
timer_new_state = TIMER_RUNNING; timer_new_state = TIMER_RUNNING;
@ -247,23 +330,28 @@ void loop(void) {
} }
break; break;
case TIMER_RUNNING: case TIMER_RUNNING:
noTone(PIEZO_PIN); //noTone(PIEZO_PIN);
if(time_offset_ok != true){ if(time_offset_ok != true){
// check if offset is still OK - if not .. set warning // check if offset is still OK - if not .. set warning
warn_during_run = true; warn_during_run = true;
} }
if(millis() - start_time > TIMER_TIMEOUT){ if((signed long)(millis() - start_time) > TIMER_TIMEOUT){
//Serial.print(millis());
//Serial.print("<- curren time ; runtime ->");
//Serial.println(millis() - start_time);
timer_new_state = TIMER_TIMEDOUT; timer_new_state = TIMER_TIMEDOUT;
} } else {
if(digitalRead(CANCELBUTTON_IN) == CANCELBUTTON_PRESSED){ if(digitalRead(CANCELBUTTON_IN) == CANCELBUTTON_PRESSED){
timer_new_state = TIMER_CANCELLED; timer_new_state = TIMER_CANCELLED;
} } else {
if(radio_data.topbuttonpressedtime > running_time_offset){ if(radio_data.topbuttonpressedtime > running_time_offset){
if((radio_data.topbuttonpressedtime - running_time_offset) > start_time){ if((radio_data.topbuttonpressedtime - running_time_offset) > start_time){
run_time = (radio_data.topbuttonpressedtime - running_time_offset) - start_time; run_time = (radio_data.topbuttonpressedtime - running_time_offset) - start_time;
timer_new_state = TIMER_STOPPED; timer_new_state = TIMER_STOPPED;
} }
} }
}
}
break; break;
case TIMER_STOPPED: case TIMER_STOPPED:
//calculate the run_time and switch to WAIT //calculate the run_time and switch to WAIT
@ -274,12 +362,13 @@ void loop(void) {
break; break;
case TIMER_FAIL: case TIMER_FAIL:
//fail start case .... //fail start case ....
failSequence();
run_time = 99999; run_time = 99999;
if(true == failsequence_done){
delay(KEY_BOUNCE_MS); delay(KEY_BOUNCE_MS);
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){ if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
timer_new_state = TIMER_WAIT; timer_new_state = TIMER_WAIT;
} }
}
break; break;
case TIMER_CANCELLED: case TIMER_CANCELLED:
// what to do in chancel mode ? // what to do in chancel mode ?
@ -298,17 +387,23 @@ void loop(void) {
} }
break; break;
case TIMER_WAIT: case TIMER_WAIT:
delay(10);
// disable interrupt if not already done // disable interrupt if not already done
detachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN)); //detachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN));
// wait until the chancel button was pressed to go ahead // wait until the chancel button was pressed to go ahead
if((digitalRead(CANCELBUTTON_IN) == CANCELBUTTON_PRESSED) && if((digitalRead(CANCELBUTTON_IN) == CANCELBUTTON_PRESSED) //&&
(digitalRead(STOPBUTTON_IN) == STOPBUTTON_PRESSED) && //(digitalRead(STOPBUTTON_IN) != STOPBUTTON_PRESSED) &&
(digitalRead(FAILSTARTBUTTON_IN) == FAILSTARTBUTTON_PRESSED)) //(digitalRead(FAILSTARTBUTTON_IN) != FAILSTARTBUTTON_PRESSED)
)
{ {
timer_new_state = TIMER_IDLE; timer_new_state = TIMER_IDLE;
} }
break; break;
} }
//Serial.print("looptime_base ");
//Serial.println(millis());
} }
} }
@ -316,6 +411,16 @@ void loop(void) {
//####################### HELPER FUNCTIONS ########################### //####################### HELPER FUNCTIONS ###########################
void receive_values(void){ void receive_values(void){
signed long current_time_offset = 0; // current offset ... signed long current_time_offset = 0; // current offset ...
// wait the connection time out time before receiving data - this is to tell the TOP_STATION to resend offset values in fast mode ...
if(keep_connection_off){
if((millis() - connection_last_established_at_ms) > (2*CONN_TIMEOUT)){
keep_connection_off = false;
Serial.println("Connection ON allowed.");
} else {
Serial.println("Connection OFF forced.");
}
} else
{
// check if radio data is available - if so read the data // check if radio data is available - if so read the data
if( radio.available()){ if( radio.available()){
// read data from TOP_STATION ... // read data from TOP_STATION ...
@ -325,14 +430,14 @@ void receive_values(void){
connection_last_established_at_ms = millis(); connection_last_established_at_ms = millis();
connection_available = true; connection_available = true;
current_time_offset = radio_data.topstationtime - millis(); // the offset between TOP_STATION and BASESTATION current_time_offset = radio_data.topstationtime - millis(); // the offset between TOP_STATION and BASESTATION
Serial.print("Current time on host in millis:"); //Serial.print("Current time on host in millis:");
Serial.print(millis()); //Serial.print(millis());
Serial.print(F(" Current time on client in millis: ")); //Serial.print(F(" Current time on client in millis: "));
Serial.println(radio_data.topstationtime); //Serial.println(radio_data.topstationtime);
Serial.print("Offset is: "); //Serial.print("Offset is: ");
Serial.println(current_time_offset); //Serial.println(current_time_offset);
Serial.print(F(" Button was pressed last time on client in millis: ")); //Serial.print(F(" Button was pressed last time on client in millis: "));
Serial.println(radio_data.topbuttonpressedtime); //Serial.println(radio_data.topbuttonpressedtime);
// offset calculation ... only needed if the variation is bigger than allowed or not enough values available already ... // offset calculation ... only needed if the variation is bigger than allowed or not enough values available already ...
if(counter_time_offset == 0){ if(counter_time_offset == 0){
@ -391,16 +496,12 @@ void receive_values(void){
} }
else{ else{
// remove the RF24 connection flag if no data was received for longer time // remove the RF24 connection flag if no data was received for longer time
if(millis() - connection_last_established_at_ms >= CONN_TIMEOUT || connection_last_established_at_ms == 0){ if((millis() - connection_last_established_at_ms >= CONN_TIMEOUT) || (connection_last_established_at_ms == 0)){
connection_available = false; connection_available = false;
Serial.println("ERROR: No connection established to TOPSTATION"); Serial.println("ERROR: No connection established to TOPSTATION");
} }
else{
Serial.println("No data from TOP_STATION avaialble - OR the connection couldn't be established");
}
} // radio available } // radio available
} // keep connection off
}// receive values }// receive values
void update_screen(timer_state_e state){ void update_screen(timer_state_e state){
@ -464,6 +565,7 @@ void update_screen(timer_state_e state){
footer = "Reaction time: "; footer = "Reaction time: ";
footer += curr_time_local; footer += curr_time_local;
footer += " sec"; footer += " sec";
Serial.println(footer);
break; break;
case TIMER_TIMEDOUT: case TIMER_TIMEDOUT:
header = "Timed out!"; header = "Timed out!";
@ -530,66 +632,107 @@ void update_screen(timer_state_e state){
} }
void set_state_LEDs(timer_state_e state, boolean warn){ void set_state_LEDs(timer_state_e state, boolean warn){
//Serial.print("led state is: ");
//Serial.println(state);
// set the LEDS corresponding to the state of the timer ... as long as the system is not waiting for input ... // set the LEDS corresponding to the state of the timer ... as long as the system is not waiting for input ...
if(TIMER_WAIT != state){ if(TIMER_WAIT != state){
digitalWrite(READY_LED, LEDStates[state][0]); leds_states = LEDStates[state];
digitalWrite(RUN_LED, LEDStates[state][1]); }
digitalWrite(FAIL_LED, LEDStates[state][2]); // loop over all the LEDs and set state ...
for(uint8_t led = 0; led<NO_LAST_LED-1;led++){
if(warn == true){ //Serial.print("led: ");
digitalWrite(WARN_LED, WARN_LED_ON); //Serial.print(led);
// warn is special so far - there is no special state for the warn led . Handle if warn flag is set - switch LED on in this case ...
if((WARN_LED == led)&&(true==warn)){
digitalWrite(LEDPins[led], HIGH);
//Serial.println(" set to HIGH");
} else {
switch(leds_states[led]){
case LED_BLINK:
digitalWrite(LEDPins[led], blink_on);
//Serial.println(" set to BLINK");
break;
case LED_OFF:
digitalWrite(LEDPins[led], LOW);
//Serial.println(" set to LOW");
break;
case LED_ON:
digitalWrite(LEDPins[led], HIGH);
//Serial.println(" set to HIGH");
break;
} }
else
{
digitalWrite(WARN_LED, WARN_LED_OFF);
} }
} }
} }
void failSequence(void){ void failSequence(void){
// first tone // first tone
tone(PIEZO_PIN, FAILSEQ_TON_FREQUENCY,FAILSEQ_TON_LENGTH_MS ); Serial.print(millis());
delay(FAILSEQ_TONEPAUSE_MS); Serial.print(" <- current time ; failsquence count -> ");
//second tone Serial.println(failsequence_count);
tone(PIEZO_PIN, FAILSEQ_TON_FREQUENCY,FAILSEQ_TON_LENGTH_MS ); if(failsequence_count <FAILSEQ_STEPS){
delay(FAILSEQ_TONEPAUSE_MS); Serial.println(FAILSEQ_DURATION[failsequence_count]);
// play the tone ...
tone( PIEZO_PIN, FAILSEQ_NOTES[failsequence_count],FAILSEQ_DURATION[failsequence_count] );
if(failsequence_count > 0){
Timer1.setPeriod(FAILSEQ_PAUSE[failsequence_count]);
}
// increase the counter
failsequence_count++;
} else {
// set the done bit and stop and detache the timer1
Timer1.detachInterrupt();
failsequence_done = true;
noTone(PIEZO_PIN); noTone(PIEZO_PIN);
} }
}
void false_start_isr(void){ void false_start_isr(void){
// this is the interrupt routine for the FALSESTART button // this is the interrupt routine for the FALSESTART button
// this will save the time when the runner is really started // this will save the time when the runner is really started
if(timer_new_state != TIMER_READY){
//Serial.print(millis());
//Serial.println("** Interrupt service routine started: false_start_ISR **"); //Serial.println("** Interrupt service routine started: false_start_ISR **");
//Serial.print("false start button: ");
//Serial.println(digitalRead(FAILSTARTBUTTON_IN));
runner_start_time = millis(); runner_start_time = millis();
if(millis() - start_time <= 0){ if(false == startsequence_done){
false_start = true; false_start = true;
Serial.println("** Interrupt service routine detected false_start. Set new state to TIMER_FAIL **"); Serial.print(millis());
detachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN)); Serial.print(" <- current time ; starttime -> ");
} else { Serial.print(start_time);
// disable this interrupt; Serial.println(" ** Interrupt service routine detected false_start. **");
}
detachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN)); detachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN));
} }
} }
void start_isr(void){ void start_isr(void){
// this is the timer interrupt routine that is called during the startsequence // this is the timer interrupt routine that is called during the startsequence
//Serial.print(millis());
//Serial.print(" <- current time ; startsquence count -> ");
//Serial.println(startsequence_count);
if(false == false_start){
if(startsequence_count < STARTSEQ_STEPS){ if(startsequence_count < STARTSEQ_STEPS){
// (re)init the interrupt timer ... // (re)init the interrupt timer ...
Timer1.initialize(STARTSEQ_PAUSE[startsequence_count]); if(startsequence_count > 0){
Timer1.attachInterrupt(start_isr); // startISR to run every given microseconds
// play the tone ... // play the tone ...
if(STARTSEQ_NOTES[startsequence_count] > 0 ){ //Serial.println(STARTSEQ_DURATION[startsequence_count]);
tone( PIEZO_PIN, STARTSEQ_NOTES[startsequence_count],STARTSEQ_DURATION[startsequence_count] ); tone( PIEZO_PIN, STARTSEQ_NOTES[startsequence_count],STARTSEQ_DURATION[startsequence_count] );
Timer1.setPeriod(STARTSEQ_PAUSE[startsequence_count]);
} }
// increase the counter // increase the counter
startsequence_count++; startsequence_count++;
} else { } else {
// set the done bit and stop and detache the timer1 // set the done bit and stop and detache the timer1
Timer1.detachInterrupt(); Timer1.detachInterrupt();
//Serial.print(millis());
//Serial.println("ms: set startsequence done to true");
startsequence_done = true; startsequence_done = true;
} }
} else {
Timer1.detachInterrupt();
}
} }