diff --git a/speedclock.h b/speedclock.h index 682aca2..f8ce06d 100644 --- a/speedclock.h +++ b/speedclock.h @@ -2,6 +2,7 @@ #ifndef speedclock_H #define speedclock_H +#define STATS_PLOTS_EVERY_MS 1000 // plots statistical values all number of milli seconds .... 0 menas no plot //-------------- defines for the radio devices NRF24 --------------------------------------------------------- #define STATION_SEL0 9 // this 9 for Nano @@ -28,6 +29,7 @@ typedef struct transcv_struct{ #define KEY_BOUNCE_MS 50 // the time we use to avoid keybouncing ... #define KEY_LONGPRESSED_MS 1000 +#define KEY_TOGGLE_MS 500 // the time between to key actions ... #define BUTTON_NOTPRESSED HIGH #define BUTTON_PRESSED LOW #define BUTTON_LONGPRESSED 3 @@ -44,9 +46,10 @@ const uint8_t BUTTONPins[NO_LAST_BUTTON] = { #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 {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 {TIMER_INIT = 0, TIMER_NOCONNECTION, TIMER_IDLE, TIMER_READY, TIMER_STARTED, TIMER_RUNNING , TIMER_CANCELLED, TIMER_STOPPED, TIMER_TIMEDOUT, TIMER_FAIL, TIMER_WAIT, TIMER_SETTINGS} timer_state_e; +typedef enum {MODE_COMPETE = 0, MODE_TRAINING, MODE_CALIBRATION, NO_LAST_MODE} 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 +const char timer_mode_short[NO_LAST_MODE] = {[MODE_COMPETE]='F',[MODE_TRAINING]='T',[MODE_CALIBRATION]='C'}; +const String timer_mode_string[NO_LAST_MODE] = {[MODE_COMPETE]="Competetion",[MODE_TRAINING]=" Training ",[MODE_CALIBRATION]="Calibration"}; #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 @@ -60,17 +63,18 @@ const uint8_t LEDPins[NO_LAST_LED] = { typedef enum {LED_OFF = 0, LED_ON, LED_BLINK } led_state_e; const uint8_t LEDStates[][NO_LAST_LED] = { - [TIMER_INIT] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_BLINK ,[WARN_LED]=LED_OFF}, // 0 - [TIMER_NOCONNECTION] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_ON, [WARN_LED]=LED_OFF}, // 1 - [TIMER_IDLE] = {[READY_LED]=LED_ON, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 2 - [TIMER_READY] = {[READY_LED]=LED_BLINK, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 3 - [TIMER_STARTED] = {[READY_LED]=LED_ON, [RUN_LED]=LED_ON, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 4 - [TIMER_RUNNING] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_ON, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 5 - [TIMER_CANCELLED] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_ON, [WARN_LED]=LED_OFF}, // 6 - [TIMER_STOPPED] = {[READY_LED]=LED_ON, [RUN_LED]=LED_ON, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 7 - [TIMER_TIMEDOUT] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_ON, [FAIL_LED]=LED_ON, [WARN_LED]=LED_OFF}, // 8 - [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 + [TIMER_INIT] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_BLINK ,[WARN_LED]=LED_OFF}, // 0 + [TIMER_NOCONNECTION] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_ON, [WARN_LED]=LED_OFF}, // 1 + [TIMER_IDLE] = {[READY_LED]=LED_ON, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 2 + [TIMER_READY] = {[READY_LED]=LED_BLINK, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 3 + [TIMER_STARTED] = {[READY_LED]=LED_ON, [RUN_LED]=LED_ON, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 4 + [TIMER_RUNNING] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_ON, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 5 + [TIMER_CANCELLED] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_ON, [WARN_LED]=LED_OFF}, // 6 + [TIMER_STOPPED] = {[READY_LED]=LED_ON, [RUN_LED]=LED_ON, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_OFF}, // 7 + [TIMER_TIMEDOUT] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_ON, [FAIL_LED]=LED_ON, [WARN_LED]=LED_OFF}, // 8 + [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 + [TIMER_SETTINGS] = {[READY_LED]=LED_OFF, [RUN_LED]=LED_OFF, [FAIL_LED]=LED_OFF, [WARN_LED]=LED_ON} // 11 }; #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 diff --git a/speedclock.ino b/speedclock.ino index 0fa2c05..fdbf8d5 100644 --- a/speedclock.ino +++ b/speedclock.ino @@ -17,6 +17,8 @@ RF24 radio(RF24_CNS,RF24_CE); /**********************************************************/ byte addresses[][12] = {"top_station","basestation"}; // Radio pipe addresses for the 2 nodes to communicate. +unsigned long stats_last_plotted = 0; + 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 ... @@ -28,6 +30,7 @@ uint8_t *leds_states = LEDStates[TIMER_INIT]; boolean time_offset_ok = false; // true as long as the offset is correctly calculated 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 current_time_offset = 0; // current 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 ... volatile unsigned long start_time = 0; // if the timer is running this is that start time ... (use volatile for all shared variables that deals with hardware) @@ -54,7 +57,7 @@ timer_state_e timer_state = TIMER_INIT; // current state needs timer_state_e timer_new_state = TIMER_NOCONNECTION; // next state - in the startup phase the first state - will be TIMER_NOCONNECTION ... checking if a connection to TOPSTATION is established timer_mode_e timer_mode = MODE_COMPETE; // mode of the BASESTATION - this can be changed in IDLE state by pressing the CANCEL button - +unsigned long timer_mode_changed_at = 0; transcv_s radio_data; void setup(){ @@ -124,6 +127,7 @@ void loop(void) { } // set state to new_state + /* if(timer_state != timer_new_state){ Serial.print(millis()); Serial.print("ms : current state:"); @@ -131,6 +135,7 @@ void loop(void) { Serial.print(" new state:"); Serial.println(timer_new_state); } + */ // update button states ... update_buttons(); @@ -243,12 +248,18 @@ void loop(void) { } else{ // check if the FALSESTATE button is pressed OR we are in trainingsmode - somebody is ready to run, but STARTBUTTON is NOT pressed ... - if(((button_state[BUTTON_FAIL] != BUTTON_NOTPRESSED) || (timer_mode == MODE_TRAINING)) && - (button_state[BUTTON_START] == BUTTON_NOTPRESSED)) + if(((button_state[BUTTON_FAIL] != BUTTON_NOTPRESSED) || (timer_mode != MODE_COMPETE)) && + (button_state[BUTTON_START] == BUTTON_NOTPRESSED) && + (button_state[BUTTON_STOPCANCEL] == BUTTON_NOTPRESSED)) { - //read again and check if still active ... - if(button_state[BUTTON_FAIL] != BUTTON_NOTPRESSED){ timer_new_state = TIMER_READY; + } else { + if((button_state[BUTTON_STOPCANCEL] == BUTTON_LONGPRESSED) && + (button_state[BUTTON_START] == BUTTON_NOTPRESSED) && + (button_state[BUTTON_FAIL] == BUTTON_NOTPRESSED)) + { + // switch to settings mode ... + timer_new_state = TIMER_SETTINGS; } } } @@ -256,7 +267,7 @@ void loop(void) { break; case TIMER_READY: if((button_state[BUTTON_FAIL] == BUTTON_NOTPRESSED) && - (timer_mode != MODE_TRAINING)) + (timer_mode == MODE_COMPETE)) { // 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_IDLE; @@ -268,13 +279,23 @@ void loop(void) { startsequence_done = false; running_time_offset = mean_time_offset; false_start = false; - attachInterrupt(digitalPinToInterrupt(BUTTONPins[BUTTON_FAIL]), false_start_isr, RISING ); + if(timer_mode == MODE_COMPETE){ + attachInterrupt(digitalPinToInterrupt(BUTTONPins[BUTTON_FAIL]), 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 start_time = millis() + STARTSEQ_LENGTH_MS; // call the start sequence interrupt routine ... Timer1.attachInterrupt(start_isr,STARTSEQ_PAUSE[startsequence_count]); // startISR to run every given microseconds + } else { + if((button_state[BUTTON_STOPCANCEL] == BUTTON_LONGPRESSED) && + (button_state[BUTTON_START] == BUTTON_NOTPRESSED) && + (button_state[BUTTON_FAIL] == BUTTON_NOTPRESSED)) + { + // switch to settings mode ... + timer_new_state = TIMER_SETTINGS; + } } } break; @@ -354,16 +375,48 @@ void loop(void) { timer_new_state = TIMER_IDLE; } break; - } + case TIMER_SETTINGS: + // switch between the different modes for now - compete, training and calibration ... + if((button_state[BUTTON_STOPCANCEL] == BUTTON_PRESSED) && + (button_state[BUTTON_START] == BUTTON_NOTPRESSED)) + { + // go back to idle + timer_new_state = TIMER_IDLE; + } else { + if((button_state[BUTTON_STOPCANCEL] == BUTTON_NOTPRESSED) && + (button_state[BUTTON_START] == BUTTON_PRESSED)) + { + if((millis() - timer_mode_changed_at) > KEY_TOGGLE_MS){ + // switch system mode ... + switch(timer_mode){ + case MODE_COMPETE: + timer_mode = MODE_TRAINING; + break; + case MODE_TRAINING: + timer_mode = MODE_CALIBRATION; + break; + case MODE_CALIBRATION: + timer_mode = MODE_COMPETE; + break; + } + timer_mode_changed_at = millis(); + } + } + + } + break; + } } - //Serial.print("mean_time_offset "); - //Serial.println(mean_time_offset); - //Serial.print("current_time_offset "); - //Serial.println(current_time_offset); - //Serial.print("looptime "); - //Serial.println(millis()); - + if( (STATS_PLOTS_EVERY_MS > 0) && + ((millis() -stats_last_plotted) > STATS_PLOTS_EVERY_MS)) + { + Serial.println("mean_time_offset-current_time_offset & looptime"); + Serial.print((signed long)(mean_time_offset - current_time_offset); + Serial.print(" "); + Serial.println(millis()- startloop_ms); + stats_last_plotted = millis(); + } } //####################### HELPER FUNCTIONS ########################### @@ -409,7 +462,6 @@ void update_buttons(void){ } void receive_values(void){ - 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)){ @@ -576,6 +628,11 @@ void update_screen(timer_state_e state){ content = curr_time_local; footer = "seconds too early"; break; + case TIMER_SETTINGS: + header = "Settings"; + content = timer_mode_string[timer_mode]; + footer = "Press Start to toggle."; + break; default: scr_update = false; break; @@ -607,6 +664,9 @@ void update_screen(timer_state_e state){ //if print the "Y" display.print("Y"); } + //check which timer_mode we are in + display.setCursor(122,0); + display.print(timer_mode_short[timer_mode]); display.setCursor(0,1); display.setLetterSpacing(0); display.print("----------------------------");