<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Crank Storyboard Documentation Blog</title>
        <link>https://docs.cranksoftware.com/knowledge-base</link>
        <description>Crank Storyboard Documentation Blog</description>
        <lastBuildDate>Thu, 30 Apr 2026 00:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <item>
            <title><![CDATA[Modularizing Lua - when to use, why and how?]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/modularizing-lua-when-to-use-why-and-how</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/modularizing-lua-when-to-use-why-and-how</guid>
            <pubDate>Thu, 30 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Many Storyboard applications evolve and accumulate Lua functions as the application fills-out. These utility Lua functions are typically clustered in a few .lua files under the root of the scripts folder of the Storyboard project file structure which works fine whilst the number of screens is limited however as this grows or the project team increases then the task of collaborating and managing the functionality can start to become more challenging.]]></description>
            <content:encoded><![CDATA[<p>Many Storyboard applications evolve and accumulate Lua functions as the application fills-out. These utility Lua functions are typically clustered in a few .lua files under the root of the scripts folder of the Storyboard project file structure which works fine whilst the number of screens is limited however as this grows or the project team increases then the task of collaborating and managing the functionality can start to become more challenging.</p>
<!-- -->
<p>By default all of the .lua files in the script folder root are loaded and parsed by the Lua plugin early on during application initialisation and global functions are executed, data variables allocated etc such that everything is ready for the application to display the initial screen. This is perfectly acceptable but perhaps there are some better ways to structure the application.</p>
<p>Taking the time to review the application structure and potentially breaking up the Lua functionality to sub-modules can help with maintenance and development flow plus it can have other benefits that may not be quite so obvious.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-benefits-of-modularizing-your-lua">The benefits of modularizing your Lua<a href="https://docs.cranksoftware.com/knowledge-base/modularizing-lua-when-to-use-why-and-how#the-benefits-of-modularizing-your-lua" class="hash-link" aria-label="Direct link to The benefits of modularizing your Lua" title="Direct link to The benefits of modularizing your Lua" translate="no">​</a></h2>
<p>One common approach is to localise the Lua functions specific to each screen in a separate file and perhaps organise these into sub-folders within the project whilst keeping common re-usable utility functions at the top of the tree directly under the scripts folder:</p>
<p><img decoding="async" loading="lazy" alt="image2-2.png" src="https://docs.cranksoftware.com/assets/images/image2-2-6b0c02c9382f6dd21a7e4cb2e5175392.png" width="1144" height="683" class="img_ev3q"></p>
<p>When you break-up the Lua code for the project in to modules by creating directories under the scripts directory as shown above this will change the way that the Lua interpreter handles the contents:</p>
<ul>
<li class="">It will not automatically load those scripts not located in the root of the scripts until a require operand is issued for the script file</li>
<li class="">Functionality and local data variables specific to a given screen that are only needed ‘just-in-time’ for that screen and can often be disposed of once you leave which helps to manage memory utilisation</li>
<li class="">Delay loading will allow certain modules to be loaded at specific times throughout the execution of the UI and for example can help you optimise the startup time and external dependencies</li>
<li class="">Larger project teams can work on developing individual screens without impacting the rest of the application by localising their code</li>
<li class="">It helps when you need to write unit tests for individual screens by localising Lua scripting functionality</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="lua-modules-101">Lua Modules 101<a href="https://docs.cranksoftware.com/knowledge-base/modularizing-lua-when-to-use-why-and-how#lua-modules-101" class="hash-link" aria-label="Direct link to Lua Modules 101" title="Direct link to Lua Modules 101" translate="no">​</a></h2>
<p>Lua modules can come in a number of different flavours from simple global function groupings to full object-oriented class implementations.</p>
<p>It first worth an overview of what the require() does in Lua and how to use it to control how Storyboard loads and parses all of the Lua files. Take a look at the details here (Note: the Storyboard Lua interpreter is currently based on standard Lua 5.1):</p>
<p><a href="https://www.lua.org/manual/5.1/manual.html#5.3" target="_blank" rel="noopener noreferrer" class="">https://www.lua.org/manual/5.1/manual.html#5.3</a></p>
<p><a href="http://lua-users.org/wiki/ModulesTutorial" target="_blank" rel="noopener noreferrer" class="">http://lua-users.org/wiki/ModulesTutorial</a></p>
<p>The default case is that the runtime loads all of Lua the files located in the root of the scripts folder on startup. The links above explain the way that the Lua module loader scans for modules referenced in a require statement and the role of the package.cpath in directing Lua which path to search.</p>
<blockquote>
</blockquote>
<p>This is a common area where you may find issues with integrating 3rd party Lua libraries and so is worth reading and understanding.</p>
<p>Within the Storyboard application you can exploit this capability to explicitly control the loading and execution of any additional Lua sub modules by inserting the appropriate require statements to pull-in module dependencies.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="re-factoring-the-lua-functionality">Re-factoring the Lua functionality<a href="https://docs.cranksoftware.com/knowledge-base/modularizing-lua-when-to-use-why-and-how#re-factoring-the-lua-functionality" class="hash-link" aria-label="Direct link to Re-factoring the Lua functionality" title="Direct link to Re-factoring the Lua functionality" translate="no">​</a></h2>
<p>As outlined in the previous sections there are some of the benefits in taking the additional steps described to restructure the application and Lua components. It hopefully delivers a better software architecture and project organisation that is recommended as best practice by our own apps developers.</p>
<p>For example if there was a module for each specific screen that was in the UI, the following design pattern could be used to delay load and unload the Lua module (and any dependencies) on demand.</p>
<p>First you might use a screenshow.pre event trigger on that screen to load the Lua module using a Lua function call:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Locan main_screen = nil</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function screenshow_pre()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  main_screen = require("modules.mainScreen")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>Then on the screenhide.post event, run the following code to unload the Lua module:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">function screenhide_post()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  main_screen = nil</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  package.loaded["modules.mainScreen"] = nil</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  collectgarbage("collect") --if you aren't using the gc option at engine level</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>The require is undone by the line main_screen = nil and the package.loaded[“screens/main_screen”] = nil will remove the code from the internal Lua table and enable that memory to be recycled if needed.</p>
<p>The runtime will do this recycling automatically and for most platforms this is transparent. However to force Lua to be more aggressive you can set the gc=1 option to the Lua plugin. This will cause the Lua interpreter to perform a garbage collection cycle after each Lua action that is performed. This can help with fragmentation as objects that were just allocated and discarded will be freed immediately rather than left to linger for an undetermined amount of time.</p>
<p>For performance however, it may be better to not use the (global) gc=1 option and instead clean-up when leaving the specific screen. This can be achieved programmatically using a call to collectgarbage("collect") at appropriate points in your application.</p>
<blockquote>
</blockquote>
<p>In our example this may slow the screen transition slightly, but should not hinder the execution once on the particular screen or impact other behaviours elsewhere in the system.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="improving-application-structure-with-template-lua-module-code">Improving application structure with template Lua module code<a href="https://docs.cranksoftware.com/knowledge-base/modularizing-lua-when-to-use-why-and-how#improving-application-structure-with-template-lua-module-code" class="hash-link" aria-label="Direct link to Improving application structure with template Lua module code" title="Direct link to Improving application structure with template Lua module code" translate="no">​</a></h2>
<p>The simplistic approach outlined above will suffice for applications with a few screens however you will see that there are some improvements that can be made once you factor in other common application requirements and look to make the code more reusable.</p>
<p>This is just a simple example and used by way of illustration rather than a reference standard for developing with Lua in Storyboard!</p>
<p>For a better structure to our application code that provides benefit for developing even simple Storyboard applications and our in-house developers recommend adopting some object oriented design practices when coding with Lua.</p>
<p>It is generally the case that each screen would have a common requirement for some initial logic and variable setup to perform ‘On Entry’ before the screen is shown plus there may be some clean-up to do such as disposing of data objects and tables or clearing down animation and timers ‘On Exit’.</p>
<p>The general idea is that each screen has a Lua module file of the same name (watch-out for case sensitivity!) under the ‘modules’ sub-folder and that each file contains as a minimum a couple of template functions for the init and tear-down specific to that screen.</p>
<p>Obviously this base Lua content can be used as a code template and copied for each screen in turn and become a basis for defining the application development guidelines for the project.</p>
<p>Lua makes heavy use of ‘tables’ at the core of the language, indexed by strings that are used for look-up and manipulation using the extensive string libraries. When we re-factor the Lua code into modules we can use the string library to access the table of functions contained in our module by updating the metatable for strings, where the __index field points to the string table, and adding to it our class namespace.</p>
<p>In practice within our example application as shown we will have only a single instance of the class created implicitly (a singleton) and we are not making use of the new member function. Hence it is not being invoked however it is useful good practice to add this to our module implementations.</p>
<p>So for example in our Lua 5.1 syntax employing object-oriented notation:</p>
<p><a href="https://www.lua.org/manual/5.1/manual.html#5.4" target="_blank" rel="noopener noreferrer" class="">https://www.lua.org/manual/5.1/manual.html#5.4</a></p>
<p>modules/mainScreen.lua</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">---</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- @module mainScreen</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--  This class contains functions to underpin the mainScreen functionality. </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">mainScreen = {}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">mainScreen.__index = mainScreen</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">print("loaded module mainScreen.lua")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- Create a new module instance and update metatable </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function mainScreen.new(subsystem)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local self = setmetatable({}, mainScreen)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">self:init(subsystem)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">return self</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- Perform any dynamic screen specific configuration here</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function mainScreen:init(mapargs) </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">print("mainScreen:init()")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- Perform any dynamic screen specific tear-down here</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function mainScreen:exit(mapargs) </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">print("mainScreen:exit()" )</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- Callback for 'Press' button to be triggered by gre.press action</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function mainScreen:CBHandlePress(mapargs) </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local data={}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">data["mainLayer.status_text.text"] = "Activated"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">gre.set_data(data)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- Callback for 'Press' button to be triggered by gre.release and gre.outbound actions</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--- @param gre#context mapargs</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function mainScreen:CBHandleRelease(mapargs) </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local data={}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">data["mainLayer.status_text.text"] = "Deactivated"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">gre.set_data(data)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>And for a second screen which in this example performs some data and screen initialisation by assigning a fairly large text string and then explicitly clearing this on exit so that the Lua garbage collection can recycle this memory once finished with.</p>
<p>modules/secondScreen.lua</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">---</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- @module secondScreen</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--  This class contains functions to underpin the secondScreen funtionality. </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">secondScreen = {}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">secondScreen.__index = secondScreen</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local screen_text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">print("loaded module secondScreen.lua")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- Create a new module instance and update metatable </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function secondScreen.new(subsystem)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local self = setmetatable({}, secondScreen)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">self:init(subsystem)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">return self</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- Perform any dynamic screen configuration here</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function secondScreen:init(mapargs) </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">print("secondScreen:init()") </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local data = {}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">data["navLayer.content_text.text"] = screen_text</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">gre.set_data(data)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- Perform any dynamic screen specific tear-down here</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function secondScreen:exit(mapargs) </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">print("secondScreen:exit()")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">screen_text = nil</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>Because these functions are now localised to a specific module namespace for each screen we must refer to them in Storyboard using their full path using module class notation eg : mainScreen::CBHandlePress and mainScreen::CBHandleRelease as below:</p>
<p><img decoding="async" loading="lazy" alt="image3-2.png" src="https://docs.cranksoftware.com/assets/images/image3-2-877ab81a4ec215f9661fea293c387e66.png" width="1999" height="1035" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="putting-it-all-together-from-the-top-down">Putting it all together from the top down<a href="https://docs.cranksoftware.com/knowledge-base/modularizing-lua-when-to-use-why-and-how#putting-it-all-together-from-the-top-down" class="hash-link" aria-label="Direct link to Putting it all together from the top down" title="Direct link to Putting it all together from the top down" translate="no">​</a></h2>
<p>By exploiting the context metadata for the events passed via the mapargs parameter we can create some generic utility functions for the application to use when triggered by loading (screenshow.pre) and unloading (screenhide.pre) screen events on each screen. As this is common (global namespace) utility code it can be located at the scripts root folder in the file called callbacks.lua for this example, so that it is always loaded and present by default.</p>
<p>The require statement coupled with the module definition and metatable entry in each module file now means that when the module namespace is loaded it is added to the global table so we can now access the functions specific to each module via their class.</p>
<p>We can load the module via lookup of the module path (here we are using screen name as the name of the Lua module) using the event context for convenience. Note the use of a ‘.’ (dot) separator in the module name to instruct the Lua loader path:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">-- Load sub module on demand</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- NOTE: this context strategy requires that the lua module for the screen</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- is named the same as the screen</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local module_name = "modules." .. mapargs.context_screen </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">require( module_name )</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local module = _G[mapargs.context_screen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">module.CBMyFunc()</span><br></div></code></pre></div></div>
<p>Pulling this together into the top-level callbacks.lua file completes our implementation.</p>
<p>callbacks.lua</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">---</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- @module callbacks</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--  This class contains functions to underpin the general callbacks funtionality. </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--  It is intended to be an application global utility class</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--  </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">callbacks = {}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">callbacks.__index = callbacks</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">print("loaded callbacks.lua")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- Load sub module on start-up</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--require("modules/mainScreen")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--require("modules/secondScreen")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--print("Lua memory used:"..collectgarbage("count").."KB")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- Create a new module instance and update metatable </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function callbacks.new(subsystem)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  local self = setmetatable({}, callbacks)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  self:init(subsystem)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  return self</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- Perform any dynamic module specific configuration here</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function callbacks:init(mapargs) </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  print("callbacks:init()")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--- @param gre#context mapargs</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function callbacks:CBHandleScreenShow(mapargs) </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -- Load sub module on demand</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -- NOTE: this context strategy requires that the lua module for the screen</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -- is named the same as the screen</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  local module_name = "modules." .. mapargs.context_screen </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  require( module_name )</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -- Invoke loaded screen module specific initialisation function</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  local module = _G[mapargs.context_screen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  module:init(mapargs)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --print("Lua memory used:"..collectgarbage("count").."KB")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--- @param gre#context mapargs</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function callbacks:CBHandleScreenHide(mapargs) </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -- Unload sub module on exit</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -- NOTE: this context strategy requires that the lua module for the screen</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -- is named the same as the screen</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  local module_name = "modules." .. mapargs.context_screen </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  local module = _G[mapargs.context_screen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  print("unloading module : " .. module_name )</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -- Invoke loaded screen module specific tear-down function</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  module:exit()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -- Release and clean-up sub module on demand</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  package.loaded[ module_name ] = nil</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  collectgarbage("collect") --if you aren't using the gc option at engine level</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --print("Lua memory used:"..collectgarbage("count").."KB")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>The Lua function call collectgarbage("count") can be used to query the memory retained by the Lua runtime for debug and monitoring purposes.</p>
<p>In Storyboard 6.2 there is now an ---ADVANCED--- option for the Function Name within the Lua callback action properties dialog which will enables you to drill down into to select functions within classes.</p>
<p>Note at present this dialog will only show the classes defined at the application global level.</p>
<p><img decoding="async" loading="lazy" alt="image1-3.png" src="https://docs.cranksoftware.com/assets/images/image1-3-0cf6cdbf205a77bf599ec44a25c00842.png" width="1999" height="1180" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="observing-the-new-modules-structure-in-action">Observing the new modules structure in action<a href="https://docs.cranksoftware.com/knowledge-base/modularizing-lua-when-to-use-why-and-how#observing-the-new-modules-structure-in-action" class="hash-link" aria-label="Direct link to Observing the new modules structure in action" title="Direct link to Observing the new modules structure in action" translate="no">​</a></h2>
<p>Now we can observe the start-up and early initialisation of our top-level callbacks.lua functions then the subsequent delay loading of the screen specific Lua modules ‘just-in-time’ for their associated screen. The utility functions manage their lifespan automatically on screenshow.pre and screenhide.pre.</p>
<p>The trace output snippet below launched in the Designer simulation environment shows the the behaviour of our application modules and functions as follows:</p>
<p>sbengine -vvvvvv -ogreio,channel=LuaModulesTest LuaModuleTest-480x272.gapp</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.070]:Initialize plugin [greio (6.2.0.36502)] [channel=LuaModulesTest]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.070]:Initialize plugin [harfbuzz (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.070]:Initialize plugin [lua (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;emphasis role="bold"&gt;ACTION [0.071]:Loading Lua script [callbacks.lua]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">loaded callbacks.lua&lt;/emphasis&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.071]:Initialize plugin [luagredom (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.071]:Initialize plugin [media (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.071]:Initialize plugin [metrics (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.072]:Initialize plugin [poly (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.072]:Initialize plugin [rext-external (6.2.0.120)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.072]:Initialize plugin [rtext (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.072]:Initialize plugin [screen-dump (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.072]:Initialize plugin [screen-fade (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.072]:Initialize plugin [screen-path (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.072]:Initialize plugin [screen-rotate (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.072]:Initialize plugin [screen-scale (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.073]:Initialize plugin [system (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INFO [0.073]:Initialize plugin [timer (6.2.0.36502)] []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [0.074]:IO: Dispatch [gre.internalinit]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [0.108]:IO: Queue [1] gre.init INFO [0.109]:gesture: not enabled, no multi-touch events used in application</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [0.115]:SBIO: Receiver registered @ [LuaModulesTest]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [0.116]:IO: Dispatch [gre.init]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [0.116]:IO: Queue [2] gre.screenshow.pre DIAG [0.116]:CONTROL RENDER: background (0x0 479x271)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [0.116]:CONTROL RENDER: status_text (277x62 476x164)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [0.118]:CONTROL RENDER: button_next (395x206 469x261)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [0.119]:CONTROL RENDER: button_test (203x86 277x141)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [0.120]:CONTROL RENDER: header_text (111x7 350x26)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [0.120]:IO: Queue [3] gre.screenshow.post EVENT [0.121]:IO: Dispatch [gre.screenshow.pre]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [0.121]:ACTION: Invoke [gre.screenshow.pre]-&gt;[gra.lua] on screen [mainScreen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;emphasis role="bold"&gt;ACTION [0.121]:Lua: [callbacks.CBHandleScreenShow]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">loaded module mainScreen.lua</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">mainScreen:init()&lt;/emphasis&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [0.122]:IO: Dispatch [gre.screenshow.post]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [1.661]:ACTION: Invoke [gre.outbound]-&gt;[gra.datachange] on control [button_test]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [1.661]:ACTION: Invoke [gre.outbound]-&gt;[gra.lua] on control [button_test]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;emphasis role="bold"&gt;ACTION [1.661]:Lua: [mainScreen::CBHandleRelease]&lt;/emphasis&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [1.661]:CONTROL RENDER: background (277x62 476x164)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [1.662]:CONTROL RENDER: status_text (277x62 476x164)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [1.662]:CONTROL RENDER: button_test (277x86 277x141)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [2.200]:IO: Queue [1] gre.press EVENT [2.200]:IO: Dispatch [gre.press]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [2.200]:ACTION: Invoke [gre.press]-&gt;[gra.datachange] on control [button_test]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [2.201]:ACTION: Invoke [gre.press]-&gt;[gra.lua] on control [button_test]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;emphasis role="bold"&gt;ACTION [2.201]:Lua: [mainScreen::CBHandlePress]&lt;/emphasis&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [2.201]:CONTROL RENDER: background (203x62 476x164)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [2.201]:CONTROL RENDER: status_text (277x62 476x164)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [2.201]:CONTROL RENDER: button_test (203x86 277x141)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [2.404]:IO: Queue [1] gre.release EVENT [2.404]:IO: Dispatch [gre.release]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [2.405]:ACTION: Invoke [gre.release]-&gt;[gra.datachange] on control [button_test]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [2.405]:ACTION: Invoke [gre.release]-&gt;[gra.lua] on control [button_test]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;emphasis role="bold"&gt;ACTION [2.405]:Lua: [mainScreen::CBHandleRelease]&lt;/emphasis&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [2.405]:CONTROL RENDER: background (203x62 476x164)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [2.405]:CONTROL RENDER: status_text (277x62 476x164)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [2.405]:CONTROL RENDER: button_test (203x86 277x141)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [3.203]:ACTION: Invoke [gre.outbound]-&gt;[gra.datachange] on control [button_test]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [3.203]:ACTION: Invoke [gre.outbound]-&gt;[gra.lua] on control [button_test]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;emphasis role="bold"&gt;ACTION [3.203]:Lua: [mainScreen::CBHandleRelease]&lt;/emphasis&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [3.373]:ACTION: Invoke [gre.outbound]-&gt;[gra.datachange] on control [button_next]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [3.699]:IO: Queue [1] gre.press EVENT [3.699]:IO: Dispatch [gre.press]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [3.699]:ACTION: Invoke [gre.press]-&gt;[gra.datachange] on control [button_next]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [3.700]:CONTROL RENDER: background (395x206 469x261)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [3.700]:CONTROL RENDER: button_next (395x206 469x261)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [3.896]:IO: Queue [1] gre.release EVENT [3.896]:IO: Dispatch [gre.release]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [3.896]:ACTION: Invoke [gre.touch]-&gt;[gra.screen] on control [button_next]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [3.897]:IO: Queue [2] gre.screenshow.pre EVENT [3.897]:IO: Queue [3] gre.screenhide.pre ACTION [3.897]:ACTION: Invoke [gre.release]-&gt;[gra.datachange] on control [button_next]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [3.897]:IO: Dispatch [gre.screenshow.pre]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [3.897]:ACTION: Invoke [gre.screenshow.pre]-&gt;[gra.lua] on screen [secondScreen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;emphasis role="bold"&gt;ACTION [3.897]:Lua: [callbacks.CBHandleScreenShow]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">loaded module secondScreen.lua</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">secondScreen:init()&lt;/emphasis&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [3.898]:IO: Dispatch [gre.screenhide.pre]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [3.898]:ACTION: Invoke [gre.screenhide.pre]-&gt;[gra.lua] on screen [mainScreen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;emphasis role="bold"&gt;ACTION [3.898]:Lua: [callbacks.CBHandleScreenHide]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">unloading module : modules.mainScreen</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">mainScreen:exit()&lt;/emphasis&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [4.915]:ACTION: Invoke [gre.touch]-&gt;[gra.screen] on control [button_back]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [4.915]:IO: Queue [2] gre.screenshow.pre EVENT [4.915]:IO: Queue [3] gre.screenhide.pre ACTION [4.915]:ACTION: Invoke [gre.release]-&gt;[gra.datachange] on control [button_back]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [4.916]:IO: Dispatch [gre.screenshow.pre]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [4.916]:ACTION: Invoke [gre.screenshow.pre]-&gt;[gra.lua] on screen [mainScreen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;emphasis role="bold"&gt;ACTION [4.916]:Lua: [callbacks.CBHandleScreenShow]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">loaded module mainScreen.lua</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">mainScreen:init()&lt;/emphasis&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [4.916]:IO: Dispatch [gre.screenhide.pre]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [4.917]:ACTION: Invoke [gre.screenhide.pre]-&gt;[gra.lua] on screen [secondScreen]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;emphasis role="bold"&gt;ACTION [4.917]:Lua: [callbacks.CBHandleScreenHide]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">unloading module : modules.secondScreen</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">secondScreen:exit()&lt;/emphasis&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [4.917]:CONTROL RENDER: background (0x0 479x271)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [4.917]:CONTROL RENDER: status_text (277x62 476x164)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [4.917]:CONTROL RENDER: button_next (395x206 469x261)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [4.917]:CONTROL RENDER: button_test (203x86 277x141)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [4.917]:CONTROL RENDER: header_text (111x7 350x26)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [4.918]:IO: Queue [2] gre.screenshow.post EVENT [4.918]:IO: Queue [3] gre.screenhide.post DIAG [4.918]:CONTROL RENDER: background (0x0 479x271)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [4.918]:CONTROL RENDER: status_text (277x62 476x164)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [4.918]:CONTROL RENDER: button_next (395x206 469x261)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [4.918]:CONTROL RENDER: button_test (203x86 277x141)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DIAG [4.919]:CONTROL RENDER: header_text (111x7 350x26)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [4.919]:IO: Dispatch [gre.screenshow.post]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [4.919]:IO: Dispatch [gre.screenhide.post]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [5.052]:ACTION: Invoke [gre.outbound]-&gt;[gra.datachange] on control [button_test]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ACTION [5.053]:ACTION: Invoke [gre.outbound]-&gt;[gra.lua] on control [button_test]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;emphasis role="bold"&gt;ACTION [5.053]:Lua: [mainScreen::CBHandleRelease]&lt;/emphasis&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [5.085]:IO: Queue [1] gre.release EVENT [5.085]:IO: Dispatch [gre.release]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">EVENT [5.832]:IO: Queue [1] gre.quit EVENT [5.832]:IO: Dispatch [gre.quit]</span><br></div></code></pre></div></div>]]></content:encoded>
            <category>knowledge-base</category>
            <category>lua</category>
            <category>workflow</category>
        </item>
        <item>
            <title><![CDATA[How to run the LuaSqliteDatabase sample on an ARM based target board]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/how-to-run-the-luasqlitedatabase-sample-on-an-arm-based-target-board</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/how-to-run-the-luasqlitedatabase-sample-on-an-arm-based-target-board</guid>
            <pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[The Storyboard Designer install ships with several sample applications which make use of additional community sourced Lua modules to extend the capabilities of the standard runtime plug-ins. Two examples are the comms modules for the SocketJSON sample and the database functionality behind the LuaSqliteDatabase sample.]]></description>
            <content:encoded><![CDATA[<p>The Storyboard Designer install ships with several sample applications which make use of additional community sourced Lua modules to extend the capabilities of the standard runtime plug-ins. Two examples are the comms modules for the SocketJSON sample and the database functionality behind the LuaSqliteDatabase sample.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="getting-started-with-the-luassqlitedatabase-sample">Getting started with the LuasSqliteDatabase sample<a href="https://docs.cranksoftware.com/knowledge-base/how-to-run-the-luasqlitedatabase-sample-on-an-arm-based-target-board#getting-started-with-the-luassqlitedatabase-sample" class="hash-link" aria-label="Direct link to Getting started with the LuasSqliteDatabase sample" title="Direct link to Getting started with the LuasSqliteDatabase sample" translate="no">​</a></h2>
<p>If we take a closer look at the LuaSqliteDatabase sample you will find an implementation that uses sqlite3 libraries and a simple database populated with some users and data values for controls:</p>
<p><img decoding="async" loading="lazy" alt="image1-2.png" src="https://docs.cranksoftware.com/assets/images/image1-2-f14c89a22e7df3bb78b7c296846b33cb.png" width="1278" height="845" class="img_ev3q"></p>
<p>The Lua modules underpinning the database functionality are required to be first loaded by the Lua engine.</p>
<p>Remember to check that these additional files and resources have been included in the deployment configuration. The Storyboard Application Export Configurations dialog and Export Resources tab allows you to add resources to your target filesystem:</p>
<p><img decoding="async" loading="lazy" alt="image4-2.png" src="https://docs.cranksoftware.com/assets/images/image4-2-bdcef7ff9470459e3c918b15e9424213.png" width="1231" height="1495" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="loading-the-external-lua-modules">Loading the external Lua modules<a href="https://docs.cranksoftware.com/knowledge-base/how-to-run-the-luasqlitedatabase-sample-on-an-arm-based-target-board#loading-the-external-lua-modules" class="hash-link" aria-label="Direct link to Loading the external Lua modules" title="Direct link to Loading the external Lua modules" translate="no">​</a></h2>
<p>The code that loads the library dependencies is as follows and can be seen in callbacks.lua:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">-- This is where we do all of the Database interaction and initialization.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-- we connect, query, and update our target database table via the sqlite3 plugin</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local myenv = gre.env({ "target_os", "target_cpu" })</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">if(myenv.target_os=="win32")then</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  package.cpath = gre.SCRIPT_ROOT .. "\\" .. myenv.target_os .. "-" .. myenv.target_cpu .."\\luasql_sqlite3.dll;" .. package.cpath</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">else</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  package.cpath = gre.SCRIPT_ROOT .. "/" .. myenv.target_os .. "-" .. myenv.target_cpu .."/luasql_sqlite3.so;" .. package.cpath</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">luasql = require("luasql_sqlite3")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local database = "usersettings.sqlite"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local env = assert(luasql.sqlite3())</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local db = assert(env:connect(gre.SCRIPT_ROOT .. "/"..database, "Failed to connect to database"))</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local users = {}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local currentUser = 1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">:</span><br></div></code></pre></div></div>
<p>You can see that we have used some OS specific environment values to automatically create the load path (package.cpath) needed for the luasql_sqlite3 libraries on x86 Linux, MacOS and Windows as part of this project so that you can run these samples in the desktop environment and simulator.</p>
<p>The Storyboard install only includes the appropriate DLLs or shared libraries for these targets but we do build them for most of our embedded targets.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="deploying-lua-modules-for-the-target-board">Deploying Lua modules for the target board<a href="https://docs.cranksoftware.com/knowledge-base/how-to-run-the-luasqlitedatabase-sample-on-an-arm-based-target-board#deploying-lua-modules-for-the-target-board" class="hash-link" aria-label="Direct link to Deploying Lua modules for the target board" title="Direct link to Deploying Lua modules for the target board" translate="no">​</a></h2>
<p>In the example screenshot above I have added the linux-armle folder and populated it with the cross-compiled luasql_sqlite3.so shared library matching the target toolchain and processor architecture.</p>
<p>This binary library is from our Lua libraries package.</p>
<blockquote>
</blockquote>
<p>We do not include the ‘Lua libraries’ package for every target runtime for installer size reasons however, they are generally available at no charge. You can request these via email to your account manager at <a href="mailto:sales@cranksoftware.com" target="_blank" rel="noopener noreferrer" class="">sales@cranksoftware.com</a> or through our support channels at <a href="mailto:support@cranksoftware.com" target="_blank" rel="noopener noreferrer" class="">support@cranksoftware.com</a> .</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="notes-on-building-lua-modules-for-your-target-board">Notes on building Lua modules for your target board<a href="https://docs.cranksoftware.com/knowledge-base/how-to-run-the-luasqlitedatabase-sample-on-an-arm-based-target-board#notes-on-building-lua-modules-for-your-target-board" class="hash-link" aria-label="Direct link to Notes on building Lua modules for your target board" title="Direct link to Notes on building Lua modules for your target board" translate="no">​</a></h2>
<p>It is recommended that you prepare and build your own Lua libraries packages where possible so that you can take advantage of updates and bug fixes as part of your product development processes and procedures.</p>
<p>This is generally straightforward as it aligns with your other build tasks. There are a few guidance notes which should make this trouble free:</p>
<ul>
<li class="">LuaRocks is awesome however this builds libraries for your desktop development environment by default which is great for simulation and testing but unfortunately probably NOT compatible with your target board</li>
<li class="">Instead, pull the sources and build your own libraries: Cross-compile for the target architecture you need using the right toolchain and make</li>
<li class="">Always link against the Storyboard Lua library (sblua) to ensure that the API is compatible</li>
<li class="">Likewise, ensure that you use the same version of the Lua libraries as the Storyboard core (Lua 5.1)</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="additional-resources">Additional Resources<a href="https://docs.cranksoftware.com/knowledge-base/how-to-run-the-luasqlitedatabase-sample-on-an-arm-based-target-board#additional-resources" class="hash-link" aria-label="Direct link to Additional Resources" title="Direct link to Additional Resources" translate="no">​</a></h2>
<p>VIDEO: How to run the LuaSqliteDatabase sample</p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>lua</category>
            <category>embedded</category>
            <category>samples</category>
        </item>
        <item>
            <title><![CDATA[Working with events and application data in a Storyboard (RTOS) Real-time Executable environment]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/working-with-events-and-application-data-in-a-storyboard-rtos-real-time-executable-environment</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/working-with-events-and-application-data-in-a-storyboard-rtos-real-time-executable-environment</guid>
            <pubDate>Thu, 25 Sep 2025 00:00:00 GMT</pubDate>
            <description><![CDATA[This document explores some examples of using the SDK API to send and receive Storyboard events and set application data variables in a Real-time Executable environment.]]></description>
            <content:encoded><![CDATA[<p>This document explores some examples of using the SDK API to send and receive Storyboard events and set application data variables in a Real-time Executable environment.</p>
<!-- -->
<p>The examples and source code are suitable for use with the Storyboard Lite and Storyboard Real-time Executive (RTExec) SDKs from Crank Software. These are available on request from <a href="mailto:sales@cranksoftware.com" target="_blank" rel="noopener noreferrer" class="">sales@cranksoftware.com</a> for a number of supported hardware platforms including the i.MXRT1050 and i.MXRT1060 family of crossover processors from NXP, the STM32F4xx and STM32F7xx families from ST Microelectonics, as well as the RA8D1 and RZ/A3M from Renesas.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="sending-events-to-the-backend-from-storyboard">Sending events to the backend from Storyboard<a href="https://docs.cranksoftware.com/knowledge-base/working-with-events-and-application-data-in-a-storyboard-rtos-real-time-executable-environment#sending-events-to-the-backend-from-storyboard" class="hash-link" aria-label="Direct link to Sending events to the backend from Storyboard" title="Direct link to Sending events to the backend from Storyboard" translate="no">​</a></h2>
<p>When testing events with Storyboard the runtime command line utilities can be used to prototype sending the event with data in a desktop environment on an SBIO connection channel, in this example 'Outgoing'.</p>
<p>For example, using the iogen utility to send an event ‘outgoingDataChange’ with one single byte (unit8_t) data parameter ‘requestState’ in the payload to the channel:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">c:\Program Files\Crank_Software\Storyboard_Engine\5.3.201807051156\win32-x86-opengles_2.0-obj\bin&gt;iogen Outgoing no_target outgoingDataChange 1u1:requestState 2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Connecting to Storyboard IO channel [Outgoing]</span><br></div></code></pre></div></div>
<p>To receive the event use the iorcv utility. You should use the following command line to listen on the same named channel ‘Outgoing’:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">c:\Program Files\Crank_Software\Storyboard_Engine\5.3.201807051156\win32-x86-opengles_2.0-obj\bin&gt;iorcv.exe Outgoing</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Waiting on channel [Outgoing]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">outgoingDataChange,no_target,1u1:requestState 2</span><br></div></code></pre></div></div>
<p>Within the Storyboard application model we can initiate the same event with data to the back-end using a Lua function eg: RequestCalibration() as follows:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">stateValue=2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--[ On calibration timer trigger (1 second interval) send event "outgoingDataChange" to back-end channel "Outgoing"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function RequestCalibration()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    local eventData = {}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    local format = "1u1 requestState"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    eventData["requestState"] = stateValue</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    local success = gre.send_event_data("outgoingDataChange",format,eventData,"Outgoing")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    if(success == false) then</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        print(error)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        --TODO : handle error</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>You should see the same output using the iorcv utility as before but this time the event is sent from within a Storyboard application</p>
<p>Now within the Storyboard RTExec environment, several changes need to be made in order to link the 'front-end' Storyboard application with the 'back-end' FreeRTOS code for receiving UI events.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="configure-the-io-channel-with-the-runtime-engine">Configure the IO channel with the runtime engine<a href="https://docs.cranksoftware.com/knowledge-base/working-with-events-and-application-data-in-a-storyboard-rtos-real-time-executable-environment#configure-the-io-channel-with-the-runtime-engine" class="hash-link" aria-label="Direct link to Configure the IO channel with the runtime engine" title="Direct link to Configure the IO channel with the runtime engine" translate="no">​</a></h2>
<p>In the desktop and OS runtime environment the IO 'channel' is normally communicated on the sbengine command line whereas for RTExec this channel name is passed in as a greio argument to the app launch API call.</p>
<p>In a typical RTExec project, the Storyboard task code is located in file sbengine_task.c . To enable the Storyboard runtime engine to access and use a named IO channel, add the name as an argument parameter ultimately via the API call gr_application_create_args().</p>
<p>We have again used the channel name 'Outgoing' as in the above example:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">/**</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * This is the main runtime task for Storyboard.  It should be possible to call this</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * task directly from within your FreeRTOS core application.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">void</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sbengine_main_task(void *arg) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  char *args[20];</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  int n = 0;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  args[n++] = "greio";</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  args[n++] = "channel=Outgoing";</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // If using VFS, the Storyboard application model will be loaded via string data</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // which is generally present in an sbengine_model.h file named 'sb_model'. This</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // string data would be passed to gr_application_create_args with the GR_APP_LOAD_STRING</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // option flag.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  //</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // In the case a filesystem is present, then the Storyboard model will be loaded</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // from a file on the filesystem. The path to the file should be passed to</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // gr_application_create_args with the GR_APP_LOAD_FILE option flag.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  run_storyboard_app(sb_model, GR_APP_LOAD_STRING, args, n);</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="add-an-event-listener-and-handler-for-receiving-events-from-storyboard">Add an event listener and handler for receiving events from Storyboard<a href="https://docs.cranksoftware.com/knowledge-base/working-with-events-and-application-data-in-a-storyboard-rtos-real-time-executable-environment#add-an-event-listener-and-handler-for-receiving-events-from-storyboard" class="hash-link" aria-label="Direct link to Add an event listener and handler for receiving events from Storyboard" title="Direct link to Add an event listener and handler for receiving events from Storyboard" translate="no">​</a></h2>
<p>For the 'back-end' FreeRTOS code to receive event notifications from the ‘front-end’ Storyboard UI we need to add an event listener and associated call-back handler to receive each type of these asynchronous events.</p>
<p>This should normally be done after the app object has been initialized but before it has been launched so that the call-back is ready to receive the events on start-up.</p>
<p>Again in sbengine_task.c you can add a call-back (here we just dump the event data by example) and add the listener for it with a convenient place, here in run_storyboard_app() works perfectly.</p>
<p>You should be able to write a single generic handler for multiple received events but the listeners are specific to each event name and channel so you may need to register several if you have lots of events.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">static void CrankEventListenerCallback(gr_application_t *app, gr_event_t *event, void *arg)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">{</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  uint8_t *pData;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  PRINTF("Received Event[%s]: %s, %d bytes;", event-&gt;name, event-&gt;format, event-&gt;nbytes);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  pData = (uint8_t *)event-&gt;data;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  for( int i=0; i&lt; event-&gt;nbytes; i++)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    PRINTF(" 0x%X", *pData++ );</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  PRINTF("\n\r");</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  return;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">static void</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">run_storyboard_app(const char *bundle, int flags, char * const *options, int option_count) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  app = gr_application_create_args(bundle, flags, options, option_count);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  if(!app) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    return;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // Sets the application logging verbosity. For more logging verbosities, refer to gre.h.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  gr_application_debug(app, GR_DEBUG_CMD_VERBOSITY, GR_LOG_TRACE2);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    // Register our debug console logger callback</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gr_register_log_cb(app, &amp;logger_cb, NULL, NULL);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    // Add event listener for event 'outgoingDataChange' via channel 'Outgoing'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  gr_application_event_listener_t *mEventListener = NULL;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  mEventListener = gr_application_add_event_listener(app,        "outgoingDataChange", &amp;CrankEventListenerCallback, NULL);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  if (mEventListener == NULL)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    PRINTF("failed to add event listener\n");</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // Start the application</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  gr_application_run(app);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // Free the application instance and close any resources.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  gr_application_free(app);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="sending-an-event-with-data-to-storyboard-front-end">Sending an event with data to Storyboard ‘front-end’<a href="https://docs.cranksoftware.com/knowledge-base/working-with-events-and-application-data-in-a-storyboard-rtos-real-time-executable-environment#sending-an-event-with-data-to-storyboard-front-end" class="hash-link" aria-label="Direct link to Sending an event with data to Storyboard ‘front-end’" title="Direct link to Sending an event with data to Storyboard ‘front-end’" translate="no">​</a></h2>
<p>The mechanism is just the same for the FreeRTOS platform as you use with Storyboard on Linux or another high-level OS. If you want to use a named SBIO channel, instead of specifying the channel name on the sbengine launcher command line you need to pass this name as an argument to the run_storyboard_app() API call as described in the section Configure the IO channel with the runtime engine above.</p>
<p>With the FreeRTOS platform you can use a slightly different, more direct approach to send an event to the Storyboard ‘front-end’ UI by using the gr_application_send_event() API call from the SDK (check in include/gre/gre.h) which uses the application handle and app object.</p>
<p>Note: Be sure to first check that this app handle is valid before calling an API function eg: from an interrupt or another RTOS task.</p>
<p>From gre.h:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">/**</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * Inject an event into the application event queue.  </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> *</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * After making this call the arguments are copied into the internal event queue and </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * the content can be freely modified without affecting the queue'ed event. </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> *</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * @param app The application handle</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * @param event_target The name of the event target, or NULL to send to the default target</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * @param event_name The name of the event to send, must not be NULL</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * @param event_format The format of the data (see &lt;data_format.h&gt;, or NULL if no data is being sent</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * @param event_data A pointer do the data to transmit, or NULL if no data is transmitted</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * @param event_nbytes The number of data bytes to transmit, or NULL if no data is transmitted</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * @return 0 on success otherwise an error.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">DLLExport int gr_application_send_event(gr_application_t *app, </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                     const char *event_target, </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                     const char *event_name,</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                     const char *event_format,</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                     const void *event_data, </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                     int event_nbytes);</span><br></div></code></pre></div></div>
<p>If you have a copy of the Storyboard SDK document (Storyboard_Suite_SDK.pdf) there are more details of the various APIs.</p>
<p>Below is an example ‘back-end’ code snippet for FreeRTOS which posts an 'update_angle' event to the Storyboard UI model with a single floating point data parameter payload as event data 'dial_angle' using the API call gr_application_send_event().</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">gr_application_t *g_app = NULL;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">float angle = 90.0f;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">int status = 0;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">if( g_app != NULL ) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  status = gr_application_send_event(g_app, NULL, "update_angle", "4f1 dial_angle", &amp;angle, sizeof(angle));</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="generating-user-input-with-key-events-to-the-storyboard-front-end">Generating user input with key events to the Storyboard ‘front-end’<a href="https://docs.cranksoftware.com/knowledge-base/working-with-events-and-application-data-in-a-storyboard-rtos-real-time-executable-environment#generating-user-input-with-key-events-to-the-storyboard-front-end" class="hash-link" aria-label="Direct link to Generating user input with key events to the Storyboard ‘front-end’" title="Direct link to Generating user input with key events to the Storyboard ‘front-end’" translate="no">​</a></h2>
<p>Below is some code that you can use with hardware resources to send a key press-release cycle triggered from for example an IO pin interrupt trigger, UART character received etc:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">#include &lt;gre/greio.h&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#include &lt;gre/iodefs.h&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">gr_application_t *g_app = NULL; </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">gr_key_event_t              key_event_data;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">memset(&amp;key_event_data, 0, sizeof(key_event_data));</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">key_event_data.code = 0;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">key_event_data.key = c; // Read key value to send from serial port, keyboard device or sample IO pin etc</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">key_event_data.modifiers = 0;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">if( g_app != NULL ) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    // key is pressed and it was not a repeat key so send new press event</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gr_application_send_event(g_app, NULL, GR_EVENT_KEY_DOWN, GR_EVENT_KEY_FMT, &amp;key_event_data, sizeof(key_event_data));</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    greal_nanosleep(&amp;sleep_time, NULL);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    // and release</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gr_application_send_event(g_app, NULL, GR_EVENT_KEY_UP, GR_EVENT_KEY_FMT, &amp;key_event_data, sizeof(key_event_data));</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div></code></pre></div></div>
<p>The key-presses and touch events are posted to the model event queue and are handled at the application scope. Check out the definitions in gre/greio.h and gre/iodefs.h for more details of the events and formats.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="interacting-with-storyboard-data-variables">Interacting with Storyboard data variables<a href="https://docs.cranksoftware.com/knowledge-base/working-with-events-and-application-data-in-a-storyboard-rtos-real-time-executable-environment#interacting-with-storyboard-data-variables" class="hash-link" aria-label="Direct link to Interacting with Storyboard data variables" title="Direct link to Interacting with Storyboard data variables" translate="no">​</a></h2>
<p>Storyboard application model data variables can be accessed directly from the C code via the Storyboard data manager either by using the gr_application_set_data() API call to update the variables, triggering a data change in the UI directly or alternatively by using the gr_application_send_event() API call as above to post an event and handle it using a call-back in Lua to set and update the variables.</p>
<p>When a data variable is updated in this way Storyboard is able to trigger data change events and subsequent event actions that you have defined in the UI model automatically.</p>
<p>The following code example shows an RTOS task function which updates several Storyboard application data variables and types (mySignedInt32, and myUnsignedInt32) once every second:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">void sbengine_gre_io_data(void *arg)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">{</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  updatedata_event_t evUpdateData;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  char myMessage[] = "Hello Storyboard!";</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  //char myFormat[4];</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  int32_t *p_myInt32 = NULL;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  uint32_t *p_myUInt32 = NULL;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  int status = 0;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  strncpy(evUpdateData.myString, myMessage, sizeof(myMessage));</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  evUpdateData.myInt32 = -1000;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  evUpdateData.myUInt32 = 1234;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  while (1) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    vTaskDelay( 1000 );</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    if( app != NULL ) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">//Dispatch data to Storyboard by writing directly to variables using the data manager API</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">// Set </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">p_myInt32 = malloc(sizeof(int32_t));</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*p_myInt32 = evUpdateData.myInt32;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">status = gr_application_set_data(app, "mySignedInt32", "4s1", p_myInt32 );</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">p_myUInt32 = malloc(sizeof(uint32_t));</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*p_myUInt32 = evUpdateData.myUInt32;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">status = gr_application_set_data(app, "myUnsignedInt32", "4u1", p_myUInt32 );</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">//Alternative method: Dispatch data to Storyboard using an event API call</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">//status = gr_application_send_event(app, NULL, UPDATEDATA_EVENT, UPDATEDATA_FMT, &amp;evUpdateData, sizeof(evUpdateData) );</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">//Increment values to show change</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">evUpdateData.myInt32 += 10;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">evUpdateData.myUInt32 += 1;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div></code></pre></div></div>
<p>Here are some helper functions that you can use to simplify setting data variables and UI control parameters:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">void setKeyToIntValue(gr_application_t *app, char *keyName, int32_t value){</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    void *pdata;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    pdata = pvPortMalloc( sizeof(value) );</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    *(int32_t *)pdata = value;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    // the API call will free() pdata when finished with..</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gr_application_set_data(app, keyName, "4s1", pdata);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">void setKeyToUIntValue(gr_application_t *app, char *keyName, uint32_t value){</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    void *pdata;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    pdata = pvPortMalloc( sizeof(value) );</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    *(uint32_t *)pdata = value;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    // the API call will free() pdata when finished with..</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gr_application_set_data(app, keyName, "4u1", pdata);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">void setKeyToStringValue(gr_application_t *app, char *keyName, char* string){</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    void *pdata;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    size_t len;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    len = strlen(string);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    pdata = pvPortMalloc( len + 1 );</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    memset(pdata, '\0', len + 1 );</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    memcpy(pdata, string, len);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    // the API call will free() pdata when finished with..</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    gr_application_set_data(app, keyName, "1s0", pdata);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">examples:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> setKeyToIntValue( app, "mySignedInt32", mySignedInt-- );</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> setKeyToUIntValue( app, "myUnsignedInt32", myUnsignedInt++ );</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> setKeyToStringValue(app, "topLayerMain.SetPoint.Temperature.text", strbuf);</span><br></div></code></pre></div></div>
<p>Note: The Storyboard data variables must be defined at the ‘application’ scope.</p>
<p><img decoding="async" loading="lazy" alt="360023272571-image-1.png" src="https://docs.cranksoftware.com/assets/images/360023272571-image-1-863e3a5592ab825e80d24223ad0def47.png" width="1600" height="556" class="img_ev3q"></p>
<p>There are several different data variable formats supported in Storyboard that are selectable via the dialog at creation:</p>
<p><img decoding="async" loading="lazy" alt="360023235312-image-2.png" src="https://docs.cranksoftware.com/assets/images/360023235312-image-2-b8b85ed0cf66313e57999662d3a77fa7.png" width="880" height="650" class="img_ev3q"></p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>events</category>
            <category>variables</category>
            <category>rtos</category>
            <category>embedded</category>
        </item>
        <item>
            <title><![CDATA[Displaying Arabic Font]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/displaying-arabic-font</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/displaying-arabic-font</guid>
            <pubDate>Thu, 27 Jul 2023 00:00:00 GMT</pubDate>
            <description><![CDATA[When displaying text that is written right-to-left, this will sometimes have the behaviour of being displayed with the characters in the reverse order. This is because text is interpreted/rendered from left-to-right. This means that the simple solution is to start off by reversing the character order before rendering. E.g. if you want your right-to-left text to be displayed as ‘ABC’ then write it as ‘CBA’.]]></description>
            <content:encoded><![CDATA[<p>When displaying text that is written right-to-left, this will sometimes have the behaviour of being displayed with the characters in the reverse order. This is because text is interpreted/rendered from left-to-right. This means that the simple solution is to start off by reversing the character order before rendering. E.g. if you want your right-to-left text to be displayed as ‘ABC’ then write it as ‘CBA’.</p>
<!-- -->
<p>For some languages/scripts this can be done simply by adjusting the string in the translation database (or wherever these strings are stored, assuming this is for localization). But this isn’t exactly an elegant, reliable, and realistic solution. There are some libraries that we can use to solve this problem for us, let’s get started.</p>
<p>For the purpose of this article, the instructions are written from the Linux perspective, but the methodology is the same across Windows and Mac.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="files-needed">Files Needed:<a href="https://docs.cranksoftware.com/knowledge-base/displaying-arabic-font#files-needed" class="hash-link" aria-label="Direct link to Files Needed:" title="Direct link to Files Needed:" translate="no">​</a></h2>
<ul>
<li class="">UTF8 library: <a href="https://github.com/meepen/Lua-5.1-UTF-8" target="_blank" rel="noopener noreferrer" class="">https://github.com/meepen/Lua-5.1-UTF-8</a></li>
<li class="">BitOp library: <a href="http://bitop.luajit.org/download.html" target="_blank" rel="noopener noreferrer" class="">http://bitop.luajit.org/download.html</a> (either .tar.gz or .zip, both work)</li>
</ul>
<blockquote>
</blockquote>
<p>If you are on Storyboard version 5.1 and above you do not have to build the BitOp library. Instead, all you have to do is download the utf8.lua file and edit to make the following change:</p>
<p>local bit = bit</p>
<p>to</p>
<p>local bit = bit32</p>
<p>You can then continue on with what's written in the "Reversing Character Order With The UTF8 Module:" section of this article.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="building-bitop-library">Building BitOp library:<a href="https://docs.cranksoftware.com/knowledge-base/displaying-arabic-font#building-bitop-library" class="hash-link" aria-label="Direct link to Building BitOp library:" title="Direct link to Building BitOp library:" translate="no">​</a></h2>
<p>The BitOp library can be compiled against our Lua library and then used as a Lua module. In order to do this, we just need to point it at the libsblua.so that is included in the runtime you are using and set the include directory to be the runtime’s include/lua directory.</p>
<ul>
<li class="">Extract the BitOp download and edit the Makefile</li>
<li class="">Comment out the first INCLUDES statement and replace it with:</li>
</ul>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">#INCLUDES= -I/usr/local/include</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">INCLUDES= -I/[installpath]/Crank_Storyboard/Storyboard_Engine/6.2.202003130919.36509/linux-x86_64-opengles_2.0-x11-obj/include/lua</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">LDFLAGS= -L/[installpath]/Crank_Storyboard/Storyboard_Engine/6.2.202003130919.36509/linux-x86_64-opengles_2.0-x11-obj/lib -lsblua</span><br></div></code></pre></div></div>
<blockquote>
</blockquote>
<p>Make sure this is formatted for whichever specific runtime and version of Storyboard you are using. The example shown is for the Linux, opengl runtime for Storyboard v6.2</p>
<ul>
<li class="">Scroll down near the bottom of the file and move SOLDFLAGS to be after the -o arguments:</li>
</ul>
<p>Before:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">$(MODSO): $(MODNAME).o</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	$(SOCC) $(SOLDFLAGS) -o $@ $&lt;</span><br></div></code></pre></div></div>
<p>After:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">$(MODSO): $(MODNAME).o</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	$(SOCC)  -o $@ $&lt; $(SOLDFLAGS)</span><br></div></code></pre></div></div>
<p>The arguments to the linker have to be placed after the -o directive otherwise it won’t bring in the sblua library.</p>
<ul>
<li class="">Save this and open a terminal session in this directory.</li>
<li class="">Execute a ‘make’ command and it should build the library successfully:</li>
</ul>
<p><img decoding="async" loading="lazy" alt="bitop_make.png" src="https://docs.cranksoftware.com/assets/images/bitop_make-0042e38a0c79ef1d5ea06615445031c4.png" width="688" height="104" class="img_ev3q"></p>
<p>Now let’s go ahead and move the newly generated bit.so into our project directory. For the purpose of this article I’ve moved it into my project’s scripts folder in a new folder titled “linux-x86_64”. This is just a nice organizational method in case you have to build for multiple platforms.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="setup-utf8-library">Setup UTF8 Library:<a href="https://docs.cranksoftware.com/knowledge-base/displaying-arabic-font#setup-utf8-library" class="hash-link" aria-label="Direct link to Setup UTF8 Library:" title="Direct link to Setup UTF8 Library:" translate="no">​</a></h2>
<p>Since the library is pure Lua, it can simply be dropped right into your project’s directory. For organizational sake, I’ll put this in the /scripts directory. The UTF8 library does have a dependency on the BitOp library, however, so we will need to add the following modifications:</p>
<ul>
<li class="">At the top of the file, we need to point the module to our bit.so. In this case, mine is located in [project root]/scripts/linux-x86_64/</li>
</ul>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">local info = gre.env({ "target_cpu","target_os" })</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local moddir = string.format("%s/%s-%s", gre.SCRIPT_ROOT, info.target_os, info.target_cpu)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">package.cpath = string.format("%s/?.so;%s", moddir, package.cpath)</span><br></div></code></pre></div></div>
<blockquote>
</blockquote>
<p>If done this way, if you have also built the BitOp lib for different platforms, it will source the correct one based on the platform, as long as you’ve labeled the folders appropriately.</p>
<ul>
<li class="">Add a require for our bit lib:</li>
</ul>
<p>Before:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">local bit = bit</span><br></div></code></pre></div></div>
<p>After:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">local bit = require “bit”</span><br></div></code></pre></div></div>
<p>And that is all the setup that is required. The next step is to actually use what we’ve included thus far.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="reversing-character-order-with-the-utf8-module">Reversing Character Order With The UTF8 Module:<a href="https://docs.cranksoftware.com/knowledge-base/displaying-arabic-font#reversing-character-order-with-the-utf8-module" class="hash-link" aria-label="Direct link to Reversing Character Order With The UTF8 Module:" title="Direct link to Reversing Character Order With The UTF8 Module:" translate="no">​</a></h2>
<p>A simple demonstration of how to use the utf8 module to reverse character order is as follows:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">-- Get the arabic string, in this case it’s just hard-written, but in your case it will likely be from a database</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> local arabic_string = 'مرحبا'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> local r = ""</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> -- Iterate through each character and append them to the end of the string to flip the order</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> for p,c in utf8.codes(arabic_string) do</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   r = utf8.char(utf8.codepoint(c))..r</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> -- Set the text of the control’s text variable</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> gre.set_value("Layer.text_ctrl.text", r)</span><br></div></code></pre></div></div>
<p>You will also need to ensure that you set the grd_text_shaper_attrs to the appropriate value:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">gre.set_value("grd_text_shaper_attrs", "script=Arabic;lang=ar")</span><br></div></code></pre></div></div>
<p>To read more about this option and to get the appropriate ISO codes for the script and language, please refer to the documentation found here: <a href="https://support.cranksoftware.com/hc/en-us/articles/360039998812-Script-Specific-Text-Shaping-and-Layout" target="_blank" rel="noopener noreferrer" class="">https://support.cranksoftware.com/hc/en-us/articles/360039998812-Script-Specific-Text-Shaping-and-Layout</a></p>
<p>And that’s it, from there everything should be rendered as intended.</p>
<p>A sample project with these libraries compiled can be found here: LINK</p>
<p>A video of the process can be found here.</p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>text</category>
            <category>localization</category>
        </item>
        <item>
            <title><![CDATA[Building a Storyboard Container]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/building-a-storyboard-container</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/building-a-storyboard-container</guid>
            <pubDate>Fri, 22 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[If you are evaluating Storyboard or the Torizon platform you will likely have seen our pre-built container images configured to run on the Toradex Torizon platform. After you’ve played around with the image a bit, you will likely want to build your own container. Docker is a very flexible and configurable platform so I’m going to show you what we’ve done internally for our container and you can use that as inspiration or a base for your container going forward. To begin you will require the followirng pieces:]]></description>
            <content:encoded><![CDATA[<p>If you are evaluating Storyboard or the Torizon platform you will likely have seen our pre-built container images configured to run on the Toradex Torizon platform. After you’ve played around with the image a bit, you will likely want to build your own container. Docker is a very flexible and configurable platform so I’m going to show you what we’ve done internally for our container and you can use that as inspiration or a base for your container going forward. To begin you will require the followirng pieces:</p>
<!-- -->
<ul>
<li class="">
<p>Runtime engine for a supported hardware platform, at the time of writing we’ve tested on the i.MX8 and i.MX6 range with OpenGL using Wayland linux-imx8yocto-armle-opengles_2.0-wayland-obj</p>
</li>
<li class="">
<p>linux-imx6wayland-armle-opengles_2.0-wayland_GST01-obj</p>
</li>
<li class="">
<p>The application you wish to use to test the container You can use one of the samples shipped with Storyboard like the “AddressBook” or look at our public SVN for a more comprehensive demo application.</p>
</li>
<li class="">
<p>Storyboard launch script This script contains the parameters used to launch the Storyboard engine and follow the patterns described in our help section for running your application</p>
</li>
</ul>
<p>Once you have these pieces in place you can connect your development platform and start to build your docker file. At this point, it’s assumed that you were able to successfully run our pre-built Docker container on your hardware. If you’ve haven’t done that yet I’d suggest doing that now so you can validate a working scenario on the platform before creating your own.</p>
<p>This is the Recipe file from the standard i.MX8 container.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">&lt;para xinfo:text="23143"/&gt;&lt;para xinfo:text="23144"/&gt;&lt;itemizedlist&gt;&lt;listitem&gt;&lt;para xinfo:text="23145"/&gt;&lt;itemizedlist&gt;&lt;listitem&gt;&lt;para xinfo:text="23146"/&gt;&lt;itemizedlist&gt;&lt;listitem&gt;&lt;para xinfo:text="23147"/&gt;&lt;/listitem&gt;&lt;/itemizedlist&gt;&lt;/listitem&gt;&lt;listitem&gt;&lt;para xinfo:text="23148"/&gt;&lt;itemizedlist&gt;&lt;listitem&gt;&lt;para xinfo:text="23149"/&gt;&lt;/listitem&gt;&lt;/itemizedlist&gt;&lt;/listitem&gt;&lt;listitem&gt;&lt;para xinfo:text="23150"/&gt;&lt;/listitem&gt;&lt;/itemizedlist&gt;&lt;/listitem&gt;&lt;/itemizedlist&gt;&lt;para xinfo:text="23151"/&gt;&lt;para xinfo:text="23152"/&gt;&lt;programlisting xml:space="preserve" xinfo:translate="no"&gt;#!/bin/bash</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#Crank Software's Torizon demo</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#Variable Setup</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">crank_dir=/usr/crank</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">scp_dir=$crank_dir/scp</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sbengine_ext=.gapp</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">runtime=/usr/crank/runtimes/linux-imx8yocto-armle-opengles_2.0-wayland-obj</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sb_binary=$runtime/bin/sbengine</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sb_app=VitalsHD/1280x720.gapp</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">demo_app=$crank_dir/apps/$sb_app</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sb_options='-orender_mgr,fullscreen -omtdev,device=/dev/input/event1'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#ENV Variables</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">export SB_PLUGINS=$runtime/plugins</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">export LD_LIBRARY_PATH=$runtime/lib:$LD_LIBRARY_PATH</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">export LD_PRELOAD=/usr/lib/aarch64-linux-gnu/libwayland-egl.so.1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#Verify that SCP directory exists, and check if .gapp file is stored in it.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#If a .gapp file exists run sbengine on this app, otherwise execute demo</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">launcher</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">scp_app=$(find /usr/crank/scp -name *.gapp -print -quit)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">echo $scp_app</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">if [ -z $scp_app ]; then</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    echo "Starting Crank Demo Application..."</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    echo $sb_app    $sb_binary $sb_options $demo_app</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">else</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    echo "Starting Storyboard SCP Application..."</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    echo $scp_app    $sb_binary $sb_options $scp_app</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">fi</span><br></div></code></pre></div></div>
<p>The script isn’t overly complicated but it does a few things. As I mentioned earlier you should consult the running your application section in our help center to learn more. However, I’ve highlighted the line which declares the “sb_options” variable. This is where we set up any engine parameters required to configure or tune the engine to our platform. As of right now, we are specifying the application to run in full-screen mode and use the mtdev plugin to read the touch device exposed as /dev/input/event1. This device might be enumerated as a different device depending on your system or the display that you’ve connected so you should check the devices exposed on the host system and once you’ve confirmed you have the right one add it to the script. This device will be exposed to the container from the host hardware based on the container’s run command. One common thing you might want to do is to expose the engine to receiving commands on a particular port. To do this you can leverage the SBIO over TCP functionality. So add the following option to your line which will expose tcp port number 55555.</p>
<p>-ogreio,channel=tcp://55555</p>
<p>Your sb_options line should now look like this.</p>
<p>sb_options='-orender_mgr,fullscreen -omtdev,device=/dev/input/event1 -ogreio,channel=tcp://55555'</p>
<p>You can experiment with adding other options or configurations but now we should try to build and run our container. Connect to your development hardware and copy the crank folder with your runtime, demo application and launch script to the hardware. Beside the crank directory create a file called “Dockerfile” without an extension. Add the lines from our i.MX8 docker recipe to the file, save it and close it. You can then build the container with the following docker command.</p>
<p>docker build -t ':' .</p>
<p>Once you’ve built the container you can launch it with the following command.</p>
<p>docker run -it --privileged -p 55555:55555 -v /home/torizon/crank/scp:/usr/crank/scp -v /dev:/dev -v /tmp:/tmp :</p>
<p>The -p flag will expose port 55555 within the container as port 55555 on the host machine. Finally, make sure to update the final path to your built image in the launch command.</p>
<p>From there you container should launch and if you want you’ll be able to send Storyboard IO events over a TCP socket connection.</p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>components</category>
            <category>workflow</category>
        </item>
        <item>
            <title><![CDATA[Creating A Custom Launcher App For Android]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/creating-a-custom-launcher-app-for-android</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/creating-a-custom-launcher-app-for-android</guid>
            <pubDate>Fri, 22 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Because we will be interacting with Android, the first order of business is to get oriented with the LuaJava bridge. I highly recommend reading this write-up which goes over the process of exporting to Android as well as the luajava API with an example application:]]></description>
            <content:encoded><![CDATA[<p>Because we will be interacting with Android, the first order of business is to get oriented with the LuaJava bridge. I highly recommend reading this write-up which goes over the process of exporting to Android as well as the luajava API with an example application:</p>
<!-- -->
<p>The best place to start when looking to perform any kind of feature on Android is to first figure out how it would be accomplished in a native Android setting. Today we’ll be looking at how to create a custom launcher using Storyboard.</p>
<p>First order of business is modifying your Android manifest file. You will have to export your Android manifest file from Storyboard. You can do this via:</p>
<p>Storyboard Application Export Configuration &gt; Change Packaging Method to Native Android Application</p>
<p><img decoding="async" loading="lazy" alt="image4-4.png" src="https://docs.cranksoftware.com/assets/images/image4-4-19d88a201267f60b8c299b5859c49b1e.png" width="935" height="774" class="img_ev3q"></p>
<p>Select the Manifest Tab &gt; Manifest File (under Advanced Options) &gt; Export current manifest settings to a file</p>
<p><img decoding="async" loading="lazy" alt="image1-4.png" src="https://docs.cranksoftware.com/assets/images/image1-4-68196a4d59d50c6eefb838a7d0f3339a.png" width="968" height="741" class="img_ev3q"></p>
<p>Choose the location you wish to export your manifest file to and click Save.</p>
<p>Open this xml file in any text editor of your choice, you can even open it in Storyboard if you want.</p>
<p>The first thing we have to change is the content inside the activity tag. By default your manifest will contain:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">&lt;intent-filter&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    &lt;action android:name="android.intent.action.MAIN"/&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    &lt;category android:name="android.intent.category.LAUNCHER"/&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;/intent-filter&gt;</span><br></div></code></pre></div></div>
<p>Simply replace with these two lines:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">&lt;category android:name="android.intent.category.HOME"/&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;category android:name="android.intent.category.DEFAULT"/&gt;</span><br></div></code></pre></div></div>
<p>What this does is ensures the Android OS identifies this application as a launcher. This means that it can be loaded when the system boots, and can be shown when the “home” button is pressed.</p>
<p>Additionally, it is a good idea to specify that this application is a “singleTask” activity.This means that there can only be one instance of the activity and it is always at the root of the activity stack.</p>
<p>To do this, add this line within your activity tag:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">android:launchMode="singleTask"</span><br></div></code></pre></div></div>
<p>And to finish up the modifications to your manifest, I would ensure that the manifest’s package name for your application matches the package name described in Storyboard. You can see and modify the package name in Storyboard within the Storyboard Application Export Configuration window.</p>
<p><img decoding="async" loading="lazy" alt="image2-4.png" src="https://docs.cranksoftware.com/assets/images/image2-4-22f7850754b35f317e9eb325e7b7f4a5.png" width="993" height="755" class="img_ev3q"></p>
<p>Upon first exporting the default manifest, the package name does not match the Android Unique Application Identifier shown in the image above.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="android_launcher_demo"&gt;</span><br></div></code></pre></div></div>
<p>So we just want to change it to reflect the unique identifier:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.storyboard.android_launcher_demo"&gt;</span><br></div></code></pre></div></div>
<p>After all these changes you will have a manifest file that looks like:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">&lt;?xml version="1.0" encoding="utf-8" standalone="no"?&gt;&lt;manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.storyboard.android_launcher_demo"&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    &lt;uses-sdk android:minSdkVersion="11"/&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    &lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    &lt;supports-screens android:xlargeScreens="false"/&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    &lt;application android:hasCode="true" android:icon="@drawable/icon" android:label="@string/app_name"&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        &lt;activity android:launchMode="singleTask" android:configChanges="orientation|keyboardHidden" android:label="@string/app_name" android:name="com.crank.android.native_activity.StoryboardNativeActivity" android:screenOrientation="landscape"&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            &lt;meta-data android:name="android.app.lib_name" android:value="sbandroid-activity"/&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            &lt;intent-filter&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                &lt;action android:name="android.intent.action.MAIN"/&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                &lt;category android:name="android.intent.category.HOME" /&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                &lt;category android:name="android.intent.category.DEFAULT" /&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;/intent-filter&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;/activity&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">&lt;/application&gt;&lt;/manifest&gt;</span><br></div></code></pre></div></div>
<p>Now let’s take a look at actually launching an application.</p>
<p>Launching an application simply requires getting the launch intent for the specific application/package. This does mean you will need the package name/unique application identifier. In a native Java environment, launching chrome is as quick as:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Intent launchIntent = getPackageManager().getLaunchIntentForPackage("com.android.chrome");</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">startActivity(launchIntent);</span><br></div></code></pre></div></div>
<p>All we have to do is convert that code into using the LuaJava module.</p>
<p>First step is to get an instance of the NativeActivity class. This is because Storyboard applications that are deployed to Android devices run as native activities. As a note, it is good practice to follow any LuaJava calls with nil checks.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">local na = luajava.nativeActivity()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">if (na == nil) then</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    print("no native activity")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    return</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>Next step is to get an instance of the PackageManager class. The PackageManager class can get information related to the applications/packages installed on the device.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">local package_manager = na:getPackageManager()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">if (package_manager == nil) then</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   print("No Package Manager Class")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   return</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>Using the package manager instance, we can now get the launch intent for any package/application as long as we know the package name. Let’s continue with launching Chrome.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">local launch_intent = package_manager:getLaunchIntentForPackage(“com.android.chrome”)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">if (launch_intent == nil) then</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   print("No Launch Intent")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">   return</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>Finally, all that is left to do is launch the application/activity.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">na:startActivity(launch_intent)</span><br></div></code></pre></div></div>
<p>That’s all the setup required to create a custom Android launcher application using Storyboard.In this example, we’ve launched a native Android application (Chrome), but you can also use this to launch other Storyboard applications. As long as they’ve been installed on your device and you know their package name, simply swap the “com.android.chrome” package name with your Storyboard package name. Again, ensure the Android Manifest package name matches the Android Unique Application Identifier in the Export Configuration.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="exporting-and-installation">Exporting and Installation<a href="https://docs.cranksoftware.com/knowledge-base/creating-a-custom-launcher-app-for-android#exporting-and-installation" class="hash-link" aria-label="Direct link to Exporting and Installation" title="Direct link to Exporting and Installation" translate="no">​</a></h2>
<p>When exporting, ensure that the apk is being exported using the Android Manifest file that we modified earlier. In the Storyboard Application Export Configuration dialog, uncheck “Use default manifest file” and browse to the custom manifest file.</p>
<p><img decoding="async" loading="lazy" alt="image3-4.png" src="https://docs.cranksoftware.com/assets/images/image3-4-a12c2a8d6c33a05e58cf85c6939d5466.png" width="828" height="655" class="img_ev3q"></p>
<p>Click Run and you will have successfully packaged your launcher application.</p>
<p>Move the apk file over to your Android device and install it. You will have to set your default Home application to be your application that you’ve just installed. This can be done by pressing the home button, which will open a dialog asking you to choose which Home launcher you wish to use.</p>
<p>It can also be set by navigating to Settings &gt; Apps and Notifications &gt; Default Apps &gt; Home App</p>
<p>Click that and set it to your Storyboard application.</p>
<p>That’s it! Congratulations! Many cool interactive experiences can be made with this, so be sure to experiment.</p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>android</category>
            <category>deployment</category>
        </item>
        <item>
            <title><![CDATA[Working with Multiple Application Design Files]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/working-with-multiple-application-design-files</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/working-with-multiple-application-design-files</guid>
            <pubDate>Fri, 22 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[Storyboard’s collaborative features help multiple users develop applications faster. You can merge multiple files (*.gde) together and produce a single output during runtime. This tutorial explains how to create a project with multiple application files.]]></description>
            <content:encoded><![CDATA[<p>Storyboard’s collaborative features help multiple users develop applications faster. You can merge multiple files (*.gde) together and produce a single output during runtime. This tutorial explains how to create a project with multiple application files.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="creating-a-project">Creating a Project<a href="https://docs.cranksoftware.com/knowledge-base/working-with-multiple-application-design-files#creating-a-project" class="hash-link" aria-label="Direct link to Creating a Project" title="Direct link to Creating a Project" translate="no">​</a></h2>
<p>A project with multiple application files starts the same as a single application file project. Select File &gt; New &gt; Storyboard Application or create a new project using the Photoshop Import or Sketch import features.</p>
<p>A project can accommodate multiple standalone applications that share project images and script resources. You can add an additional application file to an existing project in multiple ways:</p>
<ul>
<li class="">Create a new file within an existing project by selecting File &gt; New &gt; Storyboard Application and choose to create a New Model in Existing Project.</li>
<li class="">Create a new file using the Photoshop Import feature and select the option to import into an Existing Project as a New File.</li>
<li class="">Create a new file using the Sketch Import feature and select the option to import into an Existing Project as a New File.</li>
<li class="">In the navigator view select and copy an existing application, then paste and rename the copied application file.</li>
</ul>
<p>For a multiple application project to function as a whole, application files need to reference one another. Select the application from the Application Model View and in the Properties View, select Add external model resource button. In the next dialog select the .gde file that was created in Step 2 and press OK.</p>
<p><img decoding="async" loading="lazy" alt="external_model_ref.png" src="https://docs.cranksoftware.com/assets/images/external_model_ref-171edaccbf42b61cf04e78485573df0f.png" width="675" height="399" class="img_ev3q"></p>
<p>To add layers from an external model to a local application:</p>
<ul>
<li class="">Select the Import External Layer tab and then select Import Selected Layers.</li>
<li class="">Choose the layer(s) to add to the current screen. After adding an external layer, Storyboard will recognize the external content and incorporate it to function like any other layer.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="360023321651-import_external_layer.png" src="https://docs.cranksoftware.com/assets/images/360023321651-import_external_layer-a2d11f2eefd6b2fd43569c963f3f1c92.png" width="511" height="509" class="img_ev3q"></p>
<p>To create a transition from a source application screen to an external application screen, add actions to an application that perform a screen change.</p>
<p><img decoding="async" loading="lazy" alt="external_screen.png" src="https://docs.cranksoftware.com/assets/images/external_screen-049ebb9099b0e6fb4a343bc3d15d3357.png" width="600" height="366" class="img_ev3q"></p>
<p>Before launching a multi-file application with the simulator, the external .gde files have to be referenced in the Simulator Configurations dialog.</p>
<p>To manage the runtime configurations, select Run &gt; Storyboard Simulator Configurations. A list of available models that can be included in the runtime export used with Storyboard Engine is in the selected project folder. To apply changes, select Apply and then Run.</p>
<p><img decoding="async" loading="lazy" alt="export_multi_files.png" src="https://docs.cranksoftware.com/assets/images/export_multi_files-cda3315364ed314e1223be10c6b5f968.png" width="650" height="597" class="img_ev3q"></p>
<p>If no conflicts occur within the selected applications, they are merged and converted into a single unified application at runtime. If conflicts exist, they must be resolved before the application can merge.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="resolving-conflicts">Resolving Conflicts<a href="https://docs.cranksoftware.com/knowledge-base/working-with-multiple-application-design-files#resolving-conflicts" class="hash-link" aria-label="Direct link to Resolving Conflicts" title="Direct link to Resolving Conflicts" translate="no">​</a></h2>
<p>The application properties page provides an action to synchronize source content with referenced external content. Any differences are flagged as a conflict and the user is prompted to resolve the conflict based on the issue. Conflict types include:</p>
<p>Layers: If two or more layers have the same name their content needs to be identical.</p>
<p>Variables: Any application/global variables with the same name must have values that are the same.</p>
<p>Animations: If two or more animations use the same name then the animation needs to be identical.</p>
<p>Screens: Screens from all applications are compared. Two or more screens with the same name prompt the user to resolve differences between the two.</p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>workflow</category>
            <category>components</category>
        </item>
        <item>
            <title><![CDATA[gstreamer/gstreamer-backend crash course]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/gstreamer-gstreamer-backend-crash-course</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/gstreamer-gstreamer-backend-crash-course</guid>
            <pubDate>Mon, 18 Jul 2022 00:00:00 GMT</pubDate>
            <description><![CDATA[This document serves as a quick reference which aims to answer questions regarding the various ways in which gstreamer can be set-up in your Storyboard application, and explain the building blocks along the way.]]></description>
            <content:encoded><![CDATA[<p>This document serves as a quick reference which aims to answer questions regarding the various ways in which gstreamer can be set-up in your Storyboard application, and explain the building blocks along the way.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-is-gstreamergstreamer-backend">What is gstreamer/gstreamer-backend?<a href="https://docs.cranksoftware.com/knowledge-base/gstreamer-gstreamer-backend-crash-course#what-is-gstreamergstreamer-backend" class="hash-link" aria-label="Direct link to What is gstreamer/gstreamer-backend?" title="Direct link to What is gstreamer/gstreamer-backend?" translate="no">​</a></h2>
<p>Gstreamer is an open-source library that supports audio and video playback/streaming/mixing. It is a tool that Storyboard utilizes, but it is not a Crank Software product. Gstreamer-backend is a media service which uses the gstreamer framework to control audio and video. While media can be played using just the base gstreamer framework via <code>gst-launch</code>, it’s usually better to use the gstreamer-backend service because it allows Media Control features like Pause, Resume, and Seek.</p>
<p>Before delving into any gstreamer work in Storyboard, if you have never worked with gstreamer before, please take some time to read their documentation and tutorials. Specifically, I would ensure to read this page on pipelines:</p>
<p><a href="https://gstreamer.freedesktop.org/documentation/tutorials/basic/gstreamer-tools.html?gi-language=c" target="_blank" rel="noopener noreferrer" class="">https://gstreamer.freedesktop.org/documentation/tutorials/basic/gstreamer-tools.html?gi-language=c</a></p>
<p>Having good foundational knowledge will only make the next steps easier.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-do-i-implement-gstreamer-backend">How do I implement gstreamer-backend?<a href="https://docs.cranksoftware.com/knowledge-base/gstreamer-gstreamer-backend-crash-course#how-do-i-implement-gstreamer-backend" class="hash-link" aria-label="Direct link to How do I implement gstreamer-backend?" title="Direct link to How do I implement gstreamer-backend?" translate="no">​</a></h2>
<p>Okay, so now that we’ve decided we want to use gstreamer-backend, how do we actually implement it?</p>
<p>Note: it’s generally a good idea to get your media successfully playing using basic gstreamer via ‘gst-launch-1.0 [pipeline]’ before attempting to integrate into Storyboard. This will help you identify potential issues with your video, platform, pipeline, etc. before introducing it into the gstreamer-backend/Storyboard environment.</p>
<p>If your Storyboard Engine’s plugin directory contains the ffmpeg plugin (libgre-plugin-ffmpeg.so), this will either need to be disabled or removed. This is because the ffmpeg plugin is loaded automatically when it is present. If it is not disabled/removed, it will compete with gstreamer-backend to service media requests.</p>
<p>Now all media requests will be serviced by gstreamer-backend. However, gstreamer-backend needs to be launched. Gstreamer-backend should be launched before your application is.</p>
<p>Here is an example of what a launch script might look like at this point:</p>
<p>Now that gstreamer-backend has been successfully set-up, let’s look at two ways we can render media playback in our application: using an external render extension, and the dual framebuffer approach. In a nutshell, if you wish to play low-fidelity videos, it’s quicker and more convenient to use external render extensions. If your video is medium to high fidelity or your main concern is performance, use the dual framebuffer approach. The following sections discuss these two approaches in detail.</p>
<p>Please ensure to read both, as they both have some crossover information.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="external-render-extension">External Render Extension<a href="https://docs.cranksoftware.com/knowledge-base/gstreamer-gstreamer-backend-crash-course#external-render-extension" class="hash-link" aria-label="Direct link to External Render Extension" title="Direct link to External Render Extension" translate="no">​</a></h2>
<p>The external render extension creates a buffer for other system applications or tasks to render into, things such as video players and web browsers. The MediaPlayer sample that comes with Storyboard uses an external render extension, so let’s use that sample as our reference. To import a sample, go to File &gt; Import &gt; Storyboard Development &gt; Storyboard Sample &gt; select MediaPlayer &gt; Finish</p>
<p>Once the MediaPlayer application is open, select the external_buf control.</p>
<p><img decoding="async" loading="lazy" alt="image4-6.png" src="https://docs.cranksoftware.com/assets/images/image4-6-7a8d7393f62ec2010b3fdc8947669b3c.png" width="1920" height="1042" class="img_ev3q"></p>
<p>As you can see in the Properties tab on the right-hand side, this control contains an external render extension. In Designer, external render extensions will appear as a magenta square. Since external render extensions are a built in part of your application, they are easier to work with. You can overlay controls simply via re-arranging layer/control order in the application model, which adds a level of convenience when using external render extensions.</p>
<p><img decoding="async" loading="lazy" alt="image3-6.png" src="data:image/png;base64,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" width="520" height="145" class="img_ev3q"></p>
<p>These don’t have any strict naming convention, but you do need to keep what you’ve set these to in mind as they will come into play momentarily.</p>
<p>Buffer Name: this is simply an identifier for your buffer.</p>
<p>Object Path: this is the path to a shared memory object.</p>
<p>Again, these can be named whatever you wish, as long as whatever platform you are running on does not currently have a conflicting shared memory object at that path, etc.</p>
<p>Let’s take a look at how to trigger video playback. Video playback is triggered via the gra.media.new.video action, so let’s find where that’s being used in this sample.In this case, the gra.media.new.video action can be found on the application level being triggered by an event called MediaPlay.</p>
<p><img decoding="async" loading="lazy" alt="image1-6.png" src="https://docs.cranksoftware.com/assets/images/image1-6-d3f34d004d590c5a385cf02de9b585be.png" width="1920" height="1039" class="img_ev3q"></p>
<p>The Properties of this event are where the majority of the work for successful playback is done.</p>
<p><img decoding="async" loading="lazy" alt="image5-3.png" src="https://docs.cranksoftware.com/assets/images/image5-3-d36b5d2f38f088621d94c4f8ee910133.png" width="537" height="399" class="img_ev3q"></p>
<p>For greater detail on what each individual option here controls, please refer to our Plugin Action Definitions page, under gra.media.new.video:</p>
<p>(<a href="https://support.cranksoftware.com/hc/en-us/articles/360040001012-Plugin-Action-Definitions" target="_blank" rel="noopener noreferrer" class="">https://support.cranksoftware.com/hc/en-us/articles/360040001012-Plugin-Action-Definitions</a>)</p>
<p>For now, we’ll focus on the most important ones.</p>
<p>Right away, one of the first things you will want to do is populate the External Buffer Name and Object Name. You must make sure these fields contain the same information as the Buffer Name and Object Path within the external render extension properties.</p>
<p>Channel Name: The channel name the new video is to be played on</p>
<p>Media Name: The name of the media to play, full path to a video file</p>
<p>Extra Data: Any extra data that should be passed to the backend (very important)</p>
<p>The Extra Data field is where you will specify the gstreamer pipeline to be used.The pipeline specified here can be similar to the pipeline used with gst-launch with only some minor changes.</p>
<p>Most notably, a pipeline that might’ve looked like:</p>
<p>gst-launch-1.0 [pipeline]</p>
<p>Translated for use by gstreamer-backend would look like:</p>
<p>pipeline: [pipeline]</p>
<p>There are a couple of extra flags that we will have to add/modify and pass into the pipeline for Storyboard usage though:</p>
<ul>
<li class="">use_external: This is only to be used if you are writing your pipeline within Storyboard, i.e. the Extra Data field. Otherwise use “-e”. If you are using an external render extension like we are in this example, you need to add the “use_external” flag at the end of your pipeline, separated by a semi-colon. “pipeline: [pipeline];use_external;”</li>
<li class="">-e: This has the same functionality as the use_external flag, the difference in when to use which one simply comes down to where you are specifying the pipeline. Use this one if you are writing your pipeline from the command line or launch script. $ENGINE/bin/gstreamer-backend -e -p [pipeline]</li>
<li class="">-p: use the following defined pipeline to play the gstreamer content.</li>
<li class="">When it comes to specifying which video you wish to play, there are a couple ways to go about it. You could hard-code the path to the video you wish to play: “pipeline<!-- -->:filesrc<!-- --> location=/path/video.mov” Or you could tell gstreamer-backend to look for whatever you’ve specified in the Media Name field of the gra.media.new.video properties: “pipeline<!-- -->:filesrc<!-- --> name=media-src”</li>
<li class="">appsink name=video-sink: In order to use gstreamer-backend with an external render extension, appsink needs to be used as opposed to specifying any other kind of video sink. The external render extension options we’ve specified (use_external/-e) work by looking for an ‘appsink’ element named ‘video-sink’ in which to render the Storyboard application to. appsink does not inherently know the format you wish to display your content with. So you will have to specify this. If I wanted my content to be 800x480 using BGRA color space I would write: video/x-raw,height=480,width=800,format=BGRA You will also need to add videoconvert and videoscale into your pipeline before adjusting the dimensions and color format, as these arguments are what allows the scaling, positioning, etc. All together this would look like: videoconvert ! videoscale ! video/x-raw,height=480,width=800,format=BGRA</li>
</ul>
<p>Here’s an example pipeline with these modifications:</p>
<p>Pipeline<!-- -->:filesrc<!-- --> name=media-src ! videoconvert ! videoscale ! video/x-raw,height=480,width=800,format=BGRA ! appsink name=video-sink; use_external;</p>
<p>At this point these are the only modifications to your pipeline that are required in order to get your media to play on an external render extension. However, it is not uncommon to experience performance issues depending on your video or platform. Using an external render extension is slower. There are a few reasons for this. For every frame of a video, a memcopy is performed in order to copy the buffer data into the external render extension. Additionally, because we are using appsink and have to scale the video, both of these operations can be an intensive process for certain platforms.</p>
<p>If your platform has hardware acceleration capabilities, then you can add these elements into your pipeline to boost performance. If you are unsure what this pipeline element is called or what other elements you might have access to, perform <code>gst-inspect-1.0</code> and it will spew out a list of elements that your platform has.</p>
<p>If after all of your tweaking for optimization, you still find performance lacking then you may have to switch implementation to take the dual framebuffer approach.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="dual-framebuffer-approach">Dual Framebuffer Approach<a href="https://docs.cranksoftware.com/knowledge-base/gstreamer-gstreamer-backend-crash-course#dual-framebuffer-approach" class="hash-link" aria-label="Direct link to Dual Framebuffer Approach" title="Direct link to Dual Framebuffer Approach" translate="no">​</a></h2>
<p>The dual frame-buffer approach is simple in principle. In general, this is the ideal approach when it comes to performance. Since we won’t be using an external render extension, what we do instead is render the Storyboard application to one framebuffer and gstreamer-backend to a separate framebuffer.</p>
<p>You are going to want to render gstreamer-backend to the framebuffer below your application’s framebuffer.</p>
<p>What this usually means is that your Storyboard application will need to have a “hole” punched out of it to allow the video to peak through.</p>
<p>Luckily the MediaSample already does this, if you simply remove the external_buf control you will see that the background has a transparent section.</p>
<p><img decoding="async" loading="lazy" alt="image2-6.png" src="https://docs.cranksoftware.com/assets/images/image2-6-b1cb753e8e26032faf5eb2efb43d712e.png" width="837" height="598" class="img_ev3q"></p>
<p>An example of “punching a hole” in your application.</p>
<p>For gstreamer, It’s actually quite simple to specify the framebuffer to be used. Video sink elements should have a parameter to specify the framebuffer. I’ll take imxipuvideosink for example:</p>
<p>“Imxipuvideosink window-width=880 window-height=520 window-x-coord=10 window-y-coord=75 framebuffer=/dev/fb0”</p>
<p>In this case, all you need to do is add the “framebuffer” element and provide the path to the desired frame buffer. Note that I am also specifying the window’s dimensions and coordinates that the video is to be displayed at.</p>
<p>It is also simple to specify the framebuffer that Storyboard is to use. The render manager has options to specify the framebuffer, but they will vary depending on your platform. Take a look at our plugin option documentation, under “render_mgr” to determine which one is right for you:</p>
<p><a href="https://support.cranksoftware.com/hc/en-us/articles/360040000752-Storyboard-Engine-Plugin-Options" target="_blank" rel="noopener noreferrer" class="">https://support.cranksoftware.com/hc/en-us/articles/360040000752-Storyboard-Engine-Plugin-Options</a></p>
<p>You will either need to use ‘display=[x]’ or ‘fb=[x]’. Again these will vary from platform to platform.One of the key differences between these two options is that ‘display’ takes an index while ‘fb’ takes a path.</p>
<p>This just means that when you launch Storyboard, simply include something like: -orender_mgr,display=1 As part of your launch command line.</p>
<p>You may find that you will have to set up your secondary framebuffer via fbset before you are able to see content that you are rendering to that specific framebuffer. For example, that may look something like:</p>
<p>fbset -fb /dev/fb1 -g 1024 600 1024 600 32</p>
<p>Simply execute this command before trying to render to that buffer.</p>
<p>If at this point you are encountering some visual artifacts like a buffer having half-transparency, then you will have to modify the way alpha is handled in that buffer. Luckily, we do have a utility called fbalpha that can handle this for you.</p>
<p>The fbalpha utility provides added control over rendering content on the secondary framebuffer.</p>
<p>Installing fbalpha</p>
<p>Download fbalpha here</p>
<p>When installing fbalpha, ensure to install it in your runtime’s ‘bin’ directory. Add a call to start up the fbalpha utility and direct it to the top-most framebuffer.</p>
<p>$ENGINE/bin/fbalpha -l1 -f /dev/fb1</p>
<p>At this point your launch script may look like:</p>
<p>$ENGINE/bin/fbalpha -l1 -f /dev/fb1</p>
<p>$ENGINE/bin/gstreamer-backend -v ...etc.</p>
<p>$ENGINE/bin/sbengine -v ...etc.</p>
<p>The next time you launch your application, the alpha issues should be resolved. The download provided also includes the source code. So if you wish to adjust the utility to suit your needs, you can.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-do-i-stream-a-video-instead-of-play-a-video-file">How do I stream a video instead of play a video file?<a href="https://docs.cranksoftware.com/knowledge-base/gstreamer-gstreamer-backend-crash-course#how-do-i-stream-a-video-instead-of-play-a-video-file" class="hash-link" aria-label="Direct link to How do I stream a video instead of play a video file?" title="Direct link to How do I stream a video instead of play a video file?" translate="no">​</a></h2>
<p>This is purely gstreamer pipeline work. If your stream uses rtsp then instead of filesrc you will use rtspsource, which ends up looking like:</p>
<p>"rtspsrc location=[address]”</p>
<p>There are many pipeline parameters unique to rtsp streaming which you can familiarize yourself with here: (<a href="https://gstreamer.freedesktop.org/documentation/rtsp/rtspsrc.html?gi-language=c" target="_blank" rel="noopener noreferrer" class="">https://gstreamer.freedesktop.org/documentation/rtsp/rtspsrc.html?gi-language=c</a>)</p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>media-video</category>
            <category>linux</category>
            <category>embedded</category>
        </item>
        <item>
            <title><![CDATA[Aligning Text to a Defined Path or Curve for Rendering]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/aligning-text-to-a-defined-path-or-curve-for-rendering</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/aligning-text-to-a-defined-path-or-curve-for-rendering</guid>
            <pubDate>Mon, 01 Nov 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how to render text along a path or curved line within Storyboard to support GUI application development destined for curved screens, such as those found on wearable devices]]></description>
            <content:encoded><![CDATA[<p>Learn how to render text along a path or curved line within Storyboard to support GUI application development destined for curved screens, such as those found on wearable devices</p>
<!-- -->
<iframe frameborder="0" allowfullscreen="" src="https://play.vidyard.com/PQq7LrpwQriZyR2sQgezRz" title="Aligning Text to a Defined Path or Curve for Rendering" width="100%" height="480px"></iframe>]]></content:encoded>
            <category>knowledge-base</category>
            <category>text</category>
            <category>animation</category>
        </item>
        <item>
            <title><![CDATA[Creating Dynamic Tables and Lists within Storyboard]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/creating-dynamic-tables-and-lists-within-storyboard</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/creating-dynamic-tables-and-lists-within-storyboard</guid>
            <pubDate>Mon, 01 Nov 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Tables and lists can now be easily created simply by clicking and selecting the content to be contained in a table or list, removing the need to manually write code to do it. Furthermore, the size of and/or content located in a table can be dynamically updated simply by pointing the table to a data source.]]></description>
            <content:encoded><![CDATA[<p>Tables and lists can now be easily created simply by clicking and selecting the content to be contained in a table or list, removing the need to manually write code to do it. Furthermore, the size of and/or content located in a table can be dynamically updated simply by pointing the table to a data source.</p>
<!-- -->
<iframe frameborder="0" allowfullscreen="" src="https://play.vidyard.com/1zkUXftGiA6KrzadEqZ1xJ" title="Creating Dynamic Tables and Lists within Storyboard" width="100%" height="480px"></iframe>]]></content:encoded>
            <category>knowledge-base</category>
            <category>lists-tables</category>
            <category>variables</category>
        </item>
        <item>
            <title><![CDATA[Customizing the GUI App Screen Development Workflow Layout]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/customizing-the-gui-app-screen-development-workflow-layout</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/customizing-the-gui-app-screen-development-workflow-layout</guid>
            <pubDate>Mon, 01 Nov 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[As your embedded application grows sometimes all those screens make it difficult to focus on a certain aspect of the GUI application. With Storyboard, you can customize the screens being shown and the actual layout of screens being developed to align to match how the screens are transitioned to within the final GUI application for a more intuitive development workflow.]]></description>
            <content:encoded><![CDATA[<p>As your embedded application grows sometimes all those screens make it difficult to focus on a certain aspect of the GUI application. With Storyboard, you can customize the screens being shown and the actual layout of screens being developed to align to match how the screens are transitioned to within the final GUI application for a more intuitive development workflow.</p>
<!-- -->
<iframe frameborder="0" allowfullscreen="" src="https://play.vidyard.com/usVd8BRpcCmYqWx2HLjq5o" title="Customizing the GUI App Screen Development Workflow Layout" width="100%" height="480px"></iframe>]]></content:encoded>
            <category>knowledge-base</category>
            <category>workflow</category>
        </item>
        <item>
            <title><![CDATA[Modifying Custom-built Components]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/modifying-custom-built-components</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/modifying-custom-built-components</guid>
            <pubDate>Mon, 01 Nov 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Your custom-built components can now be modified after-the-fact improving their reusability across different GUI applications and helping speed up the GUI development process.]]></description>
            <content:encoded><![CDATA[<p>Your custom-built components can now be modified after-the-fact improving their reusability across different GUI applications and helping speed up the GUI development process.</p>
<!-- -->
<iframe frameborder="0" allowfullscreen="" src="https://play.vidyard.com/9Bv1TiYtegiUUs8SrdHt7a" title="Modifying Custom-built Components" width="100%" height="480px"></iframe>]]></content:encoded>
            <category>knowledge-base</category>
            <category>components</category>
            <category>workflow</category>
        </item>
        <item>
            <title><![CDATA[Optimized GUI Application Exporting Workflow for MCU Deployments]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/optimized-gui-application-exporting-workflow-for-mcu-deployments</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/optimized-gui-application-exporting-workflow-for-mcu-deployments</guid>
            <pubDate>Mon, 01 Nov 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Learn how the workflow for exporting Storyboard-built GUI applications to MCU hardware has been simplified by combining what used to be separate steps into a more unified experience for exporting to MCU-based hardware.]]></description>
            <content:encoded><![CDATA[<p>Learn how the workflow for exporting Storyboard-built GUI applications to MCU hardware has been simplified by combining what used to be separate steps into a more unified experience for exporting to MCU-based hardware.</p>
<!-- -->
<iframe frameborder="0" allowfullscreen="" src="https://play.vidyard.com/6FhoTo2TgAxqG4woNjVmXs" title="Optimized GUI Application Exporting Workflow for MCU Deployments" width="100%" height="480px"></iframe>]]></content:encoded>
            <category>knowledge-base</category>
            <category>export</category>
            <category>mcu</category>
            <category>embedded</category>
            <category>workflow</category>
        </item>
        <item>
            <title><![CDATA[Importing Sample Projects from Crank's Public SVN]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/importing-sample-projects-from-crank-s-public-svn</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/importing-sample-projects-from-crank-s-public-svn</guid>
            <pubDate>Mon, 17 May 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[Before we are able to start using the demos from the public Crank code repository we will first need to install a SVN client. To do so in Storyboard Designer we go to Help > Install New Software.]]></description>
            <content:encoded><![CDATA[<p>Before we are able to start using the demos from the public Crank code repository we will first need to install a SVN client. To do so in Storyboard Designer we go to Help &gt; Install New Software.</p>
<!-- -->
<p><img decoding="async" loading="lazy" alt="help_install_new_software.png" src="https://docs.cranksoftware.com/assets/images/help_install_new_software-bca4f59a6e9809cc099e994f3a894b61.png" width="700" height="350" class="img_ev3q"></p>
<p>We use Subclipse as our SVN client. Click on Add and enter a name (we use "Subclipse" but you can use whatever name you want). For Location, enter <a href="https://subclipse.github.io/updates" target="_blank" rel="noopener noreferrer" class="">https://subclipse.github.io/updates</a> to get the latest version and click OK.</p>
<p><img decoding="async" loading="lazy" alt="add_repo.png" src="https://docs.cranksoftware.com/assets/images/add_repo-ebb5360b715b8e4aaf47a2898172ea7f.png" width="475" height="177" class="img_ev3q"></p>
<p>There is no need for Maven so you can uncheck the option and click Next.</p>
<p><img decoding="async" loading="lazy" alt="list_of_software.png" src="https://docs.cranksoftware.com/assets/images/list_of_software-a594b89866d0850db7b6268fbab85bd7.png" width="700" height="548" class="img_ev3q"></p>
<p>Review the details and click Next.</p>
<p><img decoding="async" loading="lazy" alt="details_of_installed_software.png" src="https://docs.cranksoftware.com/assets/images/details_of_installed_software-d2b0651504fb1d974032e9d8f811edad.png" width="700" height="357" class="img_ev3q"></p>
<p>Accept the license agreements and click Finish.</p>
<p><img decoding="async" loading="lazy" alt="accept_license.png" src="https://docs.cranksoftware.com/assets/images/accept_license-0e03ff62266fbc1ae5faf4b74a1384fa.png" width="700" height="357" class="img_ev3q"></p>
<p>Review the warning about unsigned software and click OK.</p>
<p><img decoding="async" loading="lazy" alt="security.png" src="data:image/png;base64,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Kja9dHukNonWN2WUdL7ke13AG6LBEuohl0m4fH24mMKIWnI8dHvFX0o8WMDl5dw9FF8IvM8jhvEa8viM9HrjJdrNG/eBIo2t0OXRo7dDssu0GxW5/LmRkuZR29yUOJNdij31klo2/ezZd7BYc/Dc/ikPOf4Jjydr9Bse3rs2/Tx48ADe4iWDDrh3z8UDORYyMzLwkIHvabCAJXAHAGuGE/vQp3A/Wcdd1wxK//3TzJ07Jyn7NBmksgSuiMRGx6fcz778+nvJLUI27HDw6KOfImfRZiy8CFiwYMVitWCxWMiemU3WjGyyZ+aQmZmJxYpvGcACHl8y4XY5cYw4wQNDg3cZdgzjdrvxeDx43B48HjcPW6w4PVb6b96iu7uL5cuXJ3PX0sdXAveNhnQHIiIT6PgUSW5ikTUjk3cv/pahoWFGZ10sYLFYsFjBarFiIcP7WKaFTP+1CIs3ufDAaHbhwoPH5QGPBw9u3G4PHg/e5MLj8b3SzZw5s3nmmWeSuVtpkr4SuCISjY5PEb+kToUAvPvuu0lbdzif+cxnUr5NERGR+13Sp0JAb/IiIiL3k5RXNxUREZHp6/5MLFJSdjfE79nHsj1TYgwsBz05f1NeRESml/szsUio7G6yStwmoTRwWzfFvt+UP1Q1+X9URUREpr6k32MhaVReQ43vn0uK9fu+IiKSfPfnFQvDZXdNKB0dausTlo9WGpg4yg2P2yDLj1TyRkKXP0RERKK7TxOLYGHK7iZcOjq0ictHKw0cT7lhH1sTa/cUc+FkcHl1ERER8ymxAMKW3U24dLRJyydSbvhyJ+dKCpVUiIhISiixiMTU0tFxLp+o8oZJWVZXRESmJyUWBsRfOjqEeEobJ1Ju2NbEWn3VVEREUkSJRSQJl44OIezykcsXx13mXUREJIWSXitERERE7g9ffv09XbEQERER8yixEBEREdMosRARERHTKLEQERER0yixEBEREdMosRARERHTKLEQERER0yixEBEREdMosRARERHTKLEQERER0yixEBEREdMosRARERHTKLEQERER0yixEBEREdMosRARERHTKLEQERER0yixEBEREdMosRARERHTKLEQERER0yixEBEREdMosRARERHTKLEQERER0yixEBEREdMosRARERHTZKY7ABEx5q233kp3CPeF5557LuLzud+9kqJI7m/2P3sk4vM6HlIj2vEQihILkSlkxYoV6Q5hWjt//ny6Q5AY6HhIrniPByUWIiJx6Kt5IN0hTEt5TYPpDkESpHssRERExDRKLERERMQ0SixERETENEosRERExDRKLEQkcafqyMvLG/3buL838Enq8jYy9lAv+zfmkVd3KuVhTl2+NvO38cb99KZs28H9N92coi5g7E4cvwnq3c/G0fab7m3ppcRCRBJzqo68qk52nu6jr6+Pvr7TvHBsddiT86m61Rx74TR99WtSG+dU1bufjXmr6XrZ37599H0P3lZeZqLSgPHbTPGu1QYSX4NJQv5WDvcdZmu+OZFOBUosRCQBvex/7QBbmgNPnPls/d5O2LWX4FNz7/6NVNHM4fvpLJuQXvZ/cxfFzX2My8Pyt7JVeVmSrKH+9E5KD5ycMH5Tq5f9G+sixBDt+fRRYiEi8et9m2PtW1gb/CaX/yVeKO2kpzfgsbfrWH3sBU7rSoVx4do3wKm6gMv4o5+yfZ+m99eFubw/fmpl9GW9+9k4OiUwOd+0UiL/S7xQeoCTYdvlFHV5VRygnV2rx9o9Yl/0Bm8kTB+MBcHWw2s5mRdu2jDa8+mjxEJEElNaxOKQT7TT1TP27127DrDl5a3kpyqu6SJs+3qtqR+7hL/lwGsBb2Dt7Opa632ueQvto1eQetm/0Tcd5Zta8eZ6p6hbfYwX/FMCzfDadL8ZwJBQ7bKY+r5mtvinUHzJcvi+CLXavewqbg7qg2BrqO/ro2/tyTDJR7Tn00OJhYgkpr2LnpBPlFK0eOzfO5t30lk1/W9cM13Y9vUZvXG2igPjnihlZ7Xv3WrNWrbgu4LkuwrycvB0VG8Pnf5P4Hl55FUdoL0r4panOd/4jaVdwvZFCIuLKD1QZexG0TXV7CyFA1VhriJFez7FlFiISPyCLxn79b7NsfZiFucHPLZ4K4ebi9m1enKc/KaEcO3r17ufjVXQ7Ltpdmdpohvc4luX7+9+nbaaMH4NtEusfZG/lcN9fXyPb0a42uCfLvkmfK+Pvr561sT0fHoosRCRBOSz9eUtQZ+UTlG3ehfsrJ54kltTz+mdnVSl9OuSU5m/fYPeeHr3s/8U0NNFu3+qpPdtjrUbWaU3WZkwzZG/mGJCPH7f8Y7f4mbfm7TRdomnL4D8rYc5vbOUznE3JIE3afAnDKG+VRLt+fRRETIRScyaevqavZeA/bY093E4zEen/K2Hae7KY/VGOH1Y91xEtaaevtNFbFydx2gLl+7k9GGAana+tprVebugdAtbDF2xyGfr4Wa68laTt8v7yJbmPurXeL8NsXH12ONsaaav3uT9mZS8Ux3e3fbeNzH2Rh2uXdawdksVVavz2LWlmb76GPviVB15Vf4Jky009+UHLZDP1sOHI6wg2vPpY6nY/Y7nx19/Kt1xiEgUb731lspEJ9n58+d57rnnIi6T+90rgKqbJou/uqn9zx6JuJyOh+QzcjwE+/Lr72kqRERERMyjxEJERERMo8RCRERETKPEQkREREyjb4WIiMTBf5OhiIynxEJkCjl//ny6QxCZNHQ8TE5KLESmiFi/9iXJEe1rkJIaOh4mL91jISIiIqZRYiEiIiKmUWIhIiIiplFiISIiIqZRYiEiIiKmUWIhIiIiplFiISIiIqZRYiEiIiKmUWIhIiIiplFiISIiIqZRYiEiIiKmUWIhIiIiplFiISIiIqbJBPjy6++lOw4RERGZBv4/gEgBaqTKoesAAAAASUVORK5CYII=" width="534" height="162" class="img_ev3q"></p>
<p>Restart Storyboard Designer to complete the Subclipse installation.</p>
<p><img decoding="async" loading="lazy" alt="restart.png" src="data:image/png;base64,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" width="518" height="141" class="img_ev3q"></p>
<p>Now that Subclipse is installed we need to go to that perspective to add a repository.</p>
<p><img decoding="async" loading="lazy" alt="perspective.png" src="https://docs.cranksoftware.com/assets/images/perspective-9e57186a5b9908f20765d63c03134f7d.png" width="700" height="262" class="img_ev3q"></p>
<p>Select SVN Repository Exploring and click OK.</p>
<p><img decoding="async" loading="lazy" alt="svn_exploring.png" src="https://docs.cranksoftware.com/assets/images/svn_exploring-409d312d61c2e3180f55c0de9844ec4e.png" width="345" height="430" class="img_ev3q"></p>
<p>To add a repository, right-click the SVN perspective window and select New &gt; Repository Location.</p>
<p><img decoding="async" loading="lazy" alt="new_repo.png" src="https://docs.cranksoftware.com/assets/images/new_repo-70cfe96a2d557f5521dcacc7fca0474f.png" width="400" height="199" class="img_ev3q"></p>
<p>Enter the Crank public code repository URL <a href="http://svn.cranksoftware.com/repo/storyboard/public" target="_blank" rel="noopener noreferrer" class="">http://svn.cranksoftware.com/repo/storyboard/public</a> and click Finish. When you are prompted for login credentials use:</p>
<p>Username: storyboard</p>
<p>Password: crankrocks</p>
<p><img decoding="async" loading="lazy" alt="crank_repo.png" src="https://docs.cranksoftware.com/assets/images/crank_repo-9f4e057e79718744496efa5ddce39e58.png" width="511" height="388" class="img_ev3q"></p>
<p>You are now connected to the Crank Public Repository. By expanding the directories you can see the various demos available for checkout.</p>
<p><img decoding="async" loading="lazy" alt="crank_repo_list.png" src="https://docs.cranksoftware.com/assets/images/crank_repo_list-d7d9ebe84238314c538ed1636bf73245.png" width="383" height="264" class="img_ev3q"></p>
<p>To checkout from the repository, right-click on the demo and select Checkout. After checkout in complete, click the Storyboard Development tab to see the application in your workspace.</p>
<p><img decoding="async" loading="lazy" alt="check_out.png" src="https://docs.cranksoftware.com/assets/images/check_out-134be058309dd443ee234ce8c8ca06fc.png" width="352" height="477" class="img_ev3q"></p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>samples</category>
            <category>workflow</category>
        </item>
        <item>
            <title><![CDATA[Developing with Storyboard and Linux on the STM32MP15x Discovery Kit]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/developing-with-storyboard-and-linux-on-the-stm32mp15x-discovery-kit</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/developing-with-storyboard-and-linux-on-the-stm32mp15x-discovery-kit</guid>
            <pubDate>Mon, 15 Mar 2021 00:00:00 GMT</pubDate>
            <description><![CDATA[The STM32MP157C-DK2 is a great Linux capable application processor and development kit which is a perfect embedded target host for Storyboard HMI-based products.]]></description>
            <content:encoded><![CDATA[<p>The STM32MP157C-DK2 is a great Linux capable application processor and development kit which is a perfect embedded target host for Storyboard HMI-based products.</p>
<!-- -->
<p>The Discovery Kit features an 800x480 capacitive touch screen to complement the powerful STM32MP157 Arm®-based dual Cortex®-A7 32 bits + Cortex®-M4 32 bits MPU.</p>
<p><a href="https://www.st.com/en/evaluation-tools/stm32mp157c-dk2.html" target="_blank" rel="noopener noreferrer" class="">https://www.st.com/en/evaluation-tools/stm32mp157c-dk2.html</a></p>
<p><img decoding="async" loading="lazy" alt="image6-3.png" src="https://docs.cranksoftware.com/assets/images/image6-3-afed73a39ecc28cfcb7d4754a711b2c9.png" width="426" height="284" class="img_ev3q"></p>
<p>The instructions for unboxing and creating the SD Card Image using STM instructions for the Starter Package can be found on the ST Microelectronics website here:</p>
<p><a href="https://wiki.st.com/stm32mpu/wiki/STM32MP15_Discovery_kits_-_Starter_Package" target="_blank" rel="noopener noreferrer" class="">https://wiki.st.com/stm32mpu/wiki/STM32MP15_Discovery_kits_-_Starter_Package</a></p>
<p>I used a Linux virtual machine for this but any supported Windows or Linux desktop environment should enable you to follow the step-by-step guide above and use the STM32CubeProgrammer tool to commission a micro SD card image.</p>
<p>This bootable SD Card image should launch into the ST Microelectronics out-of-box demo and run the base Wayland-based graphical environment.</p>
<blockquote>
</blockquote>
<p>The initial boot of the board may trigger some maintenance scripts and processes which will automatically redimension and expand the filesystem partitions one by one. This takes some minutes so be patient!</p>
<p>Once booted there is a serial console device created and available if you connect a network cable. On boot, if this is attached the board should get a DHCP address and register a default hostname.</p>
<p>You can SSH into the board (I prefer TeraTerm on Windows) using the hostname stm32mp1 and user root (blank password by default).</p>
<p><img decoding="async" loading="lazy" alt="image1-7.png" src="https://docs.cranksoftware.com/assets/images/image1-7-ae1f73cb216ad13b0b31a9365c8607be.png" width="1051" height="606" class="img_ev3q"></p>
<p>With the network link established we can take a first look around and begin to commission the board with Storyboard….</p>
<p>The OpenSTLinux system base image from the STM website resources used for this application note is en.FLASH-stm32mp1-openstlinux-5-4-dunfell-mp1-20-11-12.tar.xz which was the latest available at the time of writing.</p>
<p>root@stm32mp1:~# uname -a</p>
<p>Linux stm32mp1 5.4.56 #1 SMP PREEMPT Wed Aug 5 07:59:52 UTC 2020 armv7l armv7l armv7l GNU/Linux</p>
<p>The filesystem is created by the STM32CubeProgrammer tool such that the ‘user’ applications are located in a separate partition on the SD Card. This is mapped to the /usr/local tree in the root filesystem.</p>
<p><img decoding="async" loading="lazy" alt="image3-7.png" src="https://docs.cranksoftware.com/assets/images/image3-7-555dcdd4939d75ee07b31ccb540a2465.png" width="1431" height="765" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-1-copy-across-the-storyboard-runtime-to-the-target-board">Step 1: Copy across the Storyboard runtime (to the target board)<a href="https://docs.cranksoftware.com/knowledge-base/developing-with-storyboard-and-linux-on-the-stm32mp15x-discovery-kit#step-1-copy-across-the-storyboard-runtime-to-the-target-board" class="hash-link" aria-label="Direct link to Step 1: Copy across the Storyboard runtime (to the target board)" title="Direct link to Step 1: Copy across the Storyboard runtime (to the target board)" translate="no">​</a></h2>
<p>Create a Crank folder under /usr/local along with a runtimes folder and apps folder where we will deploy our Storyboard applications:</p>
<p>root@stm32mp1:~# mkdir /usr/local/Crank</p>
<p>root@stm32mp1:~# mkdir /usr/local/Crank/runtimes</p>
<p>root@stm32mp1:~# mkdir /usr/local/Crank/apps</p>
<p>The Storyboard runtime for the STM32MP1 boards is linux-stmA5-armle-opengles_2.0-wayland-obj and located under the installation folder for Storyboard on the desktop.</p>
<p>Copy this folder and contents to /usr/local/Crank/runtimes on the target filesystem which can simply be done using a utility such as WinSCP for Windows or FileZilla.</p>
<p>And once copied grant the runtimes folder contents execute permissions (recursively) to keep it simple..</p>
<p>root@stm32mp1:~# chmod -R 0755 /usr/local/Crank/runtimes</p>
<p>root@stm32mp1:~# ls -la /usr/local/Crank</p>
<p>total 4</p>
<p>drwxr-xr-x 4 root root 1024 Feb 7 16:23 .</p>
<p>drwxr-xr-x 8 root root 1024 Feb 7 16:22 ..</p>
<p>drwxr-xr-x 7 root root 1024 Feb 7 16:23 apps</p>
<p>drwxr-xr-x 3 root root 1024 Feb 7 16:22 runtimes</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-2-create-a-launcher-script-for-storyboard">Step 2: Create a launcher script for Storyboard<a href="https://docs.cranksoftware.com/knowledge-base/developing-with-storyboard-and-linux-on-the-stm32mp15x-discovery-kit#step-2-create-a-launcher-script-for-storyboard" class="hash-link" aria-label="Direct link to Step 2: Create a launcher script for Storyboard" title="Direct link to Step 2: Create a launcher script for Storyboard" translate="no">​</a></h2>
<p>In the root of the Crank folder (/usr/local/Crank) on the target, we can create a script that sets up the execution environment for the Storyboard runtime and options for the application model we want to run at startup.</p>
<p>This is where we can use your own Storyboard application or perhaps one of the samples which ship with Storyboard. As the STM32MP157C-DK2 development board that I am using has an 800x480 LCD panel with touch screen and the processor variant features a GPU supporting OpenGL then we can use the splendid OpenGL3DModel sample which ships with Storyboard:</p>
<p><img decoding="async" loading="lazy" alt="image4-7.png" src="https://docs.cranksoftware.com/assets/images/image4-7-953f54bfe6db9faf8bea64daa3b4fcd5.png" width="1999" height="1506" class="img_ev3q"></p>
<p>In this sample, we deploy the Storyboard application model and all associated assets have been exported as a GAPP file (OpenGL3DModel.gapp) and deployed to a folder OpenGL3DModel under the location /usr/local/Crank/apps</p>
<p><img decoding="async" loading="lazy" alt="image5-4.png" src="https://docs.cranksoftware.com/assets/images/image5-4-1ea3c829f0dc8eacfb37ff564941471c.png" width="1775" height="1443" class="img_ev3q"></p>
<p>storyboard_launcher.sh:</p>
<p>#!/bin/sh</p>
<p>\export RUNTIME_ROOT=/usr/local/Crank/runtimes/linux-stmA5-armle-opengles_2.0-wayland-obj</p>
<p>\export SB_PLUGINS=$RUNTIME_ROOT/plugins</p>
<p>\export LD_LIBRARY_PATH=$RUNTIME_ROOT/lib</p>
<p>\export SB_DEMO=1</p>
<p>\export SB_APP=/usr/local/Crank/apps/OpenGL3DModel/OpenGL3DModel.gapp</p>
<p>killall sbengine</p>
<p>$RUNTIME_ROOT/bin/sbengine -vvvv -orender_mgr,multisample=0 $SB_APP</p>
<blockquote>
</blockquote>
<p>You can see in the command line options for sbengine we have added the -vvvv to the config. This will emit more verbose trace output to the Linux console and help us in diagnosing any issues. You can remove this trace verbosity option to minimize runtime overhead once you have everything set up and working.</p>
<p>Once the launcher script has been created we will again grant it execute permissions and we should be able to launch and run the application.</p>
<p>root@stm32mp1:~# chmod 0755 /usr/local/Crank/storyboard_launcher.sh</p>
<p>root@stm32mp1:~# ls -la /usr/local/Crank</p>
<p>total 5</p>
<p>drwxr-xr-x 4 root root 1024 Feb 7 16:23 .</p>
<p>drwxr-xr-x 8 root root 1024 Feb 7 16:22 ..</p>
<p>drwxr-xr-x 7 root root 1024 Feb 7 16:23 apps</p>
<p>drwxr-xr-x 3 root root 1024 Feb 7 16:22 runtimes</p>
<p>-rwxr-xr-x 1 root root 328 Dec 18 2020 storyboard_launcher.sh</p>
<p>root@stm32mp1:~# cd /usr/local/Crank/</p>
<p>root@stm32mp1:/usr/local/Crank# ./storyboard_launcher.sh</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-3-setup-the-board-to-auto-start-the-storyboard-application">Step 3: Setup the board to auto-start the Storyboard application<a href="https://docs.cranksoftware.com/knowledge-base/developing-with-storyboard-and-linux-on-the-stm32mp15x-discovery-kit#step-3-setup-the-board-to-auto-start-the-storyboard-application" class="hash-link" aria-label="Direct link to Step 3: Setup the board to auto-start the Storyboard application" title="Direct link to Step 3: Setup the board to auto-start the Storyboard application" translate="no">​</a></h2>
<p>In vanilla configuration, the filesystem is set up to load and run a demo application to show a simple desktop window and a launcher menu for various utilities and sample applications bundled with the board.</p>
<p>The default demo is built upon the Wayland display manager using a Weston shell and its behaviour and appearance are configured through the file /etc/xdg/weston/weston.ini.</p>
<p>We want to disable the launcher and also hide the bottom control panel so that Storyboard has the full-screen surface to work with.</p>
<p>weston.ini:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">[core]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#modules=cms-colord.so</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#xwayland=true</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">shell=desktop-shell.so</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">backend=drm-backend.so</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">idle-time=0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">repaint-window=100</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">require-input=false</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">[keyboard]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">keymap_layout=us</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">[shell]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">### Crank - disable background</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#background-image=/usr/share/weston/backgrounds/ST13028_Linux_picto_11_1366x768.png</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">background-color=0xff002244</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">background-type=scale-crop</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">clock-format=minutes</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">panel-color=0x90a1a1a1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">### Crank - disable panel</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#panel-position=bottom</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">panel-position=none</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">locking=false</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">animation=none</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">startup-animation=none</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">close-animation=none</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">focus-animation=none</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">binding-modifier=ctrl</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#num-workspaces=6</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#cursor-theme=whiteglass</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#cursor-size=24</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">### Crank - disable launcher</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#[launcher]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#icon=/usr/share/weston/icon/ST13345_Products_light_blue_24x24.png</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#path=/usr/local/demo/demo_launcher.py</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">[launcher]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">icon=/usr/share/weston/icon/space.png</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">path=/bin/true</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">[input-method]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">path=/usr/libexec/weston-keyboard</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># HDMI connector</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># If the hdmi cable is plugged, weston uses the hdmi output (else dsi output).</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Use the command "systemctl restart weston@root" after pluging/unpluging the hdmi cable.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">[output]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">name=HDMI-A-1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">mode=1280x720</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># DSI connector</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">[output]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">name=DSI-1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">mode=preferred</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">transform=270</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">:</span><br></div></code></pre></div></div>
<p>Finally, we can modify the startup script to run our storyboard_launcher.sh script instead of the existing Weston demo_launcher.py script.</p>
<p>The configuration is found in the file:</p>
<p>/usr/local/weston-start-at-startup/start_up_demo_launcher.sh</p>
<p>start_up_demo_launcher.sh:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">#!/bin/sh</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># found card</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">cards=`pacmd list-cards |  egrep -i 'alsa.card_name' | sed 's/ //g'| sed 's/alsa.card_name=\"//g'| sed 's/\"//g'`</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">index=0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">for i in $cards;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">do</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	found=`echo $i | grep -n STM32MP | wc -l`</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	if [ $found -eq 0  ];</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	then</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		su.util-linux -l root -c 'pacmd set-card-profile $index output:analog-stereo'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	fi</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	index=$((index+1))</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">done</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#/usr/local/demo/demo_launcher.py</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/usr/local/Crank/storyboard_launcher.sh</span><br></div></code></pre></div></div>
<p>Upon the next reboot, the board should automatically launch the Storyboard application specified in the storyboard_launcher.sh script file.</p>
<p>root@stm32mp1:~# reboot now</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="step-4-exporting-from-designer-and-running-on-the-target-using-scp">Step 4: Exporting from Designer and running on the target using SCP<a href="https://docs.cranksoftware.com/knowledge-base/developing-with-storyboard-and-linux-on-the-stm32mp15x-discovery-kit#step-4-exporting-from-designer-and-running-on-the-target-using-scp" class="hash-link" aria-label="Direct link to Step 4: Exporting from Designer and running on the target using SCP" title="Direct link to Step 4: Exporting from Designer and running on the target using SCP" translate="no">​</a></h2>
<p>Once the standalone launcher is up and running we can now progress to configuring your development environment so that Storyboard GUI application changes can be deployed and run on the target board directly from Storyboard Designer using the SCP secure file transfer protocol over the SSH connection.</p>
<p>Create a new modified version of the launcher script storyboard_scp.sh which configures the interactive user environment for our default user (root) and which instead accepts as an argument ($1) the application to run rather than a hard-coded path.</p>
<p>This argument is passed by Storyboard automatically and now enables us to deploy and run this script created in the root of the Crank folder (/usr/local/Crank) .</p>
<p>storyboard_scp.sh:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">#!/bin/sh</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># XDG_RUNTIME_DIR env var must be set so that the Wayland graphical </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># sub-system can locate the launching (root) user's specific data and </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># config plus a place to store temporary files</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">export XDG_RUNTIME_DIR=/run/user/0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">export RUNTIME_ROOT=/usr/local/Crank/runtimes/linux-stmA5-armle-opengles_2.0-wayland-obj</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">export SB_PLUGINS=$RUNTIME_ROOT/plugins</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">export LD_LIBRARY_PATH=$RUNTIME_ROOT/lib</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">export SB_DEMO=1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">killall sbengine</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">$RUNTIME_ROOT/bin/sbengine -vvvv -orender_mgr,multisample=0 $1</span><br></div></code></pre></div></div>
<p>Again this script needs first to be granted execute permissions before it can run and we can test that it functions correctly by manually (re-)launching the 3D Demo:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">root@stm32mp1:~# chmod 0755 /usr/local/Crank/storyboard_scp.sh</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">root@stm32mp1:~# ls -la /usr/local/Crank</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">total 6</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">drwxr-xr-x 4 root root 1024 Feb  7 16:23 .</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">drwxr-xr-x 8 root root 1024 Feb  7 16:22 ..</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">drwxr-xr-x 7 root root 1024 Feb  7 16:23 apps</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">drwxr-xr-x 3 root root 1024 Feb  7 16:22 runtimes</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-rwxr-xr-x 1 root root  328 Dec 18  2020 storyboard_launcher.sh</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">-rwxr-xr-x 1 root root  504 Dec 18  2020 storyboard_scp.sh</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">root@stm32mp1:~# cd /usr/local/Crank/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">root@stm32mp1:/usr/local/Crank# ./storyboard_scp.sh apps/OpenGL3DModel/OpenGL3DModel.gapp</span><br></div></code></pre></div></div>
<p>With a working script file we are now able to deploy the model and assets and Run the script directly from Storyboard using the Export Configuration dialog and the Post Transfer Script option:</p>
<p><img decoding="async" loading="lazy" alt="image2-7.png" src="https://docs.cranksoftware.com/assets/images/image2-7-7227ba6e227436d2ca1d8aa280665202.png" width="1116" height="850" class="img_ev3q"></p>
<p>The HMI design can be iterated then the application can be modified and run in seconds!</p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>linux</category>
            <category>embedded</category>
            <category>deployment</category>
            <category>hardware</category>
        </item>
        <item>
            <title><![CDATA[Animating an Arc Tutorial]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/animating-an-arc-tutorial</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/animating-an-arc-tutorial</guid>
            <pubDate>Thu, 14 May 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Arcs can be used for many purposes, but are often used to show some form of progress. This tutorial will walk you through how to animate an arc to create a loading wheel, using two different methods. The first will use the start and end angles that define the arc to create a spinning wheel where the tail chases the head. The second will use the arc’s rotation to create a pinwheel-like animation.]]></description>
            <content:encoded><![CDATA[<p>Arcs can be used for many purposes, but are often used to show some form of progress. This tutorial will walk you through how to animate an arc to create a loading wheel, using two different methods. The first will use the start and end angles that define the arc to create a spinning wheel where the tail chases the head. The second will use the arc’s rotation to create a pinwheel-like animation.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="create-an-arc-control">Create an Arc Control<a href="https://docs.cranksoftware.com/knowledge-base/animating-an-arc-tutorial#create-an-arc-control" class="hash-link" aria-label="Direct link to Create an Arc Control" title="Direct link to Create an Arc Control" translate="no">​</a></h2>
<p>Create a new project and add an arc control to a layer. You can do this by right-clicking on the layer and selecting Add &gt; Control, or by dragging in an arc control from the Palette.</p>
<p><img decoding="async" loading="lazy" alt="image5.png" src="https://docs.cranksoftware.com/assets/images/image5-a2846a383c8c260c9cb002e523d4c70b.png" width="1999" height="723" class="img_ev3q"></p>
<p>Change the colour of the arc to whatever you desire, and change the style from fill to Line. You can also adjust the stroke if you would like something thicker than the default.</p>
<p><img decoding="async" loading="lazy" alt="image6.png" src="https://docs.cranksoftware.com/assets/images/image6-903c62791b3485e5fb83c57bb63316b0.png" width="1999" height="731" class="img_ev3q"></p>
<p>At this point, you will want to decide where the arc will start. By default, the Start Angle is 0 degrees, which is the point on the right side of the circle, as seen in the above screenshot. If you would like it to start at a different angle, set the Start Angle to a different value and remember that you will now have to change the End Angle relative to that.</p>
<p>Set all angles to 0 so they do not show before the animation begins.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="create-the-animation">Create the Animation<a href="https://docs.cranksoftware.com/knowledge-base/animating-an-arc-tutorial#create-the-animation" class="hash-link" aria-label="Direct link to Create the Animation" title="Direct link to Create the Animation" translate="no">​</a></h2>
<p>Create a new animation and create an animation step for the end angle. Set it to change from a specific value of 0 (or whatever start angle you want) to 360 degrees relative to the start value.</p>
<p><img decoding="async" loading="lazy" alt="image7.png" src="https://docs.cranksoftware.com/assets/images/image7-933228643029eb6800e5c98305ac8fe6.png" width="1054" height="1612" class="img_ev3q"></p>
<p>You can keep the Rate linear if you would like, or use some easing to make the wheel seem smoother.</p>
<p><img decoding="async" loading="lazy" alt="image3.png" src="https://docs.cranksoftware.com/assets/images/image3-2ae0dc3d07877d2de3089f8d968971ca.png" width="722" height="426" class="img_ev3q"></p>
<p>Preview the animation to see if you are happy with how this simple animation looks. If you would like to add a little more flair, you can have the tail of the loading wheel trail behind and catch up with the head.</p>
<p>Add another animation step, this time for the Start Angle. Set it to change from the current value to a specific value of 0. Set the offset and duration both to 0, as well. This will ensure the start angle is reset every time the animation replays, since the animation will likely replay a few times while your app is in a loading state.</p>
<p>Create another animation step for the Start Angle, setting it to change from 0 to 360, mimicking the End Angle animation. Set it to the same duration as the End Angle animation, but stagger it so that it begins approximately 75% of the way through the End Angle animation. This way, it will begin just as the end angle is reaching its destination.</p>
<p><img decoding="async" loading="lazy" alt="image4.png" src="https://docs.cranksoftware.com/assets/images/image4-672afc4aa02d03db9e4da2151f77ca2c.png" width="1999" height="1170" class="img_ev3q"></p>
<p>Preview the animation to see if this looks good to you. You can play with the easing graph or staggering of the animation steps if you would like something different.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="alternative-animation-using-rotation-angle">Alternative Animation Using Rotation Angle<a href="https://docs.cranksoftware.com/knowledge-base/animating-an-arc-tutorial#alternative-animation-using-rotation-angle" class="hash-link" aria-label="Direct link to Alternative Animation Using Rotation Angle" title="Direct link to Alternative Animation Using Rotation Angle" translate="no">​</a></h2>
<p>If you would prefer a loading wheel that is more like a pinwheel, you can use the Rotation angle for the animation instead of the End and Start Angles.</p>
<p>Change the arc style to a fill and set the End Angle to 45 degrees from the Start Angle.</p>
<p><img decoding="async" loading="lazy" alt="image8.png" src="https://docs.cranksoftware.com/assets/images/image8-82962531ffa1dbdc5bc89beab7b51eb3.png" width="1999" height="946" class="img_ev3q"></p>
<p>Now remove the animation steps from the loading_wheel animation and create a new one for the variable that is tied to the arc’s Rotation. Set it to go from 0 to 360. In this case, you probably want to use a linear Rate. Test the animation and alter the progression values as desired.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="playing-and-repeating-the-animation">Playing and Repeating the Animation<a href="https://docs.cranksoftware.com/knowledge-base/animating-an-arc-tutorial#playing-and-repeating-the-animation" class="hash-link" aria-label="Direct link to Playing and Repeating the Animation" title="Direct link to Playing and Repeating the Animation" translate="no">​</a></h2>
<p>The loading animation will be triggered in very specific situations, but for the purpose of this tutorial, it will be triggered on screenshow. Right click on the Screen in the Application Model and create a new action that is triggered on screenshow and triggers the start of the loading_wheel animation.</p>
<p><img decoding="async" loading="lazy" alt="image1.png" src="https://docs.cranksoftware.com/assets/images/image1-c4e36fb8af81929cb918a52b1e33276d.png" width="1999" height="995" class="img_ev3q"></p>
<p>The important aspect of a loading wheel animation is that it should repeat continuously until it is stopped by another event in the system. To achieve this, create another event on the Screen, this time triggered by the completion of the loading_wheel animation and triggering the start of the same animation.</p>
<p><img decoding="async" loading="lazy" alt="image2.png" src="https://docs.cranksoftware.com/assets/images/image2-fb5a79c04630691519cb593e7fe12c63.png" width="1999" height="1000" class="img_ev3q"></p>
<p>Now if you start the app, the animation should begin playing and repeat until you send a gra.animate.stop event. Because this last event is on the Screen, it will not be triggered if the animation completes when the user moves to a different screen. This helps to clean up the lifecycle of the animation, so that you don’t have animations running in the background causing performance issues. Keep in mind that if you put this animation complete event at the app level (the top of the Application Model), the animation will continue to be re-triggered continuously, regardless of what screen the user is on, until you send it a stop event.</p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>animation</category>
            <category>components</category>
        </item>
        <item>
            <title><![CDATA[How to Create a Gauge Using Arcs]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/how-to-create-a-gauge-using-arcs</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/how-to-create-a-gauge-using-arcs</guid>
            <pubDate>Wed, 13 May 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Arcs are often used for gauges and methods of showing some kind of level of data or progress. This tutorial will walk you through how to handle incoming data using Storyboard IO, and use that data to display an increase or decrease using a gauge made with arcs.]]></description>
            <content:encoded><![CDATA[<p>Arcs are often used for gauges and methods of showing some kind of level of data or progress. This tutorial will walk you through how to handle incoming data using Storyboard IO, and use that data to display an increase or decrease using a gauge made with arcs.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="create-the-gauge">Create the Gauge<a href="https://docs.cranksoftware.com/knowledge-base/how-to-create-a-gauge-using-arcs#create-the-gauge" class="hash-link" aria-label="Direct link to Create the Gauge" title="Direct link to Create the Gauge" translate="no">​</a></h2>
<p>The gauge will consist of 2 arcs. The bottom arc will be used as the shell of the gauge, to show the total amount of data that the gauge represents. The second arc will sit on top and be situated so that it sits within the outer shell. This is the arc that will be updated using the incoming data, to show the gauge’s current level.</p>
<p>Create a new control by right clicking on your layer and selecting Add &gt; Control &gt; Arc, or by dragging in an arc control from the Palette. Change the size of the control to make the arc as large as you want it.</p>
<p><img decoding="async" loading="lazy" alt="image4-1.png" src="https://docs.cranksoftware.com/assets/images/image4-1-ad61e4ba56517d7dce50d98ccad33ded.png" width="1999" height="925" class="img_ev3q"></p>
<p>Now add another arc render extension to the control by pressing the + button. Shrink the size and adjust the position of the render extension so that it sits centered within the first one. You can adjust the end angle to see what it will look like at different levels.</p>
<p><img decoding="async" loading="lazy" alt="image2-1.png" src="https://docs.cranksoftware.com/assets/images/image2-1-48ae5d298629fc86f8bc2bf91262b38e.png" width="1999" height="771" class="img_ev3q"></p>
<p>Tie the end angle to a variable, since that is what we will be adjusting, using the incoming data.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="create-an-update-event">Create an Update Event<a href="https://docs.cranksoftware.com/knowledge-base/how-to-create-a-gauge-using-arcs#create-an-update-event" class="hash-link" aria-label="Direct link to Create an Update Event" title="Direct link to Create an Update Event" translate="no">​</a></h2>
<p>This event can just be temporary for testing, or you can create the final event for your application now. The important thing is to first plan out what kind of data you will be sending to update the gauge. You may choose to send the exact level of whatever you are measuring, be that speed or gas level or air pressure. Since it is better to keep most calculations on the backend in order to keep the UI as simple as possible, we are going to send a direct percentage. This will be sent as an unsigned integer, between 0 and 100.</p>
<p><img decoding="async" loading="lazy" alt="image7-1.png" src="https://docs.cranksoftware.com/assets/images/image7-1-7f084516aa738cb02d65b2eaa9e84432.png" width="1050" height="752" class="img_ev3q"></p>
<p>Now that the event is created, add a listener for it at the app level (right click on the app name at the top of the Application Model). Set it to trigger a Lua function.</p>
<p><img decoding="async" loading="lazy" alt="image3-1.png" src="https://docs.cranksoftware.com/assets/images/image3-1-4252d7deb840eb1430fb400859505447.png" width="1999" height="997" class="img_ev3q"></p>
<p>Define your event channel if you have not done that yet. Click on the Simulator Configurations icon.</p>
<p><img decoding="async" loading="lazy" alt="image6.jpg" src="https://docs.cranksoftware.com/assets/images/image6-e339e1925798012f5dca8ab36840d71e.jpg" width="1026" height="60" class="img_ev3q"></p>
<p>Select greio in the plugin options and set the Channel Name.</p>
<p><img decoding="async" loading="lazy" alt="image5-1.png" src="https://docs.cranksoftware.com/assets/images/image5-1-f3b069137d698e5af63aa25ad36acdce.png" width="1636" height="1208" class="img_ev3q"></p>
<p>Close the dialog to save the setting.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="massage-the-data-and-update-the-arc">Massage the Data and Update the Arc<a href="https://docs.cranksoftware.com/knowledge-base/how-to-create-a-gauge-using-arcs#massage-the-data-and-update-the-arc" class="hash-link" aria-label="Direct link to Massage the Data and Update the Arc" title="Direct link to Massage the Data and Update the Arc" translate="no">​</a></h2>
<p>Open up the script editor and create the function that you named earlier. You can do this by finding the event in the Actions view for the app level (Click on the app in the Application Model), or by right-clicking in the Scripts view.</p>
<p>Gather the level from the incoming mapargs event data, and print it out to ensure that the level is coming through properly.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">--- @param gre#context mapargs</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function cb_gauge_update(mapargs) </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  local ev_data = mapargs.context_event_data</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  local level = ev_data.level</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  print(level)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>Run the app and open the Storyboard IO tab. Select your event from the dropdown menu and slide the slider to ensure that the proper values are being printed in the Console.</p>
<p><img decoding="async" loading="lazy" alt="image1-1.png" src="https://docs.cranksoftware.com/assets/images/image1-1-6a6afd8238deb9fa0d399a9d05c8912f.png" width="848" height="1536" class="img_ev3q"></p>
<p>Now set some constants for your min and max gauge angles. In this case, they would be 180 and 360.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">local MIN_ANGLE = 180</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local MAX_ANGLE = 360</span><br></div></code></pre></div></div>
<p>Now comes the math to determine the angle of the gauge. Since the percentage is being sent through directly, the math becomes fairly simple.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">local angle = ((level/100) * (MAX_ANGLE - MIN_ANGLE)) + MIN_ANGLE</span><br></div></code></pre></div></div>
<p>Use gre.set_value to update the End Angle variable.</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">gre.set_value("Layer.arc_base.end_angle", angle)</span><br></div></code></pre></div></div>
<p>Run the app and use the slider in Storyboard IO to update the gauge. You should now see the arc update in the app, staying within the bounds of the gauge.</p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>animation</category>
            <category>events</category>
            <category>components</category>
        </item>
        <item>
            <title><![CDATA[Translation Tool Tutorial]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/translation-tool-tutorial</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/translation-tool-tutorial</guid>
            <pubDate>Thu, 23 Apr 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Translations are typically implemented towards the end of a project. In this scenario, you have been developing the app and filling in default text in a single language to be translated at a future time. Now that time has arrived and the translator is standing by to fill in alternative values for whatever languages will be supported. The following tutorial will walk you through the recommended workflow for exporting a translation database file for the translator to fill in and re-importing it into the project to view the translations in the app.]]></description>
            <content:encoded><![CDATA[<p>Translations are typically implemented towards the end of a project. In this scenario, you have been developing the app and filling in default text in a single language to be translated at a future time. Now that time has arrived and the translator is standing by to fill in alternative values for whatever languages will be supported. The following tutorial will walk you through the recommended workflow for exporting a translation database file for the translator to fill in and re-importing it into the project to view the translations in the app.</p>
<!-- -->
<p>1 - Create a Translation Database</p>
<p>Open your app and open the text translation view. This can be accomplished by clicking Window &gt; Show View &gt; Text Translation.</p>
<p><img decoding="async" loading="lazy" alt="image2-3.png" src="https://docs.cranksoftware.com/assets/images/image2-3-99e2bc2049a8185d0fb474e9569eb318.png" width="1999" height="197" class="img_ev3q"></p>
<p>You will see there is a dropdown to select a language, as well as an “Extract Text” button. These will be explained shortly, but for now they are unavailable because there is currently no translation database in the project. To add a translation database, click the “Create Translation Database” button.</p>
<p><img decoding="async" loading="lazy" alt="image10.png" src="https://docs.cranksoftware.com/assets/images/image10-c21ec85d681e7fe0ff36c0a82e0af1e7.png" width="1504" height="722" class="img_ev3q"></p>
<p>In the popup, there are options to create a new translation database or import an existing one. We will create a new one for this tutorial.</p>
<blockquote>
</blockquote>
<p>You will also see that there is a dropdown menu to select the database format. For now the only option is a single CSV file. This format was chosen because it is supported by programs such as Excel, which is often the preferred tool among those actually entering the translations. CSV formatting options make it easy to organize the different values being entered. Other formats, such as json, will be supported in the future.</p>
<p>Click Next</p>
<p>The attributes file will be explained in further detail later on. It is used for variables that will affect the appearance of the app depending on the current language. For example, the width of a button when a translation is longer in a specific language.</p>
<p>For now, just name the attributes file and click Finish.</p>
<p>Enter the name of your initial language and click OK.</p>
<p>2 - Getting to Know the Interface</p>
<p><img decoding="async" loading="lazy" alt="image7-2.png" src="https://docs.cranksoftware.com/assets/images/image7-2-52b59c01f9cf9670ca3e5d176e102e77.png" width="1722" height="992" class="img_ev3q"></p>
<p>The first tab of the translation view will show a list of all of the controls with text render extensions that can be translated, as well as the current text being shown and an indication of whether the text is currently clipped or not.</p>
<p>The filter box on the bottom allows you to search by control name. The “Static” and “Variable” checkboxes at the bottom can be used to filter by text that is currently linked to a variable or not, and the “Translated” checkbox can be used to filter out text that is already linked to a translation id.</p>
<p>The second tab of the translation view shows attribute variables, which, as explained previously, can be used to set certain visual aspects of the app depending on the language. For example, if a text area in the previous tab was showing that the text is clipped in the current language, you may want to create an attribute variable for the width of the text area to increase it so the text will fit. We will show the attribute variables in action later on.</p>
<p>The final tab of the translation view will show all existing translation ids, the text for that ID in the current language, and an optional description to indicate the variable’s use within the app. Currently we have no translation IDs in our database, so let’s translate some text.</p>
<p>3 - Extracting Text To Be Translated</p>
<p><img decoding="async" loading="lazy" alt="image5.jpg" src="https://docs.cranksoftware.com/assets/images/image5-34427854f23c6b322b74afb8ffa82f66.jpg" width="1999" height="459" class="img_ev3q"></p>
<p>In order to begin adding text to your database for translation, return to the Text References tab of the translation view and click “Extract Text” next to the language dropdown.</p>
<p><img decoding="async" loading="lazy" alt="image13.png" src="https://docs.cranksoftware.com/assets/images/image13-89c030de1f33c0b0e3b2de483535b085.png" width="956" height="581" class="img_ev3q"></p>
<p>This view allows you to see all text fields in the project and tie translation variables to them individually. Click on one of the text fields in the “Model Text References” box. If an ID with a similar value to the one in the selected text reference exists, the ID will appear in the “Suggested Translation Variables” box and you can select it if you would like to use it. If no ID exists, click Create New ID. Select the new ID from the “Suggested Translation Variables” box to link this text reference to the ID. Do this for each text field you would like to translate and then click OK.</p>
<p>If your text is dynamic (maybe you have “Good Morning” text that changes to “Good Afternoon” or “Good Evening”), you can add multiple translation IDs for a field and switch between them at runtime depending on context. Use the checkbox in the “Suggested Translation Variable” box to select the default id to show.</p>
<blockquote>
</blockquote>
<p>A potentially more convenient way of tying each field to a translation variable is by doing it as you create the text fields in the app. After you have placed your text, right click on the icon to the left of the text field and select Translate. This will tie the text to a translation variable that will now appear in the translation view.</p>
<p><img decoding="async" loading="lazy" alt="image1.jpg" src="https://docs.cranksoftware.com/assets/images/image1-c1c5dc9e997964413ff4ce6948921f61.jpg" width="1184" height="182" class="img_ev3q"></p>
<p>4 - Adding Another Language</p>
<p>Now that you have linked your text fields to translation IDs, you will have a database of initial values for your primary language ready to go. Before you send this database to the translator, you will have to add your secondary languages so that they can enter the proper values.</p>
<p><img decoding="async" loading="lazy" alt="image8-1.png" src="https://docs.cranksoftware.com/assets/images/image8-1-c87c68a1c2d0dbc937d114de81cd8e5a.png" width="1398" height="87" class="img_ev3q"></p>
<p>Click the Language dropdown at the top of the translation view, where you see the name of your initial language. Click the option to “Create New Language”. Enter the name of your second language.</p>
<p><img decoding="async" loading="lazy" alt="image3-3.png" src="https://docs.cranksoftware.com/assets/images/image3-3-4b0d266fd1b8c46f502ae6be1a8feb5d.png" width="566" height="283" class="img_ev3q"></p>
<p>If you would like to copy the initial values from your primary language, check the “copy values from” checkbox. If you would like to leave the values as empty strings until the translator can fill them, leave this unchecked.</p>
<p>Click OK to create your new language. Do this for all languages you would like to add to the database.</p>
<p>5 - Fill the Translation Database</p>
<p>This is the point where you give the CSV file to whomever will be filling in the translations for the secondary languages. The file can be found in the translations folder in the project directory. The translator can open it into Excel or another editor and fill in the values in the proper columns.</p>
<p><img decoding="async" loading="lazy" alt="image15.png" src="https://docs.cranksoftware.com/assets/images/image15-e2b382c102a18a9582527ef4da25620a.png" width="718" height="438" class="img_ev3q"></p>
<p>When they are finished, return the file to the translations directory and re-open the project in Storyboard.</p>
<blockquote>
</blockquote>
<p>There is an issue with refreshing in Storyboard 6.0, which requires the gde to be closed and reopened for the changes to appear in the translations view. This will be fixed in a future version of Storyboard.</p>
<p>6 - Viewing the Changes</p>
<p>Now that you have imported the updated translation database, you can easily preview each language in Designer by using the language dropdown in the translations view:</p>
<p><img decoding="async" loading="lazy" alt="image14.png" src="https://docs.cranksoftware.com/assets/images/image14-da7cd84e32fcb4be73442bcc71d91dba.png" width="535" height="125" class="img_ev3q"></p>
<p>7 - Changing the Language at Runtime</p>
<p>Storyboard currently does not include a built-in action to change the language at runtime, however this can be achieved using the VariableLoader script (accompanied by the csv script) in the TranslationUsingID sample.</p>
<p>To use it, initialize the VariableLoader at application init (change the translation and attributes path to match whatever you named the files):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">VarLoader = require("VariableLoader")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">--- @param gre#context mapargs</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function CBInit(mapargs) </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -- English is the application's base design language so we don't have</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -- to perform any loading initially. If we start with a different language</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -- then we should use loadOnInit to set those initial values.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  local attrs = {}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  attrs.language = "english"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --attrs.loadOnInit = true</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  attrs.textDB = gre.APP_ROOT .. "/translations/translations.csv"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  attrs.attributeDB = gre.APP_ROOT .. "/translations/attributes.csv"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  Translation = VarLoader.CreateLoader(attrs)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>And then you can simply call setLanguage and pass in the desired language when you would like to switch (make sure the language name you are passing in is identical to the name you gave it when you created it. This includes upper/lowercase):</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">--- @param gre#context mapargs</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function CBLoadLanguage(mapargs)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  Translation:setLanguage(mapargs.language)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>8 - Using Attributes to Adjust Your App Based on Language</p>
<p>There may be situations where your app will have to look a little different depending on the current language. For instance, some strings may be longer in different languages, and the formatting of the text field will have to be adjusted for it to fit. Storyboard’s translation tool provides attributes for this exact purpose. In this example, we will imagine that the English language uses a different font than the French language.</p>
<p>First, tie the font for all text fields to a variable. In our case, all the fonts could be tied to the same variable because they will share the same font in each language.</p>
<p><img decoding="async" loading="lazy" alt="image12.png" src="https://docs.cranksoftware.com/assets/images/image12-acc41a67041fe95954a974c085378f72.png" width="528" height="556" class="img_ev3q"></p>
<p>Now click on the attributes tab in the translations view. Right click in the empty variable list and click “Add Variable to Translation”. Select your variable and click finish.</p>
<p><img decoding="async" loading="lazy" alt="image4-3.png" src="https://docs.cranksoftware.com/assets/images/image4-3-4c6bacc89fd439abd088e357782f7215.png" width="603" height="287" class="img_ev3q"></p>
<p>Take the attributes csv file into an editor and fill in your desired values for each language. Re-import the csv into Storyboard and switch between languages in the translations view. You should see the fonts change as you move between languages. Try this out for any other variables you may want to change depending on language.</p>
<p><img decoding="async" loading="lazy" alt="image6-1.png" src="https://docs.cranksoftware.com/assets/images/image6-1-c052193b4d3870c25dd9cfbc9e04d796.png" width="770" height="184" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" alt="image11.png" src="https://docs.cranksoftware.com/assets/images/image11-c5eaa13ec825e1069178b59055ba4af0.png" width="489" height="302" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" alt="image9.png" src="https://docs.cranksoftware.com/assets/images/image9-54e17bcd59052fd351f3fd37cd7c3668.png" width="964" height="604" class="img_ev3q"></p>
<blockquote>
</blockquote>
<p>There is an issue with refreshing in Storyboard 6.0, which requires the gde to be closed and reopened for the changes to appear in the translations view. This will be fixed in a future version of Storyboard.</p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>localization</category>
            <category>workflow</category>
        </item>
        <item>
            <title><![CDATA[Creating User Interface Table Variables]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/creating-user-interface-table-variables</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/creating-user-interface-table-variables</guid>
            <pubDate>Tue, 21 Apr 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Sometimes the best way to display data in Storyboard is with tables and table variables. Tables, as well as cells within the table, are able to contain several render extensions, aka user interface table variables. This allows them to behave dynamically and perform a wide variety of tasks.]]></description>
            <content:encoded><![CDATA[<p>Sometimes the best way to display data in Storyboard is with tables and table variables. Tables, as well as cells within the table, are able to contain several render extensions, aka user interface table variables. This allows them to behave dynamically and perform a wide variety of tasks.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="getting-started-with-user-interface-table-variables">Getting started with user interface table variables<a href="https://docs.cranksoftware.com/knowledge-base/creating-user-interface-table-variables#getting-started-with-user-interface-table-variables" class="hash-link" aria-label="Direct link to Getting started with user interface table variables" title="Direct link to Getting started with user interface table variables" translate="no">​</a></h2>
<p>Before getting started, download the example that we've started for you and import it into Storyboard. You should see something that looks like this:</p>
<p><img decoding="async" loading="lazy" alt="starting-the-project.png" src="https://docs.cranksoftware.com/assets/images/starting-the-project-eb3560d1923b497b3ca46a2cd1049ebb.png" width="816" height="520" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="adding-user-interface-table-variables">Adding User Interface Table Variables<a href="https://docs.cranksoftware.com/knowledge-base/creating-user-interface-table-variables#adding-user-interface-table-variables" class="hash-link" aria-label="Direct link to Adding User Interface Table Variables" title="Direct link to Adding User Interface Table Variables" translate="no">​</a></h2>
<p>If you were to run the application at this point, it wouldn't do too much. All of the cells in the table are the same. They all have the same text and there aren't enough of them to make up an entire week. To create a table variable:</p>
<ul>
<li class="">Click the table cell.</li>
<li class="">Select the Text render extension.</li>
<li class="">Bind a variable to the text value.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="adding-table-variables.png" src="https://docs.cranksoftware.com/assets/images/adding-table-variables-f68e6003f117eb1b9d74c0ae0e1d7b83.png" width="1290" height="532" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="creating-the-new-table-variable">Creating the new table variable<a href="https://docs.cranksoftware.com/knowledge-base/creating-user-interface-table-variables#creating-the-new-table-variable" class="hash-link" aria-label="Direct link to Creating the new table variable" title="Direct link to Creating the new table variable" translate="no">​</a></h2>
<p>In the Table Variable Selection dialog:</p>
<ul>
<li class="">Click the New Variable button.</li>
<li class="">Type "text" in the Name field.</li>
<li class="">Select the Create a table cell variable checkbox to indicate that we are creating a table variable.</li>
<li class="">Extend it to the 5th row (we could do all 7 here but we may not know how many rows there will be).</li>
<li class="">Finish and select that variable.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="creating-table-variable.png" src="https://docs.cranksoftware.com/assets/images/creating-table-variable-7fac57cc4d3501e129f569310e3d9f76.png" width="1118" height="586" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="loading-data">Loading data<a href="https://docs.cranksoftware.com/knowledge-base/creating-user-interface-table-variables#loading-data" class="hash-link" aria-label="Direct link to Loading data" title="Direct link to Loading data" translate="no">​</a></h2>
<p>In the Application Model, double-click callbacks.lua to open it up for editing. Let's add code to iterate through the weekdays table to populate the table cells that we have so far.</p>
<ul>
<li class="">Copy and paste the code below to fill in the load_table function.</li>
<li class="">Run the program and press Load Table.</li>
</ul>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">function load_table(mapargs)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	local data={}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	for i=1,#weekdays do --The "#weekdays here returns the number of entries in the table."</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                --When changing table variables, you specify them by layer_name.control_name.variable_name.row.col</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		data["TableLayer.weekdays.text."..i..".1"] = weekdays[i]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> 	end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	gre.set_data(data)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> end</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="loading-data-into-table-1.png" src="https://docs.cranksoftware.com/assets/images/loading-data-into-table-1-ab5ebda8ca951a5354f344c5cbe1393d.png" width="817" height="527" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="expanding-the-table-rows-to-include-selected-data">Expanding the table rows to include selected data<a href="https://docs.cranksoftware.com/knowledge-base/creating-user-interface-table-variables#expanding-the-table-rows-to-include-selected-data" class="hash-link" aria-label="Direct link to Expanding the table rows to include selected data" title="Direct link to Expanding the table rows to include selected data" translate="no">​</a></h2>
<p>A week isn't complete with only six days, so we will need to add Sunday to the table. But right now, our table is only six rows long. There are several ways to change the number of rows in a table, but for our purposes, the Lua function gre.set_table_attrs() is most appropriate. That function expects a table of tags with their values, as well as the name of a table. Paste this function underneath the gre.set_data(data) we just added:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">local tag_table={}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tag_table["rows"] = #weekdays</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">gre.set_table_attrs("TableLayer.weekdays.text.",tag_table)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>Now, let's run the application again and check our progress.</p>
<p><img decoding="async" loading="lazy" alt="resizing-table-to-fit-data.png" src="https://docs.cranksoftware.com/assets/images/resizing-table-to-fit-data-79e8dcc2b9b2671826d35b1f3c00335b.png" width="776" height="502" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="adding-a-new-variable-for-fill">Adding a new variable for Fill<a href="https://docs.cranksoftware.com/knowledge-base/creating-user-interface-table-variables#adding-a-new-variable-for-fill" class="hash-link" aria-label="Direct link to Adding a new variable for Fill" title="Direct link to Adding a new variable for Fill" translate="no">​</a></h2>
<p>Let's add another table variable but this time to the Fill render extension. To start, you'll notice we've only included 4 of the rows.</p>
<ul>
<li class="">Click the table cell.</li>
<li class="">Select the Fill render extension.</li>
<li class="">Switch the render extension from a static to dynamic variable.</li>
<li class="">Click the New Variable button.</li>
<li class="">Type "fill" in the Name field.</li>
<li class="">Select the Create a table cell variable checkbox to indicate that we are creating a table variable.</li>
<li class="">Extend it to the 4th row as you see in the screenshot below.</li>
<li class="">Click Finish.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="creating-fill-variable.png" src="https://docs.cranksoftware.com/assets/images/creating-fill-variable-4cbcb9d3a44f0f9b4fb76ed110fd6f82.png" width="696" height="747" class="img_ev3q"></p>
<p>Run this in the simulator now and you'll find that the fills seem to be black in all the rows from 4 onward. This is because there is no data to go on when those cells are being filled out. To fix this, add the following under our text variable change in the load table:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">data["TableLayer.weekdays.fill."..i..".1"] = 0x6D8F3A</span><br></div></code></pre></div></div>
<p>Run that to see this fixed.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="adding-interactivity-to-the-table">Adding interactivity to the table<a href="https://docs.cranksoftware.com/knowledge-base/creating-user-interface-table-variables#adding-interactivity-to-the-table" class="hash-link" aria-label="Direct link to Adding interactivity to the table" title="Direct link to Adding interactivity to the table" translate="no">​</a></h2>
<p>Next, we're going to add a few actions on the user interface table cell. We'll add gre.cell.gotfocus with a Data Change action, gre.cell.lostfocus with a Data Change action, and gre.press with a Lua Script action. gotfocus will change the table fill to #A7C445 and lostfocus will change it to #6D8F3A. The press event will call the Lua function cb_cell_press. You can add those parameters below the trigger events on the Actions tab, as you can see below:</p>
<p><img decoding="async" loading="lazy" alt="selecting-table-cell.png" src="https://docs.cranksoftware.com/assets/images/selecting-table-cell-fb38c00cebb6eb7be2c0b45f39b00c73.png" width="776" height="504" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="understanding-which-row-was-pressed">Understanding which row was pressed<a href="https://docs.cranksoftware.com/knowledge-base/creating-user-interface-table-variables#understanding-which-row-was-pressed" class="hash-link" aria-label="Direct link to Understanding which row was pressed" title="Direct link to Understanding which row was pressed" translate="no">​</a></h2>
<p>Now that we have some highlighted cells and some dynamic data, let's see which row is pressed. We've already attached the Lua function cb_cell_press, so now we can add the following function and code to our callbacks.lua script:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">function cb_cell_press(mapargs)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	local cell=mapargs.context_row</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	print(cell)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> end</span><br></div></code></pre></div></div>
<p>Run it and press on a couple rows.</p>
<p><img decoding="async" loading="lazy" alt="selecting-row-number.png" src="https://docs.cranksoftware.com/assets/images/selecting-row-number-4ed15fd96a2d588c03836368fd6f767e.png" width="779" height="452" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ensuring-a-meaningful-response">Ensuring a meaningful response<a href="https://docs.cranksoftware.com/knowledge-base/creating-user-interface-table-variables#ensuring-a-meaningful-response" class="hash-link" aria-label="Direct link to Ensuring a meaningful response" title="Direct link to Ensuring a meaningful response" translate="no">​</a></h2>
<p>What we see printed out in the Storyboard console is the row number of each cell we press. While that's a step forward, we'd probably want more than just the row number. We could print weekdays[cell], but what if that data has gone through changes since it was put in the table? What we want is the text variable of that specific cell at that specific point in time. So let's add the last two lines below to our cb_cell_press function:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">function cb_cell_press(mapargs)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	local cell=mapargs.context_row</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	local data=gre.get_data("TableLayer.weekdays.text."..cell..".1")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	print(data["TableLayer.weekdays.text."..cell..".1"])</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> end</span><br></div></code></pre></div></div>
<p>Now we're getting meaningful data from that row that we can use elsewhere if required.</p>
<p><img decoding="async" loading="lazy" alt="adding-table-actions.png" src="https://docs.cranksoftware.com/assets/images/adding-table-actions-d7decd87960b3fac91da62fcbafd5d4f.png" width="1261" height="610" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="playing-with-other-data-to-make-it-your-own">Playing with other data to make it your own<a href="https://docs.cranksoftware.com/knowledge-base/creating-user-interface-table-variables#playing-with-other-data-to-make-it-your-own" class="hash-link" aria-label="Direct link to Playing with other data to make it your own" title="Direct link to Playing with other data to make it your own" translate="no">​</a></h2>
<p>We're now left with this as a final callbacks.lua file:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">local weekdays={"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> function load_table(mapargs)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	local data={}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	for i=1,#weekdays do --The "#weekdays here returns the number of entries in the table."</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                --When changing table variables, you specify them by layer_name.control_name.variable_name.row.col</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		data["TableLayer.weekdays.text."..i..".1"] = weekdays[i] </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		data["TableLayer.weekdays.fill."..i..".1"] = 0x6D8F3A</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">gre.set_data(data)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local tag_table = {}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tag_table["rows"] = #weekdays</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">gre.set_table_attrs("TableLayer.weekdays",tag_table)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function cb_cell_press(mapargs)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local cell=mapargs.context_row</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">local data=gre.get_data("TableLayer.weekdays.text."..cell..".1")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">print(data["TableLayer.weekdays.text."..cell..".1"])</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">end</span><br></div></code></pre></div></div>
<p>Now that we've play around with the different table settings to see what they do, try adding another variable and render extension, or add an image to the right of the table.</p>
<p>To help out further, take a look at our Lua documentation to see what other functionality can be added to tables in your user interfaces.</p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>lists-tables</category>
            <category>variables</category>
        </item>
        <item>
            <title><![CDATA[How to enable performance and debug tracing on RT1060 EVK using SWO back-channel]]></title>
            <link>https://docs.cranksoftware.com/knowledge-base/how-to-enable-performance-and-debug-tracing-on-rt1060-evk-using-swo-back-channel</link>
            <guid>https://docs.cranksoftware.com/knowledge-base/how-to-enable-performance-and-debug-tracing-on-rt1060-evk-using-swo-back-channel</guid>
            <pubDate>Tue, 14 Apr 2020 00:00:00 GMT</pubDate>
            <description><![CDATA[Following on from the RT1060: Enabling faster flashing article which discusses how to enable the advanced LPC-Link2 JTAG debugger features on the i.MXRT1060EVK board I was inspired to explore how we could make good use of this with Storyboard applications.]]></description>
            <content:encoded><![CDATA[<p>Following on from the RT1060: Enabling faster flashing article which discusses how to enable the advanced LPC-Link2 JTAG debugger features on the i.MXRT1060EVK board I was inspired to explore how we could make good use of this with Storyboard applications.</p>
<!-- -->
<p>There are a few resources and references that you may find useful:</p>
<p><a href="https://www.nxp.com/docs/en/user-guide/LPCXpresso_IDE_SWO_Trace.pdf" target="_blank" rel="noopener noreferrer" class="">https://www.nxp.com/docs/en/user-guide/LPCXpresso_IDE_SWO_Trace.pdf</a></p>
<p><a href="https://www.nxp.com/docs/en/quick-reference-guide/MCUXpresso_IDE_SWO_Trace.pdf" target="_blank" rel="noopener noreferrer" class="">https://www.nxp.com/docs/en/quick-reference-guide/MCUXpresso_IDE_SWO_Trace.pdf</a></p>
<p><a href="https://community.nxp.com/community/mcuxpresso/mcuxpresso-ide/blog/2019/06/10/swo-with-nxp-imx-rt1064-evk-board" target="_blank" rel="noopener noreferrer" class="">https://community.nxp.com/community/mcuxpresso/mcuxpresso-ide/blog/2019/06/10/swo-with-nxp-imx-rt1064-evk-board</a></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="configuring-the-swo-hardware">Configuring the SWO hardware<a href="https://docs.cranksoftware.com/knowledge-base/how-to-enable-performance-and-debug-tracing-on-rt1060-evk-using-swo-back-channel#configuring-the-swo-hardware" class="hash-link" aria-label="Direct link to Configuring the SWO hardware" title="Direct link to Configuring the SWO hardware" translate="no">​</a></h2>
<p>The first thing that needs to be done is to bring the SWO pin to the outside world and make this available to the onboard JTAG adapter. This requires a change to the pin MUX configuration for the single wire debug function [Pad G13] on the RT1060 device.</p>
<p>You can do this one of two ways:</p>
<p>Option 1: Using the MCUExpresso Pins configuration tool and then use the update option to insert the code automatically as follows:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">* TEXT BELOW IS USED AS SETTING FOR TOOLS *************************************</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">CopiedFunction:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">- options: {}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">- pin_list:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">- pinItem: {pin_num: G13, peripheral: ARM, signal: arm_trace_swo, pin_signal: GPIO_AD_B0_10, pull_up_down_config: Pull_Down_100K_Ohm, pull_keeper_select: Keeper,</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">pull_keeper_enable: Disable, open_drain: Disable, speed: MHZ_200, drive_strength: R0_7}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">* BE CAREFUL MODIFYING THIS COMMENT - IT IS YAML SETTINGS FOR TOOLS ***********</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="image2-5.png" src="https://docs.cranksoftware.com/assets/images/image2-5-17a3866bfc156162a4bcfc7f01a62035.png" width="1920" height="1040" class="img_ev3q"></p>
<p>Option 2: Manually insert the code below into the BOARD_InitPins() function within the pin_mux.c file to set the pin MUX and route the SWO TRACE function to the pin:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">IOMUXC_SetPinMux(</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    IOMUXC_GPIO_AD_B0_10_ARM_TRACE_SWO,     /* GPIO_AD_B0_10 is configured as ARM_TRACE_SWO */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    0U);                                    /* Software Input On Field: Input Path is determined by functionality */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    IOMUXC_SetPinConfig(</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    IOMUXC_GPIO_AD_B0_10_ARM_TRACE_SWO,     /* GPIO_AD_B0_10 PAD functional properties : */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    0xF9U);                                 /* Slew Rate Field: Fast Slew Rate</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                                 Drive Strength Field: R0/7</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                                 Speed Field: max(200MHz)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                                 Open Drain Enable Field: Open Drain Disabled</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                                 Pull / Keep Enable Field: Pull/Keeper Disabled</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                                 Pull / Keep Select Field: Keeper</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                                 Pull Up / Down Config. Field: 100K Ohm Pull Down</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                                                 Hyst. Enable Field: Hysteresis Disabled */</span><br></div></code></pre></div></div>
<p>Next the TRACE clock settings must be checked and or modified in clock_config.c within function BOARD_BootClockRUN() to be the following:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">/* Set TRACE_PODF. */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">CLOCK_SetDiv(kCLOCK_TraceDiv, 0);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/* Set Trace clock source. */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">CLOCK_SetMux(kCLOCK_TraceMux, 3);</span><br></div></code></pre></div></div>
<p>Finally the TRACE clock must be enabled on boot so insert the following in main() after BOARD_BootClockRUN():</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">// Enable SWO TRACE clock</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*((uint32_t *)(0x400E0600)) = (1 &lt;&lt; 11); /* enable TPIU clock */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">CLOCK_EnableClock(kCLOCK_Trace);</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="adding-swo-tracing-utility-functions">Adding SWO tracing utility functions<a href="https://docs.cranksoftware.com/knowledge-base/how-to-enable-performance-and-debug-tracing-on-rt1060-evk-using-swo-back-channel#adding-swo-tracing-utility-functions" class="hash-link" aria-label="Direct link to Adding SWO tracing utility functions" title="Direct link to Adding SWO tracing utility functions" translate="no">​</a></h2>
<p>Once the hardware is configured we can now make use of the SWO pin to act as a JTAG back-channel for trace output instead of the much slower UART method. The utility functions enable you to send strings out of the SWO debug channel that can be read using the SWO ITM Console.</p>
<p>SWO.h:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">/*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * SWO.h</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> *</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> *  Created on: 27 Mar 2020</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#ifndef SWO_H_</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#define SWO_H_</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">void SWO_PrintChar  (char c);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">void SWO_PrintString(const char *s);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#endif /* SWO_H_ */</span><br></div></code></pre></div></div>
<p>SWO.c:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">/*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * SWO.c</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> *</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> *  Created on: 27 Mar 2020</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#include "SWO.h"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/*******************************************************************************</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * Code</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> ******************************************************************************/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/*********************************************************************</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">* Defines for Cortex-M debug unit</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/*********************************************************************</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*       Defines for Cortex-M debug unit</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#define ITM_STIM_U32 (*(volatile unsigned int*)0xE0000000)    // Stimulus Port Register word acces</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#define ITM_STIM_U8  (*(volatile         char*)0xE0000000)    // Stimulus Port Register byte acces</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#define ITM_ENA      (*(volatile unsigned int*)0xE0000E00)    // Trace Enable Ports Register</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#define ITM_TCR      (*(volatile unsigned int*)0xE0000E80)    // Trace control register</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/*********************************************************************</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*       SWO_PrintChar()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">* Function description</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*   Checks if SWO is set up. If it is not, return,</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*    to avoid program hangs if no debugger is connected.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*   If it is set up, print a character to the ITM_STIM register</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*    in order to provide data for SWO.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">* Parameters</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*   c:    The Character to be printed.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">* Notes</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*   Additional checks for device specific registers can be added.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">void SWO_PrintChar(char c) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  volatile int timeout;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  //</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // Check if ITM_TCR.ITMENA is set</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  //</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  if ((ITM_TCR &amp; 1) == 0) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    return;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  //</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // Check if stimulus port is enabled</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  //</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  if ((ITM_ENA &amp; 1) == 0) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    return;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  //</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // Wait until STIMx is ready,</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // then send data</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  //</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  timeout = 5000; /* arbitrary timeout value */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  while ((ITM_STIM_U8 &amp; 1) == 0)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	/* Wait until STIMx is ready, then send data */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	timeout--;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	if (timeout==0) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		return; /* not able to send */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  ITM_STIM_U8 = c;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/*********************************************************************</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*       SWO_PrintString()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">* Function description</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*   Print a string via SWO.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">void SWO_PrintString(const char *s) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  //</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  // Print out character per character</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  //</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  while (*s) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    SWO_PrintChar(*s++);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div></code></pre></div></div>
<p>To test the SWO output with your application include the SWO.h header file in your source and call the utility function to emit some sample trace output:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#include "board.h"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#include "peripherals.h"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#include "SWO.h"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">SWO_PrintString("Hello world from SWO!");</span><br></div></code></pre></div></div>
<p>Build the application and flash this to the board. When the board starts-up and halts with a breakpoint on main() then move on to the next step.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="configuring-mcuxpresso-for-swo-itm-console-trace-output">Configuring MCUXpresso for SWO ITM Console trace output<a href="https://docs.cranksoftware.com/knowledge-base/how-to-enable-performance-and-debug-tracing-on-rt1060-evk-using-swo-back-channel#configuring-mcuxpresso-for-swo-itm-console-trace-output" class="hash-link" aria-label="Direct link to Configuring MCUXpresso for SWO ITM Console trace output" title="Direct link to Configuring MCUXpresso for SWO ITM Console trace output" translate="no">​</a></h2>
<p>With MCUXpresso the Analysis menu gives access to the advanced trace windows for the IDE:</p>
<p><img decoding="async" loading="lazy" alt="image3-5.png" src="https://docs.cranksoftware.com/assets/images/image3-5-bd65b5b66ea185bf598238aed8294481.png" width="480" height="408" class="img_ev3q"></p>
<p>First the SWO Trace Config needs to be configured to detect the link speed and add the ITM Console. You may need to detect the SWO link by clicking Change -&gt; Detect :</p>
<p><img decoding="async" loading="lazy" alt="image5-2.png" src="https://docs.cranksoftware.com/assets/images/image5-2-502b88df29ed1fc1536681cbf350f4b3.png" width="698" height="340" class="img_ev3q"></p>
<p>If you select the SWO ITM Console and start the ITM Trace then allow the debugger to run then you should see the trace output appear:</p>
<p><img decoding="async" loading="lazy" alt="image6-2.png" src="https://docs.cranksoftware.com/assets/images/image6-2-f65ebc40244413e4c20907b6d4801b70.png" width="699" height="344" class="img_ev3q"></p>
<p>Note you can stop and start the SWO trace without impacting the performance of the target application. You can even capture and save the ITM Trace Console contents to a file.</p>
<p>Similarly if you are using J-Link with IAR then the i.MXRT1060 setup below can be used to emit the SWO trace data in real time and be viewed through the Terminal IO window and also logged to the local file ITM.log. Note the default CPU CPU clock speed of 528MHz.</p>
<p>J-Link -&gt; SWO Configuration:</p>
<p><img decoding="async" loading="lazy" alt="image1-5.png" src="https://docs.cranksoftware.com/assets/images/image1-5-302cbef532d5632c194ee61313950fdc.png" width="1339" height="820" class="img_ev3q"></p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="using-swo-tracing-for-storyboard-performance-logging">Using SWO Tracing for Storyboard Performance logging:<a href="https://docs.cranksoftware.com/knowledge-base/how-to-enable-performance-and-debug-tracing-on-rt1060-evk-using-swo-back-channel#using-swo-tracing-for-storyboard-performance-logging" class="hash-link" aria-label="Direct link to Using SWO Tracing for Storyboard Performance logging:" title="Direct link to Using SWO Tracing for Storyboard Performance logging:" translate="no">​</a></h2>
<p>Now that the SWO back-channel is proven we can reconfigure the project to use the SWO as the default route for the STDOUT and STDERR pipes so that application printf() content will appear out of on SWO ITM Console.</p>
<p>With NEWLIB this can be achieved by re-implementing the _write() and re-entrant _write_r() library functions. Inserting the code snippet below will override the default library behaviour:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">#ifdef __NEWLIB__</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/* For GCC compiler revise _write() and _write_r() functions for printf functionality </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> * which enables the output from STDOUT and STDERR */</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">int _write(int file, char *ptr, int len)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">{</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    int i;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	file = file;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	//</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	// Print out character per character</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	//</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    for( i=0; i &lt; len; i++) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    	SWO_PrintChar( ptr[i] );</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    }</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    return len;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">_ssize_t _write_r (struct _reent *reent, int fd, const void *ptr, size_t len)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">{</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	portENTER_CRITICAL();</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        _write(fd, ptr,len);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	portEXIT_CRITICAL();</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	return (_ssize_t)len;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">#endif</span><br></div></code></pre></div></div>
<p>Now it will be possible to enable performance tracing from a Storyboard application which emits PERF data to STDOUT and now have this streamed out of the SWO ITM Console:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">void</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sbengine_main_task(void *arg) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	char *args[20];</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	int n = 0;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	// args[n++] = "screen_mgr";</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	// args[n++] = "fps";</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        args[n++] = "logger";</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        args[n++] = "perf=1";</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="image7-3.png" src="https://docs.cranksoftware.com/assets/images/image7-3-59b4018ef63354185f0443147c209694.png" width="697" height="340" class="img_ev3q"></p>
<p>The same can be done for Storyboard debug output by elevating the trace verbosity:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">static void</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">run_storyboard_app(const char *bundle, int flags, char * const *options, int option_count) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	app = gr_application_create_args(bundle, flags, options, option_count);</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	if(!app) {</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		return;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	}</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	// Sets the application logging verbosity. For more logging verbosities, refer to gre.h.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	gr_application_debug(app, GR_DEBUG_CMD_VERBOSITY, GR_LOG_EVENT2 );//GR_LOG_ERROR);</span><br></div></code></pre></div></div>
<p><img decoding="async" loading="lazy" alt="image4-5.png" src="https://docs.cranksoftware.com/assets/images/image4-5-2dc1e1f0db479615e9328da98a21432f.png" width="697" height="342" class="img_ev3q"></p>
<p>Issues:</p>
<p>There is a slight issue with MCUXpresso where the trace line output gets truncated every 100ms or so which will need some investigation as to the cause.</p>]]></content:encoded>
            <category>knowledge-base</category>
            <category>performance-debugging</category>
            <category>mcu</category>
            <category>hardware</category>
        </item>
    </channel>
</rss>