A long time ago I brought a variable frequency drive which I want to use for driving a spindle motor for a milling machine. The VFD is a Huanyang Inverter HY01D523B (1.5 kW, 7A, 220V 50Hz). I want to be able to control it through the MODBUS interface to be able to change speed and direction from g-code, and therefore I made the test setup suggested here.

The MAX485 board is connected to the Arduino ports 8, 9, 10 and 11, and to 5V and ground. The screw terminal is connected to the VFD: RS+ to A and RS- to B.

The inverter is wired as described in the manual. But how to use the MODBUS interface is not described in detail, so I had to search the internet and found these sources, which are helpful: modbustools, industryarena Bouni and LacobusVentura. The settings I use in the VFD is:
- PD001: 2 (Source of run commands: communication port)
- PD002: 2 (Source of operating frequency: communication port)
- PD163: 1 (Communication address: 1)
- PD164: 1 (Communication Baud Rate: 9600)
- PD165: 3 (Communication Data Method: 8N1 RTU)
Here is some code for testing the setup on an Arduino UNO. The code is made so that it can be copied to larger projects. At some point I want to use it with hardware serial on an Arduino MEGA, but that is not tested yet. I cannot say that code will always work, but it works for my setup. Based on the different sources for information it seems that there are many versions of software installed on the Huanyang inverters, so I cannot promise anything.
/* Modbus rtu for Huanyang Inverter
PD001: 2 (Source of run commands: communication port)
PD002: 2 (Source of operating frequency: communication port)
PD163: 1 (Communication address: 1)
PD164: 1 (Communication Baud Rate: 9600)
PD165: 3 (Communication Data Method: 8N1 RTU)
The RTU demands silence for 3.5 characters time. 8*3.5/BAUDRATE = 2.9166 ms \approx 3 ms
2025-11-19 Morten Rydahl-Haastrup
*/
#define DEBUGPRINT
#define SOFTWARESERIAL
#define MODBUS_BAUDRATE 9600
#define MODBUS_SILENCE 3
#ifdef SOFTWARESERIAL
#include <SoftwareSerial.h>
#define TX_PIN 8
#define RX_PIN 11
#else
#define myserial Serial2
#endif
#define DRIVE_ENABLE_PIN 9
#define RECEIVE_ENABLE_PIN 10
#define HY_RUN 1
#define HY_FOR 2 // Forward
#define HY_REV 17 // Backwards (it is not 3)
#define HY_STOP 8
#define HY_READ 1
#define HY_WRITE_FUNCTION 2
#define HY_WRITE_CONTROL 3
#define HY_READ_STATUS 4
#define HY_WRITE_FREQUENCY 5
static uint16_t MODBUS_CRC16_v2( unsigned char *buf, uint8_t len )
{
//https://github.com/LacobusVentura/MODBUS-CRC16
static const uint16_t table[2] = { 0x0000, 0xA001 };
uint16_t crc = 0xFFFF;
int16_t i = 0;
unsigned char bit = 0;
uint16_t x_or = 0;
for( i = 0; i < len; i++ )
{
crc ^= buf[i];
for( bit = 0; bit < 8; bit++ )
{
x_or = crc & 0x01;
crc >>= 1;
crc ^= table[x_or];
}
}
// insert the crc at the end of the buffer
buf[len] = (unsigned char)crc;
buf[++len] = (unsigned char)(crc>>8);
return crc;
}
#ifdef SOFTWARESERIAL
SoftwareSerial myserial(RX_PIN, TX_PIN);
#endif
class HYInverter{
private:
uint8_t pin_de; // Driver enable pin
uint8_t pin_re; // Receiver enable pin
unsigned char msg[8];
unsigned long t = 0;
void send(unsigned char len){
// Is there anything in the buffer?
#ifdef DEBUGPRINT
if (myserial.available()){
Serial.print("receive: ");
while (myserial.available()){
Serial.print(myserial.read(), HEX);
Serial.print(" : ");
}
Serial.println(" ");
}
#endif
// Send the message
MODBUS_CRC16_v2(msg, len);
digitalWrite(pin_re, LOW);
digitalWrite(pin_de, HIGH);
while (millis() < t); // Wait until sufficient time has passed since last message
myserial.write(msg, len+2);
t = millis() + MODBUS_SILENCE;
digitalWrite(pin_de, LOW);
digitalWrite(pin_re, HIGH);
// Print the message
#ifdef DEBUGPRINT
Serial.print("send: ");
for (uint8_t i=0; i<len+2; i++){
Serial.print(msg[i], HEX);
Serial.print(" : ");
}
Serial.println(" ");
#endif
}
public:
void init(uint8_t DE, uint8_t RE){
pin_de = DE;
pin_re = RE;
pinMode(DE, OUTPUT);
pinMode(RE, OUTPUT);
digitalWrite(DE, LOW);
myserial.begin(MODBUS_BAUDRATE);
msg[0] = 1;
}
void setAddress(unsigned char adr){
msg[0] = adr;
}
void start(){
msg[1] = HY_WRITE_CONTROL;
msg[2] = 1;
msg[3] = HY_RUN;
send(4);
}
void setspeed(uint16_t v){
msg[1] = HY_WRITE_FREQUENCY;
msg[2] = 2; // Length of data
msg[3] = (unsigned char)(v>>8);
msg[4] = (unsigned char)(v);
send(5);
}
void stop(){
msg[1] = HY_WRITE_CONTROL;
msg[2] = 1;
msg[3] = HY_STOP;
send(4);
}
void forward(){
msg[1] = HY_WRITE_CONTROL;
msg[2] = 1;
msg[3] = HY_FOR;
send(4);
}
void reverse(){
msg[1] = HY_WRITE_CONTROL;
msg[2] = 1;
msg[3] = HY_REV;
send(4);
}
};
HYInverter inverter;
void setup() {
#ifdef DEBUGPRINT
Serial.begin(9600);
#endif
// Initialize the inverter
inverter.init(DRIVE_ENABLE_PIN, RECEIVE_ENABLE_PIN);
inverter.setAddress(1);
inverter.start();
}
uint8_t d = 0;
void loop() {
if (d == 0){
inverter.forward();
d = 1;
}else{
inverter.reverse();
d = 0;
}
for (int i=0; i<20; i++){
inverter.setspeed(i*1500);
delay(1000);
}
inverter.setspeed(0);
inverter.stop();
delay(12000);
}
