emDrive User Manual

Scripting

This page will describe how to use the scripting feature. To use the feature emDrive Configurator needs to have an open connection with an emDrive device. Scripts are written in C# (.cs files). To open the Script Window navigate to the menu bar, Device > Script.

User interface

ScriptWindow.png

The Browse button will open a File Open window to choose the script file. Once a file is loaded the file path will be displayed in the textbox next to it.

The Run script button will start the execution of the script.

Stop will stop the execution of the script.

The Script has infinite loop checkbox is used for scripts with long-running loops. This will automatically get set when the file is loaded if it contains the Cancellation Token implementation.

Scripts can be added to Shortcuts by selecting the desired shortcut in the Set script to shortcut button drop-down menu and clicking the Set button.

Long-running scripts cannot be used with Shortcuts.

The window also comes with a simple script editor that can be opened with the Edit script.

Below these controls is the Command Overview section which will be populated when certain API calls are made.

To the right of it is the Log section which will print text if the Logging API calls are made.

Creating a script file

Script files are written in C#. Create a .cs file in your desired text editor or download one of the templates provided in the attachments.

Every script needs the using System; and using emDrive_Configurator; using statements as well as a public class Script with the public void Main() method.

Version 2.5.4.0 and lower use using eDrive_Configurator; instead of using emDrive_Configurator;.

If long-running or infinite loops are used the user must add using System.Threading; and change the main method to public void Main(CancellationToken).

This is available only from version 2.9.0.0 and onwards. Long-running scripts should not be used in versions below 2.9.0.0.

Basic template

The basic template is used for simple scripts that don't contain long-running loops.

/*
  Author:       Amadej Arnus
  Date:         2024-05-08
*/

using System;
using emDrive_Configurator;

public class Script
{
    public void Main()
    {
        // Your code goes here
    }
}
Long-running template

This template is used for scripts that are expected to contain long-running or infinite loops. This allows the user to click Stop in the Script Window which will cancel the cancellation token and exit the script execution. The user must check for CancellationToken.IsCancellationRequested value in their loops for this to work.

/*
  Author:       Amadej Arnus
  Date:         2024-05-98
*/

using System;
using emDrive_Configurator;

using System.Threading;

public class Script
{
    public void Main(CancellationToken cancellationToken)
    {
        // Your code goes here

        // Example of loop with cancellation token
        while (true)
        {
            // Your code goes here

            if (cancellationToken.IsCancellationRequested)
            {
                break;
            }
        }
    }
}

Scripting API

The following section will list and describe all of the available API calls.

Script - Watch Interoperability

This functionality is only available for emDrive Configurator 2.13.0.0 and later.

These API calls allow the user to add virtual script objects to Watch and write to them, as well as request the last value read for a specific object already in Watch. This can be achieved by using the WatchScriptObject class or the method calls described below.

WatchScriptObject class

The WatchScriptObject class uses the API calls described below but is a more user-friendly approach to this functionality. Unless specified the index and subindex properties will be set automatically. index will always be 0xFFFF and subindex will increment from 0 onwards. If the user wishes to reset the subindex counter they must call the ResetSubIndexCounter method.

The TryToAddToWatch method will attempt to add the virtual object to Watch. See TryAddScriptEntryToWatch method for possible errors.

The TryToAddValueToWatch method will attempt to add the value to the already created virtual object in Watch. See TryAddScriptEntryValue method for possible errors.

public class WatchScriptObject
{
    // Properties
    public string Name { get; }
    public int NodeId { get; } // Default: 0
    public uint Index { get; } // Default: 0xFFFF
    public ushort SubIndex { get; }
    public ushort DataType { get; }
  
    // Constructors
    public WatchScriptObject(string name, ushort datatype);
    public WatchScriptObject(string name, ushort subindex, ushort datatype);
    public WatchScriptObject(string name, uint index, ushort subindex, ushort datatype);

    // Methods
    public bool TryToAddToWatch();
    public bool TryToAddValueToWatch(dynamic value);
    public static void ResetSubIndexCounter();
}

TryAddScriptEntryToWatch method

Attempts to create a new script virtual object in Watch with the specified name, nodeId, index, subindex, and datatype. Returns true if successful or false if failed.

public static bool TryAddScriptEntryToWatch(string name, int nodeId, uint index, ushort subindex, ushort datatype)

Fail reasons:

TryAddScriptEntryValue method

Attempts to write value to the script virtual object in Watch. Returns true if successful or false if failed.

public static bool TryAddScriptEntryValue(int nodeId, uint index, ushort subindex, dynamic value)

Fail reasons:

TryGetLastWatchValue method

Attempts to get the last read value of the object specified with nodeId, index, subindex and saves it to value. Returns true if successful or false if failed.

public static bool TryGetWatchValue(int nodeId, uint index, ushort subindex, out double value)

Fail reasons:

Example file

Example file for Script - Watch Interoperability (also available as download)
/*
  Author:       Amadej Arnus
  Date:         2024-04-23
*/

using System;
using System.Windows.Forms;
using emDrive_Configurator;
using System.Threading;
using Timer = System.Threading.Timer;

using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Management;
using System.Text;
using System.Text.RegularExpressions;

public class Script
{
    // Prepare error string
    string err = string.Empty;

    // Create object that we will attempt to read from watch
    CO_Object HwMonitorUdc = new CO_Object(0x3071, 0x00);

    public void Main(CancellationToken cancellationToken)
    {
        // This will run the example for the WatchScriptObject
        ObjectBasedVersion(cancellationToken);

        // This will run the example for the API calls
        //ObjectBasedVersion(cancellationToken);
    }

    private void ObjectBasedVersion(CancellationToken cancellationToken)
    {
        // Create new objects for watch-script interoperability
        Procedure.WatchScriptObject intObject = new Procedure.WatchScriptObject("ScriptObject_Int32", 0x0004);
        Procedure.WatchScriptObject floatObject = new Procedure.WatchScriptObject("ScriptObject_Real32", 0x0008);

        // Add int object to watch
        if (intObject.TryToAddToWatch())
        {
            LogResult("ScriptObject_Int32 added to watch");
        }
        else
        {
            LogResult("ScriptObject_Int32 not added to watch");
        }

        // Add float object to watch
        if (floatObject.TryToAddToWatch())
        {
            LogResult("ScriptObject_Real32 added to watch");
        }
        else
        {
            LogResult("ScriptObject_Real32 not added to watch");
        }

        int i = 0;

        // Do the loop until cancellationToken is requested
        while (!cancellationToken.IsCancellationRequested)
        {
            double hwMonitorValue = 0;

            // Read last value of HwMonitorUdc object
            if (HwMonitorUdc.TryGetWatchValue(1, out hwMonitorValue))
            {
                i++;

                // Set new value to int and float objects
                if (!intObject.TryToAddValueToWatch(i))
                {
                    LogResult("Failed to write value to ScriptObject_Int32");
                }

                if (!floatObject.TryToAddValueToWatch(hwMonitorValue * 1.5))
                {
                    LogResult("Failed to write value to ScriptObject_Real32");
                }
            }
            // If not successful, log the error
            else
            {
                LogResult("Failed to get HwMonitorUdc value");
            }

            Procedure.Delay(200);
        }
    }

    private void ApiBasedVersion(CancellationToken cancellationToken)
    {
        // Add int object to watch
        if (TryAddScriptEntryToWatch("ScriptObject_Int32", 1, 0xFFFF, 0x01, datatype: 0x0004))
        {
            LogResult("ScriptObject_Int32 added to watch");
        }
        else
        {
            LogResult("ScriptObject_Int32 not added to watch");
        }

        // Add float object to watch
        if (TryAddScriptEntryToWatch("ScriptObject_Real32", 1, 0xFFFF, 0x02, datatype: 0x0008))
        {
            LogResult("ScriptObject_Real32 added to watch");
        }
        else
        {
            LogResult("ScriptObject_Real32 not added to watch");
        }

        int i = 0;

        // Do the loop until cancellationToken is requested
        while (!cancellationToken.IsCancellationRequested)
        {
            double hwMinutorValue = 0;

            // Read last value of HwMonitorUdc object
            if (TryGetWatchValue(1, 0x3071, 0x00, out hwMinutorValue))
            {
                i++;

                // Set new value to int and float objects
                if (!TryAddScriptEntryValue(1, 0xFFFF, 0x01, i))
                {
                    LogResult("Failed to write value to ScriptObject_Int32");
                }

                if (!TryAddScriptEntryValue(1, 0xFFFF, 0x02, hwMinutorValue * 1.5))
                {
                    LogResult("Failed to write value to ScriptObject_Real32");
                }
            }
            // If not successful, log the error
            else
            {
                LogResult("Failed to get HwMonitorUdc value");
            }

            Procedure.Delay(200);
        }
    }

    // The following methods are just wrappers to shorten the method calls
    bool TryAddScriptEntryToWatch(string name, int nodeId, uint index, ushort subindex, ushort datatype)
    {
        return Procedure.TryAddScriptEntryToWatch(name, nodeId, index, subindex, datatype);
    }
    bool TryAddScriptEntryValue(int nodeId, uint index, ushort subindex, dynamic value)
    {
        return Procedure.TryAddScriptEntryValue(nodeId, index, subindex, value);
    }
    bool TryGetWatchValue(int nodeId, uint index, ushort subindex, out double value)
    {
        return Procedure.TryGetWatchValue(nodeId, index, subindex, out value);
    }

    void LogResult(string LogString)
    {
        LogString = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + ": " + LogString;
        Procedure.Log(LogString);
    }
}

// CO_Object for readability
class CO_Object
{
    public uint index;
    public ushort subindex;
    public dynamic value;

    public CO_Object(uint index, ushort subindex)
    {
        this.index = index;
        this.subindex = subindex;
    }

    public bool TryGetWatchValue(int nodeId, out double value)
    {
        return Procedure.TryGetWatchValue(nodeId, index, subindex, out value);
    }
}

Script Features

Script features are extensions to the scripting functionality.

In order to use script features, the user must add 

using emDrive_Configurator.Scripts.Features;
at the start of the script file.

Modbus TCP

This functionality is only available for emDrive Configurator 2.13.1.0 and later.

Modbus_TCP.png

ModbusTCP currently supports connection with Modbus Servers (Client Mode) through TCP. In order to connect to the device, you need to provide the IP Address, Port and Modbus Server ID (Bus Address).

In the following Example, the IP is 127.0.0.1, the Port is 502 and the Modbus Server ID is 1:

ModbusTCP ModbusDevice1 = new ModbusTCP("127.0.0.1", 502, 1);

Alternatively, the device endianness can also be specified in the connection constructor:

ModbusTCP ModbusLocalhost = new ModbusTCP("127.0.0.1", 502, 1, ModbusEndianness.LittleEndian);

Default Endianness is set to Big Endian.

Currently Read and Write Holding registers operation is supported using generic functions:

short i16_var = 0;

// Read Holding Register at Address 100
if(ModbusLocalhost.ReadHoldingRegister<short>(100, out i16_var) == true)
{
  // Read Successful
}
else
{
  // Read Failed
}

// Write Holding Register at Address 100
ModbusLocalhost.WriteHoldingRegister(100, i16_var);    

Supported data types:

Parameters

Object's Attributes

Each CANopen Object in emDrive inverter has several attributes. Following attributes are related to changing, storing and loading values of objects.

Attribute Description
Access Type Defines if the object is readable and/or writable
Default Value Default value is value that the object is set to when setting parameters to factory/manufacturer defaults
Read Level Access level that is required for reading value of object
Write Level Access level that is required for writing value to object
Persistent Defines if the value of object is saved in permanent storage (keeps value after reboot or power cycle)

Object which have Persistent attribute set to "1" are stored in permanent memory and are therefore called "Parameters".

Objects attributes are visible in EDS or DCF files.

See Access Levels for more information about access to objects.

Parameters Sections

There are several different "sections" where parameters can be saved or restored from.

Current Parameter Values

Current value of parameter that is in use. This values are used by applications, firmware and can be access and changed via CANopen or custom CAN messages, depending on inverter.

Manufacturer Default Values

This values are hardcoded into firmware and are used to restore the device to factory default configuration. See section "Restore Manufacturer defaults - "Factory Reset" (5)"

Customer Default Values

Customer defaults is special section that can be used by Emsiso customer during their configuration or alternatively can be loaded at production. Customer has than the ability to reset configuration to their desired defaults i.e. "Customer defaults" without completely resetting parameters to factory defaults.

See sections "Saving to Customer defaults (3)" and "Restore Customer defaults (4)" on how to store values and restore values from this section. 

Device Parameter Values

Parameter values stored into permanent memory that are loaded at every power up. See section "Saving Device parameters (1)" on how to save current configuration.

There is also addition section "Calibration parameter" which is used for calibration in production. Customer does not have access to it. Objects that are stored in that section will be loaded from that section at powerup.

Saving and Restoring Parameters

Saving Device parameters (1)

Saving parameters is done by SDO write to CANopen object with index 0x1010 - "Store_Parameters_Field". Writing value 0x73617665 (ASCII "save") or byte swapped value 0x65766173 to any of sub indexes 0x01-0x04. Return status of SDO write will indicate success of failure.

Saving can also be done within emDrive Configurator by selecting "Device" -> "Save parameters" or by using shortcut "CTRL + S".

image.pngSaving parameters in emDrive Configurator

Saving to Customer defaults (3)

Saving parameters to "Customer Defaults" section is done by SDO write to CANopen object with index 0x1010 - "Store_Parameters_Field". Writing value 0x73617665 (ASCII "save") or byte swapped value 0x65766173 to sub index 0x05 "Save_Customer_Defined_Parameters. Return status of SDO write will indicate success of failure.

Saving can also be done within emDrive Configurator by selecting "Device" -> "Save to customer parameters" or by using shortcut "CTRL + SHIFT + S".

image.pngSaving parameters to Customer defaults in emDrive Configurator

Saving to Customer defaults is only possible with Access Level 2 (Customer) or above.

Restore Manufacturer defaults - "Factory Reset" (5)

Restoring parameter values to manufacturer defaults is done by SDO write to CANopen object with index 0x1011 - "Restore_Default_Parameters". Writing value 0x6C6F6164 (ASCII "load") or byte swapped value 0x64616F6C to any of sub index 0x01-0x04. Return status of SDO write will indicate success of failure.

Restoring can also be done within emDrive Configurator by selecting "Device" -> "Set parameters to manufacturer defaults" or by using shortcut "CTRL + D".

image.png
Setting parameters to manufacturer defaults in emDrive Configurator

Restoring from manufacturer defaults is only possible with Access Level 2 (Customer) or above.

Restore Customer defaults (4)

Restoring parameter values to Customer defaults is done by SDO write to CANopen object with index 0x1011 - "Restore_Default_Parameters". Writing value 0x6C6F6164 (ASCII "load") or byte swapped value 0x64616F6C to sub index 0x05 "Restore_Cusstomer_Define_Default_Parameters. Return status of SDO write will indicate success of failure.

Restoring to Customer defaults can also be done within emDrive Configurator by selecting "Device" -> "Set parameters to customer defaults" or by using shortcut "CTRL + SHIFT + D".

image.pngRestoring parameters from Customer defaults in emDrive Configurator

After each operation device shall be reset to apply new values.

Access Level

Access Level functionality is different depending on the emDRIVE firmware version.

Up to V1.14.x

Access Level is emDrive functionality that is used to limit read and write permissions for objects depending on user granted access.

Access Level functionality is available from firmware V1.11.0 and newer.

Each Parameter has attribute (Object's Attributes) for Read and Write access level. To be able to read or write to object Inverter must be unlocked to required level.

Access Levels
Level Name Description
1 End User Object is available to End User even when inverter is locked with "Customer Key"
2 Customer Object is available by default. Can be locked with "Customer Key"
3 Production Object is available only in production to write production related and calibration objects (only Emsiso)
4 Development Object is available only during development (only Emsiso)

Devices shipped from manufacturer are unlocked to "Customer" level. Customer has the option to lock the device and prevent additional changing of object values from 3rd parties.

Access Level CANopen Objects

Objects at index 0x2010 are used for Access Level functionality

Sub - index Name Description
0x01 Access_Level__Current Shows current access level that inverter is set to, objects with equal or lower access level can be accessed.
0x02 Access_Level_Key_input Write your key to unlock the device, depending on the key, specific level will be unlocked.
0x03 Access_Level_Customer_Key Writing value to this object will lock the device to "End User" (after reset). The same key must be used to unlock the device to "Customer" level. Remove locking by writing value "0".

Locking emDrive

Lock the emDrive inverter is intended to be used by Customer to prevent End User to make any changes of configuration. Objects with End User access level are mostly Read Only objects or only Read Access Level is set to End User.

To lock the emDrive follow the steps

  1. Chose the secret key that will be used to lock and unlock the inverter. Value can be from 1 - 4294967295 (32-bit unsigned integer).
  2. Write chosen key to object "Access_Level_Customer_Key"
  3. Save Device Parameters
  4. Reset the device
  5. After power up, device will have Current Access Level at 1 - End User. Check by reading object "Access_Level__Current"

Unlocking emDrive

If the emDrive is locked with Customer key and we want to change parameters that require Access Level 2, we have to unlock the emDrive after each power on. To verify that emDrive is locked, read object "Access_Level__Current" which should read "1".

To unlock the emDrive follow the steps

  1. Write correct Customer Key to object "Access_Level_Key_input".
  2. Read object "Access_Level__Current" which should change to "2" if the key was correct.

At this point Customer Level object can be read/written. But if the emDrive is reset, it will return to locked End User Access Level. To permanently unlock the emDrive and remove the key follow additional steps are needed.

  1. Write correct Customer Key to object "Access_Level_Key_input".
  2. Read object "Access_Level__Current" which should change to "2" if the key was correct.
  3. Write value "0" to "Access_Level_Customer_Key" to remove the key.
  4. Save Device Parameters
  5. Reset the device
  6. After power up, device will have Current Access Level at 2 - Customer. Check by reading object "Access_Level__Current"

 

V1.15.0+

Access Level is a feature of emDrive that controls read and write permissions for various objects based on the user’s granted access.

Access Level functionality is available from firmware V1.11.0 and newer.

Each parameter in emDrive has attributes (Object's Attributes) that define the required Read and Write Access Level. To interact with these parameters, the inverter must be unlocked to the corresponding access level. Higher access levels grant access to a greater number of objects.

Access Levels
Level Name Description
0 End User Available to the End User, even when the inverter is locked with the "Service Key".
1 Service Available to Service personnel. Can be locked using the "Service Key".
2 Admin Available by default. Can be locked using the "Admin Key".
3 Production Accessible only in production for writing production-related and calibration objects (Emsiso).
4 Development Accessible only during development (Emsiso).

Devices shipped from the manufacturer are unlocked to the "Admin" level. The customer has the option to lock the device, preventing third parties from changing object values.

Access Level CANopen Objects

The following objects at index 0x2010 are used to control Access Level functionality:

Sub - index Name Description
0x01 Access_Level__Current Displays the current access level of the inverter. Objects with equal or lower levels can be accessed.
0x02 Access_Level__Key_input Enter a key to unlock the device. The entered key will unlock a specific level.
0x03
Access_Level__Admin_Key
Write a value to lock the device to "Admin" level after reset. Use the same key to unlock. Write "0" to remove locking.
0x04
Access_Level__Service_Key
Write a value to lock the device to "Service" level after reset. Use the same key to unlock. Write "0" to remove locking.

Locking emDrive

Locking the emDrive inverter is typically performed by the Admin or Service personnel to prevent the End User from making configuration changes. Objects accessible to the End User are usually read-only or have read-only permissions set.

To lock the emDrive follow the steps

  1. Choose a secret key (an integer between 1 and 4,294,967,295).
  2. Write the selected key to either the "Access_Level__Service_Key" or "Access_Level__Admin_Key" depending on the desired lock level.
  3. Save the device parameters.
  4. Reset the device
  5. After powering up, the device will be locked one level below the set key. Verify this by reading the "Access_Level__Current" object.

Unlocking emDrive

To modify parameters that require higher access levels (such as Admin or Service), the emDrive must be unlocked after every power cycle. To confirm that the emDrive is locked, check the "Access_Level__Current" object, which should display the correct locked level.

To unlock the emDrive:

  1. Write the correct key to the "Access_Level__Key_input" object.
  2. Read the "Access_Level__Current" object to verify the change to the corresponding access level.

Once unlocked, objects at the unlocked level can be accessed. However, if the emDrive is reset, it will revert to the locked state. To permanently unlock the emDrive, follow these additional steps:

  1. Write the correct key to the "Access_Level__Key_input" object.
  2. Verify that the access level has changed by reading "Access_Level__Current".
  3. Write the value "0" to "Access_Level__Admin_Key" or "Access_Level__Service_Key" to remove the respective key.
  4. Save the device parameters.
  5. Reset the device.

After the reset, the device will no longer require the key to access the previous locked level. You can verify this by checking the "Access_Level__Current" object.

Lua Manual

Disclaimer
The EMSISO d.o.o. scripting functionality, based on the Lua programming language, is provided to enable flexibility and customization for motor control applications. However, EMSISO d.o.o. assumes no responsibility or liability for any damages, malfunctions, injuries, or losses resulting from the use of scripts provided by EMSISO d.o.o. or scripts created, modified, or executed by users.
Users are fully responsible for testing and verifying all scripts in a controlled and safe environment before deployment. Any script used in conjunction with EMSISO d.o.o. products is done at the user’s own risk.
By using the scripting feature, you acknowledge and agree to release EMSISO d.o.o. from all claims, demands, or liability for any incidents that may arise from its use.

1. General

1.1 Purpose

1.2 Scope

1.3 Important Note

2. emDrive library

2.1 General

The emDrive library is used instead of the standard Lua libraries. This library includes some functions that are identical to those in the standard Lua library, some that have been modified, and others that are unique to emDrive.

Do not overwrite the emDrive library functions and variables.

The emDrive library is organized into different sets, each focused on a specific group of functionalities. This section will describe these sets and their functions and variables in detail.

Here is an example of how to use a library function. This example will show a legend that explains how to read the prototypes of library functions. Words in blue represent variable types, such as arguments or return values. These types follow standard Lua conventions, with the addition of the "integer" type, which represents whole numbers.

When your script calls a function from the emDrive library, the arguments you provide must match the prototype exactly, including the type and, for tables, their structure. If they do not, an error will occur.

number | nil ret1, boolean ret2 = Namespace.FunctionName({integer arg1Field1, string arg1Field2} arg1, 
{integer, integer, integer [5:5-11], integer [13]} arg2, [, string arg3], arg4…)

This is an example of a function named Namespace.FunctionName. It takes in: 2 fixed arguments, 1 optional 
argument and a variable number of arguments. It also returns 2 values where the first can also be nil.

 

Name

Description

Arguments

arg1

Table with two named fields arg1Field1 of type integer and arg1Field2 of type string.

arg2

Table with unnamed fields with all integer types at index 1, 2, 5 with size 1 to 7 and 13.

arg3

Optional string argument.

arg4

Variable number of arguments of any type.

Returns

ret1

First returned value which can be either a number type or nil.

ret2

Second returned value which is of boolean type.

Functions can be called with more arguments then expected in which case excess arguments are ignored.

The emDrive library can raise errors in addition to those raised by the Lua kernel, such as accessing nil values. These library errors usually occur when the type or value of an argument does not match what is expected.

For each function in the emDrive library, the possible errors (exceptions) and the conditions that cause them will be listed.

2.2 Functions

2.2.1 Base

This library provides base script functionality that is not specific to any part of the emDrive hardware (HW) or firmware (FW).

Error(string message [, integer level])

The Error function raises an error with a custom message and specifies the level of the error position. It is useful for throwing exceptions, such as for debugging purposes. This function is equivalent to the standard error() function in Lua and never returns.

Table 1: Error() prototype description

 

Name

Description

Arguments

message

Error message shown.

level

Specify how to get the error position:

·         0, error position is not added,

·         1 (default), the error position is where the error() function was called,

·         2 points the error to where the function that called error() was called,

·         and so on…


2.2.2 I/O

This library works with two main objects: Lua__IO_Input and Lua__IO_Output. These objects facilitate communication between the script and the external world, such as displaying messages or providing debugging arguments to the script.

By using these objects, the script can efficiently exchange information with external systems.

I/O library objects

___________________________________________________________________________________________________________________________________________

number | string | nil value = IO.Read([option…])

You can read values from the input object using specified options.

Options:

This function is equivalent to the standard io.read() function in Lua.

Table 2: IO.Read() prototype description

 

Name

Description

Arguments

option

How to interpret the input string:

·         ‘n’ as a number,

·         ‘l’ (default) as one line string without newline,

·         ‘L’ as one line string with newline if exists,

·         ‘a’ as string of all text,

·         0, 1, 2, … as number of characters.

Returns

value

Interpreted input.

Table 3: IO.Read() exceptions

Exception

option is not an integer nor a string.

option is a string longer than 1 character.

option is a string but not one of the valid values.

___________________________________________________________________________________________________________________________________________

IO.Write(string text…)

You can write text to the output object using this function.

This function is equivalent to the standard io.write() function in Lua.

Table 4: IO.Write() prototype description

 

Name

Description

Arguments

text

Text to write to output object.

Table 5: IO.Write() exceptions

Exception

text is not a string.

___________________________________________________________________________________________________________________________________________

IO.Print(object…)

This function converts an object to a string and then writes the result to the output object.

This function is equivalent to the standard print() function in Lua.

Table 6: IO.Print() prototype description

 

Name

Description

Arguments

object

Objects to convert to string and write to output object.

2.2.3 Time

This library provides functions for time keeping and inserting delays in your scripts.

___________________________________________________________________________________________________________________________________________

integer time = Time.GetMs()

This function retrieves the current time in milliseconds since the device was powered on. The maximum value is limited to 2³² – 1.

Table 7: Time.GetMs() prototype description

 

Name

Description

Returns

time

Elapsed milliseconds since power on.

___________________________________________________________________________________________________________________________________________

Time.WaitMs(integer time)

This function halts the script execution for a specified amount of time using this function.

Table 8: Time.WaitMs() prototype description

 

Name

Description

Returns

time

Milliseconds to wait.

Table 9: Time.WaitMs() exceptions

Exception

time is negative or 0.

___________________________________________________________________________________________________________________________________________

integer newStartTime = Time.WaitMsUntil(integer startTime, integer waitTime)

This function halts script execution for specified time from the provided starting time. This function can be used to implement periodic execution independent of logic execution duration.

Table 10: Time.WaitMsUntil() prototype description

 

Name

Description

Arguments

startTime

Time (milliseconds) from which waiting time is measured.

waitTime

Milliseconds to wait.

Returns

newStartTime

Time (milliseconds) at which script was halted.

Table 11: Time.WaitMsUntil() exceptions

Exception

startTime is negative or 0.

waitTime is negative or 0.

2.2.3 CAN

This library is used to establish communication and transfer data over CAN. It provides functionality to send and receive CAN messages.

Note: This library does not support CAN FD (Flexible Data-rate).

___________________________________________________________________________________________________________________________________________

This library supports two modes of operation for CAN communication. The mode is selected during the initialization of CAN.

Table 12: CAN modes of operation

Mode

Description

CAN_RX_TX

CAN shall be used to send and receive messages (user is required to implement CAN.Received(message) function).

CAN_TX_ONLY

CAN shall be used only to send messages.

___________________________________________________________________________________________________________________________________________

CAN.Initialize(mode, boolean useExtendedFrame [, integer rxID, integer rxIDMask])

To use CAN communication, initialize CAN with this function. It can be called multiple times with different arguments, but only the last configuration will be used.

Table 13: CAN.Initialize() prototype description

 

Name

Description

Arguments

mode

CAN mode of operation (see Table 12).

useExtendedFrame

Will extended frames be transferred?

rxID

Frame ID to receive.

rxIDMask

Receive filter ID mask.

Table 14: CAN.Initialize() exceptions

Exception

mode is not one of the valid values.

useExtendedFrame is not boolean.

rxID is not an integer.

mode is set to CAN_RX_TX and rxIDMask is not an integer.

CAN initialization failed (should never happen).

___________________________________________________________________________________________________________________________________________

CAN.Send(integer id, {integer [1:1-8]} data)

Use this function to send a CAN frame with a specified ID and data.

Table 15: CAN.Send() prototype description

 

Name

Description

Arguments

id

Sent frame ID.

data

1 to 8 bytes of data.

Table 16: CAN.Send() exceptions

Exception

id is not an integer.

data is not a table.

Length of data is more than 8.

Value of data is not an integer.

Value of data is more than 0xFF.

CAN send failed (should never happen).

___________________________________________________________________________________________________________________________________________

CAN.Received({integer ID, integer Length, integer Data[1:1-8]} message)

This function is called whenever a CAN message is received.

Table 17: CAN.Received() prototype description

 

Name

Description

Arguments

message

Received message.

2.2.4 CANopen

This library is used to interact with the CANopen protocol stack.

___________________________________________________________________________________________________________________________________________

The CANopen stack includes a network management state machine that defines the communication behavior of a CANopen device. While there are multiple possible states, only a few are regularly used and available to the user.

Table 18: CANopen NMT states

NMT state

Description

CO_UNKNOWN

NMT in unknown state (unreachable).

This state cannot be set by user.

CO_OPERATIONAL

Device can use all supported communication objects.

This state can be set by user.

CO_STOPPED

Device operation is stopped.

This state cannot be set by user.

CO_PREOPERATIONAL

First state after initialization indicating that device is ready to work. Limited message transfer support.

This state can be set by user.

CO_RESET_NODE

Application reset. Application objects set to their power-on or default values.

This state cannot be set by user.

CO_RESET_COMM

Communication reset. Communication objects are set to their power-on or default values.

This state cannot be set by user.

___________________________________________________________________________________________________________________________________________

state = CANopen.GetNMTState()

Use this function to retrieve the currently set Network Management (NMT) state of the CANopen device.

Table 19: CANopen.GetNMTState() prototype description

 

Name

Description

Arguments

state

Currently set NMT state (see Table 18).

___________________________________________________________________________________________________________________________________________

CANopen.SetNMTState(state)

Use this function to request the CANopen stack to change the Network Management (NMT) state of the device.

Table 20: CANopen.SetNMTState() prototype description

 

Name

Description

Arguments

state

Next NMT state (see Table 18).

Table 21: CANopen.SetNMTState() exceptions

Exception

state is not one of the valid values.

___________________________________________________________________________________________________________________________________________

number value = CANopen.GetObjectValue({integer, integer} object)

Use this function to retrieve the value of a specified object.

Table 22: CANopen.GetObjectValue() prototype description

 

Name

Description

Arguments

object

CANopen object.

Returns

value

Object value.

Table 23: CANopen.GetObjectValue() exceptions

Exception

object is not a table.

Length of object is not 2.

Value at first index of object is not an integer.

Value at second index of object is not an integer.

Invalid object index and/or subindex.

___________________________________________________________________________________________________________________________________________

CANopen.SetObjectValue({integer, integer} object, number value)

Use this function to set the value of a specified object.

Table 24: CANopen.SetObjectValue() prototype description

 

Name

Description

Arguments

object

CANopen object.

value

Object value.

Table 25: CANopen.SetObjectValue() exceptions

Exception

object is not a table.

Length of object is not 2.

Value at first index of object is not an integer.

Value at second index of object is not an integer.

Invalid object index and/or subindex.

Value is not a number

2.2.5 Motor

This library is used to operate the motor. You can set operating parameters and retrieve the motor's current state and status.

___________________________________________________________________________________________________________________________________________

The motor can be controlled in three distinct modes. Depending on the chosen mode, the related reference will be used by the motor control algorithm.

Table 26: Motor control modes

Motor control mode

Description

TORQUE_MODE

Motor is torque controlled.

Set requested torque with Motor.SetReferenceTorque().

VELOCITY_MODE

Motor is velocity controlled.

Set requested velocity with Motor.SetReferenceVelocity().

POSITION_MODE

Motor is position controlled.

Set requested torque with Motor.SetReferencePosition().

___________________________________________________________________________________________________________________________________________

Motor control operates with a state machine that can be in one of multiple states. This state machine is responsible for top-level motor control functions such as initialization, execution, calibration, and error handling.

Table 27: Motor control states

Motor control state

Description

MOTOR_UNDEFINED

Motor control in undefined state (internal error).

MOTOR_INITIALIZATION

Motor control is initializing.

MOTOR_SELF_TEST

Motor control is performing start-up self-tests.

MOTOR_OFF

Motor control is initialized and disabled. Controlling the motor is not possible. Reference values have no effect.

MOTOR_ON

Motor control is initialized and enabled. Motor is controlled according to mode and reference value such that the operating conditions are always within defined limits.

MOTOR_ERROR

Motor control detected an error and was disabled. This state can be entered in multiple ways: limits overreached, failed calibration and other.

MOTOR_CALIBRATION

Motor control is being calibrated. In this state characteristics of the motor and angle sensor are being measured.

___________________________________________________________________________________________________________________________________________

Motor.Enable()

Enable motor control to have the motor operate according to the set mode and the corresponding reference value.

___________________________________________________________________________________________________________________________________________

Motor.Disable()

Disable motor control to stop the motor.

___________________________________________________________________________________________________________________________________________

Motor.Recover()

Use this function to attempt recovery from a motor control error.

Note: This function is only applicable when the motor control is in an error state.

___________________________________________________________________________________________________________________________________________

Motor.SetControlMode(mode)

Use this function to set the motor control mode.

Table 28: Motor.SetControlMode() prototype description

 

Name

Description

Arguments

mode

Motor control mode (see Table 26).

Table 29: Motor.SetControlMode() exceptions

Exception

mode is not one of the valid values.

___________________________________________________________________________________________________________________________________________

state = Motor.GetState()

Retrieve the current state of the motor control.

Table 30: Motor.GetState() prototype description

 

Name

Description

Returns

state

Motor control state (see Table 27).

___________________________________________________________________________________________________________________________________________

integer warnings = Motor.GetWarnings()

Retrieve the currently active motor control warnings.


Table 31: Motor.GetWarnings()

 

Name

Description

Returns

warnings

Active warnings.

Refer to emDrive manual or related CANopen object for value meaning.

___________________________________________________________________________________________________________________________________________

integer protectionsLow = Motor.GetProtectionsLow()

Retrieve the currently active motor control protections (first 32 bits).

Table 32: Motor.GetProtectionsLow()

 

Name

Description

Returns

protectionsLow

Active protections (first 32bits).

Refer to emDrive manual or related CANopen object for value meaning.

___________________________________________________________________________________________________________________________________________

integer protectionsHigh = Motor.GetProtectionsHigh()

Retrieve the currently active motor control protections (last 32 bits).

Table 33: Motor.GetProtectionsHigh()

 

Name

Description

Returns

protectionsHigh

Active protections (second 32 bits - currently empty meant for future FW releases).

Refer to emDrive manual or related CANopen object for value meaning.

___________________________________________________________________________________________________________________________________________

number velocity = Motor.GetReferenceVelocity()

Retrieve the currently set reference velocity.

Table 34: Motor.GetReferenceVelocity()

 

Name

Description

Returns

velocity

Velocity [RPM].

___________________________________________________________________________________________________________________________________________

Motor.SetReferenceVelocity(number velocity)

Set the reference velocity.

Table 35: Motor.SetReferenceVelocity() prototype description

 

Name

Description

Arguments

velocity

Velocity [RPM].

Table 34: Motor.SetReferenceVelocity() exceptions

Exception

velocity is not a number.

___________________________________________________________________________________________________________________________________________

number torque = Motor.GetReferenceTorque()

Retrieve the currently set reference torque.

Table 36: Motor.GetReferenceTorque() prototype description

 

Name

Description

Returns

torque

Torque [Nm].

___________________________________________________________________________________________________________________________________________

Motor.SetReferenceTorque(number torque)

Set the reference torque.

Table 37: Motor.SetReferenceTorque() prototype description

 

Name

Description

Arguments

torque

Torque [Nm].

Table 38: Motor.SetReferenceTorque() exceptions

Exception

torque is not a number.

___________________________________________________________________________________________________________________________________________

number position = Motor.GetReferencePosition()

Retrieve the currently set reference position.

Table 39: Motor.GetReferencePosition() prototype description

 

Name

Description

Returns

position

Position [°].

___________________________________________________________________________________________________________________________________________

Motor.SetReferencePosition(number position)

Set the reference position.

Table 40: Motor.SetReferencePosition() prototype description

 

Name

Description

Arguments

position

Position [°C].

Table 41: Motor.SetReferencePosition() exceptions

Exception

position is not a number.

___________________________________________________________________________________________________________________________________________

number velocity = Motor.GetVelocity()

Retrieve the current motor velocity.

Table 42: Motor.GetVelocity() prototype description

 

Name

Description

Returns

velocity

Velocity [RPM].

___________________________________________________________________________________________________________________________________________

number torque = Motor.GetTorque()

Retrieve the current motor torque.

Table 43: Motor.GetTorque() prototype description

 

Name

Description

Returns

torque

Torque [Nm].

___________________________________________________________________________________________________________________________________________

number position = Motor.GetPosition()

Retrieve the current motor position.

Table 44: Motor.GetPosition() prototype description

 

Name

Description

Returns

position

Position [°C].

2.2.6 Digital

This library is used to manipulate digital I/Os (ports). It enables writing to and reading from digital ports.

___________________________________________________________________________________________________________________________________________

Every emDrive contains a set of digital ports that can be freely used by the user application. Some of these ports are writable, while others are read-only. Note that some ports might not be available on all emDrives. To see which ports are available, refer to the manual or the EDS of your emDrive

Table 45: Digital ports

Digital port

Description

LOW_SIDE_1

Low-side switch 1.

LOW_SIDE_2

Low-side switch 2.

LOW_SIDE_3

Low-side switch 3.

LOW_SIDE_4

Low-side switch 4.

HIGH_SIDE_1

High-side switch 1.

DIGITAL_IN_1

Digital input 1.

This port is read-only.

DIGITAL_IN_2

Digital input 2.

This port is read-only.

DIGITAL_IN_3

Digital input 3.

This port is read-only.

DIGITAL_IN_4

Digital input 4.

This port is read-only.

DIGITAL_IN_5

Digital input 5.

This port is read-only.

DIGITAL_IN_6

Digital input 6.

This port is read-only.

DIGITAL_IN_7

Digital input 7.

This port is read-only.

___________________________________________________________________________________________________________________________________________

number state = Digital.Get(port)

Retrieve the digital state of the port.

Table 46: Digital.Get() prototype description

 

Name

Description

Arguments

port

Digital port (see Table 44).

Returns

state

Value of 0 – 1 representing signal’s duty cycle.

Table 47: Digital.Get() exceptions

Exception

port is not one of the valid values.

___________________________________________________________________________________________________________________________________________

Digital.Set(port, number state)

Set the digital state of the port.

Table 48: Digital.Set() prototype description

 

Name

Description

Arguments

port

Digital port (see Table 44).

state

Value of 0 – 1 representing signal’s duty cycle.

Table 49: Digital.Set() exceptions

Exception

port is not one of the valid values.

state is not a number between 0 and 1.

2.2.7 Analog

This library is used to retrieve values from analog inputs (ports).

___________________________________________________________________________________________________________________________________________

Every emDrive contains a set of analog ports that can be freely used by the user application. All of these ports are read-only. Note that some ports might not be available on all emDrives. To see which ports are available, refer to the manual or the EDS of your emDrive.

Table 50: Analog ports

Analog port

Description

ANALOG_IN_1

Analog input 1.

ANALOG_IN_2

Analog input 2.

ANALOG_IN_3

Analog input 3.

ANALOG_IN_4

Analog input 4.

LOW_SIDE_1_VOLTAGE

Low-side switch 1 voltage measurement.

LOW_SIDE_1_CURRENT

Low-side switch 1 current measurement.

LOW_SIDE_2_VOLTAGE

Low-side switch 2 voltage measurement.

LOW_SIDE_3_VOLTAGE

Low-side switch 3 voltage measurement.

LOW_SIDE_4_VOLTAGE

Low-side switch 4 voltage measurement.

HIGH_SIDE_1_VOLTAGE

High-side switch 1 voltage measurement.

DIGITAL_IN_1

Digital input 1.

DIGITAL_IN_2

Digital input 2.

DIGITAL_IN_3

Digital input 3.

DIGITAL_IN_4

Digital input 4.

DIGITAL_IN_5

Digital input 5.

DIGITAL_IN_6

Digital input 6.

DIGITAL_IN_7

Digital input 7.

___________________________________________________________________________________________________________________________________________

number value = Analog.Get(port)

Retrieve the analog value on the port.

Table 51: Analog.Get() prototype description

 

Name

Description

Arguments

port

Analog port (see Table 49).

Returns

value

Analog value.

Table 52: Analog.Get() exceptions

Exception

port is not one of the valid values.

3. Scripting

3.1 Setting Up Lua Scripting with Visual Studio Code

To make Lua scripting easier, follow these steps to set up Visual Studio Code with the Lua extension:

  1. Install Visual Studio Code

    • If you don't have it yet, download and install Visual Studio Code.
  2. Add the Lua Extension

    • Open Visual Studio Code.
    • Go to the Extensions view (click on the square icon on the sidebar or press Ctrl+Shift+X).
    • Search for "Lua" and install the Lua extension by sumneko.
      image.png
  3. Create a Folder for Libraries

    • Make a new folder on your computer to store the emdrive library. For example, you can name it D:\LuaLibraries.
    • Inside the folder extract the emdrive.7z which you get directly from us.
      D:\LuaLibraries\emdrive
  4. Configure Developer Mode and Add Library Path

    • Open Visual Studio Code.
    • Press Ctrl+P to open the search bar.
    • Type >settings or >Open User Settings (JSON) and select "Open User Settings (JSON)" from the list.
      image.png


  5. Edit User Settings (JSON)

    • In the opened settings.json file, add the following code.
      "Lua.misc.parameters": [
          "--develop=true"
      ],
      "Lua.workspace.checkThirdParty": true,
      "Lua.workspace.userThirdParty": ["path_to_our_library"]
    • Make sure to keep any existing code intact. Add a comma at the end of the last line of the existing code, then paste the new code below it. Update the path to match your folder (D:/LuaLibraries). Use forward slashes instead of backslashes.
  6. Save and Close

    • Save the changes to settings.json.
    • Close Visual Studio Code.


Example of "settings.json":

image.png

3.2 Getting Started with Lua Scripting

3.2.1 Create "firstscript.lua"

  1. Create a New Folder

    • Make a new folder for your project.
  2. Open the Folder with Visual Studio Code

    • Right-click the folder and select "Open with Code."
      image.png
    • This will open Visual Studio Code with your new folder as the workspace.
  3. Create a New Lua Script

    • In the Explorer window of Visual Studio Code, right-click and select "New File."
      image.png
    • Name the file firstscript.lua (valid files are also "firstscript.txt", "firstscript". But it is better to use .lua which can use our emdrive Library in VS-code).

Note: Only the content of the script is transferred, not the file name.

Limitation: You can store only one script at a time on the emDrive.

3.2.2 Structure

Initialization Routine
Loop Routine


Minimum Loop Period: The minimum allowed loop period is 10 milliseconds.
Execution Time: Ensure the Loop() function completes within the set period to maintain deterministic execution. If the loop takes longer, it can cause unpredictable behavior.

Priority Consideration: The firmware on the emDrive has higher priority than the script, which means script execution may be interrupted by higher priority tasks.
Use the resource monitor (see the Resource Monitor section) to measure loop duration and determine the minimum possible loop period.
The resource monitor can also show the maximum loop duration when interrupted by higher priority tasks.

Data Transfer: Transferring large data over CAN/CANopen will completely block script execution.

3.3 Executing script

After you've created your script, it is time to download it to emDrive and execute it. For these purposes, a few CANopen objects are provided, as seen in Figure 3. They are grouped together and are accessible at index 0x2040.

Table 52 provides a short description of objects related to script execution. More detailed meanings and use cases will be presented in the following sections.

Table 53: Script execution objects description

Name

Description

IsEnabled

Defines if Lua feature is enabled on particular emDrive:

0: Lua feature disabled

1: Lua feature enabled

Status

Lua status:

0: File is non-existent or corrupted,

1: Compilation failed (see Output),

2: Execution failed (see Output),

3: Downloaded file is valid,

4: Script execution is paused,

5: Script is executing,

6: Script timed out and was stopped.

Control

Lua script control:

0: Do nothing,

1: Pause,

2: Run,

3: Restart.

IsAutoStart

Should script be started automatically after device powers on?

Script

Download or upload Lua script.

IO_Input

Script input object. This object can be used to pass values/arguments to the script during runtime.

IO_Output

Script output object. This object is used to display error messages and can be used by script to print arbitrary text messages.

3.3.1 Download/Upload

  1. click on Script object (0x2040, 0x5),
  2. check “Write from file”,
  3. click the “…” button which brings up an open file dialog where you select your script,
  4. click “Write” button which will download the script to emDrive,
  5. after successful download you should see a green progress bar with “Successful” label.

  1. click on Script object (0x2040, 0x5),
  2. click the “Read” button which will upload the script from emDrive and display it in the “Description” text box.

3.3.2 Control

Once the script has been successfully downloaded and verified, three control options become available through the Control object (0x2040, 0x3).

  1. Pause (Control = 1): This will halt the currently executing script until Run or Restart is set. It will not stop the motor, disable outputs, or otherwise modify the state of the emDrive from what was last set by the script. This has no effect if the script is not executing.

  2. Run (Control = 2): This will resume execution of a paused script from where it was left off. If the script is not executing nor paused but is downloaded and verified, it will start executing the downloaded script. This has no effect if the script is already executing.

  3. Restart (Control = 3): This will terminate the currently executing or paused script, recompile, and restart it. If the script is not executing nor paused but is downloaded and verified, it will start executing the downloaded script.

If a script is already executing when a new script is downloaded, the executing script is terminated as the download is initiated. After the new script is successfully downloaded and verified, it is automatically executed.

NOTE: The script is executed only after motor control has been initialized, which may delay script execution after the device powers on (usually by approximately 100 ms, although this value may vary).

3.3.3 Errors

When an error with the script is detected, the Status object (0x2040, 0x2) is set to a value signaling the type of error. There are four distinct types of errors that are detectable at different stages of script execution:

  1. File is non-existent or corrupted (Status = 0): This is set when the script is not stored on the emDrive, or the stored script is corrupted. This error is triggered when script verification fails, either when the device powers on or whenever a new script is downloaded.

  2. Compilation failed (Status = 1): This is set when the script could not be compiled, which could be caused by syntax errors or running out of memory. This error is triggered every time script compilation fails, such as when the script is restarted, executed for the first time, or downloaded while another script is already executing, causing the new script to be automatically compiled after downloading.

  3. Execution failed (Status = 2): This is set when an error occurs during runtime—such as in initialization, loop, or other required routines—caused by several reasons like accessing nil objects, passing invalid values, missing functions, running out of memory, and others. This error is also raised if the Error() function is called from within the script.

  4. Script timed out and was stopped (Status = 6): This is set when the initialization, loop, or other required routines take longer to execute than expected.

The initialization routine must execute within 100 ms, while the loop and other routines are required to execute within 2 × LoopPeriodMs.

Further details of what went wrong with the script can be retrieved from the output object.

All the above-mentioned errors are permanent. When an error is raised, emDrive protection is activated, and emDrive enters an error state from which it cannot recover (refer to the emDrive manual for more details about protections and error states). At this point, the script should be corrected for errors, downloaded to the emDrive, and then the device should be reset to clear the error.

3.3.4 Debugging

There is no proper debugging mechanism in place; however, utilizing Lua I/O objects and general-purpose CANopen objects, some form of limited debugging is possible. Using the input object, the user can control the flow of the program. The user can also write debug information to the output object. Additionally, all objects at index 0x3020, can be used freely for debugging purposes.

3.3.5 Resource monitor

The resource monitor is used to measure characteristics of the script during compile-time, initialization, and runtime (loop). All available objects relevant to resource monitoring are shown under object 0x2031 and described in Table 53.


Table 54: Script resource monitor objects description

Name

Description

EnableLoopMonitor

Enable loop resource monitoring which includes execution time and RAM usage.

Time_Loop_Immediate

Immediate measured loop duration [us].

Time_Loop_Maximum

Maximum measured loop duration [us].

 

This value might sometimes appear unreasonably large that is when task running Lua script gets interrupted by higher priority tasks.

 

Write 0 to clear value and prepare it for new measurement.

Time_Initialization

Time [ms] required to execute initialization routine.

Time_Compile

Time [ms] required to load the script - that is time to script initialization.

Memory_Free

Immediately available RAM [byte].

Memory_Used

Immediately used RAM [byte].

Initialization and compile-time measurements are performed every time a new script is downloaded and executed or when an existing script is restarted. Memory and loop time monitoring must be explicitly enabled by setting EnableLoopMonitor to 1. This is because these measurements are calculated every loop, thereby shortening the processing time allocated to the script.

3.4 Using the emdrive Library

3.4.1 Option 1: Automatic Setup

  1. Add Code to Script

    • In firstscript.lua, add the following line:

      require('emdrive')
  2. Apply Path Modification

    • A pop-up window will appear in the bottom right corner. Click "Apply and modify."
    • After applying, delete the require('emdrive') line.
      image.png
  3. Check .vscode Folder

    • A new folder named .vscode will be created.
    • Inside, there will be a settings.json file containing the path to the library.

3.4.2 Option 2: Manual Setup

  1. Create .vscode Folder

    • Manually create a .vscode folder in your workspace.
  2. Create settings.json File

    • Inside the .vscode folder, create a settings.json file.
  3. Add Library Path to settings.json

    • Add the following code to settings.json:


      {
          "lua.workspace.library": [
              "D:/LuaLibraries"
          ]
      }

4. Examples

4.1 Example 1: "Hello World"

Description:

Print "Hello World" every 100ms to IO_Output.

To make the script work, you need to have two functions: Initialize and Loop.

  1. Copy the Code

    Copy the following code to an example.lua file:

    function Initialize()	
    	LoopPeriodMs = 100
    end
    
    function Loop()
        IO.Write("Hello World ")
    end
  2. Load the Script on the Inverter

    • Go to 0x2040 0x05 - Script.
    • Select "Write from file" and click "...".
    • A pop-up window will appear. Locate the saved script and click "Open".
    • Click "Write".

  3. Check if the Script is Valid

    • Go to 0x2040 0x02 - Status.
    • The value "3" indicates the downloaded file is valid.
  4. Run the Script

    • Set 0x2040 0x03 - Control to 2.
    • If the script is running, the status value should be "5".
  5. View the Output

    • Go to 0x2040 0x07 - IO_output.
    • Click "Read" to see the "Hello World" string in the Description window.

4.2 Example 2: Data types & variables

4.2.1 Example 2.1: Data types

Lua is a lightweight, high-level programming language known for its simplicity and flexibility. It has several basic data types, each serving a different purpose. Here are the main data types in Lua along with examples for each:

  1. Nil
    Represents the absence of a value.
  2. Boolean
    Represents a boolean value, either true or false.
  3. Number
    Represents both integer and floating-point numbers.
  4. String
    Represents a sequence of characters.
  5. Table
    Represents associative arrays, which can be used as arrays, dictionaries, or other data structures.
  6. Function
    Represents a callable function.
function Initialize()	
	LoopPeriodMs = 100
end

function Loop()
    
    -- 1. Nil
    local myVariable = nil
    IO.Print(myVariable)  -- Output: nil

    -- 2. Boolean
    local isTrue = true
    local isFalse = false
    IO.Print(isTrue)  -- Output: true
    IO.Print(isFalse)  -- Output: false

    -- 3. Number
    local integerNumber = 42
    local floatingNumber = 3.14
    IO.Print(integerNumber)  -- Output: 42
    IO.Print(floatingNumber)  -- Output: 3.14

    -- 4. String
    local myString = "Hello, Lua!"
    IO.Print(myString)  -- Output: Hello, Lua!

    -- 5. Table
    local myTable = { key1 = "value1", key2 = "value2" }
    IO.Print(myTable.key1)  -- Output: value1
    IO.Print(myTable.key2)  -- Output: value2

    local arrayTable = { "apple", "banana", "cherry" }
    IO.Print(arrayTable[1])  -- Output: apple

    -- 6. Function
    local function myFunction(a, b)
      return a + b
    end
    IO.Print(myFunction(2, 3))  -- Output: 5
 
    -- 6. Anonymous function
    local anonFunction = function(x, y)
      return x * y
    end
    IO.Print(anonFunction(4, 5))  -- Output: 20

end

4.2.2 Example 2.2: Variables

  1. Global variables
    Global variables are accessible from anywhere in the program unless shadowed by a local variable of the same name. By default, any variable declared without the local keyword is global.
    myGlobalVariable = 10  -- Global variable
    
    function printGlobal()
      print(myGlobalVariable)
    end
    
    printGlobal()  -- Output: 10
  2. Local Variables
    Local variables are only accessible within the block or function where they are declared. They help avoid polluting the global namespace and can be used to manage scope more effectively.
    local myLocalVariable = 20  -- Local variable
    
    function printLocal()
      local myLocalVariable = 30  -- Local to this function
      print(myLocalVariable)
    end
    
    printLocal()  -- Output: 30
    print(myLocalVariable)  -- Output: 20
  3. Table fields
    Variables can also be fields of tables, allowing for the creation of more complex data structures like arrays, dictionaries, and objects.
    local myTable = {
      field1 = "Hello",
      field2 = "World"
    }
    
    print(myTable.field1)  -- Output: Hello
    print(myTable.field2)  -- Output: World

Variable scope

  1. Global vs. Local
    myGlobal = "I am global"
    
    function testScope()
      local myLocal = "I am local"
      print(myGlobal)  -- Accessible
      print(myLocal)   -- Accessible
    end
    
    testScope()
    
    print(myGlobal)  -- Accessible
    -- print(myLocal) -- Error: myLocal is not accessible here

  2. Local Variable Shadowing
    local x = 5  -- Local variable in the main chunk
    
    function shadowTest()
      local x = 10  -- Local variable in the function
      print(x)  -- Output: 10
    end
    
    shadowTest()
    print(x)  -- Output: 5

Global variables and tables use a lot of memory, especially in embedded systems. To save memory, it's best to use local variables whenever possible. 
If you use a global variable at the start of your code and don't need it later, clear the global variable to free up memory. You can do this by adding the following code:

myGlobalVariable = nil

4.3 Example 3: Inputs/Outputs

4.3.1 Example 3.1: Use of digital inputs & outputs

Turn low side 1 - ON when switch on digital pin 1 is switched ON.

function Initialize()	
	LoopPeriodMs = 10
end

function Loop()

    local switch = Digital.Get(DIGITAL_IN_1)

    if switch == 1 then
        Digital.Set(LOW_SIDE_1, 1)
    else
        Digital.Set(LOW_SIDE_1, 0)
    end
end

4.3.2 Example 3.2: Use of analog inputs

Print analog value of analog input 1 when switch on digital pin 1 is switched ON.

function Initialize()	
	LoopPeriodMs = 10
end

function Loop()

    local switch = Digital.Get(DIGITAL_IN_1)

    if switch == 1 then
        local rawVal = Analog.Get(ANALOG_IN_1)
        IO.Print(rawVal)    
    end
end

Loop period is set to 1000ms
LED is connected on low side 1.
Blink LED every 0.5s with Time.WaitMs().

function Initialize()	
	LoopPeriodMs = 1000
end

function Loop()

    local ledState = Digital.Get(LOW_SIDE_1)

    if(ledState == 0) then
        Digital.Set(LOW_SIDE_1, 1)
    else
        Digital.Set(LOW_SIDE_1, 0)
    end

    Time.WaitMs(500)

end

If we set the LoopPeriodMs to 100ms, the script stops running and shows a status of "6", meaning "Script timed out and was stopped."

Here are the key points to understand:

Important Note: Be very careful when using the Time.WaitMs() function to avoid such errors.

We want the LED to blink every 0.5 seconds, while the main loop runs every 10ms. Here's how we do it:


  1. Count Loops: We count each loop from 1 to 50.
  2. Multiply Counter: Multiply the counter by the loop period (10ms).
  3. Check Reminder: Divide the result by 500.
  4. Toggle Output: If the remainder is 0, we toggle the LED.
Main_loop_period = 10
Counter = 1


function Initialize()	
	LoopPeriodMs = Main_loop_period
end

function Loop()

    if ((Counter)*Main_loop_period)%500==0 then
    
        local ledState = Digital.Get(LOW_SIDE_1)

        if(ledState == 0) then
            Digital.Set(LOW_SIDE_1, 1)
        else
            Digital.Set(LOW_SIDE_1, 0)
        end

    end
 
    if Counter<50 then
        Counter=Counter+1
    else
        Counter=1
    end

end


The script initializes a start time variable and defines two functions. In the Loop function, the script checks if the StartTime is nil and if so, sets it to the current time in milliseconds.
The Loop function then continuously gets the current time in milliseconds. If 500 milliseconds have passed since the StartTime, it resets the StartTime to the current time.
It then gets the state of a digital output LOW_SIDE_1.
If LOW_SIDE_1 is off (state is 0.0), it turns it on (state to 1.0).
If LOW_SIDE_1 is on (state is 1.0), it turns it off (state to 0.0).

This process repeats every 500 milliseconds, toggling the state of LOW_SIDE_1 each time.

StartTime = nil

function Initialize()	
	LoopPeriodMs = 10
end

function Loop()

    --Execute only once at the start of the loop
    if(StartTime == nil) then
        StartTime = Time.GetMs()
    end

    --Chechk every loop what is the time
    local CurrentTime = Time.GetMs() 

    if (CurrentTime - StartTime >= 500) then
        StartTime = CurrentTime

        local ledState = Digital.Get(LOW_SIDE_1)

        if(ledState == 0) then
            Digital.Set(LOW_SIDE_1, 1)
        else
            Digital.Set(LOW_SIDE_1, 0)
        end

    end
end

4.5 Example 5: CAN send

4.5.1 Example 5.1: (CAN send simple message)

To send a CAN message every 100ms with an ID of 0x205 and data value 500 using the first 2 bytes, follow these steps: 

image.png


  1. Use Template:

    • Start with example 4.3 as a template.
  2. Add Variables and Functions:

    • Define a CanID variable.
    • Initialize CAN with the CAN.Initialize() function.
  3. Sending Parameters:

    • Use the CAN_TX_ONLY parameter for sending.
    • Messages will not be extended.
    • Set filters to 0 since we are only sending.
  4. Modify the Code:

    • Delete the code for toggling the output.
  5. Create Custom Function:

    • Create a function that is called every 100ms.
    • Name the argument Data_raw.
    • Split Data_raw into 2 bytes.
    • Send the data using the CAN.Send() function.
CanID = 0x205

StartTime = nil

function Initialize()

    CAN.Initialize(CAN_TX_ONLY,false,0,0)

	LoopPeriodMs = 10
end

function Loop()

    --Execute only once at the start of the loop
    if(StartTime == nil) then
        StartTime = Time.GetMs()
    end

    --Chechk every loop what is the time
    local CurrentTime = Time.GetMs() 

    if (CurrentTime - StartTime >= 100) then
        StartTime = CurrentTime
        SendData(500)
    end
end

function SendData(Data_raw)
    local val = Data_raw
    local byte0 = math.floor(val) & 0xFF
    local byte1 = (math.floor(val) & 0xFF00) >> 8
    CAN.Send(CanID,{byte0,byte1,0,0,0,0,0,0})
end

In this example we only need 2 bytes, thus we don't need to send all 8 bytes out. You could do this instead:

 CAN.Send(CanID,{byte0,byte1})

4.5.2 Example 5.2 : CAN send extended message (J1939)

We will do the same as in example 5.1 except we will send an extended message (It will be a J1939 message PH3 - the data order might be different in the standard).

 
ID = 0x18FF8203. 

The only thing we need to change is 2 lines, we need to change the CanID and in the CAN.Initialize() function, the boolean value to true. If we change only the CanID then the message will still be sent out but the ID will be 0x199 (last 3 digits of - ID 0x18FF8199).

CanID = 0x18FF8199
CAN.Initialize(CAN_TX_ONLY,true,0,0)

4.6 Example 6: CAN receive

4.6.1 Example 6.1 : CAN receive

In this example we will turn ON and OFF a LED that is connected on low side 1 with a received can message.
The Can ID has to be 0x123 and we will send only one byte of data. If the value of data is 1 then the LED will be turned ON, otherwise it will be turned OFF. 
 
To use the CAN receive function we need to change the CAN.Initialite() and add a function called "CAN.Received(message)". With the following code we always go into the CAN.Received() function when a message is received and then we check if the ID is correct.

function Initialize()
    CAN.Initialize(CAN_RX_TX,false,0,0)
	LoopPeriodMs = 10
end

function Loop()


end

function CAN.Received(message)
    if (message.ID == 0x123) then
        IO.Print(" Data1: ", message.Data[1])
        if(message.Data[1] == 1) then
            Digital.Set(LOW_SIDE_1, 1)
        else
            Digital.Set(LOW_SIDE_1, 0)
        end
    end
end

We can also add a filter so we only go into the CAN.Received() when the message has a proper ID. We achieve this with the following code.

function Initialize()
    CAN.Initialize(CAN_RX_TX,false,0x123,0x123)
	LoopPeriodMs = 10
end

function Loop()


end

function CAN.Received(message)
    --if (message.ID == 0x123) then
    IO.Print(" Data1: ", message.Data[1])
    if(message.Data[1] == 1) then
        Digital.Set(LOW_SIDE_1, 1)
    else
        Digital.Set(LOW_SIDE_1, 0)
    end
    --end
end

4.7 Example 7: Read & Set CANopen objects


In this example, we'll use an analog input (HW AIN1) as a throttle to set the motor velocity reference (object 0x3010 0x05). We will also read this object and print its value to the Lua output. Additionally, we'll limit the maximum RPM using the math library to prevent the motor from running away if there's a problem with the analog reading.

0V = 0RPM
5V = 200RPM

For this example to work you need an inverter that is configured to work with the connected motor. You need to be first able to spin it in velocity mode using the configurator.
When you start the script go to operational and turn on PWMs manually.

If the analog throttle is damaged (either a short circuit or a broken circuit), there is no safety system in place. Here’s what can happen:

If the circuit is broken, no voltage will be applied, and the RPM will be 0.
If there is a short circuit, the inverter will receive the full 5V on the analog input, causing the RPM to go to the maximum.

To prevent these issues, we need to add safety features. These will be demonstrated in Example 8.

VelocityRef = {0x3010, 0x05}

function Initialize()
	LoopPeriodMs = 10
end

function Loop()
   

    local rpm = Analog.Get(ANALOG_IN_1) * 40
    rpm = math.min(rpm,200)

    CANopen.SetObjectValue(VelocityRef, rpm)
    
    local VelRef_from_CANopen = CANopen.GetObjectValue(VelocityRef)

    IO.Print(VelRef_from_CANopen)
end

4.8 Example 8: Demo application

4.8.1 Example 8.1 : Demo application using CANopen objects.

In this example, we will demonstrate how to use the HW AIN1 input for a simple throttle control with a potentiometer (0-5V).

function Initialize()

    CANopen.SetObjectValue(VelocityRef, 0) -- Set Velocity ref to 0
	CANopen.SetObjectValue(ControlMode, 1) -- Set velocity mode
	CANopen.SetNMTState(CO_OPERATIONAL)    -- Go into operational mode

	LoopPeriodMs = 100
end

function Loop()
    local throttleVoltage = Analog.Get(ANALOG_IN_1)
    -- Decide whether to enable or disable the motor
    if Digital.Get(DIGITAL_IN_1) == 1 then
        -- only enable if voltage on 0.2 < ANALOG_IN_1 < 0.5 V so the motor does not start ang goes to high RPM
        if  (0.2 < throttleVoltage and throttleVoltage < 0.5) then
            CANopen.SetObjectValue(PwmControl, 1)
        end
    else
        CANopen.SetObjectValue(PwmControl, 0)
    end

    if (throttleVoltage > 0.2 and throttleVoltage < 4.8) == true then
           -- Map values 0.5 - 4.5 V to 0 - 200 RPM and
            local rpm = (throttleVoltage - 0.5) / 4 * 200
            rpm = math.min(rpm, 200)
            rpm = math.max(rpm, 0)
            CANopen.SetObjectValue(VelocityRef, rpm)
    else
        CANopen.SetObjectValue(VelocityRef, 0)
        CANopen.SetObjectValue(PwmControl, 0)
    end
end

4.8.2 Example 8.2 : Demo application using dedicated motor library.

In this example we will have the same functionality of the code as in example 8.1. but with the use of motor library
With the use of the motor library the code is easier to read and write than example 8.1.

function Initialize()

    Motor.SetReferenceVelocity(0)       -- Set Velocity ref to 0
	Motor.SetControlMode(VELOCITY_MODE) -- Set velocity mode
	CANopen.SetNMTState(CO_OPERATIONAL) -- Go into operational mode

	LoopPeriodMs = 100
end

function Loop()
    local throttleVoltage = Analog.Get(ANALOG_IN_1)
    -- Decide whether to enable or disable the motor
    if Digital.Get(DIGITAL_IN_1) == 1 then
        -- only enable if voltage on 0.2 < ANALOG_IN_1 < 0.5 V so the motor does not start ang goes to high RPM
        if  (0.2 < throttleVoltage and throttleVoltage < 0.5) then
            Motor.Enable()
        end
    else
        Motor.Disable()
    end

    if (throttleVoltage > 0.2 and throttleVoltage < 4.8) == true then
           -- Map values 0.5 - 4.5 V to 0 - 200 RPM and
            local rpm = (throttleVoltage - 0.5) / 4 * 200
            rpm = math.min(rpm, 200)
            rpm = math.max(rpm, 0)
            Motor.SetReferenceVelocity(rpm)
    else
        Motor.SetReferenceVelocity(0)
        Motor.Disable()
    end
end

4.9 Example 9: Throttle script (state - machine)

Throttle Control
Motor Operation
LED Diagnostics
Voltage and RPM Details
CANopen Error Messages

If the motor spins at Forward max speed and you switch the direction with the switch then it will spin at Reverse max speed

-- Limits
MAX_RPM = 300
MIN_RPM = -300

-- Objects
FORWARD_MAX_SPEED_ID = {0x3020, 0xD}
REVERSE_MAX_SPEED_ID = {0x3020, 0xE}
LED_ID 				 = {0x30A4, 0x02}


-- Inputs
ENABLE_SW 	 = DIGITAL_IN_1
DIRECTION_SW = DIGITAL_IN_2
THROTTLE_IN  = ANALOG_IN_1
DIRECTION_IN = DIGITAL_IN_3


-- Voltage thresholds
THROTTLE_MIN_V = 0.5
THROTTLE_MAX_V = 4.5
DEADBAND 	 = 0.3


ERROR	  = 0
ERROR_reg = 0
WARNINGS  = 0


StartTime1	= nil
StartTIme2	= nil
StartTime3 	= nil
StartTime4 	= nil
FlagCounter = 0


DIN		  = {ON = 1, OFF = 0}
LED 	  = {OFF = 0x0, ON = 0x1}
DIRECTION = {FORWARD = 1, REVERSE = 0}

-- Application FSM
Application = {}


function Initialize()
	CAN.Initialize(CAN_TX_ONLY,false,0,0)

	Application.NextState = "Idle"

    Motor.SetReferenceVelocity(0)
	Motor.SetControlMode(VELOCITY_MODE)

	LoopPeriodMs = 10
end


function Loop()
	Application[Application.NextState]()
end


Application.Idle = function ()
	CANopen.SetObjectValue(LED_ID, LED.ON)
	CANopen.SetNMTState(CO_OPERATIONAL)
	Application.NextState = "Start"
end


Application.Start = function ()


    -- IF enable switch is off go to stop state
    if (Digital.Get(ENABLE_SW) == DIN.OFF ) then
		Application.NextState = "Stop"
		return
	end


    -- Get voltage
	local throttleVoltage = Analog.Get(THROTTLE_IN)


    -- Go into error state if throttle voltage is out of bounds
    if OutOfBounds(throttleVoltage, (THROTTLE_MIN_V - DEADBAND), (THROTTLE_MAX_V + DEADBAND)) == true then
		ERROR = (ERROR or 0) | 1
		Application.NextState = "Error"
		return
	end


    -- Check that voltage is bellow minimal throttle voltage - so that the motor does not start at high speed
	if Motor.GetState() == MOTOR_OFF then
		if throttleVoltage < (THROTTLE_MIN_V) then
            Motor.Enable()
			WARNINGS = WARNINGS & Negate_Xbit(1, 8)
            return
		else
			WARNINGS = (WARNINGS or 0) | 1
        end
	end

	-- Only set velocity reference when pwms are enabled
	if ((Motor.GetState() == MOTOR_RUN) and (throttleVoltage >= 0.5)) then
		-- Calculate Voltage
    	--local rpm = ((throttleVoltage - THROTTLE_MIN_V) * MAX_RPM) / (THROTTLE_MAX_V - THROTTLE_MIN_V)
		local rpm = ((throttleVoltage - THROTTLE_MIN_V) * GetMaxSpeed()) / (THROTTLE_MAX_V - THROTTLE_MIN_V)
    	-- Limit RPM
    	rpm = math.min(rpm, MAX_RPM)
    	rpm = math.max(rpm, MIN_RPM)
		IO.Print("REF: ", rpm)
    	-- Set ref
		Motor.SetReferenceVelocity(rpm)
	end

	-- Send warnings every 100ms
	local CurrentTime = Time.GetMs()
	if((StartTIme2 == nil) or (CurrentTime-StartTIme2) >= 100) then
		SendWarnings()
	end
end


Application.Stop = function ()
    Motor.SetReferenceVelocity(0)
	Motor.Disable()
    --When eneble sw is activated go to start state
	if (Digital.Get(ENABLE_SW) == DIN.ON) then
		Application.NextState = "Start"
	end
end

Application.Error = function ()

    Motor.Disable()
	Motor.SetReferenceVelocity(0)

	--local ledValue = CANopen.GetObjectValue(LED_ID)
    local throttleVoltage = Analog.Get(THROTTLE_IN)
	local ErrorBlinkCounter = 0
	local CurrentTime

	--If bit 0 = 1 then OutOfBounds is detected
	if (ERROR & 1) == 1 then
		if throttleVoltage < (THROTTLE_MIN_V - DEADBAND) then
			ERROR = ERROR | 2
			ERROR_reg = ERROR_reg | 1 -- set the generic error register
		elseif throttleVoltage > (THROTTLE_MAX_V + DEADBAND) then
			ERROR = ERROR | 4
			ERROR_reg = ERROR_reg | 1 -- set the generic error register
		else
			-- Clear ERROR bit 0, 1, 2
			ERROR = ERROR & Negate_Xbit(7, 16)
			-- Clear the generic error register
			ERROR_reg = ERROR_reg | Negate_Xbit(1, 8)
		end
	end

	-- Send Errors every 100ms
	CurrentTime = Time.GetMs()
	if ((StartTime1 == nil) or (CurrentTime - StartTime1) >= 100) then
		StartTime1 = CurrentTime
		SendError()
	end

	-- Determine how many times we want to blink the led
    if ((ERROR & 2) >> 1) == 1 then
        ErrorBlinkCounter = 2
    elseif ((ERROR & 4) >> 2) == 1 then
        ErrorBlinkCounter = 3
    end

    -- Start the blinking cycle if enough time has passed
	CurrentTime = Time.GetMs()
    if StartTime3 == nil or (CurrentTime - StartTime3) >= 3000 then
        StartTime3 = CurrentTime
        FlagCounter = 0
    end

    -- Handle the blinking logic
    if FlagCounter < 2 * ErrorBlinkCounter then
        if StartTime4 == nil or (CurrentTime - StartTime4) >= 300 then
            StartTime4 = CurrentTime
            FlagCounter = FlagCounter + 1

            -- Toggle LED
            --if ledValue == 0  then
            if FlagCounter % 2 == 1 then
				CANopen.SetObjectValue(LED_ID, LED.ON)
            else
                CANopen.SetObjectValue(LED_ID, LED.OFF)
            end
        end
	else
		-- Ensure the LED is off during the pause period
        CANopen.SetObjectValue(LED_ID, LED.OFF)
    end

    if ERROR == 0 then
		FlagCounter = 0
		Application.NextState = "Idle"
		return
	end
end


function GetMaxSpeed()
	if Digital.Get(DIRECTION_IN) == DIRECTION.FORWARD then
		return CANopen.GetObjectValue(FORWARD_MAX_SPEED_ID)
	end
	return CANopen.GetObjectValue(REVERSE_MAX_SPEED_ID)
end


function SendError()
	-- Send on ID + nodeID; we get the NodeId from CANopen off the inverter
	local CanID = 0x80 + CANopen.GetObjectValue({0x100B, 0x00})
	local byte0 = ERROR & 0xFF
	local byte1 = (ERROR & 0xFF00) >> 8
	local byte2 = ERROR_reg & 0xFF
	CAN.Send(CanID,{byte0, byte1, byte2, 0, 0, 0, 0, 0})
end

function SendWarnings()
	-- Send on PDO + nodeID;
	local CanID = 0x180 + CANopen.GetObjectValue({0x100B, 0x00})
	local byte0 = WARNINGS & 0xFF
	CAN.Send(CanID,{byte0, 0, 0, 0, 0, 0, 0, 0})
end


function OutOfBounds(throttleVoltage, minVal, maxVal)
    if (throttleVoltage < minVal) or (throttleVoltage > maxVal) then
        return true
    else
        return false
    end
end


--- Fuctino to negate bits
---@param value number Value to negate
---@param xBit number Number of bites e.g 16bit => xBit = 16
function Negate_Xbit(value, xBit)
    local negated = 0
    for i = 0, (xBit - 1) do
        if (value & (1 << i)) == 0 then
            negated = negated + (1 << i)
        end
    end
    return negated
end

4.10 Example 10: Read protections of motor control in Lua

Purpose:
This script is designed to monitor and report various hardware errors in motor control systems, using Lua to check status registers for issues such as overtemperature, voltage discrepancies, and communication failures.

Description:

Usage:
It is best to use only the protections that you need to check or send. By doing this, you can make the Protection_bits table smaller, which in turn reduces memory usage—a crucial consideration in environments with limited resources. Either way, if the value returned from the function is greater than 0, an error has occurred.

local Errors = {}
local Protection_bits = {
    {0x1, "sw_phase_error"},
    {0x2, "sw_DC_link_overvoltage_error"},
    {0x4, "sw_DC_link_undervoltage_error"},
    {0x8, "bridge_overtemperature_error"},
    {0x10, "capacitor_overtemperature_error"},
    {0x20, "motor_overtemperature_error"},
    {0x40, "current_offset_error"},
    {0x80, "CAN_command_timeout_error"},
    {0x100, "system_self_test_failed_error"},
    {0x800, "motor_feedback_error"},
    {0x8000, "CAN_communication_error"},
    {0x10000, "logic_supply_monitor_error"},
    {0x20000, "dc_link_overvoltage_comparator_error"},
    {0x80000, "gatedriver_error"},
    {0x100000, "HV_interlock"},
    {0x200000, "motor_temperature_sensor_fail"},
    {0x400000, "capacitor_temperature_sensor_fail"},
    {0x800000, "bridge_temperature_sensor_fail"},
    {0x1000000, "bridge_fault"},
    {0x2000000, "logic_supply_fault"},
    {0x4000000, "bus_bar_fault"},
    {0x8000000, "system_initialization_error"},
    {0x10000000, "fault_Vrefs_chip"},
    {0x20000000, "application_error_state"},
    {0x40000000, "motor_control_error"}
    --{0x80000000, "error_extended"}
}

-- Initialize
function Initialize()
    LoopPeriodMs = 10
end

function Loop()
    local protections = Motor.GetProtectionsLow()
    
    for i = 1, #Protection_bits do
        local bitmask = Protection_bits[i][1]
        local varname = Protection_bits[i][2]
        Errors[i] = {bitmask = bitmask, name = varname, state = (protections & bitmask) == bitmask}
    end

    -- Example usage to print all active errors
    for i = 1, #Errors do
        if Errors[i].state then
            IO.Print("Error detected: " .. Errors[i].name)
        end
    end
end

5. Benchmark

5.1 Examples for benchmark:

5.1.1 00_Empty_script

function Initialize()
	LoopPeriodMs = 10
end

function Loop()
end

5.1.2 01_GetObject

function Initialize()
	LoopPeriodMs = 10
end

function Loop()
	for i = 1,100 do
		CANopen.GetObjectValue({0x30B0, 0x04})
	end
end

5.1.3 02_GetObject_with_library

function Initialize()
	LoopPeriodMs = 10
end

function Loop()
	for i = 1,100 do
		Digital.Get(DIGITAL_IN_4)
	end
end

5.1.4 03_GetSet

function Initialize()
	LoopPeriodMs = 10
end

function Loop()
	for i = 1,50 do
		local throttle = CANopen.GetObjectValue({0x3090, 0x01})
		CANopen.SetObjectValue({0x3010, 0x05}, throttle * 40) --at 4.5V = 180RPM
	end
end

5.1.5 04_GetSet_with_library

function Initialize()
	LoopPeriodMs = 10
end

function Loop()
	for i = 1,50 do
		local throttle = Analog.Get(ANALOG_IN_1)
		Motor.SetReferenceVelocity(throttle * 40) --at 4.5V = 180RPM
	end
end

5.2 Results

We loaded each script onto the inverter and started the "Resource Manager." We ran the script for a specific period and logged the variables to a live chart, from which we then created the following table:

Examples from 05 onwards can be found under the title 4. Examples.

Table 55: Script benchmark tests

Name of script

RAM usage [kB]

RAM free
[kB]

RAM used
[%]

Avg. loop duration [us]

Compile time [ms]

Initialization time [ms]

00_Empty_script 11.800

24.040

33 13 2.7 0.175
01_GetObject 11.728 24.112 33 5000 2.9 0.173
02_GetObject_with_library 11.784 24.056 33 510 2.94 0.241
03_GetSet 12.088 23.752 34 5000 3.178 0.177
04_GetSet_with_library 12.128 23.712 34 450 3.149 0.174
05_Timer_example_4_1 12.064 23.776 34 500407 3.294 0.242
06_Timer_example_4_2 12.200 23.640 34 17 3.669 0.177
07_Timer_example_4_3 12.232 23.608  34 19 3.747 0.175
08_Example_5 12.104 23.736 34 18 4.332 0.213
09_Example_6 15.512 20.328 43 9 3.468 0.225
10_Example_7 22.048 12.792 63 125 3.506 0.191
11_Example_8_1 13.472 22.368 38 110 5.030 0.293
12_Example_8_2 13.072 22.768 36 50 4.628 0.250
13_Example_9 21.112 14.728 59 250

15.756

0.742

Ram used [%]


Average loop time [us]

Examples 01, 03, and 05 have too much loop time and were excluded from the chart.

As you can see from the table and charts, reading and writing values directly from the CANopen stack takes longer than using our library. For example, the 01_GetObject example has an average loop time execution of 5ms, whereas the 02_GetObject_with_library example has an average time of 510µs, which is approximately 10 times faster. Based on these results, it is better to use functions that directly interact with the inverter instead of the CANopen stack, where possible.

We can also see that the delay function used in example 05_Timer_example_4_1 has a very long loop time because we used the delay to blink an LED. This function must be used with caution and only for small delays if you have no other option.

For writing applications, we recommend using a state machine, which is more reliable. With a state machine, you have more control over what happens in each state and can properly define the transitions between states.

In section "5.3 Live chart data for each example", we can see the loop time, RAM used and RAM free for a specific time period. We can se that we have some spikes in loop time, which can indicate that some specific part of the code was executed or that the script was interrupted with a higher priority task. We can also see that the  RAM usage can very based on which part of the code is executed.

5.3 Global variables benchmark

In this section, we will demonstrate how the number of global variables affects script RAM usage and loop execution time.

Below is the code that will be used to test 10 global variables. We will increase the number of global variables to 20, 30, 100, and 150, but the principle will remain the same

Global1 = 1
Global2 = 1
Global3 = 1
Global4 = 1
Global5 = 1
Global6 = 1
Global7 = 1
Global8 = 1
Global9 = 1
Global10 = 1

function Initialize()
	LoopPeriodMs = 10
end

function Loop()
  IO.Print(Global1)
  IO.Print(Global2)
  IO.Print(Global3)
  IO.Print(Global4)
  IO.Print(Global5)
  IO.Print(Global6)
  IO.Print(Global7)
  IO.Print(Global8)
  IO.Print(Global9)
  IO.Print(Global10)
end

In the table below, we recorded the data for different sets of global variables. We can observe that as the number of global variables increases, the compile time, initialization time, loop execution time, and RAM usage all increase. It is important to remember that we are limited by the loop execution time set in the initialize() function. If we exceed that time, we encounter an error.

More importantly, we need to carefully manage the number of global variables because excessive use can lead to memory overflow. As shown in the table below, having 150 global variables consumes around 75% of the RAM, but our code has almost no functionality. If we add the following code right after the Loop() function:

for i = 1,50 do
		local throttle = CANopen.GetObjectValue({0x3090, 0x01})
		CANopen.SetObjectValue({0x3010, 0x05}, throttle * 40) --at 4.5V = 180RPM
end

We see that the RAM usage jumps to 98%. This shows that RAM usage depends not only on the number of global variables but also on the quality of the code itself. Therefore, we need to write efficient code, which means avoiding the use of global variables, using local variables, and implementing proper algorithms.

For these reasons, it is difficult to specify the maximum number of global and local variables. We recommend testing your script continuously during development.

Table 56: How the number of globals affect the execution of a script

Number of globals

RAM usage [kB]

RAM free
[kB]

RAM used
[%]

Avg. loop duration [us]

Compile time [ms]

Initialization time [ms]

10 globals
12.656 23.184 35 200 3.874 0.257
20 globals 14.480 21.360 40 380 4.942 0.412
30 globals 14.688 21.152 41 650 6.018 0.591
100 globals 25.408 10.432 71 2000 13.5 0.795
150 globals 26.976 8.864 75 3110 18.4 1.276
150 globals + 4 lines of code 35.072 768 98 8000 18.9 1.213

6. Best practices

For further insights and practical tips, I recommend exploring "Programming in Lua," available at: Programming in Lua. Authored by experts in the field, this comprehensive resource offers invaluable guidance for Lua developers. It's worth noting that the entire topic is accessible free of charge as of April 24, 2024. Additionally, some sections of the topic include examples to further illustrate key concepts and techniques.

6.1 Minimizing Global Variables

The most common problem with Lua scripting occurs when the script becomes "big" and the programmer uses only or to much global, resulting in an "out of memory" error. In Lua scripting, it's imperative to exercise caution with global variables to prevent memory exhaustion and script failures, especially on embedded systems. Emphasizing the utilization of local variables whenever possible is paramount. By minimizing global variable usage, we mitigate the risk of memory overflow and enhance script performance.

7. Known issues and limitations

This section describes known issues and limitations, their causes, and solutions or mitigations if they exist.

7.1 Issues

7.1.1 Lua is not enabled

Error 0x06070010 when loading a script may indicate Lua object 0x2040 0x01 isn't set to 1. To resolve, ensure proper access level in Configurator to unlock this feature. If you do not have the proper access level contact us.

image.png

image.png

7.1.2 Output object out of memory error

When reading an output object whose value is periodically changing by the script, it is possible that an "Out of memory" error, as shown in Figure 8, may be reported by the emDrive Configurator.

Cause: unsynchronized reading and writing to output object by script and emDrive Configurator at the same time.

Solution: read output object again.

7.1.3 IO.Print() isn't functioning

If IO.Print() isn't functioning, it may signal an outdated firmware version. FW 1.12.2 -  lacks support, while FW 1.13.2 onwards enables the function.

7.1.4 Protections

If multiple protections are active, the CANopen read wont work for the protection object. (New FW will fix this issue)


Throttle Application Manual

Disclaimer
Proprietary Information
: This manual contains proprietary information belonging to Emsiso d.o.o. The text and graphics included are for illustration and reference purposes only. Specifications are subject to change without notice.
Scope of Application: The Throttle Application is designed for integration with emDrive systems. Users should ensure compatibility with their specific hardware and software configurations.
User Responsibility: Proper installation, configuration, and operation of the Throttle Application require technical expertise. Users must adhere to all safety guidelines and operational instructions provided in this manual.
Updates and Revisions: Emsiso d.o.o. reserves the right to modify the product and manual without prior notice. Users are encouraged to consult the latest version of the manual for up-to-date information.
Liability Disclaimer: Emsiso d.o.o. assumes no liability for damages resulting from the use or misuse of the Throttle Application, including but not limited to personal injury, equipment damage, or financial loss.

By using the Throttle Application, you acknowledge that you have read, understood, and agreed to these terms and conditions

1. Introduction

1.1 Overview

The Throttle application allows the user to implement a simple throttle control directly on the emDrive. Input devices can be connected directly to the emDrive and mapped to the throttle application. The application calculates the desired output based on these inputs and passes it to motor control. The output can be used as either a torque input or a speed input in the motor controller, depending on which CANOpen object the output is mapped to.

1.2 Features

The Throttle application supports the following features:

All throttle application features can be independently enabled or disabled (except the brake module, which requires the throttle module to be enabled). This is done by setting the Thr1_Enable CAN objects of the throttle application as detailed in Table 1.

Table 1: Thr1_Enable CAN object
Object
Name
Object
Index
Object
Subindex
Description Unit

Thr1_Gen__Enable

0x4010 0x01

Enables the throttle application:
0 – disabled

1 – enabled (must be enabled for any modules to work)

/
Thr1_Enable__Throttle  0x4011

0x01

Enables the throttle module:

0 – disabled
1 – enabled

Bit
Thr1_Enable__Brake 0x4011 0x02

Enables the brake module:

0 – disabled 
1 – enabled (throttle must also be enabled)

Bit
Thr1_Enable__Pump  0x4011 0x03

Enables the pump control module:
0 – disabled

1 – enabled

Bit
Thr1_Enable__Precharge 0x4011 0x04

Enables the precharge module:

0 – disabled 
1 – enabled 

Bit
Thr1_Enable__DC_DC 0x4011 0x05

Enables the DC-DC turn on delay module:

0 – disabled 
1 – enabled

Bit
Thr1_Enable__SOC 0x4011 0x06

Enables the SOC monitoring module:

0 – disabled 
1 – enabled

Bit
Thr1_Enable__ChargingDetect 0x4011 0x07

Enables the charging detection module:

0 – disabled 
1 – enabled

Bit

2. Input and Output mapping

Input and output mapping for the Throttle application is managed using the Thr1_Obj object, which stores the CANOpen indexes and subindexes of the inputs and outputs mapped to the Throttle application variables. You can remap any variable to a different input by writing the CANOpen index and subindex of the new input to the corresponding Thr1_Obj sub-object.

The following table outlines the object mapping parameters for the Throttle application. Each object in the table represents a specific function or control point within the application, and the corresponding CANOpen index and subindex define where these objects are located in the CANOpen network. By configuring these parameters, users can tailor the Throttle application to their specific needs, ensuring precise control and monitoring of various functions.

By correctly configuring these object mappings, users can ensure that the Throttle application communicates accurately with the various inputs and outputs connected to the emDrive. This flexibility allows for precise control of throttle, brake, pump, and other critical functions, enhancing the overall performance and reliability of the system.

Table 2: Object mapping parameters
Object
Name
Object
Index
Object
Subindex
Description Unit
Thr1_Obj__AppControl 0x4012 0x01

Address of CAN object where commands are

sent to application

Obj
Thr1_Obj__AppState 0x4012 0x02

Address of CAN object where application

state is saved

Obj
Thr1_Obj__ThrottleInput 0x4012 0x03 Address of CAN object with throttle input Obj
Thr1_Obj__ThrottleFWD_DIvalue 0x4012 0x04

Address of CAN object to forward switch 

input

Obj
Thr1_Obj__ThrottleREW_DIvalue 0x4012 0x05

Address of CAN object to reverse switch 

input

Obj
Thr1_Obj__TargetObj 0x4012 0x06

Address of CAN object where output

of throttle application is written

Obj
Thr1_Obj__BatValid 0x4012 0x07

Address of CAN object which stores BMS 

status.

Obj
Thr1_Obj__BatSOC 0x4012 0x08

Address of CAN object which stores battery 

SOC in percentage

Obj
Thr1_Obj__BatState 0x4012 0x09

Address of CAN object which stores battery 

state

Obj
Thr1_Obj__ChargingDetectDIvalue 0x4012 0x0A

Address of CAN object which shows if 

charging is detected. If not 0 throttle 

application considers battery to be charging.

Obj
Thr1_Obj__Pump1EnableDO 0x4012

0x0B

Address of CAN object with main pump 

enable control - active always when power stage is enabled

Obj
Thr1_Obj__Pump2EnableDO 0x4012 0x0C

Address of CAN object with cooling pump 

enable control

Obj
Thr1_Obj__CoolingInputTemperature1 0x4012 0x0D

Address of CAN object with temperature 

input 1 (default bridge heatsink temp)

Obj
Thr1_Obj__CoolingInputTemperature2 0x4012 0x0E

Address of CAN object with temperature 

input 2 (default motor temp)

Obj
Thr1_Obj__DCDCenableDO 0x4012 0x0F

Address of CAN object with DC-DC enable 

control

Obj
Thr1_Obj__BuzzerDO1 0x4012 0x10

Address of CAN object with buzzer high side 

enable control

Obj
Thr1_Obj__BuzzerDO2 0x4012 0x11

Address of CAN object with buzzer low side 

enable control

Obj
Thr1_Obj__BrakeInput 0x4012 0x12 Address of CAN object with brake input Obj
Thr1_Obj__RPM_in 0x4012 0x13 Address of CAN object with RPM data Obj
Thr1_Obj__MainRelayEnableDO 0x4012 0x14

Address of CAN object with main relay 

enable control

Obj
Thr1_Obj__PrechargeRelayEnableDO 0x4012 0x15

Address of CAN object with precharge relay 

enable control

Obj
Thr1_Obj__PrechargeDC_voltage 0x4012 0x16

Address of CAN object with measured 

voltage during precharge procedure

Obj
Thr1_Obj__PrechargeBatteryVoltage 0x4012 0x17

Precharge voltage reported by battery. Set to 0 if not available/used. [obj]

Obj


2.1 Examples

2.1.1. Configuring an object to be used in the application (e.g. for Thr1_Obj__ThrottleInput)

All mapping of object to the application are done in the same way. For relays (digital outputs), switches (digital inputs), torque & velocity reference,... The main point is to show that you need to combine index and subindex of the object you want to use in your throttle application.

For example, to map the throttle voltage input to the emDrive analog input 1 (which has a CANOpen object HW_AIN_AIN1 with index 0x3090 and subindex 0x01 as shown in the picture below), you would write 0x309001 to Thr1_Obj__ThrottleVoltage.
To verify that the correct value has been written, check the HEX value indicated by the black circle (black circle).

3. How to Use Each Module

3.1 Initial Setup Assumptions

It is assumed that the user has already calibrated the angle sensor, and that all other emDrive parameters for the motor and regulation have been properly configured.

Before using any module, the CANOpen object Thr1_Gen__Enable (index 0x4010, subindex 0x01) must be enabled by writing a value of 1.

0x4010 0x01 - Thr1_Gen__Enable = 1

3.2 Precharge Module

The Precharge module limits the charging current of the emDrive capacitors by activating the precharge relay switch before the main relay switch is engaged. The precharge relay switch has a resistor connected in series, which limits the current during the capacitor charging phase.

Note that the main and precharge relay switches are not integrated within the emDrive and must be provided externally. For more details, refer to section 5.4 of the emDrive user manual.

For readability and clarity, the following abbreviations are used:

The sequence begins with the activation of the precharge relay switch. After the duration specified by the Thr1_Precharge__PrechargeTime parameter (default is 2 seconds), the drive voltage is checked. If the voltage does not exceed the threshold set by the Thr1_Precharge__MinDC_Voltage parameter, an error state is reported. If the voltage is above this threshold, the main relay switch is activated. Finally, after the time specified by the Thr1_Precharge__DelayBeforePrechargeOff parameter has elapsed, the precharge relay switch is turned off, and the run state is set to true, activating the throttle and brake functionality. Additionally, we have a diagram illustrating the above functionality. It is assumed that after the precharge time, the DC voltage exceeds the set threshold, thus no error state is encountered.


To set this module you have to set all of the objects in table 3 and also use the correct mapping for Thr1_Obj__PrechargeRelayEnableDO and Thr1_Obj__MainRelayEnableDO.

Table 3: Precharge module parameters
Object
Name
Object
Index
Object
Subindex
Description Unit
Thr1_Precharge__PrechargeTime 0x4016 0x01 Time to wait after the precharge relay switch is activated before checking the voltage s
Thr1_Precharge__DelayBeforePrechargeOff 0x4016 0x02 Time to wait before precharge relay switch 
is deactivated after the main relay switch 
has been activated
s
Thr1_Precharge__MinDC_Voltage 0x4016 0x03 Minimum voltage that must be present on 
drive capacitors in order to not enter error 
state
V
Thr1_Precharge__AllowedVoltageDifference 0x4016 0x04 In case battery voltage is provided, this is maximum difference allowed between battery voltage and DC link voltage after precharge time to not trigger precharge error V


You can also verify whether the precharge completed successfully or if an error occurred using the precharged module signals.

Table 4: Precharge module signals
Object
Name
Object
Index
Object
Subindex
Description Unit
Thr1_Precharge__Run 0x4016 0x05 Set to 1 when precharge is successfully finished bit
Thr1_Precharge__Err 0x4016 0x06 Set to 1 if there was an error during precharge bit

The module also allows configuration of the precharge module to compare the DC link voltage measured on the inverter with the actual voltage of the battery. It will turn on the main relay only when the voltage difference is less than the threshold specified by Thr1_Precharge__AllowedVoltageDifference (this needs to happen before the Thr1_Precharge__PrechargeTime). An example of this configuration can be found under "Precharge Example 2".

3.2.1 Examples

- Precharge example 1

Description:
In this example, we will use the precharge module. For this, we need two relays: the main relay and the precharge relay. We also need to determine the voltage threshold for activating the main relay and the correct timings, which are described in Section 3.2.

Relay Connections
Main Relay: Connected to HW_LS1 (Index: 0x30A0, Subindex: 0x02)
Precharge Relay: Connected to HW_LS2 (Index: 0x30A1, Subindex: 0x02)
Voltage Measurement
The voltage will be measured using the inverter, which is the standard method (0x3101 0x07 Udc).

Timings
Precharge Time: 2 seconds
Time After Precharge Relay Turns Off: 0.5 seconds
Voltage Threshold
The voltage threshold for activating the main relay is 24V.

Objects to set:

    1. Enable Throttle Application:

      0x4010 0x01 - Thr1_Gen__Enable = 1
    2. Enable the Precharge Module:

      Enable the Precharge Module by setting the corresponding flag in the throttle general object 0x4011 Thr1_Enable__Precharge to 1.

      0x4011 0x04 Precharge = 1
    3. Map Objects to the Application:

      Choose the proper digital outputs to control the main relay and precharge relay:

      e.g:
      As per description we have the main relay connected to HW_LS1 and the precharge relay connected to HW_LS2. The object indexes are as follows:

      HW_LS1 = 0x30A0 0x02
      HW_LS2 = 0x30A1 0x02

      We set the following:

      0x4012 0x14 MainRelayEnableDO = 0x30A002
      0x4012 0x15 PrechargeRelayEnableDO = 0x30A102

      It is recommended to use the default voltage measurement (0x3101 0x07 Udc), unless a different object mapping is required

      0x4012 0x16 PrechargeDC_Voltage = 0x310107

  1. Configure the Precharge Module:

    Set the precharge time:

    0x4016 0x01 PrechargeTime = 2

    Set the time after the precharge relay is turned off:

    0x4016 0x02 DelayBeforePrechargeOff = 0.5

    Set the minimum voltage that must be present:

    0x4016 0x03 MinDC_Voltage = 24

  2. Save and Reset:

    Save the settings (Ctrl+S) and perform a reset.
    Switch to operational mode.
    Test
    Enable auto start if everything works correctly (inverter will always go to operational mode after reset).

    0x3000 0x03 AutoStart = 1
- Precharge example 2

Description:
In this example, we will use the precharge module, to enable the min relay when the difference between the DC - link on the inverter and the battery voltage is less than the threshold set in Thr1_Precharge__AllowedVoltageDifference

For this example, you need to first finish the Precharge example 1 and then continue with the following steps.

First you need to use the PDOs to get the value of the battery and map it to one of the 0x3020 - General_Purpose objects.
In this example the voltage value of the battery is sent to 0x3020 0x09 - General_Purpose__gen1_32bit.

First you need to map the object where the value of the battery is stored:

0x4012 0x17 - PrechargeBatteryVoltage = 0x302009

Next you need to set allowed difference between DC-link that is measured on the inverter and the voltage of the battery.
Lets assume that the allowable voltage difference is 10V.

0x4016 0x04 - AllowedVoltageDifference = 10V

If the actual voltage difference is more than 0x4016 0x04 - AllowedVoltageDifference = 10V, after the period specified in Thr1_Precharge__PrechargeTime an error is raised.


3.3 Throttle module

The throttle module is responsible for calculating the desired output value based on the given input voltage. It provides features such as short to ground and short to power supply detection, reading forward and reverse switches, rate-limiting the output, and disabling the throttle when the brake is active. Below is a detailed guide on the state diagram and parameter settings for the throttle module.

Table 5: Throttle module parameters
Object
Name
Object
Index
Object
Subindex
Description Unit
Thr1_Throttle__ZeroValue 0x4013

0x01

At this input value output is zero. V
Thr1_Throttle__ZeroDeadBand 0x4013 0x02 Defines how large zero dead band is V
Thr1_Throttle__EnableDeadBand  0x4013 0x03 Defines how large enable dead band is. 
Should be less or equal to Thr1_Throttle__ZeroDeadBand value.
V
Thr1_Throttle__MaxInput 0x4013 0x04 At this input value output is at maximum 
value defined in 
Thr1_Throttle__OutFullPositive
V
Thr1_Throttle__MinInput 0x4013 0x05 At this input value output is at minimum 
value defined in 
Thr1_Throttle__OutFullNegative
V
Thr1_Throttle__NonValidMax 0x4013 0x06 Any input value greater than this will put 
throttle module into error state
V
Thr1_Throttle__NonValidMin 0x4013 0x07 Any input value lesser than this will put 
throttle module into error state
V
Thr1_Throttle__Progressive 0x4013 0x08 Determines how the output changes 
in relationship to the input between zero 
value and max value
/
Thr1_Throttle__Invert 0x4013 0x09 Inverts output Bit
Thr1_Throttle__RateLimit 0x4013 0x0A Limits how fast output can change. 1/s
Thr1_Throttle__OutFullPositive 0x4013 0x0B Output when input reaches max input
defined in Thr1_Throttle__MaxInput
/
Thr1_Throttle__OutFullNegative 0x4013 0x0C Output when input reaches min input
defined in Thr1_Throttle__MinInput
/
Thr1_Throttle__OutStartPositive 0x4013 0x0D Starting output value when throttle is no longer 0, for positive throttle values /
Thr1_Throttle__DisableAtBrake 0x4013 0x0E If set to 1, the throttle command is disabled (ramped down to 0) when the brake is activated; otherwise, the brake demand is subtracted from the throttle demand Bit
Thr1_Throttle__WaitBeforeBridgeDisable 0x4013 0x0F Time to wait after throttle is disabled before bridge is disabled s
Thr1_Throttle__WaitAfterStart  0x4013 0x10 How long to wait after startup before 
operational state is entered
s
Thr1_Throttle__IsNegativeBrake 0x4013 0x11 If set to 1, negative throttle will be treated as brake /
Thr1_Throttle__OutStartNegative 0x4013 0x1A Starting output value when throttle is no longer 0, for negative throttle values /
Thr1_Throttle__SkipInitWaitForSpeedDrop 0x4013 0x1B If set to 1, wait for speed drop will be skipped during initialization Bit

Table 6: Throttle module signals
Object
Name
Object
Index
Object
Subindex
Description Unit
Thr1_Throttle__Input 0x4013  0x12 Input from throttle /
Thr1_Throttle__OutNorm  0x4013  0x13 Normalized throttle output (-1 full negative, 1 full 
positive)
/
Thr1_Throttle__Out 0x4013  0x14 Throttle output in system units /
Thr1_Throttle__TotalOut 0x4013  0x15 Sum of throttle and brake in system units /
Thr1_Throttle__Enabled  0x4013  0x16 If throttle is out of centre position for EnableDeadBand - power stage enabled Bit
Thr1_Throttle__State 0x4013  0x17

State of throttle state machine:
None(0), Start(1), WaitForNeutral(2), Idle(3), Driving(4),Error(5),
Charging(6), WaitForSpeedDrop(7), WaitForSpeedDropChargingError(8), ErrorEntry(9), WaitForSpeedDropInit(10)

/
Thr1_Throttle__Err 0x4013  0x18 If throttle input is outside NonValidMax or NonValidMin Bit
Thr1_Throttle__ErrCode  0x4013  0x19 Flag bits for different errors.
0x01 -> throttle, 0x02 -> precharge, 0x04 -> App/drive low level, 0x08 -> brake
/

3.3.1 State Diagram


The state diagram for the throttle module consists of several states and transitions, ensuring the correct operation and safety of the system.

image.png

Startup State

Operational State

Idle State

Driving State

WaitForNeutral State

WaitForSpeedDrop State

Charging State

Error State

3.3.2 Input to Output Transformation

The input to output transformation converts the input voltage of the analog input to output torque, which is forwarded to motor control if the throttle is enabled. The basic transformation is illustrated in the graph below.

The transformation includes the following regions:

This transformation ensures that the throttle application accurately converts the input voltage into the appropriate output for motor control, with safeguards in place to handle invalid input values and prevent unintended behaviour.

3.3.2.1 Progressive

The following chart illustrates how the throttle output changes based on different values of the Thr1_Throttle__Progressive parameter. This parameter modifies the response curve of the throttle, allowing for either a more gradual or more aggressive acceleration profile.

Understanding the Impact of Progressive Values

By adjusting this parameter, the response can be fine-tuned so that the throttle behaviour matches specific application needs.

3.3.3 Other throttle module functions

Enable dead band

Enable Dead Band functions similarly to Zero Dead Band. However, instead of setting the throttle output to 0, it disables the driver bridge when the input is less than a specified distance from the zero point. This distance is determined by the Enable Dead Band threshold. To disable Enable Dead Band, set its value to 0.

Ensure this threshold is always smaller than the Zero Dead Band threshold to prevent unusual behaviour.

Disable at brake

When the parameter 0x4013 0x0E DissableAtBrake is set to 1, the throttle module output is set to zero whenever the brake is active. This means that as soon as the brake is applied, the emDrive system will start braking immediately, regardless of the throttle input.

When the parameter 0x4013 0x0E DissableAtBrake is set to 0, the throttle and brake outputs are combined before being sent to motor control. In this case, the emDrive system will only start braking if the brake output is greater than the throttle output.

Rate limit

The rate limit controls how quickly the output can change in response to changes in the input (Higher the number, faster the change). This is determined by the parameter 0x4013 0x0A RateLimit.

When the input changes from 0 to 1 (works on normed value), the output will change from 0 to 1 in 1 / RateLimit seconds.

Output of throttle at zero value

If the application needs a specific output (not zero) to be set at zero position of a potentiometer 0x4013 0x01 ZeroValue, the object 0x4013 0x0D OutStartPositive can be set to achieve this functionality. This will be presented in Throttle example 4. 
With this feature the 0x4013 - Thr1_Throttle__EnableDeadBand, should be disabled

3.3.4 Examples

- Throttle example 1

Description:
Monodirectional throttle using only one analog input.

In this example, we will use a potentiometer as our throttle input. Our goal is to control the motor in velocity mode, ranging from 0 to 100 RPM.

Throttle Input Details
Potentiometer: Used as the throttle input. Connected to AIN3 = 0x3090 0x03
Voltage and RPM Control
Error Condition: If the voltage from the potentiometer is lower than 0.2V or higher than 4.8V, an error will occur (indicating a possible short or broken connection).
0 RPM: At 0.5V, the motor should run at 0 RPM.
100 RPM: At 4.5V, the motor should run at the maximum speed of 100 RPM.

Objects to set:

    1. Enable Throttle Application:

      0x4010 0x01 - Thr1_Gen__Enable = 1
    2. Enable the Throttle Module:

      Use the throttle general object 0x4011 Thr1_Enable to enable the proper module.

      0x4011 0x01 Throttle = 1

    3. Map Objects to the Application:

      Choose the reference value to control (velocity or torque) and map analog/digital inputs and outputs.

      To control velocity:

      0x4012 0x06 TargetObj = 0x301005

      To control torque:

      0x4012 0x06 TargetObj = 0x301004

      Set the control mode (0 for torque, 1 for velocity):

      0x3100 0x01 ControlMode = 0 or 1

      Define the analog throttle input (potentiometer on AIN3):

      0x4012 0x03 ThrottleInput = 0x309003


    4. Configure the Throttle Module:

      Set the throttle voltage parameters as  explained in Section 4.2:

      0x4013 0x01 ZeroValue = 0.5
      0x4013 0x04 MaxInput = 4.5
      0x4013 0x05 MinInput = 0.5
      0x4013 0x06 NonValidMax = 4.8
      0x4013 0x07 NonValidMin = 0.2

      Set the maximum and minimum TorqueRef or VelocityRef:
      In this example we will use max = 100rpm and min = 0rpm

      0x4013 0x0B OutFullPositive = 100
      0x4013 0x0C OutFullNegative = 0


    5. Save and Reset:

      Save the settings (Ctrl+S) and perform a reset.
      Switch to operational mode.
      Test
      Enable auto start if everything works correctly (inverter will always go to operational mode after reset).

      0x3000 0x03 AutoStart = 1


- Throttle example 2

Description:

In this example, we will use the same potentiometer as in Throttle Example 1. The main difference is that this setup allows for bidirectional control of the throttle. Here’s how it works:

This setup enables you to control the motor speed and direction using a single analog input.

Objects to set:

    1. Same as example "Throttle example 1"
    2. Same as example "Throttle example 1"
    3. Same as example "Throttle example 1"
    4. Configure the Throttle Module:
      Set the throttle voltage parameters as  explained in Section 4.2:

      0x4013 0x01 ZeroValue = 2.5
      0x4013 0x04 MaxInput = 4.5
      0x4013 0x05 MinInput = 0.5
      0x4013 0x06 NonValidMax = 4.8
      0x4013 0x07 NonValidMin = 0.2

      Set the maximum and minimum TorqueRef or VelocityRef:
      In this example we will use max = 100rpm and min = -100rpm

      0x4013 0x0B OutFullPositive = 100
      0x4013 0x0C OutFullNegative = -100

      Additionaly we have to set IsNegativeBrake 

      0x4013 0x11 IsNegativeBrake = 0

    5. Same as example "Throttle example 1"


- Throttle example 3

Description:
In this example, we will use the same potentiometer as in Throttle Example 1, along with two additional digital inputs to control the motor's direction. 

Digital input 1 = 0x30B0 0x01 DIN1 - enable/disable positive throttle
Digital input 2 = 0x30B0 0x02 DIN2 - enable/disable negative throttle.

Objects to set:

    1. Same as example "Throttle example 1"
    2. Same as example "Throttle example 1"
    3. Same as example "Throttle example 1"
    4. Same as example "Throttle example 1"

      Additionally, we only need to map digital input objects for enabling forward/reverse throttle.

      e.g.
      We will use object 0x30B0 0x01 - DIN1 to enable/disable positive throttle.
      We will use object 0x30B0 0x02 - DIN2 to enable/disable negative throttle.

      So we need to set:

      0x4012 0x04 ThrottleFWD_Dlvalue = 0x30b001
      0x4012 0x05 ThrottleREW_Dlvalue = 0x30b002

      If DIN1 and DIN2 are the same value, the throttle will not work. Only one of them can be 1 and the other 0.

      Currently the inverter does not support using only one physical input (e.g switch) to change direction. There must be two of them!


      Additionaly we have to set IsNegativeBrake 

      0x4013 0x11 IsNegativeBrake = 0

    5. Same as example "Throttle example 1"

- Throttle example 4

Description:
In this example, we will need a working "Throttle example 1", the main difference will be that at 0.5V we want 100RPM and then we want to ramp up do the max value of 200 RPM.
We also want to have an ignition switch (enable switch). Which will be connected to 0x30B0 0x01 - DIN1 


We need to set the OutFullPositive = MAX desired RPM 
We need to set the OutStartPositive = Standby RPMs

0x4013 0x0B - OutFullPositive = 100
0x4013 0x0D - OutStartPositive = 100 

With these settings you can try the application, where you will see that you can never disable the motor. If you go to the min position of the potentiometer the motor will spin with 100 RPM.


Setting the switch:
Currently there is a workaround to have an ignition switch which will be changed in the future official release.
To get the ignition switch working we need to map the 0x30B0 0x01 - DIN1 to ThrottleFWD_Dlvalue

0x4012 0x04 - ThrottleFWD_Dlvalue = 0x30B001
0x4012 0x05 - ThrottleREW_Dlvalue = 0x302001 This is the workaround we map the 0x3020 0x01 - gen1_8bit to the throttle application. This value shall be always at default = 0.

If the throttle potentiometer is in any position other than at 0 e.g. at motor spins at 140RPM and you disable the ignition switch, the motor will stop spinning. But if you then start ignition again it will go directly to the rpm that are set with throttle (in this example back to 140RPM). You need to put the throttle to min.

With this workaround only monodirectional throttle + ignition will work. In the future FW release, there will be a new object for ignition/enable and it will work also in bidirectional throttle.
The protection will also be implemented so that it does not spin out of control.

- Throttle example 5

Description:
Monodirectional throttle that uses a single analog input and provides gradual braking when the throttle is released.

In this example, a potentiometer is used as the throttle input. The goal is to control the motor in torque mode, with a torque range from 0 to 10 Nm. Additionally, we want to simulate the behaviour of a combustion engine. This means that when the throttle pedal is released (i.e. the potentiometer goes to its lowest setting), the system does not coast but instead applies braking (engine braking). This effect is achieved using only the throttle module.

Throttle Input Details
Potentiometer: Used as the throttle input. Connected to AIN3 = 0x3090 0x03
Voltage and RPM Control
Error Condition: If the voltage from the potentiometer is lower than 0.2V or higher than 4.8V, an error will occur (indicating a possible short or broken connection).
0 Nm: At 1V
10 Nm: At 4.5V, the motor should run at the maximum torque of 10 Nm
-5 Nm: At 0.5V, the motor should brake with maximum torque of 5 Nm - With the decrease of the RPMs the braking torque is also steadily decreased.

Objects to set:

    1. Enable Throttle Application:

      0x4010 0x01 - Thr1_Gen__Enable = 1
    2. Enable the Throttle Module:

      Use the throttle general object 0x4011 Thr1_Enable to enable the proper module.

      0x4011 0x01 Throttle = 1

    3. Map Objects to the Application:

      Choose the reference value to control (velocity or torque) and map analog/digital inputs and outputs.

      To control torque:

      0x4012 0x06 TargetObj = 0x301004

      Set the control mode (0 for torque, 1 for velocity):

      0x3100 0x01 ControlMode = 0

      Define the analog throttle input (potentiometer on AIN3):

      0x4012 0x03 ThrottleInput = 0x309003


    4. Configure the Throttle Module:

      Set the throttle voltage parameters as  explained in Section 4.2:

      0x4013 0x01 ZeroValue = 1
      0x4013 0x04 MaxInput = 4.5
      0x4013 0x05 MinInput = 1
      0x4013 0x06 NonValidMax = 4.8
      0x4013 0x07 NonValidMin = 0.2

      Set the maximum and minimum TorqueRef or VelocityRef:
      In this example we will use max = 10Nm and min = -5Nm

      0x4013 0x0B OutFullPositive = 10
      0x4013 0x0C OutFullNegative = -5

      Set the setting saying that negative throttle will be treated as brake

      0x4013 0x11 IsNegativeBrake = 1


    5. Save and Reset:

      Save the settings (Ctrl+S) and perform a reset.
      Switch to operational mode.
      Test
      Enable auto start if everything works correctly (inverter will always go to operational mode after reset).

      0x3000 0x03 AutoStart = 1

3.4. Brake module


The brake module implements the brake function within the throttle application, operating similarly to the throttle module but with a crucial difference: it always outputs torque in the opposite direction of the current rotation and supports only monodirectional braking. The brake can be assigned to a different analog input or share the same analog input as the throttle module, beneficial for configurations where monodirectional throttle is used. In such cases, one direction from the zero point can manage throttle control while the other handles brake control.

The brake module is used only when the inverter is operating in torque mode. In velocity mode, it is unnecessary because the velocity control system manages deceleration. For example, when operating at 3000 RPM, the velocity control system will handle the transition to 0 RPM. However, in torque mode, if you apply 10 Nm and then reduce the torque to 0 Nm, the motor will coast rather than braking in a controlled manner.

Table 7: Brake module parameters
Object
Name
Object
Index
Object
Subindex
Description Unit
Thr1_Brake__ZeroValue 0x4014 0x01 At this input value output is zero.  V
Thr1_Brake__ZeroDeadBand  0x4014 0x02 Defines how large zero dead band is V
Thr1_Brake__EnableDeadBand  0x4014 0x03 Defines how large enable dead band is. 
Should be lesser or equal to zero dead 
band.
V
Thr1_Brake__MaxInput  0x4014 0x04 At this input value output is at maximum 
value
V
Thr1_Brake__NonValidMax 0x4014 0x05 Any input value grater then this will put 
throttle module into error state.
V
Thr1_Brake__NonValidMin  0x4014 0x06 Any input value lesser than this will put 
throttle module into error state.
V
Thr1_Brake__Progressive  0x4014 0x07 At values > 1 brake will be progressive (it will slowly rise output value at beginning and then sharply on the end). For values < 1 it is exactly opposite - sharply increase at beginning and then slow increase at end. Mathematical at normed value (0-1) out = power(in, progressive) /
Thr1_Brake__RateLimit 0x4014 0x08 Limits how fast output can change. Output 
can change from 0 to 1 in 
1/Thr1_Brake__RateLimit s
1/s
Thr1_Brake__OutFull  0x4014 0x09 Output brake torque when input reaches 
max input.
/
Thr1_Brake__OutZero  0x4014 0x0A Output brake torque when input is at zero 
point.
/
Thr1_Brake__Full_RPM 0x4014 0x0B Between 0 RPM and the value set by this parameter, the braking torque is gradually increased from 0% to 100%. Once the speed exceeds this value, full braking torque is applied. This approach helps prevent excessive braking torque at 0 RPM, especially if the speed signal is noisy. /

Table 8: Brake module signals
Object
Name
Object
Index
Object
Subindex
Description Unit
Thr1_Brake__Input 0x4014 0x0C Input signal V
Thr1_Brake__OutNorm  0x4014 0x0D Normed brake output (-1 full negative, 1 full 
positive)
/
Thr1_Brake__Out 0x4014 0x0E Output in system units /
Thr1_Brake__Enabled  0x4014 0x0F If Brake is out of centre position for EnableDeadBand - power stage enabled Bit
Thr1_Brake__Err 0x4014 0x10 If brake input is outside NonValidMax and NonValidMin  

3.4.1 Input to Output Transformation

The input-to-output transformation operates similarly to the throttle module's input-output transformation. A typical example of this process is illustrated in the picture below.

The slope between the end of the zero-dead-band and Thr1_Brake__MaxInput is defined by the parameter Thr1_Brake__Progressive, similar to the throttle module. Additionally, you can configure the brake module to increase brake torque as the input decreases by setting Thr1_Brake__MaxInput to a value lower than Thr1_Brake__ZeroValue. This setup is especially useful for one-pedal driving, where the same potentiometer controls both braking and throttle. At lower input values, the system applies braking, while at higher values, it functions as a throttle. When operating in throttle mode, no braking is applied; when the pedal is completely released, the system enters braking mode. The resulting input-to-output transformation for this configuration is shown in the picture below

For both modes, it is essential to configure the parameters Thr1_Brake__NonValidMax and Thr1_Brake__NonValidMin. Failing to do so will result in incorrect operation of the short to ground and short to power supply error detection.

3.4.2 Examples

To use the brake module, you first need to configure the throttle module and ensure it works as expected.

- Brake example 1

Description:
Monodirectional throttle & brake using only one analog input.
In this example, a potentiometer is used to adjust voltage from 0V to 5V. The operational range is 0.2V to 4.8V. If the voltage goes outside this range, an error occurs.

Throttle and Brake Operation
Neutral Position: At 2.5V, the motor is in neutral.
Increasing Voltage: Raising the voltage above 2.5V speeds up the motor.
Decreasing Voltage: Lowering the voltage below 2.5V brakes the motor.

Before proceeding with this example, ensure you have completed and prepared a working setup of "Throttle Example 1" with torque control, not velocity. To achieve this, configure the following settings:

Objects to set:

  1. Enable Throttle Application:

    0x4010 0x01 - Thr1_Gen__Enable = 1
  2. Enable the Throttle & Brake Module:

    0x4011 0x01 Throttle = 1 0x4011 0x02 Brake = 1

  3. Map Objects to the Application:

    Torque control

    0x4012 0x06 TargetObj = 0x301004

    Check that torque mode is active if not set it with control mode = 0:
    0x3100 0x01 ControlMode = 0

    Define the analog throttle input (e.g., potentiometer on AIN3):

    0x4012 0x03 ThrottleInput = 0x309003

    Define the analog brake input (same as ThrottleInput in this example):

    0x4012 0x12 BrakeInput = 0x309003


  4. Configure the Throttle Module:

    Set the throttle voltage parameters as  explained in Section 4.2:

    0x4013 0x01 ZeroValue = 2.5
    0x4013 0x04 MaxInput = 4.5
    0x4013 0x05 MinInput = 0.5
    0x4013 0x06 NonValidMax = 4.8
    0x4013 0x07 NonValidMin = 0.2

    Set the maximum and minimum TorqueRef 
    In this example we will use max = 10Nm and min = 0Nm (if there is no load) - BIDIRECTIONAL throttle must be dissabled (one of these must be 0) for using only one analog input as throttle and brake.

    0x4013 0x0B OutFullPositive = 10
    0x4013 0x0C OutFullNegative = 0


  5. Configure the Brake Module:

    Set the brake voltage parameters:

    0x4014 0x01 ZeroValue = 2.5
    0x4014 0x04 MaxInput = 0.5
    0x4014 0x05 NonValidMax = 4.8
    0x4014 0x06 NonValidMin = 0.2 0x4014 0x09 OutFullPositive = 10


  6. Save and Reset:

    Save the settings (Ctrl+S) and perform a reset.
    Switch to operational mode.
    Test
    Enable auto start if everything works correctly (inverter will always go to operational mode after reset).

    0x3000 0x03 AutoStart = 1

- Brake example 2

Description:

In this example, we will use two analog inputs to control the motor:

This setup allows you to manage both the acceleration and deceleration of the motor independently using separate analog inputs.

Objects to set:

  1. Same as "Brake example 1"
  2. Same as "Brake example 1"
  3. Map Objects to the Application:

    Torque control

    0x4012 0x06 TargetObj = 0x301004

    Check that torque mode is active if not set it with control mode = 0:
    0x3100 0x01 ControlMode = 0

    Define the analog throttle input (e.g., potentiometer on AIN3):

    0x4012 0x03 ThrottleInput = 0x309003

    Define the analog brake input (e.g., potentiometer AIN2):

    0x4012 0x12 BrakeInput = 0x309002


  4. Configure the Throttle Module:

    Set the throttle voltage parameters as  explained in Section 4.2:

    0x4013 0x01 ZeroValue = 0.5
    0x4013 0x04 MaxInput = 4.5
    0x4013 0x05 MinInput = 0.5
    0x4013 0x06 NonValidMax = 4.8
    0x4013 0x07 NonValidMin = 0.2

    Set the maximum and minimum TorqueRef 
    In this example we will use max = 10Nm and min = 0Nm

    0x4013 0x05 OutFullPositive = 10
    0x4013 0x05 OutFullNegative = 0


  5. Configure the Brake Module:

    Set the brake voltage parameters:

    0x4014 0x01 ZeroValue = 0.5
    0x4014 0x04 MaxInput = 4.5
    0x4014 0x05 NonValidMax = 4.8
    0x4014 0x06 NonValidMin = 0.2 0x4014 0x09 OutFull = 10


  6. Save and Reset:

    Save the settings (Ctrl+S) and perform a reset.
    Switch to operational mode.
    Test
    Enable auto start if everything works correctly (inverter will always go to operational mode after reset).

    0x3000 0x03 AutoStart = 1


- Brake example 3

Description:
Monodirectional throttle & brake using one digital input (when connected full braking applied)
In this example, a potentiometer is used to adjust voltage from 0V to 5V. The operational range is 0.2V to 4.8V. If the voltage goes outside this range, an error occurs.

In this example, we will use one analog input and one digital input


Throttle and Brake Operation
Neutral Position: At 0.5V, the motor is in neutral .
Increasing Voltage: Raising the voltage above 0.5V speeds up the motor to max torque of 10Nm.
If brake input is activated throttle is disabled and full braking is applied.

Before proceeding with this example, ensure you have completed and prepared a working setup of "Throttle Example 1" with torque control, not velocity. To achieve this, configure the following settings:

Objects to set:

  1. Enable Throttle Application:

    0x4010 0x01 - Thr1_Gen__Enable = 1
  2. Enable the Throttle & Brake Module:

    0x4011 0x01 Throttle = 1 0x4011 0x02 Brake = 1

  3. Map Objects to the Application:

    Torque control

    0x4012 0x06 TargetObj = 0x301004

    Check that torque mode is active if not set it with control mode = 0:
    0x3100 0x01 ControlMode = 0

    Define the analog throttle input (e.g., potentiometer on AIN3):

    0x4012 0x03 ThrottleInput = 0x309003

    Define the analog brake input (e.g. DIN1 at object 0x30B0 0x01):

    0x4012 0x12 BrakeInput = 0x0x30B001


  4. Configure the Throttle Module:

    Set the throttle voltage parameters as  explained in Section 4.2:

    0x4013 0x01 ZeroValue = 0.5
    0x4013 0x04 MaxInput = 4.5
    0x4013 0x05 MinInput = 0.5
    0x4013 0x06 NonValidMax = 4.8
    0x4013 0x07 NonValidMin = 0.2

    Set the maximum and minimum TorqueRef 
    In this example we will use max = 10Nm and min = 0Nm.

    0x4013 0x0B OutFullPositive = 10
    0x4013 0x0C OutFullNegative = 0

    Dissable throttle when brake is applied

    0x4013 0x0E DisableAtBrake = 1

  5. Configure the Brake Module:

    Set the brake voltage parameters:
    We need to dissable the out of bounds funcitonalites as the digital input has only 2 values 0 and 1.
    0x4014 0x01 ZeroValue = 0
    0x4014 0x04 MaxInput = 1
    0x4014 0x05 NonValidMax = 4.8
    0x4014 0x06 NonValidMin = -2 0x4014 0x09 OutFull = 10


  6. Save and Reset:

    Save the settings (Ctrl+S) and perform a reset.
    Switch to operational mode.
    Test
    Enable auto start if everything works correctly (inverter will always go to operational mode after reset).

    0x3000 0x03 AutoStart = 1

3.5. Pump control module


The Pump Control Module manages the operation of two pumps in the system: the Pump1 = main pump and the Pump2 = cooling pump.

The Pump1 (main pump) is activated whenever

The Pump2 (cooling pump) is regulated by a simple hysteresis controller. There are two options for activating the Pump2 (cooling pump):
If both Thr1_Pump__2OnTemperature and Thr1_Pump__2OffTemperatureare set to -40 °C, the controller uses the values of Thr1_Pump__1OnTemperature and Thr1_Pump__1OffTemperature. Otherwise, it uses the directly configured values of  Thr1_Pump__2OnTemperatureand Thr1_Pump__2OffTemperature.

Following example if Thr1_Pump__2OnTemperature = -40 and Thr1_Pump__2OffTemperature = -40

Table 9: Pump control module parameters
Object
Name
Object
Index
Object
Subindex
Description Unit
Thr1_Pump__1OnTemperature 0x4015 0x01 At which temperature cooling pump1 will be enabled °C
Thr1_Pump__1OFFTemperature 0x4015 0x02 At which temperature cooling pump1 will be disabled.
Must be lower as Thr1_Pump__Pump1OnTemperature
°C
Thr1_Pump__2OnTemperature 0x4015 0x03 At which temperature cooling pump2 will be enabled.
If -40, Pump 1thresold will be used for pump 2.
°C
Thr1_Pump__2OFFTemperature 0x4015 0x04 At which temperature cooling pump2 will be disabled.
Must be lower as Thr1_Pump__Pump2OnTemperature.
If -40, Pump1 threshold will be used.
°C

Table 10: Pump control module signals
Object
Name
Object
Index
Object
Subindex
Description Unit
Thr1_Pump__Pump1TemperatureIn 0x4015 0x05 Main pump input temperature °C
Thr1_Pump__Pump2TemperatureIn 0x4015 0x06 Cooling pump input temperature °C

3.5.3 Examples

- Pump example 1

Description

Main pump on LS3 0x30A2 0x02 Value  
Cooling pump on LS4 0x30A3 0x02 Value

When PWMs are enabled, the main pump is always ON regardless of the input temperatures.

Both Pump1TemperatureIn  and Pump2TemperatureIn are used. When PWMs are disabled, if either value exceeds the setting in Thr1_Pump__1OnTemperature , both pumps turn ON. They turn OFF only when both values fall below Thr1_Pump__1OffTemperature. When PWMs are enabled main pump is ON and the logic for cooling pump behaviour is not changed.

Temperature at which we start the pumps = 60 °C
Temperature at which we stop the pumps = 50 °C

Objects to set:

  1. Enable Throttle Application:

    0x4010 0x01 - Thr1_Gen_Enable = 1
  2. Enable the Pump Module:

    0x4011 0x03 Pump = 1

  3. Map Objects to the Application:

    For this module the CoolingInputTemperature1 and CoolingInputTemperature2 are set by default. 
    Ctrl_Bridge__StatusHeatsinkTemp is mapped to CoolingInputTemperature1
    Ctrl_Gen_Stat__MotorSensorTemp is mapped to CoolingInputTemperature2

    0x4012 0x0D CoolingInputTemperature1 = 0x31CE00 
    0x4012 0x0E CoolingInputTemperature2 = 0x31010A

    Next we need to map the digital outputs where our pumps are connected to. In this example we will use:
    Main pump on LS3 0x30A2 0x02 Value  
    Cooling pump on LS4 0x30A3 0x02 Value

    0x4012 0x0B Pump1EnableDO = 0x30A202
    0x4012 0x0C Pump2EnableDO = 0x30A302



  4. Configure the Pump Module:

    Set the temperature at which the pump starts and stops.

    0x4015 0x01 Pump1OnTemperature = 60
    0x4015 0x02 Pump1OffTemperature = 50

  5. Save and Reset:

    Save the settings (Ctrl+S) and perform a reset.
    Switch to operational mode.
    Test
    Enable auto start if everything works correctly (inverter will always go to operational mode after reset).

    0x3000 0x03 AutoStart = 1

- Pump example 2

Description

Main pump on LS3 0x30A2 0x02 Value  
Cooling pump on LS4 0x30A3 0x02 Value

With this example we use separate temperature thresholds for each pump. the logic is the same as in example 1 the only difference is now that each pump has its own hysteresis when to turn on and off. 

When PWMs are enabled, the main pump is always ON regardless of the input temperatures.

Temperature at which we start the main pump = 60 °C
Temperature at which we stop the main pump = 50 °C

Temperature at which we start the cooling pump = 80 °C
Temperature at which we stop the cooling pump = 70 °C



Objects to set:

  1. Enable Throttle Application:

    0x4010 0x01 - Thr1_Gen_Enable = 1
  2. Enable the Pump Module:

    0x4011 0x03 Pump = 1

  3. Map Objects to the Application:

    For this module the CoolingInputTemperature1 and CoolingInputTemperature2 are set by default. 
    Ctrl_Bridge__StatusHeatsinkTemp is mapped to CoolingInputTemperature1
    Ctrl_Gen_Stat__MotorSensorTemp is mapped to CoolingInputTemperature2

    0x4012 0x0D CoolingInputTemperature1 = 0x31CE00 
    0x4012 0x0E CoolingInputTemperature2 = 0x31010A

    Next we need to map the digital outputs where our pumps are connected to. In this example we will use:
    Main pump on LS3 0x30A2 0x02 Value  
    Cooling pump on LS4 0x30A3 0x02 Value

    0x4012 0x0B Pump1EnableDO = 0x30A202
    0x4012 0x0C Pump2EnableDO = 0x30A302



  4. Configure the Pump Module:

    Set the temperature at which the pump starts and stops.

    0x4015 0x01 Pump1OnTemperature = 60
    0x4015 0x02 Pump1OffTemperature = 50
    0x4015 0x01 Pump1OnTemperature = 80
    0x4015 0x02 Pump1OffTemperature = 70

  1. Save and Reset:

    Save the settings (Ctrl+S) and perform a reset.
    Switch to operational mode.
    Test
    Enable auto start if everything works correctly (inverter will always go to operational mode after reset).

    0x3000 0x03 AutoStart = 1

3.6. DC-DC turn on delay module

DC-DC turn on delay module enables an external DC-DC module with a delay after start-up. The delay time 
can be configured by setting parameter Thr1_DCDC__StartupDelay

Object
Name
Object
Index
Object
Subindex
Description Unit
Thr1_DCDC__StartupDelay 0x4017 0x00 Delay time after which external DC-DC module is turned on s

3.6.1 Examples

- DC-DC delay example 1

The relay is connected to LS2 0x30A1 0x02 Value  
DC-DC turn on delay set to 5s.

Objects to set:

  1. Enable Throttle Application:

    0x4010 0x01 - Thr1_Gen_Enable = 1
  2. Enable the DC-DC Module:

    0x4011 0x05 DC_DC = 1

  3. Map Objects to the Application:

    DC-DC relay is on LS2 0x30A1 0x02 Value

    0x4012 0x0F DCDCenableDO = 0x30A102


  4. Configure the DC-DC Module:

    Set the delay to 5s

    0x4017 0x00 Thr1_DCDC__StartupDelay = 5

  5. Save and Reset:

    Save the settings (Ctrl+S) and perform a reset.
    Switch to operational mode.
    Test
    Enable auto start if everything works correctly (inverter will always go to operational mode after reset).

    0x3000 0x03 AutoStart = 1



3.7. SOC monitoring and charging detection module

The SOC (State of Charge) monitoring module and the charging detection module are implemented together due to their similarities. However, both modules can be enabled and disabled independently. SOC monitoring requires a Battery Management System (BMS) to detect SOC. Note that the BMS is not included in the emDrive. If SOC monitoring is needed, an independent BMS must be connected to the CAN bus.

Table 11: SOC monitoring module and charging detection module parameters
Object
Name
Object
Index
Object
Subindex
Description Unit
Thr1_Bat__StartLimitingSOC 0x4018 0x01 SOC percentage at which throttle limiting starts.  %
Thr1_Bat__EndLimitingSOC  0x4018 0x02 SOC percentage at which throttle limiting is at 
maximum
%
Thr1_Bat__EndLimitingThrottleOut  0x4018 0x03 Maximum torque output when throttle limiting 
is at maximum.
/
Thr1_Bat__ThrottleLimitRamp 0x4018 0x04 Limits how fast throttle limiting can change.  1/s

Table 12: SOC monitoring module and charging detection module signals
Object
Name
Object
Index
Object
Subindex
Description Unit
Thr1_Bat__Valid 0x4018  0x05 If 0, no BMS is available /
Thr1_Bat__SOC 0x4018  0x06 Battery SOC in percentage %
Thr1_Bat__LimitThrottle 0x4018  0x07 Current throttle limit in system units /
Thr1_Bat__LimitNorm  0x4018  0x08 Normed current throttle limit. (1 -> no limiting, 0 
-> maximum allowed limit)
/
Thr1_Bat__Charging 0x4018  0x09 If 1, charging is detected. If 0 charging is not 
detected.
Bit

3.7.1 SOC Monitoring Module

The SOC monitoring module tracks the SOC state as reported by the BMS.

3.7.2 Charging detection Module

The charging detection module monitors whether the battery is being charged and reports this status to the throttle module.

This setup allows for accurate monitoring and management of the battery's state of charge and charging status, ensuring optimal performance and safety.

 

3.7.4 Examples

- SOC example 1

Description:
In this example we first need a working example of Throttle module to control torque. (It is also possible to control velocity.)
Next we need the following values:

To achieve this we can use the PDOs to get the values - in this example we will assume that we get the upper values via CAN and we set the values to our 0x2051 Customer_Data_16bit__Value. How to configure the PDOs refer to the user manual section 9.4-Data transfer – Process Data Object (PDO).

Battery (BMS) valid/present needs to be higher than 0, otherwise inverter thinks there is no BMS.


0x2051 0x01 Customer_Data_16bit__Value1 = Battery (BMS) valid/present (needs to be higher than 0)
0x2051 0x02 Customer_Data_16bit__Value2 = SOC in percent


We will start limiting the output when the battery has SOC = 10%
We will limit the output with a ramp of value 0.01 until we reach SOC = 5%, after which we stay at value 10Nm (in case of velocity control the unit of this value will be in PRM - 10RPM).

Objects to set:

    1. Enable the SOC Module:

      Use the throttle general object 0x4011 Thr1_Enable to enable the proper module.

      0x4011 0x06 SOC = 1
    2. Map Objects to the Application:

      As mentioned above we have the required date on the Costomer_Data_16bit_Values.

      Customer_Data_16bit__Value1 = 0x2051 0x01
      Customer_Data_16bit__Value2 = 0x2051 0x02

      We set the following:

      0x4012 0x07 BatValid = 0x205101
      0x4012 0x08 BatSOC = 0x205102



  1. Configure the SOC Module:

    Set at what SOC percent we start limiting the output (at 10 %):

    0x4018 0x01 StartLimitingSOC = 10

    Set at what SOC percent we stop limiting the output (at 5 %):

    0x4018 0x02 EndLimitingSOC = 5

    Set what will the max value output be at the value EndLimitingSOC   (10Nm): 

    0x4018 0x03 EndLimitingThrottleOut = 10

    Set the ramp parameter for slope:

    0x4018 0x04 ThrottleLimitRamp = 0.01


  2. Save and Reset:

    Save the settings (Ctrl+S) and perform a reset.
    Switch to operational mode.
    Test
    Enable auto start if everything works correctly (inverter will always go to operational mode after reset).

    0x3000 0x03 AutoStart = 1


- Charging detection example 2

Description:
You need a working throttle example.
As mentioned above we can detect charging with the state that is reported by BMS for that we need to get the state of the BMS in this example we will use method as in SOC example 1, where use the PDOs and save the state to Costomer_Data_16bit_Value.


Customer_Data_16bit__Value1 = 0x2051 0x01 = Battery (BMS) Valid/present (needs to be higher than 0)
Customer_Data_16bit__Value3 = 0x2051 0x03 = State of BMS

Objects to set:

    1. Enable the SOCModule and ChargingDetect Module:

      Use the throttle general object 0x4011 Thr1_Enable to enable the proper module.

      0x4011 0x06 SOC = 1
      0x4011 0x07 ChargingDetect = 1
    2. Map Objects to the Application:

      As mentioned above we have the required date on the Costomer_Data_16bit_Values.

      Customer_Data_16bit__Value1 = 0x2051 0x01
      Customer_Data_16bit__Value3 = 0x2051 0x03

      We set the following:

      0x4012 0x07 BatValid = 0x205101
      0x4012 0x09 BatState = 0x205103


  1. Configure the ChargingDetect Module:

    With this module there is no other configuration needed. If the value on Customer_Data_16bit__Value3 == (charge = 4) or (charge_done = 5) -> driving is disabled. 


  2. Save and Reset:

    Save the settings (Ctrl+S) and perform a reset.
    Switch to operational mode.
    Test
    Enable auto start if everything works correctly (inverter will always go to operational mode after reset).

    0x3000 0x03 AutoStart = 1

- Charging detection example 3

Description:
You need a working throttle example.
The next method is to detect charging with a digital input, for that we will use digital input 4.

0x30B0 0x04 DIN4


Objects to set:

    1. Enable the SOCModule and ChargingDetect Module:

      Use the throttle general object 0x4011 Thr1_Enable to enable the proper module.

      0x4011 0x06 SOC = 1
      0x4011 0x07 ChargingDetect = 1
    2. Map Objects to the Application:

      As mentioned above we have the required date on the Costomer_Data_16bit_Values.

      Customer_Data_16bit__Value1 = 0x2051 0x01
      DIN4 = 0x30B0 0x04

      We set the following:

      0x4012 0x07 BatValid = 0x205101
      0x4012 0x0A ChargingDetectDlvalue = 0x30B004


  1. Configure the ChargingDetect Module:

    With this module there is no other configuration needed. If the value of DIN4 == 1 -> driving is disabled. 


  2. Save and Reset:

    Save the settings (Ctrl+S) and perform a reset.
    Switch to operational mode.
    Test
    Enable auto start if everything works correctly (inverter will always go to operational mode after reset).

    0x3000 0x03 AutoStart = 1