<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>C# Archives | DMC, Inc.</title>
	<atom:link href="https://static.dmcinfo.com/blog/tag/c/feed/index.xml" rel="self" type="application/rss+xml" />
	<link></link>
	<description></description>
	<lastBuildDate>Fri, 04 Sep 2026 15:32:24 +0000</lastBuildDate>
	<language>en-US</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.1.2</generator>

<image>
	<url>https://static.dmcinfo.com/wp-content/uploads/2025/04/site-icon-150x150.png</url>
	<title>C# Archives | DMC, Inc.</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>FactoryTalk Optix Series 3 &#8211; NetLogic Overview and Examples</title>
		<link>https://static.dmcinfo.com/blog/16280/factorytalk-optix-series-3-netlogic-overview-and-examples/</link>
		
		<dc:creator><![CDATA[Ben Clare]]></dc:creator>
		<pubDate>Tue, 14 May 2024 17:21:41 +0000</pubDate>
				<category><![CDATA[Allen Bradley PLC]]></category>
		<category><![CDATA[HMI and SCADA]]></category>
		<category><![CDATA[Manufacturing Automation & Intelligence]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[FactoryTalk]]></category>
		<category><![CDATA[FactoryTalk Optix]]></category>
		<category><![CDATA[FT Optix]]></category>
		<category><![CDATA[HMI]]></category>
		<category><![CDATA[Optix]]></category>
		<category><![CDATA[SCADA]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/16280/factorytalk-optix-series-3-netlogic-overview-and-examples/</guid>

					<description><![CDATA[<p>HMI Programming and SCADA Programming are essential for creating efficient and responsive automation systems. When working with Optix, leveraging NetLogic allows for seamless integration between C# code and the SCADA environment, enabling advanced control and data exchange.&#160; NetLogic is C# code&#160;that is linked to Optix. Optix can call methods with parameters, set private C# variables, [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/16280/factorytalk-optix-series-3-netlogic-overview-and-examples/">FactoryTalk Optix Series 3 &#8211; NetLogic Overview and Examples</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/hmi-and-scada-programming"><strong>HMI Programming and SCADA Programming</strong></a> are essential for creating efficient and responsive automation systems. When working with Optix, leveraging NetLogic allows for seamless integration between C# code and the SCADA environment, enabling advanced control and data exchange.&nbsp;</p>



<p class="wp-block-paragraph">NetLogic is C# code&nbsp;that is linked to Optix. Optix can call methods with parameters, set private C# variables, and has numerous C# Libraries integrated by default to assist with passing data between Optix and your C# code. Optix also allows you to link the monitoring of C# code to an Optix runtime instance.</p>



<h2 id="h-linking-variables" class="wp-block-heading">Linking Variables</h2>



<p class="wp-block-paragraph">When creating NetLogic, you can define variables that interface with the C# code, and can be both written to and read from said C# code. You can also define variable categories to assist with organization.</p>



<ul class="wp-block-list">
<li>First, Variables are added onto the NetLogic object in Optix.</li>
</ul>



<p class="wp-block-paragraph"><p style="text-align: center;">&nbsp;<img decoding="async" alt="Linking Netlogic Variables" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/net-logic-linking-variables.png"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>An example of variables attached to a NetLogic object</em></p></p>



<p class="wp-block-paragraph">These variables can be referenced in the C# logic by utilizing the “GetVariable” method on our LogicObject.</p>



<ul class="wp-block-list">
<li>For any variables at the top-most level, simply call: LogicObject.GetVariable(“YourVariable”).</li>



<li>For nested variables (such as variables under “Table” in the example above), define a new IUAVariable for the nested name (Tables in this case), and then reference the individual elements of that new variable.</li>
</ul>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C#</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
private void ParametersSetup()

{
	DB_SERVER_IP = LogicObject.GetVariable("Server IP").Value;
	DB_SERVER_PORT = uint.Parse(LogicObject.GetVariable("Server port").Value);
	SERVICE_NAME = LogicObject.GetVariable("Service name").Value;
	DB_USERNAME = LogicObject.GetVariable("Username").Value;
	DB_PASSWORD = LogicObject.GetVariable("Password").Value;
	DB_QUERIES_FEEDBACK = LogicObject.GetVariable("Queries feedback");
	TABLES = LogicObject.GetVariable("Tables");

	TABLE_DESTINATION = TABLES.GetVariable("Destination").Value;
	TABLE_DISTANCE = TABLES.GetVariable("Distance").Value;
	TABLE_LOCATIONCHANGE = TABLES.GetVariable("LocationChange").Value;
	TABLE_EQUIPMENTSTATUS = TABLES.GetVariable("EquipmentStatus").Value;
	NUMBER_OF_DEVICES = LogicObject.GetVariable("Number of devices").Value;
	NUMBER_OF_EQUIPMENT_AREAS = LogicObject.GetVariable("Number of equipment areas").Value;
	CONNECTIONSTRING = $"Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST={DB_SERVER_IP})(PORT={(int)DB_SERVER_PORT})))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME={SERVICE_NAME})));User Id={DB_USERNAME};Password={DB_PASSWORD};";
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">void</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">ParametersSetup</span><span style="color: #D4D4D4">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">DB_SERVER_IP</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">LogicObject</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Server IP&quot;</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">DB_SERVER_PORT</span><span style="color: #D4D4D4"> = </span><span style="color: #569CD6">uint</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Parse</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">LogicObject</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Server port&quot;</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">SERVICE_NAME</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">LogicObject</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Service name&quot;</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">DB_USERNAME</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">LogicObject</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Username&quot;</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">DB_PASSWORD</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">LogicObject</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Password&quot;</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">DB_QUERIES_FEEDBACK</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">LogicObject</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Queries feedback&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">TABLES</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">LogicObject</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Tables&quot;</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">TABLE_DESTINATION</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">TABLES</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Destination&quot;</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">TABLE_DISTANCE</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">TABLES</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Distance&quot;</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">TABLE_LOCATIONCHANGE</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">TABLES</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;LocationChange&quot;</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">TABLE_EQUIPMENTSTATUS</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">TABLES</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;EquipmentStatus&quot;</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">NUMBER_OF_DEVICES</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">LogicObject</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Number of devices&quot;</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">NUMBER_OF_EQUIPMENT_AREAS</span><span style="color: #D4D4D4"> = </span><span style="color: #9CDCFE">LogicObject</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetVariable</span><span style="color: #D4D4D4">(</span><span style="color: #CE9178">&quot;Number of equipment areas&quot;</span><span style="color: #D4D4D4">).</span><span style="color: #9CDCFE">Value</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">CONNECTIONSTRING</span><span style="color: #D4D4D4"> = </span><span style="color: #CE9178">$&quot;Data Source=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(PROTOCOL=TCP)(HOST={</span><span style="color: #9CDCFE">DB_SERVER_IP</span><span style="color: #CE9178">})(PORT={(</span><span style="color: #569CD6">int</span><span style="color: #CE9178">)</span><span style="color: #9CDCFE">DB_SERVER_PORT</span><span style="color: #CE9178">})))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME={</span><span style="color: #9CDCFE">SERVICE_NAME</span><span style="color: #CE9178">})));User Id={</span><span style="color: #9CDCFE">DB_USERNAME</span><span style="color: #CE9178">};Password={</span><span style="color: #9CDCFE">DB_PASSWORD</span><span style="color: #CE9178">};&quot;</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre></div>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C#</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
private IUAVariable DB_QUERIES_FEEDBACK;
private string DB_SERVER_IP;
private uint DB_SERVER_PORT;
private string SERVICE_NAME;
private string DB_USERNAME;
private string DB_PASSWORD;
public IUAVariable TABLES;
private string TABLE_DESTINATION;
private string TABLE_DISTANCE;
private string TABLE_LOCATIONCHANGE;
private string TABLE_EQUIPMENTSTATUS;
private Int32 NUMBER_OF_DEVICES;
private Int32 NUMBER_OF_EQUIPMENT_AREAS;

</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IUAVariable</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">DB_QUERIES_FEEDBACK</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">DB_SERVER_IP</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">uint</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">DB_SERVER_PORT</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">SERVICE_NAME</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">DB_USERNAME</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">DB_PASSWORD</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">IUAVariable</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">TABLES</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">TABLE_DESTINATION</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">TABLE_DISTANCE</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">TABLE_LOCATIONCHANGE</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">string</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">TABLE_EQUIPMENTSTATUS</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Int32</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">NUMBER_OF_DEVICES</span><span style="color: #D4D4D4">;</span></span>
<span class="line"><span style="color: #569CD6">private</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">Int32</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">NUMBER_OF_EQUIPMENT_AREAS</span><span style="color: #D4D4D4">;</span></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>



<h2 id="h-calling-methods" class="wp-block-heading">Calling Methods</h2>



<p class="wp-block-paragraph">C# Methods can be called directly from Optix when the [ExportMethod] Line is added above said method. Any parameters associated with the Method will also be exposed, allowing you to set them dynamically from Optix, using a MethodInvocation or by linking them to events.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Invoking C# Methods from Optix" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/method-invocation.png"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>An example method invocation that shows all the [ExportMethod] methods from the Netlogic C# code</em></p></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C#</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
&#91;ExportMethod&#93;

public void SelectAllDevicesDestination()

{

…

</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #D4D4D4">&#91;</span><span style="color: #4EC9B0">ExportMethod</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">public</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">void</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">SelectAllDevicesDestination</span><span style="color: #D4D4D4">()</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">…</span></span>
<span class="line"></span>
<span class="line"></span></code></pre></div>



<h2 id="h-monitoring-code" class="wp-block-heading">Monitoring Code</h2>



<p class="wp-block-paragraph">When NetLogic is created, Optix automatically configures the codespace (if utilizing VSCode) to attach to the Optix runtime instance, allowing you to monitor your C# card while the Optix application is running.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Monitoring an Optix Project in Visual Code" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/visual-code-monitoring.png"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>Visual Code is attached to the Optix Runtime when monitoring</em></p></p>



<h2 id="h-logging-and-error-handling" class="wp-block-heading">Logging and Error Handling</h2>



<p class="wp-block-paragraph">Optix has a C# library for logging code that should be utilized, as it will output to the console in FT Optix Studio, as well as to the log file for the Optix application.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C#</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
catch (System.Exception ex)
{
	Log.Error(MethodBase.GetCurrentMethod().Name, ex.Message);
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #C586C0">catch</span><span style="color: #D4D4D4"> (</span><span style="color: #4EC9B0">System</span><span style="color: #D4D4D4">.</span><span style="color: #4EC9B0">Exception</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">ex</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">	</span><span style="color: #9CDCFE">Log</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">Error</span><span style="color: #D4D4D4">(</span><span style="color: #9CDCFE">MethodBase</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">GetCurrentMethod</span><span style="color: #D4D4D4">().</span><span style="color: #9CDCFE">Name</span><span style="color: #D4D4D4">, </span><span style="color: #9CDCFE">ex</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">Message</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph"><p style="text-align: center;">&nbsp;<img decoding="async" alt="Location of Optix Log Files" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/optix-runtime-logs.png"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>The location&nbsp;of Optix log files for an emulated project</em></p></p>



<h2 id="h-read-the-other-articles-in-this-series" class="wp-block-heading">Read the Other Articles in this Series</h2>



<ul class="wp-block-list">
<li><a href="https://static.dmcinfo.com/latest-thinking/blog/id/10576/factorytalk-optix-series-1--getting-started-with-factorytalk-optix">FactoryTalk Optix Series 1 &#8211; Getting Started with FactoryTalk Optix</a></li>



<li><a href="https://static.dmcinfo.com/latest-thinking/blog/id/10609/factorytalk-optix-series-2--variables-attributes-dynamic-links-and-converters">FactoryTalk Optix Series 2 &#8211; Variables, Attributes, Dynamic Links, and Converters</a></li>
</ul>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step"><strong>Extend FactoryTalk Optix with C# NetLogic</strong>.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Explore our <a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/" data-type="page" data-id="420">Automation</a> expertise in integrating C# methods, variables, runtime monitoring, and error handling into <a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/hmi-and-scada-programming/rockwell-factorytalk-programming/factorytalk-optix-programming/" data-type="page" data-id="44850">FactoryTalk Optix</a> applications.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>
<p>The post <a href="https://static.dmcinfo.com/blog/16280/factorytalk-optix-series-3-netlogic-overview-and-examples/">FactoryTalk Optix Series 3 &#8211; NetLogic Overview and Examples</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>FactoryTalk Optix Series 1 &#8211; Getting Started with FactoryTalk Optix</title>
		<link>https://static.dmcinfo.com/blog/16514/factorytalk-optix-series-1-getting-started-with-factorytalk-optix/</link>
		
		<dc:creator><![CDATA[Ben Clare]]></dc:creator>
		<pubDate>Mon, 25 Mar 2024 08:56:32 +0000</pubDate>
				<category><![CDATA[Allen Bradley PLC]]></category>
		<category><![CDATA[HMI and SCADA]]></category>
		<category><![CDATA[Manufacturing Automation & Intelligence]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[C# Integration]]></category>
		<category><![CDATA[Data Stores]]></category>
		<category><![CDATA[FactoryTalk]]></category>
		<category><![CDATA[FactoryTalk Optix]]></category>
		<category><![CDATA[FT Optix]]></category>
		<category><![CDATA[HMI]]></category>
		<category><![CDATA[NetLogic]]></category>
		<category><![CDATA[Optix]]></category>
		<category><![CDATA[Presentation Engines]]></category>
		<category><![CDATA[Property Bindings]]></category>
		<category><![CDATA[SCADA]]></category>
		<category><![CDATA[Style Sheets]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/16514/factorytalk-optix-series-1-getting-started-with-factorytalk-optix/</guid>

					<description><![CDATA[<p>FactoryTalk Optix is the next generation of visualization software from Rockwell Automation, meant to provide a higher degree of customized development with its integrated C# support and flexibility for HMI and SCADA applications. This blog series covers the essentials to creating your own HMI and SCADA applications using Optix as well as some helpful examples [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/16514/factorytalk-optix-series-1-getting-started-with-factorytalk-optix/">FactoryTalk Optix Series 1 &#8211; Getting Started with FactoryTalk Optix</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">FactoryTalk Optix is the next generation of visualization software from <a href="https://www.rockwellautomation.com/en-us.html" target="_blank">Rockwell Automation</a>, meant to provide a higher degree of customized development with its integrated C# support and flexibility for HMI and SCADA applications.</p>



<p class="wp-block-paragraph">This blog series covers the essentials to creating your own HMI and SCADA applications using Optix as well as some helpful examples for common applications.</p>



<h2 id="h-installation" class="wp-block-heading">Installation</h2>



<p class="wp-block-paragraph">FactoryTalk Optix Studio is installed via <a href="https://home.cloud.rockwellautomation.com/sign-in?returnTo=%2Fdashboard" target="_blank">FactoryTalk Hub</a>, and it is the IDE used to program Optix projects. You can either download the IDE application&nbsp;or utilize a web-based IDE if your license supports it.</p>



<p class="wp-block-paragraph">The Optix Runtime tools are also installed via FactoryTalk Hub, and they are required on any machine that is going to run an Optix application.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="FactoryTalk Optix Hub" height="437px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/1-ft-optix-hub.png" width="900px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>FactoryTalk Hub:&nbsp;which hosts a number of FT applications (including FactoryTalk Design Studio for Optix)</em></p></p>



<h2 id="h-licensing-and-pricing" class="wp-block-heading">Licensing and Pricing</h2>



<p class="wp-block-paragraph">Optix licensing is done on a token-based system, with more features requiring more tokens, and, therefore, a larger license. Generally speaking, the largest contributors to token usage are multiple concurrent web clients, OPC-UA connections, or database connections.</p>



<p class="wp-block-paragraph">Examples of features that affect the sizing of your application include the following:</p>



<ul class="wp-block-list">
<li>Controller connections</li>



<li>Multiple web clients</li>



<li>Alarming</li>



<li>Recipes</li>



<li>PDF reports</li>



<li>Data logging</li>



<li>Database connectivity</li>



<li>OPC UA connectivity</li>
</ul>



<p class="wp-block-paragraph">For more information on tokens and licensing, please see <a href="https://literature.rockwellautomation.com/idc/groups/literature/documents/at/optix-at001_-en-p.pdf" target="_blank">Rockwell’s documentation</a>.</p>



<h2 id="h-presentation-engines" class="wp-block-heading">Presentation Engines</h2>



<p class="wp-block-paragraph">Presentation engines are responsible for rendering and displaying UI elements during runtime. There are two available presentation engines that can be used simultaneously – the native presentation engine and the web presentation engine.</p>



<p class="wp-block-paragraph">The native presentation engine launches the project in its own window as an application.</p>



<p class="wp-block-paragraph">The web presentation engine hosts the application on a web server, accessible via the defined web page. Multiple concurrent web clients can access the application (if multiple connections are defined in the web presentation engine), but more concurrent clients require more tokens.</p>



<p class="wp-block-paragraph">Optix can make use of session-specific tools to allow concurrent clients to have different screens/panels open, have different users logged in, and display different information, provided that the Optix project was designed with multiple clients in mind.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Optix Presentation Engine" height="680px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2-optix-presentation-engine.png" width="449px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>An example configured Web Presentation Engine</em></p></p>



<h2 id="h-style-sheets" class="wp-block-heading">Style Sheets</h2>



<p class="wp-block-paragraph">Style sheets allow the definition of default colors, shapes, sizes, and other UI element configurations. A project’s style sheet can be swapped during runtime. Some properties can be defined both in the style sheet and in an individual UI element. In the case that a property is explicitly defined for an individual element, it will override the style sheet.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Style Sheet Example" height="506px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/3-style-sheet-example.png" width="585px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>An example Style Sheet</em></p></p>



<h2 id="h-screens-and-panels" class="wp-block-heading">Screens and Panels</h2>



<p class="wp-block-paragraph">FT Optix uses screens, panels, and popups as the main methods for displaying UI elements. Screens and popups can both contain multiple panel loaders, which can be used to dynamically change displayed content when a defined action is performed, such as hitting a button.</p>



<p class="wp-block-paragraph">To see panel loaders being implemented to create a dynamic device sidebar that changes what device it controls (and the associated UI elements) at the push of a button, stay tuned for our upcoming&nbsp;<em>F</em><em>actoryTalk Optix &#8211;&nbsp;Dynamic Device Faceplate</em> blog post.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Panel Loader" height="398px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/4-panel-loader.png" width="900px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>A panel loader that is a part of the main overlay of an application. It has the ability to load either a Motor or a GroundRack panel into the panel loader.</em></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Motor Panel Loaded" height="392px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/5-motor-panel-loaded.png" width="900px">&nbsp;</p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>A runtime instance with the &#8220;Motor&#8221; panel loaded into the panel loader</em></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Rack Panel Loaded" height="391px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/6-rack-panel-loaded.png" width="900px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>A runtime instance with the &#8220;Ground Rack&#8221; panel loaded into the panel loader</em></p></p>



<h2 id="h-converters" class="wp-block-heading">Converters</h2>



<p class="wp-block-paragraph">Converters allow you to modify variables dynamically based on other variables. For instance, a key-value converter could change the text displayed in a string variable when a different integer variable’s value changes, and an engineering unit converter can be used to scale variable values differently.</p>



<p class="wp-block-paragraph">There are more converter types and use cases that were not mentioned above. For more information on converters, stay tuned for our upcoming&nbsp;<em>FactoryTalk Optix &#8211; Variables, Attributes, Dynamic Links, and Converters</em> blog post.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Converter" height="346px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/7-converter.png" width="900px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>A Key-Value converter that parses an integer out into valve status strings</em></p></p>



<h2 id="h-dynamic-links" class="wp-block-heading">Dynamic Links</h2>



<p class="wp-block-paragraph">Dynamic links are used to define relationships between two or more variables or attributes&nbsp;within the FT Optix project. Complex dynamic links&nbsp;allow the&nbsp;use of built-in converters, allowing further customization of UI elements.</p>



<p class="wp-block-paragraph">For further information on Property Bindings, stay tuned for our upcoming<em>&nbsp;FactoryTalk Optix &#8211; Variables, Attributes, Dynamic Links, and Converters</em>&nbsp;blog post.</p>



<p class="wp-block-paragraph">For an example of leveraging complex dynamic links, stay tuned for our upcoming <em>F</em><em>actoryTalk Optix &#8211;&nbsp;Dynamic Device Faceplate</em> blog post.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Dynamic Link" height="532px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/8-dynamic-link.png" width="580px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>A dynamic link that performs mathematical operations on several variables to return an adjusted distance</em></p></p>



<h2 id="h-events" class="wp-block-heading">Events</h2>



<p class="wp-block-paragraph">Events call methods when a specific trigger happens, such as a button being pressed or a variable&#8217;s value changing. Events can be added onto most UI elements, and they can trigger multiple methods.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Example Event" height="645px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/9-example-event.png" width="609px"></p></p>



<h2 id="h-methods" class="wp-block-heading">Methods</h2>



<p class="wp-block-paragraph">Methods are called by events. FT Optix has a number of built-in methods, but custom methods can be defined through NetLogic as well.</p>



<p class="wp-block-paragraph">You can also create pre-defined method invocations, which allow the calling of methods with pre-defined parameters.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Methods" height="511px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/10-methods.png" width="500px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>A list of common methods available in FT Optix</em></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Method Invocation" height="137px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/11-method-invocation.png" width="450px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>A custom method invocation for renaming a MySQL Database table</em></p></p>



<h2 id="h-netlogic" class="wp-block-heading">NetLogic</h2>



<p class="wp-block-paragraph">NetLogic allows C# code to be integrated and run in an FT Optix project. FT Optix can call parameterized methods, and they can send and receive variables with the C# project.</p>



<p class="wp-block-paragraph">For further information on NetLogic, stay tuned for our upcoming&nbsp;<em>FactoryTalk Optix &#8211; NetLogic Overview and Examples</em> blog post.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Net Logic" height="766px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/12-net-logic.png" width="900px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>An example NetLogic snippet that returns a localized time</em></p></p>



<h2 id="h-data-stores" class="wp-block-heading">Data Stores</h2>



<p class="wp-block-paragraph">Data stores are databases used to store values from different loggers and NetLogic (if you so choose). You can either use an embedded database, which is a simple SQLite database created with minimal overhead, or you can choose to create a connection to a database (SQL Server or MySQL). Other database types may be supported through ODBC connections, but they have not been tested or confirmed yet.</p>



<p class="wp-block-paragraph">Loggers can be linked directly to a data store and will automatically configure tables for themselves on said database.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Data Store Table" height="644px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/13-datastore-table.png" width="504px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>An example data store with an &#8220;Alarm Logger&#8221; linked to it, auto generating the necessary tables</em></p></p>



<h2 id="h-communication-drivers" class="wp-block-heading">Communication Drivers</h2>



<p class="wp-block-paragraph">Communication Drivers allow defining communication paths to PLCs and other devices, as well as the importing of tags and other information to be used in the Optix project. A list of the currently available communication drivers is available below.</p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Available Comm Drivers" height="640px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/14-available-comm-drivers.png" width="400px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>All available communication drivers in Optix</em></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Comm Driver" height="456px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/15-comm-driver.png" width="900px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><em>The tag importer display from an example Rockwell Ethernet Driver linked to a PLC</em></p></p>



<h2 id="h-template-library" class="wp-block-heading">Template Library</h2>



<p class="wp-block-paragraph"><p style="text-align: center;"><img decoding="async" alt="Template Library" height="888px" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/16-template-library.png" width="819px"></p></p>



<p class="wp-block-paragraph"><p style="text-align: center;"><i>Optix&#8217;s Template Library</i></p></p>



<p class="wp-block-paragraph">The template library stores templates created by Rockwell and allows you to create/import your own libraries as well. Some examples of useful templates that are included with Optix are:</p>



<ul class="wp-block-list">
<li>Alarm Grid</li>



<li>Alam History</li>



<li>Alarm Banner</li>



<li>User Login Popup</li>



<li>Confirmation Dialog</li>



<li>Date and Time Display</li>



<li>File Selector</li>



<li>File System Browser</li>



<li>Alarm Importer and Exporter</li>



<li>Alarm Logger</li>
</ul>



<h2 id="h-read-the-other-articles-in-this-series" class="wp-block-heading">Read the Other Articles in this Series</h2>



<ul class="wp-block-list">
<li><a href="https://static.dmcinfo.com/latest-thinking/blog/id/10609/factorytalk-optix-series-2--variables-attributes-dynamic-links-and-converters">FactoryTalk Optix Series 2 &#8211; Variables, Attributes, Dynamic Links, and Converters</a></li>



<li><a href="https://static.dmcinfo.com/latest-thinking/blog/id/10610/factorytalk-optix-series-3--netlogic-overview-and-examples">FactoryTalk Optix Series 3 &#8211; NetLogic Overview and Examples</a></li>
</ul>



<div class="wp-block-group alignwide has-custom-light-blue-background-color has-background is-layout-flow wp-container-core-group-is-layout-dbd34961 wp-block-group-is-layout-flow" style="border-radius:20px;margin-top:var(--wp--preset--spacing--50);margin-bottom:var(--wp--preset--spacing--50);padding-top:var(--wp--preset--spacing--50);padding-right:0;padding-bottom:var(--wp--preset--spacing--50);padding-left:0">
<div class="wp-block-columns alignwide are-vertically-aligned-center is-layout-flex wp-container-core-columns-is-layout-43efaee5 wp-block-columns-is-layout-flex" style="padding-right:var(--wp--preset--spacing--60);padding-left:var(--wp--preset--spacing--60)">
<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:85%">
<h3 class="wp-block-heading has-text-align-left" id="h-have-an-upcoming-project-dmc-can-help-you-take-the-next-step"><strong>Build Modern HMI and SCADA Applications with FactoryTalk Optix</strong>.</h3>



<p class="has-text-align-left wp-block-paragraph" id="h-need-help-turning-ideas-into-outcomes-automation-project-to-the-next-level-contact-us-today-to-learn-more-about-our-solutions-and-how-we-can-help-you-achieve-your-goals">Explore our <a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/" data-type="page" data-id="420">Automation</a> expertise in <a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/hmi-and-scada-programming/rockwell-factorytalk-programming/factorytalk-optix-programming/" data-type="page" data-id="44850">FactoryTalk Optix</a> for flexible visualization, web-based HMIs, C# NetLogic, and industrial data integration.</p>
</div>



<div class="wp-block-column is-vertically-aligned-center is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:15%">
<div class="wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-2236275c wp-block-buttons-is-layout-flex">
<div class="wp-block-button is-style-fill"><a class="wp-block-button__link has-base-contrast-color has-text-color has-link-color wp-element-button" href="https://static.dmcinfo.com/contact/">Contact Us</a></div>
</div>
</div>
</div>
</div>
<p>The post <a href="https://static.dmcinfo.com/blog/16514/factorytalk-optix-series-1-getting-started-with-factorytalk-optix/">FactoryTalk Optix Series 1 &#8211; Getting Started with FactoryTalk Optix</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Getting started with C++ on a Beckhoff PLC &#8211; Part 2: Creating the ST/C++ Interface</title>
		<link>https://static.dmcinfo.com/blog/17475/getting-started-with-c-on-a-beckhoff-plc-part-2-creating-the-st-c-interface/</link>
		
		<dc:creator><![CDATA[DMC]]></dc:creator>
		<pubDate>Sat, 17 Jun 2023 18:12:23 +0000</pubDate>
				<category><![CDATA[Beckhoff PLC]]></category>
		<category><![CDATA[Manufacturing Automation & Intelligence]]></category>
		<category><![CDATA[PLC]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[TwinCAT]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/17475/getting-started-with-c-on-a-beckhoff-plc-part-2-creating-the-st-c-interface/</guid>

					<description><![CDATA[<p>Other Installments in This Series: The second part of this series on how to run C++ real-time on a Beckhoff PLC will cover creating the interface that will host the C++ code, creating methods and data types within that interface, and how to access that interface from the structured text code. The C++ Interface is [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/17475/getting-started-with-c-on-a-beckhoff-plc-part-2-creating-the-st-c-interface/">Getting started with C++ on a Beckhoff PLC &#8211; Part 2: Creating the ST/C++ Interface</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><strong><u>Other Installments in This Series:</u></strong></p>



<ul class="wp-block-list">
<li><a href="https://static.dmcinfo.com/blog/17484/getting-started-with-c-on-a-beckhoff-plc-part-one-setup/">Getting started with C++ on a Beckhoff PLC &#8211; Part One: Setup</a></li>
</ul>



<p class="wp-block-paragraph">The second part of this series on how to run C++ real-time on a Beckhoff PLC will cover creating the interface that will host the C++ code, creating methods and data types within that interface, and how to access that interface from the structured text code.</p>



<p class="wp-block-paragraph">The C++ Interface is the same concept as interfaces in structured text programming on Beckhoff PLCs, however the interface is implemented through the C++ TMC file (TwinCAT Module Class Editor) instead of a POU.</p>



<p class="wp-block-paragraph">The interface will specify method declarations, return types, and the parameters needed, while the C++ file will provide the method definitions.</p>



<h2 class="wp-block-heading" id="h-table-of-contents">Table of Contents</h2>



<ol class="wp-block-list">
<li><a href="#h-creating-the-c-interface">Creating the C++ Interface</a></li>



<li><a href="#h-static-link-to-c-module">Static Link to C++ Module</a>
<ul class="wp-block-list">
<li><a href="#h-creating-the-c-tccom-module">Creating the C++ TcCOM Module</a></li>



<li><a href="#h-connect-the-c-interface-to-structured-text">Connect the C++ Interface to Structured Text</a></li>
</ul>
</li>



<li><a href="#h-dynamic-link-to-nbsp-c-module-nbsp">Dynamic&nbsp;Link to C++ Module</a></li>



<li><a href="#h-tips-and-tricks">Tips and Tricks</a></li>
</ol>



<h2 class="wp-block-heading" id="h-creating-the-c-interface">Creating the C++ Interface</h2>



<p class="wp-block-paragraph"><a href="#h-table-of-contents"><i>Back to Table of Contents</i></a></p>



<ol class="wp-block-list">
<li>To create the interface, open the .tmc file in the C++ project.</li>
</ol>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Getting-started-with-C__-on-Beckhoff-Pt-2-_tmc-file.png" alt="A screenshot of a computerDescription automatically generated"/></figure>



<ol class="wp-block-list">
<li></li>
</ol>



<p class="wp-block-paragraph">2. In the TMC editor, click Data Types and click &#8220;Adds a new interface ()&#8221;</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Adds-a-new-interface.png" alt="A screenshot of a computerDescription automatically generated"/></figure>



<p class="wp-block-paragraph">3. The interface will contain all the methods that will be made accessible. To add new methods, click the drop-down icon to the left of the interface and select &#8220;Methods&#8221;. The buttons in the ribbon can create, remove, reorganize, or copy and paste methods.</p>



<ol class="wp-block-list">
<li></li>
</ol>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Getting-started-with-C__-on-Beckhoff-Pt-2-Method-1_1.png" alt="A screenshot of a computerDescription automatically generated"/></figure>



<p class="wp-block-paragraph">4. Once a method has been created, double click it to edit its parameters and return type.</p>



<ol class="wp-block-list">
<li></li>
</ol>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Method-Parameters.png" alt="A screenshot of a computerDescription automatically generated"/></figure>



<p class="wp-block-paragraph"><strong>Note about custom data types:</strong></p>



<p class="wp-block-paragraph">a. While the TMC editor allows you to create structs in the module and set the interface method&#8217;s return data type to a struct, TwinCAT does not allow structs as a return data type in the structured text / C++ interface. Doing so will cause a <em>Structured return value not allowed in external function calls</em> error.</p>



<p class="wp-block-paragraph">b. Additionally, structs cannot be used as parameters in method calls either. This will cause a <em>Structured value types not allowed in external function calls</em> error.</p>



<p class="wp-block-paragraph">c. Because of this limitation, to return multiple values from a method, it is recommended to use pointers or references to your desired return value as parameters of the method. TwinCAT will pass these values by reference. </p>



<p class="wp-block-paragraph">5. When editing Method Parameters, TwinCAT allows you to specify parameters as pointers, pointer to pointers, or a reference.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-15_42_59-Getting-started-with-C__-on-Beckhoff-Pt-2-pointer-type.png" alt="A screenshot of a computerDescription automatically generated"/></figure>



<ol class="wp-block-list">
<li></li>
</ol>



<p class="wp-block-paragraph">6. Add all methods with return types and parameters to the interface. </p>



<ol class="wp-block-list">
<li></li>
</ol>



<p class="wp-block-paragraph">7. Once all methods are added, add the new interface to the TMC&#8217;s Implemented Interfaces section. This specifies that we are implementing the code for the interface within the C++ module. The interface created should appear in the list next to &#8220;(local)&#8221;.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-15_44_16-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Implemented-Interfaces.png" alt="A screenshot of a computerDescription automatically generated"/></figure>



<ol class="wp-block-list">
<li></li>
</ol>



<p class="wp-block-paragraph">8. Click the &#8220;Run TMC Code Generator&#8221; button and TwinCAT will automatically create function definitions and declarations in the .cpp file. The &#8220;Fun TMC Code Generator&#8221; can be run by clicking the button.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-15_58_02-Getting-started-with-C__-on-Beckhoff-Pt-2-TwinCAT-3-TMC-Code-Generator.png" alt="A screenshot of a computerDescription automatically generated"/></figure>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Getting-started-with-C__-on-Beckhoff-Pt-2-Run-TMC-Code-Generator.png" alt="A screenshot of a computerDescription automatically generated with medium confidence"/></figure>



<p class="wp-block-paragraph">a. The TMC Code Generator must be run any time you make changes to the TMC file (editing method parameters, return types, and adding new methods, etc.).</p>



<p class="wp-block-paragraph">b. If a previously generated method&#8217;s parameters or return type were edited, TwinCAT will move the program to the bottom of the .cpp file under an &lt;AutoGeneratedContent id=&#8221;Obsolete_ImplementationOf_IInterface1&#8243;&gt; tag, and provide an empty function implementation. You can copy the previous code and make any edits as necessary.</p>



<p class="wp-block-paragraph">9. Add the C++ code into the function definitions.</p>



<p class="wp-block-paragraph">10. Once all functions are added, compile the C++ code into a TMX file by right clicking the C++ Project and selecting &#8220;Publish Modules&#8221;. The Build Output of Visual Studio will show if the C++ project compiled successfully.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-TwinCAT-publish-modules.png" alt="A screenshot of a computerDescription automatically generated"/></figure>



<p class="wp-block-paragraph">After creating the C++ interface, it must be linked to the structured text by creating a proxy function block. The structured text function block will host an interface pointer to the C++ memory which will expose the methods defined in the TMC file and allow the function block to call the C++ functions.</p>



<p class="wp-block-paragraph">This can be done by creating a TcCOM object that is linked to a structured text FB (Section B) or can be dynamically created from within the structured text. (Section C).</p>



<h2 class="wp-block-heading" id="h-static-link-to-c-module">Static Link to C++ Module</h2>



<p class="wp-block-paragraph"><a href="#h-table-of-contents"><i>Back to Table of Contents</i></a></p>



<p class="wp-block-paragraph">The static link will create individual proxy function blocks for each instance of the C++ module. This is done by creating TcCOM modules for each instance desired.&nbsp;All instances&nbsp;must be created before runtime and cannot be created/deleted during runtime.&nbsp;</p>



<h3 class="wp-block-heading" id="h-creating-the-c-tccom-module">Creating the C++ TcCOM Module</h3>



<ol class="wp-block-list">
<li>After compiling the code, a C++ Module instance must be created. This can be added in the C++ project, or under System/TcCOM Objects.
<ul class="wp-block-list">
<li>In the C++ Project, right click the C++ project and click &#8220;Add new Item&#8221;. In the Insert TcCom Object popup, it will search the local project for the module&#8217;s TMC file. Select the module created and click OK.</li>



<li>If adding under System/TcCOM, TwinCAT will search your development PC (not the target PC) for the TMC file. Search for the module and verify that the file path is correct, then click OK. </li>



<li></li>
</ul>
</li>
</ol>



<figure class="wp-block-image size-full"><img fetchpriority="high" decoding="async" width="900" height="459" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Getting-Started-with-C__-on-Beckhoff-Part-2-Add-New-Item.png" alt="A screenshot of a computer programDescription automatically generated with medium confidence" class="wp-image-17463" srcset="https://static.dmcinfo.com/wp-content/uploads/2025/05/Getting-Started-with-C__-on-Beckhoff-Part-2-Add-New-Item.png 900w, https://static.dmcinfo.com/wp-content/uploads/2025/05/Getting-Started-with-C__-on-Beckhoff-Part-2-Add-New-Item-300x153.png 300w, https://static.dmcinfo.com/wp-content/uploads/2025/05/Getting-Started-with-C__-on-Beckhoff-Part-2-Add-New-Item-768x392.png 768w" sizes="(max-width: 900px) 100vw, 900px" /></figure>



<p class="wp-block-paragraph">2. Double-click the Module Instance and select &#8220;Interfaces&#8221;. You should see your created interface in the list. This verifies that you have created your C++ interface and instance correctly. </p>



<p class="wp-block-paragraph">3. The TcCOM allows a system task to be assigned to the module in the &#8220;Context&#8221; tab; however, that is only necessary if you have cyclic code. If you are only providing interface methods, it is not required. </p>



<ol class="wp-block-list">
<li></li>
</ol>



<h3 class="wp-block-heading" id="h-connect-the-c-interface-to-structured-text">Connect the C++ Interface to Structured Text</h3>



<p class="wp-block-paragraph">Once a TcCOM module has been created for the C++ code, it must be linked to a structured text function block through an interface pointer. The function <a href="https://infosys.beckhoff.com/content/1033/tcplclib_tc3_module/1900117643.html?id=4396498500852015490" target="_blank" rel="noreferrer noopener">FW_ObjMgr_GetObjectInstance</a> can be used to create an interface pointer to an object instance upon function block initialization: FB_Init().</p>



<p class="wp-block-paragraph">Upon function block deletion, FB_Exit() is called which runs <a href="https://infosys.beckhoff.com/content/1033/tcplclib_tc3_module/1900190219.html?id=2960691403916095942" target="_blank" rel="noreferrer noopener">FW_SafeRelease</a> which releases the interface pointer memory. Note: this does not delete the C++ memory and any internal variables will be stored.</p>



<ol class="wp-block-list">
<li>Create a PLC Project if one does not exist.</li>



<li>Add a new POU for the C++ Interface.</li>
</ol>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_17_18-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Add-new-POU.png" alt="Add new POU"/></figure>



<p class="wp-block-paragraph">3. A popup should appear allowing you to input a name for the POU.</p>



<p class="wp-block-paragraph">4. Add the following vars and methods to the function block in the main FB:</p>



<pre class="wp-block-code"><code>
VAR
{attribute 'TcInitSymbol'}
oidInstance : OTCID;
//ipInterface is the name of the interface pointer. You can provide any name.
ipInterface : IInterface1;
hrInit : HRESULT;
END_VAR
}
</code></pre>



<p class="wp-block-paragraph">A. <strong>FB_Init()</strong> &#8211; This code connects the object specified at oidInstance with the interface specified at iid (in step 10) and provides the pointer to the interface in pipUnk. </p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">ST</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
METHOD FB_init : BOOL
VAR_INPUT
	bInitRetains : BOOL; // if TRUE, the retain variables are initialized (warm start / cold start)
	bInCopyCode : BOOL;  // if TRUE, the instance afterwards gets moved into the copy code (online change)
END_VAR
//Implementation
IF NOT bInCopyCode THEN 
		IF ipInterface1 = 0 THEN
			hrInit := FW_ObjMgr_GetObjectInstance ( oid:=oidInstance,
													iid:=TC_GLOBAL_IID_LIST.IID_IInterface1,
													pipUnk:=ADR(ipInterface1) );
		END_IF
END_IF
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #569CD6">METHOD</span><span style="color: #D4D4D4"> FB_init : BOOL</span></span>
<span class="line"><span style="color: #D4D4D4">VAR_INPUT</span></span>
<span class="line"><span style="color: #D4D4D4">	bInitRetains : BOOL; </span><span style="color: #6A9955">// if TRUE, the retain variables are initialized (warm start / cold start)</span></span>
<span class="line"><span style="color: #D4D4D4">	bInCopyCode : BOOL;  </span><span style="color: #6A9955">// if TRUE, the instance afterwards gets moved into the copy code (online change)</span></span>
<span class="line"><span style="color: #D4D4D4">END_VAR</span></span>
<span class="line"><span style="color: #6A9955">//Implementation</span></span>
<span class="line"><span style="color: #569CD6">IF</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">NOT</span><span style="color: #D4D4D4"> bInCopyCode </span><span style="color: #569CD6">THEN</span><span style="color: #D4D4D4"> </span></span>
<span class="line"><span style="color: #D4D4D4">		</span><span style="color: #569CD6">IF</span><span style="color: #D4D4D4"> ipInterface1 = </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">THEN</span></span>
<span class="line"><span style="color: #D4D4D4">			hrInit := FW_ObjMgr_GetObjectInstance ( oid:=oidInstance,</span></span>
<span class="line"><span style="color: #D4D4D4">													iid:=TC_GLOBAL_IID_LIST.IID_IInterface1,</span></span>
<span class="line"><span style="color: #D4D4D4">													pipUnk:=ADR(ipInterface1) );</span></span>
<span class="line"><span style="color: #D4D4D4">		END_IF</span></span>
<span class="line"><span style="color: #D4D4D4">END_IF</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">[endif]-</p>



<p class="wp-block-paragraph">B. <strong>FB_Exit()</strong> &#8211; This code deletes the interface pointer. </p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">ST</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>
METHOD FB_exit : BOOL
VAR_INPUT
	bInCopyCode : BOOL; // if TRUE, the exit method is called for exiting an instance that is copied afterwards (online change).
END_VAR
//Implementation
IF NOT bInCopyCode THEN
	FW_SafeRelease(ADR(ipInterface1));
END_IF
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"></span>
<span class="line"><span style="color: #569CD6">METHOD</span><span style="color: #D4D4D4"> FB_exit : BOOL</span></span>
<span class="line"><span style="color: #D4D4D4">VAR_INPUT</span></span>
<span class="line"><span style="color: #D4D4D4">	bInCopyCode : BOOL; </span><span style="color: #6A9955">// if TRUE, the exit method is called for exiting an instance that is copied afterwards (online change).</span></span>
<span class="line"><span style="color: #D4D4D4">END_VAR</span></span>
<span class="line"><span style="color: #6A9955">//Implementation</span></span>
<span class="line"><span style="color: #569CD6">IF</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">NOT</span><span style="color: #D4D4D4"> bInCopyCode </span><span style="color: #569CD6">THEN</span></span>
<span class="line"><span style="color: #D4D4D4">	FW_SafeRelease(ADR(ipInterface1));</span></span>
<span class="line"><span style="color: #D4D4D4">END_IF</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">5. The C++ Methods can now be called the interface pointer&#8217;s methods. These method calls can be made in the FB&#8217;s main implementation or any FB method or action.</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C++</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>IF ipInterface &lt;> 0 THEN
    // ipInterface is the interface pointer.
    getPrivInt := ipInterface.SetPrivInt(5);
END_IF</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">IF ipInterface &lt;&gt; </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4"> THEN</span></span>
<span class="line"><span style="color: #6A9955">    // ipInterface is the interface pointer.</span></span>
<span class="line"><span style="color: #D4D4D4">    getPrivInt := </span><span style="color: #9CDCFE">ipInterface</span><span style="color: #D4D4D4">.</span><span style="color: #DCDCAA">SetPrivInt</span><span style="color: #D4D4D4">(</span><span style="color: #B5CEA8">5</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">END_IF</span></span></code></pre></div>



<pre class="wp-block-preformatted">
6. Add an instance of the FB to MAIN or any other POUs. Once all methods are added, click "Build" and see if the project compiles successfully. Once built, click the PLC instance and there will be a tab called "Symbol Initialization". Select the oildInstance for the C++ object instance in the drop-down list.</pre>



<p class="wp-block-paragraph">7. Click &#8220;Apply to config&#8221; to download the structure text and C++ code to the PLC.</p>



<h2 class="wp-block-heading" id="h-dynamic-link-to-nbsp-c-module-nbsp">Dynamic Link to&nbsp;C++ Module&nbsp;</h2>



<p class="wp-block-paragraph"><a href="#h-table-of-contents"><i>Back to Table of Contents</i></a></p>



<p class="wp-block-paragraph">Instead of creating a TcCOM object that is an instance of the C++ code, the structured text function block can also dynamically create the TcCOM module during initialization: FB_Init(), or any method. This can be done using Twincat’s function <a href="https://infosys.beckhoff.com/content/1033/tcplclib_tc3_module/1899956875.html?id=6855305224713025932">FW_ObjMgr_CreateAndInitInstance</a>.</p>



<p class="wp-block-paragraph">Upon function block deletion, FB_Exit() is called which will run <a href="https://infosys.beckhoff.com/content/1033/tcplclib_tc3_module/1900069259.html?id=7445048424541430396" target="_blank" rel="noreferrer noopener">FW_ObjMgr_DeleteInstance</a>.&nbsp;</p>



<ol class="wp-block-list">
<li>Click the &#8220;TcCOM Objects&#8221; and select &#8220;Class Factories&#8221;. This list displays all C++ drivers on the computer. Ensure that the C++ driver you wish to implement has &#8220;Load&#8221; checked.</li>
</ol>



<figure class="wp-block-image size-full"><img decoding="async" width="900" height="226" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_22_15-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Load-Driver.png" alt="2023 06 17 16 22 15 Getting started with C on Beckhoff Pt 2 AutoRecovered Load Driver" class="wp-image-17467" srcset="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_22_15-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Load-Driver.png 900w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_22_15-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Load-Driver-300x75.png 300w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_22_15-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Load-Driver-768x193.png 768w" sizes="(max-width: 900px) 100vw, 900px" /></figure>



<p class="wp-block-paragraph">2. Create a structured text function block. This function block is what will create the C++ interface instance. </p>



<p class="wp-block-paragraph">3. Add the Tc2_Utilities library to your project references. This library is needed for the &#8220;STRING_TO_GUID&#8221; function used in FB_Init().</p>



<figure class="wp-block-image size-full"><img decoding="async" width="900" height="296" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_22_41-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Add-Library.png" alt="A screenshot of a computerDescription automatically generated with medium confidence" class="wp-image-17468" srcset="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_22_41-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Add-Library.png 900w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_22_41-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Add-Library-300x99.png 300w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_22_41-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Add-Library-768x253.png 768w" sizes="(max-width: 900px) 100vw, 900px" /></figure>



<p class="wp-block-paragraph">&nbsp;a. Under References, right click and select &#8220;Add library&#8221;.</p>



<p class="wp-block-paragraph">b. Search &#8220;Utilities&#8221; at the top and add Tc2_Utilities.</p>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph">4. Next, get the GUID and Library ID of the interface. This can be done by creating a TcCOM object and copying the GUID and Class ID. (instructions on how to create a TcCOM object can be found&nbsp;in part B). <!-- width="515" height="219" src="file:///C:/Users/aizhat/AppData/Local/Temp/msohtmlclip1/01/clip_image046.jpg" alt="A screenshot of a computer

Description automatically generated" /--></p>



<p class="wp-block-paragraph">5. Add the following functions and code to your function block:<!-- -->In FB’s implementation.</p>



<p class="wp-block-paragraph">a. In FB&#8217;s implementation. The &lt;GUID&gt; is the GUID specified in the previous step, and &lt;Library ID&gt; is the text in the parentheses in Class Factory. </p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">ST</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>FUNCTION_BLOCK FB_CppDynamicLink VAR_OUTPUT   // this does not have to be an array     
ipInterface  : ARRAY &#91;1..5&#93; OF CppInterface;     
numInterfaces : INT := 1; 
END_VAR 
VAR     
  objName : STRING;     
  classId : CLSID := STRING_TO_GUID('&lt;GUID>');     
  sLibraryId  : STRING := '&lt;Class ID>';     
  classIdVersioned : CLSID;     
  iid : IID := TC_GLOBAL_IID_LIST.IID_&lt;InterfaceName>;     
  hrInit  : HRESULT; 
END_VAR</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">FUNCTION_BLOCK FB_CppDynamicLink VAR_OUTPUT   </span><span style="color: #6A9955">// this does not have to be an array     </span></span>
<span class="line"><span style="color: #D4D4D4">ipInterface  : </span><span style="color: #569CD6">ARRAY</span><span style="color: #D4D4D4"> &#91;</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">.</span><span style="color: #B5CEA8">.5</span><span style="color: #D4D4D4">&#93; </span><span style="color: #569CD6">OF</span><span style="color: #D4D4D4"> CppInterface;     </span></span>
<span class="line"><span style="color: #D4D4D4">numInterfaces : INT := </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">; </span></span>
<span class="line"><span style="color: #D4D4D4">END_VAR </span></span>
<span class="line"><span style="color: #569CD6">VAR</span><span style="color: #D4D4D4">     </span></span>
<span class="line"><span style="color: #D4D4D4">  objName : </span><span style="color: #569CD6">STRING</span><span style="color: #D4D4D4">;     </span></span>
<span class="line"><span style="color: #D4D4D4">  classId : CLSID := STRING_TO_GUID(</span><span style="color: #CE9178">&apos;&lt;GUID&gt;&apos;</span><span style="color: #D4D4D4">);     </span></span>
<span class="line"><span style="color: #D4D4D4">  sLibraryId  : </span><span style="color: #569CD6">STRING</span><span style="color: #D4D4D4"> := </span><span style="color: #CE9178">&apos;&lt;Class ID&gt;&apos;</span><span style="color: #D4D4D4">;     </span></span>
<span class="line"><span style="color: #D4D4D4">  classIdVersioned : CLSID;     </span></span>
<span class="line"><span style="color: #D4D4D4">  iid : IID := TC_GLOBAL_IID_LIST.IID_&lt;InterfaceName&gt;;     </span></span>
<span class="line"><span style="color: #D4D4D4">  hrInit  : HRESULT; </span></span>
<span class="line"><span style="color: #D4D4D4">END_VAR</span></span></code></pre></div>



<p class="wp-block-paragraph">b. FB_Init() &#8211; This code will create C++ instance upon function block&#8217;s initialization. This code can also be adapted to create additional instances during runtime. </p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Pascal</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&#91;Declaration&#93; 
METHOD FB_init : BOOL VAR_INPUT bInitRetains : BOOL; // if TRUE, the retain variables are initialized (warm start / cold start) 
bInCopyCode : BOOL; // if TRUE, the instance afterwards gets moved into the copy code (online change) 
sObjName : STRING; 
eObjState : TCOM_STATE; 
END_VAR &#91;Implementation&#93; 
IF NOT bInCopyCode THEN objName := sObjName; 
  F_GetClassIdVersioned(sLibraryId := sLibraryId, clsId := classId, clsIdVersioned:=classIdVersioned); 
  hrInit := FW_ObjMgr_CreateAndInitInstance( clsId := classIdVersioned, iid := iid, pipUnk := ADR(ipInterface) + SIZEOF(ipInterface&#91;1&#93;)*(numInterfaces-1), objId := OTCID_CreateNewId, parentId:= TwinCAT_SystemInfoVarList._AppInfo.ObjId, name := sObjName, state := eObjState, pInitData:= 0); 
END_IF</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&#91;Declaration&#93; </span></span>
<span class="line"><span style="color: #569CD6">METHOD</span><span style="color: #D4D4D4"> FB_init : BOOL VAR_INPUT bInitRetains : BOOL; </span><span style="color: #6A9955">// if TRUE, the retain variables are initialized (warm start / cold start) </span></span>
<span class="line"><span style="color: #D4D4D4">bInCopyCode : BOOL; </span><span style="color: #6A9955">// if TRUE, the instance afterwards gets moved into the copy code (online change) </span></span>
<span class="line"><span style="color: #D4D4D4">sObjName : </span><span style="color: #569CD6">STRING</span><span style="color: #D4D4D4">; </span></span>
<span class="line"><span style="color: #D4D4D4">eObjState : TCOM_STATE; </span></span>
<span class="line"><span style="color: #D4D4D4">END_VAR &#91;</span><span style="color: #569CD6">Implementation</span><span style="color: #D4D4D4">&#93; </span></span>
<span class="line"><span style="color: #569CD6">IF</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">NOT</span><span style="color: #D4D4D4"> bInCopyCode </span><span style="color: #569CD6">THEN</span><span style="color: #D4D4D4"> objName := sObjName; </span></span>
<span class="line"><span style="color: #D4D4D4">  F_GetClassIdVersioned(sLibraryId := sLibraryId, clsId := classId, clsIdVersioned:=classIdVersioned); </span></span>
<span class="line"><span style="color: #D4D4D4">  hrInit := FW_ObjMgr_CreateAndInitInstance( clsId := classIdVersioned, iid := iid, pipUnk := ADR(ipInterface) + SIZEOF(ipInterface&#91;</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">&#93;)*(numInterfaces-</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">), objId := OTCID_CreateNewId, parentId:= TwinCAT_SystemInfoVarList._AppInfo.ObjId, </span><span style="color: #569CD6">name</span><span style="color: #D4D4D4"> := sObjName, state := eObjState, pInitData:= </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4">); </span></span>
<span class="line"><span style="color: #D4D4D4">END_IF</span></span></code></pre></div>



<p class="wp-block-paragraph">c. FB_Exit()</p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">ST</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>&#91;Declaration&#93;
METHOD FB_exit : BOOL
VAR_INPUT
    bInCopyCode : BOOL; // if TRUE, the exit method is called for exiting an instance that is copied afterwards (online change).
END_VAR
VAR
    i : INT;
END_VAR
&#91;Implementation&#93;
IF NOT bInCopyCode THEN
    FOR i := 1 TO 5 DO
        IF ipInterface&#91;i&#93; &lt;> 0 THEN
            FW_ObjMgr_DeleteInstance(ADR(ipInterface) + SIZEOF(ipInterface&#91;1&#93;)*(numInterfaces-1));
        END_IF
    END_FOR
END_IF
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4">&#91;Declaration&#93;</span></span>
<span class="line"><span style="color: #569CD6">METHOD</span><span style="color: #D4D4D4"> FB_exit : BOOL</span></span>
<span class="line"><span style="color: #D4D4D4">VAR_INPUT</span></span>
<span class="line"><span style="color: #D4D4D4">    bInCopyCode : BOOL; </span><span style="color: #6A9955">// if TRUE, the exit method is called for exiting an instance that is copied afterwards (online change).</span></span>
<span class="line"><span style="color: #D4D4D4">END_VAR</span></span>
<span class="line"><span style="color: #569CD6">VAR</span></span>
<span class="line"><span style="color: #D4D4D4">    i : INT;</span></span>
<span class="line"><span style="color: #D4D4D4">END_VAR</span></span>
<span class="line"><span style="color: #D4D4D4">&#91;</span><span style="color: #569CD6">Implementation</span><span style="color: #D4D4D4">&#93;</span></span>
<span class="line"><span style="color: #569CD6">IF</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">NOT</span><span style="color: #D4D4D4"> bInCopyCode </span><span style="color: #569CD6">THEN</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">FOR</span><span style="color: #D4D4D4"> i := </span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">TO</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">5</span><span style="color: #D4D4D4"> </span><span style="color: #C586C0">DO</span></span>
<span class="line"><span style="color: #D4D4D4">        </span><span style="color: #569CD6">IF</span><span style="color: #D4D4D4"> ipInterface&#91;i&#93; &lt;&gt; </span><span style="color: #B5CEA8">0</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">THEN</span></span>
<span class="line"><span style="color: #D4D4D4">            FW_ObjMgr_DeleteInstance(ADR(ipInterface) + SIZEOF(ipInterface&#91;</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">&#93;)*(numInterfaces-</span><span style="color: #B5CEA8">1</span><span style="color: #D4D4D4">));</span></span>
<span class="line"><span style="color: #D4D4D4">        END_IF</span></span>
<span class="line"><span style="color: #D4D4D4">    END_FOR</span></span>
<span class="line"><span style="color: #D4D4D4">END_IF</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">6. Add all function block methods to call the C++ methods as desired. The example at the bottom of the article shows how an internal C++ variable can be modified and accessed from the structured text.</p>



<p class="wp-block-paragraph">7. Once the proxy function block is made, create an instance of the FB in MAIN or any other program. Any of the proxy function block&#8217;s methods can be called from that program.&nbsp;</p>



<h2 class="wp-block-heading" id="h-tips-and-tricks">Tips and Tricks</h2>



<p class="wp-block-paragraph"><a href="#h-table-of-contents"><i>Back to Table of Contents</i></a></p>



<ol class="wp-block-list">
<li><!-- -->While doable in C++ real time code, it may be easier to do any file IO (Reading/Writing from CSV) within the structured text and passing the read data to the C++ through a method call.&nbsp;</li>



<li><!-- -->TwinCAT had difficulty passing in 2D array pointers from Structured Text to C++. Because of this, I found the best way was to split the 2D array within the structured text into multiple 1D arrays that are passed in, and a Boolean to flag when it is the last row in the 2D array.<!-- --></li>



<li>To debug the C++, Follow these steps:<!-- --></li>
</ol>



<p class="wp-block-paragraph">a. Build the C++ project in Debug instead of Release.</p>



<p class="wp-block-paragraph"><img decoding="async" alt="" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_25_20-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Debug.png"> <!-- width="555" height="560" src="file:///C:/Users/aizhat/AppData/Local/Temp/msohtmlclip1/01/clip_image058.jpg" alt="A screenshot of a computer

Description automatically generated" /--></p>



<p class="wp-block-paragraph">b. In the C++ Node settings, select the Enable C++ Debugger checkbox.</p>



<figure class="wp-block-image size-full"><img decoding="async" width="900" height="571" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_25_45-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Enable-C-__-Debugger.png" alt="2023 06 17 16 25 45 Getting started with C on Beckhoff Pt 2 AutoRecovered Enable C Debugger" class="wp-image-17470" srcset="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_25_45-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Enable-C-__-Debugger.png 900w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_25_45-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Enable-C-__-Debugger-300x190.png 300w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_25_45-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Enable-C-__-Debugger-768x487.png 768w" sizes="(max-width: 900px) 100vw, 900px" /></figure>



<p class="wp-block-paragraph">c. Right click your C++ project, go to Debug and click &#8220;Start New Instance&#8221;.</p>



<figure class="wp-block-image size-full"><img decoding="async" width="700" height="710" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_26_23-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Start-new-instance_1.png" alt="2023 06 17 16 26 23 Getting started with C on Beckhoff Pt 2 AutoRecovered Start new instance 1" class="wp-image-17471" srcset="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_26_23-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Start-new-instance_1.png 700w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_26_23-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Start-new-instance_1-296x300.png 296w" sizes="(max-width: 700px) 100vw, 700px" /></figure>



<p class="wp-block-paragraph">d. The C++ Debugger should start if you are running the C++ code. Keep in mind that the step controls for the C++ debugger are different from the Structured Text step controls and PLC controls. These are the specific controls for debugging the C++ :</p>



<figure class="wp-block-image size-full"><img decoding="async" width="900" height="70" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_27_59-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Controls.png" alt="2023 06 17 16 27 59 Getting started with C on Beckhoff Pt 2 AutoRecovered Controls" class="wp-image-17472" srcset="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_27_59-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Controls.png 900w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_27_59-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Controls-300x23.png 300w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_27_59-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Controls-768x60.png 768w" sizes="(max-width: 900px) 100vw, 900px" /></figure>



<p class="wp-block-paragraph">4. Running multiple instances of the C++ Module are allowed and will have separate memory from one another. This can be done by:</p>



<p class="wp-block-paragraph">a. Creating multiple TcCOM objects and FB instances as described in Section B. Once all new FB instances are made, select the correct OID for each instances in the Symbol Initialization page. (Step 7 of Part B) Note: Both TcCOM objects are C++ project Instances are allowed.</p>



<figure class="wp-block-image size-full"><img decoding="async" width="900" height="89" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_28_38-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Tcom-options-and-Project-instances.png" alt="2023 06 17 16 28 38 Getting started with C on Beckhoff Pt 2 AutoRecovered Tcom options and Project instances" class="wp-image-17473" srcset="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_28_38-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Tcom-options-and-Project-instances.png 900w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_28_38-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Tcom-options-and-Project-instances-300x30.png 300w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_28_38-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-Tcom-options-and-Project-instances-768x76.png 768w" sizes="(max-width: 900px) 100vw, 900px" /></figure>



<p class="has-text-align-center wp-block-paragraph"><em>Figure 1: Object 1 and 2 are TcCOM objects, while Untitled3_Obj1 is a C++ Project instance.</em></p>



<p class="wp-block-paragraph">b. Creating multiple instances of the dynamic function block as described in Section C. Because the function block dynamically creates the C++ instances, the oidInstances do not need to be set. The sObjName can be modified however, it is not required. Both instances will be unique.</p>



<p class="wp-block-paragraph">c. A mix of dynamic and static instances.</p>



<figure class="wp-block-image size-full"><img decoding="async" width="900" height="359" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_33_02-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-static-and-dynamic-instances.png" alt="2023 06 17 16 33 02 Getting started with C on Beckhoff Pt 2 AutoRecovered static and dynamic instances" class="wp-image-17474" srcset="https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_33_02-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-static-and-dynamic-instances.png 900w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_33_02-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-static-and-dynamic-instances-300x120.png 300w, https://static.dmcinfo.com/wp-content/uploads/2025/05/2023-06-17-16_33_02-Getting-started-with-C__-on-Beckhoff-Pt-2-AutoRecovered-static-and-dynamic-instances-768x306.png 768w" sizes="(max-width: 900px) 100vw, 900px" /></figure>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph"><strong>Learn more about our&nbsp;<a href="/services/manufacturing-automation-and-intelligence/plc-programming/beckhoff-and-twincat-3-programming">Beckhoff and TwinCAT 3 programming</a>&nbsp;expertise and&nbsp;<a href="/contact">contact us</a>&nbsp;for your next project.</strong></p>



<p class="wp-block-paragraph"><!--![endif]----></p>
<p>The post <a href="https://static.dmcinfo.com/blog/17475/getting-started-with-c-on-a-beckhoff-plc-part-2-creating-the-st-c-interface/">Getting started with C++ on a Beckhoff PLC &#8211; Part 2: Creating the ST/C++ Interface</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>Getting started with C++ on a Beckhoff PLC &#8211; Part One: Setup</title>
		<link>https://static.dmcinfo.com/blog/17484/getting-started-with-c-on-a-beckhoff-plc-part-one-setup/</link>
		
		<dc:creator><![CDATA[DMC]]></dc:creator>
		<pubDate>Sat, 17 Jun 2023 12:54:34 +0000</pubDate>
				<category><![CDATA[Beckhoff PLC]]></category>
		<category><![CDATA[Manufacturing Automation & Intelligence]]></category>
		<category><![CDATA[PLC]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[TwinCAT]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/17484/getting-started-with-c-on-a-beckhoff-plc-part-one-setup/</guid>

					<description><![CDATA[<p>Beckhoff PLCs are one of the few industrial PLCs that allow you to run real-time C++ directly on the PLC hardware. This allows for C++ code to run cyclically on a separate system task, or as a TcCOM object allowing for function calls from structured text. This guide will cover how to create the external [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/17484/getting-started-with-c-on-a-beckhoff-plc-part-one-setup/">Getting started with C++ on a Beckhoff PLC &#8211; Part One: Setup</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph"><a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/plc-programming/beckhoff-and-twincat-3-programming">Beckhoff PLCs</a> are one of the few industrial PLCs that allow you to run real-time C++ directly on the PLC hardware. This allows for C++ code to run cyclically on a separate system task, or as a TcCOM object allowing for function calls from structured text.</p>



<p class="wp-block-paragraph">This guide will cover how to create the external interface and how to make C++ function calls and algorithms within your PLC program.</p>



<h2 id="h-steps" class="wp-block-heading"><a id="Steps List" name="Steps List">Steps</a></h2>



<ol class="wp-block-list">
<li><a href="#Setting up Twincat C++ IDE">Setting up the TwinCAT C++ IDE</a></li>



<li><a href="#Step 2: Create C++ and PLC programs">Creating your C++ and PLC programs.</a></li>



<li><a href="#Step 3: Adding Twincat Test Certificate">Adding a Test Signature Certificate.</a></li>
</ol>



<h2 id="h-step-1-setting-up-twincat-c-ide" class="wp-block-heading"><a id="Setting up Twincat C++ IDE" name="Setting up Twincat C++ IDE">Step 1: Setting up TwinCAT C++ IDE</a></h2>



<p class="wp-block-paragraph"><a href="#Steps List"><i>Back to Steps List</i></a></p>



<p class="wp-block-paragraph">While structured text development on TwinCAT can be done within the TwinCAT XAE Shell, TwinCAT C++ development must be done within Visual Studio 2019 with the TwinCAT extensions installed. If&nbsp;a TwinCAT solution with a C++ project is opened in&nbsp;XAE shell, you will be unable to view&nbsp;or edit the C++ code. TwinCAT integration will support&nbsp;Visual Studio 2022 in build 4026.</p>



<p class="wp-block-paragraph">If&nbsp;XAE Shell was&nbsp;installed before Visual Studio 2019, XAE shell must be uninstalled before installing Visual Studio, then XAE Shell can be reinstalled with the integration added.</p>



<p class="wp-block-paragraph">You can follow <a href="https://infosys.beckhoff.com/content/1033/tc3_installation/179467147.html?id=4514876775714218857">TwinCAT’s guide on integrating</a> with Visual Studio.</p>



<h2 id="h-step-2-creating-the-c-and-plc-programs" class="wp-block-heading"><a id="Step 2: Create C++ and PLC programs" name="Step 2: Create C++ and PLC programs">Step 2: Creating the C++ and PLC programs</a></h2>



<p class="wp-block-paragraph"><a href="#Steps List"><i>Back to Steps List</i></a></p>



<p class="wp-block-paragraph">The C++ integration can be added to a new or existing PLC program. Here&#8217;s a <a href="https://static.dmcinfo.com/latest-thinking/blog/id/10162/getting-started-with-a-beckhoff-plc-part-one--setup" type="link" id="https://static.dmcinfo.com/latest-thinking/blog/id/10162/getting-started-with-a-beckhoff-plc-part-one--setup">series of DMC guides</a> on how to set up and connect to a TwinCAT PLC.</p>



<p class="wp-block-paragraph">To add or create the C++ project:&nbsp;</p>



<ol class="wp-block-list">
<li>Under the C++ node of the solution, right click and select “Add New Item…”</li>
</ol>



<p class="wp-block-paragraph"><p style="margin-left:.25in;"></p></p>



<ol class="wp-block-list">
<li value="2">A popup will appear displaying the different types of TwinCAT C++ Projects.
 

<ol class="wp-block-list" style="list-style-type:lower-alpha;">
  

<li>Select &ldquo;Versioned C++ Project&rdquo; to create an interface accessible by structured text.</li>


  

<li>&ldquo;Driver projects&rdquo; are no longer recommended by TwinCAT and have fewer features compared to &ldquo;Versioned C++ Projects&rdquo;.</li>


  

<li>&ldquo;Static Library Projects&rdquo; create static libraries that create functions and classes that can be accessed from other C++ projects; however, they cannot create an interface accessible from structured text code.</li>


 </ol>


 </li>



<li>For this external interface, I will select &#8220;Versioned C++ Project&#8221;.</li>



<li>A popup will appear asking what type of Module Class to add a preset configuration.
 
 
<ol class="wp-block-list" style="list-style-type:lower-alpha;">
  

<li>To create a project that provides functions to call from structured text, select &ldquo;TwinCAT Module Class for RT Context&rdquo;.&nbsp;</li>


  

<li>The Cyclic Caller, Cyclic IO, and Data Pointer options all create a CyclicUpdate() function that can be attached to a task and have C++ run cyclically. This will run C++ code every scan.</li>


 </ol>
</li>
</ol>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Getting-Started-with-C__-On-Beckhoff-Module-Class-Options_1.png" alt=""/></figure>



<ol class="wp-block-list">
<li value="4">Enter a name and click OK.</li>
</ol>



<h2 id="h-step-3-adding-a-twincat-test-certificate" class="wp-block-heading"><a id="Step 3: Adding Twincat Test Certificate" name="Step 3: Adding Twincat Test Certificate">Step 3: Adding a TwinCAT Test Certificate</a></h2>



<p class="wp-block-paragraph"><a href="#Steps List"><i>Back to Steps List</i></a></p>



<p class="wp-block-paragraph">To run C++ programs on the PLC, a certificate must be added to ensure all C++ code is verified. TwinCAT allows for officially signed certificates, but a test certificate is sufficient for developing and testing code. TwinCAT recommends that all new projects use Versioned C++ projects, which requires the new TwinCAT Certificate. If developing software that uses the older &#8220;Driver Project&#8221;, then an Operating System certificate is needed instead.</p>



<p class="wp-block-paragraph">For more information about Driver signing, reference the <a href="https://infosys.beckhoff.com/content/1033/tc3_c/110691083.html?id=370147429722095671" target="_blank" rel="noreferrer noopener">Beckhoff Information System</a>.</p>



<p class="wp-block-paragraph">To add a new test certificate for a C++ Versioned Project:</p>



<ol class="wp-block-list">
<li>In Visual Studio 2019, go to Extensions/TwinCAT/Software Protection.&nbsp;In XAE Shell, go to TwinCAT/Software Protection.</li>
</ol>



<p class="wp-block-paragraph"><p style="margin-left:.5in;"></p></p>



<ol class="wp-block-list">
<li value="2">In the wizard, click &ldquo;Create New&hellip;&rdquo;&nbsp;and fill out the OEM name, Unique Name, and select the checkbox for &ldquo;Sign TwinCAT C++ Executables (.tmx). Click &quot;Yes&quot; on the popup that appears.</li>
</ol>



<p class="wp-block-paragraph"><p style="margin-left:.5in;"></p></p>



<ol class="wp-block-list">
<li value="4">Click &ldquo;Start&rdquo; and save the file in the default location&nbsp;(C:/TwinCAT/3.1/CustomConfig/Certificates/).&nbsp;</li>



<li value="5">Enter a password.&nbsp;
 

<ol class="wp-block-list">
  

<li>It is recommended to use the <a href="http://infosys.beckhoff.com/english.php?content=../content/1033/tc3_c/6829815563.html&amp;id=5788251524726684479">TcSignTool </a>to create the test signature password as TwinCAT will store the password in plaintext in the project file which can be easily shared.</li>


 </ol>


 </li>



<li value="6">Once the certificate has been created, it needs to be added to&nbsp;the C++ project. Right click the C++ Project and click &quot;Properties.&quot; Under the &ldquo;Tc Sign&rdquo; tab, select &quot;Yes&quot; for TwinCAT signing, and enter the certificate name and password.</li>
</ol>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/Getting-Started-with-C__-On-Beckhoff-TwinCAT-Certificate-Name.png" alt=""/></figure>



<ol class="wp-block-list">
<li value="8">Because these test certificates are not countersigned by Beckhoff, the target PLC must be put into test mode to run the C++ code. Once the certificates have been countersigned by Beckhoff, your target PLC will not need to be in test mode.
 

<ol class="wp-block-list">
  

<li>On your Target PLC, open Command Prompt with Administrator privileges.</li>


  

<li>Run &ldquo;bcdedit /set testsigning yes&rdquo; in Command Prompt (Admin).</li>


  

<li>Restart your computer/PLC.</li>


  

<li>The Target PLC will now allow test certificate-signed C++ code to run on the PLC.</li>


 </ol>


 </li>



<li value="9">Right click the C++ Project and click &ldquo;Publish Modules&rdquo;. It should create an error that says the certificate is not valid or ADS has failed.</li>



<li value="10">Open (C:/TwinCAT/3.1/Target/OemCertificates) on the target PLC and locate the file with the same name as the certificate&rsquo;s &ldquo;Unique Name&rdquo;.</li>



<li value="11">Double click the file to add the certificate to Window&rsquo;s certificate registry. The &quot;Publish Modules&quot; button should now work, and the C++ project&nbsp;can build and run on your machine.&nbsp;</li>
</ol>



<p class="wp-block-paragraph"><p style="margin-left:.5in;"></p></p>



<p class="wp-block-paragraph"><p style="margin-left:.5in;">&nbsp;</p></p>



<p class="wp-block-paragraph"><p style="margin-left:.5in;">Now that the IDE and test certificate have been configured, the C++ project can be configured with the desired functions and interfaces and linked to a structured text function block, which will be covered in part two of this blog series.</p></p>



<p class="wp-block-paragraph"><strong>Learn more about our <a href="https://static.dmcinfo.com/services/manufacturing-automation-and-intelligence/plc-programming/beckhoff-and-twincat-3-programming">Beckhoff and TwinCAT 3 Programming</a> expertise and <a href="https://static.dmcinfo.com/contact/" type="page" id="411">contact us</a> for your next project.</strong></p>
<p>The post <a href="https://static.dmcinfo.com/blog/17484/getting-started-with-c-on-a-beckhoff-plc-part-one-setup/">Getting started with C++ on a Beckhoff PLC &#8211; Part One: Setup</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
		<item>
		<title>LVGL for International GUI Design</title>
		<link>https://static.dmcinfo.com/blog/17764/lvgl-for-international-gui-design/</link>
		
		<dc:creator><![CDATA[Ben Dyer]]></dc:creator>
		<pubDate>Wed, 01 Mar 2023 10:12:27 +0000</pubDate>
				<category><![CDATA[Embedded Development & Programming]]></category>
		<category><![CDATA[User Interface Design]]></category>
		<category><![CDATA[C#]]></category>
		<category><![CDATA[embedded]]></category>
		<category><![CDATA[i18n]]></category>
		<category><![CDATA[internationalization]]></category>
		<category><![CDATA[LVGL]]></category>
		<category><![CDATA[microcontroller]]></category>
		<category><![CDATA[multi-language]]></category>
		<category><![CDATA[user interface]]></category>
		<guid isPermaLink="false">https://static.dmcinfo.com/blog/17764/lvgl-for-international-gui-design/</guid>

					<description><![CDATA[<p>Creating a high-quality user interface that can run on embedded systems is a challenging task, and that’s doubly (or perhaps triply) true if it’s intended for an international user base. As with any difficult job, however, it becomes much more manageable with the right tools. Read on to learn the basics of one such tool, [&#8230;]</p>
<p>The post <a href="https://static.dmcinfo.com/blog/17764/lvgl-for-international-gui-design/">LVGL for International GUI Design</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></description>
										<content:encoded><![CDATA[<p><!-- Injected  Highlight.js Code --><script type="text/javascript" src="/Providers/HtmlEditorProviders/CKEditor/plugins/codesnippet/lib/highlight/highlight.pack.js?ver=1790905719"></script><link type="text/css" rel="stylesheet" href="/Providers/HtmlEditorProviders/CKEditor/plugins/codesnippet/lib/highlight/styles/default.css?ver=1790905719"/><script type="text/javascript">window.onload = function() {var aCodes = document.getElementsByTagName('pre');for (var i=0; i < aCodes.length;i++){hljs.highlightBlock(aCodes[i]);} };</script></p>


<p class="wp-block-paragraph">Creating a high-quality user interface that can run on embedded systems is a challenging task, and that’s doubly (or perhaps triply) true if it’s intended for an international user base.</p>



<p class="wp-block-paragraph">As with any difficult job, however, it becomes much more manageable with the right tools. Read on to learn the basics of one such tool, LVGL, and its associated utilities.</p>



<h2 id="h-getting-started-with-lvgl" class="wp-block-heading">Getting Started with LVGL</h2>



<p class="wp-block-paragraph">In order to develop with LVGL, it will need&nbsp;to be ported to your platform of choice.</p>



<p class="wp-block-paragraph">First, pull the latest release of LVGL into a project configured for your target and create “lv_conf.h” from the included template. This file contains a variety of parameters for tweaking the library’s behavior, but they can be left at their default values for now. Add a call to <strong>lv_init</strong>&nbsp;to your program, ensuring it’s in a location where it will occur before any other LVGL functions are called.</p>



<p class="wp-block-paragraph">Next, you will need to define drivers for the display showing your UI and any input devices used to interact with it. The specifics of these drivers will vary greatly from project to project, and LVGL already has excellent documentation on porting, so only the general structure will be covered here.</p>



<p class="wp-block-paragraph">In simple cases, you will only need to allocate some memory to act as a draw buffer, define the resolution of the display, and implement a function to send the information LVGL places into the draw buffer to your screen.&nbsp;The example below shows a basic setup, but additional configuration options exist to implement things like double-buffering or screen rotation.</p>



<p class="wp-block-paragraph"></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>static lv_color_t draw_buffer&#91;SCREEN_HOR_RES * SCREEN_VER_RES / 10&#93;;
static lv_disp_drv_t disp_drv;
static lv_disp_draw_buf_t disp_buf;

void flush_buffer_callback(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p);

lv_disp_t* lvgl_display_port_init()
{
    //configure any platform specific peripherals here

    //set up draw buffer
    lv_disp_draw_buf_init(&amp;disp_buf, draw_buffer, NULL, SCREEN_HOR_RES * SCREEN_VER_RES / 10);

    //initialize display driver
    lv_disp_drv_init(&amp;disp_drv);

    //configure display driver
    disp_drv.draw_buf = &amp;disp_buf;
    disp_drv.hor_res = SCREEN_HOR_RES;
    disp_drv.ver_res = SCREEN_VER_RES;
    disp_drv.flush_cb = flush_buffer_callback;

    //set optional fields to customize display driver here
   
    //finalize driver setup
    return lv_disp_drv_register(&amp;disp_drv);
}

void flush_buffer_callback(lv_disp_drv_t* disp_drv, const lv_area_t* area, lv_color_t* color_p)
{
    /* copy pixels from 'color_p' to the area of the screen described by 'area' */
}</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">lv_color_t</span><span style="color: #D4D4D4"> </span><span style="color: #9CDCFE">draw_buffer</span><span style="color: #D4D4D4">&#91;SCREEN_HOR_RES * SCREEN_VER_RES / </span><span style="color: #B5CEA8">10</span><span style="color: #D4D4D4">&#93;;</span></span>
<span class="line"><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">lv_disp_drv_t</span><span style="color: #D4D4D4"> disp_drv;</span></span>
<span class="line"><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">lv_disp_draw_buf_t</span><span style="color: #D4D4D4"> disp_buf;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">void</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">flush_buffer_callback</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">lv_disp_drv_t</span><span style="color: #D4D4D4">* </span><span style="color: #9CDCFE">disp_drv</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">lv_area_t</span><span style="color: #D4D4D4">* </span><span style="color: #9CDCFE">area</span><span style="color: #D4D4D4">, </span><span style="color: #4EC9B0">lv_color_t</span><span style="color: #D4D4D4">* </span><span style="color: #9CDCFE">color_p</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #4EC9B0">lv_disp_t</span><span style="color: #D4D4D4">* </span><span style="color: #DCDCAA">lvgl_display_port_init</span><span style="color: #D4D4D4">()</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">//configure any platform specific peripherals here</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">//set up draw buffer</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">lv_disp_draw_buf_init</span><span style="color: #D4D4D4">(&amp;disp_buf, draw_buffer, </span><span style="color: #569CD6">NULL</span><span style="color: #D4D4D4">, SCREEN_HOR_RES * SCREEN_VER_RES / </span><span style="color: #B5CEA8">10</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">//initialize display driver</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">lv_disp_drv_init</span><span style="color: #D4D4D4">(&amp;disp_drv);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">//configure display driver</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">disp_drv</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">draw_buf</span><span style="color: #D4D4D4"> = &amp;disp_buf;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">disp_drv</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">hor_res</span><span style="color: #D4D4D4"> = SCREEN_HOR_RES;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">disp_drv</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">ver_res</span><span style="color: #D4D4D4"> = SCREEN_VER_RES;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">disp_drv</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">flush_cb</span><span style="color: #D4D4D4"> = flush_buffer_callback;</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">//set optional fields to customize display driver here</span></span>
<span class="line"><span style="color: #D4D4D4">   </span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">//finalize driver setup</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #C586C0">return</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">lv_disp_drv_register</span><span style="color: #D4D4D4">(&amp;disp_drv);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">void</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">flush_buffer_callback</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">lv_disp_drv_t</span><span style="color: #D4D4D4">* </span><span style="color: #9CDCFE">disp_drv</span><span style="color: #D4D4D4">, </span><span style="color: #569CD6">const</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">lv_area_t</span><span style="color: #D4D4D4">* </span><span style="color: #9CDCFE">area</span><span style="color: #D4D4D4">, </span><span style="color: #4EC9B0">lv_color_t</span><span style="color: #D4D4D4">* </span><span style="color: #9CDCFE">color_p</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #6A9955">    /* copy pixels from &apos;color_p&apos; to the area of the screen described by &apos;area&apos; */</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span></code></pre></div>


<pre><code class="language-cpp">
</code></pre>


<p class="wp-block-paragraph"><span id="cke_bm_275C" style="display: none;">&nbsp;</span></p>



<p class="wp-block-paragraph">Configuring an input device follows a similar process. Use <strong>lv_indev_drv_init</strong>&nbsp;to set up the driver, define its type,&nbsp;attach a function for reading its value, and register it using <strong>lv_indev_drv_register</strong>.<span id="cke_bm_399C" style="display: none;">&nbsp;</span></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">C</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly>static lv_indev_drv_t indev_drv;
void input_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data);

void lvgl_indev_port_init(void);
{
    //configure any necessary platform specific peripherals here

    //set up input device driver
    lv_indev_drv_init(&amp;indev_drv);

    //configure input device driver
    indev_drv.type = LV_INDEV_TYPE_POINTER;
    indev_drv.read_cb = input_read;
    
    //finalize driver setup
    lv_indev_t* touch_indev = lv_indev_drv_register(&amp;indev_drv);
}

void input_read(lv_indev_drv_t *indev_drv, lv_indev_data_t *data)
{
    /* Read location from input device (i.e. last touch location on a touchscreen) */

    /* Read state from input device (i.e. is a touchscreen currently being touched) */

    /* Store current location (x, y) and state (pressed, released) in 'data' */
}
</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #569CD6">static</span><span style="color: #D4D4D4"> </span><span style="color: #4EC9B0">lv_indev_drv_t</span><span style="color: #D4D4D4"> indev_drv;</span></span>
<span class="line"><span style="color: #569CD6">void</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">input_read</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">lv_indev_drv_t</span><span style="color: #D4D4D4"> *</span><span style="color: #9CDCFE">indev_drv</span><span style="color: #D4D4D4">, </span><span style="color: #4EC9B0">lv_indev_data_t</span><span style="color: #D4D4D4"> *</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">void</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">lvgl_indev_port_init</span><span style="color: #D4D4D4">(</span><span style="color: #569CD6">void</span><span style="color: #D4D4D4">);</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">//configure any necessary platform specific peripherals here</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">//set up input device driver</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #DCDCAA">lv_indev_drv_init</span><span style="color: #D4D4D4">(&amp;indev_drv);</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">//configure input device driver</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">indev_drv</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">type</span><span style="color: #D4D4D4"> = LV_INDEV_TYPE_POINTER;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #9CDCFE">indev_drv</span><span style="color: #D4D4D4">.</span><span style="color: #9CDCFE">read_cb</span><span style="color: #D4D4D4"> = input_read;</span></span>
<span class="line"><span style="color: #D4D4D4">    </span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #6A9955">//finalize driver setup</span></span>
<span class="line"><span style="color: #D4D4D4">    </span><span style="color: #4EC9B0">lv_indev_t</span><span style="color: #D4D4D4">* touch_indev = </span><span style="color: #DCDCAA">lv_indev_drv_register</span><span style="color: #D4D4D4">(&amp;indev_drv);</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span>
<span class="line"><span style="color: #569CD6">void</span><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">input_read</span><span style="color: #D4D4D4">(</span><span style="color: #4EC9B0">lv_indev_drv_t</span><span style="color: #D4D4D4"> *</span><span style="color: #9CDCFE">indev_drv</span><span style="color: #D4D4D4">, </span><span style="color: #4EC9B0">lv_indev_data_t</span><span style="color: #D4D4D4"> *</span><span style="color: #9CDCFE">data</span><span style="color: #D4D4D4">)</span></span>
<span class="line"><span style="color: #D4D4D4">{</span></span>
<span class="line"><span style="color: #6A9955">    /* Read location from input device (i.e. last touch location on a touchscreen) */</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    /* Read state from input device (i.e. is a touchscreen currently being touched) */</span></span>
<span class="line"></span>
<span class="line"><span style="color: #6A9955">    /* Store current location (x, y) and state (pressed, released) in &apos;data&apos; */</span></span>
<span class="line"><span style="color: #D4D4D4">}</span></span>
<span class="line"></span></code></pre></div>



<p class="wp-block-paragraph">&nbsp;</p>



<p class="wp-block-paragraph">With the display and input device drivers implemented, the final step is to create an environment for LVGL to run in. In simple applications, this might be an infinite loop at the end of main function, while more complex applications may utilize an RTOS (real-time operating system) and give LVGL its own thread. Many approaches will work as long as <strong>lv_tick_inc</strong>&nbsp;and <strong>lv_timer_handler</strong>&nbsp;are called periodically, with <strong>lv_tick_inc</strong>&nbsp;occurring at least as frequently as <strong>lv_timer_handler</strong>.</p>



<p class="wp-block-paragraph">Note that LVGL functions are not inherently thread-safe or interrupt-safe; if using interrupts or an RTOS, utilize synchronization tools like mutexes or take care to limit calls to LVGL such that they cannot overlap.</p>



<p class="wp-block-paragraph">With all of the above in place, LVGL should be ready to use.</p>



<h2 id="h-internationalization-tools" class="wp-block-heading">Internationalization Tools</h2>



<p class="wp-block-paragraph">In addition to the base library, LVGL has several supplementary tools to aid in development. When creating a UI for an international user base, two such tools stand out as essential:&nbsp;LVGL’s internationalization library, which provides&nbsp;simple tools for text substitution, and LVGL's&nbsp;font converter, which allows a developer to parse glyphs from several fonts into a single file for use with LVGL.</p>



<p class="wp-block-paragraph"><span style="font-family:arial;">For both of these tools, you will need node.js (at least version 14) and the&nbsp;package manager it comes bundled with (called npm). You can</span> <span style="font-family:arial;"><a href="https://nodejs.org/en/download/" type="link" id="https://nodejs.org/en/download/">download them</a>&nbsp;here. Sticking with the default installation options will work for the tools covered here.</span></p>



<p class="wp-block-paragraph"><span style="font-family:arial;">After installing, ensure that both node.js and npm are added to your system PATH. Open command prompt, and install the font converter with </span><strong>npm i lv_font_conv -g</strong><span style="font-family:arial;"> and the internationalization library with <strong>npm i lv_i18n -g</strong> Both tools should now be usable in your project.</span></p>



<h2 id="h-lv-i18n" class="wp-block-heading">lv_i18n</h2>



<p class="wp-block-paragraph"><span style="font-family:arial;">The process described below was adapted from the setup instructions provided with lv_i18n&nbsp;with notes from personal experience. For the original instructions, see the <a href="https://github.com/lvgl/lv_i18n" rel="no follow" target="_blank">lv_i18n github</a>.</span></p>



<p class="wp-block-paragraph">Start off with a simple example, and wrap any text which will be translated in <strong>_( )</strong>. For strings containing plurals, <strong>_p( )</strong>&nbsp;can be used instead to support languages with different pluralization rules from your base language. Include “lv_i18n.h” (this file doesn’t exist yet as&nbsp;it will be generated by the tool later) and, after initializing LVGL, call <code>lv_i18n_init</code>&nbsp;and <code>lv_i18n_set_locale</code>. The locale can be set to any language that you plan to support, and it can be changed any time that it’s safe to call an LVGL API function.</p>



<p class="wp-block-paragraph"><span style="font-family:arial;">Next, create a folder to store translations, add a .yml file for each language you plan to support (including the language you are developing in), and title the files&nbsp;with <a href="https://www.andiamo.co.uk/resources/iso-language-codes/" rel="no follow" target="_blank">language codes</a>. Write the language code, followed by a colon, into the first line of each file (ex: Portuguese would have a file called “pt.yml”, and it’s first line would be “pt:”). Once your example is done, from a command prompt in your project, run the following (without angle brackets):</span></p>



<p class="wp-block-paragraph"><code>lv_i18n extract -s ‘&lt;path to your source files&gt;/*.+(c|cpp|h|hpp)’ -t ‘&lt;path to your yml translations&gt;/*.yml’ </code></p>



<p class="wp-block-paragraph"><span style="font-family:arial;">This will populate the translation files with a list of every string from your project wrapped in <code>_( )</code>. Next to each, add its translation in the language corresponding to the yml file it’s in. To implement these translations, run the following:</span><br>
<br>
<code>lv_i18n compile -t ‘&lt;path to your yml translations&gt;/*.yml’ -o ‘&lt;path where you want lv_i18n.h to go&gt;’</code><br>
<br>
<span style="font-family:arial;">The output location of this command will receive lv_i18n.h and lv_i18n.c, which will contain all of your translations in a form that the tool can reference. It will need to be accessible by any file that calls <code>_( )</code>, so set up its location accordingly. </span></p>



<p class="wp-block-paragraph"><span style="font-family:arial;">Now, your example should be ready to run. Any time that code containing a string wrapped in <code>_( )</code> executes, the current locale will be checked and the string will be substituted for its translation in that locale. This is a simple, literal substitution, so anything referencing the string will be affected, not just LVGL functions. Additionally, if new text is added, you will need to run <code>lv_i18n extract</code> and <code>lv_i18n compile</code> again.</span></p>



<h2 id="h-lv-font-conv" class="wp-block-heading">lv_font_conv</h2>



<p class="wp-block-paragraph"><span style="font-family:arial;">If one of your&nbsp;languages uses&nbsp;a script other than the Latin alphabet, you may notice missing characters when its translation is&nbsp;displayed. This is where the font converter comes in. Most fonts don’t describe a glyph for every Unicode character, so you will need to either find one that has all the characters you need&nbsp;or create one by splicing together a few fonts. </span></p>



<p class="wp-block-paragraph"><span style="font-family:arial;">First, prepare a list of characters needed, or ranges of characters. Even if you have a font which contains all the characters needed, it’s likely that it also contains many that you don’t, and fonts take up a lot of space (especially physically larger ones; by default, LVGL stores them as byte arrays, and more pixels means more bytes). </span></p>



<p class="wp-block-paragraph">Next, parse this list into a font converter call. Broadly, calls to the font converter are structured as a sequential list of font files, which characters to take from them, and then a few parameters to customize the output. Supply a path to the font file with <code>--font</code>, specify which characters to include from it with <code>--range</code>&nbsp;or <code>--symbols</code>, and repeat for each font to be included.</p>



<p class="wp-block-paragraph">When adding characters, <code>--symbols</code>&nbsp;accepts a list of characters. Duplicates are ignored, so you can freely copy and paste everything from one language’s translation into this argument, but be careful to remove any spaces as they may be treated as the end of the argument. <code>--range</code>&nbsp;accepts a single Unicode value or a contiguous range of Unicode values. See <a href="https://jrgraphix.net/research/unicode.php" rel="no follow" target="_blank">Unicode ranges</a> for reference. You can add as many <code>--range</code>&nbsp;and <code>--symbols</code>&nbsp;arguments as needed. Next, add the font size to generate with <code>--size</code>, the format with <code>--format</code>, the level of detail with <code>--bpp</code>, and a file path to output to with <code>-o</code>. The <a href="https://github.com/lvgl/lv_font_conv" rel="no follow" target="_blank">font converter github page</a> has more details on each of these parameters.</p>



<p class="wp-block-paragraph">&nbsp;<span style="font-family:arial;">Altogether, a call might look like the following:</span></p>



<div class="wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers" data-code-block-pro-font-family="Code-Pro-JetBrains-Mono" style="font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#D4D4D4;--cbp-line-number-width:calc(2 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)"><span style="display:flex;align-items:center;padding:16px 0 0 16px;width:100%;text-align:left;background-color:#1e1e1e"><span style="background:#c7c7c7;padding:0.3rem 0.5rem 0.2rem;border-radius:1rem;font-size:0.8em;line-height:1;height:1.25rem;text-align:center;display:inline-flex;align-items:center;justify-content:center;color:#1e1e1e">Bash</span></span><span role="button" tabindex="0" style="color:#D4D4D4;display:none" aria-label="Copy" class="code-block-pro-copy-button"><pre class="code-block-pro-copy-button-pre" aria-hidden="true"><textarea class="code-block-pro-copy-button-textarea" tabindex="-1" aria-hidden="true" readonly> lv_font_conv --font ARIALUNI.TTF

  --range 0x0000-0x017F

  --range 0x0400-0x04FF

  --symbols КАСБОЙ

 --font NotoSansArabic-Regulat.ttf

  --range 0x0600-0x06FF

  --range 0xFE70-0xFEFF

 --size 10 --format lvgl --bpp 3 -o examplefont.c</textarea></pre><svg xmlns="http://www.w3.org/2000/svg" style="width:24px;height:24px" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path class="with-check" stroke-linecap="round" stroke-linejoin="round" d="M4.5 12.75l6 6 9-13.5"></path><path class="without-check" stroke-linecap="round" stroke-linejoin="round" d="M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6"></path></svg></span><pre class="shiki dark-plus" style="background-color: #1E1E1E" tabindex="0"><code><span class="line"><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">lv_font_conv</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--font</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">ARIALUNI.TTF</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">--range</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">0x0000</span><span style="color: #CE9178">-0x017F</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">--range</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">0x0400</span><span style="color: #CE9178">-0x04FF</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">--symbols</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">КАСБОЙ</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">--font</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">NotoSansArabic-Regulat.ttf</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">--range</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">0x0600</span><span style="color: #CE9178">-0x06FF</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4">  </span><span style="color: #DCDCAA">--range</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">0xFE70</span><span style="color: #CE9178">-0xFEFF</span></span>
<span class="line"></span>
<span class="line"><span style="color: #D4D4D4"> </span><span style="color: #DCDCAA">--size</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">10</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--format</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">lvgl</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">--bpp</span><span style="color: #D4D4D4"> </span><span style="color: #B5CEA8">3</span><span style="color: #D4D4D4"> </span><span style="color: #569CD6">-o</span><span style="color: #D4D4D4"> </span><span style="color: #CE9178">examplefont.c</span></span></code></pre></div>



<pre class="wp-block-code"><code> </code></pre>



<p class="wp-block-paragraph"></p>



<p class="wp-block-paragraph"><span style="font-family:arial;">Note that newlines and indentation are included for clarity, the only separator should be spaces since this is invoked from a command line. To that end, it’s helpful to store arguments in a text file, then parse them into calls in a script with something like bash’s <code>mapfile</code>&nbsp;command.</span></p>



<p class="wp-block-paragraph">Once a font has been generated, it can be included in the project, declared with <code>LV_FONT_DECLARE</code>, and used the same as LVGL’s default fonts.</p>



<h2 id="h-common-challenges" class="wp-block-heading">Common Challenges</h2>



<p class="wp-block-paragraph">What follows is a collection of some common challenges one might encounter when creating internationalized UIs, and some tips as to how they can be handled with&nbsp;the tools discussed here.</p>



<p class="wp-block-paragraph"><u><strong>Base Direction</strong></u></p>



<p class="wp-block-paragraph">Languages vary in their base directions; English, for example, is a left-to-right (LTR) language, while languages using the Arabic script are right-to-left languages (RTL). LVGL has some built-in handling for different base directions, namely the <code>base_dir</code>&nbsp;style property.</p>



<p class="wp-block-paragraph">When creating an object, it can be given a base direction with <code>lv_obj_set_style_base_dir</code>, or attaching a style with the property already set (by <code>lv_style_set_base_dir</code>). This will work in most cases, but, with strings containing both LTR and RTL languages, you may need to specify text direction manually. For this, one can use Unicode directional indicators. Including the following in your project will allow you to change direction within a string.</p>



<p class="wp-block-paragraph"><code>#define LRI "\xE2\x81\xA6"</code></p>



<p class="wp-block-paragraph"><code>#define RLI "\xE2\x81\xA7"</code></p>



<p class="wp-block-paragraph"><code>#define PDI "\xE2\x81\xA9"</code></p>



<p class="wp-block-paragraph">LRI indicates left-to-right, RLI the opposite, and PDI (pop directional indicator) undoes the most recent indicator. Wrapping chunks of text in LRI/RLI … PDI can be thought of as splitting the overall string into substrings, with the base direction of the overall string governing the order these substrings will appear. As an example, if the following string were to be placed in a label with RTL base direction:</p>



<p class="wp-block-paragraph">“&lt;LRI&gt;123&lt;PDI&gt; &lt;RLI&gt;456&lt;PDI&gt; &lt;LRI&gt;789&lt;PDI&gt;”</p>



<p class="wp-block-paragraph">(note: angle brackets included for clarity), it would appear on screen as</p>



<p class="wp-block-paragraph">“789 654 123”.</p>



<p class="wp-block-paragraph">Note also that LVGL may output warnings for missing glyph descriptions when rendering strings containing these characters. They are zero-width characters and function correctly regardless of whether they are included in the font, so these warnings can be safely ignored&nbsp;or suppressed in lv_conf.h (though missing glyph warnings are very helpful when working with custom fonts).</p>



<p class="wp-block-paragraph"><u><strong>Information Density</strong></u></p>



<p class="wp-block-paragraph">Languages vary in information density; three Chinese characters might convey the same information as twenty Latin ones in English or thirty in Spanish. As such, it’s helpful to leave extra space in screen layouts for languages less dense than the one being used for development.</p>



<p class="wp-block-paragraph">When this isn't possible, however, one can also use LVGL’s long text handling. Calling <code>lv_label_set_long_mode</code>&nbsp;will allow the developer to specify whether a label with text that does not fit in its width scrolls, wraps, or is cut off (optionally ending the displayed text with “…”) . Make sure to set the width of the label <u>after</u> calling this function.</p>



<p class="wp-block-paragraph">In cases where built-in long modes are not desirable, another option is to decrease font size. Using <code>lv_txt_get_size</code>&nbsp;to check whether a string will fit in a space with a given font, it’s possible to implement automatic font scaling&nbsp;(though the exact process will vary greatly from UI to UI depending on the desired behavior).</p>



<p class="wp-block-paragraph"><u><strong>Inserting Variables into Translated Text</strong></u></p>



<p class="wp-block-paragraph">One common use of text in a UI is to provide context for numerical information, which is complicated by the fact that translations need to be compiled beforehand. Thankfully, there’s a simple solution. As mentioned before, lv_i18n’s translation function&nbsp;<code>_( )</code> is a simple, direct replacement. This means that the translated string can be used with basic C text functions, namely <code>snprintf</code>. Including “%s” (or other format codes) in both the initial string and its translations will allow values to be inserted into the string by code after translation. When using this method, take care to allocate enough space in the buffer passed to <code>snprintf</code>&nbsp;for your longest translation.</p>



<p class="wp-block-paragraph">When a string contains multiple variable values, they can appear in different orders from language to language. This can be solved with either clever translation to keep fields in the same order, or by tracking which language is active and supplying arguments to <code>sprintf</code>&nbsp;in the corresponding order.</p>



<p class="wp-block-paragraph"><u><strong>Duplicate words</strong></u></p>



<p class="wp-block-paragraph">Languages often have words that&nbsp;mean several different things. As an example, an English UI might include the string “set” in multiple places, some using the word as a verb, others as a noun. These words don’t necessarily line up between languages, so the string “set” would likely need two translations in other languages. lv_i18n can only attach one translation to each original string per language, so this may initially seem like a significant issue, but the tool is perfectly capable of handling these situations.</p>



<p class="wp-block-paragraph">lv_i18n creates a lookup table for each language, mapping strings to other strings. The initial, untranslated strings scraped from your file are really just IDs to search translations by, and you can supply corresponding “translations” for them in your base locale (development language) just like any other language.</p>



<p class="wp-block-paragraph">Returning to the previous example with the word “set”, you can change the strings in your code to “set (v)” and “set (n)” and re-run <code>lv_i18n extract</code>. In the yml files belonging to other languages, fill in the translation for "set" as a noun next to “set (n)” and "set" as a verb next to “set (v)”. In “en.yml”, just place the word “set” next to both.</p>



<figure class="wp-block-image"><img decoding="async" src="https://static.dmcinfo.com/wp-content/uploads/2025/05/duplicate_word_example.png" alt="Translation files implementing duplicate words"/></figure>



<p class="wp-block-paragraph">Re-run <code>lv_i18n compile</code>. Running the program in English, you should still see “set” everywhere, but changing the language will now show different translations for different uses of “set”.</p>



<p class="wp-block-paragraph"><strong>Learn More about DMC's <a href="/services/embedded-development-and-embedded-programming/embedded-user-interface-design">Embedded User Interface Design</a> services and <a href="/contact">contact us</a> today for your next project.</strong></p>



<p class="wp-block-paragraph"></p>
<p>The post <a href="https://static.dmcinfo.com/blog/17764/lvgl-for-international-gui-design/">LVGL for International GUI Design</a> appeared first on <a href="https://static.dmcinfo.com/">DMC, Inc.</a>.</p>
]]></content:encoded>
					
		
		
			</item>
	</channel>
</rss>
