How to build IO sample for Embedded OPC UA Server

Tutorial to add IO nodes for Embedded OPC UA Server that represents ioThinx-4533 device IO modules status.

Table of Contents

* Beginners Guide
* User Scenario 1
* User Scenario 2
* User Scenario 3
* Compile Guide
* Appendix and FAQ

Beginners Guide

If you are a total beginner to this, start here!

  1. make sure your firmware version is above or equal to V1.1
    1 kversion
  2. download embeddedopcuaserver-plugin-dev_<version>_armhf.deb to 4533
  3. install embeddedopcuaserver-plugin-dev_<version>_armhf.deb
    1 dpkg -i embeddedopcuaserver-plugin-dev_<version>_armhf.deb
  4. Check io.conf under /usr/local/bin/embeddedopcuaserver/Config fits your ioThinx-4533 IO modules layout
  5. Execute systemctl start embedded-opcua-server to run up server
  6. Start using OPC UA client to browse IO data
  7. Other general usage of OPC UA Server, please reference How to build node sample for Embedded OPC UA Server tutorial

User Scenario

User Scenario 1: Add an diStatus node(config only)

Add Slot 1/Channel 4 diStatus to OPC UA Server, just modify io.conf under /usr/local/bin/embeddedopcuaserver/Config to

1 {
2  "slot": [
3  null,
4  [
5  {"ch": 0, "access": 1, "variant_type": 3, "func_type": 10, "name": "diMode", "description": "di mode"},
6  {"ch": 0, "access": 1, "variant_type": 3, "func_type": 11, "name": "diStatus", "description": "di status"},
7  {"ch": 1, "access": 1, "variant_type": 3, "func_type": 11, "name": "diStatus", "description": "di status"},
8  {"ch": 2, "access": 1, "variant_type": 3, "func_type": 11, "name": "diStatus", "description": "di status"},
9  {"ch": 3, "access": 3, "variant_type": 3, "func_type": 12, "name": "diCounterStatus", "description": "di counter status"},
10  {"ch": 3, "access": 3, "variant_type": 7, "func_type": 13, "name": "diCounterValue", "description": "di counter value"},
11  {"ch": 4, "access": 1, "variant_type": 3, "func_type": 11, "name": "diStatus", "description": "di status"}
12  ]
13  ]
14 }

Please notice:

User Scenario 2: Change module slot order(config only)

Adjust io.config to follow ioThinx-4533 real hardware modules order. Now slot 1 is 45MR-1600 and slot 2 is 45MR-2600 which the io.conf may looks like below.

1 {
2  "slot": [
3  null,
4  [
5  {"ch": 0, "access": 1, "variant_type": 3, "func_type": 10, "name": "diMode", "description": "di mode"},
6  {"ch": 0, "access": 1, "variant_type": 3, "func_type": 11, "name": "diStatus", "description": "di status"},
7  {"ch": 1, "access": 1, "variant_type": 3, "func_type": 11, "name": "diStatus", "description": "di status"},
8  {"ch": 2, "access": 1, "variant_type": 3, "func_type": 11, "name": "diStatus", "description": "di status"},
9  {"ch": 3, "access": 3, "variant_type": 3, "func_type": 12, "name": "diCounterStatus", "description": "di counter status"},
10  {"ch": 3, "access": 3, "variant_type": 7, "func_type": 13, "name": "diCounterValue", "description": "di counter value"},
11  {"ch": 4, "access": 1, "variant_type": 3, "func_type": 11, "name": "diStatus", "description": "di status"}
12  ],
13  [
14  {"ch": 0, "access": 1, "variant_type": 3, "func_type": 20, "name": "doMode", "description": "do mode"},
15  {"ch": 0, "access": 3, "variant_type": 3, "func_type": 21, "name": "doStatus", "description": "do status"},
16  {"ch": 1, "access": 3, "variant_type": 3, "func_type": 21, "name": "doStatus", "description": "do status"},
17  {"ch": 2, "access": 3, "variant_type": 3, "func_type": 22, "name": "doPulseStatus", "description": "do pluse status"},
18  {"ch": 2, "access": 3, "variant_type": 7, "func_type": 23, "name": "doPulseCount", "description": "do pluse count"},
19  {"ch": 3, "access": 3, "variant_type": 3, "func_type": 22, "name": "doPulseStatus", "description": "do pluse status"},
20  {"ch": 3, "access": 3, "variant_type": 7, "func_type": 23, "name": "doPulseCount", "description": "do pluse count"}
21  ]
22  ]
23 }

Now switch slot 1 and slot 2 and insert an 45MR-2404 in between which make ioThinx-4533 hardware modules order into slot 1: 45MR-2600, slot 2: 45MR-2404, slot 3: 45MR-1600 and if we don't want to monitor 45MR-2404, the io.conf may replace like below.

1 {
2  "slot": [
3  null,
4  [
5  {"ch": 0, "access": 1, "variant_type": 3, "func_type": 20, "name": "doMode", "description": "do mode"},
6  {"ch": 0, "access": 3, "variant_type": 3, "func_type": 21, "name": "doStatus", "description": "do status"},
7  {"ch": 1, "access": 3, "variant_type": 3, "func_type": 21, "name": "doStatus", "description": "do status"},
8  {"ch": 2, "access": 3, "variant_type": 3, "func_type": 22, "name": "doPulseStatus", "description": "do pluse status"},
9  {"ch": 2, "access": 3, "variant_type": 7, "func_type": 23, "name": "doPulseCount", "description": "do pluse count"},
10  {"ch": 3, "access": 3, "variant_type": 3, "func_type": 22, "name": "doPulseStatus", "description": "do pluse status"},
11  {"ch": 3, "access": 3, "variant_type": 7, "func_type": 23, "name": "doPulseCount", "description": "do pluse count"}
12  ],
13  null,
14  [
15  {"ch": 0, "access": 1, "variant_type": 3, "func_type": 10, "name": "diMode", "description": "di mode"},
16  {"ch": 0, "access": 1, "variant_type": 3, "func_type": 11, "name": "diStatus", "description": "di status"},
17  {"ch": 1, "access": 1, "variant_type": 3, "func_type": 11, "name": "diStatus", "description": "di status"},
18  {"ch": 2, "access": 1, "variant_type": 3, "func_type": 11, "name": "diStatus", "description": "di status"},
19  {"ch": 3, "access": 3, "variant_type": 3, "func_type": 12, "name": "diCounterStatus", "description": "di counter status"},
20  {"ch": 3, "access": 3, "variant_type": 7, "func_type": 13, "name": "diCounterValue", "description": "di counter value"},
21  {"ch": 4, "access": 1, "variant_type": 3, "func_type": 11, "name": "diStatus", "description": "di status"}
22  ]
23  ]
24 }

Please notice:

User Scenario 3: Add new IO func_type (coding & config)

Define new FUNC_TYPE in DataSet.h and add read/write function case in io_control_read() and io_control_write() of io_node_operator.c. If this io data need to be configured first during the initialization, then configure function should be add in add_io_nodes().

Modify io.conf to load hello do info and add new func_type 99 as below.

1 {
2  "slot": [
3  null,
4  [
5  {"ch": 0, "access": 1, "variant_type": 3, "func_type": 20, "name": "doMode", "description": "do mode"},
6  {"ch": 3, "access": 1, "variant_type": 3, "func_type": 99, "name": "helloDO", "description": "hello do"}
7  ]
8  ]
9 }

Compile Guide

Using native toolchain

  1. Copy sample.tar.gz contained in the programing guide from your PC to ioThinx:
    For example, if the IP address of the ioThinx is "192.168.127.254", use the following command:
    1 user@Linux:~$ scp sample.tar.gz moxa@192.168.127.254:~
  2. Extract the sample code
    1 moxa@Moxa:~$ tar zxvf sample.tar.gz
  3. Build IO sample code
    1 moxa@Moxa:~$ cd sample/mx_node_sdk/c/io
    Build via cmake & make
    1 moxa@Moxa:~/sample/mx_node_sdk/c/io$ cmake .
    2 moxa@Moxa:~/sample/mx_node_sdk/c/io$ make
  4. Move liboperator.so to OperatorLib
    1 moxa@Moxa:~/sample/mx_node_sdk/c/io$ cp liboperator.so /usr/local/bin/embeddedopcuaserver/OperatorLib/io/
  5. Restart server via systemd
    1 moxa@Moxa:~/sample/mx_node_sdk/c/io$ sudo systemctl restart embedded-opcua-server

Using cross toolchain

  1. Copy sample.tar.gz contained in the programing guide to your PC
  2. Extract the sample code
    1 user@Linux:~$ tar zxvf sample.tar.gz
  3. Build IO sample code
    1 user@Linux:~$ cd sample/mx_node_sdk/c/io
    Build via cmake & make
    1 user@Linux:~/sample/mx_node_sdk/c/io$ cmake . -DCMAKE_TOOLCHAIN_FILE=toolchain-cross.cmake
    2 user@Linux:~/sample/mx_node_sdk/c/io$ make
  4. Copy the sample program to ioThinx:
    For example, if the IP address of the ioThinx is "192.168.127.254", use the following command:
    1 user@Linux:~/sample/mx_node_sdk/c/io$ scp liboperator.so moxa@192.168.127.254:~
  5. Move liboperator.so to OperatorLib
    1 moxa@Moxa:~$ cp liboperator.so /usr/local/bin/embeddedopcuaserver/OperatorLib/io/
  6. Restart server via systemd
    1 moxa@Moxa:~$ sudo systemctl restart embedded-opcua-server

Appendix and FAQ

Programming Source Code

1 mx_node_sdk
2 c
3 ├── demo
4 │   ├── CMakeLists.txt
5 │   ├── demo_node_operator.c
6 │   ├── toolchain-cross.cmake
7 │   └── toolchain-native.cmake
8 ├── example
9 │   ├── CMakeLists.txt
10 │   ├── sample_node_operator.c
11 │   ├── toolchain-cross.cmake
12 │   └── toolchain-native.cmake
13 └── io
14  ├── cJSON.c
15  ├── cJSON.h
16  ├── CMakeLists.txt
17  ├── DataSet.h
18  ├── io_node_operator.c
19  ├── toolchain-cross.cmake
20  └── toolchain-native.cmake

IO Config Format

Explain how to use io.conf to fit your usage

Execute OPC UA server

1 moxa@Moxa:~$ sudo systemctl start embedded-opcua-server
2 moxa@Moxa:~$ sudo systemctl stop embedded-opcua-server
3 moxa@Moxa:~$ sudo systemctl restart embedded-opcua-server
4 moxa@Moxa:~$ sudo systemctl status embedded-opcua-server
5 moxa@Moxa:~$ sudo systemctl disable embedded-opcua-server
6 moxa@Moxa:~$ sudo systemctl enable embedded-opcua-server

Add/Delete OPC UA server account

1 moxa@Moxa:~$ sudo opcuauser --add moxa
2 moxa@Moxa:~$ sudo opcuauser --del moxa
3 
4 moxa@Moxa:~$ sudo opcuauser --help
5 Usage: opcuauser [options] username
6 
7 Options:
8  -a, --add Add user account
9  -d, --del Delete user account
10  -c, --change Change user password
11  -v, --verion Show version
12  -h, --help Show help
13 
14 Example:
15  opcuauser Add user account
16  opcuauser username Change user password
17  opcuauser --help Show help

Please notice that embedded-opcua-server should be reboot to take effect of user account list. Current user account list can be verify by checking /etc/opcuauser_passwd.

Anonymous user account setting

Default Anonymous user account is enable, use opcuauser command to add or delete anonymous user will not change anything. If OPC UA Server want to delete anonymous user account, please move to file /usr/local/bin/embeddedopcuaserver/OPCUAServerData/Config/OPCUA.json

1 ...
2  "SupportAnonymous":1,
3 ...

Modify setting to disable,

1 ...
2  "SupportAnonymous":0,
3 ...

Debug

log: Systemd service syslog must be active(running)

Limitation

  1. This OPC UA Server can only be executed on ioThinx-4533-LX.
  2. Please backup /usr/local/bin/embeddedopcuaserver/OPCUAServerData/Config before FWR upgrade.
  3. Use Lan1 internet setting as default connection setting.