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 |
|
no |
Microplate |
1.0 |
Incubator Shaker MP |
|
yes |
Microplate |
1.6 |
Incubator DWP |
|
no |
Deepwell Plate |
1.25 |
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 |
|---|---|
|
Circular shaking (default) |
|
Elliptical motion |
|
Figure-eight (Lissajous) motion |
|
Linear motion along X |
|
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.