hotloop
GitHub
HotLoop Transmitter Released, 1.1.0

Put the process value on the wire from inside the PLC.

You want a PLC's values on the network as OPC UA PubSub, and the PLC has no PubSub library on it. Transmitter is a CODESYS library that fixes that: you add it to the project, call one function block once per cycle, and a UADP message goes out every publishing interval to a unicast address or a multicast group, the way Part 14 of the spec says. It needs no other library, no extra license, and no configurator. The consequence: a bare PLC can feed anything that subscribes to OPC UA PubSub, with a few lines of Structured Text.

What it does

Add the library, call one block.

Declare a publisher, add the variables you want out once at start, and call it every cycle. The block builds one message in a fixed buffer and sends it on a non-blocking socket, and the cost of a call is the cost of building one message, paid only on the cycles where one goes out. A bad address or a field list that does not fit becomes a state and a counter on the block, never a fault in your program, so the scan keeps scanning.

It sends BOOL through LREAL, STRING and TIME, and arrays, in two encodings. Variant is read by any subscriber without a layout. RawData is smaller and needs the receiver to know the layout.

PROGRAM PLC_PRG
VAR
    pub : HLTX.FB_HLTX_Publisher;
    bInit : BOOL;
END_VAR

IF NOT bInit THEN
    bInit := TRUE;
    pub.sDestination := '239.0.0.22';
    pub.uiPublisherId := 1;
    pub.tPublishingInterval := T#100MS;
    pub.AddReal(ADR(GVL.rTemperature));
    pub.AddBool(ADR(GVL.bRunning));
END_IF
pub.bEnable := TRUE;
pub();
Where it fits

It lives in the PLC, so it is Structured Text and not Go.

Every new HotLoop tool is written in Go unless there is a real reason. This is the reason: it has to run inside the PLC's own scan, as a library the programmer references from the project, and a CODESYS runtime runs Structured Text. The tools around it, the capture tool and the verifier, are Go.

Next to the rest of HotLoop

It publishes. The other end is somebody else's.

Transmitter only publishes: the subscriber side is not part of it. It sits on its own release line beside the rest of the lineup. HotLoop core's subscriber reads it, from core's main branch, which is not in a core release yet. It is under the same HotLoop license as every product.

How it installs

One library file in the CODESYS library repository.

Download the library below and install it in the CODESYS IDE. This is version 1.1.0. You need a CODESYS development system, 3.5 SP22 or later, and a runtime whose target has the SysSocket and SysTimeRtc components, which the CODESYS Control runtimes for Linux SL have.

  1. Install HotLoopTransmitter-1.1.0.library with Tools, Library Repository, Install, or install the .package with the CODESYS Installer.
  2. Open the application's Library Manager, Add Library, and pick HotLoop Transmitter. SysSocket and its interface libraries come along as dependencies.
  3. Declare an HLTX.FB_HLTX_Publisher, add your fields, and call it every cycle, as in the example above.
  4. Download to the controller. The destination has to be reachable from its network, and for multicast the switches have to carry the group.

The .compiled-library file is the same library without its source, for when you don't want the Structured Text visible in the Library Manager. The install page in the docs lists every error the block can report, and what to do about each.

Proven, and not

What was run, and what it showed.

The full runs are on the proof page, and the timing on the timing page.

CheckWhat
BuiltThe library compiled with 0 errors and 0 warnings in the CODESYS 3.5.22.30 development system, and the example application built against it for a CODESYS Control for Linux SL device. The same holds for 1.1.0.
Run onThe CODESYS Control for Linux SL 4.22.0.0 runtime in a container, in demo mode, with a 20 ms task. Two publishers, one in each encoding, seventeen fields each, on The Beast.
Read by a decoder written from the specEvery field equalled the PLC's own value, in both encodings, with no gap in the message sequence numbers.
Read by open62541 1.4.9, a stack that is not oursIn the Variant encoding all 15 values checked equal, strings and arrays included. In RawData the 11 numeric scalars equal. That stack's RawData reader cannot read a longer string or any array at all.
Read by HotLoop coreHotLoop's UADP subscriber, from HotLoop core's main branch (it is not in a core release yet, and the next one carries it), read both publishers live and checked 21 values equal, including the string, the TIME, both arrays and two counters that moved between reads. It refused nothing, and in 1.1.0 every reading carried the publisher's timestamp as its time.
Read by a second Go decoderAll 111 messages of each capture decoded, and every checked value was correct, in both encodings.
Timestamps (1.1.0)Every message carries both timestamps, the same instant, never going backwards, within the whole-second tolerance of the clock the capture ran on. Over an 11 second capture the stamp moved 11 seconds. open62541 1.4.9 read the timestamps of all 113 messages of each writer with its own header decoders. The container shares the host's clock, so this shows the stamp is the PLC clock's reading, and does not test a controller whose clock is wrong.
The package (1.1.0)It installs through the CODESYS PackageManagerCLI, the library appears in the System repository, and the example builds against it as the Installer put it there, 0 errors and 0 warnings. It has not been run through the IDE's own Package Manager window.
What it costs the scanIn 1.1.0 with timestamps on, about 9.6 microseconds added to a 20 ms cycle on average. A cycle that sends takes 10 to 100 microseconds, and the longest cycle in that run was 81 microseconds, with three publish slots skipped and no send refused. In the 1.0.0 run one send took 14.3 ms in the operating system's send call, on a container on a busy 64-thread host. If a stall like that matters to your control logic, publish from its own lower priority task.
Not runA physical controller, any other runtime or CPU, a controller whose real-time clock is wrong, multicast, a runtime that loses and regains its network, and the end of the two hour demo window. A small ARM controller can be ten to a hundred times slower than the host these numbers came from, so measure it on the target with the block's own timing outputs.
What is built

What 1.1.0 ships.

One block, called once per cycle

FB_HLTX_Publisher sends one NetworkMessage per publishing interval, with one DataSetMessage that carries every field you added.

The types a PLC uses

BOOL, SINT, USINT, INT, UINT, DINT, UDINT, LINT, ULINT, REAL, LREAL, STRING and TIME, plus arrays of any of them but STRING.

Two encodings

Variant, which any subscriber reads without a layout, and RawData, which is smaller and needs the receiver to know the layout.

Unicast or multicast

A dotted address, a port, and for multicast a TTL and the interface to send from. Publisher id, writer group, dataset writer and the interval are inputs of the block.

A timestamp on every message

From 1.1.0 the NetworkMessage and the DataSetMessage each carry the PLC clock's reading, in whole seconds, so a subscriber knows when a value was sampled and does not guess from when it arrived. Set bTimestamp to FALSE and the block sends exactly the 1.0.0 bytes. It needs the SysTimeRtc component, which Control for Linux SL 4.22.0.0 has.

Counters that tell the truth

Messages sent, sends refused, publish slots skipped, the size of the last message, and the time the block spent in its last call and its longest.

Nothing to license

It is Structured Text over SysSocket, which every CODESYS runtime with sockets has. No other library, no configurator.

What it won't do

Four refusals, each on purpose.

It won't block your scan.

The socket is non-blocking and the message is built in a fixed buffer. A socket error, a bad address, or a field list that does not fit becomes a state and a counter on the block, never a fault in your application.

It won't fragment.

A message is at most 1472 bytes, so it fits one Ethernet frame. A field list that does not fit puts the block in an error state and sends nothing, instead of cutting your data.

It won't encrypt.

This version sends in the clear. Message signing and encryption are not built, and the roadmap says so.

It won't subscribe.

It publishes. The subscriber side is not part of this library.

Roadmap

What comes after.

WhenWhat
NextSub-second timestamps, several DataSetMessages in one NetworkMessage, delta frames and keep-alives, WSTRING, and a per-field status.
Not plannedMessage security, chunked messages, the subscriber side, and an MQTT transport.
Licensing

Under the HotLoop Business Source License 1.1.

Like the rest of HotLoop. Home and lab use is free. Business use is free through EmberNet.

Read the license