Initial commit: D1 Mini Blinkin LED Driver Emulator for FTC

This commit is contained in:
Ryan Hill 2025-11-18 12:03:24 -06:00
commit dca4c4cb26
12 changed files with 2155 additions and 0 deletions

89
.gitignore vendored Normal file
View file

@ -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/

10
.vscode/extensions.json vendored Normal file
View file

@ -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"
]
}

224
INSTALLATION_GUIDE.md Normal file
View file

@ -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 <Adafruit_NeoPixel.h>
#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!

178
QUICK_FLASH_GUIDE.md Normal file
View file

@ -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!**

209
README.md Normal file
View file

@ -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

View file

@ -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!

View file

@ -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!

37
include/README Normal file
View file

@ -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

46
lib/README Normal file
View file

@ -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 <Foo.h>
#include <Bar.h>
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

27
platformio.ini Normal file
View file

@ -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

898
src/main.cpp Normal file
View file

@ -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 <Arduino.h>
#include <ESP8266WiFi.h>
#include <FastLED.h>
// 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<WS2812B, LED_DATA_PIN, GRB>(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";
}
}

11
test/README Normal file
View file

@ -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