Optimized the memory usage
This commit is contained in:
parent
d7a1c92a47
commit
af6a6920e9
5 changed files with 20215 additions and 103 deletions
128
speedclock.ino
128
speedclock.ino
|
@ -1,6 +1,3 @@
|
||||||
#include <Arduino.h>
|
|
||||||
//#include <U8g2lib.h>
|
|
||||||
//#include "fonts.h"
|
|
||||||
#include <Wire.h>
|
#include <Wire.h>
|
||||||
#include "SSD1306Ascii.h"
|
#include "SSD1306Ascii.h"
|
||||||
#include "SSD1306AsciiWire.h"
|
#include "SSD1306AsciiWire.h"
|
||||||
|
@ -20,20 +17,18 @@ 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 counter_time_offset = 0; // number of used values for the mean value calculation
|
uint8_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 current_time_offset = 0; // current 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 ...
|
||||||
unsigned long start_time = 0; // if the timer is running this is that start time ...
|
unsigned long start_time = 0; // if the timer is running this is that start time ...
|
||||||
unsigned long runner_start_time = 0; // this is the time the runner left the pad - so the status of the falsetstart pin goes to high again - but this is OK and a real start
|
unsigned long runner_start_time = 0; // this is the time the runner left the pad - so the status of the falsetstart pin goes to high again - but this is OK and a real start
|
||||||
signed long runner_run_time = 0; // this is the time the runner really needed or the time started to early - depending on sign ...
|
|
||||||
unsigned long run_time = 0; // if the timer is running this is that start time ...
|
unsigned long run_time = 0; // if the timer is running this is that start time ...
|
||||||
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 ...
|
||||||
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
|
||||||
|
boolean false_start = false;
|
||||||
boolean topbuttonwaspressed = false;
|
boolean topbuttonwaspressed = false;
|
||||||
|
|
||||||
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...
|
||||||
|
@ -63,14 +58,6 @@ void setup(){
|
||||||
pinMode(STATION_SEL0, INPUT);
|
pinMode(STATION_SEL0, INPUT);
|
||||||
radio_sel0 = digitalRead(STATION_SEL0);
|
radio_sel0 = digitalRead(STATION_SEL0);
|
||||||
radio_sel1 = digitalRead(STATION_SEL1);
|
radio_sel1 = digitalRead(STATION_SEL1);
|
||||||
Serial.print(F(" The station select[1,0] pins (pin "));
|
|
||||||
Serial.print(STATION_SEL0);
|
|
||||||
Serial.print(F(","));
|
|
||||||
Serial.print(STATION_SEL1);
|
|
||||||
Serial.print(F(") are set to level: '"));
|
|
||||||
Serial.print(radio_sel0);
|
|
||||||
Serial.print(radio_sel1);
|
|
||||||
Serial.println("'");
|
|
||||||
|
|
||||||
if((radio_sel0 == 1) & (radio_sel1 == 0)){
|
if((radio_sel0 == 1) & (radio_sel1 == 0)){
|
||||||
stationNumber = TOPSTATION;
|
stationNumber = TOPSTATION;
|
||||||
|
@ -120,8 +107,6 @@ 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;
|
||||||
Serial.print("Stopp button was pressed at:");
|
|
||||||
Serial.println(radio_data.topbuttonpressedtime);
|
|
||||||
digitalWrite(RUN_LED, RUN_LED_ON);
|
digitalWrite(RUN_LED, RUN_LED_ON);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -136,18 +121,10 @@ void loop(void) {
|
||||||
if(topbuttonwaspressed || ((millis()-radio_data.topstationtime) >= MIN_DELAY_BETWEEN_SEND_MS)){
|
if(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(millis());
|
|
||||||
Serial.print("ms - Send data to bottom station: topstationtime: ");
|
Serial.print("ms - Send data to bottom station: topstationtime: ");
|
||||||
Serial.print( radio_data.topstationtime );
|
Serial.print( radio_data.topstationtime );
|
||||||
Serial.print(" stoppressedtime: ");
|
Serial.print(" stoppressedtime: ");
|
||||||
Serial.print( radio_data.topbuttonpressedtime );
|
Serial.print( radio_data.topbuttonpressedtime );
|
||||||
if(topbuttonwaspressed)
|
|
||||||
{
|
|
||||||
Serial.println(" .Stopp button was pressed.");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Serial.println(" .Stopp button was NOT pressed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -173,9 +150,6 @@ void loop(void) {
|
||||||
// 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();
|
||||||
|
|
||||||
// show state debug outputs at serial monitor
|
|
||||||
update_statemessage(timer_new_state);
|
|
||||||
|
|
||||||
// update the OLED screen
|
// update the OLED screen
|
||||||
update_screen(timer_new_state);
|
update_screen(timer_new_state);
|
||||||
|
|
||||||
|
@ -239,7 +213,6 @@ void loop(void) {
|
||||||
// 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){
|
||||||
// now enable the interrupt for the FALSESTART button
|
// now enable the interrupt for the FALSESTART button
|
||||||
attachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN), false_start_isr, CHANGE);
|
|
||||||
timer_new_state = TIMER_STARTED;
|
timer_new_state = TIMER_STARTED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -250,8 +223,13 @@ void loop(void) {
|
||||||
break;
|
break;
|
||||||
case TIMER_STARTED:
|
case TIMER_STARTED:
|
||||||
//initialize the start countdown
|
//initialize the start countdown
|
||||||
timer_new_state = TIMER_RUNNING;
|
|
||||||
startSequence();
|
startSequence();
|
||||||
|
if(false_start == false){
|
||||||
|
timer_new_state = TIMER_RUNNING;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
timer_new_state = TIMER_FAIL;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case TIMER_RUNNING:
|
case TIMER_RUNNING:
|
||||||
noTone(PIEZO_PIN);
|
noTone(PIEZO_PIN);
|
||||||
|
@ -269,7 +247,6 @@ void loop(void) {
|
||||||
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;
|
||||||
runner_run_time = runner_start_time - run_time;
|
|
||||||
timer_new_state = TIMER_STOPPED;
|
timer_new_state = TIMER_STOPPED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -285,7 +262,6 @@ void loop(void) {
|
||||||
//fail start case ....
|
//fail start case ....
|
||||||
failSequence();
|
failSequence();
|
||||||
run_time = 99999;
|
run_time = 99999;
|
||||||
runner_run_time = runner_start_time - start_time;
|
|
||||||
delay(10);
|
delay(10);
|
||||||
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
|
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
|
||||||
timer_new_state = TIMER_WAIT;
|
timer_new_state = TIMER_WAIT;
|
||||||
|
@ -294,7 +270,6 @@ void loop(void) {
|
||||||
case TIMER_CANCELLED:
|
case TIMER_CANCELLED:
|
||||||
// what to do in chancel mode ?
|
// what to do in chancel mode ?
|
||||||
run_time = 99999;
|
run_time = 99999;
|
||||||
runner_run_time = runner_start_time - start_time;
|
|
||||||
delay(10);
|
delay(10);
|
||||||
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
|
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
|
||||||
timer_new_state = TIMER_WAIT;
|
timer_new_state = TIMER_WAIT;
|
||||||
|
@ -303,7 +278,6 @@ void loop(void) {
|
||||||
case TIMER_TIMEDOUT:
|
case TIMER_TIMEDOUT:
|
||||||
// time out
|
// time out
|
||||||
run_time = millis() - start_time;
|
run_time = millis() - start_time;
|
||||||
runner_run_time = runner_start_time - start_time;
|
|
||||||
delay(10);
|
delay(10);
|
||||||
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
|
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
|
||||||
timer_new_state = TIMER_WAIT;
|
timer_new_state = TIMER_WAIT;
|
||||||
|
@ -324,6 +298,7 @@ void loop(void) {
|
||||||
|
|
||||||
//####################### HELPER FUNCTIONS ###########################
|
//####################### HELPER FUNCTIONS ###########################
|
||||||
void receive_values(void){
|
void receive_values(void){
|
||||||
|
signed long current_time_offset = 0; // current offset ...
|
||||||
// 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 ...
|
||||||
|
@ -407,52 +382,9 @@ void receive_values(void){
|
||||||
|
|
||||||
}// receive values
|
}// receive values
|
||||||
|
|
||||||
void update_statemessage(timer_state_e state){
|
|
||||||
//check if the state has changed ... only show the message below (of the given state) in this case ...
|
|
||||||
if(timer_state != timer_new_state){
|
|
||||||
switch(state){
|
|
||||||
case TIMER_NOCONNECTION:
|
|
||||||
Serial.println("*** TIMER_NOCONNECTION ***");
|
|
||||||
break;
|
|
||||||
case TIMER_INIT:
|
|
||||||
Serial.println("*** TIMER_INIT ***");
|
|
||||||
break;
|
|
||||||
case TIMER_IDLE:
|
|
||||||
Serial.println("*** TIMER_IDLE ***");
|
|
||||||
break;
|
|
||||||
case TIMER_READY:
|
|
||||||
Serial.println("*** TIMER_READY ***");
|
|
||||||
break;
|
|
||||||
case TIMER_STARTED:
|
|
||||||
Serial.println("*** TIMER_STARTED ***");
|
|
||||||
break;
|
|
||||||
case TIMER_RUNNING:
|
|
||||||
Serial.println("*** TIMER_RUNNING ***");
|
|
||||||
break;
|
|
||||||
case TIMER_CANCELLED:
|
|
||||||
Serial.println("*** TIMER_CANCELLED ***");
|
|
||||||
break;
|
|
||||||
case TIMER_STOPPED:
|
|
||||||
Serial.println("*** TIMER_STOPPED ***");
|
|
||||||
break;
|
|
||||||
case TIMER_TIMEDOUT:
|
|
||||||
Serial.println("*** TIMER_TIMEDOUT ***");
|
|
||||||
break;
|
|
||||||
case TIMER_FAIL:
|
|
||||||
Serial.println("*** TIMER_FAIL ***");
|
|
||||||
break;
|
|
||||||
case TIMER_WAIT:
|
|
||||||
Serial.println("*** TIMER_WAIT ***");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_screen(timer_state_e state){
|
void update_screen(timer_state_e state){
|
||||||
bool scr_update = true;
|
bool scr_update = true;
|
||||||
int ypos = 64-42/2;
|
uint8_t ypos = 64-42/2;
|
||||||
String header = "no state";
|
String header = "no state";
|
||||||
String content = "";
|
String content = "";
|
||||||
String footer = "";
|
String footer = "";
|
||||||
|
@ -596,31 +528,29 @@ void set_state_LEDs(timer_state_e state, boolean warn){
|
||||||
void startSequence(void){
|
void startSequence(void){
|
||||||
// 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
|
||||||
running_time_offset = mean_time_offset;
|
running_time_offset = mean_time_offset;
|
||||||
|
false_start = false;
|
||||||
|
attachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN), false_start_isr, CHANGE);
|
||||||
start_time = millis() + STARTSEQ_LENGTH_MS;
|
start_time = millis() + STARTSEQ_LENGTH_MS;
|
||||||
Serial.print("Start time is: ");
|
//Serial.print("Start time is: ");
|
||||||
Serial.println(start_time);
|
//Serial.println(start_time);
|
||||||
// this is sequence of usually three tones after a wait time 1sec , in between the tones there is also a delay of 1 sec. Each tone is 200ms seconds long, except the last
|
// this is sequence of usually three tones after a wait time 1sec , in between the tones there is also a delay of 1 sec. Each tone is 200ms seconds long, except the last
|
||||||
update_statemessage(timer_new_state);
|
if(false_start == false){
|
||||||
if(timer_new_state == TIMER_RUNNING){
|
delay(STARTSEQ_STARTPAUSE_MS);
|
||||||
wait(STARTSEQ_STARTPAUSE_MS);
|
|
||||||
}
|
}
|
||||||
// first tone
|
// first tone
|
||||||
update_statemessage(timer_new_state);
|
if(false_start == false){
|
||||||
if(timer_new_state == TIMER_RUNNING){
|
|
||||||
tone(PIEZO_PIN, STARTSEQ_TON_1_2_FREQUENCY,STARTSEQ_TON_1_2_LENGTH_MS );
|
tone(PIEZO_PIN, STARTSEQ_TON_1_2_FREQUENCY,STARTSEQ_TON_1_2_LENGTH_MS );
|
||||||
wait(STARTSEQ_TONEPAUSE_MS);
|
delay(STARTSEQ_TONEPAUSE_MS);
|
||||||
}
|
}
|
||||||
//second tone
|
//second tone
|
||||||
update_statemessage(timer_new_state);
|
if(false_start == false){
|
||||||
if(timer_new_state == TIMER_RUNNING){
|
|
||||||
tone(PIEZO_PIN, STARTSEQ_TON_1_2_FREQUENCY,STARTSEQ_TON_1_2_LENGTH_MS );
|
tone(PIEZO_PIN, STARTSEQ_TON_1_2_FREQUENCY,STARTSEQ_TON_1_2_LENGTH_MS );
|
||||||
wait(STARTSEQ_TONEPAUSE_MS);
|
delay(STARTSEQ_TONEPAUSE_MS);
|
||||||
}
|
}
|
||||||
//third tone
|
//third tone
|
||||||
update_statemessage(timer_new_state);
|
if(false_start == false){
|
||||||
if(timer_new_state == TIMER_RUNNING){
|
|
||||||
tone(PIEZO_PIN, STARTSEQ_TON_3_FREQUENCY,STARTSEQ_TON_3_LENGTH_MS );
|
tone(PIEZO_PIN, STARTSEQ_TON_3_FREQUENCY,STARTSEQ_TON_3_LENGTH_MS );
|
||||||
wait(STARTSEQ_TON_3_LENGTH_MS);
|
delay(STARTSEQ_TON_3_LENGTH_MS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,13 +567,12 @@ void failSequence(void){
|
||||||
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
|
||||||
Serial.println("** Interrupt service routine started: false_start_ISR **");
|
//Serial.println("** Interrupt service routine started: false_start_ISR **");
|
||||||
runner_start_time = millis();
|
runner_start_time = millis();
|
||||||
if((timer_state == TIMER_STARTED) & (timer_new_state == TIMER_RUNNING)){
|
if(millis() - start_time <= 0){
|
||||||
timer_new_state = TIMER_FAIL;
|
false_start = true;
|
||||||
noTone(PIEZO_PIN);
|
noTone(PIEZO_PIN);
|
||||||
Serial.println("** Interrupt service routine detected false_start. Set new state to TIMER_FAIL **");
|
Serial.println("** Interrupt service routine detected false_start. Set new state to TIMER_FAIL **");
|
||||||
update_statemessage(timer_new_state);
|
|
||||||
detachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN));
|
detachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN));
|
||||||
} else {
|
} else {
|
||||||
if((timer_state == TIMER_RUNNING) | (timer_new_state == TIMER_RUNNING) ){
|
if((timer_state == TIMER_RUNNING) | (timer_new_state == TIMER_RUNNING) ){
|
||||||
|
@ -653,12 +582,5 @@ void false_start_isr(void){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait(unsigned long ms){
|
|
||||||
unsigned long current = 0;
|
|
||||||
unsigned long started = millis();
|
|
||||||
do{
|
|
||||||
current = millis()-started;
|
|
||||||
}while(current < ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
19477
speedclock_bottom/fonts.h
Normal file
19477
speedclock_bottom/fonts.h
Normal file
File diff suppressed because it is too large
Load diff
95
speedclock_bottom/pitch.h
Normal file
95
speedclock_bottom/pitch.h
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*************************************************
|
||||||
|
* Public Constants
|
||||||
|
*************************************************/
|
||||||
|
|
||||||
|
#define NOTE_B0 31
|
||||||
|
#define NOTE_C1 33
|
||||||
|
#define NOTE_CS1 35
|
||||||
|
#define NOTE_D1 37
|
||||||
|
#define NOTE_DS1 39
|
||||||
|
#define NOTE_E1 41
|
||||||
|
#define NOTE_F1 44
|
||||||
|
#define NOTE_FS1 46
|
||||||
|
#define NOTE_G1 49
|
||||||
|
#define NOTE_GS1 52
|
||||||
|
#define NOTE_A1 55
|
||||||
|
#define NOTE_AS1 58
|
||||||
|
#define NOTE_B1 62
|
||||||
|
#define NOTE_C2 65
|
||||||
|
#define NOTE_CS2 69
|
||||||
|
#define NOTE_D2 73
|
||||||
|
#define NOTE_DS2 78
|
||||||
|
#define NOTE_E2 82
|
||||||
|
#define NOTE_F2 87
|
||||||
|
#define NOTE_FS2 93
|
||||||
|
#define NOTE_G2 98
|
||||||
|
#define NOTE_GS2 104
|
||||||
|
#define NOTE_A2 110
|
||||||
|
#define NOTE_AS2 117
|
||||||
|
#define NOTE_B2 123
|
||||||
|
#define NOTE_C3 131
|
||||||
|
#define NOTE_CS3 139
|
||||||
|
#define NOTE_D3 147
|
||||||
|
#define NOTE_DS3 156
|
||||||
|
#define NOTE_E3 165
|
||||||
|
#define NOTE_F3 175
|
||||||
|
#define NOTE_FS3 185
|
||||||
|
#define NOTE_G3 196
|
||||||
|
#define NOTE_GS3 208
|
||||||
|
#define NOTE_A3 220
|
||||||
|
#define NOTE_AS3 233
|
||||||
|
#define NOTE_B3 247
|
||||||
|
#define NOTE_C4 262
|
||||||
|
#define NOTE_CS4 277
|
||||||
|
#define NOTE_D4 294
|
||||||
|
#define NOTE_DS4 311
|
||||||
|
#define NOTE_E4 330
|
||||||
|
#define NOTE_F4 349
|
||||||
|
#define NOTE_FS4 370
|
||||||
|
#define NOTE_G4 392
|
||||||
|
#define NOTE_GS4 415
|
||||||
|
#define NOTE_A4 440
|
||||||
|
#define NOTE_AS4 466
|
||||||
|
#define NOTE_B4 494
|
||||||
|
#define NOTE_C5 523
|
||||||
|
#define NOTE_CS5 554
|
||||||
|
#define NOTE_D5 587
|
||||||
|
#define NOTE_DS5 622
|
||||||
|
#define NOTE_E5 659
|
||||||
|
#define NOTE_F5 698
|
||||||
|
#define NOTE_FS5 740
|
||||||
|
#define NOTE_G5 784
|
||||||
|
#define NOTE_GS5 831
|
||||||
|
#define NOTE_A5 880
|
||||||
|
#define NOTE_AS5 932
|
||||||
|
#define NOTE_B5 988
|
||||||
|
#define NOTE_C6 1047
|
||||||
|
#define NOTE_CS6 1109
|
||||||
|
#define NOTE_D6 1175
|
||||||
|
#define NOTE_DS6 1245
|
||||||
|
#define NOTE_E6 1319
|
||||||
|
#define NOTE_F6 1397
|
||||||
|
#define NOTE_FS6 1480
|
||||||
|
#define NOTE_G6 1568
|
||||||
|
#define NOTE_GS6 1661
|
||||||
|
#define NOTE_A6 1760
|
||||||
|
#define NOTE_AS6 1865
|
||||||
|
#define NOTE_B6 1976
|
||||||
|
#define NOTE_C7 2093
|
||||||
|
#define NOTE_CS7 2217
|
||||||
|
#define NOTE_D7 2349
|
||||||
|
#define NOTE_DS7 2489
|
||||||
|
#define NOTE_E7 2637
|
||||||
|
#define NOTE_F7 2794
|
||||||
|
#define NOTE_FS7 2960
|
||||||
|
#define NOTE_G7 3136
|
||||||
|
#define NOTE_GS7 3322
|
||||||
|
#define NOTE_A7 3520
|
||||||
|
#define NOTE_AS7 3729
|
||||||
|
#define NOTE_B7 3951
|
||||||
|
#define NOTE_C8 4186
|
||||||
|
#define NOTE_CS8 4435
|
||||||
|
#define NOTE_D8 4699
|
||||||
|
#define NOTE_DS8 4978
|
||||||
|
|
||||||
|
|
103
speedclock_bottom/speedclock.h
Normal file
103
speedclock_bottom/speedclock.h
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
|
||||||
|
#ifndef speedclock_H
|
||||||
|
#define speedclock_H
|
||||||
|
|
||||||
|
//-------------- defines for the radio devices NRF24 ---------------------------------------------------------
|
||||||
|
|
||||||
|
#define STATION_SEL0 9 // this 9 for Nano
|
||||||
|
#define STATION_SEL1 10 // this 10 for Nano
|
||||||
|
|
||||||
|
typedef enum {BASESTATION = 0, TOPSTATION} radio_type_e;
|
||||||
|
#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_PA_LEVEL RF24_PA_LOW // sending power level RF24_PA_LOW, ????
|
||||||
|
|
||||||
|
//--------------- defines for the I2C
|
||||||
|
//#define SCL A5 // I2C clock pin
|
||||||
|
//#define SDA A4 // I2C data pin
|
||||||
|
|
||||||
|
//--------------- define the structure and type of data that sender and receiver will exchange ----------------
|
||||||
|
|
||||||
|
typedef struct transcv_struct{
|
||||||
|
unsigned long topstationtime; // the top station sends its time (millis()) continously to the base station
|
||||||
|
unsigned long topbuttonpressedtime; // the top station sends the time in millis() when the button was pressed - this is already the calculated time
|
||||||
|
}transcv_s;
|
||||||
|
|
||||||
|
|
||||||
|
#define STOPBUTTON_IN 2 // this is the input for the button
|
||||||
|
#define STOPBUTTON_PRESSED LOW // this the signal level the top button will be at as soon as pressed
|
||||||
|
#define MIN_DELAY_BETWEEN_PRESSED_MS 1000 // this defines the time in milliseconds before the button is expected to be pressed again. We do this to avaoid keybouncing
|
||||||
|
#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 STARTBUTTON_IN 4 // start button
|
||||||
|
#define STARTBUTTON_PRESSED LOW
|
||||||
|
#define CANCELBUTTON_IN 2 // chancle button
|
||||||
|
#define CANCELBUTTON_PRESSED LOW
|
||||||
|
#define FAILSTARTBUTTON_IN 3 // fail start button
|
||||||
|
#define FAILSTARTBUTTON_PRESSED LOW
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
// READY_LED, WARN_LED, RUN_LED, FAIL_LED
|
||||||
|
const float LEDStates[][3] =
|
||||||
|
{
|
||||||
|
[TIMER_INIT] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_OFF},
|
||||||
|
[TIMER_NOCONNECTION] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_ON},
|
||||||
|
[TIMER_IDLE] = {READY_LED_ON, RUN_LED_OFF, FAIL_LED_OFF},
|
||||||
|
[TIMER_READY] = {READY_LED_ON, RUN_LED_OFF, FAIL_LED_OFF},
|
||||||
|
[TIMER_STARTED] = {READY_LED_ON, RUN_LED_ON, FAIL_LED_OFF},
|
||||||
|
[TIMER_RUNNING] = {READY_LED_OFF, RUN_LED_ON, FAIL_LED_OFF},
|
||||||
|
[TIMER_CANCELLED] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_ON},
|
||||||
|
[TIMER_STOPPED] = {READY_LED_ON, RUN_LED_ON, FAIL_LED_OFF},
|
||||||
|
[TIMER_TIMEDOUT] = {READY_LED_OFF, RUN_LED_ON, FAIL_LED_ON},
|
||||||
|
[TIMER_FAIL] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_ON}
|
||||||
|
};
|
||||||
|
|
||||||
|
#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 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 STARTSEQ_LENGTH_MS 3100 // the length of the start sequence from the time the button was pressed ... includes the 3 tones
|
||||||
|
#define STARTSEQ_STARTPAUSE_MS 1000
|
||||||
|
#define STARTSEQ_TONEPAUSE_MS 1000
|
||||||
|
#define STARTSEQ_TON_1_2_LENGTH_MS 200
|
||||||
|
#define STARTSEQ_TON_1_2_FREQUENCY NOTE_G4
|
||||||
|
#define STARTSEQ_TON_3_LENGTH_MS 100
|
||||||
|
#define STARTSEQ_TON_3_FREQUENCY NOTE_C6
|
||||||
|
|
||||||
|
#define FAILSEQ_TONEPAUSE_MS 400
|
||||||
|
#define FAILSEQ_TON_LENGTH_MS 300
|
||||||
|
#define FAILSEQ_TON_FREQUENCY NOTE_G1
|
||||||
|
|
||||||
|
#define TIMER_MAX_TIME 99999
|
||||||
|
#define TIMER_TIMEOUT 20000
|
||||||
|
|
||||||
|
//--------------------------------------- function declarations ----------------------------------------------
|
||||||
|
void receive_values(void);
|
||||||
|
void false_start_isr(void);
|
||||||
|
void update_screen(timer_state_e state);
|
||||||
|
void set_state_LEDs(timer_state_e state, boolean warn);
|
||||||
|
void startSequence(void);
|
||||||
|
void update_statemessage(timer_state_e timer_state);
|
||||||
|
void failSequence(void);
|
||||||
|
void wait(unsigned long ms);
|
||||||
|
#endif
|
515
speedclock_bottom/speedclock_bottom.ino
Normal file
515
speedclock_bottom/speedclock_bottom.ino
Normal file
|
@ -0,0 +1,515 @@
|
||||||
|
|
||||||
|
#include <Wire.h>
|
||||||
|
#include "SSD1306Ascii.h"
|
||||||
|
#include "SSD1306AsciiWire.h"
|
||||||
|
#include "RF24.h"
|
||||||
|
#include "speedclock.h"
|
||||||
|
#include "pitch.h"
|
||||||
|
|
||||||
|
// internal defines for the OLED display ...
|
||||||
|
SSD1306AsciiWire display;
|
||||||
|
/****************** User Config for NRF24***************************/
|
||||||
|
/*** Set this radio as radio number RADIO0 or RADIO1 ***/
|
||||||
|
radio_type_e stationNumber = BASESTATION; //---> TOPSTATION has the button connected, BASESTATION is the default ...
|
||||||
|
|
||||||
|
/* Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 7 & 8 */
|
||||||
|
RF24 radio(RF24_CNS,RF24_CE);
|
||||||
|
/**********************************************************/
|
||||||
|
byte addresses[][12] = {"top_station","basestation"}; // Radio pipe addresses for the 2 nodes to communicate.
|
||||||
|
|
||||||
|
uint8_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 mean_time_offset = 0; // mean value for the offset
|
||||||
|
signed long current_time_offset = 0; // current offset ...
|
||||||
|
signed long running_time_offset = 0; // offset that will be used for this run ...
|
||||||
|
unsigned long start_time = 0; // if the timer is running this is that start time ...
|
||||||
|
unsigned long runner_start_time = 0; // this is the time the runner left the pad - so the status of the falsetstart pin goes to high again - but this is OK and a real start
|
||||||
|
unsigned long run_time = 0; // if the timer is running this is that start time ...
|
||||||
|
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
|
||||||
|
boolean connection_available = false; // if there were no data for longer then CONN_TIMEOUT the connection will be flaged as lost ...
|
||||||
|
uint8_t failed_offsets = MAX_ALLOWED_FAILED_OFFSETS; // number of offset values that did not fullfill the MAX_DIFFERENCE_OFFSET_MS criterion
|
||||||
|
|
||||||
|
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_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
|
||||||
|
|
||||||
|
transcv_s radio_data;
|
||||||
|
|
||||||
|
void setup(){
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
|
||||||
|
// this is the top button - will be pressed by the speed climber as soon she/he reaches the top ...
|
||||||
|
pinMode(STOPBUTTON_IN, INPUT_PULLUP);
|
||||||
|
pinMode(STARTBUTTON_IN, INPUT_PULLUP);
|
||||||
|
pinMode(CANCELBUTTON_IN, INPUT_PULLUP);
|
||||||
|
pinMode(FAILSTARTBUTTON_IN, INPUT_PULLUP);
|
||||||
|
|
||||||
|
pinMode(WARN_LED, OUTPUT);
|
||||||
|
pinMode(FAIL_LED, OUTPUT);
|
||||||
|
pinMode(READY_LED, OUTPUT);
|
||||||
|
|
||||||
|
// Setup and configure the NRF radio
|
||||||
|
// radio setup ...
|
||||||
|
radio.begin();
|
||||||
|
radio.setRetries(15, 15); //the first is the time between reties in multiple of 250ms, the second is the numer of attempts
|
||||||
|
radio.openWritingPipe(addresses[0]);
|
||||||
|
radio.openReadingPipe(1,addresses[1]);
|
||||||
|
radio.startListening();
|
||||||
|
radio_data.topstationtime = millis(); // set the current milli second count
|
||||||
|
radio_data.topbuttonpressedtime = 0; // set the time the button was pressed last time to 0
|
||||||
|
|
||||||
|
//initialise Wire and OLED
|
||||||
|
Wire.begin();
|
||||||
|
Wire.setClock(400000L);
|
||||||
|
display.begin(&Adafruit128x64, DISPLAY_I2C_ADDRESS);
|
||||||
|
display.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop(void) {
|
||||||
|
|
||||||
|
|
||||||
|
/****************** Code for the TOPSTATION is here - the stop button is connected to the top station ***************************/
|
||||||
|
|
||||||
|
if ( stationNumber == BASESTATION ) {
|
||||||
|
|
||||||
|
// receive data from top_station, calculate offset and set 'last connection' time stamp
|
||||||
|
receive_values();
|
||||||
|
|
||||||
|
// update the OLED screen
|
||||||
|
update_screen(timer_new_state);
|
||||||
|
|
||||||
|
// set state to new_state
|
||||||
|
timer_state = timer_new_state;
|
||||||
|
|
||||||
|
// set LEDs
|
||||||
|
set_state_LEDs(timer_state, warn_during_run );
|
||||||
|
|
||||||
|
switch(timer_state){
|
||||||
|
case TIMER_NOCONNECTION:
|
||||||
|
// as long as there is no connection to TOP_STATION we will end up here
|
||||||
|
if(connection_available == true){
|
||||||
|
timer_new_state = TIMER_INIT;
|
||||||
|
}
|
||||||
|
case TIMER_INIT:
|
||||||
|
// init the system offset ...
|
||||||
|
if(connection_available == false){
|
||||||
|
// if the connection was lost ... switch to noconnection state
|
||||||
|
timer_new_state = TIMER_NOCONNECTION;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// if the offset is claculated switch to IDLE mode ...
|
||||||
|
if(counter_time_offset > REQUIRED_NUMBER_MEANVALS){
|
||||||
|
// check if offset is OK - if not .. set state back to INIT
|
||||||
|
timer_new_state = TIMER_IDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_IDLE:
|
||||||
|
warn_during_run = false;
|
||||||
|
if(connection_available == false){
|
||||||
|
// if the connection was lost ... switch to noconnection state
|
||||||
|
timer_new_state = TIMER_NOCONNECTION;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if(counter_time_offset < REQUIRED_NUMBER_MEANVALS){
|
||||||
|
// check if offset is OK - if not .. set state back to INIT
|
||||||
|
timer_new_state = TIMER_INIT;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// check if the FALSESTATE button is pressed - somebody is ready to run ...
|
||||||
|
if(digitalRead(FAILSTARTBUTTON_IN) == FAILSTARTBUTTON_PRESSED){
|
||||||
|
//wait a few milliseconds to prevent keybouncing - this is a very simplistic method here
|
||||||
|
delay(10);
|
||||||
|
//read again and check if still active ...
|
||||||
|
if(digitalRead(FAILSTARTBUTTON_IN) == FAILSTARTBUTTON_PRESSED){
|
||||||
|
timer_new_state = TIMER_READY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_READY:
|
||||||
|
if(connection_available == false){
|
||||||
|
// if the connection was lost ... switch to noconnection state
|
||||||
|
timer_new_state = TIMER_NOCONNECTION;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
if(digitalRead(FAILSTARTBUTTON_IN) == FAILSTARTBUTTON_PRESSED){
|
||||||
|
// check if the start button was pressed ... there is at least still someone waiting for the run .
|
||||||
|
if(digitalRead(STARTBUTTON_IN) == STARTBUTTON_PRESSED){
|
||||||
|
// now enable the interrupt for the FALSESTART button
|
||||||
|
attachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN), false_start_isr, CHANGE);
|
||||||
|
timer_new_state = TIMER_STARTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
timer_new_state = TIMER_IDLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_STARTED:
|
||||||
|
//initialize the start countdown
|
||||||
|
timer_new_state = TIMER_RUNNING;
|
||||||
|
startSequence();
|
||||||
|
break;
|
||||||
|
case TIMER_RUNNING:
|
||||||
|
noTone(PIEZO_PIN);
|
||||||
|
if(counter_time_offset < REQUIRED_NUMBER_MEANVALS){
|
||||||
|
// check if offset is still OK - if not .. set state to TIMER_RUNNING
|
||||||
|
warn_during_run = true;
|
||||||
|
}
|
||||||
|
if(millis() - start_time > TIMER_TIMEOUT){
|
||||||
|
timer_new_state = TIMER_TIMEDOUT;
|
||||||
|
}
|
||||||
|
if(digitalRead(CANCELBUTTON_IN) == CANCELBUTTON_PRESSED){
|
||||||
|
timer_new_state = TIMER_CANCELLED;
|
||||||
|
}
|
||||||
|
Serial.println(radio_data.topbuttonpressedtime);
|
||||||
|
if(radio_data.topbuttonpressedtime > running_time_offset){
|
||||||
|
if((radio_data.topbuttonpressedtime - running_time_offset) > start_time){
|
||||||
|
run_time = (radio_data.topbuttonpressedtime - running_time_offset) - start_time;
|
||||||
|
timer_new_state = TIMER_STOPPED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_STOPPED:
|
||||||
|
//calculate the run_time and switch to WAIT
|
||||||
|
delay(10);
|
||||||
|
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
|
||||||
|
timer_new_state = TIMER_WAIT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_FAIL:
|
||||||
|
//fail start case ....
|
||||||
|
failSequence();
|
||||||
|
run_time = 99999;
|
||||||
|
delay(10);
|
||||||
|
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
|
||||||
|
timer_new_state = TIMER_WAIT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_CANCELLED:
|
||||||
|
// what to do in chancel mode ?
|
||||||
|
run_time = 99999;
|
||||||
|
delay(10);
|
||||||
|
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
|
||||||
|
timer_new_state = TIMER_WAIT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_TIMEDOUT:
|
||||||
|
// time out
|
||||||
|
run_time = millis() - start_time;
|
||||||
|
delay(10);
|
||||||
|
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
|
||||||
|
timer_new_state = TIMER_WAIT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_WAIT:
|
||||||
|
// disable interrupt if not already done
|
||||||
|
detachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN));
|
||||||
|
// wait until the chancel button was pressed to go ahead
|
||||||
|
if(digitalRead(CANCELBUTTON_IN) == CANCELBUTTON_PRESSED){
|
||||||
|
timer_new_state = TIMER_IDLE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//####################### HELPER FUNCTIONS ###########################
|
||||||
|
void receive_values(void){
|
||||||
|
// check if radio data is available - if so read the data
|
||||||
|
if( radio.available()){
|
||||||
|
// read data from TOP_STATION ...
|
||||||
|
while( radio.available()){ // Read all available payloads
|
||||||
|
radio.read( &radio_data, sizeof(radio_data) ); // Read the data the TOPSTATION sent
|
||||||
|
}
|
||||||
|
connection_last_established_at_ms = millis();
|
||||||
|
connection_available = true;
|
||||||
|
current_time_offset = radio_data.topstationtime - millis(); // the offset between TOP_STATION and BASESTATION
|
||||||
|
Serial.print("Current time on host in millis:");
|
||||||
|
Serial.print(millis());
|
||||||
|
Serial.print(F(" Current time on client in millis: "));
|
||||||
|
Serial.println(radio_data.topstationtime);
|
||||||
|
Serial.print("Offset is: ");
|
||||||
|
Serial.println(current_time_offset);
|
||||||
|
Serial.print(F(" Button was pressed last time on client in millis: "));
|
||||||
|
Serial.println(radio_data.topbuttonpressedtime);
|
||||||
|
|
||||||
|
// offset calculation ... only needed if the variation is bigger than allowed or not enough values available already ...
|
||||||
|
if(counter_time_offset == 0){
|
||||||
|
// this is the initial setup to start with something - in this case the last offset value that was received
|
||||||
|
mean_time_offset = current_time_offset;
|
||||||
|
}
|
||||||
|
// check current offset of the TOP_STATIOn and the BASESTATION if more than allowed ...
|
||||||
|
if(abs(current_time_offset - mean_time_offset) < MAX_DIFFERENCE_OFFSET_MS){
|
||||||
|
// if the current value is in range - decrease the fail counter by 1 if it was not zero already
|
||||||
|
if(failed_offsets > 0){
|
||||||
|
Serial.println("INFO: The last received TOPSTATION offset time stamp was again in range. Decrease internal fail counter");
|
||||||
|
failed_offsets--;
|
||||||
|
}
|
||||||
|
// the offset is in range - check if we have already enough values of if we need to add more ...
|
||||||
|
if(counter_time_offset <= REQUIRED_NUMBER_MEANVALS){
|
||||||
|
//add the next value to meanvalue calculation ...
|
||||||
|
sum_time_offset = sum_time_offset + current_time_offset;
|
||||||
|
counter_time_offset++;
|
||||||
|
mean_time_offset = sum_time_offset/counter_time_offset;
|
||||||
|
Serial.print(F("Offset calulation. We have "));
|
||||||
|
Serial.print(counter_time_offset);
|
||||||
|
Serial.print(F("/"));
|
||||||
|
Serial.print(REQUIRED_NUMBER_MEANVALS);
|
||||||
|
Serial.print(F(" values. Mean offset value based on that is: "));
|
||||||
|
Serial.println(mean_time_offset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the current offset is out of range ...
|
||||||
|
// if the values before also already failed the criterion but the max allowed number of such fails is not reached ... just increase the counter.
|
||||||
|
if(failed_offsets < MAX_ALLOWED_FAILED_OFFSETS){
|
||||||
|
Serial.println("WARNING: The last received TOPSTATION offset time stamp was out of range. Increase internal fail counter");
|
||||||
|
failed_offsets++;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// if the values before also already failed the criterion AND the max allowed number of such fails is reached ... we need to restart the mean calculation and set the timer to unready state ...
|
||||||
|
Serial.print(F("ERROR: TopStation and BaseStation are out off sync. Offset calculation will be restarted. Last "));
|
||||||
|
Serial.print(MAX_ALLOWED_FAILED_OFFSETS);
|
||||||
|
//offset "));
|
||||||
|
Serial.print(current_time_offset);
|
||||||
|
Serial.print("is to far from the mean offset ");
|
||||||
|
Serial.println( abs(current_time_offset - mean_time_offset) );
|
||||||
|
Serial.print(F(" is more than the allowed: "));
|
||||||
|
Serial.print(MAX_DIFFERENCE_OFFSET_MS);
|
||||||
|
Serial.print(F(" compared to the mean offset: "));
|
||||||
|
Serial.println(mean_time_offset);
|
||||||
|
counter_time_offset = 0;
|
||||||
|
sum_time_offset = 0;
|
||||||
|
mean_time_offset = 0;
|
||||||
|
failed_offsets = 0;
|
||||||
|
} // out of range counter exceeds maximum value
|
||||||
|
} // time offset of TOPSTATION out of range
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// 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){
|
||||||
|
connection_available = false;
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
}// receive values
|
||||||
|
|
||||||
|
|
||||||
|
void update_screen(timer_state_e state){
|
||||||
|
bool scr_update = true;
|
||||||
|
int ypos = 64-42/2;
|
||||||
|
String header = "no state";
|
||||||
|
String content = "";
|
||||||
|
String footer = "";
|
||||||
|
|
||||||
|
char header_to_char[50];
|
||||||
|
char content_to_char[50];
|
||||||
|
char footer_to_char[50];
|
||||||
|
|
||||||
|
float curr_time_local = 0.0;
|
||||||
|
|
||||||
|
switch(state){
|
||||||
|
case TIMER_NOCONNECTION:
|
||||||
|
header = "No connection";
|
||||||
|
content = "Waiting..";
|
||||||
|
footer = "Waiting for TOPSTATION";
|
||||||
|
break;
|
||||||
|
case TIMER_INIT:
|
||||||
|
header = "Init";
|
||||||
|
content = "...";
|
||||||
|
footer = "please wait";
|
||||||
|
break;
|
||||||
|
case TIMER_IDLE:
|
||||||
|
header = "Idle!";
|
||||||
|
content = "00.00 sec";
|
||||||
|
footer = "Waiting for climber";
|
||||||
|
break;
|
||||||
|
case TIMER_READY:
|
||||||
|
header = "Ready!";
|
||||||
|
content = "00.00 sec";
|
||||||
|
footer = "Waiting for start";
|
||||||
|
break;
|
||||||
|
case TIMER_STARTED:
|
||||||
|
header = "Starting ...";
|
||||||
|
content = "00.00 sec";
|
||||||
|
footer = "...";
|
||||||
|
break;
|
||||||
|
case TIMER_RUNNING:
|
||||||
|
header = "Running ...";
|
||||||
|
curr_time_local = (millis() - start_time)/1000.000;
|
||||||
|
content = curr_time_local;
|
||||||
|
content += " sec";
|
||||||
|
curr_time_local = (runner_start_time - start_time)/1000.000;
|
||||||
|
footer = "Reaction time: ";
|
||||||
|
footer += curr_time_local;
|
||||||
|
footer += " sec";
|
||||||
|
break;
|
||||||
|
case TIMER_CANCELLED:
|
||||||
|
header = "Cancelled!";
|
||||||
|
break;
|
||||||
|
case TIMER_STOPPED:
|
||||||
|
header = "Stopped!";
|
||||||
|
curr_time_local = run_time/1000.000;
|
||||||
|
content = curr_time_local;
|
||||||
|
content += " sec";
|
||||||
|
curr_time_local = (runner_start_time - start_time)/1000.000;
|
||||||
|
footer = "Reaction time: ";
|
||||||
|
footer += curr_time_local;
|
||||||
|
footer += " sec";
|
||||||
|
break;
|
||||||
|
case TIMER_TIMEDOUT:
|
||||||
|
header = "Timed out!";
|
||||||
|
content = "Invalid!";
|
||||||
|
break;
|
||||||
|
case TIMER_FAIL:
|
||||||
|
header = "False start!";
|
||||||
|
curr_time_local = (start_time - runner_start_time)/1000.000;
|
||||||
|
content = curr_time_local;
|
||||||
|
footer = "seconds too early";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
scr_update = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(scr_update == true){
|
||||||
|
if(timer_new_state != timer_state){
|
||||||
|
display.clear(0,200,0,0);
|
||||||
|
display.clear(0,200,3,5);
|
||||||
|
display.clear(0,200,7,7);
|
||||||
|
}
|
||||||
|
//snprintf( string_to_char, sizeof(string_to_char),"%s", header);
|
||||||
|
header.toCharArray(header_to_char, sizeof(header_to_char));
|
||||||
|
content.toCharArray(content_to_char, sizeof(content_to_char));
|
||||||
|
footer.toCharArray(footer_to_char, sizeof(footer_to_char));
|
||||||
|
//Serial.print("DISPLAY: ");
|
||||||
|
//Serial.println(string_to_char);
|
||||||
|
|
||||||
|
display.setFont(System5x7);
|
||||||
|
display.set1X();
|
||||||
|
int xpos = (128 - (display.strWidth(header_to_char)))/2 - 10;
|
||||||
|
display.home();
|
||||||
|
display.setLetterSpacing(1);
|
||||||
|
display.setCursor(64 - (display.strWidth(header_to_char) / 2), 0);
|
||||||
|
display.print(header_to_char);
|
||||||
|
display.setCursor(1,0);
|
||||||
|
//check if there is a connection to the topstation
|
||||||
|
if(connection_available != true){
|
||||||
|
//if not remove the "Y"
|
||||||
|
display.print(" ");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//if print the "Y"
|
||||||
|
display.print("Y");
|
||||||
|
}
|
||||||
|
display.setCursor(0,1);
|
||||||
|
display.setLetterSpacing(0);
|
||||||
|
display.print("----------------------------");
|
||||||
|
|
||||||
|
//end of the Header
|
||||||
|
//display.setLetterSpacing(1);
|
||||||
|
display.set2X();
|
||||||
|
display.setCursor(64 - (display.strWidth(content_to_char) / 2), 3);
|
||||||
|
display.print(content_to_char);
|
||||||
|
//end of the Content
|
||||||
|
display.set1X();
|
||||||
|
display.setCursor(0,6);
|
||||||
|
display.setLetterSpacing(0);
|
||||||
|
display.print("----------------------------");
|
||||||
|
display.setCursor(64 - (display.strWidth(footer_to_char) / 2), 7);
|
||||||
|
display.print(footer_to_char);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_state_LEDs(timer_state_e state, boolean warn){
|
||||||
|
// set the LEDS corresponding to the state of the timer ... as long as the system is not waiting for input ...
|
||||||
|
if(TIMER_WAIT != state){
|
||||||
|
digitalWrite(READY_LED, LEDStates[state][0]);
|
||||||
|
digitalWrite(RUN_LED, LEDStates[state][1]);
|
||||||
|
digitalWrite(FAIL_LED, LEDStates[state][2]);
|
||||||
|
|
||||||
|
if(warn == true){
|
||||||
|
digitalWrite(WARN_LED, WARN_LED_ON);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
digitalWrite(WARN_LED, WARN_LED_OFF);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startSequence(void){
|
||||||
|
// set the startime - this is the current time plus the length of this sequence
|
||||||
|
running_time_offset = mean_time_offset;
|
||||||
|
start_time = millis() + STARTSEQ_LENGTH_MS;
|
||||||
|
Serial.print("Start time is: ");
|
||||||
|
Serial.println(start_time);
|
||||||
|
// this is sequence of usually three tones after a wait time 1sec , in between the tones there is also a delay of 1 sec. Each tone is 200ms seconds long, except the last
|
||||||
|
if(timer_new_state == TIMER_RUNNING){
|
||||||
|
wait(STARTSEQ_STARTPAUSE_MS);
|
||||||
|
}
|
||||||
|
// first tone
|
||||||
|
if(timer_new_state == TIMER_RUNNING){
|
||||||
|
tone(PIEZO_PIN, STARTSEQ_TON_1_2_FREQUENCY,STARTSEQ_TON_1_2_LENGTH_MS );
|
||||||
|
wait(STARTSEQ_TONEPAUSE_MS);
|
||||||
|
}
|
||||||
|
//second tone
|
||||||
|
if(timer_new_state == TIMER_RUNNING){
|
||||||
|
tone(PIEZO_PIN, STARTSEQ_TON_1_2_FREQUENCY,STARTSEQ_TON_1_2_LENGTH_MS );
|
||||||
|
wait(STARTSEQ_TONEPAUSE_MS);
|
||||||
|
}
|
||||||
|
//third tone
|
||||||
|
if(timer_new_state == TIMER_RUNNING){
|
||||||
|
tone(PIEZO_PIN, STARTSEQ_TON_3_FREQUENCY,STARTSEQ_TON_3_LENGTH_MS );
|
||||||
|
wait(STARTSEQ_TON_3_LENGTH_MS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void failSequence(void){
|
||||||
|
// first tone
|
||||||
|
tone(PIEZO_PIN, FAILSEQ_TON_FREQUENCY,FAILSEQ_TON_LENGTH_MS );
|
||||||
|
delay(FAILSEQ_TONEPAUSE_MS);
|
||||||
|
//second tone
|
||||||
|
tone(PIEZO_PIN, FAILSEQ_TON_FREQUENCY,FAILSEQ_TON_LENGTH_MS );
|
||||||
|
delay(FAILSEQ_TONEPAUSE_MS);
|
||||||
|
noTone(PIEZO_PIN);
|
||||||
|
}
|
||||||
|
|
||||||
|
void false_start_isr(void){
|
||||||
|
// this is the interrupt routine for the FALSESTART button
|
||||||
|
// this will save the time when the runner is really started
|
||||||
|
Serial.println("** Interrupt service routine started: false_start_ISR **");
|
||||||
|
runner_start_time = millis();
|
||||||
|
if((timer_state == TIMER_STARTED) & (timer_new_state == TIMER_RUNNING)){
|
||||||
|
timer_new_state = TIMER_FAIL;
|
||||||
|
noTone(PIEZO_PIN);
|
||||||
|
Serial.println("** Interrupt service routine detected false_start. Set new state to TIMER_FAIL **");
|
||||||
|
detachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN));
|
||||||
|
} else {
|
||||||
|
if((timer_state == TIMER_RUNNING) | (timer_new_state == TIMER_RUNNING) ){
|
||||||
|
// disable this interrupt;
|
||||||
|
detachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait(unsigned long ms){
|
||||||
|
unsigned long current = 0;
|
||||||
|
unsigned long started = millis();
|
||||||
|
do{
|
||||||
|
current = millis()-started;
|
||||||
|
}while(current < ms);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Reference in a new issue