Initial commit
This commit is contained in:
parent
060ba3f2e8
commit
ccdfaa56d3
4 changed files with 19894 additions and 0 deletions
BIN
SpeedclockNano.fzz
Normal file
BIN
SpeedclockNano.fzz
Normal file
Binary file not shown.
71
speedclock.h
Normal file
71
speedclock.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
|
||||||
|
#ifndef Remote_Control_transceiver_H
|
||||||
|
#define Remote_Control_transceiver_H
|
||||||
|
|
||||||
|
//-------------- defines fpr the radio devices NRF24 ---------------------------------------------------------
|
||||||
|
|
||||||
|
#define STATION_SEL 4 // this 4 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 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 D2 // this is the input for the button
|
||||||
|
#define STOPBUTTON_PRESSED HIGH // 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 STARTBUTTON_IN D4 // start button
|
||||||
|
#define STARTBUTTON_PRESSED LOW
|
||||||
|
#define CHANCELBUTTON_IN D2 // chancle button
|
||||||
|
#define CHANCELBUTTON_PRESSED LOW
|
||||||
|
#define FAILSTARTBUTTON_IN D3 // fail start button
|
||||||
|
#define FAILSTARTBUTTON_PRESSED LOW
|
||||||
|
|
||||||
|
#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
|
||||||
|
|
||||||
|
|
||||||
|
typedef enum {TIMER_INIT = 0, TIMER_READY, TIMER_STARTED, TIMER_RUNNING , TIMER_CHANCELED, TIMER_STOPPED, TIMER_TIMEDOUT, TIMER_FAIL, TIMER_WAIT} timer_state_e;
|
||||||
|
|
||||||
|
// READY_LED, WARN_LED, RUN_LED, FAIL_LED
|
||||||
|
const float LEDStates[][3] =
|
||||||
|
{
|
||||||
|
TIMER_INIT = {READY_LED_OFF, 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_CHANCELED = {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 10 // 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 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 = 800
|
||||||
|
#define STARTSEQ_TON_1_2_LENGTH_MS = 200
|
||||||
|
#define STARTSEQ_TON_3_LENGTH_MS = 100
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
346
speedclock.ino
Normal file
346
speedclock.ino
Normal file
|
@ -0,0 +1,346 @@
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <U8g2lib.h>
|
||||||
|
//#include "fonts.h"
|
||||||
|
#include <SPI.h>
|
||||||
|
#include "RF24.h"
|
||||||
|
#include "speedclock.h"
|
||||||
|
|
||||||
|
// internal defines for the OLED display ...
|
||||||
|
U8G2_SSD1306_128X64_NONAME_1_SW_I2C display(U8G2_R0, /* clock=*/ SCL, /* data=*/ SDA, /* reset=*/ U8X8_PIN_NONE); // All Boards without Reset of the Display
|
||||||
|
|
||||||
|
|
||||||
|
/****************** User Config for NRF24***************************/
|
||||||
|
/*** Set this radio as radio number RADIO0 or RADIO1 ***/
|
||||||
|
radio_type_e radioNumber = BASESTATION; //---> TOPSTATION has the button connected
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
unsigned long sum_time_offset = 0; // sum of offset values
|
||||||
|
unsigned long mean_time_offset = 0; // mean value for the offset
|
||||||
|
unsigned long current_time_offset = 0; // current offset ...
|
||||||
|
unsigned 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 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
|
||||||
|
|
||||||
|
timer_state_e timer_state = TIMER_INIT; // timer needs to be initialized ...
|
||||||
|
// timer is ready to be started ...
|
||||||
|
// will be true if the timer was started
|
||||||
|
// will be true if the timer is running and the stopbutton was pressed at the TOPSTATION
|
||||||
|
// will be true if the timeout of the timer is reached
|
||||||
|
// will be true if the chancel button was pressed - e.g. in case of a early start
|
||||||
|
|
||||||
|
|
||||||
|
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);
|
||||||
|
pinMode(STARTBUTTON_IN, INPUT);
|
||||||
|
pinMode(CHANCELBUTTON_IN, INPUT);
|
||||||
|
pinMode(FAILSTARTBUTTON_IN, INPUT);
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
pinMode(STATION_SEL, INPUT);
|
||||||
|
Serial.print(F(" The station select pin (pin "));
|
||||||
|
Serial.print(STATION_SEL);
|
||||||
|
Serial.print(F(") is set to level: "));
|
||||||
|
Serial.println(RADIO_SEL);
|
||||||
|
if(digitalRead(RADIO_SEL) == HIGH){
|
||||||
|
stationNumber = TOPSTATION;
|
||||||
|
Serial.print(F("The level of the station select pin makes the current node set to the TOPSTATION."));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
Serial.print(F("The level of the station select pin makes the current node set to the BASESTATION"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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){
|
||||||
|
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]);
|
||||||
|
}
|
||||||
|
radio.startListening(); // Start listening
|
||||||
|
|
||||||
|
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 OLED and display Welcome Message ...
|
||||||
|
display.begin();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
bool topbuttonwaspressed = false;
|
||||||
|
// check for pressed button ...
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
|
||||||
|
// 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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/****************** Code for the BASESTATION is here - the display and the start button is connected here. All caclulation will be done here ***************************/
|
||||||
|
|
||||||
|
if ( stationNumber == BASESTATION ) {
|
||||||
|
|
||||||
|
// read data from TOP_STATION ...
|
||||||
|
if( radio.available()){
|
||||||
|
// check if radio data is available - if so read the data
|
||||||
|
radio.read( &radio_data, sizeof(radio_data) ); // Read the data the TOPSTATION sent
|
||||||
|
current_time_offset = radio_data.topstationtime - millis(); // the offset between TOP_STATION and BASESTATION
|
||||||
|
Serial.print(F(" Current time on client in millis: "));
|
||||||
|
Serial.println(radio_data.topstationtime);
|
||||||
|
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 ...
|
||||||
|
// 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){
|
||||||
|
// 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 already have "))
|
||||||
|
Serial.print(counter_time_offset)
|
||||||
|
Serial.print(F(" of "));
|
||||||
|
Serial.print(REQUIRED_NUMBER_MEANVALS);
|
||||||
|
Serial.print(F(" values used for offset calculation. Mean value of offset based on that is: "));
|
||||||
|
Serial.println(mean_time_offset);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// the current offset is out of range so we need to restart the mean calculation and set the timer to unready state ...
|
||||||
|
Serial.print(F("Will restart offset calculation because the variation of the current offset: "));
|
||||||
|
Serial.print(current_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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// set LEDs
|
||||||
|
set_state_LEDs(timer_state, warn_during_run );
|
||||||
|
|
||||||
|
switch(timer_state){
|
||||||
|
case TIMER_INIT:
|
||||||
|
// check if we are ready ...
|
||||||
|
if(counter_time_offset > REQUIRED_NUMBER_MEANVALS){
|
||||||
|
// check if offset is OK - if not .. set state back to INIT
|
||||||
|
timer_state = TIMER_READY;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_READY:
|
||||||
|
warn_during_run = false;
|
||||||
|
if(counter_time_offset < REQUIRED_NUMBER_MEANVALS){
|
||||||
|
// check if offset is OK - if not .. set state back to INIT
|
||||||
|
timer_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(300);
|
||||||
|
//read again and check if still active ...
|
||||||
|
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)){
|
||||||
|
timer_state = TIMER_STARTED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_STARTED:
|
||||||
|
//enable the interrupt for the FALSESTART button and start the StartSequence - no interruption possible here anymore, except the FALSESTART button was released to early
|
||||||
|
|
||||||
|
//initialize the start countdown here ... if done set to RUNNING ... maybe check fail start already here ... ?? or in running state ... or extra state ...
|
||||||
|
startSequence();
|
||||||
|
|
||||||
|
case TIMER_FAILCHECK:
|
||||||
|
//check for fail start in this phase - interrupt is active, this state can only be reached from STARTED state ...
|
||||||
|
if(start_time - millis() >= STARTOK_TOLERANCETIME_MS)
|
||||||
|
{
|
||||||
|
timer_state = TIMER_RUNNING;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_RUNNING:
|
||||||
|
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(digitalRead(CHANCELBUTTON_IN, CHANCELBUTTON_PRESSED)){
|
||||||
|
timer_state = TIMER_CHANCELED;
|
||||||
|
}
|
||||||
|
if((radio_data.topbuttonpressedtime - running_time_offset) > millis()){
|
||||||
|
timer_state = TIMER_STOPPED;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TIMER_STOPPED:
|
||||||
|
//calculate the run_time and switch to WAIT
|
||||||
|
|
||||||
|
timer_state = TIMER_WAIT;
|
||||||
|
break;
|
||||||
|
case TIMER_FAIL:
|
||||||
|
//fail start case ....
|
||||||
|
|
||||||
|
timer_state = TIMER_WAIT;
|
||||||
|
break;
|
||||||
|
case TIMER_CHANCELED:
|
||||||
|
// what to do in chancel mode ?
|
||||||
|
|
||||||
|
timer_state = TIMER_WAIT;
|
||||||
|
break;
|
||||||
|
case TIMER_TIMEDOUT:
|
||||||
|
//
|
||||||
|
|
||||||
|
timer_state = TIMER_WAIT;
|
||||||
|
break;
|
||||||
|
case TIMER_WAIT:
|
||||||
|
// wait until the start button was pressed to go ahead
|
||||||
|
if(digitalRead(STARTBUTTON_IN, STARTBUTTON_PRESSED)){
|
||||||
|
timer_state = TIMER_READY;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
int ypos = 64-42/2;
|
||||||
|
snprintf ( temp_string, sizeof(temp_string),"%d.%1d", int(client_data.temperature), int(abs(client_data.temperature - int(client_data.temperature))*10 + 0.5));
|
||||||
|
Serial.println(temp_string);
|
||||||
|
|
||||||
|
display.setFontPosCenter();
|
||||||
|
display.setFont(u8g2_font_logisoso34_tn);
|
||||||
|
int xpos = (128-display.getStrWidth(temp_string))/2 - 10;
|
||||||
|
|
||||||
|
display.firstPage();
|
||||||
|
do {
|
||||||
|
display.setFont(u8g2_font_logisoso34_tn);
|
||||||
|
display.setCursor(xpos,ypos);
|
||||||
|
display.print(temp_string);
|
||||||
|
display.setCursor(xpos + display.getStrWidth(temp_string)+ 5,ypos-15);
|
||||||
|
display.setFont(u8g2_font_ncenB12_tr);
|
||||||
|
display.print(F("°C"));
|
||||||
|
} while ( display.nextPage() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if( radio.available(&pipeNo)){
|
||||||
|
while( radio.available(&pipeNo)){ // Read all available payloads
|
||||||
|
radio.read( &control_data, sizeof(control_data) );
|
||||||
|
}
|
||||||
|
|
||||||
|
radio.stopListening(); // First, stop listening so we can talk
|
||||||
|
|
||||||
|
Serial.print(F("Got remote data counter: "));
|
||||||
|
Serial.print(control_data.counter);
|
||||||
|
Serial.print(F(" time: "));
|
||||||
|
Serial.println(control_data.time);
|
||||||
|
radio_data.counter = control_data.counter + 1; // Ack payloads are much more efficient than switching to transmit mode to respond to a call
|
||||||
|
radio_data.time = control_data.time;
|
||||||
|
Serial.print(F("Loaded next response "));
|
||||||
|
Serial.println(radio_data.counter);
|
||||||
|
|
||||||
|
if( radio.write(&radio_data, sizeof(radio_data) )){
|
||||||
|
Serial.println(F("Sending failed.")); // If no ack response, sending failed
|
||||||
|
//add error handling here if remote does not recieve the data anymore
|
||||||
|
}
|
||||||
|
|
||||||
|
radio.startListening();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//####################### HELPER FUNCTIONS ###########################
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
// first - enable the interrupt
|
||||||
|
|
||||||
|
// set the startime - this is the current time plus the length of this sequence
|
||||||
|
start_time = millis() + STARTSEQ_LENGTH_MS;
|
||||||
|
running_time_offset = mean_time_offset;
|
||||||
|
|
||||||
|
// 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
|
||||||
|
delay(STARTSEQ_STARTPAUSE_MS);
|
||||||
|
// first tone
|
||||||
|
|
||||||
|
delay(STARTSEQ_TONEPAUSE_MS);
|
||||||
|
//second tone
|
||||||
|
|
||||||
|
delay(STARTSEQ_TONEPAUSE_MS);
|
||||||
|
//third tone
|
||||||
|
|
||||||
|
// disable the interrupt and than
|
||||||
|
|
||||||
|
|
Reference in a new issue