PHPackages                             dept-of-scrapyard-robotics/pironman5 - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Framework](/categories/framework)
4. /
5. dept-of-scrapyard-robotics/pironman5

ActiveLibrary[Framework](/categories/framework)

dept-of-scrapyard-robotics/pironman5
====================================

Daemon/WebServer for Interacting with the GPIO of a RaspberryPi 5 in a Pironman5(-MAX) case.

0.2.0(1mo ago)00MITPHPPHP ^8.3|^8.4

Since Apr 17Pushed 1mo agoCompare

[ Source](https://github.com/DeptOfScrapyardRobotics/pironman5)[ Packagist](https://packagist.org/packages/dept-of-scrapyard-robotics/pironman5)[ Docs](https://dosr.projectsaturnstudios.com)[ RSS](/packages/dept-of-scrapyard-robotics-pironman5/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (4)Versions (2)Used By (0)

dept-of-scrapyard-robotics/pironman5
====================================

[](#dept-of-scrapyard-roboticspironman5)

A Laravel package that exposes a real-time system-stats API for a **Raspberry Pi 5** enclosed in a [Pironman5 or Pironman5 MAX](https://github.com/sunfounder/pironman5) case. The API is served by **Laravel Octane** and is accessible both locally and over your LAN. A built-in daemon service layer continuously samples stats, diffs them against the previous reading, and appends changes to per-subsystem CSV logs.

---

Hardware Context
----------------

[](#hardware-context)

ComponentDetailsSBCRaspberry Pi 5CasePironman5 / Pironman5 MAXBoot driveNVMe via PCIe (up to 2 drives on MAX)SD card slotOnboard — readable even when not booted from SDUSB storageAny attached USB storage devicesThe package understands the Pi 5 block-device layout (`nvme*`, `mmcblk*`, `sd*`) and classifies each disk automatically.

---

Requirements
------------

[](#requirements)

DependencyVersionPHP^8.3 | ^8.4Laravel^11 | ^12 | ^13`ext-fd`0.1.0`ext-gpio`0.1.0`scrapyard-io/support`0.3.0`lorisleiva/laravel-actions`dev-main`spatie/laravel-data`^4.0**Suggested (strongly recommended):**

- `laravel/octane` ^2.17 — high-performance application server
- `laravel/reverb` ^1.10 — WebSocket broadcasting for live stat streaming

---

Installation
------------

[](#installation)

```
composer require dept-of-scrapyard-robotics/pironman5
```

The package auto-discovers its service provider via Laravel's package discovery.

### Publishing the config

[](#publishing-the-config)

```
php artisan vendor:publish --tag=pironman5
```

This copies `config/pironman5.php` into your application's `config/` directory.

```
php artisan vendor:publish --tag=pironman5 --force   # overwrite an existing published config
```

After publishing, edit `config/pironman5.php` to configure middleware, toggle Octane/Reverb support, set log paths, etc.

---

Configuration
-------------

[](#configuration)

**`config/pironman5.php`**

```
use DeptOfScrapyardRobotics\Pironman5\Services\Daemon\SystemMonitorService;

return [
    // Set to true when running under Laravel Octane
    'octane' => false,

    // Set to true when Laravel Reverb is installed for WebSocket broadcasting
    'reverb' => false,

    // Middleware applied to all /pironman5/* API routes.
    // Defaults to an empty array (no middleware). Add 'auth:sanctum', throttle rules, etc.
    'api_middleware' => [],

    // Microseconds to sleep at the end of each daemon loop iteration (~60 Hz by default).
    'daemon_eow_delay' => ((1 / 60) * 1000),

    // Daemon service definitions — each entry is instantiated and driven by the daemon loop.
    'services' => [
        'system_monitor' => [
            'class' => SystemMonitorService::class,
            'settings' => [
                'logs' => [
                    'cpu' => storage_path('cpu_log.csv'),
                    'gpu' => storage_path('gpu_log.csv'),
                    'hdd' => storage_path('hdd_log.csv'),
                    'ram' => storage_path('ram_log.csv'),
                ],
            ],
        ],
    ],
];
```

---

Serving the API
---------------

[](#serving-the-api)

The package is designed to run under **Laravel Octane**, which keeps the application in memory for near-zero latency stat reads.

### Start Octane (FrankenPHP or Swoole)

[](#start-octane-frankenphp-or-swoole)

```
php artisan octane:start --port=8000
```

### Access the API

[](#access-the-api)

ContextBase URLOn the Pi itself`http://127.0.0.1:8000/pironman5`From your LAN`http://192.168.x.x:8000/pironman5`All routes are prefixed with `/api/pironman5/system/stats/`.

---

Running at Startup (systemd)
----------------------------

[](#running-at-startup-systemd)

The package ships two Artisan commands that manage a systemd service unit, so the daemon starts automatically on boot.

### Register the service

[](#register-the-service)

Run the following from your Laravel project root **on the Pi**:

```
sudo php artisan pironman:service:register
```

The command auto-detects the PHP binary, artisan path, and current OS user, then shows you the generated unit file before asking for confirmation. You can override any value:

```
sudo php artisan pironman:service:register \
    --service-name=pironman5 \
    --user=angel \
    --php=/usr/bin/php8.4
```

Pass `--start` to also start the service immediately:

```
sudo php artisan pironman:service:register --start
```

This is equivalent to:

```
# What the command does internally:
sudo tee /etc/systemd/system/pironman5.service  Both `idle` and `total` are required for every core entry when `prev` is present; passing a partial snapshot will fail validation.

**First call (no `prev`):**

```
GET /api/pironman5/system/stats/cpu/load
```

```
{
    "cpu_load": 0.0,
    "cpu_cores": [],
    "prev": {
        "cpu0": { "idle": 123456, "total": 234567 },
        "cpu1": { "idle": 124567, "total": 235678 },
        "cpu2": { "idle": 125678, "total": 236789 },
        "cpu3": { "idle": 126789, "total": 237890 }
    }
}
```

**Subsequent calls (pass `prev` back):**

```
GET /api/pironman5/system/stats/cpu/load?prev[cpu0][idle]=123456&prev[cpu0][total]=234567&...
```

```
{
    "cpu_load": 12.34,
    "cpu_cores": [10.12, 14.56, 11.00, 13.78],
    "prev": {
        "cpu0": { "idle": 124100, "total": 235200 },
        ...
    }
}
```

FieldTypeDescription`cpu_load``float`Average load across all cores (0–100)`cpu_cores``float[]`Per-core load percentage, indexed by core number`prev``object`Current snapshot — pass this back as `prev` on the next request---

#### `GET /api/pironman5/system/stats/cpu/temp`

[](#get-apipironman5systemstatscputemp)

Returns the CPU die temperature read from `/sys/class/thermal/thermal_zone0/temp`.

**Route name:** `pironman5.system.stats.cpu.temp`

**Response:**

```
{
    "temp": 52.312
}
```

FieldTypeDescription`temp``float`Temperature in °C---

#### `GET /api/pironman5/system/stats/cpu/log`

[](#get-apipironman5systemstatscpulog)

Returns the last N rows from the CPU stat log as an array of objects keyed by CSV column name.

**Route name:** `pironman5.system.stats.cpu.log`

**Request parameters:**

ParameterRuleDescription`num_lines``sometimes|required|integer|min:1`Number of rows to return. Defaults to `50`.**Response:**

```
{
    "log": [
        { "timestamp": "2026-04-16 12:00:01", "cpu_temp": "52.31", "cpu_load": "8.12", "core_0": "7.40", "core_1": "9.20", "core_2": "7.80", "core_3": "8.08" },
        { "timestamp": "2026-04-16 12:00:02", "cpu_temp": "52.43", "cpu_load": "9.55", "core_0": "8.10", "core_1": "11.20", "core_2": "9.40", "core_3": "9.50" }
    ]
}
```

---

### GPU

[](#gpu)

#### `GET /api/pironman5/system/stats/gpu/load`

[](#get-apipironman5systemstatsgpuload)

Returns the VideoCore GPU utilisation read from `/sys/class/devfreq/devfreq0/load`.

**Route name:** `pironman5.system.stats.gpu.load`

**Response:**

```
{
    "load": 4.0
}
```

FieldTypeDescription`load``float`GPU load percentage (0–100)---

#### `GET /api/pironman5/system/stats/gpu/log`

[](#get-apipironman5systemstatsgpulog)

Returns the last N rows from the GPU stat log.

**Route name:** `pironman5.system.stats.gpu.log`

**Request parameters:**

ParameterRuleDescription`num_lines``sometimes|required|integer|min:1`Number of rows to return. Defaults to `50`.**Response:**

```
{
    "log": [
        { "timestamp": "2026-04-16 12:00:01", "gpu_load": "4.0" },
        { "timestamp": "2026-04-16 12:00:02", "gpu_load": "6.0" }
    ]
}
```

---

### RAM

[](#ram)

#### `GET /api/pironman5/system/stats/ram/info`

[](#get-apipironman5systemstatsraminfo)

Returns memory statistics parsed from `/proc/meminfo`.

**Route name:** `pironman5.system.stats.ram.info`

**Response:**

```
{
    "total_bytes": 8323186688.0,
    "available_bytes": 7516192768.0,
    "used_bytes": 806993920.0,
    "total_gb": 7.75,
    "used_gb": 0.75,
    "percent_used": 9.69
}
```

FieldTypeDescription`total_bytes``float`Total installed RAM in bytes`available_bytes``float`Available (not used) RAM in bytes`used_bytes``float`Used RAM in bytes`total_gb``float`Total RAM in GiB (2 dp)`used_gb``float`Used RAM in GiB (2 dp)`percent_used``float`Percentage used (0–100, 2 dp)---

#### `GET /api/pironman5/system/stats/ram/log`

[](#get-apipironman5systemstatsramlog)

Returns the last N rows from the RAM stat log.

**Route name:** `pironman5.system.stats.ram.log`

**Request parameters:**

ParameterRuleDescription`num_lines``sometimes|required|integer|min:1`Number of rows to return. Defaults to `50`.**Response:**

```
{
    "log": [
        { "timestamp": "2026-04-16 12:00:01", "used_bytes": "806993920", "total_bytes": "8323186688", "used_gb": "0.75", "total_gb": "7.75", "percent_used": "9.69" }
    ]
}
```

---

### HDD / Storage

[](#hdd--storage)

#### `GET /api/pironman5/system/stats/hdd/disks`

[](#get-apipironman5systemstatshdddisks)

Returns all physical block devices known to the kernel, including devices that are **not mounted** (e.g. an SD card when booting from NVMe). Devices are discovered via `/sys/block/` and cross-referenced with `/proc/mounts`.

**Route name:** `pironman5.system.stats.hdd.disks`

**Response:**

```
{
    "disks": [
        {
            "device": "/dev/nvme0n1p2",
            "mount_point": "/",
            "fs_type": "ext4",
            "type": "nvme",
            "mounted": true,
            "total_bytes": 1008184320000,
            "used_bytes": 52876881920,
            "free_bytes": 955307438080,
            "total_gb": 938.94,
            "used_gb": 49.25,
            "free_gb": 889.70,
            "percent_used": 5.24
        },
        {
            "device": "/dev/mmcblk0p1",
            "mount_point": null,
            "fs_type": null,
            "type": "sdcard",
            "mounted": false,
            "total_bytes": 31268536320,
            "used_bytes": null,
            "free_bytes": null,
            "total_gb": 29.13,
            "used_gb": null,
            "free_gb": null,
            "percent_used": null
        }
    ]
}
```

**Per-disk fields:**

FieldTypeDescription`device``string`Resolved block device path`mount_point``string|null`Filesystem mount point, `null` if not mounted`fs_type``string|null`Filesystem type (`ext4`, `vfat`, …), `null` if not mounted`type``string`Hardware class: `nvme`, `sdcard`, `usb`, or `unknown``mounted``bool`Whether the partition is currently mounted`total_bytes``int`Total partition size in bytes`used_bytes``int|null`Used bytes — `null` when not mounted`free_bytes``int|null`Free bytes — `null` when not mounted`total_gb``float`Total size in GiB (2 dp)`used_gb``float|null`Used in GiB — `null` when not mounted`free_gb``float|null`Free in GiB — `null` when not mounted`percent_used``float|null`Percentage used — `null` when not mounted**Device type classification:**

`type` valueDevice patternTypical source`nvme``nvme*`PCIe NVMe SSD (up to 2 on MAX)`sdcard``mmcblk*`Onboard SD card slot`usb``sd[a-z]*`USB-attached storage`unknown`anything elseOther block device---

#### `GET /api/pironman5/system/stats/hdd/log`

[](#get-apipironman5systemstatshddlog)

Returns the last N rows from the HDD stat log. Each row represents one disk device at the time it was sampled; multiple disks produce multiple rows per timestamp.

**Route name:** `pironman5.system.stats.hdd.log`

**Request parameters:**

ParameterRuleDescription`num_lines``sometimes|required|integer|min:1`Number of rows to return. Defaults to `50`.**Response:**

```
{
    "log": [
        { "timestamp": "2026-04-16 12:00:01", "device": "/dev/nvme0n1p2", "type": "nvme", "mounted": "1", "total_gb": "938.94", "used_gb": "49.25", "free_gb": "889.70", "percent_used": "5.24" },
        { "timestamp": "2026-04-16 12:00:01", "device": "/dev/mmcblk0p1", "type": "sdcard", "mounted": "0", "total_gb": "29.13", "used_gb": "", "free_gb": "", "percent_used": "" }
    ]
}
```

---

Daemon Services
---------------

[](#daemon-services)

The package includes a daemon service layer designed to be driven by a long-running process (e.g. an Artisan command). Each service implements a three-phase lifecycle per tick:

PhaseMethodResponsibilityPrepare`prepare(array &$shared): ?array`Fetches fresh data from the hardware and returns it as `$prep_result`.Execute`execute(?array $payload): ?array`Diffs `$payload` (new data) against the current class-level state. Returns only keys whose values changed; unchanged keys remain `null`.Finish`finish(?array $exec_result, ?array $prep_result, array &$shared): static`Updates class-level state from `$prep_result` and writes CSV log entries for any subsystems that reported a change in `$exec_result`.### `SystemMonitorService`

[](#systemmonitorservice)

Samples CPU load &amp; temperature, GPU load, RAM usage, and disk info on every tick.

```
use DeptOfScrapyardRobotics\Pironman5\Services\Daemon\SystemMonitorService;

$service = SystemMonitorService::start(
    config('pironman5.services.system_monitor.settings')
);

// Drive the service manually:
$shared   = [];
$prep     = $service->prepare($shared);
$changes  = $service->execute($prep);
$service->finish($changes, $prep, $shared);
```

**Getters:**

```
$service->getCpuTemp();    // float (°C)
$service->getCoreLoads();  // CPUCoreLoads DTO
$service->getGpuLoad();    // float (%)
$service->getMemoryInfo(); // MemoryInfo DTO
$service->getDisks();      // DiskInfo[]
```

### CSV Logging

[](#csv-logging)

Each subsystem writes to its own append-only CSV file, configured under `services.system_monitor.settings.logs`. A header row is written automatically on first creation. Log entries are only appended when `execute()` detects a change for that subsystem.

Config keyDefault pathColumns`logs.cpu``storage/cpu_log.csv``timestamp`, `cpu_temp`, `cpu_load`, `core_0` … `core_N``logs.gpu``storage/gpu_log.csv``timestamp`, `gpu_load``logs.ram``storage/ram_log.csv``timestamp`, `used_bytes`, `total_bytes`, `used_gb`, `total_gb`, `percent_used``logs.hdd``storage/hdd_log.csv``timestamp`, `device`, `type`, `mounted`, `total_gb`, `used_gb`, `free_gb`, `percent_used`---

Package Structure
-----------------

[](#package-structure)

```
src/
├── Actions/
│   ├── Daemon/
│   │   └── StartUp/
│   │       └── StartSystemMonitorService.php  — factory action for SystemMonitorService
│   └── SystemStats/
│       ├── CPU/
│       │   ├── GetCPUCoreLoads.php            — /proc/stat delta-based load
│       │   └── GetCPUTemp.php                 — /sys/class/thermal temperature
│       ├── GPU/
│       │   └── GetGPULoad.php                 — devfreq0 GPU utilisation
│       ├── HDD/
│       │   └── GetMountedDiskInfo.php         — /sys/block + /proc/mounts
│       ├── Log/
│       │   └── ReadStatLog.php                — reads last N rows from a stat CSV
│       └── RAM/
│           └── GetMemoryInfo.php              — /proc/meminfo
├── Console/
│   └── Commands/
│       ├── StartDaemonCommand.php             — pironman:go
│       ├── RegisterDaemonCommand.php          — pironman:service:register
│       ├── RestartDaemonCommand.php           — pironman:service:restart
│       └── UnregisterDaemonCommand.php        — pironman:service:unregister
├── Contracts/
│   └── Services/
│       └── Daemon/
│           └── DaemonService.php              — prepare/execute/finish interface
├── DTO/
│   └── SystemStats/
│       ├── SystemStats.php                    — abstract base (Spatie Data)
│       ├── CPUCoreLoads.php
│       ├── DiskInfo.php
│       └── MemoryInfo.php
├── Http/
│   ├── Controllers/
│   │   └── API/
│   │       ├── CPUStatsAPIController.php
│   │       ├── GPUStatsAPIController.php
│   │       ├── HDDStatsAPIController.php
│   │       └── RAMStatsAPIController.php
│   └── Requests/
│       └── API/
│           ├── GetCPUCoreLoadsRequest.php     — validates prev[*][idle|total]
│           └── GetStatLogRequest.php          — validates num_lines
└── Services/
    └── Daemon/
        ├── DaemonService.php                  — abstract base service
        └── SystemMonitorService.php           — CPU/GPU/RAM/HDD monitor + CSV logger

routes/
├── api.php
└── web.php

config/
└── pironman5.php

```

---

Actions
-------

[](#actions)

All actions use [`lorisleiva/laravel-actions`](https://laravelactions.com/) and can be called via `::run()` anywhere in your application, not just through the HTTP layer.

```
use DeptOfScrapyardRobotics\Pironman5\Actions\SystemStats\RAM\GetMemoryInfo;
use DeptOfScrapyardRobotics\Pironman5\Actions\SystemStats\CPU\GetCPUCoreLoads;
use DeptOfScrapyardRobotics\Pironman5\Actions\SystemStats\CPU\GetCPUTemp;
use DeptOfScrapyardRobotics\Pironman5\Actions\SystemStats\GPU\GetGPULoad;
use DeptOfScrapyardRobotics\Pironman5\Actions\SystemStats\HDD\GetMountedDiskInfo;
use DeptOfScrapyardRobotics\Pironman5\Actions\SystemStats\Log\ReadStatLog;

$memory  = GetMemoryInfo::run();                          // MemoryInfo DTO
$cpu     = GetCPUCoreLoads::run($prev);                   // CPUCoreLoads DTO
$temp    = GetCPUTemp::run();                             // float (°C)
$gpu     = GetGPULoad::run();                             // float (%)
$disks   = GetMountedDiskInfo::run();                     // DiskInfo[]
$rows    = ReadStatLog::run(storage_path('cpu_log.csv')); // array
```

DTOs
----

[](#dtos)

All DTOs extend `DeptOfScrapyardRobotics\Pironman5\DTO\SystemStats\SystemStats`, which itself extends `Spatie\LaravelData\Data`. This means every DTO supports `.toArray()`, `.toJson()`, and is directly `JsonSerializable` — pass one straight to `response()->json()` with no extra mapping.

---

License
-------

[](#license)

MIT — see [LICENSE](LICENSE) for details.

###  Health Score

35

—

LowBetter than 77% of packages

Maintenance89

Actively maintained with recent releases

Popularity0

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity41

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Unknown

Total

1

Last Release

53d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/10563160?v=4)[Angel Gonzalez](/maintainers/projectsaturnstudios)[@projectsaturnstudios](https://github.com/projectsaturnstudios)

---

Top Contributors

[![projectsaturnstudios](https://avatars.githubusercontent.com/u/10563160?v=4)](https://github.com/projectsaturnstudios "projectsaturnstudios (3 commits)")

---

Tags

frameworkpironman5php-io-extensions

### Embed Badge

![Health badge](/badges/dept-of-scrapyard-robotics-pironman5/health.svg)

```
[![Health](https://phpackages.com/badges/dept-of-scrapyard-robotics-pironman5/health.svg)](https://phpackages.com/packages/dept-of-scrapyard-robotics-pironman5)
```

###  Alternatives

[lavalite/framework

The lavalite framework

5861.6k1](/packages/lavalite-framework)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
