scrapyard-io/framework

The ScrapyardIO Framework.

Maintainers

Package info

github.com/ScrapyardIO/framework

Homepage

Issues

pkg:composer/scrapyard-io/framework

Statistics

Installs: 6

Dependents: 0

Suggesters: 0

Stars: 0

0.4.4 2026-06-15 02:15 UTC

This package is auto-updated.

Last update: 2026-06-15 02:15:37 UTC


README

Latest Version on Packagist Total Downloads License

A PHP 8.3+ framework for talking to real hardware — displays, sensors, GPIO pins, and serial buses — from a single-board computer or a laptop via USB. Write the same PHP code whether you are running on a Raspberry Pi or wiring up an FT232H breakout on macOS.

Contents

Architecture

ScrapyardIO is a layered monorepo. The scrapyard-io/framework package replaces four logical sub-packages via Composer's replace key:

Sub-package Purpose
scrapyard-io/waveforms Protocol transports — SPI, I²C, GPIO, UART
scrapyard-io/reality-interface Typed display and sensor abstractions
scrapyard-io/nuts-and-bolts Pixel buffers, FormatSpec, enums, helpers
scrapyard-io/bare-metal IntegratedCircuit base class, config repositories
┌──────────────────────────────────────────────────────────────────┐
│  Application  (Laravel, CLI, Artisan commands, etc.)             │
├──────────────────────────────────────────────────────────────────┤
│  RealityInterface                                                 │
│    Displays:  Screen → Display → EmbeddedDisplay (chip driver)   │
│    Sensors:   Sensor → SensorChip (chip driver)                  │
├──────────────────────────────────────────────────────────────────┤
│  DeptOfScrapyardRobotics  (chip drivers for DOSR hardware)       │
├──────────────────────────────────────────────────────────────────┤
│  Waveforms  (SPI · I²C · GPIO · UART)                            │
├──────────────────────────────────────────────────────────────────┤
│  BareMetal  (IntegratedCircuit, config repositories)             │
├──────────────────────────────────────────────────────────────────┤
│  NutsAndBolts  (buffers · FormatSpec · enums · helpers)          │
└──────────────────────────────────────────────────────────────────┘

Each layer depends only on the one below it. Chip drivers (DOSR packages) sit between Waveforms and RealityInterface — they implement the chip-level contracts (DisplayInterface, GenericDistanceSensor, etc.) and expose a fluent ::connection() builder.

Two transport modes run the same application code on different hardware:

Mode Used on Backed by
native Linux SBCs (Raspberry Pi, Jetson Orin, …) ext-posi (POSIX syscalls)
usb Any Linux or macOS machine with an FTDI adapter ext-ftdi + microscrap/mpsse (SPI/I²C/GPIO); ext-ftdi + microscrap/ftdi (UART)

Requirements

  • PHP 8.3+
  • For native (SBC) transport: ext-posi ^0.4.0
  • For USB transport: ext-ftdi ^0.4.0 + libftdi1 (SPI/I²C/GPIO go over MPSSE; UART is plain async serial)

Optional Composer packages that wrap those extensions with helpers and enums:

Package Wraps Adds
microscrap/posix ext-posi Global posix_open, posix_read, fcntl, ioctl, … helpers
microscrap/ftdi ext-ftdi Global ftdi_usb_open, ftdi_read_data, … helpers
microscrap/spi ext-posi spi_open, spi_transfer, SPITransfer, SPIMode enum
microscrap/i2c ext-posi i2c_open, i2c_rdwr, SMBus helpers, I2CMsgFlag enum
microscrap/gpio ext-posi GPIO character device helpers, edge-event polling
microscrap/uart ext-posi uart_open, termios helpers, BaudRate enum
microscrap/mpsse ext-ftdi MPSSE static API, mpsse_open, typed enums, ACK/NACK, GPIO pins
microscrap/phpdafruit-gfx GFX renderer, partial-refresh buffers, fonts

The framework itself (scrapyard-io/framework) has no hard PHP-extension dependencies — it composes these optional packages at runtime.

Installation

composer require scrapyard-io/framework

Install the DOSR chip drivers you need alongside it:

composer require dept-of-scrapyard-robotics/ssd1680  # ePaper B/W (122×250)
composer require dept-of-scrapyard-robotics/ssd1306  # OLED I²C/SPI
composer require dept-of-scrapyard-robotics/st77xx    # RGB565 TFT
composer require dept-of-scrapyard-robotics/vl53lxx  # Time-of-flight distance
composer require dept-of-scrapyard-robotics/hc-sr04  # Ultrasonic distance
# … etc.

The Extension Stack

ScrapyardIO's native transport is built on two PHP C extensions.

ext-posi — POSIX I/O

php-io-extensions/posi provides direct access to POSIX system calls from PHP: open(2), read(2), write(2), fcntl(2), ioctl(2), and more. It is the foundation for all native SBC transports (SPI via spidev, I²C via i2c-dev, UART via termios, GPIO via gpiod).

Build and install on target platforms:

# Raspberry Pi / Debian
bash install-debian-trixie.sh

# Jetson Orin (JetPack 6)
bash install-jetpack6.sh

# macOS
bash install-macos.sh

# Or via PIE
pie install php-io-extensions/posi

Then add to php.ini:

extension=posi

microscrap/posix wraps the extension with C-style global helpers so calling code reads naturally:

$fd = posix_open('/dev/spidev0.0', O_RDWR);
posix_write($fd, $payload, strlen($payload));
posix_close($fd);

ext-ftdi — libftdi1 Bindings

php-io-extensions/ftdi wraps libftdi1 so PHP can drive FTDI USB adapters (FT232H, FT232RL, etc.) directly. It powers the usb transport path — SPI, I²C, and GPIO are tunnelled over USB MPSSE, while UART runs as plain async serial through microscrap/ftdi (no MPSSE).

Runtime library requirements:

# Debian/Ubuntu/Raspberry Pi OS
apt install libftdi1-2

# macOS
brew install libftdi

microscrap/ftdi adds global helpers and microscrap/mpsse layers the higher-level MPSSE session API (start/stop, read/write, GPIO pin control) on top of them:

use Microscrap\Bindings\MPSSE\MPSSE;
use Microscrap\Bindings\MPSSE\Enums\{MPSSEMode, MPSSEEndianness, MPSSEClockRate, MpsseSupportedDevice};

$ctx = MPSSE::openDevice(
    MpsseSupportedDevice::FT232H,
    MPSSEMode::SPI0,
    MPSSEClockRate::ONE_MHZ->value,
    MPSSEEndianness::MSB,
);

MPSSE::start($ctx);
MPSSE::write($ctx, "\x9F");
$id = MPSSE::read($ctx, 3);
MPSSE::stop($ctx);
MPSSE::close($ctx);

Config-based Device Loading

For production deployments where chips are permanently wired, you can preload all device definitions from a config file rather than constructing them inline.

1. Set the config path in .env:

SCRAPYARD_CONFIG_PATH=/home/pi/my-project/config

2. Create scrapyard-io.php in that directory:

use DeptOfScrapyardRobotics\Displays\SSD1680\SSD1680;
use DeptOfScrapyardRobotics\Sensors\VL53Lxx\VL53L0X;

return [
    'displays' => [
        'eink-panel' => [
            'class_name' => SSD1680::class,
            'connection' => ['driver' => 'usb'],
            'startup' => [
                'spi'      => ['ft232h', 0],
                'gpiochip' => ['ft232h'],
                'rst'      => [0],
                'dc'       => [1],
                'create'   => [],
            ],
        ],
    ],
    'sensors' => [
        'tof-sensor' => [
            'class_name' => VL53L0X::class,
            'connection' => ['driver' => 'usb'],
            'startup' => [
                'i2c'    => ['ft232h', 0x29],
                'create' => [],
            ],
        ],
    ],
];

3. Resolve devices by config key:

use RealityInterface\Displays\BWePaperDisplay;
use RealityInterface\Sensors\TimeOfFlightDistanceSensor;

$display = BWePaperDisplay::using('eink-panel');
$sensor  = TimeOfFlightDistanceSensor::using('tof-sensor');

Consult each DOSR package's README for the exact startup key shape for that chip.

Waveforms — the Transport Layer

Waveforms\ provides the four bus protocols that chip drivers build on. Each has a native path (Linux kernel drivers via ext-posi) and a usb path. SPI, I²C, and GPIO drive the FT232H over ext-ftdi + microscrap/mpsse; UART instead uses ext-ftdi + microscrap/ftdi as plain async serial (no MPSSE).

SPI

use Waveforms\Carriers\SPI\SPI;

// Native — spidev on a Raspberry Pi
$spi = SPI::connection('native')
    ->device('/dev/spidev0.0')
    ->mode(0)
    ->speed(8_000_000)
    ->create();

// USB — FT232H via MPSSE
$spi = SPI::connection('usb')
    ->device('ft232h')
    ->speed(1_000_000)
    ->create();

Under the hood the native path uses microscrap/spi (spi_open, spi_transfer). The USB path opens an MPSSE session in SPI0 mode.

I²C

use Waveforms\Carriers\I2C\I2C;

// Native — i2c-dev on a Raspberry Pi
$i2c = I2C::connection('native')
    ->bus('/dev/i2c-1')
    ->address(0x38)
    ->create();

// USB — FT232H via MPSSE (I2C mode)
$i2c = I2C::connection('usb')
    ->device('ft232h')
    ->address(0x38)
    ->create();

GPIO

use Waveforms\Carriers\GPIO\GPIO;
use Waveforms\Carriers\GPIO\API\Native\{NativeGPIOInput, NativeGPIOOutput};
use Waveforms\Carriers\GPIO\API\USB\{UsbGPIOInput, UsbGPIOOutput};

// Native — Linux gpiod character device
$echo = NativeGPIOInput::line(22)->edgeEvents()->alias('echo');
$trig = NativeGPIOOutput::line(24)->alias('trig');

$bus = GPIO::connection('native')
    ->gpiochip(0)
    ->addInput($echo)
    ->addOutput($trig)
    ->nonblocking()
    ->consumer('distance-sensor')
    ->boot();

$bus->trig()->high();
usleep(10);
$bus->trig()->low();
$value = $bus->echo()->read();

// USB — FT232H CBUS/ADBUS GPIO pins
$echo = UsbGPIOInput::pin(0)->edgeEvents()->alias('echo');
$trig = UsbGPIOOutput::pin(1)->alias('trig');

$bus = GPIO::connection('usb')
    ->device('ft232h')
    ->addInput($echo)
    ->addOutput($trig)
    ->consumer('distance-sensor')
    ->boot();

UART

use Waveforms\Carriers\UART\UART;

// Native — /dev/ttyUSB0 via termios
$uart = UART::connection('native')
    ->port('/dev/ttyUSB0')
    ->baud(115200)
    ->create();

// USB — FT232RL serial over libftdi
$uart = UART::connection('usb')
    ->device('ft232rl')
    ->baud(115200)
    ->create();

Under the hood the native path uses microscrap/uart (termios). Unlike SPI, I²C, and GPIO, the USB path does not use MPSSE — it drives the FTDI chip as a plain async serial port via microscrap/ftdi (the adapter is reset to async-serial bitmode on open).

RealityInterface — Displays

RealityInterface\Displays\ provides the display abstraction stack. Application code works with the applied classes (BWePaperDisplay, ColorTFTDisplay, etc.) and never touches chip registers directly.

Applied display classes

Class Pixel format Chip contracts it targets
MonochromeDisplay 1bpp mono SSD1306, SH1106 (OLED)
ColorTFTDisplay RGB565 ST7735, ST7789, ST7796, GC9A01
BWePaperDisplay 1bpp B/W SSD1680, IL3820
TriColorePaperDisplay 1bpp B/W + 1bpp red SSD1680 (B/W/R mode)
QuadColorePaperDisplay 2bpp B/W/R/Y JD79661

Constructing a display

use DeptOfScrapyardRobotics\Displays\SSD1680\SSD1680;
use RealityInterface\Displays\BWePaperDisplay;

// Inline — useful during development
$chip = SSD1680::connection('usb')
    ->spi('ft232h', 0)
    ->gpiochip('ft232h')
    ->rst(0)
    ->dc(1)
    ->create();

$display = BWePaperDisplay::as($chip);

Drawing with Screen

Screen is the fluent drawing surface. It wraps a GFXRenderer (from microscrap/phpdafruit-gfx), transcodes the rendered buffer through the display's FormatSpec, and ships the bytes to the panel.

use RealityInterface\Displays\Screen;

$screen = Screen::on($display);

$screen
    ->fill(0x00)
    ->drawRect(2, 2, 118, 60, 0xFF)
    ->setTextColor(0xFF)
    ->setCursor(10, 20)
    ->print('Hello from PHP')
    ->render();

render() calls display->transmit(DumpedBuffer) internally; partial-refresh panels receive only dirty pages/regions.

RealityInterface — Sensors

RealityInterface\Sensors\ mirrors the display stack for sensor chips. Each applied class carries a capability attribute that chip drivers must declare.

Applied sensor classes

Class Capability Chip contract
DistanceSensor MeasuresDistance GenericDistanceSensor
UltrasonicDistanceSensor extends DistanceSensor PulseDerivedDistanceSensor
TimeOfFlightDistanceSensor extends DistanceSensor LaserGuidedDistanceSensor
Accelerometer MeasuresAcceleration GenericAccelerometer
AmbientLightSensor MeasuresLuminance GenericLuxSensor
TemperatureSensor MeasuresTemperature TemperatureSensor
RelativeHumiditySensor MeasuresRelativeHumidity RHSensor
BarometricPressureSensor MeasuresBarometricPressure PressureSensor
RFIDScanner NearFieldCommunications GenericRFIDScanner
GPSSensor TriangulatesPosition GlobalPositioningSystem
HumanPresenceDetector MeasuresObjectPresence HumanPresenseSensor

Reading a sensor

use DeptOfScrapyardRobotics\Sensors\VL53Lxx\VL53L0X;
use RealityInterface\Sensors\TimeOfFlightDistanceSensor;

$chip   = VL53L0X::connection('native')->i2c('/dev/i2c-1', 0x29)->create();
$sensor = TimeOfFlightDistanceSensor::as($chip);

$reading = $sensor->getDistance();
echo $reading->millimeters() . ' mm';

Driver-agnostic GPS

GPSSensor wraps any NMEA GPS chip behind one common API. Inject an MTK3339 or a GY-NEO6M and the application code is identical — fixQuality() returns a driver-agnostic GpsFixQuality enum so callers never need to know which module is plugged in.

use DeptOfScrapyardRobotics\Sensors\GYNEO6M\GYNEO6M\GYNEO6M;
use RealityInterface\Sensors\Applied\GPS\GPSSensor;
use RealityInterface\Sensors\Enums\GpsFixQuality;

$chip = GYNEO6M::connection('usb')->uart('ft232h', 9600)->create();
$gps  = GPSSensor::as($chip);

$gps->update(); // fold the next NMEA sentence into the running fix

if ($gps->fixQuality() === GpsFixQuality::GPS) {
    printf("%.6f, %.6f\n", $gps->getLatitude(), $gps->getLongitude());
}

GFX Renderer

microscrap/phpdafruit-gfx is the rendering engine used by Screen. It is modelled on Adafruit-GFX and draws into a FormatSpecFrameBuffer. The buffer's FormatSpec determines how pixels are packed on dump() — the same drawing calls produce 1bpp mono page buffers for an OLED or RGB565 for a TFT.

Primitives

Category Methods
Pixels drawPixel, drawPixels, fill, fillScreen
Lines drawLine, drawHLine, drawVLine, drawLines
Rectangles drawRect, fillRect, drawRoundRect, fillRoundRect
Curves drawCircle, fillCircle, drawEllipse, fillEllipse
Triangles drawTriangle, fillTriangle
Text print, println, setCursor, setTextColor, setTextSize, setFont
Bitmaps drawBitmap, drawXBitmap, drawColorMap
Images drawImageFromFile (requires ext-gd)
Dithering ditherFloydSteinberg, drawImageDithered (requires ext-gd)

Partial-refresh buffers

Buffer Use case
PageSegmentBuffer Monochrome OLED panels — emits one update per dirty 8-row page
DirtyRegionsBuffer Color TFTs — coalesces changed rectangles, emits one update per region
use Microscrap\GFX\PhpdaFruit\GFXRenderer;
use Microscrap\GFX\PhpdaFruit\Buffers\PageSegmentBuffer;
use ScrapyardIO\NutsAndBolts\Enums\{BitDepth, BitOrder, PixelFormat};

$buffer = PageSegmentBuffer::size(128, 64)
    ->pixelFormat(PixelFormat::MONO_VERTICAL_PAGE)
    ->bitDepth(BitDepth::B1)
    ->bitOrder(BitOrder::LSB_FIRST)
    ->build();

$renderer = new GFXRenderer($buffer);
$renderer->fill(0)->drawCircle(64, 32, 20, 1)->print('ok');

$updates = $renderer->render(); // array of DumpedBuffer

Device Drivers (DeptOfScrapyardRobotics)

All chip drivers live in separate dept-of-scrapyard-robotics/* packages. Every driver follows the same fluent builder pattern and plugs cleanly into the RealityInterface applied classes above.

Displays

Package Chips Wrapper
ssd1680 SSD1680 — 122×250 ePaper BWePaperDisplay, TriColorePaperDisplay
jd79661 JD79661 — 2bpp quad-color ePaper QuadColorePaperDisplay
st77xx ST7735 (128×160), ST7789 (240×320), ST7796 (480×320) ColorTFTDisplay
gc9a01 GC9A01 — 240×240 round TFT ColorTFTDisplay
ssd1306 SSD1306 — monochrome OLED (I²C or SPI) MonochromeDisplay
sh1106 SH1106 — monochrome OLED (I²C) MonochromeDisplay
il3820 IL3820 — ePaper controller ePaper wrappers

Sensors

Package Chips Type Wrapper
vl53lxx VL53L0X, VL53L1X, VL6180X Time-of-flight (I²C) TimeOfFlightDistanceSensor
hc-sr04 HC-SR04 Ultrasonic (GPIO) UltrasonicDistanceSensor
adxl34x ADXL345 Accelerometer (I²C/SPI) Accelerometer
lis3dx LIS3DH, LIS3DSH Accelerometer (I²C/SPI) Accelerometer
msa3xx MSA311, MSA301 Accelerometer (I²C) Accelerometer
bh1750 BH1750 Ambient light (I²C) AmbientLightSensor
tsl2591 TSL2591 Dual-channel lux (I²C) AmbientLightSensor
veml7000 VEML7000 Ambient light (I²C) AmbientLightSensor
ahtx0 AHT10, AHT20, AHT30 Temp + RH (I²C) TemperatureSensor, RelativeHumiditySensor
bmp BMP280 Temp + pressure (I²C/SPI) TemperatureSensor, BarometricPressureSensor
as3935 AS3935 Lightning detector (SPI) Direct (no typed wrapper)
rfid NFC/RFID controllers I²C/SPI/UART + GPIO RFIDScanner
mtk3339 MTK3339 GPS (UART + optional GPIO) GPSSensor
gy-neo6m GY-NEO6M (u-blox NEO-6M) GPS (UART) GPSSensor
ld24xx LD2410C Human presence radar (UART) HumanPresenceDetector

Each package's README documents the exact ::connection() builder chain and the scrapyard-io.php config shape for that chip.

License

MIT