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.
#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);
}
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.
#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);
}
