abse/isv57t090-motion-controller

A 36 V-powered RP2040 motion-controller PCB that regulates 5 V, drives isolated STEP/DIR servo inputs, monitors alarms, temperature, limits and E-stop signals, and provides protected safety/field connectors.

Version
0.0.67
License
unset
Stars
0

examples/ISV57T090MotionController/firmware/main.py

"""MicroPython safety supervisor for the iSV57T-090 controller.

This is a fail-closed safety supervisor, not a motion-command implementation.
GPIO12 drives only the board's low-current external relay-coil output; the
relay's suitably rated contacts must interrupt the motor's separate DC supply.
"""

from machine import ADC, I2C, Pin, WDT
import time

from thermal_guard import ThermalGuard, remote_ntc_temperature_c, tmp117_temperature_c


# Set every motion/safety output inactive before enabling the sensor bus.
pulse = Pin(2, Pin.OUT, value=0)
direction = Pin(3, Pin.OUT, value=0)
safe_relay = Pin(12, Pin.OUT, value=0)
status_led = Pin(25, Pin.OUT, value=1)  # Solid=fault/off, blink=armed.

estop = Pin(4, Pin.IN)  # NC switch to GND; high/open is a fault.
motor_alarm = Pin(5, Pin.IN)  # iSV57T: low=normal, high/open=fault.
remote_ntc = ADC(26)  # ADC0, never a digital-only GPIO.
i2c = I2C(0, sda=Pin(8), scl=Pin(9), freq=100000)
guard = ThermalGuard()
watchdog = WDT(timeout=2000)
last_fault = None
heartbeat = 0


def read_board_temperature_c():
    sample = i2c.readfrom_mem(0x48, 0x00, 2)
    return tmp117_temperature_c(sample[0], sample[1])


while True:
    try:
        board_c = read_board_temperature_c()
        remote_c = remote_ntc_temperature_c(remote_ntc.read_u16())
        allow_power = guard.update(
            board_c,
            remote_c,
            estop_closed=(estop.value() == 0),
            alarm_ok=(motor_alarm.value() == 0),
        )
    except Exception as exc:
        allow_power = guard.trip("temperature_read_error")
        print("SAFETY SENSOR ERROR:", exc)

    # The motor alarm cannot be valid until its supply contactor closes.
    # ThermalGuard allows a bounded alarm-startup window, but motion must
    # remain disabled until guard.ready is true. This file generates no motion.
    # Removing power or rebooting releases the relay by base pull-down.
    if not allow_power:
        safe_relay.value(0)
        pulse.value(0)
        status_led.value(1)
    else:
        safe_relay.value(1)
        if not guard.ready:
            pulse.value(0)
            status_led.value(1)

    if guard.fault != last_fault:
        last_fault = guard.fault
        if last_fault is not None:
            print("LATCHED SAFETY FAULT:", last_fault)

    heartbeat += 1
    if allow_power and guard.ready and heartbeat >= 10:
        heartbeat = 0
        status_led.value(1 - status_led.value())
    watchdog.feed()
    time.sleep_ms(50)