Initial commit: D1 Mini Blinkin LED Driver Emulator for FTC
This commit is contained in:
commit
dca4c4cb26
12 changed files with 2155 additions and 0 deletions
89
.gitignore
vendored
Normal file
89
.gitignore
vendored
Normal 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
10
.vscode/extensions.json
vendored
Normal 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
224
INSTALLATION_GUIDE.md
Normal 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
178
QUICK_FLASH_GUIDE.md
Normal 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
209
README.md
Normal 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
|
||||||
188
VOLTAGE_DIVIDER_ALTERNATIVES.md
Normal file
188
VOLTAGE_DIVIDER_ALTERNATIVES.md
Normal 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!
|
||||||
238
VOLTAGE_DIVIDER_BUILD_GUIDE.md
Normal file
238
VOLTAGE_DIVIDER_BUILD_GUIDE.md
Normal 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
37
include/README
Normal 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
46
lib/README
Normal 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
27
platformio.ini
Normal 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
898
src/main.cpp
Normal 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
11
test/README
Normal 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
|
||||||
Loading…
Add table
Add a link
Reference in a new issue