diff --git a/Grbl_Esp32/src/SettingsDefinitions.cpp b/Grbl_Esp32/src/SettingsDefinitions.cpp
index e400e129a..c47ddb170 100644
--- a/Grbl_Esp32/src/SettingsDefinitions.cpp
+++ b/Grbl_Esp32/src/SettingsDefinitions.cpp
@@ -74,6 +74,7 @@ enum_opt_t spindleTypes = {
{ "10V", int8_t(SpindleType::_10V) },
{ "H2A", int8_t(SpindleType::H2A) },
{ "YL620", int8_t(SpindleType::YL620) },
+ { "H100", int8_t(SpindleType::H100) },
// clang-format on
};
diff --git a/Grbl_Esp32/src/Spindles/H100Spindle.cpp b/Grbl_Esp32/src/Spindles/H100Spindle.cpp
new file mode 100644
index 000000000..22e08284a
--- /dev/null
+++ b/Grbl_Esp32/src/Spindles/H100Spindle.cpp
@@ -0,0 +1,145 @@
+#include "H100Spindle.h"
+
+/*
+ H100Spindle.cpp
+
+ This is for a Changrong Electric H100 VFD based spindle to be controlled via RS485 Modbus RTU.
+
+ Part of Grbl_ESP32
+ 2024 - Jaka Kordez
+
+ Grbl is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ Grbl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with Grbl. If not, see .
+
+ WARNING!!!!
+ VFDs are very dangerous. They have high voltages and are very powerful
+ Remove power before changing bits.
+
+*/
+
+namespace Spindles {
+ H100::H100() : VFD() {}
+
+ void H100::direction_command(SpindleState mode, ModbusCommand& data) {
+ // NOTE: data length is excluding the CRC16 checksum.
+ data.tx_length = 6;
+ data.rx_length = 6;
+
+ // data.msg[0] is omitted (modbus address is filled in later)
+ data.msg[1] = 0x06; // 06: write single holding register
+ data.msg[2] = 0x02; // 0x200: main control bit
+ data.msg[3] = 0x00;
+
+ switch (mode) {
+ case SpindleState::Cw:
+ data.msg[4] = 0x00;
+ data.msg[5] = 0x03;
+ break;
+ case SpindleState::Ccw:
+ data.msg[4] = 0x00;
+ data.msg[5] = 0x05;
+ break;
+ default: // SpindleState::Disable
+ data.msg[4] = 0x00;
+ data.msg[5] = 0x08;
+ break;
+ }
+ }
+
+ void H100::set_speed_command(uint32_t rpm, ModbusCommand& data) {
+ // NOTE: data length is excluding the CRC16 checksum.
+ data.tx_length = 6;
+ data.rx_length = 6;
+
+ // We have to know the max RPM before we can set the current RPM:
+ auto max_rpm = this->_max_rpm;
+ auto max_frequency = this->_maxFrequency;
+
+ uint16_t freqFromRPM = (uint16_t(rpm) * uint16_t(max_frequency)) / uint16_t(max_rpm);
+
+#ifdef VFD_DEBUG_MODE
+ grbl_msg_sendf(CLIENT_SERIAL, MsgLevel::Info, "For %d RPM the output frequency is set to %d Hz*10", int(rpm), int(freqFromRPM));
+#endif
+
+ data.msg[1] = 0x06; // 06: write single holding register
+ data.msg[2] = 0x02; // 0x0201: given frequency
+ data.msg[3] = 0x01;
+ data.msg[4] = uint8_t(freqFromRPM >> 8);
+ data.msg[5] = uint8_t(freqFromRPM & 0xFF);
+ }
+
+ VFD::response_parser H100::initialization_sequence(int index, ModbusCommand& data) {
+ this->_min_rpm = 0;
+ this->_max_rpm = 24000;
+
+ return nullptr;
+ }
+
+ VFD::response_parser H100::get_current_rpm(ModbusCommand& data) {
+ // NOTE: data length is excluding the CRC16 checksum.
+ data.tx_length = 6;
+ data.rx_length = 5;
+
+ // Send: 01 04 0000 0001
+ data.msg[1] = 0x04; // 0x04: read input register
+ data.msg[2] = 0x00; // 0x0000: register address
+ data.msg[3] = 0x00;
+ data.msg[4] = 0x00; // 0x0001: read 1 byte
+ data.msg[5] = 0x01;
+
+ // Recv: 01 04 02 xx xx
+ // ---- = 1500
+ return [](const uint8_t* response, Spindles::VFD* vfd) -> bool {
+ uint16_t freq = (uint16_t(response[3]) << 8) | uint16_t(response[4]);
+
+ auto h100 = static_cast(vfd);
+
+ uint16_t rpm = freq * uint16_t(vfd->_max_rpm) / uint16_t(h100->_maxFrequency);
+
+ // Set current RPM value? Somewhere?
+ vfd->_sync_rpm = rpm;
+ return true;
+ };
+ }
+
+ VFD::response_parser H100::get_current_direction(ModbusCommand& data) {
+ // NOTE: data length is excluding the CRC16 checksum.
+ data.tx_length = 6;
+ data.rx_length = 5;
+
+ // Send: 01 03 0210 0001
+ data.msg[1] = 0x03; // 0x03: read holding register
+ data.msg[2] = 0x02; // 0x0210: in forward/reverse rotation
+ data.msg[3] = 0x10;
+ data.msg[4] = 0x00; // 0x0001: read 1 byte
+ data.msg[5] = 0x01;
+
+ // TODO: What are we going to do with this? Update sys.spindle_speed? Update vfd state?
+ return [](const uint8_t* response, Spindles::VFD* vfd) -> bool {
+ uint8_t status = response[4];
+
+ if (status & 0x9) {
+ // Operation: ON
+ if (status & 0x2) {
+ // Direction: Reverse
+ }
+ else {
+ // Direction: Forward
+ }
+ }
+ else {
+ // Operation: OFF
+ }
+
+ return true;
+ };
+ }
+}
diff --git a/Grbl_Esp32/src/Spindles/H100Spindle.h b/Grbl_Esp32/src/Spindles/H100Spindle.h
new file mode 100644
index 000000000..c20393226
--- /dev/null
+++ b/Grbl_Esp32/src/Spindles/H100Spindle.h
@@ -0,0 +1,44 @@
+#pragma once
+
+#include "VFDSpindle.h"
+
+/*
+ H100Spindle.h
+
+ Part of Grbl_ESP32
+ 2024 - Jaka Kordez
+
+ Grbl is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ Grbl is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with Grbl. If not, see .
+
+*/
+
+namespace Spindles {
+ class H100 : public VFD {
+ protected:
+ uint16_t _minFrequency = 0; // frequency lower limit. Factor 10 of actual frequency
+ uint16_t _maxFrequency = 4000; // max frequency the VFD will allow. Normally 400.0. Factor 10 of actual frequency
+
+ void direction_command(SpindleState mode, ModbusCommand& data) override;
+ void set_speed_command(uint32_t rpm, ModbusCommand& data) override;
+
+ response_parser initialization_sequence(int index, ModbusCommand& data) override;
+ response_parser get_current_rpm(ModbusCommand& data) override;
+ response_parser get_current_direction(ModbusCommand& data) override;
+ response_parser get_status_ok(ModbusCommand& data) override { return nullptr; }
+
+ bool supports_actual_rpm() const override { return true; }
+ bool safety_polling() const override { return false; }
+
+ public:
+ H100();
+ };
+}
diff --git a/Grbl_Esp32/src/Spindles/Spindle.cpp b/Grbl_Esp32/src/Spindles/Spindle.cpp
index bc15ab96f..4780c0b48 100644
--- a/Grbl_Esp32/src/Spindles/Spindle.cpp
+++ b/Grbl_Esp32/src/Spindles/Spindle.cpp
@@ -39,6 +39,7 @@
#include "BESCSpindle.h"
#include "10vSpindle.h"
#include "YL620Spindle.h"
+#include "H100Spindle.h"
#include "TecoL510.h"
namespace Spindles {
@@ -55,6 +56,7 @@ namespace Spindles {
BESC besc;
_10v _10v;
YL620 yl620;
+ H100 h100;
L510 l510;
void Spindle::select() {
@@ -86,6 +88,9 @@ namespace Spindles {
case SpindleType::YL620:
spindle = &yl620;
break;
+ case SpindleType::H100:
+ spindle = &h100;
+ break;
case SpindleType::L510:
spindle = &l510;
break;
diff --git a/Grbl_Esp32/src/Spindles/Spindle.h b/Grbl_Esp32/src/Spindles/Spindle.h
index ff2ea5d48..eec513f78 100644
--- a/Grbl_Esp32/src/Spindles/Spindle.h
+++ b/Grbl_Esp32/src/Spindles/Spindle.h
@@ -39,6 +39,7 @@ enum class SpindleType : int8_t {
_10V,
H2A,
YL620,
+ H100,
L510
};