Modfied Init step and added NoConnection state

This commit is contained in:
Fenoglio 2018-07-11 13:30:57 +02:00
parent 6989862dc4
commit d7a1c92a47
2 changed files with 149 additions and 96 deletions

View file

@ -1,6 +1,6 @@
#ifndef Remote_Control_transceiver_H #ifndef speedclock_H
#define Remote_Control_transceiver_H #define speedclock_H
//-------------- defines for the radio devices NRF24 --------------------------------------------------------- //-------------- defines for the radio devices NRF24 ---------------------------------------------------------
@ -10,6 +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, ????
//--------------- defines for the I2C //--------------- defines for the I2C
//#define SCL A5 // I2C clock pin //#define SCL A5 // I2C clock pin
@ -27,7 +28,7 @@ typedef struct transcv_struct{
#define STOPBUTTON_PRESSED LOW // this the signal level the top button will be at as soon as pressed #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_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 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 #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_IN 4 // start button
#define STARTBUTTON_PRESSED LOW #define STARTBUTTON_PRESSED LOW
@ -53,20 +54,22 @@ typedef struct transcv_struct{
#define DISPLAY_I2C_ADDRESS 0x3C //Adress of the Display #define DISPLAY_I2C_ADDRESS 0x3C //Adress of the Display
typedef enum {TIMER_INIT = 0, 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
// READY_LED, WARN_LED, RUN_LED, FAIL_LED // READY_LED, WARN_LED, RUN_LED, FAIL_LED
const float LEDStates[][3] = const float LEDStates[][3] =
{ {
[TIMER_INIT] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_OFF}, [TIMER_INIT] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_OFF},
[TIMER_IDLE] = {READY_LED_ON, RUN_LED_OFF, FAIL_LED_OFF}, [TIMER_NOCONNECTION] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_ON},
[TIMER_READY] = {READY_LED_ON, RUN_LED_OFF, FAIL_LED_OFF}, [TIMER_IDLE] = {READY_LED_ON, RUN_LED_OFF, FAIL_LED_OFF},
[TIMER_STARTED] = {READY_LED_ON, RUN_LED_ON, FAIL_LED_OFF}, [TIMER_READY] = {READY_LED_ON, RUN_LED_OFF, FAIL_LED_OFF},
[TIMER_RUNNING] = {READY_LED_OFF, RUN_LED_ON, FAIL_LED_OFF}, [TIMER_STARTED] = {READY_LED_ON, RUN_LED_ON, FAIL_LED_OFF},
[TIMER_CANCELLED] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_ON}, [TIMER_RUNNING] = {READY_LED_OFF, RUN_LED_ON, FAIL_LED_OFF},
[TIMER_STOPPED] = {READY_LED_ON, RUN_LED_ON, FAIL_LED_OFF}, [TIMER_CANCELLED] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_ON},
[TIMER_TIMEDOUT] = {READY_LED_OFF, RUN_LED_ON, FAIL_LED_ON}, [TIMER_STOPPED] = {READY_LED_ON, RUN_LED_ON, FAIL_LED_OFF},
[TIMER_FAIL] = {READY_LED_OFF, RUN_LED_OFF, FAIL_LED_ON} [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 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

View file

@ -1,5 +1,5 @@
#include <Arduino.h> #include <Arduino.h>
#include <U8g2lib.h> //#include <U8g2lib.h>
//#include "fonts.h" //#include "fonts.h"
#include <Wire.h> #include <Wire.h>
#include "SSD1306Ascii.h" #include "SSD1306Ascii.h"
@ -30,15 +30,16 @@ unsigned long runner_start_time = 0; // this is the time the runner lef
signed long runner_run_time = 0; // this is the time the runner really needed or the time started to early - depending on sign ... 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_established = 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 ...
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 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_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_state_e timer_state = TIMER_WAIT; // timer needs to be initialized ... timer_mode_e timer_mode = MODE_COMPETE; // mode of the BASESTATION - this can be changed in IDLE state by pressing the CANCEL button
timer_state_e timer_new_state = TIMER_INIT; // timer needs to be initialized ...
transcv_s radio_data; transcv_s radio_data;
@ -84,6 +85,8 @@ void setup(){
radio.begin(); 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.setRetries(15, 15); //the first is the time between reties in multiple of 250ms, the second is the numer of attempts
if(stationNumber == TOPSTATION){ if(stationNumber == TOPSTATION){
// Set the PA Level of the sendin TOP_STATION
radio.setPALevel(RF24_PA_LEVEL);
radio.openWritingPipe(addresses[1]); // Both radios listen on the same pipes by default, but opposite addresses 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.openReadingPipe(1,addresses[0]); // Open a reading pipe on address 0, pipe 1
radio.stopListening(); // top station will never receive data. radio.stopListening(); // top station will never receive data.
@ -92,14 +95,12 @@ void setup(){
radio.openReadingPipe(1,addresses[1]); radio.openReadingPipe(1,addresses[1]);
radio.startListening(); radio.startListening();
} }
radio_data.topstationtime = millis(); // set the current milli second count 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 radio_data.topbuttonpressedtime = 0; // set the time the button was pressed last time to 0
//initialise Wire and OLED //initialise Wire and OLED
Wire.begin(); Wire.begin();
Wire.setClock(400000L); Wire.setClock(400000L);
display.begin(&Adafruit128x64, DISPLAY_I2C_ADDRESS); display.begin(&Adafruit128x64, DISPLAY_I2C_ADDRESS);
display.clear(); display.clear();
} }
@ -172,55 +173,79 @@ 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();
// set state to new_state // show state debug outputs at serial monitor
if(timer_state != timer_new_state){ update_statemessage(timer_new_state);
update_statemessage(timer_new_state);
}
// update the OLED screen
update_screen(timer_new_state); update_screen(timer_new_state);
// set state to new_state
timer_state = timer_new_state; timer_state = timer_new_state;
// set LEDs // set LEDs
set_state_LEDs(timer_state, warn_during_run ); set_state_LEDs(timer_state, warn_during_run );
switch(timer_state){ switch(timer_state){
case TIMER_INIT: case TIMER_NOCONNECTION:
// check if we are ready ... // as long as there is no connection to TOP_STATION we will end up here
if(counter_time_offset > REQUIRED_NUMBER_MEANVALS){ if(connection_available == true){
// 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(counter_time_offset < REQUIRED_NUMBER_MEANVALS){
// check if offset is OK - if not .. set state back to INIT
timer_new_state = TIMER_INIT; 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{ else{
// check if the FALSESTATE button is pressed - somebody is ready to run ... // if the offset is claculated switch to IDLE mode ...
if(digitalRead(FAILSTARTBUTTON_IN) == FAILSTARTBUTTON_PRESSED){ if(counter_time_offset > REQUIRED_NUMBER_MEANVALS){
//wait a few milliseconds to prevent keybouncing - this is a very simplistic method here // check if offset is OK - if not .. set state back to INIT
delay(10); timer_new_state = TIMER_IDLE;
//read again and check if still active ... }
}
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){ if(digitalRead(FAILSTARTBUTTON_IN) == FAILSTARTBUTTON_PRESSED){
timer_new_state = TIMER_READY; //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; break;
case TIMER_READY: case TIMER_READY:
if(digitalRead(FAILSTARTBUTTON_IN) == FAILSTARTBUTTON_PRESSED){ if(connection_available == false){
// check if the start button was pressed ... there is at least still someone waiting for the run . // if the connection was lost ... switch to noconnection state
if(digitalRead(STARTBUTTON_IN) == STARTBUTTON_PRESSED){ timer_new_state = TIMER_NOCONNECTION;
// now enable the interrupt for the FALSESTART button
attachInterrupt(digitalPinToInterrupt(FAILSTARTBUTTON_IN), false_start_isr, CHANGE);
timer_new_state = TIMER_STARTED;
}
} }
else{ else{
timer_new_state = TIMER_IDLE; 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; break;
case TIMER_STARTED: case TIMER_STARTED:
@ -299,14 +324,14 @@ void loop(void) {
//####################### HELPER FUNCTIONS ########################### //####################### HELPER FUNCTIONS ###########################
void receive_values(void){ void receive_values(void){
byte pipeNo;
// 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()){
while( radio.available(&pipeNo)){ // Read all available payloads // read data from TOP_STATION ...
// read data from TOP_STATION ... while( radio.available()){ // Read all available payloads
connection_established = millis();
radio.read( &radio_data, sizeof(radio_data) ); // Read the data the TOPSTATION sent 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 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());
@ -326,6 +351,7 @@ void receive_values(void){
if(abs(current_time_offset - mean_time_offset) < MAX_DIFFERENCE_OFFSET_MS){ 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 the current value is in range - decrease the fail counter by 1 if it was not zero already
if(failed_offsets > 0){ if(failed_offsets > 0){
Serial.println("INFO: The last received TOPSTATION offset time stamp was again in range. Decrease internal fail counter");
failed_offsets--; failed_offsets--;
} }
// the offset is in range - check if we have already enough values of if we need to add more ... // the offset is in range - check if we have already enough values of if we need to add more ...
@ -345,11 +371,12 @@ void receive_values(void){
// the current offset is out of range ... // 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 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){ 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++; failed_offsets++;
} }
else{ 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 ... // 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("TopStation BaseStation are out off sync. Offset calculation will be restarted. Last ")); Serial.print(F("ERROR: TopStation and BaseStation are out off sync. Offset calculation will be restarted. Last "));
Serial.print(MAX_ALLOWED_FAILED_OFFSETS); Serial.print(MAX_ALLOWED_FAILED_OFFSETS);
//offset ")); //offset "));
Serial.print(current_time_offset); Serial.print(current_time_offset);
@ -363,45 +390,63 @@ void receive_values(void){
sum_time_offset = 0; sum_time_offset = 0;
mean_time_offset = 0; mean_time_offset = 0;
failed_offsets = 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
void update_statemessage(timer_state_e timer_state){
switch(timer_state){ }// receive values
case TIMER_INIT:
Serial.println("*** TIMER_INIT ***"); void update_statemessage(timer_state_e state){
break; //check if the state has changed ... only show the message below (of the given state) in this case ...
case TIMER_IDLE: if(timer_state != timer_new_state){
Serial.println("*** TIMER_IDLE ***"); switch(state){
break; case TIMER_NOCONNECTION:
case TIMER_READY: Serial.println("*** TIMER_NOCONNECTION ***");
Serial.println("*** TIMER_READY ***"); break;
break; case TIMER_INIT:
case TIMER_STARTED: Serial.println("*** TIMER_INIT ***");
Serial.println("*** TIMER_STARTED ***"); break;
break; case TIMER_IDLE:
case TIMER_RUNNING: Serial.println("*** TIMER_IDLE ***");
Serial.println("*** TIMER_RUNNING ***"); break;
break; case TIMER_READY:
case TIMER_CANCELLED: Serial.println("*** TIMER_READY ***");
Serial.println("*** TIMER_CANCELLED ***"); break;
break; case TIMER_STARTED:
case TIMER_STOPPED: Serial.println("*** TIMER_STARTED ***");
Serial.println("*** TIMER_STOPPED ***"); break;
break; case TIMER_RUNNING:
case TIMER_TIMEDOUT: Serial.println("*** TIMER_RUNNING ***");
Serial.println("*** TIMER_TIMEDOUT ***"); break;
break; case TIMER_CANCELLED:
case TIMER_FAIL: Serial.println("*** TIMER_CANCELLED ***");
Serial.println("*** TIMER_FAIL ***"); break;
break; case TIMER_STOPPED:
case TIMER_WAIT: Serial.println("*** TIMER_STOPPED ***");
Serial.println("*** TIMER_WAIT ***"); break;
break; case TIMER_TIMEDOUT:
default: Serial.println("*** TIMER_TIMEDOUT ***");
break; break;
case TIMER_FAIL:
Serial.println("*** TIMER_FAIL ***");
break;
case TIMER_WAIT:
Serial.println("*** TIMER_WAIT ***");
break;
default:
break;
}
} }
} }
@ -419,6 +464,11 @@ void update_screen(timer_state_e state){
float curr_time_local = 0.0; float curr_time_local = 0.0;
switch(state){ switch(state){
case TIMER_NOCONNECTION:
header = "No connection";
content = "Waiting..";
footer = "Waiting for TOPSTATION";
break;
case TIMER_INIT: case TIMER_INIT:
header = "Init"; header = "Init";
content = "..."; content = "...";
@ -499,7 +549,7 @@ void update_screen(timer_state_e state){
display.print(header_to_char); display.print(header_to_char);
display.setCursor(1,0); display.setCursor(1,0);
//check if there is a connection to the topstation //check if there is a connection to the topstation
if(millis() - connection_established >= CONN_TIMEOUT || connection_established == 0){ if(connection_available != true){
//if not remove the "Y" //if not remove the "Y"
display.print(" "); display.print(" ");
} }