├── .gitignore ├── 3D_Files ├── Freecad_Model.FCStd └── STL │ ├── Circuit_Board_Box.stl │ ├── Flat_Panel_Back.stl │ ├── Flat_Panel_Front.stl │ ├── Flat_Panel_Lid.stl │ └── Flat_Panel_Spacer.stl ├── ASCOM_Driver ├── ASCOM.DarkSkyGeek.FlatPanel.csproj ├── ASCOM.DarkSkyGeek.FlatPanel.sln ├── ASCOM.ico ├── CoverCalibratorDriver.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── Resources │ ├── darkskygeek.bmp │ ├── icon-cancel-24.png │ └── icon-ok-24.png ├── SetupDialogForm.cs ├── SetupDialogForm.designer.cs ├── SetupDialogForm.resx └── app.config ├── Arduino_Firmware └── Arduino_Firmware.ino ├── Electronics └── Breadboard_Circuit.fzz ├── LICENSE ├── README.md └── images ├── Breadboard-Schematics.png ├── YouTube-thumbnail.png ├── circuit-board-box.png ├── donate.png ├── flat-panel-1.png ├── flat-panel-2.png ├── flat-panel-3.png ├── flat-panel-4.png ├── flat-panel-front.png ├── lgp+diffuser.png └── lgp.png /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | 352 | # Arduino 353 | __vm/ 354 | 355 | # Mac OS X files 356 | .DS_Store 357 | 358 | # FreeCAD backup files 359 | *.FCStd1 360 | -------------------------------------------------------------------------------- /3D_Files/Freecad_Model.FCStd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/3D_Files/Freecad_Model.FCStd -------------------------------------------------------------------------------- /3D_Files/STL/Circuit_Board_Box.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/3D_Files/STL/Circuit_Board_Box.stl -------------------------------------------------------------------------------- /3D_Files/STL/Flat_Panel_Back.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/3D_Files/STL/Flat_Panel_Back.stl -------------------------------------------------------------------------------- /3D_Files/STL/Flat_Panel_Front.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/3D_Files/STL/Flat_Panel_Front.stl -------------------------------------------------------------------------------- /3D_Files/STL/Flat_Panel_Lid.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/3D_Files/STL/Flat_Panel_Lid.stl -------------------------------------------------------------------------------- /3D_Files/STL/Flat_Panel_Spacer.stl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/3D_Files/STL/Flat_Panel_Spacer.stl -------------------------------------------------------------------------------- /ASCOM_Driver/ASCOM.DarkSkyGeek.FlatPanel.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 9.0.30729 7 | 2.0 8 | {64308775-BD4A-469C-BCAB-3ED830B811AF} 9 | Library 10 | Properties 11 | ASCOM.DarkSkyGeek 12 | ASCOM.DarkSkyGeek.FlatPanel 13 | 14 | 15 | 16 | 17 | 3.5 18 | v4.7.2 19 | ASCOM.ico 20 | false 21 | ASCOMDriverTemplate.snk 22 | publish\ 23 | true 24 | Disk 25 | false 26 | Foreground 27 | 7 28 | Days 29 | false 30 | false 31 | true 32 | 0 33 | 1.0.0.%2a 34 | false 35 | false 36 | true 37 | 38 | 39 | 40 | 41 | true 42 | full 43 | false 44 | bin\Debug\ 45 | DEBUG;TRACE 46 | prompt 47 | 4 48 | true 49 | AnyCPU 50 | false 51 | 52 | 53 | pdbonly 54 | true 55 | bin\Release\ 56 | TRACE 57 | prompt 58 | 4 59 | AnyCPU 60 | true 61 | false 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 3.5 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | True 88 | True 89 | Resources.resx 90 | 91 | 92 | True 93 | True 94 | Settings.settings 95 | 96 | 97 | Form 98 | 99 | 100 | SetupDialogForm.cs 101 | 102 | 103 | 104 | 105 | Designer 106 | ResXFileCodeGenerator 107 | Resources.Designer.cs 108 | 109 | 110 | SetupDialogForm.cs 111 | Designer 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | SettingsSingleFileGenerator 122 | Settings.Designer.cs 123 | 124 | 125 | 126 | 127 | 128 | False 129 | .NET Framework 3.5 SP1 Client Profile 130 | false 131 | 132 | 133 | False 134 | .NET Framework 3.5 SP1 135 | true 136 | 137 | 138 | False 139 | Windows Installer 3.1 140 | true 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 155 | -------------------------------------------------------------------------------- /ASCOM_Driver/ASCOM.DarkSkyGeek.FlatPanel.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.1.32228.430 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ASCOM.DarkSkyGeek.FlatPanel", "ASCOM.DarkSkyGeek.FlatPanel.csproj", "{64308775-BD4A-469C-BCAB-3ED830B811AF}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {64308775-BD4A-469C-BCAB-3ED830B811AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {64308775-BD4A-469C-BCAB-3ED830B811AF}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {64308775-BD4A-469C-BCAB-3ED830B811AF}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {64308775-BD4A-469C-BCAB-3ED830B811AF}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {6AA0B01A-996D-4541-86FA-BDDBC47E59BA} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /ASCOM_Driver/ASCOM.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/ASCOM_Driver/ASCOM.ico -------------------------------------------------------------------------------- /ASCOM_Driver/CoverCalibratorDriver.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * CoverCalibratorDriver.cs 3 | * Copyright (C) 2022 - Present, Julien Lecomte - All Rights Reserved 4 | * Licensed under the MIT License. See the accompanying LICENSE file for terms. 5 | */ 6 | 7 | using ASCOM.DeviceInterface; 8 | using ASCOM.Utilities; 9 | 10 | using System; 11 | using System.Collections; 12 | using System.Globalization; 13 | using System.Linq; 14 | using System.Runtime.InteropServices; 15 | 16 | namespace ASCOM.DarkSkyGeek 17 | { 18 | // 19 | // Your driver's DeviceID is ASCOM.DarkSkyGeek.CoverCalibrator 20 | // 21 | // The Guid attribute sets the CLSID for ASCOM.DarkSkyGeek.CoverCalibrator 22 | // The ClassInterface/None attribute prevents an empty interface called 23 | // _DarkSkyGeek from being created and used as the [default] interface 24 | // 25 | 26 | /// 27 | /// ASCOM CoverCalibrator Driver for DarkSkyGeek. 28 | /// 29 | [Guid("b034dc4e-3ea6-4ed3-9c41-b7a462337972")] 30 | [ClassInterface(ClassInterfaceType.None)] 31 | public class CoverCalibrator : ICoverCalibratorV1 32 | { 33 | /// 34 | /// ASCOM DeviceID (COM ProgID) for this driver. 35 | /// The DeviceID is used by ASCOM applications to load the driver at runtime. 36 | /// 37 | internal static string driverID = "ASCOM.DarkSkyGeek.CoverCalibrator"; 38 | 39 | /// 40 | /// Driver description that displays in the ASCOM Chooser. 41 | /// 42 | private static string deviceName = "DarkSkyGeek’s Flat Panel"; 43 | 44 | // Constants used for Profile persistence 45 | internal static string autoDetectComPortProfileName = "Auto-Detect COM Port"; 46 | internal static string autoDetectComPortDefault = "true"; 47 | internal static string comPortProfileName = "COM Port"; 48 | internal static string comPortDefault = "COM1"; 49 | internal static string traceStateProfileName = "Trace Level"; 50 | internal static string traceStateDefault = "false"; 51 | 52 | // Variables to hold the current device configuration 53 | internal static bool autoDetectComPort = Convert.ToBoolean(autoDetectComPortDefault); 54 | internal static string comPortOverride = comPortDefault; 55 | 56 | /// 57 | /// Private variable to hold the connected state 58 | /// 59 | private bool connectedState; 60 | 61 | /// 62 | /// Private variable to hold the COM port we are actually connected to 63 | /// 64 | private string comPort; 65 | 66 | /// 67 | /// Variable to hold the trace logger object (creates a diagnostic log file with information that you specify) 68 | /// 69 | internal TraceLogger tl; 70 | 71 | // The object used to communicate with the device using serial port communication. 72 | private Serial objSerial; 73 | 74 | // Constants used to communicate with the device 75 | // Make sure those values are identical to those in the Arduino Firmware. 76 | // (I could not come up with an easy way to share them across the two projects) 77 | private const string SEPARATOR = "\n"; 78 | 79 | private const string DEVICE_GUID = "7e2006ab-88b5-4b09-b0b3-1ac3ca8da43e"; 80 | 81 | private const string COMMAND_PING = "COMMAND:PING"; 82 | private const string RESULT_PING = "RESULT:PING:OK:"; 83 | 84 | private const string COMMAND_CALIBRATOR_GETBRIGHTNESS = "COMMAND:CALIBRATOR:GETBRIGHTNESS"; 85 | private const string COMMAND_CALIBRATOR_ON = "COMMAND:CALIBRATOR:ON:"; 86 | private const string COMMAND_CALIBRATOR_OFF = "COMMAND:CALIBRATOR:OFF"; 87 | private const string RESULT_CALIBRATOR_BRIGHTNESS = "RESULT:CALIBRATOR:BRIGHTNESS:"; 88 | 89 | private const int MAX_BRIGHTNESS = 255; 90 | 91 | /// 92 | /// Initializes a new instance of the class. 93 | /// Must be public for COM registration. 94 | /// 95 | public CoverCalibrator() 96 | { 97 | tl = new TraceLogger("", "DarkSkyGeek"); 98 | ReadProfile(); 99 | tl.LogMessage("CoverCalibrator", "Starting initialization"); 100 | connectedState = false; 101 | tl.LogMessage("CoverCalibrator", "Completed initialization"); 102 | } 103 | 104 | // 105 | // PUBLIC COM INTERFACE ICoverCalibratorV1 IMPLEMENTATION 106 | // 107 | 108 | #region Common properties and methods. 109 | 110 | /// 111 | /// Displays the Setup Dialog form. 112 | /// If the user clicks the OK button to dismiss the form, then 113 | /// the new settings are saved, otherwise the old values are reloaded. 114 | /// THIS IS THE ONLY PLACE WHERE SHOWING USER INTERFACE IS ALLOWED! 115 | /// 116 | public void SetupDialog() 117 | { 118 | // consider only showing the setup dialog if not connected 119 | // or call a different dialog if connected 120 | if (IsConnected) 121 | System.Windows.Forms.MessageBox.Show("Already connected, just press OK"); 122 | 123 | using (SetupDialogForm F = new SetupDialogForm(tl)) 124 | { 125 | var result = F.ShowDialog(); 126 | if (result == System.Windows.Forms.DialogResult.OK) 127 | { 128 | // Persist device configuration values to the ASCOM Profile store 129 | WriteProfile(); 130 | } 131 | } 132 | } 133 | 134 | public ArrayList SupportedActions 135 | { 136 | get 137 | { 138 | tl.LogMessage("SupportedActions Get", "Returning empty arraylist"); 139 | return new ArrayList(); 140 | } 141 | } 142 | 143 | public string Action(string actionName, string actionParameters) 144 | { 145 | LogMessage("", "Action {0}, parameters {1} not implemented", actionName, actionParameters); 146 | throw new ASCOM.ActionNotImplementedException("Action " + actionName + " is not implemented by this driver"); 147 | } 148 | 149 | public void CommandBlind(string command, bool raw) 150 | { 151 | CheckConnected("CommandBlind"); 152 | throw new ASCOM.MethodNotImplementedException("CommandBlind"); 153 | } 154 | 155 | public bool CommandBool(string command, bool raw) 156 | { 157 | CheckConnected("CommandBool"); 158 | throw new ASCOM.MethodNotImplementedException("CommandBool"); 159 | } 160 | 161 | public string CommandString(string command, bool raw) 162 | { 163 | CheckConnected("CommandString"); 164 | throw new ASCOM.MethodNotImplementedException("CommandString"); 165 | } 166 | 167 | public void Dispose() 168 | { 169 | // Clean up the trace logger object 170 | tl.Enabled = false; 171 | tl.Dispose(); 172 | tl = null; 173 | } 174 | 175 | public bool Connected 176 | { 177 | get 178 | { 179 | LogMessage("Connected", "Get {0}", IsConnected); 180 | return IsConnected; 181 | } 182 | set 183 | { 184 | tl.LogMessage("Connected", "Set {0}", value); 185 | if (value == IsConnected) 186 | return; 187 | 188 | if (value) 189 | { 190 | if (autoDetectComPort) 191 | { 192 | comPort = DetectCOMPort(); 193 | } 194 | 195 | // Fallback, in case of detection error... 196 | if (comPort == null) 197 | { 198 | comPort = comPortOverride; 199 | } 200 | 201 | if (!System.IO.Ports.SerialPort.GetPortNames().Contains(comPort)) 202 | { 203 | throw new InvalidValueException("Invalid COM port", comPort.ToString(), String.Join(", ", System.IO.Ports.SerialPort.GetPortNames())); 204 | } 205 | 206 | LogMessage("Connected Set", "Connecting to port {0}", comPort); 207 | 208 | objSerial = new Serial 209 | { 210 | Speed = SerialSpeed.ps57600, 211 | PortName = comPort, 212 | Connected = true 213 | }; 214 | 215 | // Wait a second for the serial connection to establish 216 | System.Threading.Thread.Sleep(1000); 217 | 218 | objSerial.ClearBuffers(); 219 | 220 | // Poll the device (with a short timeout value) until successful, 221 | // or until we've reached the retry count limit of 3... 222 | objSerial.ReceiveTimeout = 1; 223 | bool success = false; 224 | for (int retries = 3; retries >= 0; retries--) 225 | { 226 | string response = ""; 227 | try 228 | { 229 | objSerial.Transmit(COMMAND_PING + SEPARATOR); 230 | response = objSerial.ReceiveTerminated(SEPARATOR).Trim(); 231 | } 232 | catch (Exception) 233 | { 234 | // PortInUse or Timeout exceptions may happen here! 235 | // We ignore them. 236 | } 237 | if (response == RESULT_PING + DEVICE_GUID) 238 | { 239 | success = true; 240 | break; 241 | } 242 | } 243 | 244 | if (!success) 245 | { 246 | objSerial.Connected = false; 247 | objSerial.Dispose(); 248 | objSerial = null; 249 | throw new ASCOM.NotConnectedException("Failed to connect"); 250 | } 251 | 252 | // Restore default timeout value... 253 | objSerial.ReceiveTimeout = 10; 254 | 255 | connectedState = true; 256 | } 257 | else 258 | { 259 | CalibratorOff(); 260 | 261 | connectedState = false; 262 | 263 | LogMessage("Connected Set", "Disconnecting from port {0}", comPort); 264 | 265 | objSerial.Connected = false; 266 | objSerial.Dispose(); 267 | objSerial = null; 268 | } 269 | } 270 | } 271 | 272 | public string Description 273 | { 274 | get 275 | { 276 | tl.LogMessage("Description Get", deviceName); 277 | return deviceName; 278 | } 279 | } 280 | 281 | public string DriverInfo 282 | { 283 | get 284 | { 285 | Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; 286 | string driverInfo = deviceName + " ASCOM Driver Version " + String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.Major, version.Minor); 287 | tl.LogMessage("DriverInfo Get", driverInfo); 288 | return driverInfo; 289 | } 290 | } 291 | 292 | public string DriverVersion 293 | { 294 | get 295 | { 296 | Version version = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version; 297 | string driverVersion = String.Format(CultureInfo.InvariantCulture, "{0}.{1}", version.Major, version.Minor); 298 | tl.LogMessage("DriverVersion Get", driverVersion); 299 | return driverVersion; 300 | } 301 | } 302 | 303 | public short InterfaceVersion 304 | { 305 | get 306 | { 307 | LogMessage("InterfaceVersion Get", "1"); 308 | return Convert.ToInt16("1"); 309 | } 310 | } 311 | 312 | public string Name 313 | { 314 | get 315 | { 316 | tl.LogMessage("Name Get", deviceName); 317 | return deviceName; 318 | } 319 | } 320 | 321 | #endregion 322 | 323 | #region ICoverCalibrator Implementation 324 | 325 | /// 326 | /// Returns the state of the device cover, if present, otherwise returns "NotPresent" 327 | /// 328 | public CoverStatus CoverState 329 | { 330 | get 331 | { 332 | return CoverStatus.NotPresent; 333 | } 334 | } 335 | 336 | /// 337 | /// Initiates cover opening if a cover is present 338 | /// 339 | public void OpenCover() 340 | { 341 | CheckConnected("OpenCover"); 342 | throw new ASCOM.MethodNotImplementedException("OpenCover"); 343 | } 344 | 345 | /// 346 | /// Initiates cover closing if a cover is present 347 | /// 348 | public void CloseCover() 349 | { 350 | CheckConnected("CloseCover"); 351 | throw new ASCOM.MethodNotImplementedException("CloseCover"); 352 | } 353 | 354 | /// 355 | /// Stops any cover movement that may be in progress if a cover is present and cover movement can be interrupted. 356 | /// 357 | public void HaltCover() 358 | { 359 | CheckConnected("HaltCover"); 360 | throw new ASCOM.MethodNotImplementedException("HaltCover"); 361 | } 362 | 363 | /// 364 | /// Returns the state of the calibration device, if present, otherwise returns "NotPresent" 365 | /// 366 | public CalibratorStatus CalibratorState 367 | { 368 | get 369 | { 370 | return CalibratorStatus.Ready; 371 | } 372 | } 373 | 374 | /// 375 | /// Returns the current calibrator brightness in the range 0 (completely off) to (fully on) 376 | /// 377 | public int Brightness 378 | { 379 | get 380 | { 381 | CheckConnected("Brightness"); 382 | tl.LogMessage("Brightness", "Sending request to device..."); 383 | objSerial.Transmit(COMMAND_CALIBRATOR_GETBRIGHTNESS + SEPARATOR); 384 | tl.LogMessage("Brightness", "Waiting for response from device..."); 385 | string response; 386 | try 387 | { 388 | response = objSerial.ReceiveTerminated(SEPARATOR).Trim(); 389 | } 390 | catch (Exception e) 391 | { 392 | tl.LogMessage("Brightness", "Exception: " + e.Message); 393 | throw e; 394 | } 395 | tl.LogMessage("Brightness", "Response from device: " + response); 396 | if (!response.StartsWith(RESULT_CALIBRATOR_BRIGHTNESS)) 397 | { 398 | tl.LogMessage("Brightness", "Invalid response from device: " + response); 399 | throw new ASCOM.DriverException("Invalid response from device: " + response); 400 | } 401 | string arg = response.Substring(RESULT_CALIBRATOR_BRIGHTNESS.Length); 402 | int value; 403 | try 404 | { 405 | value = Int32.Parse(arg); 406 | } 407 | catch (FormatException) 408 | { 409 | tl.LogMessage("Brightness", "Invalid brightness value received from device: " + arg); 410 | throw new ASCOM.DriverException("Invalid brightness value received from device: " + arg); 411 | } 412 | if (value < 0 || value > MAX_BRIGHTNESS) 413 | { 414 | tl.LogMessage("Brightness", "Invalid brightness value received from device: " + arg); 415 | throw new ASCOM.DriverException("Invalid brightness value received from device: " + arg); 416 | } 417 | return value; 418 | } 419 | } 420 | 421 | /// 422 | /// The Brightness value that makes the calibrator deliver its maximum illumination. 423 | /// 424 | public int MaxBrightness 425 | { 426 | get 427 | { 428 | return MAX_BRIGHTNESS; 429 | } 430 | } 431 | 432 | /// 433 | /// Turns the calibrator on at the specified brightness if the device has calibration capability 434 | /// 435 | /// 436 | public void CalibratorOn(int Brightness) 437 | { 438 | if (Brightness < 0 || Brightness > MAX_BRIGHTNESS) 439 | { 440 | throw new ASCOM.InvalidValueException("Invalid brightness value", Brightness.ToString(), "[0, " + MAX_BRIGHTNESS.ToString() + "]"); 441 | } 442 | CheckConnected("CalibratorOn"); 443 | tl.LogMessage("CalibratorOn", "Sending request to device..."); 444 | objSerial.Transmit(COMMAND_CALIBRATOR_ON + Brightness + SEPARATOR); 445 | tl.LogMessage("CalibratorOn", "Request sent to device!"); 446 | } 447 | 448 | /// 449 | /// Turns the calibrator off if the device has calibration capability 450 | /// 451 | public void CalibratorOff() 452 | { 453 | CheckConnected("CalibratorOff"); 454 | tl.LogMessage("CalibratorOff", "Sending request to device..."); 455 | objSerial.Transmit(COMMAND_CALIBRATOR_OFF + SEPARATOR); 456 | tl.LogMessage("CalibratorOff", "Request sent to device!"); 457 | } 458 | 459 | #endregion 460 | 461 | #region Private properties and methods 462 | // here are some useful properties and methods that can be used as required 463 | // to help with driver development 464 | 465 | #region ASCOM Registration 466 | 467 | // Register or unregister driver for ASCOM. This is harmless if already 468 | // registered or unregistered. 469 | // 470 | /// 471 | /// Register or unregister the driver with the ASCOM Platform. 472 | /// This is harmless if the driver is already registered/unregistered. 473 | /// 474 | /// If true, registers the driver, otherwise unregisters it. 475 | private static void RegUnregASCOM(bool bRegister) 476 | { 477 | using (var P = new ASCOM.Utilities.Profile()) 478 | { 479 | P.DeviceType = "CoverCalibrator"; 480 | if (bRegister) 481 | { 482 | P.Register(driverID, deviceName); 483 | } 484 | else 485 | { 486 | P.Unregister(driverID); 487 | } 488 | } 489 | } 490 | 491 | /// 492 | /// This function registers the driver with the ASCOM Chooser and 493 | /// is called automatically whenever this class is registered for COM Interop. 494 | /// 495 | /// Type of the class being registered, not used. 496 | /// 497 | /// This method typically runs in two distinct situations: 498 | /// 499 | /// 500 | /// In Visual Studio, when the project is successfully built. 501 | /// For this to work correctly, the option Register for COM Interop 502 | /// must be enabled in the project settings. 503 | /// 504 | /// During setup, when the installer registers the assembly for COM Interop. 505 | /// 506 | /// This technique should mean that it is never necessary to manually register a driver with ASCOM. 507 | /// 508 | [ComRegisterFunction] 509 | public static void RegisterASCOM(Type t) 510 | { 511 | RegUnregASCOM(true); 512 | } 513 | 514 | /// 515 | /// This function unregisters the driver from the ASCOM Chooser and 516 | /// is called automatically whenever this class is unregistered from COM Interop. 517 | /// 518 | /// Type of the class being registered, not used. 519 | /// 520 | /// This method typically runs in two distinct situations: 521 | /// 522 | /// 523 | /// In Visual Studio, when the project is cleaned or prior to rebuilding. 524 | /// For this to work correctly, the option Register for COM Interop 525 | /// must be enabled in the project settings. 526 | /// 527 | /// During uninstall, when the installer unregisters the assembly from COM Interop. 528 | /// 529 | /// This technique should mean that it is never necessary to manually unregister a driver from ASCOM. 530 | /// 531 | [ComUnregisterFunction] 532 | public static void UnregisterASCOM(Type t) 533 | { 534 | RegUnregASCOM(false); 535 | } 536 | 537 | #endregion 538 | 539 | /// 540 | /// Returns true if there is a valid connection to the driver hardware 541 | /// 542 | private bool IsConnected 543 | { 544 | get 545 | { 546 | return connectedState; 547 | } 548 | } 549 | 550 | /// 551 | /// Use this function to throw an exception if we aren't connected to the hardware 552 | /// 553 | /// 554 | private void CheckConnected(string message) 555 | { 556 | if (!IsConnected) 557 | { 558 | throw new ASCOM.NotConnectedException(message); 559 | } 560 | } 561 | 562 | /// 563 | /// Read the device configuration from the ASCOM Profile store 564 | /// 565 | internal void ReadProfile() 566 | { 567 | using (Profile driverProfile = new Profile()) 568 | { 569 | driverProfile.DeviceType = "CoverCalibrator"; 570 | tl.Enabled = Convert.ToBoolean(driverProfile.GetValue(driverID, traceStateProfileName, string.Empty, traceStateDefault)); 571 | autoDetectComPort = Convert.ToBoolean(driverProfile.GetValue(driverID, autoDetectComPortProfileName, string.Empty, autoDetectComPortDefault)); 572 | comPortOverride = driverProfile.GetValue(driverID, comPortProfileName, string.Empty, comPortDefault); 573 | } 574 | } 575 | 576 | /// 577 | /// Write the device configuration to the ASCOM Profile store 578 | /// 579 | internal void WriteProfile() 580 | { 581 | using (Profile driverProfile = new Profile()) 582 | { 583 | driverProfile.DeviceType = "CoverCalibrator"; 584 | driverProfile.WriteValue(driverID, traceStateProfileName, tl.Enabled.ToString()); 585 | driverProfile.WriteValue(driverID, autoDetectComPortProfileName, autoDetectComPort.ToString()); 586 | if (comPortOverride != null) 587 | { 588 | driverProfile.WriteValue(driverID, comPortProfileName, comPortOverride.ToString()); 589 | } 590 | } 591 | } 592 | 593 | internal string DetectCOMPort() 594 | { 595 | foreach (string portName in System.IO.Ports.SerialPort.GetPortNames()) 596 | { 597 | Serial serial = null; 598 | 599 | try 600 | { 601 | serial = new Serial 602 | { 603 | Speed = SerialSpeed.ps57600, 604 | PortName = portName, 605 | Connected = true, 606 | ReceiveTimeout = 1 607 | }; 608 | } 609 | catch (Exception) 610 | { 611 | // If trying to connect to a port that is already in use, an exception will be thrown. 612 | continue; 613 | } 614 | 615 | // Wait a second for the serial connection to establish 616 | System.Threading.Thread.Sleep(1000); 617 | 618 | serial.ClearBuffers(); 619 | 620 | // Poll the device (with a short timeout value) until successful, 621 | // or until we've reached the retry count limit of 3... 622 | bool success = false; 623 | for (int retries = 3; retries >= 0; retries--) 624 | { 625 | string response = ""; 626 | try 627 | { 628 | serial.Transmit(COMMAND_PING + SEPARATOR); 629 | response = serial.ReceiveTerminated(SEPARATOR).Trim(); 630 | } 631 | catch (Exception) 632 | { 633 | // PortInUse or Timeout exceptions may happen here! 634 | // We ignore them. 635 | } 636 | if (response == RESULT_PING + DEVICE_GUID) 637 | { 638 | success = true; 639 | break; 640 | } 641 | } 642 | 643 | serial.Connected = false; 644 | serial.Dispose(); 645 | 646 | if (success) 647 | { 648 | return portName; 649 | } 650 | } 651 | 652 | return null; 653 | } 654 | 655 | /// 656 | /// Log helper function that takes formatted strings and arguments 657 | /// 658 | /// 659 | /// 660 | /// 661 | internal void LogMessage(string identifier, string message, params object[] args) 662 | { 663 | var msg = string.Format(message, args); 664 | tl.LogMessage(identifier, msg); 665 | } 666 | #endregion 667 | } 668 | } 669 | -------------------------------------------------------------------------------- /ASCOM_Driver/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | [assembly: AssemblyTitle("ASCOM.DarkSkyGeek.FlatPanel")] 5 | [assembly: AssemblyDescription("DarkSkyGeek’s Flat Panel")] 6 | [assembly: AssemblyConfiguration("")] 7 | [assembly: AssemblyCompany("")] 8 | [assembly: AssemblyProduct("DarkSkyGeek’s Flat Panel")] 9 | [assembly: AssemblyCopyright("Copyright © 2022 - Present, Julien Lecomte - All Rights Reserved")] 10 | [assembly: AssemblyTrademark("")] 11 | [assembly: AssemblyCulture("")] 12 | 13 | // Setting ComVisible to false makes the types in this assembly not visible 14 | // to COM components. If you need to access a type in this assembly from 15 | // COM, set the ComVisible attribute to true on that type. 16 | [assembly: ComVisible(true)] 17 | 18 | // The following GUID is for the ID of the typelib if this project is exposed to COM 19 | [assembly: Guid("f2f1d50c-1a65-4c2b-bc50-28037fc4789d")] 20 | 21 | // Version information for an assembly consists of the following four values: 22 | // 23 | // Major Version 24 | // Minor Version 25 | // Build Number 26 | // Revision 27 | // 28 | // You can specify all the values or you can default the Revision and Build Numbers 29 | // by using the '*' as shown below: 30 | // 31 | [assembly: AssemblyVersion("1.0.0.0")] 32 | [assembly: AssemblyFileVersion("1.0.0.0")] 33 | -------------------------------------------------------------------------------- /ASCOM_Driver/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ASCOM.DarkSkyGeek.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("ASCOM.DarkSkyGeek.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized resource of type System.Drawing.Bitmap. 65 | /// 66 | internal static System.Drawing.Bitmap darkskygeek { 67 | get { 68 | object obj = ResourceManager.GetObject("darkskygeek", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | 73 | /// 74 | /// Looks up a localized resource of type System.Drawing.Icon similar to (Icon). 75 | /// 76 | internal static System.Drawing.Icon DefaultIcon { 77 | get { 78 | object obj = ResourceManager.GetObject("DefaultIcon", resourceCulture); 79 | return ((System.Drawing.Icon)(obj)); 80 | } 81 | } 82 | 83 | /// 84 | /// Looks up a localized resource of type System.Drawing.Bitmap. 85 | /// 86 | internal static System.Drawing.Bitmap icon_cancel_24 { 87 | get { 88 | object obj = ResourceManager.GetObject("icon_cancel_24", resourceCulture); 89 | return ((System.Drawing.Bitmap)(obj)); 90 | } 91 | } 92 | 93 | /// 94 | /// Looks up a localized resource of type System.Drawing.Bitmap. 95 | /// 96 | internal static System.Drawing.Bitmap icon_ok_24 { 97 | get { 98 | object obj = ResourceManager.GetObject("icon_ok_24", resourceCulture); 99 | return ((System.Drawing.Bitmap)(obj)); 100 | } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /ASCOM_Driver/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\darkskygeek.bmp;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | 125 | ..\ASCOM.ico;System.Drawing.Icon, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 126 | 127 | 128 | ..\Resources\icon-cancel-24.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | 131 | ..\Resources\icon-ok-24.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 132 | 133 | -------------------------------------------------------------------------------- /ASCOM_Driver/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace ASCOM.DarkSkyGeek.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.4.0.0")] 17 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase 18 | { 19 | 20 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 21 | 22 | public static Settings Default 23 | { 24 | get 25 | { 26 | return defaultInstance; 27 | } 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ASCOM_Driver/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /ASCOM_Driver/Resources/darkskygeek.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/ASCOM_Driver/Resources/darkskygeek.bmp -------------------------------------------------------------------------------- /ASCOM_Driver/Resources/icon-cancel-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/ASCOM_Driver/Resources/icon-cancel-24.png -------------------------------------------------------------------------------- /ASCOM_Driver/Resources/icon-ok-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/ASCOM_Driver/Resources/icon-ok-24.png -------------------------------------------------------------------------------- /ASCOM_Driver/SetupDialogForm.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * SetupDialogForm.cs 3 | * Copyright (C) 2022 - Present, Julien Lecomte - All Rights Reserved 4 | * Licensed under the MIT License. See the accompanying LICENSE file for terms. 5 | */ 6 | 7 | using ASCOM.Utilities; 8 | using System; 9 | using System.Runtime.InteropServices; 10 | using System.Windows.Forms; 11 | 12 | namespace ASCOM.DarkSkyGeek 13 | { 14 | // Form not registered for COM! 15 | [ComVisible(false)] 16 | 17 | public partial class SetupDialogForm : Form 18 | { 19 | // Holder for a reference to the driver's trace logger 20 | TraceLogger tl; 21 | 22 | public SetupDialogForm(TraceLogger tlDriver) 23 | { 24 | InitializeComponent(); 25 | 26 | // Save the provided trace logger for use within the setup dialogue 27 | tl = tlDriver; 28 | 29 | // Initialise current values of user settings from the ASCOM Profile 30 | InitUI(); 31 | } 32 | 33 | private void cmdOK_Click(object sender, EventArgs e) // OK button event handler 34 | { 35 | // Place any validation constraint checks here 36 | // Update the state variables with results from the dialogue 37 | CoverCalibrator.autoDetectComPort = chkAutoDetect.Checked; 38 | CoverCalibrator.comPortOverride = (string)comboBoxComPort.SelectedItem; 39 | tl.Enabled = chkTrace.Checked; 40 | } 41 | 42 | private void cmdCancel_Click(object sender, EventArgs e) // Cancel button event handler 43 | { 44 | Close(); 45 | } 46 | 47 | private void BrowseToAscom(object sender, EventArgs e) // Click on ASCOM logo event handler 48 | { 49 | try 50 | { 51 | System.Diagnostics.Process.Start("https://ascom-standards.org/"); 52 | } 53 | catch (System.ComponentModel.Win32Exception noBrowser) 54 | { 55 | if (noBrowser.ErrorCode == -2147467259) 56 | MessageBox.Show(noBrowser.Message); 57 | } 58 | catch (System.Exception other) 59 | { 60 | MessageBox.Show(other.Message); 61 | } 62 | } 63 | 64 | private void InitUI() 65 | { 66 | chkAutoDetect.Checked = CoverCalibrator.autoDetectComPort; 67 | chkTrace.Checked = tl.Enabled; 68 | comboBoxComPort.Enabled = !chkAutoDetect.Checked; 69 | // Set the list of COM ports to those that are currently available 70 | comboBoxComPort.Items.Clear(); 71 | // Use System.IO because it's static 72 | comboBoxComPort.Items.AddRange(System.IO.Ports.SerialPort.GetPortNames()); 73 | // Select the current port if possible 74 | if (comboBoxComPort.Items.Contains(CoverCalibrator.comPortOverride)) 75 | { 76 | comboBoxComPort.SelectedItem = CoverCalibrator.comPortOverride; 77 | } 78 | } 79 | 80 | private void chkAutoDetect_CheckedChanged(object sender, EventArgs e) 81 | { 82 | comboBoxComPort.Enabled = !((CheckBox)sender).Checked; 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /ASCOM_Driver/SetupDialogForm.designer.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * SetupDialogForm.designer.cs 3 | * Copyright (C) 2022 - Present, Julien Lecomte - All Rights Reserved 4 | * Licensed under the MIT License. See the accompanying LICENSE file for terms. 5 | */ 6 | 7 | namespace ASCOM.DarkSkyGeek 8 | { 9 | partial class SetupDialogForm 10 | { 11 | /// 12 | /// Required designer variable. 13 | /// 14 | private System.ComponentModel.IContainer components = null; 15 | 16 | /// 17 | /// Clean up any resources being used. 18 | /// 19 | /// true if managed resources should be disposed; otherwise, false. 20 | protected override void Dispose(bool disposing) 21 | { 22 | if (disposing && (components != null)) 23 | { 24 | components.Dispose(); 25 | } 26 | base.Dispose(disposing); 27 | } 28 | 29 | #region Windows Form Designer generated code 30 | 31 | /// 32 | /// Required method for Designer support - do not modify 33 | /// the contents of this method with the code editor. 34 | /// 35 | private void InitializeComponent() 36 | { 37 | this.cmdOK = new System.Windows.Forms.Button(); 38 | this.cmdCancel = new System.Windows.Forms.Button(); 39 | this.comPortOverrideLabel = new System.Windows.Forms.Label(); 40 | this.chkTrace = new System.Windows.Forms.CheckBox(); 41 | this.comboBoxComPort = new System.Windows.Forms.ComboBox(); 42 | this.picASCOM = new System.Windows.Forms.PictureBox(); 43 | this.chkAutoDetect = new System.Windows.Forms.CheckBox(); 44 | ((System.ComponentModel.ISupportInitialize)(this.picASCOM)).BeginInit(); 45 | this.SuspendLayout(); 46 | // 47 | // cmdOK 48 | // 49 | this.cmdOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 50 | this.cmdOK.DialogResult = System.Windows.Forms.DialogResult.OK; 51 | this.cmdOK.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 52 | this.cmdOK.Image = global::ASCOM.DarkSkyGeek.Properties.Resources.icon_ok_24; 53 | this.cmdOK.Location = new System.Drawing.Point(177, 94); 54 | this.cmdOK.Name = "cmdOK"; 55 | this.cmdOK.Padding = new System.Windows.Forms.Padding(5, 0, 0, 0); 56 | this.cmdOK.Size = new System.Drawing.Size(76, 35); 57 | this.cmdOK.TabIndex = 0; 58 | this.cmdOK.UseVisualStyleBackColor = true; 59 | this.cmdOK.Click += new System.EventHandler(this.cmdOK_Click); 60 | // 61 | // cmdCancel 62 | // 63 | this.cmdCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); 64 | this.cmdCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel; 65 | this.cmdCancel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 66 | this.cmdCancel.Image = global::ASCOM.DarkSkyGeek.Properties.Resources.icon_cancel_24; 67 | this.cmdCancel.Location = new System.Drawing.Point(259, 94); 68 | this.cmdCancel.Name = "cmdCancel"; 69 | this.cmdCancel.Size = new System.Drawing.Size(74, 37); 70 | this.cmdCancel.TabIndex = 1; 71 | this.cmdCancel.UseVisualStyleBackColor = true; 72 | this.cmdCancel.Click += new System.EventHandler(this.cmdCancel_Click); 73 | // 74 | // comPortOverrideLabel 75 | // 76 | this.comPortOverrideLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 77 | this.comPortOverrideLabel.AutoSize = true; 78 | this.comPortOverrideLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 79 | this.comPortOverrideLabel.Location = new System.Drawing.Point(153, 38); 80 | this.comPortOverrideLabel.Name = "comPortOverrideLabel"; 81 | this.comPortOverrideLabel.Size = new System.Drawing.Size(99, 13); 82 | this.comPortOverrideLabel.TabIndex = 5; 83 | this.comPortOverrideLabel.Text = "COM Port Override:"; 84 | // 85 | // chkTrace 86 | // 87 | this.chkTrace.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 88 | this.chkTrace.AutoSize = true; 89 | this.chkTrace.Location = new System.Drawing.Point(268, 62); 90 | this.chkTrace.Name = "chkTrace"; 91 | this.chkTrace.Size = new System.Drawing.Size(69, 17); 92 | this.chkTrace.TabIndex = 6; 93 | this.chkTrace.Text = "Trace on"; 94 | this.chkTrace.UseVisualStyleBackColor = true; 95 | // 96 | // comboBoxComPort 97 | // 98 | this.comboBoxComPort.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 99 | this.comboBoxComPort.FormattingEnabled = true; 100 | this.comboBoxComPort.Location = new System.Drawing.Point(258, 35); 101 | this.comboBoxComPort.Name = "comboBoxComPort"; 102 | this.comboBoxComPort.Size = new System.Drawing.Size(71, 21); 103 | this.comboBoxComPort.TabIndex = 7; 104 | // 105 | // picASCOM 106 | // 107 | this.picASCOM.Cursor = System.Windows.Forms.Cursors.Hand; 108 | this.picASCOM.Image = global::ASCOM.DarkSkyGeek.Properties.Resources.darkskygeek; 109 | this.picASCOM.Location = new System.Drawing.Point(12, 9); 110 | this.picASCOM.Name = "picASCOM"; 111 | this.picASCOM.Size = new System.Drawing.Size(120, 120); 112 | this.picASCOM.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; 113 | this.picASCOM.TabIndex = 3; 114 | this.picASCOM.TabStop = false; 115 | this.picASCOM.Click += new System.EventHandler(this.BrowseToAscom); 116 | this.picASCOM.DoubleClick += new System.EventHandler(this.BrowseToAscom); 117 | // 118 | // chkAutoDetect 119 | // 120 | this.chkAutoDetect.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 121 | this.chkAutoDetect.AutoSize = true; 122 | this.chkAutoDetect.Checked = true; 123 | this.chkAutoDetect.CheckState = System.Windows.Forms.CheckState.Checked; 124 | this.chkAutoDetect.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); 125 | this.chkAutoDetect.Location = new System.Drawing.Point(202, 12); 126 | this.chkAutoDetect.Name = "chkAutoDetect"; 127 | this.chkAutoDetect.Size = new System.Drawing.Size(131, 17); 128 | this.chkAutoDetect.TabIndex = 8; 129 | this.chkAutoDetect.Text = "Auto-Detect COM port"; 130 | this.chkAutoDetect.UseVisualStyleBackColor = true; 131 | this.chkAutoDetect.CheckedChanged += new System.EventHandler(this.chkAutoDetect_CheckedChanged); 132 | // 133 | // SetupDialogForm 134 | // 135 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 136 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 137 | this.ClientSize = new System.Drawing.Size(345, 139); 138 | this.Controls.Add(this.chkAutoDetect); 139 | this.Controls.Add(this.comboBoxComPort); 140 | this.Controls.Add(this.chkTrace); 141 | this.Controls.Add(this.comPortOverrideLabel); 142 | this.Controls.Add(this.picASCOM); 143 | this.Controls.Add(this.cmdCancel); 144 | this.Controls.Add(this.cmdOK); 145 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; 146 | this.MaximizeBox = false; 147 | this.MinimizeBox = false; 148 | this.Name = "SetupDialogForm"; 149 | this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide; 150 | this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; 151 | this.Text = "DarkSkyGeek’s Flat Panel"; 152 | ((System.ComponentModel.ISupportInitialize)(this.picASCOM)).EndInit(); 153 | this.ResumeLayout(false); 154 | this.PerformLayout(); 155 | 156 | } 157 | 158 | #endregion 159 | 160 | private System.Windows.Forms.Button cmdOK; 161 | private System.Windows.Forms.Button cmdCancel; 162 | private System.Windows.Forms.Label comPortOverrideLabel; 163 | private System.Windows.Forms.CheckBox chkTrace; 164 | private System.Windows.Forms.ComboBox comboBoxComPort; 165 | private System.Windows.Forms.PictureBox picASCOM; 166 | private System.Windows.Forms.CheckBox chkAutoDetect; 167 | } 168 | } -------------------------------------------------------------------------------- /ASCOM_Driver/SetupDialogForm.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /ASCOM_Driver/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Arduino_Firmware/Arduino_Firmware.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Arduino_Firmware.ino 3 | * Copyright (C) 2022 - Present, Julien Lecomte - All Rights Reserved 4 | * Licensed under the MIT License. See the accompanying LICENSE file for terms. 5 | */ 6 | 7 | constexpr auto DEVICE_GUID = "7e2006ab-88b5-4b09-b0b3-1ac3ca8da43e"; 8 | 9 | constexpr auto COMMAND_PING = "COMMAND:PING"; 10 | constexpr auto RESULT_PING = "RESULT:PING:OK:"; 11 | 12 | constexpr auto COMMAND_INFO = "COMMAND:INFO"; 13 | constexpr auto RESULT_INFO = "RESULT:INFO:DarkSkyGeek's Flat Panel Firmware v1.0"; 14 | 15 | constexpr auto COMMAND_CALIBRATOR_GETBRIGHTNESS = "COMMAND:CALIBRATOR:GETBRIGHTNESS"; 16 | constexpr auto COMMAND_CALIBRATOR_ON = "COMMAND:CALIBRATOR:ON:"; 17 | constexpr auto COMMAND_CALIBRATOR_OFF = "COMMAND:CALIBRATOR:OFF"; 18 | constexpr auto RESULT_CALIBRATOR_BRIGHTNESS = "RESULT:CALIBRATOR:BRIGHTNESS:"; 19 | 20 | constexpr auto ERROR_INVALID_COMMAND = "ERROR:INVALID_COMMAND"; 21 | 22 | #define MIN_BRIGHTNESS 0 23 | #define MAX_BRIGHTNESS 255 24 | #define PWM_FREQ 20000 25 | 26 | byte brightness = 0; 27 | 28 | int ledPin = 8; 29 | 30 | // The `setup` function runs once when you press reset or power the board. 31 | void setup() { 32 | // Initialize serial port I/O. 33 | Serial.begin(57600); 34 | while (!Serial) { 35 | ; // Wait for serial port to connect. Required for native USB! 36 | } 37 | Serial.flush(); 38 | 39 | // Make sure the RX, TX, and built-in LEDs don't turn on, they are very bright! 40 | // Even though the board is inside an enclosure, the light can be seen shining 41 | // through the small opening for the USB connector! Unfortunately, it is not 42 | // possible to turn off the power LED (green) in code... 43 | pinMode(PIN_LED_TXL, INPUT); 44 | pinMode(PIN_LED_RXL, INPUT); 45 | pinMode(LED_BUILTIN, OUTPUT); 46 | digitalWrite(LED_BUILTIN, HIGH); 47 | 48 | // Setup LED pin as output 49 | pinMode(ledPin, OUTPUT); 50 | } 51 | 52 | // The `loop` function runs over and over again until power down or reset. 53 | void loop() { 54 | if (Serial.available() > 0) { 55 | String command = Serial.readStringUntil('\n'); 56 | if (command == COMMAND_PING) { 57 | handlePing(); 58 | } 59 | else if (command == COMMAND_INFO) { 60 | sendFirmwareInfo(); 61 | } 62 | else if (command == COMMAND_CALIBRATOR_GETBRIGHTNESS) { 63 | sendCalibratorBrightness(); 64 | } 65 | else if (command.startsWith(COMMAND_CALIBRATOR_ON)) { 66 | String arg = command.substring(strlen(COMMAND_CALIBRATOR_ON)); 67 | byte value = (byte) arg.toInt(); 68 | calibratorOn(value); 69 | } 70 | else if (command == COMMAND_CALIBRATOR_OFF) { 71 | calibratorOff(); 72 | } 73 | else { 74 | handleInvalidCommand(); 75 | } 76 | } 77 | } 78 | 79 | //-- CALIBRATOR HANDLING ------------------------------------------------------ 80 | 81 | void sendCalibratorBrightness() { 82 | Serial.print(RESULT_CALIBRATOR_BRIGHTNESS); 83 | Serial.println(brightness); 84 | } 85 | 86 | void setBrightness() { 87 | // This only works on Seeeduino Xiao. 88 | // The `pwm` function is defined in the following file: 89 | // %localappdata%\Arduino15\packages\Seeeduino\hardware\samd\1.8.2\cores\arduino\wiring_pwm.cpp 90 | // For other Arduino-compatible boards, consider using: 91 | // analogWrite(ledPin, brightness); 92 | // The nice thing about the `pwm` function is that we can set the frequency 93 | // to a much higher value (I use 20kHz) This does not work on all pins! 94 | // For example, it does not work on pin 7 of the Xiao, but it works on pin 8. 95 | int value = map(brightness, MIN_BRIGHTNESS, MAX_BRIGHTNESS, 0, 1023); 96 | pwm(ledPin, PWM_FREQ, value); 97 | } 98 | 99 | void calibratorOn(byte _brightness) { 100 | brightness = _brightness; 101 | setBrightness(); 102 | } 103 | 104 | void calibratorOff() { 105 | brightness = 0; 106 | setBrightness(); 107 | } 108 | 109 | //-- MISCELLANEOUS ------------------------------------------------------------ 110 | 111 | void handlePing() { 112 | Serial.print(RESULT_PING); 113 | Serial.println(DEVICE_GUID); 114 | } 115 | 116 | void sendFirmwareInfo() { 117 | Serial.println(RESULT_INFO); 118 | } 119 | 120 | void handleInvalidCommand() { 121 | Serial.println(ERROR_INVALID_COMMAND); 122 | } 123 | -------------------------------------------------------------------------------- /Electronics/Breadboard_Circuit.fzz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/Electronics/Breadboard_Circuit.fzz -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 - Present, Julien Lecomte - All Rights Reserved 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ASCOM-Compatible Flat Panel 2 | 3 | **IMPORTANT:** This project is no longer actively maintained, and this repository is now a public archive. It has been superseded by [my **wireless** ASCOM-compatible flat panel](https://github.com/jlecomte/ascom-wireless-flat-panel). 4 | 5 | I do not charge anything to create and maintain these open-source projects. But if you would like to say "thanks" for this project, feel free to send any amount through Paypal using the button below. I appreciate your support! 6 | 7 | [![](images/donate.png)](https://www.paypal.com/donate/?hosted_button_id=49UXY8F6VVYFA) 8 | 9 | 10 | 11 | - [Introduction](#introduction) 12 | - [Finished Product](#finished-product) 13 | - [Pre-Requisites](#pre-requisites) 14 | - [Hardware](#hardware) 15 | - [ASCOM Driver](#ascom-driver) 16 | - [Downloading And Installing The Driver](#downloading-and-installing-the-driver) 17 | - [Compiling The Driver (For Developers Only)](#compiling-the-driver-for-developers-only) 18 | - [Arduino Firmware](#arduino-firmware) 19 | - [Microcontroller Compatibility](#microcontroller-compatibility) 20 | - [Compiling And Uploading The Firmware](#compiling-and-uploading-the-firmware) 21 | - [Mechanical Components](#mechanical-components) 22 | - [Electronic Circuit](#electronic-circuit) 23 | - [Assembling The Flat Panel](#assembling-the-flat-panel) 24 | - [Ideas For Future Enhancements](#ideas-for-future-enhancements) 25 | 26 | 27 | 28 | ## Introduction 29 | 30 | Up until now, I have been using an OSC (One Shot Color) camera (ZWO ASI533MC Pro) to capture images of the night sky with my telescope, along with two filters, a Luminance (L) filter (I use an Astronomik L3 because my budget APO has some residual chromatism in the blue part of the spectrum) and a dual narrowband filter (Optolong L-eXtreme). This makes taking flats relatively easy. I just use an LED tracing panel, which I bought for $20 on Amazon, and I dim the panel using a few sheets of paper in order to accommodate the stark difference in transmission between the two filters (the transmission ratio between these two filters, for a light source that has a relatively broad spectrum, is roughly 1 to 20!) 31 | 32 | Recently, in order to get better results, both in terms of SNR and resolution, and to gain more flexibility, specifically with narrowband targets, I have decided to switch to a monochrome camera (I settled on the new and very reasonably priced ZWO ASI533MM Pro). This means that going forward, I will be going from 2 filters to 7 filters (L, R, G, B, H⍺, OIII, SII)! All these filters have wildly varying transmission characteristics, and that makes taking flats a lot more time consuming and difficult. 33 | 34 | The solution is relatively straightforward, however. An ASCOM-compatible flat panel is capable of modulating its brightness, and software like N.I.N.A., for example, can control it to adjust its brightness for each filter, while respecting the criteria you set (minimum/maximum exposure time, histogram mean, etc.) The only problem is that commercial flat panels can be quite expensive (depending on their diameter), and unless you are lucky, the flat panel you purchase will likely be bigger than you really need. For instance, for my AT130EDT refractor, the Gerd Neumann flat panel closest in size would be able to fit a 220mm diameter telescope, which is a lot bigger than I need! 35 | 36 | What I want is a high quality, affordable (BOM cost ~ $50), yet professional looking ASCOM-compatible flat panel that would be the perfect size for my telescope. The only way to get that was to design it and built it myself, and that is what I did. In this repository, I share everything you need to build your own. 37 | 38 | Make sure you check out [this page](https://github.com/jlecomte/circular-led-flat-panel-evaluation) to understand why a Light Guiding Plate (LGP) is absolutely critical for the realization of a high quality flat panel! 39 | 40 | [![YouTube video talking about this flat panel](images/YouTube-thumbnail.png)](https://www.youtube.com/watch?v=6Nm4YFr4F-w) 41 | 42 | ## Finished Product 43 | 44 | Here is what the finished product looks like: 45 | 46 | ![Finished Product](images/flat-panel-1.png) 47 | 48 | ![Finished Product](images/flat-panel-2.png) 49 | 50 | ![Finished Product](images/flat-panel-3.png) 51 | 52 | ![Finished Product](images/flat-panel-4.png) 53 | 54 | ## Pre-Requisites 55 | 56 | * A Windows computer (Windows 10 or newer) 57 | * [Microsoft Visual Studio](https://visualstudio.microsoft.com/) (FYI, I used the 2022 edition...) 58 | * [ASCOM Platform](https://ascom-standards.org/) 59 | * [ASCOM Platform Developer Components](https://ascom-standards.org/COMDeveloper/Index.htm) 60 | * [Arduino IDE](https://www.arduino.cc/en/software) 61 | * [FreeCAD](https://www.freecadweb.org/), a free and open-source 3D parametric modeler 62 | * A 3D printer able to print PETG, and a slicer (I use a heavily upgraded Creality Ender 3 v2, and Ultimaker Cura) 63 | * A few basic tools that any tinkerer must own, such as a breadboard, a soldering iron, etc. 64 | 65 | ## Hardware 66 | 67 | The following are just suggestions... Also, over time, some of the links may no longer work... 68 | 69 | * [Seeeduino XIAO](https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html) (You can get it quicker from Amazon, but you will have to pay twice as much!) See note on microcontroller compatibility below. 70 | * [Natural white LED strip](https://www.amazon.com/dp/B08H4YST71?tag=darkskygeek-20) 71 | * [Mini360 Buck Converter](https://www.amazon.com/dp/B07T7L51ZW?tag=darkskygeek-20) 72 | * IRF520N MOSFET 73 | * 1KΩ resistor 74 | * [Perforated Circuit Board](https://www.amazon.com/dp/B07NM68FXK?tag=darkskygeek-20) 75 | * [DC Power Jack](https://www.amazon.com/dp/B01N8VV78D?tag=darkskygeek-20) 76 | * [Bench Power Supply](https://www.amazon.com/dp/B07GCJ5QHF?tag=darkskygeek-20) 77 | * [Taloya LED Flush Mount Ceiling Light](https://www.amazon.com/dp/B08GX81JB1?tag=darkskygeek-20) (to extract the background disk and the Light Guiding Plate, aka LGP) 78 | * [White Acrylic Sheet](https://www.amazon.com/dp/B083XQ2QS7?tag=darkskygeek-20) (to make the diffuser) 79 | * [Threaded inserts for 3D printed parts](https://www.amazon.com/dp/B07VFZWWXY?tag=darkskygeek-20) 80 | * [Assortment of small metric screws, nuts, and washers](https://www.amazon.com/dp/B08JCKH31Q?tag=darkskygeek-20) 81 | * [22AWG solid core electrical wires](https://www.amazon.com/dp/B088KQFHV7?tag=darkskygeek-20) 82 | * [Easy-to-print PETG filament](https://www.amazon.com/dp/B07PGYHYV8?tag=darkskygeek-20) 83 | 84 | **Important note about the LED strip:** Pick a "natural white" LED strip. Stay away from "warm white" because you will run into some problems with your OIII filter (it does not emit enough in the blue part of the spectrum) or "cool white" because you will have similar issues, but with the H⍺ or SII filters (it does not emit enough in the red part of the spectrum). Also, stay away from so-called "high density" LED strips, they are simply too bright for our application. Finally, depending on the exact LED strip you picked and how much voltage you apply (you can set that using the potentiometer on the MINI360 module), you may need to insert additional diffusers. It will be a process of trial and error in order to get the perfect setup. But once you're done, and the Flat Wizard in N.I.N.A. (for example) has been configured for all your filters, taking flats will become a simple "fire and forget" operation. 85 | 86 | ## ASCOM Driver 87 | 88 | ### Downloading And Installing The Driver 89 | 90 | **Step 1:** Download the driver from the [releases page](https://github.com/jlecomte/ascom-flat-panel/releases), and place the file `ASCOM.DarkSkyGeek.FlatPanel.dll` somewhere on your system (example: `C:\Users\julien\ascom-flat-panel\`). 91 | 92 | **Step 2:** Open a command prompt, but make sure you run it **as an administrator**! 93 | 94 | **Step 3:** Then, proceed with the installation of the driver using `RegAsm.exe`, a utility that should already be present on your system (it comes with the .NET framework). Just don't forget to use the 64 bit version, and to pass the `/tlb /codebase` flags. Here is what it looked like on my imaging mini computer: 95 | 96 | ``` 97 | > cd C:\Users\julien\ascom-flat-panel\ 98 | > C:\Windows\Microsoft.NET\Framework64\v4.0.30319\RegAsm.exe /tlb /codebase ASCOM.DarkSkyGeek.FlatPanel.dll 99 | Microsoft .NET Framework Assembly Registration Utility version 4.8.4161.0 100 | for Microsoft .NET Framework version 4.8.4161.0 101 | Copyright (C) Microsoft Corporation. All rights reserved. 102 | 103 | Types registered successfully 104 | ``` 105 | 106 | **Note:** The output may be more verbose than the above. As long as it says `Types registered successfully`, you are good to go! 107 | 108 | **Note:** During registration, you will see a warning that the assembly is unsigned. This is normal as I did not bother going through the pain of signing the assembly, so you will just have to trust that you are registering the DLL that I built and uploaded to GitHub. And if you don't trust me / GitHub, you can build the DLL yourself using Visual Studio. 109 | 110 | **Note:** Once the driver has been installed, make sure you do _not_ delete or move the `ASCOM.DarkSkyGeek.FlatPanel.dll` file, or things will not work! (if you do move it, you will need to register it again in its new location) 111 | 112 | **Step 4:** Start (or restart, if it was already running) N.I.N.A. (or whatever application you use to control your equipment). 113 | 114 | ### Compiling The Driver (For Developers Only) 115 | 116 | Open Microsoft Visual Studio as an administrator (right click on the Microsoft Visual Studio shortcut, and select "Run as administrator"). This is required because when building the code, by default, Microsoft Visual Studio will register the compiled COM components, and this operation requires special privileges (Note: This is something you can disable in the project settings...) Then, open the solution (`ASCOM_Driver\ASCOM.DarkSkyGeek.FlatPanel.sln`), change the solution configuration to `Release` (in the toolbar), open the `Build` menu, and click on `Build Solution`. As long as you have properly installed all the required dependencies, the build should succeed and the ASCOM driver will be registered on your system. The binary file generated will be `ASCOM_Driver\bin\Release\ASCOM.DarkSkyGeek.FlatPanel.dll`. You may also download this file from the [Releases page](https://github.com/jlecomte/ascom-flat-panel/releases). 117 | 118 | ## Arduino Firmware 119 | 120 | ### Microcontroller Compatibility 121 | 122 | The firmware was written specifically for, and tested with a Seeeduino Xiao. It will not work on all Arduino-compatible boards. Indeed, if you look at the firmware code, you will notice that I don't use `analogWrite()`. Indeed, the frequency of the PWM wave generated by `analogWrite()` is too low (the exact value depends on the microcontroller — on an Uno, it is 490 Hz, and on a Seeeduino Xiao, it is 790 Hz by default) and any flat taken with an exposure < 1 second showed obvious artifacts (horizontal lines). So I ended up using the Xiao-specific function `pwm()` (might also work on other SAMD boards) to set both the frequency and duty cycle. The frequency I use is 20 kHz, and with that, even flats taken with exposures as low as 0.1 seconds are perfectly usable. 123 | 124 | ### Compiling And Uploading The Firmware 125 | 126 | * Add support for Seeeduino boards by following [the instructions from the board manufacturer](https://wiki.seeedstudio.com/Seeeduino-XIAO/). 127 | * To customize the name of the device when connected to your computer, open the file `boards.txt`, which, on my system and for the version of the Seeeduino board I am using, is located at `%LOCALAPPDATA%\Arduino15\packages\Seeeduino\hardware\samd\1.8.2\boards.txt`. Then, change the value of the key `seeed_XIAO_m0.build.usb_product` from `Seeed XIAO M0` (default) to whatever you'd like. I set it to `DarkSkyGeekʼs ASCOM Flat Panel`. 128 | * Finally, connect your Seeeduino board to your computer (make sure you use a USB-C cable that supports data and not just power!), open the sketch file located at `Arduino_Firmware\Arduino_Firmware.ino`, and click on the `Upload` button in the toolbar. 129 | 130 | ## Mechanical Components 131 | 132 | The STL files you will find in the `3D_Files/STL/` folder are just a starting point. You will likely have to create your own version of this project, if only to match the size of the panel to your own OTA... That is why I included the FreeCAD model in this repository (`3D_Files/Freecad_Model.FCStd`). Since there are no moving parts, it is a relatively simple assembly. 133 | 134 | ## Electronic Circuit 135 | 136 | The electronics circuit is pretty trivial. I included a Fritzing file in the `Electronics/` folder. Here are the schematics: 137 | 138 | ![Circuit Board Box](images/Breadboard-Schematics.png) 139 | 140 | **IMPORTANT:** When I first tested my circuit, I noticed that the LED strip was flickering. This could not be due to PWM because the microcontroller I am using emits a PWM signal at 490Hz, which is way too fast for your eye to pick it up. For a long time, I thought that there was a bad connection somewhere, but it turned out to be something completely different. The LED strip that I had purchased was rated for 12V. I was under the assumption that I could feed it a slightly higher voltage without any issues (most devices that take in 12V usually work well wihin a range around that median value) but it turned out that the strip's operating range was between 7V and 12V! Since my portable field battery has a voltage of between 12.8V and 13.4V, I had to use a buck converter (in the form of a MINI 360) to bring the voltage down. Actually, tuning the buck converter to output a voltage of about 8.5V (again, highly dependent on the specific LED strip you are using) will lower the brightness, which is a good thing for our application! After that small modification, the flickering is gone and the panel works very well! You can also consider making a simple voltage regulator using a Zener diode and a resistor. There are many ways to manage this kind of situations! 141 | 142 | Here is what the finished electronic circuit looks like: 143 | 144 | ![Electronic Circuit](images/circuit-board-box.png) 145 | 146 | In the photograph above, the components were soldered onto a 70mm x 30mm PCB. 147 | 148 | ## Assembling The Flat Panel 149 | 150 | The most difficult part of this project is to cleanly and solidly solder wire leads to the appropriate length of LED strip. Once I have a good solder joint, I usually put a nice glob of epoxy resin on and around it to provide some mechanical reinforcement. Then, the LED strip is stuck against the inside of the panel body. The Light Guiding Plate (LGP), cut to size, is inserted and should barely fit within the LED strip, pushing the LEDs against the wall of the panel body. Finally, the spacer is placed on top of the LGP, the diffusing sheet sits on top of the spacer, and the front part of the panel is bolted to the back. Here are a few photos to illustrate this process: 151 | 152 | ![Assembly Process Step 1](images/lgp.png) 153 | 154 | ![Assembly Process Step 2](images/lgp+diffuser.png) 155 | 156 | ![Assembly Process Step 3](images/flat-panel-front.png) 157 | 158 | ## Ideas For Future Enhancements 159 | 160 | * This flat panel could potentially be integrated in the [ASCOM Telescope Cover](https://github.com/jlecomte/ascom-telescope-cover). I decided not to do that because, in my case, it would have added too much weight to the front of my already front-heavy OTA, making it more difficult to balance, and causing all kinds of issues with the camera possibly hitting the tripod legs since the OTA would have to be moved back to provide proper balance. 161 | * It would be nice to power the LED strip from the USB cable, instead of requiring a separate power supply (or maybe use a 9V battery?) Some Arduino-compatible boards boast the ability to deliver as much as 500mA @ 5V, which should be enough to power the LED strip... 162 | -------------------------------------------------------------------------------- /images/Breadboard-Schematics.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/images/Breadboard-Schematics.png -------------------------------------------------------------------------------- /images/YouTube-thumbnail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/images/YouTube-thumbnail.png -------------------------------------------------------------------------------- /images/circuit-board-box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/images/circuit-board-box.png -------------------------------------------------------------------------------- /images/donate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/images/donate.png -------------------------------------------------------------------------------- /images/flat-panel-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/images/flat-panel-1.png -------------------------------------------------------------------------------- /images/flat-panel-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/images/flat-panel-2.png -------------------------------------------------------------------------------- /images/flat-panel-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/images/flat-panel-3.png -------------------------------------------------------------------------------- /images/flat-panel-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/images/flat-panel-4.png -------------------------------------------------------------------------------- /images/flat-panel-front.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/images/flat-panel-front.png -------------------------------------------------------------------------------- /images/lgp+diffuser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/images/lgp+diffuser.png -------------------------------------------------------------------------------- /images/lgp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jlecomte/ascom-flat-panel/5e768e5cfdb66026caa3820872eea1ff223cbc2a/images/lgp.png --------------------------------------------------------------------------------