This repository has been archived on 2022-08-16. You can view files and clone it, but cannot push or open issues or pull requests.
speedclock/speedclock.ino

665 lines
25 KiB
Arduino
Raw Normal View History

2018-07-06 23:56:33 +02:00
#include <Arduino.h>
//#include <U8g2lib.h>
2018-07-06 23:56:33 +02:00
//#include "fonts.h"
2018-07-07 19:27:47 +02:00
#include <Wire.h>
#include "SSD1306Ascii.h"
#include "SSD1306AsciiWire.h"
2018-07-06 23:56:33 +02:00
#include "RF24.h"
#include "speedclock.h"
2018-07-07 18:47:18 +02:00
#include "pitch.h"
2018-07-06 23:56:33 +02:00
// internal defines for the OLED display ...
2018-07-07 19:27:47 +02:00
SSD1306AsciiWire display;
2018-07-06 23:56:33 +02:00
/****************** User Config for NRF24***************************/
/*** Set this radio as radio number RADIO0 or RADIO1 ***/
2018-07-07 18:47:18 +02:00
radio_type_e stationNumber = BASESTATION; //---> TOPSTATION has the button connected, BASESTATION is the default ...
uint8_t radio_sel0, radio_sel1; // code of type of station
2018-07-06 23:56:33 +02:00
/* 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.
unsigned long counter_time_offset = 0; // number of used values for the mean value calculation
2018-07-08 01:34:57 +02:00
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 ...
2018-07-06 23:56:33 +02:00
unsigned long start_time = 0; // if the timer is running this is that start time ...
2018-07-07 18:47:18 +02:00
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 ...
2018-07-06 23:56:33 +02:00
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
2018-07-06 23:56:33 +02:00
2018-07-08 01:34:57 +02:00
boolean topbuttonwaspressed = false;
2018-07-07 23:50:28 +02:00
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
2018-07-06 23:56:33 +02:00
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 ...
2018-07-07 18:47:18 +02:00
pinMode(STOPBUTTON_IN, INPUT_PULLUP);
pinMode(STARTBUTTON_IN, INPUT_PULLUP);
pinMode(CANCELBUTTON_IN, INPUT_PULLUP);
pinMode(FAILSTARTBUTTON_IN, INPUT_PULLUP);
2018-07-06 23:56:33 +02:00
pinMode(WARN_LED, OUTPUT);
pinMode(FAIL_LED, OUTPUT);
pinMode(READY_LED, OUTPUT);
// Get the station type (base or top) as set by the station select pin - BASESTATION is default
2018-07-07 18:47:18 +02:00
pinMode(STATION_SEL0, INPUT);
pinMode(STATION_SEL0, INPUT);
radio_sel0 = digitalRead(STATION_SEL0);
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)){
2018-07-06 23:56:33 +02:00
stationNumber = TOPSTATION;
2018-07-08 01:34:57 +02:00
Serial.println(F("The level of the station select pin makes the current node set to the TOPSTATION."));
2018-07-06 23:56:33 +02:00
}
else{
2018-07-08 01:34:57 +02:00
Serial.println(F("The level of the station select pin makes the current node set to the BASESTATION"));
2018-07-06 23:56:33 +02:00
}
// 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
if(stationNumber == TOPSTATION){
// Set the PA Level of the sendin TOP_STATION
radio.setPALevel(RF24_PA_LEVEL);
2018-07-06 23:56:33 +02:00
radio.openWritingPipe(addresses[1]); // Both radios listen on the same pipes by default, but opposite addresses
radio.openReadingPipe(1,addresses[0]); // Open a reading pipe on address 0, pipe 1
radio.stopListening(); // top station will never receive data.
}else{
radio.openWritingPipe(addresses[0]);
radio.openReadingPipe(1,addresses[1]);
2018-07-08 01:34:57 +02:00
radio.startListening();
}
2018-07-06 23:56:33 +02:00
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
2018-07-07 19:27:47 +02:00
//initialise Wire and OLED
Wire.begin();
Wire.setClock(400000L);
2018-07-07 19:27:47 +02:00
display.begin(&Adafruit128x64, DISPLAY_I2C_ADDRESS);
display.clear();
2018-07-06 23:56:33 +02:00
}
void loop(void) {
/****************** 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.
2018-07-08 01:34:57 +02:00
2018-07-06 23:56:33 +02:00
// check for pressed button ...
2018-07-08 01:34:57 +02:00
if(topbuttonwaspressed == false){
if( (millis() - radio_data.topbuttonpressedtime) > MIN_DELAY_BETWEEN_PRESSED_MS){
// ignore if the button was "pressed" a few millis before - this is keybouncing and would give a false result and if the button is pressed for a longer time that would effect the time as well
if(digitalRead(STOPBUTTON_IN) == STOPBUTTON_PRESSED){
// button was pressed - store the time
radio_data.topbuttonpressedtime = millis();
topbuttonwaspressed = true;
Serial.print("Stopp button was pressed at:");
Serial.println(radio_data.topbuttonpressedtime);
digitalWrite(RUN_LED, RUN_LED_ON);
}
}
} else {
if(digitalRead(STOPBUTTON_IN) != STOPBUTTON_PRESSED){
topbuttonwaspressed = false;
digitalWrite(RUN_LED, RUN_LED_OFF);
2018-07-06 23:56:33 +02:00
}
}
2018-07-08 01:34:57 +02:00
2018-07-06 23:56:33 +02:00
// 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)){
// store current millis to be send as reference ...
radio_data.topstationtime = millis(); // set the current milli second count
2018-07-08 01:34:57 +02:00
Serial.print(millis());
Serial.print("ms - Send data to bottom station: topstationtime: ");
Serial.print( radio_data.topstationtime );
Serial.print(" stoppressedtime: ");
Serial.print( radio_data.topbuttonpressedtime );
if(topbuttonwaspressed)
{
Serial.println(" .Stopp button was pressed.");
}
else {
Serial.println(" .Stopp button was NOT pressed.");
}
2018-07-06 23:56:33 +02:00
// send data ...
if (!radio.write(&radio_data,sizeof(radio_data) )){ // Send the counter variable to the other radio
Serial.println(F("Failed to send data to BASESSTATION ... will retry"));
2018-07-08 01:34:57 +02:00
digitalWrite(FAIL_LED, FAIL_LED_ON);
digitalWrite(READY_LED, READY_LED_OFF);
}
else
{
Serial.println("Data sent to BASESSTATION");
digitalWrite(FAIL_LED, FAIL_LED_OFF);
digitalWrite(READY_LED, READY_LED_ON);
}
2018-07-06 23:56:33 +02:00
}
}
/****************** Code for the BASESTATION is here - the display and the start button is connected here. All caclulation will be done here ***************************/
if ( stationNumber == BASESTATION ) {
// receive data from top_station, calculate offset and set 'last connection' time stamp
receive_values();
2018-07-06 23:56:33 +02:00
// show state debug outputs at serial monitor
update_statemessage(timer_new_state);
2018-07-07 22:50:05 +02:00
// update the OLED screen
2018-07-07 22:50:05 +02:00
update_screen(timer_new_state);
// set state to new_state
2018-07-07 18:47:18 +02:00
timer_state = timer_new_state;
2018-07-06 23:56:33 +02:00
// 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;
}
2018-07-06 23:56:33 +02:00
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;
}
2018-07-06 23:56:33 +02:00
}
break;
2018-07-07 22:50:05 +02:00
case TIMER_IDLE:
2018-07-06 23:56:33 +02:00
warn_during_run = false;
if(connection_available == false){
// if the connection was lost ... switch to noconnection state
timer_new_state = TIMER_NOCONNECTION;
}
2018-07-06 23:56:33 +02:00
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 ...
2018-07-07 18:47:18 +02:00
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;
}
2018-07-06 23:56:33 +02:00
}
}
}
break;
2018-07-07 22:50:05 +02:00
case TIMER_READY:
if(connection_available == false){
// if the connection was lost ... switch to noconnection state
timer_new_state = TIMER_NOCONNECTION;
}
2018-07-07 22:50:05 +02:00
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;
}
2018-07-07 22:50:05 +02:00
}
break;
2018-07-06 23:56:33 +02:00
case TIMER_STARTED:
2018-07-07 18:47:18 +02:00
//initialize the start countdown
timer_new_state = TIMER_RUNNING;
2018-07-06 23:56:33 +02:00
startSequence();
break;
2018-07-07 18:47:18 +02:00
case TIMER_RUNNING:
noTone(PIEZO_PIN);
2018-07-06 23:56:33 +02:00
if(counter_time_offset < REQUIRED_NUMBER_MEANVALS){
// check if offset is still OK - if not .. set state to TIMER_RUNNING
warn_during_run = true;
}
2018-07-07 18:47:18 +02:00
if(millis() - start_time > TIMER_TIMEOUT){
timer_new_state = TIMER_TIMEDOUT;
}
if(digitalRead(CANCELBUTTON_IN) == CANCELBUTTON_PRESSED){
timer_new_state = TIMER_CANCELLED;
2018-07-06 23:56:33 +02:00
}
2018-07-08 01:57:34 +02:00
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;
runner_run_time = runner_start_time - run_time;
timer_new_state = TIMER_STOPPED;
}
2018-07-07 18:47:18 +02:00
}
2018-07-06 23:56:33 +02:00
break;
case TIMER_STOPPED:
//calculate the run_time and switch to WAIT
2018-07-07 23:13:48 +02:00
delay(10);
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
timer_new_state = TIMER_WAIT;
}
2018-07-06 23:56:33 +02:00
break;
case TIMER_FAIL:
//fail start case ....
2018-07-07 18:47:18 +02:00
failSequence();
run_time = 99999;
runner_run_time = runner_start_time - start_time;
2018-07-07 23:13:48 +02:00
delay(10);
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
timer_new_state = TIMER_WAIT;
}
2018-07-06 23:56:33 +02:00
break;
2018-07-07 18:47:18 +02:00
case TIMER_CANCELLED:
2018-07-06 23:56:33 +02:00
// what to do in chancel mode ?
2018-07-07 18:47:18 +02:00
run_time = 99999;
runner_run_time = runner_start_time - start_time;
2018-07-07 23:13:48 +02:00
delay(10);
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
timer_new_state = TIMER_WAIT;
}
2018-07-06 23:56:33 +02:00
break;
case TIMER_TIMEDOUT:
2018-07-07 18:47:18 +02:00
// time out
run_time = millis() - start_time;
runner_run_time = runner_start_time - start_time;
2018-07-07 23:13:48 +02:00
delay(10);
if(digitalRead(CANCELBUTTON_IN) != CANCELBUTTON_PRESSED){
timer_new_state = TIMER_WAIT;
}
2018-07-06 23:56:33 +02:00
break;
case TIMER_WAIT:
2018-07-07 18:47:18 +02:00
// 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){
2018-07-07 22:50:05 +02:00
timer_new_state = TIMER_IDLE;
2018-07-06 23:56:33 +02:00
}
break;
}
2018-07-07 18:47:18 +02:00
}
}
2018-07-06 23:56:33 +02:00
2018-07-07 18:47:18 +02:00
//####################### 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
2018-07-06 23:56:33 +02:00
}// 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;
}
2018-07-07 18:47:18 +02:00
}
2018-07-06 23:56:33 +02:00
}
2018-07-07 22:50:05 +02:00
void update_screen(timer_state_e state){
2018-07-07 18:47:18 +02:00
bool scr_update = true;
int ypos = 64-42/2;
2018-07-07 21:41:53 +02:00
String header = "no state";
String content = "";
String footer = "";
char header_to_char[50];
char content_to_char[50];
char footer_to_char[50];
2018-07-07 22:00:53 +02:00
2018-07-07 23:25:46 +02:00
float curr_time_local = 0.0;
2018-07-07 18:47:18 +02:00
2018-07-07 22:50:05 +02:00
switch(state){
case TIMER_NOCONNECTION:
header = "No connection";
content = "Waiting..";
footer = "Waiting for TOPSTATION";
break;
2018-07-07 18:47:18 +02:00
case TIMER_INIT:
2018-07-07 21:41:53 +02:00
header = "Init";
2018-07-07 22:00:53 +02:00
content = "...";
footer = "please wait";
2018-07-07 18:47:18 +02:00
break;
2018-07-07 22:50:05 +02:00
case TIMER_IDLE:
header = "Idle!";
2018-07-08 00:04:43 +02:00
content = "00.00 sec";
2018-07-07 22:50:05 +02:00
footer = "Waiting for climber";
break;
2018-07-07 18:47:18 +02:00
case TIMER_READY:
2018-07-07 21:41:53 +02:00
header = "Ready!";
2018-07-08 00:04:43 +02:00
content = "00.00 sec";
2018-07-07 23:25:46 +02:00
footer = "Waiting for start";
2018-07-07 18:47:18 +02:00
break;
case TIMER_STARTED:
2018-07-07 22:00:53 +02:00
header = "Starting ...";
2018-07-08 00:04:43 +02:00
content = "00.00 sec";
2018-07-07 22:00:53 +02:00
footer = "...";
2018-07-07 18:47:18 +02:00
break;
case TIMER_RUNNING:
2018-07-07 21:41:53 +02:00
header = "Running ...";
2018-07-07 23:25:46 +02:00
curr_time_local = (millis() - start_time)/1000.000;
content = curr_time_local;
2018-07-08 00:04:43 +02:00
content += " sec";
2018-07-07 23:50:28 +02:00
curr_time_local = (runner_start_time - start_time)/1000.000;
2018-07-08 00:04:43 +02:00
footer = "Reaction time: ";
2018-07-07 23:50:28 +02:00
footer += curr_time_local;
2018-07-08 00:04:43 +02:00
footer += " sec";
2018-07-07 18:47:18 +02:00
break;
case TIMER_CANCELLED:
2018-07-07 21:41:53 +02:00
header = "Cancelled!";
2018-07-07 18:47:18 +02:00
break;
case TIMER_STOPPED:
2018-07-07 21:41:53 +02:00
header = "Stopped!";
2018-07-08 01:57:34 +02:00
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";
2018-07-07 18:47:18 +02:00
break;
case TIMER_TIMEDOUT:
2018-07-08 01:57:34 +02:00
header = "Timed out!";
2018-07-08 00:04:43 +02:00
content = "Invalid!";
2018-07-07 18:47:18 +02:00
break;
case TIMER_FAIL:
2018-07-07 21:41:53 +02:00
header = "False start!";
2018-07-07 23:25:46 +02:00
curr_time_local = (start_time - runner_start_time)/1000.000;
content = curr_time_local;
footer = "seconds too early";
2018-07-07 18:47:18 +02:00
break;
default:
scr_update = false;
break;
}
2018-07-07 22:50:05 +02:00
2018-07-07 18:47:18 +02:00
if(scr_update == true){
2018-07-07 22:50:05 +02:00
if(timer_new_state != timer_state){
2018-07-07 23:14:10 +02:00
display.clear(0,200,0,0);
display.clear(0,200,3,5);
display.clear(0,200,7,7);
2018-07-07 22:50:05 +02:00
}
2018-07-07 21:41:53 +02:00
//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));
2018-07-07 18:47:18 +02:00
//Serial.print("DISPLAY: ");
//Serial.println(string_to_char);
2018-07-07 21:41:53 +02:00
2018-07-07 19:27:47 +02:00
display.setFont(System5x7);
2018-07-07 21:41:53 +02:00
display.set1X();
int xpos = (128 - (display.strWidth(header_to_char)))/2 - 10;
2018-07-07 19:27:47 +02:00
display.home();
2018-07-07 21:41:53 +02:00
display.setLetterSpacing(1);
display.setCursor(64 - (display.strWidth(header_to_char) / 2), 0);
display.print(header_to_char);
display.setCursor(1,0);
2018-07-08 11:29:03 +02:00
//check if there is a connection to the topstation
if(connection_available != true){
2018-07-08 11:29:03 +02:00
//if not remove the "Y"
display.print(" ");
}
else {
2018-07-08 11:29:03 +02:00
//if print the "Y"
display.print("Y");
}
2018-07-07 21:41:53 +02:00
display.setCursor(0,1);
display.setLetterSpacing(0);
display.print("----------------------------");
2018-07-07 21:41:53 +02:00
//end of the Header
2018-07-07 23:50:28 +02:00
//display.setLetterSpacing(1);
2018-07-07 21:41:53 +02:00
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);
2018-07-07 23:50:28 +02:00
display.setLetterSpacing(0);
display.print("----------------------------");
2018-07-07 22:00:53 +02:00
display.setCursor(64 - (display.strWidth(footer_to_char) / 2), 7);
display.print(footer_to_char);
2018-07-07 18:47:18 +02:00
}
}
2018-07-06 23:56:33 +02:00
void set_state_LEDs(timer_state_e state, boolean warn){
2018-07-06 23:56:33 +02:00
// set the LEDS corresponding to the state of the timer ... as long as the system is not waiting for input ...
if(TIMER_WAIT != state){
2018-07-07 18:47:18 +02:00
digitalWrite(READY_LED, LEDStates[state][0]);
digitalWrite(RUN_LED, LEDStates[state][1]);
digitalWrite(FAIL_LED, LEDStates[state][2]);
2018-07-06 23:56:33 +02:00
if(warn == true){
2018-07-07 18:47:18 +02:00
digitalWrite(WARN_LED, WARN_LED_ON);
2018-07-06 23:56:33 +02:00
}
else
{
2018-07-07 18:47:18 +02:00
digitalWrite(WARN_LED, WARN_LED_OFF);
2018-07-06 23:56:33 +02:00
}
}
}
void startSequence(void){
2018-07-06 23:56:33 +02:00
// set the startime - this is the current time plus the length of this sequence
running_time_offset = mean_time_offset;
2018-07-07 18:47:18 +02:00
start_time = millis() + STARTSEQ_LENGTH_MS;
Serial.print("Start time is: ");
Serial.println(start_time);
2018-07-06 23:56:33 +02:00
// 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
2018-07-07 23:13:48 +02:00
update_statemessage(timer_new_state);
2018-07-07 18:47:18 +02:00
if(timer_new_state == TIMER_RUNNING){
2018-07-07 23:51:35 +02:00
wait(STARTSEQ_STARTPAUSE_MS);
2018-07-07 18:47:18 +02:00
}
2018-07-06 23:56:33 +02:00
// first tone
2018-07-07 23:13:48 +02:00
update_statemessage(timer_new_state);
2018-07-07 18:47:18 +02:00
if(timer_new_state == TIMER_RUNNING){
tone(PIEZO_PIN, STARTSEQ_TON_1_2_FREQUENCY,STARTSEQ_TON_1_2_LENGTH_MS );
2018-07-07 23:51:35 +02:00
wait(STARTSEQ_TONEPAUSE_MS);
2018-07-07 18:47:18 +02:00
}
2018-07-06 23:56:33 +02:00
//second tone
2018-07-07 23:13:48 +02:00
update_statemessage(timer_new_state);
2018-07-07 18:47:18 +02:00
if(timer_new_state == TIMER_RUNNING){
tone(PIEZO_PIN, STARTSEQ_TON_1_2_FREQUENCY,STARTSEQ_TON_1_2_LENGTH_MS );
2018-07-07 23:51:35 +02:00
wait(STARTSEQ_TONEPAUSE_MS);
2018-07-07 18:47:18 +02:00
}
2018-07-06 23:56:33 +02:00
//third tone
2018-07-07 23:13:48 +02:00
update_statemessage(timer_new_state);
2018-07-07 18:47:18 +02:00
if(timer_new_state == TIMER_RUNNING){
tone(PIEZO_PIN, STARTSEQ_TON_3_FREQUENCY,STARTSEQ_TON_3_LENGTH_MS );
2018-07-07 23:51:35 +02:00
wait(STARTSEQ_TON_3_LENGTH_MS);
2018-07-07 18:47:18 +02:00
}
}
2018-07-06 23:56:33 +02:00
void failSequence(void){
2018-07-07 18:47:18 +02:00
// 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);
}
2018-07-06 23:56:33 +02:00
void false_start_isr(void){
2018-07-07 18:47:18 +02:00
// 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();
2018-07-07 23:13:48 +02:00
if((timer_state == TIMER_STARTED) & (timer_new_state == TIMER_RUNNING)){
2018-07-07 18:47:18 +02:00
timer_new_state = TIMER_FAIL;
noTone(PIEZO_PIN);
2018-07-07 23:13:48 +02:00
Serial.println("** Interrupt service routine detected false_start. Set new state to TIMER_FAIL **");
update_statemessage(timer_new_state);
detachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN));
2018-07-07 18:47:18 +02:00
} else {
2018-07-07 22:50:05 +02:00
if((timer_state == TIMER_RUNNING) | (timer_new_state == TIMER_RUNNING) ){
2018-07-07 18:47:18 +02:00
// disable this interrupt;
detachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN));
}
}
}
2018-07-07 23:51:35 +02:00
void wait(unsigned long ms){
unsigned long current = 0;
unsigned long started = millis();
do{
current = millis()-started;
}while(current < ms);
}