Skip to content

milliK¤

MilliK (MultiMessageBased) ¤

MilliK(equipment: Equipment)

              flowchart LR
              msl.equipment_resources.isotech.millik.MilliK[MilliK]
              msl.equipment_resources.multi_message_based.MultiMessageBased[MultiMessageBased]
              msl.equipment.interfaces.message_based.MessageBased[MessageBased]
              msl.equipment.schema.Interface[Interface]

                              msl.equipment_resources.multi_message_based.MultiMessageBased --> msl.equipment_resources.isotech.millik.MilliK
                                msl.equipment.interfaces.message_based.MessageBased --> msl.equipment_resources.multi_message_based.MultiMessageBased
                                msl.equipment.schema.Interface --> msl.equipment.interfaces.message_based.MessageBased
                




              click msl.equipment_resources.isotech.millik.MilliK href "" "msl.equipment_resources.isotech.millik.MilliK"
              click msl.equipment_resources.multi_message_based.MultiMessageBased href "" "msl.equipment_resources.multi_message_based.MultiMessageBased"
              click msl.equipment.interfaces.message_based.MessageBased href "" "msl.equipment.interfaces.message_based.MessageBased"
              click msl.equipment.schema.Interface href "" "msl.equipment.schema.Interface"
            

IsoTech milliK Precision Thermometer.

Regular-expression patterns that are used to select this Resource when connect() is called.

manufacturer=r"Iso.*Tech.*"
model=r"milli.*K.*"
flags=IGNORECASE

Parameters:

Name Type Description Default
equipment Equipment

An Equipment instance.

required
Source code in packages/resources/src/msl/equipment_resources/isotech/millik.py
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
def __init__(self, equipment: Equipment) -> None:
    """[IsoTech](https://isotech.co.uk/) milliK Precision Thermometer.

    Regular-expression patterns that are used to select this Resource when
    [connect()][msl.equipment.schema.Equipment.connect] is called.
    ```python
    manufacturer=r"Iso.*Tech.*"
    model=r"milli.*K.*"
    flags=IGNORECASE
    ```

    Args:
        equipment: An [Equipment][] instance.
    """
    super().__init__(equipment)
    self.rstrip: bool = True
    self.read_termination: bytes = b"\r"
    self.write_termination: bytes = b"\r"

    # REMOTE mode speeds up communication and is required for voltage measurements
    _ = self.write("MILLIK:REMOTE")

    devices, channels = _find_channel_numbers(self)

    # These are the strings that would be returned from each device by the *IDN? command
    # e.g. ['Isothermal Technology,millisKanner,21-P2593,2.01', 'Isothermal Technology,milliK,21-P2460,4.0.0']
    self._devices: list[MilliKDevice] = [MilliKDevice(*d.split(",")) for d in devices]

    self._channels: list[int] = channels

    self.channel_configuration: dict[int, Current | Resistance | Voltage] = {}
    """The channels that have been configured."""

channel_configuration instance-attribute ¤

channel_configuration: dict[int, Current | Resistance | Voltage] = {}

The channels that have been configured.

channel_numbers property ¤

channel_numbers: list[int]

A list of available channel numbers.

For example, [1, 2] for a single milliK or [1, 10, 11, 12, 13, 14, 15, 16, 17] for a milliK connected to a single millisKanner.

connected_devices property ¤

connected_devices: list[MilliKDevice]

A list of information about the connected devices.

num_devices property ¤

num_devices: int

The number of connected devices.

configure_current_measurement ¤

configure_current_measurement() -> None

Configure the milliK to measure current on channel 3.

The current is from a 4-20 mA transmitter on the rear of the milliK.

Source code in packages/resources/src/msl/equipment_resources/isotech/millik.py
161
162
163
164
165
166
def configure_current_measurement(self) -> None:
    """Configure the milliK to measure current on channel 3.

    The current is from a 4-20 mA transmitter on the rear of the milliK.
    """
    self.channel_configuration[3] = Current()

configure_resistance_measurement ¤

configure_resistance_measurement(
    channel: int, resistance: float, *, root2: bool = False, wire3: bool = False
) -> None

Configure the milliK to measure resistance for the specified channel.

Parameters:

Name Type Description Default
channel int

The channel to configure for resistance measurements.

required
resistance float

The largest resistance value, in Ω, that is expected to be measured. The milliK selects the most sensitive range that can accommodate the specified value (up to 115 Ω, 460 Ω or 500 kΩ for the three supported ranges).

required
root2 bool

Use \(\sqrt{2}\) mA sense current instead of the normal 1 mA sense current. Thermistors (resistance measurements in the 500 kΩ range) always use 2 μA.

False
wire3 bool

Whether the wiring arrangement is for 3 wires instead of the typical 4 wires.

False
Source code in packages/resources/src/msl/equipment_resources/isotech/millik.py
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
def configure_resistance_measurement(
    self, channel: int, resistance: float, *, root2: bool = False, wire3: bool = False
) -> None:
    r"""Configure the milliK to measure resistance for the specified channel.

    Args:
        channel: The channel to configure for resistance measurements.
        resistance: The largest resistance value, in Ω, that is expected to be measured.
            The milliK selects the most sensitive range that can accommodate the specified value
            (up to 115 Ω, 460 Ω or 500 kΩ for the three supported ranges).
        root2: Use $\sqrt{2}$ mA sense current instead of the normal 1 mA sense current.
            Thermistors (resistance measurements in the 500 kΩ range) always use 2 μA.
        wire3: Whether the wiring arrangement is for 3 wires instead of the typical 4 wires.
    """
    if channel not in self._channels:
        msg = f"Channel {channel} is not available in the connected milliK devices"
        raise ValueError(msg)

    self.channel_configuration[channel] = Resistance(
        range=round(resistance), current="ROOT2" if root2 else "NORMAL", wires=3 if wire3 else 4
    )

configure_voltage_measurement ¤

configure_voltage_measurement(
    channel: int, *, rjc: bool = False, thermocouple: str | Type | None = None
) -> None

Configure the milliK to measure voltage for the specified channel.

Parameters:

Name Type Description Default
channel int

The channel to configure for voltage measurements.

required
rjc bool

Whether to use reference junction compensation for the measurements.

False
thermocouple str | Type | None

The type of thermocouple that is used. If the thermocouple value is of type str, it must be a member name of the Type enumeration, e.g., K, J, AU_PT.

None
Source code in packages/resources/src/msl/equipment_resources/isotech/millik.py
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
def configure_voltage_measurement(
    self, channel: int, *, rjc: bool = False, thermocouple: str | Type | None = None
) -> None:
    """Configure the milliK to measure voltage for the specified channel.

    Args:
        channel: The channel to configure for voltage measurements.
        rjc: Whether to use reference junction compensation for the measurements.
        thermocouple: The type of thermocouple that is used. If the `thermocouple` value is
            of type [str][], it must be a member name of the [Type][msl.equipment_resources.isotech.millik.Type]
            enumeration, e.g., `K`, `J`, `AU_PT`.
    """
    if channel not in self._channels:
        msg = f"Channel {channel} is not available in the connected milliK devices"
        raise ValueError(msg)

    self.channel_configuration[channel] = Voltage(
        rjc="INTERNAL" if rjc else "NONE",
        thermocouple="NONE" if thermocouple is None else to_enum(thermocouple, Type, to_upper=True),
    )

disconnect ¤

disconnect() -> None

Return the milliK device to LOCAL mode then disconnect from the device.

Source code in packages/resources/src/msl/equipment_resources/isotech/millik.py
211
212
213
214
215
216
217
def disconnect(self) -> None:  # pyright: ignore[reportImplicitOverride]
    """Return the milliK device to LOCAL mode then disconnect from the device."""
    if not self._connected:
        return

    _ = self.write("MILLIK:LOCAL")
    super().disconnect()

read_all_channels ¤

read_all_channels(n: int = 1) -> Iterator[tuple[int, float]]

Read from all configured channels.

Parameters:

Name Type Description Default
n int

The number of readings to average for each channel.

1

Yields:

Type Description
tuple[int, float]

The channel number and the average measurement value for that channel.

Source code in packages/resources/src/msl/equipment_resources/isotech/millik.py
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
def read_all_channels(self, n: int = 1) -> Iterator[tuple[int, float]]:
    """Read from all configured channels.

    Args:
        n: The number of readings to average for each channel.

    Yields:
        The channel number and the average measurement value for that channel.
    """
    for c in sorted(self.channel_configuration):
        if n == 1:  # already a single float value
            yield c, self.read_channel(c)
        else:  # average multiple readings
            readings = self.read_channel(c, n=n)
            yield c, sum(readings) / len(readings)

read_channel ¤

read_channel(channel: int, n: Literal[1] = 1) -> float
read_channel(channel: int, n: int) -> list[float]
read_channel(channel: int, n: int = 1) -> float | list[float]

Read a configured channel.

Parameters:

Name Type Description Default
channel int

The channel to read.

required
n int

The number of readings to acquire.

1

Returns:

Type Description
float | list[float]

A list of n readings or a single value if only one reading is requested.

Source code in packages/resources/src/msl/equipment_resources/isotech/millik.py
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
def read_channel(self, channel: int, n: int = 1) -> float | list[float]:
    """Read a configured channel.

    Args:
        channel: The channel to read.
        n: The number of readings to acquire.

    Returns:
        A list of `n` readings or a single value if only one reading is requested.
    """
    cfg = self.channel_configuration.get(channel)
    if cfg is None:
        msg = f"Please first configure channel {channel} before attempting to read values"
        raise ValueError(msg)

    if isinstance(cfg, Resistance):
        commands = [
            f"SENSE:CHANNEL {channel}",
            "SENSE:FUNCTION RESISTANCE",
            f"SENSE:RESISTANCE:RANGE {cfg.range}",
            f"SENSE:RESISTANCE:WIRES {cfg.wires}",
            f"SENSE:CURRENT {cfg.current}",
        ]
    elif isinstance(cfg, Voltage):
        commands = [
            f"SENSE:CHANNEL {channel}",
            "SENSE:FUNCTION VOLTAGE",
            f"SENSE:PROBE {cfg.thermocouple}",
            f"SENSE:RJC {cfg.rjc}",
        ]
    else:
        commands = ["SENSE:CHANNEL 3", "SENSE:FUNCTION CURRENT"]

    commands.append(f"READ? {n}" if n > 1 else "READ?")
    reply = self.query(";".join(commands))

    try:
        readings = list(map(float, reply.split(",")))
    except ValueError:
        raise MSLConnectionError(self, f"Cannot map reply to float, {reply!r}") from None

    if len(readings) == 1:
        return readings[0]
    return readings

IsoTech milliK Precision Thermometer.

There can also be multiple millisKanner Channel Expanders connected to the milliK.

Current ¤

Measure the current on channel 3.

Resistance dataclass ¤

Resistance(
    range: int, current: Literal["NORMAL", "ROOT2"], wires: Literal[3, 4]
)

A channel configured to measure resistance.

Parameters:

Name Type Description Default
range int

The largest resistance that is expected to be measured.

required
current Literal['NORMAL', 'ROOT2']

The type of sense current to use.

required
wires Literal[3, 4]

The number of wires that are used for the resistance measurement.

required

Type (Enum) ¤

Standard thermocouple types.

Attributes:

Name Type Description
B str

"TYPE B"

E str

"TYPE E"

J str

"TYPE J"

K str

"TYPE K"

L str

"TYPE L"

N str

"TYPE N"

R str

"TYPE R"

S str

"TYPE S"

T str

"TYPE T"

AU_PT str

"TYPE AU-PT"

PT_PD str

"TYPE PT-PD"

Voltage dataclass ¤

Voltage(rjc: Literal['NONE', 'INTERNAL'], thermocouple: Type | Literal['NONE'])

A channel configured to measure voltage.

Parameters:

Name Type Description Default
rjc Literal['NONE', 'INTERNAL']

The reference junction compensation type.

required
thermocouple Type | Literal['NONE']

The thermocouple type.

required