The RTU-X can run a program that allows local intelligence to be incorporated into the device. This script, which is written in the user interface in an intuitive language and which bears many similarities to the C programming language, allows all kinds of operations to be carried out with inputs and outputs, slaves, log, etc.
The following describes the characteristics of the language and its use in the user interface.
The script should start with the declaration of all the variables to be used. It is not allowed to declare variables in the middle of the program.
After the declaration of variables, the program code must be written. This code runs only once, so if you want it to run continuously it must be placed inside an infinite loop.
The sintax is the following:
if (Condition1)
{
Instructions1
}
else if (Condition2)
{
Instructions2
}
else
{
Instructions3
}
When Condition1
is true, the Instructions1
are executed, on the contrary, if theCondition2
is true the Instructions2
are executed. On the contrary the Instructions3
are executed.
The blocks else if
and else
are opcional.
The sintax is the following:
while (Condition)
{
Instrucciones
}
The Instructions
inside the whilewhile
are executed continuously as long as the Condition
is true. The Instructions
may be void.
The sintax is the following:
for (Exp1; Exp2; Exp3)
{
Instructions
}
Exp1
is is an expresion that is executed only once when the loop starts. Generally Exp1
contains an expresion that initializes a counter which is used in the in the loop.
Exp2
is the expression that indicates when the loop should end, and therefore it is a conditional expression. This expression is evaluated at the beginning of each cycle in the loop, and the loop stops executing when this expression stops being satisfied. Therefore the loop might not run at all.
Exp3
is an expression that is executed at the end of each iteration, and is generally used to update the counter used in the loop.
In each iteration, all the statements within the Instructions
block are executed.
There are two types of variables, general purpose variables and system variables.
General purpose variables are those defined by the user in the script and can be of one of the following types:
Type | Description |
int | 16 bits signed integer. The range is from -32.768 to 32.767 |
uint | 16 bits unsigned integer. The range is from 0 to 65.535 |
long | 32 bits signed integer. The range is from -2.147.483.647 to 2.147.483.647 |
ulong | 32 bits unsignd integer. The range is from 0 to 4.294.967.295 |
float | 32 simple presision floating point numbers (IEEE 754) |
General purpose variables can be defined as arrays.
Some examples of defining variables:
int i; // Variable of type int called i
float f; // Variable of type float called f
int a[10]; // Array of 10 int values
Any general purpose variable can be defined with the prefix telemetry
, attribute
o shared
.
The variables must be defined as telemetry
or attribute
in order to be used in the functionslog
and report
.
The prfix shared
is used exclusively when the RTU-X is integrated by MQTT to the Nettra Telemetry+ system, in the case of variables that store configuration parameters that are adjusted from the system.
Some examples of defining variables:
attribute float f; // Variable of type attribute float called f
telemetry int t; // Variable of type telemetry int called t
Any general purpose variable can be defined with the prefix static
.
When defined in this way, the variable is stored in the non-volatile memory of the RTU-X, in such a way that its value persists even in the event of power and battery loss.
To give an initial value to a variable static
, it must be initialized when declared. In this way, the initial value is only loaded in the first execution of the script, but not in subsequent executions.
For example:
static int a = 10;
static int b[3] = {1,2,3};
There is a set of predefined variables related to the operation of the RTU-X and its peripherals that can be used in the script.
The following variables are available:
Variable | Description | Type |
INPUTS | ||
ain | Analog inputs Represents voltage in mV (0 - 10,000) for voltage inputs or current in uA (0 - 20,000) for current inputs. | int[8] |
pulses | Pulse count of digital inputs | ulong[2] |
MODBUS | ||
slave_error | It reflects the connection status between the RTU-X and the slaves. One bit per slave is used, where 1 indicates error. For example, bit 2 of the variable represents the connection status with slave 2. | int |
TIME | ||
hours | Current hour (0 – 24) | uint |
minutes | Current minute (0 – 59) | uint |
seconds | Current sencond (0 – 59) | uint |
day | Current day of the month (0 - 31) | uint |
month | Current month (1 – 12) | uint |
year | Current year | uint |
week_day | Day of the week (0 = sunday) | uint |
time_synced | Indicates whether the time of the device is synchronized. The variable can be 0 or 1. | int |
unix_ts_utc | Unix timestamp in seconds in UTC time | ulong |
unix_ts_local | Unix timestamp in seconds in local time | ulong |
SMS | ||
sms_message |
Index of the received message. If there are no messages received, the variable is -1. After receiving and processing a message, the user must set this variable back to -1. |
int |
sms_parameter | Received message parameter | float |
sms_phone | Index of the phone that sent the received message | int |
MODEM | ||
modem_on | In case the WAN module is not configured to turn on automatically when the RTU-X is turned on, setting this variable to 1 or 0 allows the modem to be turned on or off from the script. | int |
modem_status |
Its value depends on the operating status of the modem. Possible values are:
|
int |
modem_signal | Modem signal level in dBm | int |
LORA | ||
lora_on | In case the WAN module is not configured to turn on automatically when the RTU-X is turned on, setting this variable to 1 or 0 allows the LoraWan module to be turned on or off from the script. | int |
lora_status |
Its value depends on the operating status of the LoraWan module. Possible values are:
|
int |
lora_snr | Signal to noise ratio in dB measured by the LoraWan module in the last transmission. | int |
lora_rssi | Received Signal Strength Indicator in dBm measured by the LoraWan module in the last transmission. | int |
WIFI | ||
wifi_status |
Its value depends on the working status of the Wi-Fi interface. Possible values are:
|
int |
wifi_signal | Wi-Fi signal level in dBm | int |
MQTT | ||
mqtt_status |
Its value depends on the status of the MQTT connection. Possible values are:
|
int |
mqtt_pending_log | Number of records pending to be sent by mqtt. | ulong |
GPS | ||
latitude | Latitude measured by the GPS module | float |
longitude | Longitude measured by the GPS module | float |
altitude | Altitude measured by the GPS module | float |
speed | Speed measured by the GPS module | float |
BATTERY | ||
battery_percentage | Battery percentage | uint |
battery_status |
Its value depends on the operating status of the battery. Possible values are:
|
int |
SCANNER | ||
scanner_wifi_current_count | Current wifi device count | uint |
scanner_wifi_total_count | Total wifi device count | uint |
scanner_bluetooth_current_count | Current bluetooth device count | uint |
scanner_bluetooth_total_count | Total bluetooth device count | uint |
Any individual bit of any variable can be accessed by placing a period and the bit number after the variable name.
Example that stores bit 2 of variable x in bit 3 of variable y:
y.3 = x.2;
You can use an alias to refer to both general-purpose variables and system variables. You can define an alias to any variable, to an element of an array or even to a bit of a variable.
The following examples show the definition of different aliases:
alias error as slave_error.2; // Alias to the error of slave 3
alias analogica2 as ain[1]; // Alias to the analog input 2
If constants are to be used in the script, these can also be defined at the beginning of the script.
For example, to define a constant calledTHRESHOLD
whose value is 30, do the following:
const THRESHOLD = 30;
The compiler will replace the wordTHRESHOLD
with the value 30 whenever it appears in the script.
As in the case of variables, there is a set of predefined constants related to the operation of the RTU-X and its peripherals that can be used from the script when appropriate.
Operation | Description |
+ | Sum |
- | Substraction |
* | Multiplication |
/ | División |
% | Remainder of division |
& | Bitwise AND |
| | Bitwise OR |
^ | Bitwise XOR |
~ | Bitwise NOT |
&& | Logical AND |
|| | Logical OR |
! | Logical NOT |
== | Equal |
! = | Not equal |
> | Greater than |
> = | Greater than or equal to |
< | Smaller than |
< = | Smaller than or equal to |
The RTU-X can function as a master of up to 32 devices (from firmware version 3.3.02). Currently, these slaves can be Modbus over RS-485 or BLE (Bluetooth).
Queries can be defined for each slave. The types of query depend on the type of slave.
The configuration of the slaves and the queries must be done at the beginning of the script, before the definition of the variables and the code.
To define a slave, the reserved word slave
is used, indicating the configuration parameters, and then within the block of that slave, the queries are defined using the reserved word query
.
Here is the general way to define a slave and its queries:
slave(interface, …)
{
var_type var1 = query(query_type, …);
var_type var2 = query(query_type, …);
}
The parameters of the slave
and query
functions depend on the interface and query_type, and each case is detailed below.
The definition of the slave is carried out as follows:
slave(modbus_rs485_ext1, slave_id, polling_period, format)
slave(modbus_rs485_ext2, slave_id, polling_period, format)
where:
slave_id
: is the number tht identifies the Modbus slave.polling_period
: is the time interval between consecutive queries in seconds.format
: may belittle_endian
o big_endian
. Defines how bytes are ordered for variables of more than one byte. The most usual is little_endian
.var_type var1 = query(coils, address, r/w/rw);
var_type var2 = query(inputs, address, r);
var_type var3 = query(input_registers, address, r);
var_type var4 = query(holding_registers, address, r/w/rw);
where:
address
: is the address within the Modbus block (starting at zero).r/w/rw
: indicates that the variable to define is read only (r
), write only (w
) or read and write (rw
).
slave(ble, “mac”, timeout)
where:
mac
: is the mac address of the Bluetooth device.timeout
: It is the time that must pass, in seconds, without receiving any message to consider that there is a communication error with the slave.Up to the moment, we have integrated the possibility to define BLE sensors from Efento.
The definition of the queries is carried out as follows:
float var1 = query(efento, temperature); // Query of temperature
uint var2 = query(efento, humidity); // Query of relative humidity
float var3 = query(efento, pressure); // Query of atmosferic pressure
uint var4 = query(efento, onoff); // Query of on/off state sensors
uint var5 = query(efento, iaq); // Query of air quality
slave(sdi12_ext1,“address”, polling_period)
slave(sdi12_ext2,“address”, polling_period)
where:
address
: is the address of the device in the SDI-12 bus.polling_period
: is the time between queries.The definition of the queries is carried out as follows:
float name = query(sdi12, index);
The queries to the sensores are implemened C type commands according to the version 1.4 of the SDI-12 standard, published on January of 2019. It establihes that the concurrent measurement comand must be of the form <address>C<index>!
All the variables that are the result of a sdi12
query must be of type float
.
The language supports the definition of various tasks that are executed in the form of cooperative multitasking.
In a cooperative multitasking scheme, tasks voluntarily yield control periodically or when they are idle or logically locked.
The way multitasking is implemented in the script is by using the reserved word task
.
When a task is reached, it is executed until it is finished or control is relinquished. The way to cede control is through the use of any of the following functions: wait
, delay
, delay_loop
. These functions are explained below.
Here is a code example where two tasks coexist executing "in parallel":
while (1)
{
task
{
// This piece of code runs periodically every 1 second
delay(1000);
}
task
{
// This section of code runs periodically every 3 seconds
delay(3000);
}
}
Defining a task within another task is not allowed.
All variables are global. Defining local variables within a task is not allowed.
The maximum number of tasks that can be defined is 16.
Function | Description | |
delay(ulong time) |
Performs a wait of time milliseconds. If the function is called outside of a task, it hangs the entire script for the specified time. If the function is called from a task, the function only locks the task, and continues executing the other tasks. Example:
|
|
delay_loop(ulong time) |
Same as the It should only be used within tasks, and it is recommended for tasks that must be repeated periodically every certain time. Example:
|
|
sleep(ulong time) |
Causes the RTU-X to go into low power mode for time milliseconds. During that time the digital outputs maintain their state and the pulse count continues to run, but the script stops. When it returns from low power mode, the script continues to run from the line where the function was called. Example:
|
|
reset() |
Restarts the RTU-X. Upon restart, the script starts from the beginning. Variables that are not initialized at the beginning of the script will remain with the value they had before the reset. The variables that are initialized at the beginning of the script, will be initialized again, except in special cases (see Example:
|
|
set_green_led(uint mode) |
This function is valid only in the case that the green led is configured to be controlled from the script, it sets the operating mode according to the constants defined with prefix Ejemplo:
|
|
set_red_led(uint mode) |
Esta función es válida solo en el caso que el led rojo esté configurado para ser controlado desde el script. Example:
|
|
set_output(uint output, uint value) |
Turns the digital Example:
|
|
int = get_output(uint output) |
Returns the value (0 or 1) of the Example:
|
|
int = get_input(uint input) |
Returns the value (0 or 1) of the Example:
|
|
set_power(uint value) |
Turns the power output on or off depending on the value. value can be one of the constants defined with prefix Example:
|
|
log(telemetry/attribute value, ...) |
Saves the specified variables in the log. For this, they must be of type Calling the function passing several variables guarantees that all will be registered in the log with the same timestamp. For more information see section Log. Example:
|
|
report(telemetry/attribute value, ...) |
Saves the specified variables to a list in RAM to send by MQTT. For this they must be of type Example:
|
|
log_on_change(telemetry/attribute value) |
Saves the specified variable to the log only when it changes. The variable must be of type Example:
|
|
report_on_change(telemetry/attribute value) |
Saves the specified variable to the RAM log only when it changes. The variable must be of type Example:
|
|
ulong = set_timeout(ulong timeout) |
Set a timer to timeout in milliseconds. Returns an ulong value set to use later in the check_timeout function. |
|
check_timeout(ulong timer) |
Returns 1 if the time configured with the Example:
|
|
send_sms( uint phone_index, uint message_index, long/float param1, long/float param2) |
Send an SMS to the number configured in the Outgoing messages can include up to 2 numeric parameters anywhere in the message. To insert an integer variable ( If it is a Example:
|
|
float = flow( bool value, float liters_per_pulse, ulong debounce) |
The function calculates the water flow from the pulses of a flow meter. It must be called by passing in value the value of the pulse input, in liters_per_pulse the number of liters between pulses of the flowmeter and a debounce time in milliseconds to filter rebounds and noise in the flowmeter. |
|
float = filter( float value, uint size, uint average, ulong timeout) |
The The The function implements a sliding window with Example:
|
|
int = wait( bool condition, ulong timeout) |
The function waits for the If the function terminates because the If the function is called outside of a task, it stops the entire script until finished. If the function is called from within a task, the function only locks the task, and continues executing the other tasks. Example:
|
|
scale( float value, float x0, float y0, float x1, float y1) |
Performs a linear interpolation of Example:
|
|
float = pid( float value, float set_point, float kp, float ki, float kd) |
Implements a PID controller, where The RTU-X can implement up to 4 simultaneous PID controls. Example:
|
|
bool = alarm( bool condition, ulong timeout_start, ulong timeout_end) |
Implements an alarm with a start and end delay. If condition is true for more than timeout_start milliseconds, it goes into alarm state. If condition stops being true for more than timeout_end milliseconds, the alarm state ends. The function always returns 0 or 1 whether it is in an alarm state or not. |
|
bool = interval( ulong value, ulong start, ulong end) |
Checks if If |
|
ulong = sunrise( ulong day, ulong month, ulong year, float latitude, float longitude) |
By means of an internal astronomical clock, it returns the second of the day on which the sun will appear based on the date ( Returns the instant in seconds of the day in local time as The current second can be calculated as: In combination with the |
|
ulong = sunset( ulong day, ulong month, ulong year, float latitude, float longitude) |
By means of an internal astronomical clock, it returns the second of the day on which the sun will set based on the date ( Returns the instant in seconds of the day in local time as The current second can be calculated as: In combination with the |
|
pow(float x,float y) |
Returns the result of the base x raised to the power y . |
|
log_e(float x) |
Returns the logarithm of x with base e |
|
log_10(float x) |
Returns the logarithm of x with base 10. |
|
cos(float x) |
Retuns the cosine of x . With x in radians. |
|
acos(float x) |
Returns the arc cosine de x with -1 ≤ x ≤ 1 and the result in radians. |
|
sin(float x) |
Retuns the sine of x . With x in radians. |
|
asin(float x) |
Returns the arc sine de x with -1 ≤ x ≤ 1 and the result in radians. |
|
tan(float x) |
Retuns the tangent of x . With x in radians. |
|
atan(float x) |
Retuns the arc tangent of x . With x radians. |
|
sqrt(float x) |
Returns the square root of x . With x ≥ 0. |
|
abs(float x) |
Returns the absolute value of x . |