Arduino double clap sensing code breaking - arduino

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,

Related

Need help for making the cycle-sequence of a program

I'm working on a dishwasher program that can implement few modes, well like the hot water, long mode; hot water, short mode; cold water, long mode; fruit wash mode, etc.
Problem is, on my final project presentation (on design stage, not even into coding yet), I presented on how I implement this with basically real pre-wash/wash/rinse/dry cycles on a dishwasher. It was before I realized if that's actually complicating things, a lot, and unfortunately I have to implement it anyway, since I presented it like so.
Using ESP32 DEVKIT V1 DOIT here, if you're wondering.
What I got confused now, I already made how the cycles works, which below (it's incomplete, yet, and not even modified much as I don't know to implement the prewash/wash/rinse/dry cycle well).
switch (currProg) {
// set up parameters for selected wash program:
//water level, prewash time, wash time, wash Temperature , rinse time, rinse temp, dry time, dry Temperature
case hotFastProg:washprogconfig (high,5,15,60,10, 60,10,70); break;
case hotLongProg:washprogconfig (high,10,25,60,10, 60,10,70); break;
case coldFastProg:washprogconfig (high,5,15,60,10, 60,10,70); break;
case coldLongProg:washprogconfig (high,10,25,60,10, 60,10,70); break;
case fruitProg:washprogconfig (high,5,15,60,10, 60,10,70); break;
}
//below, how we can put those parameters above to the cycles.
void washprogconfig (int inwaterLevel, int inprewashTime, int inwashTime, float inwashTemp, int inrinseTime, float inrinseTemp, int indryTime, float indryTemp)
{
waterLevel = inwaterLevel;
prewashTime = inprewashTime;
washTime = inwashTime;
rinseTime = inrinseTime;
dryTime = indryTime;
washTemp = inwashTemp;
rinseTemp = inrinseTemp;
dryTemp = indryTemp;
}
//switch between cycles (drain, fill, prewash, wash, rinse, dry)
void switchmode ( int induration, String newmsg)
{
String outmsg = " mode started";
prevMode = currMode;
startTime = timeNow;
endTime = startTime + induration;
outmsg = ": " + newmsg + outmsg;
digitalWrite (outlet, switchOff);
digitalWrite (washPump, switchOff);
digitalWrite (inlet, switchOff);
digitalWrite (heater, switchOff);
heaterStatus = false;
}
////// ------------------------- few lines of codes that's unnecesary to show (for showing text)
// record the time
timeNow = millis() / timingFactor;
// calculate remaining progtime (to be honest, on this part I didn't get it much, it just works on the past before I modify it. hence probably it'd be wrong)
remainProgTime = 0;
remainProgTime += (endTime - timeNow); // remprogrime = remprogtrime + (endtime-timenow)
switch (currMode) {
case fillin : remainProgTime += (washTime + drainTime);
break;
case preWash : remainProgTime += drainTime;
break;
case wash : remainProgTime += drainTime;
break;
case rinse : remainProgTime += drainTime;
break;
case drain: //nothing to add
break;
}
// add the time of the remaining full cycles and the Dry Time
remainProgTime += ( fillTime + prewashTime + washTime + rinseTime + drainTime) + dryTime;
////// ------------------------- few lines of codes that's unnecesary to show
//(but at least I can get it done on the cycle modes, still lots of mistake ofc tho)
switch (currMode)
{
// ----------------------------------- START
case progStart:
if (currMode != prevMode) {
switchmode (0, "Program Dimulai");
}
currMode = drain;
break;
// ------------------------------ DRAIN
case drain:
if (currMode != prevMode) {
switchmode (drainTime, "Drain");
delay (2000);
digitalWrite (outlet, switchOn);
}
else if (waterLevel == empty || (currProg == testProg && timeNow >= endTime))
{ // drain completed
currCycle ++;
if (currCycle <= nbrCycle)
currMode = fillin;
else
if (dryTime > 0)
currMode = dry;
else
currMode = complete;
}
else if (timeNow >= endTime) {
currMode = errPause; //Error
}
break;
// ------------------------------ FILL
case fillin:
if (currMode != prevMode) {
switchmode (fillTime, "fill");
digitalWrite (inlet, switchOn); // open the inlet to let water in from the hose.
}
else if (waterLevel == high or (currProg == testProg and timeNow >= endTime))
currMode = wash;
else if (timeNow >= endTime) {
currMode = errPause; // Error
}
break;
// ------------------------- PREWASH
case prewash:
if (currMode != prevMode)
{
switchmode (prewashTime, "Pre-Wash");
digitalWrite (washPump, switchOn);
}
else if (timeNow >= endTime)
{
currMode = drain;
}
break;
// ------------------------ WASH
case wash:
if (currMode != prevMode)
{
switchmode (washTime, "Wash");
//tambah buat sabun
digitalWrite (washPump, switchOn);
}
else if (timeNow >= endTime)
{
currMode = drain;
}
if (washTemp > 0)
{ // turn off heater if above max. Temp.
if ( waterTemp > (washTemp + marginTemp) ) {
digitalWrite (heater, switchOff);
heaterStatus = false;
}
// turn on heater if below max. Temp
else if ( waterTemp < (washTemp - marginTemp) ) {
digitalWrite (heater, switchOn);
heaterStatus = true;
}
}
break;
// ----------------------------- RINSE
case rinse:
if (currMode != prevMode)
{
switchmode (rinseTime, "Rinse");
digitalWrite (washPump, switchOn);
}
else if (timeNow >= endTime)
{
currMode = drain;
}
if (rinseTemp > 0)
{ // turn off heater if above max. Temp.
if ( waterTemp > (rinseTemp + marginTemp) ) {
digitalWrite (heater, switchOff);
heaterStatus = false;
}
// turn on heater if below max. Temp
else if ( waterTemp < (rinseTemp - marginTemp) ) {
digitalWrite (heater, switchOn);
heaterStatus = true;
}
}
break;
//------------------------------- DRY
case dry:
if (currMode != prevMode) {
switchmode (dryTime, "Dry");
}
else if (timeNow >= endTime)
currMode = complete;
if (dryTemp > 0)
{ // turn off heater if above max. Temp.
if ( waterTemp > (dryTemp + marginTemp) ) {
digitalWrite (dryer, switchOff);
}
// turn on heater if below max. Temp
else if ( waterTemp < (dryTemp - marginTemp) ) {
digitalWrite (dryer, switchOn);
}
}
break;
case complete:
switchmode (0, "Program Selesai");
pressedButton = 0;
currentPos = 1;
updateMenu();
currMode = progSelect;
menuNo = 0;
prevProg =1;
break;
case errPause:
switchmode (0, "Program Ditunda");
pressedButton = 0;
currentPos = 3;
updateMenu();
currMode = standby;
menuNo = 1;
prevProg =1;
break;
delay (100);
}
Here's what I want, in dishwashing (which, actually wash the dishes) program, I want to make the cycle runs like this:
Start -> drain -> fill -> prewash -> drain -> fill -> wash -> drain -> fill -> rinse -> drain -> dry -> done.
While on the fruit one, I just want to run it like this.
Start -> drain -> fill -> prewash -> drain -> done.
Basically the program before I 'mess' it, well, the program went well on only 'wash', without the additional pre-wash and rinse.
How can I implement the cycle like so? I'm looking online for references, but unfortunately there's no explaining on how can I use multiple modes, mostly just running on one fixed mode. I'm kind of confused with that so far.
Any recommendation and suggestion would be greatly appreciated.
Thanks in advance.
This is a difficult question to answer because there are countless ways to approach it, but it's an assigned project so there might be specific patterns you're supposed to demonstrate
*I'm going to assume this is supposed to show OOP fundamentals, so I'll avoid stuff like method references and task communication primitives (which is how I would probably implement a lot of this normally)
I would start with a simple interface that defines all the things the dishwasher can do in a "physical" sense, so drain/wash/heat up/ etc.:
class Dishwasher{
setWaterTemp(temperature) //True starts prewash, false ends prewash
setDraining(enabled) //True starts draining, false ends draining
setPrewash(enabled) //True starts prewash, false ends prewash
setDrying(enabled)
}
Then define a cycle with how long it should take, and what it does to the dishwasher:
class CycleAction{
int howLongToRun;
virtual void run(dishwasher);
}
For example:
class PrewashAction{
constructor(howLongToRun)
override void run(dw){
dw.setPrewashing(true)
vTaskDelay(howLongToRun)
dw.setPrewashing(false)
}
}
class TemperatureAction{
constructor(howLongToRun, whatTempToUse)
override void run(dw){
dw.setTemperature(whatTempToUse)
vTaskDelay(howLongToRun)
}
}
class DrainAction{
constructor(howLongToRun)
override void run(dw){
dw.setDraining(true)
vTaskDelay(howLongToRun)
dw.setDraining(false)
}
}
Now we can define a Program as a list of actions:
class WashProgram{
virtual vector<CycleAction> getActions()
int startingTime;
void startCycle(dishwasher){
startingTime = currentTime
Loop Through GetActions
action.run();
}
getTotalTime(){
totalTime = 0
Loop Through this->getActions()
totalTime += action.
}
int remainingTime(){
return currentTime - startingTime
}
}
Now your Programs are easy to define and update:
class FruitWashProgram : WashProgram{
override void getActions(){
return {
SetTemperatureAction(100 degrees),
DrainAction(2 minutes),
FillAction(2 minutes),
PrewashAction(3 minutes),
DrainAction(2 minutes)
}
}
}
class QuickHotCycle : WashProgram{
override void getActions(){
return {
SetTemperatureAction(250 degrees),
DrainAction(1 minutes),
FillAction(1 minutes),
WashAction(1 minutes),
DrainAction(2 minutes)
}
}
}
class QuickCold : WashProgram{
override void getActions(){
return {
SetTemperatureAction(60 degrees),
DrainAction(1 minutes),
FillAction(1 minutes),
WashAction(1 minutes),
DrainAction(2 minutes)
}
}
}
Now there is a ton of room for improvement, but I leave that as an exercise for you. Some things to think about:
A real washing machine has "fuzzy logic", each step won't take a predefined amount of time but will depend on factors like how hot the tap water was when it started. You could replicate that by adding an "estimate" function to CycleAction
Some of the programs will be very similar. QuickHot and QuickCold for example. Maybe you can make a QuickProgram, then extend that with specific temperatures for Hot and Cold
Maybe certain steps need something to have happened before running. For example, you don't want to dry unless a drain has occurred. You could enforce that in a "validate" function somewhere, or add a way for the dishwasher to communicate it's in an error state
I used psuedo-code that doesn't really match any language, so if you need me to clarify anything just drop a comment and I'll explain as best as I can

How can I exit a 'for' loop in the main Arduino loop with a serial command?

I need to exit a 'for' loop running in the main loop on an Arduino board through a serial command, but it looks like that up to the end of the main loop the Serial Event is not taken in consideration. How can I solve this?
I supply some simplified code to have a better understanding of the problem:
boolean start = false;
boolean abortLoop = false;
String inputString = "";
void setup() {
Serial.begin(9600);
}
void loop() {
if(start) {
Serial.println("start");
for(int i=0; i <10; i++) {
if(abortLoop)
break;
Serial.println(i);
delay(1000);
}
start = false;
abortLoop = false;
}
}
void serialEvent()
{
while (Serial.available())
{
// Get the new byte:
char inChar = (char)Serial.read();
// Add it to the inputString:
inputString += inChar;
//Serial.println(inputString);
// If the incoming character is a newline, set a flag
// so the main loop can do something about it:
if (inChar == '\n')
{
inputString = inputString.substring(0, inputString.length() - 1);
//Serial.println(inputString);
//Serial.println(inputString.length());
if (inputString[0] == 's')
{
Serial.println("start");
start = true;
}
if (inputString[0] == 'a')
{
abortLoop = true;
Serial.println("abort");
}
inputString = "";
}
}
}
If sending the command 'a' in the middle of the loop, the loop keeps going up to the end.
The result is:
start
start
0
1
2
3
4
5
6
7
8
9
abort
abort
In this case, the 'a' command was sent two times.
The solution is pretty simple. The only thing you have to do is to call your function in the for loop, else the function can't be executed.
// ...
for(int i=0; i <10; i++) {
serialEvent();
if(abortLoop)
break;
Serial.println(i);
delay(1000);
}
// ...
Moreover, I think it's better if you call your function at this place too:
// ...
void loop() {
serialEvent();
if(start) {
// ...
It's strangely not necessary, but in my opinion it's better. And 'start' is printed out two times, because you call it two times. One time here:
void loop() {
if(start) {
Serial.println("start");
for(int i=0; i <10; i++) {
and another time here:
if (inputString[0] == 'a')
{
abortLoop = true;
Serial.println("abort");
}
so you can remove one of them.

Use a DS3231 RTC module and Arduino to fire function every other day

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);
}

Sparkfun Pro Micro (ATMega32u4) as a controller of an USB Keyboard

Im trying to use a Sparkfun Pro Micro as a controller for my keyboard. I can't achieve a normal keyboard's behaviour. This is my code:
#include <Keyboard.h>
int firstButtonPin = 18, secondButtonPin = 19;
char firstButtonChar = 'z', secondButtonChar = 'x';
bool firstButtonPressed = false, secondButtonPressed = true;
void setup() {
pinMode(firstButtonPin, INPUT);
pinMode(secondButtonPin, INPUT);
Keyboard.begin();
}
void loop() {
if(digitalRead(firstButtonPin) == HIGH && firstButtonPressed == false) Keyboard.press(firstButtonChar), firstButtonPressed = true;
else if(digitalRead(firstButtonPin) != HIGH && firstButtonPressed) Keyboard.release(firstButtonChar), firstButtonPressed = false;
if(digitalRead(secondButtonPin) == HIGH && secondButtonPressed == false) Keyboard.press(secondButtonChar), secondButtonPressed = true;
else if(digitalRead(secondButtonPin) != HIGH && secondButtonPressed) Keyboard.release(secondButtonChar), secondButtonPressed = false;
}
YT link: https://youtu.be/VfHNOtq4HHo.
As you can see, normal keyboard outputs a single key and after some time it spams. When i switch AVR, i get spam all the time (it looks like the keys are not being holded, just pressed and released in a very short period of time. How can I achieve a real keyboard's behaviour? I'd like to use it in games like Osu!. I hope you can help me. Thanks in advance
The problem was button bouncing.
u can use millis() to handle debouncing.
uint32_t time = millis(); //must be 32 bit or greater
uint8_t debounceTime = 50 //this is in milli seconds
void loop() {
if (millis() - time > debounceTime) {
//put your if-else-if code here (for keypress and release)
time = millis();
}
}
you have play around with the debounceTime. for most buttons 50 milli-seconds will do. but if you are using keyboard switches (like cherry mx, gateron etc) you can go as low as 5 milli-seconds

Arduino not entering for loop

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.

Resources