├── .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 | 
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
--------------------------------------------------------------------------------