Skip to content

Commit 5adc74a

Browse files
committed
docs: Update README & misc other small adjustments
1 parent c469dc6 commit 5adc74a

10 files changed

Lines changed: 101 additions & 80 deletions

File tree

.github/workflows/pyinstaller.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ jobs:
6565

6666
- name: Create release zip
6767
run: |
68-
for DIR in *; do 7z a ../${DIR}-${{ github.ref_name }}.zip *; done
68+
for DIR in *; do 7z a ../${DIR}-${{ github.ref_name }}.zip ${DIR}; done
6969
7070
- name: Create release
7171
uses: softprops/action-gh-release@v2

README.md

Lines changed: 65 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,16 @@
22

33
# Durango RF Unit I2C tooling
44

5+
[![GitHub Release](https://img.shields.io/github/v/release/xboxoneresearch/DuRFUnitI2C)](https://github.com/xboxoneresearch/DuRFUnitI2C/releases/latest)
6+
57
> [!WARNING]
68
> Use at your own risk!
79
10+
> [!INFO]
11+
> Grab the latest release @ [Releases page](https://github.com/xboxoneresearch/DuRFUnitI2C/releases)
12+
13+
This repo contains tooling for Xbox One's RF Unit to dump / write flash and edit the firmware binaries with custom sounds.
14+
815
Technical infos: <https://xboxoneresearch.github.io/wiki/hardware/rf-unit/>
916

1017
## Kudos
@@ -20,15 +27,37 @@ Special thanks to:
2027

2128
I2C Device clients:
2229
- GreatFET One board
30+
2331
or
2432
- Raspberry Pi (untested)
33+
2534
or
35+
2636
- Micropython device (ESP8266, ESP32, Pi Pico ..)
2737

2838
Depending on the device, pull-up resistors might be necessary.
2939

30-
## Connections
40+
## Quickstart Raspberry Pi Pico
41+
42+
- Download latest [release](https://github.com/xboxoneresearch/DuRFUnitI2C/releases)
43+
- Unpack the archive
44+
- Read [QUICKSTART_PICO.txt](./QUICKSTART_PICO.txt)
45+
- Profit
46+
47+
## Features
3148

49+
**RFUnit tool**
50+
51+
- Play sounds
52+
- Dump flash
53+
- Write flash
54+
55+
**VPE tool**
56+
57+
- Play sounds from firmware
58+
- Inject custom sounds
59+
60+
## Connections
3261

3362
| Xbox / Pin | 3V3 | GND | SDA (DATA) | SCL (CLOCK) | Note |
3463
| ---------------- | --- | --- | ---------- | ----------- | ------------------------------------------------------ |
@@ -43,7 +72,7 @@ Depending on the device, pull-up resistors might be necessary.
4372
| RasPi Pico | 3V3 | Any | 1 (GP0) | 2 (GP1) |
4473
| ESP 8266 | 3V3 | Any | GPIO 4 | GPIO 5|
4574

46-
### Notes on FACET
75+
### Notes when directly soldering to Xbox motherboard (FACET-port)
4776

4877
General info: [Wiki](https://xboxoneresearch.github.io/wiki/hardware/facet/)
4978

@@ -63,94 +92,77 @@ Xbox One S
6392

6493
![Pi Pico Rf Unit connection diagram One S](./pi_pico_diagram_one_s.png)
6594

66-
## Features
67-
68-
- Play sounds
69-
- Dump flash
70-
- Write flash
71-
7295
## Usage
7396

7497
- Solder I2C connections and 5V/3.3V/GND
75-
- Install python requirements, preferrably in a python venv
76-
- GreatFET: `pip install greatfet`
77-
- Raspberry Pi (not Pico): `pip install smbus2`
78-
- Micropython: `pip install pyserial tqdm`
7998
- For flashing: Copy `flash.bin` next to script/executable
80-
- Execute `rfunit.py` (see below for micropython)
99+
- Execute tool
100+
- For GreatFET / RPi (non-Pico): `rfunit-cli` for command-line
101+
- Graphical interface: `rfunit-gui`
102+
- For Micropython (RPi Pico, ESP8266 ...): see below @ [Micropython](#micropython)
81103

82104
### GUI
83105

84106
For interactive control (play sounds, dump/flash, raw command TX/RX), run:
85107

86-
- `python rfunit_gui.py`
108+
- `rfunit-gui`
87109

88110
Device notes:
89111

90112
- GreatFET on Windows: select `greatfet`
91113
- Pi Pico running MicroPython: select `pico` (leave port blank and click `Detect`)
92114
- Raspberry Pi (Linux): select `rpi`
93115

116+
![RFUnit GUI - Screenshot Main](./assets/rfunit_gui-main.png)
117+
![RFUnit GUI - Screenshot Flash](./assets/rfunit_gui-flash.png)
118+
94119
### Micropython
95120

96121
Check [Pyboard tool](https://docs.micropython.org/en/latest/reference/pyboard.py.html) for an overview.
97122

98123
Easy:
99124

100125
- For flashing: Copy `flash.bin` next to script/executable
101-
- Execute `micropython_rfunit.py`
102-
103-
Manual:
104-
105-
- Identify the serial port where you micropython device got enumerated (via `dmesg`), f.e. `/dev/ttyACM0`
106-
107-
- Dumping
108-
- Execute the script: `./vendor/pyboard.py --device /dev/ttyACM0 rfunit.py`
109-
- Copy the dump to the PC: `./vendor/pyboard.py --device /dev/ttyACM0 -f cp :dump.bin .`
110-
- Flashing
111-
- Copy flash.bin to the Micropython device: `./vendor/pyboard.py --device /dev/ttyACM0 -f cp flash.bin :flash.bin`
112-
- Execute the script: `./vendor/pyboard.py --device /dev/ttyACM0 rfunit.py`
126+
- Execute `rfunit-micropython`
113127

114128
## ISD9160 VPE Editor tool
115129

116130
This tool can be used to playback audio segments of ISD9160 firmware files, wrapped in custom VPE blob.
117131
Used on Xbox One/S/X for button-sounds.
118132

119-
More features coming soon...
133+
NOTE: The tool requires an original firmware as a base.
134+
135+
Run: `vpe-gui`
136+
137+
![VPE Editor screenshot](./assets/vpe_gui-creator.png)
138+
139+
## Manual
120140

121-
Kudos to [flynnyfoo](https://github.com/FJCFJC123) for not giving up on this major task <3
141+
Obviously you can do everything manually too, instead of relying on the pyinstaller binaries.
122142

123-
### Requirements
143+
Requirements:
124144

125-
- [uv](https://docs.astral.sh/uv/)
145+
- Install uv (<https://docs.astral.sh/uv>)
126146

127-
### Usage
147+
### Micropython dump / flash
148+
149+
- Identify the serial port where you micropython device got enumerated (via `dmesg`), f.e. `/dev/ttyACM0`
150+
- Dumping
151+
- Execute the script: `uv run pyboard --device /dev/ttyACM0 ./src/rfunit.py`
152+
- Copy the dump to the PC: `uv run pyboard --device /dev/ttyACM0 -f cp :dump.bin .`
153+
- Flashing
154+
- Copy flash.bin to the Micropython device: `uv run pyboard --device /dev/ttyACM0 -f cp flash.bin :flash.bin`
155+
- Execute the script: `uv run pyboard --device /dev/ttyACM0 ./src/rfunit.py`
128156

129-
Run the GUI
157+
### RFUnit GUI
130158

131-
```
132-
uv run vpe_gui.py
133-
```
159+
Run: `uv run rfunit-gui`
134160

161+
### VPE Editor (CLI)
135162

136-
## Flashdump
163+
Run: `uv run vpe-cli`
137164

138-
Size: 0x24400
165+
### VPE Editor (GUI)
139166

140-
Checksum (SHA256)
141-
```
142-
8150af1889203260bbbd25884447c0540f1cbc843a13e86bb0b6d501a6d032e1 dump_phat_cod_aw.bin
143-
3789e76fd6c37bdf39751afd6164c1d8d67a53c31eca5b630901210b55753dba dump_phat_forza6.bin
144-
388d252d7783fd2946b9b3fca57ecd1ed13a4f17ea6665f577da34224f9930da dump_phat_halo5.bin
145-
abc699513959372faee038c78a1d7509c2020f65cb78ad07ab9c90b21b406a87 dump_phat_retail.bin
146-
4812af697716464dff1a49e0ab2f45497492fec71aa0a73308f522db45d2b42e dump_one_s_minecraft.bin
147-
222fcaec65d1ffd7f275616df1af5f1128d34e3b32035e3377ab48f91b232ffc dump_one_s_gow4.bin
148-
c39871fcfef69c632955658f8e876d35a35dafb9c88c7fc082dca23d2102289f dump_one_x_tacobell.bin
149-
```
167+
Run: `uv run vpe-gui`
150168

151-
Some strings
152-
```
153-
ISD9160FIMS03 FW Jun 14 2013 at 10:41:12 (C) Nuvoton 2013
154-
Nuvoton ISD9160MS Boot FW Jun 14 2013 10:40:21
155-
ISD-VPE Ver 920.000c 08/05/2013 PV_Prod_Units_Rev5 VERSION:0x10000007
156-
```

assets/rfunit_gui-flash.png

28.9 KB
Loading

assets/rfunit_gui-main.png

28.3 KB
Loading

assets/vpe_gui-creator.png

96.9 KB
Loading

pyinstaller.spec

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
# -*- mode: python ; coding: utf-8 -*-
22

3+
from PyInstaller.utils.hooks import collect_data_files
4+
5+
use_upx=False
6+
# 'cmsis_svd' is a dependency of 'greatfet'
7+
rfunit_py_datas = [('src/rfunit.py', '.'), *collect_data_files('cmsis_svd', includes=['schemas/*'])]
8+
rfunit_hiddenimports = ["smbus2", "greatfet"]
9+
310
a1 = Analysis(
411
['src/rfunit.py'],
512
pathex=[],
613
binaries=[],
7-
datas=[],
8-
hiddenimports=[],
14+
datas=rfunit_py_datas,
15+
hiddenimports=rfunit_hiddenimports,
916
hookspath=[],
1017
hooksconfig={},
1118
runtime_hooks=[],
@@ -18,10 +25,8 @@ a2 = Analysis(
1825
['src/rfunit_gui.py'],
1926
pathex=[],
2027
binaries=[],
21-
datas=[
22-
('src/rfunit.py', '.')
23-
],
24-
hiddenimports=[],
28+
datas=rfunit_py_datas,
29+
hiddenimports=rfunit_hiddenimports,
2530
hookspath=[],
2631
hooksconfig={},
2732
runtime_hooks=[],
@@ -62,10 +67,8 @@ a5 = Analysis(
6267
['src/micropython_rfunit.py'],
6368
pathex=[],
6469
binaries=[],
65-
datas=[
66-
('src/rfunit.py', '.')
67-
],
68-
hiddenimports=[],
70+
datas=rfunit_py_datas,
71+
hiddenimports=rfunit_hiddenimports,
6972
hookspath=[],
7073
hooksconfig={},
7174
runtime_hooks=[],
@@ -88,7 +91,7 @@ exe_rfunit_cli = EXE(
8891
debug=False,
8992
bootloader_ignore_signals=False,
9093
strip=False,
91-
upx=True,
94+
upx=use_upx,
9295
disable_windowed_traceback=False,
9396
argv_emulation=False,
9497
target_arch=None,
@@ -106,7 +109,7 @@ exe_rfunit_gui = EXE(
106109
debug=False,
107110
bootloader_ignore_signals=False,
108111
strip=False,
109-
upx=True,
112+
upx=use_upx,
110113
disable_windowed_traceback=False,
111114
argv_emulation=False,
112115
target_arch=None,
@@ -124,7 +127,7 @@ exe_vpe_cli = EXE(
124127
debug=False,
125128
bootloader_ignore_signals=False,
126129
strip=False,
127-
upx=True,
130+
upx=use_upx,
128131
disable_windowed_traceback=False,
129132
argv_emulation=False,
130133
target_arch=None,
@@ -142,7 +145,7 @@ exe_vpe_gui = EXE(
142145
debug=False,
143146
bootloader_ignore_signals=False,
144147
strip=False,
145-
upx=True,
148+
upx=use_upx,
146149
disable_windowed_traceback=False,
147150
argv_emulation=False,
148151
target_arch=None,
@@ -160,13 +163,13 @@ exe_rfunit_cli_easy = EXE(
160163
debug=False,
161164
bootloader_ignore_signals=False,
162165
strip=False,
163-
upx=True,
166+
upx=use_upx,
164167
disable_windowed_traceback=False,
165168
argv_emulation=False,
166169
target_arch=None,
167170
codesign_identity=None,
168171
entitlements_file=None,
169-
name='rfunit-easy',
172+
name='rfunit-micropython',
170173
console=True,
171174
)
172175

@@ -197,7 +200,7 @@ coll = COLLECT(
197200
a5.datas,
198201

199202
strip=False,
200-
upx=True,
203+
upx=use_upx,
201204
upx_exclude=[],
202205

203206
name="durango-rfunit-tools"

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ dev = [
2525
[project.scripts]
2626
rfunit-cli = "rfunit:main"
2727
rfunit-gui = "rfunit_gui:main"
28-
rfunit-easy = "micropython_rfunit:main"
28+
rfunit-micropython = "micropython_rfunit:main"
2929
vpe-cli = "vpe:main"
3030
vpe-gui = "vpe_gui:main"
31+
pyboard = "vendor.pyboard:main"

src/rfunit.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
Xbox One I2C RF Unit
33
"""
44

5+
# NOTE: These typing hints in try/except-wrap are not available for micropython
56
try:
67
from typing import List, Generator
78
from io import BufferedReader, BufferedWriter
@@ -114,7 +115,7 @@ class DummyDevice(I2CClient):
114115
def __init__(self):
115116
pass
116117

117-
def scan(self) -> bool:
118+
def scan(self) -> List[int]:
118119
return [I2C_ADDR]
119120

120121
def read(self, read_len: int) -> List[int]:
@@ -427,7 +428,7 @@ def dump_flash(self, offset: int, count: int) -> Generator[bytes, None, None]:
427428
bytecnt = min(CHUNK_SIZE, end_offset - addr)
428429
yield res[:bytecnt]
429430

430-
def print_position(position: int, mod_value: int = None):
431+
def print_position(position: int, mod_value: int | None = None):
431432
# As we dont want to print on each iteration..
432433
if not mod_value or position % mod_value == 0:
433434
print(f"{position:#08x}")

src/rfunit_gui.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -475,8 +475,10 @@ def work() -> None:
475475
if dev_type == "greatfet":
476476
try:
477477
dev = rfunit.GreatFetDevice()
478-
except Exception as e:
478+
except ImportError as e:
479479
raise RuntimeError("Failed to init GreatFET. Did you `pip install greatfet`?") from e
480+
except Exception as e:
481+
raise RuntimeError("No GreatFET device found") from e
480482
elif dev_type == "rpi":
481483
if os.name == "nt":
482484
raise RuntimeError(
@@ -489,12 +491,14 @@ def work() -> None:
489491
raise ValueError("Invalid RPi bus id") from e
490492
try:
491493
dev = rfunit.RPiDevice(bus_id=bus_id)
492-
except Exception as e:
494+
except ImportError as e:
493495
raise RuntimeError("Failed to init smbus2. Did you `pip install smbus2` (and run on a Pi)?") from e
496+
except PermissionError as e:
497+
raise RuntimeError("Could not access RPi smbus2 - permission error?") from e
494498
elif dev_type == "pico":
495499
try:
496500
from vendor import pyboard
497-
except Exception as e:
501+
except ImportError as e:
498502
raise RuntimeError("Failed to import vendor.pyboard") from e
499503

500504
port = (self.pico_port_var.get() or "").strip()
@@ -864,4 +868,4 @@ def main() -> int:
864868

865869

866870
if __name__ == "__main__":
867-
raise SystemExit(main())
871+
sys.exit(main())

uv.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)