#include <Bounce2.h> // Include the debouncing library
const int buttonPin = 9; // Pin for the pushbutton
const int ledPin = 3; // Pin for the LED
const int motorPin = 10; // Pin for the vibrating motor
Bounce2::Button button = Bounce2::Button();//button class of bounce2 library to debounce pushbutton
int rstate[20];//array to store the state of the pushbutton when recorded in repeaterState
int rduration[20];//array to store the duration of the state of the pushbutton when recorded in repeaterMode
int recordIndex=0;//integer to iterate through arrays of record
bool waitingState=true;//flag to keep track and turn waitingState
bool gameState=false;
bool longPress=false;//flag to escape int owaitingState or into gameState
bool repeaterState=false;//flag to keep track and turn repeaterState
bool doubleClick=false;//flag to initiate repeater state
bool isDoubleClicked=false;
bool serialWrite=false;//flag to lessen the amount of serial writing in waitingState
int clickCount = 0;//integer to keep track of to sense double click
unsigned long lastClick = 0;//variable to keep track of the the time first click in the repeaterState
unsigned long doubleClickInterval = 500; // change this to the desired time in milliseconds to be accepted as sensible double click
bool recordStarted=false;//flag to start or stop recording user input
unsigned long lastPressTime=0; //variable to store when the button pressed lastly
unsigned long buttonPressStart=0;//variable to store first button press time to keep track of recording time
unsigned long playRecordPreviousMillis = 0;
bool firstButtonClick=false;//flag to initiate first button press time
int rsize = 0;//initiate zero integer to be equla to theedited array size
bool playingEnabled=false;//flag to initiate playing of the record
void toggleState(String flagname) {
// Toggle the game logic state
waitingState = !waitingState;
if (flagname=="gameState"){
gameState=!gameState;
}
if(flagname == "repeaterState"){
repeaterState=!repeaterState;
}
if (flagname ==""){
//do nothing
if(!gameState){
gameState=!gameState;
}
}
}
void printBoolean(String name, bool value) {
Serial.print(name); // Print the name of the variable
Serial.print(": "); // Print a separator
if (value) {
Serial.println("True");
} else {
Serial.println("False");
}
}
void printVariable(String name, int value){
Serial.print(name);// Print the name of the variable
Serial.print(": ");// Print a seperator
Serial.print(value);//Print the value of the variable
Serial.print("\n");//Print new line character
}
void printString(String name, String value){
Serial.print(name);// Print the name of the variable
Serial.print(": ");// Print a seperator
Serial.print(value);//Print the value of the variable
Serial.print("\n");//Print new line character
}
void vibrate(int numVibrations, int vibrationDuration, int delayDuration, int motorPower) {
// Set the start time for the first vibration
for (int i = 0; i < numVibrations; i++) {
unsigned long startTime = millis();
// Turn on the vibration motor
analogWrite(motorPin, motorPower);
// Wait until the vibrationDuration has passed
while (millis() - startTime < vibrationDuration) {
// Do nothing
}
Serial.print("buzz. \n");
// Turn off the vibration motor
analogWrite(motorPin, 0);
// Wait until the delayDuration has passed
unsigned long elapsedTime = millis();
if(numVibrations>1){
while (millis() - elapsedTime < delayDuration) {
// Do nothing
}
}
}
}
void blinkLEDInterval(int blinkDuration, int waitingTime) {
static unsigned long previousMillis = 0;
unsigned long currentMillis = millis();
if (currentMillis - previousMillis >= blinkDuration) {
digitalWrite(ledPin, !digitalRead(ledPin));
previousMillis = currentMillis;
serialWrite=true;
return serialWrite;
}
if (currentMillis - previousMillis >= waitingTime) {
previousMillis = currentMillis;
serialWrite=false;
return serialWrite;
}
}
void checkLongPress(int duration, String flagname){
if((button.released() && (button.previousDuration() >= duration))){
vibrate(1,100,300,165);
longPress=true; //toogle the flag to change state of the toy
toggleState(flagname);
wait(1000);
return longPress;
} else {
longPress=false;
return longPress;
}
}
void checkForDoubleClick(int waitingTime) {
button.update();
if ( button.released() ) {
if (millis() - lastClick < waitingTime) {
// double click detected
lastClick = 0;
doubleClick = true;
vibrate(1,100,300,165);
} else {
lastClick = millis();
doubleClick = false;
}
}
}
void wait(int duration) {
unsigned long waitStartTime = millis(); // Record the current time
while (millis() - waitStartTime < duration) { // Wait until the desired duration has passed
// Do nothing
}
}
void recordButton(){
vibrate(1,100,300,165);
printBoolean("RB Recording started", recordStarted);//print the state whether recording started
button.update();//update the debouncer button
unsigned long startTime = millis(); // store the current time
unsigned long recordDuration = 10000; // 10 seconds in milliseconds of recording
for (unsigned long i = 0; i < recordDuration; i = millis() - startTime) {//for loop to iterate over 10 seconds to record user input
button.update();//update the debouncer button
lastPressTime=millis();
/*vibrate simultanously when the button pressed to give user feedback*/
if (button.isPressed()){
// Turn on the vibration motor
analogWrite(motorPin, 170);
lastPressTime=millis();
} else {
//Turn off the vibration motor
analogWrite(motorPin, 0);
}
if (recordStarted&&!isArrayEmpty(rsize,20)){//if record flag turned and recording started
//printBoolean("Record has started with first click",firstButtonClick);
//printVariable("Recording Start Time", buttonPressStart);
int recordLength=millis()-buttonPressStart;//keep track of the record duration
if (button.released()&&button.changed()){//if button is released and state has changed previously
/*Add recordInput string button pressed and after , letter duration of the state*/
rstate[recordIndex]=1;//add "1" to string and comma seperator
rduration[recordIndex]=button.previousDuration();//add duration in milliseconds to array
recordIndex++;//increment the index number to store new state
/*Serial print the state and duration*/
Serial.println("1,"+String(button.previousDuration()));
//Serial.print(String(millis()-buttonPressStart)+"\n");
firstButtonClick=true;
lastPressTime = millis();//update last button press time to keep track of inactivity
}
if (button.pressed()&&button.changed()){//if button is pressed and state has changed previously
/*Add recordInput string button released and after , letter duration of the state*/
rstate[recordIndex]=0;//add "1" to string and comma seperator
rduration[recordIndex]=button.previousDuration();//add duration in milliseconds to array
recordIndex++;//add duration in milliseconds to array
/*Serial print the state and duration*/
Serial.println("0,"+String(button.previousDuration()));
firstButtonClick=true;
//Serial.print(String(millis()-buttonPressStart)+"\n");
}
/*Check for long press to escape into waiting mode*/
checkLongPress(5000,"repeaterState");
if (longPress){
//firstButtonClick=false;
recordStarted=false;
playingEnabled=false;
return;
}
if(recordLength>=recordDuration){//if record duration is over 10 seconds
Serial.println("Record Duration Has Passed");
recordStarted=false;//toggle the record started flag
playingEnabled=false;
return;
}
/*If there's inactivity of at least 8 second, stop recording*/
if (millis()-lastPressTime>=8000){
recordStarted=false;
playingEnabled=false;
Serial.print("Record ended for waiting 8 seconds.\n");
return;
}
}
}
recordStarted=false;//toggle the flag to escape
playingEnabled=true;
return;
}
void removeZeroes(int array1[],int array2[], int size) {
while (array1[0] != 1) {
for (int i = 0; i < size - 1; i++) {
array1[i] = array1[i + 1];
array2[i] = array2[i+1];
}
size--;
}
while (array1[size - 1] != 1) {
size--;
}
rsize = size;
return rsize;
}
void printArray(String arrayName, int array[], int size) {
Serial.print(arrayName);
Serial.print(": ");
for (int i = 0; i < size; i++) {
Serial.print(array[i]);
if (i < size - 1) {
Serial.print(", ");
}
}
Serial.println();
}
void emptyArray(int array[], int size) {
for (int i = 0; i < size; i++) {
array[i] = 0;
}
}
bool isArrayEmpty(int array[], int size) {
for (int i = 0; i < size; i++) {
if (array[i] != 0) {
return false;
}
}
return true;
}
void waitMode(){
if (serialWrite){
printBoolean("Waiting State", waitingState);
printBoolean("Game State", gameState);
printBoolean("Repeater State", repeaterState);
printBoolean("Long Press", longPress);
printBoolean("Double Click", doubleClick);
printBoolean("isDoubleClicked",isDoubleClicked);
Serial.print("Waiting...\n"); // prints the state
serialWrite=false;
}
blinkLEDInterval(400,600);//Blink LED slowly while the toy is in waiting state
//blinkLEDSlow();
}
void playRecording(int vibrationArray[], int durationArray[], int size, int quantTime) {
Serial.print("Playing recorded input...\n");
for (int i = 0; i < size; i++) {
if (vibrationArray[i]==1){
digitalWrite(motorPin,HIGH);
//Serial.print("-");
//wait(durationArray[i]);
for (int j = 0; j < ceil(durationArray[i]/quantTime); j++) {
Serial.print("*");
wait(quantTime);
}
} else {
digitalWrite(motorPin, LOW);
//wait(durationArray[i]);
for (int j = 0; j < ceil(durationArray[i]/quantTime); j++) {
Serial.print("_");
wait(quantTime);
}
}
}
}
bool playedOnce=false;
void repeaterMode(){
button.update();//update debouncer button
serialWrite=true;//flag to keep serial printing speed sensible
// Turn on the LED
digitalWrite(ledPin, HIGH);
if (isArrayEmpty(rstate,rsize)){
firstButtonClick=false;
recordStarted=true;
playingEnabled=false;
Serial.print("There is no recording.\n");
} else{
recordStarted=false;
playingEnabled=true;
Serial.print("There is a recording. \n");
//printArray("Button State", rstate, rsize);
//printArray("State Duration", rduration, rsize);
}
if (recordStarted){//if the recording flag is on start recording
recordButton();//functon to record the state and the suration of the state
}
removeZeroes(rstate, rduration, 20);
Serial.print("Recorded Arrays-> \n");
printArray("Button State", rstate, rsize);
printArray("State Duration", rduration, rsize);
bool arrayEmpty = isArrayEmpty(rstate,rsize);
if (playingEnabled){
playRecording(rstate, rduration, rsize, 65);
Serial.print("\n");
playingEnabled=false;
playedOnce=true;
}
if (!playingEnabled&&playedOnce){
if(!isArrayEmpty(rstate,rsize)){
emptyArray(rstate, rsize);
emptyArray(rduration, rsize);
recordStarted=true;
playedOnce=false;
}
}
checkLongPress(5000,"repeaterState");
if (longPress){
Serial.print("__________\n");
printBoolean("Waiting State", waitingState);
printBoolean("Game State", gameState);
printBoolean("Repeater State", repeaterState);
printBoolean("Long Press", longPress);
printBoolean("Double Click", doubleClick);
printBoolean("isDoubleClicked",isDoubleClicked);
playingEnabled=false;
recordStarted=false;
doubleClick=false;
emptyArray(rstate, rsize);
emptyArray(rduration, rsize);
return;
//break;
}
}
void setup() {
pinMode(buttonPin, INPUT_PULLUP); // Set the button pin as an input with a pull-up resistor
pinMode(ledPin, OUTPUT); // Set the LED pin as an output
pinMode(motorPin, OUTPUT); // Set the motor pin as an output
/*Debouncing button*/
button.attach(buttonPin, INPUT_PULLUP);
button.interval(5);
button.setPressedState(LOW);
Serial.begin(9600); // open the serial port at 9600 bps:
randomSeed(analogRead(0)); // Seed the random number generator with a value read from an analog input pin
}
void loop() {
button.update();//update debouncer button
checkLongPress(3000,"");
checkForDoubleClick(1200);
/*If a double click is happened and flag is true*/
if (doubleClick){
//repeaterState=true;
printBoolean("Repeater State", repeaterState);//print the state of the repeater mode
/*If this is a first double click*/
if(!isDoubleClicked){
toggleState("repeaterState");//toggle the repeater state flag
}
doubleClick=false;
isDoubleClicked=true;
}
if (waitingState && !repeaterState){
waitMode();
checkLongPress(3000,"gameState");
} else if (!waitingState && repeaterState){
repeaterMode();
//checkForDoubleClick(1200);
checkLongPress(3000, "repeaterState");
} else {
//A state that couldnt be anticipated will result in here then will switch to waiting mode.
//Appropriate measures can be taken with flags.
Serial.println("Purgatory...");
printBoolean("Waiting State", waitingState);
printBoolean("Repeater State", repeaterState);
printBoolean("Long Press", longPress);
printBoolean("Double Click", doubleClick);
printBoolean("isDoubleClicked",isDoubleClicked);
gameState=false;
repeaterState=false;
}
}