/*
* FILENAME: Gray_ButtonTogglerTimer.ino
*
* Code by: Gray Mack
* License: MIT License
* https://choosealicense.com/licenses/mit/
* Created: 10/12/2022
* One Line Description: Demonstrate intermediate Arduino techniques using some libraries
* Board: Arduino Uno R3 compatible such as Inventr.io Hero board
* Select Board: Arduino UNO
* Other Hardware: Keypad from "30 Days Lost In Space kit" or similar parts (I have no affiliation with this vendor)
* Simulation at: https://wokwi.com/projects/345363308263506516
* Source control at: TBD
* Video documentation: TBD
* I don't respond to IM. No specific support is offered though I am in a few Facebook arduino forums.
*
* Detailed Description:
* Use a button debouncer to toggle LEDs on or off.
* I only need 3 buttons, but I had a 16 button matrix. Thus, only using left column of buttons labled s5,s9,s13
* Demonstrate the arduino-timer library which was the simplest timer library I could find for non repeating tasks
* The LEDs can either be toggled by buttons or go off on their own after a few seconds
* Using a mini speaker or piezo element is optional
* No delays used
*
* Exercise 1: What would happen if a button was held down past the duration of the turn off timer?
* Will the LED stay on or go off? When it is finally released, would anything special happen?
* Exercies 2: See end of code
* Exercise 3: Add a fourth button+LED
* Exercise 4: The tone command takes 3 arguments: pin, frequency, and optionally how long to stay on in milliseconds, 0=forever
* It runs in the background and does not hold up the execution regardless of the duration in constant SPEAKER_BEEP_MS
* Can you make the last tone stay on but then call noTone(SPEAKER_PIN) when any goes off?
* It will probably be annoying.
* Exercies 5: Add the TonePitch library and using this example https://github.com/RodrigoDornelles/arduino-tone-pitch/blob/master/examples/ArduinoTone/ArduinoTone.ino
* play standard musical notes like NOTE_C4 instead of 800
* Exercise 6: Add another LED and make it blink every second using the timer example https://github.com/contrem/arduino-timer/blob/master/examples/blink/blink.ino
* (It should not interfeer with the existing behavior.)
* Note the example use of a little trick: digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); // toggle the LED
* This works on the UNO, however may not work on other platforms such as ESP processors
* This is because some processors do not reliably digitalRead a pin that is in OUTPUT mode.
* Can you change it to use a State variable instead?
* Exercise 7: Uncomment the block of code called "Zip sounds"
* Now replace the tone() in the turn on/off functions with for example StartSoundZip(RED_TONE_FREQ, +25);
* For led off functions, down bend the frequency with -25 and for on functions, up bend with +25
* Don't forget to add SoundZip.tick(); in loop() so the timer can do it's thing
*
* Rev History:
* 10/12/2022 initial code creation
* //2022 ...
*/
// ----[ configuration ]------------------------------------
// ----[ included libraries ]------------------------------------
#include <Bounce2.h> // https://github.com/thomasfredericks/Bounce2/ tested with version 2.71.0
#include <arduino-timer.h> // https://github.com/contrem/arduino-timer tested with version 2.3.1
// ----[ pin definitions ]------------------------------------
const int BUTTON_RED_PIN = 2;
const int BUTTON_GREEN_PIN = 3;
const int BUTTON_BLUE_PIN = 4;
const int LED_RED_PIN = 6;
const int LED_GREEN_PIN = 7;
const int LED_BLUE_PIN = 8;
// LED_BUILTIN is already defined as pin 13 on UNO
const int SPEAKER_PIN = A5; // analog pin can also be used as digital out, also known as pin 18
// ----[ constants ]------------------------------------
const unsigned long AUTO_LIGHT_OFF_MS = 10000L;
const bool TIMER_NO_REPEAT = false;
const int SPEAKER_BEEP_MS = 20;
const int RED_TONE_FREQ = 800;
const int GREEN_TONE_FREQ = 1000;
const int BLUE_TONE_FREQ = 1200;
// ----[ global variables ]------------------------------------
Bounce ButtonRed = Bounce();
Bounce ButtonGreen = Bounce();
Bounce ButtonBlue = Bounce();
int RedState = LOW;
int GreenState = LOW;
int BlueState = LOW;
Timer<1, millis> RedTimer;
Timer<1, millis> GreenTimer;
Timer<1, millis> BlueTimer;
// ----[ code ]------------------------------------
void setup() {
pinMode(LED_RED_PIN, OUTPUT);
TurnRedOff(nullptr);
pinMode(LED_GREEN_PIN, OUTPUT);
TurnGreenOff(nullptr);
pinMode(LED_BLUE_PIN, OUTPUT);
TurnBlueOff(nullptr);
ButtonRed.attach(BUTTON_RED_PIN, INPUT_PULLUP);
ButtonGreen.attach(BUTTON_GREEN_PIN, INPUT_PULLUP);
ButtonBlue.attach(BUTTON_BLUE_PIN, INPUT_PULLUP);
pinMode(SPEAKER_PIN, OUTPUT);
digitalWrite(SPEAKER_PIN, LOW); // caution low ohm load, do not write HIGH, tone command seems ok to use
}
/* See Exercise 7
// Zip sounds - instead of beep, make pitch bend zip tones
int StartSoundFrequency;
int CurrentSoundFrequency;
int SoundDirection;
Timer<1, millis> SoundZip; // a timer to bend the note up or down
bool SoundZipTone(void *)
{
CurrentSoundFrequency += SoundDirection; // a positive or negative number
if( CurrentSoundFrequency > StartSoundFrequency+500
|| CurrentSoundFrequency < StartSoundFrequency-500
|| CurrentSoundFrequency > 10000
|| CurrentSoundFrequency < 0)
{
noTone(SPEAKER_PIN);
return false; // stop
}
else{
tone(SPEAKER_PIN, CurrentSoundFrequency);
return true;
}
}
void StartSoundZip(int StartFreq, int StartDirection)
{
SoundZip.cancel();
StartSoundFrequency = StartFreq;
CurrentSoundFrequency = StartFreq;
SoundDirection = StartDirection;
SoundZip.every(5, SoundZipTone);
}
// end of block Zip sounds
*/
// Because this can called by the timer library,
// it is required to take some object pointer (which is not needed for my use) and return a bool
// to call it manually use: TurnRedOff(nullptr)
bool TurnRedOff(void *)
{
RedState = LOW;
digitalWrite(LED_RED_PIN, RedState);
tone(SPEAKER_PIN,RED_TONE_FREQ,SPEAKER_BEEP_MS); // speaker beep
RedTimer.cancel(); // if manually turned off then we don't need the timer to turn it off later, no harm if the timer itself called this
return TIMER_NO_REPEAT; // instruction to the timer library
}
void TurnRedOn()
{
RedState = HIGH;
digitalWrite(LED_RED_PIN, RedState);
tone(SPEAKER_PIN,RED_TONE_FREQ,SPEAKER_BEEP_MS);
RedTimer.in(AUTO_LIGHT_OFF_MS, TurnRedOff);
}
bool TurnGreenOff(void *)
{
GreenState = LOW;
digitalWrite(LED_GREEN_PIN, GreenState);
tone(SPEAKER_PIN,GREEN_TONE_FREQ,SPEAKER_BEEP_MS);
GreenTimer.cancel();
return TIMER_NO_REPEAT;
}
void TurnGreenOn()
{
GreenState = HIGH;
digitalWrite(LED_GREEN_PIN, GreenState);
tone(SPEAKER_PIN,GREEN_TONE_FREQ,SPEAKER_BEEP_MS);
GreenTimer.in(AUTO_LIGHT_OFF_MS, TurnGreenOff);
}
bool TurnBlueOff(void *)
{
BlueState = LOW;
digitalWrite(LED_BLUE_PIN, BlueState);
tone(SPEAKER_PIN,BLUE_TONE_FREQ,SPEAKER_BEEP_MS);
BlueTimer.cancel();
return TIMER_NO_REPEAT;
}
void TurnBlueOn()
{
BlueState = HIGH;
digitalWrite(LED_BLUE_PIN, BlueState);
tone(SPEAKER_PIN,BLUE_TONE_FREQ,SPEAKER_BEEP_MS);
BlueTimer.in(AUTO_LIGHT_OFF_MS, TurnBlueOff);
}
void loop() {
ButtonRed.update();
if(ButtonRed.fell())
{
if(RedState == LOW)
TurnRedOn();
else
TurnRedOff(nullptr);
}
ButtonGreen.update();
if(ButtonGreen.fell())
{
if(GreenState == LOW)
TurnGreenOn();
else
TurnGreenOff(nullptr);
}
ButtonBlue.update();
if(ButtonBlue.fell())
{
if(BlueState == LOW)
TurnBlueOn();
else
TurnBlueOff(nullptr);
}
RedTimer.tick();
GreenTimer.tick();
BlueTimer.tick();
//If we dig down in arduino source, we see HIGH == 1 == true and LOW == 0 == false
//This is pretty fundamental in many arduino toolchains and would certainly never change
//So this will make builtin led be true/HIGH if any LED is true/HIGH
digitalWrite(LED_BUILTIN, RedState || GreenState || BlueState); // logical or
// Exercise 2: explain what would happen if this were changed to any of the following
//digitalWrite(LED_BUILTIN, RedState | GreenState | BlueState); // bitwise or
//digitalWrite(LED_BUILTIN, RedState & GreenState & BlueState); // bitwise and
//digitalWrite(LED_BUILTIN, RedState ^ GreenState ^ BlueState); // bitwise xor
//digitalWrite(LED_BUILTIN, (RedState & GreenState) | (GreenState & BlueState));
}