// SeveralThingsAtTheSameTimeRev1.ino
// from:
// https://forum.arduino.cc/t/demonstration-code-for-several-things-at-the-same-time/217158/1
// https://forum.arduino.cc/t/demonstration-code-for-several-things-at-the-same-time/217158/2
// implemented on a Wokwi simulation per
// https://forum.arduino.cc/t/circuit-with-1-button-and-3-leds/1134897/3
// Wokwi: https://wokwi.com/projects/366664987128970241
// Note that the Adafruit series at https://learn.adafruit.com/multi-tasking-the-arduino-part-1/all-together-now
// expands on this to do two steppers using classes
// An implementation of the class-based Adafruit one in Wokwi is at
// https://wokwi.com/projects/397172393690134529
// An expansion of the BlinkWithoutDelay concept to illustrate how a script
// can appear to do several things at the same time
// this sketch does the following
// it blinks the onboard LED (as in the blinkWithoutDelay sketch)
// it blinks two external LEDs (LedA and LedB) that are connected to pins 12 and 11.
// it turns another Led (buttonLed connected to pin 10) on or off whenever a button
// connected to pin 7 is pressed
// it sweeps a servo (connected to pin 5) back and forth at different speeds
// One leg of each LED should be connected to the relevant pin and the other leg should be connected to a
// resistor of 470 ohms or more and the other end of the resistor to the Arduino GND.
// If the LED doesn't light its probably connected the wrong way round.
// On my Uno and Mega the "button" is just a piece of wire inserted into pin 7.
// Touching the end of the wire with a moist finger is sufficient to cause the switching action
// Of course a proper press-on-release-off button switch could also be used!
// The Arduino is not capable of supplying enough 5v power to operate a servo
// The servo should have it's own power supply and the power supply Ground should
// be connected to the Arduino Ground.
// The sketch is written to illustrate a few different programming features.
// The use of many functions with short pieces of code.
// Short pieces of code are much easier to follow and debug
// The use of variables to record the state of something (e.g. onBoardLedState) as a means to
// enable the different functions to determine what to do.
// The use of millis() to manage the timing of activities
// The definition of all numbers used by the program at the top of the sketch where
// they can easily be found if they need to be changed
//=======
// -----LIBRARIES
#include <Servo.h>
// ----CONSTANTS (won't change)
const int onBoardLedPin = 13; // the pin numbers for the LEDs
const int led_A_Pin = 12;
const int led_B_Pin = 11;
const int buttonLed_Pin = 10;
const int buttonPin = 7; // the pin number for the button
const int servoPin = 5; // the pin number for the servo signal
const int onBoardLedInterval = 500; // number of millisecs between blinks
const int led_A_Interval = 2500;
const int led_B_Interval = 4500;
const int blinkDuration = 500; // number of millisecs that Led's are on - all three leds use this
const int buttonInterval = 300; // number of millisecs between button readings
const int servoMinDegrees = 20; // the limits to servo movement
const int servoMaxDegrees = 150;
//------- VARIABLES (will change)
byte onBoardLedState = LOW; // used to record whether the LEDs are on or off
byte led_A_State = LOW; // LOW = off
byte led_B_State = LOW;
byte buttonLed_State = LOW;
Servo myservo; // create servo object to control a servo
int servoPosition = 90; // the current angle of the servo - starting at 90.
int servoSlowInterval = 80; // millisecs between servo moves
int servoFastInterval = 10;
int servoInterval = servoSlowInterval; // initial millisecs between servo moves
int servoDegrees = 2; // amount servo moves at each step
// will be changed to negative value for movement in the other direction
unsigned long currentMillis = 0; // stores the value of millis() in each iteration of loop()
unsigned long previousOnBoardLedMillis = 0; // will store last time the LED was updated
unsigned long previousLed_A_Millis = 0;
unsigned long previousLed_B_Millis = 0;
unsigned long previousButtonMillis = 0; // time when button press last checked
unsigned long previousServoMillis = 0; // the time when the servo was last moved
//========
void setup() {
Serial.begin(9600);
Serial.println("Starting SeveralThingsAtTheSameTimeRev1.ino"); // so we know what sketch is running
// set the Led pins as output:
pinMode(onBoardLedPin, OUTPUT);
pinMode(led_A_Pin, OUTPUT);
pinMode(led_B_Pin, OUTPUT);
pinMode(buttonLed_Pin, OUTPUT);
// set the button pin as input with a pullup resistor to ensure it defaults to HIGH
pinMode(buttonPin, INPUT_PULLUP);
myservo.write(servoPosition); // sets the initial position
myservo.attach(servoPin);
}
//=======
void loop() {
// Notice that none of the action happens in loop() apart from reading millis()
// it just calls the functions that have the action code
currentMillis = millis(); // capture the latest value of millis()
// this is equivalent to noting the time from a clock
// use the same time for all LED flashes to keep them synchronized
readButton(); // call the functions that do the work
updateOnBoardLedState();
updateLed_A_State();
updateLed_B_State();
switchLeds();
servoSweep();
}
//========
void updateOnBoardLedState() {
if (onBoardLedState == LOW) {
// if the Led is off, we must wait for the interval to expire before turning it on
if (currentMillis - previousOnBoardLedMillis >= onBoardLedInterval) {
// time is up, so change the state to HIGH
onBoardLedState = HIGH;
// and save the time when we made the change
previousOnBoardLedMillis += onBoardLedInterval;
// NOTE: The previous line could alternatively be
// previousOnBoardLedMillis = currentMillis
// which is the style used in the BlinkWithoutDelay example sketch
// Adding on the interval is a better way to ensure that succesive periods are identical
}
}
else { // i.e. if onBoardLedState is HIGH
// if the Led is on, we must wait for the duration to expire before turning it off
if (currentMillis - previousOnBoardLedMillis >= blinkDuration) {
// time is up, so change the state to LOW
onBoardLedState = LOW;
// and save the time when we made the change
previousOnBoardLedMillis += blinkDuration;
}
}
}
//=======
void updateLed_A_State() {
if (led_A_State == LOW) {
if (currentMillis - previousLed_A_Millis >= led_A_Interval) {
led_A_State = HIGH;
previousLed_A_Millis += led_A_Interval;
}
}
else {
if (currentMillis - previousLed_A_Millis >= blinkDuration) {
led_A_State = LOW;
previousLed_A_Millis += blinkDuration;
}
}
}
//=======
void updateLed_B_State() {
if (led_B_State == LOW) {
if (currentMillis - previousLed_B_Millis >= led_B_Interval) {
led_B_State = HIGH;
previousLed_B_Millis += led_B_Interval;
}
}
else {
if (currentMillis - previousLed_B_Millis >= blinkDuration) {
led_B_State = LOW;
previousLed_B_Millis += blinkDuration;
}
}
}
//========
void switchLeds() {
// this is the code that actually switches the LEDs on and off
digitalWrite(onBoardLedPin, onBoardLedState);
digitalWrite(led_A_Pin, led_A_State);
digitalWrite(led_B_Pin, led_B_State);
digitalWrite(buttonLed_Pin, buttonLed_State);
}
//=======
void readButton() {
// this only reads the button state after the button interval has elapsed
// this avoids multiple flashes if the button bounces
// every time the button is pressed it changes buttonLed_State causing the Led to go on or off
// Notice that there is no need to synchronize this use of millis() with the flashing Leds
if (millis() - previousButtonMillis >= buttonInterval) {
if (digitalRead(buttonPin) == LOW) {
buttonLed_State = ! buttonLed_State; // this changes it to LOW if it was HIGH
// and to HIGH if it was LOW
previousButtonMillis += buttonInterval;
}
}
}
//========
void servoSweep() {
// this is similar to the servo sweep example except that it uses millis() rather than delay()
// nothing happens unless the interval has expired
// the value of currentMillis was set in loop()
if (currentMillis - previousServoMillis >= servoInterval) {
// its time for another move
previousServoMillis += servoInterval;
servoPosition = servoPosition + servoDegrees; // servoDegrees might be negative
if (servoPosition <= servoMinDegrees) {
// when the servo gets to its minimum position change the interval to change the speed
if (servoInterval == servoSlowInterval) {
servoInterval = servoFastInterval;
}
else {
servoInterval = servoSlowInterval;
}
}
if ((servoPosition >= servoMaxDegrees) || (servoPosition <= servoMinDegrees)) {
// if the servo is at either extreme change the sign of the degrees to make it move the other way
servoDegrees = - servoDegrees; // reverse direction
// and update the position to ensure it is within range
servoPosition = servoPosition + servoDegrees;
}
// make the servo move to the next position
myservo.write(servoPosition);
// and record the time when the move happened
}
}
//=====END