// https://wokwi.com/projects/394997170662617089
#include <mechButton.h>
#include <Servo.h>
#include <squareWave.h>
#include <mapper.h>
#define lEDG1 6 // Pin numbers..
#define lEDG2 9
#define lEDG3 8
#define lEDP1 7
#define bz1 3
#define SERVO_PIN 11
#define MODE1_SEC 45 // Durations of the different modes.
#define MODE2_SEC 30
#define MODE3_SEC 15
#define MAX_PERIOD 750 // How long & close are beeps at start.
#define MIN_PERIOD 200 // At the end.
#define FREQ 600 // What frequency in Hz are they.
#define SERVO_HOME_POS 0 // When the servo is sitting idle put it here.
#define SERVO_ACTIVE_POS 90 // When active, here is good.
#define SERVO_WAIT_MS 1000 // Wait this long before returning it home.
// ***********************************************************
//
// An object that beeps slow then fast depending on time.
//
// ***********************************************************
class alarm : public squareWave {
public :
alarm(void);
virtual ~alarm(void);
void start(float inSec);
void stop(void);
virtual void pulseOn(void);
virtual void pulseOff(void);
mapper* signalMapper; // Know the fraction left? Gives period.
timeObj* signalTimer; // Used to watch the looping time.
};
// Constructor. Creates it's mapper and timer objects.
alarm::alarm(void) {
signalMapper = new mapper(1,0,MAX_PERIOD,MIN_PERIOD);
signalTimer = new timeObj(10,false);
}
// Destructor, recycles the mapper and timer objects.
alarm::~alarm(void) {
delete(signalMapper);
delete(signalTimer);
}
// User calls this to start all this nonsense.
void alarm::start(float inSec) {
float newPeriod;
signalTimer->setTime(inSec*1000); // Set & start timer in Ms.
newPeriod = signalMapper->map(1); // Map period of a full tank of time.
setPeriod(newPeriod); // Set the period to our underlying squarewave.
setPercent(50); // GIve it 50% pulsewidth.
setOnOff(true); // Give 'er a kick and start the machine!
}
// User call this to shut down the machine.
void alarm::stop(void) {
noTone(bz1); // If it's beeping, for lord sakes shut it up!
digitalWrite(lEDP1,LOW); // Purple LED off!
setOnOff(false); // Pull the plug on the machinery.
}
// When the underlying square wave goes high, it calls this for us.
// Us, as in we are the alarm.
void alarm::pulseOn(void) {
float fractionLeft;
float newPeriod;
unsigned long duration;
if (signalTimer->ding()) { // Are we out of time?
stop(); // Shut everything down.
return; // And exit.
}
digitalWrite(lEDP1,HIGH); // Still here? Fire up the LED.
fractionLeft = signalTimer->getFraction(); // How much is in the time tank?
newPeriod = signalMapper->map(fractionLeft); // Using that, map to a new period value.
setPeriod(newPeriod); // Set in the new period.
setPercent(50); // Set in the 50% duty cycle.
duration = newPeriod/2; // Calculate duration of sound.
tone(bz1,FREQ,duration); // Fire up the noise!
}
// When the underlying square wave goes low, it calls this for us.
void alarm::pulseOff(void) {
digitalWrite(lEDP1,LOW); // LED off!
noTone(bz1); // Sound off! (Should'nt be necessary)
if (signalTimer->ding()) stop(); // If we've run out of time.. SHut 'er down!
}
// ***********************************************************
//
// Main program starts here.
//
// ***********************************************************
enum state { waiting, looping, doingServo }; // What CAN we focus on.
state ourState; // What ARE we focused on.
enum mode { mode1, mode2, mode3 }; // What modes do we have.
mode ourMode; // What mode has been choosen.
mechButton bt1(4); // Can cancel the timer loop
mechButton bt2(5); // Selects the mode.
mechButton bt3(10); // begins the loop
Servo servo_11; // Servo for.. something?
timeObj btn1Timer(1000,false); // How long in ms IS holding it down?
alarm ourSignal; // Chopped tone generator.
timeObj servoTimer(SERVO_WAIT_MS,false); // How long to wait to reset servo.
void setup() {
Serial.begin(9600); // Fire up the serial port.
pinMode(lEDG1,OUTPUT); // Set all the pinmodes that arn't automatic.
pinMode(lEDG2,OUTPUT); //
pinMode(lEDG3,OUTPUT); //
pinMode(lEDP1,OUTPUT); //
pinMode(bz1,OUTPUT); //
servo_11.attach(SERVO_PIN); // Set up the servo..
servo_11.write(SERVO_HOME_POS); // Send to to it's home position.
bt1.setCallback(btn1Clk); // Attach the buttons to their callback finctions.
bt2.setCallback(btn2Clk); //
bt3.setCallback(btn3Clk); //
ourState = waiting; // Set our initial state.
ourMode = mode1; // Set our initial mode.
showMode(); // Show the mode on the LEDs
}
// Called when bt1 changes state.
void btn1Clk(void) {
if (!bt1.getState()) { // If the button went down..
btn1Timer.start(); // We start it's long press timer.
Serial.println("Button 1 clicked."); // Amuse the user a little.
} else { // Else, the button went up!
if (!btn1Timer.ding()) { // If the timer has not expired..
btn1Timer.reset(); // Reset the timer and move on.
}
}
}
// Called when bt2 changes state.
void btn2Clk(void) {
if (!bt2.getState()) { // If the button went down..
if (ourState==waiting) { // If we're just sittin/ around smokeing cigarettes.
switch(ourMode) { // Check our mode of..
case mode1 : ourMode = mode2; break; // Go through and increment the mode.
case mode2 : ourMode = mode3; break; //
case mode3 : ourMode = mode1; break; //
} //
showMode(); // SHow the result.
} else { // Else, we are busy..
Serial.println("Not changing modes while running.");
}
Serial.println("Button 2 clicked.");
}
}
// Called when bt3 changes state.
void btn3Clk(void) {
if (!bt3.getState()) { // If the button went down..
if (ourState==waiting) { // If we're just sittin' here causing trouble.
startLooping(); // Start the alarm looping thing!
} else { // Else, we're busy. Tell user to shush!
Serial.println("Umm.. Already running a pattern.");
}
Serial.println("Button 3 clicked.");
}
}
// Start the looping!
void startLooping(void) {
switch(ourMode) {
case mode1 : ourSignal.start(MODE1_SEC); break;
case mode2 : ourSignal.start(MODE2_SEC); break;
case mode3 : ourSignal.start(MODE3_SEC); break;
}
ourState = looping;
}
// Shuts off the noise!
void stopLooping(void) {
ourSignal.stop(); // Shut off
ourState = waiting; // Mark us as currently waiting.
}
// Set the LEDs to match our mode. 1, 2 or 3
void showMode(void) {
switch(ourMode) {
digitalWrite(lEDP1,LOW);
case mode1 :
digitalWrite(lEDG1,HIGH);
digitalWrite(lEDG2,LOW);
digitalWrite(lEDG3,LOW);
break;
case mode2 :
digitalWrite(lEDG1,LOW);
digitalWrite(lEDG2,HIGH);
digitalWrite(lEDG3,LOW);
break;
case mode3 :
digitalWrite(lEDG1,LOW);
digitalWrite(lEDG2,LOW);
digitalWrite(lEDG3,HIGH);
break;
}
}
void loop() {
idle(); // Runs background things. Buttons, alarm buzzers..
switch(ourState) { // If our state is..
case waiting : return; // Waiting? Exit, nothing to do here.
case looping : // Looping?
if (btn1Timer.ding()) { // If they are holding down "stop" button..
stopLooping(); // Stop the looping!
btn1Timer.reset(); // Reset the timer.
ourState = waiting; // We bailed, no servo, just waiting.
} else if (!ourSignal.running()) { // Else, if the alarm as finished..
servo_11.write(SERVO_ACTIVE_POS); // Move the servo!
servoTimer.start(); // Set it's timer.
ourState = doingServo; // Now we are doing the servo!
} //
break; //
case doingServo : // doingServo?
if (servoTimer.ding()) { // If it's timer has gone 'ding'..
servo_11.write(SERVO_HOME_POS); // Send it back to home position.
servoTimer.reset(); // Reset it's timer.
ourState = waiting; // We're back to cigarettes and coffee.
} //
break; //
} //
}