// https://wokwi.com/projects/409770554156515329
// https://forum.arduino.cc/t/toggle-switch-state/1303593
//================================================^================================================
//
// https://forum.arduino.cc/t/toggle-switch-state/1303593
//
//
//
// Version YY/MM/DD Comments
// ======= ======== ========================================================================
// 1.00 24/09/20 Running code
// 1.10 24/09/21 Minor changes, added comments, baud ate changed to 115200
// 1.20 24/09/21 Added IR code, added LDR code
// 1.30 24/09/22 Added IR keypad button controls for LarryD's remote, delete these as needed
// 1.40 24/09/22 A switch operation is now reversed
// 1.50 24/09/22 Modified so any change in the toggle switch state operates the servo
//
//
// Notes:
//
#include <IRremote.hpp>
#include <Servo.h>
//================================================
#define LEDon HIGH //PIN---[220R]---A[LED]K---GND
#define LEDoff LOW
#define PRESSED LOW //+5V---[Internal 50k]---PIN---[Switch]---GND
#define RELEASED HIGH
#define CLOSED LOW //+5V---[Internal 50k]---PIN---[Switch]---GND
#define OPENED HIGH
#define ENABLED true
#define DISABLED false
#define MAXIMUM 180
#define MINIMUM 0
// millis() / micros() B a s e d T I M E R S
//================================================^================================================
//To keep the sketch tidy, you can put this structure in a different tab in the IDE
//
//These TIMER objects are non-blocking
struct makeTIMER
{
#define MILLIS 0
#define MICROS 1
#define ENABLED true
#define DISABLED false
#define YES true
#define NO false
#define STILLtiming 0
#define EXPIRED 1
#define TIMERdisabled 2
//these are the bare minimum "members" needed when defining a TIMER
byte TimerType; //what kind of TIMER is this? MILLIS/MICROS
unsigned long Time; //when the TIMER started
unsigned long Interval; //delay time which we are looking for
bool TimerFlag; //is the TIMER enabled ? ENABLED/DISABLED
bool Restart; //do we restart this TIMER ? YES/NO
//================================================
//condition returned: STILLtiming (0), EXPIRED (1) or TIMERdisabled (2)
//function to check the state of our TIMER ex: if(myTimer.checkTIMER() == EXPIRED);
byte checkTIMER()
{
//========================
//is this TIMER enabled ?
if (TimerFlag == ENABLED)
{
//========================
//has this TIMER expired ?
if (getTime() - Time >= Interval)
{
//========================
//should this TIMER restart again?
if (Restart == YES)
{
//restart this TIMER
Time = getTime();
}
//this TIMER has expired
return EXPIRED;
}
//========================
else
{
//this TIMER has not expired
return STILLtiming;
}
} //END of if (TimerFlag == ENABLED)
//========================
else
{
//this TIMER is disabled
return TIMERdisabled;
}
} //END of checkTime()
//================================================
//function to enable and restart this TIMER ex: myTimer.enableRestartTIMER();
void enableRestartTIMER()
{
TimerFlag = ENABLED;
//restart this TIMER
Time = getTime();
} //END of enableRestartTIMER()
//================================================
//function to disable this TIMER ex: myTimer.disableTIMER();
void disableTIMER()
{
TimerFlag = DISABLED;
} //END of disableTIMER()
//================================================
//function to restart this TIMER ex: myTimer.restartTIMER();
void restartTIMER()
{
Time = getTime();
} //END of restartTIMER()
//================================================
//function to force this TIMER to expire ex: myTimer.expireTimer();
void expireTimer()
{
//force this TIMER to expire
Time = getTime() - Interval;
} //END of expireTimer()
//================================================
//function to set the Interval for this TIMER ex: myTimer.setInterval(100);
void setInterval(unsigned long value)
{
//set the Interval
Interval = value;
} //END of setInterval()
//================================================
//function to return the current time
unsigned long getTime()
{
//return the time i.e. millis() or micros()
//========================
if (TimerType == MILLIS)
{
return millis();
}
//========================
else
{
return micros();
}
} //END of getTime()
}; //END of struct makeTIMER
// D e f i n e a l l a r e T I M E R S
//================================================^================================================
/*example
//========================
makeTIMER toggleLED =
{
MILLIS/MICROS, 0, 500ul, ENABLED/DISABLED, YES/NO //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
TIMER functions we can access:
toggleLED.checkTIMER();
toggleLED.enableRestartTIMER();
toggleLED.disableTIMER();
toggleLED.expireTimer();
toggleLED.setInterval(100ul);
*/
//========================
makeTIMER heartbeatTIMER =
{
MILLIS, 0, 250ul, ENABLED, YES //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
//========================
makeTIMER switchesTIMER =
{
MILLIS, 0, 50ul, ENABLED, YES //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
//========================
makeTIMER CHECKirTIMER =
{
MICROS, 0, 1000ul, ENABLED, YES //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
//========================
makeTIMER servoTIMER =
{
MILLIS, 0, 10ul, DISABLED, YES //.TimerType, .Time, .Interval, .TimerFlag, .Restart
};
// G P I O s
//================================================^================================================
//
const byte photoPin = A0;
const byte IRpin = 2;
const byte toggleSwitchPin = 5;
const byte servoPin = 9;
const byte heartbeatLED = 13;
// V A R I A B L E S
//================================================^================================================
//
Servo servo;
bool photoSensor = DISABLED; //LDR
byte lastButtonState = RELEASED;
int currentServoPosition = 0;
int destinationServoPosition = MINIMUM;
// s e t u p ( )
//================================================^================================================
void setup()
{
Serial.begin(115200);
IrReceiver.begin(IRpin);
pinMode(toggleSwitchPin, INPUT_PULLUP);
digitalWrite(heartbeatLED, LEDoff);
pinMode(heartbeatLED, OUTPUT);
//========================
//connect the servo, it moves to 90°
servo.attach(servoPin);
delay(1000);
currentServoPosition = 90;
destinationServoPosition = MINIMUM;
Serial.print("Servo position = ");
Serial.println(currentServoPosition);
//enabled the TIMER to allow the servo to step
servoTIMER.enableRestartTIMER();
} //END of setup()
// l o o p ( )
//================================================^================================================
void loop()
{
//======================================================================== T I M E R heartbeatLED
//condition returned: STILLtiming, EXPIRED or TIMERdisabled
//is it time to toggle the heartbeat LED ?
if (heartbeatTIMER.checkTIMER() == EXPIRED)
{
//toggle the heartbeat LED
digitalWrite(heartbeatLED, digitalRead(heartbeatLED) == HIGH ? LOW : HIGH);
}
//======================================================================== T I M E R switches
//condition returned: STILLtiming, EXPIRED or TIMERdisabled
//is it time to check our switches ?
if (switchesTIMER.checkTIMER() == EXPIRED)
{
checkSwitches();
}
//======================================================================== T I M E R CHECKir
//condition returned: STILLtiming, EXPIRED or TIMERdisabled
//is it time to check for an IR code ?
if (CHECKirTIMER.checkTIMER() == EXPIRED)
{
checkIRcode();
}
//======================================================================== T I M E R servo
//condition returned: STILLtiming, EXPIRED or TIMERdisabled
//if this TIMER is enabled, is it time to move the servo 1 step ?
if (servoTIMER.checkTIMER() == EXPIRED)
{
//go CCW to maximum position ?
if (destinationServoPosition == MAXIMUM && currentServoPosition <= destinationServoPosition)
{
//move one step CCW
servo.write(currentServoPosition);
Serial.print("CCW, servo position = ");
Serial.println(currentServoPosition);
Serial.print("Destination = ");
Serial.println(destinationServoPosition);
//get ready for the next step
currentServoPosition++;
//don't go over 180
if (currentServoPosition > destinationServoPosition)
{
//re-adjust
currentServoPosition = 180;
Serial.println("We are at maximum, LDR disabled");
//we have arrived at our destination, disable this TIMER
servoTIMER.disableTIMER();
//cancel an LDR pending control
photoSensor = DISABLED;
}
}
//go CW to minimum position ?
else if (destinationServoPosition == MINIMUM && currentServoPosition >= destinationServoPosition)
{
//move one step CW
servo.write(currentServoPosition);
Serial.print("CW, servo position = ");
Serial.println(currentServoPosition);
Serial.print("Destination = ");
Serial.println(destinationServoPosition);
//get ready for the next step
currentServoPosition--;
//don't go under 0
if (currentServoPosition < 0)
{
//re-adjust
currentServoPosition = 0;
Serial.println("We are at minimum, LDR disabled");
//we have arrived at our destination, disable this TIMER
servoTIMER.disableTIMER();
//cancel an LDR pending control
photoSensor = DISABLED;
}
}
}
//================================================
//other non blocking code goes here
//================================================
} //END of loop()
// c h e c k I R c o d e ( )
//================================================^================================================
void checkIRcode()
{
//========================
if (IrReceiver.decode())
{
static unsigned long rawData;
//is this a repeat code ?
if (IrReceiver.decodedIRData.decodedRawData == 0)
{
IrReceiver.resume();
return;
}
//get the new IR code
rawData = IrReceiver.decodedIRData.decodedRawData;
// Serial.print("\nIR Receive code = ");
// Serial.println(rawData, HEX);
//============ LDR ON
//when we are not at maximum, check for IR code to enable LDR
//... changed! so it is keys 7, 8 and 9
// if (destinationServoPosition == MINIMUM && (rawData == 0xB847FF00 || rawData == 0xBA45FF00 || rawData == 0xBF40FF00))
//... say more aboiut this
if (destinationServoPosition == MINIMUM && (rawData == 0xBD42FF00 || rawData == 0xB54AFF00 || rawData == 0xAD52FF00))
{
if (photoSensor == ENABLED) Serial.println("LDR still ENABLED");
else {
photoSensor = ENABLED;
Serial.println("LDR got ENABLED");
}
}
//============ go to 180° ?
//Check for other IR codes (servo control)
else if (destinationServoPosition == MINIMUM && (rawData == 0xBC43FF00 || rawData == 0xF609FF00 || rawData == 0xB946FF00))
{
destinationServoPosition = MAXIMUM;
//enabled the TIMER to allow the servo to step
servoTIMER.enableRestartTIMER();
}
//============ go to 0° ?
else if (destinationServoPosition == MAXIMUM && (rawData == 0xBB44FF00 || rawData == 0xF807FF00 || rawData == 0xEA15FF00))
{
destinationServoPosition = MINIMUM;
//enabled the TIMER to allow the servo to step
servoTIMER.enableRestartTIMER();
}
IrReceiver.resume();
} //END of if (receiver.decode())
} //END of checkIRcode()
// c h e c k S w i t c h e s ( )
//================================================^================================================
void checkSwitches()
{
byte pinState;
int LDRvalue;
//======================================================================== photoSensor
//are we allowed to read the LDR ?
if (photoSensor == ENABLED)
{
LDRvalue = analogRead(photoPin);
//have we reached the trigger point ?
if (LDRvalue > 300)
{
//we have now reached the trigger point, disable the LDR
photoSensor = DISABLED;
destinationServoPosition = MAXIMUM;
//enabled the TIMER to allow the servo to step
servoTIMER.enableRestartTIMER();
}
}
//======================================================================== toggleSwitchPin
pinState = digitalRead(toggleSwitchPin);
//===================================
//has this switch changed state ?
if (lastButtonState != pinState)
{
//update to this new state
lastButtonState = pinState;
//========================
//the switch has changed state
//cancel any LDR pending control
photoSensor = DISABLED;
Serial.println("\nSwitch is released");
//============
//toggle the servo destination ?
if (destinationServoPosition == MINIMUM)
{
destinationServoPosition = MAXIMUM;
}
//============
//toggle the servo destination ?
else if (destinationServoPosition == MAXIMUM)
{
destinationServoPosition = MINIMUM;
}
//allow the servo to step
servoTIMER.enableRestartTIMER();
} //END of toggleSwitchPin
//======================================================================== Next Switch
} //END of checkSwitches()
//================================================^================================================