Inheco Incubator Shaker#

The Inheco Incubator Shaker is a family of enclosed incubator devices with optional shaking, used for plate storage, temperature control, and orbital mixing.

Multiple units can be stacked (up to 5 “power credits” per stack) and controlled through a single USB connection. All 4 variants share the same serial firmware and a single backend covers the entire family.

Model

PLR Name

Shaking

Plate Format

Power Credits

Incubator MP

incubator_mp

no

Microplate

1.0

Incubator Shaker MP

incubator_shaker_mp

yes

Microplate

1.6

Incubator DWP

incubator_dwp

no

Deepwell Plate

1.25

Incubator Shaker DWP

incubator_shaker_dwp

yes

Deepwell Plate

2.5

See the Inheco Incubator Shaker product page for hardware specifications. Connect via USB-A/B (VID:PID 0403:6001). Each stack requires one USB cable and one power cable to the bottom-most unit; units above connect to the unit below via 15-pin SUB-D connectors.

Setup#

Set the DIP switch on the back of the bottom-most unit to a unique 4-bit address (0–15). This address identifies the stack.

from pylabrobot.legacy.storage.inheco import (
  IncubatorShakerStack,
  InhecoIncubatorShakerStackBackend,
)

backend = InhecoIncubatorShakerStackBackend(dip_switch_id=2)
stack = IncubatorShakerStack(backend=backend)
await stack.setup()

The stack auto-discovers all connected units during setup(). Pass verbose=True to see serial port, DIP switch verification, and unit composition.

Addressing units#

Access individual units by index. The stack reports how many units are connected:

stack.num_units  # e.g. 2
unit_0 = stack[0]
unit_1 = stack[1]

Loading tray#

Each unit has a motorized loading tray (drawer) for plate access:

await stack[0].open()
# ... load or unload plate ...
await stack[0].close()
# Check plate presence (reflection-based sensor)
await stack[0].request_plate_in_incubator()  # returns True/False

Temperature control#

await stack[0].start_temperature_control(37)
current = await stack[0].get_temperature()
print(f"{current:.1f} °C")

The device has three independent temperature sensors ("main", "dif", "boost"). You can also read a geometric "mean" of all three:

await stack[0].get_temperature(sensor="mean")
# Block until temperature is reached
await stack[0].wait_for_temperature(sensor="mean", tolerance=0.2)
await stack[0].stop_temperature_control()

Shaking#

Shaker models (MP Shaker and DWP Shaker) support programmable shaking patterns.

# Orbital shaking at 800 rpm (default pattern)
await stack[0].shake(rpm=800)
await stack[0].stop_shaking()

Predefined shaking patterns:

Pattern

Description

orbital

Circular shaking (default)

elliptical

Elliptical motion

figure_eight

Figure-eight (Lissajous) motion

linear_x

Linear motion along X

linear_y

Linear motion along Y

await stack[0].shake(pattern="figure_eight", rpm=400)
await stack[0].stop_shaking()

Stack-level commands#

The stack frontend provides master commands that apply to all units at once:

# Open/close all loading trays
await stack.open_all()
await stack.close_all()
# Temperature control for all units
await stack.start_all_temperature_control(target_temperature=37)
await stack.get_all_temperatures()  # {0: 37.1, 1: 36.8}
await stack.stop_all_temperature_control()
# Query states
await stack.request_loading_tray_states()      # {0: 'closed', 1: 'closed'}
await stack.request_temperature_control_states() # {0: False, 1: False}
await stack.request_shaking_states()             # {0: False, 1: False}

Multiple stacks#

When using more than one physical stack, pass the serial port explicitly to avoid ambiguity:

backend_a = InhecoIncubatorShakerStackBackend(dip_switch_id=2, port="/dev/cu.usbserial-130")
stack_a = IncubatorShakerStack(backend=backend_a)
await stack_a.setup()

backend_b = InhecoIncubatorShakerStackBackend(dip_switch_id=7, port="/dev/cu.usbserial-42")
stack_b = IncubatorShakerStack(backend=backend_b)
await stack_b.setup()

Teardown#

await stack.stop()

This stops all temperature control and shaking before disconnecting from the stack.