diff --git a/.gitignore b/.gitignore
index 077f454b..a6ac31d4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,12 @@ embedded/dist
.vscode/
.history/
*.pyc
+.vs/
+Debug/
+Release/
+*.vsarduino.h
+__vm/
+*.user
+*.vcxproj
+*.vcxproj.filters
+*.suo
diff --git a/Grbl_Esp32.sln b/Grbl_Esp32.sln
new file mode 100644
index 00000000..0ae0bade
--- /dev/null
+++ b/Grbl_Esp32.sln
@@ -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
diff --git a/VisualStudio.md b/VisualStudio.md
new file mode 100644
index 00000000..f79aef28
--- /dev/null
+++ b/VisualStudio.md
@@ -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/).
diff --git a/generate_vcxproj.py b/generate_vcxproj.py
new file mode 100644
index 00000000..36b77907
--- /dev/null
+++ b/generate_vcxproj.py
@@ -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 .
+ @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([
+ ' ',
+ ' {0}',
+ ' {1}',
+ ' '])
+ # item name, item file
+ ItemFmt = '\n'.join([
+ ' <{0} Include="{1}" />'])
+
+ # configuration, platform
+ ConfigTypePropertyGroupFmt = '\n'.join([
+ ' ',
+ ' Makefile',
+ ' true',
+ ' v142',
+ ' '])
+
+ # configuration, platform
+ ImportGroupFmt = '\n'.join([
+ ' ',
+ ' ',
+ ' '
+ ])
+
+ # configuration, platform
+ PIOPropertyGroupFmt = '\n'.join([
+ ' ',
+ ' platformio --force run',
+ ' platformio --force run -t clean',
+ ' WIN32;_DEBUG;$(NMakePreprocessorDefinitions)',
+ ' $(HOMEDRIVE)$(HOMEPATH)\\.platformio\\packages\\toolchain-xtensa32\\xtensa-esp32-elf\\include;$(HOMEDRIVE)$(HOMEPATH)\\.platformio\\packages\\framework-arduinoespressif32\\cores\\esp32;$(NMakeIncludeSearchPath)',
+ ' '
+ ])
+
+ @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}">',
+ ' {2}',
+ ' {0}>'])
+
+ # folder, uuid
+ FilterFmt = '\n'.join([
+ ' ',
+ ' {{{1}}}',
+ ' '])
+
+ @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('')
+ project.append('')
+
+ project.append('')
+ for p in self.Platforms:
+ for c in self.Configurations:
+ project.append(Vcxproj.Configuration(c, p))
+ project.append('')
+
+ # cpp header files
+ project.append('')
+ for f in self.Headers:
+ project.append(Vcxproj.Item('ClInclude', f))
+ project.append('')
+
+ # cpp source files
+ project.append('')
+ for f in self.Sources:
+ project.append(Vcxproj.Item('ClCompile', f))
+ project.append('')
+
+ # md files and ino file
+ project.append('')
+ for f in self.Others:
+ project.append(Vcxproj.Item('None', f))
+ project.append('')
+
+ # Bookkeeping, compilation, etc.
+ project.append('')
+ project.append(' 16.0')
+ project.append(' {11C8A44F-A303-4885-B5AD-5B65F7FE41C0}')
+ project.append(' Win32Proj')
+ project.append('')
+
+ project.append('')
+ for p in self.Platforms:
+ for c in self.Configurations:
+ project.append(Vcxproj.ConfigTypePropertyGroup(c, p))
+
+ project.append('')
+ project.append('')
+ project.append('')
+ project.append(' ')
+ project.append('')
+
+ for p in self.Platforms:
+ for c in self.Configurations:
+ project.append(Vcxproj.ImportGroup(c, p))
+ project.append('')
+
+ for p in self.Platforms:
+ for c in self.Configurations:
+ project.append(Vcxproj.PIOPropertyGroup(c, p))
+
+ project.append('')
+ project.append('')
+ project.append('')
+ project.append(' ')
+ project.append('')
+ project.append('')
+ project.append(' ')
+ project.append(' ')
+ project.append(' ')
+ project.append(' ')
+ project.append('')
+ return '\n'.join(project)
+
+ def CreateFilters(self):
+ project = []
+ project.append('')
+ project.append('')
+
+ project.append('')
+ for f in self.Folders:
+ project.append(Filters.Filter(f))
+ project.append('')
+
+ project.append('')
+ for f in self.Headers:
+ project.append(Filters.Item('ClInclude', f))
+ project.append('')
+
+ project.append('')
+ for f in self.Sources:
+ project.append(Filters.Item('ClCompile', f))
+ project.append('')
+
+ project.append('')
+ for f in self.Others:
+ project.append(Filters.Item('None', f))
+ project.append('')
+
+ project.append('')
+ 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)