#include <Adafruit_NeoPixel.h>
#include <math.h>
// -------------------------
// PINS
// -------------------------
#define HALL_PIN 2
#define TOUCH_PIN 3
#define LED_PIN 4
#define NUM_LEDS 10
Adafruit_NeoPixel strip(
NUM_LEDS,
LED_PIN,
NEO_GRB + NEO_KHZ800
);
// -------------------------
// SETTINGS
// -------------------------
const unsigned long WATER_SPEED = 120;
const unsigned long DOUBLE_CLICK_TIME = 350;
const unsigned long LONG_PRESS_TIME = 1000;
const unsigned long DEBOUNCE_TIME = 30;
// -------------------------
// MODES
// -------------------------
enum LampMode {
MODE_OFF,
MODE_NIGHT,
MODE_COLOR,
MODE_BREATHING
};
LampMode mode = MODE_OFF;
LampMode previousMode = MODE_OFF;
// -------------------------
// WATERING
// -------------------------
int waterPosition = -1;
bool watering = false;
bool returning = false;
unsigned long lastWaterStep = 0;
// -------------------------
// HALL SENSOR
// -------------------------
bool hallRaw = false;
bool hallStable = false;
unsigned long hallChangeTime = 0;
// -------------------------
// TOUCH SENSOR
// -------------------------
bool touchRaw = false;
bool touchStable = false;
unsigned long touchChangeTime = 0;
unsigned long pressStartTime = 0;
unsigned long lastReleaseTime = 0;
int clickCount = 0;
bool longPressHandled = false;
// -------------------------
// COLORS
// -------------------------
int colorIndex = 0;
const uint8_t colors[][3] = {
{0, 255, 0}, // Green
{0, 180, 255}, // Cyan
{0, 0, 255}, // Blue
{150, 0, 255}, // Purple
{255, 0, 80}, // Pink
{255, 120, 40} // Warm white-ish
};
const int COLOR_COUNT =
sizeof(colors) / sizeof(colors[0]);
// -------------------------
// HELPERS
// -------------------------
void clearStrip() {
strip.clear();
strip.show();
}
void fillAll(uint8_t r, uint8_t g, uint8_t b) {
for (int i = 0; i < NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(r, g, b));
}
strip.show();
}
// -------------------------
// WATER EFFECT
// -------------------------
void showWater() {
strip.clear();
for (int i = 0; i <= waterPosition; i++) {
// Always pure green during watering
strip.setPixelColor(i, strip.Color(0, 255, 0));
}
strip.show();
}
// -------------------------
// HALL SENSOR
// -------------------------
void updateHall() {
unsigned long now = millis();
// A3144 usually goes LOW when magnet is detected
bool current = (digitalRead(HALL_PIN) == LOW);
if (current != hallRaw) {
hallRaw = current;
hallChangeTime = now;
}
if ((now - hallChangeTime >= DEBOUNCE_TIME) &&
(hallStable != hallRaw)) {
hallStable = hallRaw;
if (hallStable) {
// Magnet detected
watering = true;
returning = false;
waterPosition = -1;
lastWaterStep = now;
clearStrip();
} else {
// Magnet removed
watering = false;
if (waterPosition >= 0) {
returning = true;
} else {
returning = false;
}
lastWaterStep = now;
}
}
}
// -------------------------
// WATER ANIMATION
// -------------------------
void updateWater() {
if (!watering && !returning) return;
unsigned long now = millis();
if (now - lastWaterStep < WATER_SPEED) return;
lastWaterStep = now;
if (watering) {
if (waterPosition < NUM_LEDS - 1) {
waterPosition++;
showWater();
} else {
// Stay fully green while magnet remains near
showWater();
}
}
else if (returning) {
waterPosition--;
if (waterPosition < 0) {
returning = false;
return;
}
showWater();
}
}
// -------------------------
// TOUCH ACTIONS
// -------------------------
void singleTouch() {
if (mode == MODE_OFF) {
mode = MODE_NIGHT;
} else {
mode = MODE_OFF;
}
}
void doubleTouch() {
mode = MODE_COLOR;
colorIndex++;
if (colorIndex >= COLOR_COUNT) {
colorIndex = 0;
}
}
void longTouch() {
if (mode != MODE_BREATHING) {
previousMode = mode;
mode = MODE_BREATHING;
} else {
mode = previousMode;
}
}
// -------------------------
// TOUCH SENSOR
// -------------------------
void updateTouch() {
unsigned long now = millis();
// TTP223 default:
// HIGH when touched
bool current = (digitalRead(TOUCH_PIN) == HIGH);
if (current != touchRaw) {
touchRaw = current;
touchChangeTime = now;
}
if ((now - touchChangeTime >= DEBOUNCE_TIME) &&
(touchStable != touchRaw)) {
touchStable = touchRaw;
if (touchStable) {
pressStartTime = now;
longPressHandled = false;
} else {
if (!longPressHandled) {
clickCount++;
lastReleaseTime = now;
}
}
}
// Long press
if (touchStable && !longPressHandled) {
if (now - pressStartTime >= LONG_PRESS_TIME) {
longTouch();
longPressHandled = true;
clickCount = 0;
}
}
// Single / double click
if (!touchStable &&
clickCount > 0 &&
now - lastReleaseTime > DOUBLE_CLICK_TIME) {
if (clickCount == 1) {
singleTouch();
} else {
doubleTouch();
}
clickCount = 0;
}
}
// -------------------------
// NORMAL LIGHT MODES
// -------------------------
void updateLamp() {
// Watering has priority
if (watering || returning) return;
switch (mode) {
case MODE_OFF:
clearStrip();
break;
case MODE_NIGHT:
// Warm light
fillAll(120, 55, 12);
break;
case MODE_COLOR:
fillAll(
colors[colorIndex][0],
colors[colorIndex][1],
colors[colorIndex][2]
);
break;
case MODE_BREATHING: {
float phase =
(millis() % 3000UL) / 3000.0f;
float wave =
(1.0f - cosf(phase * 6.2831853f)) / 2.0f;
float brightness =
0.05f + wave * 0.65f;
uint8_t r = 0;
uint8_t g = (uint8_t)(220 * brightness);
uint8_t b = (uint8_t)(255 * brightness);
fillAll(r, g, b);
break;
}
}
}
// -------------------------
// SETUP
// -------------------------
void setup() {
Serial.begin(115200);
pinMode(HALL_PIN, INPUT_PULLUP);
pinMode(TOUCH_PIN, INPUT);
strip.begin();
strip.setBrightness(150);
strip.clear();
strip.show();
hallRaw =
(digitalRead(HALL_PIN) == LOW);
hallStable = hallRaw;
touchRaw =
(digitalRead(TOUCH_PIN) == HIGH);
touchStable = touchRaw;
if (hallStable) {
watering = true;
waterPosition = -1;
lastWaterStep = millis();
}
}
// -------------------------
// LOOP
// -------------------------
void loop() {
updateHall();
updateTouch();
updateWater();
updateLamp();
}