How to communicate with L510 VFD using Max485 module

Disclosure: This post contains affiliate links. Meaning that if you purchase the products using these links Ezbuilds will be compensated at no extra cost to you. The use of these links will help support the site so i can keep providing quality content for you!

Introduction

For my plastic shredder I purchased a Westinghouse L510 VFD to control the motor.  I decided to go with the VFD for the following reasons:

  1. This allowed me to buy a three phase motor, because a VFD can convert single phase power to three phase power. Benefits of a three phase motor over a single phase is they are simpler as they require no starting or running capacitors. Also they are smaller for the same rating of HP.
  2. Ability to easily control speed of the motor, which also allows for easy switching between forward and reverse. Which would be helpful in unjamming the shredder with the motor, and not my hands!
  3. Soft starting of the motor which eliminates inrush current and can prolong the life of the motor

Initially I was going to control the VFD by wiring up all the buttons to the terminals themselves but luckily this VFD comes with an embedded RS-485 MODBUS interface port! With this, all i would need is a pair of wires and i could issue any command BUT I also can read a bunch of VFD data and parameters in which i can use to make decisions on how to control the motor, for example could measure the current and if it goes HIGH then REVERSE motor.

If you have never heard these terms before you can think of RS-485 as the medium the communication goes over, for example if i were to communicate with you RS-485 would be the tool or interface i use to talk to you, wether that be verbal talking, email, or texting.  Modbus would be the language I speak to you in, for example english.

There are a lot of resources on the internet on theses things so i encourage you to search it up if you want more information or to learn it in depth.  One good website i found for modbus is https://www.simplymodbus.ca/ and provides a very straightforward explanation to get you going. I will talk a bit further in the post on how the messaging works where it will make some more sense when we use it.

Required parts

ImageDescriptionWhere to Buy
L510 Westinghouse VFD – needs to be wired up
(not covered in this post)
eMotors Direct
MAX485 moduleAmazon Canada
Atmega development boardAmazon Canada

Optional parts

ImageDescriptionWhere to Buy
USB to RS485 Converter (can be use with modbus simulator/monitor software on your computer)Amazon Canada

Explanation on hardware used:

ATMEGA 2560 Development board – microcontroller used for holding your sketch lots of different versions most notable is the Arduino but there are cheaper versions around that are essentially the same in this tutorial i am using a keystudio version. I chose to use the mega over the smaller boards as it has multiple serial ports instead of having to share the serial port on a UNO or similar board for the USB communication and the RS-485.

Max485 Module – A max485 IC is a special purpose chip that can convert UART to RS-485.  There are lots of details on many other websites about this module so i won’t go into exhaustive detail.  But below you can see the schematic of the IC boa rd that is typically sold everywhere.  You can get these things for pretty cheap. I got mine from amazon but you can get some cheaper versions on aliexpress they will just take longer to get. This device is half duplex meaning it can only send or receive at a time. The moe can be chosen by setting the DE & RE pins.

Library used:

Mod busMaster written by Doc walker. – https://github.com/4-20ma/ModbusMaster

I chose this library because it works well with the MAX485 module.  As above how the DE and RE pins are set will determine if you are sending or receiving.  If these are not set correctly you will be unable to send requests or read the replies from the device you are talking to, in this case the VFD.

Another reason i chose it, is its very lightweight compared to other libraries.  I originally tried to use the Arduino library intended for their module, and it was very cumbersome and when loading it into the UNO it would cause it to almost use all the memory not leaving much for any other code to be made.  Even though i moved to a MEGA that has more space i still am not a fan of needlessly utilizing memory if i don’t have too.

Instructions

Hardware

Instructions:

Wire up the MAX485 module to the Arduino board, using the below schematic, and pin schedule as reference:

Connection schedule for Arduino to MAX-485 module:

Arduino PinArduino Pin DescriptionMAX485 PinMAX485 Pin Description
18Serial Port 1 TX PinDIDriver Input
19Serial Port 1 RX PinROReceiver Output
2GPIO PinREReceiver Enable
3GPIO PinDEDriver Enable

Serial connections:

You can use any serial port connections required, in this example we will use Serial1 which is pins 18(Tx) and 19(Rx).

Useful link: https://www.arduino.cc/reference/en/language/functions/communication/serial/ 

GPIO pins:

These pins are very flexible as well, in this example we are using 2 & 3 but in reality you can use any GPIO pin, the code will just have to reflect it

Power:

5V and common can be grabbed from anywhere on the Arduino board

Connection of MAX-485 module to the L510 VFD:

The L510 VFD has a RJ-45 connection to its serial port so the simplest thing to do is get a ethernet cable and strip one end to do your connections

A word on grounding:

  • The L510 manual does not show there being a GND pin on there RS-485 connection, but it is good practice to ensure all the devices on the network have a common ground for reliability and to prevent any unintended equipment damage.  In this example it is not to big of a deal as the equipment is very close and can be coupled together ground wise very easily, but in some instances where your polling device(MAX485 module) is far away from the equipment (VFD) it’s a good idea to include an additional ground wire.

Software

Prerequisites

Computer running Arduino IDE, goto arduino.cc and follow the instructions there do get the software running

Steps

First you need to download the ModbusMaster library we will be using the arduino IDE:

  1. On the Arduino IDE goto Tools -> Manage Libraries…. 
  2. Search for “modbusmaster” and install the one by DocWalker

Writing the code:

Below is a sketch we will use I have added comments in the sketch to make it as straightforward as possibly after the sketch i will go over some details on how certain things are setup. You can copy the program and load it in your controller and just start running it.

// Title: L510 Modbus RS-485 Program
// Author: EZBuilds
// Description: Arduino program to read serial data from a TECO Westinghouse
//              L510 VFD.  Provides interface to VFD to start/stop, change Direction
//              and manipulate the speed.
// License: This work is licensed under the Creative Commons -
//          Attribution-ShareAlike 4.0 International (CC BY-SA 4.0)
//          https://creativecommons.org/licenses/by-sa/4.0/

// Include the following librarires
#include <ModbusMaster.h>

// * Hardware configuration
// Digital Outputs
const int REpin = 2; // Connect to RE pin of Max485 module
const int DEpin = 3; // Connect to DE pin of Max485 module

// VFD Serial Commands
  /* Pre-loaded 16 bit integers with the specific bit for the command set
    To turn ON bits a bitwise OR function is used and a variable containing
    the specific bit set to one and all others 0
    To turn OFF bits a bitwise AND function is used and a variable containing the
    specific bit to turn off set to 0 and all others 1 */

uint16_t Start = 0x1;  // Must be bitwise OR to start
uint16_t Stop = 0xFFFE;
uint16_t Forward = 0xFFFD;
uint16_t Reverse = 0x2;
uint16_t Efo = 0x4;
uint16_t FaultReset = 0x8;
uint16_t Freq_cmd = 0;

// Used as a comparison to current time in mS
unsigned long timepoint = 0;
// How often the serial read code should activate in mS
unsigned long readDelay = 1000;

// VFD Variables
bool run_stat = 0; // 0 = stopped, 1 = running
bool direction = 0; // 0 = forward, 1 = reverse
bool ready = 0; // 0 = not ready, 1 = ready
bool fault = 0; // 1 = fault
bool dataSetErr = 0;
uint16_t fault_code = 0;
float cmd_freq = 0;
float out_freq = 0;
float last_frequency = 0;
float out_volt = 0;
float cmd_volt = 0;
float current = 0;
// Initial Value of 226 (failed read) to prevent a any code from running before
// a succesful read
uint8_t result = 226; // Serial Read Result

// VFD Control bits
bool stop_cmd = 0;
bool start_cmd = 0;
bool dir_cmd = 0; // 0 = forward, 1 = reverse

// VFD Speed Control Variables
int speed_cmd = 0;
int last_speed_cmd = 0;
int speed_dband = 5;
int speed_cmd_dif = 0;

// Modbus Paramaters
int max_retries = 3;  //max read retries to device bfore declaring failed
int retries = 0;          //number of current retries

// instantiate ModbusMaster object
ModbusMaster VFD;

void setup(){
  //HW configuration
  //Outputs
  pinMode(REpin, OUTPUT); // Sets the RE pin to a output
  pinMode(DEpin, OUTPUT); // Sets the DE pin to a output
  // Initialize Outputs
  postTransmission();
  // Serial port configuration
  // Serial will be used for the USB port
  Serial.begin(9600);
  // Serial1 will be used for modbus on Max485
  Serial1.begin(19200);
  VFD.begin(1, Serial1);
  // Callbacks allow us to configure the RS485 transceiver correctly
  VFD.preTransmission(preTransmission);
  VFD.postTransmission(postTransmission);
  // This function will run while waiting for modbus response
  VFD.idle(readSerialBuff);

  //Write help menu to serial monitor upon Startup
  help_menu();
}

void loop(){

readSerialBuff();

// Read data from VFD
if (millis() - timepoint >= readDelay) {
  timepoint = millis();
  // Perform read of VFD
  result = VFD.readHoldingRegisters(0x2520, 9);
  // If read is succesful
  if (result == VFD.ku8MBSuccess)
    {
      Serial.println("Succesful Read");
      // Set retries to 0 if communication suceeds
      retries = 0;
      // Dissect the bits from the status word
      run_stat = bitRead(VFD.getResponseBuffer(0x00),0);
      direction = bitRead(VFD.getResponseBuffer(0x00),1);
      ready = bitRead(VFD.getResponseBuffer(0x00),2);
      fault = bitRead(VFD.getResponseBuffer(0x00),3);
      dataSetErr = bitRead(VFD.getResponseBuffer(0x00),4);
      // Read fault code number if != 0 there is a fault
      fault_code = VFD.getResponseBuffer(0x01);
      cmd_freq = VFD.getResponseBuffer(0x03)/100.0;//Div by 100 to get correct scaling
      out_freq = VFD.getResponseBuffer(0x04)/100.0;//Div  by 100 to get correct scaling
      out_volt = VFD.getResponseBuffer(0x05)/10.0; //Divby 10 to get correct scaling
      cmd_volt = VFD.getResponseBuffer(0x06);
      current = VFD.getResponseBuffer(0x07)/10.0; // Div by 10 to get correct scaling

      VFD.clearResponseBuffer();

      // Print status messages to Serial Monitor
      
        VFD_status();
      }

      // If communication has failed
      else
      {
        Serial.print("VFD Comm failed, with code: ");
        Serial.println(result);
      }
    }
// This performs our writes based on the commands received
// Only perform write actions if communication has been established with VFD

if (result == VFD.ku8MBSuccess)
{
  // Send a STOP
  if (stop_cmd == 1)
  {
    if(result = VFD_command("Stop", Stop)) stop_cmd = 0;
  }
  // Send a START if VFD is NOT RUNNING
  if (start_cmd == 1 && run_stat == 0)
  {
    VFD_command("Start", Start);
    start_cmd = 0;
  }
  // Send a FORWARD command if running REVERSE
  if (dir_cmd == 1 && direction == 1)
  {
    VFD_command("Forward", Forward);
   }
  // Send a REVERSE command if running FORWARD
  if (dir_cmd == 0 && direction == 0)
  {
    VFD_command("Reverse", Reverse);
  }
  // Find absolute value to determine if past deaband, this allows for minor
  // changes in the speed input if using a potentiometer

  speed_cmd_dif = speed_cmd - last_speed_cmd;
  speed_cmd_dif = abs(speed_cmd_dif);
  if (speed_cmd_dif >= speed_dband)
      {
        result = VFD.writeSingleRegister(0x2502, speed_cmd);
        if(result == VFD.ku8MBSuccess)
          {
            last_speed_cmd = speed_cmd;
            Serial.print("Speed command write succesful: ");
            Serial.println(speed_cmd);
          }
          else
          {
            Serial.print("Speed command failed: ");
            Serial.println(result);
          }
       }
  }

}


// Functions

void preTransmission()
  {
    digitalWrite(REpin, 1);
    digitalWrite(DEpin, 1);
  }

void postTransmission()
  {
    digitalWrite(REpin, 0);
    digitalWrite(DEpin, 0);
  }

int VFD_command (String command_string, uint16_t command) {
  /* This will write to the command register of the L510 VFD over serial
     using the bit mask provided as "Command", and doing a modbus function
     0x16 Mask Write Register

     The IF statement determines if this going to be a OR mask (turn a bit to 1)
     or a AND mask (turn a bit to 0), and build the correct command based on that
     */
  uint8_t result;
  uint16_t read_value;
  // Read the holding register we will write too
  result = VFD.readHoldingRegisters(0x2501, 1);
  if (result == VFD.ku8MBSuccess) {
    // Save that holding register as a variable
    read_value = VFD.getResponseBuffer(0x00);
    VFD.clearResponseBuffer();
      // If command value is equal to or less then 128 perform OR operator
      if (command <= 128) {
        command = read_value | command;
        Serial.println("OR Command: ");
        Serial.println(read_value);
        Serial.println(command);
      }
      // Else perform AND operator
      else {
      command = read_value & command;
      Serial.println("AND Command: ");
      Serial.println(read_value);
      Serial.println(command);
    }
    // Write the masked word to the same register
    result = VFD.writeSingleRegister(0x2501, command);

      // Determine if write suceeded or failed
      if (result == VFD.ku8MBSuccess) {
        Serial.println(command_string+" Command Write Success");
        return result;
        }
      }
      // Output Failure notification and information for Write
        Serial.print(command_string+"Write Failed: ");
        Serial.println(result);
        return result;
    }

void readSerialBuff()
  {
  // Check to see of there is anything in serial buffer
  if (Serial.available() > 0)
  {
    String buffer = Serial.readString();
    //If Run command Received
    if (buffer == "start")
    {
      start_cmd = 1;
      Serial.println("Run command received");
    }
    //If stop command received
    else if (buffer == "stop")
    {
      stop_cmd = 1;
      Serial.println("Stop command received");
    }
    //If forward command received
    else if (buffer == "fwd")
    {
      dir_cmd = 1;
      Serial.println("Reverse command received");
    }
    //If forward command received
    else if (buffer == "rev")
    {
      dir_cmd = 0;
      Serial.println("Reverse command received");
    }
    //If Frequency command received
    else if (buffer[0] == 'f')
    {
      Serial.println("Frequency command received");

      if (int freq = atoi(&buffer[1])) // atoi converts string to integer
      {
        if (freq <= 60 && freq >= 0)
        {
          Serial.print("Frequency CMD valid: ");
          Serial.println(freq);
          //Convert from 0 - 60 to 0 - 6000
          speed_cmd = freq * 100;
        }
      }
      else
      {
        Serial.println("Invalid frequency set point");
      }
    }
    else if(buffer == "status")
    {
      VFD_status();
    }
    else if(buffer == "help")
    {
      help_menu();
    }
    else
      {
        Serial.println("Invalid command, for availble commands type help");
      }
  }
}

void help_menu()
{
  Serial.println("Availble commands:");
  Serial.println("start - sends run command to VFD");
  Serial.println("stop - sends stop command to VFD");
  Serial.println("fwd - sends a forward command");
  Serial.println("rev - sends a reverse command");
  Serial.println("fXX - set frequency command XX can be any number");
  Serial.println("beteen 0 and 60 Hz in increments of 1 Hz");
}

void VFD_status()
{
  if (result == VFD.ku8MBSuccess)
  {
    if (run_stat) Serial.println("Running");
    else Serial.println("Stopped");

    if (direction) Serial.println("Forward");
    else Serial.println("Reverse");

    if (ready) Serial.println("Ready");

    if (fault) Serial.println("Faulted");

    Serial.println("Fault code: " + fault_code);
    Serial.print("Command frequency: ");
    Serial.println(cmd_freq);
    Serial.print("Output frequency: ");
    Serial.println(out_freq);
    Serial.print("Output Voltage: ");
    Serial.println(out_volt);
    Serial.print("Command Voltage: ");
    Serial.println(cmd_volt);
    Serial.print("Current: ");
    Serial.println(current);
  }
  else Serial.println("NO COMMUNICATION WITH VFD");
}

First, we include the ModbusMaster library, this allows us to use the Modbus functions, we include the header file which declares all of its functions, the actual function code is written in the .cpp file

#include <ModbusMaster.h>

Then we define the RE and DE pin on the board, after these lines we can now use “REpin” and “DEpin” instead of hardcoding the pin number each time, the other benefit is if you want to change the pin in the future for whatever reason for example the pin is damaged or you need to use it for something else you can just change the number here in the code and don’t have to change at every instance it is used.

const int REpin = 2; // Connect to RE pin of Max485 module
const int DEpin = 3; // Connect to DE pin of Max485 module

On line 25 to 31 we define some variables of type uint_16t which are used as bitmasks for when we send commands to the VFD we will touch more on this later in the article.

uint16_t Start = 0x1;  // Must be bitwise OR to start
uint16_t Stop = 0xFFFE;
uint16_t Forward = 0xFFFD;
uint16_t Reverse = 0x2;
uint16_t Efo = 0x4;
uint16_t FaultReset = 0x8;
uint16_t Freq_cmd = 0;

After that we define variables used in the program, all variables decalred in this area are called global variables meaning they can be used anywhere in the program and will always be in memory.

Create a instance of the ModbusMaster class called VFD:

ModbusMaster VFD;

Now we are at our setup function, this function runs once and only once whenever the board starts up.

We declare our REpin and DEpin as outputs then we initialize the outputs utilizing our postTransmission function.

pinMode(REpin, OUTPUT); // Sets the RE pin to a output
pinMode(DEpin, OUTPUT); // Sets the DE pin to a output
postTransmission();

Next we initialize our serial ports, Serial will be used for our USB interface, and Serial1 will be used by the ModbusMaster library.

Serial.begin(baud_rate of serial monitor);
Serial1.begin(baud_rate of VFD node on RS485 bus);

Important: The baud for Serial1 needs to be consistent to all devices used on the RS485 bus as we will only be talking to the single VFD in this example this has to be the same as the VFD setting.

We then run the ModbusMaster library begin function which takes the node address (which is the node configured in the VFD), and the serial port being used as arguments:

VFD.begin(node_address of VFD, Serial1);

Finally we pass our predefined preTransmisstion and postTransmission functions into the ModbusMaster preTransmission and postTransmission functions. When passing a function onto another function this is referred to as a callback. These functions are defined at the end of the sketch.

VFD.preTransmission(preTransmission);
VFD.postTransmission(postTransmission);

OK now that we got all the initialization and setup code complete we can get into our main function. This code will be constantly looped through after the setup function and till the board is powered down. BUT first I just want to talk about the information we need about the VFD in order to write this function which is critical in us accomplishing our programs goal. Below is a table from the L510 Communication addendum that outlines information on talking to the VFD, it’s called a mapping table which shows which register(address) is tied to what information, so we can read that address and know what the value refers to.

Information taken from L510 Comm Addendum

On the left side of the table we see the address in which the data is stored in this case that number is represented in HEX which is very important because it is different than DECIMAL and numbers won’t match up if you treat them as a decimal as you will get incorrect data or failed reads due to reading an illegal address.

I have highlighted the first 8 rows as this will be the registers we will read on our program. So now with this table we can determine our starting address we want our data from which is 2520(Hexadecimal), and how many registers.

We need to find this information in order to find at what address when we want to start our read at, then we need to see how many addresses we want to read, the other thing we need to figure out is what address type we read, in the modbus standard there is many different function codes and that’s how the request is built on. For this example we will be using two:

Modbus Function Code 3 – Read holding registers, which are data held in the 4x range of memory of the device we are reading. This Modbus function is called using the function readHoldingRegisters()

Modbus Function Code 6 – Write a WORD to register, which is writing to a single 4x register. This Modbus function is called using the function writeSingleRegister()

Below you can see another table for the L510 and this is the write area for the VFD, and is where we will be directing our commands to using the writeSingleRegister() function:

Information taken from L510 Comm Addendum

We start of the loop function with a call to readSerialBuff(); this is a function ive added to this program to provide control functionality from the serial monitor through the Arduino, with this you will be able to Stop, Start, change direction & speed of the VFD easily. When you start to fit your program to your own purpose this can be substituted with any other interface like push buttons. This function is defined at the end of the sketch.

Next we have a conditional function in order to delay how often we read data from the VFD:

if (millis() - timepoint >= readDelay) {
  timepoint = millis();
}

How this function works is it calls built in Arduino function which returns a millisecond value that has passed since the program has started, and will subtract that value from the timepoint variable which will update whenever the conditions are met, as we initialized the value to 0 once the millis() function returns 1000 (1 second) or more it will run the function, and subsequently update timepoint so it can be compared again and will cause this function to run again in a second. This timepoint variable can be adjusted to your liking depending on your polling requirements.

Once our conditions are met we make a call to the Modbusmaster library function:

result = VFD.readHoldingRegisters(0x2520, 8);

The arguments for this function are the starting address we want to read from which is 0x2520 the 0x just means its a hexadecimal number, which we obtained from the table above, we then add the number of registers we want to read including that address. We then assign the result of the function to the variable result, this variable is then used to determine if the operation was successful or not.

If the read is successful(by comparing result to ku8MBSuccess which is a constant in the ModbusMaster library, it happens to be 0) we then set all the data read from the VFD to our Global Variables

We use the bitRead function to dissect the bits from the status word, this makes it simpler to run logic on them and the code more readable.

For the analog value we apply our scaling factors which were obtained from the modbus mapping table and also assign them to our own variables.

After that we call clearResponseBuffer, which sets the buffer back to zero.

If we fail(readHoldingRegisters returns a exception code) we compare the current retires to the limit and if that value is reached we determine the device is not talking.

If the max retries hasn’t been reached we then increment the retries variable and continue on.

OK! So now we have got our reading out of the way but, it’s pretty boring if your just reading data alone and pointless if the thing isn’t running and moving. So the next lines of code send the commands to the control register and frequency input register of the VFD with these we can start, stop, forward, reverse, and change speed.

// This performs our writes based of the commands received
if (result == VFD.ku8MBSuccess)
{
  if (stop_cmd == 1)
  {
    if(result = VFD_command("Stop", Stop)) stop_cmd = 0;
  }

  if (start_cmd == 1 && run_stat == 0)
  {
    VFD_command("Start", Start);
    start_cmd = 0;
  }

  if (dir_cmd == 1 && direction == 1)
  {
    VFD_command("Forward", Forward);
   }

  if (dir_cmd == 0 && direction == 0)
  {
    VFD_command("Reverse", Reverse);
  }

  if (speed_cmd != cmd_freq)
      {
        if(result = VFD.writeSingleRegister(0x2502, speed_cmd))
          {
            Serial.print("Speed command write succesful: ");
            Serial.println(speed_cmd);
          }
          else
          {
            Serial.print("Speed command failed: ");
            Serial.println(result);
          }
       }
  }

I wont talk to much about this code, but all it is, is a set of conditionals looking for a particular value of the control bits, if the condition is met it then for the instance of a discrete command we call the function VFD_command:

int VFD_command (String command_string, uint16_t command) {
  /* This will write to the command register of the L510 VFD over serial
     using the bit mask provided as "Command", and doing a modbus function
     0x16 Mask Write Register

     The IF statement determines if this going to be a OR mask (turn a bit to 1)
     or a AND mask (turn a bit to 0), and build the correct command based on that
     */
  uint8_t result;
  uint16_t read_value;
  // Read the holding register we will write to
  result = VFD.readHoldingRegisters(0x2501, 1);
  if (result == VFD.ku8MBSuccess) {
    // Save that holding register as a variable
    read_value = VFD.getResponseBuffer(0x00);
    VFD.clearResponseBuffer();
      // If command value is equal to or less then 128 perform OR operator
      if (command <= 128) {
        command = read_value | command;
      }
      // Else perform AND operator
      else {
      command = read_value & command;
    }
    // Write the masked word to the same register
    result = VFD.writeSingleRegister(0x2501, command);

      // Determine if write suceeded or failed
      if (result == VFD.ku8MBSuccess) {
        Serial.println(command_string+" Command Write Success");
        return result;
        }
      }
      // Output Failure notification and information for Write
        Serial.println(command_string+"Write Failed: "+ result);
        return result;
    }

For this function we read the current command word and apply a bit mask so we only turn on or off the particular bit we want, and leave the rest of the bits alone.

For these commands if we want to turn a bit “On” we would use the OR operator and put that particular bit to “1” on our bitmask and leave all the other to “0”, and if we want turn a bit “Off” we would use the AND operator and put that particular bit to “0” on our bitmask and place all the other to “1”. To make this concrete you can look at this example below where the VFD is currently running and we would like to reverse it:

Bitwise OR operation

When we apply this bit mask we have the reverse command bit set to 1 as we want to turn that bit ON and since we are applying a bitwise OR operation if either value between the two words is 1 it will set the resulting word to 1. So now we can command the VFD to goto reverse without also stopping the VFD or if there were any other conditions TRUE they would be left alone. Below is another example for the reverse operation if we want TURN OFF A BIT, for that we will use a bitwise AND operation and set the bit we want to TURN OFF to 0 in our bitmask:

Bitwise AND operation

For all our commands we can build the following bitmasks:

CommandBit representationHex ValueDecimal value
Start0000 0000 0000 000111
Reverse0000 0000 0000 001022
Jog Forward0000 0000 0001 00001016
Jog Reverse0000 0000 0010 00002032
CMD S1 “On”0000 0000 0100 00004064
CMD S2 “On”0000 0000 1000 000080128
CMD S3 “On”0000 0001 0000 0000100256
CMD S4 “On”0000 0010 0000 0000200512
Relay R1 “On”0000 1000 0000 00008002048
Relay R1 “Off”1111 0111 1111 1111F7FF63487
CMD S4 “Off”1111 1101 1111 1111FDFF65023
CMD S3 “Off”1111 1110 1111 1111FEFF65279
CMD S2 “Off”1111 1111 0111 1111FF7F65407
CMD S1 “Off”1111 1111 1011 1111FFBF65471
Forward1111 1111 1111 1101FFFD65533
Stop1111 1111 1111 1110FFFE65534
Generated from information on L510 Communication addendum

Next write the preTransmission and postTransmission functions, these will be used to set our RE and DE pins for our MAX485 module. in these functions its telling the controller at what state to write the output pins to, the first argument is the Pin # and the second is what state 1 being HIGH, and 0 being LOW:

void preTransmission()
  {
    digitalWrite(REpin, 1);
    digitalWrite(DEpin, 1);
  }

  void postTransmission()
  {
    digitalWrite(REpin, 0);
    digitalWrite(DEpin, 0);
  }

Running the program

If everything is working you will see the following in your serial monitor when booting up:

If the VFD is not communicating you will see this:

Goto the troubleshooting section if the VFD is not talking.

Now utilizing the serial input you can enter commands to control the VFD:

For example i’ll send a start command, then the VFD will start up, then i can set a f30 then the VFD will goto 50% speed:

If i want to see the current values of the VFD i can send a status command and it will pop up in the serial monitor:

To stop it all i have to do is send a stop:

Troubleshooting

Ok so you got your wiring done you have everything powered up, and your ready to start controlling! But wait its not working???? This section is here to help you with that serial communications can be tricky and everything has to be just right in order for it to work so here i will outline steps to go through to get it working.

  1. First verify what exception code you are receiving when the read fails, if this doesn’t lead you to the culprit do the following below:
  • Verify settings of the VFD and Arduino sketch using the table below:
VFD SettingArduino Sketch
Param 09-00 Node Address(we use 1 in this example)VFD.begin(node_address, SerialPort);
Param 09-01 value needs to be 0(RTU)n/a – modbusmaster library is RTU
Param 09-02 value needs to be same as speed in the right column
0 = 4600
1 = 9600
2 = 19200 (this is default)
3 = 38400
Serial1.begin(speed);
VFD.begin(node_address, Serial1);
Param 09-03 this should be 0 n/a
Param 09-04 this should be 0 n/a
Param 09-05 this should be 0 n/a
  • Check your wiring, A & B terminals need have same polarity as VFD, swap the wires and see if anything works

Going from here:

Now you have a setup to be able to read and control your VFD over just TWO WIRES!

In the current setup it’s not really practical to be controlling it with the serial monitor all the time but is the easiest way to get up and running with minimal wiring and parts and get a feel on how it will work.

In my setup i have built a control box that your able to control it with push buttons and a potentiometer for the speed:

If you would like me to write about this control box let me know in the comments and i will come up with a write up for that! As well if anything is unclear or you feel is not explained thoroughly let me know!

Leave a Comment

Your email address will not be published. Required fields are marked *

Shopping Cart