Drive a TI-83 Plus link port directly from a Raspberry Pi's GPIO pins — no
SilverLink, no USB cable, no proprietary software. Send and receive .8xp
programs, .8xg groups, pictures and anything else the calculator stores.
Three wires and some Python.
Windows PC ──SSH──> Raspberry Pi 4 ──GPIO──> TI-83 Plus
Status · Wiring · Install · Usage · Flash applications · Interpreter integration · Notes from building this · Contributing
Everything below works and is used against real hardware.
Variables — send, receive and delete .8xp programs, pictures and any
other variable type. .8xg group files too, which is how Ion and multi-file
games ship.
Flash applications — .8xk apps install over the link, including
MirageOS. **TIFL** container, Intel HEX decoding and paged transfer.
Memory — write to the 160 KB Flash archive as well as the ~24 KB user RAM, and delete variables to make room.
Authoring — a TI-BASIC source tokenizer, so you can write a program on a PC and put it on the calculator.
Not implemented, deliberately: OS transfer (.8xu). See
Contributing.
Tested on exactly one setup: a TI-83 Plus (OS 1.19, boot 1.01) wired to a Raspberry Pi 4 Model B Rev 1.5 running Raspberry Pi OS Trixie. Everything else is unverified — reports from other hardware are genuinely useful.
The link port is two open-collector lines with 10 kΩ pull-ups inside the calculator. You do not need level shifters, transistors or resistors.
| Calculator | Pi physical pin | BCM | TI line |
|---|---|---|---|
| TIP | 36 | GPIO16 | D0 — asserted to send a 0 bit |
| RING | 32 | GPIO12 | D1 — asserted to send a 1 bit |
| SLEEVE / GND | 34 | — | ground |
Open-drain is emulated by switching pin direction:
- released = claimed as input, internal bias disabled (high-Z)
- asserted = claimed as output at level 0, with
SET_OPEN_DRAIN
No pin is ever claimed as a plain push-pull output, and gpio_claim_output
defaults to level=0, so there is no window in which a pin drives high.
On safety: worst-case backfeed into an unpowered Pi is
(3.2 V − 0.6 V) / 10 kΩ ≈ 260 µA, limited by the calculator's own pull-up.
That is not a hazard. Contention is prevented in software rather than hardware.
On the Pi:
sudo apt install python3-lgpio gpiodCopy the contents of pi/ to ~/ti83/ on the Raspberry Pi.
Checking the link
python3 bias_test.py # wiring check - run this FIRST when anything breaks
python3 ti83.py ping # is the calculator answering?
python3 ti83.py ver # OS and boot code version
python3 watch.py # read-only logic monitor on the two linesMoving files
python3 ti83.py send GAME.8xp # to user RAM
python3 ti83.py send GAME.8xp --archive # to the 160 KB Flash archive
python3 ti83.py send SHELL.8xg # a group: sends every variable in it
python3 ti83.py sendapp MIRAGEOS.8xk # a Flash application
python3 ti83.py get GAME # pull one back as a .8xp
python3 ti83.py del GAME # delete, to free memoryInspecting files, without a calculator attached
python3 ti83.py info GAME.8xp # variables, sizes, header, checksum
python3 ti83.py appinfo APP.8xk # Flash pages, load addresses, packet count
python3 ti83.py decode GAME.8xp # hex-dump the TI-BASIC token streamThe calculator must be sitting at the home screen. The TI-83+ answers link packets silently in the background, but only while no program is running. Inside Ion or a game, transfers simply time out.
Throughput is roughly 1 KB/s — a 4,895-byte program takes 4.8 s, and MirageOS (124 Flash blocks) takes 41 s.
.8xk files are a different container from .8xp — **TIFL** header, and the
body is the application encoded as Intel HEX text, which decodes into 16 KB
Flash pages.
python3 ti83.py appinfo MIRAGEOS.8xk # inspect first, sends nothing
python3 ti83.py sendapp MIRAGEOS.8xkPer 128-byte block:
VAR (10-byte flash header) -> ACK -> CTS -> ACK -> DATA -> ACK
and a single EOT -> ACK once every block of every page has gone. The
10-byte VAR payload is length(2) | type | length_high(2) | flag | offset(2) | page(2).
Two things the protocol guide gets wrong or omits, both resolved by reading libticalcs instead:
- The guide says DATA carries "four extra bytes at the beginning". It does not. The load address and page number travel in the VAR packet.
- A plain 6 MHz TI-83+ needs a 1 s pause after the second page and a 2.5 s pause before the last. Those same pauses break transfers on 15 MHz models (83+SE, 84+), so they must be conditional on hardware.
sendapp refuses OS images (.8xu, data type 0x23) and always will.
A failed application transfer is recoverable; a failed OS transfer can brick
the calculator.
interpreter/tilink.py adds a Send to Calculator action to a TI-BASIC
editor: it tokenizes the open buffer, wraps it in a .8xp, and ships it over
SSH to the Pi, which clocks it into the calculator.
The editor it was built for is the companion project —
ti83-basic-interpreter,
a TI-BASIC interpreter with a desktop GUI. Write and debug a program there at
whatever speed you like, then press F9 to put it on real hardware.
The tokenizer refuses unknown tokens rather than guessing — see below for why that matters.
DirectUSBTransport is a deliberate stub. A TI SilverLink speaks different
framing to the two-wire link port; it is not the same bytes down another pipe,
and shipping untested transport code that looks finished would be worse than an
honest error message.
Things that cost real time, recorded so they cost you less.
Every TI-BASIC token value taken from memory or a web source was wrong often enough to matter. Every value read back off the calculator was right.
| Token | Commonly cited | Actual |
|---|---|---|
ClrHome |
0xE3 |
0xE1 |
Output( |
0xE1 |
0xE0 |
getKey |
0xE2 |
0xAD |
While |
0xD2 |
0xD1 |
< |
0x6E |
0x6B |
≠ |
0x6B |
0x6F |
| space | 0x20 |
0x29 |
That last one is the nastiest. 0x20 is the randM( token, not a space.
A program built with 0x20 as its space transfers with a perfect checksum,
round-trips byte-identically, and then prints randM( on screen.
Use ti83.py get + ti83.py decode to read real values off your own
calculator. Type the token in a program, pull it back, look at the bytes.
Sending a group as RTS/CTS/DATA ×N followed by a single EOT hangs: the
calculator stops answering after the first variable's DATA and never replies
to the second RTS. The correct flow is
RTS → ACK → CTS → ACK → DATA → ACK → EOT per variable.
Files from the archives often carry a non-zero version byte and the archive
flag set. The calculator may then answer RTS with an undocumented
SKIP/EXIT 0x04. Documented codes are only 01 EXIT, 02 SKIP,
03 OUT OF MEMORY.
A non-zero version byte also means the program wants a newer OS. It will
transfer happily and then throw ERR:VERSION at run time.
A group file is byte-identical in structure to a single-variable file — the data section just holds several entries back to back:
hlen(2) | header(hlen) | datalen(2) | data(datalen) ... repeat
Also accept the **TI83** and **TI82** signatures. Ion's group files use
the former even for the 83+ build, and some level packs are renamed TI-82
files.
If the TI-83+ feels slow, freezes, or drops keypresses, it is blocked in its
link-port routines waiting on a bus it cannot get control of. It is not a
calculator fault. Re-seat the header wires and run bias_test.py. One
loose jumper cost hours, disguised as stuck lines, ERR:XMIT, and a
mysteriously laggy calculator.
RAM is only ~24 KB, and a program needs working room on top of its own size.
The Flash archive is 160 KB but programs must be unarchived to run. Pre-loaded
Flash apps can consume nearly all of the archive; GarbageCollect reclaims
nothing if there is no garbage.
This has been tested against one TI-83 Plus and one Raspberry Pi 4. If you run it on anything else — a TI-84 Plus, a Pi 5, a Pi Zero — results are genuinely unknown, and a report either way is useful.
Particularly wanted:
- Multi-page Flash apps. The transfer loop handles them and follows the reference implementation, but the only app tested here (MirageOS) is a single page. The inter-page pauses in particular have never actually fired.
- 15 MHz hardware. The pauses that a 6 MHz TI-83+ requires are known to break 83+SE / 84+ transfers. There is currently no hardware detection.
- Direct USB / SilverLink transport.
- More verified token values — a
learnmode that types a token on the calculator, reads it back and records the bytes.
.8xu OS files. A failed application transfer is
recoverable; a failed OS transfer can brick the calculator. This project does
not implement OS transfer and should not.
Protocol reconstructed from the TI-83+/84+ Link Protocol Guide by Tim Singer and Romain Liévin, WikiTI, and ArTICL by Christopher Mitchell.
Token values and protocol quirks verified against real hardware.
MIT