Addon Motors
-
+
Stepper 3
+
+
Stepper 5
+
+
+ {state?.stepper_state?.stepper5_mode || "Standby"}
+
+
diff --git a/electron/src/machines/winder/gluetex/gluetexNamespace.ts b/electron/src/machines/winder/gluetex/gluetexNamespace.ts
index 576a61b3..49772c98 100644
--- a/electron/src/machines/winder/gluetex/gluetexNamespace.ts
+++ b/electron/src/machines/winder/gluetex/gluetexNamespace.ts
@@ -305,6 +305,7 @@ export const stateEventDataSchema = z.object({
connected_machine_state: connectedMachineStateSchema,
addon_motor_3_state: addonMotorStateSchema,
addon_motor_4_state: addonMotorStateSchema,
+ addon_motor_5_state: addonMotorStateSchema,
slave_puller_state: slavePullerStateSchema,
addon_tension_arm_state: tensionArmStateSchema,
tension_arm_monitor_state: tensionArmMonitorStateSchema,
@@ -331,11 +332,14 @@ export type MotorRatiosState = {
stepper3_slave: number;
stepper4_master: number;
stepper4_slave: number;
+ stepper5_master: number;
+ stepper5_slave: number;
};
export type StepperState = {
stepper3_mode: StepperMode;
stepper4_mode: StepperMode;
+ stepper5_mode: StepperMode;
};
/**
@@ -344,12 +348,15 @@ export type StepperState = {
function getMotorRatiosFromBackend(
motor3: z.infer
,
motor4: z.infer,
+ motor5: z.infer,
): MotorRatiosState {
return {
stepper3_master: motor3.master_ratio,
stepper3_slave: motor3.slave_ratio,
stepper4_master: motor4.master_ratio,
stepper4_slave: motor4.slave_ratio,
+ stepper5_master: motor5.master_ratio,
+ stepper5_slave: motor5.slave_ratio,
};
}
@@ -371,6 +378,15 @@ function getStepper4ModeFromBackend(
return motor4.enabled ? "Run" : "Standby";
}
+/**
+ * Helper to determine stepper5 mode from backend enabled state
+ */
+function getStepper5ModeFromBackend(
+ motor5: z.infer,
+): StepperMode {
+ return motor5.enabled ? "Run" : "Standby";
+}
+
/**
* Helper to determine heating mode from backend enabled state
*/
@@ -516,10 +532,13 @@ const DEFAULT_ADDON_STATE = {
stepper3_slave: 1.0,
stepper4_master: 1.0,
stepper4_slave: 1.0,
+ stepper5_master: 1.0,
+ stepper5_slave: 1.0,
},
stepper_state: {
stepper3_mode: "Standby" as StepperMode,
stepper4_mode: "Standby" as StepperMode,
+ stepper5_mode: "Standby" as StepperMode,
},
heating_state: {
heating_mode: "Standby" as HeatingMode,
@@ -607,6 +626,7 @@ export function gluetexMessageHandler(
const motorRatiosState = getMotorRatiosFromBackend(
stateEvent.data.addon_motor_3_state,
stateEvent.data.addon_motor_4_state,
+ stateEvent.data.addon_motor_5_state,
);
// Derive stepper modes from backend enabled state
@@ -616,6 +636,9 @@ export function gluetexMessageHandler(
const stepper4Mode = getStepper4ModeFromBackend(
stateEvent.data.addon_motor_4_state,
);
+ const stepper5Mode = getStepper5ModeFromBackend(
+ stateEvent.data.addon_motor_5_state,
+ );
// Derive heating mode from backend enabled state
const heatingMode = getHeatingModeFromBackend(
@@ -631,6 +654,7 @@ export function gluetexMessageHandler(
stepper_state: {
stepper3_mode: stepper3Mode,
stepper4_mode: stepper4Mode,
+ stepper5_mode: stepper5Mode,
},
heating_state: {
heating_mode: heatingMode,
diff --git a/electron/src/machines/winder/gluetex/useGluetex.ts b/electron/src/machines/winder/gluetex/useGluetex.ts
index c1dab6bd..57fba138 100644
--- a/electron/src/machines/winder/gluetex/useGluetex.ts
+++ b/electron/src/machines/winder/gluetex/useGluetex.ts
@@ -218,6 +218,17 @@ export function useGluetex() {
z.object({ SetAddonMotor4SlaveRatio: z.number() }),
);
+ // Addon Motor 5 mutations
+ const { request: requestAddonMotor5SetEnabled } = useMachineMutation(
+ z.object({ SetAddonMotor5Enabled: z.boolean() }),
+ );
+ const { request: requestAddonMotor5SetMasterRatio } = useMachineMutation(
+ z.object({ SetAddonMotor5MasterRatio: z.number() }),
+ );
+ const { request: requestAddonMotor5SetSlaveRatio } = useMachineMutation(
+ z.object({ SetAddonMotor5SlaveRatio: z.number() }),
+ );
+
// Slave Puller mutations
const { request: requestSlavePullerSetEnabled } = useMachineMutation(
z.object({ SetSlavePullerEnabled: z.boolean() }),
@@ -818,6 +829,47 @@ export function useGluetex() {
);
};
+ const setStepper5Mode = (mode: StepperMode) => {
+ const enabled = mode === "Run";
+ updateStateOptimistically(
+ (current) => {
+ current.data.stepper_state.stepper5_mode = mode;
+ },
+ () => {
+ requestAddonMotor5SetEnabled({
+ machine_identification_unique: machineIdentification,
+ data: { SetAddonMotor5Enabled: enabled },
+ });
+ },
+ );
+ };
+
+ const setStepper5Master = (value: number) => {
+ updateStateOptimistically(
+ (current) => {
+ current.data.motor_ratios_state.stepper5_master = value;
+ },
+ () =>
+ requestAddonMotor5SetMasterRatio({
+ machine_identification_unique: machineIdentification,
+ data: { SetAddonMotor5MasterRatio: value },
+ }),
+ );
+ };
+
+ const setStepper5Slave = (value: number) => {
+ updateStateOptimistically(
+ (current) => {
+ current.data.motor_ratios_state.stepper5_slave = value;
+ },
+ () =>
+ requestAddonMotor5SetSlaveRatio({
+ machine_identification_unique: machineIdentification,
+ data: { SetAddonMotor5SlaveRatio: value },
+ }),
+ );
+ };
+
// ========== Slave Puller Functions ==========
const setSlavePullerEnabled = (enabled: boolean) => {
@@ -1194,6 +1246,7 @@ export function useGluetex() {
// Addon action functions (local only)
setStepper3Mode,
setStepper4Mode,
+ setStepper5Mode,
setHeatingMode,
setTemperature1Min,
setTemperature1Max,
@@ -1203,5 +1256,7 @@ export function useGluetex() {
setStepper3Slave,
setStepper4Master,
setStepper4Slave,
+ setStepper5Master,
+ setStepper5Slave,
};
}
diff --git a/machines/src/gluetex/act.rs b/machines/src/gluetex/act.rs
index 3a7a2197..7d7686c3 100644
--- a/machines/src/gluetex/act.rs
+++ b/machines/src/gluetex/act.rs
@@ -21,6 +21,7 @@ impl MachineAct for Gluetex {
// sync addon motor speeds
self.sync_addon_motor_3_speed(now);
self.sync_addon_motor_4_speed(now);
+ self.sync_addon_motor_5_speed(now);
// sync the traverse speed
self.sync_traverse_speed();
diff --git a/machines/src/gluetex/api.rs b/machines/src/gluetex/api.rs
index 59c66574..d478a53a 100644
--- a/machines/src/gluetex/api.rs
+++ b/machines/src/gluetex/api.rs
@@ -139,10 +139,13 @@ pub enum Mutation {
// Addon Motors
SetAddonMotor3Enabled(bool),
SetAddonMotor4Enabled(bool),
+ SetAddonMotor5Enabled(bool),
SetAddonMotor3MasterRatio(f64),
SetAddonMotor3SlaveRatio(f64),
SetAddonMotor4MasterRatio(f64),
SetAddonMotor4SlaveRatio(f64),
+ SetAddonMotor5MasterRatio(f64),
+ SetAddonMotor5SlaveRatio(f64),
// Slave Puller
SetSlavePullerEnabled(bool),
@@ -235,6 +238,8 @@ pub struct StateEvent {
pub addon_motor_3_state: AddonMotorState,
/// addon motor 4 state
pub addon_motor_4_state: AddonMotorState,
+ /// addon motor 5 state
+ pub addon_motor_5_state: AddonMotorState,
/// slave puller state
pub slave_puller_state: SlavePullerState,
/// addon tension arm state
@@ -563,6 +568,18 @@ impl MachineApi for Gluetex {
self.addon_motor_4_controller.set_slave_ratio(ratio);
self.emit_state();
}
+ Mutation::SetAddonMotor5Enabled(enabled) => {
+ self.addon_motor_5_controller.set_enabled(enabled);
+ self.emit_state();
+ }
+ Mutation::SetAddonMotor5MasterRatio(ratio) => {
+ self.addon_motor_5_controller.set_master_ratio(ratio);
+ self.emit_state();
+ }
+ Mutation::SetAddonMotor5SlaveRatio(ratio) => {
+ self.addon_motor_5_controller.set_slave_ratio(ratio);
+ self.emit_state();
+ }
Mutation::SetSlavePullerEnabled(enabled) => {
self.slave_puller_user_enabled = enabled;
// Re-apply the current mode to update the controller state
diff --git a/machines/src/gluetex/emit.rs b/machines/src/gluetex/emit.rs
index f9e73336..29b7ba5e 100644
--- a/machines/src/gluetex/emit.rs
+++ b/machines/src/gluetex/emit.rs
@@ -450,6 +450,11 @@ impl Gluetex {
master_ratio: self.addon_motor_4_controller.get_master_ratio(),
slave_ratio: self.addon_motor_4_controller.get_slave_ratio(),
},
+ addon_motor_5_state: api::AddonMotorState {
+ enabled: self.addon_motor_5_controller.is_enabled(),
+ master_ratio: self.addon_motor_5_controller.get_master_ratio(),
+ slave_ratio: self.addon_motor_5_controller.get_slave_ratio(),
+ },
slave_puller_state: api::SlavePullerState {
enabled: self.slave_puller_user_enabled,
forward: self.slave_puller_speed_controller.get_forward(),
diff --git a/machines/src/gluetex/mod.rs b/machines/src/gluetex/mod.rs
index 03afcb70..b2360bdc 100644
--- a/machines/src/gluetex/mod.rs
+++ b/machines/src/gluetex/mod.rs
@@ -144,6 +144,7 @@ pub struct Gluetex {
// addon motors
pub addon_motor_3: StepperVelocityEL70x1,
pub addon_motor_4: StepperVelocityEL70x1,
+ pub addon_motor_5: StepperVelocityEL70x1,
// controllers
pub traverse_controller: TraverseController,
@@ -152,6 +153,7 @@ pub struct Gluetex {
// addon motor controllers
pub addon_motor_3_controller: AddonMotorController,
pub addon_motor_4_controller: AddonMotorController,
+ pub addon_motor_5_controller: AddonMotorController,
// temperature controllers (PID-controlled heaters with temperature sensors)
pub temperature_controller_1: TemperatureController,
@@ -424,6 +426,14 @@ impl Gluetex {
.sync_motor_speed(&mut self.addon_motor_4, puller_angular_velocity);
}
+ /// Sync addon motor 5 speed based on puller angular velocity and ratio
+ /// called by `act`
+ pub fn sync_addon_motor_5_speed(&mut self, t: Instant) {
+ let puller_angular_velocity = self.puller_speed_controller.calc_angular_velocity(t);
+ self.addon_motor_5_controller
+ .sync_motor_speed(&mut self.addon_motor_5, puller_angular_velocity);
+ }
+
/// Sync slave puller speed based on master puller speed and slave tension arm
/// called by `act`
pub fn sync_slave_puller_speed(&mut self, t: Instant) {
diff --git a/machines/src/gluetex/new.rs b/machines/src/gluetex/new.rs
index b1111815..09bb7ace 100644
--- a/machines/src/gluetex/new.rs
+++ b/machines/src/gluetex/new.rs
@@ -333,6 +333,40 @@ impl MachineNewTrait for Gluetex {
device.0
};
+ // Role 11: Addon Motor 5 EL7031-0030
+ let el7031_addon5 = {
+ let device = get_ethercat_device::(
+ hardware,
+ params,
+ 11,
+ vec![EL7031_0030_IDENTITY_A, EL7031_IDENTITY_A, EL7031_IDENTITY_B],
+ )
+ .await?;
+
+ let el7031_0030_config = EL7031_0030Configuration {
+ stm_features: el7031_0030::coe::StmFeatures {
+ operation_mode: EL70x1OperationMode::DirectVelocity,
+ speed_range: shared_config::el70x1::EL70x1SpeedRange::Steps1000,
+ ..Default::default()
+ },
+ stm_motor: StmMotorConfiguration {
+ max_current: 1500,
+ ..Default::default()
+ },
+ pdo_assignment: EL7031_0030PredefinedPdoAssignment::VelocityControlCompact,
+ ..Default::default()
+ };
+
+ device
+ .0
+ .write()
+ .await
+ .write_config(&device.1, &el7031_0030_config)
+ .await?;
+
+ device.0
+ };
+
// Digital outputs for SSR control (24V to external SSRs for 60W heaters)
let heater_ssr_1 = DigitalOutput::new(el2008.clone(), EL2008Port::DO1);
let heater_ssr_2 = DigitalOutput::new(el2008.clone(), EL2008Port::DO2);
@@ -468,6 +502,10 @@ impl MachineNewTrait for Gluetex {
el7031_addon4.clone(),
EL7031_0030StepperPort::STM1,
),
+ addon_motor_5: StepperVelocityEL70x1::new(
+ el7031_addon5,
+ EL7031_0030StepperPort::STM1,
+ ),
addon_tension_arm: TensionArm::new(AnalogInput::new(
el7031_addon4,
EL7031_0030AnalogInputPort::AI1,
@@ -476,6 +514,8 @@ impl MachineNewTrait for Gluetex {
super::addon_motor_controller::AddonMotorController::new(200),
addon_motor_4_controller:
super::addon_motor_controller::AddonMotorController::new(200),
+ addon_motor_5_controller:
+ super::addon_motor_controller::AddonMotorController::new(200),
tension_arm: TensionArm::new(AnalogInput::new(
el7031_0030,
EL7031_0030AnalogInputPort::AI1,