To upgrade an existing battery-powered LED lantern into a dynamic, interactive work of art. The lantern's cylindrical 2D LED matrix will run a "water simulation" effect that reacts in real-time to physical movement, creating an organic and mesmerizing display.
- Realistic Physics: The primary goal is to simulate "living water." This involves two distinct physics models:
- The "Water Level" (Gravity): A stable fluid line that remains parallel to the ground, regardless of how the lantern is tilted.
- The "Slosh & Settle" (Damped Oscillation): A "jolt" (shake or bump) will add energy to the system, creating realistic waves that "slosh" back and forth and gradually "calm down" (dampen) over time.
- Modular WLED Code: The logic will be built as a custom WLED Usermod (the "Brain") and a custom 2D Effect (the "Canvas"), allowing it to be part of a full WLED installation.
- Autonomous Operation: The device must be fully functional (physics, effects) with or without a WiFi connection.
- Tactile, Real-time Control: Include physical rotary encoders to modify physics and visual parameters (e.g., gravity, damping, color) in real-time. These will have whimsical labels (e.g., "Encabulation," "Brainpower").
- Compass Integration: The system will read a magnetometer to get an absolute "yaw" (heading), allowing for future effects that are aware of their direction (e.g., "water" flows North).
- Battery Monitoring: The device will monitor its own 18650 battery level via a voltage divider and display it on the LED matrix at boot and on-demand.
- Controller: ESP32-S3 (Dual-Core w/ PSRAM, e.g., Adafruit QT Py S3) - Required for dual-core processing (WLED on Core 1, Physics on Core 0) and PSRAM for WLED.
- LEDs: 10x8 (or 13x8) 5V WS2812B LED matrix, to be formed into a cylinder.
- Primary Sensor (6-dof): MPU-6050.
- Key Insight: We will use its onboard Digital Motion Processor (DMP) to offload all sensor fusion for pitch/roll, freeing the ESP32 for effects.
- Secondary Sensor (Compass): HMC5883L / QMC5883L (on a GY-271 breakout) - For Phase 7.
- Physical Inputs: 2x Rotary Encoders with Push-button.
- Power System:
- 1x 18650 Li-Ion Battery.
- 1x 5V Boost/Charger Module (e.g., TP5400/TP4056 based).
- 2x 100kΩ Resistors (for voltage divider).
- Misc: 3D Printed internal chassis, wiring, etc.
The system is decoupled into three parts that communicate via a "whiteboard."
- Part 1: The IMU Usermod (The "Brain")
- File: wled00/usermods/usermod_living_water.h
- Core: Runs on ESP32 Core 0 (the "utility" core).
- On setup():
- Initializes MPU-6050 and enables its DMP (the "magic ritual").
- Initializes I2C for the magnetometer.
- Initializes ADC pin for battery reading.
- Initializes pins for rotary encoders.
- On loop():
- Reads stable pitch / roll from the DMP.
- Reads raw yaw from the magnetometer and performs tilt-compensation.
- Reads raw accelerometer to detect a "jolt" (spike > JOLT_THRESHOLD_SQ).
- Runs the Physics Simulation:
- If "jolt" detected: Adds energy to wave_velocity.
- Applies damping (friction) to wave_velocity.
- Applies spring force (gravity) to wave_velocity -> wave_position.
- Reads the rotary encoder positions.
- Reads the ADC pin for battery voltage.
- Publishes all data to the "Whiteboard."
- Part 2: The "Whiteboard" (Global Variables)
- File: wled00/my_globals.h
- Purpose: A simple, high-speed, thread-safe (using portMUX_TYPE) struct for passing data from Core 0 to Core 1.
- Contents:
- float global_pitch, global_roll, global_yaw
- float global_wave_amplitude (from the physics simulation)
- int global_knob_1_value, global_knob_2_value
- float global_battery_level (0.0 to 1.0)
- bool global_knob_1_pressed
- Part 3: The Custom 2D Effect (The "Canvas")
- File: wled00/FX.cpp (Mode: FX_MODE_LIVING_WATER)
- Core: Runs on ESP32 Core 1 (the "WLED/WiFi" core).
- On loop() (render loop):
- Subscribes to data: Reads all values from the "Whiteboard" safely.
- Checks for on-demand battery display (e.g., knob press) and draws that if needed.
- Applies Tuned Parameters: Uses global_knob_1_value to adjust gravity_strength, etc.
- Calculates "Water Level": Uses global_pitch and global_roll to calculate the main y = mx + b water line.
- Calculates "Slosh": Uses global_wave_amplitude to set the height of a sin() wave.
- Draws: Iterates all (x,y) pixels, coloring them "water" or "sky" based on their position relative to the combined level + slosh line.
We will build and validate this project in isolated phases.
- Phase 1: Sensor Validation (The "Brain")
- Goal: Tame the MPU-6050 DMP.
- Action: Use a minimal test sketch (testing/Phase1-DMP-Validation) to:
- Initialize the MPU-6050 DMP.
- Calibrate the sensor to find offsets.
- Apply offsets, re-init, and confirm stable, filtered pitch and roll near 0.0.
- Confirm "jolt" detection (using squared magnitude) is working.
- Status: COMPLETE
- Phase 2: Physical Input Validation
- Goal: Validate rotary encoders and battery level ADC.
- Action: Create a new test sketch (testing/Phase2-Input-Validation) to:
- Read rotary encoder turns and button presses and print to Serial.
- Read the ADC pin via the voltage divider and print a stable voltage.
- Phase 3: WLED Integration (The "Whiteboard")
- Goal: Set up the WLED build and link the "Brain" to the "Canvas."
- Action:
- Set up the full WLED build environment (firmware/LivingWater-WLED).
- Port the working Phase 1 & 2 code into a new Usermod.
- Create a "stub" 2D Effect (e.g., FX_MODE_LIVING_WATER).
- Have the Effect read global_pitch from the Whiteboard and write it to the WLED Info Panel.
- Milestone: Tilting the sensor physically changes the number in the WLED UI.
- Phase 4: The "Water Level" (The 3D Level)
- Goal: Draw the stable water level on the LED matrix.
- Action: Implement the 2D matrix math inside the Effect.
- Translate pitch and roll into a 2D line.
- Color pixels blue below the line, black above it.
- Milestone: The LED "water level" stays parallel to the ground as you tilt the lantern.
- Phase 5: The "Slosh" (Damped Oscillation)
- Goal: Implement the "slosh" physics.
- Action:
- Implement the damped harmonic oscillator (physics simulation) in the Usermod.
- Feed the resulting global_wave_amplitude to the Effect.
- Modify the Effect to add a sin() wave (whose height is the amplitude) to the water line.
- Milestone: Shaking the lantern makes the water "slosh" realistically and then settle.
- Phase 6: The "Knobs" (Parameter Tuning)
- Goal: Connect the physical knobs to the simulation.
- Action: In the Usermod, use global_knob_1_value to modify the damping_factor, etc.
- Milestone: Turning the "Encabulation" knob visibly changes the water's behavior.
- Phase 7: The "Compass" (Magnetometer)
- Goal: Add absolute heading (yaw).
- Action:
- Add the HMC5883L/QMC5883L to the I2C bus.
- Read the raw yaw data in the Usermod.
- Apply tilt-compensation using the DMP's pitch/roll.
- Publish global_yaw to the Whiteboard.
- Milestone: A new WLED effect (e.g., "Flow") can be created that uses global_yaw to make particles flow North.