├── GUI └── MHTG │ ├── MHTG │ ├── Resources │ │ ├── green_icon.ico │ │ └── green_icon.png │ ├── bin │ │ └── Debug │ │ │ └── MHTG_V10X_GUI.zip │ ├── App.config │ ├── Properties │ │ ├── Settings.settings │ │ ├── Settings.Designer.cs │ │ ├── AssemblyInfo.cs │ │ ├── Resources.Designer.cs │ │ └── Resources.resx │ ├── Program.cs │ ├── AboutForm.cs │ ├── MHTG.csproj │ ├── Util.cs │ ├── AboutForm.Designer.cs │ ├── AboutForm.resx │ ├── MainForm.resx │ ├── MainForm.Designer.cs │ └── MainForm.cs │ └── MHTG.sln ├── PCB ├── V102 │ └── MHTG_V102 │ │ ├── box │ │ ├── MHTG_V102_PCB_box_top.STL │ │ ├── MHTG_V101_reset_button.STL │ │ ├── MHTG_V102_PCB_box_bottom.STL │ │ ├── MHTG_V102_LED_extender_cover.STL │ │ ├── MHTG_V102_LED_extender_fixture.STL │ │ └── MHTG_V101_LED_extender_transparent.STL │ │ ├── renders │ │ ├── MHTG_V102_top_render.png │ │ └── MHTG_V102_bottom_render.png │ │ ├── schematic │ │ └── MHTG_V102_schematic.pdf │ │ └── bom │ │ └── MHTG_V102_BOM.html └── V110 │ └── MHTG_V110 │ └── schematic │ ├── MHTG_V110_schematic.pdf │ └── MHTG_V110_schematic_bw.pdf ├── README.md └── LICENSE /GUI/MHTG/MHTG/Resources/green_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/GUI/MHTG/MHTG/Resources/green_icon.ico -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/Resources/green_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/GUI/MHTG/MHTG/Resources/green_icon.png -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/bin/Debug/MHTG_V10X_GUI.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/GUI/MHTG/MHTG/bin/Debug/MHTG_V10X_GUI.zip -------------------------------------------------------------------------------- /PCB/V102/MHTG_V102/box/MHTG_V102_PCB_box_top.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/PCB/V102/MHTG_V102/box/MHTG_V102_PCB_box_top.STL -------------------------------------------------------------------------------- /PCB/V102/MHTG_V102/box/MHTG_V101_reset_button.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/PCB/V102/MHTG_V102/box/MHTG_V101_reset_button.STL -------------------------------------------------------------------------------- /PCB/V102/MHTG_V102/box/MHTG_V102_PCB_box_bottom.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/PCB/V102/MHTG_V102/box/MHTG_V102_PCB_box_bottom.STL -------------------------------------------------------------------------------- /PCB/V102/MHTG_V102/renders/MHTG_V102_top_render.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/PCB/V102/MHTG_V102/renders/MHTG_V102_top_render.png -------------------------------------------------------------------------------- /PCB/V102/MHTG_V102/schematic/MHTG_V102_schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/PCB/V102/MHTG_V102/schematic/MHTG_V102_schematic.pdf -------------------------------------------------------------------------------- /PCB/V110/MHTG_V110/schematic/MHTG_V110_schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/PCB/V110/MHTG_V110/schematic/MHTG_V110_schematic.pdf -------------------------------------------------------------------------------- /PCB/V102/MHTG_V102/box/MHTG_V102_LED_extender_cover.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/PCB/V102/MHTG_V102/box/MHTG_V102_LED_extender_cover.STL -------------------------------------------------------------------------------- /PCB/V102/MHTG_V102/renders/MHTG_V102_bottom_render.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/PCB/V102/MHTG_V102/renders/MHTG_V102_bottom_render.png -------------------------------------------------------------------------------- /PCB/V110/MHTG_V110/schematic/MHTG_V110_schematic_bw.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/PCB/V110/MHTG_V110/schematic/MHTG_V110_schematic_bw.pdf -------------------------------------------------------------------------------- /PCB/V102/MHTG_V102/box/MHTG_V102_LED_extender_fixture.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/PCB/V102/MHTG_V102/box/MHTG_V102_LED_extender_fixture.STL -------------------------------------------------------------------------------- /PCB/V102/MHTG_V102/box/MHTG_V101_LED_extender_transparent.STL: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/laszlodaniel/ModularHydroponicTowerGarden/HEAD/PCB/V102/MHTG_V102/box/MHTG_V101_LED_extender_transparent.STL -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using System.Windows.Forms; 6 | 7 | namespace MHTG 8 | { 9 | static class Program 10 | { 11 | /// 12 | /// The main entry point for the application. 13 | /// 14 | [STAThread] 15 | static void Main() 16 | { 17 | Application.EnableVisualStyles(); 18 | Application.SetCompatibleTextRenderingDefault(false); 19 | Application.Run(new MainForm()); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/AboutForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.Windows.Forms; 10 | 11 | namespace MHTG 12 | { 13 | public partial class AboutForm : Form 14 | { 15 | MainForm originalForm; 16 | 17 | public AboutForm(MainForm incomingForm) 18 | { 19 | originalForm = incomingForm; 20 | InitializeComponent(); 21 | Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath); 22 | } 23 | 24 | private void AboutForm_FormClosed(object sender, FormClosedEventArgs e) 25 | { 26 | GC.Collect(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.29609.76 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MHTG", "MHTG\MHTG.csproj", "{ADF72752-8DB0-4EA1-BDC5-E71F3CF1532F}" 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 | {ADF72752-8DB0-4EA1-BDC5-E71F3CF1532F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {ADF72752-8DB0-4EA1-BDC5-E71F3CF1532F}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {ADF72752-8DB0-4EA1-BDC5-E71F3CF1532F}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {ADF72752-8DB0-4EA1-BDC5-E71F3CF1532F}.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 = {5EF43353-86D5-4466-AF50-1FC34D7F6F5F} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/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 MHTG.Properties 12 | { 13 | 14 | 15 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 16 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.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 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Resources; 2 | using System.Reflection; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | // General Information about an assembly is controlled through the following 7 | // set of attributes. Change these attribute values to modify the information 8 | // associated with an assembly. 9 | [assembly: AssemblyTitle("MHTG")] 10 | [assembly: AssemblyDescription("Modular Hydroponic Tower Garden")] 11 | [assembly: AssemblyConfiguration("")] 12 | [assembly: AssemblyCompany("Lucid Dreaming Corp.")] 13 | [assembly: AssemblyProduct("MHTG")] 14 | [assembly: AssemblyCopyright("Copyright © 2019")] 15 | [assembly: AssemblyTrademark("")] 16 | [assembly: AssemblyCulture("")] 17 | 18 | // Setting ComVisible to false makes the types in this assembly not visible 19 | // to COM components. If you need to access a type in this assembly from 20 | // COM, set the ComVisible attribute to true on that type. 21 | [assembly: ComVisible(false)] 22 | 23 | // The following GUID is for the ID of the typelib if this project is exposed to COM 24 | [assembly: Guid("adf72752-8db0-4ea1-bdc5-e71f3cf1532f")] 25 | 26 | // Version information for an assembly consists of the following four values: 27 | // 28 | // Major Version 29 | // Minor Version 30 | // Build Number 31 | // Revision 32 | // 33 | // You can specify all the values or you can default the Build and Revision Numbers 34 | // by using the '*' as shown below: 35 | // [assembly: AssemblyVersion("1.0.*")] 36 | [assembly: AssemblyVersion("1.0.0.0")] 37 | [assembly: AssemblyFileVersion("1.0.0.0")] 38 | [assembly: NeutralResourcesLanguage("en")] 39 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/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 MHTG.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", "16.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("MHTG.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 green_icon { 67 | get { 68 | object obj = ResourceManager.GetObject("green_icon", resourceCulture); 69 | return ((System.Drawing.Bitmap)(obj)); 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/MHTG.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {ADF72752-8DB0-4EA1-BDC5-E71F3CF1532F} 8 | WinExe 9 | MHTG 10 | MHTG 11 | v4.5 12 | 512 13 | true 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | Resources\green_icon.ico 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Form 53 | 54 | 55 | AboutForm.cs 56 | 57 | 58 | Form 59 | 60 | 61 | MainForm.cs 62 | 63 | 64 | 65 | 66 | 67 | AboutForm.cs 68 | 69 | 70 | MainForm.cs 71 | 72 | 73 | ResXFileCodeGenerator 74 | Resources.Designer.cs 75 | Designer 76 | 77 | 78 | True 79 | Resources.resx 80 | True 81 | 82 | 83 | SettingsSingleFileGenerator 84 | Settings.Designer.cs 85 | 86 | 87 | True 88 | Settings.settings 89 | True 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/Util.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Windows.Forms; 6 | 7 | namespace MHTG 8 | { 9 | public static class Util 10 | { 11 | public static bool CompareArrays(byte[] first, byte[] second, int index, int length) 12 | { 13 | bool ret = false; 14 | 15 | for (int i = index; i < length; i++) 16 | { 17 | if (first[i] == second[i]) ret = true; 18 | else ret = false; 19 | } 20 | 21 | return ret; 22 | } 23 | 24 | public static string ByteToHexString(byte[] data, int index, int length) 25 | { 26 | StringBuilder ret = new StringBuilder(); 27 | byte counter = 0; 28 | 29 | if (data != null) 30 | { 31 | for (int i = index; i < length; i++) 32 | { 33 | if (length < 17) ret.Append(Convert.ToString(data[i], 16).PadLeft(2, '0').PadRight(3, ' ').ToUpper()); 34 | else 35 | { 36 | ret.Append(Convert.ToString(data[i], 16).PadLeft(2, '0').PadRight(3, ' ').ToUpper()); 37 | 38 | counter++; 39 | if (counter > 15) 40 | { 41 | if (i != (length - 1)) ret.Append(Environment.NewLine); // New line after every 16 bytes 42 | counter = 0; 43 | } 44 | } 45 | } 46 | //if (counter == 0) ret = ret.Remove(ret.Length - 1, 1); // remove newline character if text is touching the textbox wall 47 | ret.Replace(" ", String.Empty, ret.Length - 1, 1); // remove whitespace at the end caused by PadRight(3, ' ') 48 | } 49 | return ret.ToString(); 50 | } 51 | 52 | public static byte[] HexStringToByte(string str) 53 | { 54 | // Remove whitespaces, commas, semi-colons and hex number identifiers 55 | string ret = str.Trim().Replace(" ", String.Empty).Replace(",", String.Empty).Replace(";", String.Empty).Replace("$", String.Empty).Replace("0x", String.Empty); 56 | try 57 | { 58 | return Enumerable.Range(0, ret.Length).Where(x => x % 2 == 0).Select(x => Convert.ToByte(ret.Substring(x, 2), 16)).ToArray(); 59 | } 60 | catch 61 | { 62 | return new byte[] { }; // return an empty byte array if something is wrong 63 | } 64 | } 65 | 66 | public static void UpdateTextBox(TextBox textbox, string text, byte[] bytes) 67 | { 68 | string ret = String.Empty; 69 | 70 | if (textbox.Text != "") ret += Environment.NewLine + Environment.NewLine; 71 | ret += text; 72 | if (bytes != null) ret += Environment.NewLine + ByteToHexString(bytes, 0, bytes.Length); 73 | 74 | if (textbox.InvokeRequired) 75 | { 76 | textbox.BeginInvoke((MethodInvoker)delegate 77 | { 78 | if (textbox.TextLength + ret.Length > textbox.MaxLength) 79 | { 80 | textbox.Clear(); 81 | GC.Collect(); 82 | } 83 | textbox.AppendText(ret); 84 | }); 85 | } 86 | else 87 | { 88 | if (textbox.TextLength + ret.Length > textbox.MaxLength) 89 | { 90 | textbox.Clear(); 91 | GC.Collect(); 92 | } 93 | textbox.AppendText(ret); 94 | } 95 | 96 | // Save text to log-file 97 | File.AppendAllText(MainForm.USBLogFilename, ret); 98 | 99 | // Save raw USB packet to a binary logfile 100 | using (BinaryWriter writer = new BinaryWriter(File.Open(MainForm.USBBinaryLogFilename, FileMode.Append))) 101 | { 102 | if (bytes != null) 103 | { 104 | writer.Write(bytes); 105 | writer.Close(); 106 | } 107 | } 108 | } 109 | 110 | public static DateTime UnixTimeStampToDateTime(double unixTimeStamp) 111 | { 112 | // Unix timestamp is seconds past epoch 113 | DateTime dtDateTime = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); 114 | dtDateTime = dtDateTime.AddSeconds(unixTimeStamp).ToLocalTime(); 115 | return dtDateTime; 116 | } 117 | 118 | // Chop off input string's end so it becomes maxLength long 119 | public static string Truncate(string value, int maxLength) 120 | { 121 | if (string.IsNullOrEmpty(value)) return value; 122 | return value.Length <= maxLength ? value : value.Substring(0, maxLength); 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/AboutForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MHTG 2 | { 3 | partial class AboutForm 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.AboutPictureBox = new System.Windows.Forms.PictureBox(); 32 | this.AboutTitleLabel = new System.Windows.Forms.Label(); 33 | this.AboutDescriptionLabel = new System.Windows.Forms.Label(); 34 | this.VersionLabel = new System.Windows.Forms.Label(); 35 | ((System.ComponentModel.ISupportInitialize)(this.AboutPictureBox)).BeginInit(); 36 | this.SuspendLayout(); 37 | // 38 | // AboutPictureBox 39 | // 40 | this.AboutPictureBox.BackgroundImage = global::MHTG.Properties.Resources.green_icon; 41 | this.AboutPictureBox.BackgroundImageLayout = System.Windows.Forms.ImageLayout.None; 42 | this.AboutPictureBox.InitialImage = global::MHTG.Properties.Resources.green_icon; 43 | this.AboutPictureBox.Location = new System.Drawing.Point(5, 5); 44 | this.AboutPictureBox.Name = "AboutPictureBox"; 45 | this.AboutPictureBox.Size = new System.Drawing.Size(128, 128); 46 | this.AboutPictureBox.TabIndex = 0; 47 | this.AboutPictureBox.TabStop = false; 48 | // 49 | // AboutTitleLabel 50 | // 51 | this.AboutTitleLabel.AutoSize = true; 52 | this.AboutTitleLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point, ((byte)(238))); 53 | this.AboutTitleLabel.Location = new System.Drawing.Point(139, 8); 54 | this.AboutTitleLabel.Name = "AboutTitleLabel"; 55 | this.AboutTitleLabel.Size = new System.Drawing.Size(286, 20); 56 | this.AboutTitleLabel.TabIndex = 1; 57 | this.AboutTitleLabel.Text = "Modular Hydroponic Tower Garden"; 58 | // 59 | // AboutDescriptionLabel 60 | // 61 | this.AboutDescriptionLabel.AutoSize = true; 62 | this.AboutDescriptionLabel.Location = new System.Drawing.Point(140, 35); 63 | this.AboutDescriptionLabel.Name = "AboutDescriptionLabel"; 64 | this.AboutDescriptionLabel.Size = new System.Drawing.Size(278, 13); 65 | this.AboutDescriptionLabel.TabIndex = 2; 66 | this.AboutDescriptionLabel.Text = "A user interface to interact with the water pump controller."; 67 | // 68 | // VersionLabel 69 | // 70 | this.VersionLabel.AutoSize = true; 71 | this.VersionLabel.Location = new System.Drawing.Point(296, 120); 72 | this.VersionLabel.Name = "VersionLabel"; 73 | this.VersionLabel.Size = new System.Drawing.Size(134, 13); 74 | this.VersionLabel.TabIndex = 3; 75 | this.VersionLabel.Text = "Version: MHTG.2020.0012"; 76 | // 77 | // AboutForm 78 | // 79 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 80 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 81 | this.ClientSize = new System.Drawing.Size(434, 141); 82 | this.Controls.Add(this.VersionLabel); 83 | this.Controls.Add(this.AboutDescriptionLabel); 84 | this.Controls.Add(this.AboutTitleLabel); 85 | this.Controls.Add(this.AboutPictureBox); 86 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 87 | this.MaximizeBox = false; 88 | this.MinimizeBox = false; 89 | this.Name = "AboutForm"; 90 | this.Text = "About"; 91 | this.FormClosed += new System.Windows.Forms.FormClosedEventHandler(this.AboutForm_FormClosed); 92 | ((System.ComponentModel.ISupportInitialize)(this.AboutPictureBox)).EndInit(); 93 | this.ResumeLayout(false); 94 | this.PerformLayout(); 95 | 96 | } 97 | 98 | #endregion 99 | 100 | private System.Windows.Forms.PictureBox AboutPictureBox; 101 | private System.Windows.Forms.Label AboutTitleLabel; 102 | private System.Windows.Forms.Label AboutDescriptionLabel; 103 | private System.Windows.Forms.Label VersionLabel; 104 | } 105 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ModularHydroponicTowerGarden 2 | A modular 3D-printable hydroponic tower garden with Arduino compatible water pump controller. 3 | 4 | [Get one or two on Tindie!](https://www.tindie.com/products/19512/) 5 | 6 | **3DPrint** ~~folder contains the STL-files to print tower parts.~~ [STL-files are hosted on Thingiverse](https://www.thingiverse.com/thing:3405964) 7 | 8 | **Arduino** folder contains the source code and compiled binaries for the water pump controller written in Arduino IDE 1.8.12. 9 | 10 | **GUI** folder contains the C# source code and compiled Windows binary for an external computer written in Visual Studio 2019. 11 | 12 | **PCB** folder contains everything to manufacture and assemble the hardware. You can buy it on [Tindie.](https://www.tindie.com/products/19512/) 13 | 14 | ## Command Line Interface (CLI) 15 | 16 | The water pump controller can be interfaced with the Arduino Serial Monitor, or any other Serial Monitor application, regardless of OS platform, to change its settings via text-based commands. The current GUI works on Windows OS only. 17 | 18 | By default the Serial Monitor shows regular status updates: 19 | 20 | ``` 21 | ... 22 | -----------STATUS----------- 23 | Service mode : disabled 24 | Water pump : on @ 80% 25 | Timer : 00:04:46 left 26 | Temperatures 27 | external : 0.0°C 28 | internal : 0.0°C 29 | ---------------------------- 30 | 31 | -----------STATUS----------- 32 | Service mode : disabled 33 | Water pump : on @ 80% 34 | Timer : 00:04:45 left 35 | Temperatures 36 | external : 0.0°C 37 | internal : 0.0°C 38 | ---------------------------- 39 | 40 | -----------STATUS----------- 41 | Service mode : disabled 42 | Water pump : on @ 80% 43 | Timer : 00:04:44 left 44 | Temperatures 45 | external : 0.0°C 46 | internal : 0.0°C 47 | ---------------------------- 48 | ... 49 | ``` 50 | 51 | To turn off regular status update display send this command: 52 | 53 | ``` 54 | status=0 55 | ``` 56 | 57 | The Serial Monitor reacts the following way to user inputs: 58 | 59 | ``` 60 | Input: status=0 61 | Command: status 62 | Value: 0 63 | Result: regular status updates disabled. 64 | ``` 65 | 66 | With the command "help" a complete description is printed to the Serial Monitor on the available commands: 67 | 68 | ``` 69 | ----------------------------------HELP--------------------------------- 70 | help : print this description. 71 | restart : restart controller. 72 | info : print controller information. 73 | servicemode=X : set service mode, 74 | X=0: disabled (default), 75 | X=1: enabled. 76 | status=X : print regular status updates, 77 | X=0: disabled, 78 | X=1: enabled (default). 79 | pumpspeed=X : set water pump speed in percents, 80 | X=0...100 [%]. 81 | ontime=X : set water pump on-time in minutes, 82 | X=0...71582 [min], 83 | when set to 0 the water pump is permanently turned off. 84 | offtime=X : set water pump off-time in minutes, 85 | X=0...71582 [min], 86 | when set to 0 the water pump is permanently turned on. 87 | tempsensors=X : set temperature sensor configuration, 88 | X=0: none (default), 89 | X=1: external, 90 | X=2: internal, 91 | X=3: both external and internal. 92 | tempunit=X : set temperature unit, 93 | X=1: Celsius (default), 94 | X=2: Fahrenheit, 95 | X=4: Kelvin. 96 | ntcbeta=X : set temperature sensor beta value in Kelvin, 97 | X=3950 [K] by default. 98 | pwmfreq=X : set water pump driver PWM frequency in Hertz, 99 | X=490 [Hz] by default. 100 | 101 | Note: settings are automatically saved to the internal EEPROM. 102 | ----------------------------------------------------------------------- 103 | ``` 104 | 105 | To display controller information send the "info" command: 106 | 107 | ``` 108 | --------------------INFO--------------------- 109 | AVR signature : 1E 95 0F (ATmega328P) 110 | Hardware version : V1.01 111 | Hardware date : 2019.11.18 18:07:36 112 | Assembly date : 2020.03.09 13:20:28 113 | Firmware date : 2021.04.20 14:02:43 114 | Service mode : disabled 115 | Water pump state : off 116 | Water pump on-time : 5 minute(s) 117 | Water pump off-time : 60 minute(s) 118 | Temperature sensor(s) : external and internal 119 | Temperature unit : Celsius 120 | NTC beta value : 3950 K 121 | PWM frequency : 490 Hz 122 | --------------------------------------------- 123 | ``` 124 | 125 | ## Pictures 126 | 127 | ![MHTG empty](https://thingiverse-production-new.s3.amazonaws.com/assets/93/72/7a/2c/28/IMG_20190417_153821_02.jpg) 128 | 129 | ![MHTG seedlings](https://thingiverse-production-new.s3.amazonaws.com/assets/ca/d1/2b/71/79/IMG_20190503_190128_02.jpg) 130 | 131 | ![MHTG halfway through](https://thingiverse-production-new.s3.amazonaws.com/assets/ac/ff/6e/5a/bc/IMG_20190516_110757.jpg) 132 | 133 | ![MHTG CAD drawing](https://thingiverse-production-new.s3.amazonaws.com/assets/13/7a/7d/81/b4/MHTG_V113_03.png) 134 | 135 | ![MHTG planting module](https://thingiverse-production-new.s3.amazonaws.com/assets/95/2c/2f/54/be/IMG_20190401_095039_02.jpg) 136 | 137 | ![V1.02 water pump controller](https://cdn.thingiverse.com/assets/e1/6c/c4/92/b1/IMG_20200112_160445_02.jpg) 138 | 139 | ![MHTG GUI 01](https://cdn.thingiverse.com/assets/28/b2/d1/ac/16/MHTG_GUI_01.png) -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/AboutForm.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 | -------------------------------------------------------------------------------- /PCB/V102/MHTG_V102/bom/MHTG_V102_BOM.html: -------------------------------------------------------------------------------- 1 | Partlist exported from Z:/Programok/_eagle-win64-7.4.0/eagle/MHTG_V102/MHTG_V102.sch at 2020.01.13. 14:42 2 |

3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 |
PartValueDevicePackageDescription
C115nFC-EU_12061206Ceramic capacitor (50V / X7R)
C20.1uFC_EU_12061206Ceramic capacitor (50V / X7R)
C3100uFC_POL-EU8X8X10.58X8X10.5SMD Aluminum Electrolytic Capacitor (50V)
C4100uFC_POL-EU8X8X10.58X8X10.5SMD Aluminum Electrolytic Capacitor (50V)
C50.1uFC-EU_12061206Ceramic capacitor (50V / X7R)
C60.1uFC-EU_12061206Ceramic capacitor (50V / X7R)
C70.1uFC-EU_12061206Ceramic capacitor (50V / X7R)
C80.1uFC-EU_12061206Ceramic capacitor (50V / X7R)
C91uFC-EU_12061206Ceramic capacitor (50V / X7R)
C100.1uFC-EU_12061206Ceramic capacitor (50V / X7R)
C110.1uFC-EU_12061206Ceramic capacitor (50V / X7R)
C120.1uFC-EU_12061206Ceramic capacitor (50V / X7R)
C1322pFC-EU_12061206Ceramic capacitor (50V / X7R)
C1422pFC-EU_12061206Ceramic capacitor (50V / X7R)
C150.1uFC-EU_12061206Ceramic capacitor (50V / X7R)
D1BZV55C16D-EU_12061206Zener diode (16V)
D2SS34D-EU_DO214ACDO-214ACSchottky diode
D3GREEND-EU_12061206LED
D4REDD-EU_12061206LED
D5SS34D-EU_DO214ACDO-214ACSchottky diode
D6SS34D-EU_DO214ACDO-214ACSchottky diode
D71N4148D-EU_DO214ACDO-214ACSmall signal diode
IC1LM2576S-5.0LM2576STO263-5LSwitching power supply IC
J1POWERTERMINAL_BLOCK_KF301-2PKF301-2PScrew terminal
J2WPUMPTERMINAL_BLOCK_KF301-2PKF301-2PScrew terminal
J3ISP-6PROGRAMMER_ISP-6_ISP-62x3P - ZL231-6PGAVR ISP-6 programmer
J4I2CCONNECTOR_4P_SHIELD_XH2.54XH2.54-4P-HORIZONTAL-SMDI2C LCD connector
L1100uHL-EU_DLG-0504DLG-0504Standard inductor
L210uHL-EU_12061206Inductor
MH1-MH_50X50MH/50X50Mounting holes (40x40mm)
N1NTD5865NLN-MOSFET_DPAKDPAKPower N-MOSFET
P1IRFR5305P-MOSFET_DPAKDPAKPower P-MOSFET
Q116MHzCRYSTAL_OSCILLATOR_HC49/SMDHC49/SMDCrystal oscillator
R110kR-EU_12061206Resistor 1% (1/4W)
R227kR-EU_12061206Resistor 1% (1/4W)
R31kR-EU_12061206Resistor 1% (1/4W)
R410kR-EU_12061206Resistor 1% (1/4W)
R52kR-EU_12061206Resistor 1% (1/4W)
R610kR-EU_12061206Resistor 1% (1/4W)
R722kR-EU_12061206Resistor 1% (1/4W)
R847kR-EU_12061206Resistor 1% (1/4W)
R910kR-EU_12061206Resistor 1% (1/4W)
R1010kR-EU_12061206Resistor 1% (1/4W)
R1110kR-EU_12061206Resistor 1% (1/4W)
R1210kR-EU_12061206Resistor 1% (1/4W)
R134k7R-EU_12061206Resistor 1% (1/4W)
R144k7R-EU_12061206Resistor 1% (1/4W)
S1RESETTACTILE-SWITCH-7914G7914GTactile switch 4 pins, SPST
S2MODETACTILE-SWITCH-7914G7914GTactile switch 4 pins, SPST
T1EXTCONNECTOR_2P_SHIELD_XH2.54XH2.54-2P-HORIZONTAL-SMDExternal temperature sensor (10k thermistor) connector
T2INTCONNECTOR_2P_SHIELD_XH2.54XH2.54-2P-HORIZONTAL-SMDInternal temperature sensor (10k thermistor) connector
U1ATMEGA328P-AUATMEGA328P-16AUTQFP-32AVR microcontroller
U2CP2102CP210X_USB_UARTMLP28Single-chip USB to UART bridge
U3USBLC6-2SC6USBLC6-2SC6_SOT23-6SOT23-6USB ESD protection
USB-USB_MICROB_PLUGCONNUSB-B-MICRO-THTUSB Micro-B connector / https://www.ebay.com/itm/152798942373
61 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/MainForm.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 | 17, 17 122 | 123 | 124 | 48 125 | 126 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/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\green_icon.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 123 | 124 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU General Public License is a free, copyleft license for 11 | software and other kinds of works. 12 | 13 | The licenses for most software and other practical works are designed 14 | to take away your freedom to share and change the works. By contrast, 15 | the GNU General Public License is intended to guarantee your freedom to 16 | share and change all versions of a program--to make sure it remains free 17 | software for all its users. We, the Free Software Foundation, use the 18 | GNU General Public License for most of our software; it applies also to 19 | any other work released this way by its authors. You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not 23 | price. Our General Public Licenses are designed to make sure that you 24 | have the freedom to distribute copies of free software (and charge for 25 | them if you wish), that you receive source code or can get it if you 26 | want it, that you can change the software or use pieces of it in new 27 | free programs, and that you know you can do these things. 28 | 29 | To protect your rights, we need to prevent others from denying you 30 | these rights or asking you to surrender the rights. Therefore, you have 31 | certain responsibilities if you distribute copies of the software, or if 32 | you modify it: responsibilities to respect the freedom of others. 33 | 34 | For example, if you distribute copies of such a program, whether 35 | gratis or for a fee, you must pass on to the recipients the same 36 | freedoms that you received. You must make sure that they, too, receive 37 | or can get the source code. And you must show them these terms so they 38 | know their rights. 39 | 40 | Developers that use the GNU GPL protect your rights with two steps: 41 | (1) assert copyright on the software, and (2) offer you this License 42 | giving you legal permission to copy, distribute and/or modify it. 43 | 44 | For the developers' and authors' protection, the GPL clearly explains 45 | that there is no warranty for this free software. For both users' and 46 | authors' sake, the GPL requires that modified versions be marked as 47 | changed, so that their problems will not be attributed erroneously to 48 | authors of previous versions. 49 | 50 | Some devices are designed to deny users access to install or run 51 | modified versions of the software inside them, although the manufacturer 52 | can do so. This is fundamentally incompatible with the aim of 53 | protecting users' freedom to change the software. The systematic 54 | pattern of such abuse occurs in the area of products for individuals to 55 | use, which is precisely where it is most unacceptable. Therefore, we 56 | have designed this version of the GPL to prohibit the practice for those 57 | products. If such problems arise substantially in other domains, we 58 | stand ready to extend this provision to those domains in future versions 59 | of the GPL, as needed to protect the freedom of users. 60 | 61 | Finally, every program is threatened constantly by software patents. 62 | States should not allow patents to restrict development and use of 63 | software on general-purpose computers, but in those that do, we wish to 64 | avoid the special danger that patents applied to a free program could 65 | make it effectively proprietary. To prevent this, the GPL assures that 66 | patents cannot be used to render the program non-free. 67 | 68 | The precise terms and conditions for copying, distribution and 69 | modification follow. 70 | 71 | TERMS AND CONDITIONS 72 | 73 | 0. Definitions. 74 | 75 | "This License" refers to version 3 of the GNU General Public License. 76 | 77 | "Copyright" also means copyright-like laws that apply to other kinds of 78 | works, such as semiconductor masks. 79 | 80 | "The Program" refers to any copyrightable work licensed under this 81 | License. Each licensee is addressed as "you". "Licensees" and 82 | "recipients" may be individuals or organizations. 83 | 84 | To "modify" a work means to copy from or adapt all or part of the work 85 | in a fashion requiring copyright permission, other than the making of an 86 | exact copy. The resulting work is called a "modified version" of the 87 | earlier work or a work "based on" the earlier work. 88 | 89 | A "covered work" means either the unmodified Program or a work based 90 | on the Program. 91 | 92 | To "propagate" a work means to do anything with it that, without 93 | permission, would make you directly or secondarily liable for 94 | infringement under applicable copyright law, except executing it on a 95 | computer or modifying a private copy. Propagation includes copying, 96 | distribution (with or without modification), making available to the 97 | public, and in some countries other activities as well. 98 | 99 | To "convey" a work means any kind of propagation that enables other 100 | parties to make or receive copies. Mere interaction with a user through 101 | a computer network, with no transfer of a copy, is not conveying. 102 | 103 | An interactive user interface displays "Appropriate Legal Notices" 104 | to the extent that it includes a convenient and prominently visible 105 | feature that (1) displays an appropriate copyright notice, and (2) 106 | tells the user that there is no warranty for the work (except to the 107 | extent that warranties are provided), that licensees may convey the 108 | work under this License, and how to view a copy of this License. If 109 | the interface presents a list of user commands or options, such as a 110 | menu, a prominent item in the list meets this criterion. 111 | 112 | 1. Source Code. 113 | 114 | The "source code" for a work means the preferred form of the work 115 | for making modifications to it. "Object code" means any non-source 116 | form of a work. 117 | 118 | A "Standard Interface" means an interface that either is an official 119 | standard defined by a recognized standards body, or, in the case of 120 | interfaces specified for a particular programming language, one that 121 | is widely used among developers working in that language. 122 | 123 | The "System Libraries" of an executable work include anything, other 124 | than the work as a whole, that (a) is included in the normal form of 125 | packaging a Major Component, but which is not part of that Major 126 | Component, and (b) serves only to enable use of the work with that 127 | Major Component, or to implement a Standard Interface for which an 128 | implementation is available to the public in source code form. A 129 | "Major Component", in this context, means a major essential component 130 | (kernel, window system, and so on) of the specific operating system 131 | (if any) on which the executable work runs, or a compiler used to 132 | produce the work, or an object code interpreter used to run it. 133 | 134 | The "Corresponding Source" for a work in object code form means all 135 | the source code needed to generate, install, and (for an executable 136 | work) run the object code and to modify the work, including scripts to 137 | control those activities. However, it does not include the work's 138 | System Libraries, or general-purpose tools or generally available free 139 | programs which are used unmodified in performing those activities but 140 | which are not part of the work. For example, Corresponding Source 141 | includes interface definition files associated with source files for 142 | the work, and the source code for shared libraries and dynamically 143 | linked subprograms that the work is specifically designed to require, 144 | such as by intimate data communication or control flow between those 145 | subprograms and other parts of the work. 146 | 147 | The Corresponding Source need not include anything that users 148 | can regenerate automatically from other parts of the Corresponding 149 | Source. 150 | 151 | The Corresponding Source for a work in source code form is that 152 | same work. 153 | 154 | 2. Basic Permissions. 155 | 156 | All rights granted under this License are granted for the term of 157 | copyright on the Program, and are irrevocable provided the stated 158 | conditions are met. This License explicitly affirms your unlimited 159 | permission to run the unmodified Program. The output from running a 160 | covered work is covered by this License only if the output, given its 161 | content, constitutes a covered work. This License acknowledges your 162 | rights of fair use or other equivalent, as provided by copyright law. 163 | 164 | You may make, run and propagate covered works that you do not 165 | convey, without conditions so long as your license otherwise remains 166 | in force. You may convey covered works to others for the sole purpose 167 | of having them make modifications exclusively for you, or provide you 168 | with facilities for running those works, provided that you comply with 169 | the terms of this License in conveying all material for which you do 170 | not control copyright. Those thus making or running the covered works 171 | for you must do so exclusively on your behalf, under your direction 172 | and control, on terms that prohibit them from making any copies of 173 | your copyrighted material outside their relationship with you. 174 | 175 | Conveying under any other circumstances is permitted solely under 176 | the conditions stated below. Sublicensing is not allowed; section 10 177 | makes it unnecessary. 178 | 179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 180 | 181 | No covered work shall be deemed part of an effective technological 182 | measure under any applicable law fulfilling obligations under article 183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 184 | similar laws prohibiting or restricting circumvention of such 185 | measures. 186 | 187 | When you convey a covered work, you waive any legal power to forbid 188 | circumvention of technological measures to the extent such circumvention 189 | is effected by exercising rights under this License with respect to 190 | the covered work, and you disclaim any intention to limit operation or 191 | modification of the work as a means of enforcing, against the work's 192 | users, your or third parties' legal rights to forbid circumvention of 193 | technological measures. 194 | 195 | 4. Conveying Verbatim Copies. 196 | 197 | You may convey verbatim copies of the Program's source code as you 198 | receive it, in any medium, provided that you conspicuously and 199 | appropriately publish on each copy an appropriate copyright notice; 200 | keep intact all notices stating that this License and any 201 | non-permissive terms added in accord with section 7 apply to the code; 202 | keep intact all notices of the absence of any warranty; and give all 203 | recipients a copy of this License along with the Program. 204 | 205 | You may charge any price or no price for each copy that you convey, 206 | and you may offer support or warranty protection for a fee. 207 | 208 | 5. Conveying Modified Source Versions. 209 | 210 | You may convey a work based on the Program, or the modifications to 211 | produce it from the Program, in the form of source code under the 212 | terms of section 4, provided that you also meet all of these conditions: 213 | 214 | a) The work must carry prominent notices stating that you modified 215 | it, and giving a relevant date. 216 | 217 | b) The work must carry prominent notices stating that it is 218 | released under this License and any conditions added under section 219 | 7. This requirement modifies the requirement in section 4 to 220 | "keep intact all notices". 221 | 222 | c) You must license the entire work, as a whole, under this 223 | License to anyone who comes into possession of a copy. This 224 | License will therefore apply, along with any applicable section 7 225 | additional terms, to the whole of the work, and all its parts, 226 | regardless of how they are packaged. This License gives no 227 | permission to license the work in any other way, but it does not 228 | invalidate such permission if you have separately received it. 229 | 230 | d) If the work has interactive user interfaces, each must display 231 | Appropriate Legal Notices; however, if the Program has interactive 232 | interfaces that do not display Appropriate Legal Notices, your 233 | work need not make them do so. 234 | 235 | A compilation of a covered work with other separate and independent 236 | works, which are not by their nature extensions of the covered work, 237 | and which are not combined with it such as to form a larger program, 238 | in or on a volume of a storage or distribution medium, is called an 239 | "aggregate" if the compilation and its resulting copyright are not 240 | used to limit the access or legal rights of the compilation's users 241 | beyond what the individual works permit. Inclusion of a covered work 242 | in an aggregate does not cause this License to apply to the other 243 | parts of the aggregate. 244 | 245 | 6. Conveying Non-Source Forms. 246 | 247 | You may convey a covered work in object code form under the terms 248 | of sections 4 and 5, provided that you also convey the 249 | machine-readable Corresponding Source under the terms of this License, 250 | in one of these ways: 251 | 252 | a) Convey the object code in, or embodied in, a physical product 253 | (including a physical distribution medium), accompanied by the 254 | Corresponding Source fixed on a durable physical medium 255 | customarily used for software interchange. 256 | 257 | b) Convey the object code in, or embodied in, a physical product 258 | (including a physical distribution medium), accompanied by a 259 | written offer, valid for at least three years and valid for as 260 | long as you offer spare parts or customer support for that product 261 | model, to give anyone who possesses the object code either (1) a 262 | copy of the Corresponding Source for all the software in the 263 | product that is covered by this License, on a durable physical 264 | medium customarily used for software interchange, for a price no 265 | more than your reasonable cost of physically performing this 266 | conveying of source, or (2) access to copy the 267 | Corresponding Source from a network server at no charge. 268 | 269 | c) Convey individual copies of the object code with a copy of the 270 | written offer to provide the Corresponding Source. This 271 | alternative is allowed only occasionally and noncommercially, and 272 | only if you received the object code with such an offer, in accord 273 | with subsection 6b. 274 | 275 | d) Convey the object code by offering access from a designated 276 | place (gratis or for a charge), and offer equivalent access to the 277 | Corresponding Source in the same way through the same place at no 278 | further charge. You need not require recipients to copy the 279 | Corresponding Source along with the object code. If the place to 280 | copy the object code is a network server, the Corresponding Source 281 | may be on a different server (operated by you or a third party) 282 | that supports equivalent copying facilities, provided you maintain 283 | clear directions next to the object code saying where to find the 284 | Corresponding Source. Regardless of what server hosts the 285 | Corresponding Source, you remain obligated to ensure that it is 286 | available for as long as needed to satisfy these requirements. 287 | 288 | e) Convey the object code using peer-to-peer transmission, provided 289 | you inform other peers where the object code and Corresponding 290 | Source of the work are being offered to the general public at no 291 | charge under subsection 6d. 292 | 293 | A separable portion of the object code, whose source code is excluded 294 | from the Corresponding Source as a System Library, need not be 295 | included in conveying the object code work. 296 | 297 | A "User Product" is either (1) a "consumer product", which means any 298 | tangible personal property which is normally used for personal, family, 299 | or household purposes, or (2) anything designed or sold for incorporation 300 | into a dwelling. In determining whether a product is a consumer product, 301 | doubtful cases shall be resolved in favor of coverage. For a particular 302 | product received by a particular user, "normally used" refers to a 303 | typical or common use of that class of product, regardless of the status 304 | of the particular user or of the way in which the particular user 305 | actually uses, or expects or is expected to use, the product. A product 306 | is a consumer product regardless of whether the product has substantial 307 | commercial, industrial or non-consumer uses, unless such uses represent 308 | the only significant mode of use of the product. 309 | 310 | "Installation Information" for a User Product means any methods, 311 | procedures, authorization keys, or other information required to install 312 | and execute modified versions of a covered work in that User Product from 313 | a modified version of its Corresponding Source. The information must 314 | suffice to ensure that the continued functioning of the modified object 315 | code is in no case prevented or interfered with solely because 316 | modification has been made. 317 | 318 | If you convey an object code work under this section in, or with, or 319 | specifically for use in, a User Product, and the conveying occurs as 320 | part of a transaction in which the right of possession and use of the 321 | User Product is transferred to the recipient in perpetuity or for a 322 | fixed term (regardless of how the transaction is characterized), the 323 | Corresponding Source conveyed under this section must be accompanied 324 | by the Installation Information. But this requirement does not apply 325 | if neither you nor any third party retains the ability to install 326 | modified object code on the User Product (for example, the work has 327 | been installed in ROM). 328 | 329 | The requirement to provide Installation Information does not include a 330 | requirement to continue to provide support service, warranty, or updates 331 | for a work that has been modified or installed by the recipient, or for 332 | the User Product in which it has been modified or installed. Access to a 333 | network may be denied when the modification itself materially and 334 | adversely affects the operation of the network or violates the rules and 335 | protocols for communication across the network. 336 | 337 | Corresponding Source conveyed, and Installation Information provided, 338 | in accord with this section must be in a format that is publicly 339 | documented (and with an implementation available to the public in 340 | source code form), and must require no special password or key for 341 | unpacking, reading or copying. 342 | 343 | 7. Additional Terms. 344 | 345 | "Additional permissions" are terms that supplement the terms of this 346 | License by making exceptions from one or more of its conditions. 347 | Additional permissions that are applicable to the entire Program shall 348 | be treated as though they were included in this License, to the extent 349 | that they are valid under applicable law. If additional permissions 350 | apply only to part of the Program, that part may be used separately 351 | under those permissions, but the entire Program remains governed by 352 | this License without regard to the additional permissions. 353 | 354 | When you convey a copy of a covered work, you may at your option 355 | remove any additional permissions from that copy, or from any part of 356 | it. (Additional permissions may be written to require their own 357 | removal in certain cases when you modify the work.) You may place 358 | additional permissions on material, added by you to a covered work, 359 | for which you have or can give appropriate copyright permission. 360 | 361 | Notwithstanding any other provision of this License, for material you 362 | add to a covered work, you may (if authorized by the copyright holders of 363 | that material) supplement the terms of this License with terms: 364 | 365 | a) Disclaiming warranty or limiting liability differently from the 366 | terms of sections 15 and 16 of this License; or 367 | 368 | b) Requiring preservation of specified reasonable legal notices or 369 | author attributions in that material or in the Appropriate Legal 370 | Notices displayed by works containing it; or 371 | 372 | c) Prohibiting misrepresentation of the origin of that material, or 373 | requiring that modified versions of such material be marked in 374 | reasonable ways as different from the original version; or 375 | 376 | d) Limiting the use for publicity purposes of names of licensors or 377 | authors of the material; or 378 | 379 | e) Declining to grant rights under trademark law for use of some 380 | trade names, trademarks, or service marks; or 381 | 382 | f) Requiring indemnification of licensors and authors of that 383 | material by anyone who conveys the material (or modified versions of 384 | it) with contractual assumptions of liability to the recipient, for 385 | any liability that these contractual assumptions directly impose on 386 | those licensors and authors. 387 | 388 | All other non-permissive additional terms are considered "further 389 | restrictions" within the meaning of section 10. If the Program as you 390 | received it, or any part of it, contains a notice stating that it is 391 | governed by this License along with a term that is a further 392 | restriction, you may remove that term. If a license document contains 393 | a further restriction but permits relicensing or conveying under this 394 | License, you may add to a covered work material governed by the terms 395 | of that license document, provided that the further restriction does 396 | not survive such relicensing or conveying. 397 | 398 | If you add terms to a covered work in accord with this section, you 399 | must place, in the relevant source files, a statement of the 400 | additional terms that apply to those files, or a notice indicating 401 | where to find the applicable terms. 402 | 403 | Additional terms, permissive or non-permissive, may be stated in the 404 | form of a separately written license, or stated as exceptions; 405 | the above requirements apply either way. 406 | 407 | 8. Termination. 408 | 409 | You may not propagate or modify a covered work except as expressly 410 | provided under this License. Any attempt otherwise to propagate or 411 | modify it is void, and will automatically terminate your rights under 412 | this License (including any patent licenses granted under the third 413 | paragraph of section 11). 414 | 415 | However, if you cease all violation of this License, then your 416 | license from a particular copyright holder is reinstated (a) 417 | provisionally, unless and until the copyright holder explicitly and 418 | finally terminates your license, and (b) permanently, if the copyright 419 | holder fails to notify you of the violation by some reasonable means 420 | prior to 60 days after the cessation. 421 | 422 | Moreover, your license from a particular copyright holder is 423 | reinstated permanently if the copyright holder notifies you of the 424 | violation by some reasonable means, this is the first time you have 425 | received notice of violation of this License (for any work) from that 426 | copyright holder, and you cure the violation prior to 30 days after 427 | your receipt of the notice. 428 | 429 | Termination of your rights under this section does not terminate the 430 | licenses of parties who have received copies or rights from you under 431 | this License. If your rights have been terminated and not permanently 432 | reinstated, you do not qualify to receive new licenses for the same 433 | material under section 10. 434 | 435 | 9. Acceptance Not Required for Having Copies. 436 | 437 | You are not required to accept this License in order to receive or 438 | run a copy of the Program. Ancillary propagation of a covered work 439 | occurring solely as a consequence of using peer-to-peer transmission 440 | to receive a copy likewise does not require acceptance. However, 441 | nothing other than this License grants you permission to propagate or 442 | modify any covered work. These actions infringe copyright if you do 443 | not accept this License. Therefore, by modifying or propagating a 444 | covered work, you indicate your acceptance of this License to do so. 445 | 446 | 10. Automatic Licensing of Downstream Recipients. 447 | 448 | Each time you convey a covered work, the recipient automatically 449 | receives a license from the original licensors, to run, modify and 450 | propagate that work, subject to this License. You are not responsible 451 | for enforcing compliance by third parties with this License. 452 | 453 | An "entity transaction" is a transaction transferring control of an 454 | organization, or substantially all assets of one, or subdividing an 455 | organization, or merging organizations. If propagation of a covered 456 | work results from an entity transaction, each party to that 457 | transaction who receives a copy of the work also receives whatever 458 | licenses to the work the party's predecessor in interest had or could 459 | give under the previous paragraph, plus a right to possession of the 460 | Corresponding Source of the work from the predecessor in interest, if 461 | the predecessor has it or can get it with reasonable efforts. 462 | 463 | You may not impose any further restrictions on the exercise of the 464 | rights granted or affirmed under this License. For example, you may 465 | not impose a license fee, royalty, or other charge for exercise of 466 | rights granted under this License, and you may not initiate litigation 467 | (including a cross-claim or counterclaim in a lawsuit) alleging that 468 | any patent claim is infringed by making, using, selling, offering for 469 | sale, or importing the Program or any portion of it. 470 | 471 | 11. Patents. 472 | 473 | A "contributor" is a copyright holder who authorizes use under this 474 | License of the Program or a work on which the Program is based. The 475 | work thus licensed is called the contributor's "contributor version". 476 | 477 | A contributor's "essential patent claims" are all patent claims 478 | owned or controlled by the contributor, whether already acquired or 479 | hereafter acquired, that would be infringed by some manner, permitted 480 | by this License, of making, using, or selling its contributor version, 481 | but do not include claims that would be infringed only as a 482 | consequence of further modification of the contributor version. For 483 | purposes of this definition, "control" includes the right to grant 484 | patent sublicenses in a manner consistent with the requirements of 485 | this License. 486 | 487 | Each contributor grants you a non-exclusive, worldwide, royalty-free 488 | patent license under the contributor's essential patent claims, to 489 | make, use, sell, offer for sale, import and otherwise run, modify and 490 | propagate the contents of its contributor version. 491 | 492 | In the following three paragraphs, a "patent license" is any express 493 | agreement or commitment, however denominated, not to enforce a patent 494 | (such as an express permission to practice a patent or covenant not to 495 | sue for patent infringement). To "grant" such a patent license to a 496 | party means to make such an agreement or commitment not to enforce a 497 | patent against the party. 498 | 499 | If you convey a covered work, knowingly relying on a patent license, 500 | and the Corresponding Source of the work is not available for anyone 501 | to copy, free of charge and under the terms of this License, through a 502 | publicly available network server or other readily accessible means, 503 | then you must either (1) cause the Corresponding Source to be so 504 | available, or (2) arrange to deprive yourself of the benefit of the 505 | patent license for this particular work, or (3) arrange, in a manner 506 | consistent with the requirements of this License, to extend the patent 507 | license to downstream recipients. "Knowingly relying" means you have 508 | actual knowledge that, but for the patent license, your conveying the 509 | covered work in a country, or your recipient's use of the covered work 510 | in a country, would infringe one or more identifiable patents in that 511 | country that you have reason to believe are valid. 512 | 513 | If, pursuant to or in connection with a single transaction or 514 | arrangement, you convey, or propagate by procuring conveyance of, a 515 | covered work, and grant a patent license to some of the parties 516 | receiving the covered work authorizing them to use, propagate, modify 517 | or convey a specific copy of the covered work, then the patent license 518 | you grant is automatically extended to all recipients of the covered 519 | work and works based on it. 520 | 521 | A patent license is "discriminatory" if it does not include within 522 | the scope of its coverage, prohibits the exercise of, or is 523 | conditioned on the non-exercise of one or more of the rights that are 524 | specifically granted under this License. You may not convey a covered 525 | work if you are a party to an arrangement with a third party that is 526 | in the business of distributing software, under which you make payment 527 | to the third party based on the extent of your activity of conveying 528 | the work, and under which the third party grants, to any of the 529 | parties who would receive the covered work from you, a discriminatory 530 | patent license (a) in connection with copies of the covered work 531 | conveyed by you (or copies made from those copies), or (b) primarily 532 | for and in connection with specific products or compilations that 533 | contain the covered work, unless you entered into that arrangement, 534 | or that patent license was granted, prior to 28 March 2007. 535 | 536 | Nothing in this License shall be construed as excluding or limiting 537 | any implied license or other defenses to infringement that may 538 | otherwise be available to you under applicable patent law. 539 | 540 | 12. No Surrender of Others' Freedom. 541 | 542 | If conditions are imposed on you (whether by court order, agreement or 543 | otherwise) that contradict the conditions of this License, they do not 544 | excuse you from the conditions of this License. If you cannot convey a 545 | covered work so as to satisfy simultaneously your obligations under this 546 | License and any other pertinent obligations, then as a consequence you may 547 | not convey it at all. For example, if you agree to terms that obligate you 548 | to collect a royalty for further conveying from those to whom you convey 549 | the Program, the only way you could satisfy both those terms and this 550 | License would be to refrain entirely from conveying the Program. 551 | 552 | 13. Use with the GNU Affero General Public License. 553 | 554 | Notwithstanding any other provision of this License, you have 555 | permission to link or combine any covered work with a work licensed 556 | under version 3 of the GNU Affero General Public License into a single 557 | combined work, and to convey the resulting work. The terms of this 558 | License will continue to apply to the part which is the covered work, 559 | but the special requirements of the GNU Affero General Public License, 560 | section 13, concerning interaction through a network will apply to the 561 | combination as such. 562 | 563 | 14. Revised Versions of this License. 564 | 565 | The Free Software Foundation may publish revised and/or new versions of 566 | the GNU General Public License from time to time. Such new versions will 567 | be similar in spirit to the present version, but may differ in detail to 568 | address new problems or concerns. 569 | 570 | Each version is given a distinguishing version number. If the 571 | Program specifies that a certain numbered version of the GNU General 572 | Public License "or any later version" applies to it, you have the 573 | option of following the terms and conditions either of that numbered 574 | version or of any later version published by the Free Software 575 | Foundation. If the Program does not specify a version number of the 576 | GNU General Public License, you may choose any version ever published 577 | by the Free Software Foundation. 578 | 579 | If the Program specifies that a proxy can decide which future 580 | versions of the GNU General Public License can be used, that proxy's 581 | public statement of acceptance of a version permanently authorizes you 582 | to choose that version for the Program. 583 | 584 | Later license versions may give you additional or different 585 | permissions. However, no additional obligations are imposed on any 586 | author or copyright holder as a result of your choosing to follow a 587 | later version. 588 | 589 | 15. Disclaimer of Warranty. 590 | 591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 599 | 600 | 16. Limitation of Liability. 601 | 602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 610 | SUCH DAMAGES. 611 | 612 | 17. Interpretation of Sections 15 and 16. 613 | 614 | If the disclaimer of warranty and limitation of liability provided 615 | above cannot be given local legal effect according to their terms, 616 | reviewing courts shall apply local law that most closely approximates 617 | an absolute waiver of all civil liability in connection with the 618 | Program, unless a warranty or assumption of liability accompanies a 619 | copy of the Program in return for a fee. 620 | 621 | END OF TERMS AND CONDITIONS 622 | 623 | How to Apply These Terms to Your New Programs 624 | 625 | If you develop a new program, and you want it to be of the greatest 626 | possible use to the public, the best way to achieve this is to make it 627 | free software which everyone can redistribute and change under these terms. 628 | 629 | To do so, attach the following notices to the program. It is safest 630 | to attach them to the start of each source file to most effectively 631 | state the exclusion of warranty; and each file should have at least 632 | the "copyright" line and a pointer to where the full notice is found. 633 | 634 | 635 | Copyright (C) 636 | 637 | This program is free software: you can redistribute it and/or modify 638 | it under the terms of the GNU General Public License as published by 639 | the Free Software Foundation, either version 3 of the License, or 640 | (at your option) any later version. 641 | 642 | This program is distributed in the hope that it will be useful, 643 | but WITHOUT ANY WARRANTY; without even the implied warranty of 644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 645 | GNU General Public License for more details. 646 | 647 | You should have received a copy of the GNU General Public License 648 | along with this program. If not, see . 649 | 650 | Also add information on how to contact you by electronic and paper mail. 651 | 652 | If the program does terminal interaction, make it output a short 653 | notice like this when it starts in an interactive mode: 654 | 655 | Copyright (C) 656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 657 | This is free software, and you are welcome to redistribute it 658 | under certain conditions; type `show c' for details. 659 | 660 | The hypothetical commands `show w' and `show c' should show the appropriate 661 | parts of the General Public License. Of course, your program's commands 662 | might be different; for a GUI interface, you would use an "about box". 663 | 664 | You should also get your employer (if you work as a programmer) or school, 665 | if any, to sign a "copyright disclaimer" for the program, if necessary. 666 | For more information on this, and how to apply and follow the GNU GPL, see 667 | . 668 | 669 | The GNU General Public License does not permit incorporating your program 670 | into proprietary programs. If your program is a subroutine library, you 671 | may consider it more useful to permit linking proprietary applications with 672 | the library. If this is what you want to do, use the GNU Lesser General 673 | Public License instead of this License. But first, please read 674 | . 675 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/MainForm.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace MHTG 2 | { 3 | partial class MainForm 4 | { 5 | ///

6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.MenuStrip = new System.Windows.Forms.MenuStrip(); 32 | this.ToolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 33 | this.UpdateFirmwareToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 34 | this.ShowDebugToolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 35 | this.ServiceModeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 36 | this.AboutToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); 37 | this.CommunicationGroupBox = new System.Windows.Forms.GroupBox(); 38 | this.SendButton = new System.Windows.Forms.Button(); 39 | this.SendComboBox = new System.Windows.Forms.ComboBox(); 40 | this.CommunicationTextBox = new System.Windows.Forms.TextBox(); 41 | this.SettingsGroupBox = new System.Windows.Forms.GroupBox(); 42 | this.NTCBetaValueModifyCheckBox = new System.Windows.Forms.CheckBox(); 43 | this.NTCBetaKelvinLabel = new System.Windows.Forms.Label(); 44 | this.NTCBetaValueLabel = new System.Windows.Forms.Label(); 45 | this.NTCBetaValueTextBox = new System.Windows.Forms.TextBox(); 46 | this.WaterPumpLabel = new System.Windows.Forms.Label(); 47 | this.DefaultSettingsButton = new System.Windows.Forms.Button(); 48 | this.WriteSettingsButton = new System.Windows.Forms.Button(); 49 | this.ReadSettingsButton = new System.Windows.Forms.Button(); 50 | this.TemperatureSensorsComboBox = new System.Windows.Forms.ComboBox(); 51 | this.TemperatureSensorsLabel = new System.Windows.Forms.Label(); 52 | this.TemperatureUnitComboBox = new System.Windows.Forms.ComboBox(); 53 | this.TemperatureUnitLabel = new System.Windows.Forms.Label(); 54 | this.MinutesLabel02 = new System.Windows.Forms.Label(); 55 | this.WaterPumpOffTimeLabel = new System.Windows.Forms.Label(); 56 | this.WaterPumpOffTimeTextBox = new System.Windows.Forms.TextBox(); 57 | this.MinutesLabel01 = new System.Windows.Forms.Label(); 58 | this.WaterPumpOnTimeTextBox = new System.Windows.Forms.TextBox(); 59 | this.WaterPumpOnTimeLabel = new System.Windows.Forms.Label(); 60 | this.WaterPumpSpeedPercentageLabel = new System.Windows.Forms.Label(); 61 | this.WaterPumpSpeedLabel = new System.Windows.Forms.Label(); 62 | this.WaterPumpSpeedTrackBar = new System.Windows.Forms.TrackBar(); 63 | this.ExternalTemperatureTextBox = new System.Windows.Forms.TextBox(); 64 | this.InternalTemperatureTextBox = new System.Windows.Forms.TextBox(); 65 | this.ControlGroupBox = new System.Windows.Forms.GroupBox(); 66 | this.StatusButton = new System.Windows.Forms.Button(); 67 | this.ResetButton = new System.Windows.Forms.Button(); 68 | this.RefreshButton = new System.Windows.Forms.Button(); 69 | this.COMPortsComboBox = new System.Windows.Forms.ComboBox(); 70 | this.ConnectButton = new System.Windows.Forms.Button(); 71 | this.ExternalTemperatureLabel = new System.Windows.Forms.Label(); 72 | this.InternalTemperatureLabel = new System.Windows.Forms.Label(); 73 | this.TemperatureUnitLabel01 = new System.Windows.Forms.Label(); 74 | this.TemperatureUnitLabel02 = new System.Windows.Forms.Label(); 75 | this.TemperaturesGroupBox = new System.Windows.Forms.GroupBox(); 76 | this.DebugGroupBox = new System.Windows.Forms.GroupBox(); 77 | this.WaterPumpOnOffCheckBox = new System.Windows.Forms.CheckBox(); 78 | this.GetStatusUpdatesCheckBox = new System.Windows.Forms.CheckBox(); 79 | this.PWMFrequencyLabel = new System.Windows.Forms.Label(); 80 | this.PWMFrequencyApplyButton = new System.Windows.Forms.Button(); 81 | this.PWMFrequencyTextBox = new System.Windows.Forms.TextBox(); 82 | this.HzLabel = new System.Windows.Forms.Label(); 83 | this.MenuStrip.SuspendLayout(); 84 | this.CommunicationGroupBox.SuspendLayout(); 85 | this.SettingsGroupBox.SuspendLayout(); 86 | ((System.ComponentModel.ISupportInitialize)(this.WaterPumpSpeedTrackBar)).BeginInit(); 87 | this.ControlGroupBox.SuspendLayout(); 88 | this.TemperaturesGroupBox.SuspendLayout(); 89 | this.DebugGroupBox.SuspendLayout(); 90 | this.SuspendLayout(); 91 | // 92 | // MenuStrip 93 | // 94 | this.MenuStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { 95 | this.ToolsToolStripMenuItem, 96 | this.AboutToolStripMenuItem}); 97 | this.MenuStrip.Location = new System.Drawing.Point(0, 0); 98 | this.MenuStrip.Name = "MenuStrip"; 99 | this.MenuStrip.Size = new System.Drawing.Size(764, 24); 100 | this.MenuStrip.TabIndex = 0; 101 | this.MenuStrip.Text = "menuStrip1"; 102 | // 103 | // ToolsToolStripMenuItem 104 | // 105 | this.ToolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { 106 | this.UpdateFirmwareToolStripMenuItem, 107 | this.ShowDebugToolsToolStripMenuItem, 108 | this.ServiceModeToolStripMenuItem}); 109 | this.ToolsToolStripMenuItem.Name = "ToolsToolStripMenuItem"; 110 | this.ToolsToolStripMenuItem.Size = new System.Drawing.Size(46, 20); 111 | this.ToolsToolStripMenuItem.Text = "Tools"; 112 | // 113 | // UpdateFirmwareToolStripMenuItem 114 | // 115 | this.UpdateFirmwareToolStripMenuItem.Enabled = false; 116 | this.UpdateFirmwareToolStripMenuItem.Name = "UpdateFirmwareToolStripMenuItem"; 117 | this.UpdateFirmwareToolStripMenuItem.Size = new System.Drawing.Size(169, 22); 118 | this.UpdateFirmwareToolStripMenuItem.Text = "Update firmware"; 119 | this.UpdateFirmwareToolStripMenuItem.Click += new System.EventHandler(this.UpdateFirmwareToolStripMenuItem_Click); 120 | // 121 | // ShowDebugToolsToolStripMenuItem 122 | // 123 | this.ShowDebugToolsToolStripMenuItem.CheckOnClick = true; 124 | this.ShowDebugToolsToolStripMenuItem.Name = "ShowDebugToolsToolStripMenuItem"; 125 | this.ShowDebugToolsToolStripMenuItem.Size = new System.Drawing.Size(169, 22); 126 | this.ShowDebugToolsToolStripMenuItem.Text = "Show debug tools"; 127 | this.ShowDebugToolsToolStripMenuItem.CheckedChanged += new System.EventHandler(this.ShowDebugToolsToolStripMenuItem_CheckedChanged); 128 | // 129 | // ServiceModeToolStripMenuItem 130 | // 131 | this.ServiceModeToolStripMenuItem.CheckOnClick = true; 132 | this.ServiceModeToolStripMenuItem.Enabled = false; 133 | this.ServiceModeToolStripMenuItem.Name = "ServiceModeToolStripMenuItem"; 134 | this.ServiceModeToolStripMenuItem.Size = new System.Drawing.Size(169, 22); 135 | this.ServiceModeToolStripMenuItem.Text = "Service mode"; 136 | this.ServiceModeToolStripMenuItem.CheckedChanged += new System.EventHandler(this.ServiceModeToolStripMenuItem_CheckedChanged); 137 | // 138 | // AboutToolStripMenuItem 139 | // 140 | this.AboutToolStripMenuItem.Name = "AboutToolStripMenuItem"; 141 | this.AboutToolStripMenuItem.Size = new System.Drawing.Size(52, 20); 142 | this.AboutToolStripMenuItem.Text = "About"; 143 | this.AboutToolStripMenuItem.Click += new System.EventHandler(this.AboutToolStripMenuItem_Click); 144 | // 145 | // CommunicationGroupBox 146 | // 147 | this.CommunicationGroupBox.Controls.Add(this.SendButton); 148 | this.CommunicationGroupBox.Controls.Add(this.SendComboBox); 149 | this.CommunicationGroupBox.Controls.Add(this.CommunicationTextBox); 150 | this.CommunicationGroupBox.Location = new System.Drawing.Point(12, 27); 151 | this.CommunicationGroupBox.Name = "CommunicationGroupBox"; 152 | this.CommunicationGroupBox.Size = new System.Drawing.Size(365, 265); 153 | this.CommunicationGroupBox.TabIndex = 1; 154 | this.CommunicationGroupBox.TabStop = false; 155 | this.CommunicationGroupBox.Text = "Communication"; 156 | // 157 | // SendButton 158 | // 159 | this.SendButton.Location = new System.Drawing.Point(312, 237); 160 | this.SendButton.Name = "SendButton"; 161 | this.SendButton.Size = new System.Drawing.Size(51, 25); 162 | this.SendButton.TabIndex = 2; 163 | this.SendButton.Text = "Send"; 164 | this.SendButton.UseVisualStyleBackColor = true; 165 | this.SendButton.Click += new System.EventHandler(this.SendButton_Click); 166 | // 167 | // SendComboBox 168 | // 169 | this.SendComboBox.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); 170 | this.SendComboBox.FormattingEnabled = true; 171 | this.SendComboBox.Location = new System.Drawing.Point(3, 238); 172 | this.SendComboBox.Name = "SendComboBox"; 173 | this.SendComboBox.Size = new System.Drawing.Size(308, 23); 174 | this.SendComboBox.TabIndex = 1; 175 | this.SendComboBox.KeyPress += new System.Windows.Forms.KeyPressEventHandler(this.SendComboBox_KeyPress); 176 | // 177 | // CommunicationTextBox 178 | // 179 | this.CommunicationTextBox.BackColor = System.Drawing.SystemColors.Window; 180 | this.CommunicationTextBox.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); 181 | this.CommunicationTextBox.Location = new System.Drawing.Point(3, 16); 182 | this.CommunicationTextBox.Multiline = true; 183 | this.CommunicationTextBox.Name = "CommunicationTextBox"; 184 | this.CommunicationTextBox.ReadOnly = true; 185 | this.CommunicationTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; 186 | this.CommunicationTextBox.Size = new System.Drawing.Size(359, 220); 187 | this.CommunicationTextBox.TabIndex = 0; 188 | // 189 | // SettingsGroupBox 190 | // 191 | this.SettingsGroupBox.Controls.Add(this.NTCBetaValueModifyCheckBox); 192 | this.SettingsGroupBox.Controls.Add(this.NTCBetaKelvinLabel); 193 | this.SettingsGroupBox.Controls.Add(this.NTCBetaValueLabel); 194 | this.SettingsGroupBox.Controls.Add(this.NTCBetaValueTextBox); 195 | this.SettingsGroupBox.Controls.Add(this.WaterPumpLabel); 196 | this.SettingsGroupBox.Controls.Add(this.DefaultSettingsButton); 197 | this.SettingsGroupBox.Controls.Add(this.WriteSettingsButton); 198 | this.SettingsGroupBox.Controls.Add(this.ReadSettingsButton); 199 | this.SettingsGroupBox.Controls.Add(this.TemperatureSensorsComboBox); 200 | this.SettingsGroupBox.Controls.Add(this.TemperatureSensorsLabel); 201 | this.SettingsGroupBox.Controls.Add(this.TemperatureUnitComboBox); 202 | this.SettingsGroupBox.Controls.Add(this.TemperatureUnitLabel); 203 | this.SettingsGroupBox.Controls.Add(this.MinutesLabel02); 204 | this.SettingsGroupBox.Controls.Add(this.WaterPumpOffTimeLabel); 205 | this.SettingsGroupBox.Controls.Add(this.WaterPumpOffTimeTextBox); 206 | this.SettingsGroupBox.Controls.Add(this.MinutesLabel01); 207 | this.SettingsGroupBox.Controls.Add(this.WaterPumpOnTimeTextBox); 208 | this.SettingsGroupBox.Controls.Add(this.WaterPumpOnTimeLabel); 209 | this.SettingsGroupBox.Controls.Add(this.WaterPumpSpeedPercentageLabel); 210 | this.SettingsGroupBox.Controls.Add(this.WaterPumpSpeedLabel); 211 | this.SettingsGroupBox.Controls.Add(this.WaterPumpSpeedTrackBar); 212 | this.SettingsGroupBox.Location = new System.Drawing.Point(12, 296); 213 | this.SettingsGroupBox.Name = "SettingsGroupBox"; 214 | this.SettingsGroupBox.Size = new System.Drawing.Size(365, 184); 215 | this.SettingsGroupBox.TabIndex = 2; 216 | this.SettingsGroupBox.TabStop = false; 217 | this.SettingsGroupBox.Text = "Settings"; 218 | // 219 | // NTCBetaValueModifyCheckBox 220 | // 221 | this.NTCBetaValueModifyCheckBox.AutoSize = true; 222 | this.NTCBetaValueModifyCheckBox.Location = new System.Drawing.Point(304, 118); 223 | this.NTCBetaValueModifyCheckBox.Name = "NTCBetaValueModifyCheckBox"; 224 | this.NTCBetaValueModifyCheckBox.Size = new System.Drawing.Size(56, 17); 225 | this.NTCBetaValueModifyCheckBox.TabIndex = 23; 226 | this.NTCBetaValueModifyCheckBox.Text = "modify"; 227 | this.NTCBetaValueModifyCheckBox.UseVisualStyleBackColor = true; 228 | this.NTCBetaValueModifyCheckBox.CheckedChanged += new System.EventHandler(this.NTCBetaValueModifyCheckBox_CheckedChanged); 229 | // 230 | // NTCBetaKelvinLabel 231 | // 232 | this.NTCBetaKelvinLabel.AutoSize = true; 233 | this.NTCBetaKelvinLabel.Location = new System.Drawing.Point(286, 119); 234 | this.NTCBetaKelvinLabel.Name = "NTCBetaKelvinLabel"; 235 | this.NTCBetaKelvinLabel.Size = new System.Drawing.Size(14, 13); 236 | this.NTCBetaKelvinLabel.TabIndex = 22; 237 | this.NTCBetaKelvinLabel.Text = "K"; 238 | // 239 | // NTCBetaValueLabel 240 | // 241 | this.NTCBetaValueLabel.AutoSize = true; 242 | this.NTCBetaValueLabel.Location = new System.Drawing.Point(162, 119); 243 | this.NTCBetaValueLabel.Name = "NTCBetaValueLabel"; 244 | this.NTCBetaValueLabel.Size = new System.Drawing.Size(85, 13); 245 | this.NTCBetaValueLabel.TabIndex = 21; 246 | this.NTCBetaValueLabel.Text = "NTC beta value:"; 247 | // 248 | // NTCBetaValueTextBox 249 | // 250 | this.NTCBetaValueTextBox.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); 251 | this.NTCBetaValueTextBox.Location = new System.Drawing.Point(248, 115); 252 | this.NTCBetaValueTextBox.Name = "NTCBetaValueTextBox"; 253 | this.NTCBetaValueTextBox.Size = new System.Drawing.Size(35, 21); 254 | this.NTCBetaValueTextBox.TabIndex = 20; 255 | // 256 | // WaterPumpLabel 257 | // 258 | this.WaterPumpLabel.AutoSize = true; 259 | this.WaterPumpLabel.Location = new System.Drawing.Point(6, 49); 260 | this.WaterPumpLabel.Name = "WaterPumpLabel"; 261 | this.WaterPumpLabel.Size = new System.Drawing.Size(68, 13); 262 | this.WaterPumpLabel.TabIndex = 19; 263 | this.WaterPumpLabel.Text = "Water pump:"; 264 | // 265 | // DefaultSettingsButton 266 | // 267 | this.DefaultSettingsButton.Location = new System.Drawing.Point(128, 149); 268 | this.DefaultSettingsButton.Name = "DefaultSettingsButton"; 269 | this.DefaultSettingsButton.Size = new System.Drawing.Size(110, 25); 270 | this.DefaultSettingsButton.TabIndex = 18; 271 | this.DefaultSettingsButton.Text = "Default settings"; 272 | this.DefaultSettingsButton.UseVisualStyleBackColor = true; 273 | this.DefaultSettingsButton.Click += new System.EventHandler(this.DefaultSettingsButton_Click); 274 | // 275 | // WriteSettingsButton 276 | // 277 | this.WriteSettingsButton.Location = new System.Drawing.Point(247, 149); 278 | this.WriteSettingsButton.Name = "WriteSettingsButton"; 279 | this.WriteSettingsButton.Size = new System.Drawing.Size(110, 25); 280 | this.WriteSettingsButton.TabIndex = 17; 281 | this.WriteSettingsButton.Text = "Write settings"; 282 | this.WriteSettingsButton.UseVisualStyleBackColor = true; 283 | this.WriteSettingsButton.Click += new System.EventHandler(this.WriteSettingsButton_Click); 284 | // 285 | // ReadSettingsButton 286 | // 287 | this.ReadSettingsButton.Location = new System.Drawing.Point(9, 149); 288 | this.ReadSettingsButton.Name = "ReadSettingsButton"; 289 | this.ReadSettingsButton.Size = new System.Drawing.Size(110, 25); 290 | this.ReadSettingsButton.TabIndex = 16; 291 | this.ReadSettingsButton.Text = "Read settings"; 292 | this.ReadSettingsButton.UseVisualStyleBackColor = true; 293 | this.ReadSettingsButton.Click += new System.EventHandler(this.ReadSettingsButton_Click); 294 | // 295 | // TemperatureSensorsComboBox 296 | // 297 | this.TemperatureSensorsComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 298 | this.TemperatureSensorsComboBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F); 299 | this.TemperatureSensorsComboBox.FormattingEnabled = true; 300 | this.TemperatureSensorsComboBox.Items.AddRange(new object[] { 301 | "none", 302 | "external", 303 | "internal", 304 | "ext+int"}); 305 | this.TemperatureSensorsComboBox.Location = new System.Drawing.Point(248, 68); 306 | this.TemperatureSensorsComboBox.Name = "TemperatureSensorsComboBox"; 307 | this.TemperatureSensorsComboBox.Size = new System.Drawing.Size(80, 21); 308 | this.TemperatureSensorsComboBox.TabIndex = 12; 309 | this.TemperatureSensorsComboBox.SelectedIndexChanged += new System.EventHandler(this.TemperatureSensorsComboBox_SelectedIndexChanged); 310 | // 311 | // TemperatureSensorsLabel 312 | // 313 | this.TemperatureSensorsLabel.AutoSize = true; 314 | this.TemperatureSensorsLabel.Location = new System.Drawing.Point(138, 71); 315 | this.TemperatureSensorsLabel.Name = "TemperatureSensorsLabel"; 316 | this.TemperatureSensorsLabel.Size = new System.Drawing.Size(109, 13); 317 | this.TemperatureSensorsLabel.TabIndex = 11; 318 | this.TemperatureSensorsLabel.Text = "Temperature sensors:"; 319 | // 320 | // TemperatureUnitComboBox 321 | // 322 | this.TemperatureUnitComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 323 | this.TemperatureUnitComboBox.Font = new System.Drawing.Font("Microsoft Sans Serif", 8.25F); 324 | this.TemperatureUnitComboBox.FormattingEnabled = true; 325 | this.TemperatureUnitComboBox.Items.AddRange(new object[] { 326 | "Celsius", 327 | "Fahrenheit", 328 | "Kelvin"}); 329 | this.TemperatureUnitComboBox.Location = new System.Drawing.Point(248, 91); 330 | this.TemperatureUnitComboBox.Name = "TemperatureUnitComboBox"; 331 | this.TemperatureUnitComboBox.Size = new System.Drawing.Size(80, 21); 332 | this.TemperatureUnitComboBox.TabIndex = 10; 333 | // 334 | // TemperatureUnitLabel 335 | // 336 | this.TemperatureUnitLabel.AutoSize = true; 337 | this.TemperatureUnitLabel.Location = new System.Drawing.Point(157, 94); 338 | this.TemperatureUnitLabel.Name = "TemperatureUnitLabel"; 339 | this.TemperatureUnitLabel.Size = new System.Drawing.Size(90, 13); 340 | this.TemperatureUnitLabel.TabIndex = 9; 341 | this.TemperatureUnitLabel.Text = "Temperature unit:"; 342 | // 343 | // MinutesLabel02 344 | // 345 | this.MinutesLabel02.AutoSize = true; 346 | this.MinutesLabel02.Location = new System.Drawing.Point(98, 95); 347 | this.MinutesLabel02.Name = "MinutesLabel02"; 348 | this.MinutesLabel02.Size = new System.Drawing.Size(23, 13); 349 | this.MinutesLabel02.TabIndex = 8; 350 | this.MinutesLabel02.Text = "min"; 351 | // 352 | // WaterPumpOffTimeLabel 353 | // 354 | this.WaterPumpOffTimeLabel.AutoSize = true; 355 | this.WaterPumpOffTimeLabel.Location = new System.Drawing.Point(6, 95); 356 | this.WaterPumpOffTimeLabel.Name = "WaterPumpOffTimeLabel"; 357 | this.WaterPumpOffTimeLabel.Size = new System.Drawing.Size(50, 13); 358 | this.WaterPumpOffTimeLabel.TabIndex = 7; 359 | this.WaterPumpOffTimeLabel.Text = "- off-time:"; 360 | // 361 | // WaterPumpOffTimeTextBox 362 | // 363 | this.WaterPumpOffTimeTextBox.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); 364 | this.WaterPumpOffTimeTextBox.Location = new System.Drawing.Point(60, 91); 365 | this.WaterPumpOffTimeTextBox.Name = "WaterPumpOffTimeTextBox"; 366 | this.WaterPumpOffTimeTextBox.Size = new System.Drawing.Size(35, 21); 367 | this.WaterPumpOffTimeTextBox.TabIndex = 6; 368 | // 369 | // MinutesLabel01 370 | // 371 | this.MinutesLabel01.AutoSize = true; 372 | this.MinutesLabel01.Location = new System.Drawing.Point(98, 71); 373 | this.MinutesLabel01.Name = "MinutesLabel01"; 374 | this.MinutesLabel01.Size = new System.Drawing.Size(23, 13); 375 | this.MinutesLabel01.TabIndex = 5; 376 | this.MinutesLabel01.Text = "min"; 377 | // 378 | // WaterPumpOnTimeTextBox 379 | // 380 | this.WaterPumpOnTimeTextBox.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); 381 | this.WaterPumpOnTimeTextBox.Location = new System.Drawing.Point(60, 68); 382 | this.WaterPumpOnTimeTextBox.Name = "WaterPumpOnTimeTextBox"; 383 | this.WaterPumpOnTimeTextBox.Size = new System.Drawing.Size(35, 21); 384 | this.WaterPumpOnTimeTextBox.TabIndex = 4; 385 | // 386 | // WaterPumpOnTimeLabel 387 | // 388 | this.WaterPumpOnTimeLabel.AutoSize = true; 389 | this.WaterPumpOnTimeLabel.Location = new System.Drawing.Point(6, 72); 390 | this.WaterPumpOnTimeLabel.Name = "WaterPumpOnTimeLabel"; 391 | this.WaterPumpOnTimeLabel.Size = new System.Drawing.Size(50, 13); 392 | this.WaterPumpOnTimeLabel.TabIndex = 3; 393 | this.WaterPumpOnTimeLabel.Text = "- on-time:"; 394 | // 395 | // WaterPumpSpeedPercentageLabel 396 | // 397 | this.WaterPumpSpeedPercentageLabel.AutoSize = true; 398 | this.WaterPumpSpeedPercentageLabel.Location = new System.Drawing.Point(328, 27); 399 | this.WaterPumpSpeedPercentageLabel.Name = "WaterPumpSpeedPercentageLabel"; 400 | this.WaterPumpSpeedPercentageLabel.Size = new System.Drawing.Size(27, 13); 401 | this.WaterPumpSpeedPercentageLabel.TabIndex = 2; 402 | this.WaterPumpSpeedPercentageLabel.Text = "50%"; 403 | // 404 | // WaterPumpSpeedLabel 405 | // 406 | this.WaterPumpSpeedLabel.AutoSize = true; 407 | this.WaterPumpSpeedLabel.Location = new System.Drawing.Point(6, 26); 408 | this.WaterPumpSpeedLabel.Name = "WaterPumpSpeedLabel"; 409 | this.WaterPumpSpeedLabel.Size = new System.Drawing.Size(100, 13); 410 | this.WaterPumpSpeedLabel.TabIndex = 1; 411 | this.WaterPumpSpeedLabel.Text = "Water pump speed:"; 412 | // 413 | // WaterPumpSpeedTrackBar 414 | // 415 | this.WaterPumpSpeedTrackBar.Location = new System.Drawing.Point(107, 13); 416 | this.WaterPumpSpeedTrackBar.Maximum = 100; 417 | this.WaterPumpSpeedTrackBar.Name = "WaterPumpSpeedTrackBar"; 418 | this.WaterPumpSpeedTrackBar.Size = new System.Drawing.Size(225, 45); 419 | this.WaterPumpSpeedTrackBar.TabIndex = 0; 420 | this.WaterPumpSpeedTrackBar.TickFrequency = 5; 421 | this.WaterPumpSpeedTrackBar.TickStyle = System.Windows.Forms.TickStyle.Both; 422 | this.WaterPumpSpeedTrackBar.Value = 50; 423 | this.WaterPumpSpeedTrackBar.Scroll += new System.EventHandler(this.WaterPumpSpeedTrackBar_Scroll); 424 | // 425 | // ExternalTemperatureTextBox 426 | // 427 | this.ExternalTemperatureTextBox.BackColor = System.Drawing.SystemColors.Window; 428 | this.ExternalTemperatureTextBox.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); 429 | this.ExternalTemperatureTextBox.Location = new System.Drawing.Point(57, 17); 430 | this.ExternalTemperatureTextBox.Name = "ExternalTemperatureTextBox"; 431 | this.ExternalTemperatureTextBox.ReadOnly = true; 432 | this.ExternalTemperatureTextBox.Size = new System.Drawing.Size(41, 21); 433 | this.ExternalTemperatureTextBox.TabIndex = 15; 434 | // 435 | // InternalTemperatureTextBox 436 | // 437 | this.InternalTemperatureTextBox.BackColor = System.Drawing.SystemColors.Window; 438 | this.InternalTemperatureTextBox.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); 439 | this.InternalTemperatureTextBox.Location = new System.Drawing.Point(174, 17); 440 | this.InternalTemperatureTextBox.Name = "InternalTemperatureTextBox"; 441 | this.InternalTemperatureTextBox.ReadOnly = true; 442 | this.InternalTemperatureTextBox.Size = new System.Drawing.Size(41, 21); 443 | this.InternalTemperatureTextBox.TabIndex = 14; 444 | // 445 | // ControlGroupBox 446 | // 447 | this.ControlGroupBox.Controls.Add(this.StatusButton); 448 | this.ControlGroupBox.Controls.Add(this.ResetButton); 449 | this.ControlGroupBox.Controls.Add(this.RefreshButton); 450 | this.ControlGroupBox.Controls.Add(this.COMPortsComboBox); 451 | this.ControlGroupBox.Controls.Add(this.ConnectButton); 452 | this.ControlGroupBox.Location = new System.Drawing.Point(12, 542); 453 | this.ControlGroupBox.Name = "ControlGroupBox"; 454 | this.ControlGroupBox.Size = new System.Drawing.Size(365, 57); 455 | this.ControlGroupBox.TabIndex = 3; 456 | this.ControlGroupBox.TabStop = false; 457 | this.ControlGroupBox.Text = "Control"; 458 | // 459 | // StatusButton 460 | // 461 | this.StatusButton.Enabled = false; 462 | this.StatusButton.Location = new System.Drawing.Point(251, 19); 463 | this.StatusButton.Name = "StatusButton"; 464 | this.StatusButton.Size = new System.Drawing.Size(50, 25); 465 | this.StatusButton.TabIndex = 4; 466 | this.StatusButton.Text = "Status"; 467 | this.StatusButton.UseVisualStyleBackColor = true; 468 | this.StatusButton.Click += new System.EventHandler(this.StatusButton_Click); 469 | // 470 | // ResetButton 471 | // 472 | this.ResetButton.Enabled = false; 473 | this.ResetButton.Location = new System.Drawing.Point(306, 19); 474 | this.ResetButton.Name = "ResetButton"; 475 | this.ResetButton.Size = new System.Drawing.Size(50, 25); 476 | this.ResetButton.TabIndex = 3; 477 | this.ResetButton.Text = "Reset"; 478 | this.ResetButton.UseVisualStyleBackColor = true; 479 | this.ResetButton.Click += new System.EventHandler(this.ResetButton_Click); 480 | // 481 | // RefreshButton 482 | // 483 | this.RefreshButton.BackgroundImageLayout = System.Windows.Forms.ImageLayout.Stretch; 484 | this.RefreshButton.Location = new System.Drawing.Point(156, 19); 485 | this.RefreshButton.Name = "RefreshButton"; 486 | this.RefreshButton.Size = new System.Drawing.Size(55, 25); 487 | this.RefreshButton.TabIndex = 2; 488 | this.RefreshButton.Text = "Refresh"; 489 | this.RefreshButton.UseVisualStyleBackColor = true; 490 | this.RefreshButton.Click += new System.EventHandler(this.RefreshButton_Click); 491 | // 492 | // COMPortsComboBox 493 | // 494 | this.COMPortsComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; 495 | this.COMPortsComboBox.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(238))); 496 | this.COMPortsComboBox.FormattingEnabled = true; 497 | this.COMPortsComboBox.Location = new System.Drawing.Point(90, 20); 498 | this.COMPortsComboBox.Name = "COMPortsComboBox"; 499 | this.COMPortsComboBox.Size = new System.Drawing.Size(60, 23); 500 | this.COMPortsComboBox.TabIndex = 1; 501 | this.COMPortsComboBox.SelectedIndexChanged += new System.EventHandler(this.COMPortsComboBox_SelectedIndexChanged); 502 | // 503 | // ConnectButton 504 | // 505 | this.ConnectButton.Location = new System.Drawing.Point(9, 19); 506 | this.ConnectButton.Name = "ConnectButton"; 507 | this.ConnectButton.Size = new System.Drawing.Size(75, 25); 508 | this.ConnectButton.TabIndex = 0; 509 | this.ConnectButton.Text = "Connect"; 510 | this.ConnectButton.UseVisualStyleBackColor = true; 511 | this.ConnectButton.Click += new System.EventHandler(this.ConnectButton_Click); 512 | // 513 | // ExternalTemperatureLabel 514 | // 515 | this.ExternalTemperatureLabel.AutoSize = true; 516 | this.ExternalTemperatureLabel.Location = new System.Drawing.Point(8, 21); 517 | this.ExternalTemperatureLabel.Name = "ExternalTemperatureLabel"; 518 | this.ExternalTemperatureLabel.Size = new System.Drawing.Size(48, 13); 519 | this.ExternalTemperatureLabel.TabIndex = 20; 520 | this.ExternalTemperatureLabel.Text = "External:"; 521 | // 522 | // InternalTemperatureLabel 523 | // 524 | this.InternalTemperatureLabel.AutoSize = true; 525 | this.InternalTemperatureLabel.Location = new System.Drawing.Point(128, 21); 526 | this.InternalTemperatureLabel.Name = "InternalTemperatureLabel"; 527 | this.InternalTemperatureLabel.Size = new System.Drawing.Size(45, 13); 528 | this.InternalTemperatureLabel.TabIndex = 21; 529 | this.InternalTemperatureLabel.Text = "Internal:"; 530 | // 531 | // TemperatureUnitLabel01 532 | // 533 | this.TemperatureUnitLabel01.AutoSize = true; 534 | this.TemperatureUnitLabel01.Location = new System.Drawing.Point(99, 21); 535 | this.TemperatureUnitLabel01.Name = "TemperatureUnitLabel01"; 536 | this.TemperatureUnitLabel01.Size = new System.Drawing.Size(10, 13); 537 | this.TemperatureUnitLabel01.TabIndex = 22; 538 | this.TemperatureUnitLabel01.Text = " "; 539 | // 540 | // TemperatureUnitLabel02 541 | // 542 | this.TemperatureUnitLabel02.AutoSize = true; 543 | this.TemperatureUnitLabel02.Location = new System.Drawing.Point(216, 21); 544 | this.TemperatureUnitLabel02.Name = "TemperatureUnitLabel02"; 545 | this.TemperatureUnitLabel02.Size = new System.Drawing.Size(10, 13); 546 | this.TemperatureUnitLabel02.TabIndex = 23; 547 | this.TemperatureUnitLabel02.Text = " "; 548 | // 549 | // TemperaturesGroupBox 550 | // 551 | this.TemperaturesGroupBox.Controls.Add(this.InternalTemperatureLabel); 552 | this.TemperaturesGroupBox.Controls.Add(this.TemperatureUnitLabel01); 553 | this.TemperaturesGroupBox.Controls.Add(this.InternalTemperatureTextBox); 554 | this.TemperaturesGroupBox.Controls.Add(this.ExternalTemperatureLabel); 555 | this.TemperaturesGroupBox.Controls.Add(this.TemperatureUnitLabel02); 556 | this.TemperaturesGroupBox.Controls.Add(this.ExternalTemperatureTextBox); 557 | this.TemperaturesGroupBox.Location = new System.Drawing.Point(12, 486); 558 | this.TemperaturesGroupBox.Name = "TemperaturesGroupBox"; 559 | this.TemperaturesGroupBox.Size = new System.Drawing.Size(365, 50); 560 | this.TemperaturesGroupBox.TabIndex = 24; 561 | this.TemperaturesGroupBox.TabStop = false; 562 | this.TemperaturesGroupBox.Text = "Temperatures"; 563 | // 564 | // DebugGroupBox 565 | // 566 | this.DebugGroupBox.Controls.Add(this.HzLabel); 567 | this.DebugGroupBox.Controls.Add(this.PWMFrequencyTextBox); 568 | this.DebugGroupBox.Controls.Add(this.PWMFrequencyApplyButton); 569 | this.DebugGroupBox.Controls.Add(this.PWMFrequencyLabel); 570 | this.DebugGroupBox.Controls.Add(this.WaterPumpOnOffCheckBox); 571 | this.DebugGroupBox.Controls.Add(this.GetStatusUpdatesCheckBox); 572 | this.DebugGroupBox.Location = new System.Drawing.Point(388, 27); 573 | this.DebugGroupBox.Name = "DebugGroupBox"; 574 | this.DebugGroupBox.Size = new System.Drawing.Size(365, 572); 575 | this.DebugGroupBox.TabIndex = 25; 576 | this.DebugGroupBox.TabStop = false; 577 | this.DebugGroupBox.Text = "Debug"; 578 | // 579 | // WaterPumpOnOffCheckBox 580 | // 581 | this.WaterPumpOnOffCheckBox.AutoSize = true; 582 | this.WaterPumpOnOffCheckBox.Checked = true; 583 | this.WaterPumpOnOffCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; 584 | this.WaterPumpOnOffCheckBox.Location = new System.Drawing.Point(6, 528); 585 | this.WaterPumpOnOffCheckBox.Name = "WaterPumpOnOffCheckBox"; 586 | this.WaterPumpOnOffCheckBox.Size = new System.Drawing.Size(290, 17); 587 | this.WaterPumpOnOffCheckBox.TabIndex = 1; 588 | this.WaterPumpOnOffCheckBox.Text = "Water pump on/off (only when service mode is enabled)"; 589 | this.WaterPumpOnOffCheckBox.UseVisualStyleBackColor = true; 590 | this.WaterPumpOnOffCheckBox.CheckedChanged += new System.EventHandler(this.WaterPumpOnOffCheckBox_CheckedChanged); 591 | // 592 | // GetStatusUpdatesCheckBox 593 | // 594 | this.GetStatusUpdatesCheckBox.AutoSize = true; 595 | this.GetStatusUpdatesCheckBox.Checked = true; 596 | this.GetStatusUpdatesCheckBox.CheckState = System.Windows.Forms.CheckState.Checked; 597 | this.GetStatusUpdatesCheckBox.Location = new System.Drawing.Point(6, 551); 598 | this.GetStatusUpdatesCheckBox.Name = "GetStatusUpdatesCheckBox"; 599 | this.GetStatusUpdatesCheckBox.Size = new System.Drawing.Size(226, 17); 600 | this.GetStatusUpdatesCheckBox.TabIndex = 0; 601 | this.GetStatusUpdatesCheckBox.Text = "Get regular status updates from the device"; 602 | this.GetStatusUpdatesCheckBox.UseVisualStyleBackColor = true; 603 | this.GetStatusUpdatesCheckBox.CheckedChanged += new System.EventHandler(this.GetStatusUpdatesCheckBox_CheckedChanged); 604 | // 605 | // PWMFrequencyLabel 606 | // 607 | this.PWMFrequencyLabel.AutoSize = true; 608 | this.PWMFrequencyLabel.Location = new System.Drawing.Point(6, 20); 609 | this.PWMFrequencyLabel.Name = "PWMFrequencyLabel"; 610 | this.PWMFrequencyLabel.Size = new System.Drawing.Size(177, 13); 611 | this.PWMFrequencyLabel.TabIndex = 2; 612 | this.PWMFrequencyLabel.Text = "Water pump driver PWM-frequency:"; 613 | // 614 | // PWMFrequencyApplyButton 615 | // 616 | this.PWMFrequencyApplyButton.Location = new System.Drawing.Point(274, 16); 617 | this.PWMFrequencyApplyButton.Name = "PWMFrequencyApplyButton"; 618 | this.PWMFrequencyApplyButton.Size = new System.Drawing.Size(55, 23); 619 | this.PWMFrequencyApplyButton.TabIndex = 4; 620 | this.PWMFrequencyApplyButton.Text = "Apply"; 621 | this.PWMFrequencyApplyButton.UseVisualStyleBackColor = true; 622 | this.PWMFrequencyApplyButton.Click += new System.EventHandler(this.PWMFrequencyApplyButton_Click); 623 | // 624 | // PWMFrequencyTextBox 625 | // 626 | this.PWMFrequencyTextBox.Location = new System.Drawing.Point(184, 17); 627 | this.PWMFrequencyTextBox.Name = "PWMFrequencyTextBox"; 628 | this.PWMFrequencyTextBox.Size = new System.Drawing.Size(59, 20); 629 | this.PWMFrequencyTextBox.TabIndex = 5; 630 | this.PWMFrequencyTextBox.Text = "490"; 631 | // 632 | // HzLabel 633 | // 634 | this.HzLabel.AutoSize = true; 635 | this.HzLabel.Location = new System.Drawing.Point(246, 20); 636 | this.HzLabel.Name = "HzLabel"; 637 | this.HzLabel.Size = new System.Drawing.Size(20, 13); 638 | this.HzLabel.TabIndex = 6; 639 | this.HzLabel.Text = "Hz"; 640 | // 641 | // MainForm 642 | // 643 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 644 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 645 | this.ClientSize = new System.Drawing.Size(764, 611); 646 | this.Controls.Add(this.DebugGroupBox); 647 | this.Controls.Add(this.TemperaturesGroupBox); 648 | this.Controls.Add(this.ControlGroupBox); 649 | this.Controls.Add(this.SettingsGroupBox); 650 | this.Controls.Add(this.CommunicationGroupBox); 651 | this.Controls.Add(this.MenuStrip); 652 | this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; 653 | this.MainMenuStrip = this.MenuStrip; 654 | this.MaximizeBox = false; 655 | this.Name = "MainForm"; 656 | this.Text = "Modular Hydroponic Tower Garden"; 657 | this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); 658 | this.Load += new System.EventHandler(this.MainForm_Load); 659 | this.MenuStrip.ResumeLayout(false); 660 | this.MenuStrip.PerformLayout(); 661 | this.CommunicationGroupBox.ResumeLayout(false); 662 | this.CommunicationGroupBox.PerformLayout(); 663 | this.SettingsGroupBox.ResumeLayout(false); 664 | this.SettingsGroupBox.PerformLayout(); 665 | ((System.ComponentModel.ISupportInitialize)(this.WaterPumpSpeedTrackBar)).EndInit(); 666 | this.ControlGroupBox.ResumeLayout(false); 667 | this.TemperaturesGroupBox.ResumeLayout(false); 668 | this.TemperaturesGroupBox.PerformLayout(); 669 | this.DebugGroupBox.ResumeLayout(false); 670 | this.DebugGroupBox.PerformLayout(); 671 | this.ResumeLayout(false); 672 | this.PerformLayout(); 673 | 674 | } 675 | 676 | #endregion 677 | 678 | private System.Windows.Forms.MenuStrip MenuStrip; 679 | private System.Windows.Forms.ToolStripMenuItem AboutToolStripMenuItem; 680 | private System.Windows.Forms.GroupBox CommunicationGroupBox; 681 | private System.Windows.Forms.TextBox CommunicationTextBox; 682 | private System.Windows.Forms.Button SendButton; 683 | private System.Windows.Forms.ComboBox SendComboBox; 684 | private System.Windows.Forms.GroupBox SettingsGroupBox; 685 | private System.Windows.Forms.TrackBar WaterPumpSpeedTrackBar; 686 | private System.Windows.Forms.Label WaterPumpSpeedPercentageLabel; 687 | private System.Windows.Forms.Label WaterPumpSpeedLabel; 688 | private System.Windows.Forms.TextBox WaterPumpOnTimeTextBox; 689 | private System.Windows.Forms.Label WaterPumpOnTimeLabel; 690 | private System.Windows.Forms.Label MinutesLabel02; 691 | private System.Windows.Forms.Label WaterPumpOffTimeLabel; 692 | private System.Windows.Forms.TextBox WaterPumpOffTimeTextBox; 693 | private System.Windows.Forms.Label MinutesLabel01; 694 | private System.Windows.Forms.GroupBox ControlGroupBox; 695 | private System.Windows.Forms.Button RefreshButton; 696 | private System.Windows.Forms.ComboBox COMPortsComboBox; 697 | private System.Windows.Forms.Button ConnectButton; 698 | private System.Windows.Forms.ComboBox TemperatureUnitComboBox; 699 | private System.Windows.Forms.Label TemperatureUnitLabel; 700 | private System.Windows.Forms.ComboBox TemperatureSensorsComboBox; 701 | private System.Windows.Forms.Label TemperatureSensorsLabel; 702 | private System.Windows.Forms.TextBox InternalTemperatureTextBox; 703 | private System.Windows.Forms.TextBox ExternalTemperatureTextBox; 704 | private System.Windows.Forms.Button DefaultSettingsButton; 705 | private System.Windows.Forms.Button WriteSettingsButton; 706 | private System.Windows.Forms.Button ReadSettingsButton; 707 | private System.Windows.Forms.Button ResetButton; 708 | private System.Windows.Forms.Label TemperatureUnitLabel02; 709 | private System.Windows.Forms.Label TemperatureUnitLabel01; 710 | private System.Windows.Forms.Label InternalTemperatureLabel; 711 | private System.Windows.Forms.Label ExternalTemperatureLabel; 712 | private System.Windows.Forms.Label WaterPumpLabel; 713 | private System.Windows.Forms.GroupBox TemperaturesGroupBox; 714 | private System.Windows.Forms.Label NTCBetaKelvinLabel; 715 | private System.Windows.Forms.Label NTCBetaValueLabel; 716 | private System.Windows.Forms.TextBox NTCBetaValueTextBox; 717 | private System.Windows.Forms.CheckBox NTCBetaValueModifyCheckBox; 718 | private System.Windows.Forms.Button StatusButton; 719 | private System.Windows.Forms.ToolStripMenuItem ToolsToolStripMenuItem; 720 | private System.Windows.Forms.GroupBox DebugGroupBox; 721 | private System.Windows.Forms.ToolStripMenuItem UpdateFirmwareToolStripMenuItem; 722 | private System.Windows.Forms.ToolStripMenuItem ShowDebugToolsToolStripMenuItem; 723 | private System.Windows.Forms.CheckBox GetStatusUpdatesCheckBox; 724 | private System.Windows.Forms.ToolStripMenuItem ServiceModeToolStripMenuItem; 725 | private System.Windows.Forms.CheckBox WaterPumpOnOffCheckBox; 726 | private System.Windows.Forms.Label PWMFrequencyLabel; 727 | private System.Windows.Forms.Button PWMFrequencyApplyButton; 728 | private System.Windows.Forms.Label HzLabel; 729 | private System.Windows.Forms.TextBox PWMFrequencyTextBox; 730 | } 731 | } 732 | 733 | -------------------------------------------------------------------------------- /GUI/MHTG/MHTG/MainForm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Drawing; 5 | using System.IO; 6 | using System.IO.Ports; 7 | using System.Linq; 8 | using System.Net; 9 | using System.Text; 10 | using System.Threading; 11 | using System.Timers; 12 | using System.Windows.Forms; 13 | 14 | namespace MHTG 15 | { 16 | public partial class MainForm : Form 17 | { 18 | public bool SerialPortAvailable = false; 19 | public bool Timeout = false; 20 | public bool DeviceFound = false; 21 | public List bufferlist = new List(); 22 | 23 | public byte WaterPumpSpeed = 0; 24 | public uint WaterPumpOnTime = 0; 25 | public uint WaterPumpOffTime = 0; 26 | public byte TemperatureUnit = 0; 27 | public int NTCBetaValue = 0; 28 | public byte TemperatureSensors = 0; 29 | public float ExternalTemperature = 0; 30 | public float InternalTemperature = 0; 31 | public ushort PWMFrequency = 0; 32 | 33 | public string SelectedPort = String.Empty; 34 | public static string UpdatePort = String.Empty; 35 | public static UInt64 OldFWUNIXTime = 0; 36 | public static UInt64 NewFWUNIXTime = 0; 37 | 38 | public string DateTimeNow; 39 | public static string USBLogFilename; 40 | public static string USBBinaryLogFilename; 41 | 42 | public bool WaterPumpState = false; 43 | public bool ServiceMode = false; 44 | 45 | public byte[] ResetDevice = new byte[] { 0x3D, 0x00, 0x02, 0x00, 0x00, 0x02 }; 46 | public byte[] HandshakeRequest = new byte[] { 0x3D, 0x00, 0x02, 0x01, 0x01, 0x04 }; 47 | public byte[] ExpectedHandshake = new byte[] { 0x3D, 0x00, 0x06, 0x81, 0x00, 0x4D, 0x48, 0x54, 0x47, 0xB7 }; 48 | public byte[] StatusRequest = new byte[] { 0x3D, 0x00, 0x02, 0x02, 0x00, 0x04 }; 49 | public byte[] EEPROMDumpRequest = new byte[] { 0x3D, 0x00, 0x06, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x20, 0x35 }; // read full EEPROM (1024 bytes) 50 | public byte[] ReadSettings = new byte[] { 0x3D, 0x00, 0x02, 0x03, 0x01, 0x06 }; 51 | public byte[] HwFwInfoRequest = new byte[] { 0x3D, 0x00, 0x02, 0x04, 0x02, 0x08 }; 52 | public byte[] TemperatureRequest = new byte[] { 0x3D, 0x00, 0x02, 0x04, 0x04, 0x0A }; 53 | public byte[] GetStatusUpdatesOn = new byte[] { 0x3D, 0x00, 0x03, 0x03, 0x03, 0x01, 0x0A }; 54 | public byte[] GetStatusUpdatesOff = new byte[] { 0x3D, 0x00, 0x03, 0x03, 0x03, 0x00, 0x09 }; 55 | public byte[] ServiceModeOn = new byte[] { 0x3D, 0x00, 0x03, 0x03, 0x04, 0x01, 0x0B }; 56 | public byte[] ServiceModeOff = new byte[] { 0x3D, 0x00, 0x03, 0x03, 0x04, 0x00, 0x0A }; 57 | public byte[] TurnOnWaterPump = new byte[] { 0x3D, 0x00, 0x03, 0x03, 0x05, 0x00, 0x0B }; 58 | public byte[] TurnOffWaterPump = new byte[] { 0x3D, 0x00, 0x03, 0x03, 0x05, 0x00, 0x0B }; 59 | 60 | AboutForm about; 61 | SerialPort Serial = new SerialPort(); 62 | System.Timers.Timer TimeoutTimer = new System.Timers.Timer(); 63 | WebClient Downloader = new WebClient(); 64 | Uri FlashFile = new Uri("https://github.com/laszlodaniel/ModularHydroponicTowerGarden/raw/master/Arduino/MHTG/MHTG.ino.standard.hex"); 65 | Uri SourceFile = new Uri("https://github.com/laszlodaniel/ModularHydroponicTowerGarden/raw/master/Arduino/MHTG/MHTG.ino"); 66 | 67 | public MainForm() 68 | { 69 | InitializeComponent(); 70 | Icon = Icon.ExtractAssociatedIcon(Application.ExecutablePath); 71 | } 72 | 73 | private void MainForm_Load(object sender, EventArgs e) 74 | { 75 | DebugGroupBox.Visible = false; 76 | this.Size = new Size(405, 650); // resize form to collapsed view 77 | this.CenterToScreen(); // put window at the center of the screen 78 | 79 | // Create LOG directory if it doesn't exist 80 | if (!Directory.Exists("LOG")) Directory.CreateDirectory("LOG"); 81 | 82 | // Set logfile names inside the LOG directory 83 | DateTimeNow = DateTime.Now.ToString("yyyyMMdd_HHmmss"); 84 | USBLogFilename = @"LOG/usblog_" + DateTimeNow + ".txt"; 85 | USBBinaryLogFilename = @"LOG/usblog_" + DateTimeNow + ".bin"; 86 | 87 | UpdateCOMPortList(); 88 | 89 | // Setup timeout timer 90 | TimeoutTimer.Elapsed += new ElapsedEventHandler(TimeoutHandler); 91 | TimeoutTimer.Interval = 2000; // ms 92 | TimeoutTimer.Enabled = false; 93 | 94 | SettingsGroupBox.Enabled = false; 95 | TemperaturesGroupBox.Enabled = false; 96 | NTCBetaValueModifyCheckBox_CheckedChanged(this, EventArgs.Empty); 97 | SendComboBox.Items.Clear(); 98 | 99 | ActiveControl = ConnectButton; // put focus on the connect button 100 | } 101 | 102 | private void MainForm_FormClosing(object sender, FormClosingEventArgs e) 103 | { 104 | Application.DoEvents(); 105 | if (DeviceFound) ConnectButton.PerformClick(); // disconnect first 106 | if (Serial.IsOpen) Serial.Close(); 107 | } 108 | 109 | private void TimeoutHandler(object source, ElapsedEventArgs e) 110 | { 111 | Timeout = true; 112 | } 113 | 114 | private void UpdateCOMPortList() 115 | { 116 | COMPortsComboBox.Items.Clear(); // clear combobox 117 | string[] ports = SerialPort.GetPortNames(); // get available ports 118 | 119 | if (ports.Length > 0) 120 | { 121 | COMPortsComboBox.Items.AddRange(ports); 122 | SerialPortAvailable = true; 123 | ConnectButton.Enabled = true; 124 | 125 | if (SelectedPort == String.Empty) // if no port has been selected 126 | { 127 | COMPortsComboBox.SelectedIndex = 0; // select first available port 128 | SelectedPort = COMPortsComboBox.Text; 129 | } 130 | else 131 | { 132 | try 133 | { 134 | COMPortsComboBox.SelectedIndex = COMPortsComboBox.Items.IndexOf(SelectedPort); 135 | } 136 | catch 137 | { 138 | COMPortsComboBox.SelectedIndex = 0; 139 | } 140 | } 141 | } 142 | else 143 | { 144 | COMPortsComboBox.Items.Add("N/A"); 145 | SerialPortAvailable = false; 146 | ConnectButton.Enabled = false; 147 | COMPortsComboBox.SelectedIndex = 0; // select "N/A" 148 | SelectedPort = String.Empty; 149 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] No device available", null); 150 | } 151 | } 152 | 153 | private void COMPortsComboBox_SelectedIndexChanged(object sender, EventArgs e) 154 | { 155 | SelectedPort = COMPortsComboBox.Text; 156 | } 157 | 158 | private void RefreshButton_Click(object sender, EventArgs e) 159 | { 160 | UpdateCOMPortList(); 161 | } 162 | 163 | private void WaterPumpSpeedTrackBar_Scroll(object sender, EventArgs e) 164 | { 165 | WaterPumpSpeedPercentageLabel.Text = WaterPumpSpeedTrackBar.Value.ToString() + "%"; 166 | } 167 | 168 | private void ConnectButton_Click(object sender, EventArgs e) 169 | { 170 | if (!DeviceFound) // connect 171 | { 172 | UpdateCOMPortList(); 173 | 174 | if (SerialPortAvailable) 175 | { 176 | byte[] buffer = new byte[2048]; 177 | byte ConnectionCounter = 0; 178 | 179 | while (ConnectionCounter < 5) // try connecting to the device 5 times, then give up 180 | { 181 | ConnectButton.Enabled = false; // no double-click 182 | 183 | if (Serial.IsOpen) Serial.Close(); // can't overwrite fields if serial port is open 184 | Serial.PortName = COMPortsComboBox.Text; 185 | Serial.BaudRate = 250000; 186 | Serial.DataBits = 8; 187 | Serial.StopBits = StopBits.One; 188 | Serial.Parity = Parity.None; 189 | Serial.ReadTimeout = 500; 190 | Serial.WriteTimeout = 500; 191 | 192 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Connecting to " + Serial.PortName, null); 193 | 194 | try 195 | { 196 | Serial.Open(); // open current serial port 197 | } 198 | catch 199 | { 200 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] " + Serial.PortName + " is opened by another application", null); 201 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Device not found at " + Serial.PortName, null); 202 | break; 203 | } 204 | 205 | if (Serial.IsOpen) 206 | { 207 | Serial.DiscardInBuffer(); 208 | Serial.DiscardOutBuffer(); 209 | Serial.BaseStream.Flush(); 210 | 211 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Handshake request (" + Serial.PortName + ")", HandshakeRequest); 212 | Serial.Write(HandshakeRequest, 0, HandshakeRequest.Length); 213 | 214 | Timeout = false; 215 | TimeoutTimer.Enabled = true; 216 | 217 | while (!Timeout) 218 | { 219 | if (Serial.BytesToRead > 9) 220 | { 221 | Serial.Read(buffer, 0, 10); 222 | break; 223 | } 224 | } 225 | 226 | TimeoutTimer.Enabled = false; 227 | 228 | Serial.DiscardInBuffer(); 229 | Serial.DiscardOutBuffer(); 230 | Serial.BaseStream.Flush(); 231 | 232 | if (Timeout) 233 | { 234 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Device is not responding at " + Serial.PortName, null); 235 | Timeout = false; 236 | Serial.Close(); 237 | ConnectionCounter++; // increase counter value and try again 238 | } 239 | else 240 | { 241 | DeviceFound = Util.CompareArrays(buffer, ExpectedHandshake, 0, ExpectedHandshake.Length); 242 | 243 | if (DeviceFound) 244 | { 245 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Handshake response", ExpectedHandshake); 246 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Handshake OK: MHTG", null); 247 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Device connected (" + Serial.PortName + ")", null); 248 | UpdatePort = Serial.PortName; 249 | ConnectButton.Text = "Disconnect"; 250 | SettingsGroupBox.Enabled = true; 251 | TemperaturesGroupBox.Enabled = true; 252 | StatusButton.Enabled = true; 253 | ResetButton.Enabled = true; 254 | UpdateFirmwareToolStripMenuItem.Enabled = true; 255 | ServiceModeToolStripMenuItem.Enabled = true; 256 | Serial.DataReceived += new SerialDataReceivedEventHandler(SerialDataReceivedHandler); 257 | Serial.Write(HwFwInfoRequest, 0, HwFwInfoRequest.Length); // send a bunch of requests 258 | Serial.Write(ReadSettings, 0, ReadSettings.Length); 259 | Serial.Write(StatusRequest, 0, StatusRequest.Length); 260 | break; // exit while-loop 261 | } 262 | else 263 | { 264 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", buffer.Take(10).ToArray()); 265 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Handshake ERROR: " + Encoding.ASCII.GetString(buffer, 5, 4), null); 266 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Device not found at " + Serial.PortName, null); 267 | Serial.Close(); 268 | ConnectionCounter++; // increase counter value and try again 269 | } 270 | } 271 | } 272 | } 273 | 274 | ConnectButton.Enabled = true; 275 | } 276 | } 277 | else // disconnect 278 | { 279 | if (Serial.IsOpen) 280 | { 281 | Serial.DiscardInBuffer(); 282 | Serial.DiscardOutBuffer(); 283 | Serial.BaseStream.Flush(); 284 | Serial.Close(); 285 | DeviceFound = false; 286 | ConnectButton.Text = "Connect"; 287 | SettingsGroupBox.Enabled = false; 288 | TemperaturesGroupBox.Enabled = false; 289 | StatusButton.Enabled = false; 290 | ResetButton.Enabled = false; 291 | UpdateFirmwareToolStripMenuItem.Enabled = false; 292 | ServiceModeToolStripMenuItem.Enabled = false; 293 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Device disconnected (" + Serial.PortName + ")", null); 294 | Serial.DataReceived -= new SerialDataReceivedEventHandler(SerialDataReceivedHandler); 295 | } 296 | } 297 | } 298 | 299 | private void SerialDataReceivedHandler(object sender, SerialDataReceivedEventArgs e) 300 | { 301 | SerialPort sp = (SerialPort)sender; 302 | int DataLength = sp.BytesToRead; 303 | 304 | // This approach enables reading multiple broken transmissions 305 | for (int i = 0; i < DataLength; i++) 306 | { 307 | try 308 | { 309 | bufferlist.Add((byte)sp.ReadByte()); 310 | } 311 | catch 312 | { 313 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Serial read error", null); 314 | break; 315 | } 316 | } 317 | 318 | // Multiple packets are handled one after another in this while-loop 319 | while (bufferlist.Count > 0) 320 | { 321 | if (bufferlist[0] == 0x3D) 322 | { 323 | if (bufferlist.Count < 3) break; // wait for the length bytes 324 | 325 | int PacketLength = (bufferlist[1] << 8) + bufferlist[2]; 326 | int FullPacketLength = PacketLength + 4; 327 | 328 | if (bufferlist.Count < FullPacketLength) break; // wait for the rest of the bytes to arrive 329 | 330 | byte[] packet = new byte[FullPacketLength]; 331 | int PayloadLength = PacketLength - 2; 332 | byte[] Payload = new byte[PayloadLength]; 333 | int ChecksumLocation = PacketLength + 3; 334 | byte DataCode = 0; 335 | byte Source = 0; 336 | byte Command = 0; 337 | byte SubDataCode = 0; 338 | byte Checksum = 0; 339 | byte CalculatedChecksum = 0; 340 | Array.Copy(bufferlist.ToArray(), 0, packet, 0, packet.Length); 341 | 342 | Checksum = packet[ChecksumLocation]; // get packet checksum byte 343 | 344 | for (int i = 1; i < ChecksumLocation; i++) 345 | { 346 | CalculatedChecksum += packet[i]; // calculate checksum 347 | } 348 | 349 | if (CalculatedChecksum == Checksum) // verify checksum 350 | { 351 | DataCode = packet[3]; 352 | Source = (byte)((DataCode >> 7) & 0x01); 353 | Command = (byte)(DataCode & 0x0F); 354 | SubDataCode = packet[4]; 355 | 356 | if (Source == 1) // highest bit set in the DataCode byte means the packet is coming from the device 357 | { 358 | if (PayloadLength > 0) // copy payload bytes if available 359 | { 360 | Array.Copy(packet, 5, Payload, 0, Payload.Length); 361 | } 362 | 363 | switch (Command) // based on the datacode decide what to do with this packet 364 | { 365 | case 0x00: // Reset 366 | switch (SubDataCode) 367 | { 368 | case 0x00: 369 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Device is resetting", packet); 370 | break; 371 | case 0x01: 372 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Device is ready", packet); 373 | break; 374 | default: 375 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Unknown reset packet", packet); 376 | break; 377 | } 378 | break; 379 | case 0x01: // Handshake 380 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Handshake received", packet); 381 | if (Encoding.ASCII.GetString(Payload, 0, Payload.Length) == "MHTG") Util.UpdateTextBox(CommunicationTextBox, "[INFO] Handshake OK: MHTG", null); 382 | else Util.UpdateTextBox(CommunicationTextBox, "[INFO] Handshake ERROR: " + Encoding.ASCII.GetString(Payload, 0, Payload.Length), null); 383 | break; 384 | case 0x02: // Status 385 | if (Payload.Length > 13) 386 | { 387 | string WaterPumpStateString = string.Empty; 388 | byte WaterPumpPercentValue = (byte)(Math.Round((float)(Payload[1] + 1) / 256 * 100)); 389 | string WaterPumpPercentLevel = WaterPumpPercentValue.ToString("0") + "%"; 390 | string ServiceModeString = string.Empty; 391 | TimeSpan RemainingTime = TimeSpan.FromMilliseconds(Payload[2] << 24 | Payload[3] << 16 | Payload[4] << 8 | Payload[5]); 392 | string RemainingTimeString = string.Format("{0:00}:{1:00}:{2:00}", (int)RemainingTime.TotalHours, RemainingTime.Minutes, RemainingTime.Seconds); 393 | 394 | if ((Payload[0] & 0x01) == 0x01) WaterPumpState = true; // enabled 395 | else WaterPumpState = false; // disabled 396 | 397 | if (WaterPumpState) 398 | { 399 | if (WaterPumpOnTime > 0) WaterPumpStateString = "on @ " + WaterPumpPercentLevel; 400 | else WaterPumpStateString = "off"; 401 | } 402 | else 403 | { 404 | WaterPumpStateString = "off"; 405 | WaterPumpState = false; 406 | } 407 | 408 | if (((Payload[0] >> 1) & 0x01) == 0x01) ServiceMode = true; 409 | else ServiceMode = false; 410 | 411 | if (ServiceMode) 412 | { 413 | ServiceModeString = "enabled"; 414 | WaterPumpStateString = "off"; 415 | } 416 | else ServiceModeString = "disabled"; 417 | 418 | ExternalTemperature = BitConverter.ToSingle(new byte[4] { Payload[6], Payload[7], Payload[8], Payload[9] }, 0); 419 | InternalTemperature = BitConverter.ToSingle(new byte[4] { Payload[10], Payload[11], Payload[12], Payload[13] }, 0); 420 | string TemperatureString = " - external: ----" + TemperatureUnitLabel01.Text + Environment.NewLine + 421 | " - internal: ----" + TemperatureUnitLabel02.Text; 422 | 423 | TemperaturesGroupBox.BeginInvoke((MethodInvoker)delegate 424 | { 425 | switch (TemperatureSensors) 426 | { 427 | case 0: // none 428 | TemperatureString = " - external: ----" + Environment.NewLine + 429 | " - internal: ----"; 430 | ExternalTemperatureTextBox.Text = "----"; 431 | InternalTemperatureTextBox.Text = "----"; 432 | break; 433 | case 1: // external 434 | TemperatureString = " - external: " + ExternalTemperature.ToString("0.0") + TemperatureUnitLabel01.Text + Environment.NewLine + 435 | " - internal: ----" + TemperatureUnitLabel02.Text; 436 | ExternalTemperatureTextBox.Text = ExternalTemperature.ToString("0.0"); 437 | InternalTemperatureTextBox.Text = "----"; 438 | break; 439 | case 2: // internal 440 | TemperatureString = " - external: ----" + TemperatureUnitLabel01.Text + Environment.NewLine + 441 | " - internal: " + InternalTemperature.ToString("0.0") + TemperatureUnitLabel02.Text; 442 | ExternalTemperatureTextBox.Text = "----"; 443 | InternalTemperatureTextBox.Text = InternalTemperature.ToString("0.0"); 444 | break; 445 | case 3: // external and internal (both) 446 | TemperatureString = " - external: " + ExternalTemperature.ToString("0.0") + TemperatureUnitLabel01.Text + Environment.NewLine + 447 | " - internal: " + InternalTemperature.ToString("0.0") + TemperatureUnitLabel02.Text; 448 | ExternalTemperatureTextBox.Text = ExternalTemperature.ToString("0.0"); 449 | InternalTemperatureTextBox.Text = InternalTemperature.ToString("0.0"); 450 | break; 451 | default: 452 | TemperatureString = " - external: ----" + TemperatureUnitLabel01.Text + Environment.NewLine + 453 | " - internal: ----" + TemperatureUnitLabel02.Text; 454 | ExternalTemperatureTextBox.Text = "----"; 455 | InternalTemperatureTextBox.Text = "----"; 456 | break; 457 | } 458 | }); 459 | 460 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Status response", packet); 461 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Current status: " + Environment.NewLine + 462 | " - service mode: " + ServiceModeString + Environment.NewLine + 463 | " - water pump state: " + WaterPumpStateString + Environment.NewLine + 464 | " - remaining time: " + RemainingTimeString + Environment.NewLine + 465 | " - temperatures: " + Environment.NewLine + 466 | TemperatureString, null); 467 | } 468 | else 469 | { 470 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 471 | } 472 | break; 473 | case 0x03: // Settings 474 | switch (SubDataCode) 475 | { 476 | case 0x01: // read settings 477 | if (PayloadLength > 14) 478 | { 479 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Settings response", packet); 480 | WaterPumpSpeed = (byte)(Math.Round((float)(Payload[0] + 1) / 256 * 100)); // convert PWM value to percent 481 | WaterPumpOnTime = (uint)((Payload[1] << 24) | (Payload[2] << 16) | (Payload[3] << 8) | Payload[4]); 482 | WaterPumpOffTime = (uint)((Payload[5] << 24) | (Payload[6] << 16) | (Payload[7] << 8) | Payload[8]); 483 | TemperatureSensors = Payload[9]; 484 | TemperatureUnit = Payload[10]; 485 | NTCBetaValue = (Payload[11] << 8) | Payload[12]; 486 | PWMFrequency = (ushort)((Payload[13] << 8) | Payload[14]); 487 | 488 | SettingsGroupBox.BeginInvoke((MethodInvoker)delegate 489 | { 490 | WaterPumpSpeedTrackBar.Value = WaterPumpSpeed; 491 | WaterPumpSpeedTrackBar_Scroll(this, EventArgs.Empty); 492 | WaterPumpOnTimeTextBox.Text = (WaterPumpOnTime / 60000).ToString("0"); 493 | WaterPumpOffTimeTextBox.Text = (WaterPumpOffTime / 60000).ToString("0"); 494 | TemperatureSensorsComboBox.SelectedIndex = TemperatureSensors; 495 | 496 | switch (TemperatureSensors) 497 | { 498 | case 0: // None 499 | TemperaturesGroupBox.Enabled = false; 500 | TemperatureUnitLabel.Enabled = false; 501 | TemperatureUnitComboBox.Enabled = false; 502 | break; 503 | case 1: // External sensor only 504 | TemperaturesGroupBox.Enabled = true; 505 | ExternalTemperatureLabel.Enabled = true; 506 | ExternalTemperatureTextBox.Enabled = true; 507 | TemperatureUnitLabel01.Enabled = true; 508 | InternalTemperatureLabel.Enabled = false; 509 | InternalTemperatureTextBox.Enabled = false; 510 | TemperatureUnitLabel02.Enabled = false; 511 | TemperatureUnitLabel.Enabled = true; 512 | TemperatureUnitComboBox.Enabled = true; 513 | break; 514 | case 2: // Internal sensor only 515 | TemperaturesGroupBox.Enabled = true; 516 | ExternalTemperatureLabel.Enabled = false; 517 | ExternalTemperatureTextBox.Enabled = false; 518 | TemperatureUnitLabel01.Enabled = false; 519 | InternalTemperatureLabel.Enabled = true; 520 | InternalTemperatureTextBox.Enabled = true; 521 | TemperatureUnitLabel02.Enabled = true; 522 | TemperatureUnitLabel.Enabled = true; 523 | TemperatureUnitComboBox.Enabled = true; 524 | break; 525 | case 3: // Both external and internal sensors enabled 526 | TemperaturesGroupBox.Enabled = true; 527 | ExternalTemperatureLabel.Enabled = true; 528 | ExternalTemperatureTextBox.Enabled = true; 529 | TemperatureUnitLabel01.Enabled = true; 530 | InternalTemperatureLabel.Enabled = true; 531 | InternalTemperatureTextBox.Enabled = true; 532 | TemperatureUnitLabel02.Enabled = true; 533 | TemperatureUnitLabel.Enabled = true; 534 | TemperatureUnitComboBox.Enabled = true; 535 | break; 536 | default: 537 | TemperaturesGroupBox.Enabled = false; 538 | TemperatureUnitLabel.Enabled = false; 539 | TemperatureUnitComboBox.Enabled = false; 540 | break; 541 | } 542 | 543 | switch (TemperatureUnit) 544 | { 545 | case 1: 546 | TemperatureUnitComboBox.SelectedIndex = 0; // Celsius 547 | TemperatureUnitLabel01.Text = "°C"; 548 | TemperatureUnitLabel02.Text = "°C"; 549 | break; 550 | case 2: 551 | TemperatureUnitComboBox.SelectedIndex = 1; // Fahrenheit 552 | TemperatureUnitLabel01.Text = "°F"; 553 | TemperatureUnitLabel02.Text = "°F"; 554 | break; 555 | case 4: 556 | TemperatureUnitComboBox.SelectedIndex = 2; // Kelvin 557 | TemperatureUnitLabel01.Text = "K"; 558 | TemperatureUnitLabel02.Text = "K"; 559 | break; 560 | default: 561 | TemperatureUnitComboBox.SelectedIndex = 0; // Celsius 562 | TemperatureUnitLabel01.Text = "°C"; 563 | TemperatureUnitLabel02.Text = "°C"; 564 | break; 565 | } 566 | 567 | NTCBetaValueTextBox.Text = NTCBetaValue.ToString("0"); 568 | 569 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Current settings:" + Environment.NewLine + 570 | " - water pump speed: " + WaterPumpSpeedTrackBar.Value.ToString("0") + "%" + Environment.NewLine + 571 | " - water pump on-time: " + WaterPumpOnTimeTextBox.Text + " minutes" + Environment.NewLine + 572 | " - water pump off-time: " + WaterPumpOffTimeTextBox.Text + " minutes" + Environment.NewLine + 573 | " - temperature sensors: " + TemperatureSensorsComboBox.Text + Environment.NewLine + 574 | " - temperature unit: " + TemperatureUnitComboBox.Text + Environment.NewLine + 575 | " - NTC beta value: " + NTCBetaValueTextBox.Text + " K" + Environment.NewLine + 576 | " - PWM-frequency: " + PWMFrequency.ToString("0") + " Hz", null); 577 | }); 578 | 579 | SettingsGroupBox.BeginInvoke((MethodInvoker)delegate 580 | { 581 | PWMFrequencyTextBox.Text = PWMFrequency.ToString("0"); 582 | }); 583 | } 584 | else 585 | { 586 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 587 | } 588 | break; 589 | case 0x02: // write settings confirmation 590 | if ((Payload.Length > 0) && (Payload[0] == 0x00)) 591 | { 592 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Settings updated", packet); 593 | Serial.Write(ReadSettings, 0, ReadSettings.Length); 594 | } 595 | else 596 | { 597 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Settings update error", packet); 598 | } 599 | break; 600 | case 0x03: // auto status update 601 | if (Payload.Length > 0) 602 | { 603 | if (Payload[0] == 0x00) 604 | { 605 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Regular status update response", packet); 606 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Regular status update disabled", null); 607 | } 608 | else 609 | { 610 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Regular status update response", packet); 611 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Regular status update enabled", null); 612 | } 613 | } 614 | else 615 | { 616 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 617 | } 618 | break; 619 | case 0x04: // service mode 620 | if (Payload.Length > 0) 621 | { 622 | if (Payload[0] == 0x00) 623 | { 624 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Service mode response", packet); 625 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Service mode disabled", null); 626 | } 627 | else 628 | { 629 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Service mode response", packet); 630 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Service mode enabled", null); 631 | } 632 | } 633 | else 634 | { 635 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 636 | } 637 | break; 638 | case 0x05: // water pump on/off 639 | if (Payload.Length > 0) 640 | { 641 | if (Payload[0] == 0x00) 642 | { 643 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Water pump status", packet); 644 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Water pump turned off", null); 645 | } 646 | else 647 | { 648 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Water pump status", packet); 649 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Water pump turned on @ " + Math.Round((float)(Payload[0] + 1) / 256 * 100).ToString("0") + "%", null); 650 | } 651 | } 652 | else 653 | { 654 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 655 | } 656 | break; 657 | default: 658 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 659 | break; 660 | } 661 | break; 662 | case 0x05: // response to request 663 | switch (SubDataCode) 664 | { 665 | case 0x01: // EEPROM content 666 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] EEPROM content", packet); 667 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] First 32 bytes of the EEPROM", Payload); 668 | break; 669 | case 0x02: // hardware/firmware version 670 | if (Payload.Length > 25) 671 | { 672 | string HardwareVersionString = "V" + ((Payload[0] << 8 | Payload[1]) / 100.00).ToString("0.00").Replace(",", "."); 673 | DateTime _HardwareDate = Util.UnixTimeStampToDateTime(Payload[2] << 56 | Payload[3] << 48 | Payload[4] << 40 | Payload[5] << 32 | Payload[6] << 24 | Payload[7] << 16 | Payload[8] << 8 | Payload[9]); 674 | DateTime _AssemblyDate = Util.UnixTimeStampToDateTime(Payload[10] << 56 | Payload[11] << 48 | Payload[12] << 40 | Payload[13] << 32 | Payload[14] << 24 | Payload[15] << 16 | Payload[16] << 8 | Payload[17]); 675 | DateTime _FirmwareDate = Util.UnixTimeStampToDateTime(Payload[18] << 56 | Payload[19] << 48 | Payload[20] << 40 | Payload[21] << 32 | Payload[22] << 24 | Payload[23] << 16 | Payload[24] << 8 | Payload[25]); 676 | string _HardwareDateString = _HardwareDate.ToString("yyyy.MM.dd HH:mm:ss"); 677 | string _AssemblyDateString = _AssemblyDate.ToString("yyyy.MM.dd HH:mm:ss"); 678 | string _FirmwareDateString = _FirmwareDate.ToString("yyyy.MM.dd HH:mm:ss"); 679 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Hardware/Firmware information response", packet); 680 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Hardware ver.: " + HardwareVersionString + Environment.NewLine + 681 | " Hardware date: " + _HardwareDateString + Environment.NewLine + 682 | " Assembly date: " + _AssemblyDateString + Environment.NewLine + 683 | " Firmware date: " + _FirmwareDateString, null); 684 | OldFWUNIXTime = (UInt64)(Payload[18] << 56 | Payload[19] << 48 | Payload[20] << 40 | Payload[21] << 32 | Payload[22] << 24 | Payload[23] << 16 | Payload[24] << 8 | Payload[25]); 685 | } 686 | else 687 | { 688 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 689 | } 690 | break; 691 | case 0x03: // timestamp 692 | if (Payload.Length > 3) 693 | { 694 | TimeSpan ElapsedTime = TimeSpan.FromMilliseconds(Payload[0] << 24 | Payload[1] << 16 | Payload[2] << 8 | Payload[3]); 695 | DateTime Timestamp = DateTime.Today.Add(ElapsedTime); 696 | string TimestampString = Timestamp.ToString("HH:mm:ss.fff"); 697 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Timestamp response", packet); 698 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Timestamp: " + TimestampString, null); 699 | } 700 | else 701 | { 702 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 703 | } 704 | break; 705 | case 0x04: // temperatures 706 | if (Payload.Length > 7) 707 | { 708 | ExternalTemperature = BitConverter.ToSingle(new byte[4] { Payload[0], Payload[1], Payload[2], Payload[3] }, 0); 709 | InternalTemperature = BitConverter.ToSingle(new byte[4] { Payload[4], Payload[5], Payload[6], Payload[7] }, 0); 710 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Temperature response", packet); 711 | 712 | TemperaturesGroupBox.BeginInvoke((MethodInvoker)delegate 713 | { 714 | switch (TemperatureSensors) 715 | { 716 | case 0: // none 717 | ExternalTemperatureTextBox.Text = "---.-"; 718 | InternalTemperatureTextBox.Text = "---.-"; 719 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Temperatures:" + Environment.NewLine + 720 | " - external: ---.-" + TemperatureUnitLabel01.Text + Environment.NewLine + 721 | " - internal: ---.-" + TemperatureUnitLabel02.Text, null); 722 | break; 723 | case 1: // external 724 | ExternalTemperatureTextBox.Text = ExternalTemperature.ToString("0.0"); 725 | InternalTemperatureTextBox.Text = "---.-"; 726 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Temperatures:" + Environment.NewLine + 727 | " - external: " + ExternalTemperature.ToString("0.0") + TemperatureUnitLabel01.Text + Environment.NewLine + 728 | " - internal: ---.-" + TemperatureUnitLabel02.Text, null); 729 | break; 730 | case 2: // internal 731 | ExternalTemperatureTextBox.Text = "---.-"; 732 | InternalTemperatureTextBox.Text = InternalTemperature.ToString("0.0"); 733 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Temperatures:" + Environment.NewLine + 734 | " - external: ---.-" + TemperatureUnitLabel01.Text + Environment.NewLine + 735 | " - internal: " + InternalTemperature.ToString("0.0") + TemperatureUnitLabel02.Text, null); 736 | break; 737 | case 3: // external and internal (both) 738 | ExternalTemperatureTextBox.Text = ExternalTemperature.ToString("0.0"); 739 | InternalTemperatureTextBox.Text = InternalTemperature.ToString("0.0"); 740 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Temperatures:" + Environment.NewLine + 741 | " - external: " + ExternalTemperature.ToString("0.0") + TemperatureUnitLabel01.Text + Environment.NewLine + 742 | " - internal: " + InternalTemperature.ToString("0.0") + TemperatureUnitLabel02.Text, null); 743 | break; 744 | default: 745 | break; 746 | } 747 | }); 748 | } 749 | else 750 | { 751 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 752 | } 753 | break; 754 | default: 755 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 756 | break; 757 | } 758 | break; 759 | case 0x0E: // Debug 760 | switch (SubDataCode) 761 | { 762 | case 0x01: // read EEPROM 763 | if (Payload.Length > 4) 764 | { 765 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] EEPROM dump", packet); 766 | int offset = Payload[0] << 8 | Payload[1]; 767 | int count = Payload[2] << 8 | Payload[3]; 768 | byte[] values = Payload.Skip(4).Take(Payload.Length - 4).ToArray(); 769 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] EEPROM values (offset=" + Convert.ToString(offset, 16).PadLeft(4, '0').PadRight(3, ' ').ToUpper() + ", count=" + Convert.ToString(count, 16).PadLeft(4, '0').PadRight(3, ' ').ToUpper() + ")", values); 770 | } 771 | else 772 | { 773 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 774 | } 775 | break; 776 | case 0x02: // write EEPROM 777 | if (Payload.Length > 0) 778 | { 779 | if (Payload[0] == 0x00) Util.UpdateTextBox(CommunicationTextBox, "[RX->] EEPROM write successful", packet); 780 | } 781 | break; 782 | case 0x03: // PWM-frequency changed 783 | if (Payload.Length > 0) 784 | { 785 | if (Payload[0] == 0x00) Util.UpdateTextBox(CommunicationTextBox, "[RX->] PWM-frequency changed successfully", packet); 786 | } 787 | break; 788 | default: 789 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 790 | break; 791 | } 792 | break; 793 | case 0x0F: // OK/Error 794 | switch (SubDataCode) 795 | { 796 | case 0x00: 797 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] OK", packet); 798 | break; 799 | case 0x01: 800 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Error: invalid length", packet); 801 | break; 802 | case 0x02: 803 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Error: invalid command", packet); 804 | break; 805 | case 0x03: 806 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Error: invalid sub-data code", packet); 807 | break; 808 | case 0x04: 809 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Error: invalid payload value(s)", packet); 810 | break; 811 | case 0x05: 812 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Error: invalid packet checksum", packet); 813 | break; 814 | case 0x06: 815 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Error: packet timeout occured", packet); 816 | break; 817 | case 0xFC: 818 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Error: EEPROM checksum mismatch", packet); 819 | break; 820 | case 0xFD: 821 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Error: not enough MCU RAM", packet); 822 | break; 823 | case 0xFE: 824 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Error: internal error", packet); 825 | break; 826 | case 0xFF: 827 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Error: fatal error", packet); 828 | break; 829 | default: 830 | break; 831 | } 832 | break; 833 | default: 834 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received", packet); 835 | break; 836 | } 837 | } 838 | } 839 | else 840 | { 841 | Util.UpdateTextBox(CommunicationTextBox, "[RX->] Data received with checksum error", packet); 842 | } 843 | 844 | bufferlist.RemoveRange(0, FullPacketLength); 845 | } 846 | else 847 | { 848 | bufferlist.RemoveAt(0); // remove this byte and see what's next 849 | } 850 | } 851 | } 852 | 853 | private void ReadSettingsButton_Click(object sender, EventArgs e) 854 | { 855 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Settings request", ReadSettings); 856 | Serial.Write(ReadSettings, 0, ReadSettings.Length); 857 | } 858 | 859 | private void DefaultSettingsButton_Click(object sender, EventArgs e) 860 | { 861 | WaterPumpSpeedTrackBar.Value = 50; 862 | WaterPumpSpeedTrackBar_Scroll(this, EventArgs.Empty); // trigger event manually to update percentage label 863 | WaterPumpOnTimeTextBox.Text = "15"; 864 | WaterPumpOffTimeTextBox.Text = "15"; 865 | NTCBetaValueTextBox.Text = "3950"; 866 | TemperatureUnitComboBox.SelectedIndex = 0; 867 | TemperatureSensorsComboBox.SelectedIndex = 0; 868 | PWMFrequencyTextBox.Text = "490"; 869 | 870 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Default settings restored." + Environment.NewLine + 871 | " Click the \"Write settings\" button" + Environment.NewLine + 872 | " to update the device.", null); 873 | } 874 | 875 | private void WriteSettingsButton_Click(object sender, EventArgs e) 876 | { 877 | // Get values from controls 878 | if (WaterPumpSpeedTrackBar.Value == 0) WaterPumpSpeed = 0; 879 | else WaterPumpSpeed = (byte)((WaterPumpSpeedTrackBar.Value * 256 / 100) - 1); // convert percent to PWM value (0-255) 880 | 881 | uint.TryParse(WaterPumpOnTimeTextBox.Text, out WaterPumpOnTime); // 0 if failed 882 | if (WaterPumpOnTime < 0) WaterPumpOnTime = 15; // minutes default 883 | if (WaterPumpOnTime > 71582) WaterPumpOnTime = 71582; // maximum value 884 | WaterPumpOnTime *= 60 * 1000; // convert minutes to milliseconds 885 | 886 | byte[] WaterPumpOnTimeArray = new byte[4]; 887 | WaterPumpOnTimeArray[0] = (byte)((WaterPumpOnTime >> 24) & 0xFF); 888 | WaterPumpOnTimeArray[1] = (byte)((WaterPumpOnTime >> 16) & 0xFF); 889 | WaterPumpOnTimeArray[2] = (byte)((WaterPumpOnTime >> 8) & 0xFF); 890 | WaterPumpOnTimeArray[3] = (byte)(WaterPumpOnTime & 0xFF); 891 | 892 | uint.TryParse(WaterPumpOffTimeTextBox.Text, out WaterPumpOffTime); // 0 if failed 893 | if (WaterPumpOffTime < 0) WaterPumpOffTime = 15; // minutes default 894 | if (WaterPumpOffTime > 71582) WaterPumpOffTime = 71582; // maximum value 895 | WaterPumpOffTime *= 60 * 1000; // convert minutes to milliseconds 896 | 897 | byte[] WaterPumpOffTimeArray = new byte[4]; 898 | WaterPumpOffTimeArray[0] = (byte)((WaterPumpOffTime >> 24) & 0xFF); 899 | WaterPumpOffTimeArray[1] = (byte)((WaterPumpOffTime >> 16) & 0xFF); 900 | WaterPumpOffTimeArray[2] = (byte)((WaterPumpOffTime >> 8) & 0xFF); 901 | WaterPumpOffTimeArray[3] = (byte)(WaterPumpOffTime & 0xFF); 902 | 903 | TemperatureSensors = (byte)TemperatureSensorsComboBox.SelectedIndex; 904 | if ((TemperatureSensorsComboBox.SelectedIndex < 0) || (TemperatureSensorsComboBox.SelectedIndex > 3)) TemperatureSensors = 0; 905 | 906 | switch (TemperatureUnitComboBox.SelectedIndex) 907 | { 908 | case 0: // Celsius 909 | TemperatureUnit = 1; 910 | break; 911 | case 1: // Fahrenheit 912 | TemperatureUnit = 2; 913 | break; 914 | case 2: // Kelvin 915 | TemperatureUnit = 4; 916 | break; 917 | default: // Celsius 918 | TemperatureUnit = 1; 919 | break; 920 | } 921 | 922 | int.TryParse(NTCBetaValueTextBox.Text, out NTCBetaValue); // 0 if failed 923 | if (NTCBetaValue == 0) NTCBetaValue = 3950; // K default 924 | byte[] NTCBetaValueArray = new byte[2]; 925 | NTCBetaValueArray[0] = (byte)((NTCBetaValue >> 8) & 0xFF); 926 | NTCBetaValueArray[1] = (byte)(NTCBetaValue & 0xFF); 927 | 928 | ushort.TryParse(PWMFrequencyTextBox.Text, out PWMFrequency); 929 | if (PWMFrequency == 0) PWMFrequency = 490; 930 | byte[] PWMFrequencyArray = new byte[2]; 931 | PWMFrequencyArray[0] = (byte)((PWMFrequency >> 8) & 0xFF); 932 | PWMFrequencyArray[1] = (byte)(PWMFrequency & 0xFF); 933 | 934 | byte[] SettingsPayload = new byte[15]; 935 | SettingsPayload[0] = WaterPumpSpeed; 936 | SettingsPayload[1] = WaterPumpOnTimeArray[0]; 937 | SettingsPayload[2] = WaterPumpOnTimeArray[1]; 938 | SettingsPayload[3] = WaterPumpOnTimeArray[2]; 939 | SettingsPayload[4] = WaterPumpOnTimeArray[3]; 940 | SettingsPayload[5] = WaterPumpOffTimeArray[0]; 941 | SettingsPayload[6] = WaterPumpOffTimeArray[1]; 942 | SettingsPayload[7] = WaterPumpOffTimeArray[2]; 943 | SettingsPayload[8] = WaterPumpOffTimeArray[3]; 944 | SettingsPayload[9] = TemperatureSensors; 945 | SettingsPayload[10] = TemperatureUnit; 946 | SettingsPayload[11] = NTCBetaValueArray[0]; 947 | SettingsPayload[12] = NTCBetaValueArray[1]; 948 | SettingsPayload[13] = PWMFrequencyArray[0]; 949 | SettingsPayload[14] = PWMFrequencyArray[1]; 950 | 951 | byte[] WriteSettings = new byte[21]; 952 | WriteSettings[0] = 0x3D; // sync 953 | WriteSettings[1] = 0x00; // length low byte 954 | WriteSettings[2] = 0x11; // length high byte 955 | WriteSettings[3] = 0x03; // data code 956 | WriteSettings[4] = 0x02; // sub-data code 957 | 958 | for (int i = 0; i < SettingsPayload.Length; i++) 959 | { 960 | WriteSettings[5 + i] = SettingsPayload[i]; 961 | } 962 | 963 | byte Checksum = 0; 964 | for (int j = 1; j < WriteSettings.Length - 1; j++) 965 | { 966 | Checksum += WriteSettings[j]; 967 | } 968 | WriteSettings[20] = Checksum; 969 | 970 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Write settings", WriteSettings); 971 | Serial.Write(WriteSettings, 0, WriteSettings.Length); 972 | } 973 | 974 | private void NTCBetaValueModifyCheckBox_CheckedChanged(object sender, EventArgs e) 975 | { 976 | if (NTCBetaValueModifyCheckBox.Checked) 977 | { 978 | NTCBetaValueLabel.Enabled = true; 979 | NTCBetaValueTextBox.Enabled = true; 980 | NTCBetaKelvinLabel.Enabled = true; 981 | } 982 | else 983 | { 984 | NTCBetaValueLabel.Enabled = false; 985 | NTCBetaValueTextBox.Enabled = false; 986 | NTCBetaKelvinLabel.Enabled = false; 987 | } 988 | } 989 | 990 | private void TemperatureSensorsComboBox_SelectedIndexChanged(object sender, EventArgs e) 991 | { 992 | if (TemperatureSensorsComboBox.SelectedIndex != 0) 993 | { 994 | TemperatureUnitComboBox.Enabled = true; 995 | TemperatureUnitLabel.Enabled = true; 996 | } 997 | else 998 | { 999 | TemperatureUnitComboBox.Enabled = false; 1000 | TemperatureUnitLabel.Enabled = false; 1001 | } 1002 | } 1003 | 1004 | private void StatusButton_Click(object sender, EventArgs e) 1005 | { 1006 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Status request", StatusRequest); 1007 | Serial.Write(StatusRequest, 0, StatusRequest.Length); 1008 | } 1009 | 1010 | private void ResetButton_Click(object sender, EventArgs e) 1011 | { 1012 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Reset device", ResetDevice); 1013 | Serial.Write(ResetDevice, 0, ResetDevice.Length); 1014 | } 1015 | 1016 | private void SendButton_Click(object sender, EventArgs e) 1017 | { 1018 | if (DeviceFound) 1019 | { 1020 | if (SendComboBox.Text != String.Empty) 1021 | { 1022 | byte[] bytes = Util.HexStringToByte(SendComboBox.Text); 1023 | if ((bytes.Length > 5) && (bytes != null)) 1024 | { 1025 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Data transmitted", bytes); 1026 | Serial.Write(bytes, 0, bytes.Length); 1027 | 1028 | if (!SendComboBox.Items.Contains(SendComboBox.Text)) // only add unique items (no repeat!) 1029 | { 1030 | SendComboBox.Items.Add(SendComboBox.Text); // add command to the list so it can be selected later 1031 | } 1032 | } 1033 | } 1034 | } 1035 | } 1036 | 1037 | private void AboutToolStripMenuItem_Click(object sender, EventArgs e) 1038 | { 1039 | about = new AboutForm(this) 1040 | { 1041 | StartPosition = FormStartPosition.CenterParent 1042 | }; 1043 | about.ShowDialog(); 1044 | } 1045 | 1046 | private void ShowDebugToolsToolStripMenuItem_CheckedChanged(object sender, EventArgs e) 1047 | { 1048 | if (ShowDebugToolsToolStripMenuItem.Checked) 1049 | { 1050 | DebugGroupBox.Visible = true; 1051 | this.Size = new Size(780, 650); // resize form to expanded view 1052 | this.CenterToScreen(); 1053 | } 1054 | else 1055 | { 1056 | DebugGroupBox.Visible = false; 1057 | this.Size = new Size(405, 650); // resize form to collapsed view 1058 | this.CenterToScreen(); 1059 | } 1060 | } 1061 | 1062 | private void GetStatusUpdatesCheckBox_CheckedChanged(object sender, EventArgs e) 1063 | { 1064 | if (Serial.IsOpen) 1065 | { 1066 | if (GetStatusUpdatesCheckBox.Checked) 1067 | { 1068 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Enable regular status updates", GetStatusUpdatesOn); 1069 | Serial.Write(GetStatusUpdatesOn, 0, GetStatusUpdatesOn.Length); 1070 | } 1071 | else 1072 | { 1073 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Disable regular status updates", GetStatusUpdatesOff); 1074 | Serial.Write(GetStatusUpdatesOff, 0, GetStatusUpdatesOff.Length); 1075 | } 1076 | } 1077 | } 1078 | 1079 | private void SendComboBox_KeyPress(object sender, KeyPressEventArgs e) 1080 | { 1081 | if (e.KeyChar == (char)Keys.Return) 1082 | { 1083 | SendButton.PerformClick(); 1084 | e.Handled = true; 1085 | } 1086 | } 1087 | 1088 | private void UpdateFirmwareToolStripMenuItem_Click(object sender, EventArgs e) 1089 | { 1090 | //if (true) 1091 | if ((UpdatePort != String.Empty) && (OldFWUNIXTime != 0) && DeviceFound) 1092 | { 1093 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Searching for new device firmware" + Environment.NewLine + 1094 | " This may take a few seconds...", null); 1095 | 1096 | // Download latest MHTG.ino file from GitHub 1097 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; 1098 | try 1099 | { 1100 | Downloader.DownloadFile(SourceFile, @"Tools/MHTG.ino"); 1101 | } 1102 | catch 1103 | { 1104 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Download error", null); 1105 | } 1106 | 1107 | if (File.Exists(@"Tools/MHTG.ino")) 1108 | { 1109 | // Get new UNIX time value from the downloaded file 1110 | string line = String.Empty; 1111 | bool done = false; 1112 | using (Stream stream = File.Open(@"Tools/MHTG.ino", FileMode.Open)) 1113 | { 1114 | using (StreamReader reader = new StreamReader(stream)) 1115 | { 1116 | while (!done) 1117 | { 1118 | line = reader.ReadLine(); 1119 | if (line.Contains("#define FW_DATE")) 1120 | { 1121 | done = true; 1122 | } 1123 | } 1124 | } 1125 | } 1126 | 1127 | string hexline = line.Substring(16, 18); 1128 | NewFWUNIXTime = Convert.ToUInt64(hexline, 16); 1129 | 1130 | DateTime OldFirmwareDate = Util.UnixTimeStampToDateTime(OldFWUNIXTime); 1131 | DateTime NewFirmwareDate = Util.UnixTimeStampToDateTime(NewFWUNIXTime); 1132 | string OldFirmwareDateString = OldFirmwareDate.ToString("yyyy.MM.dd HH:mm:ss"); 1133 | string NewFirmwareDateString = NewFirmwareDate.ToString("yyyy.MM.dd HH:mm:ss"); 1134 | 1135 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Old firmware date: " + OldFirmwareDateString, null); 1136 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] New firmware date: " + NewFirmwareDateString, null); 1137 | 1138 | if (NewFWUNIXTime > OldFWUNIXTime) 1139 | { 1140 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Downloading new firmware", null); 1141 | Downloader.DownloadFile(FlashFile, @"Tools/MHTG.ino.standard.hex"); 1142 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Beginning firmware update", null); 1143 | ConnectButton.PerformClick(); // disconnect 1144 | Thread.Sleep(500); // wait until UI updates its controls 1145 | this.Refresh(); 1146 | Process process = new Process(); 1147 | process.StartInfo.WorkingDirectory = "Tools"; 1148 | process.StartInfo.FileName = "avrdude.exe"; 1149 | process.StartInfo.Arguments = "-C avrdude.conf -p m328p -c arduino -P " + UpdatePort + " -b 115200 -D -U flash:w:MHTG.ino.standard.hex:i"; 1150 | process.Start(); 1151 | process.WaitForExit(); 1152 | this.Refresh(); 1153 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Device firmware update finished" + Environment.NewLine + " Connect again manually", null); 1154 | File.Delete(@"Tools/MHTG.ino"); 1155 | File.Delete(@"Tools/MHTG.ino.standard.hex"); 1156 | } 1157 | else 1158 | { 1159 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] No device firmware update available", null); 1160 | File.Delete(@"Tools/MHTG.ino"); 1161 | } 1162 | } 1163 | } 1164 | else 1165 | { 1166 | Util.UpdateTextBox(CommunicationTextBox, "[INFO] Device firmware update error", null); 1167 | } 1168 | } 1169 | 1170 | private void ServiceModeToolStripMenuItem_CheckedChanged(object sender, EventArgs e) 1171 | { 1172 | if (Serial.IsOpen) 1173 | { 1174 | if (ServiceModeToolStripMenuItem.Checked) 1175 | { 1176 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Enable service mode", ServiceModeOn); 1177 | Serial.Write(ServiceModeOn, 0, ServiceModeOn.Length); 1178 | } 1179 | else 1180 | { 1181 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Disable service mode", ServiceModeOff); 1182 | Serial.Write(ServiceModeOff, 0, ServiceModeOff.Length); 1183 | } 1184 | } 1185 | } 1186 | 1187 | private void WaterPumpOnOffCheckBox_CheckedChanged(object sender, EventArgs e) 1188 | { 1189 | if (Serial.IsOpen) 1190 | { 1191 | if (WaterPumpOnOffCheckBox.Checked) 1192 | { 1193 | TurnOnWaterPump[5] = (byte)((WaterPumpSpeedTrackBar.Value * 256 / 100) - 1); // payload contains pwm-level 1194 | 1195 | byte checksum = 0; 1196 | for (int i = 1; i < 6; i++) 1197 | { 1198 | checksum += TurnOnWaterPump[i]; 1199 | } 1200 | TurnOnWaterPump[6] = checksum; // re-calculate checksum 1201 | 1202 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Turn on water pump", TurnOnWaterPump); 1203 | Serial.Write(TurnOnWaterPump, 0, TurnOnWaterPump.Length); 1204 | } 1205 | else 1206 | { 1207 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Turn off water pump", TurnOffWaterPump); 1208 | Serial.Write(TurnOffWaterPump, 0, TurnOffWaterPump.Length); 1209 | } 1210 | } 1211 | } 1212 | 1213 | private void PWMFrequencyApplyButton_Click(object sender, EventArgs e) 1214 | { 1215 | ushort.TryParse(PWMFrequencyTextBox.Text, out PWMFrequency); 1216 | if (PWMFrequency == 0) PWMFrequency = 490; // sweet-spot 1217 | if (PWMFrequency < 245) 1218 | { 1219 | PWMFrequency = 245; // minimum suppoorted PWM-frequency, it's very rough, though 1220 | PWMFrequencyTextBox.Text = "245"; 1221 | } 1222 | 1223 | byte PWMFrequencyHB = (byte)((PWMFrequency >> 8) & 0xFF); 1224 | byte PWMFrequencyLB = (byte)(PWMFrequency & 0xFF); 1225 | 1226 | List ChangePWMFrequencyPacketBytes = new List(); 1227 | byte checksum = 0; 1228 | ChangePWMFrequencyPacketBytes.AddRange(new byte[] { 0x3D, 0x00, 0x04, 0x0E, 0x03, PWMFrequencyHB, PWMFrequencyLB }); 1229 | 1230 | for (int i = 1; i < ChangePWMFrequencyPacketBytes.Count; i++) 1231 | { 1232 | checksum += ChangePWMFrequencyPacketBytes[i]; 1233 | } 1234 | 1235 | ChangePWMFrequencyPacketBytes.Add(checksum); 1236 | 1237 | byte[] ChangePWMFrequencyPacket = ChangePWMFrequencyPacketBytes.ToArray(); 1238 | Util.UpdateTextBox(CommunicationTextBox, "[<-TX] Change PWM-frequency", ChangePWMFrequencyPacket); 1239 | Serial.Write(ChangePWMFrequencyPacket, 0, ChangePWMFrequencyPacket.Length); 1240 | } 1241 | } 1242 | } --------------------------------------------------------------------------------