mirror of
https://github.com/MoHadiShibli/Control4Free.git
synced 2026-10-06 09:00:29 +02:00
Documentation for 1.0.0: README, guides, screenshots, GitHub files
- README: what Control4Free is and why it reaches menus and sign-in, features, a quick start, screenshots, requirements, limitations, credits. - docs/: installation, playing, troubleshooting (every message the page and the app can show), how it works, and the WebSocket protocol. - CONTRIBUTING.md: building, testing, code style, releasing. - Screenshots of the page on phones and a PC and of the app, made against the real service built for the host, at the example address 192.168.1.20. - Issue forms, a contact list pointing at the guides and private security reporting, and FUNDING.yml. - CHANGELOG.md dated. CI now publishes this version's section of it as the release notes rather than the whole file. - THIRD_PARTY.md: the DualShock 4 diagram the buttons are drawn after (CC BY 3.0), and Apollo Save Tool for the package settings. - Page: the Vibration setting no longer promises to buzz when a game rumbles, which 1.0.0 cannot do yet.
This commit is contained in:
1 parent
77a1bba68d
commit
5335bc09ce
20 files changed
+881
-226
No files matched your search
@@ -0,0 +1 @@
|
||||
ko_fi: mohadishibli
|
||||
@@ -0,0 +1,49 @@
|
||||
name: Bug report
|
||||
description: Something doesn't work as it should
|
||||
labels: [bug]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for reporting! First, please check the [troubleshooting guide](https://github.com/MoHadiShibli/Control4Free/blob/main/docs/troubleshooting.md): most setup problems are covered there.
|
||||
- type: input
|
||||
id: version
|
||||
attributes:
|
||||
label: Control4Free version
|
||||
description: Shown at the bottom of the controller page and of the app, for example "Control4Free 1.0.0".
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: console
|
||||
attributes:
|
||||
label: Firmware and GoldHEN version
|
||||
placeholder: "Firmware 10.01, GoldHEN v2.4b18.10"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: client
|
||||
attributes:
|
||||
label: Phone or computer, and browser
|
||||
placeholder: "Android phone, Chrome"
|
||||
validations:
|
||||
required: true
|
||||
- type: input
|
||||
id: where
|
||||
attributes:
|
||||
label: Where it happened
|
||||
description: The home screen, the sign-in screen, a game (name and title ID, like CUSA00001), or the app.
|
||||
- type: textarea
|
||||
id: what
|
||||
attributes:
|
||||
label: What happened?
|
||||
description: What did you do, what did you expect, and what happened instead?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: log
|
||||
attributes:
|
||||
label: Log (optional)
|
||||
description: |
|
||||
`/data/control4free/control4free.log` on the PS4, over GoldHEN's FTP server (port 2121). If the problem was before a restart, it's in `control4free.log.previous`.
|
||||
The log contains your console's network address: remove it if you prefer.
|
||||
render: text
|
||||
@@ -0,0 +1,8 @@
|
||||
blank_issues_enabled: true
|
||||
contact_links:
|
||||
- name: Installation and troubleshooting
|
||||
url: https://github.com/MoHadiShibli/Control4Free/blob/main/docs/troubleshooting.md
|
||||
about: Setting up, playing, and fixes for the most common problems.
|
||||
- name: Report a security problem
|
||||
url: https://github.com/MoHadiShibli/Control4Free/security
|
||||
about: Please report vulnerabilities privately, not in a public issue.
|
||||
@@ -0,0 +1,16 @@
|
||||
name: Feature request
|
||||
description: An idea to make Control4Free better
|
||||
labels: [enhancement]
|
||||
body:
|
||||
- type: textarea
|
||||
id: idea
|
||||
attributes:
|
||||
label: What would you like?
|
||||
description: Describe the idea and the problem it solves.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
id: games
|
||||
attributes:
|
||||
label: Games it matters for (optional)
|
||||
description: Game names and title IDs, if it's about specific games.
|
||||
@@ -56,4 +56,9 @@ jobs:
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh release create "${GITHUB_REF#refs/tags/}" artifacts/* --verify-tag --notes-file CHANGELOG.md
|
||||
run: |
|
||||
# The release notes are this version's section of the changelog.
|
||||
version=$(cat VERSION)
|
||||
awk -v head="## $version " 'index($0, head) == 1 { keep = 1; next } /^## / { keep = 0 } keep' CHANGELOG.md > notes.md
|
||||
test -s notes.md || { echo "CHANGELOG.md has no section for $version"; exit 1; }
|
||||
gh release create "${GITHUB_REF#refs/tags/}" artifacts/* --verify-tag --title "Control4Free $version" --notes-file notes.md
|
||||
+19
-22
@@ -1,32 +1,29 @@
|
||||
# Changelog
|
||||
|
||||
Dates are the day the work landed. Versions follow [Semantic Versioning](https://semver.org):
|
||||
PATCH for fixes, MINOR for features that break nothing, MAJOR for anything people
|
||||
rely on that changes.
|
||||
Versions follow [Semantic Versioning](https://semver.org): PATCH for fixes, MINOR for features that break
|
||||
nothing, MAJOR for anything people rely on that changes.
|
||||
|
||||
## 1.0.0 — unreleased
|
||||
## 1.0.0 — 2026-10-05
|
||||
|
||||
First release.
|
||||
|
||||
Control4Free is a GoldHEN payload that turns a phone or PC into a PS4 controller
|
||||
that works everywhere: the home screen, the sign-in screen and games. Up to four
|
||||
of them, through the PS4's own virtual-device API, with the PS4's own
|
||||
"Who's using this controller?" screen deciding who each one is.
|
||||
Control4Free is a GoldHEN payload that turns a phone or PC into a PS4 controller that works everywhere: the
|
||||
home screen, the sign-in screen and games. Up to four of them, through the PS4's own virtual-device API, with
|
||||
the PS4's own *"Who's using this controller?"* screen deciding who each one is.
|
||||
|
||||
- A controller page the payload serves itself on port 4264. Touch controls shaped
|
||||
like a DualShock 4, keyboard keys, or any gamepad the browser exposes. A layout
|
||||
editor, key remapping and a per-controller picker, saved per device.
|
||||
- An installable app for the PS4's home screen that starts and stops Control4Free,
|
||||
shows its address and a QR code, and sets up GoldHEN's AutoRun so the console
|
||||
starts it by itself after every restart.
|
||||
- A controller page the service serves itself on port 4264: a DualShock 4 on the screen, keyboard keys, or any
|
||||
gamepad the browser can see. A layout editor, key remapping and a per-gamepad controller picker, saved on
|
||||
each device.
|
||||
- An app for the PS4's home screen, under Applications, that starts and stops Control4Free, shows its address
|
||||
and a QR code and the number of connected controllers, and sets up GoldHEN's AutoRun so the console starts
|
||||
it by itself after every restart.
|
||||
- The page can be kept on a phone's home screen.
|
||||
- Input is reported the moment it arrives, at a DualShock 4's own 4 ms cadence
|
||||
while a player is doing something, and at a slow keepalive while nobody is.
|
||||
- Input is reported the moment it arrives, at a DualShock 4's own 4 ms rate while a player is doing something,
|
||||
and at a slow keepalive while nobody is.
|
||||
- Connecting a controller never stops the other players being served.
|
||||
- Recovers by itself after rest mode or a network failure: controllers have to be
|
||||
picked again, which is what rest mode does to real ones too.
|
||||
- Open on the local network by design, with no pairing. What that does and does
|
||||
not protect is written down in [SECURITY.md](SECURITY.md).
|
||||
- Recovers by itself after rest mode or a network failure. Controllers have to be picked again after rest
|
||||
mode, which signs every user out.
|
||||
- Open on the local network by design, with no pairing. What that does and does not protect is written down in
|
||||
[SECURITY.md](SECURITY.md).
|
||||
|
||||
Tested on firmware 10.01 with GoldHEN 2.4b18.10. Rumble, light bar and motion are
|
||||
not implemented.
|
||||
Tested on firmware 10.01 with GoldHEN v2.4b18.10. Rumble, light bar and motion are not passed on yet.
|
||||
@@ -0,0 +1,93 @@
|
||||
# Contributing to Control4Free
|
||||
|
||||
Thanks for helping! Bug reports, compatibility reports, fixes and reviews are all welcome.
|
||||
|
||||
## Reporting a bug
|
||||
|
||||
Use the [bug report form](https://github.com/MoHadiShibli/Control4Free/issues/new/choose). The most useful
|
||||
details:
|
||||
- the Control4Free version, shown at the bottom of the page and of the app;
|
||||
- the firmware and GoldHEN versions;
|
||||
- the phone or computer and browser you used;
|
||||
- what you did and what happened;
|
||||
- the service's log, if you can get it: `/data/control4free/control4free.log` on the PS4, over GoldHEN's FTP
|
||||
server (port 2121). The previous run is kept in `control4free.log.previous`.
|
||||
|
||||
The log contains your console's network address. Remove it before posting if you prefer.
|
||||
|
||||
Security problems: please follow [SECURITY.md](SECURITY.md) instead of opening a public issue.
|
||||
|
||||
## Building
|
||||
|
||||
Everything builds in Docker, so Docker is all you need:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/MoHadiShibli/Control4Free
|
||||
cd Control4Free
|
||||
docker build -t control4free-build docker/
|
||||
docker build -t control4free-launcher-build -f docker/Dockerfile.launcher docker/
|
||||
docker run --rm -v "$PWD:/src" -w /src control4free-launcher-build bash -lc 'make && make -C launcher'
|
||||
```
|
||||
|
||||
- `build/control4free.elf` is the service, with the page and its icon built in.
|
||||
- `build/Control4Free-<version>.pkg` is the app, with its own copy of the service.
|
||||
|
||||
The first image pins the [ps4-payload-sdk](https://github.com/ps4-payload-dev/sdk) release that builds the
|
||||
service; the second adds the [OpenOrbis toolchain](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain),
|
||||
pinned by digest, for the app and the package. On Windows, `tools/build-launcher.ps1` runs all three steps.
|
||||
|
||||
The version comes from the `VERSION` file and nowhere else: the Makefiles and the packager read it, and
|
||||
the page and the app show the version the service reports.
|
||||
|
||||
## Testing
|
||||
|
||||
```shell
|
||||
docker run --rm --network none -v "$PWD:/src" -w /src control4free-launcher-build python3 -B tests/run.py
|
||||
```
|
||||
|
||||
The suites build the real sources for the host with only the PS4 calls stubbed, so they need no console:
|
||||
- `test_web` and `test_recovery` run the actual service against a fake kernel log
|
||||
(`tests/web_stub.c`) that writes the same lines firmware 10.01 does, over real HTTP and WebSocket
|
||||
connections;
|
||||
- `test_frontend` runs the page's own functions under Node;
|
||||
- `test_launcher` drives the app's logic, renders every state of its screen to `build/launcher-preview-*.png`,
|
||||
and checks the package's `param.sfo`;
|
||||
- `test_logging` covers the log, the kernel-log opener and DeviceId matching.
|
||||
|
||||
Add a test with your fix: one that fails without it. Then test on a real console if you can, and say in your
|
||||
pull request which firmware, GoldHEN version and games you tried.
|
||||
|
||||
To try changes to the page without rebuilding, open `client/index.html` from disk and enter the console's
|
||||
address, or add `?host=<ps4-ip>:4264` to the page's URL.
|
||||
|
||||
## Code style
|
||||
|
||||
Match the code around your change:
|
||||
- C with GNU extensions (gnu11), 4-space indent, `camelCase` functions with a `c4f` prefix, `C4f` types and
|
||||
`C4F_` macros;
|
||||
- log with `c4fLog`. `c4fNotify` shows a notification on the TV: keep those rare;
|
||||
- comments explain *why*, not *what*;
|
||||
- only button bits confirmed on hardware go in `include/c4f_sce.h`;
|
||||
- the page is one file with no build step and no libraries: keep it that way;
|
||||
- OpenOrbis's headers give `MSG_NOSIGNAL`, `SIGSYS` and `CLOCK_MONOTONIC` their Linux values, which the PS4
|
||||
reads as something else. Don't use them directly in `launcher/`; a test checks.
|
||||
|
||||
Build without new warnings; the app is built with `-Werror`.
|
||||
|
||||
## Releasing
|
||||
|
||||
1. Update `VERSION`, and add a section to `CHANGELOG.md`.
|
||||
2. Commit, tag `vX.Y.Z`, and push the tag.
|
||||
3. CI builds both images, runs the tests, and publishes the release with the ELF, the package and
|
||||
`SHA256SUMS`. It refuses a tag that doesn't match `VERSION`.
|
||||
|
||||
## Reviewing AI-written code
|
||||
|
||||
Much of Control4Free was written with the help of AI models (Claude by Anthropic and Codex by OpenAI). It has
|
||||
been tested, on the console as well, but AI-written code can look right and still be wrong. Please read it
|
||||
critically: if you find dead code, wrong assumptions or needless complexity,
|
||||
[open an issue](https://github.com/MoHadiShibli/Control4Free/issues) or send a pull request.
|
||||
|
||||
## License
|
||||
|
||||
Contributions are accepted under the [GNU General Public License v3](LICENSE), the project's license.
|
||||
@@ -1,231 +1,133 @@
|
||||
# Control4Free
|
||||
<p align="center"><img src="docs/images/icon.png" width="128" alt="Control4Free"></p>
|
||||
|
||||
A GoldHEN payload for controlling the PS4 from a phone or PC, using touch,
|
||||
keyboard, or an Xbox/other controller exposed by the browser's Gamepad API.
|
||||
Virtual controllers use the PS4's native user-selection screen, so they work on
|
||||
the home screen, at sign-in and in games.
|
||||
<h1 align="center">Control4Free</h1>
|
||||
|
||||
Browser control, native user sign-in, gameplay, the launcher app, GoldHEN
|
||||
AutoRun and recovery after rest mode are all confirmed on the development
|
||||
console. Four controllers at a time; the console's own device limits apply.
|
||||
<p align="center">
|
||||
<a href="https://github.com/MoHadiShibli/Control4Free/releases/latest"><img src="https://img.shields.io/github/v/release/MoHadiShibli/Control4Free?label=release" alt="Latest release"></a>
|
||||
<a href="https://github.com/MoHadiShibli/Control4Free/actions/workflows/build.yml"><img src="https://github.com/MoHadiShibli/Control4Free/actions/workflows/build.yml/badge.svg" alt="Build"></a>
|
||||
<a href="LICENSE"><img src="https://img.shields.io/badge/license-GPL--3.0-blue" alt="License: GPL v3"></a>
|
||||
<a href="https://ko-fi.com/mohadishibli"><img src="https://img.shields.io/badge/Ko--fi-support%20this%20project-FF5E5B?logo=ko-fi&logoColor=white" alt="Support on Ko-fi"></a>
|
||||
</p>
|
||||
|
||||
See [CHANGELOG.md](CHANGELOG.md) for what is in a release, and
|
||||
[SECURITY.md](SECURITY.md) for what being open on your network does and does not
|
||||
mean.
|
||||
**Use your phone or PC as a PS4 controller everywhere: on the home screen, at sign-in, and in every game.**
|
||||
|
||||
## Requirements
|
||||
Control4Free adds up to four extra DualShock 4 controllers to a jailbroken PS4. Open its page in a browser on
|
||||
your network, pick a controller, and choose who's playing on the PS4's own *"Who's using this controller?"*
|
||||
screen. Play with touch, a keyboard, or the gamepad you already have: Xbox, DualSense, Switch Pro and most
|
||||
others work through the phone or PC. There's nothing to install on the phone or computer.
|
||||
|
||||
- A PS4 running GoldHEN. Development console: firmware 10.01, GoldHEN 2.4b18.10.
|
||||
Starting Control4Free from GoldHEN itself (LaunchPad or AutoRun) needs 2.4b18.10
|
||||
or later; the launcher app and PC sending need PayLoader.
|
||||
- Nothing connected to GoldHEN's klog viewer while you add a controller.
|
||||
Control4Free reads the kernel log itself to recover a new device's handle, and
|
||||
the log has a single reader.
|
||||
- A phone or PC on the same network.
|
||||
- Turn off other GoldHEN controller plugins in games you play with Control4Free. A
|
||||
plugin that takes over a signed-in user's controller can stop games from reading
|
||||
the virtual controllers.
|
||||

|
||||
|
||||
## Install
|
||||
## Why it's different
|
||||
|
||||
1. Copy `build/Control4Free-<version>.pkg` to a USB drive (or to `/data/pkg/` over
|
||||
GoldHEN's FTP server) and install it with GoldHEN's Package Installer.
|
||||
2. Open **Control4Free** from the home screen and press **Cross** once. The app
|
||||
adds Control4Free to GoldHEN's AutoRun (GoldHEN 2.4b18.10 or later), so GoldHEN
|
||||
starts it every time it loads. It also starts it right away if GoldHEN's
|
||||
PayLoader is on; otherwise restart the PS4 and run the jailbreak again.
|
||||
3. Scan the QR code or enter the address on your phone or PC. Select a controller
|
||||
there and sign in through the PS4 screen.
|
||||
Controller plugins live inside games, so they can't reach the home screen or the sign-in screen. Control4Free
|
||||
is a GoldHEN payload that goes through Sony's own virtual-device system, the one Remote Play uses. The PS4
|
||||
sees a real controller, so it works wherever a DualShock does:
|
||||
|
||||
The app stays useful afterwards: it shows whether Control4Free is running, its
|
||||
address and QR code, and how many controllers are connected.
|
||||
- **on the home screen and in every menu**, PS button included;
|
||||
- **at sign-in**, through the PS4's own user picker, for real users and guests alike;
|
||||
- **in every game**, with no per-game setup.
|
||||
|
||||
- **Cross** starts Control4Free when it is not running (PayLoader must be on).
|
||||
- **Triangle** turns GoldHEN's auto-start on or off, or updates it after you
|
||||
install a newer package.
|
||||
- **Square**, then **Cross**, stops Control4Free and disconnects its controllers.
|
||||
- **Circle** closes the app. Control4Free keeps running without it.
|
||||
## Features
|
||||
|
||||
### Without the app
|
||||
- **Up to four controllers**, from as many phones, tablets or computers as you like.
|
||||
- **A full DualShock 4 on the screen**: analog sticks, a two-finger touchpad, L3/R3, PS, Share and Options.
|
||||
Move, resize or hide any button with the layout editor; each device keeps its own layout.
|
||||
- **The gamepads you already have.** Connect one to the phone or PC and give it a controller. One computer
|
||||
can run all four.
|
||||
- **A keyboard**, with keys you can change.
|
||||
- **Fast.** Input reaches the console the moment it arrives, at a DualShock 4's own rate while you play.
|
||||
- **An app on the PS4** that shows the address and a QR code, how many controllers are connected, and starts
|
||||
or stops Control4Free.
|
||||
- **Starts by itself.** One button in the app sets up GoldHEN's AutoRun, so Control4Free is ready after
|
||||
every restart.
|
||||
- **On your phone's home screen**, like an app.
|
||||
- **Stays awake**: your phone's screen doesn't turn off while you play.
|
||||
- **Gets through rest mode.** After the PS4 wakes up, open the page and pick your controller again.
|
||||
|
||||
GoldHEN's own **Payloader LaunchPad** (under **Utilities**) does the same job:
|
||||
## Quick start
|
||||
|
||||
1. Put `control4free.elf` in `/data/payloads/` on the PS4, for example through
|
||||
GoldHEN's FTP server (port 2121).
|
||||
2. In the LaunchPad, select `control4free.elf` to start it, or press **Square** on
|
||||
it to add it to the AutoRun queue. The queue is `/data/GoldHEN/payloads.ini`:
|
||||
1. Download `Control4Free-1.0.0.pkg` from the [latest release](https://github.com/MoHadiShibli/Control4Free/releases/latest).
|
||||
2. Install it with GoldHEN's Package Installer: **Settings → Debug Settings → Game → Package Installer**, from a
|
||||
USB drive or `/data/pkg/`.
|
||||
3. Open **Control4Free** from **Library → Applications** and press **Cross**. It sets up auto-start and
|
||||
starts Control4Free.
|
||||
4. On your phone or PC, scan the QR code or open the address shown, for example `http://192.168.1.20:4264`.
|
||||
Pick a controller, then choose your user on the TV with the D-pad and Cross.
|
||||
|
||||
```ini
|
||||
[AutoRun]
|
||||
/user/data/payloads/control4free.elf = 1
|
||||
```
|
||||
New to GoldHEN packages? The **[installation guide](docs/installation.md)** walks through every step.
|
||||
|
||||
When you update Control4Free this way, replace `/data/payloads/control4free.elf`.
|
||||
## Screenshots
|
||||
|
||||
**Upgrading from the original test payload:** use **Stop Control4Free** in the
|
||||
controller page's menu, or restart the PS4. That old version has no launcher API,
|
||||
so the app will not start a second copy over it.
|
||||
| Home screen on a phone | On a PC | The app on the PS4 |
|
||||
|---|---|---|
|
||||
|  |  |  |
|
||||
|
||||
**Rest mode:** the service closes stale connections and rebuilds its
|
||||
listener after socket failures or a long pause. Queued input is discarded, and
|
||||
disconnected controllers report neutral input. Reopen the controller page and
|
||||
select your controller after waking; an unused controller is removed after the
|
||||
reconnect grace period. This can recover a surviving payload's network service;
|
||||
it cannot revive a host process that the console terminated or stopped running.
|
||||
When upgrading a stuck older instance, restart the PS4 and load GoldHEN again.
|
||||
## Documentation
|
||||
|
||||
**Kernel logs:** controller sign-in is detected in the PS4's kernel log, which
|
||||
has a single reader. Control4Free opens `/dev/klog` only while a controller is
|
||||
waiting for sign-in and releases it afterwards, so GoldHEN's klog server works
|
||||
the rest of the time. It does not use GoldHEN's klog stream: GoldHEN serves one
|
||||
client at a time and, after one leaves, can go minutes without serving the next.
|
||||
If a klog viewer is connected to GoldHEN when you add a controller, the page
|
||||
says so; close the viewer and try again.
|
||||
- **[Installation](docs/installation.md)**: requirements, installing, auto-start, updating, uninstalling.
|
||||
- **[Playing](docs/playing.md)**: signing in, touch, keyboard and gamepads, the layout editor, settings.
|
||||
- **[Troubleshooting](docs/troubleshooting.md)**: the messages you might see and what to do about each.
|
||||
- **[How it works](docs/how-it-works.md)**: the virtual-device API, sign-in, and what runs where.
|
||||
- **[Protocol](docs/protocol.md)**: the WebSocket protocol between the page and the console.
|
||||
- **[Security](SECURITY.md)**: what being open on your home network does and doesn't mean.
|
||||
- **[Contributing](CONTRIBUTING.md)**: building, testing and code style.
|
||||
- **[Changelog](CHANGELOG.md)**.
|
||||
|
||||
Diagnostics are saved at `/data/control4free/control4free.log`, with elapsed timestamps
|
||||
such as `[c4f] [+00:01:23.456]`; the PS4's calendar setting is not used. Starting
|
||||
a new instance preserves the last run in `control4free.log.previous`. A heartbeat every
|
||||
minute and explicit network recovery messages help locate any remaining hang.
|
||||
## Requirements and compatibility
|
||||
|
||||
## Use
|
||||
- A PS4 with GoldHEN. Tested on firmware 10.01 with GoldHEN v2.4b18.10. Auto-start and starting Control4Free
|
||||
from GoldHEN's menu need v2.4b18.10 or later.
|
||||
- A phone, tablet or computer with a browser, on the same network as the PS4.
|
||||
- Turn off other GoldHEN controller plugins in games you play with Control4Free. A plugin that takes over a
|
||||
signed-in user's controller can stop games from reading the virtual ones.
|
||||
- A Remote Play session (chiaki-ng, for example) works alongside Control4Free.
|
||||
|
||||
1. Stop any older Control4Free payload before loading another. They all use port
|
||||
4264.
|
||||
2. Start Control4Free: from GoldHEN (above), from the launcher app, or by sending
|
||||
`build/control4free.elf` from a PC to GoldHEN's PayLoader on port 9090 with a
|
||||
payload sender. The payload serves its own page; no PC web server is needed.
|
||||
3. Open `http://YOUR-PS4-IP:4264` in the phone or PC browser. Control4Free shows
|
||||
the exact address on the TV when it starts. On a phone you can add the page to
|
||||
the home screen (**Share → Add to Home Screen** on iOS, the browser menu on
|
||||
Android) and skip typing it next time.
|
||||
4. Select a free controller. This creates it and opens native PS4 user selection.
|
||||
Use Left/Right and Cross to select a user or follow the PS4's guest flow.
|
||||
**Do not press PS while the initial user-selection screen is open:** in the
|
||||
hardware experiment this cancelled selection. PS works after sign-in.
|
||||
5. Test the home screen and then a game before adding another controller.
|
||||
## Limitations
|
||||
|
||||
For an Xbox or other physical controller, connect it to the phone/PC, press a
|
||||
button so the browser detects it, then choose its virtual-controller slot under
|
||||
connected controllers. Sign it in through the same PS4 screen. Add and sign in
|
||||
controllers one at a time. Touch and keyboard can also operate the selected slot.
|
||||
- **No rumble, light bar or motion yet.** The PS4 does send rumble and light-bar changes to the virtual
|
||||
controllers; passing them to the phone is planned for 1.1.
|
||||
- **One controller is added at a time.** If two people pick at once, the second is asked to try again a
|
||||
moment later.
|
||||
- **Rest mode signs everyone out**, so controllers have to be picked again after the PS4 wakes up, just like
|
||||
real ones.
|
||||
- **No pairing or password.** Anyone on your network can take a free controller. Read [SECURITY.md](SECURITY.md)
|
||||
and keep port 4264 off the internet.
|
||||
|
||||
The corner menu provides local layout/keyboard preferences, **Disconnect
|
||||
controller**, and **Stop Control4Free**. PS and Share send actual controller
|
||||
buttons. The PS4 assigns users; there are no user settings on the page.
|
||||
## Support
|
||||
|
||||
Some browsers restrict Gamepad API access on an HTTP page. If the page reports
|
||||
that restriction, try opening a saved copy of `client/index.html` locally and
|
||||
entering `YOUR-PS4-IP:4264` in its connection settings. Browser support for local
|
||||
files varies. Keep the controller page in the foreground while playing.
|
||||
Found a bug? [Open an issue](https://github.com/MoHadiShibli/Control4Free/issues/new/choose); I maintain this
|
||||
project and read every report. If Control4Free is useful to you, you can help keep it going on
|
||||
[Ko-fi](https://ko-fi.com/mohadishibli).
|
||||
|
||||
## Troubleshooting
|
||||
## Credits
|
||||
|
||||
**"Signing a controller in needs the PS4's kernel log, and something else has
|
||||
it."** A klog viewer is connected to GoldHEN. Close it and select the controller
|
||||
again. Only controller sign-in needs the log, so this never interrupts play.
|
||||
- [seregonwar/SplashDown](https://github.com/seregonwar/SplashDown): Control4Free's virtual-device code is
|
||||
ported from its `psbutton.c`, the first working use of this API on a PS4.
|
||||
- [GoldHEN](https://github.com/GoldHEN/GoldHEN), its PayLoader and AutoRun, and the
|
||||
[GoldHEN Plugins SDK](https://github.com/GoldHEN/GoldHEN_Plugins_SDK).
|
||||
- [ps4-payload-sdk](https://github.com/ps4-payload-dev/sdk) by John Törnblom builds the payload, and the
|
||||
[OpenOrbis PS4 Toolchain](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain) builds the app and its package.
|
||||
- [Ghostcontrol](https://github.com/srbraboo/Ghostcontrol-PS5-USB-Controller-Patcher) was a research reference.
|
||||
- [Apollo Save Tool](https://github.com/bucanero/apollo-ps4) showed the package settings that file an app under
|
||||
Applications.
|
||||
- [jsmn](https://github.com/zserge/jsmn), Project Nayuki's
|
||||
[QR Code generator](https://github.com/nayuki/QR-Code-generator),
|
||||
[stb_truetype](https://github.com/nothings/stb) and the [Roboto](https://github.com/googlefonts/roboto) font.
|
||||
- The on-screen buttons are drawn after the
|
||||
[DualShock 4 layout diagram](https://commons.wikimedia.org/wiki/File:Dualshock_4_Layout.svg) by Tokyoship
|
||||
(CC BY 3.0).
|
||||
|
||||
**"Control4Free cannot add controllers until it is restarted."** The PS4 did not
|
||||
report a new device, so Control4Free stops adding more rather than leave devices
|
||||
behind that it can no longer address. Stop it in the app (Square, then Cross) and
|
||||
start it again with Cross, or restart the PS4.
|
||||
Full licence details are in [THIRD_PARTY.md](THIRD_PARTY.md).
|
||||
|
||||
**"Another controller is being connected; try again in a moment."** Controllers
|
||||
are created one at a time. Wait a second and select yours again; everyone already
|
||||
playing is unaffected.
|
||||
### Built with AI help
|
||||
|
||||
**The page does not load.** Check the address on the TV, that the phone or PC is on
|
||||
the same network, and that `http://` is used rather than `https://`. If the browser
|
||||
corrects it to a search, type the address with `http://` in front.
|
||||
Control4Free was built with the help of AI models, Claude by Anthropic and Codex by OpenAI, which worked on the
|
||||
code, tests and documentation with me. AI-written code can look right and still be wrong. Reviews are very
|
||||
welcome: if you spot AI slop (dead code, wrong assumptions, needless complexity), please
|
||||
[open an issue](https://github.com/MoHadiShibli/Control4Free/issues) or send a pull request.
|
||||
|
||||
**The browser says it cannot use controllers on this page.** Some browsers only
|
||||
allow the Gamepad API on secure pages. Touch and keyboard still work. For a
|
||||
physical pad, save a copy of the controller page to the device, open the saved
|
||||
file, and enter the console address in its connection box.
|
||||
## License
|
||||
|
||||
**A game ignores the virtual controller.** Turn off other GoldHEN controller
|
||||
plugins for that game. A plugin that takes over a signed-in user's controller can
|
||||
stop games from reading Control4Free's.
|
||||
|
||||
**Nothing happens after waking the PS4 from rest mode.** Rest mode signs everyone
|
||||
out, so controllers have to be selected again. Reopen the page and pick yours. If
|
||||
the page will not connect at all, start Control4Free again from the app.
|
||||
|
||||
**The app says Control4Free is not responding.** Wait a few seconds after waking,
|
||||
then press Cross again. If it stays stuck, restart the PS4 and run the jailbreak.
|
||||
|
||||
The payload's own log is at `/data/control4free/control4free.log` on the console,
|
||||
readable over GoldHEN's FTP server, with the previous run kept beside it.
|
||||
|
||||
## Connection behavior
|
||||
|
||||
- Opening the page does not create a controller. Creation follows an explicit
|
||||
controller selection; the payload never presses sign-in buttons automatically.
|
||||
- Each virtual slot has one browser owner. Multiple local inputs may share that
|
||||
browser's selected slot, or use separate slots.
|
||||
- A lost connection releases held buttons. After 3 seconds without updates,
|
||||
inputs become neutral; after 15 seconds the controller is removed. A short
|
||||
disconnect leaves a 15-second window to select that controller again.
|
||||
- Explicit disconnect removes the controller immediately. Stopping the payload
|
||||
removes all controllers and restores the saved host credentials. Browser Stop
|
||||
refuses while another browser owns a controller. The launcher's confirmed Stop
|
||||
can disconnect all controllers through its launcher API, which websites cannot use.
|
||||
- If the kernel log is not available yet when Control4Free starts (AutoRun can
|
||||
start it early), it connects to it when the first controller is created.
|
||||
- There is no time limit. Rebooting the console ends it; AutoRun, the launcher or
|
||||
a PC starts it again.
|
||||
|
||||
## Build
|
||||
|
||||
Everything builds in Docker, so nothing has to be installed on the machine. CI
|
||||
(`.github/workflows/build.yml`) runs exactly these steps and attaches the payload,
|
||||
the package and their SHA256 sums to each tagged release.
|
||||
|
||||
The [ps4-payload-sdk](https://github.com/ps4-payload-dev/sdk) toolchain is pinned
|
||||
in the Docker image:
|
||||
|
||||
```sh
|
||||
docker build -t control4free-build docker/
|
||||
docker run --rm -v "$PWD:/src" -w /src control4free-build make
|
||||
```
|
||||
|
||||
Output: `build/control4free.elf`, with the compressed page embedded. The version
|
||||
comes from the `VERSION` file at the top of the repository.
|
||||
|
||||
To build the installable launcher after the payload image is available:
|
||||
|
||||
```sh
|
||||
docker build -t control4free-launcher-build -f docker/Dockerfile.launcher docker/
|
||||
docker run --rm -v "$PWD:/src" -w /src control4free-launcher-build bash -lc 'make && make -C launcher'
|
||||
```
|
||||
|
||||
The host test suites build the real sources with the PS4 calls stubbed, so they
|
||||
need no console:
|
||||
|
||||
```sh
|
||||
docker run --rm --network none -v "$PWD:/src" -w /src control4free-launcher-build python3 -B tests/run.py
|
||||
```
|
||||
|
||||
Output: `build/Control4Free-<version>.pkg` (title ID `CFRE00001`). On Windows,
|
||||
`tools/build-launcher.ps1` runs both image builds and the package build. It does
|
||||
not send anything to a console. Packaging uses OpenOrbis and LibOrbisPkg. The
|
||||
launcher draws its screen and its icon in software, in the controller page's style.
|
||||
|
||||
## Current limits
|
||||
|
||||
Rumble, lightbar feedback and motion input are not implemented. Multiple native
|
||||
user sessions are unverified. User-assignment status comes from kernel-log events
|
||||
and may lag or miss an event; check the TV.
|
||||
Logs are written to `/data/control4free/control4free.log` on the console.
|
||||
|
||||
## Credits and license
|
||||
|
||||
The VDA code is ported from [seregonwar/SplashDown](https://github.com/seregonwar/SplashDown).
|
||||
The build uses John Törnblom's [ps4-payload-sdk](https://github.com/ps4-payload-dev/sdk)
|
||||
and the [OpenOrbis PS4 Toolchain](https://github.com/OpenOrbis/OpenOrbis-PS4-Toolchain).
|
||||
[Ghostcontrol](https://github.com/srbraboo/Ghostcontrol-PS5-USB-Controller-Patcher)
|
||||
was a research reference. JSON parsing uses [jsmn](https://github.com/zserge/jsmn);
|
||||
the launcher uses [QR Code generator](https://github.com/nayuki/QR-Code-generator),
|
||||
[stb_truetype](https://github.com/nothings/stb) and the
|
||||
[Roboto](https://github.com/googlefonts/roboto) font.
|
||||
|
||||
GPL-3.0; see [LICENSE](LICENSE) and [THIRD_PARTY.md](THIRD_PARTY.md).
|
||||
Control4Free is free software under the [GNU General Public License v3](LICENSE).
|
||||
Copyright © 2026 MoHadiShibli.
|
||||
@@ -31,6 +31,13 @@
|
||||
`Roboto-Light.ttf` `a08729d794801eaa158c53b1558f4cc351c3b1791eb3d22faf7058b2b7df9c8c`,
|
||||
`Roboto-Regular.ttf` `f3edb8058e523f5612bfd99d0745e661568ad85e1b6217bc62f786fabae624c6`.
|
||||
|
||||
- The on-screen DualShock 4 buttons in `client/index.html` are drawn after the
|
||||
[DualShock 4 layout diagram](https://commons.wikimedia.org/wiki/File:Dualshock_4_Layout.svg)
|
||||
by Tokyoship, licensed under CC BY 3.0
|
||||
(https://creativecommons.org/licenses/by/3.0/).
|
||||
- The package settings that file the app under Applications (`CATEGORY`,
|
||||
`ATTRIBUTE`) follow Apollo Save Tool (https://github.com/bucanero/apollo-ps4).
|
||||
|
||||
These license notices are also included inside the package.
|
||||
|
||||
The HTTP/WebSocket transport in `src/net.c` is a new implementation.
|
||||
+2
-2
@@ -1192,10 +1192,10 @@
|
||||
set(key, value) { try { localStorage.setItem('c4f.' + key, JSON.stringify(value)); } catch (e) { /* private mode */ } },
|
||||
};
|
||||
|
||||
// Settings of this device (the console's settings live on the console)
|
||||
// Settings of this device. They stay in this browser.
|
||||
const DEVICE_SETTINGS = [
|
||||
['keepAwake', 'Keep the screen on', 'While this page drives a controller, the screen doesn\'t turn off.', true],
|
||||
['haptics', 'Vibration', 'Buzz this device when you press a button or the game rumbles (Android).', 'vibrate' in navigator],
|
||||
['haptics', 'Vibration', 'Buzz this device when you press a button (Android).', 'vibrate' in navigator],
|
||||
['floating', 'Floating sticks', 'A stick starts wherever your thumb lands inside its area.', false],
|
||||
['tapClick', 'Tap the touchpad to click', 'A quick tap presses the touchpad button. Hold still to keep it pressed.', true],
|
||||
['stickClick', 'Double-tap a stick for L3 / R3', 'Tap a stick, then touch it again right away to press it in. Hold the second touch to keep it pressed. Off: L3 and R3 get their own buttons.', true],
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
# How it works
|
||||
|
||||
```
|
||||
phone / PC browser PS4
|
||||
┌──────────────────┐ WebSocket ┌───────────────────────────────────────────────┐
|
||||
│ controller page │ ────────────► │ Control4Free service (GoldHEN payload, :4264) │
|
||||
│ touch · keys · │ JSON, LAN │ ├─ scePadVirtualDevice* ─► virtual DS4 ×4 │
|
||||
│ gamepads │ ◄──────────── │ └─ /dev/klog (only while one signs in) │
|
||||
└──────────────────┘ status │ │
|
||||
│ Control4Free app (home screen) │
|
||||
│ └─ /api/status · /api/stop · AutoRun setup │
|
||||
└───────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Why a payload, not a plugin
|
||||
|
||||
GoldHEN loads plugins into game processes only. The home screen and the sign-in screen belong to the system's
|
||||
own processes, which never load plugins, so a plugin can't reach them. Control4Free is a payload instead, and
|
||||
uses Sony's virtual-device API: the one Remote Play goes through. A device made with it is a real MBus device,
|
||||
and the whole system sees it like a DualShock 4 plugged in over USB.
|
||||
|
||||
## Where it runs
|
||||
|
||||
GoldHEN's PayLoader doesn't start a process of its own: it runs the ELF inside an existing system process
|
||||
(ScePartyDaemon on the development console). Control4Free:
|
||||
|
||||
1. takes an exclusive lock on `/data/control4free/instance.lock`, so a second copy (AutoRun plus the app, say)
|
||||
leaves without touching anything;
|
||||
2. saves the host process's credentials, then raises them to the authority the virtual-device calls need. It
|
||||
restores exactly what it saved before it exits, and refuses to change them at all if it couldn't save them;
|
||||
3. loads `libSceMbus` with `dlopen` before the first pad call. `libScePad`'s imports from it are unresolved in a
|
||||
payload, and calling into `libScePad` first can kill the process;
|
||||
4. calls `scePadInit()`, then `scePadSetProcessPrivilege(1)`, in that order: the other way round the privilege
|
||||
call fails with *not initialised*;
|
||||
5. serves the page and the WebSocket on port 4264 until it's stopped.
|
||||
|
||||
## Creating a controller
|
||||
|
||||
`scePadVirtualDeviceAddDevice` returns a status, not a handle; on this console it returns `0x803b0006` even
|
||||
when the device is created. The handle is the device's MBus *DeviceId*, and the only place a payload can learn
|
||||
it is the kernel log, where the login manager announces every new device:
|
||||
|
||||
```
|
||||
#LOGIN MGR# Receive Event : SCE_MBUS_EVENT_DEVICE_ADDED [DeviceId:0x7030d][type:1][subType:2]
|
||||
```
|
||||
|
||||
So the service reads `/dev/klog` while it adds a device and accepts only that line, `subType:2` being the
|
||||
Remote Play pad. Anything looser could hand it the DeviceId of a real controller plugged in at the same moment.
|
||||
If no line arrives, it stops creating controllers until it's restarted, rather than leave devices behind that
|
||||
it can no longer address.
|
||||
|
||||
The device is created for user 1, so it arrives with no user, and the PS4 asks *"Who's using this
|
||||
controller?"*. The service never presses anything itself: the choice is made at the TV. When a user is picked,
|
||||
the login manager logs `DEVICE_OWNER_CHANGED [DeviceId:…][UserId:…]`, which is how the page learns the
|
||||
controller is signed in.
|
||||
|
||||
None of this blocks. Creating a controller is a small state machine the main loop advances: drain the log's
|
||||
backlog, write a marker and wait for it to come back (proof that the reader really delivers), call
|
||||
`AddDevice`, wait for the line. The other players keep playing and keep being answered meanwhile.
|
||||
|
||||
## The kernel log
|
||||
|
||||
`/dev/klog` has a single reader. GoldHEN's log server on port 3232 opens it only while it has a client, serves
|
||||
one client at a time, and can go minutes without serving the next one after a client leaves. So the service
|
||||
never uses the log server: it opens the device itself, only while a controller is signing in, and closes it
|
||||
once every controller has a user, leaving the log to GoldHEN for the rest of the session.
|
||||
|
||||
## Input
|
||||
|
||||
The page sends the whole controller state as a compact array (see [protocol.md](protocol.md)). The service
|
||||
turns it into a `ScePadData` sample and hands it to `scePadVirtualDeviceInsertData`:
|
||||
|
||||
- as soon as it arrives, unless a report went out in the last 4 ms;
|
||||
- every 4 ms while input changed in the last half second, as fast as a real DualShock 4 reports;
|
||||
- every 16 ms otherwise, to keep the controller alive;
|
||||
- each sample carries a rising timestamp and counter, as a real pad's do;
|
||||
- a short queue keeps every button press and release, so a quick tap can't fall between two reports, while
|
||||
stick movement only ever keeps the latest position.
|
||||
|
||||
When a page stops sending, its buttons are released at once; after 3 seconds the controller goes neutral, and
|
||||
after 15 it's removed. A page that reconnects within 15 seconds gets its controller back.
|
||||
|
||||
## Rest mode and failures
|
||||
|
||||
The main loop watches both the monotonic clock and the wall clock. A gap of more than 5 seconds means the
|
||||
console slept: the service closes every connection, releases the kernel log, rebuilds its listening socket,
|
||||
and carries on. The same happens on a network failure. Users are signed out by rest mode anyway, so pages pick
|
||||
their controllers again.
|
||||
|
||||
## The app
|
||||
|
||||
The app on the home screen is an OpenOrbis homebrew application that draws its own screen in software, in the
|
||||
same style as the page. Before it does anything else it leaves the application sandbox through GoldHEN's SDK
|
||||
call (syscall 500, command 2), because the sandbox refuses connections to the console itself, including
|
||||
`127.0.0.1`. Then it:
|
||||
|
||||
- checks the service with `GET /api/status` every 2 seconds, and stops it with `POST /api/stop`;
|
||||
- starts it by sending its own bundled copy of the payload to GoldHEN's PayLoader on port 9090;
|
||||
- sets up auto-start by copying that payload to `/data/payloads/control4free.elf` and adding one line to the
|
||||
`[AutoRun]` section of `/data/GoldHEN/payloads.ini`, written to a temporary file and renamed over the
|
||||
original so a power cut can't leave it half written.
|
||||
|
||||
## Security
|
||||
|
||||
Being open on the home network is deliberate. What that does and doesn't protect is in
|
||||
[SECURITY.md](../SECURITY.md).
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.3 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 535 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.1 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 840 KiB |
@@ -0,0 +1,96 @@
|
||||
# Installation
|
||||
|
||||
Control4Free comes in two pieces:
|
||||
|
||||
- **The service** (`control4free.elf`), a GoldHEN payload that runs in the background, creates the virtual
|
||||
controllers, and serves the controller page on port 4264.
|
||||
- **The app** (`Control4Free-<version>.pkg`), which goes on the PS4's home screen. It starts and stops the
|
||||
service, shows the address and a QR code, and sets up auto-start. The package carries its own copy of the
|
||||
service, so the package is all you need.
|
||||
|
||||
## Requirements
|
||||
|
||||
- A PS4 with [GoldHEN](https://github.com/GoldHEN/GoldHEN). Tested on firmware 10.01 with GoldHEN v2.4b18.10.
|
||||
Auto-start and GoldHEN's Payloader LaunchPad need v2.4b18.10 or later.
|
||||
- **Debug Settings** turned on in GoldHEN, for the Package Installer.
|
||||
- A phone, tablet or computer with a modern browser, on the same network as the PS4.
|
||||
|
||||
## Install the app
|
||||
|
||||
1. Download `Control4Free-<version>.pkg` from the
|
||||
[latest release](https://github.com/MoHadiShibli/Control4Free/releases/latest).
|
||||
2. Get it onto the PS4, either way:
|
||||
- copy it to a USB drive formatted exFAT or FAT32 and plug it in; or
|
||||
- turn on GoldHEN's FTP server and upload it to `/data/pkg/`.
|
||||
3. On the PS4, open **Settings → Debug Settings → Game → Package Installer**, choose the USB drive or the
|
||||
internal storage, and install **Control4Free**.
|
||||
|
||||
The app appears in **Library → Applications**.
|
||||
|
||||
## First start
|
||||
|
||||
Open **Control4Free** and press **Cross**. The app:
|
||||
|
||||
1. copies the service to `/data/payloads/control4free.elf`;
|
||||
2. adds it to GoldHEN's AutoRun list, `/data/GoldHEN/payloads.ini`, so GoldHEN starts it every time it
|
||||
loads;
|
||||
3. starts it straight away if GoldHEN's **PayLoader** is on. If it isn't, restart the PS4 and run the
|
||||
jailbreak: AutoRun starts Control4Free from then on.
|
||||
|
||||
Once it's running, the app shows the address to open, for example `http://192.168.1.20:4264`, and a QR code
|
||||
for your phone's camera. The PS4 also shows the address in a notification when the service starts.
|
||||
|
||||
AutoRun keeps other entries in `payloads.ini` as they are.
|
||||
|
||||
## The app's buttons
|
||||
|
||||
| Button | What it does |
|
||||
|---|---|
|
||||
| **Cross** | Starts Control4Free when it isn't running. Until auto-start is on, it sets that up first. |
|
||||
| **Triangle** | Turns auto-start on or off. After you install a newer package, it updates the auto-start copy. |
|
||||
| **Square**, then **Cross** | Stops Control4Free and disconnects every controller. |
|
||||
| **Circle** | Closes the app. Control4Free keeps running without it. |
|
||||
|
||||
## Updating
|
||||
|
||||
1. Install the new package over the old one. Delete the old app first if the installer refuses.
|
||||
2. Open Control4Free. It says the auto-start copy is older than the one in the app: press **Triangle** to
|
||||
update it.
|
||||
3. The old version is still the one running. Press **Square**, then **Cross** to stop it, and **Cross**
|
||||
again to start the new one (this needs GoldHEN's PayLoader). Or restart the PS4 and run the jailbreak.
|
||||
|
||||
Your controller layouts and keys live in each phone's browser, so they survive updates.
|
||||
|
||||
## Without the app
|
||||
|
||||
The service is an ordinary GoldHEN payload, so the app is optional.
|
||||
|
||||
**With GoldHEN's Payloader LaunchPad** (under **Utilities** in GoldHEN's menu):
|
||||
|
||||
1. Put `control4free.elf` from the release in `/data/payloads/` on the PS4, for example over GoldHEN's FTP
|
||||
server (port 2121).
|
||||
2. In the LaunchPad, select `control4free.elf` to start it once, or press **Square** on it to add it to the
|
||||
AutoRun queue. The queue lives in `/data/GoldHEN/payloads.ini`:
|
||||
|
||||
```ini
|
||||
[AutoRun]
|
||||
/user/data/payloads/control4free.elf = 1
|
||||
```
|
||||
|
||||
**From a computer**: send `control4free.elf` to GoldHEN's PayLoader on port 9090 with any payload sender.
|
||||
It runs until the PS4 restarts.
|
||||
|
||||
## Uninstalling
|
||||
|
||||
1. In the app, press **Triangle** to turn auto-start off, then **Square** and **Cross** to stop the service.
|
||||
2. Delete the app from the Library.
|
||||
3. Delete `/data/payloads/control4free.elf`, and `/data/control4free/` if you want the logs gone too.
|
||||
|
||||
## Files on the console
|
||||
|
||||
| Path | What it is |
|
||||
|---|---|
|
||||
| `/data/payloads/control4free.elf` | The copy of the service that AutoRun starts. |
|
||||
| `/data/GoldHEN/payloads.ini` | GoldHEN's AutoRun list. The app adds or removes one line. |
|
||||
| `/data/control4free/control4free.log` | The service's log, with the previous run in `control4free.log.previous`. |
|
||||
| `/data/control4free/instance.lock` | Keeps a second copy from starting next to the first. |
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
# Playing
|
||||
|
||||
## Connect a controller
|
||||
|
||||
1. Make sure Control4Free is running: the app on the PS4 says **Running** and shows the address.
|
||||
2. On a phone, tablet or computer on the same network, open that address, for example
|
||||
`http://192.168.1.20:4264`, or scan the app's QR code.
|
||||
3. Pick a free controller. The PS4 shows its *"Who's using this controller?"* screen.
|
||||
4. Choose a user with the D-pad (**Left** and **Right**) and press **Cross**, or follow the guest option.
|
||||
|
||||
Connect one controller at a time, and finish the user screen before the next person picks theirs. **Don't
|
||||
press PS while that screen is up**: it cancels the choice. PS works normally once you're signed in.
|
||||
|
||||
The home screen shows all four controllers live:
|
||||
|
||||
| State | Meaning |
|
||||
|---|---|
|
||||
| **Free** | Nobody is using it. Pick it to play. |
|
||||
| **Connecting** | The PS4 is setting it up. Takes a moment. |
|
||||
| **Choose user on TV** | Waiting for someone to pick a user on the PS4's screen. |
|
||||
| **Connected** | Signed in and ready. |
|
||||
| **Paused** | Its phone stopped sending input, for example because the screen locked. |
|
||||
| **This device** / **+1 device** | Who's driving it: this browser, or other ones. |
|
||||
|
||||
## Touch controls
|
||||
|
||||
The controller screen is a DualShock 4 laid out for your screen, best held sideways:
|
||||
|
||||
- **Sticks** follow your thumb. With **Floating sticks** on, a stick starts wherever your thumb lands.
|
||||
- **The touchpad** takes one or two fingers. A quick tap clicks it; hold still to keep it pressed.
|
||||
- **L3 and R3**: tap a stick, then touch it again straight away to press it in. If you'd rather have separate
|
||||
buttons, turn off **Double-tap a stick for L3 / R3**.
|
||||
- **PS, Share and Options** are real controller buttons, so PS takes you to the PS4's home screen.
|
||||
|
||||
## Your own layout
|
||||
|
||||
Open the menu (**☰**) and choose **Edit the button layout**. Drag a button to move it, change its size, or
|
||||
hide it. **Reset to default** puts everything back; tap it twice, so a stray tap can't throw a layout away. Each device keeps its own layout, separately for portrait
|
||||
and landscape.
|
||||
|
||||
## Keyboard
|
||||
|
||||
On a computer the keyboard drives the controller open on the page. Change any key in **All settings →
|
||||
Keyboard**, and turn on **Show keyboard keys** to see them on the buttons.
|
||||
|
||||
| Controller | Key | Controller | Key |
|
||||
|---|---|---|---|
|
||||
| Cross | Space | L1 / R1 | Q / E |
|
||||
| Circle | Esc | L2 / R2 | 1 / 3 |
|
||||
| Square | Backspace | L3 / R3 | Z / C |
|
||||
| Triangle | Enter | Options | O |
|
||||
| D-pad | I J K L | Share | V |
|
||||
| Left stick | W A S D | PS | P |
|
||||
| Right stick | Arrow keys | Touchpad click | U |
|
||||
| Touchpad corners | T Y G H | | |
|
||||
|
||||
## Gamepads
|
||||
|
||||
Connect an Xbox, DualSense, Switch Pro or other gamepad to the phone or computer and press a button on it. It
|
||||
appears under **Gamepads on this device**:
|
||||
|
||||
- a gamepad that turns up while a controller is open on the page drives that controller;
|
||||
- otherwise, pick the controller each gamepad plays as. One computer can run all four controllers this way;
|
||||
- a row lights up while you use its gamepad, so you can tell which is which.
|
||||
|
||||
Keep the page's window in front: browsers only read gamepads for the active window.
|
||||
|
||||
**"This browser only allows gamepads on secure pages."** Some browsers only offer gamepads on `https`
|
||||
pages, and the console's page is plain `http`. Save the page to the device (**Save page as** on a computer),
|
||||
open the saved file, and enter the console's address in its connection box. Touch and the keyboard work either
|
||||
way.
|
||||
|
||||
## Settings
|
||||
|
||||
**☰ → All settings** has this device's settings. They stay in this browser.
|
||||
|
||||
| Setting | What it does |
|
||||
|---|---|
|
||||
| **Keep the screen on** | The phone's screen doesn't turn off while the page drives a controller. |
|
||||
| **Vibration** | The phone buzzes briefly when you press a button (Android). |
|
||||
| **Floating sticks** | A stick starts wherever your thumb lands in its area. |
|
||||
| **Tap the touchpad to click** | A quick tap presses the touchpad button. |
|
||||
| **Double-tap a stick for L3 / R3** | Off: L3 and R3 get their own buttons. |
|
||||
| **Show keyboard keys** | Labels the buttons with their keyboard keys. |
|
||||
|
||||
The bottom of the home screen shows the version, and the round trip to the console in milliseconds: a quick
|
||||
way to see whether your Wi-Fi is the slow part.
|
||||
|
||||
## On your phone's home screen
|
||||
|
||||
Add the page to your home screen and it opens like an app:
|
||||
|
||||
- **iPhone and iPad (Safari)**: **Share → Add to Home Screen**. It opens without Safari's bars.
|
||||
- **Android (Chrome)**: **⋮ → Add to Home screen**. Because the page is plain `http`, it opens in a Chrome
|
||||
tab.
|
||||
|
||||
The icon only works while Control4Free is running, and it remembers the PS4's address: reserve that address
|
||||
for the PS4 in your router so it doesn't change.
|
||||
|
||||
**Full screen**: the **⛶** button in the corner. Safari on iPhone doesn't let pages go full screen, so there
|
||||
it explains how to use the home screen instead.
|
||||
|
||||
## Leaving
|
||||
|
||||
- **☰ → Disconnect controller** removes it from the PS4 straight away.
|
||||
- **Closing the page or locking the phone** releases its buttons at once. After 3 seconds without input the
|
||||
controller goes idle, and after 15 seconds it's removed. Come back within 15 seconds and you keep it.
|
||||
- **☰ → Stop Control4Free** stops the service and disconnects everyone. It's refused while someone else is
|
||||
still playing; the app on the PS4 can always stop it.
|
||||
|
||||
## Rest mode
|
||||
|
||||
Rest mode signs every user out, so controllers have to be picked again after the PS4 wakes up, as real ones
|
||||
do. Control4Free itself carries on: reopen the page and pick your controller.
|
||||
@@ -0,0 +1,148 @@
|
||||
# Protocol
|
||||
|
||||
The service listens on TCP port 4264 and speaks plain HTTP/1.1 and WebSocket (RFC 6455). This page is for
|
||||
anyone writing their own client or checking what the page does.
|
||||
|
||||
## HTTP
|
||||
|
||||
| Request | Answer |
|
||||
|---|---|
|
||||
| `GET /`, `GET /index.html`, `GET /?…` | The controller page, gzip-compressed, with a Content-Security-Policy. |
|
||||
| `GET /manifest.webmanifest` | The web app manifest, for a home-screen shortcut. |
|
||||
| `GET /icon-192.png` | The app icon. |
|
||||
| `GET /ws` with `Upgrade: websocket` | The control socket, below. |
|
||||
| `GET /api/status`, `POST /api/stop` | The launcher API, below. |
|
||||
| Anything else | `404`. |
|
||||
|
||||
Every request needs a `Host` header that is a literal IPv4 address or `localhost`, with or without a port;
|
||||
anything else gets `403`. This keeps out domain names that resolve to the console (DNS rebinding).
|
||||
|
||||
## The control socket
|
||||
|
||||
`GET /ws` upgrades to a WebSocket. The handshake is refused with `403` when it carries an `Origin` other than
|
||||
`http://<the Host header>` or `null`; no `Origin` at all is accepted. Messages are UTF-8 JSON text frames of at
|
||||
most 4096 bytes; a longer frame closes the connection. Fragmented messages and ping/pong control frames are
|
||||
handled.
|
||||
|
||||
### Requests
|
||||
|
||||
```json
|
||||
{"id": 7, "method": "claim", "params": [0]}
|
||||
```
|
||||
|
||||
- `method`: a string.
|
||||
- `params`: an array of up to 16 integers; required, even if empty.
|
||||
- `id`: optional, an integer from 0 to 2^53−1. Requests with an `id` get a reply carrying the same `id`.
|
||||
- `jsonrpc: "2.0"` is allowed and ignored. Any other key, or anything that isn't this shape, is answered with
|
||||
an `Invalid request` error.
|
||||
|
||||
Controllers are numbered 0 to 3 in the protocol and 1 to 4 on screen.
|
||||
|
||||
| Method | Params | Reply |
|
||||
|---|---|---|
|
||||
| `info` | `[]` | `{"version": "1.0.0", "protocol": 2, "pads": 4}` |
|
||||
| `status` | `[]` | A status object, below. |
|
||||
| `claim` | the controllers this connection wants, for example `[0]` or `[0, 2]` | A status object, once every controller in the claim exists. |
|
||||
| `u` | input, below | None. |
|
||||
| `leave` | `[pad]` | A status object. |
|
||||
| `stop` | `[]` | None; the service stops and the connection closes. |
|
||||
| `ping` | `[]` | `{}` |
|
||||
|
||||
**`claim`** is the whole set this connection wants: controllers missing from it that it owned are removed,
|
||||
and free ones in it are created. A claim that needs a new controller is answered when the controller exists,
|
||||
usually within a second; the service keeps serving every other connection in the meantime. It's refused,
|
||||
changing nothing, if any controller in it belongs to another connection (`409`), if another controller is
|
||||
being created at that moment (`409`), or if controllers can't be created (`503`).
|
||||
|
||||
**`u`** sends the complete state of one controller this connection owns. Input for any other controller is
|
||||
ignored: input never claims a controller.
|
||||
|
||||
```
|
||||
[pad, buttons, lx, ly, rx, ry, l2, r2, fingers, (id, x, y) × fingers]
|
||||
```
|
||||
|
||||
| Field | Range |
|
||||
|---|---|
|
||||
| `buttons` | a bit mask, below |
|
||||
| `lx`, `ly`, `rx`, `ry` | 0 to 255, 128 is centred; up and left are 0 |
|
||||
| `l2`, `r2` | 0 to 255 |
|
||||
| `fingers` | 0 to 2 touches on the touchpad |
|
||||
| touch `id` | 0 to 127, stable while the finger stays down |
|
||||
| touch `x`, `y` | 0 to 1919, 0 to 941 |
|
||||
|
||||
| Button | Bit | Button | Bit |
|
||||
|---|---|---|---|
|
||||
| Share | `0x0001` | L2 | `0x0100` |
|
||||
| L3 | `0x0002` | R2 | `0x0200` |
|
||||
| R3 | `0x0004` | L1 | `0x0400` |
|
||||
| Options | `0x0008` | R1 | `0x0800` |
|
||||
| D-pad up | `0x0010` | Triangle | `0x1000` |
|
||||
| D-pad right | `0x0020` | Circle | `0x2000` |
|
||||
| D-pad down | `0x0040` | Cross | `0x4000` |
|
||||
| D-pad left | `0x0080` | Square | `0x8000` |
|
||||
| PS | `0x10000` | Touchpad click | `0x100000` |
|
||||
|
||||
Send the state whenever it changes, and at least once a second while the controller is in use. After 3
|
||||
seconds without input the controller goes neutral and shows as `paused`; after 15 it's removed. When a
|
||||
connection closes, its controllers' buttons are released at once, and they're removed 15 seconds later unless
|
||||
a connection claims them again; the page does so when it reconnects.
|
||||
|
||||
**`stop`** is refused with `409` while another connection owns a controller.
|
||||
|
||||
### The status object
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"protocol": 2,
|
||||
"pads": [
|
||||
{"pad": 0, "name": "Controller 1", "enabled": true, "open": true, "connected": true, "clients": 1,
|
||||
"mine": true, "state": "ready", "uid": "1a2b3c4d", "color": [32, 96, 255], "reports": 5120, "error": 0}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Meaning |
|
||||
|---|---|
|
||||
| `open` | The virtual controller exists. |
|
||||
| `connected` | A connection owns it and is sending input. |
|
||||
| `clients` | 1 if a connection owns it, else 0. |
|
||||
| `mine` | This connection owns it. |
|
||||
| `state` | `free`, `connecting`, `select` (waiting for a user on the TV), `ready` (signed in), `paused` (no input lately, or nobody connected), or `error` (the console refused input). |
|
||||
| `uid` | The signed-in user's ID in hex, or `unassigned-…` before sign-in. |
|
||||
| `color` | The controller's colour on the page. |
|
||||
| `reports` | Samples given to the console so far. |
|
||||
| `error` | The console's error code for the last refused sample, or 0. |
|
||||
|
||||
### Messages from the service
|
||||
|
||||
- `{"method": "s", "params": <status>}`: the status, whenever something changes and at least once a second.
|
||||
- `{"method": "error", "params": {"message": "…"}}`: a problem that isn't the reply to a request: a controller
|
||||
removed for sitting unused, a request that couldn't be parsed, or an error for a request sent without an
|
||||
`id`.
|
||||
|
||||
Errors in reply to a request:
|
||||
|
||||
```json
|
||||
{"id": 7, "error": {"code": 409, "message": "Controller is in use on another device"}}
|
||||
```
|
||||
|
||||
| Code | Meaning |
|
||||
|---|---|
|
||||
| `400` | The request or its values are invalid. |
|
||||
| `404` | Unknown method: the page is newer than the service. |
|
||||
| `409` | Someone else owns it, another controller is being created, or it isn't yours to `leave`. |
|
||||
| `503` | Controllers can't be created now, the kernel log is busy, or the service is stopping. |
|
||||
|
||||
## The launcher API
|
||||
|
||||
For the app on the PS4. Both requests need the header `X-Control4Free-Launcher: 1`, and are refused with `403`
|
||||
if they carry an `Origin`, an `Upgrade`, a body or `Transfer-Encoding`. A browser only sends a custom header
|
||||
cross-origin after a CORS preflight, which the service never answers, so websites can't use these.
|
||||
|
||||
```
|
||||
GET /api/status → {"application": "Control4Free", "api": 1, "version": "1.0.0", "controllers": 2, "stopping": false}
|
||||
POST /api/stop → {"application": "Control4Free", "stopping": true}
|
||||
```
|
||||
|
||||
`/api/stop` removes every controller, even ones in use, and the service exits a quarter of a second later.
|
||||
@@ -0,0 +1,113 @@
|
||||
# Troubleshooting
|
||||
|
||||
Find the message you see, or the symptom, below. If nothing here helps,
|
||||
[open an issue](https://github.com/MoHadiShibli/Control4Free/issues/new/choose) with the log described at the
|
||||
end of this page.
|
||||
|
||||
## On the controller page
|
||||
|
||||
**"Signing a controller in needs the PS4's kernel log, and something else has it."**
|
||||
A klog viewer is connected to GoldHEN's log server (port 3232). Close it and pick the controller again. Only
|
||||
signing a controller in needs the log, so this never interrupts play.
|
||||
|
||||
**"Another controller is being connected; try again in a moment."**
|
||||
Controllers are added one at a time. Wait a second and pick yours again. Everyone already playing carries on
|
||||
undisturbed.
|
||||
|
||||
**"Control4Free cannot add controllers until it is restarted."** or
|
||||
**"The PS4 did not report the new controller."**
|
||||
The PS4 didn't confirm a new controller, so one may exist that Control4Free can't address. Rather than leave
|
||||
more of those behind, it stops adding controllers. Stop it in the app (**Square**, then **Cross**) and start
|
||||
it again with **Cross**, or restart the PS4.
|
||||
|
||||
**"Controller is in use on another device."**
|
||||
Someone else has that controller. Pick a free one. If it's yours from a phone that has gone, it frees itself
|
||||
15 seconds after that phone stopped sending input.
|
||||
|
||||
**"That controller was disconnected after sitting unused."**
|
||||
Nothing drove it for 15 seconds, for example because the phone's screen locked. Pick it again. To keep the
|
||||
phone awake while you play, leave **Keep the screen on** turned on in the settings.
|
||||
|
||||
**"Someone else is still using a controller."**
|
||||
The page's **Stop Control4Free** won't cut other players off. Ask them to disconnect, or stop it from the app
|
||||
on the PS4.
|
||||
|
||||
**"Control4Free on the PS4 is older than this page."**
|
||||
The page is newer than the service it's talking to, typically a saved copy of the page. Update the PS4 side:
|
||||
see [Updating](installation.md#updating).
|
||||
|
||||
## The page doesn't open
|
||||
|
||||
- Use the address the app or the notification shows, including `:4264`.
|
||||
- Type `http://`, not `https://`. If the browser turns the address into a search, put `http://` in front.
|
||||
- The phone or computer must be on the same network as the PS4: guest Wi-Fi networks often keep devices
|
||||
apart.
|
||||
- Check the app on the PS4 says **Running**. If it says **Not running**, press **Cross**.
|
||||
- If the address worked before and doesn't now, the router may have given the PS4 a new one. Reserving an
|
||||
address for the PS4 in the router's settings keeps it the same.
|
||||
|
||||
## Picking a user
|
||||
|
||||
**The *"Who's using this controller?"* screen doesn't appear.** Wait a few seconds after picking the
|
||||
controller. If it still doesn't, disconnect the controller from the page's menu and pick it again.
|
||||
|
||||
**The screen went away without a user.** PS cancels it. Pick the controller again and use only the D-pad and
|
||||
Cross until you're signed in.
|
||||
|
||||
**The page keeps saying "Choose user on TV" after signing in.** The page learns about sign-ins from the PS4's
|
||||
kernel log. If a klog viewer was connected at that moment, it missed it. Play on: the controller works either
|
||||
way.
|
||||
|
||||
## In games
|
||||
|
||||
**A game ignores the virtual controller.** Turn off other GoldHEN controller plugins for that game. A plugin
|
||||
that takes over a signed-in user's controller can stop games from reading Control4Free's.
|
||||
|
||||
**Buttons feel slow.** The number next to the version at the bottom of the home screen is the round trip to
|
||||
the console. Above about 30 ms, your Wi-Fi is the slow part: move closer to the router or use 5 GHz.
|
||||
|
||||
**No vibration from the game.** Rumble isn't passed to the phone or gamepad yet. It's planned for the next
|
||||
version.
|
||||
|
||||
## Gamepads
|
||||
|
||||
**"This browser only allows gamepads on secure pages."** Save the page to the device, open the saved file, and
|
||||
enter the console's address in its connection box. See [Gamepads](playing.md#gamepads).
|
||||
|
||||
**The buttons are mixed up.** The page says *"buttons may be mixed up"* when the browser doesn't recognise the
|
||||
gamepad's layout. Try another browser, or connect the gamepad another way (cable instead of Bluetooth, or the
|
||||
other way round).
|
||||
|
||||
**The gamepad does nothing.** Keep the page's window in front, press a button on the gamepad after the page
|
||||
has loaded, and check it has a controller under **Gamepads on this device**.
|
||||
|
||||
## In the app on the PS4
|
||||
|
||||
**"PayLoader did not answer."** Turn on GoldHEN's PayLoader and press **Cross** again, or restart the PS4 and
|
||||
run the jailbreak: with auto-start on, Control4Free starts by itself.
|
||||
|
||||
**"Control4Free is not responding."** Wait a few seconds after the PS4 wakes from rest mode, then press
|
||||
**Cross** again. If it stays stuck, restart the PS4 and run the jailbreak.
|
||||
|
||||
**"Sent, but Control4Free never answered."** or **"The transfer to PayLoader broke off."** Restart the PS4
|
||||
before trying again. The app won't send a second copy until then, so two can't end up running.
|
||||
|
||||
**"Something answers on port 4264 but not the way this app expects."** or **"No answer the app
|
||||
understands."** Something else is answering on port 4264, often an older version of Control4Free. Stop it
|
||||
from its own page, or restart the PS4.
|
||||
|
||||
**"The app cannot reach Control4Free."** The app couldn't connect to the service from its own sandbox. Close
|
||||
the app and open it again; if that doesn't help, restart the PS4 and run the jailbreak.
|
||||
|
||||
**"The bundled payload is damaged."** Reinstall the package.
|
||||
|
||||
**Control4Free is listed under Games instead of Applications.** That's a package from before 1.0.0. Delete the
|
||||
app and install the current package.
|
||||
|
||||
## The log
|
||||
|
||||
The service writes a log to `/data/control4free/control4free.log` on the PS4, and keeps the previous run in
|
||||
`control4free.log.previous`. Turn on GoldHEN's FTP server and download them from port 2121, or connect to
|
||||
GoldHEN's log server on port 3232 and look for lines starting with `[c4f]`.
|
||||
|
||||
The log contains your console's network address. Remove it before posting the log if you prefer.
|
||||
Reference in new issue
Block a user