Skip to content

First Connection

openocd-python provides two ways to talk to OpenOCD: connect to an already-running instance, or spawn a new OpenOCD process and connect to it. Both use the Session class as the entry point.

Before connecting, you need a running OpenOCD instance or a valid OpenOCD configuration file. Start OpenOCD in a separate terminal:

Terminal window
openocd -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg

You should see output ending with something like:

Info : Listening on port 6666 for tcl connections
Info : Listening on port 4444 for telnet connections
Info : Listening on port 3333 for gdb connections

Port 6666 is the TCL RPC port that openocd-python uses.

The most common pattern is connecting to an OpenOCD instance that is already running. Use Session.connect() for async code or Session.connect_sync() for synchronous scripts.

import asyncio
from openocd import Session
async def main():
async with Session.connect() as ocd:
state = await ocd.target.state()
print(f"Target: {state.name}")
print(f"State: {state.state}")
if state.current_pc is not None:
print(f"PC: 0x{state.current_pc:08X}")
asyncio.run(main())

Session.connect() accepts three optional arguments:

ParameterDefaultDescription
host"localhost"Hostname or IP address of the OpenOCD instance
port6666TCL RPC port number
timeout10.0Connection timeout in seconds
# Connect to OpenOCD on a remote machine
async with Session.connect(host="192.168.1.50", port=6666, timeout=5.0) as ocd:
state = await ocd.target.state()

If you want openocd-python to manage the OpenOCD process for you, use Session.start(). This spawns OpenOCD as a subprocess, waits for the TCL RPC port to become available, and connects to it. When the context manager exits, the process is stopped automatically.

import asyncio
from openocd import Session
async def main():
async with Session.start("interface/cmsis-dap.cfg -f target/stm32f1x.cfg") as ocd:
state = await ocd.target.state()
print(f"Target: {state.name}, State: {state.state}")
asyncio.run(main())

Session.start() accepts these arguments:

ParameterDefaultDescription
config(required)Config file path or -f/-c flags as a string
tcl_port6666TCL RPC port for the spawned process
openocd_binNonePath to OpenOCD binary (auto-detected from PATH if None)
timeout10.0Seconds to wait for OpenOCD to start and become ready
extra_argsNoneAdditional CLI arguments passed to OpenOCD

The config parameter is flexible. You can pass a plain filename (which gets wrapped with -f), or include -f and -c flags explicitly:

# Plain config file -- automatically wrapped as "-f interface/cmsis-dap.cfg"
await Session.start("interface/cmsis-dap.cfg")
# Multiple config files with explicit flags
await Session.start("-f interface/cmsis-dap.cfg -f target/stm32f1x.cfg")
# Inline TCL commands
await Session.start("-f interface/cmsis-dap.cfg -f target/stm32f1x.cfg -c 'adapter speed 4000'")
# With extra args
await Session.start(
"interface/cmsis-dap.cfg -f target/stm32f1x.cfg",
extra_args=["-d2"], # debug level 2
)

openocd-python communicates over OpenOCD’s TCL RPC protocol on port 6666. The protocol is straightforward:

  1. The client sends a command string followed by a \x1a (ASCII SUB) byte
  2. The server responds with the result string followed by a \x1a byte

The library uses a dual-socket design: one TCP connection for command/response pairs, and a separate connection for asynchronous notifications (target halt events, reset events, etc.). This prevents notification messages from corrupting the command stream.

Every Session (and SyncSession) exposes a command() method that sends an arbitrary OpenOCD TCL command and returns the raw response string. This is the escape hatch when you need functionality that is not yet wrapped by a subsystem.

async with Session.connect() as ocd:
version = await ocd.command("version")
print(version)
# Any valid OpenOCD TCL command works
await ocd.command("adapter speed 4000")

Both Session.connect() and Session.start() return a Session object that acts as an async context manager. Using async with (or with for sync) ensures the connection is closed and any spawned OpenOCD process is terminated when you are done:

# The connection is closed automatically at the end of the block
async with Session.connect() as ocd:
await ocd.target.halt()
# Connection is now closed
# If you started OpenOCD, the process is also terminated
async with Session.start("interface/cmsis-dap.cfg -f target/stm32f1x.cfg") as ocd:
await ocd.target.halt()
# OpenOCD process has been stopped

If you need manual lifecycle control, you can call close() directly:

ocd = await Session.connect()
try:
state = await ocd.target.state()
finally:
await ocd.close()
  • Quick Start — complete working examples for common tasks
  • Session Lifecycle — deep dive into session management, lazy subsystems, and the process manager
  • Async vs Sync — understand when to use each API style