#include <Servo.h>
#include <EEPROM.h>
#include <stdio.h>
#include <avr/sleep.h>
// === Pin assignments ===
const int servoPin = 13;
const int uvLedPin = 12; // UV light (purple)
const int statusLED = 11; // Blue LED = temp too cold/hot
const int lightStatusLED = 10; // Yellow LED = light too bright
const int batteryStatusLED = 9; // Red LED = battery low indicator
// === User-adjustable parameters ===
const int flailRPM = 30;
const unsigned long servoOnMs = 1000;
const unsigned long servoOffMs = 1000;
const float dutyPauseSec = 3.0;
const float lightThreshold = 500.0;
const float minTempF = 50.0;
const float maxTempF = 95.0;
const float lowBatteryCutoff = 6.5;
const unsigned long logInterval = 10000; // iterate conditions every 10s
const int intervalSec = logInterval / 1000;
Servo flailServo;
unsigned long lastLogTime = 0;
unsigned long lastBlinkTime = 0;
bool blinkState = false;
unsigned long currentTime;
bool controlFlail = false;
int flailCycleCount = 0;
bool batteryOK = true;
bool uvOn = false;
int bootCount = 0;
void goToSleep() {
set_sleep_mode(SLEEP_MODE_PWR_DOWN);
sleep_enable();
sleep_cpu();
sleep_disable();
}
void setup() {
Serial.begin(9600);
flailServo.attach(servoPin);
pinMode(uvLedPin, OUTPUT);
pinMode(statusLED, OUTPUT);
pinMode(lightStatusLED, OUTPUT);
pinMode(batteryStatusLED, OUTPUT);
bootCount = EEPROM.read(0);
if (bootCount == 255) bootCount = 0;
bootCount++;
EEPROM.write(0, bootCount);
Serial.println("Iteration,Flail,Light,Temp,Battery,SleepState,Day,Time,IntervalSec,JitterSec,DurationSec,TempF,LightLux,FlailCycles,ServoRPM,DutyOn,DutyPause,BootCount");
}
void loop() {
currentTime = millis();
static int iteration = 1;
static float temperatureF = 60;
static float lightLux = 400;
static float batteryV = 7.0;
static int step = 0;
static bool tempTooCold, tempTooHot, lightTooBright;
if (currentTime - lastBlinkTime >= 500) {
blinkState = !blinkState;
lastBlinkTime = currentTime;
}
bool doLog = false;
if (currentTime - lastLogTime >= logInterval) {
lastLogTime = currentTime;
doLog = true;
step = (step + 1) % 8;
switch (step) {
case 0: temperatureF = 40; lightLux = 300; batteryV = 7.5; break;
case 1: temperatureF = 60; lightLux = 600; batteryV = 7.5; break;
case 2: temperatureF = 100; lightLux = 300; batteryV = 7.5; break;
case 3: temperatureF = 60; lightLux = 300; batteryV = 6.0; break;
case 4: temperatureF = 60; lightLux = 300; batteryV = 7.5; break;
case 5: temperatureF = 40; lightLux = 600; batteryV = 7.5; break;
case 6: temperatureF = 100; lightLux = 600; batteryV = 7.5; break;
case 7: temperatureF = 100; lightLux = 600; batteryV = 6.0; break;
}
tempTooCold = (temperatureF <= minTempF);
tempTooHot = (temperatureF >= maxTempF);
lightTooBright = (lightLux >= lightThreshold);
batteryOK = (batteryV >= lowBatteryCutoff);
controlFlail = batteryOK && !tempTooCold && !tempTooHot && !lightTooBright;
}
if (controlFlail) {
if (!uvOn) {
digitalWrite(uvLedPin, HIGH);
uvOn = true;
}
// ✅ Turn off red battery LED during active flailing
digitalWrite(batteryStatusLED, LOW);
unsigned long startFlailTime = millis();
while (millis() - startFlailTime < logInterval) {
flailServo.write(180);
delay(servoOnMs);
flailServo.write(0);
delay(servoOffMs);
flailCycleCount++;
}
} else {
if (uvOn) {
digitalWrite(uvLedPin, LOW);
uvOn = false;
}
flailServo.write(0);
}
// LED indicators
digitalWrite(statusLED, tempTooHot ? HIGH : (tempTooCold ? blinkState : LOW));
digitalWrite(lightStatusLED, lightTooBright ? blinkState : LOW);
if (!controlFlail) {
digitalWrite(batteryStatusLED, batteryOK ? LOW : blinkState);
}
if (doLog) {
unsigned long totalSeconds = currentTime / 1000;
int hours = (totalSeconds / 3600) % 24;
int minutes = (totalSeconds / 60) % 60;
int seconds = totalSeconds % 60;
char timeStr[9];
sprintf(timeStr, "%02d:%02d:%02d", hours, minutes, seconds);
String lightStr = lightTooBright ? "light_detected" : "dark";
String tempStr = tempTooCold ? "temp_cold" : (tempTooHot ? "temp_hot" : "temp_inrange");
String batteryStr = batteryOK ? "battery_okay" : "battery_low";
String flailStr = controlFlail ? "active" : "idle";
String sleepStr = controlFlail ? "awake" : "sleep";
Serial.print(iteration++); Serial.print(",");
Serial.print(flailStr); Serial.print(",");
Serial.print(lightStr); Serial.print(",");
Serial.print(tempStr); Serial.print(",");
Serial.print(batteryStr); Serial.print(",");
Serial.print(sleepStr); Serial.print(",");
Serial.print(0); Serial.print(",");
Serial.print(timeStr); Serial.print(",");
Serial.print(intervalSec); Serial.print(",");
Serial.print("0.00"); Serial.print(",");
Serial.print(intervalSec); Serial.print(",");
Serial.print((int)temperatureF); Serial.print(",");
Serial.print((int)lightLux); Serial.print(",");
Serial.print(flailCycleCount); Serial.print(",");
Serial.print(controlFlail ? String(flailRPM) : "NA"); Serial.print(",");
Serial.print(controlFlail ? String(servoOnMs / 1000.0, 1) + "s" : "NA"); Serial.print(",");
Serial.print(dutyPauseSec, 1); Serial.print("s,");
Serial.print(bootCount); Serial.println();
flailCycleCount = 0;
if (!controlFlail) {
delay(50); // Allow Serial to flush
goToSleep();
}
}
}