I'm having a problem with an if ladder and the for loops in each of them in this gameProcess function. Essentially the for loop in the (mode == 1) loop is not entered at all and I'm not sure why.
I think it's something to do with the positioning because if (mode == 1) is switched with (mode == 0), the (mode == 1) for loop will be entered but the (mode == 0) won't. Been stuck on this for a while and can't seem to spot what's up with the function. Any help would be greatly appreciated. Thanks.
// 5 means 10 switches as one iteration is a change between one mode to another, not on and off.
int gameProcess(int mode) {
Serial.println("> Starting game process.");
// 4 second delay between
int interval = 1000;
int initialDelay = 5000;
enter code here:
//delay(initialDelay);
if (mode == 1) {
// for a standard 25 round with a 2 sec interval
Serial.println("starting mode 1");
for (int i; i < 51; i++) {
Serial.println("In loop");
Serial.println(interval);
flickPin(13);
workingDelay(interval);
}
Serial.println("finished mode 1");
} else if (mode == 2) {
while(true) {
flickPin(13);
int minRandVal = 1000;
int maxRandVal = 15000;
int randomDelay = random(minRandVal, maxRandVal);
Serial.println(randomDelay);
workingDelay(randomDelay);
}
} else if (mode == 0) {
// for 10 rounds. Find out why it needs 35 and not 20.
Serial.println(" - Mode 0 .");
for (int i; i < 35; i++) {
Serial.println(interval);
flickPin(13);
workingDelay(interval);
}
Serial.println(" - finished Mode 0 .");
} else {
char error[80];
sprintf(error, "Unknown mode = %d", mode);
Serial.println(error);
}
}
Here is the main loop and the initializeGame function where the gameProcess function is called from.
int initializeGame(bool started, int mode) {
if (started == true) {
Serial.println(" -> in startMonitor, start button pressed");
Serial.println(mode);
gameProcess(mode);
// if the mode is 0 (none of the lights are on), that means it's in random mode and any interval between 5 and 60 secs come up until you stop it!
// set it back to false and turn the game light off because the game is over.
started = false;
digitalWrite(9, LOW);
} else {
started = false;
}
return started;
}
void loop()
{
// check if the pushbutton is pressed.
mode = stateMonitor(activeButton);
// now set the game mode led.
lightUpModeLed(ledPins[mode]);
// now check to see if the game has been initialized.
started = startMonitor(startButton, ledPins[4], started);
// read the state of the pushbutton value:
// initialize game if it hasn't already started.
// TODO this is where the loop will likely spend the majority of it's time so how can this be
started = initializeGame(started, mode);
saveData();
}
if you are using windows then install Atmel Studio and the Visual Micro Plugin then you can debug and watch what the code is doing and the values of variables.
This will take away the mystery, which it did for me.
Related
I'm doing a project on Arduino that involves traffic lights.
If the crossing button is pressed within a 7 second interval, after waitng for 3 seconds(delay3000), I will call a function. If it's not pressed, the loop will resume as normal.
I have tried with a for loop but can't seem to get around it. Help?
This is the base code that I have. How could I use a millis() fucntion in this problem? Or are there any possible solutions to this problem?
//Street1-Estado Inicial
void loop() {
digitalWrite(str1_verd,HIGH);
digitalWrite(str1_ama,LOW);
digitalWrite(str1_verm,LOW);
digitalWrite(str2_verd,LOW);
digitalWrite(str2_ama,LOW);
digitalWrite(str2_verm,HIGH);
digitalWrite(ped1_verd,LOW);
digitalWrite(ped1_verm,HIGH);
digitalWrite(ped2_verd,HIGH);
digitalWrite(ped2_verm,LOW);
//Verifica se o botao foi pressionado dps de 3s -------ISSUE
delay(3000);
int stateButton1=digitalRead(button_street1);
for (int t=7;t>=1;t--){
if(stateButton1 == false){
B_change_1();
}}
delay(1000);
digitalWrite(str1_verd,LOW);
digitalWrite(str2_ama,HIGH);
digitalWrite(str1_ama,HIGH);
delay(5000);
digitalWrite(str1_ama,LOW);
digitalWrite(str2_ama,LOW);
digitalWrite(str2_verm,LOW);
digitalWrite(str1_verm,HIGH);
digitalWrite(ped2_verd,LOW);
digitalWrite(str2_verd,HIGH);
digitalWrite(ped2_verm,HIGH);
digitalWrite(ped1_verm,LOW);
digitalWrite(ped1_verd,HIGH);
//Verifica se o botao foi pressionado dps de 3s --------ISSUE
delay(3000);
for (int t=7;t>=1;t--){
if(button_street2 == true){
B_change_2();
}}
delay(1000);
digitalWrite(str2_verd,LOW);
digitalWrite(str1_ama,HIGH);
digitalWrite(str2_ama,HIGH);
delay(5000);
loop();
}
//Funcao b_change1
void B_change_1(){
digitalWrite(str1_verd,LOW);
digitalWrite(str2_ama,HIGH);
digitalWrite(str1_ama,HIGH);
delay(5000);
digitalWrite(str1_ama,LOW);
digitalWrite(str2_ama,LOW);
digitalWrite(str2_verm,LOW);
digitalWrite(str1_verm,HIGH);
digitalWrite(str2_verd,HIGH);
digitalWrite(ped2_verm,HIGH);
digitalWrite(ped1_verm,LOW);
digitalWrite(ped1_verd,HIGH);
buzzer_alert();
digitalWrite(ped2_verd,LOW);
}
//Funcao bchange2
void B_change_2(){
digitalWrite(str2_verd,LOW);
digitalWrite(str1_ama,HIGH);
digitalWrite(str2_ama,HIGH);
delay(5000);
digitalWrite(str2_ama,LOW);
digitalWrite(str1_ama,LOW);
digitalWrite(str1_verm,LOW);
digitalWrite(str2_verm,HIGH);
digitalWrite(str1_verd,HIGH);
digitalWrite(ped1_verm,HIGH);
digitalWrite(ped2_verm,LOW);
digitalWrite(ped2_verd,HIGH);
}}}
First of all, you are repeatedly checking button press variables that can't change (though this may be intentional?) :
int stateButton1=digitalRead(button_street1);
for (int t=7; t>=1; t--) {
if(stateButton1 == false) { <---- stateButton1 will never change
B_change_1();
}
}
and
delay(3000);
for (int t=7;t>=1;t--){
if(button_street2 == true) { <---- this isn't set, and won't change
B_change_2();
}
}
You need to update stateButton1 by reading from digitalRead in the loop, or at least every time you test it.
To test if a certain amount of time has passed, you could do something similar to
long button_wait_timeout = 7000; // Maximum time to wait for button press
long button_wait_allowed = 3000; // If pressed within three seconds do action
long starttime = millis();
while (digitalRead(button_street1) == LOW && ((millis() - starttime) < button_wait_timeout)) {
delay(10); // how man millilseconds before trying the button again
}
if ((millis() - starttime) < button_wait_allowed) {
do_action_1();
}
You can remove the timeout bits if you want to wait indefinitely.
I am making a program that turns my lights on when I clap twice. I coded the project to:
1. listen for a noise (first clap)
2. listen for quiet (break between claps) this is to avoid false triggers (talking, etc.).
3. listen for a second noise (second clap).
The code runs through and works properly, but it has several breaking points that I am struggling to find/fix. I know if I make a noise, then I am quiet, then I make another noise repeatedly, and in rapid succession the code either gets stuck in the first while loop, or stops entering the first while loop altogether. The code often breaks when I am talking.
I have provided the code if anybody is willing to spare me some of there time and effort. I will be appreciative of all suggestions as I am still a beginner.
bool quiet = false;
bool loud = false;
int runtime = 1000;
int start = millis();
bool clap = false;
bool clap2 = false;
void setup() {
Serial.begin(9600);
pinMode(2, INPUT);
}
void loop() {
int mic = digitalRead(2);
if (mic == 0) {
Serial.println("loud");
clap = true;
} else {
Serial.println("quiet");
}
if (clap == true) {
clap = false;
Serial.println("clap");
start = millis();
delay(500);
while (start >= millis() - runtime && loud != true) {
mic = digitalRead(2);
if (mic == 0) {
Serial.println("noise detected!");
loud = true;
} else {
Serial.println("scilence");
quiet = true;
}
}
} else {
delay(1);
}
if (quiet == true && loud == false) {
quiet = false;
Serial.println("listening for second clap...");
delay(500);
start = millis();
while (start >= millis() - runtime) {
mic = digitalRead(2);
if (mic == 0) {
clap2 = true;
} else {
delay(1);
}
}
} else {
loud = false;
quiet = false;
}
if (clap2 == true) {
clap2 = false;
Serial.println("clap 2");
delay(1000);
} else {
delay(1);
}
}
Welcome to StackOverflow!
From the program you posted I think you are over-complicating it ;). First of all, the delay() function is a blocking function: nothing can happen during the lapse of time of the delay. Thus it is usually advise to compare times in order to know if you have waited enough: you can then perform other tasks in the meantime.
I would propose something like this for your loop() function:
if(digitalRead(2)==0) //loud
{
unsigned long first_clap_time = millis();
unsigned long elapsed_time = 0;
bool silence = false;
bool exit = false;
while (elapsed_time < 5000 || exit) // allow a max delay of 5sec
{
if (digitalRead(2)!=0) silence = true;
elapsed_time = millis() - first_clap_time;
if (digitalRead(2)==0 && elapsed_time > 500 && silence)
{
Serial.println("Two claps detected!"); //if a second clap is detected 500 milliseconds after the first, and before 5 sec, and a silence was detected
exit = true;
}
}
}
This should be able to detect only two claps, separated by a time between 500 milliseconds and 5 seconds (to adjust of course). I have no way to test this program so do not hesitate to provide feedback!
Hope it helps,
I'm attempting to create a timer which reminds me to do something ever other day, however it seems that my options with the DS3231 module allows me to set an hour or a day of the week. Unfortunately neither option is good enough (if only there were 8 days in a week) and i'm at a loss on how to tell it to fire every other day.
I considered the idea of setting a 'not_today' variable which will probably work but that has the downside of relying on the arduino and not the RTC which means if it loses power then that variable will reset.
It looks like the DS3231's alarm functionality doesn't really have this option and any code-based solution will have the aforementioned power-loss problems.
Is there an RTC module which can do what I need it to do?
User #RamblinRose pointed me in the direction of EEPROM writing and reading which I wasn't aware was possible. This solved my issue. Here is the full code for anyone else coming here and wanting a more comprehensive answer:
#include <DS3231.h>
#include <EEPROM.h>
// Init the DS3231 using the hardware interface
DS3231 rtc(SDA, SCL);
Time t;
bool active = false;
void setup()
{
// Setup Serial connection
Serial.begin(115200);
// Initialize the rtc object
rtc.begin();
pinMode(12, OUTPUT);
pinMode(11, INPUT_PULLUP);
// The following lines can be uncommented to set the date and time
// rtc.setDOW(MONDAY); // Set Day-of-Week to SUNDAY
// rtc.setTime(15, 51, 0); // Set the time to 12:00:00 (24hr format)
rtc.setDate(14, 9, 2017); // Set the date to January 1st, 2014
}
int blinker(int state = 1) {
if(state == 1) {
if (active == true) {
digitalWrite(12, HIGH); // turn the LED on (HIGH is the voltage level)
delay(500); // wait for a second
digitalWrite(12, LOW); // turn the LED off by making the voltage LOW
delay(500);
buttonCheck();
}
}
}
int buttonCheck() {
if(digitalRead(11) == 0) {
active = false;
blinker(0);
}
}
void loop()
{
t = rtc.getTime();
int hour = t.hour;
int min = t.min;
int sec = t.sec;
// // Send time to serial monitor (for debugging)
// Serial.print(hour);
// Serial.print(":");
// Serial.print(min);
// Serial.println(" ");
// Put there different timers in here for demo purposes
// Set activation time
if(hour == 13 && min == 31 && sec == 00) {
if(EEPROM.read(0) == 0) {
active = true;
EEPROM.write(0, 1);
Serial.println("Not run recently, activating");
} else {
active = false;
EEPROM.write(0, 0);
Serial.println("Run recently, skipping this one");
}
}
if(hour == 13 && min == 35 && sec == 00) {
if(EEPROM.read(0) == 0) {
active = true;
EEPROM.write(0, 1);
Serial.println("Not run recently, activating");
} else {
active = false;
EEPROM.write(0, 0);
Serial.println("Run recently, skipping this one");
}
}
if(hour == 13 && min == 45 && sec == 00) {
if(EEPROM.read(0) == 0) {
active = true;
EEPROM.write(0, 1);
Serial.println("Not run recently, activating");
} else {
active = false;
EEPROM.write(0, 0);
Serial.println("Run recently, skipping this one");
}
}
blinker();
delay(1000);
}
Due to a shortage of pins on a esp8266 in arduino, I need a way to detect a button where;
momentary press runs snooze()
15 sec press runs conf_Desk()
30 sec press runs calibration()
the preconfig;
int buttonPin = D7;
pinMode( buttonPin , INPUT_PULLUP);
All while allowing the main loop to function.
If I trap an interrupt it stops cycling the loop(), a few millisec delays are OK but seconds of delay is too much.
The functions are already written I just can't seem to come up how to track and confirm the hold length to call the right function based on the right timing without stopping other process that must stay cycling.
Using an interrupt is, IMHO, overkill. Interrupts are made for when you need to reply to a stimulus quickly, and a button press is something slow. Unless your loop is blocking, thing that I highly discourage.
ADDITION: as Patrick pointed out in the comments, there is in fact another reason to use interrupts: sleep mode. In fact, if you want to go into sleep mode and wake with a button, you have to use interrupts to wake later. However usually you have to do something continuously and not only reply to the button inputs. If you can't go into sleep mode, using an interrupt for button detection is still overkill in my opinion.
So, if you properly designed your loop not to block, here is a brief part of code doing what I think you should implement:
uint8_t buttonState;
unsigned long lastPressTime;
void setup()
{
...
buttonState = digitalRead(buttonPin);
lastPressTime = 0;
}
void loop()
{
uint8_t currRead = digitalRead(buttonPin);
if (buttonState != currRead)
{ // Button transition
buttonState = currRead;
if (buttonState == LOW)
{ // Button pressed, start tracking
lastPressTime = millis();
}
else
{ // Button released, check which function to launch
if (lastPressTime < 100)
{} // Discard (it is just a bounce)
else if (lastPressTime < 15000)
snooze();
else if (lastPressTime < 30000)
conf_Desk();
else
calibration();
}
}
...
}
Since you made three very distant intervals though, I think that this part better suits your needs:
if ((lastPressTime > 100) && (lastPressTime < 7000))
snooze();
else if ((lastPressTime > 12000) && (lastPressTime < 20000))
conf_Desk();
else if ((lastPressTime > 26000) && (lastPressTime < 40000))
calibration();
So you define validity ranges, so if someone presses the button for 10 seconds nothing happens (this is useful because if someone presses the button for 14.9 seconds in the previous code it will trigger the snooze function).
i would use a simple state machine structure with two global vars to avoid complex nested logic:
int buttonDown = 0;
unsigned long buttonStart;
void loop(){
int snapshot = digitalRead(buttonPin);
if(!buttonDown && snapshot ){ //pressed, reset time
buttonDown = 1; // no longer unpressed
buttonStart = millis(); // when it was pressed
}
if(buttonDown && !snapshot ){ //released, count time
buttonDown = 0; // no longer pressed
int duration = millis() - buttonStart; // how long since pressed?
// now the "event part"
if(duration>30000) return calibration();
if(duration>15000) return conf_Desk();
snooze();
}
sleep(1); // or whatever
}
Interrupt service routines should be a short as possible. You don't have to wait inside the ISR and suspend your main loop for seconds.
Just use two different ISRs for a rising and a falling edge.
When the button is pressed, ISR1 starts a timer, when it is released ISR2 stop it and triggers whatever is necessary depending on the time passed.
Make sure your button is debounced.
https://www.arduino.cc/en/Reference/attachInterrupt
Another way to do this is with a pointer-to-function based state machine.
The advantage on this is that you can easily introduce more functionalities to your button (say, another function called at 45 seconds).
try this:
typedef void(*state)();
#define pressed (millis() - lastPressed)
void waitPress();
void momentPress();
void shortPress();
void longPress();
state State = waitPress;
unsigned long lastPressed;
int buttonState;
int buttonPin = 7;// or whathever pin you use
void snooze(){} // stubs for your functions
void conf_Desk(){}
void callibration(){}
void waitPress()
{
if (buttonState == HIGH)
{
lastPressed = millis();
State = momentPress;
return;
}
else
return;
}
void momentPress()
{
if (buttonState == LOW)
{
snooze();
State = waitPress;
return;
}
if (pressed > 15000)
State = shortPress;
return;
return;
}
void shortPress()
{
if (buttonState == LOW)
{
conf_Desk();
return;
}
if (pressed > 30000)
State = longPress;
return;
return;
}
void longPress()
{
if (buttonState == LOW)
{
callibration();
return;
}
return;
}
void loop()
{
buttonState = digitalRead(buttonPin);
State();
}
I'm making a stopwatch with an arduino with a vibration motor and a simple button as the interface.
I'm coming to a hold with this project as it just won't work, i've been testing it on my uno with no success so far, I was wondering if someone could give me a quick little run-down and see if they can spot any big issues that i've overlooked.
No errors in code, I think it might be a logical error on my behalf or possibly even an error with my board, but I doubt that!
void setup() {
Serial.begin(9600);
}
int lengthOf(int i)
{
if (i < 0){i = -i;}
if (i < 10){ return 1;}
if (i < 100){ return 2;}
if (i < 1000){ return 3;}
if (i < 10000){ return 4;}
if (i < 100000){ return 5;}
if (i < 1000000){ return 6;}
if (i < 10000000){ return 7;}
if (i < 100000000){ return 8;}
if (i < 1000000000){ return 9;}
return 10;
}
void loop() {
int ButtonSwitch = 4;
pinMode(4, INPUT);
int motor = 5;
int timerA = 0; int timed;
bool checker = false; //checker acts to see if the current state is timing/counting
bool shown; //Shown acts as a check to show if the time has already been shown
if (analogRead(ButtonSwitch) == HIGH && checker == false)//When the button is pressed and the state is false then
{
checker = true;//sets checker to true, meaning the timing should begin
shown = false;//sets the shown variable to false so as to
timerA = 0;//reset timer to a 0 value
}
while (checker == true)//while the timer is active then do the following
{
timerA++;//Increment the timer
if (digitalRead(ButtonSwitch) == HIGH)
{
checker = false;
break;
}
delay(1000);//No needfor the simpleTimer library as I don't need to run any code inbetween each second
}
//Sets the timed to the real value of
timed = lengthOf(timerA);//Grabs the length of timerA (1223 would be 4)
int recTime[timed - 1]; //creates an array of the same length as the timer
//append int to chars and by extent an array
char str[timed];
sprintf(str, "%d", timerA);
int numbers;
for (int i = 0; i < timed; i++)
{
recTime[i] = str[i] - '0';//This grabs STR which is an empty array of the length of the time then sets recTime to be the same
}
while ((analogRead(ButtonSwitch) == LOW) && (checker == false) && (shown == false))//Loop checks that the button is not pressed, the checker is false, and that the time has not been shown,
{
for (int i = 0; i < timed; i++)//
{
for (int o = 0; o < recTime[i]; o++)
{
digitalWrite(motor, HIGH);//Motor set to vibrate
delay(500);//1/2 second delay
digitalWrite(motor, LOW);//motor off
delay(300);//3/10 second delay
}
delay(3000);//3 second delay
}
shown = true;
}
}
If there's any more info I can provide then let me know,
any help is greatly appreciated!
I've read through your code and have a few suggestions. First, let's be consistent and use a digitalRead instead of the analogRead near the top and bottom of the loop routine. Let's move all of your variable declarations before the setup routine. (you will still need to initialize timerA, checker and shown at the top of loop) Also, you only need to set the pinMode for the button once, so we'll move that line to the setup routine and, while we're at it, let's use the variable ButtonSwitch in the pinMode command. e.g.
int ButtonSwitch = 4;
int motor = 5;
int timerA = 0;
int timed;
bool checker = false;
bool shown;
void setup() {
Serial.begin(9600);
pinMode(ButtonSwitch, INPUT);
}
But I think the real problem could be with the logical flow of the program. Assume the loop starts, checker is false and the button is not pressed. When you press the button, checker gets set to true. Immediately after that, within a few clocks ticks, the next block is entered (because checker is true) and it sets checker to false and exits the block if the button is still being pressed. The chances of you releasing the button between the first block and the second is pretty much zero. So your program just keeps setting timerA to 0, then incrementing it by 1 and moving on. The delay(1000) is never reached and timerA never gets above 1, the recTime array will always be defined as recTime[0] and the str array will always be defined as str[1].
In the second block, the while loop, I think you want to change
if (digitalRead(ButtonSwitch) == HIGH) to if (digitalRead(ButtonSwitch) == LOW).
I think if you rectify these issues, you'll be a long way towards getting git working!
In C, when timed is the number of chars needed to write the decimal digits of a number, then you should allocate one more for the terminating '\0'.
char str[timed+1];
Then, I'm wondering what you're doing with the recTime values. Indeed, whenever for a value of 123, str would contain "123", i.e. ['1','2','3'], then retTime would contain [1,2,3].