Skip to content

GMH 3000 Series¤

Communicate with a Greisinger GMH 3000 Series thermometer.

GMH3000 (Serial) ¤

GMH3000(equipment: Equipment)

              flowchart LR
              msl.equipment_resources.greisinger.gmh3000.GMH3000[GMH3000]
              msl.equipment.interfaces.serial.Serial[Serial]
              msl.equipment.interfaces.message_based.MessageBased[MessageBased]
              msl.equipment.schema.Interface[Interface]

                              msl.equipment.interfaces.serial.Serial --> msl.equipment_resources.greisinger.gmh3000.GMH3000
                                msl.equipment.interfaces.message_based.MessageBased --> msl.equipment.interfaces.serial.Serial
                                msl.equipment.schema.Interface --> msl.equipment.interfaces.message_based.MessageBased
                




              click msl.equipment_resources.greisinger.gmh3000.GMH3000 href "" "msl.equipment_resources.greisinger.gmh3000.GMH3000"
              click msl.equipment.interfaces.serial.Serial href "" "msl.equipment.interfaces.serial.Serial"
              click msl.equipment.interfaces.message_based.MessageBased href "" "msl.equipment.interfaces.message_based.MessageBased"
              click msl.equipment.schema.Interface href "" "msl.equipment.schema.Interface"
            

Communicate with a Greisinger GMH 3000 Series thermometer.

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

manufacturer=r"Greisinger"
model=r"GMH\s*3\d{3}"

Parameters:

Name Type Description Default
equipment Equipment

An Equipment instance.

required

A Connection instance supports the following properties for a GMH thermometer.

Connection Properties:

Name Type Description
gmh_address int

The GMH address of the device. Default: 1

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
def __init__(self, equipment: Equipment) -> None:
    r"""Communicate with a Greisinger GMH 3000 Series thermometer.

    Regular-expression patterns that are used to select this Resource when
    [connect()][msl.equipment.schema.Equipment.connect] is called.
    ```python
    manufacturer=r"Greisinger"
    model=r"GMH\s*3\d{3}"
    ```

    Args:
        equipment: An [Equipment][] instance.

    A [Connection][msl.equipment.schema.Connection] instance supports the following _properties_
    for a GMH thermometer.

    Attributes: Connection Properties:
        gmh_address (int): The GMH address of the device. _Default: `1`_
    """
    assert equipment.connection is not None  # noqa: S101
    props = equipment.connection.properties
    props.setdefault("baud_rate", 4800)
    super().__init__(equipment)

    # termination characters are not used
    self.read_termination: None = None
    self.write_termination: None = None

    self._address: int = self._invert(int(props.get("gmh_address", 1)))

channel_count ¤

channel_count() -> int

Get the number of channels.

Returns:

Type Description
int

The channel count.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
157
158
159
160
161
162
163
164
def channel_count(self) -> int:
    """Get the number of channels.

    Returns:
        The channel count.
    """
    reply = self._get(code=208)
    return self._to_uint16(*reply[3:5])

clear_max_value ¤

clear_max_value() -> float

Clear the maximum value stored in the device memory.

Returns:

Type Description
float

The current value.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
def clear_max_value(self) -> float:
    """Clear the maximum value stored in the device memory.

    Returns:
        The current value.
    """
    # used Wireshark with the USBPcap plugin to eavesdrop on the
    # GMH_Transmit(1, 175, 0, 0.0, 1) call of the DLL to get the
    # hex values and message lengths
    code, value = self._invert(175), 1
    _ = self.write(
        bytes(
            [
                self._address,
                0xF6,
                self._crc(self._address, 0xF6),
                code,
                value,
                self._crc(code, value),
                0x00,
                0xFF,
                0x0C,
                0x00,
                0xFF,
                0x0C,
            ]
        )
    )
    reply = self.read(size=12, decode=False)
    self._check_crc(reply)
    return self._decode32(*reply[6:8], *reply[9:11])

clear_min_value ¤

clear_min_value() -> float

Clear the minimum value stored in the device memory.

Returns:

Type Description
float

The current value.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
def clear_min_value(self) -> float:
    """Clear the minimum value stored in the device memory.

    Returns:
        The current value.
    """
    # used Wireshark with the USBPcap plugin to eavesdrop on the
    # GMH_Transmit(1, 174, 0, 0.0, 1) call of the DLL to get the
    # hex values and message lengths
    code, value = self._invert(174), 1
    _ = self.write(
        bytes(
            [
                self._address,
                0xF6,
                self._crc(self._address, 0xF6),
                code,
                value,
                self._crc(code, value),
                0x00,
                0xFF,
                0x0C,
                0x00,
                0xFF,
                0x0C,
            ]
        )
    )
    reply = self.read(size=12, decode=False)
    self._check_crc(reply)
    return self._decode32(*reply[6:8], *reply[9:11])

display_range ¤

display_range() -> tuple[float, float]

Get the range of the display.

Returns:

Type Description
tuple[float, float]

The (minimum, maximum) value that the device can display.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
230
231
232
233
234
235
236
237
238
239
240
def display_range(self) -> tuple[float, float]:
    """Get the range of the display.

    Returns:
        The `(minimum, maximum)` value that the device can display.
    """
    reply = self._get(code=200)
    minimum = self._decode32(*reply[3:5], *reply[6:8])
    reply = self._get(code=201)
    maximum = self._decode32(*reply[3:5], *reply[6:8])
    return minimum, maximum

firmware_version ¤

firmware_version() -> tuple[int, int]

Get the version information of the firmware.

Returns:

Type Description
tuple[int, int]

The version number, version identifier.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
242
243
244
245
246
247
248
249
def firmware_version(self) -> tuple[int, int]:
    """Get the version information of the firmware.

    Returns:
        The version number, version identifier.
    """
    reply = self._get(code=254)
    return reply[4], self._invert(reply[3])

id_number ¤

id_number() -> str

Get the device ID (serial) number.

Returns:

Type Description
str

The ID (serial) number of the device.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
251
252
253
254
255
256
257
258
259
260
def id_number(self) -> str:
    """Get the device ID (serial) number.

    Returns:
        The ID (serial) number of the device.
    """
    reply = self._get(code=12)
    a = self._to_uint16(*reply[:2])
    b = self._to_uint16(*reply[3:5])
    return f"{self._to_uint32(a, b):x}"

max_value ¤

max_value() -> float

Get the maximum value that has been read.

Returns:

Type Description
float

The maximum value that has been read since the device was turned on or since clear_max_value was called.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
262
263
264
265
266
267
268
269
270
def max_value(self) -> float:
    """Get the maximum value that has been read.

    Returns:
        The maximum value that has been read since the device was turned on or since
            [clear_max_value][msl.equipment_resources.greisinger.gmh3000.GMH3000.clear_max_value]
            was called.
    """
    return self._decode(self._get(code=7))

measurement_range ¤

measurement_range() -> tuple[float, float]

Get the measurement range.

Returns:

Type Description
tuple[float, float]

The (minimum, maximum) value that the device can measure.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
272
273
274
275
276
277
278
279
280
281
282
def measurement_range(self) -> tuple[float, float]:
    """Get the measurement range.

    Returns:
        The `(minimum, maximum)` value that the device can measure.
    """
    reply = self._get(code=176)
    minimum = self._decode16(*reply[3:5])
    reply = self._get(code=177)
    maximum = self._decode16(*reply[3:5])
    return minimum, maximum

min_value ¤

min_value() -> float

Get the minimum value that has been read.

Returns:

Type Description
float

The minimum value that has been read since the device was turned on or since clear_min_value was called.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
284
285
286
287
288
289
290
291
292
def min_value(self) -> float:
    """Get the minimum value that has been read.

    Returns:
        The minimum value that has been read since the device was turned on or since
            [clear_min_value][msl.equipment_resources.greisinger.gmh3000.GMH3000.clear_min_value]
            was called.
    """
    return self._decode(self._get(code=6))

offset_correction ¤

offset_correction() -> float

Get the offset-correction value.

The zero point (intercept in a linear calibration equation) of the measurement will be displaced by this value to compensate for deviations in the temperature probe or in the measuring device.

Returns:

Type Description
float

The offset-correction value.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
294
295
296
297
298
299
300
301
302
303
304
305
def offset_correction(self) -> float:
    """Get the offset-correction value.

    The zero point (intercept in a linear calibration equation) of the
    measurement will be displaced by this value to compensate for
    deviations in the temperature probe or in the measuring device.

    Returns:
        The offset-correction value.
    """
    reply = self._get(code=216)
    return self._decode16(*reply[3:5])

power_off_time ¤

power_off_time() -> int

Get the power-off time.

Returns:

Type Description
int

The number of minutes that the device will automatically power off as soon as this time has elapsed if no key is pressed or if no interface communication takes place. A value of 0 means that power off is disabled.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
307
308
309
310
311
312
313
314
315
316
def power_off_time(self) -> int:
    """Get the power-off time.

    Returns:
        The number of minutes that the device will automatically power off as soon as
            this time has elapsed if no key is pressed or if no interface communication
            takes place. A value of 0 means that power off is disabled.
    """
    reply = self._get(code=222)
    return self._to_uint16(*reply[3:5])

resolution ¤

resolution() -> int

Get the measurement resolution.

Returns:

Type Description
int

The number of digits after the decimal point that is acquired for the measured value.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
318
319
320
321
322
323
324
325
326
327
328
def resolution(self) -> int:
    """Get the measurement resolution.

    Returns:
        The number of digits after the decimal point that is acquired for the measured value.
    """
    # The manual says to use code=204, however, using Wireshark to eavesdrop
    # on the GMH_Transmit(address, 204, ...) DLL call the actual code sent
    # is 0, which is the code to read the nominal value
    reply = self._get(code=0)
    return (self._invert(reply[0]) >> 3) - 15

scale_correction ¤

scale_correction() -> float

Get the scale-correction factor.

The scale (slope in a linear calibration equation) of the measurement will be changed by this factor to compensate for deviations in the temperature probe or in the measuring device.

Returns:

Type Description
float

The scale-correction factor.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
330
331
332
333
334
335
336
337
338
339
340
341
def scale_correction(self) -> float:
    """Get the scale-correction factor.

    The scale (slope in a linear calibration equation) of the measurement
    will be changed by this factor to compensate for deviations in the
    temperature probe or in the measuring device.

    Returns:
        The scale-correction factor.
    """
    reply = self._get(code=214)
    return self._decode16(*reply[3:5])

set_power_off_time ¤

set_power_off_time(minutes: int) -> int

Set the power-off time.

Parameters:

Name Type Description Default
minutes int

The number of minutes that the device will automatically power off as soon as this time has elapsed if no key is pressed or if no interface communication takes place. A value of 0 means that power off is disabled.

required

Returns:

Type Description
int

The actual power-off time that the device was set to. If you set the power-off time to a value greater than the maximum time allowed, the device automatically coerces the value to be the maximum time.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
def set_power_off_time(self, minutes: int) -> int:
    """Set the power-off time.

    Args:
        minutes: The number of minutes that the device will automatically power off
            as soon as this time has elapsed if no key is pressed or if no interface
            communication takes place. A value of 0 means that power off is disabled.

    Returns:
        The actual power-off time that the device was set to. If you set
            the power-off time to a value greater than the maximum time allowed,
            the device automatically coerces the value to be the maximum time.
    """
    # used Wireshark with the USBPcap plugin to eavesdrop on the
    # GMH_Transmit(1, 223, 0, 0.0, minutes) call of the DLL to get the
    # hex values and message lengths
    code = self._invert(223)
    _ = self.write(
        bytes(
            [
                self._address,
                0xF4,
                self._crc(self._address, 0xF4),
                code,
                0x00,
                self._crc(code, 0x00),
                0xFF,
                minutes,
                self._crc(0xFF, minutes),
            ]
        )
    )
    reply = self.read(size=9, decode=False)
    self._check_crc(reply)
    # do not check if reply[7]==minutes and raise an exception if not equal
    # because if, for example, minutes=121 the device will automatically
    # set the power-off time to the maximum value that it supports (120) and
    # raising an exception would be very confusing to the end user because
    # the power-off value has changed, but not to the expected value. It's
    # better to mention in the docstring that the returned value is what
    # actually happened so the end user can do their own checks.
    return reply[7]

status ¤

status() -> int

Get the system status.

The status value represents a bit mask:

Bit Index Value Description
0 1 Max. alarm
1 2 Min. alarm
2 4 Display over range
3 8 Display under range
4 16 Reserved
5 32 Reserved
6 64 Reserved
7 128 Reserved
8 256 Measuring over range
9 512 Measuring under range
10 1024 Sensor error
11 2048 Reserved
12 4096 System fault
13 8192 Calculation not possible
14 16384 Reserved
15 32768 Low battery

Returns:

Type Description
int

The system status.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
def status(self) -> int:
    """Get the system status.

    The status value represents a bit mask:

    | Bit Index | Value | Description              |
    | :-------: | :---: | :----------------------- |
    |   0       |     1 | Max. alarm               |
    |   1       |     2 | Min. alarm               |
    |   2       |     4 | Display over range       |
    |   3       |     8 | Display under range      |
    |   4       |    16 | Reserved                 |
    |   5       |    32 | Reserved                 |
    |   6       |    64 | Reserved                 |
    |   7       |   128 | Reserved                 |
    |   8       |   256 | Measuring over range     |
    |   9       |   512 | Measuring under range    |
    |   10      |  1024 | Sensor error             |
    |   11      |  2048 | Reserved                 |
    |   12      |  4096 | System fault             |
    |   13      |  8192 | Calculation not possible |
    |   14      | 16384 | Reserved                 |
    |   15      | 32768 | Low battery              |

    Returns:
        The system status.
    """
    reply = self._get(code=3)
    return self._to_uint16(*reply[:2])

unit ¤

unit() -> str

Get the measurement unit.

Returns:

Type Description
str

The measurement unit.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
416
417
418
419
420
421
422
423
424
425
426
427
428
429
def unit(self) -> str:
    """Get the measurement unit.

    Returns:
        The measurement unit.
    """
    reply = self._get(code=202)
    unit = self._to_uint16(*reply[3:5])
    if unit == 1:
        return "\u00b0C"
    if unit == 2:  # noqa: PLR2004
        return "\u00b0F"

    raise MSLConnectionError(self, f"Unimplemented unit ID {unit}")

value ¤

value() -> float

Get the current measurement value.

Returns:

Type Description
float

The current value.

Source code in packages/resources/src/msl/equipment_resources/greisinger/gmh3000.py
431
432
433
434
435
436
437
def value(self) -> float:
    """Get the current measurement value.

    Returns:
        The current value.
    """
    return self._decode(self._get(code=0))