Using QMP to interact with QEMU Board
- 15 Jul, 2025

QEMU Machine Protocol (QMP)
QEMU is a very useful tool for learning embedded system operation since different processors, configurations and peripherals using different protocols can be emulated.
For this blog I’ve made several custom peripherals and it is not always easy to make a demo, since some meaningful data has to be provided. For instance, in the custom peripherals examples for I2C and SPI, the temperature sensors generate random data in certain range and that data can be read. It was a way to make some data which is not static, so the example can be interactive, but the downside is that the values cannot be reproduced and tested due to randomness.
QEMU has another way of providing that data from outside of the running instance. It is via device properties and QEMU Machine Protocol (QMP).
In order to use QMP the QEMU instance has to be started with the following argument
-qmp <socket>
where <socket>
can be a tcp or unix domain socket. In this example we will be using a unix domain socket, so the
argument would be
-qmp unix:/tmp/qmp.sock,server,nowait
This will start a QMP server in the QEMU instance.
A client can interact via QMP using scripts available in the scripts/
directory in QEMU source code, or via Python
qemu.qmp
module.
TMP105 QEMU Model
Before we go into details on the QMP we will take a look at the TMP105 peripheral that can be added to the emulated QEMU
board. This peripheral is interesting since it already has the temperature
property exported.
TMP105 is a I2C temperature sensor which can provide temperature in the range from -128 to 128 degrees in 9-12 bit resolution. For the purpose of this test, we will use only top 8 bits since that will give temperature with resolution of 1 degree.
The temperature can be read from the register at offset 0x0, so i2c-tools
command to read the current temperature
would be
i2cget -y <bus> <address> 0x0
The TMP105 can be added in the source code of the emulated board (same way as it was done in the Custom I2C peripheral example), but it can also be added when invoking QEMU by passing the following argument
-device tmp105,id=sensor,address=0x50
The ID of the peripheral is passed so we can easily identify it from QMP. This command will make the device attached to the I2C bus 1.
For reading the values from the emulated TMP105 peripheral in the QEMU we can use i2c-tools
, so a read of temperature
is performed using i2cget -y 1 0x50
.
The value that is read will be a hex representation of the temperature in degrees.
QMP scripts
QEMU has several scripts in the scripts/
directory that can be used to interact over QMP
qmp-shell
- universal one, can work with all of the commands; we are interested with ones working with the QEMU Object Model (QOM)qom-list
- list all objects that are availableqom-get
- get a property of a objectqom-set
- set a value of the property of an object
In this example we will use the qmp-shell
for issuing commands. It is started using
qmp-shell -p /tmp/qmp.sock
(-p
in the command above is for pretty print of JSON return values).
Once QMP shell is started we can issue commands.
We can list all the properties of the sensor
node using
qom-list path=sensor
{
"return": [
{
"name": "type",
"type": "string"
},
{
"name": "parent_bus",
"type": "link<bus>"
},
{
"name": "realized",
"type": "bool"
},
{
"name": "hotplugged",
"type": "bool"
},
{
"name": "hotpluggable",
"type": "bool"
},
{
"name": "address",
"type": "uint8"
},
{
"name": "unnamed-gpio-out[0]",
"type": "link<irq>"
},
{
"name": "temperature",
"type": "int"
}
]
}
The temperature
property is at the bottom. In order to read the current value of the temperature
property, following
command can be used
qom-get path=sensor property=temperature
{
"return": 0
}
The value of the temperature
property can be changed using the following command
qom-set path=sensor property=temperature value=30000
{
"return": {}
}
This will set the value to 30 degrees (temperature is written in milicelsius).
Python QMP access
QEMU has several Python modules available in the source code, one of them is the QMP module.
Assuming QEMU_SRC_PATH
holds path to the QEMU source code, Python QEMU module can be installed in the virtual
environment using pip
python3 -m venv venv
. ./venv/bin/activate
pip3 install ${QEMU_SRC_PATH}/python/
Once installed, the QMP module can be imported using import qemu.qmp
. It provides functions for creating and
connecting a client, as well as executing the QMP JSON commands.
In this example, a simple interactive python script is created. The script will initialize a curses screen and a QMP client and then read the initial temperature value.
Once initialized, the script will wait for key press and following characters will be accepted:
+
to increase temperature value by 1 degree-
to decrease temperature value by 1 degreeq
to exit the program
The source code for the script follows
import asyncio
from qemu.qmp import QMPClient
from curses import ERR, wrapper
async def update_temperature(client, value):
await client.execute(
"qom-set",
{
"path": "/machine/peripheral/sensor",
"property": "temperature",
"value": value,
},
)
def temperature_to_tmp105_hex(value):
return hex(value // 1000)
def log_current_temperature(win, temperature):
win.clear()
win.addstr(
f"Current temperature {temperature // 1000} ({temperature_to_tmp105_hex(temperature)})"
)
async def display_main(win):
qmp = QMPClient("cubieboard-ng vm")
await qmp.connect("/tmp/qmp.sock")
win.nodelay(True)
temperature = await qmp.execute(
"qom-get", {"path": "/machine/peripheral/sensor", "property": "temperature"}
)
log_current_temperature(win, temperature)
while True:
char = win.getch()
if char == ERR:
await asyncio.sleep(0.1)
elif char in (ord("q"), ord("Q")):
break
elif char == ord("+"):
temperature += 1000
if temperature > 127000:
temperature = 127000
log_current_temperature(win, temperature)
await update_temperature(qmp, temperature)
elif char == ord("-"):
temperature -= 1000
if temperature < 0:
temperature = 0
log_current_temperature(win, temperature)
await update_temperature(qmp, temperature)
await qmp.disconnect()
def main(stdscr) -> None:
return asyncio.run(display_main(stdscr))
if __name__ == "__main__":
wrapper(main)
In order to test that the value changes are propagated to QEMU we can use watch
to read the temperature value
periodically with
watch i2cget -y 1 0x50
A short recording of how python script is used to modify the value of temperature property follows. The left screen is
the QEMU instance with the watch
command and on the right side is the python script showing current temperature and
waiting for key press.
Summary
This post demonstrates use of QMP to change a value of property of a peripheral.
The approach can be used for other custom components to make them more interactive instead of relying on random data.