Skip to Content

← CANopen VFD Control of a LOVATO VLB3 from a Raspberry PLC

Two-axis solar trackersRaspberry PLC 21CANopenControl

CANopen VFD Control of a LOVATO VLB3 from a Raspberry PLC — full example

Control a LOVATO VLB3 variable frequency drive over CANopen SDO from a Raspberry PLC 21 in Python. Run, stop, frequency setpoint, current and errors.

Complete, runnable program for the Raspberry PLC 21 (lovato-vlb3-vfd-canopen-sdo.py): wiring header, requirements and integration notes included.

Download the full project pack — freeThis example + the related ones + bill of materials

Read-only preview.

#!/usr/bin/env python3
# -----------------------------------------------------------------------------
# COMPLETE EXAMPLE — LOVATO VLB3 VFD control via CANopen SDO
#
# Device:    Raspberry PLC 21 (Industrial Shields), built-in CAN bus (can0)
# Based on:  dual-axis solar tracker project
#
# CAN bus / requirements:
#   sudo ip link set can0 up type can bitrate 125000
#   pip3 install canopen
#   EDS file of the LOVATO VLB3 VFD in the same directory (VLB3.eds)
#
# Logic:
#   1. Connect to the can0 bus and register the VFD node (node 3).
#   2. Motor parameterization via SDO: boost, cos φ and nominal
#      voltage/frequency, written once at startup.
#   3. Operation via the control word 0x400B sub 1:
#        0x0061 Run Forward · 0x0062 Run Backward · 0x0060 Stop with ramp
#   4. Frequency setpoint in 0x400B sub 3 (tenths of Hz: 250 = 25.0 Hz).
#   5. Monitoring: motor current (0x2D88, tenths of A) and CiA error code
#      (0x603F). On error, immediate stop and notification.
# -----------------------------------------------------------------------------

import time
import canopen

CAN_CHANNEL = 'can0'
CAN_BITRATE = 125000
NODE_VFD    = 3
EDS_VFD     = 'VLB3.eds'

# --- Control word 0x400B sub 1 ------------------------------------------------
CMD_RUN_FWD = 0x0061   # run forward
CMD_RUN_BWD = 0x0062   # run backward
CMD_STOP    = 0x0060   # stop with deceleration ramp

# --- Motor parameters (nameplate of the axis motor) ----------------------------
BOOST_PCT       = 25     # torque boost at low frequency (%)
COS_PHI_x100    = 76     # cos φ = 0.76
V_NOMINAL_V     = 230    # nominal voltage (V)
F_NOMINAL_x10   = 500    # nominal frequency = 50.0 Hz


def connect():
    """Opens the CAN bus and returns (network, VFD node)."""
    network = canopen.Network()
    network.connect(channel=CAN_CHANNEL, bustype='socketcan',
                    bitrate=CAN_BITRATE)
    driver = network.add_node(NODE_VFD, EDS_VFD)
    driver.nmt.state = 'OPERATIONAL'
    return network, driver


def parameterize_motor(driver):
    """Writes the motor nameplate parameters via SDO (once at startup)."""
    driver.sdo[0x2B12][1].write(BOOST_PCT)      # torque boost
    driver.sdo[0x2C01][5].write(COS_PHI_x100)   # cos φ
    driver.sdo[0x2C01][7].write(V_NOMINAL_V)    # nominal V
    driver.sdo[0x2C01][8].write(F_NOMINAL_x10)  # nominal f (x0.1 Hz)
    print("VFD parameterized (boost, cos phi, nominal V/f)")


def run(driver, forward=True, frequency_hz=25.0):
    """Starts the motor in the requested direction with the given setpoint."""
    driver.sdo[0x400B][3].write(int(frequency_hz * 10))  # setpoint in 0.1 Hz
    cmd = CMD_RUN_FWD if forward else CMD_RUN_BWD
    driver.sdo[0x400B][1].write(cmd)
    direction = "FORWARD" if forward else "BACKWARD"
    print(f"Running {direction} at {frequency_hz:.1f} Hz")


def stop(driver):
    """Stop with deceleration ramp."""
    driver.sdo[0x400B][1].write(CMD_STOP)
    print("Stop with ramp")


def read_current(driver):
    """Motor current in amps (0x2D88 returns tenths of A)."""
    return driver.sdo[0x2D88].raw * 0.1


def read_error(driver):
    """CiA 402 error code (0 = no error)."""
    return driver.sdo[0x603F].raw


def monitor(driver, seconds):
    """Watches current and error during a maneuver; stops on fault."""
    t_end = time.time() + seconds
    while time.time() < t_end:
        amps = read_current(driver)
        err  = read_error(driver)
        print(f"  Motor I = {amps:5.2f} A   error = 0x{err:04X}")
        if err != 0:
            stop(driver)
            raise RuntimeError(f"VFD in fault: code 0x{err:04X}")
        time.sleep(1)


if __name__ == '__main__':
    network, driver = connect()
    try:
        parameterize_motor(driver)

        # Demo maneuver: 10 s forward, stop, 10 s backward, stop.
        run(driver, forward=True, frequency_hz=25.0)
        monitor(driver, 10)
        stop(driver)
        time.sleep(3)                      # wait for the ramp to finish

        run(driver, forward=False, frequency_hz=15.0)
        monitor(driver, 10)
        stop(driver)
    finally:
        # Whatever happens, the motor ends up stopped and the bus closed.
        try:
            stop(driver)
        except Exception:
            pass
        network.disconnect()
        print("CAN bus closed")
Download the full project pack — freeThis example + the related ones + bill of materials