├── .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 | [](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 | [](https://www.youtube.com/watch?v=6Nm4YFr4F-w)
41 |
42 | ## Finished Product
43 |
44 | Here is what the finished product looks like:
45 |
46 | 
47 |
48 | 
49 |
50 | 
51 |
52 | 
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 | 
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 | 
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 | 
153 |
154 | 
155 |
156 | 
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
--------------------------------------------------------------------------------