Programming Help

Table of Contents

Getting Started
Auto Run Program Checkbox
Downloading
Debugging
First Program
Flasher Program
Setpoint Control
Middle Line Operator (:)
Local Variables
Labels Gotos and Gosubs
While and For Loops
Floating Point Features
Reference

Getting Started

When you first bring up the programming interface, you will get a message "No source file found! Please begin programming."

Click OK and we can get started. There are some things you should know about your programming interface however.

Auto Run Program Checkbox

The program has the ability to run as a PLC so that when the end of the program is reached, it will start at the beginning. Simply check the Auto Run Program checkbox at the top of the window and click the Download button to activate this mode of operation.

If the Auto Run Program is unchecked, it can help with debugging by being able to run through the program one PLC cycle at a time.
After a Download, clicking the Run button will run the program for one cycle typically. Clicking the Continue button after that will run the program one scan at a time.
Clicking the Run button will reinitialize all local variables and Run for one scan again.
Leaving this checkbox off however will prevent the program from running when the device is restarted or power cycled.

Downloading

When Downloading to the device, the code is compiled and on a successful compile, downloads the runtime code and the source code. The two can never be out of sync this way. If the program document has been modified, the Download button will enable telling you that a download is necessary. If you exit the page without doing this step, your program changes will be lost.

Debugging

Debugging tools have been provided for you to help troubleshoot programs and prove logical concepts. Some of those features are

A lot of syntax checking is done before the program is downloaded. This checker is programmed in the browser and will prevent the program from being downloaded if there is an issue. There is still the possibility of a runtime error, but the system will notify you if this happens while the development page is still active.

In the upper left hand corner of the interface is the "Navigation Menu". Hover over this black square to get some extended debug functionality and information. In there are some other debugging options:

The icon in the Navigation Menu square will flash when it communicates to the device. The system will warn you if there is a communication failure and a timeout has occurred. Communications and status are maintained while the programming editor is open.

Before we get started, hover over the Navigation Menu and click on the Console button. Leave this window open while we go through the next steps, you can drag this window out of your way for the time being.

First Program

Clear all text out of the document and enter:

print "Hello World"

This line can also be written as:

? "Hello World"

Be sure that the Auto Run Program is unchecked and click the Download button.

This is a simple string print to the console.

You should see the Current Status: field update with Downloading Program... then Downloading Source Code... and finally a Compile Successful! Usage: PGM:0%, VAR:0%, ARR:0%, SRC:0%

Click the Run button and take a look at the Console window we opened earlier.

You should see:

Hello World

You have just written, downloaded and run your first program!

Click the Auto Run Program checkbox and Download again. The program will run automatically now and repeat the program (each repeat is called a scan). You should see the program run as soon as the Download is complete and see the following output in the Console window:

Hello World
Hello World
Hello World
...

While the program does little, it shows the repetitive nature of the Auto Run Program configuration.

Click the Stop button to stop the program. You should see the Current Status: update to Program Stopped Line:1!

Click the New button to erase the program and say okay when the system asks for confirmation.

Let's do some real work.

Flasher Program

At the bottom of your screen are Register selectors. Click on the BOOLS selector and select DIGITAL_OUT_1. Clicking the Register in the selector will insert the clicked register into your program document.

The Register selectors contain all the registers that are currently configured as "Visible" in the unit's register interface. Any of these registers may be used in the program as variables. The register interface in the unit will also allow you to change "Tag Names". It is suggested that you do this first as any renaming of Registers that are already used in the program will need to be reconciled before the program can be downloaded.

It should be noted that some registers are read-only and will not allow values to be assigned (written) to them. The compiler will tell you if you attempt to write to any read-only registers at Download time.

Any changes to the configuration of the unit will automatically reload the Register lists.

Let's enter a new program:

if tpulse( 1, 1000 ) then DIGITAL_OUT_1 = not DIGITAL_OUT_1

The if statement looks to see if the output from the tpulse() is true and will read and invert (not) the DIGITAL_OUT_1 and write the inverted value back to DIGITAL_OUT_1

The tpulse() function says that if the enable is true (the 1 in the first argument) then it will return true for one program scan when the setpoint (1000) in milliseconds has elapsed.

Note: more information on tpulse() and other functions can be found under the Navigation Menu | Functions dialog and later in this document.

Check the Auto Run Program and click the Download button. You should hear and see DIGITAL_OUT_1 toggle every second.

The same if statement can also be written as:

if tpulse( 1, 10000 ) then

DIGITAL_OUT_1 = not DIGITAL_OUT_1

endif

The if-then-else-endif format allows you to place multiple lines inside of the if statement and also to nest other if statements.

Setpoint Control

You can also use the programming interface for setpoint control. This is a small example where

DIGITAL_IN_1 is the enable for the setpoint control.
UNIVERSAL_IN1 is the sump level or other input level.
DIGITAL_OUT_1 is a pump or other control to bring the level down.
ton() is an On Delay timer turned on by the conditional in the first argument.

if DIGITAL_IN_1 then

if ton( UNIVERSAL_IN1 > 12000, 10000 ) then

DIGITAL_OUT_1= 1

elseif ton( UNIVERSAL_IN1 < 8000, 10000 ) then

DIGITAL_OUT_1= 0

endif

endif

When DIGITAL_IN_1 is on the setpoint processing is enabled.
When UNIVERSAL_IN1 is greater than 12000 for 10 seconds then DIGITAL_OUT_1 is turned on.
Conversely when UNIVERSAL_IN1 is less than 8000 for 10 seconds, DIGITAL_OUT_1 is turn off.

Any one of the constants above can be replaced with a variable or register in the system for configurable setpoint control.

Middle Line Operator (:)

In many cases it may be desirable to execute several operations on one line. This increases code efficiency as many operations can be done with one line call or can be executed after a then or else statement.

c = 1 : d = 1

if ( c and d ) then DO1 = 1 : goto mythencode else DO1 = 0 : goto myelsecode

It should be noted that programming this way does not generate new line numbers so the code is smaller and more efficient. However code that is compacted in this manner, is more difficult to step through as breakpoints and steps are line based.

Local Variables

Local variables may be used and reused throughout the program. They simply need to be applied somewhere in the program and not match any register or label names. The first use of the variable set aside 4 bytes of memory for it and initializes it to zero. These variables are 32 bit signed values by default but may be "promoted" to floating point should the compile detect a floating point operation with that variable. See Floating Point Features...

The simple line:

a = a + 1

Declares the a variable and will increment the value of a each time the line is run. Once a is set it will retain its value through the program life. Stopping and Running the program or restarting the unit (with Auto Run Program checked) will reset it as well. Try running the above little program and look at the Watch List window to see the value increment.

The "a =" is an assignment and can also be written as let a = a + 1

If you want to use this functionality to count pump starts for instance you could do something like this:

if redge( DIGITAL_IN_1 ) then

a = a + 1

endif

The redge() function looks for a false to true transition of DIGITAL_IN_1 and returns true for one scan and the a variable is incremented.

Note: a word of warning. If a register name is mistyped, there is a good possibility that the compiler will count that register as a local variable. You can see when this happens by looking in the Watch List window to see what local variables are defined. If one of them is intended as a register, it can cause the program to execute in ways that are not intended.

Labels Gotos and Gosubs

The system can jump over large groups of code and even call sub-routines.

The format of a goto and label is:

if (some condition) then goto mylabel

(some code you don't want to execute)

#mylabel

(some code you do want to execute)

Labels always begin in the first column with a # pound operator and a name immediately after.

Some rules are:

Labels must start with a letter and be alpha-numeric (can have underscores) with no spaces.
They must not match any other register or local variable names.
Labels are not code in themselves just markers to the next piece of active code so breakpoints cannot be set on that line.

If you goto a line that is above where the goto is called, a compile time warning is generated and logged in the console:

"Compile WARNING <line#> GOTO jumps to previous line <label or line#>!".

This is to let you know that you are interrupting the scan of the program and might possibly be bypassing code in the rest of the scan cycle. In this case only code between the label and the goto will get executed and it is only a warning so the code is allowed to download after a compile.

The gosub command uses the same label format as goto but with a return key word.

if (some condition) then gosub mylabel ' jump to sub-routine

(continue code execution after sub-routine call)

end 'end program so we don't run into the sub-routine

#mylabel 'start of sub-routine

(some code you do want to execute)

return 'return from sub-routine

Goto and gosub commands cannot use the same labels as a gosub requires a return statement. If a return statement is executed and there has been no gosub to call it, a runtime error will result. If the Auto Run Program is checked then the end keyword will tell the program to jump back to the start. The end keyword will keep the program from executing the sub-routines. Without the end keyword the program will continue executing to the return keyword and will generate the error RETURN without GOSUB.

While and For loops

A while loop is an iterative execute while a test is true.

while condition

(iterative code)

endwh

'accumulate the number of seconds that DIGITAL_IN_1 is off

while not DIGITAL_IN_1

if tpulse( 1, 1000 ) then

a = a + 1

endif

endwh

It should be noted that while the test condition is true, that no other code in the program will run until the while loop test is false. This should be taken into careful consideration when implementing while loop functionality in a system that uses a repetitive program scan like this one.

A FOR loop is a good tool for iterating a known number of times.

a = $11223344 'set a to a constant hex value

for i = 1 to 4 'iterate 4 times

a = shftr( a, 32, 8 ) 'shift a 8 bits to the right each operation

print hex( a ) ' print the hex value to the console

next i ' next iteration

' at the end of loop i will be equal to 5

The shftr (shift right) function will shift the a value 8 bits and operate on all 32 bits of the a variable. Notice that a is set to a known value with the $ operator. This operator tells the system this is a hexadecimal value.

Print will output the function hex() to the console. When running this little program with the Auto Run Program option off ( and clicking the Run button after Download ) the console output will be:

00112233
00001122
00000011
00000000

A for loop can also be used to step through values by a certain step amount.

for a = 0 to 25 step 5 'iterate 5 times incrementing a by 5 each time

print a 'print a to output console

next a 'next iteration

print a 'print final value to console

The console output for this program will be:

0
5
10
15
20
25
30

Notice that the final value of the variable a is 30 and not 25. The test the for loop to exit at the end of the to keyword is always a "greater than" test and not a "greater than or equal to" test as the variable is first incremented by the step then the limit is checked.

For loops may also be used in reverse with a negative step value:

for a = 25 to 0 step -5 'iterate 5 times incrementing a by -5 each time starting at 25

print a 'print a to output console

next a 'next iteration

print a 'print final value to console

Variables may also be used in any or all of the FOR statement parameters. If variables are used and set inappropriately, a runtime warning will be generated and may be checked in the ERROR register as 114.

NOTE: A FOR / NEXT statement is an integer only operation see Integer Only Functions

Reference

Errors and Warnings

There are three classifications of errors that can occur in the system.

Compiler Errors are errors that occur when a download attempt is made and will stop the download of new code until they are fixed.

Runtime Warnings are errors that occur in the execution of the program, but may be read and recovered from.

Severe Runtime Errors occur after the program has been downloaded and run. These errors are catastrophic and will stop the program execution.

Compiler Errors

Compiler errors happen when a download is attempted. The compiler checks that the syntax, most operations and data types are legal. When Downloading, the compiler will stop on any error and present the line number where the error occurred if possible. The download operation is aborted.

Table of Compiler errors:

* indicates those errors that can also be Warning errors (see Warning Errors).

Error Call out
Error Explanation

* "Missing comma"

A comma was not found where required

* "Syntax Error"

A general syntax issue has been encountered

* "Invalid hex digit"

A conversion from a hexadecimal constant or string found an invalid character.

"Constant value too large"

A constant value of greater than a 32 bit number has been specified.

"Invalid expression"

Usually associated with a math or IF statement test and the system cannot evaluate the result.

"Data type mismatch"

An unexpected variable data type has been used in an operation.

"Illegal operator"

A math or logical operator was expected but not found in a logic or math expression.

"Illegal variable"

The variable name format has been violated.

"Missing quote"

A quote was expected at the end of a string constant.

"Equals '=' required"

An assignment or a FOR statement is missing the "=" operator.

"Expected open or close paren"

An open or close paren was expected at the start or end of an array or function call.

"Illegal data type"

A string was submitted where a numerical variable was expected for example.

"Undimensioned array"

An array access was attempted with no corresponding DIM() statement for that array variable.

"Illegal READ; No Data statement"

A READ was attempted but no DATA statement exists.

"Illegal RESTORE; No Data statement"

A RESTORE statement was attempted but there are not DATA statements in the program.

"GOSUB without RETURN"

GOSUB to a label or line number but that section of code has no RETURN statement.

"GOTO or GOSUB Invalid Label"

The label referred to in the GOSUB or GOTO does not exist.

"No valid code after label"

GOTO or GOSUB label exists but there is no valid code after the label

"Illegal Assignment to Read/Only Variable"

An assignment is being attempted to a Register that is Read-Only.

"String cannot be assigned and passed as an argument"

This is specific to the RIGHT, LEFT and MID string functions. The string being written to is also being passed in as the string source argument.

"String type variable required"

The function required a string variable for an argument but some other data type was attempted.

"Invalid string or string too long"

The functions FIELDS, PARSE and COMOPEN require a one character string.

"String type variable or constant required"

A numeric variable or constant was passed to a function where a string variable or constant is required.

"DIM statements must come first in the program"

DIMension statements must be in the first lines of code before any other operation. They are done once at runtime.

"Missing THEN in IF statement"

An IF statement without a required corresponding THEN clause.

"Misplaced ENDIF"

No IF statement is associated with the ENDIF operator.

"Empty ELSE line required"

IF statement ELSE clause is out of sequence.

"ELSEIF requires multiline IF THEN"

An ELSEIF operator was encountered with no multiline IF THEN clause.

"ENDIF required multiline IF THEN"

A multiline IF THEN statement was found but there was not corresponding ENDIF statement to terminate the operation.

"Illegal IF statement"

An ENDIF, ELSE or ELSEIF was found with no corresponding multiline IF statement.

"Integer constant required in DIM() statement"

Something besides an integer constant was passed to the DIM() statement.

"Function returns a value that must be assigned"

A function is being called that has a return value that must be assigned to some variable or register.

"ENDWH required for WHILE"

A WHILE statement was found with no corresponding ENDWH terminator.

"Missing TO in FOR statement"

A FOR statement without a TO clause.

"FOR without NEXT"

A FOR statement was found with no corresponding NEXT terminator

"NEXT missing FOR statement"

A NEXT statement was found with no corresponding FOR statement.

"Invalid STEP constant cannot be zero"

In a FOR statement, the STEP parameter is declared as zero.

"Negative STEP constant required"

A FOR statement was configured with a starting constant that is greater than the TO constant value. In order to count properly a STEP parameter must be added that will be negative.

"Positive STEP constant required"

A FOR statement was configured with a starting constant that is less than the TO constant value and the STEP parameter was configured as a negative number.

"Invalid constants in FOR statement"

This usually means that zero was used in both the FOR = and TO parameters.

"Not enough contiguous registers for blocksize"

When a register blocksize is required, the registers in the system must be in contiguous order and enabled.

"Register type required"

A function requires a register input only and cannot support other data types such as constants or internal variables.

"Too many tokens in a single line"

There is a built-in limit of 127 tokens to every line of code. This error is usually the result of using the multiline token ":" to execute several operations on one line.

"Ambiguous logical operators; Use parenthesis"

An IF or WHILE conditional has multiple AND, OR, or EOR operators and cannot resolve the logic intended for normal rules of precedence. Use parenthesis to define the order of operation to get the proper result.

"String type variable required"

A String variable is required to be passed in to a function but a different variable type or constant was found.

"Invalid constant or constant out of range"

A constant is required or the constant that was specified is out of range for the calling argument of the function.

"Register type required"

A register is required for the calling parameter of a function. An internal variable cannot be used.

"Not enough contiguous registers for blocksize"

A block of registers is required for the function. The registers must be contiguous and writable.

"Misplaced Semicolon used only for print statements"

Semicolons are not allowed on the end of a line except for print statements.

"Internal Variable required"

Only an internal variable may be used as an argument. Some other data type was used.

"Compile WARNING <line#> GOTO jumps to previous line <label or line#>!".

Only code between the label and the goto statement will be executed possibly bypassing code that is intended to execute every scan. Caution should be used when operating the program this way.

"Float variable used where integer is required"

A function or operation that requires an integer has a floating point variable, register or constant applied to it. see Restricted Integer Only Functions...

"Float variable previously used in integer operation"

A variable that was previously used in an integer only operation is a candidate for promoting to floating point. see see Restricted Integer Only Functions...

"Possible loss of Floating Point precision"

An operation that resulted in a floating point output is being assigned to an integer register.

"Invalid Unicode character"

Strings require ASCII characters for assignments. An invalid UNICODE character was found in the string constant.

"Illegal Label Name"

A label name for a GOTO or GOSUB does not follow the rules for standard variable names.

"Duplicate label is not allowed"

Two or more labels with the same name were found during compile.

Runtime Warning Errors

Warning Errors occur at runtime after a successful Download. When warnings occur, the program will continue to execute but the warning will abort the offending operation. If the operation has a return value either from a function, variable or array is required, it will be returned as zero. If the warning occurs during an assignment to an array, the assignment is aborted.

Warning errors can be read (and are subsequently cleared) from the ERROR register. Since execution is continued, the ERROR register may be checked after an operation to implement a recovery strategy if needed. If the ERROR register is used, it is a good idea to read it first before attempting the operation that could generate a new warning. Then going into the function, the ERROR register will be preset to zero. Reading the ERROR register does not clear the Warning code itself that is read from the web page and the programming editor.

Table of Warning Errors

* Indicates those warnings that may also be Compile errors (see Compile Errors).

ERROR Register Value

Warning Message

Explanation

100

"Subscript out of range

An array access was attempted on an array element that does not exist. If the operation is an assignment then a 0 is assigned. If the operation is a read, then a zero is returned from that operation.

101

"Divide by zero"

A zero was in the denominator of a division operation. A zero result will be returned from the division expression.

102

* "Constant value too large"

A constant defined was too large to be entered into a system variable. A zero will be returned the operation that reads the constant.

103

"String conversion too long in parse() function"

A string being converted to a number in the PARSE function was either too large in value or contained too many characters to be parsed properly. A zero will be returned from the PARSE function.

104

"Out of data in READ or RESTORE"

A DATA operation usually a READ operation has been done and there are no more DATA statement elements. If this happens a RESTORE is automatically called and the first value of the DATA statement(s) is used.

107

"Invalid Register Access"

A register index that does not exist is being accessed by GETHOLD, GETCOIL, GETINPUT, GETSTATUS, PUTHOLD or PUTCOIL register access functions. The ERROR register may be checked after any GETx or PUTx

112

* "Invalid com port"

When using the COMOPEN, COMCLOSE, COMWRITE or COMREAD function, the port specified is not valid.

113

"Com port not open"

When using the COMCLOSE, COMWRITE or COMREAD function, the port specified is has not been opened with the COMOPEN function.

114

"Invalid FOR loop range"

A variable combination used as FOR TO parameters were both set to zero when the FOR statement was called.

115

"Invalid floating point number"

When using the FLOATTOINT function, the translation of bytes from a 32 bit value did not yield a valid floating point result and the system could not translate the floating point number into an integer.

116

"Floating point number out of range"

When using the FLOATTOINT function, the translation was valid but the value is too large to be assigned to a signed 32 bit integer.

117

"nlscale() Invalid Register Value(s) Configuration"

This can happen if registers in the list have duplicate values or values are not in ascending or descending order according to the first and last register values in the blocksize. nlscale() will return a zero when this happens.

118

"nlscale() Invalid Output Scaling"

Output scaling parameters are set to zero or minimum and maximum values are the same.

119

"nlscale() Invalid Register Blocksize"

This occurs if the registers specified by the startreg to blocksize are not of the same data type or the blocksize is less than the required three registers.

120

"nlscale() Invalid Input Start and End Values"

The first and last registers specified are either both zero or are both the same value.

121

 

"playxxxxx() Voice Option not supported"

Audio Voice option is not installed or is not available to play tracks or numbers.

Severe Runtime Errors

Severe Runtime Errors stop the execution of a program! They are catastrophic in nature and cannot allow the program to continue to run. Runtime errors are reported to both the editor and web interface of the device and should be reported to Technical Support.

Comments

Comments may be added to the code with a rem statement or an ' (apostrophe). Anything to the right of these markers will be colored in green and considered not part of the program. Comments may be placed at the beginning of a line or at the end of active code. Nested comments are not supported. Comments are only stored in the source code and have no effect on run-time code.

rem this is my line comment

'this is also a line comment

if DO1 then goto updlabel ' this is an end of line comment

Constants

There are several types of constants that may be used in the program.

Numerical Constants

-1
-1000
31415
1.105

Hex Constants

The $ operator in front of a numerical constant allows a hex number to be specified using A - F as digits in the value:

$aa
$55
-$FFFF

String Constants

String constants may be used to initialize strings or in a print statement as shown here:

print "a = ";a
print "Hex Value"; hex( a )

' print three values in column format commas separate output by inserting a tab at each comma
print "a","b","c"
print a, b, c

When using a print statement, placing a semi-colon operator after the last element will inhibit the carriage return/line feed from being printed and will print just a space instead. This can be handy for placing more data on the screen as the console is limited to 1000 lines of back buffer.

Care should used not to overload the print buffer as it will lose data if the program writes to the buffer faster than the web page can update (which is what clears the print buffer on the device). When the web page is not present, the print buffer will fill up and stop updating once it is full. While there is no harm in leaving them in, print statements are primarily for debugging and should be commented out or removed in the final runtime version of the code.

Math Operators

Standard math operator's order of precedence applies.

a = a + 1

Parentheticals may be used for complex statements and to force order of precedence.

y = m * x + b * ( x + 1 )

Math expressions may be used anywhere a variable can be used so an assignment to a local variable is not always necessary.

If (a + b) > 100 then...

+
Addition
-
Subtraction and negation
*
Multiplication
/
Division
mod
Modulus
^
Power
&
32 bit binary and
|
32 bit binary or
^|
32 bit binary exclusive or
inv
32 bit binary not (invert)

During a division operation, if the divisor is zero, a Divide by Zero Warning will be posted to the ERROR register as 101, and the result returned will be zero.

Strings and Arrays with Dim() Statement

There are two types of arrays used in the system. 32 bit integer arrays and string arrays. Both use the same dynamic memory resource. The Dim() statement(s) may only be configured at the start of a program are run one time at program startup. Dim statements must be before any other active code in the program or an error will be generated.

The array memory is referred to as ARR x% when the compile status returns successfully. The memory is limited to what is available on the target device. The percentage reflects how much of that memory is set aside after all the dim() statements are processed. If the memory is exceeded at compile time, the program will not be downloaded.

Numerical Arrays

To allocate and array of signed 32 bit integers the Dim() statement may be used in the following manner

dim array( [constant] ), array2( [constant] )

Only a constant number may be used to allocate the space used by the array. Dim() statements are only executed once at runtime and must be at the top of the program listing before any other active line. To assign a value to an array element by index the following may be used:

array( x ) = (some expression, register or variable)

Reading the array element is like any other variable outside or inside an expression but with the parenthetical index called a "subscript".

a = a + array( [ some variable or constant ] )

If a subscript is out of the range allocated or is a negative number, a Subscript Warning will be generated and may be read from the ERROR register as 100 and the array element will be zero. Care should be used when accessing any array by a variable subscript value.

Floating Point Arrays

You may also allocate a floating point array as well. To do this, just change the subscript specification to a floating point constant

dim arr( 3.0 )

Accessing the array is the same as the integer example above.

String Arrays

String arrays are special strings used to format and display human readable text as well as being used for ASCII or binary communication access via the comopen(), comwrite(), comread(), fields() and parse() functions. Only ASCII characters are allowed to be used in strings of any kind. UNICODE characters are not allowed.

String arrays are allocated using the following format:

dim $string( [constant] )

Notice the $ at the front. If the $ is missing, this array cannot be used as a string. This allocation will set aside the number of bytes specified + 3 management bytes for maximum length and current length. String arrays (called strings) may be assigned values in the following manner:

dim $string( 40 )
string
= "Some Text " + a + "," + b + "," + c

Where there are 32 bit variables, registers or constants, the elements are translated into ASCII text representing the 32 bit number. Binary values may be placed in the buffer using binary constants and appended to the string in the following format:

string = string + $0A 'append a carriage return character after the string

The same assignments may be accomplished via a \ control character like so:

string = string + "\r" 'append a carriage return character after the string

Other control characters are supported and may be added to any string within a string constant such as:

"\t" 'append a tab character
"\n" 'append a newline character
"\0" 'append a null character

Strings only support the + operator for appending.

You can use the hex(), hex2(), hex4(), and format() functions to format a number. You can also use the len(), find(), left(), mid() and right() string functions to parse and dissect an incoming string. See Print and String Functions for details:

a = 3462
string
= "a = " + format( a, 2 ) + " ft.\r" 'format a with 2 decimal places with units at the end followed by a carriage return

You may print a string to the console at anytime using the print command to see the format result:

print string

The above will show on the console as:

a = 34.62 ft.

To facilitate using binary protocols, strings may also be accessed by subscript and used to transmit and parse binary values.

To use a subscript and get the first byte from the above string you can use the expression:

print string( 0 ) which will print the ASCII value of the first character "a" of the string array:

97

You can use this in an if statement for example to qualify incoming values:

if string( 0 ) = 97 then

'more parsing code here

endif

If using a string subscript when writing a value (assignment) , only values from 0 to 255 are supported as one element is exactly one byte.

If using a variable for a subscript value, take care not to go negative or go beyond the length specified in the dim() statement. If this happens, a runtime subscript warning will be generated. If reading the subscript out of range, the returned value will be zero. If writing to the array, the value will not be written. This warning may be read from the ERROR register as 100.

Only ASCII characters are allowed to be used as string values. Unicode characters are not allowed and will generate a compile time error.

Data, Read and Restore Statements

The data keyword sets aside constants that can be used later by the read command to initialize variables to constant values. Think of it as an array in memory that cannot be modified at runtime. It is primarily used for initializing arrays or registers where iterative values are not appropriate.

dim arr( 3 )

data 35, 10, 22

restore ' start reading from the beginning of the data statement

for i = 0 to 2 'read the three values

read x 'read the next value

arr( i ) = x 'load the value into the array

print arr( i ); 'print each value

next i

This program reads each value of the data statement and stores it to the array. Then prints that array element. Each read command increments an internal pointer to the delimited data in the data statements. As each is read that internal pointer is moved to the next element. The output of the program is:

35 10 22

If the end of the data statements is reached a Warning is posted:

Out of data in READ or RESTORE

This error may be read from the ERROR register as 104 and may be checked after each read. On the warning the internal pointer is reset and the read statement will return the first data in the data statement(s) defined and continue on from there.

A restore command may be used to reset the internal data pointer to the first element.

read a 'read the first three elements and initialize a, b and c values
read b
read c

Logical Operators and If Statements

Logical operators are used in if, elseif and while statements to determine if a logical state is true. Parentheticals may be used to insure order of operation and/or improve clarity in the program. If multiple logical expressions such as multiple AND and OR, multiple AND and EOR or multiple EOR statements are used, parentheticals will be required. An error "Ambiguous logical operators; Use parenthesis" will be posted if these rules are violated.

if a and b then
...
elseif ( ( not a ) or ( not b ) ) and ( c >= d ) then
...
else 'neither the if or the elseif conditions are true
...
endif

while a < b
...
endwh

and
Logical AND
or
Logical OR
eor
Logical Exclusive OR
not
Logical NOT (may also use !)
=
Equal to
<
Less than
>
Greater than
<=
Less than or Equal to
>=
Greater than or Equal to
<>
Not Equal to

When evaluating logical expressions with and, or, eor, and not, the system will always evaluate a non-zero value as true and a zero value as false.

Functions

Functions may be used in place of a variable if they return a number based on the arguments passed in.

Functions that have internal data are noted below. These functions update internal data when they get executed and keep that data from one scan to the next. If execution is bypassed with an if statement or goto, then the internal data is not updated. If they are used in an if statement logically and'ed with another expression, they should be the first expression in the if statement. This way they will always run and update. You may also assign local variables the return value of these functions as an alternative ie. a = ton( DI1, 1000 ) then use that variable in your if statement.

All parameters of a function can be a variable, register or constant except where specified. Functions that return values are called out in front of the function as value, boolean or string based on the intended return usage. A string is returned for print-only and string assignment functions.

Numerical Functions

value sgn( variable )

If the number is positive a 1 is returned.
If the number is 0 a 0 is returned.
If the number is negative a -1 is returned.

value abs( variable )

If the number is negative, returns the reciprocal positive value.
A positive value is returned as-is.

Logical Functions

boolean redge( variable )

Detects a boolean value changing from false to true and returns true for one program scan.
Has internal data.

boolean fedge( variable )

Detects a boolean value changing from true to false and returns true for one program scan.
Has internal data.

boolean edge( variable )

Detects a boolean value change from true to false or false to true and returns true for one program scan.
Has internal data.

Binary Functions

value shftr( variable, radix, number of bits )

Returns the variable value binary shifted to the right starting from the right at the radix bit and shifting right the number of bits.
Typical values for the radix are 16 and 32. If radix of 16 is used then the upper 16 bits will be left alone. If a different radix value is used then the bits to the left of the radix are left alone.

value shftl( variable, radix, number of bits )

Returns the variable value binary shifted to the left starting from the right at the radix bit and shifting right the number of bits.
Typical values for the radix are 16 and 32. If radix of 16 is used then the upper 16 bits will be left alone. If a different radix value is used then the bits to the left of the radix are left alone.

value rotr( variable, radix, number of bits )

Returns the variable value binary rotated to the right starting from the right at the radix bit and rotating right the number of bits.
Typical values for the radix are 16 and 32. If radix of 16 is used then the upper 16 bits will be left alone. If a different radix value is used then the bits to the left of the radix are left alone.

value rotl( variable, radix, number of bits )

Returns the variable value binary rotated to the left starting from the right at the radix bit and rotating left the number of bits.
Typical values for the radix are 16 and 32. If radix of 16 is used then the upper 16 bits will be left alone. If a different radix value is used then the bits to the left of the radix are left alone.

unpack( variable, number of bits, boolean start register )

Reads the bits of the variable input from least significant to most significant writing them to boolean registers specified from the boolean start register to the number of bits specified. The boolean start register parameter must be a valid register in the system and all of the number of bits blocksize registers must be contiguous and writable or a compiler error will result.

value pack( number of bits, boolean start register )

Returns the encoded value of the boolean start register and the number of bits blocksize. The boolean start register parameter must be a valid register in the system and all of the number of bits blocksize registers must be contiguous or a compiler error will result.

Timer Functions

Each of the timer functions below has an optional third argument which can be an integer variable or register. When the timer is in the active state and not "timed out", the elapsed time will represent the amount of time in milliseconds since the timer function has been activated. The elapsed time will be reset to zero should the elapsed time reach the setpoint or the input has been deactivated.

It should be noted that using a timer function in an IF statement as a conditional, the timer function should be first so it always executes.

You may also use a logical boolean sequence and number comparisons in any combination as the activating argument:

PUMP_CALL = ton( PUMP_ENABLED and TANK_LEVEL <= PUMP_LEAD_SETPOINT, PUMP_ON_DELAY_SETPOINT )

NOTE: Timer functions are integer only operations see Integer Only Functions

boolean ton( variable, timeMs, [elapsedtime] )

On Debounce timer function.
If the input changes from false to true, the ton() function begins timing.
When the timeMs (time in milliseconds) has elapsed and the input has remained true for the entire timeMs time ton() returns true until the input goes false again.
If the input goes false, the ton() time is reset until the input goes true again and ton() begins timing again.
Any variable or integer register may be used for the elapsedtime parameter. This argument is optional. Elapsedtime is only shown when the timer function is actively timing. Otherwise it will be set to zero.
Has internal data.

boolean toff( variable, timeMs, [elapsedtime] )

Off Debounce timer function.
If the input changes from true to false, the toff() function begins timing.
When the timeMs (time in milliseconds) has elapsed and the input has remained false for the entire timeMs time toff() returns true until the input goes true again.
If the input goes true, the toff() time is reset until the input goes false again and toff() begins timing again.
Any variable or integer register may be used for the elapsedtime parameter. This argument is optional. Elapsedtime is only shown when the timer function is actively timing. Otherwise it will be set to zero.
Has internal data.

boolean tpulse( variable, timeMs, [elapsedtime] )

Pulse timer function.
When the input is true tpulse() will return a true as a one-shot every timeMs (time in milliseconds).
When the input is false tpulse will always returns false.
Any variable or integer register may be used for the elapsedtime parameter. This argument is optional. Elapsedtime is only shown when the timer function is actively timing. Otherwise it will be set to zero.
Has internal data.

Instrumentation Functions

boolean delta( variable, change setpoint )

Detect a change in variable by a setpoint amount.
If the input changes by the setpoint amount, the delta() function will return a true for one scan.
The function will continue to return false until the input has changed by the setpoint amount again.
Has internal data.

boolean cmplt( variable, setpoint, hysteresis )

If the variable is less than the setpoint the function returns a true. It will continue to return true until the variable is greater than the setpoint + the hysteresis parameter.
Has internal data.

boolean cmpgt( variable, setpoint, hysteresis )

If the variable is greater than the setpoint the function returns a true. It will continue to return true until the variable is less than the setpoint - the hysteresis parameter.
Has internal data.

value scale( input, inputMinimum, inputMaximum, outputMinimum, outputMaximum, outputOffset )

Linear scale function. Scales the input according to the inputMinimum and inputMaximum and scales that range to the outputMinimum and outputMaximum range. At the end of the operation, outputOffset is added to the scaled output.

Care should be used if any of the parameters are less than -32767 or greater than 32767 (larger than a 16 bit number). This could cause an overflow of the signed 32 bit calculation. The formula is calculated as:

( ( input - inputMinimum ) * ( outputMaximum - outputMinimum ) / ( inputMaximum - inputMinimum ) ) + outputMinimum + outputOffset

If the numerator of this formula is too large the output number will "Roll over" and will no longer be meaningful.

runtime( booleanInput, secondsInterval, runtimeRegister )

While the booleanInput is a non-zero, this function increments the runtimeRegister submitted every period based on the secondsInterval setpoint. This is useful for knowing how long a boolean is true. This function is intended for accumulating pump runtime. It is suggested that the runtimeRegister be an INT32 or UINT32 retained register to accumulate large values. This function does not return a value.

totalize( analogInput, secondsInterval, totalizeRegister )

This function accumulates the analogInput register into the totalizeRegister submitted every period based on the secondsInterval. It is suggested that the totalizeRegister be an INT32 or UINT32 retained register to accumulate large values. This function does not return a value.

integer floattoint( analogInput, decimalplaces, swapmode )

This function allows a 32 bit floating point number, brought in as 4 bytes to be translated to a 32 bit integer with a specified number of decimal places. The decimalplaces argument tells the system to multiply by 10, 100, 1000 etc... when decimalplaces is 1, 2, 3 etc... respectively up to 11 which is a multiplication of 100,000,000,000. This way very small floating point numbers may be translated to an integer with a significant number of digits to work with.

The swapmode parameter when set to 0 leaves the bytes in the order they were originally received or assigned. In some cases where Modbus or other protocols are involved the bytes may be stored in a format that is not valid.

Once the value is stored into a 32 bit register, we can fix up the byte ordering so that the proper floating point value is received. To provide an example, we start out with a known set of bytes such as $44332211 where each pair of characters represents 1 byte of data in hexadecimal for the examples below. This number the floating point representation of 716.5323.

swapmode 0 leaves the order of the bytes alone.
swapmode 1 reveres all the bytes in the word so if the number comes in as $11223344.
swapmode 2 reverses the upper and lower word if the number comes in as $22114433 with the upper and lower words swapped.
swapmode 3 reverses the upper and lower byte of each word if the number comes in as $33441122.

After the bytes have been moved to their proper location, then the floattoint multiplication and translation to a signed 32 bit variable are performed.

If you do not know what swapmode you need, after getting the 32 bit value into a register you can experiment to see which swapmode will work for you to get the proper values.

If the floating point value cannot be translated or the value returned is out of range for a signed 32 bit integer, a warning will be generated and may be read from the ERROR register directly after floattoint() is called.

115 = Invalid Floating point number
116 = Floating point number out of range

As an example let's convert PI to an integer number:

PI = $40490fdb ' floating point single precision (32 bit) version of PI 3.1415927
a = floattoint( PI, 8, 0 ) ' convert to a 9 digit integer with no byte translation
print a 'print the result

The console output will be:

314159264

integer intswap( analogInput, swapmode )

When using protocols such as Modbus, it may be necessary to decode 32 bit integer values from 2 16 bit registers. These may be presented in a variety of configurations depending on the device being read. First get the two 16 bit registers into a 32 bit register. From there, intswap() can move the bytes around to get the proper value.

The swapmode parameter when set to 0 leaves the bytes in the order they were originally received or assigned.

If we start out with a known set of bytes such as $44332211 for the examples below (each pair of characters represents 1 byte of data in hexadecimal) we can see how the swapmode parameter will operate on the data passed in to intswap().

swapmode 0 leaves the order of the bytes alone.
swapmode 1 reveres all the bytes in the word if the number comes in as $11223344.
swapmode 2 reverses the upper and lower word if the number comes in as $22114433 with the upper and lower words swapped.
swapmode 3 reverses the upper and lower byte of each word if the number comes in as $33441122.

If you do not know what mode you need, after getting the 32 bit value into a register you can experiment to see which swapmode will work for you to get the proper values.

integer inttofloat( analogInput, decimalplaces, swapmode )

This function allows an integer to be converted to a floating point value and stored into a 32 bit variable or register to be transmitted to or polled from a device that can use the floating point value. Since only integers are represented in this interface, there is no way to view the new floating point value other than on the remote device that uses it.

The translation first converts the value to a floating point number, then divides that number to represent the data with the number of decimal places specified making a useful floating point value from an integer value. For instance, a number like 12345 with two decimal places will be stored as an IEEE 754 formatted number which represents 123.45. The system can convert up to 22 decimal places so that very small numbers may be converted. If zero is specified then the number is converted to an integer in floating point form.

The swapmode parameter when set to 0 leaves the bytes in the order they were originally converted. In some cases where Modbus or other protocols are involved the bytes may be stored in a format that is not valid. The swapmode parameter allows this to be corrected before that data is transmitted or polled for.

Once the value is stored into a 32 bit register, we can fix up the byte ordering so that the proper floating point value is presented to the remote device. To provide an example, we start out with a known value that has a known set of bytes. 716.5323 is represented in a 32 bit number as $44332211 where each pair of characters represents 1 byte of data in hexadecimal for the examples below.

swapmode 0 leaves the order of the bytes alone.
swapmode 1 reveres all the bytes in the word so if the number comes in as $11223344.
swapmode 2 reverses the upper and lower word if the number comes in as $22114433 with the upper and lower words swapped.
swapmode 3 reverses the upper and lower byte of each word if the number comes in as $33441122.

If you do not know what swapmode you need, after getting the 32 bit value into a register you can experiment to see which swapmode will work for you to get the proper values to the remove device.

If the floating point value cannot be translated or the value returned is out of range, a warning will be generated and may be read from the ERROR register directly after inttofloat() is called.

115 = Invalid Floating point number
116 = Floating point number out of range

As an example let's convert PI to a floating point number:

PI = 314159264 ' floating point single precision (32 bit) version of PI 3.1415927
REG_PI = inttofloat( PI, 8, 0 ) ' convert to a 9 digit integer to floating point number with no byte translation
print hex( a )'print the result in hex

The console output will be:

40490fdb

integer ctu( input, setpoint, resetcounter, counter )

Up Counter with reset.

This function examines the input variable for a rising edge (must be 0 or false for at least one scan then set to true). Upon the rising edge detection the counter variable or register will be incremented once for each rising edge until the setpoint input is reached. Once the setpoint is reached the counter will no longer be incremented and the return value will then be 1 or true. While the counter input is less than the setpoint this function returns a false.

When the resetcounter input is a non-zero, the counter will always be set to zero and the ctu() will return a 0 or false. This function always returns false while the resetcounter input is set to true.

Has internal data. NOTE: Integer only operations see Integer Only Functions

integer ctd( input, setpoint, resetcounter, counter )

Down Counter with reset.

This function examines the input variable for a rising edge (must be 0 or false for at least one scan then set to true). Upon the rising edge detection the counter variable or register will be decremented once for each rising edge until the the counter reaches zero. Once zero is reached the counter will no longer be decremented and the return value will then be 1 or true. While the counter input is greater than 0 this function returns a false.

When the resetcounter input is a non-zero, the counter will be set to the setpoint and the ctd() will return a 0 or false. This function always returns false while the resetcounter is set to true.

Has internal data. NOTE: Integer only operations see Integer Only Functions


regdata( startRegister, constantValue1, [constantValue2], ... )

This function allows the initialization of a block of registers to constant values.
The startRegister parameter is the first register in the block. All subsequent contiguous registers must be configured to the same type ie. 16 bit, 32 bit or Floating Point.

The constantValueX parameters are a list of comma delimited constants to initialize the registers to starting at the submitted register. The number of constants is the number of registers initialized.

This function does not return a value. If the register block's data type is changed, then a recompile and download will be necessary.

regdata( N1, 0, 10, 20, 50, 100 ) ' initialize register N1 through N5 to these values

After a successful compile and download, the register(s) specified will be added to the Watch List.

floatValue nlscale( input, startRegister, blocksize, scale1, scale2, [scaleOutput] )

Apply a non-linear scale from a block of registers used as a lookup table and scale to an output value.

input is the value to be scaled.
startRegister is the first register in the list of registers to be used as the linearization points for the scale. The values must be ascending or descending order depending on the signal to be linearized. The list of register values cannot contain duplicates. Values within the list of register values MUST be in the range of the first and last registers or a warning will occur and a value of zero returned.
blocksize is the number of registers used as a lookup list to linearize to points in the scale. Registers used in the list MUST be of the same type ie. 16 bit, 32 bit, or Floating Point registers. A minimum of three registers is required to implement the scaling. The values of the registers must be in ascending or descending order with no duplicate values or a warning will occur after aborting the nlscale function. A zero is returned on any warning.
scale1 and scale2 are the high and low values to scale the linearized output to. These parameters are reversible and can apply any offset (say scaling from 4000 to 20000 for an analog output).

[scaleOutput] is an optional flag to use scale1 and scale2 to take a linear input and return a non-linearized the output instead. If set to TRUE (non-zero), the scale1 and scale2 represent the high and low points of the input parameter, then scale by percentage, and apply the non-linearized values of the register array. In this case the first and last registers of the table are used for the output scaling and the table can be in ascending or descending order to facilitate reverse scaling. The default for this flag is FALSE ( zero ) if not specified.

The return floatValue of this function is a floating point number but may be assigned to an integer. If this is the case then the output will be truncated to an integer meaning the decimal point portion of the number will be removed (not rounded).

In this simple example, only 5 points are used to keep the output simple but many more points may be utilized for a more accurate result over any curve.

if firstscan then regdata( N1, 100, 50, 20, 10, 0 ) ' on initialization (firstscan is true) set register N1 through N5 to these values from high to low (reverse scaling)
N13 = nlscale( UI1, N1, 5, 0, 10000 ) ' scale UI1 input and linearize to the above values then scale from 0 to 10000

The first and last registers are used as the high and low points of the input signal. In the above example, an input of 20 will represent 50% of the input scale.
This example will yield these numbers based on the UI1 input value:

Input Clamping
UI1 <input>
N13 <floatValue>
Output Clamping
Input Maximum
100
0
Output Minimum
95
250
90
500
85
750
80
1000
75
1250
70
1500
65
1750
60
2000
55
2250
50
2500
45
2916.67
40
3333.33
35
3750
30
4166.67
25
4583.33
20
5000
15
6250
10
7500
5
8750
Input Minimum
0
10000
Output Maximum

If the [scaleOutput] flag is set to TRUE (non-zero), then UI1 and N13 columns would be reversed making the input linear, and the output non-linear.

Clamping is applied in the following conditions:

If the input is less than Input Minimum then the input is clamped to Input Minimum.
If the input is greater than the Input Maximum then the input is clamped to Input Maximum.
If the calculated output is less than Output Minimum, then the output is clamped to the Output Minimum.
If the calculated output is greater than the Output Maximum, then the output is clamped to Output Maximum.

There are some configuration warnings that can happen if the register list is not initialized properly. These warnings are posted at runtime but some may only occur during the scaling process. If any of these warnings are encountered then the output value will be set to zero.

Warning
Meaning
nlscale() Invalid Register Value(s) Configuration A value within the list of registers is out of the range of Input Minimum and Input Maximum or numbers in the list of registers do not adhere to the ascending or descending rules.
nlscale() Invalid Input Start and End Values Input Minimum and Input Maximum are equal to each other and cannot be used.
nlscale() Invalid Output Scaling This typically means that two or more of the output scaling numbers are equal to one another.
nlscale() Invalid Register Blocksize Blocksize parameter is less than three.

After a successful compile and download, the registers specified will be added to the Watch List.

Communication Functions

The following functions are to ease the work of sending and receiving ASCII ( human readable text ) protocols over a serial port.

comopen( port, stringchar or 1 byte constant )

This sets up a serial port on the device to use the current settings in the configuration and take over the port. The port specified is opened and the value passed in is the terminator of the message and can be any single byte character. This character triggers the comread() command to read in the bytes from the port (see below):

comopen( 1, "\n" ) ' Open the port and look for a new line character as a terminator

comopen( 1, $ff ) ' Open the port and look for a binary 255 as a terminator

Only values from 0 to 255 or single string characters are allowed for the terminator. See String Arrays for details on special control character "escape sequences". Comopen does not return a value.

comclose( port )

Closes the port specified and releases it to the system for other purposes. A warning will be generated if the port has not been opened first. Comclose does not return a value.

value comwrite( port, string )

Writes string to the port specified and returns the number of bytes written out the port.

value comread( port, string )

When the terminator defined in the comopen() function is received, the comread function fills the string passed in with the data from the serial port and returns the number of bytes fill in the string. If the string is not large enough, the data will be truncated to the available space in the string. In this case, some data may be lost. If the return value is 0, a terminator has not been received.

ping( edgeenable, ipaddress, interface, statusvariable, timeoutms )

This function allows a ping command to be sent to any ipaddress when a positive transition is detected on the edgeenable input. To retrigger the ping, edgeenable must go to zero for one scan and then non-zero for one scan to fire the ping command again.

ipaddress may be a dimensioned string variable or a string constant consisting of a dotted ip address such as "192.168.237.199". The interface may be selected as a 1 for Ethernet or 2 for Cellular Data Connection.

When called this function will assign a value to the statusvariable passed as the last argument. This must be a local integer or integer register. It will begin the ping process and write the status to the statusvariable until the ping process is done or timed out according to the timeoutms parameter. If the ping is successful then a minimum value of 2 is returned. Any value greater than 2 represents the time the ping took to respond after being sent.

The ping function should be called then the variable monitored in code to determine if the ping was successful. The status values are:

STATUS
VALUE
EXPLANATION
Bad Interface
-1
The interface specified is not correct.
Invalid IP
-2
The IP address specified could not be decoded.
Interface Down
-3
The interface specified is inactive (cable unplugged?).
Ping Sent Timeout
-11
Timed out receiving ping message.
Timeout Ping Not Sent
-12
Interface busy timeout.
Invalid Response
-20
A message was returned but was different than expected.
Ping Start
0
Interface is inactive or ping has not been activated.
Ping Active
1
Ping has been sent waiting for a response.
Ping Success
Greater Than or Equal to 2
Ping response has been received. Response time in milliseconds.

ping( testping, "8.8.8.8", 1, pingstatus, 1000 )

if testping then

if ton( pingstatus < 2, 2000 ) then 'if the status is less than 2 after 2000 milliseconds

pingerror = -1 'post an error

else

pingerror = 0

endif

endif

Print And String Functions

These functions are used only with print and string array statements.

NOTE: These are Integer only operations see Integer Only Functions

value fields( string, delimiter )

This function returns the number of fields found with the given delimiter. A string with 5 comma delimited values will return a 5. This allows for looping through and using the following parse() function for reading the right number of values out of the string.

value parse( string, delimiter, decimalplaces )

This function may used anywhere an expression is used. Decodes a value one record at a time from a string. If a delimiter is encountered in the string, then the conversion stops there and removes the converted bytes and waits for the next call. Typically, Up to 10 digits including decimal places can be decoded if the number is a floating point number. The ASCII represented number including decimal places must not exceed the range of a 32 bit integer from −2,147,483,648 to 2,147,483,647 or a parse() function Warning will be posted and a zero returned for the value. This function is useful for parsing data from the ASCII comread( ) function for comma, tab or any other character delimited strings. If the decimalplaces parameter is set to zero, then all digits will be decoded regardless of decimal point up to the next delimiter or the end of the string. If no delimiter is encountered in the string, then the parse() function will stop at the end of the string allowing it to be used for parsing single numbers. A delimiter must be specified however.

val = parse( rcvbuf, "," ,0 )

This function can be used in conjunction with the fields() function to parse multiple records until the end of the string is encountered as in this example:

r = comread( rcv )

if r then ' if we have received bytes

flds = fields( rcv, "," ) ' get the number of fields based on a comma delimiter

for i = 0 to flds - 1 ' parse all fields

v = parse( rcv, "," ,2 ) ' get the next value from the rcv string with two decimal places

puthold( 11 + i, v ) ' write it to the next holding register

next i

endif

When using the parse function, a failure to parse can be detected as an Error 104 in the ERROR register.

string format( variable, decimalposition )

Prints a value to the console or to a string formatted with a decimal point at position specified from the right.

string hex( variable )

Prints a 32 bit hex value to the console or stores it to a string.

string hex4( variable )

Prints a 16 bit hex value to the console or stores it to a string.

string hex2( variable )

Prints an 8 bit hex value to the console or stores it to a string.

value find( searchstring, patternstring )

Search a string for a particular pattern. Returns the position of the string found (one based). Returns 0 of the patternstring was not found. The pattern string may be a string variable or a string constant.

value len( string )

Returns the current length of the string submitted.

string right( string, numberofcharacters )

Returns a string that is the number of characters from the right (the end of the string). The string that is being assigned cannot be the same string as the one passed to the function.

string left( string, numberofcharacters )

Returns a string that is the number of characters from the left (the start of the string). The string that is being assigned cannot be the same string as the one passed to the function.

string mid( string, position, numberofcharacters )

Returns a string that is the number of characters from the position submitted ( position is 1 for the start of the string ). The string that is being assigned cannot be the same string as the one passed to the function.

Event Logging

Event logging is the recording of formatted strings to a storage area known as the "Event Log" on the target unit. There is only one log area per unit and each logging function call will simply append to that area. The event log is circular meaning if it runs out of new space, the oldest space will be overwritten. The event log may be downloaded from the units main web interface.

log( string )

This function appends any string variable to the event log with no treatment of data. See Strings and Arrays with Dim() Statement for string usage.

logline( string )

This function appends any string variable to the event log adding carriage return and line feed characters per logline function call. See Strings and Arrays with Dim() Statement for string usage.

string timedate( integer )

This function allows you to return a time stamp, date stamp or both (space delimited). This may be used anywhere a string assignment or print function is used.

Integer input and return output strings:

Integer
Output String
1
"hh:mm:ss"
2
"mm/dd/yyyy"
3
"hh:mm:ss mm/dd/yyyy"

dim $logentry( 80 ) 'dimension the logentry string

'look for a change of at least 10 in either UI1 or UI2
if delta( UI1, 10 ) or delta( UI2, 10 ) then

'compose the data with a leading timestamp
logentry = timedate( 3 ) + "," + UI1 + "," + UI2

'write data to the event log on a single line
logline( logentry )

endif

Register Access Functions

Allows access to all registers in the target device's register map by index and the following register banks:

Bank Type
Description
Status Read-only boolean registers (Digital Inputs)
Coil Read/Write boolean registers (Digital Outputs)
Input Read-only integer registers (Analog Inputs)
Holding Read/Write integer registers (Analog Outputs)

These functions write a value to a register and do not return a value.

putcoil( RegisterIndex, value ) 'put a value to a writable coil register (does not return a value)

puthold( RegisterIndex, value ) 'put a value to a writable holding register (does not return a value)

These functions read a value from a register.

boolean getstat( RegisterIndex ) 'get a value from a read/only status register

value getinput( RegisterIndex ) 'get a value from a read/only input register

boolean getcoil( RegisterIndex ) 'get a value from a read/write coil register

value gethold( RegisterIndex ) 'get a value from a read/write holding register

Audio Voice Play Functions

These functions only apply if a audio voice option is installed with the controller. If the audio voice option is not installed and one of these function is called, then a runtime warning will be posted "playxxxxx() Voice Option not supported" but will not interrupt the operation of the program.

boolean playtrack( trackid, delayAfterPlay ) 'play a track on the audio option card specifying the time to wait before playing the next message

Each time this function is called in simply puts the trackid on the play queue. If there are tracks or numbers already submitted, they will play first.

To stop after the currently playing message has finished, call the function with trackid set to 0.

To stop all playing messages immediately and clear the play queue, call the function with trackid set to -1.

delayAfterPlay is the time in milliseconds to wait before moving on to the next track in the play queue (if there is one).

This function returns true if the play queue on the audio voice option card is full and cannot play anymore messages.

boolean playnumber( number, decimalPlaces, delayAfterPlay ) 'vocalize a number( and its decimal places ) on the audio option card specifying the time to wait before playing the next message

This function allows a number to be put on the play queue and vocalized.

decimalPlaces specifies the number of decimal places in the number.

If number is floating point, decimalPlaces limits the placeholders after the decimal point to be vocalized.

If number is an integer, then decimalPlaces inserts a false decimal point into the number and vocalizes as a floating point number.

delayAfterPlay is the time in milliseconds to wait before moving on to the next track in the play queue (if there is one).

This function returns true if the play queue on the audio voice option card is full and cannot play anymore messages.

 

Floating Point Features

Floating point calculations are available for both registers and internal variables. Registers that are configured for floating point storage are available for 32 bit IEEE 754 calculations.

Internal variables are typically signed 32 bit numbers but will be promoted to floating point type when used in floating point operations. This operation is automatic for the compiler and totally based on usage. If a fixed data type is needed then an N register should be used.

To promote an internal variable to floating point, it simply needs to be used as floating point operation somewhere in the code.

For example:

a = 1.1 'declare as float using a decimal point

b = 10

c = a + b 'c will equal 11.1 and a, b and c will be promoted to floating point

Since a is assigned a floating point value, c and b will also be promoted to floating point so that they may be used later in the program as floating point numbers.

If an integer register is assigned a value from a floating point variable, register or function return value, then the decimal portion of that floating point result will be truncated and only whole numbers are assigned to the integer register.

N1 = c 'continuation of the program above; N1 is an integer register

Here the result c which is 11.1 will be written to N1 which is an integer. N1 will then equal 11.

Another source of promotion to floating point would be the assignment from a register that is configured as a floating point register.

a = N3 'N3 is configured as a floating point register

Here, the compiler knows that N3 is a floating point register, and will automatically promote the a variable to floating point.

If later on in the program, a were assigned to say an integer register N1, a warning would be posted "Possible loss of Floating Point precision" telling the programmer that the resolution of the floating point variable may be lost. Sometimes this is intended as an easy way to truncate a value to its integer part so the operation is allowed.

Floating Point variables and registers show up in the Watch List and are color coded differently. The Watch List also has a Number of Decimals configuration so you can see more or less resolution on the Watch List display while debugging your program.

Restricted Integer Only Functions

In some functionality, floating point promoted variables and floating point registers are not allowed to be used. The following are integer only functions and could cause issue if variable(s) they are operating on are promoted to floating point:

TON, TOFF, TPULSE , PACK, UNPACK, LEN, FIND, PARSE, LEFT, MID, RIGHT, TOFLOAT and FOR / NEXT operations.

If a variable that is used in one of the above function gets used in the program in the capacity where floating point is needed, the compiler will attempt to promote the variable. If the variable is flagged as used in an integer only function, then an error will be posted:

"Float variable used where integer is required" or "Float variable previously used in integer operation"

With either of these errors, the console will pop up so that information where the promotion occurred can be viewed.

Pass :1
Promoted Variable 'A' to Float. Line:1
Error: Line# 3 Float variable used where integer is required.

Here Line 1 was where the promotion happened, Line 3 is where the promoted variable was used illegally.

One way to prevent this type of issue is to use integer registers instead of internal variables so that the data type is fixed.

Testing Zero or Non-zero for TRUE

Floating point variables and registers can be used to test for TRUE and FALSE states in IF THEN ELSEIF statements and in most other boolean functions (except for those mentioned above).

A floating point number of 0.0 or -0.0 evaluates as FALSE

Any other value will evaluate as TRUE.

Floating Point Specific Functions

The functions listed below always output a floating point number and accept floating point numbers as input(s). Any variables used with them as either inputs or return values will be promoted to floating point. As always, registers remain configured as they are at compile time.

It should be noted that if register types are changed from either 16 bit, 32 bit, or float data type to a different data type, a new compile and download will need to be executed. The program only knows a register's data type at compile time.

Trigonometric Functions

These function return various functions from degrees:

floatValue sin( degrees ) 'Get the sine of an angle.

floatValue cos( degrees ) 'Get the cosine of an angle.

floatValue tan( degrees ) 'Get the tangent of an angle

floatValue sinHyp( degrees ) 'Get the hyperbolic sine of an angle

floatValue cosHyp( degrees ) 'Get the hyperbolic cosine of an angle

floatValue tanHyp( degrees ) 'Get the hyperbolic tangent of an angle

floatValue degtorad( degrees ) 'Convert degrees to radians

These functions return degrees from various coefficients:

floatValue arcsin( sineCoeff ) 'Get the angle from a sine coefficient

floatValue arccos( cosCoeff ) 'Get the angle from a cosine coefficient

floatValue arctan( tanCoeff ) 'Get the angle from a tangent coefficient

floatValue arctanxy( x, y ) 'Get the angle from x and y coordinates

floatValue radtodeg( radians ) 'Convert radians to degrees

Floating Point Functions

floatValue expf( x ) 'Base-e exponential function of x, which is e raised to the power x

floatValue exp10f( x ) '10 to the power of x

floatValue logf( x ) 'The natural logarithm of x

floatValue log10f( x ) 'The common (base-10) logarithm of x

floatValue roundf( x ) 'Round a floating point value UP or DOWN to the nearest integer value

floatValue floorf( x ) 'Round a floating point value DOWN to the nearest integer value

floatValue ceilf( x ) 'Round a floating point value UP to the nearest integer value

floatValue toFloat( x, [ decimalPlaces ] ) 'Convert an integer value to a floating point value. decimalPlaces is optional.

This function requires an integer value only for x and returns a floating point value that can be assigned to a floating point register or variable. If a variable is used for the return value, the compiler will attempt to promote that variable to floating point but will NOT promote the x input variable. If the optional decimalPlaces parameter is provided, x is divided by by 10 * decimalPlaces. For example if x is 1049 and decimalPlaces is 1 then 104.9 will be returned. decimalPlaces is always evaluated as an integer. If x is a floating point value, variable or register, a "Float variable used where integer is required" compiler error will be posted. If decimalPlaces is not specified, then 0 decimal places is used in the calculation.

integerValue toInt( x, [ decimalPlaces ] ) 'Convert a floating point value to an integer value. decimalPlaces is optional.

This function is the reciprocal of the toFloat function above. It takes a floating point number and returns an integer value. If the return value is assigned to a variable, that variable will not be promoted to a floating point. decimalPlaces is optional and is assumed to be 0 if not specified. If decimalPlaces is specified, the function will multiply the x value by 10 for each decimal increment, then round the number to the nearest integer value.