Magic Mushroom Lamp - Bambu3Design – 3D Printing & Design
magic mushroom lamp

Magic Mushroom Lamp

#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();
}
Scroll to Top