Hi Guys, I hope everyone is having a nice summer. It is almost over up here in the North. Can someone can help with my question? I want to give the user 10 seconds to decide in the menu. If they don't hit 1, 2, or 3 within ten seconds, the program should run option 1 for the user. Here's my sketch:
char myArray[10];
void setup() {
Serial.begin(9600);
Serial.println(F(""));
Serial.println(F("Select from the following:"));
Serial.println(F(" 1 - Configure this device"));
Serial.println(F(" 2 - Reset to factory defaults"));
Serial.println(F(" 3 - Refresh Page"));
while (1) {
while (Serial.available() == 0) {}
byte n = Serial.available(); //there is at least one char
if ( n != 0)
{
byte m = Serial.readBytesUntil('\n', myArray, 10);
myArray[m] = '\0'; //insert null-character
}
if (myArray[0] == '1') {
Serial.println("You chose option 1");
}
else if (myArray[0] == '2') {
Serial.println("You chose option 2");
}
else if (myArray[0] == '3') {
Serial.println("You chose option 3");
}
break;
}
}
void loop(){}
I tried using attachInterrupt() which I thought was going to work awesome with a cool count down but, then I couldn't terminate it and it kept looping. detachInterrupt() didn't seem to be a solution for that. Any suggestions would be greatly appreciated.
LarryD I'm in Maine. State Machines look advanced for a novice. I'll spend some time on them. Thank you for the suggestion. lastchancename I'll noodle over your suggestion to "start a millis() timer". I found this post on millis() for timing which also talks about State Machine. I'll see if I can apply that. Thank you both!
const byte heartbeatLED = 13;
char myArray[10];
//timing stuff
unsigned long heartbeatMillis;
unsigned long commonMillis;
unsigned long delayInterval;
//State Machine stuff
//use names that mean something to you
enum Machine {StartState, StateOne, StateTwo, Option1, Option2, Option3, EndState};
Machine mState = StartState;
//********************************************^************************************************
void setup()
{
Serial.begin(9600);
pinMode(heartbeatLED, OUTPUT);
} //END of setup()
//********************************************^************************************************
void loop()
{
//********************************* heartbeat TIMER
//is it time to toggle the heartbeatLED (every 500ms)?
if (millis() - heartbeatMillis >= 500ul)
{
//restart this TIMER
heartbeatMillis = millis();
//toggle the heartbeatLED
digitalWrite(heartbeatLED, !digitalRead(heartbeatLED));
}
//*********************************
//service our state Machine
checkStateMachine();
//*********************************
//are we are waiting for input from the user
if (mState == StateTwo)
{
//********************
//are there receive characters ?
if (Serial.available() > 0)
{
byte n = Serial.available(); //there is at least one char
//*********
if ( n != 0)
{
byte m = Serial.readBytesUntil('\n', myArray, 10);
myArray[m] = '\0'; //insert null-character
}
//*********
if (myArray[0] == '1')
{
//next state in our machine
mState = Option1;
}
//*********
else if (myArray[0] == '2')
{
//next state in our machine
mState = Option2;
}
//*********
else if (myArray[0] == '3')
{
//next state in our machine
mState = Option3;
}
}
}
//*********************************
//other non blocking code goes here
//*********************************
} //END of loop()
// c h e c k S t a t e M a c h i n e ( )
//********************************************^************************************************
//servicing the "current state" in our State Machine and doing what is required.
void checkStateMachine()
{
switch (mState)
{
//*****************
case StartState:
{
//next state in our machine
mState = StateOne;
}
break;
//*****************
case StateOne:
{
Serial.println(F(""));
Serial.println(F("Select from the following:"));
Serial.println(F(" 1 - Configure this device"));
Serial.println(F(" 2 - Reset to factory defaults"));
Serial.println(F(" 3 - Refresh Page"));
//new interval for the common TIMER (10seconds)
delayInterval = 10000ul;
//restart the common TIMER
commonMillis = millis();
//next state in our machine
mState = StateTwo;
}
break;
//*****************
case StateTwo:
{
//has the common TIMER expired ?
if (millis() - commonMillis >= delayInterval)
{
//10 seconds has gone by
//next state in our machine
mState = Option1;
}
}
break;
//*****************
case Option1:
{
Serial.println("\n You chose option 1");
//new interval for the common TIMER (3 seconds)
delayInterval = 3000ul;
//restart the common TIMER
commonMillis = millis();
//next state in our machine
mState = EndState;
}
break;
//*****************
case Option2:
{
Serial.println("\n You chose option 2");
//new interval for the common TIMER (3 seconds)
delayInterval = 3000ul;
//restart the common TIMER
commonMillis = millis();
//next state in our machine
mState = EndState;
}
break;
//*****************
case Option3:
{
Serial.println("\n You chose option 3");
//new interval for the common TIMER (3 seconds)
delayInterval = 3000ul;
//restart the common TIMER
commonMillis = millis();
//next state in our machine
mState = EndState;
}
break;
//*****************
case EndState:
{
//has the common TIMER expired ?
if (millis() - commonMillis >= delayInterval)
{
//3 seconds has gone by
//next state in our machine
mState = StartState;
}
}
break;
} //END of switch/state
} //END of checkStateMachine()
This code will only run 1 time right after the controller reset. For it to work stably, you need to store the current millis when entering the menu cycle and start your timeout from this moment.
Hello b707:
Regarding your comment "This code will only run 1 time right after the controller reset.". This is a multi file sketch and this code is of course in a function by itself. I plan on just calling the function again if necessary.
Regarding "For it to work stably" unsigned long currentMillis = millis();
I thought the above stored the current millis. Every time the function is run, it grabs a new millis. Do I still have potential stability issues?
Millis is a counter that increments continuously after the controller starts up. Immediately after the reset, it is equal to zero, and after a minute it is already 60 thousands.
In this piece of code, you are comparing the current value of millis with the variable interval=5000. If you do this immediately after starting, you have a millis value less than 5000 and therefore the loop will wait for some time, although less than the expected 5 seconds.
If you enter this loop after a minute, then you have a value of 60k in millis and the condition
if(currentMillis > interval) {
is triggered immediately, there will be no timeout at all
My C skills aren't allowing me to see the problem but, I'll take your and b707's word and for it. I am seeing a problem in the serial monitor where it keeps displaying my count down even after I've left the function and entered another one. Something is wrong. b707's comment "there will be no timeout at all" is probably the issue.
I'm working with that code you gave me in #6 right now. Thank you for posting that. I pasted it into a sketch and tinkering and trying to understand. Need time....
The issue can be solved very simply.
You need to compare the duration of the interval not with the current value of millis, but with the difference between the current value and the value at the beginning of the timeout
The point behind that sample sketch is to enable different pieces of code at an appropriate time and when certain events happen, example: when a switch closes, when an analog reaches a value, when a TIMER expires, or in you case, when a serial receive character comes in.
Hey LarryD, The sketch provided in post #6 looks to be very useful. Thank you. I understand it although some code I had to tinker with to figure out. !digitalRead(heartbeatLED)) was new to me as was enum and 10000ul (unsigned long). Thanks for the introduction. I'm learning and hope to implement this code into my core this week. You and the other guys on this forum are awesome.