dept-of-scrapyard-robotics / laravel-rgb
A Laravel package for controlling RGB LED strips using the phpixel extension.
Installs: 0
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
pkg:composer/dept-of-scrapyard-robotics/laravel-rgb
Requires
- php: ^8.3
- ext-phpixel: *
- ext-sockets: *
- illuminate/support: ^11.0|^12.0
- phpdafruit/neopixel-php: ^0.2.0
- projectsaturnstudios/laravel-design-patterns: ^0.1.0
Requires (Dev)
- mockery/mockery: ^1.6
- pestphp/pest: ^4.1
README
A Laravel wrapper for controlling WS281x/NeoPixel LED strips, fans, and devices on embedded Linux systems. Provides elegant Laravel integration with configuration, facades, async operations, and Artisan commands for the neopixel-php library.
Features
- Laravel Integration - Service provider, facades, and configuration
- Async Operations - Non-blocking animations using Laravel Concurrency
- Multi-Device Management - Configure and control multiple LED devices
- Device Shapes - Pre-built device abstractions (strips, fans, custom shapes)
- Bus Animations - Multi-channel coordinated animations
- Artisan Commands - Command-line interface for playing animations
- Type-Safe Configuration - Laravel-style config files
- All neopixel-php Features - Full access to 35+ built-in animations
Requirements
- PHP 8.3+
- Laravel 11.x (or 10.x)
- phpixel extension - Low-level SPI interface
- neopixel-php - Core LED control library
- Embedded Linux device with SPI support (Raspberry Pi, Jetson Orin Nano, etc.)
Installation
1. Install phpixel Extension
First, install the phpixel extension on your embedded device:
# Clone and build the extension git clone https://github.com/projectsaturnstudios/phpdafruit_phpixel_extension.git cd phpdafruit_phpixel_extension phpize ./configure make sudo make install # Enable the extension echo "extension=phpixel.so" | sudo tee /etc/php/8.3/cli/conf.d/20-phpixel.ini # Verify php -m | grep phpixel
For detailed installation instructions, see the phpixel repository.
2. Install neopixel-php
composer require phpdafruit/neopixel-php
See the neopixel-php documentation for core library features.
3. Install LaravelRGB
composer require dept-of-scrapyard-robotics/laravel-rgb
4. Publish Configuration
php artisan vendor:publish --tag=rgb-lighting-config
This creates config/rgb-lighting.php
where you can configure your LED devices.
Configuration
Edit config/rgb-lighting.php
to define your LED devices:
return [ // Concurrency driver for async operations 'async_concurrency_driver' => 'fork', // 'fork', 'process', or 'sync' // Device definitions 'devices' => [ 'strip' => [ 'shape' => \PhpdaFruit\NeoPixels\DeviceShapes\RGBStrip::class, 'num_pixels' => 30, 'spi_device' => 'SPI_0_0', // Maps to /dev/spidev0.0 'neopixel_type' => 'GRB', // Color order ], 'fan' => [ 'shape' => \PhpdaFruit\NeoPixels\DeviceShapes\DoubleDots::class, 'spi_device' => 'SPI_1_0', // Maps to /dev/spidev1.0 'neopixel_type' => 'RGB', ], 'status_led' => [ 'shape' => \PhpdaFruit\NeoPixels\DeviceShapes\SingleDiode::class, 'spi_device' => 'SPI_0_1', 'neopixel_type' => 'GRB', ], ], ];
Note: In most cases, you'll only need one or two devices. The above shows multiple devices for demonstration.
Available Shapes
RGBStrip
- Linear LED strips (requiresnum_pixels
)SingleDiode
- Single LED (status indicators)DoubleDots
- Two LEDs (PC fans, dual indicators)- Custom shapes - Your own device shapes
SPI Device Mapping
SPI_0_0
→/dev/spidev0.0
SPI_0_1
→/dev/spidev0.1
SPI_1_0
→/dev/spidev1.0
SPI_1_1
→/dev/spidev1.1
- And so on...
Color Orders (neopixel_type)
GRB
- Most common (WS2812B)RGB
- Alternative orderRGBW
- With white channelGRBW
- GRB with white channel
Usage
Basic Usage with Facades
use DeptOfScrapyardRobotics\LaravelRGB\Support\Facades\LightingSetup; // Get a device channel $strip = LightingSetup::getPixelChannel('strip'); // Control the strip $strip->fill(0xFF0000)->show(); // Red $strip->setBrightness(128)->show(); // 50% brightness $strip->clear()->show(); // Turn off
Using the Dispatcher (Fluent API)
The RGBDispatcher
provides a fluent interface with async support:
use DeptOfScrapyardRobotics\LaravelRGB\Support\Facades\LightingSetup; // Get dispatcher for a device $dispatcher = LightingSetup::channel('strip'); // Synchronous operations $dispatcher->setColor(0x00FF00) // Green ->wait(1000) // Wait 1 second ->setColor(0x0000FF) // Blue ->wait(1000) ->off(); // Turn off // Async operations (non-blocking) $dispatcher->withAsync() ->setColor(0xFF00FF) // Magenta ->wait(2000) ->status('success') // Green blink pattern ->wait(500) ->off() ->fire(); // Execute all queued operations
Playing Animations
use PhpdaFruit\NeoPixels\Enums\Animation; use PhpdaFruit\NeoPixels\AnimationRegistry; // Initialize animation system (do once) AnimationRegistry::initialize(); // Get device and play animation $strip = LightingSetup::getPixelChannel('strip'); // Play a 5-second rainbow animation $strip->animate(Animation::RAINBOW, 5000); // Play fire effect with custom options $strip->animate(Animation::FIRE_FLICKER, 10000, [ 'intensity' => 0.9, 'cooling' => 60 ]); // Meteor shower $strip->animate(Animation::METEOR_SHOWER, 8000);
For a complete list of 35+ animations, see the neopixel-php ANIMATIONS.md.
Async Multi-Device Control
Control multiple devices concurrently using Laravel's Concurrency facade:
use Illuminate\Support\Facades\Concurrency; use DeptOfScrapyardRobotics\LaravelRGB\Support\Facades\LightingSetup; $strip = LightingSetup::channel('strip'); $fan = LightingSetup::channel('fan'); // Both execute simultaneously Concurrency::run([ fn() => $strip->withAsync() ->setColor(0xFF0000) ->wait(1000) ->setColor(0x00FF00) ->fire(), fn() => $fan->withAsync() ->spin(0x0000FF, 5, 200) ->wait(500) ->alternate(0xFFFF00, 10, 100) ->fire() ]);
Artisan Commands
rgb:animate - Play Animations from CLI
Play any animation on any configured device:
# Play rainbow on strip for 5 seconds php artisan rgb:animate rainbow strip --duration=5000 # Play comet on all devices forever (Ctrl+C to stop) php artisan rgb:animate comet all # Play fire flicker on strip (runs until interrupted) php artisan rgb:animate fire-flicker strip # Play meteor shower on fan for 10 seconds php artisan rgb:animate meteor-shower fan --duration=10000
Features:
- Play on specific device or all devices
- Optional duration (runs forever if not specified)
- Graceful shutdown on Ctrl+C:
- All devices flash red
- Devices turn off one by one (400ms between each)
- Animation name is case-insensitive, accepts dashes or underscores
Available Commands:
# See all options php artisan rgb:animate --help # List animations (shows first 20) php artisan rgb:animate invalid_name
AnimatablePixelBus
AnimatablePixelBus
extends PixelBus
to support async operations and multi-channel animations. Perfect for devices with multiple coordinated LED channels (like a carnival High Striker game).
What is AnimatablePixelBus?
It's an abstract class that:
- Manages multiple
PixelChannel
instances - Supports async operation queuing (like
RGBDispatcher
) - Provides
animate()
for bus-level animations - Coordinates effects across multiple channels
- Uses Laravel Concurrency for parallel execution
Creating Custom Device Shapes
Extend AnimatablePixelBus
to create multi-channel devices:
namespace App\LEDs; use DeptOfScrapyardRobotics\LaravelRGB\AnimatablePixelBus; use PhpdaFruit\NeoPixels\DeviceShapes\RGBStrip; use PhpdaFruit\NeoPixels\PixelChannel; use PhpdaFruit\NeoPixels\Enums\SPIDevice; use PhpdaFruit\NeoPixels\Enums\NeoPixelType; class CustomDevice extends AnimatablePixelBus { public function __construct() { parent::__construct([ 'main_strip' => new RGBStrip(30, SPIDevice::SPI_0_0->value, NeoPixelType::GRB), 'accent_strip' => new RGBStrip(15, SPIDevice::SPI_0_1->value, NeoPixelType::GRB), 'status_led' => new PixelChannel(1, SPIDevice::SPI_1_0->value, NeoPixelType::RGB), ]); } /** * Custom animation method */ public function rainbowWave(): static { // Access channels $main = $this->getChannel('main_strip'); $accent = $this->getChannel('accent_strip'); // Coordinate animation across both strips for ($i = 0; $i < 256; $i++) { $color = $this->wheel($i); $main->fill($color)->show(); $accent->fill($this->wheel($i + 128))->show(); usleep(20000); } return $this; } /** * Helper: Get color from color wheel */ protected function wheel(int $pos): int { $pos = $pos % 256; if ($pos < 85) { return (($pos * 3) << 16) | ((255 - $pos * 3) << 8); } elseif ($pos < 170) { $pos -= 85; return ((255 - $pos * 3) << 16) | (($pos * 3)); } else { $pos -= 170; return (($pos * 3) << 8) | (255 - $pos * 3); } } /** * Get individual channels */ public function getMainStrip(): RGBStrip { return $this->getChannel('main_strip'); } public function getAccentStrip(): RGBStrip { return $this->getChannel('accent_strip'); } public function getStatusLed(): PixelChannel { return $this->getChannel('status_led'); } }
Using Your Custom Device Shape
use App\LEDs\CustomDevice; $device = new CustomDevice(); // Use custom methods $device->rainbowWave(); // Access individual channels $device->getMainStrip()->fill(0xFF0000)->show(); $device->getStatusLed()->fill(0x00FF00)->show(); // Use async operations $device->withAsync() ->rainbowWave() ->wait(1000) ->clearAll() ->fire();
Example: HighStriker (Carnival Game)
The package includes a HighStriker
device shape as an example:
use DeptOfScrapyardRobotics\LaravelRGB\DeviceShapes\HighStriker; // Create with rail and bell channels $striker = new HighStriker( rail_dots: 15, // Rail strip length bell_dots: 2, // Bell indicator flip_rail: true // Flip for upside-down mounting ); // Play physics simulation $striker->strike('strong'); // Random velocity strike $striker->strike('weak'); // Likely to fail $striker->strike('random'); // Random strength // Manual control $striker->getRail()->fill(0xFF0000)->show(); $striker->getBell()->fill(0xFFD700)->show(); // Async operation $striker->withAsync() ->strike('medium') ->wait(1000) ->celebrateSuccess() ->fire();
Bus Animation Architecture
For multi-channel animations, create BusAnimationVisualization
implementations:
use DeptOfScrapyardRobotics\LaravelRGB\Animations\BusVisualization; use DeptOfScrapyardRobotics\LaravelRGB\AnimatablePixelBus; use DeptOfScrapyardRobotics\LaravelRGB\Enums\BusAnimation; class MyBusAnimation extends BusVisualization { public function getAnimationType(): BusAnimation { return BusAnimation::CUSTOM; } public function run(AnimatablePixelBus $bus, int $duration_ms, array $options = []): void { $channels = $bus->channels(); // Coordinate animation across all channels foreach ($channels as $name => $channel) { // Your animation logic } } public function getRequiredChannels(): array { return ['channel1', 'channel2']; // Or return [] for any bus } }
Available Methods
RGBDispatcher
// Async control ->withAsync() // Enable async mode ->withoutAsync() // Disable async mode ->fire() // Execute queued operations // Operations ->setColor(int $color, ?int $brightness) // Set color ->on(int $color) // Turn on with color ->off() // Turn off ->wait(int $milliseconds) // Wait/delay ->clear() // Clear LEDs ->status(string $status, int $blinks) // Status indicator // Proxy to underlying channel methods // Any method on PixelChannel/DeviceShape can be called ->fill(int $color) ->setBrightness(int $brightness) ->animate(Animation $animation, int $duration_ms) // ... and many more
AnimatablePixelBus
// Async control ->withAsync() // Enable async mode ->withoutAsync() // Disable async mode ->fire() // Execute queued operations ->wait(int $milliseconds) // Wait/delay // Channel access ->channels() // Get all channels ->getChannel(string $name) // Get specific channel ->clearAll() // Clear all channels ->showAll() // Update all channels // Animations ->animate(BusAnimation $animation, int $duration_ms, array $options) // Magic method proxy // Any method calls are broadcast to all channels ->fill(int $color) // Fills all channels ->setBrightness(int $brightness) // Sets brightness on all
Dependencies
Required Packages
-
- C extension providing low-level SPI interface
- Must be installed system-wide
- See installation instructions in repository
-
- Core LED control library
- Object-oriented API
- 35+ built-in animations
- Device shapes (RGBStrip, DoubleDots, SingleDiode)
-
Laravel 11.x (or 10.x)
- Service providers
- Facades
- Concurrency support
- Artisan commands
Troubleshooting
Extension Not Found
PHP Fatal error: Uncaught Error: Class 'NeoPixel' not found
Solution: Install the phpixel extension.
Device Not Found in Config
Device 'strip' not found
Solution: Publish and configure config/rgb-lighting.php
:
php artisan vendor:publish --tag=rgb-lighting-config
SPI Permission Denied
Failed to open SPI device: Permission denied
Solution: Add your user to the spi
group:
sudo usermod -a -G spi $USER # Log out and back in for group changes to take effect
Wrong Colors
If colors are wrong (e.g., red appears as green), adjust neopixel_type
in config:
'neopixel_type' => 'GRB', // Try RGB, RGBW, or GRBW
License
MIT License. See LICENSE file for details.
Links
- neopixel-php: https://github.com/phpdafruit/neopixel-php
- phpixel extension: https://github.com/projectsaturnstudios/phpdafruit_phpixel_extension
- Laravel Concurrency: https://laravel.com/docs/concurrency
Contributing
Contributions are welcome! Please:
- Follow PSR-12 coding standards
- Add tests for new features
- Update documentation
- Ensure all tests pass
Support
- LaravelRGB issues: Open an issue in this repository
- neopixel-php issues: See neopixel-php repository
- phpixel extension issues: See phpixel repository