From dca4c4cb267e8ee80f4f1228fef4e148fbcea67e Mon Sep 17 00:00:00 2001 From: Ryan Hill Date: Tue, 18 Nov 2025 12:03:24 -0600 Subject: [PATCH] Initial commit: D1 Mini Blinkin LED Driver Emulator for FTC --- .gitignore | 89 ++++ .vscode/extensions.json | 10 + INSTALLATION_GUIDE.md | 224 ++++++++ QUICK_FLASH_GUIDE.md | 178 +++++++ README.md | 209 ++++++++ VOLTAGE_DIVIDER_ALTERNATIVES.md | 188 +++++++ VOLTAGE_DIVIDER_BUILD_GUIDE.md | 238 +++++++++ include/README | 37 ++ lib/README | 46 ++ platformio.ini | 27 + src/main.cpp | 898 ++++++++++++++++++++++++++++++++ test/README | 11 + 12 files changed, 2155 insertions(+) create mode 100644 .gitignore create mode 100644 .vscode/extensions.json create mode 100644 INSTALLATION_GUIDE.md create mode 100644 QUICK_FLASH_GUIDE.md create mode 100644 README.md create mode 100644 VOLTAGE_DIVIDER_ALTERNATIVES.md create mode 100644 VOLTAGE_DIVIDER_BUILD_GUIDE.md create mode 100644 include/README create mode 100644 lib/README create mode 100644 platformio.ini create mode 100644 src/main.cpp create mode 100644 test/README diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1341eec --- /dev/null +++ b/.gitignore @@ -0,0 +1,89 @@ +# PlatformIO +.pio +.pioenvs +.piolibdeps +.clang_complete +.gcc-flags.json + +# VSCode +.vscode/* +!.vscode/extensions.json +.vscode/.browse.c_cpp.db* +.vscode/c_cpp_properties.json +.vscode/launch.json +.vscode/ipch + +# IDE - IntelliJ +.idea/ +*.iml +*.iws +*.ipr + +# IDE - CLion +cmake-build-*/ + +# OS generated files +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db +*.swp +*.swo +*~ + +# Build artifacts +*.bin +*.elf +*.hex +*.map +*.lst +*.su +*.o +*.a +*.lib +*.ko +*.so +*.so.* +*.dll +*.exe +*.out +*.app + +# Logs and databases +*.log +*.sql +*.sqlite + +# Python +__pycache__/ +*.py[cod] +*$py.class +*.pyc + +# Node +node_modules/ + +# Archives +*.7z +*.dmg +*.gz +*.iso +*.jar +*.rar +*.tar +*.zip + +# Backup files +*.bak +*.backup +*.old +*.orig +*.tmp + +# Project specific +/build/ +/dist/ +/output/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..080e70d --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,10 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "platformio.platformio-ide" + ], + "unwantedRecommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} diff --git a/INSTALLATION_GUIDE.md b/INSTALLATION_GUIDE.md new file mode 100644 index 0000000..54b327b --- /dev/null +++ b/INSTALLATION_GUIDE.md @@ -0,0 +1,224 @@ +# Installation Guide - D1 Mini Blinkin Emulator + +## Two Ways to Install + +### Option 1: PlatformIO (Recommended for VS Code Users) + +#### Step 1: Install PlatformIO +1. Open VS Code +2. Go to Extensions (Ctrl+Shift+X) +3. Search for "PlatformIO IDE" +4. Click Install +5. Restart VS Code + +#### Step 2: Open the Project +1. File → Open Folder +2. Select `KrakenKodersAllianceLights` folder +3. PlatformIO will auto-detect the project + +#### Step 3: Install Libraries +The libraries should install automatically when you build. If not: + +**Method A: Through PlatformIO Home** +1. Click PlatformIO icon in sidebar +2. Open "PIO Home" → Libraries +3. Search for "Adafruit NeoPixel" +4. Click "Add to Project" +5. Select your project + +**Method B: Manual Command** +1. Open PlatformIO Terminal (bottom toolbar) +2. Run: `pio lib install "Adafruit NeoPixel"` + +#### Step 4: Configure Your Settings +Edit `src/D1Mini_Blinkin_Ready.ino`: +```cpp +#define NUM_LEDS 60 // Your LED count +#define BRIGHTNESS 100 // 0-255 +``` + +#### Step 5: Build and Upload +1. Connect D1 Mini via USB +2. Click checkmark (✓) to build +3. Click arrow (→) to upload + +### Option 2: Arduino IDE (Simpler for Beginners) + +#### Step 1: Install Arduino IDE +Download from: https://www.arduino.cc/en/software + +#### Step 2: Add ESP8266 Board Support +1. Open Arduino IDE +2. File → Preferences +3. In "Additional Board Manager URLs" add: + ``` + http://arduino.esp8266.com/stable/package_esp8266com_index.json + ``` +4. Click OK +5. Tools → Board → Board Manager +6. Search "ESP8266" +7. Install "ESP8266 by ESP8266 Community" + +#### Step 3: Install NeoPixel Library +1. Tools → Manage Libraries +2. Search "Adafruit NeoPixel" +3. Click Install +4. Also install any dependencies it asks for + +#### Step 4: Open the Sketch +1. File → Open +2. Navigate to `D1Mini_Blinkin_Arduino.ino` + +#### Step 5: Configure Board Settings +Tools menu: +- Board: "LOLIN(WEMOS) D1 R2 & mini" +- Upload Speed: 921600 +- CPU Frequency: 80 MHz +- Flash Size: 4MB (FS:2MB OTA:~1019KB) +- Port: Select your COM port (appears when D1 Mini connected) + +#### Step 6: Configure Your LEDs +Edit these lines in the code: +```cpp +#define NUM_LEDS 60 // Your LED count +#define BRIGHTNESS 100 // 0-255 +``` + +#### Step 7: Upload +1. Connect D1 Mini via USB +2. Click Upload button (→) +3. Wait for "Done uploading" + +## Troubleshooting + +### PlatformIO Issues + +**"Adafruit_NeoPixel.h not found"** +- Ensure platformio.ini contains: + ```ini + lib_deps = + adafruit/Adafruit NeoPixel@^1.11.0 + ``` +- Clean and rebuild: PlatformIO → Clean, then Build + +**"Platform not installed"** +- Terminal: `pio platform install espressif8266` + +**Wrong COM Port** +- Add to platformio.ini: + ```ini + upload_port = COM3 ; Change to your port + monitor_port = COM3 + ``` + +### Arduino IDE Issues + +**"Board not found"** +- Ensure ESP8266 package is installed +- Restart Arduino IDE +- Select correct board from Tools → Board menu + +**"Port not showing"** +- Install CH340 drivers: https://sparks.gogo.co.nz/ch340.html +- Try different USB cable (data cable, not charge-only) +- Windows: Check Device Manager for COM port + +**"Upload failed"** +- Hold FLASH button on D1 Mini while uploading starts +- Release after upload begins +- Try slower upload speed (115200) + +### General Issues + +**LEDs not working after upload** +- Check wiring (D4 → LED Data) +- Verify LED strip has power +- Test with Serial Monitor for debug output +- Check voltage divider if using servo port + +**Wrong colors or flickering** +- Some LED strips are RGB instead of GRB +- Change in code: + ```cpp + // From: + Adafruit_NeoPixel strip(NUM_LEDS, LED_DATA_PIN, NEO_GRB + NEO_KHZ800); + // To: + Adafruit_NeoPixel strip(NUM_LEDS, LED_DATA_PIN, NEO_RGB + NEO_KHZ800); + ``` + +## Testing Your Installation + +### Serial Monitor Test +1. Open Serial Monitor (115200 baud) +2. Should see: + ``` + D1 Mini Blinkin Emulator + Kraken Koders FTC Team + LEDs: 60 + Ready! + ``` + +### LED Test +1. LEDs should show green sweep on startup +2. Without PWM input, LEDs turn off +3. With PWM input, patterns change + +### Simple Blink Test +Upload this minimal test first: +```cpp +#include +#define PIN 2 // D4 +#define NUMPIXELS 10 + +Adafruit_NeoPixel pixels(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); + +void setup() { + pixels.begin(); + pixels.setBrightness(50); +} + +void loop() { + pixels.clear(); + pixels.setPixelColor(0, pixels.Color(255, 0, 0)); + pixels.show(); + delay(500); + pixels.clear(); + pixels.show(); + delay(500); +} +``` + +## Next Steps + +1. **Build the voltage divider** - See VOLTAGE_DIVIDER_BUILD_GUIDE.md +2. **Test with servo tester** - Verify PWM reading +3. **Connect to FTC robot** - Configure as servo device +4. **Customize patterns** - Add your team colors! + +## Quick Command Reference + +### PlatformIO Commands +```bash +pio run # Build +pio run -t upload # Upload +pio run -t clean # Clean +pio device monitor # Serial monitor +pio lib install "name" # Install library +``` + +### Arduino IDE Shortcuts +- Ctrl+R - Verify/Compile +- Ctrl+U - Upload +- Ctrl+Shift+M - Serial Monitor +- Ctrl+Shift+L - Library Manager + +## Support + +If you're still having issues: +1. Check all connections with multimeter +2. Try the simple blink test first +3. Enable DEBUG_MODE in code for more output +4. Verify voltage divider output is 2.5-3.3V +5. Test with different USB cable/port + +Remember: The D1 Mini is 3.3V logic - never connect 5V directly to GPIO pins! \ No newline at end of file diff --git a/QUICK_FLASH_GUIDE.md b/QUICK_FLASH_GUIDE.md new file mode 100644 index 0000000..27a84ea --- /dev/null +++ b/QUICK_FLASH_GUIDE.md @@ -0,0 +1,178 @@ +# Quick Flash Guide - D1 Mini Blinkin Emulator + +## What You'll Build +A $7 replacement for the $35 REV Blinkin LED Driver that works identically with FTC code! + +## Parts List +- **D1 Mini** (ESP8266) - $4 +- **2.2kΩ resistor** - $0.10 +- **3.3kΩ resistor** - $0.10 +- **Servo extension cable** - $2 +- **WS2812B LED strip** (30-60 LEDs) - $10-15 +- **Soldering supplies** + +## Step 1: Build the Voltage Divider + +### Why It's Needed +- Servo port outputs 5V signals +- D1 Mini only handles 3.3V +- Without this, you'll fry your D1 Mini! + +### Quick Assembly +1. Cut servo cable 6" from female connector +2. Strip wires (Red=5V, Black=GND, White=PWM) +3. Solder resistors like this: +``` +White wire ──[2.2kΩ]──┬── Wire to D2 + │ + [3.3kΩ] + │ + Black wire +``` +4. Heat shrink everything + +## Step 2: Flash the D1 Mini + +### Install Arduino IDE +1. Download from [arduino.cc](https://www.arduino.cc/en/software) +2. Open Arduino IDE +3. File → Preferences → Additional Board URLs: + ``` + http://arduino.esp8266.com/stable/package_esp8266com_index.json + ``` +4. Tools → Board → Board Manager → Search "ESP8266" → Install + +### Install Library +Tools → Manage Libraries → Search "Adafruit NeoPixel" → Install + +### Flash the Code +1. Open `D1Mini_Blinkin_Ready.ino` +2. **IMPORTANT: Edit line 20-21 for your setup:** + ```cpp + #define NUM_LEDS 60 // Change to your LED count + #define BRIGHTNESS 100 // Adjust brightness (0-255) + ``` +3. Tools → Board → "LOLIN(WEMOS) D1 R2 & mini" +4. Tools → Port → Select your COM port +5. Click Upload (→ button) + +## Step 3: Wire Everything + +### Final Connections +``` +Servo Port D1 Mini LED Strip +========== ======= ========= +Red ───────────────→ 5V +Black ─────────────→ GND ─────────────→ GND +White ─→[Divider]──→ D2 + D4 ───────────────→ Data In + 5V ← External Power +``` + +### Power Notes +- < 30 LEDs: Can use servo port power +- > 30 LEDs: Need external 5V supply for LEDs + +## Step 4: Test It + +### With Serial Monitor +1. Open Tools → Serial Monitor +2. Set to 115200 baud +3. You should see: + ``` + D1 Mini Blinkin Emulator v2.0 + Kraken Koders FTC Team + Ready for PWM signal... + ``` + +### With FTC Robot +1. Configure as servo in robot config +2. Name it "blinkin" +3. Use this test code: +```java +RevBlinkinLedDriver blinkin = hardwareMap.get(RevBlinkinLedDriver.class, "blinkin"); +blinkin.setPattern(RevBlinkinLedDriver.BlinkinPattern.RAINBOW_RAINBOW_PALETTE); +``` + +## Troubleshooting + +### LEDs Don't Light +- Check D4 → LED data connection +- Verify LED strip arrow points away from D1 Mini +- Test with simple color first + +### No PWM Reading +- Measure voltage divider output (should be ~3V) +- Check servo port is powered +- Verify resistor values + +### Wrong Colors +- Some strips are RGB instead of GRB +- Change line in code: + ```cpp + // Change from: + Adafruit_NeoPixel strip(NUM_LEDS, LED_DATA_PIN, NEO_GRB + NEO_KHZ800); + // To: + Adafruit_NeoPixel strip(NUM_LEDS, LED_DATA_PIN, NEO_RGB + NEO_KHZ800); + ``` + +### D1 Mini Keeps Resetting +- Too many LEDs for power supply +- Reduce brightness or LED count +- Add external 5V power + +## Quick Test Without Robot + +Use a servo tester or Arduino to generate PWM: +```cpp +// Arduino test signal generator +void setup() { + pinMode(9, OUTPUT); +} +void loop() { + // Sweep through patterns + for(int pw = 1000; pw <= 2000; pw += 10) { + digitalWrite(9, HIGH); + delayMicroseconds(pw); + digitalWrite(9, LOW); + delay(20); + } +} +``` + +## Pattern Reference + +| PWM (μs) | Pattern | +|----------|---------| +| 1005-1015 | Rainbow | +| 1065 | Confetti | +| 1315 | Breath Red | +| 1325 | Breath Blue | +| 1515 | Solid Red | +| 1645 | Solid Blue | +| 1595 | Solid Green | +| 1675 | Solid White | +| 1995 | Off | + +## Success Checklist + +- [ ] Voltage divider outputs 3V (measured) +- [ ] D1 Mini powers on +- [ ] Serial monitor shows "Ready" +- [ ] Green sweep on startup +- [ ] Responds to PWM changes +- [ ] Works with FTC code + +## Total Cost +- D1 Mini: $4 +- Resistors: $0.20 +- Cable: $2 +- **Total: $6.20** (vs $35 for REV Blinkin) + +## Need Help? +- Check serial monitor for debug info +- Onboard LED blinks = receiving PWM +- Green startup = code running +- No response = check voltage divider + +**You now have a fully functional Blinkin emulator for 1/5 the price!** \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..5770e06 --- /dev/null +++ b/README.md @@ -0,0 +1,209 @@ +# D1 Mini Blinkin LED Driver Emulator + +This project allows a D1 Mini (ESP8266) to emulate a REV Blinkin LED Driver, enabling control of WS2812B LED strips via PWM signals from an FTC robot's REV Control Hub or Expansion Hub. + +## Features + +- **Full Blinkin Pattern Support**: Emulates 100+ Blinkin patterns including solid colors, animations, and effects +- **PWM Signal Input**: Reads standard servo PWM signals (900-2100 microseconds) +- **60 LED Support**: Configured for 60 WS2812B LEDs (adjustable in code) +- **FastLED Library**: High-performance LED control with color correction +- **Debug Mode**: Serial output for troubleshooting PWM values and patterns +- **No WiFi**: WiFi disabled to save power and improve performance + +## Hardware Requirements + +- D1 Mini (ESP8266) or compatible board +- WS2812B LED strip (60 LEDs default, adjustable) +- Voltage divider circuit (5V to 3.3V for PWM input) +- 5V power supply capable of 3-4A for 60 LEDs +- REV Control Hub or Expansion Hub + +## Wiring Connections + +### PWM Signal Input (from REV Hub Servo Port) +**IMPORTANT**: The REV Hub outputs 5V PWM signals. The D1 Mini requires 3.3V input. You MUST use a voltage divider! + +``` +REV Hub Servo Port | Voltage Divider | D1 Mini +-------------------|-----------------|---------- +Signal (White) | Input → Output | D2 (GPIO4) +Power (Red) | - | 5V +Ground (Black) | - | GND +``` + +### Voltage Divider Circuit +``` +REV PWM Signal (5V) ──┬── R1 (2.2kΩ) ──┬── To D1 Mini D2 + │ │ + │ R2 (3.3kΩ) + │ │ + GND ───────────────┴── GND +``` + +### LED Strip Connection +``` +D1 Mini Pin | LED Strip | Description +-------------|--------------|------------- +D4 (GPIO2) | Data In | LED data signal +5V | 5V | Power (use external supply) +GND | GND | Ground +``` + +**Power Note**: For 60 LEDs at full brightness, current draw can reach 3.6A. Use an external 5V power supply, not the REV Hub's servo power. + +## Software Setup + +### Method 1: PlatformIO (Recommended) + +1. Install [Visual Studio Code](https://code.visualstudio.com/) +2. Install PlatformIO IDE extension +3. Clone this repository +4. Open project folder in VS Code +5. Connect D1 Mini via USB +6. Click PlatformIO: Upload (→) in the bottom toolbar + +### Method 2: Arduino IDE + +1. Install [Arduino IDE](https://www.arduino.cc/en/software) +2. Add ESP8266 Board Support: + - File → Preferences → Additional Board Manager URLs: + - Add: `http://arduino.esp8266.com/stable/package_esp8266com_index.json` + - Tools → Board → Board Manager → Search "ESP8266" → Install +3. Install FastLED Library: + - Tools → Manage Libraries → Search "FastLED" → Install +4. Select Board: Tools → Board → "LOLIN(WEMOS) D1 R2 & mini" +5. Open `src/main.cpp` and upload + +## FTC Robot Configuration + +### Hardware Configuration +1. Connect the voltage divider output to a servo port on the REV Hub +2. In the Robot Controller app, configure the servo in your hardware map +3. Name it something meaningful like "blinkin" or "ledDriver" + +### Example FTC Java Code + +```java +import com.qualcomm.robotcore.hardware.Servo; +import com.qualcomm.robotcore.eventloop.opmode.LinearOpMode; + +public class BlinkinExample extends LinearOpMode { + private Servo blinkin; + + @Override + public void runOpMode() { + // Initialize + blinkin = hardwareMap.get(Servo.class, "blinkin"); + + // Set to solid red (pattern value from Blinkin manual) + blinkin.setPosition(0.61); // Solid Red + + waitForStart(); + + while (opModeIsActive()) { + // Alliance color selection + if (gamepad1.x) { + blinkin.setPosition(0.87); // Solid Blue + } else if (gamepad1.b) { + blinkin.setPosition(0.61); // Solid Red + } else if (gamepad1.a) { + blinkin.setPosition(0.77); // Solid Green + } else if (gamepad1.y) { + blinkin.setPosition(0.93); // Solid White + } + + // Special effects + if (gamepad1.left_bumper) { + blinkin.setPosition(0.41); // Rainbow + } + if (gamepad1.right_bumper) { + blinkin.setPosition(0.43); // Confetti + } + } + } +} +``` + +### Common Blinkin Pattern Values + +| Pattern | Servo Position | PWM (μs) | Description | +|---------|---------------|----------|-------------| +| Rainbow | 0.41 | 1005 | Rainbow palette | +| Confetti | 0.43 | 1015 | Random colored pixels | +| Red Shot | 0.53 | 1065 | Red chase pattern | +| Blue Shot | 0.54 | 1075 | Blue chase pattern | +| Red | 0.61 | 1505 | Solid red | +| Orange | 0.65 | 1535 | Solid orange | +| Yellow | 0.69 | 1555 | Solid yellow | +| Green | 0.77 | 1595 | Solid green | +| Blue | 0.87 | 1645 | Solid blue | +| Violet | 0.91 | 1665 | Solid violet | +| White | 0.93 | 1675 | Solid white | +| Black/Off | 0.99 | 1695 | All LEDs off | + +## Configuration + +Edit these values in `src/main.cpp`: + +```cpp +#define NUM_LEDS 60 // Number of LEDs in your strip +#define BRIGHTNESS 100 // Brightness (0-255) +#define DEBUG_MODE true // Serial debug output +``` + +## Testing & Debugging + +1. **Serial Monitor**: Connect via USB and open serial monitor at 115200 baud + - Shows current PWM value and selected pattern + - Displays startup messages and status + +2. **LED Test**: On startup, a green sweep animation indicates the system is ready + +3. **Manual PWM Test**: Use a servo tester to send PWM signals and verify pattern changes + +## Troubleshooting + +### LEDs not lighting up +- Check 5V power supply (must provide sufficient current) +- Verify D4 (GPIO2) is connected to LED data line +- Confirm ground connections between all components + +### Wrong patterns displaying +- Verify voltage divider is working (PWM signal must be ~3.3V) +- Check serial monitor for PWM values (should be 900-2100μs) +- Ensure servo position in FTC code is between 0.0 and 1.0 + +### Erratic behavior +- Add a 1000μF capacitor across LED power lines +- Add a 470Ω resistor between D4 and LED data +- Ensure all grounds are connected together + +### Pattern doesn't match Blinkin +- Some complex patterns may differ slightly from original Blinkin +- Adjust PWM thresholds in `pwmToPattern()` function if needed + +## Power Calculations + +- Each WS2812B LED: ~60mA at full white brightness +- 60 LEDs × 60mA = 3.6A maximum +- With brightness set to 100/255: ~1.4A typical +- D1 Mini consumption: ~80mA +- **Recommended PSU: 5V 4A minimum** + +## Build Guide Documentation + +Additional documentation is available: +- [INSTALLATION_GUIDE.md](INSTALLATION_GUIDE.md) - Detailed setup instructions +- [VOLTAGE_DIVIDER_BUILD_GUIDE.md](VOLTAGE_DIVIDER_BUILD_GUIDE.md) - How to build the voltage divider +- [VOLTAGE_DIVIDER_ALTERNATIVES.md](VOLTAGE_DIVIDER_ALTERNATIVES.md) - Alternative protection methods +- [QUICK_FLASH_GUIDE.md](QUICK_FLASH_GUIDE.md) - Quick programming reference + +## License + +This project is open source and available for FTC teams to use and modify. + +## Credits + +Created for Kraken Koders FTC Team +Based on REV Blinkin LED Driver patterns and functionality \ No newline at end of file diff --git a/VOLTAGE_DIVIDER_ALTERNATIVES.md b/VOLTAGE_DIVIDER_ALTERNATIVES.md new file mode 100644 index 0000000..12aec86 --- /dev/null +++ b/VOLTAGE_DIVIDER_ALTERNATIVES.md @@ -0,0 +1,188 @@ +# Voltage Divider Alternatives - Using What You Have + +## Solution 1: Using Only 2.2kΩ Resistors (RECOMMENDED) + +### Option A: Two 2.2kΩ in Series for Bottom Resistor +This creates a 2.2kΩ top and 4.4kΩ bottom divider. + +``` +PWM (5V) ────[2.2kΩ]────┬──── To D1 Mini D2 (3.33V) + │ + [2.2kΩ] + │ + [2.2kΩ] + │ + GND +``` + +**Output Voltage:** 5V × (4.4kΩ ÷ 6.6kΩ) = **3.33V** ✓ SAFE! + +**How to Build:** +1. Solder white wire to first 2.2kΩ resistor +2. Connect other end to junction point +3. Solder TWO 2.2kΩ resistors in series from junction to ground +4. Take output from junction to D1 Mini D2 + +### Option B: Equal Divider (Simplest) +Uses just two 2.2kΩ resistors for a 50/50 divider. + +``` +PWM (5V) ────[2.2kΩ]────┬──── To D1 Mini D2 (2.5V) + │ + [2.2kΩ] + │ + GND +``` + +**Output Voltage:** 5V × (2.2kΩ ÷ 4.4kΩ) = **2.5V** ✓ SAFE! + +**Pros:** +- Simplest to build +- Only 2 resistors needed +- Still safe for D1 Mini + +**Cons:** +- Lower voltage might miss some PWM pulses in noisy environments +- But should work fine in most cases + +## Solution 2: Common Resistor Combinations + +### If You Have These Resistors: + +| Top Resistor | Bottom Resistor | Output | Safe? | Notes | +|--------------|-----------------|--------|-------|-------| +| 2.2kΩ | 4.7kΩ | 3.4V | ✓ Yes | Very common resistor | +| 2.2kΩ | 2.7kΩ | 2.75V | ✓ Yes | Close to ideal | +| 2.2kΩ | 3.9kΩ | 3.2V | ✓ Yes | Good alternative | +| 2.2kΩ | 2.2kΩ | 2.5V | ✓ Yes | Equal divider | +| 1kΩ | 2.2kΩ | 3.4V | ✓ Yes | If you have 1k | +| 1kΩ | 1.5kΩ | 3.0V | ✓ Yes | Perfect output | + +## Solution 3: No Resistors? Emergency Options + +### Use LEDs as Voltage Droppers (NOT RECOMMENDED) +``` +PWM (5V) ──── Red LED ──── To D1 Mini D2 (~3.3V) +``` +- Red LED drops ~1.7V +- Output: 5V - 1.7V = 3.3V +- **WARNING:** Not reliable, use only for testing! + +### Use Diodes (Better than LEDs) +``` +PWM (5V) ──── 1N4148 ──── 1N4148 ──── To D1 Mini D2 (~3.6V) +``` +- Each silicon diode drops ~0.7V +- Two diodes: 5V - 1.4V = 3.6V +- **Borderline safe** - D1 Mini can usually handle 3.6V + +## Solution 4: Find Resistors in Old Electronics + +### Where to Look: +1. **Old computer power supplies** - Full of resistors +2. **Broken LED bulbs** - Often have 1kΩ-10kΩ resistors +3. **Old TVs/Monitors** - Tons of resistors +4. **Broken phone chargers** - Usually have some resistors +5. **Old Arduino/electronics kits** - Check breadboard projects + +### How to Identify Values: +Use online resistor color code calculator or multimeter + +## Solution 5: Make Your Own 3kΩ-ish Resistor + +### Parallel/Series Combinations: +To get ~3kΩ from 2.2kΩ resistors: + +**Option 1:** 2.2kΩ + 1kΩ in series = 3.2kΩ +**Option 2:** 2.2kΩ + 680Ω in series = 2.88kΩ +**Option 3:** 2.2kΩ + 820Ω in series = 3.02kΩ + +## Quick Test Your Divider + +### With Multimeter: +1. Connect to 5V source (USB charger works) +2. Measure voltage at junction +3. Should read between 2.5V - 3.3V + +### Test Code for D1 Mini: +```cpp +void setup() { + Serial.begin(115200); + pinMode(D2, INPUT); + pinMode(LED_BUILTIN, OUTPUT); +} + +void loop() { + // Read digital state + int state = digitalRead(D2); + digitalWrite(LED_BUILTIN, !state); + + // Try to read PWM + unsigned long pulse = pulseIn(D2, HIGH, 50000); + if (pulse > 0) { + Serial.print("SUCCESS! PWM: "); + Serial.println(pulse); + } else { + Serial.println("Waiting for PWM..."); + } + delay(100); +} +``` + +## Recommended Build with 2.2kΩ Only + +### Materials: +- 3× 2.2kΩ resistors +- Servo cable +- Small piece of wire + +### Steps: +1. **Cut and strip servo cable** (keep female end) +2. **Build the divider:** + - White wire → 2.2kΩ → Junction + - Junction → 2.2kΩ → 2.2kΩ → Black wire + - Junction → Wire to D1 Mini D2 +3. **Connect power:** + - Red wire → D1 Mini 5V + - Black wire → D1 Mini GND +4. **Test it!** + +### Visual Build: +``` + [Servo Cable] + | + R━━━B━━━W━━━━━[2.2k]━━━━●━━━━> To D1 Mini D2 + | | | + | | [2.2k]━━[2.2k] + | | | + | └────────────────────┘ + | + └──> To D1 Mini 5V +``` + +## Why These Work + +The D1 Mini GPIO pins are rated for 3.3V but can tolerate up to 3.6V briefly. Any voltage between 2.5V and 3.3V will work reliably: + +- **2.5V** - Minimum reliable HIGH signal +- **3.0V** - Ideal target +- **3.3V** - Perfect match +- **3.6V** - Maximum safe limit +- **5.0V** - WILL DAMAGE THE D1 MINI! + +## Final Tips + +1. **When in doubt, measure!** Use a multimeter if you have one +2. **2.5V is better than 5V** - Lower voltage is safe, too high will kill the D1 Mini +3. **Test with LED first** - The onboard LED should blink when receiving PWM +4. **Use what you have** - Many combinations work, just stay under 3.6V + +## Shopping List (if you need to buy) + +Minimum parts from Amazon/eBay: +- **Resistor kit** (~$5-10) - Includes hundreds of values +- **2.7kΩ resistor** (5 pack ~$1) - Makes perfect 3V with 2.2kΩ +- **3kΩ resistor** (5 pack ~$1) - Close enough to 3.3kΩ +- **4.7kΩ resistor** (5 pack ~$1) - Common value, works great + +Any of these will work perfectly with your 2.2kΩ resistor! \ No newline at end of file diff --git a/VOLTAGE_DIVIDER_BUILD_GUIDE.md b/VOLTAGE_DIVIDER_BUILD_GUIDE.md new file mode 100644 index 0000000..5140e0c --- /dev/null +++ b/VOLTAGE_DIVIDER_BUILD_GUIDE.md @@ -0,0 +1,238 @@ +# Voltage Divider Build Guide for D1 Mini Servo Port Connection + +## Why You Need a Voltage Divider + +The servo port outputs a 5V PWM signal, but the D1 Mini's GPIO pins can only safely handle 3.3V. Without a voltage divider, you risk damaging your D1 Mini! + +## Required Components + +### For the Voltage Divider: +- **1x 2.2kΩ resistor** (Red-Red-Red-Gold color bands) +- **1x 3.3kΩ resistor** (Orange-Orange-Red-Gold color bands) +- **Alternative:** 2kΩ and 3kΩ resistors also work fine +- **Alternative:** 1kΩ and 2kΩ resistors (gives ~3.33V output) + +### Tools & Materials: +- Soldering iron and solder +- Heat shrink tubing (3mm and 6mm diameter) +- Servo extension cable (to cut) +- Wire strippers +- Multimeter (optional but recommended) + +## How a Voltage Divider Works + +``` +5V Signal ──────[2.2kΩ]──────┬────── 3.3V Output (to D1 Mini) + │ + [3.3kΩ] + │ + GND +``` + +The voltage divider reduces 5V to 3.3V using this formula: +- Output = 5V × (3.3kΩ ÷ (2.2kΩ + 3.3kΩ)) +- Output = 5V × (3.3 ÷ 5.5) = 3.0V (safe for D1 Mini) + +## Step-by-Step Assembly Instructions + +### Step 1: Prepare the Servo Cable +1. Take a servo extension cable (keep the female end) +2. Cut it about 6 inches from the female connector +3. Strip the three wires about 1/4 inch: + - **Red wire** = 5V power + - **Black/Brown wire** = Ground + - **White/Orange/Yellow wire** = PWM signal + +### Step 2: Build the Voltage Divider + +#### Method A: Inline Construction (Easiest) +``` + ┌─────────────┐ +PWM Wire (White) ────┤ 2.2kΩ ├──── Junction ──── To D1 Mini D2 + └─────────────┘ │ + ┌─┴─────────┐ + │ 3.3kΩ │ + └───────────┘ + │ + GND +``` + +**Assembly Steps:** +1. **Solder the 2.2kΩ resistor to the white wire:** + - Tin the white wire with solder + - Bend one leg of the 2.2kΩ resistor into a small hook + - Hook it around the white wire and solder + - Trim excess resistor lead + +2. **Create the junction point:** + - Leave about 1/2 inch of the resistor's other leg exposed + - This will be your junction point + +3. **Attach the 3.3kΩ resistor:** + - Wrap one leg of the 3.3kΩ resistor around the junction + - Solder the connection + - The other leg of the 3.3kΩ resistor needs to connect to GND + +4. **Add the output wire:** + - Cut a 6-inch piece of wire (any thin wire works) + - Strip both ends + - Solder one end to the junction point + - This wire goes to D1 Mini pin D2 + +5. **Connect ground:** + - Solder the free end of the 3.3kΩ resistor to the black (GND) wire + +6. **Insulate everything:** + - Slide heat shrink tubing over each connection + - Heat with lighter or heat gun to shrink + - Use larger heat shrink to cover the entire assembly + +#### Method B: On a Small Piece of Perfboard (More Robust) +``` + Perfboard Layout (viewed from top): + + ●───●───●───●───● + │ │ │ │ │ + A B C D E + + A: PWM input (white wire) + B: 2.2kΩ resistor start + C: Junction (2.2kΩ end, 3.3kΩ start, output wire) + D: 3.3kΩ resistor end + E: Ground connection +``` + +1. Cut a small piece of perfboard (5 holes × 3 holes) +2. Insert resistors through holes +3. Solder on the bottom and connect traces +4. Add wires for input/output/ground +5. Cover with hot glue for protection + +### Step 3: Complete D1 Mini Connections + +Final wiring to D1 Mini: +``` +Servo Connector Voltage Divider D1 Mini +=============== =============== ======= +Red (5V) ────────────────────────────────────── 5V +Black (GND) ─────┬───────────────────────────── GND + │ +White (PWM) ────[2.2kΩ]────┬─────────────────── D2 + │ + [3.3kΩ] + │ + GND + +D1 Mini D4 ──────────────────────────────────── LED Strip Data +``` + +## Testing Your Voltage Divider + +### With a Multimeter: +1. Connect servo connector to a servo tester or Control Hub +2. Set multimeter to DC voltage mode +3. Measure between the divider output and ground +4. You should see ~3V when PWM is high, 0V when low + +### Quick Test Code: +Upload this to your D1 Mini to test PWM reading: + +```cpp +void setup() { + Serial.begin(115200); + pinMode(D2, INPUT); +} + +void loop() { + unsigned long pwm = pulseIn(D2, HIGH, 50000); + if (pwm > 0) { + Serial.print("PWM: "); + Serial.print(pwm); + Serial.println(" microseconds"); + } + delay(100); +} +``` + +## Common Issues and Solutions + +### Problem: No PWM reading +- Check voltage divider connections with multimeter +- Verify resistor values (use multimeter to measure) +- Ensure good solder joints +- Check that servo port is powered and configured + +### Problem: Erratic readings +- Add a 100nF ceramic capacitor from D2 to GND +- Check for loose connections +- Keep wires short (under 12 inches) + +### Problem: D1 Mini resets/restarts +- Insufficient power - add external 5V for LEDs +- Check for shorts in wiring +- Voltage spike - add 1000µF capacitor on power rails + +## Alternative Resistor Values + +If you don't have 2.2kΩ and 3.3kΩ resistors: + +| R1 (top) | R2 (bottom) | Output Voltage | Safe? | +|----------|-------------|----------------|-------| +| 2.2kΩ | 3.3kΩ | 3.0V | ✓ Yes | +| 2kΩ | 3kΩ | 3.0V | ✓ Yes | +| 1kΩ | 2kΩ | 3.33V | ✓ Yes | +| 10kΩ | 15kΩ | 3.0V | ✓ Yes | +| 4.7kΩ | 6.8kΩ | 2.96V | ✓ Yes | +| 1kΩ | 1kΩ | 2.5V | ✓ Yes (but low) | + +## Pro Tips + +1. **Use 1/4 watt resistors** - They're easier to work with +2. **Color code your wires** - Makes troubleshooting easier +3. **Test before connecting** - Always verify voltage levels +4. **Make it modular** - Use connectors so you can disconnect for testing +5. **Document your build** - Take photos for future reference + +## Safety First! + +⚠️ **NEVER** connect 5V directly to D1 Mini GPIO pins! +⚠️ **ALWAYS** test with a multimeter before connecting +⚠️ **DOUBLE CHECK** resistor values before soldering + +## Quick Reference Resistor Color Codes + +**2.2kΩ (2,200Ω):** +- Red (2) +- Red (2) +- Red (×100) +- Gold (±5% tolerance) + +**3.3kΩ (3,300Ω):** +- Orange (3) +- Orange (3) +- Red (×100) +- Gold (±5% tolerance) + +## Building Without Soldering (Temporary Testing) + +For testing, you can use: +1. **Breadboard** - Insert components for testing +2. **Twist and tape** - Temporary but unreliable +3. **Wire nuts** - Better than twisting +4. **WAGO connectors** - Professional temporary connections + +**Note:** For competition use, always solder connections! + +## Final Assembly Checklist + +- [ ] Voltage divider outputs 3.0-3.3V when tested +- [ ] All connections are soldered +- [ ] Heat shrink applied to all exposed connections +- [ ] No shorts between power and ground +- [ ] Servo cable connects firmly +- [ ] D1 Mini powers on when connected +- [ ] PWM test code shows readings 900-2100 microseconds +- [ ] LED strip connected with proper power +- [ ] Everything secured and won't come loose + +Once complete, your D1 Mini is ready to emulate a REV Blinkin LED Driver! \ No newline at end of file diff --git a/include/README b/include/README new file mode 100644 index 0000000..49819c0 --- /dev/null +++ b/include/README @@ -0,0 +1,37 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the convention is to give header files names that end with `.h'. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/lib/README b/lib/README new file mode 100644 index 0000000..9379397 --- /dev/null +++ b/lib/README @@ -0,0 +1,46 @@ + +This directory is intended for project specific (private) libraries. +PlatformIO will compile them to static libraries and link into the executable file. + +The source code of each library should be placed in a separate directory +("lib/your_library_name/[Code]"). + +For example, see the structure of the following example libraries `Foo` and `Bar`: + +|--lib +| | +| |--Bar +| | |--docs +| | |--examples +| | |--src +| | |- Bar.c +| | |- Bar.h +| | |- library.json (optional. for custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html +| | +| |--Foo +| | |- Foo.c +| | |- Foo.h +| | +| |- README --> THIS FILE +| +|- platformio.ini +|--src + |- main.c + +Example contents of `src/main.c` using Foo and Bar: +``` +#include +#include + +int main (void) +{ + ... +} + +``` + +The PlatformIO Library Dependency Finder will find automatically dependent +libraries by scanning project source files. + +More information about PlatformIO Library Dependency Finder +- https://docs.platformio.org/page/librarymanager/ldf.html diff --git a/platformio.ini b/platformio.ini new file mode 100644 index 0000000..8b5828e --- /dev/null +++ b/platformio.ini @@ -0,0 +1,27 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + +[env:d1_mini] +platform = espressif8266 +board = d1_mini +framework = arduino +upload_speed = 921600 +monitor_speed = 115200 +lib_deps = + fastled/FastLED@^3.6.0 + +; Optional build flags for debugging +build_flags = + -D DEBUG_ESP_PORT=Serial + -D DEBUG_ESP_BAUD=115200 + +; If you have multiple D1 Minis connected, uncomment and set specific port +; upload_port = COM3 +; monitor_port = COM3 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..72ca418 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,898 @@ +/** + * D1 Mini Blinkin LED Controller - Corrected to match REV Blinkin Java Driver + * + * This version correctly matches the pattern ordering from the Java RevBlinkinLedDriver class + * + * Hardware connections: + * - D2 (GPIO4): PWM input from REV Control Hub servo port + * - D4 (GPIO2): LED data output (WS2812B) + * - GND: Common ground with Control Hub + * - 5V: Power for D1 Mini (can be from USB or external) + * + * IMPORTANT: The pattern enum order MUST match the Java driver exactly! + */ + +#include +#include +#include + +// Configuration +#define NUM_LEDS 120 // Number of LEDs in your strip +#define BRIGHTNESS 128 // LED brightness (0-255) +#define DEBUG_MODE true // Enable serial debugging +#define PWM_INPUT_PIN D2 // Pin for PWM input (GPIO4) +#define LED_DATA_PIN D4 // Pin for LED data (GPIO2) +#define ONBOARD_LED LED_BUILTIN // Onboard LED for status + +// PWM constants +#define PWM_MIN 900 // Minimum valid PWM +#define PWM_MAX 2100 // Maximum valid PWM +#define PWM_TIMEOUT 100000 // Timeout in microseconds (100ms) +#define PWM_DEADBAND 5 // Deadband for PWM changes +#define PWM_FILTER_SAMPLES 3 // Number of samples for filtering + +// LED strip +CRGB leds[NUM_LEDS]; + +// Pattern enumeration - MUST MATCH Java RevBlinkinLedDriver enum order EXACTLY! +enum Pattern { + // Fixed Palette Patterns (0-31) + RAINBOW_RAINBOW_PALETTE, // 0 + RAINBOW_PARTY_PALETTE, // 1 + RAINBOW_OCEAN_PALETTE, // 2 + RAINBOW_LAVA_PALETTE, // 3 + RAINBOW_FOREST_PALETTE, // 4 + RAINBOW_WITH_GLITTER, // 5 + CONFETTI, // 6 + SHOT_RED, // 7 + SHOT_BLUE, // 8 + SHOT_WHITE, // 9 + SINELON_RAINBOW_PALETTE, // 10 + SINELON_PARTY_PALETTE, // 11 + SINELON_OCEAN_PALETTE, // 12 + SINELON_LAVA_PALETTE, // 13 + SINELON_FOREST_PALETTE, // 14 + BEATS_PER_MINUTE_RAINBOW_PALETTE, // 15 + BEATS_PER_MINUTE_PARTY_PALETTE, // 16 + BEATS_PER_MINUTE_OCEAN_PALETTE, // 17 + BEATS_PER_MINUTE_LAVA_PALETTE, // 18 + BEATS_PER_MINUTE_FOREST_PALETTE, // 19 + FIRE_MEDIUM, // 20 + FIRE_LARGE, // 21 + TWINKLES_RAINBOW_PALETTE, // 22 + TWINKLES_PARTY_PALETTE, // 23 + TWINKLES_OCEAN_PALETTE, // 24 + TWINKLES_LAVA_PALETTE, // 25 + TWINKLES_FOREST_PALETTE, // 26 + COLOR_WAVES_RAINBOW_PALETTE, // 27 + COLOR_WAVES_PARTY_PALETTE, // 28 + COLOR_WAVES_OCEAN_PALETTE, // 29 + COLOR_WAVES_LAVA_PALETTE, // 30 + COLOR_WAVES_FOREST_PALETTE, // 31 + LARSON_SCANNER_RED, // 32 + LARSON_SCANNER_GRAY, // 33 + LIGHT_CHASE_RED, // 34 + LIGHT_CHASE_BLUE, // 35 + LIGHT_CHASE_GRAY, // 36 + HEARTBEAT_RED, // 37 + HEARTBEAT_BLUE, // 38 + HEARTBEAT_WHITE, // 39 + HEARTBEAT_GRAY, // 40 + BREATH_RED, // 41 + BREATH_BLUE, // 42 + BREATH_GRAY, // 43 + STROBE_RED, // 44 + STROBE_BLUE, // 45 + STROBE_GOLD, // 46 + STROBE_WHITE, // 47 + + // CP1: Color 1 Patterns (48-57) + CP1_END_TO_END_BLEND_TO_BLACK, // 48 + CP1_LARSON_SCANNER, // 49 + CP1_LIGHT_CHASE, // 50 + CP1_HEARTBEAT_SLOW, // 51 + CP1_HEARTBEAT_MEDIUM, // 52 + CP1_HEARTBEAT_FAST, // 53 + CP1_BREATH_SLOW, // 54 + CP1_BREATH_FAST, // 55 + CP1_SHOT, // 56 + CP1_STROBE, // 57 + + // CP2: Color 2 Patterns (58-67) + CP2_END_TO_END_BLEND_TO_BLACK, // 58 + CP2_LARSON_SCANNER, // 59 + CP2_LIGHT_CHASE, // 60 + CP2_HEARTBEAT_SLOW, // 61 + CP2_HEARTBEAT_MEDIUM, // 62 + CP2_HEARTBEAT_FAST, // 63 + CP2_BREATH_SLOW, // 64 + CP2_BREATH_FAST, // 65 + CP2_SHOT, // 66 + CP2_STROBE, // 67 + + // CP1_2: Color 1 and 2 Patterns (68-77) + CP1_2_SPARKLE_1_ON_2, // 68 + CP1_2_SPARKLE_2_ON_1, // 69 + CP1_2_COLOR_GRADIENT, // 70 + CP1_2_BEATS_PER_MINUTE, // 71 + CP1_2_END_TO_END_BLEND_1_TO_2, // 72 + CP1_2_END_TO_END_BLEND, // 73 + CP1_2_NO_BLENDING, // 74 + CP1_2_TWINKLES, // 75 + CP1_2_COLOR_WAVES, // 76 + CP1_2_SINELON, // 77 + + // Solid Colors (78-99) + HOT_PINK, // 78 + DARK_RED, // 79 + RED, // 80 + RED_ORANGE, // 81 + ORANGE, // 82 + GOLD, // 83 + YELLOW, // 84 + LAWN_GREEN, // 85 + LIME, // 86 + DARK_GREEN, // 87 + GREEN, // 88 + BLUE_GREEN, // 89 + AQUA, // 90 + SKY_BLUE, // 91 + DARK_BLUE, // 92 + BLUE, // 93 + BLUE_VIOLET, // 94 + VIOLET, // 95 + WHITE, // 96 + GRAY, // 97 + DARK_GRAY, // 98 + BLACK // 99 +}; + +// Custom color palette support +CRGB customColor1 = CRGB::Red; +CRGB customColor2 = CRGB::Blue; + +// Global variables +Pattern currentPattern = BLACK; +Pattern lastPattern = BLACK; +unsigned long lastPWM = 0; +unsigned long lastPatternChange = 0; +unsigned long lastPWMReceived = 0; +bool isActive = false; +uint8_t gHue = 0; // For rainbow effects + +// PWM filtering buffer +unsigned long pwmBuffer[PWM_FILTER_SAMPLES]; +int pwmBufferIndex = 0; + +void setup() { + // Initialize serial for debugging + if (DEBUG_MODE) { + Serial.begin(115200); + Serial.println("\n\nD1 Mini Blinkin LED Controller - Corrected Version"); + Serial.println("Pattern order matches Java RevBlinkinLedDriver"); + Serial.println("Using D2 for PWM input (GPIO4)"); + Serial.println("Using D4 for LED output (GPIO2)"); + Serial.println("Waiting for PWM signal..."); + } + + // Disable WiFi to save power + WiFi.mode(WIFI_OFF); + WiFi.forceSleepBegin(); + + // Initialize pins + pinMode(PWM_INPUT_PIN, INPUT); + pinMode(ONBOARD_LED, OUTPUT); + digitalWrite(ONBOARD_LED, HIGH); // LED off (inverted on D1 Mini) + + // Initialize LED strip + FastLED.addLeds(leds, NUM_LEDS); + FastLED.setBrightness(BRIGHTNESS); + FastLED.clear(); + FastLED.show(); + + // Initialize PWM buffer + for (int i = 0; i < PWM_FILTER_SAMPLES; i++) { + pwmBuffer[i] = 0; + } + + // Show startup animation + startupAnimation(); +} + +void loop() { + // Read PWM with filtering + unsigned long pwm = readPWMFiltered(); + + // Check if we have a valid PWM signal + if (pwm > 0) { + lastPWMReceived = millis(); + isActive = true; + digitalWrite(ONBOARD_LED, LOW); // LED on + + // Convert PWM to pattern + int newPattern = pwmToPattern(pwm); + + // Check if pattern changed (with deadband) + if (newPattern != currentPattern && abs((int)(pwm - lastPWM)) > PWM_DEADBAND) { + lastPattern = currentPattern; + currentPattern = (Pattern)newPattern; + lastPatternChange = millis(); + lastPWM = pwm; + + if (DEBUG_MODE) { + Serial.print("Pattern: "); + Serial.print(getPatternName(currentPattern)); + Serial.print(" (#"); + Serial.print(newPattern); + Serial.print(", PWM: "); + Serial.print(pwm); + Serial.println(" us)"); + } + } + } else { + // No PWM signal - check for timeout + if (millis() - lastPWMReceived > 500) { // 500ms timeout + isActive = false; + digitalWrite(ONBOARD_LED, HIGH); // LED off + currentPattern = BLACK; + } + } + + // Update LED pattern + updatePattern(currentPattern); + + // Periodic status update + static unsigned long lastStatus = 0; + if (DEBUG_MODE && millis() - lastStatus > 1000) { + lastStatus = millis(); + Serial.print("Status: "); + Serial.print(isActive ? "Active" : "Inactive"); + Serial.print(" | PWM: "); + Serial.print(lastPWM); + Serial.print(" | Pattern: "); + Serial.println(getPatternName(currentPattern)); + } +} + +unsigned long readPWMFiltered() { + // Read PWM pulse width with filtering + unsigned long pulse = pulseIn(PWM_INPUT_PIN, HIGH, PWM_TIMEOUT); + + // Filter out invalid readings + if (pulse < PWM_MIN || pulse > PWM_MAX) { + return 0; + } + + // Add to filter buffer + pwmBuffer[pwmBufferIndex] = pulse; + pwmBufferIndex = (pwmBufferIndex + 1) % PWM_FILTER_SAMPLES; + + // Calculate average + unsigned long sum = 0; + int validSamples = 0; + for (int i = 0; i < PWM_FILTER_SAMPLES; i++) { + if (pwmBuffer[i] > 0) { + sum += pwmBuffer[i]; + validSamples++; + } + } + + if (validSamples > 0) { + return sum / validSamples; + } + + return 0; +} + +int pwmToPattern(unsigned long pwm) { + // Based on Java driver calculation: + // BASE_SERVO_POSITION = 0.2525 (505 * 0.0005) + // PATTERN_OFFSET = 10 + // PWM = 1005 + (pattern_number * 10) microseconds + + // Valid PWM range check + if (pwm < 1000 || pwm > 2000) { + return BLACK; // Default to BLACK for invalid PWM + } + + // Calculate pattern number from PWM + // Pattern 0 should be at 1005μs, Pattern 1 at 1015μs, etc. + int patternNumber = (pwm - 1005) / 10; + + // Add rounding for values in between + if ((pwm - 1005) % 10 >= 5) { + patternNumber++; + } + + // Clamp to valid pattern range (0-99) + if (patternNumber < 0) patternNumber = 0; + if (patternNumber > 99) patternNumber = 99; + + if (DEBUG_MODE && pwm != lastPWM) { + Serial.print("PWM decode: "); + Serial.print(pwm); + Serial.print("us -> Pattern #"); + Serial.println(patternNumber); + } + + return patternNumber; +} + +void updatePattern(Pattern pattern) { + static unsigned long lastUpdate = 0; + unsigned long currentMillis = millis(); + + // Update animations at 60 FPS + if (currentMillis - lastUpdate < 16) return; + lastUpdate = currentMillis; + + switch(pattern) { + // Fixed Palette Patterns + case RAINBOW_RAINBOW_PALETTE: rainbowCycle(); break; + case RAINBOW_PARTY_PALETTE: partyMode(); break; + case RAINBOW_OCEAN_PALETTE: oceanWave(); break; + case RAINBOW_LAVA_PALETTE: lavaFlow(); break; + case RAINBOW_FOREST_PALETTE: forestBreeze(); break; + case RAINBOW_WITH_GLITTER: rainbowWithGlitter(); break; + case CONFETTI: confetti(); break; + case SHOT_RED: shot(CRGB::Red); break; + case SHOT_BLUE: shot(CRGB::Blue); break; + case SHOT_WHITE: shot(CRGB::White); break; + case SINELON_RAINBOW_PALETTE: sinelon(RainbowColors_p); break; + case SINELON_PARTY_PALETTE: sinelon(PartyColors_p); break; + case SINELON_OCEAN_PALETTE: sinelon(OceanColors_p); break; + case SINELON_LAVA_PALETTE: sinelon(LavaColors_p); break; + case SINELON_FOREST_PALETTE: sinelon(ForestColors_p); break; + case BEATS_PER_MINUTE_RAINBOW_PALETTE: bpm(RainbowColors_p); break; + case BEATS_PER_MINUTE_PARTY_PALETTE: bpm(PartyColors_p); break; + case BEATS_PER_MINUTE_OCEAN_PALETTE: bpm(OceanColors_p); break; + case BEATS_PER_MINUTE_LAVA_PALETTE: bpm(LavaColors_p); break; + case BEATS_PER_MINUTE_FOREST_PALETTE: bpm(ForestColors_p); break; + case FIRE_MEDIUM: fire(55); break; + case FIRE_LARGE: fire(120); break; + case TWINKLES_RAINBOW_PALETTE: twinkles(RainbowColors_p); break; + case TWINKLES_PARTY_PALETTE: twinkles(PartyColors_p); break; + case TWINKLES_OCEAN_PALETTE: twinkles(OceanColors_p); break; + case TWINKLES_LAVA_PALETTE: twinkles(LavaColors_p); break; + case TWINKLES_FOREST_PALETTE: twinkles(ForestColors_p); break; + case COLOR_WAVES_RAINBOW_PALETTE: colorWaves(RainbowColors_p); break; + case COLOR_WAVES_PARTY_PALETTE: colorWaves(PartyColors_p); break; + case COLOR_WAVES_OCEAN_PALETTE: colorWaves(OceanColors_p); break; + case COLOR_WAVES_LAVA_PALETTE: colorWaves(LavaColors_p); break; + case COLOR_WAVES_FOREST_PALETTE: colorWaves(ForestColors_p); break; + case LARSON_SCANNER_RED: larsonScanner(CRGB::Red); break; + case LARSON_SCANNER_GRAY: larsonScanner(CRGB::Gray); break; + case LIGHT_CHASE_RED: lightChase(CRGB::Red); break; + case LIGHT_CHASE_BLUE: lightChase(CRGB::Blue); break; + case LIGHT_CHASE_GRAY: lightChase(CRGB::Gray); break; + case HEARTBEAT_RED: heartbeat(CRGB::Red); break; + case HEARTBEAT_BLUE: heartbeat(CRGB::Blue); break; + case HEARTBEAT_WHITE: heartbeat(CRGB::White); break; + case HEARTBEAT_GRAY: heartbeat(CRGB::Gray); break; + case BREATH_RED: breathe(CRGB::Red); break; + case BREATH_BLUE: breathe(CRGB::Blue); break; + case BREATH_GRAY: breathe(CRGB::Gray); break; + case STROBE_RED: strobe(CRGB::Red); break; + case STROBE_BLUE: strobe(CRGB::Blue); break; + case STROBE_GOLD: strobe(CRGB::Gold); break; + case STROBE_WHITE: strobe(CRGB::White); break; + + // CP1 Patterns (use customColor1) + case CP1_END_TO_END_BLEND_TO_BLACK: endToEndBlendToBlack(customColor1); break; + case CP1_LARSON_SCANNER: larsonScanner(customColor1); break; + case CP1_LIGHT_CHASE: lightChase(customColor1); break; + case CP1_HEARTBEAT_SLOW: heartbeatSlow(customColor1); break; + case CP1_HEARTBEAT_MEDIUM: heartbeat(customColor1); break; + case CP1_HEARTBEAT_FAST: heartbeatFast(customColor1); break; + case CP1_BREATH_SLOW: breatheSlow(customColor1); break; + case CP1_BREATH_FAST: breatheFast(customColor1); break; + case CP1_SHOT: shot(customColor1); break; + case CP1_STROBE: strobe(customColor1); break; + + // CP2 Patterns (use customColor2) + case CP2_END_TO_END_BLEND_TO_BLACK: endToEndBlendToBlack(customColor2); break; + case CP2_LARSON_SCANNER: larsonScanner(customColor2); break; + case CP2_LIGHT_CHASE: lightChase(customColor2); break; + case CP2_HEARTBEAT_SLOW: heartbeatSlow(customColor2); break; + case CP2_HEARTBEAT_MEDIUM: heartbeat(customColor2); break; + case CP2_HEARTBEAT_FAST: heartbeatFast(customColor2); break; + case CP2_BREATH_SLOW: breatheSlow(customColor2); break; + case CP2_BREATH_FAST: breatheFast(customColor2); break; + case CP2_SHOT: shot(customColor2); break; + case CP2_STROBE: strobe(customColor2); break; + + // CP1_2 Patterns (use both colors) + case CP1_2_SPARKLE_1_ON_2: sparkle(customColor1, customColor2); break; + case CP1_2_SPARKLE_2_ON_1: sparkle(customColor2, customColor1); break; + case CP1_2_COLOR_GRADIENT: colorGradient(customColor1, customColor2); break; + case CP1_2_BEATS_PER_MINUTE: bpmTwoColor(customColor1, customColor2); break; + case CP1_2_END_TO_END_BLEND_1_TO_2: endToEndBlend(customColor1, customColor2); break; + case CP1_2_END_TO_END_BLEND: endToEndBlend(customColor1, customColor2); break; + case CP1_2_NO_BLENDING: noBlending(customColor1, customColor2); break; + case CP1_2_TWINKLES: twinklesTwoColor(customColor1, customColor2); break; + case CP1_2_COLOR_WAVES: colorWavesTwoColor(customColor1, customColor2); break; + case CP1_2_SINELON: sinelonTwoColor(customColor1, customColor2); break; + + // Solid Colors + case HOT_PINK: setAll(CRGB::HotPink); break; + case DARK_RED: setAll(CRGB::DarkRed); break; + case RED: setAll(CRGB::Red); break; + case RED_ORANGE: setAll(CRGB::OrangeRed); break; + case ORANGE: setAll(CRGB::Orange); break; + case GOLD: setAll(CRGB::Gold); break; + case YELLOW: setAll(CRGB::Yellow); break; + case LAWN_GREEN: setAll(CRGB::LawnGreen); break; + case LIME: setAll(CRGB::Lime); break; + case DARK_GREEN: setAll(CRGB::DarkGreen); break; + case GREEN: setAll(CRGB::Green); break; + case BLUE_GREEN: setAll(CRGB(0, 128, 128)); break; + case AQUA: setAll(CRGB::Aqua); break; + case SKY_BLUE: setAll(CRGB::SkyBlue); break; + case DARK_BLUE: setAll(CRGB::DarkBlue); break; + case BLUE: setAll(CRGB::Blue); break; + case BLUE_VIOLET: setAll(CRGB::BlueViolet); break; + case VIOLET: setAll(CRGB::Violet); break; + case WHITE: setAll(CRGB::White); break; + case GRAY: setAll(CRGB::Gray); break; + case DARK_GRAY: setAll(CRGB::DarkGray); break; + case BLACK: setAll(CRGB::Black); break; + + default: setAll(CRGB::Black); break; + } + + FastLED.show(); +} + +// Pattern implementation functions +void setAll(CRGB color) { + fill_solid(leds, NUM_LEDS, color); +} + +void rainbowCycle() { + fill_rainbow(leds, NUM_LEDS, gHue, 7); + gHue++; +} + +void rainbowWithGlitter() { + rainbowCycle(); + if(random8() < 80) { + leds[random16(NUM_LEDS)] += CRGB::White; + } +} + +void partyMode() { + fadeToBlackBy(leds, NUM_LEDS, 10); + int pos = random16(NUM_LEDS); + leds[pos] += CHSV(gHue + random8(64), 200, 255); + gHue++; +} + +void oceanWave() { + fill_palette(leds, NUM_LEDS, gHue, 9, OceanColors_p, 255, LINEARBLEND); + gHue++; +} + +void lavaFlow() { + fill_palette(leds, NUM_LEDS, gHue, 9, LavaColors_p, 255, LINEARBLEND); + gHue++; +} + +void forestBreeze() { + fill_palette(leds, NUM_LEDS, gHue, 9, ForestColors_p, 255, LINEARBLEND); + gHue++; +} + +void confetti() { + fadeToBlackBy(leds, NUM_LEDS, 10); + int pos = random16(NUM_LEDS); + leds[pos] += CHSV(gHue + random8(64), 200, 255); +} + +void shot(CRGB color) { + fadeToBlackBy(leds, NUM_LEDS, 10); + static int pos = 0; + leds[pos] = color; + pos = (pos + 1) % NUM_LEDS; +} + +void sinelon(CRGBPalette16 palette) { + fadeToBlackBy(leds, NUM_LEDS, 20); + int pos = beatsin16(13, 0, NUM_LEDS - 1); + leds[pos] += ColorFromPalette(palette, gHue, 192); + gHue++; +} + +void bpm(CRGBPalette16 palette) { + uint8_t BeatsPerMinute = 62; + uint8_t beat = beatsin8(BeatsPerMinute, 64, 255); + for(int i = 0; i < NUM_LEDS; i++) { + leds[i] = ColorFromPalette(palette, gHue+(i*2), beat-gHue+(i*10)); + } + gHue++; +} + +void fire(int cooling) { + static byte heat[NUM_LEDS]; + + // Cool down + for(int i = 0; i < NUM_LEDS; i++) { + heat[i] = qsub8(heat[i], random8(0, ((cooling * 10) / NUM_LEDS) + 2)); + } + + // Heat drift + for(int k = NUM_LEDS - 1; k >= 2; k--) { + heat[k] = (heat[k - 1] + heat[k - 2] + heat[k - 2]) / 3; + } + + // Ignite + if(random8() < 120) { + int y = random8(7); + heat[y] = qadd8(heat[y], random8(160, 255)); + } + + // Convert to LED colors + for(int j = 0; j < NUM_LEDS; j++) { + CRGB color = HeatColor(heat[j]); + leds[j] = color; + } +} + +void twinkles(CRGBPalette16 palette) { + fadeToBlackBy(leds, NUM_LEDS, 80); + if(random8() < 80) { + leds[random16(NUM_LEDS)] = ColorFromPalette(palette, random8(), 255); + } +} + +void colorWaves(CRGBPalette16 palette) { + static uint16_t sPseudotime = 0; + static uint16_t sLastMillis = 0; + static uint16_t sHue16 = 0; + + uint8_t brightdepth = beatsin88(341, 96, 224); + uint16_t brightnessthetainc16 = beatsin88(203, (25 * 256), (40 * 256)); + uint8_t msmultiplier = beatsin88(147, 23, 60); + + uint16_t hue16 = sHue16; + uint16_t hueinc16 = beatsin88(113, 1, 3000); + + uint16_t ms = millis(); + uint16_t deltams = ms - sLastMillis; + sLastMillis = ms; + sPseudotime += deltams * msmultiplier; + sHue16 += deltams * beatsin88(400, 5, 9); + uint16_t brightnesstheta16 = sPseudotime; + + for(uint16_t i = 0; i < NUM_LEDS; i++) { + hue16 += hueinc16; + uint8_t hue8 = hue16 / 256; + + brightnesstheta16 += brightnessthetainc16; + uint16_t b16 = sin16(brightnesstheta16) + 32768; + + uint16_t bri16 = (uint32_t)((uint32_t)b16 * (uint32_t)b16) / 65536; + uint8_t bri8 = (uint32_t)(((uint32_t)bri16) * brightdepth) / 65536; + bri8 += (255 - brightdepth); + + leds[i] = ColorFromPalette(palette, hue8, bri8); + } +} + +void larsonScanner(CRGB color) { + static int pos = 0; + static int dir = 1; + + fadeToBlackBy(leds, NUM_LEDS, 50); + leds[pos] = color; + + pos += dir; + if(pos <= 0 || pos >= NUM_LEDS - 1) dir = -dir; +} + +void lightChase(CRGB color) { + static int pos = 0; + + fadeToBlackBy(leds, NUM_LEDS, 90); + for(int i = 0; i < NUM_LEDS; i += 3) { + leds[(i + pos) % NUM_LEDS] = color; + } + pos = (pos + 1) % 3; +} + +void heartbeat(CRGB color) { + static uint8_t BeatsPerMinute = 62; + uint8_t beat = beatsin8(BeatsPerMinute, 0, 255); + + for(int i = 0; i < NUM_LEDS; i++) { + leds[i] = color; + leds[i].fadeLightBy(255 - beat); + } +} + +void heartbeatSlow(CRGB color) { + static uint8_t BeatsPerMinute = 40; + uint8_t beat = beatsin8(BeatsPerMinute, 0, 255); + + for(int i = 0; i < NUM_LEDS; i++) { + leds[i] = color; + leds[i].fadeLightBy(255 - beat); + } +} + +void heartbeatFast(CRGB color) { + static uint8_t BeatsPerMinute = 100; + uint8_t beat = beatsin8(BeatsPerMinute, 0, 255); + + for(int i = 0; i < NUM_LEDS; i++) { + leds[i] = color; + leds[i].fadeLightBy(255 - beat); + } +} + +void breathe(CRGB color) { + uint8_t brightness = beatsin8(12, 50, 255); + fill_solid(leds, NUM_LEDS, color); + FastLED.setBrightness(scale8(BRIGHTNESS, brightness)); +} + +void breatheSlow(CRGB color) { + uint8_t brightness = beatsin8(8, 50, 255); + fill_solid(leds, NUM_LEDS, color); + FastLED.setBrightness(scale8(BRIGHTNESS, brightness)); +} + +void breatheFast(CRGB color) { + uint8_t brightness = beatsin8(20, 50, 255); + fill_solid(leds, NUM_LEDS, color); + FastLED.setBrightness(scale8(BRIGHTNESS, brightness)); +} + +void strobe(CRGB color) { + static unsigned long lastStrobe = 0; + static bool on = false; + + if(millis() - lastStrobe > 50) { + on = !on; + lastStrobe = millis(); + fill_solid(leds, NUM_LEDS, on ? color : CRGB::Black); + } +} + +void endToEndBlendToBlack(CRGB color) { + static uint8_t pos = 0; + + fadeToBlackBy(leds, NUM_LEDS, 10); + + // Create gradient from color to black + for(int i = 0; i < NUM_LEDS; i++) { + uint8_t brightness = 255 - (abs(i - pos) * 8); + if(brightness > 0) { + leds[i] = color; + leds[i].fadeLightBy(255 - brightness); + } + } + + pos = (pos + 1) % NUM_LEDS; +} + +void sparkle(CRGB sparkleColor, CRGB baseColor) { + fill_solid(leds, NUM_LEDS, baseColor); + + if(random8() < 80) { + leds[random16(NUM_LEDS)] = sparkleColor; + } +} + +void colorGradient(CRGB color1, CRGB color2) { + fill_gradient_RGB(leds, 0, color1, NUM_LEDS-1, color2); +} + +void bpmTwoColor(CRGB color1, CRGB color2) { + uint8_t BeatsPerMinute = 62; + uint8_t beat = beatsin8(BeatsPerMinute, 0, 255); + + for(int i = 0; i < NUM_LEDS; i++) { + if(i % 2 == 0) { + leds[i] = color1; + leds[i].fadeLightBy(255 - beat); + } else { + leds[i] = color2; + leds[i].fadeLightBy(beat); + } + } +} + +void endToEndBlend(CRGB color1, CRGB color2) { + static uint8_t pos = 0; + + for(int i = 0; i < NUM_LEDS; i++) { + uint8_t blend = (i + pos) * 255 / NUM_LEDS; + leds[i] = blend_CRGB(color1, color2, blend); + } + + pos = (pos + 1) % NUM_LEDS; +} + +void noBlending(CRGB color1, CRGB color2) { + // Split the strip in half + for(int i = 0; i < NUM_LEDS/2; i++) { + leds[i] = color1; + } + for(int i = NUM_LEDS/2; i < NUM_LEDS; i++) { + leds[i] = color2; + } +} + +void twinklesTwoColor(CRGB color1, CRGB color2) { + fadeToBlackBy(leds, NUM_LEDS, 80); + + if(random8() < 80) { + int pos = random16(NUM_LEDS); + leds[pos] = (random8() < 128) ? color1 : color2; + } +} + +void colorWavesTwoColor(CRGB color1, CRGB color2) { + static uint8_t hue = 0; + + for(int i = 0; i < NUM_LEDS; i++) { + uint8_t brightness = sin8(hue + (i * 10)); + uint8_t blend = sin8(hue + (i * 5)); + CRGB color = blend_CRGB(color1, color2, blend); + leds[i] = color; + leds[i].fadeLightBy(255 - brightness); + } + + hue++; +} + +void sinelonTwoColor(CRGB color1, CRGB color2) { + fadeToBlackBy(leds, NUM_LEDS, 20); + + int pos1 = beatsin16(13, 0, NUM_LEDS - 1); + int pos2 = beatsin16(11, 0, NUM_LEDS - 1); + + leds[pos1] += color1; + leds[pos2] += color2; +} + +// Helper function for blending colors +CRGB blend_CRGB(CRGB color1, CRGB color2, uint8_t blend) { + return CRGB( + ((color1.r * (255 - blend)) + (color2.r * blend)) / 255, + ((color1.g * (255 - blend)) + (color2.g * blend)) / 255, + ((color1.b * (255 - blend)) + (color2.b * blend)) / 255 + ); +} + +void startupAnimation() { + // Quick rainbow sweep + for(int i = 0; i < NUM_LEDS; i++) { + leds[i] = CHSV(i * 255 / NUM_LEDS, 255, 255); + FastLED.show(); + delay(10); + } + + // Fade to black + for(int i = 0; i < 50; i++) { + fadeToBlackBy(leds, NUM_LEDS, 10); + FastLED.show(); + delay(20); + } +} + +String getPatternName(Pattern pattern) { + switch(pattern) { + // Fixed Palette Patterns + case RAINBOW_RAINBOW_PALETTE: return "RAINBOW_RAINBOW_PALETTE"; + case RAINBOW_PARTY_PALETTE: return "RAINBOW_PARTY_PALETTE"; + case RAINBOW_OCEAN_PALETTE: return "RAINBOW_OCEAN_PALETTE"; + case RAINBOW_LAVA_PALETTE: return "RAINBOW_LAVA_PALETTE"; + case RAINBOW_FOREST_PALETTE: return "RAINBOW_FOREST_PALETTE"; + case RAINBOW_WITH_GLITTER: return "RAINBOW_WITH_GLITTER"; + case CONFETTI: return "CONFETTI"; + case SHOT_RED: return "SHOT_RED"; + case SHOT_BLUE: return "SHOT_BLUE"; + case SHOT_WHITE: return "SHOT_WHITE"; + case SINELON_RAINBOW_PALETTE: return "SINELON_RAINBOW_PALETTE"; + case SINELON_PARTY_PALETTE: return "SINELON_PARTY_PALETTE"; + case SINELON_OCEAN_PALETTE: return "SINELON_OCEAN_PALETTE"; + case SINELON_LAVA_PALETTE: return "SINELON_LAVA_PALETTE"; + case SINELON_FOREST_PALETTE: return "SINELON_FOREST_PALETTE"; + case BEATS_PER_MINUTE_RAINBOW_PALETTE: return "BPM_RAINBOW_PALETTE"; + case BEATS_PER_MINUTE_PARTY_PALETTE: return "BPM_PARTY_PALETTE"; + case BEATS_PER_MINUTE_OCEAN_PALETTE: return "BPM_OCEAN_PALETTE"; + case BEATS_PER_MINUTE_LAVA_PALETTE: return "BPM_LAVA_PALETTE"; + case BEATS_PER_MINUTE_FOREST_PALETTE: return "BPM_FOREST_PALETTE"; + case FIRE_MEDIUM: return "FIRE_MEDIUM"; + case FIRE_LARGE: return "FIRE_LARGE"; + case TWINKLES_RAINBOW_PALETTE: return "TWINKLES_RAINBOW_PALETTE"; + case TWINKLES_PARTY_PALETTE: return "TWINKLES_PARTY_PALETTE"; + case TWINKLES_OCEAN_PALETTE: return "TWINKLES_OCEAN_PALETTE"; + case TWINKLES_LAVA_PALETTE: return "TWINKLES_LAVA_PALETTE"; + case TWINKLES_FOREST_PALETTE: return "TWINKLES_FOREST_PALETTE"; + case COLOR_WAVES_RAINBOW_PALETTE: return "COLOR_WAVES_RAINBOW_PALETTE"; + case COLOR_WAVES_PARTY_PALETTE: return "COLOR_WAVES_PARTY_PALETTE"; + case COLOR_WAVES_OCEAN_PALETTE: return "COLOR_WAVES_OCEAN_PALETTE"; + case COLOR_WAVES_LAVA_PALETTE: return "COLOR_WAVES_LAVA_PALETTE"; + case COLOR_WAVES_FOREST_PALETTE: return "COLOR_WAVES_FOREST_PALETTE"; + case LARSON_SCANNER_RED: return "LARSON_SCANNER_RED"; + case LARSON_SCANNER_GRAY: return "LARSON_SCANNER_GRAY"; + case LIGHT_CHASE_RED: return "LIGHT_CHASE_RED"; + case LIGHT_CHASE_BLUE: return "LIGHT_CHASE_BLUE"; + case LIGHT_CHASE_GRAY: return "LIGHT_CHASE_GRAY"; + case HEARTBEAT_RED: return "HEARTBEAT_RED"; + case HEARTBEAT_BLUE: return "HEARTBEAT_BLUE"; + case HEARTBEAT_WHITE: return "HEARTBEAT_WHITE"; + case HEARTBEAT_GRAY: return "HEARTBEAT_GRAY"; + case BREATH_RED: return "BREATH_RED"; + case BREATH_BLUE: return "BREATH_BLUE"; + case BREATH_GRAY: return "BREATH_GRAY"; + case STROBE_RED: return "STROBE_RED"; + case STROBE_BLUE: return "STROBE_BLUE"; + case STROBE_GOLD: return "STROBE_GOLD"; + case STROBE_WHITE: return "STROBE_WHITE"; + + // CP1 Patterns + case CP1_END_TO_END_BLEND_TO_BLACK: return "CP1_END_TO_END_BLEND_TO_BLACK"; + case CP1_LARSON_SCANNER: return "CP1_LARSON_SCANNER"; + case CP1_LIGHT_CHASE: return "CP1_LIGHT_CHASE"; + case CP1_HEARTBEAT_SLOW: return "CP1_HEARTBEAT_SLOW"; + case CP1_HEARTBEAT_MEDIUM: return "CP1_HEARTBEAT_MEDIUM"; + case CP1_HEARTBEAT_FAST: return "CP1_HEARTBEAT_FAST"; + case CP1_BREATH_SLOW: return "CP1_BREATH_SLOW"; + case CP1_BREATH_FAST: return "CP1_BREATH_FAST"; + case CP1_SHOT: return "CP1_SHOT"; + case CP1_STROBE: return "CP1_STROBE"; + + // CP2 Patterns + case CP2_END_TO_END_BLEND_TO_BLACK: return "CP2_END_TO_END_BLEND_TO_BLACK"; + case CP2_LARSON_SCANNER: return "CP2_LARSON_SCANNER"; + case CP2_LIGHT_CHASE: return "CP2_LIGHT_CHASE"; + case CP2_HEARTBEAT_SLOW: return "CP2_HEARTBEAT_SLOW"; + case CP2_HEARTBEAT_MEDIUM: return "CP2_HEARTBEAT_MEDIUM"; + case CP2_HEARTBEAT_FAST: return "CP2_HEARTBEAT_FAST"; + case CP2_BREATH_SLOW: return "CP2_BREATH_SLOW"; + case CP2_BREATH_FAST: return "CP2_BREATH_FAST"; + case CP2_SHOT: return "CP2_SHOT"; + case CP2_STROBE: return "CP2_STROBE"; + + // CP1_2 Patterns + case CP1_2_SPARKLE_1_ON_2: return "CP1_2_SPARKLE_1_ON_2"; + case CP1_2_SPARKLE_2_ON_1: return "CP1_2_SPARKLE_2_ON_1"; + case CP1_2_COLOR_GRADIENT: return "CP1_2_COLOR_GRADIENT"; + case CP1_2_BEATS_PER_MINUTE: return "CP1_2_BEATS_PER_MINUTE"; + case CP1_2_END_TO_END_BLEND_1_TO_2: return "CP1_2_END_TO_END_BLEND_1_TO_2"; + case CP1_2_END_TO_END_BLEND: return "CP1_2_END_TO_END_BLEND"; + case CP1_2_NO_BLENDING: return "CP1_2_NO_BLENDING"; + case CP1_2_TWINKLES: return "CP1_2_TWINKLES"; + case CP1_2_COLOR_WAVES: return "CP1_2_COLOR_WAVES"; + case CP1_2_SINELON: return "CP1_2_SINELON"; + + // Solid Colors + case HOT_PINK: return "HOT_PINK"; + case DARK_RED: return "DARK_RED"; + case RED: return "RED"; + case RED_ORANGE: return "RED_ORANGE"; + case ORANGE: return "ORANGE"; + case GOLD: return "GOLD"; + case YELLOW: return "YELLOW"; + case LAWN_GREEN: return "LAWN_GREEN"; + case LIME: return "LIME"; + case DARK_GREEN: return "DARK_GREEN"; + case GREEN: return "GREEN"; + case BLUE_GREEN: return "BLUE_GREEN"; + case AQUA: return "AQUA"; + case SKY_BLUE: return "SKY_BLUE"; + case DARK_BLUE: return "DARK_BLUE"; + case BLUE: return "BLUE"; + case BLUE_VIOLET: return "BLUE_VIOLET"; + case VIOLET: return "VIOLET"; + case WHITE: return "WHITE"; + case GRAY: return "GRAY"; + case DARK_GRAY: return "DARK_GRAY"; + case BLACK: return "BLACK"; + + default: return "UNKNOWN"; + } +} \ No newline at end of file diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html