#include <Servo.h>
#include "CatDX4001.h"
// https://wokwi.com/projects/368049679345794049
// A recipe for non-blocking arduino code can be found here:
// https://github.com/nosknut/arduino-course-v2023/blob/main/IELET1002/ArduinoExampleCode/FromBlockingToNonBlocking/FromBlockingToNonBlocking.ino
/////////////////////////////////////
///////// Custom Data Types /////////
/////////////////////////////////////
// NB! Custom data types in C++ (Arduino) must be defined before any
// functions, or the compiler will give an error saying the data type is not defined.
// Normally, all variables and data types would be defined at the top of the file,
// but an exception is made for this example to make it easier to follow.
// Is used to make setting the state of the LED indicator more readable.
// Using enum class instead of enum because it is safer and groups the enum
// values under the enum name.
// In prectice the difference between enim and enum is the way it is used:
// LedIndicatorMode::CASE_1 instead of just CASE_1.
// If you are interested in the reasons why you should use one over the other, see the post below:
// https://www.geeksforgeeks.org/enum-classes-in-c-and-their-advantage-over-enum-datatype/
enum class LedIndicatorMode
{
OFF,
CASE_1,
CASE_2,
CASE_3,
CASE_4
};
enum class HatchStatus
{
OPENING,
CLOSING,
OPEN,
CLOSED
};
enum class RgbMode
{
OFF,
PULSE,
GREEN,
PULSE_GREEN,
PULSE_YELLOW_ORANGE,
CYCLE
};
/////////////////////////////////////
//////////// Button A State /////////
/////////////////////////////////////
const int BUTTON_A_PIN = 2;
// The debounced state of the button
bool buttonAState = false;
// The time when the button state last changed
unsigned long debounceTimer = 0;
unsigned long debounceTimeout = 50;
// Updates the global variable buttonAState with
// the current debounced state of the button.
// Do not call this function directly. Use updateButtonA instead.
// https://docs.arduino.cc/built-in-examples/digital/Debounce
void updateButtonAState()
{
if ((millis() - debounceTimer) < debounceTimeout)
{
// Exit the function if the timeout has not expired
return;
}
bool newState = digitalRead(BUTTON_A_PIN);
if (newState != buttonAState)
{
// Reset the debounce timer if the state has changed
debounceTimer = millis();
}
// Update the global variable that
// the rest of the program uses
buttonAState = newState;
}
/////////////////////////////////////
///// Button A Edge Detection ///////
/////////////////////////////////////
/*
Becomes true for one loop when the button state has a rising edge.
A rising edge is when the button state changes from 0 to 1
Example:
if (buttonARisingEdge)
{
// Do something once per button press
}
*/
bool buttonARisingEdge = false;
// The oposite of buttonARisingEdge
bool buttonAFallingEdge = false;
bool buttonAPreviousState = false;
// Updates the global variables buttonARisingEdge and buttonARisingEdge.
// Do not call this function directly. Use updateButtonA instead.
// https://docs.arduino.cc/built-in-examples/digital/StateChangeDetection
void updateButtonAEdgeDetection()
{
// A rising edge is detected when the state is 1 and it used to be 0: 1 && !0
buttonARisingEdge = buttonAState && !buttonAPreviousState;
// A falling edge is detected when the state is 0 and it used to be 1: !0 && 1
buttonAFallingEdge = !buttonAState && buttonAPreviousState;
// Update the previous state for the next time this function is called
buttonAPreviousState = buttonAState;
}
/////////////////////////////////////
//////////// Button A ///////////////
/////////////////////////////////////
void setupButtonA()
{
pinMode(BUTTON_A_PIN, INPUT);
}
// Updates everything needed to use the global variables
// buttonAState, buttonARisingEdge and buttonAFallingEdge.
// Call this function in void loop().
void updateButtonA()
{
updateButtonAState();
updateButtonAEdgeDetection();
}
/////////////////////////////////////
//////////// LED Code ///////////////
/////////////////////////////////////
const int CASE_1_LED_PIN = 4;
const int CASE_2_LED_PIN = 7;
const int CASE_3_LED_PIN = 8;
const int CASE_4_LED_PIN = 9;
// Activates and deactivates the LEDS in the case LED
// indicator based on the provided indicator mode.
// Why use an enum for this instead of an int with the numbers 0 to 4?
// Because it makes the code more readable.
int setCaseLedIndicatorMode(LedIndicatorMode mode)
{
switch (mode)
{
case LedIndicatorMode::OFF:
digitalWrite(CASE_1_LED_PIN, LOW);
digitalWrite(CASE_2_LED_PIN, LOW);
digitalWrite(CASE_3_LED_PIN, LOW);
digitalWrite(CASE_4_LED_PIN, LOW);
break;
case LedIndicatorMode::CASE_1:
digitalWrite(CASE_1_LED_PIN, HIGH);
digitalWrite(CASE_2_LED_PIN, LOW);
digitalWrite(CASE_3_LED_PIN, LOW);
digitalWrite(CASE_4_LED_PIN, LOW);
break;
case LedIndicatorMode::CASE_2:
digitalWrite(CASE_1_LED_PIN, LOW);
digitalWrite(CASE_2_LED_PIN, HIGH);
digitalWrite(CASE_3_LED_PIN, LOW);
digitalWrite(CASE_4_LED_PIN, LOW);
break;
case LedIndicatorMode::CASE_3:
digitalWrite(CASE_1_LED_PIN, LOW);
digitalWrite(CASE_2_LED_PIN, LOW);
digitalWrite(CASE_3_LED_PIN, HIGH);
digitalWrite(CASE_4_LED_PIN, LOW);
break;
case LedIndicatorMode::CASE_4:
digitalWrite(CASE_1_LED_PIN, LOW);
digitalWrite(CASE_2_LED_PIN, LOW);
digitalWrite(CASE_3_LED_PIN, LOW);
digitalWrite(CASE_4_LED_PIN, HIGH);
break;
}
return 1;
}
void setupCaseLedIndicator()
{
pinMode(CASE_1_LED_PIN, OUTPUT);
pinMode(CASE_2_LED_PIN, OUTPUT);
pinMode(CASE_3_LED_PIN, OUTPUT);
pinMode(CASE_4_LED_PIN, OUTPUT);
}
/////////////////////////////////////
///////// Sine Wave Code ////////////
/////////////////////////////////////
// https://www.wikihow.com/Convert-Degrees-to-Radians
double degreesToRadians(double degrees)
{
return (degrees * PI) / 180.0;
}
// Generates a sine wave centered around the center value
// with a max=centerValue+amplitude and min=centerValue-amplitude
// The offset argument lets you add or remove time, and is
// useful if you need multiple sinewaves working together
// In 3 phase AC power for example there are 3 sine waves
// working at a 120 degree offset.
// https://en.wikipedia.org/wiki/Three-phase_electric_power#/media/File:3_phase_AC_waveform.svg
// https://www.google.com/search?q=generate+sine+wave+with+arduino
// Example: https://www.tinkercad.com/things/8wqREUCNY4p
double getSineWave(double amplitude, double frequency, double centerValue, double offset)
{
// Creating a variable for the current moment in time
// given in seconds. Using double as the datatype because
// it can store massive numbers as decimals. The datatype
// for the sin() function argument is also double.
// https://reference.arduino.cc/reference/en/language/variables/data-types/double/
double t = millis() / 1000.0;
// How to use the sin() function: https://reference.arduino.cc/reference/tr/language/functions/trigonometry/sin/
// The formula for a sine wave: https://en.wikipedia.org/wiki/Sine_wave
return (amplitude * sin((2.0 * PI * frequency * t) + degreesToRadians(offset))) + centerValue;
}
/////////////////////////////////////
/////////// Buzzer Code /////////////
/////////////////////////////////////
const int BUZZER_PIN = 10;
unsigned long int case2BuzzCounterTimer = 0;
int case2RemainingBuzzCounter = 0;
bool dryBlendCompleteBuzzActive = false;
void playCaseChangeBuzz()
{
tone(BUZZER_PIN, 500, 10);
}
void playHatchStepBuzz()
{
tone(BUZZER_PIN, 1000, 100);
}
void playHatchClosedBuzz()
{
tone(BUZZER_PIN, 250, 1000);
}
void playCase2ShortBuzz()
{
tone(BUZZER_PIN, 500, 100);
}
void playCase2ShortBuzzes(int buzzCount)
{
case2RemainingBuzzCounter = buzzCount;
}
void updateCase2ShortBuzz()
{
if (case2RemainingBuzzCounter > 0)
{
if ((millis() - case2BuzzCounterTimer) >= 500)
{
playCase2ShortBuzz();
case2RemainingBuzzCounter--;
case2BuzzCounterTimer = millis();
}
}
if (dryBlendCompleteBuzzActive)
{
tone(BUZZER_PIN, getSineWave(250.0, 3.0, 750.0, 0.0));
}
}
void playCase2ConstantBuzz()
{
tone(BUZZER_PIN, 250);
}
void playDryBlendCompleteBuzz()
{
dryBlendCompleteBuzzActive = true;
}
void stopBuzzer()
{
dryBlendCompleteBuzzActive = false;
noTone(BUZZER_PIN);
}
void setupBuzzer()
{
pinMode(BUZZER_PIN, OUTPUT);
}
/////////////////////////////////////
/// Hatch Servo Controller Code /////
/////////////////////////////////////
// The code in this section slowly moves the
// servo to a specific position. It does not
// contain any logic relating to what the target
// position should be.
int hatchServoPosition = 0;
// Use this variable to set the desired servo position
int hatchServoTargetPosition = 0;
const int HATCH_SERVO_PIN = 11;
Servo hatchServo;
unsigned long hatchServoTimer = 0;
unsigned long hatchServoSpeed = 20;
// Moves the hatch servo to hatchServoTargetPosition at a specific speed.
// Call this function in void loop()
void updateHatchServo()
{
if ((millis() - hatchServoTimer) < hatchServoSpeed)
{
// Exit the funtion until the timer finished
return;
}
if (hatchServoPosition > hatchServoTargetPosition)
{
hatchServoPosition -= 1;
}
if (hatchServoPosition < hatchServoTargetPosition)
{
hatchServoPosition += 1;
}
hatchServo.write(hatchServoPosition);
hatchServoTimer = millis();
}
// Call this function in void setup()
void setupHatchServo()
{
hatchServo.attach(HATCH_SERVO_PIN);
}
/////////////////////////////////////
////// Hatch Sequence Code //////////
/////////////////////////////////////
// This code contains the hatch opening and closing sequence.
// The hatch code is split into a servo section and a sequence
// secion because smoothly moving the servo and controlling the
// hatch steps are different tasks that become much more complex
// when combined. Now that these responsibilities are divided,
// we can simply use the hatchServoTargetPosition variable to
// request a specific servo position, and we can trust that
// the servo section will take care of the details.
// Indicates the status of the hatch sequence.
// Do not change this variable directly.
// Use openHatch() and closeHatch() instead.
HatchStatus hatchStatus = HatchStatus::CLOSED;
unsigned long hatchTimer = 0;
unsigned long hatchStepDuration = 15000;
int hatchSequenceStep = 0;
void startHatchSequence()
{
hatchStatus = HatchStatus::OPENING;
}
// Opens and closes the hatch in steps of 10 degrees every 15 seconds.
// Call this function in void loop()
void updateHatchSequence()
{
switch (hatchSequenceStep)
{
//////////////////
////// Idle //////
//////////////////
case 0:
if (hatchStatus == HatchStatus::OPENING)
{
// Continue to the next step
hatchSequenceStep += 1;
}
break;
//////////////////
/// Open Hatch ///
//////////////////
case 1:
playHatchStepBuzz();
hatchServoTargetPosition += 10;
Serial.println("Moving the hatch to: " + String(hatchServoTargetPosition) + " degrees");
hatchTimer = millis();
hatchSequenceStep += 1;
break;
case 2:
if ((millis() - hatchTimer) >= hatchStepDuration)
{
if (hatchServoTargetPosition >= 90)
{
// When the hatch is fully open, continue to the next step
hatchSequenceStep += 1;
}
else
{
// If the hatch is not fully open, go back to the
// previous step and keep opening
hatchSequenceStep -= 1;
}
}
break;
case 3:
hatchServoTargetPosition = 90;
hatchStatus = HatchStatus::OPEN;
Serial.println("Hatch is open!");
hatchTimer = millis();
hatchSequenceStep += 1;
break;
//////////////////
/// Stay Open ////
//////////////////
case 4:
if ((millis() - hatchTimer) >= hatchStepDuration)
{
hatchSequenceStep += 1;
}
break;
//////////////////
/// Close Hatch //
//////////////////
case 5:
Serial.println("Closing hatch ...");
hatchStatus = HatchStatus::CLOSING;
hatchSequenceStep += 1;
break;
case 6:
playHatchStepBuzz();
hatchServoTargetPosition -= 10;
Serial.println("Moving the hatch to: " + String(hatchServoTargetPosition) + " degrees");
hatchTimer = millis();
hatchSequenceStep += 1;
break;
case 7:
if ((millis() - hatchTimer) >= hatchStepDuration)
{
if (hatchServoTargetPosition <= 0)
{
// When the hatch is fully closed, continue to the next step
hatchSequenceStep += 1;
}
else
{
// If the hatch is not fully closed, go back to the
// previous step and keep closing
hatchSequenceStep -= 1;
}
}
break;
case 8:
hatchServoTargetPosition = 0;
hatchStatus = HatchStatus::CLOSED;
Serial.println("Hatch is closed!");
playHatchClosedBuzz();
hatchSequenceStep = 0;
break;
}
}
/////////////////////////////////////
//////// Thermometer Code ///////////
/////////////////////////////////////
const int THERMOMETER_PIN = A0;
int temperature = 0;
// Reads the temperature from the thermometer.
// Call this function in void loop()
void updateThermometer()
{
// Using a potentiometer to emulate the thermometer
// Converting the potmeter value to a range of 0 to 30 degrees
temperature = map(analogRead(THERMOMETER_PIN), 0, 1024, 0, 30);
}
// Run this code in void setup()
void setupThermometer()
{
pinMode(THERMOMETER_PIN, INPUT);
}
/////////////////////////////////////
//////// Light Sensor Code //////////
/////////////////////////////////////
const int LIGHT_SENSOR_PIN = A1;
int lightLevel = 0;
// Reads the light level from the light sensor.
// Call this function in void loop()
void updateLightSensor()
{
// Using a potentiometer to emulate the light sensor
// Converting the potmeter value to a range of 0 to 100
lightLevel = map(analogRead(LIGHT_SENSOR_PIN), 0, 1024, 0, 100);
}
// Run this code in void setup()
void setupLightSensor()
{
pinMode(LIGHT_SENSOR_PIN, INPUT);
}
/////////////////////////////////////
//////////// RGB Code ///////////////
/////////////////////////////////////
const int RGB_RED_PIN = 6;
const int RGB_GREEN_PIN = 5;
const int RGB_BLUE_PIN = 3;
RgbMode rgbMode = RgbMode::OFF;
void updateRgb()
{
switch (rgbMode)
{
case RgbMode::OFF:
analogWrite(RGB_RED_PIN, 0);
analogWrite(RGB_GREEN_PIN, 0);
analogWrite(RGB_BLUE_PIN, 0);
break;
case RgbMode::GREEN:
analogWrite(RGB_RED_PIN, 0);
analogWrite(RGB_GREEN_PIN, 255);
analogWrite(RGB_BLUE_PIN, 0);
break;
case RgbMode::PULSE:
// A quick google search shows that yellow is (R=255, G=255, B=0)
// To pulse between red and yellow we just have to pulse the green color
// https://www.google.com/search?q=rgb+values+for+yellow
// After experimenting with the simulation,
// the best value to produce orange is (R=255, G=40, B=0)
analogWrite(RGB_RED_PIN, 255);
analogWrite(RGB_GREEN_PIN, getSineWave(20.0, 0.5, 20.0, 0.0));
analogWrite(RGB_BLUE_PIN, 0);
break;
case RgbMode::PULSE_YELLOW_ORANGE:
analogWrite(RGB_RED_PIN, 255);
// Yellow is ~60 and orange is ~20 = 40+-20
analogWrite(RGB_GREEN_PIN, getSineWave(20.0, 0.5, 40.0, 0.0));
analogWrite(RGB_BLUE_PIN, 0);
break;
case RgbMode::PULSE_GREEN:
analogWrite(RGB_RED_PIN, 0);
// Pulsing at 100+-100
analogWrite(RGB_GREEN_PIN, getSineWave(100.0, 1.0, 100.0, 0.0));
analogWrite(RGB_BLUE_PIN, 0);
break;
case RgbMode::CYCLE:
// The sine waves look like this: https://www.tinkercad.com/things/8wqREUCNY4p
analogWrite(RGB_RED_PIN, getSineWave(255.0, 1.0, 0.0, 0.0));
analogWrite(RGB_GREEN_PIN, getSineWave(255.0, 1.0, 0.0, 120.0));
analogWrite(RGB_BLUE_PIN, getSineWave(255.0, 1.0, 0.0, 240.0));
break;
}
}
void setupRgb()
{
pinMode(RGB_RED_PIN, OUTPUT);
pinMode(RGB_GREEN_PIN, OUTPUT);
pinMode(RGB_BLUE_PIN, OUTPUT);
}
/////////////////////////////////////
///// DryBlend Sequence Code ////////
/////////////////////////////////////
int dryBledSequenceActive = false;
int dryBlendCounter = 0;
int dryBlendSequenceStep = 0;
unsigned long dryBlendTimer = 0;
void startDryBlendSequence()
{
dryBledSequenceActive = true;
dryBlendSequenceStep = 0;
}
void updateDryBlendSequence()
{
if (!dryBledSequenceActive)
{
// Exit the function
return;
}
switch (dryBlendSequenceStep)
{
//////////////////
////// Init //////
//////////////////
case 0:
dryBlendCounter = 0;
dryBlendSequenceStep += 1;
break;
//////////////////
///// Drying /////
//////////////////
case 1:
dryBlendCounter += 1;
Serial.println("Drying " + String(dryBlendCounter) + "/3 ...");
rgbMode = RgbMode::PULSE;
dryBlendTimer = millis();
dryBlendSequenceStep += 1;
break;
case 2:
if ((millis() - dryBlendTimer) >= 10e3)
{
dryBlendSequenceStep += 1;
}
break;
//////////////////
///// Pause //////
//////////////////
case 3:
Serial.println("Drying complete!");
Serial.println("Waiting 13 seconds ...");
rgbMode = RgbMode::PULSE_YELLOW_ORANGE;
dryBlendTimer = millis();
dryBlendSequenceStep += 1;
break;
case 4:
if ((millis() - dryBlendTimer) >= 7e3)
{
dryBlendSequenceStep += 1;
}
break;
case 5:
rgbMode = RgbMode::PULSE_GREEN;
dryBlendTimer = millis();
dryBlendSequenceStep += 1;
break;
case 6:
if ((millis() - dryBlendTimer) >= 3e3)
{
dryBlendSequenceStep += 1;
}
break;
case 7:
rgbMode = RgbMode::GREEN;
dryBlendTimer = millis();
dryBlendSequenceStep += 1;
break;
case 8:
if ((millis() - dryBlendTimer) >= 3e3)
{
dryBlendSequenceStep += 1;
}
break;
//////////////////
/// Blending /////
//////////////////
case 9:
Serial.println("Blending " + String(dryBlendCounter) + "/3 ...");
rgbMode = RgbMode::CYCLE;
dryBlendTimer = millis();
dryBlendSequenceStep += 1;
break;
case 10:
if ((millis() - dryBlendTimer) >= 5e3)
{
dryBlendSequenceStep += 1;
}
break;
case 11:
Serial.println("Blending complete!");
if (dryBlendCounter < 3)
{
// Replay the sequence from the beginning 3 times
dryBlendSequenceStep = 1;
}
else
{
// Continue the sequence after it
// has completed its 3 runs
dryBlendSequenceStep += 1;
}
break;
//////////////////
/// Play tone ////
//////////////////
case 12:
Serial.println("Dry and Blend process complete!");
rgbMode = RgbMode::OFF;
playDryBlendCompleteBuzz();
dryBlendTimer = millis();
dryBlendSequenceStep += 1;
break;
case 13:
if ((millis() - dryBlendTimer) >= 2e3)
{
dryBlendSequenceStep += 1;
}
break;
//////////////////
////// End ///////
//////////////////
case 14:
stopBuzzer();
// Stop running the sequence
dryBledSequenceActive = false;
break;
}
}
/////////////////////////////////////
//////////// Main Code //////////////
/////////////////////////////////////
CatDX4001 cat;
int step = 0;
unsigned long case1LightTimer = 0;
unsigned long case2BuzzTimer = 0;
unsigned long case2BatchDestructionTimer = 0;
void setup()
{
Serial.begin(9600);
setupButtonA();
setupCaseLedIndicator();
setupHatchServo();
setupThermometer();
setupLightSensor();
setupRgb();
}
void updateSequence()
{
switch (step)
{
//////////////////
///// Case 1 /////
//////////////////
case 0:
// Code that should run once during case 1
setCaseLedIndicatorMode(LedIndicatorMode::CASE_1);
playCaseChangeBuzz();
Serial.println("Case 1 is active");
Serial.println("Press the confirmation button to continue");
step += 1;
break;
case 1:
if (buttonARisingEdge)
{
step += 1;
}
break;
case 2:
if (cat.get_status())
{
step += 1;
}
break;
case 3:
if (cat.select_operation(1))
{
step += 1;
}
break;
case 4:
if (lightLevel > 50)
{
Serial.println("Waiting for the light level to get below 50");
}
step += 1;
break;
case 5:
if (lightLevel < 50)
{
step += 1;
}
break;
case 6:
Serial.println("Waiting 2 minutes for the batch settle");
case1LightTimer = millis();
step += 1;
break;
case 7:
{
// Reset the sequence to the start of the light level check
if (lightLevel > 50)
{
step = 4;
}
// Continuing to the next step after 2 minutes
if ((millis() - case1LightTimer) > (2 * 60e3))
{
step += 1;
}
break;
}
//////////////////
///// Case 2 /////
//////////////////
case 8:
// Code that should run once during case 2
setCaseLedIndicatorMode(LedIndicatorMode::CASE_2);
playCaseChangeBuzz();
Serial.println("Case 2 is active");
Serial.println("Waiting for temperature to rise above 25 degrees");
step += 1;
break;
case 9:
if (temperature > 25)
{
Serial.println("Temperature is above 25 degrees");
Serial.println("Press the confirmation button within 2 minutes or the batch will be destroyed");
step += 1;
}
break;
case 10:
case2BuzzTimer = millis();
case2BatchDestructionTimer = millis();
step += 1;
break;
case 11:
{
// The code in this case is surrounded by curly braces {} to
// create a new scope. This allows for the creation of local variables
// inside the case.
if (buttonARisingEdge)
{
stopBuzzer();
step += 1;
}
unsigned long batchAge = millis() - case2BatchDestructionTimer;
unsigned long timeSinceLastBuzz = millis() - case2BuzzTimer;
// Using scientific notation to make the code more readable
// 60e3 = 60 * 10^3 = 60 * 1000 = 60000 where 3 stands for 3 zeros (1000)
if (batchAge <= 60e3)
{
if ((millis() - case2BuzzTimer) >= 10e3)
{
playCase2ShortBuzz();
case2BuzzTimer = millis();
}
}
else if (batchAge <= (60e3 + 30e3))
{
if (timeSinceLastBuzz >= 5e3)
{
playCase2ShortBuzzes(2);
case2BuzzTimer = millis();
}
}
else if (batchAge <= (60e3 + 30e3 + 15e3))
{
if (timeSinceLastBuzz >= 5e3)
{
playCase2ShortBuzzes(3);
case2BuzzTimer = millis();
}
}
else if (batchAge <= (2 * 60e3))
{
playCase2ConstantBuzz();
}
else
{
step = 0;
stopBuzzer();
Serial.println("Batch is destroyed!");
}
break;
}
//////////////////
///// Case 3 /////
//////////////////
case 12:
// Code that should run once during case 3
setCaseLedIndicatorMode(LedIndicatorMode::CASE_3);
playCaseChangeBuzz();
Serial.println("Case 3 is active");
startHatchSequence();
step += 1;
break;
case 13:
{
// Code that should run over and over again while
// waiting for the hatch sequence to complete
// The code in this case is surrounded by curly braces {} to
// create a new scope. This allows for the creation of local variables
// inside the case.
bool hatchSequenceCompleted = hatchStatus == HatchStatus::CLOSED;
// The hatch sequence gets updated by updateHatchSequence() in void loop()
if (hatchSequenceCompleted)
{
Serial.println("Press the confirmation button to continue to step 4");
step += 1;
}
break;
}
case 14:
// Code that should run while waiting for the confirmation button press
if (buttonARisingEdge)
{
step += 1;
}
break;
//////////////////
///// Case 4 /////
//////////////////
case 15:
setCaseLedIndicatorMode(LedIndicatorMode::CASE_4);
playCaseChangeBuzz();
Serial.println("Case 4 is active");
startDryBlendSequence();
step += 1;
break;
case 16:
if (!dryBledSequenceActive)
{
Serial.println("The batch has been completed!");
Serial.println("Press the confirm button to start a new batch");
step += 1;
}
break;
case 17:
if (buttonARisingEdge)
{
step = 0;
}
break;
}
}
void loop()
{
updateButtonA();
updateThermometer();
updateLightSensor();
updateSequence();
updateHatchSequence();
updateDryBlendSequence();
updateHatchServo();
updateCase2ShortBuzz();
updateRgb();
}