mirror of
https://github.com/bdring/Grbl_Esp32.git
synced 2025-09-07 04:40:43 +02:00
Merge branch 'master' of https://github.com/JensHauser/Grbl_Esp32_JH into master
This commit is contained in:
48
.gitignore
vendored
48
.gitignore
vendored
@@ -314,51 +314,3 @@ paket-files/
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Fody - auto-generated XML schema
|
||||
FodyWeavers.xsd
|
||||
.vs/slnx.sqlite
|
||||
.vs/Grbl_Esp32_JH/v16/.suo
|
||||
|
31
Grbl_Esp32.sln
Normal file
31
Grbl_Esp32.sln
Normal file
@@ -0,0 +1,31 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29306.81
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Grbl_Esp32", "Grbl_Esp32.vcxproj", "{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}.Debug|x64.Build.0 = Debug|x64
|
||||
{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}.Debug|x86.Build.0 = Debug|Win32
|
||||
{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}.Release|x64.ActiveCfg = Release|x64
|
||||
{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}.Release|x64.Build.0 = Release|x64
|
||||
{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}.Release|x86.ActiveCfg = Release|Win32
|
||||
{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {EEC94F4B-059C-4596-94B8-1C4C9CE5E0DD}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
@@ -83,8 +83,8 @@
|
||||
#define Y_LIMIT_PIN GPIO_NUM_32
|
||||
#define Z_LIMIT_PIN GPIO_NUM_35
|
||||
#define A_LIMIT_PIN GPIO_NUM_34
|
||||
#define B_LIMIT_PIN GPIO_NUM_39
|
||||
#define C_LIMIT_PIN GPIO_NUM_36
|
||||
//#define B_LIMIT_PIN GPIO_NUM_39
|
||||
//#define C_LIMIT_PIN GPIO_NUM_36
|
||||
|
||||
#define PROBE_PIN GPIO_NUM_25
|
||||
|
||||
@@ -117,13 +117,13 @@
|
||||
#define COOLANT_FLOOD_PIN I2SO(27)
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
// RS485 In socket #3
|
||||
#define SPINDLE_TYPE SPINDLE_TYPE_HUANYANG // only one spindle at a time
|
||||
#define HUANYANG_TXD_PIN GPIO_NUM_26
|
||||
#define HUANYANG_RTS_PIN GPIO_NUM_4
|
||||
#define HUANYANG_RXD_PIN GPIO_NUM_16
|
||||
*/
|
||||
|
||||
|
||||
|
||||
// === Default settings
|
||||
|
@@ -2,6 +2,8 @@
|
||||
HuanyangSpindle.cpp
|
||||
|
||||
This is for a Huanyang VFD based spindle via RS485 Modbus.
|
||||
Sorry for the lengthy comments, but finding the details on this
|
||||
VFD was a PITA. I am just trying to help the next person.
|
||||
|
||||
Part of Grbl_ESP32
|
||||
2020 - Bart Dring
|
||||
@@ -21,6 +23,21 @@
|
||||
VFDs are very dangerous. They have high voltages and are very powerful
|
||||
Remove power before changing bits.
|
||||
|
||||
==============================================================================
|
||||
|
||||
If a user changes state or RPM level, the command to do that is sent. If
|
||||
the command is not responded to a message is sent to serial that there was
|
||||
a timeout. If the Grbl is in a critical state, an alarm will be generated and
|
||||
the machine stopped.
|
||||
|
||||
If there are no commands to execute, various status items will be polled. If there
|
||||
is no response, it will behave as described above. It will stop any running jobs with
|
||||
an alarm.
|
||||
|
||||
===============================================================================
|
||||
|
||||
Protocol Details
|
||||
|
||||
VFD frequencies are in Hz. Multiply by 60 for RPM
|
||||
|
||||
before using spindle, VFD must be setup for RS485 and match your spindle
|
||||
@@ -32,20 +49,65 @@
|
||||
PD015 10 Deceleration time (Test to optimize)
|
||||
PD023 1 Reverse run enabled
|
||||
PD142 3.7 Max current Amps (0.8kw=3.7 1.5kw=7.0, 2.2kw=??)
|
||||
PD143 2 Poles most are 2 (I think this is only used for RPM calc from Hz)
|
||||
PD163 1 RS485 Address: 1 (Typical. OK to change...see below)
|
||||
PD164 1 RS485 Baud rate: 9600 (Typical. OK to change...see below)
|
||||
PD165 3 RS485 Mode: RTU, 8N1
|
||||
|
||||
Some references....
|
||||
Manual: http://www.hy-electrical.com/bf/inverter.pdf
|
||||
The official documentation of the RS485 is horrible. I had to piece it together from
|
||||
a lot of different sources
|
||||
|
||||
Manuals: https://github.com/RobertOlechowski/Huanyang_VFD/tree/master/Documentations/pdf
|
||||
Reference: https://github.com/Smoothieware/Smoothieware/blob/edge/src/modules/tools/spindle/HuanyangSpindleControl.cpp
|
||||
Refernece: https://gist.github.com/Bouni/803492ed0aab3f944066
|
||||
VFD settings: https://www.hobbytronics.co.za/Content/external/1159/Spindle_Settings.pdf
|
||||
Spindle Talker 2 https://github.com/GilchristT/SpindleTalker2/releases
|
||||
Python https://github.com/RobertOlechowski/Huanyang_VFD
|
||||
|
||||
=========================================================================
|
||||
|
||||
Commands
|
||||
ADDR CMD LEN DATA CRC
|
||||
0x01 0x03 0x01 0x01 0x31 0x88 Start spindle clockwise
|
||||
0x01 0x03 0x01 0x08 0xF1 0x8E Stop spindle
|
||||
0x01 0x03 0x01 0x11 0x30 0x44 Start spindle counter-clockwise
|
||||
|
||||
Return values are
|
||||
0 = run
|
||||
1 = jog
|
||||
2 = r/f
|
||||
3 = running
|
||||
4 = jogging
|
||||
5 = r/f
|
||||
6 = Braking
|
||||
7 = Track start
|
||||
|
||||
==========================================================================
|
||||
|
||||
Setting RPM
|
||||
ADDR CMD LEN DATA CRC
|
||||
0x01 0x05 0x02 0x09 0xC4 0xBF 0x0F Write Frequency (0x9C4 = 2500 = 25.00HZ)
|
||||
|
||||
Response is same as data sent
|
||||
|
||||
==========================================================================
|
||||
|
||||
Status registers
|
||||
Addr Read Len Reg DataH DataL CRC CRC
|
||||
0x01 0x04 0x03 0x00 0x00 0x00 CRC CRC // Set Frequency * 100 (25Hz = 2500)
|
||||
0x01 0x04 0x03 0x01 0x00 0x00 CRC CRC // Ouput Frequency * 100
|
||||
0x01 0x04 0x03 0x02 0x00 0x00 CRC CRC // Ouput Amps * 10
|
||||
0x01 0x04 0x03 0x03 0x00 0x00 0xF0 0x4E // Read RPM (example CRC shown)
|
||||
0x01 0x04 0x03 0x0 0x00 0x00 CRC CRC // DC voltage
|
||||
0x01 0x04 0x03 0x05 0x00 0x00 CRC CRC // AC voltage
|
||||
0x01 0x04 0x03 0x06 0x00 0x00 CRC CRC // Cont
|
||||
0x01 0x04 0x03 0x07 0x00 0x00 CRC CRC // VFD Temp
|
||||
Message is returned with requested value = (DataH * 16) + DataL (see decimal offset above)
|
||||
|
||||
TODO:
|
||||
Move CRC Calc to task to free up main task
|
||||
|
||||
|
||||
TODO
|
||||
Returning errors to Grbl and handling them in Grbl.
|
||||
What happens if the VFD does not respond
|
||||
Add periodic pinging of VFD in run mode to see if it is still at correct RPM
|
||||
*/
|
||||
#include "SpindleClass.h"
|
||||
|
||||
@@ -54,8 +116,10 @@
|
||||
#define HUANYANG_UART_PORT UART_NUM_2 // hard coded for this port right now
|
||||
#define ECHO_TEST_CTS UART_PIN_NO_CHANGE // CTS pin is not used
|
||||
#define HUANYANG_BUF_SIZE 127
|
||||
#define HUANYANG_QUEUE_SIZE 10 // numv\ber of commands that can be queued up.
|
||||
#define RESPONSE_WAIT_TICKS 50 // how long to wait for a response
|
||||
#define HUANYANG_MAX_MSG_SIZE 16 // more than enough for a modbus message
|
||||
#define HUANYANG_POLL_RATE 200 // in milliseconds betwwen commands
|
||||
|
||||
// OK to change these
|
||||
// #define them in your machine definition file if you want different values
|
||||
@@ -75,12 +139,27 @@ typedef struct {
|
||||
char msg[HUANYANG_MAX_MSG_SIZE];
|
||||
} hy_command_t;
|
||||
|
||||
typedef enum : uint8_t {
|
||||
READ_SET_FREQ = 0, // The set frequency
|
||||
READ_OUTPUT_FREQ = 1, // The current operating frequency
|
||||
READ_OUTPUT_AMPS = 2, //
|
||||
READ_SET_RPM = 3, // This is the last requested freq even in off mode
|
||||
READ_DC_VOLTAGE = 4, //
|
||||
READ_AC_VOLTAGE = 5, //
|
||||
READ_CONT = 6, // counting value???
|
||||
READ_TEMP = 7, //
|
||||
} read_register_t;
|
||||
|
||||
QueueHandle_t hy_cmd_queue;
|
||||
|
||||
static TaskHandle_t vfd_cmdTaskHandle = 0;
|
||||
|
||||
bool hy_spindle_ok = true;
|
||||
|
||||
// The communications task
|
||||
void vfd_cmd_task(void* pvParameters) {
|
||||
static bool unresponsive = false; // to pop off a message once each time it becomes unresponsive
|
||||
uint8_t reg_item = 0x00;
|
||||
hy_command_t next_cmd;
|
||||
uint8_t rx_message[HUANYANG_MAX_MSG_SIZE];
|
||||
|
||||
@@ -88,33 +167,55 @@ void vfd_cmd_task(void* pvParameters) {
|
||||
if (xQueueReceive(hy_cmd_queue, &next_cmd, 0) == pdTRUE) {
|
||||
|
||||
uart_flush(HUANYANG_UART_PORT);
|
||||
//report_hex_msg(next_cmd.msg, "Tx: ", next_cmd.tx_length);
|
||||
uart_write_bytes(HUANYANG_UART_PORT, next_cmd.msg, next_cmd.tx_length);
|
||||
|
||||
uint16_t read_length = uart_read_bytes(HUANYANG_UART_PORT, rx_message, next_cmd.rx_length, RESPONSE_WAIT_TICKS);
|
||||
|
||||
if (read_length < next_cmd.rx_length) {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Spindle RS485 Unresponsive");
|
||||
if (next_cmd.critical)
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Critical Spindle RS485 Unresponsive");
|
||||
// TODO Do something with this error
|
||||
system_set_exec_alarm(EXEC_ALARM_SPINDLE_CONTROL);
|
||||
if (!unresponsive) {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Spindle RS485 Unresponsive %d", read_length);
|
||||
if (next_cmd.critical) {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Critical Spindle RS485 Unresponsive");
|
||||
system_set_exec_alarm(EXEC_ALARM_SPINDLE_CONTROL);
|
||||
}
|
||||
unresponsive = true;
|
||||
}
|
||||
} else {
|
||||
|
||||
// success
|
||||
unresponsive = false;
|
||||
//report_hex_msg(rx_message, "Rx: ", read_length);
|
||||
uint32_t ret_value = ((uint32_t)rx_message[4] << 8) + rx_message[5];
|
||||
//grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Item:%d value:%05d ", rx_message[3], ret_value);
|
||||
}
|
||||
|
||||
} else {
|
||||
// TODO: Should we ping the spindle here to make sure it does not go off line?
|
||||
// But there is virtually no real documentation on how to do this.
|
||||
HuanyangSpindle :: read_value(reg_item); // only this appears to work all the time. Other registers are flakey.
|
||||
if (reg_item < 0x03)
|
||||
reg_item++;
|
||||
else
|
||||
{
|
||||
reg_item = 0x00;
|
||||
}
|
||||
|
||||
}
|
||||
vTaskDelay(100); // TODO: What is the best value here?
|
||||
vTaskDelay(HUANYANG_POLL_RATE); // TODO: What is the best value here?
|
||||
}
|
||||
}
|
||||
|
||||
// ================== Class methods ==================================
|
||||
|
||||
void HuanyangSpindle :: init() {
|
||||
hy_spindle_ok = true; // initialize
|
||||
|
||||
// fail if required items are not defined
|
||||
if (!get_pins_and_settings()) {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Huanyang spindle errors");
|
||||
return;
|
||||
}
|
||||
|
||||
if (! _task_running) { // init can happen many times, we only want to start one task
|
||||
hy_cmd_queue = xQueueCreate(5, sizeof(hy_command_t));
|
||||
hy_cmd_queue = xQueueCreate(HUANYANG_QUEUE_SIZE, sizeof(hy_command_t));
|
||||
xTaskCreatePinnedToCore(vfd_cmd_task, // task
|
||||
"vfd_cmdTaskHandle", // name for task
|
||||
2048, // size of task stack
|
||||
@@ -124,13 +225,7 @@ void HuanyangSpindle :: init() {
|
||||
0 // core
|
||||
);
|
||||
_task_running = true;
|
||||
}
|
||||
|
||||
// fail if required items are not defined
|
||||
if (!get_pins_and_settings()) {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Huanyang spindle errors");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// this allows us to init() again later.
|
||||
// If you change certain settings, init() gets called agian
|
||||
@@ -176,30 +271,38 @@ void HuanyangSpindle :: init() {
|
||||
// It returns a message for each missing pin
|
||||
// Returns true if all pins are defined.
|
||||
bool HuanyangSpindle :: get_pins_and_settings() {
|
||||
bool pins_ok = true;
|
||||
|
||||
|
||||
#ifdef HUANYANG_TXD_PIN
|
||||
_txd_pin = HUANYANG_TXD_PIN;
|
||||
#else
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Missing HUANYANG_TXD_PIN");
|
||||
pins_ok = false;
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Undefined HUANYANG_TXD_PIN");
|
||||
hy_spindle_ok = false;
|
||||
#endif
|
||||
|
||||
#ifdef HUANYANG_RXD_PIN
|
||||
_rxd_pin = HUANYANG_RXD_PIN;
|
||||
#else
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "No HUANYANG_RXD_PIN");
|
||||
pins_ok = false;
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Undefined HUANYANG_RXD_PIN");
|
||||
hy_spindle_ok = false;
|
||||
#endif
|
||||
|
||||
#ifdef HUANYANG_RTS_PIN
|
||||
_rts_pin = HUANYANG_RTS_PIN;
|
||||
#else
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "No HUANYANG_RTS_PIN");
|
||||
pins_ok = false;
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Undefined HUANYANG_RTS_PIN");
|
||||
hy_spindle_ok = false;
|
||||
#endif
|
||||
|
||||
return pins_ok;
|
||||
if (laser_mode->get()) {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Huanyang spindle disabled in laser mode. Set $GCode/LaserMode=Off and restart");
|
||||
hy_spindle_ok = false;
|
||||
}
|
||||
|
||||
_min_rpm = rpm_min->get();
|
||||
_max_rpm = rpm_max->get();
|
||||
|
||||
return hy_spindle_ok;
|
||||
}
|
||||
|
||||
void HuanyangSpindle :: config_message() {
|
||||
@@ -209,24 +312,15 @@ void HuanyangSpindle :: config_message() {
|
||||
pinName(_txd_pin).c_str(),
|
||||
pinName(_rxd_pin).c_str(),
|
||||
pinName(_rts_pin).c_str());
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
ADDR CMD LEN DATA CRC
|
||||
0x01 0x03 0x01 0x01 0x31 0x88 Start spindle clockwise
|
||||
0x01 0x03 0x01 0x08 0xF1 0x8E Stop spindle
|
||||
0x01 0x03 0x01 0x11 0x30 0x44 Start spindle counter-clockwise
|
||||
|
||||
|
||||
0x01 0x04 0x03 0x00 0x00 0x00 0xF0 0x4E Read Frequency
|
||||
*/
|
||||
void HuanyangSpindle :: set_state(uint8_t state, uint32_t rpm) {
|
||||
if (sys.abort)
|
||||
return; // Block during abort.
|
||||
|
||||
bool critical = (sys.state == STATE_CYCLE || state != SPINDLE_DISABLE);
|
||||
|
||||
|
||||
if (_current_state != state) { // already at the desired state. This function gets called a lot.
|
||||
set_mode(state, critical); // critical if we are in a job
|
||||
set_rpm(rpm);
|
||||
@@ -251,6 +345,9 @@ void HuanyangSpindle :: set_state(uint8_t state, uint32_t rpm) {
|
||||
}
|
||||
|
||||
bool HuanyangSpindle :: set_mode(uint8_t mode, bool critical) {
|
||||
|
||||
if (!hy_spindle_ok) return false;
|
||||
|
||||
hy_command_t mode_cmd;
|
||||
|
||||
mode_cmd.tx_length = 6;
|
||||
@@ -260,32 +357,47 @@ bool HuanyangSpindle :: set_mode(uint8_t mode, bool critical) {
|
||||
mode_cmd.msg[1] = 0x03;
|
||||
mode_cmd.msg[2] = 0x01;
|
||||
|
||||
if (mode == SPINDLE_ENABLE_CW)
|
||||
if (mode == SPINDLE_ENABLE_CW)
|
||||
mode_cmd.msg[3] = 0x01;
|
||||
else if (mode == SPINDLE_ENABLE_CCW)
|
||||
mode_cmd.msg[3] = 0x11;
|
||||
else //SPINDLE_DISABLE
|
||||
mode_cmd.msg[3] = 0x08;
|
||||
else { //SPINDLE_DISABLE
|
||||
mode_cmd.msg[3] = 0x08;
|
||||
|
||||
if (! xQueueReset(hy_cmd_queue)) {
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "VFD spindle off, queue could not be reset");
|
||||
}
|
||||
}
|
||||
|
||||
add_ModRTU_CRC(mode_cmd.msg, mode_cmd.rx_length);
|
||||
|
||||
mode_cmd.critical = critical;
|
||||
;
|
||||
|
||||
if (xQueueSend(hy_cmd_queue, &mode_cmd, 0) != pdTRUE)
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "VFD Queue Full");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
ADDR CMD LEN DATA CRC
|
||||
0x01 0x05 0x02 0x09 0xC4 0xBF 0x0F Write Frequency (0x9C4 = 2500 = 25.00HZ)
|
||||
*/
|
||||
|
||||
uint32_t HuanyangSpindle :: set_rpm(uint32_t rpm) {
|
||||
if (!hy_spindle_ok) return 0;
|
||||
|
||||
hy_command_t rpm_cmd;
|
||||
|
||||
// apply override
|
||||
rpm = rpm * sys.spindle_speed_ovr / 100; // Scale by spindle speed override value (uint8_t percent)
|
||||
|
||||
// apply limits
|
||||
if ((_min_rpm >= _max_rpm) || (rpm >= _max_rpm))
|
||||
rpm = _max_rpm;
|
||||
else if (rpm != 0 && rpm <= _min_rpm)
|
||||
rpm = _min_rpm;
|
||||
|
||||
sys.spindle_speed = rpm;
|
||||
|
||||
if (rpm == _current_rpm) // prevent setting same RPM twice
|
||||
return rpm;
|
||||
return rpm;
|
||||
|
||||
_current_rpm = rpm;
|
||||
|
||||
@@ -305,7 +417,7 @@ uint32_t HuanyangSpindle :: set_rpm(uint32_t rpm) {
|
||||
|
||||
add_ModRTU_CRC(rpm_cmd.msg, rpm_cmd.tx_length);
|
||||
|
||||
rpm_cmd.critical = (sys.state == STATE_CYCLE);
|
||||
rpm_cmd.critical = false;
|
||||
|
||||
if (xQueueSend(hy_cmd_queue, &rpm_cmd, 0) != pdTRUE)
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "VFD Queue Full");
|
||||
@@ -313,44 +425,34 @@ uint32_t HuanyangSpindle :: set_rpm(uint32_t rpm) {
|
||||
return rpm;
|
||||
}
|
||||
|
||||
// 0x01 0x04 0x03 0x00 0x00 0x00 0xF0 0x4E Read Frequency
|
||||
|
||||
// This appears to read the control register and will return an RPM running or not.
|
||||
uint16_t HuanyangSpindle :: read_rpm() {
|
||||
void HuanyangSpindle :: read_value(uint8_t reg) {
|
||||
uint16_t ret_value = 0;
|
||||
hy_command_t read_cmd;
|
||||
uint8_t rx_message[HUANYANG_MAX_MSG_SIZE];
|
||||
|
||||
read_cmd.tx_length = 8;
|
||||
read_cmd.rx_length = 6;
|
||||
read_cmd.rx_length = 8;
|
||||
|
||||
read_cmd.msg[0] = HUANYANG_ADDR;
|
||||
read_cmd.msg[1] = 0x04;
|
||||
read_cmd.msg[2] = 0x03;
|
||||
|
||||
read_cmd.msg[3] = 0x00;
|
||||
read_cmd.msg[3] = reg;
|
||||
|
||||
read_cmd.msg[4] = 0x00;
|
||||
read_cmd.msg[5] = 0x00;
|
||||
|
||||
read_cmd.critical = (sys.state == STATE_CYCLE); // only critical if running a job TBD.... maybe spindle on?
|
||||
|
||||
add_ModRTU_CRC(read_cmd.msg, read_cmd.tx_length);
|
||||
|
||||
uart_flush(HUANYANG_UART_PORT);
|
||||
uart_write_bytes(HUANYANG_UART_PORT, read_cmd.msg, read_cmd.tx_length);
|
||||
|
||||
uint16_t read_length = uart_read_bytes(HUANYANG_UART_PORT, rx_message, read_cmd.rx_length, RESPONSE_WAIT_TICKS);
|
||||
|
||||
if (read_length < read_cmd.rx_length)
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "Read RPM Spindle RS485 Unresponsive");
|
||||
else {
|
||||
uint32_t hz = ((uint32_t)rx_message[4] << 8) + rx_message[5];
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "speed %d", (hz * 60) / 100);
|
||||
|
||||
report_hex_msg(rx_message, "Rx:", read_length);
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (xQueueSend(hy_cmd_queue, &read_cmd, 0) != pdTRUE)
|
||||
grbl_msg_sendf(CLIENT_SERIAL, MSG_LEVEL_INFO, "VFD Queue Full");
|
||||
}
|
||||
|
||||
void HuanyangSpindle ::stop() {
|
||||
void HuanyangSpindle ::stop() {
|
||||
set_mode(SPINDLE_DISABLE, false);
|
||||
}
|
||||
|
||||
|
@@ -173,8 +173,12 @@ class HuanyangSpindle : public Spindle {
|
||||
uint8_t get_state();
|
||||
uint32_t set_rpm(uint32_t rpm);
|
||||
void stop();
|
||||
static uint16_t read_rpm();
|
||||
static void add_ModRTU_CRC(char* buf, int full_msg_len);
|
||||
static void read_value(uint8_t reg);
|
||||
static void add_ModRTU_CRC(char* buf, int full_msg_len);
|
||||
|
||||
protected:
|
||||
uint32_t _min_rpm;
|
||||
uint32_t _max_rpm;
|
||||
|
||||
|
||||
|
||||
|
@@ -22,7 +22,7 @@
|
||||
// Grbl versioning system
|
||||
|
||||
#define GRBL_VERSION "1.3a"
|
||||
#define GRBL_VERSION_BUILD "20200725"
|
||||
#define GRBL_VERSION_BUILD "20200727"
|
||||
|
||||
|
||||
//#include <sdkconfig.h>
|
||||
|
@@ -39,21 +39,17 @@ typedef struct {
|
||||
} client_line_t;
|
||||
client_line_t client_lines[CLIENT_COUNT];
|
||||
|
||||
static void empty_line(uint8_t client)
|
||||
{
|
||||
static void empty_line(uint8_t client) {
|
||||
client_line_t* cl = &client_lines[client];
|
||||
cl->len = 0;
|
||||
cl->buffer[0] = '\0';
|
||||
}
|
||||
static void empty_lines() {
|
||||
for (uint8_t client = 0; client < CLIENT_COUNT; client++) {
|
||||
for (uint8_t client = 0; client < CLIENT_COUNT; client++)
|
||||
empty_line(client);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
err_t add_char_to_line(char c, uint8_t client)
|
||||
{
|
||||
err_t add_char_to_line(char c, uint8_t client) {
|
||||
client_line_t* cl = &client_lines[client];
|
||||
// Simple editing for interactive input
|
||||
if (c == '\b') {
|
||||
@@ -64,9 +60,8 @@ err_t add_char_to_line(char c, uint8_t client)
|
||||
}
|
||||
return STATUS_OK;
|
||||
}
|
||||
if (cl->len == (LINE_BUFFER_SIZE - 1)) {
|
||||
if (cl->len == (LINE_BUFFER_SIZE - 1))
|
||||
return STATUS_OVERFLOW;
|
||||
}
|
||||
if (c == '\r' || c == '\n') {
|
||||
cl->len = 0;
|
||||
cl->line_number++;
|
||||
@@ -77,21 +72,17 @@ err_t add_char_to_line(char c, uint8_t client)
|
||||
return STATUS_OK;
|
||||
}
|
||||
|
||||
err_t execute_line(char* line, uint8_t client, auth_t auth_level)
|
||||
{
|
||||
err_t execute_line(char* line, uint8_t client, auth_t auth_level) {
|
||||
err_t result = STATUS_OK;
|
||||
// Empty or comment line. For syncing purposes.
|
||||
if (line[0] == 0) {
|
||||
if (line[0] == 0)
|
||||
return STATUS_OK;
|
||||
}
|
||||
// Grbl '$' or WebUI '[ESPxxx]' system command
|
||||
if (line[0] == '$' || line[0] == '[') {
|
||||
if (line[0] == '$' || line[0] == '[')
|
||||
return system_execute_line(line, client, auth_level);
|
||||
}
|
||||
// Everything else is gcode. Block if in alarm or jog mode.
|
||||
if (sys.state & (STATE_ALARM | STATE_JOG)) {
|
||||
if (sys.state & (STATE_ALARM | STATE_JOG))
|
||||
return STATUS_SYSTEM_GC_LOCK;
|
||||
}
|
||||
return gc_execute_line(line, client);
|
||||
}
|
||||
|
||||
@@ -99,7 +90,7 @@ err_t execute_line(char* line, uint8_t client, auth_t auth_level)
|
||||
GRBL PRIMARY LOOP:
|
||||
*/
|
||||
void protocol_main_loop() {
|
||||
empty_client_buffers();
|
||||
serial_reset_read_buffer(CLIENT_ALL);
|
||||
empty_lines();
|
||||
//uint8_t client = CLIENT_SERIAL; // default client
|
||||
// Perform some machine checks to make sure everything is good to go.
|
||||
@@ -156,25 +147,25 @@ void protocol_main_loop() {
|
||||
while ((c = serial_read(client)) != SERIAL_NO_DATA) {
|
||||
err_t res = add_char_to_line(c, client);
|
||||
switch (res) {
|
||||
case STATUS_OK:
|
||||
break;
|
||||
case STATUS_EOL:
|
||||
protocol_execute_realtime(); // Runtime command check point.
|
||||
if (sys.abort) {
|
||||
return; // Bail to calling function upon system abort
|
||||
}
|
||||
line = client_lines[client].buffer;
|
||||
case STATUS_OK:
|
||||
break;
|
||||
case STATUS_EOL:
|
||||
protocol_execute_realtime(); // Runtime command check point.
|
||||
if (sys.abort) {
|
||||
return; // Bail to calling function upon system abort
|
||||
}
|
||||
line = client_lines[client].buffer;
|
||||
#ifdef REPORT_ECHO_RAW_LINE_RECEIVED
|
||||
report_echo_line_received(line, client);
|
||||
report_echo_line_received(line, client);
|
||||
#endif
|
||||
// auth_level can be upgraded by supplying a password on the command line
|
||||
report_status_message(execute_line(line, client, LEVEL_GUEST), client);
|
||||
empty_line(client);
|
||||
break;
|
||||
case STATUS_OVERFLOW:
|
||||
report_status_message(STATUS_OVERFLOW, client);
|
||||
empty_line(client);
|
||||
break;
|
||||
// auth_level can be upgraded by supplying a password on the command line
|
||||
report_status_message(execute_line(line, client, LEVEL_GUEST), client);
|
||||
empty_line(client);
|
||||
break;
|
||||
case STATUS_OVERFLOW:
|
||||
report_status_message(STATUS_OVERFLOW, client);
|
||||
empty_line(client);
|
||||
break;
|
||||
}
|
||||
} // while serial read
|
||||
} // for clients
|
||||
@@ -188,9 +179,8 @@ void protocol_main_loop() {
|
||||
}
|
||||
// check to see if we should disable the stepper drivers ... esp32 work around for disable in main loop.
|
||||
if (stepper_idle) {
|
||||
if (esp_timer_get_time() > stepper_idle_counter) {
|
||||
if (esp_timer_get_time() > stepper_idle_counter)
|
||||
motors_set_disable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
return; /* Never reached */
|
||||
@@ -456,6 +446,9 @@ void protocol_exec_rt_system() {
|
||||
bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_RPM);
|
||||
sys.spindle_speed_ovr = last_s_override;
|
||||
sys.report_ovr_counter = 0; // Set to report change immediately
|
||||
// If spinlde is on, tell it the rpm has been overridden
|
||||
if (gc_state.modal.spindle != SPINDLE_DISABLE)
|
||||
spindle->set_rpm(gc_state.spindle_speed);
|
||||
}
|
||||
if (rt_exec & EXEC_SPINDLE_OVR_STOP) {
|
||||
// Spindle stop override allowed only while in HOLD state.
|
||||
@@ -564,13 +557,13 @@ static void protocol_exec_rt_suspend() {
|
||||
// NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
|
||||
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL
|
||||
if (homing_enable->get() &&
|
||||
(parking_target[PARKING_AXIS] < PARKING_TARGET) &&
|
||||
laser_mode->get() &&
|
||||
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
|
||||
#else
|
||||
if (homing_enable->get() &&
|
||||
(parking_target[PARKING_AXIS] < PARKING_TARGET) &&
|
||||
laser_mode->get()) {
|
||||
!laser_mode->get() &&
|
||||
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
|
||||
#else
|
||||
if (homing_enable->get() &&
|
||||
(parking_target[PARKING_AXIS] < PARKING_TARGET) &&
|
||||
!laser_mode->get()) {
|
||||
#endif
|
||||
// Retract spindle by pullout distance. Ensure retraction motion moves away from
|
||||
// the workpiece and waypoint motion doesn't exceed the parking target location.
|
||||
@@ -584,9 +577,9 @@ static void protocol_exec_rt_suspend() {
|
||||
// NOTE: Clear accessory state after retract and after an aborted restore motion.
|
||||
pl_data->condition = (PL_COND_FLAG_SYSTEM_MOTION | PL_COND_FLAG_NO_FEED_OVERRIDE);
|
||||
pl_data->spindle_speed = 0.0;
|
||||
spindle->set_state((SPINDLE_DISABLE, 0); // De-energize
|
||||
coolant_set_state(COOLANT_DISABLE); // De-energize
|
||||
// Execute fast parking retract motion to parking target location.
|
||||
spindle->set_state(SPINDLE_DISABLE, 0); // De-energize
|
||||
coolant_set_state(COOLANT_DISABLE); // De-energize
|
||||
// Execute fast parking retract motion to parking target location.
|
||||
if (parking_target[PARKING_AXIS] < PARKING_TARGET) {
|
||||
parking_target[PARKING_AXIS] = PARKING_TARGET;
|
||||
pl_data->feed_rate = PARKING_RATE;
|
||||
@@ -595,9 +588,9 @@ static void protocol_exec_rt_suspend() {
|
||||
} else {
|
||||
// Parking motion not possible. Just disable the spindle and coolant.
|
||||
// NOTE: Laser mode does not start a parking motion to ensure the laser stops immediately.
|
||||
->set_state((SPINDLE_DISABLE, 0.0); // De-energize
|
||||
coolant_set_state(COOLANT_DISABLE); // De-energize
|
||||
}
|
||||
spindle->set_state(SPINDLE_DISABLE, 0); // De-energize
|
||||
coolant_set_state(COOLANT_DISABLE); // De-energize
|
||||
}
|
||||
#endif
|
||||
sys.suspend &= ~(SUSPEND_RESTART_RETRACT);
|
||||
sys.suspend |= SUSPEND_RETRACT_COMPLETE;
|
||||
@@ -624,7 +617,7 @@ static void protocol_exec_rt_suspend() {
|
||||
// NOTE: State is will remain DOOR, until the de-energizing and retract is complete.
|
||||
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL
|
||||
if (homing_enable->get() && !laser_mode->get()) &&
|
||||
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
|
||||
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
|
||||
#else
|
||||
if (homing_enable->get() && !laser_mode->get()) {
|
||||
#endif
|
||||
@@ -638,8 +631,8 @@ static void protocol_exec_rt_suspend() {
|
||||
#endif
|
||||
// Delayed Tasks: Restart spindle and coolant, delay to power-up, then resume cycle.
|
||||
if (gc_state.modal.spindle != SPINDLE_DISABLE) {
|
||||
// Block if safety door re-opened during prior restore actions.
|
||||
if (bit_isfalse(sys.suspend, SUSPEND_RESTART_RETRACT)) {
|
||||
// Block if safety door re-opened during prior restore actions.
|
||||
if (bit_isfalse(sys.suspend, SUSPEND_RESTART_RETRACT)) {
|
||||
if (laser_mode->get()) {
|
||||
// When in laser mode, ignore spindle spin-up delay. Set to turn on laser when cycle starts.
|
||||
bit_true(sys.step_control, STEP_CONTROL_UPDATE_SPINDLE_RPM);
|
||||
@@ -650,8 +643,8 @@ static void protocol_exec_rt_suspend() {
|
||||
}
|
||||
}
|
||||
if (gc_state.modal.coolant != COOLANT_DISABLE) {
|
||||
// Block if safety door re-opened during prior restore actions.
|
||||
if (bit_isfalse(sys.suspend, SUSPEND_RESTART_RETRACT)) {
|
||||
// Block if safety door re-opened during prior restore actions.
|
||||
if (bit_isfalse(sys.suspend, SUSPEND_RESTART_RETRACT)) {
|
||||
// NOTE: Laser mode will honor this delay. An exhaust system is often controlled by this pin.
|
||||
coolant_set_state((restore_condition & (PL_COND_FLAG_COOLANT_FLOOD | PL_COND_FLAG_COOLANT_FLOOD)));
|
||||
delay_sec(SAFETY_DOOR_COOLANT_DELAY, DELAY_MODE_SYS_SUSPEND);
|
||||
@@ -661,7 +654,7 @@ static void protocol_exec_rt_suspend() {
|
||||
// Execute slow plunge motion from pull-out position to resume position.
|
||||
#ifdef ENABLE_PARKING_OVERRIDE_CONTROL
|
||||
if (homing_enable->get() && !laser_mode->get()) &&
|
||||
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
|
||||
(sys.override_ctrl == OVERRIDE_PARKING_MOTION)) {
|
||||
#else
|
||||
if (homing_enable->get() && !laser_mode->get()) {
|
||||
#endif
|
||||
@@ -678,8 +671,8 @@ static void protocol_exec_rt_suspend() {
|
||||
}
|
||||
#endif
|
||||
if (bit_isfalse(sys.suspend, SUSPEND_RESTART_RETRACT)) {
|
||||
sys.suspend |= SUSPEND_RESTORE_COMPLETE;
|
||||
system_set_exec_state_flag(EXEC_CYCLE_START); // Set to resume program.
|
||||
sys.suspend |= SUSPEND_RESTORE_COMPLETE;
|
||||
system_set_exec_state_flag(EXEC_CYCLE_START); // Set to resume program.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -63,12 +63,6 @@ static TaskHandle_t serialCheckTaskHandle = 0;
|
||||
|
||||
InputBuffer client_buffer[CLIENT_COUNT]; // create a buffer for each client
|
||||
|
||||
void empty_client_buffers() {
|
||||
for (uint8_t i = 0; i < CLIENT_COUNT; i++) {
|
||||
client_buffer[i].end();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the number of bytes available in a client buffer.
|
||||
uint8_t serial_get_rx_buffer_available(uint8_t client) {
|
||||
return client_buffer[client].availableforwrite();
|
||||
@@ -160,7 +154,7 @@ void serialCheckTask(void* pvParameters) {
|
||||
void serial_reset_read_buffer(uint8_t client) {
|
||||
for (uint8_t client_num = 0; client_num < CLIENT_COUNT; client_num++) {
|
||||
if (client == client_num || client == CLIENT_ALL)
|
||||
client_buffer[client].begin();
|
||||
client_buffer[client_num].begin();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -56,6 +56,4 @@ void execute_realtime_command(uint8_t command, uint8_t client);
|
||||
bool any_client_has_data();
|
||||
bool is_realtime_command(uint8_t data);
|
||||
|
||||
void empty_client_buffers();
|
||||
|
||||
#endif
|
||||
|
26
VisualStudio.md
Normal file
26
VisualStudio.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Getting started with GRBL_ESP32 in Visual Studio
|
||||
|
||||
**!! Important !! There's a huge difference between Visual Studio and
|
||||
Visual Studio Code. This document is about Visual Studio, not Visual
|
||||
Studio Code!**
|
||||
|
||||
First, get PlatformIO to work with Visual studio. The steps that
|
||||
need to be taken for this are the following:
|
||||
|
||||
1. Install python. This is needed for both PlatformIO and for generating
|
||||
the vcxproj file.
|
||||
2. From https://docs.platformio.org/en/latest/core/index.html#piocore
|
||||
you should install the PlatformIO Core (CLI). Make sure you update
|
||||
the command line search path.
|
||||
3. Use python to generate a vcxproj file: `python generate_vcxproj.py`.
|
||||
4. Start Grbl_Esp32.sln
|
||||
|
||||
## Building
|
||||
|
||||
Building is as easy as building your solution.
|
||||
|
||||
## Uploading
|
||||
|
||||
Uploading can be done from the command line using platformio. For
|
||||
example, run `platformio run --target upload --upload-port COM7`.
|
||||
For more details, see [the documentation of pio](https://dokk.org/documentation/platformio/v3.6.1/platforms/espressif32/).
|
275
generate_vcxproj.py
Normal file
275
generate_vcxproj.py
Normal file
@@ -0,0 +1,275 @@
|
||||
'''
|
||||
Visual studio project file generator
|
||||
Grbl_ESP32 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_ESP32. If not, see <http://www.gnu.org/licenses/>.
|
||||
@authors: atlaste [github.com/atlaste]
|
||||
'''
|
||||
|
||||
PATHS_TO_SEARCH = ['Grbl_Esp32']
|
||||
HEADER_EXT = ['.h', '.inl']
|
||||
SOURCE_EXT = ['.c', '.cpp']
|
||||
OTHER_EXT = ['.ino', '.md']
|
||||
|
||||
import os, uuid
|
||||
|
||||
def UUID(name):
|
||||
return str(uuid.uuid3(uuid.NAMESPACE_OID, name)).upper()
|
||||
|
||||
def FilterFromPath(path):
|
||||
(head, tail) = os.path.split(path)
|
||||
head = head.replace('/', '\\').replace('..\\', '').replace('.\\', '')
|
||||
if head == '.':
|
||||
return ''
|
||||
|
||||
h = head[0:10];
|
||||
if h == 'Grbl_Esp32':
|
||||
h = head[11:]
|
||||
return h
|
||||
|
||||
class Vcxproj:
|
||||
# configuration, platform
|
||||
ConfigurationFmt = '\n'.join([
|
||||
' <ProjectConfiguration Include="Grbl_Esp32\{0}|{1}">',
|
||||
' <Configuration>{0}</Configuration>',
|
||||
' <Platform>{1}</Platform>',
|
||||
' </ProjectConfiguration>'])
|
||||
# item name, item file
|
||||
ItemFmt = '\n'.join([
|
||||
' <{0} Include="{1}" />'])
|
||||
|
||||
# configuration, platform
|
||||
ConfigTypePropertyGroupFmt = '\n'.join([
|
||||
' <PropertyGroup Condition="\'$(Configuration)|$(Platform)\'==\'{0}|{1}\'" Label="Configuration">',
|
||||
' <ConfigurationType>Makefile</ConfigurationType>',
|
||||
' <UseDebugLibraries>true</UseDebugLibraries>',
|
||||
' <PlatformToolset>v142</PlatformToolset>',
|
||||
' </PropertyGroup>'])
|
||||
|
||||
# configuration, platform
|
||||
ImportGroupFmt = '\n'.join([
|
||||
' <ImportGroup Label="PropertySheets" Condition="\'$(Configuration)|$(Platform)\'==\'{0}|{1}\'">',
|
||||
' <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists(\'$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props\')" Label="LocalAppDataPlatform" />',
|
||||
' </ImportGroup>'
|
||||
])
|
||||
|
||||
# configuration, platform
|
||||
PIOPropertyGroupFmt = '\n'.join([
|
||||
' <PropertyGroup Condition="\'$(Configuration)|$(Platform)\'==\'{0}|{1}\'">',
|
||||
' <NMakeBuildCommandLine>platformio --force run</NMakeBuildCommandLine>',
|
||||
' <NMakeCleanCommandLine>platformio --force run -t clean</NMakeCleanCommandLine>',
|
||||
' <NMakePreprocessorDefinitions>WIN32;_DEBUG;$(NMakePreprocessorDefinitions)</NMakePreprocessorDefinitions>',
|
||||
' <NMakeIncludeSearchPath>$(HOMEDRIVE)$(HOMEPATH)\\.platformio\\packages\\toolchain-xtensa32\\xtensa-esp32-elf\\include;$(HOMEDRIVE)$(HOMEPATH)\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32;$(NMakeIncludeSearchPath)</NMakeIncludeSearchPath>',
|
||||
' </PropertyGroup>'
|
||||
])
|
||||
|
||||
@staticmethod
|
||||
def Configuration(configuration, platform):
|
||||
return Vcxproj.ConfigurationFmt.format(configuration, platform)
|
||||
|
||||
@staticmethod
|
||||
def Item(name, file):
|
||||
return Vcxproj.ItemFmt.format(name, file)
|
||||
|
||||
@staticmethod
|
||||
def ConfigTypePropertyGroup(configuration, platform):
|
||||
return Vcxproj.ConfigTypePropertyGroupFmt.format(configuration, platform)
|
||||
|
||||
@staticmethod
|
||||
def ImportGroup(configuration, platform):
|
||||
return Vcxproj.ImportGroupFmt.format(configuration, platform)
|
||||
|
||||
@staticmethod
|
||||
def PIOPropertyGroup(configuration, platform):
|
||||
return Vcxproj.PIOPropertyGroupFmt.format(configuration, platform)
|
||||
|
||||
class Filters:
|
||||
# itemtype, path, folder
|
||||
ItemFmt = '\n'.join([
|
||||
' <{0} Include="{1}">',
|
||||
' <Filter>{2}</Filter>',
|
||||
' </{0}>'])
|
||||
|
||||
# folder, uuid
|
||||
FilterFmt = '\n'.join([
|
||||
' <Filter Include="{0}">',
|
||||
' <UniqueIdentifier>{{{1}}}</UniqueIdentifier>',
|
||||
' </Filter>'])
|
||||
|
||||
@staticmethod
|
||||
def Item(itemtype, path):
|
||||
folder = FilterFromPath(path)
|
||||
return Filters.ItemFmt.format(itemtype, path, folder)
|
||||
|
||||
@staticmethod
|
||||
def Filter(folder):
|
||||
uid = UUID(folder)
|
||||
return Filters.FilterFmt.format(folder, uid)
|
||||
|
||||
class Generator:
|
||||
Folders = set()
|
||||
|
||||
# Files
|
||||
Headers = set()
|
||||
Sources = set()
|
||||
Others = set()
|
||||
|
||||
# Stuffs
|
||||
Platforms = set(['Win32','x64'])
|
||||
Configurations = set(['Debug','Release'])
|
||||
Name = 'Grbl_Esp32'
|
||||
|
||||
def AddFolder(self, path):
|
||||
filt = FilterFromPath(path)
|
||||
if filt == '':
|
||||
return
|
||||
if filt not in self.Folders:
|
||||
self.Folders.add(filt)
|
||||
filters = ''
|
||||
for f in os.path.split(filt):
|
||||
filters = os.path.join(filters, f)
|
||||
if filters != '':
|
||||
self.Folders.add(filters)
|
||||
|
||||
def AddFile(self, path):
|
||||
(root, ext) = os.path.splitext(path)
|
||||
if ext in HEADER_EXT:
|
||||
self.Headers.add(path)
|
||||
elif ext in SOURCE_EXT:
|
||||
self.Sources.add(path)
|
||||
elif ext in OTHER_EXT:
|
||||
self.Others.add(path)
|
||||
else:
|
||||
return
|
||||
|
||||
self.AddFolder(path)
|
||||
|
||||
def Walk(self, path):
|
||||
if path == 'Grbl_Esp32\\Custom' or path == 'Grbl_Esp32/Custom':
|
||||
return
|
||||
if os.path.isfile(path):
|
||||
self.AddFile(path)
|
||||
else:
|
||||
for subPath in os.listdir(path):
|
||||
self.Walk(os.path.join(path, subPath))
|
||||
|
||||
def CreateProject(self):
|
||||
project = []
|
||||
project.append('<?xml version="1.0" encoding="utf-8"?>')
|
||||
project.append('<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">')
|
||||
|
||||
project.append('<ItemGroup Label="ProjectConfigurations">')
|
||||
for p in self.Platforms:
|
||||
for c in self.Configurations:
|
||||
project.append(Vcxproj.Configuration(c, p))
|
||||
project.append('</ItemGroup>')
|
||||
|
||||
# cpp header files
|
||||
project.append('<ItemGroup>')
|
||||
for f in self.Headers:
|
||||
project.append(Vcxproj.Item('ClInclude', f))
|
||||
project.append('</ItemGroup>')
|
||||
|
||||
# cpp source files
|
||||
project.append('<ItemGroup>')
|
||||
for f in self.Sources:
|
||||
project.append(Vcxproj.Item('ClCompile', f))
|
||||
project.append('</ItemGroup>')
|
||||
|
||||
# md files and ino file
|
||||
project.append('<ItemGroup>')
|
||||
for f in self.Others:
|
||||
project.append(Vcxproj.Item('None', f))
|
||||
project.append('</ItemGroup>')
|
||||
|
||||
# Bookkeeping, compilation, etc.
|
||||
project.append('<PropertyGroup Label="Globals">')
|
||||
project.append(' <VCProjectVersion>16.0</VCProjectVersion>')
|
||||
project.append(' <ProjectGuid>{11C8A44F-A303-4885-B5AD-5B65F7FE41C0}</ProjectGuid>')
|
||||
project.append(' <Keyword>Win32Proj</Keyword>')
|
||||
project.append('</PropertyGroup>')
|
||||
|
||||
project.append('<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />')
|
||||
for p in self.Platforms:
|
||||
for c in self.Configurations:
|
||||
project.append(Vcxproj.ConfigTypePropertyGroup(c, p))
|
||||
|
||||
project.append('<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />')
|
||||
project.append('<ImportGroup Label="ExtensionSettings">')
|
||||
project.append('</ImportGroup>')
|
||||
project.append(' <ImportGroup Label="Shared">')
|
||||
project.append('</ImportGroup>')
|
||||
|
||||
for p in self.Platforms:
|
||||
for c in self.Configurations:
|
||||
project.append(Vcxproj.ImportGroup(c, p))
|
||||
project.append('<PropertyGroup Label="UserMacros" />')
|
||||
|
||||
for p in self.Platforms:
|
||||
for c in self.Configurations:
|
||||
project.append(Vcxproj.PIOPropertyGroup(c, p))
|
||||
|
||||
project.append('<ItemDefinitionGroup>')
|
||||
project.append('</ItemDefinitionGroup>')
|
||||
project.append('<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />')
|
||||
project.append(' <ImportGroup Label="ExtensionTargets">')
|
||||
project.append('</ImportGroup>')
|
||||
project.append('<ProjectExtensions>')
|
||||
project.append(' <VisualStudio>')
|
||||
project.append(' <UserProperties config.Debug.customdebug_esp32_esp32_debugger_type="universal" />')
|
||||
project.append(' </VisualStudio>')
|
||||
project.append(' </ProjectExtensions>')
|
||||
project.append('</Project>')
|
||||
return '\n'.join(project)
|
||||
|
||||
def CreateFilters(self):
|
||||
project = []
|
||||
project.append('<?xml version="1.0" encoding="utf-8"?>')
|
||||
project.append('<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">')
|
||||
|
||||
project.append('<ItemGroup>')
|
||||
for f in self.Folders:
|
||||
project.append(Filters.Filter(f))
|
||||
project.append('</ItemGroup>')
|
||||
|
||||
project.append('<ItemGroup>')
|
||||
for f in self.Headers:
|
||||
project.append(Filters.Item('ClInclude', f))
|
||||
project.append('</ItemGroup>')
|
||||
|
||||
project.append('<ItemGroup>')
|
||||
for f in self.Sources:
|
||||
project.append(Filters.Item('ClCompile', f))
|
||||
project.append('</ItemGroup>')
|
||||
|
||||
project.append('<ItemGroup>')
|
||||
for f in self.Others:
|
||||
project.append(Filters.Item('None', f))
|
||||
project.append('</ItemGroup>')
|
||||
|
||||
project.append('</Project>')
|
||||
return '\n'.join(project)
|
||||
|
||||
def Generate(self):
|
||||
f = open(self.Name + '.vcxproj', 'w')
|
||||
f.write(self.CreateProject())
|
||||
f.close()
|
||||
|
||||
f = open(self.Name + '.vcxproj.filters', 'w')
|
||||
f.write(self.CreateFilters())
|
||||
f.close()
|
||||
|
||||
def main(paths):
|
||||
generator = Generator()
|
||||
for path in paths:
|
||||
generator.Walk(path)
|
||||
generator.Generate()
|
||||
|
||||
main(PATHS_TO_SEARCH)
|
Reference in New Issue
Block a user