super simple ATO arduino code

js-3Design

Active Member
View Badges
Joined
May 29, 2024
Messages
179
Reaction score
144
Location
Netherlands
Rating - 0%
0   0   0
I dessided to share this little bit of code.
I use it to top off my pico.

It uses an arduino nano, floatsensor, pushbutton LED and 220Ohm resistor.

when floatsensor reads HIGH for 2 seconds. the relais is activated, until sensor is LOW for 1 second. I set the maximum runtime at 10 seconds ( this can easily be set to anything you want) after that LED starts blinking and system is in protection until pushbutton is pressed.
Longpress activates maintenance mode for water changes. hold button again and the ATO is active again, also maintenance mode deactivates after 30 minutes.
you can adjust all the parameters in the code to fit your needs. This system you can find parts for on aliexpress for less then 10 dollars.
for this pico im running a 12v dc pomp at 5.5v and have a 3mm restriction in the waterhose. this way an top off is arround 70ml in 5 seconds.

20260113_092019.jpg 20260113_092045.jpg 20260113_093636.jpg

#include <Arduino.h>
const uint8_t PIN_SENSOR = 2;
const uint8_t PIN_BUTTON = 3;
const uint8_t PIN_RELAY = 4;
const uint8_t PIN_LED =5;
const uint8_t SENSOR_ACTIVE_LEVEL = LOW;
const uint8_t BUTTON_PRESSED_LEVEL = LOW;
const uint8_t RELAY_ACTIVE_LEVEL = HIGH;
const unsigned long SENSOR_ON_DELAY_MS = 2000;
const unsigned long SENSOR_OFF_DELAY_MS = 1000;
const unsigned long MAX_RUN_TIME_MS = 10000;
const unsigned long STARTUP_INHIBIT_MS = 2000;
const unsigned long DEBOUNCE_MS = 30;
const unsigned long SHORT_PRESS_MIN_MS = 100;
const unsigned long LONG_PRESS_MS = 1000;
const unsigned long FAULT_BLINK_MS = 300;
const unsigned long RESET_FLASH_ON_MS = 90;
const unsigned long RESET_FLASH_OFF_MS = 90;
const unsigned long MAINTENANCE_TIMEOUT_MS = 1800000UL;
bool relayOn = false;
bool faultLockout = false;
bool maintenanceMode = false;
unsigned long bootMs = 0;
bool sensorPrev = false;
unsigned long sensorOnSince = 0;
unsigned long sensorOffSince = 0;
unsigned long relayOnSince = 0;
bool btnRawPrev = false;
bool btnStable = false;
unsigned long btnLastEdge = 0;
unsigned long btnPressedSince = 0;
bool longPressFired = false;
bool resetFlashActive = false;
uint8_t resetFlashCount = 0;
bool resetFlashLedOn = false;
unsigned long resetFlashTs = 0;
unsigned long maintenanceStartMs = 0;
inline bool sensorActive() { return digitalRead(PIN_SENSOR) == SENSOR_ACTIVE_LEVEL; }
inline bool btnRawPressed() { return digitalRead(PIN_BUTTON) == BUTTON_PRESSED_LEVEL; }
void setRelay(bool on) {
relayOn = on;
digitalWrite(PIN_RELAY, on ? RELAY_ACTIVE_LEVEL : !RELAY_ACTIVE_LEVEL);
if (on) relayOnSince = millis();
}
void startFault() {
faultLockout = true;
setRelay(false);
unsigned long now = millis();
sensorOnSince = now;
sensorOffSince = now;
}
void startResetFlash() {
resetFlashActive = true;
resetFlashCount = 0;
resetFlashLedOn = false;
resetFlashTs = millis();
}
void updateLed(unsigned long now) {
if (resetFlashActive) {
if (!resetFlashLedOn) {
if (now - resetFlashTs >= RESET_FLASH_OFF_MS) {
resetFlashLedOn = true;
resetFlashTs = now;
digitalWrite(PIN_LED, HIGH);
}
} else {
if (now - resetFlashTs >= RESET_FLASH_ON_MS) {
resetFlashLedOn = false;
resetFlashTs = now;
digitalWrite(PIN_LED, LOW);
if (++resetFlashCount >= 3) {
resetFlashActive = false;
digitalWrite(PIN_LED, LOW);
}
}
}
return;
}
if (faultLockout) {
static unsigned long lastToggle = 0;
static bool state = false;
if (now - lastToggle >= FAULT_BLINK_MS) {
lastToggle = now;
state = !state;
digitalWrite(PIN_LED, state ? HIGH : LOW);
}
return;
}
digitalWrite(PIN_LED, maintenanceMode ? HIGH : LOW);
}
void onShortPress() {
if (faultLockout) {
faultLockout = false;
startResetFlash();
return;
}
if (maintenanceMode) {
maintenanceMode = false;
startResetFlash(); // feedback when leaving maintenance manually
return;
}
}
void onLongPress() {
if (!faultLockout && !maintenanceMode) {
maintenanceMode = true;
maintenanceStartMs = millis();
setRelay(false);
}
}
void handleButton(unsigned long now) {
bool raw = btnRawPressed();
if (raw != btnRawPrev) {
btnRawPrev = raw;
btnLastEdge = now;
}
if ((now - btnLastEdge) >= DEBOUNCE_MS && raw != btnStable) {
btnStable = raw;
if (btnStable) {
btnPressedSince = now;
longPressFired = false;
} else {
unsigned long dur = now - btnPressedSince;
if (!longPressFired && dur >= SHORT_PRESS_MIN_MS && dur < LONG_PRESS_MS) {
onShortPress();
}
}
}
if (btnStable && !longPressFired && (now - btnPressedSince) >= LONG_PRESS_MS) {
longPressFired = true;
onLongPress();
}
}
void handleMaintenanceTimeout(unsigned long now) {
if (maintenanceMode && (now - maintenanceStartMs) >= MAINTENANCE_TIMEOUT_MS) {
maintenanceMode = false;
startResetFlash(); // feedback when leaving maintenance automatically
}
}
void handleSensorRelay(unsigned long now) {
if (now - bootMs < STARTUP_INHIBIT_MS) {
setRelay(false);
sensorOnSince = now;
sensorOffSince = now;
sensorPrev = sensorActive();
return;
}
if (faultLockout || maintenanceMode || resetFlashActive) {
setRelay(false);
return;
}
bool s = sensorActive();
if (s && !sensorPrev) sensorOnSince = now;
if (!s && sensorPrev) sensorOffSince = now;
sensorPrev = s;
if (relayOn && (now - relayOnSince >= MAX_RUN_TIME_MS)) {
startFault();
return;
}
if (!relayOn) {
if (s && (now - sensorOnSince >= SENSOR_ON_DELAY_MS)) setRelay(true);
return;
}
if (!s && (now - sensorOffSince >= SENSOR_OFF_DELAY_MS)) setRelay(false);
}
void setup() {
bootMs = millis();
digitalWrite(PIN_RELAY, !RELAY_ACTIVE_LEVEL);
pinMode(PIN_RELAY, OUTPUT);
pinMode(PIN_SENSOR, INPUT_PULLUP);
pinMode(PIN_BUTTON, INPUT_PULLUP);
pinMode(PIN_LED, OUTPUT);
setRelay(false);
digitalWrite(PIN_LED, LOW);
unsigned long now = millis();
sensorPrev = sensorActive();
sensorOnSince = now;
sensorOffSince = now;
btnRawPrev = btnRawPressed();
btnStable = btnRawPrev;
btnLastEdge = now;
if (btnStable) btnPressedSince = now;
maintenanceStartMs = now;
}
void loop() {
unsigned long now = millis();
handleButton(now);
handleMaintenanceTimeout(now);
handleSensorRelay(now);
updateLed(now);
}
 

Gumbies R Us

God, Bouldering, and Reefing
View Badges
Joined
Nov 10, 2022
Messages
28,944
Reaction score
51,308
Location
North Georgia
Rating - 0%
0   0   0
Awesome looking design! Thanks for sharing!
 

TOP 10 Trending Threads

WHAT AMOUNT OF LIVE ROCK AND SAND SHOULD BE PRIORITIZED FOR OPTIMAL BIODIVERSITY/FILTRATION?

  • 100% live rock + bagged sand

    Votes: 34 27.0%
  • 100% dry rock + 100% live sand

    Votes: 45 35.7%
  • 50/50 live/dry rock, 50/50 live/bagged sand

    Votes: 27 21.4%
  • 75% live rock, 25% live sand

    Votes: 11 8.7%
  • 25% live rock, 75% live sand

    Votes: 9 7.1%
Back
Top