Skip to main content

ControlForge Specialized Utilities Guide

James M. Belcher Founder, JMB Technical Services LLC April 2026 | ControlForge v1.0.535


1. Overview

This guide covers specialized utility functions that don't fit into the major protocol or library guides.

CategoryFunctionsUse Case
KNX6Building automation (lighting, HVAC, blinds)
M-Bus12Utility meter reading (water, gas, heat, electric)
ZPL8Zebra label printing
Barcode6Barcode parsing and validation
URL8URL parsing and building
TLV/BER12Tag-Length-Value encoding (smart cards, ASN.1)
GSV/SSV17Get/Set System Value (Allen-Bradley compatibility)
ctrlX EtherCAT10Bosch Rexroth ctrlX I/O
DIR4Directory operations

2. KNX — Building Automation (6)

Control KNX/EIB devices (lights, blinds, HVAC) on a KNX/IP network.

(* Switch a light on/off *)
KNX_SWITCH('knx-gw:3671', '1/1/1', TRUE);

(* Dim to 75% *)
KNX_DIM('knx-gw:3671', '1/1/2', 75);

(* Set temperature setpoint (2-byte float) *)
KNX_SET_FLOAT('knx-gw:3671', '3/1/0', 22.5);

(* Set 1-byte value *)
KNX_SET_VALUE('knx-gw:3671', '1/1/3', 128);

(* Raw data send *)
KNX_SEND('knx-gw:3671', '1/1/4', 16#01, 16#FF);

(* Build group address from components *)
addr := KNX_GROUP_ADDR(1, 1, 1); (* "1/1/1" *)
FunctionReturnsDescription
KNX_SWITCH(target, addr, on_off)BOOLDPT 1.001 switch
KNX_DIM(target, addr, percent)BOOLDPT 5.001 dimming (0-100)
KNX_SET_VALUE(target, addr, byte)BOOLDPT 5.x 1-byte value
KNX_SET_FLOAT(target, addr, float)BOOLDPT 9.001 2-byte float
KNX_SEND(target, addr, bytes...)BOOLRaw data telegram
KNX_GROUP_ADDR(main, mid, sub)STRINGBuild "main/mid/sub"

3. M-Bus — Utility Metering (12)

Read utility meters (water, gas, heat, electricity) via M-Bus over TCP gateway.

(* Connect to M-Bus gateway *)
ok := MBUS_TCP_CONNECT('meter', '10.0.0.60:10001');

(* Request data from meter at address 1 *)
raw := MBUS_REQUEST_DATA('meter', 1);

(* Parse response *)
resp := MBUS_PARSE_RESPONSE(raw);
mfr := MBUS_GET_MANUFACTURER(resp); (* "KAM" *)
medium := MBUS_GET_MEDIUM(resp); (* "Water" *)
records := MBUS_GET_RECORD_COUNT(resp); (* 4 *)

(* Read individual records *)
FOR i := 0 TO records - 1 DO
value := MBUS_GET_RECORD_VALUE(resp, i); (* 1234.56 *)
unit := MBUS_GET_RECORD_UNIT(resp, i); (* "m3" *)
rtype := MBUS_GET_RECORD_TYPE(resp, i); (* "instantaneous" *)
END_FOR;

MBUS_TCP_CLOSE('meter');
FunctionReturnsDescription
MBUS_TCP_CONNECT(name, host_port)BOOLConnect to TCP gateway
MBUS_TCP_CLOSE(name)BOOLClose connection
MBUS_REQUEST_DATA(name, addr)ARRAYSend SND_NKE + REQ_UD2, get response
MBUS_PARSE_RESPONSE(bytes)HandleParse long frame
MBUS_GET_MANUFACTURER(h)STRINGManufacturer code
MBUS_GET_MEDIUM(h)STRINGMedium type (Water, Gas, Heat...)
MBUS_GET_RECORD_COUNT(h)INTData record count
MBUS_GET_RECORD_VALUE(h, idx)REALRecord value
MBUS_GET_RECORD_UNIT(h, idx)STRINGUnit (m3, kWh, etc.)
MBUS_GET_RECORD_TYPE(h, idx)STRINGRecord type
MBUS_BUILD_SND_NKE(addr)ARRAYBuild init frame
MBUS_CHECKSUM(bytes...)INTCalculate checksum

4. ZPL — Zebra Label Printing (8)

Build ZPL II commands for Zebra thermal printers.

(* Build a shipping label *)
label := ZPL_BEGIN(400, 600);
label := ZPL_TEXT(label, 50, 50, 30, 'SHIP TO:');
label := ZPL_TEXT(label, 50, 90, 20, '123 Main Street');
label := ZPL_TEXT(label, 50, 120, 20, 'Anytown, USA 12345');
label := ZPL_LINE(label, 50, 160, 300, 2);
label := ZPL_BOX(label, 40, 40, 320, 200, 3);
label := ZPL_QR(label, 250, 250, 5, 'https://track.example.com/PKG123');
zpl := ZPL_END(label);

(* Send to printer via HTTP or serial *)
HTTP_POST('http://zebra-printer:9100', zpl, 'text/plain');

(* Quick single-text label *)
quick := ZPL_LABEL('Part: 12345', 'Qty: 100', 'Date: 2026-04-05');
FunctionReturnsDescription
ZPL_BEGIN([width, height])HandleStart label
ZPL_END(h)STRINGFinalize ZPL string
ZPL_LABEL(lines...)STRINGQuick multi-line label
ZPL_TEXT(h, x, y, size, text)HandleAdd text
ZPL_FIELD(h, x, y, field_num)HandleVariable field placeholder
ZPL_BOX(h, x, y, w, h, thick)HandleDraw rectangle
ZPL_LINE(h, x, y, w, thick)HandleDraw horizontal line
ZPL_QR(h, x, y, mag, data)HandleQR code (magnification 1-10)

5. Barcode — Parse & Validate (6)

Parse raw barcode scanner input and validate check digits.

parsed := BARCODE_PARSE('0012345678905');
btype := BARCODE_GET_TYPE(parsed); (* "EAN-13" *)
data := BARCODE_GET_DATA(parsed); (* "0012345678905" *)

valid := BARCODE_VALIDATE_UPC('012345678905'); (* TRUE *)
check := BARCODE_CHECK_DIGIT('01234567890'); (* 5 *)
clean := BARCODE_STRIP(']E00012345678905'); (* Remove AIM identifier *)
FunctionReturnsDescription
BARCODE_PARSE(raw)HandleParse and detect type
BARCODE_GET_TYPE(h)STRINGType (EAN-13, CODE128, etc.)
BARCODE_GET_DATA(h)STRINGCleaned data
BARCODE_VALIDATE_UPC(code)BOOLValidate 12-digit UPC
BARCODE_CHECK_DIGIT(digits)INTCalculate Mod 10 check digit
BARCODE_STRIP(raw)STRINGRemove AIM identifiers

6. URL — Parse & Build (8)

(* Parse URL into components *)
parts := URL_PARSE('https://api.example.com:8443/v2/data?key=abc&limit=10#section');
(* {scheme:"https", host:"api.example.com:8443", path:"/v2/data",
query:"key=abc&limit=10", fragment:"section"} *)

(* Build URL from components *)
url := URL_BUILD('https', 'api.example.com', '/v2/data');

(* Encode/decode *)
encoded := URL_ENCODE('hello world & more'); (* "hello%20world%20%26%20more" *)
decoded := URL_DECODE(encoded);

(* Join paths *)
full := URL_JOIN('https://api.example.com/v2', 'data/123');

(* Query parameter manipulation *)
val := URL_QUERY_GET('https://x.com?page=3', 'page', '1'); (* "3" *)
url := URL_QUERY_SET('https://x.com?page=3', 'limit', '20');
url := URL_QUERY_DELETE(url, 'page');
FunctionReturnsDescription
URL_PARSE(url)MAPDecompose URL
URL_BUILD(scheme, host, path)STRINGBuild URL
URL_ENCODE(str)STRINGPercent-encode
URL_DECODE(str)STRINGPercent-decode
URL_JOIN(base, path)STRINGJoin base + relative
URL_QUERY_GET(url, key [,default])STRINGRead query param
URL_QUERY_SET(url, key, value)STRINGSet query param
URL_QUERY_DELETE(url, key)STRINGRemove query param

7. TLV/BER — Tag-Length-Value Encoding (12)

Parse and build BER-TLV structures (smart cards, ASN.1, EMV payment).

(* Parse TLV data *)
nodes := TLV_PARSE('6F 1A 84 07 A0000000041010 A5 0F 50 0A 4D617374657243617264');
count := TLV_COUNT(nodes);

tag := TLV_GET_TAG(nodes);
len := TLV_GET_LENGTH(nodes);
hex := TLV_GET_VALUE_HEX(nodes);
str := TLV_GET_VALUE_STRING(nodes);

(* Navigate constructed nodes *)
IF TLV_IS_CONSTRUCTED(nodes) THEN
children := TLV_GET_CHILDREN(nodes);
END_IF;

(* Find by tag *)
app_label := TLV_FIND_TAG(nodes, 16#50);

(* Build TLV *)
tlv_bytes := TLV_BUILD(16#84, 16#A0, 16#00, 16#00, 16#00, 16#04);
FunctionReturnsDescription
TLV_PARSE(hex_or_bytes)ARRAYParse BER-TLV
TLV_BUILD(tag, value_bytes...)ARRAYEncode TLV
TLV_COUNT(nodes)INTTop-level node count
TLV_GET_TAG(node)INTTag number
TLV_GET_LENGTH(node)INTValue length
TLV_GET_VALUE(node)ARRAYRaw bytes
TLV_GET_VALUE_HEX(node)STRINGValue as hex
TLV_GET_VALUE_INT(node)INTValue as integer
TLV_GET_VALUE_STRING(node)STRINGValue as UTF-8
TLV_GET_CHILDREN(node)ARRAYChild nodes
TLV_FIND_TAG(nodes, tag)HandleFind tag recursively
TLV_IS_CONSTRUCTED(node)BOOLHas children?

8. GSV/SSV — Get/Set System Value (17)

Allen-Bradley Logix compatibility functions for accessing runtime system attributes.

(* Read system clock *)
ts := GSV_WALLCLOCKTIME(); (* Unix ms *)

(* Read task scan time *)
scan := GSV_TASKSCANTIME('MainTask'); (* ms *)

(* Set task scan time *)
SSV_TASKSCANTIME('MainTask', 100);

(* System status *)
faulted := GSV_FAULTED();
state := GSV_ENTRYSTATE();
module := GSV_MODULESTATUS(0); (* slot *)
port := GSV_PORTSTATUS(1); (* port *)
io := GSV_IOCONNECTION(1); (* connection instance *)
ethernet := GSV_ETHERNET_STATUS(1); (* port *)
dlr := GSV_DLR_STATUS();
FunctionReturnsDescription
GSV_WALLCLOCKTIME()INTUnix timestamp (ms)
GSV_TASKSCANTIME([task])INTTask scan time (ms)
SSV_TASKSCANTIME(task, ms)BOOLSet task scan time
GSV_FAULTED()BOOLPLC faulted?
GSV_ENTRYSTATE()INTEntry state code
GSV_MODULESTATUS()STRINGModule status JSON
GSV_PORTSTATUS()STRINGPort status JSON
GSV_IOCONNECTION()STRINGI/O connection JSON
GSV_ACTIVEPROCESSOR()STRINGActive processor ID
GSV_REDUNDANCYSTATUS()STRINGRedundancy mode
GSV_SYNCSTATUS()STRINGSync/RTC status
GSV_ETHERNET_STATUS()STRINGEthernet port status
GSV_DLR_STATUS()STRINGDevice Level Ring status
GSV(generic)ANY
SSV(generic)BOOL

9. ctrlX EtherCAT I/O (10)

Read/write digital I/O on Bosch Rexroth ctrlX CORE via EtherCAT Data Layer.

(* Create EtherCAT I/O client *)
ok := CTRLX_EC_CREATE('io', 'https://localhost', 'boschrexroth', 'boschrexroth',
'ethercatio/fieldbus/di', 'ethercatio/fieldbus/do',
16, 16, 100);

CTRLX_EC_START('io');

(* Read digital inputs *)
sensor := CTRLX_EC_READ_DI('io', 1); (* Channel 1, 1-based *)
limit := CTRLX_EC_READ_DI('io', 5);

(* Write digital outputs *)
CTRLX_EC_WRITE_DO('io', 1, TRUE);
CTRLX_EC_WRITE_DO('io', 2, FALSE);

(* Read back output state *)
out_state := CTRLX_EC_READ_DO('io', 1);

(* Diagnostics *)
connected := CTRLX_EC_CONNECTED('io');
modules := CTRLX_EC_BROWSE('io'); (* JSON module list *)
stats := CTRLX_EC_STATS('io'); (* JSON diagnostics *)

CTRLX_EC_STOP('io');
CTRLX_EC_DELETE('io');
FunctionReturnsDescription
CTRLX_EC_CREATE(name, host, user, pass, di, do, di_count, do_count, poll_ms)BOOLCreate I/O client
CTRLX_EC_START(name)BOOLStart polling
CTRLX_EC_STOP(name)BOOLStop polling
CTRLX_EC_DELETE(name)BOOLRemove client
CTRLX_EC_CONNECTED(name)BOOLConnection alive?
CTRLX_EC_READ_DI(name, ch)BOOLRead digital input (1-based)
CTRLX_EC_READ_DO(name, ch)BOOLRead back digital output (1-based)
CTRLX_EC_WRITE_DO(name, ch, val)BOOLWrite digital output (1-based)
CTRLX_EC_BROWSE(name)STRINGDiscover I/O modules (JSON)
CTRLX_EC_STATS(name)STRINGDiagnostic statistics (JSON)

10. Directory Operations (4)

Supplement to the File I/O guide — manage directories from ST.

DIR_CREATE('/data/logs/2026'); (* Creates parents *)

IF DIR_EXISTS('/data/logs') THEN
files := DIR_LIST('/data/logs'); (* Array of filenames *)
END_IF;

DIR_DELETE('/data/temp'); (* Empty directories only *)
FunctionReturnsDescription
DIR_CREATE(path)BOOLCreate directory (including parents)
DIR_EXISTS(path)BOOLCheck if directory exists
DIR_LIST(path)ARRAYList directory contents
DIR_DELETE(path)BOOLDelete empty directory

ControlForge v1.0.535 | KNX, M-Bus, ZPL, Barcode, URL, TLV, GSV/SSV, ctrlX EtherCAT, DIR

© 2026 JMB Technical Services LLC. All rights reserved. Back to All Guides