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:
MoHadiShibli committed 2026-10-05 15:53:01 +03:00
1 parent 77a1bba68d
commit 5335bc09ce
20 files changed
+881 -226

No files matched your search

+1
View File
@@ -0,0 +1 @@
ko_fi: mohadishibli
+49
View File
@@ -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
+8
View File
@@ -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.
+6 -1
View File
@@ -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
View File
@@ -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.
+93
View File
@@ -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.
+103 -201
View File
@@ -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.
![The controller on a phone](docs/images/controller.png)
## 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 |
|---|---|---|
| ![Home screen on a phone](docs/images/home.png) | ![The page in a PC's browser](docs/images/desktop.png) | ![The Control4Free app on the PS4](docs/images/launcher.png) |
**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.
+7
View File
@@ -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
View File
@@ -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],
+106
View File
@@ -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

+96
View File
@@ -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
View File
@@ -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.
+148
View File
@@ -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.
+113
View File
@@ -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.