Warning
This is proof-of-concept code used mainly for reverse-engineering. Do not rely on it for therapy!
This script lets you connect to a 700-series Medtronic pump from a Linux computer, just like Medtronic's MiniMed Mobile app does, but without a mobile phone or any Medtronic software.
Important
Currently only Linux is supported.
-
Clone this repo, including submodules
git clone --recurse-submodules https://github.com/OpenMinimed/PythonPumpConnector.git
-
Install system dependencies
# Ubuntu/Debian (untested) sudo apt install libcairo2-dev pkg-config python3-dev python3-pip libgirepository1.0-dev libcairo-gobject2 # Fedora (tested on 43) sudo dnf install cairo-devel pkg-config python3-devel gobject-introspection-devel cairo-gobject-devel
-
Install Python dependencies
pip install -r requirements.txt
-
Get sources for BlueZ 5.66 (or older)
Consult your distribution's guide or fetch the upstream sources.
-
Patch and rebuild BlueZ — force ATT_MTU size to 23 bytes
In
src/shared/gatt-server.c, findfind_info_cb()and the call toencode_find_info_rsp(). Insertmtu = 23;right before that call. Rebuild and reinstall BlueZ.Why: After the pump connects, BlueZ automatically increases the MTU size to 184 bytes (the pump's advertised max). The pump, however, does not actually handle MTUs larger than 23 bytes during GATT discovery — it stops responding, terminating the connection. Patching
find_info_cb()is the only reliable fix so far (ExchangeMTU = 23inmain.confdoes not seem to work and would also limit the size for all of the packets; which is not what we want).Tested and working on BlueZ 5.55 and 5.66, Linux 6.1.0-42-amd64.
-
Update BlueZ config
In
/etc/bluetooth/main.conf, section[General], addPrivacy = device. Restart:sudo systemctl restart bluetoothd
Why: The pump only reconnects to devices that are using a Resolvable Private Address (RPA). This setting makes our computer use that instead of its actual Bluetooth MAC address. The initial pairing also works without it though.
-
Patch bluezero (tested with v0.9.1 only)
Find the bluezero install location (
pip show bluezero). Openbluezero/localGATT.py, classCharacteristic, functionWriteValue(). Comment out the call toself.Set():def WriteValue(self, value, options): if self.write_callback: self.write_callback(dbus_tools.dbus_to_python(value), dbus_tools.dbus_to_python(options)) # REMOVED: # self.Set(constants.GATT_CHRC_IFACE, 'Value', value)
Why: Without this patch, written characteristic data is echoed back instead of forwarding the response (see bluezero#382).
The pump communicates over Bluetooth Low Energy (BLE), so you need a computer with support for that.
Pump and computer must be paired once. Once paired, reconnects do not require pairing again.
Open two terminals. In the first:
bluetoothctl --agent=NoInputNoOutputNoInputNoOutput is essential — the pump requests the MITM flag but does not support LE Secure Connections, so the kernel falls back to Just Works pairing. Without this agent, pairing is rejected.
If your pump still shows another connected mobile device, remove it first (the pump only connects to one "phone" at a time).
On the pump, start searching for new devices. In the second terminal, run:
./main.pyThe script advertises as Mobile XXXXXX (random number). After a few seconds, the pump should find it and prompt you to connect. Accept the pairing request in the bluetoothctl terminal when prompted.
Alternatively, you can provide a custom name instead of the random number (max. 7 characters):
./main.py <name>Note
The script may prompt for sudo to set up BLE advertising.
After GATT discovery and the SAKE handshake, the script presents an interactive menu of commands for reading pump data (CGM, features, event history, etc.).
Note
One special command syncs the pump's event history with a local SQLite database. This is useful for preserving the full history offline, because we can currently only access the last 4–5 days worth of history data.
If the script was stopped after a successful SAKE handshake, reconnect without re-pairing:
./main.py --reconnectThe name used during pairing need not be supplied here again. The second terminal with bluetoothctl is not needed either.
After syncing history data, explore the local SQLite database:
python3 -m database.viewerThis parses all stored records, prints them, lists event types, detects sequence-number gaps, and generates a daily datapoint-count graph (history_graph.png).
Note
The viewer does not require a connection to the pump. It works solely on the local database file.
Capture BLE traffic for Wireshark analysis:
btmon -w $(date +"%Y-%m-%d_%T")_pump.logRun this in a separate terminal before starting main.py.
Sometimes no BLE traffic is sent and pairing does not start — likely a GUI bug in the pump. Go back to Paired Devices > Pair New Device and retry. Disabling the Bluetooth adapter on the PC while the pump waits on a timeout regains button control faster.
Most computers default to an advertising interval > 1 second. This works for initial pairing but can prevent reliable reconnects (the pump may miss sparse advertising packets while scanning intermittently to save battery).
To shorten the interval, use debugfs (requires CONFIG_BT_DEBUGFS=y in the kernel):
Note
The script tries to automatically apply this workaround.
echo 50 > /sys/kernel/debug/bluetooth/hci0/adv_min_interval
echo 50 > /sys/kernel/debug/bluetooth/hci0/adv_max_intervalThe actual interval is value × 0.625 ms (50 → ~31 ms). Change adv_min_interval first, then adv_max_interval (min must never exceed max).
If this fails with "operation not permitted", check kernel lockdown:
cat /sys/kernel/security/lockdownIf not none, disable Secure Boot in BIOS/EFI.
Verify with btmon — you should see the following during advertising:
Min advertising interval: 31.250 msec (0x0032)
Max advertising interval: 31.250 msec (0x0032)
This is only needed for reconnects. Initial pairing works fine with the default interval.

