Skip to content
khalil edited this page Oct 3, 2026 · 1 revision

The Mixer V1 doses two masterbatch/additive hoppers into a mixing chamber with a simple on/off mixing motor. Each hopper can run at a fixed manual RPM, or — when subscribed to a running Extruder — dose automatically as a percentage of the extruder's live output rate, so the mix ratio holds steady as extrusion speed changes.

Machine ID 18
Schema mixer_v1.yaml
Code mixer_v1.rs

Not yet merged into jse-control-v2 — links above point at mixer-v2-pr. Update them once the branch lands.

In the tables below, <hopper> stands for hopper_a or hopper_b: every dosing setting exists once per hopper.

Hardware

Role Terminal Purpose
0 EK1100 Bus coupler
1 EL2002 Mixing motor relay
2 EL7041-0052 Hopper A stepper driver
3 EL7041-0052 Hopper B stepper driver

Both steppers run in DirectVelocity mode with a 2800 mA current limit, configured and DC-synced at build time. Roles are assigned in Identification.

How it works

flowchart TB
    subgraph EXT["Extruder (separate machine)"]
        RPM["motor.rpm measurement"]:::hardware
    end
    subgraph SUB["<a href='https://github.com/qitechgmbh/control/blob/mixer-v2-pr/qitech_control/src/machines/mixer_v1.rs'>Subscription</a>"]
        direction LR
        REM["RemoteProperty&lt;AngularVelocity&gt;<br/>read each act() tick"]:::control
        RATE["current_extruder_output_rate<br/>rpm × extruder_kg_per_rpm"]:::control
        REM --> RATE
    end
    subgraph DOSE["Per hopper"]
        direction LR
        PCT["dosing_percent"]:::control
        CAL["calibration_steps_per_kgh"]:::control
        MIX["masterbatch kg/h = output rate × dosing_percent ÷ 100<br/>steps/s = masterbatch kg/h × calibration_steps_per_kgh"]:::control
        MAN["target_rpm<br/>manual mode, no subscription"]:::control
        PCT --> MIX
        CAL --> MIX
    end
    subgraph HW["Hardware"]
        direction LR
        STEP["EL7041-0052<br/>StepperVelocityEL70x1Device"]:::hardware
    end
    RPM --> REM
    RATE --> MIX
    MIX -- "subscribed" --> STEP
    MAN -- "not subscribed" --> STEP
    classDef frontend fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef control fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef framework fill:#fef3c7,stroke:#b45309,color:#78350f
    classDef lib fill:#ede9fe,stroke:#6d28d9,color:#4c1d95
    classDef hardware fill:#f1f5f9,stroke:#475569,color:#0f172a
Loading

Controllers

Mixing motor

A plain relay, EL2002 output 0: mixing_motor.start / mixing_motor.stop commands drive mixing_motor_on directly, no regulation involved.

Hopper dosing

Each hopper (StepperVelocityEL70x1Device, 200 full steps/rev) is driven by set_speed, signed by the <hopper>.forward direction flag. hopper_<x>_rpm reports back the same value converted to RPM, before the stepper driver sees it.

Two sources can drive the stepper's step rate, and whichever last wrote wins — there is no blend:

Mode Trigger Formula
Manual <hopper>.target_rpm changes steps/s = target_rpm × 200 ÷ 60
Ratio (extruder-linked) Every act() tick while an extruder subscription is active masterbatch kg/h = extruder_rpm × extruder_kg_per_rpm × dosing_percent ÷ 100
steps/s = masterbatch kg/h × calibration_steps_per_kgh

Ratio mode re-evaluates every tick (not just on config change), so the hopper speed tracks the extruder's live RPM continuously. With no subscription, current_extruder_output_rate() returns 0, so ratio mode drives the hopper to a stop rather than falling back to the manual RPM.

Parameter Unit Default Range
extruder_kg_per_rpm kg/h per rpm 0.1 ≥ 0 — converts the subscribed extruder's screw rpm into a kg/h output estimate
<hopper>.target_rpm rpm 0 ≥ 0
<hopper>.forward bool true Direction of the stepper
<hopper>.dosing_percent % 0 0–100 (UI step 0.1) — share of the extruder's output this hopper doses
hopper_a.calibration_steps_per_kgh steps/s per kg/h 34.47 ≥ 0
hopper_b.calibration_steps_per_kgh steps/s per kg/h 6.37 ≥ 0

<hopper>.enable / .disable gate the stepper driver's own enable line; <hopper>_ready and <hopper>_error mirror the terminal's input word each tick.

Calibration

The Settings page runs a physical collection test rather than requiring the operator to compute calibration_steps_per_kgh by hand: it drives the hopper at a chosen test RPM for a chosen duration (enable + manual target RPM, defaults 20 rpm / 60 s), the operator weighs what was collected, and the page derives:

rate_kg_per_hour          = collected_grams × 3.6 ÷ duration_seconds
steps_per_second_at_test  = test_rpm × 200 ÷ 60
calibration_steps_per_kgh = steps_per_second_at_test ÷ rate_kg_per_hour

Save Calibration writes the result to <hopper>.calibration_steps_per_kgh; only one hopper can run a calibration test at a time.

Extruder subscription

The Mixer subscribes directly to another machine's live measurement using the framework's cross-machine subscription mechanism (SubscribeMachine / UnsubscribeMachine runtime requests), rather than polling a REST endpoint. subscribe() stores a RemoteProperty<AngularVelocity> bound to the provider's motor.rpm measurement; unsubscribe() clears it if the unsubscribing identity matches. The adapter resolves which machine is currently subscribed by scanning instance.subscriptions for an Extruder V1/V2 identity, so the frontend's "connected machine" selector reflects the real subscription state rather than a locally-cached choice.

Unsubscribing must go through UnsubscribeMachine rather than simply clearing the reference client-side — doing the latter leaves the subscriber holding a now-invalid lifetime token, which panics on the next read. (This was a real bug, fixed upstream in qitech_framework: unsubscribe() is now called on the machine before its token is invalidated.)

Clone this wiki locally