Device Control and Status¶
Most day-to-day interaction with a SenXor device happens through its registers (raw 8-bit values) and the higher-level fields (individual bits or bit-ranges inside those registers). This page shows you how to inspect device status and apply configuration changes using these two powerful APIs.
How the Register System Works¶
The core of every SenXor device is the MI48 chip, which exposes a bank of 8-bit registers. Each register has:
- Address (e.g.,
0xB1
) - Value (e.g.,
0b00000000
)
To save space, small pieces of configuration, called fields, are packed into those 8-bit registers. Each field represents a specific function or status bit, and several fields may share a single register.
Example: Unpacking a Register¶
Take the FRAME_MODE
register at address 0xB1
as an example. It's an 8-bit value structured as follows:
Bit 7 | Bit 6 | Bit 5 | Bits 4-2 | Bit 1 | Bit 0 |
---|---|---|---|---|---|
Reserved | Reserved | NO_HEADER | READOUT_MODE | CONTINUOUS_STREAM | GET_SINGLE_FRAME |
Instead of forcing you to do binary math (like (value & 0b00000010) >> 1
), pysenxor
provides a simple, name-based API: dev.fields.CONTINUOUS_STREAM
.
Getting Help: Discovering Registers and Fields¶
After connecting to your device (see Quick Start for details), you can easily explore available registers and fields, as well as their descriptions and help texts.
>>> import senxor
>>> addrs = senxor.list_senxor()
>>> addrs
['COM3']
>>> dev = senxor.connect(addrs[0])
>>> print(f"Connected to device {dev.address}")
Connected to device COM3
Inspecting Registers¶
>>> regs = dev.regs
>>> print(regs.FRAME_MODE)
FRAME_MODE (0xB1)
>>> print(regs.FRAME_MODE.desc)
Control capture and readout of thermal data
Inspecting Fields¶
fields = dev.fields
print(fields.FRAME_RATE_DIVIDER)
print(fields.FRAME_RATE_DIVIDER.desc)
print(fields.FRAME_RATE_DIVIDER.help)
All regs
and fields
support autocompletion in most editors.
You can also access register and field metadata statically, without connecting to a device:
from senxor.regmap import Fields, Registers
print(Fields.FRAME_RATE_DIVIDER.desc)
print(Registers.FRAME_MODE.desc)
Reading Device Status and Configuration¶
You can use fields and registers to read device information, configuration, and status.
Example: Reading Device Information¶
>>> mcu_type = dev.fields.MCU_TYPE
>>> mcu_type.get(), mcu_type.display()
(3, 'MI48G')
The .display()
method provides a human-readable string for enumerated or mapped values.
Example: Reading Device Configuration¶
>>> emissivity = dev.fields.EMISSIVITY
>>> emissivity.display()
'56%'
>>> dev.fields.OTF.display()
'1.06'
# Dictionary-style access
>>> dev.fields["OTF"].get()
6
Example: Reading Device Status¶
Some fields and registers reflect the device's current state. For general usage. For example:
>>> dev.fields.CONTINUOUS_STREAM.get()
0 # 0 = disabled, 1 = enabled
>>> dev.regs.FRAME_MODE.get()
0b00000000
Understanding Caching and IO¶
The .get()
method on registers and fields reads the value from the device and caches it. Subsequent .get()
calls will return the cached value, unless the register/field is marked as auto-reset (i.e., its value may change due to hardware events).
For example, DATA_READY
indicates the frame data readiness status: if DATA_READY
is 0, the frame data is not ready; if it is 1, the frame data is ready.
For auto-reset fields (such as DATA_READY
), .get()
always queries the device directly, bypassing the cache. Frequent polling of such fields may impact device performance, especially for high-frequency operations.
The .display()
and .value
properties both call .get()
internally.
To force a direct hardware read and update the cache, use the .read()
method on registers:
fresh_value = dev.regs.FRAME_MODE.read()
If you want a snapshot of all cached values (without triggering any IO), use:
cached_regs = dev.regs.status
cached_fields = dev.fields.status
When (and Why) to Call refresh_regmap()
¶
Sometimes you need a full, up-to-date picture of the device's configuration—for example, right before saving a diagnostic log or after an external system has modified the camera settings. Instead of crawling through every register one-by-one (which could generate hundreds of I/O transactions), you can issue a single bulk read:
dev.refresh_regmap() # single round-trip to the device
What just happened?
- The library performed one low-level operation to read all registers.
- Both register and field caches were refreshed in RAM.
- Subsequent
.get()
calls will now hit the fresh cache, keeping I/O traffic to zero until you ask for another live read.
Performance Tip – For normal UI updates or tight acquisition loops, rely on cached values (
.status
) and refresh only when real-time accuracy is essential. A polling rate faster than 10 Hz (every 100 ms) is generally unnecessary and may congest the bus.
Configuring the Device¶
You can change device settings by writing to fields or registers. The recommended way is to use field-level access for clarity and safety.
Example: Setting the Frame Rate¶
>>> dev.fields.FRAME_RATE_DIVIDER.get()
4
>>> dev.fields.FRAME_RATE_DIVIDER.set(1)
>>> dev.fields.FRAME_RATE_DIVIDER.get()
1
Example: Adjusting Emissivity¶
>>> dev.fields.EMISSIVITY.set(98)
>>> dev.fields.EMISSIVITY.get()
98
Alternative APIs and Best Practices¶
While attribute-style access (dev.fields.FIELD_NAME
) is recommended for its clarity and auto-completion support, other helpers exist:
- Dictionary access –
dev.fields["FIELD_NAME"]
- Register-level direct access –
dev.regs.read_reg(0xCA)
anddev.regs.write_reg(0xCA, 98)
- Batch operations –
dev.regs.read_regs([0x00, 0x01, 0xB1])
ordev.fields.set_fields({"FRAME_RATE_DIVIDER": 2, "EMISSIVITY": 90})
- Iteration –
>>> for reg in dev.regs: ... print(hex(reg.addr), reg.name) >>> for field in dev.fields: ... print(field.name)
For more details, please refer to the API Reference.
Best Practices¶
- Prefer fields when available – less bit-twiddling, fewer mistakes.
- Refresh sparingly – call
refresh_regmap()
only when you need a full, live snapshot. - Leverage
.display()
– convert raw values into human-readable strings. - Monitor error flags – e.g.,
dev.fields.CAPTURE_ERROR
– to keep your application robust.