|
| 1 | +# RevolutionPi |
| 2 | + |
| 3 | +This package provides a comprehensive abstraction for interacting with Revolution Pi hardware (and virtualized devices) |
| 4 | +within a PHP application. It enables process image access, IO control, LED control, serial port management, and |
| 5 | +more, with a convenient and flexible API. |
| 6 | + |
| 7 | +The package has specific support for Laravel, and Laravel Zero. It can also be used with plain PHP, although some |
| 8 | +functionality such as the auto-generation commands will not be available. |
| 9 | + |
| 10 | +The package works with the built-in PHP 8.2 on the Debian Bookworm image shipped in the RevolutionPi. No extra |
| 11 | +extensions or configuration changes are required. |
| 12 | + |
| 13 | +## Features |
| 14 | + |
| 15 | +- **ProcessImage**: Full read/write access to PiControl variables. |
| 16 | +- **Trait-based interface**: Add Revolution Pi capabilities to your own app classes via the `RevolutionPi` trait. |
| 17 | +- **LED support**: Control and read RevPi device LEDs (color, position). |
| 18 | +- **Serial communications**: Robust non-blocking serial port abstraction for baud rate, parity, stop/data bits, flags |
| 19 | +- **Monitor/Observer**: Register IO monitor callbacks for change detection. |
| 20 | +- **Magic method & attribute support**: Easy, expressive access to IO variables, matching your PiCtory configuration. |
| 21 | +- **Remote (WebSocket/RPC) and Virtual device support**. |
| 22 | + |
| 23 | +--- |
| 24 | + |
| 25 | +## Table of Contents |
| 26 | + |
| 27 | +- [Installation](#installation) |
| 28 | +- [Simplified interface](#simplified-interface) |
| 29 | + - [Accessing IO variables](#accessing-io-variables) |
| 30 | + - [Monitoring IO variables](#monitoring-io-variables) |
| 31 | + - [Process image](#process-image-low-level) |
| 32 | + - [LED control](#led-control) |
| 33 | + - [Serial port access](#serial-port-access) |
| 34 | +- [Low-level interface](#low-level-interface) |
| 35 | + - [Hardware devices (PiControl, Terminal)](#hardware-devices-picontrol-terminal) |
| 36 | + - [ProcessImage interface](#processimage-interface) |
| 37 | + - [SerialPort interface](#serialport-interface) |
| 38 | + - [Monitoring with custom monitors](#monitoring-with-custom-monitors) |
| 39 | + - [Remote module usage](#remote-module-usage) |
| 40 | +- [Deployment](#deployment) |
| 41 | +- [Example: Polling and running the event loop](#example-polling-and-running-the-event-loop) |
| 42 | +- [CLI Commands](#cli-commands) |
| 43 | +- [License](#license) |
| 44 | + |
| 45 | +--- |
| 46 | + |
| 47 | +## Installation |
| 48 | + |
| 49 | +**Require with Composer** |
| 50 | + |
| 51 | + ``` |
| 52 | + composer require flat3/revpi |
| 53 | + ``` |
| 54 | + |
| 55 | +Out of the box `revpi` pulls in only the required packages. |
| 56 | + |
| 57 | +**To include the PHP class generator** |
| 58 | + |
| 59 | +``` |
| 60 | +composer require --dev nette/php-generator |
| 61 | +``` |
| 62 | + |
| 63 | +**To include websocket support** |
| 64 | + |
| 65 | +``` |
| 66 | +composer require amphp/websocket-server amphp/websocket-client |
| 67 | +``` |
| 68 | + |
| 69 | +--- |
| 70 | + |
| 71 | +## Quick start |
| 72 | + |
| 73 | +`flat3/revpi` makes heavy use of the Laravel service container. All concrete implementations can be resolved from |
| 74 | +interfaces. Code should either be run *on* the RevolutionPi device, or via an IDE that can run code remotely like |
| 75 | +PhpStorm. |
| 76 | + |
| 77 | +```php |
| 78 | + use Flat3\RevPi\Interfaces\Module; |
| 79 | + use Flat3\RevPi\Led\LedColour; |
| 80 | + use Flat3\RevPi\Led\LedPosition; |
| 81 | + |
| 82 | + $pi = app(Module::class); // Resolved from the interface to the correct module type |
| 83 | + |
| 84 | + $pi->led(LedPosition::A1)->set(LedColour::Red); |
| 85 | + print_r($pi->led(LedPosition::A1)->get()); |
| 86 | +``` |
| 87 | + |
| 88 | +--- |
| 89 | + |
| 90 | +## Simplified Interface |
| 91 | + |
| 92 | +It's recommended to generate a device-specific class from your exported PiCtory JSON. This provides IDE auto-completion |
| 93 | +for all IO variables as strongly typed methods and documented properties. |
| 94 | + |
| 95 | +1. **Export your configuration from the PiCtory web interface as a project file** |
| 96 | + (e.g. `pictory.rsc`) |
| 97 | + |
| 98 | +2. **Generate the PHP class** |
| 99 | + |
| 100 | +```bash |
| 101 | + php artisan revpi:generate pictory.rsc MyPi |
| 102 | +``` |
| 103 | + |
| 104 | +This creates `app/MyPi.php`. |
| 105 | + |
| 106 | +3. **Use your class** |
| 107 | + |
| 108 | +```php |
| 109 | + use Flat3\RevPi\Interfaces\Module; |
| 110 | + use Flat3\RevPi\Led\LedColour; |
| 111 | + use Flat3\RevPi\Led\LedPosition; |
| 112 | + use Flat3\RevPi\Monitors\DigitalMonitor; |
| 113 | + use Flat3\RevPi\SerialPort\BaudRate; |
| 114 | + use Flat3\RevPi\SerialPort\LocalFlag; |
| 115 | + use Illuminate\Console\Command; |
| 116 | + use Revolt\EventLoop; |
| 117 | + |
| 118 | + $pi = new \App\MyPi; |
| 119 | + |
| 120 | + // Create an instance of the serial port that loops the input back to the output |
| 121 | + $port = $pi->serialPort(); |
| 122 | + $port->setSpeed(BaudRate::B576000); |
| 123 | + $port->clearFlag(LocalFlag::CanonicalInput); |
| 124 | + $port->onReadable(function (string $data) use ($port) { |
| 125 | + $port->write($data); |
| 126 | + }); |
| 127 | + |
| 128 | + // Monitor the core temperature, writing updated values to the serial port |
| 129 | + $pi->Core_Temperature()->monitor(new DigitalMonitor, function ($value) use ($port) { |
| 130 | + $port->write($value."\n"); |
| 131 | + }); |
| 132 | + |
| 133 | + // Start polling |
| 134 | + $pi->module()->resume(); |
| 135 | + |
| 136 | + // Start the event loop |
| 137 | + EventLoop::run(); |
| 138 | +``` |
| 139 | + |
| 140 | +### Accessing IO Variables |
| 141 | + |
| 142 | +Assuming your PiCtory-exported class has an input named `input1` and an output named `output1`: |
| 143 | + |
| 144 | +```php |
| 145 | +$pi = new \App\MyPi; |
| 146 | + |
| 147 | +// Read input (as property) |
| 148 | +$level = $pi->input1; // int|bool |
| 149 | + |
| 150 | +// Or as a method (returns InputIO object) |
| 151 | +$input = $pi->input1(); |
| 152 | +$currentValue = $input->get(); |
| 153 | + |
| 154 | +// Read output |
| 155 | +$currentStatus = $pi->output1; |
| 156 | + |
| 157 | +// Set output (as property) |
| 158 | +$pi->output1 = true; |
| 159 | + |
| 160 | +// Or as a method (returns OutputIO object) |
| 161 | +$pi->output1()->set(1); |
| 162 | + |
| 163 | +// Reset output to its default value |
| 164 | +$pi->output1()->reset(); |
| 165 | +``` |
| 166 | + |
| 167 | +### Monitoring IO Variables |
| 168 | + |
| 169 | +The `DigitalMonitor` will cause the callback to be called whenever the monitored value changes. |
| 170 | + |
| 171 | +```php |
| 172 | +use Flat3\RevPi\Monitors\DigitalMonitor; |
| 173 | + |
| 174 | +$pi = new \App\MyPi; |
| 175 | + |
| 176 | +$pi->input1()->monitor(new DigitalMonitor, function($newValue) { |
| 177 | + // React to input1 changes |
| 178 | + logger("input1 changed: $newValue"); |
| 179 | +}); |
| 180 | +``` |
| 181 | + |
| 182 | +### Process Image (Low-Level) |
| 183 | + |
| 184 | +Get the raw process image interface for advanced access: |
| 185 | + |
| 186 | +```php |
| 187 | +$image = $pi->processImage(); |
| 188 | + |
| 189 | +$value = $image->readVariable('input1'); |
| 190 | +$image->writeVariable('output1', 1); |
| 191 | +$dump = $image->dumpImage(); // Raw string of process image data |
| 192 | +$info = $image->getDeviceInfo(); // Information about the base module |
| 193 | +$infoList = $image->getDeviceInfoList(); // Information about all expansion modules |
| 194 | +``` |
| 195 | + |
| 196 | +### LED Control |
| 197 | + |
| 198 | +```php |
| 199 | +use Flat3\RevPi\Led\LedColour; |
| 200 | +use Flat3\RevPi\Led\LedPosition; |
| 201 | + |
| 202 | +// Set LED A1 to green |
| 203 | +$pi->led(LedPosition::A1)->set(LedColour::Green); |
| 204 | + |
| 205 | +// Get current LED color (as enum) |
| 206 | +$current = $pi->led(LedPosition::A1)->get(); // LedColour instance |
| 207 | + |
| 208 | +// Turn an LED off |
| 209 | +$pi->led(LedPosition::A1)->off(); |
| 210 | +``` |
| 211 | + |
| 212 | +### Serial Port Access |
| 213 | + |
| 214 | +```php |
| 215 | +use Flat3\RevPi\SerialPort\BaudRate; |
| 216 | + |
| 217 | +// Open default serial port |
| 218 | +$port = $pi->serialPort(); |
| 219 | + |
| 220 | +// Or specify device path: |
| 221 | +$port = $pi->serialPort('/dev/ttyRS485-1'); |
| 222 | + |
| 223 | +// Configure the port: |
| 224 | +$port->setSpeed(BaudRate::B9600); |
| 225 | +$port->setParity(Parity::Even); |
| 226 | +$port->setDataBits(DataBits::CS8); |
| 227 | + |
| 228 | +// Write and read |
| 229 | +$port->write("Hello, RevPi!"); |
| 230 | +$response = $port->read(128); // up to 128 bytes |
| 231 | + |
| 232 | +// Register event handler for readable data |
| 233 | +$port->onReadable(function($data) { |
| 234 | + echo "Serial received: $data\n"; |
| 235 | +}); |
| 236 | + |
| 237 | +// Flush or break |
| 238 | +$port->flush(QueueSelector::Both); |
| 239 | +$port->break(); |
| 240 | +``` |
| 241 | + |
| 242 | +--- |
| 243 | + |
| 244 | +## Low-level interface |
| 245 | + |
| 246 | +### Hardware Devices (PiControl, Terminal) |
| 247 | + |
| 248 | +You can inject or instantiate the underlying devices (see `Flat3\RevPi\Interfaces\Hardware\*`) for advanced operations, |
| 249 | +e.g. binary FFI device IO, custom IOCTLs, etc. |
| 250 | + |
| 251 | +```php |
| 252 | +$picontrol = app(\Flat3\RevPi\Interfaces\Hardware\PiControl::class); |
| 253 | +$terminal = app(\Flat3\RevPi\Interfaces\Hardware\Terminal::class); |
| 254 | +``` |
| 255 | + |
| 256 | +### ProcessImage Interface |
| 257 | + |
| 258 | +Everything required for direct process image manipulation: |
| 259 | + |
| 260 | +```php |
| 261 | +$image = app(\Flat3\RevPi\Interfaces\ProcessImage::class); |
| 262 | + |
| 263 | +$value = $image->readVariable('SomeName'); |
| 264 | +$image->writeVariable('OtherVar', 123); |
| 265 | +``` |
| 266 | + |
| 267 | +### SerialPort Interface |
| 268 | + |
| 269 | +Inject or use via the module interface. |
| 270 | + |
| 271 | +```php |
| 272 | +$port = app(\Flat3\RevPi\Interfaces\SerialPort::class); // Usually you want $pi->serialPort(...) |
| 273 | +``` |
| 274 | + |
| 275 | +### Monitoring with Custom Monitors |
| 276 | + |
| 277 | +If you want to create a custom monitor (beyond DigitalMonitor): |
| 278 | + |
| 279 | +```php |
| 280 | +use Flat3\RevPi\Monitors\Monitor; |
| 281 | + |
| 282 | +class MyMonitor extends Monitor { |
| 283 | + public function evaluate(mixed $next): bool { |
| 284 | + // Implement custom transition/action logic here |
| 285 | + // e.g. if crossing a threshold, fire webhook |
| 286 | + // Return true if the monitor has detected sufficient change |
| 287 | + } |
| 288 | +} |
| 289 | + |
| 290 | +// Register: |
| 291 | +$pi->module()->monitor('input1', new MyMonitor, function($newValue) { |
| 292 | + // Callback logic |
| 293 | +}); |
| 294 | +``` |
| 295 | + |
| 296 | +### Remote Module Usage |
| 297 | + |
| 298 | +You can communicate with a *remote* RevPi device via a WebSocket. |
| 299 | + |
| 300 | +```php |
| 301 | +use Flat3\RevPi\RevolutionPi; |
| 302 | + |
| 303 | +$pi = new \App\MyPi; |
| 304 | +$pi->remote('ws://10.1.2.3:12873'); // Adding the remote call creates a connection to a device |
| 305 | + |
| 306 | +// From now, other methods act remotely: |
| 307 | +$pi->output1 = 1; |
| 308 | +$status = $pi->input2; |
| 309 | +$pi->led(LedPosition::A1)->set(LedColour::Cyan); |
| 310 | +``` |
| 311 | + |
| 312 | +The remote device should listen for incoming connections, use `php artisan revpi:listen` to start the server. |
| 313 | + |
| 314 | +Out of the box the package does not provide for authentication, or encryption. These can be added by creating a more |
| 315 | +complex websocket handshake object and passing it to the `remote` method. On the server-side, the basic `revpi:listen` |
| 316 | +code can be modified to support encryption and authentication. |
| 317 | + |
| 318 | +--- |
| 319 | + |
| 320 | +## Deployment |
| 321 | + |
| 322 | +The package has been specifically designed to work with [Laravel Zero](https://laravel-zero.com). Using this technique |
| 323 | +will convert your RevolutionPi project into a single executable file that can be started automatically when the base |
| 324 | +module boots up. |
| 325 | + |
| 326 | +--- |
| 327 | + |
| 328 | +## Example: Polling and running the event loop |
| 329 | + |
| 330 | +Typically you'll want to run your polling/event loop. The package provides an artisan command (see below) or call from |
| 331 | +code: |
| 332 | + |
| 333 | +```php |
| 334 | +use Revolt\EventLoop; |
| 335 | + |
| 336 | +$pi = new \App\MyPi; |
| 337 | + |
| 338 | +$pi->repeat(1, function($pi) { |
| 339 | + // This is called every second |
| 340 | + logger("Current value is: " . $pi->input1); |
| 341 | +}); |
| 342 | + |
| 343 | +EventLoop::run(); |
| 344 | +``` |
| 345 | + |
| 346 | +Or as a CLI command: |
| 347 | + |
| 348 | +```bash |
| 349 | +php artisan revpi:run |
| 350 | +``` |
| 351 | + |
| 352 | +--- |
| 353 | + |
| 354 | +## CLI Commands |
| 355 | + |
| 356 | +This package includes artisan commands (`php artisan revpi`): |
| 357 | + |
| 358 | +- `revpi:generate <pictory.json> [class]` |
| 359 | + Generate a typed PHP class for your device from your pictory export. |
| 360 | + |
| 361 | +- `revpi:run` |
| 362 | + Run your polling/event loop (call `resume()` and enter main loop). |
| 363 | + |
| 364 | +- `revpi:led:get <position>` |
| 365 | + Display current color of the chosen LED. |
| 366 | + |
| 367 | +- `revpi:led:set <position> <colour>` |
| 368 | + Set a given LED (positions: a1–a5; colors: off, red, green, orange, blue, magenta, cyan, white). |
| 369 | + |
| 370 | +- `revpi:info` |
| 371 | + List available device(s) and their info/status. |
| 372 | + |
| 373 | +- `revpi:dump <file>` |
| 374 | + Dump the current process image binary to a file. |
| 375 | + |
| 376 | +- `revpi:listen [--address=0.0.0.0] [--port=12873]` |
| 377 | + Run the JSON-RPC/WS server for remote connections. |
| 378 | + |
| 379 | +--- |
| 380 | + |
| 381 | +## License |
| 382 | + |
| 383 | +[MIT](LICENSE) |
| 384 | +Flat3/RevPi, 2025. |
| 385 | + |
| 386 | +--- |
| 387 | + |
| 388 | +## Further Reading |
| 389 | + |
| 390 | +- [Revolution Pi Documentation](https://revolutionpi.com/) |
| 391 | +- [PiCtory Configuration Tool](https://revolutionpi.com/tutorials/pictory/) |
| 392 | +- [Laravel Documentation](https://laravel.com/docs/) |
| 393 | +- [Amp](https://amphp.org/) |
| 394 | +- [FFI Manual (PHP)](https://www.php.net/manual/en/book.ffi.php) |
| 395 | + |
| 396 | +--- |
0 commit comments