<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Sergey Romanov</title>
    <description>The latest articles on DEV Community by Sergey Romanov (@serhioromano).</description>
    <link>https://dev.to/serhioromano</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3132475%2F71b1ff0c-1a0c-41df-a12f-8ffaf8d247b5.jpg</url>
      <title>DEV Community: Sergey Romanov</title>
      <link>https://dev.to/serhioromano</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/serhioromano"/>
    <language>en</language>
    <item>
      <title>Modbus RTU Master in FX3G\U PLCs</title>
      <dc:creator>Sergey Romanov</dc:creator>
      <pubDate>Thu, 08 May 2025 07:29:25 +0000</pubDate>
      <link>https://dev.to/serhioromano/modbus-rtu-master-in-fx3gu-plcs-fhl</link>
      <guid>https://dev.to/serhioromano/modbus-rtu-master-in-fx3gu-plcs-fhl</guid>
      <description>&lt;h2&gt;
  
  
  Enhancing Automation with FX3G and FX3U PLCs: Overcoming IDE Limitations and Modbus Challenges
&lt;/h2&gt;

&lt;p&gt;The FX3G and FX3U PLCs are excellent solutions for small to medium-sized automation projects. Their affordability and compatibility with budget-friendly, China-made devices further enhance their appeal, making them an attractive choice for many engineers and developers.&lt;/p&gt;

&lt;p&gt;However, these PLCs come with certain limitations—particularly in the IDE environment. I primarily use GX Works 2, and while it provides Structured Text (ST) programming, its implementation is fairly restricted compared to even older versions like CoDeSys 2.3. That said, the fact that ST is available at all is a significant advantage. Despite its limitations, GX Works 2 remains highly capable of solving most automation tasks.&lt;/p&gt;

&lt;h2&gt;
  
  
  Challenges with Modbus Master Implementation
&lt;/h2&gt;

&lt;p&gt;One of the more complex challenges arises when trying to configure the PLC as a Modbus Master. For less experienced developers, this process can be particularly daunting. If you're only communicating with a single device on a single channel, the setup is relatively straightforward. However, as soon as multiple devices and multiple registers enter the picture, things quickly become complicated—especially if two Modbus Masters operate within the same PLC.&lt;/p&gt;

&lt;p&gt;Another challenge is that Modbus communications cannot be configured through the IDE interface, as is possible in CoDeSys. Instead, all configuration must be handled programmatically within the code.&lt;/p&gt;

&lt;p&gt;Not only do developers need to issue instructions for each channel, but they must also ensure that these instructions do not overlap, which can become increasingly difficult to manage as the system scales.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing the ModbusDriver Library
&lt;/h2&gt;

&lt;p&gt;This complexity was the primary barrier preventing me from deploying FX3G and FX3U PLCs in medium-sized automation systems. To resolve this issue once and for all, I developed the ModbusDriver library. Now at version 1.6, it includes all the functionality I envisioned for a seamless Modbus Master implementation.&lt;/p&gt;

&lt;p&gt;In the following section, I'll provide a quick overview of how to use the library, as its approach to automation may not be immediately intuitive.&lt;/p&gt;

&lt;h2&gt;
  
  
  Core Objective of ModbusDriver
&lt;/h2&gt;

&lt;p&gt;The fundamental goal behind ModbusDriver is straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ensure that only one ADPRW instruction is executed at a time.&lt;/li&gt;
&lt;li&gt;Automate as much of the Modbus communication process as possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;br&gt;
In my work, I primarily use Coolmay L02 and Coolmay QM3G PLCs. The ModbusDriver library is fully compatible with these models, though minor adjustments may be required for use with PLCs from other manufacturers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;br&gt;
Library may be downloaded by &lt;a href="https://1drv.ms/f/s!AoZIPjc0O6zHiZgloAwO_z7ALUGaIw" rel="noopener noreferrer"&gt;this link&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Initialisation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Make sure you have GX Works 2 (version 1.622) installed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxw98zwf4yc6s2vhyh68.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcxw98zwf4yc6s2vhyh68.png" alt="Fig. 1 – About window" width="365" height="143"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a structured project and select ST as the programming language.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdk1ys76qh33xk39wtstq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdk1ys76qh33xk39wtstq.png" alt="Fig. 2 – Creating an FX3G project" width="425" height="263"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add the Modbus Driver, Utils, and TimeControl50 libraries to the project.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyiik19567w7xcdch5kac.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyiik19567w7xcdch5kac.png" alt="Fig. 3 – Adding libraries" width="278" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new task named TCO by right-clicking on &lt;code&gt;MAIN&lt;/code&gt; in the Program Settings tree. Link it to the &lt;code&gt;TCO_TICKER_50&lt;/code&gt; program from the &lt;strong&gt;TimeControl50&lt;/strong&gt; library.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fntaynifu1zp4wd3yz0x3.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fntaynifu1zp4wd3yz0x3.jpg" alt="Fig. 4 – Adding a new task" width="603" height="331"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up the task execution conditions by right-clicking on &lt;code&gt;TCO&lt;/code&gt;, selecting Properties.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4myaxleroprx9t80lat.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4myaxleroprx9t80lat.png" alt="Fig. 5 – Task properties" width="302" height="385"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure the program to trigger via an interrupt every 50ms by entering &lt;strong&gt;&lt;code&gt;I750&lt;/code&gt;&lt;/strong&gt; in the Event field.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F92jpjg2ftb74n1vpbcek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F92jpjg2ftb74n1vpbcek.png" alt="Fig. 6 – TCO settings" width="340" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Program Setup
&lt;/h2&gt;

&lt;p&gt;Now that we've completed the initial configuration, we can proceed with writing the program.&lt;/p&gt;

&lt;p&gt;The first step is to declare several variables:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1z1r3utzbji9d3djghq5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1z1r3utzbji9d3djghq5.png" alt="Fig. 7 – Program variables" width="674" height="134"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;PortSettings&lt;/code&gt; – Stores the port settings for initialization.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;arMB&lt;/code&gt; – An array of polling channels. As seen in the diagram, the maximum number of channels is &lt;strong&gt;30&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fbMbProcess&lt;/code&gt; – A function block responsible for handling polling operations.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At the very beginning of the program, we need to add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EI(TRUE)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable Interupt (EI). This command enables the program to be interrupted whenever the &lt;strong&gt;&lt;code&gt;I750&lt;/code&gt;&lt;/strong&gt; interrupt is triggered. This line must be included in any additional programs you define.&lt;/p&gt;

&lt;p&gt;Next, we need to specify that our PLC will function as a Modbus Master on one of the two available ports—Port 2 or Port 3—at least for Coolmay PLCs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MOV(M8002, 2, MB_TIMEOUT_COUNT);
MOV(M8002, 20, MB_TIMEOUT_TIME);
MOV(M8002, 80, MB_SUSPEND_RETRY);

PortSettings := MB_PORT_SETTINGS(
    MB_PARITY_EVEN,
    MB_STOPBIT_1,
    MB_BPS_9600
);

M0 := MB_MASTER_INIT_PORT3(M8002 OR M8014, PortSettings);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first three lines define the Modbus driver settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MB_TIMEOUT_TIME&lt;/code&gt; – Timeout duration in polling intervals (each interval is &lt;strong&gt;50ms&lt;/strong&gt;). A value of &lt;strong&gt;10&lt;/strong&gt; corresponds to &lt;strong&gt;500ms&lt;/strong&gt;. For polling channels exceeding 100 registers, a longer timeout is recommended. I currently use &lt;strong&gt;20&lt;/strong&gt;, which equals &lt;strong&gt;1 second&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MB_TIMEOUT_COUNT&lt;/code&gt; – The number of retry attempts before a polling channel enters monitoring mode due to a timeout. In this case, the system will attempt twice before switching to monitoring mode.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MB_SUSPEND_RETRY&lt;/code&gt; – The frequency at which polling occurs for channels in monitoring mode. Once a channel successfully responds, it exits monitoring and resumes normal polling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, we configure the port settings, ensuring they match the device configurations being polled.&lt;/p&gt;

&lt;p&gt;Finally, we initialize the Modbus Master on Port 3 using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;M0 := MB_MASTER_INIT_PORT3(M8002 OR M8014, PortSettings);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If needed, Port 2 can be configured by replacing &lt;code&gt;MB_MASTER_INIT_PORT3&lt;/code&gt; with &lt;code&gt;MB_MASTER_INIT_PORT2&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Important Considerations:
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;This initialization process should only run once at PLC startup. It can be placed in a separate program specifically executed on startup.&lt;/li&gt;
&lt;li&gt;The condition &lt;code&gt;M8002 OR M8014&lt;/code&gt; ensures proper execution of the Modbus Master initialization. I noticed that &lt;code&gt;M8002&lt;/code&gt; alone might not be sufficient, as the PLC seems to perform additional procedures after &lt;code&gt;M8002&lt;/code&gt;, potentially resetting the port to default settings. To ensure stability, you can bind initialization to a button trigger, a ticker, or simply use &lt;code&gt;TRUE&lt;/code&gt; as the condition.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configuring Polling Channels
&lt;/h2&gt;

&lt;p&gt;Polling channels are also part of the PLC initialization and should be defined at startup (&lt;code&gt;M8002&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;arMB[0].xEnabled := TRUE;
arMB[0].iNum := 3;
arMB[0].iDev := 1;
arMB[0].iPort := MB_PORT_3;
arMB[0].iRDevNum := 100;
arMB[0].iReg := K514;
arMB[0].iRF := H4;
arMB[0].iWF := H10;
arMB[0].iWR := MB_READ_WRITE;
arMB[0].xWriteOnChange := TRUE;
arMB[0].tCycle := 2;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Parameter Breakdown:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;xEnabled&lt;/code&gt;  &lt;strong&gt;Bit&lt;/strong&gt; Activates polling for this channel. If &lt;code&gt;TRUE&lt;/code&gt;, polling is enabled; if &lt;code&gt;FALSE&lt;/code&gt;, polling is disabled. Default is &lt;code&gt;TRUE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iReg&lt;/code&gt;  &lt;strong&gt;Word[Unsigned]&lt;/strong&gt;  Register or coil address. If polling register 514, specify K514 (decimal) or H202 (hexadecimal).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iNum&lt;/code&gt;  &lt;strong&gt;Word[Signed]&lt;/strong&gt;  Number of registers or coils to poll. Default is 1. If polling 3 registers, set 3.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iRDevNum&lt;/code&gt;  Word[Signed]  Device register number where results are stored. If set to &lt;code&gt;100&lt;/code&gt;, results will be stored starting from &lt;code&gt;D100&lt;/code&gt; (or &lt;code&gt;M100&lt;/code&gt; for bit registers).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iRF&lt;/code&gt;  &lt;strong&gt;Word[Unsigned]&lt;/strong&gt;  Read function code. Default is &lt;code&gt;H4&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iWF&lt;/code&gt;  &lt;strong&gt;Word[Unsigned]&lt;/strong&gt;  Write function code. Default is &lt;code&gt;H6&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iDev&lt;/code&gt;  &lt;strong&gt;Word[Unsigned]&lt;/strong&gt;  Device address. If polling device 1, set 1.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iWR&lt;/code&gt;  &lt;strong&gt;Word[Signed]&lt;/strong&gt;  Read/write mode. Default is MB_READ_WRITE, but you can set MB_READ or MB_WRITE.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;iPort&lt;/code&gt;  &lt;strong&gt;Word[Signed]&lt;/strong&gt;  Specifies the port assigned to this polling channel. If the PLC has two Modbus Masters, assign the correct port.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tCycle&lt;/code&gt;  &lt;strong&gt;Word[Signed]&lt;/strong&gt;  Polling frequency in milliseconds (default 0). A value of 40 corresponds to 2 seconds (each unit = 50ms).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;xWriteOnChange&lt;/code&gt;  &lt;strong&gt;Bit&lt;/strong&gt;  Enables automatic writing upon value change. If &lt;code&gt;TRUE&lt;/code&gt;, data is immediately written; if &lt;code&gt;FALSE&lt;/code&gt;, data is written only at the scheduled cycle (&lt;code&gt;tCycle&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Possible Modbus Functions
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;H1&lt;/code&gt; - Read Coils&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;H2&lt;/code&gt; - Read Discrete Inputs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;H3&lt;/code&gt; - Read Holding Register&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;H4&lt;/code&gt; - Read Input Register&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;H5&lt;/code&gt; - Write Single Coil&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;H6&lt;/code&gt; - Write Single Register&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HF&lt;/code&gt; - Write Multiple Coils&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;H10&lt;/code&gt; - Write Multiple Registers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Possible Coolmay PLC Posrts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;MB_PORT_2&lt;/code&gt; - RS485 A,B&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MB_PORT_3&lt;/code&gt; - RS485 A1,B1&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MB_PORT_CAN&lt;/code&gt; - CAN H,L&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;MB_PORT_TCP&lt;/code&gt; - Ethernet&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Important Note on iRDevNum
&lt;/h3&gt;

&lt;p&gt;It's crucial to understand that &lt;code&gt;iRDevNum&lt;/code&gt; occupies twice the space. For example, if &lt;code&gt;iNum&lt;/code&gt; (the number of registers to poll) is &lt;strong&gt;3&lt;/strong&gt;, and &lt;code&gt;iRDevNum&lt;/code&gt; is set to &lt;strong&gt;100&lt;/strong&gt;, the polling results will be stored in &lt;code&gt;D100&lt;/code&gt;, &lt;code&gt;D101&lt;/code&gt;, and &lt;code&gt;D102&lt;/code&gt;. However, &lt;code&gt;D103&lt;/code&gt;, &lt;code&gt;D104&lt;/code&gt;, and &lt;code&gt;D105&lt;/code&gt; will also be occupied to store previous cycle values, allowing the system to track changes in the program.&lt;/p&gt;

&lt;p&gt;This consideration is essential when allocating polling channels, as improper planning could lead to data overlap issues.&lt;/p&gt;

&lt;h2&gt;
  
  
  Executing the Polling Process
&lt;/h2&gt;

&lt;p&gt;Once the polling channels are configured, the next step is to run the polling process using the &lt;code&gt;fbMbProcess&lt;/code&gt; function block:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fbMbProcess(
    mb_arRegs := arMB,
    mb_xEnable := TRUE,
    mb_Timeout := D200,
    mb_iBuffer := 3000
);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Explanation of Key Variables:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;mb_Timeout&lt;/code&gt; – Stores the channel number that encountered a timeout (active for one cycle).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mb_iBuffer&lt;/code&gt; – Defines the buffer memory location (&lt;code&gt;D3000&lt;/code&gt; in this case). The buffer size should match the largest polling channel. If a channel polls &lt;strong&gt;50&lt;/strong&gt; registers, then &lt;code&gt;D3000–D3049&lt;/code&gt; will be used as a temporary buffer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;At this stage, everything runs automatically. For example, in our test setup:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;D100&lt;/code&gt;, &lt;code&gt;D101&lt;/code&gt;, and &lt;code&gt;D102&lt;/code&gt; store the polled values.&lt;/li&gt;
&lt;li&gt;If any value changes, they are written as a group (using function &lt;code&gt;H10&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;H6&lt;/code&gt; is used instead of &lt;code&gt;H10&lt;/code&gt;, only the modified device is written.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Manual Polling and Writing
&lt;/h2&gt;

&lt;p&gt;For manual polling or writing, set &lt;code&gt;tCycle := 0&lt;/code&gt; in the polling channel and use these examples:&lt;/p&gt;

&lt;h3&gt;
  
  
  Manual Read (Triggering on M1)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IF M1 THEN
    arMB[0].xReadOnce := TRUE;
    IF arMB[0].xDone = TRUE THEN
        M1 := FALSE;
        arMB[0].xReadOnce := FALSE;
    END_IF;
END_IF;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Manual Write (Triggering on M2)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IF M2 THEN
    D100 := 100;
    D101 := 100;
    D102 := 100;
    arMB[0].xWriteOnce := TRUE;
    IF arMB[0].xDone = TRUE THEN
        M2 := FALSE;
        arMB[0].xWriteOnce := FALSE;
    END_IF;
END_IF;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When using xWriteOnce, function H10 is automatically applied to registers and HF to coils.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alternative Method for Verifying Successful Writes
&lt;/h3&gt;

&lt;p&gt;Instead of relying on &lt;code&gt;xDone&lt;/code&gt;, a verification method can be used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;IF M2 THEN
    D100 := 200;
    D101 := 200;
    D102 := 200;
    arMB[0].xWriteOnce := TRUE;
    IF D100 = D103 AND D101 = D104 AND D102 = D105 THEN
        M2 := FALSE;
        arMB[0].xWriteOnce := FALSE;
    END_IF;
END_IF;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Final Notes
&lt;/h2&gt;

&lt;p&gt;For further examples, check the &lt;strong&gt;ModbusDriver.gxw&lt;/strong&gt; file in the &lt;strong&gt;Examples&lt;/strong&gt; folder in the link provided earlier.&lt;/p&gt;

&lt;p&gt;Additionally, the ModbusDriver library can configure any port as a Modbus Slave or use the Mitsubishi protocol. Refer to the library documentation for details.&lt;/p&gt;

&lt;p&gt;There are not many articles about PLC in this portal, and this is my first article in English, please make a comment for me to understand if such a content is wanted here.&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
