├── .gitattributes ├── .gitignore ├── Documentation.chm ├── LICENSE ├── ModbusSampleCommon ├── App.ico ├── AssemblyInfo.cs ├── Modbus Sample Common.sln ├── ModbusTester.csproj ├── Thumbs.db ├── frmStart.cs └── frmStart.resx ├── ModbusTCP ├── ModbusTCP.sln └── ModbusTCP │ ├── ModbusTCP.csproj │ ├── Properties │ └── AssemblyInfo.cs │ ├── modbusTCP.cs │ └── revision.txt ├── README.md └── Screenshot.png /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | 56 | # StyleCop 57 | StyleCopReport.xml 58 | 59 | # Files built by Visual Studio 60 | *_i.c 61 | *_p.c 62 | *_h.h 63 | *.ilk 64 | *.meta 65 | *.obj 66 | *.iobj 67 | *.pch 68 | *.pdb 69 | *.ipdb 70 | *.pgc 71 | *.pgd 72 | *.rsp 73 | *.sbr 74 | *.tlb 75 | *.tli 76 | *.tlh 77 | *.tmp 78 | *.tmp_proj 79 | *.log 80 | *.vspscc 81 | *.vssscc 82 | .builds 83 | *.pidb 84 | *.svclog 85 | *.scc 86 | 87 | # Chutzpah Test files 88 | _Chutzpah* 89 | 90 | # Visual C++ cache files 91 | ipch/ 92 | *.aps 93 | *.ncb 94 | *.opendb 95 | *.opensdf 96 | *.sdf 97 | *.cachefile 98 | *.VC.db 99 | *.VC.VC.opendb 100 | 101 | # Visual Studio profiler 102 | *.psess 103 | *.vsp 104 | *.vspx 105 | *.sap 106 | 107 | # Visual Studio Trace Files 108 | *.e2e 109 | 110 | # TFS 2012 Local Workspace 111 | $tf/ 112 | 113 | # Guidance Automation Toolkit 114 | *.gpState 115 | 116 | # ReSharper is a .NET coding add-in 117 | _ReSharper*/ 118 | *.[Rr]e[Ss]harper 119 | *.DotSettings.user 120 | 121 | # JustCode is a .NET coding add-in 122 | .JustCode 123 | 124 | # TeamCity is a build add-in 125 | _TeamCity* 126 | 127 | # DotCover is a Code Coverage Tool 128 | *.dotCover 129 | 130 | # AxoCover is a Code Coverage Tool 131 | .axoCover/* 132 | !.axoCover/settings.json 133 | 134 | # Visual Studio code coverage results 135 | *.coverage 136 | *.coveragexml 137 | 138 | # NCrunch 139 | _NCrunch_* 140 | .*crunch*.local.xml 141 | nCrunchTemp_* 142 | 143 | # MightyMoose 144 | *.mm.* 145 | AutoTest.Net/ 146 | 147 | # Web workbench (sass) 148 | .sass-cache/ 149 | 150 | # Installshield output folder 151 | [Ee]xpress/ 152 | 153 | # DocProject is a documentation generator add-in 154 | DocProject/buildhelp/ 155 | DocProject/Help/*.HxT 156 | DocProject/Help/*.HxC 157 | DocProject/Help/*.hhc 158 | DocProject/Help/*.hhk 159 | DocProject/Help/*.hhp 160 | DocProject/Help/Html2 161 | DocProject/Help/html 162 | 163 | # Click-Once directory 164 | publish/ 165 | 166 | # Publish Web Output 167 | *.[Pp]ublish.xml 168 | *.azurePubxml 169 | # Note: Comment the next line if you want to checkin your web deploy settings, 170 | # but database connection strings (with potential passwords) will be unencrypted 171 | *.pubxml 172 | *.publishproj 173 | 174 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 175 | # checkin your Azure Web App publish settings, but sensitive information contained 176 | # in these scripts will be unencrypted 177 | PublishScripts/ 178 | 179 | # NuGet Packages 180 | *.nupkg 181 | # The packages folder can be ignored because of Package Restore 182 | **/[Pp]ackages/* 183 | # except build/, which is used as an MSBuild target. 184 | !**/[Pp]ackages/build/ 185 | # Uncomment if necessary however generally it will be regenerated when needed 186 | #!**/[Pp]ackages/repositories.config 187 | # NuGet v3's project.json files produces more ignorable files 188 | *.nuget.props 189 | *.nuget.targets 190 | 191 | # Microsoft Azure Build Output 192 | csx/ 193 | *.build.csdef 194 | 195 | # Microsoft Azure Emulator 196 | ecf/ 197 | rcf/ 198 | 199 | # Windows Store app package directories and files 200 | AppPackages/ 201 | BundleArtifacts/ 202 | Package.StoreAssociation.xml 203 | _pkginfo.txt 204 | *.appx 205 | 206 | # Visual Studio cache files 207 | # files ending in .cache can be ignored 208 | *.[Cc]ache 209 | # but keep track of directories ending in .cache 210 | !*.[Cc]ache/ 211 | 212 | # Others 213 | ClientBin/ 214 | ~$* 215 | *~ 216 | *.dbmdl 217 | *.dbproj.schemaview 218 | *.jfm 219 | *.pfx 220 | *.publishsettings 221 | orleans.codegen.cs 222 | 223 | # Including strong name files can present a security risk 224 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 225 | #*.snk 226 | 227 | # Since there are multiple workflows, uncomment next line to ignore bower_components 228 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 229 | #bower_components/ 230 | 231 | # RIA/Silverlight projects 232 | Generated_Code/ 233 | 234 | # Backup & report files from converting an old project file 235 | # to a newer Visual Studio version. Backup files are not needed, 236 | # because we have git ;-) 237 | _UpgradeReport_Files/ 238 | Backup*/ 239 | UpgradeLog*.XML 240 | UpgradeLog*.htm 241 | ServiceFabricBackup/ 242 | *.rptproj.bak 243 | 244 | # SQL Server files 245 | *.mdf 246 | *.ldf 247 | *.ndf 248 | 249 | # Business Intelligence projects 250 | *.rdl.data 251 | *.bim.layout 252 | *.bim_*.settings 253 | *.rptproj.rsuser 254 | 255 | # Microsoft Fakes 256 | FakesAssemblies/ 257 | 258 | # GhostDoc plugin setting file 259 | *.GhostDoc.xml 260 | 261 | # Node.js Tools for Visual Studio 262 | .ntvs_analysis.dat 263 | node_modules/ 264 | 265 | # Visual Studio 6 build log 266 | *.plg 267 | 268 | # Visual Studio 6 workspace options file 269 | *.opt 270 | 271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 272 | *.vbw 273 | 274 | # Visual Studio LightSwitch build output 275 | **/*.HTMLClient/GeneratedArtifacts 276 | **/*.DesktopClient/GeneratedArtifacts 277 | **/*.DesktopClient/ModelManifest.xml 278 | **/*.Server/GeneratedArtifacts 279 | **/*.Server/ModelManifest.xml 280 | _Pvt_Extensions 281 | 282 | # Paket dependency manager 283 | .paket/paket.exe 284 | paket-files/ 285 | 286 | # FAKE - F# Make 287 | .fake/ 288 | 289 | # JetBrains Rider 290 | .idea/ 291 | *.sln.iml 292 | 293 | # CodeRush 294 | .cr/ 295 | 296 | # Python Tools for Visual Studio (PTVS) 297 | __pycache__/ 298 | *.pyc 299 | 300 | # Cake - Uncomment if you are using it 301 | # tools/** 302 | # !tools/packages.config 303 | 304 | # Tabs Studio 305 | *.tss 306 | 307 | # Telerik's JustMock configuration file 308 | *.jmconfig 309 | 310 | # BizTalk build output 311 | *.btp.cs 312 | *.btm.cs 313 | *.odx.cs 314 | *.xsd.cs 315 | 316 | # OpenCover UI analysis results 317 | OpenCover/ 318 | 319 | # Azure Stream Analytics local run output 320 | ASALocalRun/ 321 | 322 | # MSBuild Binary and Structured Log 323 | *.binlog 324 | 325 | # NVidia Nsight GPU debugger configuration file 326 | *.nvuser 327 | 328 | # MFractors (Xamarin productivity tool) working folder 329 | .mfractor/ 330 | 331 | # Local History for Visual Studio 332 | .localhistory/ 333 | /help 334 | -------------------------------------------------------------------------------- /Documentation.chm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephan1827/modbusTCP-DotNET/1dbfa4d7a5e764aab99f647ec215e490ee3b8ed1/Documentation.chm -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Stephan Stricker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ModbusSampleCommon/App.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephan1827/modbusTCP-DotNET/1dbfa4d7a5e764aab99f647ec215e490ee3b8ed1/ModbusSampleCommon/App.ico -------------------------------------------------------------------------------- /ModbusSampleCommon/AssemblyInfo.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephan1827/modbusTCP-DotNET/1dbfa4d7a5e764aab99f647ec215e490ee3b8ed1/ModbusSampleCommon/AssemblyInfo.cs -------------------------------------------------------------------------------- /ModbusSampleCommon/Modbus Sample Common.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.27703.2047 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModbusTester", "ModbusTester.csproj", "{3538217B-6E35-4E2F-8B4D-8A8FBDEA1BB6}" 6 | ProjectSection(ProjectDependencies) = postProject 7 | {78A23B5B-CB72-4EDB-B594-5370D086197D} = {78A23B5B-CB72-4EDB-B594-5370D086197D} 8 | EndProjectSection 9 | EndProject 10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModbusTCP", "..\ModbusTCP\ModbusTCP\ModbusTCP.csproj", "{78A23B5B-CB72-4EDB-B594-5370D086197D}" 11 | EndProject 12 | Global 13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 14 | Debug|Any CPU = Debug|Any CPU 15 | Release|Any CPU = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 18 | {3538217B-6E35-4E2F-8B4D-8A8FBDEA1BB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {3538217B-6E35-4E2F-8B4D-8A8FBDEA1BB6}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {3538217B-6E35-4E2F-8B4D-8A8FBDEA1BB6}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {3538217B-6E35-4E2F-8B4D-8A8FBDEA1BB6}.Release|Any CPU.Build.0 = Release|Any CPU 22 | {78A23B5B-CB72-4EDB-B594-5370D086197D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 23 | {78A23B5B-CB72-4EDB-B594-5370D086197D}.Debug|Any CPU.Build.0 = Debug|Any CPU 24 | {78A23B5B-CB72-4EDB-B594-5370D086197D}.Release|Any CPU.ActiveCfg = Release|Any CPU 25 | {78A23B5B-CB72-4EDB-B594-5370D086197D}.Release|Any CPU.Build.0 = Release|Any CPU 26 | EndGlobalSection 27 | GlobalSection(SolutionProperties) = preSolution 28 | HideSolutionNode = FALSE 29 | EndGlobalSection 30 | GlobalSection(ExtensibilityGlobals) = postSolution 31 | SolutionGuid = {225DA569-AA74-4570-B8CD-6D087389131E} 32 | EndGlobalSection 33 | EndGlobal 34 | -------------------------------------------------------------------------------- /ModbusSampleCommon/ModbusTester.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Local 5 | 8.0.50727 6 | 2.0 7 | {3538217B-6E35-4E2F-8B4D-8A8FBDEA1BB6} 8 | Debug 9 | AnyCPU 10 | App.ico 11 | 12 | 13 | ModbusTester 14 | 15 | 16 | JScript 17 | Grid 18 | IE50 19 | false 20 | WinExe 21 | ModbusTester 22 | OnBuildSuccess 23 | Modbus.frmStart 24 | 25 | 26 | 27 | 28 | v2.0 29 | 2.0 30 | 31 | 32 | bin\Debug\ 33 | false 34 | 285212672 35 | false 36 | 37 | 38 | DEBUG;TRACE 39 | 40 | 41 | true 42 | 4096 43 | false 44 | 45 | 46 | false 47 | false 48 | false 49 | false 50 | 4 51 | full 52 | prompt 53 | 54 | 55 | bin\Release\ 56 | false 57 | 285212672 58 | false 59 | 60 | 61 | TRACE 62 | 63 | 64 | false 65 | 4096 66 | false 67 | 68 | 69 | true 70 | false 71 | false 72 | false 73 | 4 74 | none 75 | prompt 76 | 77 | 78 | 79 | System 80 | 81 | 82 | System.Data 83 | 84 | 85 | System.Drawing 86 | 87 | 88 | System.Windows.Forms 89 | 90 | 91 | System.XML 92 | 93 | 94 | 95 | 96 | 97 | Code 98 | 99 | 100 | Form 101 | 102 | 103 | frmStart.cs 104 | 105 | 106 | 107 | 108 | {78a23b5b-cb72-4edb-b594-5370d086197d} 109 | ModbusTCP 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | -------------------------------------------------------------------------------- /ModbusSampleCommon/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephan1827/modbusTCP-DotNET/1dbfa4d7a5e764aab99f647ec215e490ee3b8ed1/ModbusSampleCommon/Thumbs.db -------------------------------------------------------------------------------- /ModbusSampleCommon/frmStart.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Drawing; 4 | using System.Collections; 5 | using System.ComponentModel; 6 | using System.Windows.Forms; 7 | using System.Data; 8 | using ModbusTCP; 9 | 10 | namespace Modbus 11 | { 12 | public class frmStart : System.Windows.Forms.Form 13 | { 14 | private ModbusTCP.Master MBmaster; 15 | private TextBox txtData; 16 | private Label labData; 17 | private byte[] data; 18 | 19 | private System.Windows.Forms.GroupBox grpData; 20 | private System.Windows.Forms.Label label1; 21 | private System.Windows.Forms.Button btnConnect; 22 | private System.Windows.Forms.TextBox txtIP; 23 | private System.Windows.Forms.Button btnReadDisInp; 24 | private System.Windows.Forms.Label label3; 25 | private System.Windows.Forms.TextBox txtSize; 26 | private System.Windows.Forms.Label label2; 27 | private System.Windows.Forms.TextBox txtStartAdress; 28 | private System.Windows.Forms.Button btnReadCoils; 29 | private System.Windows.Forms.GroupBox grpStart; 30 | private System.Windows.Forms.GroupBox grpExchange; 31 | private System.Windows.Forms.Button btnReadHoldReg; 32 | private System.Windows.Forms.Button btnReadInpReg; 33 | private System.Windows.Forms.Button btnWriteSingleCoil; 34 | private System.Windows.Forms.GroupBox groupBox1; 35 | private System.Windows.Forms.RadioButton radBits; 36 | private System.Windows.Forms.RadioButton radBytes; 37 | private System.Windows.Forms.RadioButton radWord; 38 | private System.Windows.Forms.Button btnWriteSingleReg; 39 | private System.Windows.Forms.Button btnWriteMultipleCoils; 40 | private System.Windows.Forms.Button btnWriteMultipleReg; 41 | private Label label4; 42 | private TextBox txtUnit; 43 | private System.ComponentModel.IContainer components; 44 | 45 | public frmStart() 46 | { 47 | InitializeComponent(); 48 | } 49 | 50 | protected override void Dispose( bool disposing ) 51 | { 52 | if( disposing ) 53 | { 54 | if (components != null) 55 | { 56 | components.Dispose(); 57 | } 58 | } 59 | base.Dispose( disposing ); 60 | } 61 | 62 | #region Vom Windows Form-Designer generierter Code 63 | /// 64 | /// Erforderliche Methode für die Designerunterstützung. 65 | /// Der Inhalt der Methode darf nicht mit dem Code-Editor geändert werden. 66 | /// 67 | private void InitializeComponent() 68 | { 69 | this.grpData = new System.Windows.Forms.GroupBox(); 70 | this.grpStart = new System.Windows.Forms.GroupBox(); 71 | this.label1 = new System.Windows.Forms.Label(); 72 | this.btnConnect = new System.Windows.Forms.Button(); 73 | this.txtIP = new System.Windows.Forms.TextBox(); 74 | this.grpExchange = new System.Windows.Forms.GroupBox(); 75 | this.label4 = new System.Windows.Forms.Label(); 76 | this.txtUnit = new System.Windows.Forms.TextBox(); 77 | this.btnWriteMultipleReg = new System.Windows.Forms.Button(); 78 | this.btnWriteMultipleCoils = new System.Windows.Forms.Button(); 79 | this.btnWriteSingleReg = new System.Windows.Forms.Button(); 80 | this.groupBox1 = new System.Windows.Forms.GroupBox(); 81 | this.radWord = new System.Windows.Forms.RadioButton(); 82 | this.radBytes = new System.Windows.Forms.RadioButton(); 83 | this.radBits = new System.Windows.Forms.RadioButton(); 84 | this.btnWriteSingleCoil = new System.Windows.Forms.Button(); 85 | this.btnReadInpReg = new System.Windows.Forms.Button(); 86 | this.btnReadHoldReg = new System.Windows.Forms.Button(); 87 | this.btnReadDisInp = new System.Windows.Forms.Button(); 88 | this.label3 = new System.Windows.Forms.Label(); 89 | this.txtSize = new System.Windows.Forms.TextBox(); 90 | this.label2 = new System.Windows.Forms.Label(); 91 | this.txtStartAdress = new System.Windows.Forms.TextBox(); 92 | this.btnReadCoils = new System.Windows.Forms.Button(); 93 | this.grpStart.SuspendLayout(); 94 | this.grpExchange.SuspendLayout(); 95 | this.groupBox1.SuspendLayout(); 96 | this.SuspendLayout(); 97 | // 98 | // grpData 99 | // 100 | this.grpData.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 101 | | System.Windows.Forms.AnchorStyles.Left) 102 | | System.Windows.Forms.AnchorStyles.Right))); 103 | this.grpData.Location = new System.Drawing.Point(8, 230); 104 | this.grpData.Name = "grpData"; 105 | this.grpData.Size = new System.Drawing.Size(825, 234); 106 | this.grpData.TabIndex = 9; 107 | this.grpData.TabStop = false; 108 | this.grpData.Text = "Data"; 109 | this.grpData.Visible = false; 110 | // 111 | // grpStart 112 | // 113 | this.grpStart.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 114 | | System.Windows.Forms.AnchorStyles.Right))); 115 | this.grpStart.Controls.Add(this.label1); 116 | this.grpStart.Controls.Add(this.btnConnect); 117 | this.grpStart.Controls.Add(this.txtIP); 118 | this.grpStart.Location = new System.Drawing.Point(8, 8); 119 | this.grpStart.Name = "grpStart"; 120 | this.grpStart.Size = new System.Drawing.Size(825, 64); 121 | this.grpStart.TabIndex = 11; 122 | this.grpStart.TabStop = false; 123 | this.grpStart.Text = "Start communication"; 124 | // 125 | // label1 126 | // 127 | this.label1.Location = new System.Drawing.Point(16, 32); 128 | this.label1.Name = "label1"; 129 | this.label1.Size = new System.Drawing.Size(88, 16); 130 | this.label1.TabIndex = 7; 131 | this.label1.Text = "IP Address"; 132 | // 133 | // btnConnect 134 | // 135 | this.btnConnect.Location = new System.Drawing.Point(224, 24); 136 | this.btnConnect.Name = "btnConnect"; 137 | this.btnConnect.Size = new System.Drawing.Size(104, 33); 138 | this.btnConnect.TabIndex = 6; 139 | this.btnConnect.Text = "Connect"; 140 | this.btnConnect.Click += new System.EventHandler(this.btnConnect_Click); 141 | // 142 | // txtIP 143 | // 144 | this.txtIP.Location = new System.Drawing.Point(112, 29); 145 | this.txtIP.Name = "txtIP"; 146 | this.txtIP.Size = new System.Drawing.Size(104, 22); 147 | this.txtIP.TabIndex = 5; 148 | this.txtIP.Text = "192.168.100.1"; 149 | this.txtIP.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; 150 | // 151 | // grpExchange 152 | // 153 | this.grpExchange.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 154 | | System.Windows.Forms.AnchorStyles.Right))); 155 | this.grpExchange.Controls.Add(this.label4); 156 | this.grpExchange.Controls.Add(this.txtUnit); 157 | this.grpExchange.Controls.Add(this.btnWriteMultipleReg); 158 | this.grpExchange.Controls.Add(this.btnWriteMultipleCoils); 159 | this.grpExchange.Controls.Add(this.btnWriteSingleReg); 160 | this.grpExchange.Controls.Add(this.groupBox1); 161 | this.grpExchange.Controls.Add(this.btnWriteSingleCoil); 162 | this.grpExchange.Controls.Add(this.btnReadInpReg); 163 | this.grpExchange.Controls.Add(this.btnReadHoldReg); 164 | this.grpExchange.Controls.Add(this.btnReadDisInp); 165 | this.grpExchange.Controls.Add(this.label3); 166 | this.grpExchange.Controls.Add(this.txtSize); 167 | this.grpExchange.Controls.Add(this.label2); 168 | this.grpExchange.Controls.Add(this.txtStartAdress); 169 | this.grpExchange.Controls.Add(this.btnReadCoils); 170 | this.grpExchange.Location = new System.Drawing.Point(8, 80); 171 | this.grpExchange.Name = "grpExchange"; 172 | this.grpExchange.Size = new System.Drawing.Size(825, 144); 173 | this.grpExchange.TabIndex = 12; 174 | this.grpExchange.TabStop = false; 175 | this.grpExchange.Text = "Data exhange"; 176 | this.grpExchange.Visible = false; 177 | // 178 | // label4 179 | // 180 | this.label4.Location = new System.Drawing.Point(16, 31); 181 | this.label4.Name = "label4"; 182 | this.label4.Size = new System.Drawing.Size(88, 16); 183 | this.label4.TabIndex = 25; 184 | this.label4.Text = "Unit"; 185 | // 186 | // txtUnit 187 | // 188 | this.txtUnit.Location = new System.Drawing.Point(104, 29); 189 | this.txtUnit.Name = "txtUnit"; 190 | this.txtUnit.Size = new System.Drawing.Size(60, 22); 191 | this.txtUnit.TabIndex = 24; 192 | this.txtUnit.Text = "0"; 193 | this.txtUnit.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; 194 | // 195 | // btnWriteMultipleReg 196 | // 197 | this.btnWriteMultipleReg.Location = new System.Drawing.Point(688, 88); 198 | this.btnWriteMultipleReg.Name = "btnWriteMultipleReg"; 199 | this.btnWriteMultipleReg.Size = new System.Drawing.Size(104, 50); 200 | this.btnWriteMultipleReg.TabIndex = 23; 201 | this.btnWriteMultipleReg.Text = "Write multiple register"; 202 | this.btnWriteMultipleReg.Click += new System.EventHandler(this.btnWriteMultipleReg_Click); 203 | // 204 | // btnWriteMultipleCoils 205 | // 206 | this.btnWriteMultipleCoils.Location = new System.Drawing.Point(688, 32); 207 | this.btnWriteMultipleCoils.Name = "btnWriteMultipleCoils"; 208 | this.btnWriteMultipleCoils.Size = new System.Drawing.Size(104, 49); 209 | this.btnWriteMultipleCoils.TabIndex = 22; 210 | this.btnWriteMultipleCoils.Text = "Write multiple coils"; 211 | this.btnWriteMultipleCoils.Click += new System.EventHandler(this.btnWriteMultipleCoils_Click); 212 | // 213 | // btnWriteSingleReg 214 | // 215 | this.btnWriteSingleReg.Location = new System.Drawing.Point(568, 88); 216 | this.btnWriteSingleReg.Name = "btnWriteSingleReg"; 217 | this.btnWriteSingleReg.Size = new System.Drawing.Size(104, 50); 218 | this.btnWriteSingleReg.TabIndex = 21; 219 | this.btnWriteSingleReg.Text = "Write single register"; 220 | this.btnWriteSingleReg.Click += new System.EventHandler(this.btnWriteSingleReg_Click); 221 | // 222 | // groupBox1 223 | // 224 | this.groupBox1.Controls.Add(this.radWord); 225 | this.groupBox1.Controls.Add(this.radBytes); 226 | this.groupBox1.Controls.Add(this.radBits); 227 | this.groupBox1.Location = new System.Drawing.Point(192, 24); 228 | this.groupBox1.Name = "groupBox1"; 229 | this.groupBox1.Size = new System.Drawing.Size(104, 104); 230 | this.groupBox1.TabIndex = 20; 231 | this.groupBox1.TabStop = false; 232 | this.groupBox1.Text = "Show as"; 233 | // 234 | // radWord 235 | // 236 | this.radWord.Location = new System.Drawing.Point(16, 72); 237 | this.radWord.Name = "radWord"; 238 | this.radWord.Size = new System.Drawing.Size(80, 24); 239 | this.radWord.TabIndex = 2; 240 | this.radWord.Text = "Word"; 241 | this.radWord.CheckedChanged += new System.EventHandler(this.ShowAs); 242 | // 243 | // radBytes 244 | // 245 | this.radBytes.Location = new System.Drawing.Point(16, 48); 246 | this.radBytes.Name = "radBytes"; 247 | this.radBytes.Size = new System.Drawing.Size(80, 24); 248 | this.radBytes.TabIndex = 1; 249 | this.radBytes.Text = "Bytes"; 250 | this.radBytes.CheckedChanged += new System.EventHandler(this.ShowAs); 251 | // 252 | // radBits 253 | // 254 | this.radBits.Location = new System.Drawing.Point(16, 24); 255 | this.radBits.Name = "radBits"; 256 | this.radBits.Size = new System.Drawing.Size(80, 24); 257 | this.radBits.TabIndex = 0; 258 | this.radBits.Text = "Bits"; 259 | this.radBits.CheckedChanged += new System.EventHandler(this.ShowAs); 260 | // 261 | // btnWriteSingleCoil 262 | // 263 | this.btnWriteSingleCoil.Location = new System.Drawing.Point(568, 32); 264 | this.btnWriteSingleCoil.Name = "btnWriteSingleCoil"; 265 | this.btnWriteSingleCoil.Size = new System.Drawing.Size(104, 49); 266 | this.btnWriteSingleCoil.TabIndex = 19; 267 | this.btnWriteSingleCoil.Text = "Write single coil"; 268 | this.btnWriteSingleCoil.Click += new System.EventHandler(this.btnWriteSingleCoil_Click); 269 | // 270 | // btnReadInpReg 271 | // 272 | this.btnReadInpReg.Location = new System.Drawing.Point(448, 88); 273 | this.btnReadInpReg.Name = "btnReadInpReg"; 274 | this.btnReadInpReg.Size = new System.Drawing.Size(104, 50); 275 | this.btnReadInpReg.TabIndex = 18; 276 | this.btnReadInpReg.Text = "Read input register"; 277 | this.btnReadInpReg.Click += new System.EventHandler(this.btnReadInpReg_Click); 278 | // 279 | // btnReadHoldReg 280 | // 281 | this.btnReadHoldReg.Location = new System.Drawing.Point(448, 32); 282 | this.btnReadHoldReg.Name = "btnReadHoldReg"; 283 | this.btnReadHoldReg.Size = new System.Drawing.Size(104, 49); 284 | this.btnReadHoldReg.TabIndex = 17; 285 | this.btnReadHoldReg.Text = "Read holding register"; 286 | this.btnReadHoldReg.Click += new System.EventHandler(this.btnReadHoldReg_Click); 287 | // 288 | // btnReadDisInp 289 | // 290 | this.btnReadDisInp.Location = new System.Drawing.Point(328, 88); 291 | this.btnReadDisInp.Name = "btnReadDisInp"; 292 | this.btnReadDisInp.Size = new System.Drawing.Size(104, 50); 293 | this.btnReadDisInp.TabIndex = 16; 294 | this.btnReadDisInp.Text = "Read discrete inputs"; 295 | this.btnReadDisInp.Click += new System.EventHandler(this.btnReadDisInp_Click); 296 | // 297 | // label3 298 | // 299 | this.label3.Location = new System.Drawing.Point(16, 90); 300 | this.label3.Name = "label3"; 301 | this.label3.Size = new System.Drawing.Size(88, 16); 302 | this.label3.TabIndex = 15; 303 | this.label3.Text = "Size"; 304 | // 305 | // txtSize 306 | // 307 | this.txtSize.Location = new System.Drawing.Point(104, 90); 308 | this.txtSize.Name = "txtSize"; 309 | this.txtSize.Size = new System.Drawing.Size(60, 22); 310 | this.txtSize.TabIndex = 14; 311 | this.txtSize.Text = "100"; 312 | this.txtSize.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; 313 | // 314 | // label2 315 | // 316 | this.label2.Location = new System.Drawing.Point(16, 61); 317 | this.label2.Name = "label2"; 318 | this.label2.Size = new System.Drawing.Size(88, 16); 319 | this.label2.TabIndex = 13; 320 | this.label2.Text = "Start Adress"; 321 | // 322 | // txtStartAdress 323 | // 324 | this.txtStartAdress.Location = new System.Drawing.Point(104, 59); 325 | this.txtStartAdress.Name = "txtStartAdress"; 326 | this.txtStartAdress.Size = new System.Drawing.Size(60, 22); 327 | this.txtStartAdress.TabIndex = 12; 328 | this.txtStartAdress.Text = "0"; 329 | this.txtStartAdress.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; 330 | // 331 | // btnReadCoils 332 | // 333 | this.btnReadCoils.Location = new System.Drawing.Point(328, 32); 334 | this.btnReadCoils.Name = "btnReadCoils"; 335 | this.btnReadCoils.Size = new System.Drawing.Size(104, 49); 336 | this.btnReadCoils.TabIndex = 11; 337 | this.btnReadCoils.Text = "Read coils"; 338 | this.btnReadCoils.CursorChanged += new System.EventHandler(this.btnReadCoils_Click); 339 | this.btnReadCoils.Click += new System.EventHandler(this.btnReadCoils_Click); 340 | // 341 | // frmStart 342 | // 343 | this.AutoScaleBaseSize = new System.Drawing.Size(6, 15); 344 | this.ClientSize = new System.Drawing.Size(841, 471); 345 | this.Controls.Add(this.grpExchange); 346 | this.Controls.Add(this.grpStart); 347 | this.Controls.Add(this.grpData); 348 | this.Name = "frmStart"; 349 | this.Text = "ModbusTCP Tester V1.3"; 350 | this.Closing += new System.ComponentModel.CancelEventHandler(this.frmStart_Closing); 351 | this.Load += new System.EventHandler(this.frmStart_Load); 352 | this.SizeChanged += new System.EventHandler(this.frmStart_Resize); 353 | this.grpStart.ResumeLayout(false); 354 | this.grpStart.PerformLayout(); 355 | this.grpExchange.ResumeLayout(false); 356 | this.grpExchange.PerformLayout(); 357 | this.groupBox1.ResumeLayout(false); 358 | this.ResumeLayout(false); 359 | 360 | } 361 | #endregion 362 | 363 | /// 364 | /// Der Haupteinstiegspunkt für die Anwendung. 365 | /// 366 | [STAThread] 367 | static void Main() 368 | { 369 | Application.Run(new frmStart()); 370 | } 371 | 372 | 373 | // ------------------------------------------------------------------------ 374 | // Programm start 375 | // ------------------------------------------------------------------------ 376 | private void frmStart_Load(object sender, System.EventArgs e) 377 | { 378 | // Set standard format byte, make some textboxes 379 | radBytes.Checked = true; 380 | data = new byte[0]; 381 | ResizeData(); 382 | } 383 | 384 | // ------------------------------------------------------------------------ 385 | // Programm stop 386 | // ------------------------------------------------------------------------ 387 | private void frmStart_Closing(object sender, System.ComponentModel.CancelEventArgs e) 388 | { 389 | if(MBmaster != null) 390 | { 391 | MBmaster.Dispose(); 392 | MBmaster = null; 393 | } 394 | Application.Exit(); 395 | } 396 | 397 | // ------------------------------------------------------------------------ 398 | // Button connect 399 | // ------------------------------------------------------------------------ 400 | private void btnConnect_Click(object sender, System.EventArgs e) 401 | { 402 | try 403 | { 404 | // Create new modbus master and add event functions 405 | MBmaster = new Master(txtIP.Text, 502, true); 406 | MBmaster.OnResponseData += new ModbusTCP.Master.ResponseData(MBmaster_OnResponseData); 407 | MBmaster.OnException += new ModbusTCP.Master.ExceptionData(MBmaster_OnException); 408 | // Show additional fields, enable watchdog 409 | grpExchange.Visible = true; 410 | grpData.Visible = true; 411 | } 412 | catch(SystemException error) 413 | { 414 | MessageBox.Show(error.Message); 415 | } 416 | } 417 | 418 | // ------------------------------------------------------------------------ 419 | // Button read coils 420 | // ------------------------------------------------------------------------ 421 | private void btnReadCoils_Click(object sender, System.EventArgs e) 422 | { 423 | ushort ID = 1; 424 | byte unit = Convert.ToByte(txtUnit.Text); 425 | ushort StartAddress = ReadStartAdr(); 426 | UInt16 Length = Convert.ToUInt16(txtSize.Text); 427 | 428 | MBmaster.ReadCoils(ID, unit, StartAddress, Length); 429 | } 430 | 431 | // ------------------------------------------------------------------------ 432 | // Button read discrete inputs 433 | // ------------------------------------------------------------------------ 434 | private void btnReadDisInp_Click(object sender, System.EventArgs e) 435 | { 436 | ushort ID = 2; 437 | byte unit = Convert.ToByte(txtUnit.Text); 438 | ushort StartAddress = ReadStartAdr(); 439 | UInt16 Length = Convert.ToUInt16(txtSize.Text); 440 | 441 | MBmaster.ReadDiscreteInputs(ID, unit, StartAddress, Length); 442 | } 443 | 444 | // ------------------------------------------------------------------------ 445 | // Button read holding register 446 | // ------------------------------------------------------------------------ 447 | private void btnReadHoldReg_Click(object sender, System.EventArgs e) 448 | { 449 | ushort ID = 3; 450 | byte unit = Convert.ToByte(txtUnit.Text); 451 | ushort StartAddress = ReadStartAdr(); 452 | UInt16 Length = Convert.ToUInt16(txtSize.Text); 453 | 454 | MBmaster.ReadHoldingRegister(ID, unit, StartAddress, Length); 455 | } 456 | 457 | // ------------------------------------------------------------------------ 458 | // Button read holding register 459 | // ------------------------------------------------------------------------ 460 | private void btnReadInpReg_Click(object sender, System.EventArgs e) 461 | { 462 | ushort ID = 4; 463 | byte unit = Convert.ToByte(txtUnit.Text); 464 | ushort StartAddress = ReadStartAdr(); 465 | UInt16 Length = Convert.ToUInt16(txtSize.Text); 466 | 467 | MBmaster.ReadInputRegister(ID, unit, StartAddress, Length); 468 | } 469 | 470 | // ------------------------------------------------------------------------ 471 | // Button write single coil 472 | // ------------------------------------------------------------------------ 473 | private void btnWriteSingleCoil_Click(object sender, System.EventArgs e) 474 | { 475 | ushort ID = 5; 476 | byte unit = Convert.ToByte(txtUnit.Text); 477 | ushort StartAddress = ReadStartAdr(); 478 | 479 | data = GetData(1); 480 | txtSize.Text = "1"; 481 | 482 | MBmaster.WriteSingleCoils(ID, unit, StartAddress, Convert.ToBoolean(data[0])); 483 | } 484 | 485 | // ------------------------------------------------------------------------ 486 | // Button write multiple coils 487 | // ------------------------------------------------------------------------ 488 | private void btnWriteMultipleCoils_Click(object sender, System.EventArgs e) 489 | { 490 | ushort ID = 6; 491 | byte unit = Convert.ToByte(txtUnit.Text); 492 | ushort StartAddress = ReadStartAdr(); 493 | UInt16 Length = Convert.ToUInt16(txtSize.Text); 494 | 495 | data = GetData(Convert.ToUInt16(txtSize.Text)); 496 | MBmaster.WriteMultipleCoils(ID, unit, StartAddress, Length, data); 497 | } 498 | 499 | // ------------------------------------------------------------------------ 500 | // Button write single register 501 | // ------------------------------------------------------------------------ 502 | private void btnWriteSingleReg_Click(object sender, System.EventArgs e) 503 | { 504 | ushort ID = 7; 505 | byte unit = Convert.ToByte(txtUnit.Text); 506 | ushort StartAddress = ReadStartAdr(); 507 | 508 | if (radBits.Checked) data = GetData(16); 509 | else if (radBytes.Checked) data = GetData(2); 510 | else data = GetData(1); 511 | txtSize.Text = "1"; 512 | txtData.Text = data[0].ToString(); 513 | 514 | MBmaster.WriteSingleRegister(ID, unit, StartAddress, data); 515 | } 516 | 517 | // ------------------------------------------------------------------------ 518 | // Button write multiple register 519 | // ------------------------------------------------------------------------ 520 | private void btnWriteMultipleReg_Click(object sender, System.EventArgs e) 521 | { 522 | ushort ID = 8; 523 | byte unit = Convert.ToByte(txtUnit.Text); 524 | ushort StartAddress = ReadStartAdr(); 525 | 526 | data = GetData(Convert.ToByte(txtSize.Text)); 527 | MBmaster.WriteMultipleRegister(ID, unit, StartAddress, data); 528 | } 529 | 530 | // ------------------------------------------------------------------------ 531 | // Event for response data 532 | // ------------------------------------------------------------------------ 533 | private void MBmaster_OnResponseData(ushort ID, byte unit, byte function, byte[] values) 534 | { 535 | // ------------------------------------------------------------------ 536 | // Seperate calling threads 537 | if (this.InvokeRequired) 538 | { 539 | this.BeginInvoke(new Master.ResponseData(MBmaster_OnResponseData), new object[] { ID, unit, function, values }); 540 | return; 541 | } 542 | 543 | // ------------------------------------------------------------------------ 544 | // Identify requested data 545 | switch(ID) 546 | { 547 | case 1: 548 | grpData.Text = "Read coils"; 549 | data = values; 550 | ShowAs(null, null); 551 | break; 552 | case 2: 553 | grpData.Text = "Read discrete inputs"; 554 | data = values; 555 | ShowAs(null, null); 556 | break; 557 | case 3: 558 | grpData.Text = "Read holding register"; 559 | data = values; 560 | ShowAs(null, null); 561 | break; 562 | case 4: 563 | grpData.Text = "Read input register"; 564 | data = values; 565 | ShowAs(null, null); 566 | break; 567 | case 5: 568 | grpData.Text = "Write single coil"; 569 | break; 570 | case 6: 571 | grpData.Text = "Write multiple coils"; 572 | break; 573 | case 7: 574 | grpData.Text = "Write single register"; 575 | break; 576 | case 8: 577 | grpData.Text = "Write multiple register"; 578 | break; 579 | } 580 | } 581 | 582 | // ------------------------------------------------------------------------ 583 | // Modbus TCP slave exception 584 | // ------------------------------------------------------------------------ 585 | private void MBmaster_OnException(ushort id, byte unit, byte function, byte exception) 586 | { 587 | string exc = "Modbus says error: "; 588 | switch(exception) 589 | { 590 | case Master.excIllegalFunction: exc += "Illegal function!"; break; 591 | case Master.excIllegalDataAdr: exc += "Illegal data adress!"; break; 592 | case Master.excIllegalDataVal: exc += "Illegal data value!"; break; 593 | case Master.excSlaveDeviceFailure: exc += "Slave device failure!"; break; 594 | case Master.excAck: exc += "Acknoledge!"; break; 595 | case Master.excGatePathUnavailable: exc += "Gateway path unavailbale!"; break; 596 | case Master.excExceptionTimeout: exc += "Slave timed out!"; break; 597 | case Master.excExceptionConnectionLost: exc += "Connection is lost!"; break; 598 | case Master.excExceptionNotConnected: exc += "Not connected!"; break; 599 | } 600 | 601 | MessageBox.Show(exc, "Modbus slave exception"); 602 | } 603 | 604 | // ------------------------------------------------------------------------ 605 | // Generate new number of text boxes 606 | // ------------------------------------------------------------------------ 607 | private void ResizeData() 608 | { 609 | // Create as many textboxes as fit into window 610 | grpData.Controls.Clear(); 611 | int x = 0; 612 | int y = 10; 613 | int z = 20; 614 | while(y < grpData.Size.Width - 100) 615 | { 616 | labData = new Label(); 617 | grpData.Controls.Add(labData); 618 | labData.Size = new System.Drawing.Size(30, 20); 619 | labData.Location = new System.Drawing.Point(y, z); 620 | labData.Text = Convert.ToString(x + 1); 621 | 622 | txtData = new TextBox(); 623 | grpData.Controls.Add(txtData); 624 | txtData.Size = new System.Drawing.Size(50, 20); 625 | txtData.Location = new System.Drawing.Point(y + 30, z); 626 | txtData.TextAlign = System.Windows.Forms.HorizontalAlignment.Right; 627 | txtData.Tag = x; 628 | 629 | x++; 630 | z = z + txtData.Size.Height + 5; 631 | if(z > grpData.Size.Height - 40) 632 | { 633 | y = y + 100; 634 | z = 20; 635 | } 636 | } 637 | } 638 | 639 | // ------------------------------------------------------------------------ 640 | // Resize form elements 641 | // ------------------------------------------------------------------------ 642 | private void frmStart_Resize(object sender, System.EventArgs e) 643 | { 644 | if(grpData.Visible == true) ResizeData(); 645 | } 646 | 647 | // ------------------------------------------------------------------------ 648 | // Read start address 649 | // ------------------------------------------------------------------------ 650 | private ushort ReadStartAdr() 651 | { 652 | // Convert hex numbers into decimal 653 | if(txtStartAdress.Text.IndexOf("0x", 0, txtStartAdress.Text.Length) == 0) 654 | { 655 | string str = txtStartAdress.Text.Replace("0x", ""); 656 | ushort hex = Convert.ToUInt16(str, 16); 657 | return hex; 658 | } 659 | else 660 | { 661 | return Convert.ToUInt16(txtStartAdress.Text); 662 | } 663 | } 664 | 665 | // ------------------------------------------------------------------------ 666 | // Read values from textboxes 667 | // ------------------------------------------------------------------------ 668 | private byte[] GetData(int num) 669 | { 670 | bool[] bits = new bool[num]; 671 | byte[] data = new Byte[num]; 672 | int[] word = new int[num]; 673 | 674 | // ------------------------------------------------------------------------ 675 | // Convert data from text boxes 676 | foreach(Control ctrl in grpData.Controls) 677 | { 678 | if (ctrl is TextBox) 679 | { 680 | int x = Convert.ToInt16(ctrl.Tag); 681 | if(radBits.Checked) 682 | { 683 | if((x <= bits.GetUpperBound(0)) && (ctrl.Text != "")) bits[x] = Convert.ToBoolean(Convert.ToByte(ctrl.Text)); 684 | else break; 685 | } 686 | if(radBytes.Checked) 687 | { 688 | if((x <= data.GetUpperBound(0)) && (ctrl.Text != "")) data[x] = Convert.ToByte(ctrl.Text); 689 | else break; 690 | } 691 | if(radWord.Checked) 692 | { 693 | if ((x <= word.GetUpperBound(0)) && (ctrl.Text != "")) 694 | { 695 | try { word[x] = Convert.ToInt16(ctrl.Text); } 696 | catch(SystemException) { word[x] = Convert.ToUInt16(ctrl.Text);}; 697 | } 698 | else break; 699 | } 700 | } 701 | } 702 | if(radBits.Checked) 703 | { 704 | int numBytes = (num / 8 + (num % 8 > 0 ? 1 : 0)); 705 | data = new Byte[numBytes]; 706 | BitArray bitArray = new BitArray(bits); 707 | bitArray.CopyTo(data, 0); 708 | } 709 | if(radWord.Checked) 710 | { 711 | data = new Byte[num*2]; 712 | for(int x=0;x 0); 748 | word = new int[length]; 749 | for(int x=0;x 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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /ModbusTCP/ModbusTCP.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual Studio 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ModbusTCP", "ModbusTCP\ModbusTCP.csproj", "{78A23B5B-CB72-4EDB-B594-5370D086197D}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|Any CPU = Debug|Any CPU 9 | Release|Any CPU = Release|Any CPU 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {78A23B5B-CB72-4EDB-B594-5370D086197D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 13 | {78A23B5B-CB72-4EDB-B594-5370D086197D}.Debug|Any CPU.Build.0 = Debug|Any CPU 14 | {78A23B5B-CB72-4EDB-B594-5370D086197D}.Release|Any CPU.ActiveCfg = Release|Any CPU 15 | {78A23B5B-CB72-4EDB-B594-5370D086197D}.Release|Any CPU.Build.0 = Release|Any CPU 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /ModbusTCP/ModbusTCP/ModbusTCP.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | AnyCPU 6 | 8.0.50727 7 | 2.0 8 | {78A23B5B-CB72-4EDB-B594-5370D086197D} 9 | Library 10 | Properties 11 | ModbusTCP 12 | ModbusTCP 13 | v2.0 14 | 15 | 16 | 2.0 17 | 18 | 19 | 20 | true 21 | full 22 | false 23 | bin\Debug\ 24 | DEBUG;TRACE 25 | prompt 26 | 4 27 | bin\Debug\ModbusTCP.xml 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | bin\Release\ModbusTCP.XML 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 58 | -------------------------------------------------------------------------------- /ModbusTCP/ModbusTCP/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ModbusTCP")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("BuR")] 12 | [assembly: AssemblyProduct("ModbusTCP")] 13 | [assembly: AssemblyCopyright("Copyright © 2009")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("a6057265-c585-4e57-89f9-613c7dccd52d")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("3.6")] 35 | [assembly: AssemblyFileVersion("3.6")] 36 | -------------------------------------------------------------------------------- /ModbusTCP/ModbusTCP/modbusTCP.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Text; 4 | using System.IO; 5 | using System.Net; 6 | using System.Net.Sockets; 7 | using System.Threading; 8 | 9 | /// 10 | /// Modbus TCP common driver class. 11 | /// 12 | namespace ModbusTCP 13 | { 14 | /// 15 | /// Modbus TCP common driver class. 16 | /// 17 | /// 18 | /// This class implements a modbus TCP master driver. It supports the following commands: 19 | /// 20 | /// Read coils 21 | /// Read discrete inputs 22 | /// Write single coil 23 | /// Write multiple cooils 24 | /// Read holding register 25 | /// Read input register 26 | /// Write single register 27 | /// Write multiple register 28 | /// 29 | /// All commands can be sent in synchronous or asynchronous mode. If a value is accessed 30 | /// in synchronous mode the program will stop and wait for slave to response. If the 31 | /// slave didn't answer within a specified time a timeout exception is called. 32 | /// The class uses multi threading for both synchronous and asynchronous access. For 33 | /// the communication two lines are created. This is necessary because the synchronous 34 | /// thread has to wait for a previous command to finish. 35 | /// The synchronous channel can be disabled during connection. This can be necessary when 36 | /// the slave only supports one connection. 37 | /// 38 | public class Master 39 | { 40 | // ------------------------------------------------------------------------ 41 | // Constants for access 42 | private const byte fctReadCoil = 1; 43 | private const byte fctReadDiscreteInputs = 2; 44 | private const byte fctReadHoldingRegister = 3; 45 | private const byte fctReadInputRegister = 4; 46 | private const byte fctWriteSingleCoil = 5; 47 | private const byte fctWriteSingleRegister = 6; 48 | private const byte fctWriteMultipleCoils = 15; 49 | private const byte fctWriteMultipleRegister = 16; 50 | private const byte fctReadWriteMultipleRegister = 23; 51 | 52 | /// Constant for exception illegal function. 53 | public const byte excIllegalFunction = 1; 54 | /// Constant for exception illegal data address. 55 | public const byte excIllegalDataAdr = 2; 56 | /// Constant for exception illegal data value. 57 | public const byte excIllegalDataVal = 3; 58 | /// Constant for exception slave device failure. 59 | public const byte excSlaveDeviceFailure = 4; 60 | /// Constant for exception acknowledge. This is triggered if a write request is executed while the watchdog has expired. 61 | public const byte excAck = 5; 62 | /// Constant for exception slave is busy/booting up. 63 | public const byte excSlaveIsBusy = 6; 64 | /// Constant for exception gate path unavailable. 65 | public const byte excGatePathUnavailable = 10; 66 | /// Constant for exception not connected. 67 | public const byte excExceptionNotConnected = 253; 68 | /// Constant for exception connection lost. 69 | public const byte excExceptionConnectionLost = 254; 70 | /// Constant for exception response timeout. 71 | public const byte excExceptionTimeout = 255; 72 | /// Constant for exception wrong offset. 73 | private const byte excExceptionOffset = 128; 74 | /// Constant for exception send failt. 75 | private const byte excSendFailt = 100; 76 | 77 | // ------------------------------------------------------------------------ 78 | // Private declarations 79 | private static ushort _timeout = 500; 80 | private static ushort _refresh = 10; 81 | private static bool _connected = false; 82 | private static bool _no_sync_connection = false; 83 | 84 | private Socket tcpAsyCl; 85 | private byte[] tcpAsyClBuffer = new byte[2048]; 86 | 87 | private Socket tcpSynCl; 88 | private byte[] tcpSynClBuffer = new byte[2048]; 89 | 90 | // ------------------------------------------------------------------------ 91 | /// Response data event. This event is called when new data arrives 92 | public delegate void ResponseData(ushort id, byte unit, byte function, byte[] data); 93 | /// Response data event. This event is called when new data arrives 94 | public event ResponseData OnResponseData; 95 | /// Exception data event. This event is called when the data is incorrect 96 | public delegate void ExceptionData(ushort id, byte unit, byte function, byte exception); 97 | /// Exception data event. This event is called when the data is incorrect 98 | public event ExceptionData OnException; 99 | 100 | // ------------------------------------------------------------------------ 101 | /// Response timeout. If the slave didn't answers within in this time an exception is called. 102 | /// The default value is 500ms. 103 | public ushort timeout 104 | { 105 | get { return _timeout; } 106 | set { _timeout = value; } 107 | } 108 | 109 | // ------------------------------------------------------------------------ 110 | /// Refresh timer for slave answer. The class is polling for answer every X ms. 111 | /// The default value is 10ms. 112 | public ushort refresh 113 | { 114 | get { return _refresh; } 115 | set { _refresh = value; } 116 | } 117 | 118 | // ------------------------------------------------------------------------ 119 | /// Displays the state of the synchronous channel 120 | /// True if channel was diabled during connection. 121 | public bool NoSyncConnection 122 | { 123 | get { return _no_sync_connection; } 124 | } 125 | 126 | // ------------------------------------------------------------------------ 127 | /// Shows if a connection is active. 128 | public bool connected 129 | { 130 | get { return _connected; } 131 | } 132 | 133 | // ------------------------------------------------------------------------ 134 | /// Create master instance without parameters. 135 | public Master() 136 | { 137 | } 138 | 139 | // ------------------------------------------------------------------------ 140 | /// Create master instance with parameters. 141 | /// IP adress of modbus slave. 142 | /// Port number of modbus slave. Usually port 502 is used. 143 | public Master(string ip, ushort port) 144 | { 145 | connect(ip, port, false); 146 | } 147 | 148 | // ------------------------------------------------------------------------ 149 | /// Create master instance with parameters. 150 | /// IP adress of modbus slave. 151 | /// Port number of modbus slave. Usually port 502 is used. 152 | /// Disable second connection for synchronous requests 153 | public Master(string ip, ushort port, bool no_sync_connection) 154 | { 155 | connect(ip, port, no_sync_connection); 156 | } 157 | 158 | // ------------------------------------------------------------------------ 159 | /// Start connection to slave. 160 | /// IP adress of modbus slave. 161 | /// Port number of modbus slave. Usually port 502 is used. 162 | /// Disable sencond connection for synchronous requests 163 | public void connect(string ip, ushort port, bool no_sync_connection) 164 | { 165 | try 166 | { 167 | IPAddress _ip; 168 | _no_sync_connection = no_sync_connection; 169 | if (IPAddress.TryParse(ip, out _ip) == false) 170 | { 171 | IPHostEntry hst = Dns.GetHostEntry(ip); 172 | ip = hst.AddressList[0].ToString(); 173 | } 174 | // ---------------------------------------------------------------- 175 | // Connect asynchronous client 176 | tcpAsyCl = new Socket(IPAddress.Parse(ip).AddressFamily, SocketType.Stream, ProtocolType.Tcp); 177 | tcpAsyCl.Connect(new IPEndPoint(IPAddress.Parse(ip), port)); 178 | tcpAsyCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout); 179 | tcpAsyCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout); 180 | tcpAsyCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, 1); 181 | // ---------------------------------------------------------------- 182 | // Connect synchronous client 183 | if (!_no_sync_connection) 184 | { 185 | tcpSynCl = new Socket(IPAddress.Parse(ip).AddressFamily, SocketType.Stream, ProtocolType.Tcp); 186 | tcpSynCl.Connect(new IPEndPoint(IPAddress.Parse(ip), port)); 187 | tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, _timeout); 188 | tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, _timeout); 189 | tcpSynCl.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.NoDelay, 1); 190 | } 191 | _connected = true; 192 | } 193 | catch (System.IO.IOException error) 194 | { 195 | _connected = false; 196 | throw (error); 197 | } 198 | } 199 | 200 | // ------------------------------------------------------------------------ 201 | /// Stop connection to slave. 202 | public void disconnect() 203 | { 204 | Dispose(); 205 | } 206 | 207 | // ------------------------------------------------------------------------ 208 | /// Destroy master instance. 209 | ~Master() 210 | { 211 | Dispose(); 212 | } 213 | 214 | // ------------------------------------------------------------------------ 215 | /// Destroy master instance 216 | public void Dispose() 217 | { 218 | if (tcpAsyCl != null) 219 | { 220 | if (tcpAsyCl.Connected) 221 | { 222 | try { tcpAsyCl.Shutdown(SocketShutdown.Both); } 223 | catch { } 224 | tcpAsyCl.Close(); 225 | } 226 | tcpAsyCl = null; 227 | } 228 | if (tcpSynCl != null) 229 | { 230 | if (tcpSynCl.Connected) 231 | { 232 | try { tcpSynCl.Shutdown(SocketShutdown.Both); } 233 | catch { } 234 | tcpSynCl.Close(); 235 | } 236 | tcpSynCl = null; 237 | } 238 | } 239 | 240 | internal void CallException(ushort id, byte unit, byte function, byte exception) 241 | { 242 | if ((tcpAsyCl == null) || (tcpSynCl == null && !_no_sync_connection)) return; 243 | if (exception == excExceptionConnectionLost) 244 | { 245 | tcpSynCl = null; 246 | tcpAsyCl = null; 247 | } 248 | if(OnException != null) OnException(id, unit, function, exception); 249 | } 250 | 251 | internal static UInt16 SwapUInt16(UInt16 inValue) 252 | { 253 | return (UInt16)(((inValue & 0xff00) >> 8) | 254 | ((inValue & 0x00ff) << 8)); 255 | } 256 | 257 | // ------------------------------------------------------------------------ 258 | /// Read coils from slave asynchronous. The result is given in the response function. 259 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 260 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 261 | /// Address from where the data read begins. 262 | /// Length of data. 263 | public void ReadCoils(ushort id, byte unit, ushort startAddress, ushort numInputs) 264 | { 265 | if (numInputs > 2000) 266 | { 267 | CallException(id, unit, fctReadCoil, excIllegalDataVal); 268 | return; 269 | } 270 | WriteAsyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadCoil), id); 271 | } 272 | 273 | // ------------------------------------------------------------------------ 274 | /// Read coils from slave synchronous. 275 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 276 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 277 | /// Address from where the data read begins. 278 | /// Length of data. 279 | /// Contains the result of function. 280 | public void ReadCoils(ushort id, byte unit, ushort startAddress, ushort numInputs, ref byte[] values) 281 | { 282 | if (numInputs > 2000) 283 | { 284 | CallException(id, unit, fctReadCoil, excIllegalDataVal); 285 | return; 286 | } 287 | values = WriteSyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadCoil), id); 288 | } 289 | 290 | // ------------------------------------------------------------------------ 291 | /// Read discrete inputs from slave asynchronous. The result is given in the response function. 292 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 293 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 294 | /// Address from where the data read begins. 295 | /// Length of data. 296 | public void ReadDiscreteInputs(ushort id, byte unit, ushort startAddress, ushort numInputs) 297 | { 298 | if (numInputs > 2000) 299 | { 300 | CallException(id, unit, fctReadDiscreteInputs, excIllegalDataVal); 301 | return; 302 | } 303 | WriteAsyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadDiscreteInputs), id); 304 | } 305 | 306 | // ------------------------------------------------------------------------ 307 | /// Read discrete inputs from slave synchronous. 308 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 309 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 310 | /// Address from where the data read begins. 311 | /// Length of data. 312 | /// Contains the result of function. 313 | public void ReadDiscreteInputs(ushort id, byte unit, ushort startAddress, ushort numInputs, ref byte[] values) 314 | { 315 | if (numInputs > 2000) 316 | { 317 | CallException(id, unit, fctReadDiscreteInputs, excIllegalDataVal); 318 | return; 319 | } 320 | values = WriteSyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadDiscreteInputs), id); 321 | } 322 | 323 | // ------------------------------------------------------------------------ 324 | /// Read holding registers from slave asynchronous. The result is given in the response function. 325 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 326 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 327 | /// Address from where the data read begins. 328 | /// Length of data. 329 | public void ReadHoldingRegister(ushort id, byte unit, ushort startAddress, ushort numInputs) 330 | { 331 | if (numInputs > 125) 332 | { 333 | CallException(id, unit, fctReadHoldingRegister, excIllegalDataVal); 334 | return; 335 | } 336 | WriteAsyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadHoldingRegister), id); 337 | } 338 | 339 | // ------------------------------------------------------------------------ 340 | /// Read holding registers from slave synchronous. 341 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 342 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 343 | /// Address from where the data read begins. 344 | /// Length of data. 345 | /// Contains the result of function. 346 | public void ReadHoldingRegister(ushort id, byte unit, ushort startAddress, ushort numInputs, ref byte[] values) 347 | { 348 | if (numInputs > 125) 349 | { 350 | CallException(id, unit, fctReadHoldingRegister, excIllegalDataVal); 351 | return; 352 | } 353 | values = WriteSyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadHoldingRegister), id); 354 | } 355 | 356 | // ------------------------------------------------------------------------ 357 | /// Read input registers from slave asynchronous. The result is given in the response function. 358 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 359 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 360 | /// Address from where the data read begins. 361 | /// Length of data. 362 | public void ReadInputRegister(ushort id, byte unit, ushort startAddress, ushort numInputs) 363 | { 364 | if (numInputs > 125) 365 | { 366 | CallException(id, unit, fctReadInputRegister, excIllegalDataVal); 367 | return; 368 | } 369 | WriteAsyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadInputRegister), id); 370 | } 371 | 372 | // ------------------------------------------------------------------------ 373 | /// Read input registers from slave synchronous. 374 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 375 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 376 | /// Address from where the data read begins. 377 | /// Length of data. 378 | /// Contains the result of function. 379 | public void ReadInputRegister(ushort id, byte unit, ushort startAddress, ushort numInputs, ref byte[] values) 380 | { 381 | if (numInputs > 125) 382 | { 383 | CallException(id, unit, fctReadInputRegister, excIllegalDataVal); 384 | return; 385 | } 386 | values = WriteSyncData(CreateReadHeader(id, unit, startAddress, numInputs, fctReadInputRegister), id); 387 | } 388 | 389 | // ------------------------------------------------------------------------ 390 | /// Write single coil in slave asynchronous. The result is given in the response function. 391 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 392 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 393 | /// Address from where the data read begins. 394 | /// Specifys if the coil should be switched on or off. 395 | public void WriteSingleCoils(ushort id, byte unit, ushort startAddress, bool OnOff) 396 | { 397 | byte[] data; 398 | data = CreateWriteHeader(id, unit, startAddress, 1, 1, fctWriteSingleCoil); 399 | if (OnOff == true) data[10] = 255; 400 | else data[10] = 0; 401 | WriteAsyncData(data, id); 402 | } 403 | 404 | // ------------------------------------------------------------------------ 405 | /// Write single coil in slave synchronous. 406 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 407 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 408 | /// Address from where the data read begins. 409 | /// Specifys if the coil should be switched on or off. 410 | /// Contains the result of the synchronous write. 411 | public void WriteSingleCoils(ushort id, byte unit, ushort startAddress, bool OnOff, ref byte[] result) 412 | { 413 | byte[] data; 414 | data = CreateWriteHeader(id, unit, startAddress, 1, 1, fctWriteSingleCoil); 415 | if (OnOff == true) data[10] = 255; 416 | else data[10] = 0; 417 | result = WriteSyncData(data, id); 418 | } 419 | 420 | // ------------------------------------------------------------------------ 421 | /// Write multiple coils in slave asynchronous. The result is given in the response function. 422 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 423 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 424 | /// Address from where the data read begins. 425 | /// Specifys number of bits. 426 | /// Contains the bit information in byte format. 427 | public void WriteMultipleCoils(ushort id, byte unit, ushort startAddress, ushort numBits, byte[] values) 428 | { 429 | ushort numBytes = Convert.ToUInt16(values.Length); 430 | if(numBytes > 250 || numBits > 2000) 431 | { 432 | CallException(id, unit, fctWriteMultipleCoils, excIllegalDataVal); 433 | return; 434 | } 435 | 436 | byte[] data; 437 | data = CreateWriteHeader(id, unit, startAddress, numBits, (byte)(numBytes + 2), fctWriteMultipleCoils); 438 | Array.Copy(values, 0, data, 13, numBytes); 439 | WriteAsyncData(data, id); 440 | } 441 | 442 | // ------------------------------------------------------------------------ 443 | /// Write multiple coils in slave synchronous. 444 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 445 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 446 | /// Address from where the data read begins. 447 | /// Specifys number of bits. 448 | /// Contains the bit information in byte format. 449 | /// Contains the result of the synchronous write. 450 | public void WriteMultipleCoils(ushort id, byte unit, ushort startAddress, ushort numBits, byte[] values, ref byte[] result) 451 | { 452 | ushort numBytes = Convert.ToUInt16(values.Length); 453 | if (numBytes > 250 || numBits > 2000) 454 | { 455 | CallException(id, unit, fctWriteMultipleCoils, excIllegalDataVal); 456 | return; 457 | } 458 | 459 | byte[] data; 460 | data = CreateWriteHeader(id, unit, startAddress, numBits, (byte)(numBytes + 2), fctWriteMultipleCoils); 461 | Array.Copy(values, 0, data, 13, numBytes); 462 | result = WriteSyncData(data, id); 463 | } 464 | 465 | // ------------------------------------------------------------------------ 466 | /// Write single register in slave asynchronous. The result is given in the response function. 467 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 468 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 469 | /// Address to where the data is written. 470 | /// Contains the register information. 471 | public void WriteSingleRegister(ushort id, byte unit, ushort startAddress, byte[] values) 472 | { 473 | if (values.GetUpperBound(0) != 1) 474 | { 475 | CallException(id, unit, fctReadCoil, excIllegalDataVal); 476 | return; 477 | } 478 | byte[] data; 479 | data = CreateWriteHeader(id, unit, startAddress, 1, 1, fctWriteSingleRegister); 480 | data[10] = values[0]; 481 | data[11] = values[1]; 482 | WriteAsyncData(data, id); 483 | } 484 | 485 | // ------------------------------------------------------------------------ 486 | /// Write single register in slave synchronous. 487 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 488 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 489 | /// Address to where the data is written. 490 | /// Contains the register information. 491 | /// Contains the result of the synchronous write. 492 | public void WriteSingleRegister(ushort id, byte unit, ushort startAddress, byte[] values, ref byte[] result) 493 | { 494 | if (values.GetUpperBound(0) != 1) 495 | { 496 | CallException(id, unit, fctReadCoil, excIllegalDataVal); 497 | return; 498 | } 499 | byte[] data; 500 | data = CreateWriteHeader(id, unit, startAddress, 1, 1, fctWriteSingleRegister); 501 | data[10] = values[0]; 502 | data[11] = values[1]; 503 | result = WriteSyncData(data, id); 504 | } 505 | 506 | // ------------------------------------------------------------------------ 507 | /// Write multiple registers in slave asynchronous. The result is given in the response function. 508 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 509 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 510 | /// Address to where the data is written. 511 | /// Contains the register information. 512 | public void WriteMultipleRegister(ushort id, byte unit, ushort startAddress, byte[] values) 513 | { 514 | ushort numBytes = Convert.ToUInt16(values.Length); 515 | if (numBytes > 250) 516 | { 517 | CallException(id, unit, fctWriteMultipleRegister, excIllegalDataVal); 518 | return; 519 | } 520 | 521 | if (numBytes % 2 > 0) numBytes++; 522 | byte[] data; 523 | 524 | data = CreateWriteHeader(id, unit, startAddress, Convert.ToUInt16(numBytes / 2), Convert.ToUInt16(numBytes + 2), fctWriteMultipleRegister); 525 | Array.Copy(values, 0, data, 13, values.Length); 526 | WriteAsyncData(data, id); 527 | } 528 | 529 | // ------------------------------------------------------------------------ 530 | /// Write multiple registers in slave synchronous. 531 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 532 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 533 | /// Address to where the data is written. 534 | /// Contains the register information. 535 | /// Contains the result of the synchronous write. 536 | public void WriteMultipleRegister(ushort id, byte unit, ushort startAddress, byte[] values, ref byte[] result) 537 | { 538 | ushort numBytes = Convert.ToUInt16(values.Length); 539 | if (numBytes > 250) 540 | { 541 | CallException(id, unit, fctWriteMultipleRegister, excIllegalDataVal); 542 | return; 543 | } 544 | 545 | if (numBytes % 2 > 0) numBytes++; 546 | byte[] data; 547 | 548 | data = CreateWriteHeader(id, unit, startAddress, Convert.ToUInt16(numBytes / 2), Convert.ToUInt16(numBytes + 2), fctWriteMultipleRegister); 549 | Array.Copy(values, 0, data, 13, values.Length); 550 | result = WriteSyncData(data, id); 551 | } 552 | 553 | // ------------------------------------------------------------------------ 554 | /// Read/Write multiple registers in slave asynchronous. The result is given in the response function. 555 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 556 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 557 | /// Address from where the data read begins. 558 | /// Length of data. 559 | /// Address to where the data is written. 560 | /// Contains the register information. 561 | public void ReadWriteMultipleRegister(ushort id, byte unit, ushort startReadAddress, ushort numInputs, ushort startWriteAddress, byte[] values) 562 | { 563 | ushort numBytes = Convert.ToUInt16(values.Length); 564 | if (numBytes > 250) 565 | { 566 | CallException(id, unit, fctReadWriteMultipleRegister, excIllegalDataVal); 567 | return; 568 | } 569 | 570 | if (numBytes % 2 > 0) numBytes++; 571 | byte[] data; 572 | 573 | data = CreateReadWriteHeader(id, unit, startReadAddress, numInputs, startWriteAddress, Convert.ToUInt16(numBytes / 2)); 574 | Array.Copy(values, 0, data, 17, values.Length); 575 | WriteAsyncData(data, id); 576 | } 577 | 578 | // ------------------------------------------------------------------------ 579 | /// Read/Write multiple registers in slave synchronous. The result is given in the response function. 580 | /// Unique id that marks the transaction. In asynchonous mode this id is given to the callback function. 581 | /// Unit identifier (previously slave address). In asynchonous mode this unit is given to the callback function. 582 | /// Address from where the data read begins. 583 | /// Length of data. 584 | /// Address to where the data is written. 585 | /// Contains the register information. 586 | /// Contains the result of the synchronous command. 587 | public void ReadWriteMultipleRegister(ushort id, byte unit, ushort startReadAddress, ushort numInputs, ushort startWriteAddress, byte[] values, ref byte[] result) 588 | { 589 | ushort numBytes = Convert.ToUInt16(values.Length); 590 | if (numBytes > 250) 591 | { 592 | CallException(id, unit, fctReadWriteMultipleRegister, excIllegalDataVal); 593 | return; 594 | } 595 | 596 | if (numBytes % 2 > 0) numBytes++; 597 | byte[] data; 598 | 599 | data = CreateReadWriteHeader(id, unit, startReadAddress, numInputs, startWriteAddress, Convert.ToUInt16(numBytes / 2)); 600 | Array.Copy(values, 0, data, 17, values.Length); 601 | result = WriteSyncData(data, id); 602 | } 603 | 604 | // ------------------------------------------------------------------------ 605 | // Create modbus header for read action 606 | private byte[] CreateReadHeader(ushort id, byte unit, ushort startAddress, ushort length, byte function) 607 | { 608 | byte[] data = new byte[12]; 609 | 610 | byte[] _id = BitConverter.GetBytes((short)id); 611 | data[0] = _id[1]; // Slave id high byte 612 | data[1] = _id[0]; // Slave id low byte 613 | data[5] = 6; // Message size 614 | data[6] = unit; // Slave address 615 | data[7] = function; // Function code 616 | byte[] _adr = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startAddress)); 617 | data[8] = _adr[0]; // Start address 618 | data[9] = _adr[1]; // Start address 619 | byte[] _length = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)length)); 620 | data[10] = _length[0]; // Number of data to read 621 | data[11] = _length[1]; // Number of data to read 622 | return data; 623 | } 624 | 625 | // ------------------------------------------------------------------------ 626 | // Create modbus header for write action 627 | private byte[] CreateWriteHeader(ushort id, byte unit, ushort startAddress, ushort numData, ushort numBytes, byte function) 628 | { 629 | byte[] data = new byte[numBytes + 11]; 630 | 631 | byte[] _id = BitConverter.GetBytes((short)id); 632 | data[0] = _id[1]; // Slave id high byte 633 | data[1] = _id[0]; // Slave id low byte 634 | byte[] _size = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)(5 + numBytes))); 635 | data[4] = _size[0]; // Complete message size in bytes 636 | data[5] = _size[1]; // Complete message size in bytes 637 | data[6] = unit; // Slave address 638 | data[7] = function; // Function code 639 | byte[] _adr = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startAddress)); 640 | data[8] = _adr[0]; // Start address 641 | data[9] = _adr[1]; // Start address 642 | if (function >= fctWriteMultipleCoils) 643 | { 644 | byte[] _cnt = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)numData)); 645 | data[10] = _cnt[0]; // Number of bytes 646 | data[11] = _cnt[1]; // Number of bytes 647 | data[12] = (byte)(numBytes - 2); 648 | } 649 | return data; 650 | } 651 | 652 | // ------------------------------------------------------------------------ 653 | // Create modbus header for read/write action 654 | private byte[] CreateReadWriteHeader(ushort id, byte unit, ushort startReadAddress, ushort numRead, ushort startWriteAddress, ushort numWrite) 655 | { 656 | byte[] data = new byte[numWrite * 2 + 17]; 657 | 658 | byte[] _id = BitConverter.GetBytes((short)id); 659 | data[0] = _id[1]; // Slave id high byte 660 | data[1] = _id[0]; // Slave id low byte 661 | byte[] _size = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)(11 + numWrite * 2))); 662 | data[4] = _size[0]; // Complete message size in bytes 663 | data[5] = _size[1]; // Complete message size in bytes 664 | data[6] = unit; // Slave address 665 | data[7] = fctReadWriteMultipleRegister; // Function code 666 | byte[] _adr_read = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startReadAddress)); 667 | data[8] = _adr_read[0]; // Start read address 668 | data[9] = _adr_read[1]; // Start read address 669 | byte[] _cnt_read = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)numRead)); 670 | data[10] = _cnt_read[0]; // Number of bytes to read 671 | data[11] = _cnt_read[1]; // Number of bytes to read 672 | byte[] _adr_write = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)startWriteAddress)); 673 | data[12] = _adr_write[0]; // Start write address 674 | data[13] = _adr_write[1]; // Start write address 675 | byte[] _cnt_write = BitConverter.GetBytes((short)IPAddress.HostToNetworkOrder((short)numWrite)); 676 | data[14] = _cnt_write[0]; // Number of bytes to write 677 | data[15] = _cnt_write[1]; // Number of bytes to write 678 | data[16] = (byte)(numWrite * 2); 679 | 680 | return data; 681 | } 682 | 683 | // ------------------------------------------------------------------------ 684 | // Write asynchronous data 685 | private void WriteAsyncData(byte[] write_data, ushort id) 686 | { 687 | if ((tcpAsyCl != null) && (tcpAsyCl.Connected)) 688 | { 689 | try 690 | { 691 | tcpAsyCl.BeginSend(write_data, 0, write_data.Length, SocketFlags.None, new AsyncCallback(OnSend), null); 692 | } 693 | catch (SystemException) 694 | { 695 | CallException(id, write_data[6], write_data[7], excExceptionConnectionLost); 696 | } 697 | } 698 | else CallException(id, write_data[6], write_data[7], excExceptionConnectionLost); 699 | } 700 | 701 | // ------------------------------------------------------------------------ 702 | // Write asynchronous data acknowledge 703 | private void OnSend(System.IAsyncResult result) 704 | { 705 | Int32 size = tcpAsyCl.EndSend(result); 706 | if (result.IsCompleted == false) CallException(0xFFFF, 0xFF, 0xFF, excSendFailt); 707 | else tcpAsyCl.BeginReceive(tcpAsyClBuffer, 0, tcpAsyClBuffer.Length, SocketFlags.None, new AsyncCallback(OnReceive), tcpAsyCl); 708 | } 709 | 710 | // ------------------------------------------------------------------------ 711 | // Write asynchronous data response 712 | private void OnReceive(System.IAsyncResult result) 713 | { 714 | if (tcpAsyCl == null) return; 715 | 716 | try 717 | { 718 | tcpAsyCl.EndReceive(result); 719 | if (result.IsCompleted == false) CallException(0xFF, 0xFF, 0xFF, excExceptionConnectionLost); 720 | } 721 | catch (Exception) { } 722 | 723 | ushort id = SwapUInt16(BitConverter.ToUInt16(tcpAsyClBuffer, 0)); 724 | byte unit = tcpAsyClBuffer[6]; 725 | byte function = tcpAsyClBuffer[7]; 726 | byte[] data; 727 | 728 | // ------------------------------------------------------------ 729 | // Write response data 730 | if ((function >= fctWriteSingleCoil) && (function != fctReadWriteMultipleRegister)) 731 | { 732 | data = new byte[2]; 733 | Array.Copy(tcpAsyClBuffer, 10, data, 0, 2); 734 | } 735 | // ------------------------------------------------------------ 736 | // Read response data 737 | else 738 | { 739 | data = new byte[tcpAsyClBuffer[8]]; 740 | Array.Copy(tcpAsyClBuffer, 9, data, 0, tcpAsyClBuffer[8]); 741 | } 742 | // ------------------------------------------------------------ 743 | // Response data is slave exception 744 | if (function > excExceptionOffset) 745 | { 746 | function -= excExceptionOffset; 747 | CallException(id, unit, function, tcpAsyClBuffer[8]); 748 | } 749 | // ------------------------------------------------------------ 750 | // Response data is regular data 751 | else if (OnResponseData != null) OnResponseData(id, unit, function, data); 752 | } 753 | 754 | // ------------------------------------------------------------------------ 755 | // Write data and and wait for response 756 | private byte[] WriteSyncData(byte[] write_data, ushort id) 757 | { 758 | 759 | if (tcpSynCl.Connected) 760 | { 761 | try 762 | { 763 | tcpSynCl.Send(write_data, 0, write_data.Length, SocketFlags.None); 764 | int result = tcpSynCl.Receive(tcpSynClBuffer, 0, tcpSynClBuffer.Length, SocketFlags.None); 765 | 766 | byte unit = tcpSynClBuffer[6]; 767 | byte function = tcpSynClBuffer[7]; 768 | byte[] data; 769 | 770 | if (result == 0) CallException(id, unit, write_data[7], excExceptionConnectionLost); 771 | 772 | // ------------------------------------------------------------ 773 | // Response data is slave exception 774 | if (function > excExceptionOffset) 775 | { 776 | function -= excExceptionOffset; 777 | CallException(id, unit, function, tcpSynClBuffer[8]); 778 | return null; 779 | } 780 | // ------------------------------------------------------------ 781 | // Write response data 782 | else if ((function >= fctWriteSingleCoil) && (function != fctReadWriteMultipleRegister)) 783 | { 784 | data = new byte[2]; 785 | Array.Copy(tcpSynClBuffer, 10, data, 0, 2); 786 | } 787 | // ------------------------------------------------------------ 788 | // Read response data 789 | else 790 | { 791 | data = new byte[tcpSynClBuffer[8]]; 792 | Array.Copy(tcpSynClBuffer, 9, data, 0, tcpSynClBuffer[8]); 793 | } 794 | return data; 795 | } 796 | catch (SystemException) 797 | { 798 | CallException(id, write_data[6], write_data[7], excExceptionConnectionLost); 799 | } 800 | } 801 | else CallException(id, write_data[6], write_data[7], excExceptionConnectionLost); 802 | return null; 803 | } 804 | } 805 | } 806 | -------------------------------------------------------------------------------- /ModbusTCP/ModbusTCP/revision.txt: -------------------------------------------------------------------------------- 1 | ----------------------------------------- 2 | Version 3.6 3 | ----------------------------------------- 4 | - Fixed: Handle null reference in receive function block when cable is disconnected 5 | 6 | ----------------------------------------- 7 | Version 3.5 8 | ----------------------------------------- 9 | - Fixed: Exception is not triggered when synchonous channel is disabled 10 | - Fixed: WriteSingleRegister now fires exception excIllegalDataVal if number of bytes is not 2 11 | 12 | ----------------------------------------- 13 | Version 3.4 14 | ----------------------------------------- 15 | - Added: Option to disable synchronous channel to limit number of connections to one 16 | - Added: Trigger exception if number of registers (250) or discrete values (2000) is exceeded 17 | 18 | ----------------------------------------- 19 | Version 3.3 20 | ----------------------------------------- 21 | - Fixed: ID high and low byte swapped 22 | - Added: Modbus paramater UNIT added to function calls 23 | 24 | ----------------------------------------- 25 | Version 3.2 26 | ----------------------------------------- 27 | First release -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | Visual Studio modbusTCP class 3 | 4 | This class implements a Modbus TCP master driver. The code is written in C# but the DLL can 5 | be used in any other language. 6 | 7 | ![Picture](https://github.com/stephanstricker/modbusTCP/blob/master/Screenshot.png) 8 | 9 | ## It supports the following function codes: 10 | 11 | * Read coils 12 | * Read discrete inputs 13 | * Write single coil 14 | * Write multiple coils 15 | * Read holding register 16 | * Read input register 17 | * Write single register 18 | * Write multiple registers 19 | 20 | Background 21 | I will not explain how the Modbus protocol works in detail because there is plenty of information available online. 22 | 23 | [Modbus IDA home page](http://www.modbus.org/) 24 | 25 | [Modbus IDA PDF documentation](http://www.modbus.org/docs/Modbus_Messaging_Implementation_Guide_V1_0a.pdf) 26 | 27 | ## Using the code 28 | All commands are sent in synchronous or asynchronous mode. If a value is accessed in synchronous mode, the program will stop and wait for the slave to respond. If the slave does not answer within a specified time, a timeout exception is called. The class uses multi-threading for both synchronous and asynchronous access. The class opens two communication channels for each slave. This is necessary because the synchronous thread has to wait for a command to finish. This would block the asynchronous connection. The class directory contains a help file (ModbusClassTCP\doc) with detailed information for every function. 29 | 30 | A sample application is included in the package that shows the basic features. It uses asynchronous mode for all function calls. The sample has some additional code to make the result handling more comfortable. 31 | 32 | ## Points of Interest 33 | Remember that a lot of Modbus clients use a watchdog telegram to make sure the master is still active. If you don’t call this watchdog within a specified time, the slave will close the connection. This feature is used to avoid unused open ports when the connection is interrupted. 34 | 35 | ## History 36 | Next Version (04/03/2022) 37 | - Fixed: Typo in sample 38 | 39 | Version 3.6 (26/03/2019) 40 | - Fixed: Handle null reference in receive function block when cable is disconnected 41 | 42 | Version 3.5 (12/06/2018) 43 | - Fixed: Exception is not triggered when synchonous channel is disabled 44 | - Fixed: WriteSingleRegister now fires exception excIllegalDataVal if number of bytes is not 2 45 | 46 | Version 3.4 (01/01/2015) 47 | - Added: Option to disable synchronous channel to limit number of connections to one 48 | - Added: Trigger exception if number of registers (250) or discrete values (2000) is exceeded 49 | 50 | Version 3.3 (6/14/2013) 51 | 52 | - Fixed: ID high and low byte swapped 53 | - Added: Modbus parameter UNIT added to function calls 54 | 55 | Version 3.2 56 | 57 | First release 58 | Edit message 59 | 60 | Write a small message here explaining this change. (Optional) 61 | © 2018 GitHub, Inc. 62 | Terms 63 | Privacy 64 | Security 65 | Status 66 | Help 67 | Contact GitHub 68 | API 69 | Training 70 | Shop 71 | Blog 72 | About 73 | Press h to open a hovercard with more details. 74 | -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stephan1827/modbusTCP-DotNET/1dbfa4d7a5e764aab99f647ec215e490ee3b8ed1/Screenshot.png --------------------------------------------------------------------------------