To download NI software, including the products shown below, visit ni.com/downloads.
LabVIEW (NXG) web modules allow you to create custom web applications using a graphical environment.
The benefit of this is that it can run on any environment with a modern web browser. Like our phone!
Android and IOS devices allow webview based applications.
These are applications based on HTML, CSS and JavaScript but have access to system resources like:
media, sensors, network, phone info, …
This is great news when you have requirements that need access to these resources and/or must work offline.
This document focusses on android as I do not have access to a Mac so I can't compile for IOS.
However, almost everything should also translate to IOS, just a different target to compile for.
Description-Separate-2
1. Application Builder
There are multiple environments available to create your own mobile application.
This document will focus on Apache Cordova.
It is an amazing platform that adds a level of abstraction so you don't have to worry about your target system (Android or IOS) and it has a simple interface to the systems resources.
To create android applications I used the following installation tutorial by Tom Spencer to install Apache Cordova and all resources needed.
The added benefit of this tutorial is that you will also have access to a full Android emulator!
*NOTE:
The newest release of Android Studio (2020.3.1) does not support cordova out of the box.
When configuring Android Studio, please set the Build tools to an earlier version. I've been using 29.0.2 instead of 31 with great success.
If you are using and emulator, please use an image with an API level of 29/Android 10.0 or lower. Higher version are yet to be supported by Cordova.
This does not mean that it wil not run on your newest device. This has all to do with how the emulator communicates with the outside world.
2. Start a Project
To start a project we need to open a command window. (type "cmd" in your start bar)
Navigate to the location where you want to store the project. ("cd" and "dir" are your friend)
Now initialize your Cordova project in the CMD window.
cordova create <Application Name>
cd <Application Name>
I will refer to this location as the application directory
To build your application execute the following in your application directory:
cordova build android
If you want to test your build application in an emulator execute the following in your application directory:
cordova run --emulator
If you want to test your build application on your android device execute the following in your application directory:
cordova run android
3. Prepare the project for your LabVIEW web module
LabVIEW web modules have a pesky requirement. Resources need to be loaded via HTTP, even when they are local.
This means we can't just plop them in our project as a panel and build it.
To remedy this we have to embed a HTTP server within our application.
The httpd plugin for cordova is a nice plugin that can do this for us.
To install it type the following in your application directory:
cordova plugin add cordova-plugin-httpd
Unfortunately I have not found a way that works to directly embed pages on the server into the index.html file of cordova.
I do know this is possible in normal browsers so it seems something that is disabled on purpose.
In order to get around this limitation there is a system resource we can use.
The in app browser allows us to embed a webviewer on top of our application.
You have seen this resource when clicking on a add in YouTube.
To install it type the following in your application directory:
cordova plugin add cordova-plugin-inappbrowser
Unfortunately this will not work straight out-of-the-box on android.
If we would load a page we would only see an "err_cleartext_not_permitted" error message.
To remedy this you will have to alter the "config.xml" file in your application directory.
Please add the following alterations:
Before <?xml version='1.0' encoding='utf-8'?> <widget id="io.cordova.hellocordova" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"> <name>HelloCordova</name> <description> A sample Apache Cordova application that responds to the deviceready event. </description> <author email="dev@cordova.apache.org" href="http://cordova.io"> Apache Cordova Team </author> <content src="index.html" /> <access origin="*" /> <allow-intent href="http://*/*" /> <allow-intent href="https://*/*" /> <allow-intent href="tel:*" /> <allow-intent href="sms:*" /> <allow-intent href="mailto:*" /> allow-intent href="geo:*" /> <platform name="android"> <allow-intent href="market:*" /> </platform> <platform name="ios"> <allow-intent href="itms:*" /> <allow-intent href="itms-apps:*" /> </platform> </widget> |
After <?xml version='1.0' encoding='utf-8'?> |
In short:
xmlns:android="http://schemas.android.com/apk/res/android"
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
<application android:usesCleartextTraffic="true" />
</edit-config>
4. Prepare your application code to load your LabVIEW web modules
Now we the project allows us to embed and show the web modules we have to create some startup code in order to make sure everything is working as intended.
We will do this in the index.js file. You can find it at: <application directory>/www/js/index.js
On startup we will need to launch the server. The server is configured to only run on localhost as not to give everyone around you access to your device.
We will also tell the server to use a certain directory within the www folder to serve our LabVIEW web module files.
Once the server is started we will need to open the inappbrowser and load our LabVIEW web module page.
Example:
// Wait for the deviceready event before using any of Cordova's device APIs.
// See https://cordova.apache.org/docs/en/latest/cordova/events/events.html#deviceready
document.addEventListener('deviceready', onDeviceReady, false);
var httpd = null;
var ref = null;
function onDeviceReady() {
httpd = ( cordova && cordova.plugins && cordova.plugins.CorHttpd ) ? cordova.plugins.CorHttpd : null;
startServer("mypage") // no need to stop as the server runs for the lifetime of the program.
}
function startServer( wwwroot ) {
if ( httpd ) {
// before start, check whether its up or not
httpd.getURL(function(url){
if(url.length > 0) {
//server already up
} else {
/* wwwroot is the root dir of web server, it can be absolute or relative path
* if a relative path is given, it will be relative to cordova assets/www/ in APK.
* "", by default, it will point to cordova assets/www/, it's good to use 'htdocs' for 'www/htdocs'
* if a absolute path is given, it will access file system.
* "/", set the root dir as the www root, it maybe a security issue, but very powerful to browse all dir
*/
httpd.startServer({
'www_root' : wwwroot,
'port' : 8080,
'localhost_only' : true
}, function( url ){
// if server is up, it will return the url of http://<server ip>:port/
// the ip is the active network connection
// if no wifi or no cell, "127.0.0.1" will be returned.
try{
ref = cordova.InAppBrowser.open('http://127.0.0.1:8080/index.html', '_blank', 'location=no');
} catch(err) {
alert(err.message);
}
}, function( error ){
alert("failed to start server: " + error);
});
}
});
} else {
alert('CorHttpd plugin not available/ready.');
}
}
5. Embed your LabVIEW web modules
Embedding your web module is as the same as a normal deployment to a server.
Build your LabVIEW application and copy the output files to the server location in your project.
("<application directory>/www/mypage/" in the example)
Now all that's left to do is to build the cordova project and run it.
cordova build
cordova run --emulator
or
cordova run android
The command prompt that shows up will show you where to find the apk file if you want to share it.
7.Using system resources
Now you are able to run LabVIEW web modules as an app we can start looking at accessing device resources.
On the cordova plugin API page (bottom of the left tab) you can search for resources that are natively supported.
There are also third party plugins that you can also use.
To get access to the resources we need to set up a flexible communication channel to and from the inappbrowser.
Luckily where already covered.
We can send messages to the base application using the postmessage protocol. On which we can add a listener to the inappbrowser in our base application.
We can cover the communication from the base application to the LabVIEW webapplication via the executeScript functionality of the inappbrowser.
To Facilitate this we will need to make some adjustments to the existing Index.js at <application directory>/www/js/index.js:
// Wait for the deviceready event before using any of Cordova's device APIs.
// See https://cordova.apache.org/docs/en/latest/cordova/events/events.html#deviceready
document.addEventListener('deviceready', onDeviceReady, false);
var httpd = null;
var ref = null;
function onDeviceReady() {
httpd = ( cordova && cordova.plugins && cordova.plugins.CorHttpd ) ? cordova.plugins.CorHttpd : null;
startServer("mypage") // no need to stop as the server runs for the lifetime of the program.
}
function startServer( wwwroot ) {
if ( httpd ) {
// before start, check whether its up or not
httpd.getURL(function(url){
if(url.length > 0) {
//server already loaded
} else {
/* wwwroot is the root dir of web server, it can be absolute or relative path
* if a relative path is given, it will be relative to cordova assets/www/ in APK.
* "", by default, it will point to cordova assets/www/, it's good to use 'htdocs' for 'www/htdocs'
* if a absolute path is given, it will access file system.
* "/", set the root dir as the www root, it maybe a security issue, but very powerful to browse all dir
*/
httpd.startServer({
'www_root' : wwwroot,
'port' : 8080,
'localhost_only' : true
}, function( url ){
// if server is up, it will return the url of http://<server ip>:port/
// the ip is the active network connection
// if no wifi or no cell, "127.0.0.1" will be returned.
try{
ref = cordova.InAppBrowser.open('http://127.0.0.1:8080/index.html', '_blank', 'location=no');
ref.addEventListener('message', messageCallBack); when get a message
ref.addEventListener('loadstop', loadStopCallBack);// on inappbrowser ready
} catch(err){
alert(err.message);
}
}, function( error ){
alert("failed to start server: " + error);
});
}
});
} else {
alert('CorHttpd plugin not available/ready.');
}
}
function loadStopCallBack(params){
ref.executeScript({ code: ""}); //initializes the cordova library in our LabVIEW web application. We cant do post messages until this has been executed
}
function messageCallBack(params){
try{
eval(params.data.to_execute_code); // Execute the string send from the LabVIEW application as code
} catch(error){
alert(error);
}
}
With this alteration we can execute code coming from our LabVIEW web module application in the base application.
From here we can execute code asking for sensor information and push it back to the LabVIEW web module application using the executescript function of the inappbrowser.
The code to send depends on the plugin and the way you want to approach it.
To be able to send a post message we will need to create a little javascript library in our project:
In this library we will add the Javascript global "eval".
This javascript function allows us to execute strings as javascript code.
This function has a string as input and a javascript Reference as output. (We can cast the outputs using other functions, see example project)
Now to send code we can make a small "SendCommand.gvi" function that allows us to send strings to the base application to be executed:
This function will package the string we want to send into a JSON object and then message it in a postmessage to the base application.
And That's it. We can now access the API through our LabVIEW web application using the above function.
To request data we simply send the javascript code to access the resource defined in the help page of that resource and, if needed, the executescript method to send the data back to the LabVIEW application.
Battery Indicator Example:
Make sure the battery plugin is added
cordova plugin add cordova-plugin-battery-status
First we have to subscribe to the battery event listener:
Here the "onBatteryStatus" function is called every time there is a battery event.
When this happens we us the executeScript function to execute:
"var BatteryValue = "status.level
This is going to create the variable "BatteryValue" in our LabVIEW web module application and put in the batterylevel.
This value will be updated with each event.
Now all that is left to do is to read it it in our LabVIEW web module application:
The string function here is a small addition I made to the javascript library and is put beside the eval function.
It reads a javascript reference and translates it to a string.
How-Separate-2
Example code from the Example Code Exchange in the NI Community is licensed with the MIT license.
Fantastic work!
Before
<?xml version='1.0' encoding='utf-8'?>
<widget id="io.cordova.hellocordova" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>HelloCordova</name>
<description> A sample Apache Cordova application that responds to the deviceready event. </description>
<author email="dev@cordova.apache.org" href="http://cordova.io"> Apache Cordova Team </author>
<content src="index.html" />
<access origin="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
allow-intent href="geo:*" />
<platform name="android">
<allow-intent href="market:*" />
</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
</platform>
</widget>
@PeanutButterAndLabVIEW wrote:
Description
Description-Separate-1LabVIEW (NXG) web modules allow you to create custom web applications using a graphical environment.
The benefit of this is that it can run on any environment with a modern web browser. Like our phone!
Android and IOS devices allow webview based applications.
These are applications based on HTML, CSS and JavaScript but have access to system resources like:
media, sensors, network, phone info, …
This is great news when you have requirements that need access to these resources and/or must work offline.
This document focusses on android as I do not have access to a Mac so I can't compile for IOS.
However, almost everything should also translate to IOS, just a different target to compile for.
Description-Separate-2
How to Use
How-Separate-11. Application Builder
There are multiple environments available to create your own mobile application.
This document will focus on Apache Cordova.
It is an amazing platform that adds a level of abstraction so you don't have to worry about your target system (Android or IOS) and it has a simple interface to the systems resources.
To create android applications I used the following installation tutorial by Tom Spencer to install Apache Cordova and all resources needed.
The added benefit of this tutorial is that you will also have access to a full Android emulator!
*NOTE:
The newest release of Android Studio (2020.3.1) does not support cordova out of the box.
When configuring Android Studio, please set the Build tools to an earlier version. I've been using 29.0.2 instead of 31 with great success.
If you are using and emulator, please use an image with an API level of 29/Android 10.0 or lower. Higher version are yet to be supported by Cordova.
This does not mean that it wil not run on your newest device. This has all to do with how the emulator communicates with the outside world.
2. Start a Project
To start a project we need to open a command window. (type "cmd" in your start bar)
Navigate to the location where you want to store the project. ("cd" and "dir" are your friend)
Now initialize your Cordova project in the CMD window.
cordova create <Application Name>
cd <Application Name>I will refer to this location as the application directory
To build your application execute the following in your application directory:
cordova build android
If you want to test your build application in an emulator execute the following in your application directory:
cordova run --emulator
If you want to test your build application on your android device execute the following in your application directory:
cordova run android
3. Prepare the project for your LabVIEW web module
LabVIEW web modules have a pesky requirement. Resources need to be loaded via HTTP, even when they are local.
This means we can't just plop them in our project as a panel and build it.
To remedy this we have to embed a HTTP server within our application.
The httpd plugin for cordova is a nice plugin that can do this for us.
To install it type the following in your application directory:
cordova plugin add cordova-plugin-httpd
Unfortunately I have not found a way that works to directly embed pages on the server into the index.html file of cordova.
I do know this is possible in normal browsers so it seems something that is disabled on purpose.
In order to get around this limitation there is a system resource we can use.
The in app browser allows us to embed a webviewer on top of our application.
You have seen this resource when clicking on a add in YouTube.
To install it type the following in your application directory:
cordova plugin add cordova-plugin-inappbrowser
Unfortunately this will not work straight out-of-the-box on android.
If we would load a page we would only see an "err_cleartext_not_permitted" error message.
To remedy this you will have to alter the "config.xml" file in your application directory.
Please add the following alterations:
Before
<?xml version='1.0' encoding='utf-8'?>
<widget id="io.cordova.hellocordova" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>HelloCordova</name>
<description> A sample Apache Cordova application that responds to the deviceready event. </description>
<author email="dev@cordova.apache.org" href="http://cordova.io"> Apache Cordova Team </author>
<content src="index.html" />
<access origin="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
allow-intent href="geo:*" />
<platform name="android">
<allow-intent href="market:*" />
</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
</platform>
</widget>
After
<?xml version='1.0' encoding='utf-8'?>
<widget id="io.cordova.hellocordova" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:cdv="http://cordova.apache.org/ns/1.0">
<name>LabVIEWonYourPhone</name>
<description>
Simple LabVIEW application for a mobile device. Tested on Android.
</description>
<author email="natan.biesmans@hotmail.com" href="https://www.linkedin.com/in/natan-biesmans-2875ab101/">
Natan Biesmans
</author>
<content src="index.html" />
<access origin="*" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<platform name="android">
<allow-intent href="market:*" />
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
<application android:usesCleartextTraffic="true" />
</edit-config>
</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
</platform>
</widget>
In short:
- Add the following in the opening "widget" tag.
xmlns:android="http://schemas.android.com/apk/res/android"
- Add the following in the platform with name android
<edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application">
<application android:usesCleartextTraffic="true" />
</edit-config>
4. Prepare your application code to load your LabVIEW web modules
Now we the project allows us to embed and show the web modules we have to create some startup code in order to make sure everything is working as intended.
We will do this in the index.js file. You can find it at: <application directory>/www/js/index.js
On startup we will need to launch the server. The server is configured to only run on localhost as not to give everyone around you access to your device.
We will also tell the server to use a certain directory within the www folder to serve our LabVIEW web module files.
Once the server is started we will need to open the inappbrowser and load our LabVIEW web module page.
Example:
// Wait for the deviceready event before using any of Cordova's device APIs. // See https://cordova.apache.org/docs/en/latest/cordova/events/events.html#deviceready document.addEventListener('deviceready', onDeviceReady, false); var httpd = null; var ref = null; function onDeviceReady() { httpd = ( cordova && cordova.plugins && cordova.plugins.CorHttpd ) ? cordova.plugins.CorHttpd : null; startServer("mypage") // no need to stop as the server runs for the lifetime of the program. } function startServer( wwwroot ) { if ( httpd ) { // before start, check whether its up or not httpd.getURL(function(url){ if(url.length > 0) { //server already up } else { /* wwwroot is the root dir of web server, it can be absolute or relative path * if a relative path is given, it will be relative to cordova assets/www/ in APK. * "", by default, it will point to cordova assets/www/, it's good to use 'htdocs' for 'www/htdocs' * if a absolute path is given, it will access file system. * "/", set the root dir as the www root, it maybe a security issue, but very powerful to browse all dir */ httpd.startServer({ 'www_root' : wwwroot, 'port' : 8080, 'localhost_only' : true }, function( url ){ // if server is up, it will return the url of http://<server ip>:port/ // the ip is the active network connection // if no wifi or no cell, "127.0.0.1" will be returned. try{ ref = cordova.InAppBrowser.open('http://127.0.0.1:8080/index.html', '_blank', 'location=no'); } catch(err) { alert(err.message); } }, function( error ){ alert("failed to start server: " + error); }); } }); } else { alert('CorHttpd plugin not available/ready.'); } }
5. Embed your LabVIEW web modules
Embedding your web module is as the same as a normal deployment to a server.
Build your LabVIEW application and copy the output files to the server location in your project.
("<application directory>/www/mypage/" in the example)
Now all that's left to do is to build the cordova project and run it.
cordova build
cordova run --emulator
or
cordova run android
The command prompt that shows up will show you where to find the apk file if you want to share it.
7.Using system resources
Now you are able to run LabVIEW web modules as an app we can start looking at accessing device resources.
On the cordova plugin API page (bottom of the left tab) you can search for resources that are natively supported.
There are also third party plugins that you can also use.
To get access to the resources we need to set up a flexible communication channel to and from the inappbrowser.
Luckily where already covered.
We can send messages to the base application using the postmessage protocol. On which we can add a listener to the inappbrowser in our base application.
We can cover the communication from the base application to the LabVIEW webapplication via the executeScript functionality of the inappbrowser.
To Facilitate this we will need to make some adjustments to the existing Index.js at <application directory>/www/js/index.js:
// Wait for the deviceready event before using any of Cordova's device APIs.
// See https://cordova.apache.org/docs/en/latest/cordova/events/events.html#deviceready
document.addEventListener('deviceready', onDeviceReady, false);
var httpd = null;
var ref = null;
function onDeviceReady() {
httpd = ( cordova && cordova.plugins && cordova.plugins.CorHttpd ) ? cordova.plugins.CorHttpd : null;
startServer("mypage") // no need to stop as the server runs for the lifetime of the program.
}
function startServer( wwwroot ) {
if ( httpd ) {
// before start, check whether its up or not
httpd.getURL(function(url){
if(url.length > 0) {
//server already loaded
} else {
/* wwwroot is the root dir of web server, it can be absolute or relative path
* if a relative path is given, it will be relative to cordova assets/www/ in APK.
* "", by default, it will point to cordova assets/www/, it's good to use 'htdocs' for 'www/htdocs'
* if a absolute path is given, it will access file system.
* "/", set the root dir as the www root, it maybe a security issue, but very powerful to browse all dir
*/
httpd.startServer({
'www_root' : wwwroot,
'port' : 8080,
'localhost_only' : true
}, function( url ){
// if server is up, it will return the url of http://<server ip>:port/
// the ip is the active network connection
// if no wifi or no cell, "127.0.0.1" will be returned.
try{
ref = cordova.InAppBrowser.open('http://127.0.0.1:8080/index.html', '_blank', 'location=no');
ref.addEventListener('message', messageCallBack); when get a message
ref.addEventListener('loadstop', loadStopCallBack);// on inappbrowser ready
} catch(err){
alert(err.message);
}
}, function( error ){
alert("failed to start server: " + error);
});
}
});
} else {
alert('CorHttpd plugin not available/ready.');
}
}
function loadStopCallBack(params){
ref.executeScript({ code: ""}); //initializes the cordova library in our LabVIEW web application. We cant do post messages until this has been executed
}
function messageCallBack(params){
try{
eval(params.data.to_execute_code); // Execute the string send from the LabVIEW application as code
} catch(error){
alert(error);
}
}With this alteration we can execute code coming from our LabVIEW web module application in the base application.
From here we can execute code asking for sensor information and push it back to the LabVIEW web module application using the executescript function of the inappbrowser.
The code to send depends on the plugin and the way you want to approach it.
- However the base idea stays the same.
- Create Javscript Code string
- send as a post message
- execute Javascript Code string
- (send code to execute in LabVIEW web module if executescript is added in the Javascript code string)
To be able to send a post message we will need to create a little javascript library in our project:
In this library we will add the Javascript global "eval".
This javascript function allows us to execute strings as javascript code.
This function has a string as input and a javascript Reference as output. (We can cast the outputs using other functions, see example project)
Now to send code we can make a small "SendCommand.gvi" function that allows us to send strings to the base application to be executed:
This function will package the string we want to send into a JSON object and then message it in a postmessage to the base application.
And That's it. We can now access the API through our LabVIEW web application using the above function.
To request data we simply send the javascript code to access the resource defined in the help page of that resource and, if needed, the executescript method to send the data back to the LabVIEW application.
Battery Indicator Example:
Make sure the battery plugin is added
cordova plugin add cordova-plugin-battery-status
First we have to subscribe to the battery event listener:
Here the "onBatteryStatus" function is called every time there is a battery event.
When this happens we us the executeScript function to execute:
"var BatteryValue = "status.level
This is going to create the variable "BatteryValue" in our LabVIEW web module application and put in the batterylevel.
This value will be updated with each event.
Now all that is left to do is to read it it in our LabVIEW web module application:
The string function here is a small addition I made to the javascript library and is put beside the eval function.
It reads a javascript reference and translates it to a string.
How-Separate-2