Home Site map Contacts CZ

Jan Ringoš, Tringi.TRIMCORE.cz

Endeavours  » 

IEC 60870-5-104 protocol

A specialized industry project required me to implement a complete layered architecture of communication protocols as specified in the ISO/IEC 60870-5 standards set. On top of this foundation I have implemented the concrete protocol defined in the companion standard 104 with all of the relevant components from the companion standard 101. The library by design allows programmers to easily implement completely different communication protocols on top of the prepared foundation.

The library is not free (as in beer) nor open-source, but can be commercionally licensed. See bottom of the page for details.

In electrical engineering the International Electrotechnical Commission 60870 standards define systems used for telecontrol (supervisory control and data acquisition). Such systems are used for controlling electric power transmission grids and other geographically widespread control systems. By use of standardized protocols, equipment from many different suppliers can be made to interoperate. IEC standard 60870 has six parts, defining general information related to the standard, operating conditions, electrical interfaces, performance requirements, and data transmission protocols.
  - quote from Wikipedia page on ISO/IEC 60870 [link leads out of this web]

Personal notes on ISO/IEC 60870-5

The standards documentation to 60870 is quite exhaustive and far from plain or obvious. Although once I got through everything a few times around, I saw that the actual protocol is not as complicated as it may seem at first. I don't know how many hours it took me to finally understand everything, because I was working on more projects/issues at the same time, but it was almost 9 months since I opened the first documentation sheet until I had a correctly working RTU.

Project foundations

The project development had to start long before the specifications were finalized. Many details were uncertain and had to be prepared for quick change. Many limitations were imposed by the initial requirements. A 16-bit architecture with less than megabyte of RAM and legacy and buggy ANSI C compiler, see C167: 16-bit C code; although the target hardware changed and improved performance-wise in few orders of magnitude. Due to very embedded nature of the project, the protocol variation where field lengths were in bits not whole bytes was deemed viable. This luckily changed, but only as soon as after I had implemented everything with bit-based offsets.

It was soon obvious that current, and now I can say legacy, RTUs (Remote Telemetry Units) weren't capable to handle the requirements. The device chosen, MOXA UC-7101 with μClinux is not exactly what I would have called an RTU, and is nor real-time nor has infallible industry-strength reliability, but the price (a fraction of price of heavy-duty RTU hardware) won by far. After all, only a marginal number of these units had to be replaced, and mostly due to failure of other hardware (supply, protection, cooling, …).

Architecture and implementation

What really helped me to get a firm grasp on the protocol was to break the complete project into few parts, mostly corresponding with the standard documents. There I have:

  • TB plain C library - a toolbox, packing bits, deque container, set container, …
  • #1 - frame type interface and implementations of FT1.1 and both variations of FT1.2
  • #2 - lowest level of protocol, bounding to communication interface and so called variation
  • #3 - APCI, ASDU, LPCI and ASDU Information Object structures, and relevant methods (function and macros)
  • #4 - conversion functions for almost all defined data types to/from plan-C types (int, float, tm, …), some of them are really funny
  • IEC - library front end binding everything together
  • VAR - so called variations which define the protocol logic itself;
    The ISO/IEC 60870-5-104 is actually one of those variations. I never got to implementing the 101, because in the end nobody really actually needed 101. But because it was easy to obtain robust library by just implementing the variation, I reused the library for other proprietary protocols as well.

See Talks for simple slides used as a background for my talk on the ISO/IEC 60870-5-104.

Further characteristics of my implementation:

  • My IEC library is written in fully commented platform-independent plain C. The library does not require any other library.
  • All the comments, and variable and function names, are in English. Primary documentation is available in both English and Czech translation.
  • It can be compiled into both 16-bit and 32-bit code.
  • The code is embedded-friendly, it doesn't allocate any memory.
  • The implementation uses a single-threaded context-based event-driven model.
  • The library focuses on receiving and decoding, the user needs to write some pack data into ASDU code himself.
  • There is a test-suite, but partially outdated and doesn't cover the whole broad of the library.

Extensions

  1. Although the library is strict when parsing incoming data on the lowest level, it can be configured to accept several 101 standard ASDUs in the 104 variation even though that ASDUs are forbidden by the 104 standard.
  2. A proprietary extension to the 104 standard that allows 104 over UDP protocol by adding specific proprietary connection layer is implemented.

Using my library

Using the library is as easy as initializing a few members of a structure, calling initialization function at the beginning, and passing data received from socket (or COM) to processing function (and/or periodically requesting idle processing). I used the library in both Windows and Linux, and not only over TCP sockets but also over UDP socket and RS-232 COM line.

Quick and dirty simplified example application:

void iec_cb_error_handler (const char * msg, const char * file, size_t line) {
    printf ("Error in data: \"%s\", line %u in %s\r\n", msg, line, file);
    return;
};

void callback (iec_context *, const unsigned char *, size_t);

int main () {
    // ...
    tbx_set_signal_handler (tbx_error_condition, iec_cb_error_handler);
    // ...
    iec_context   context;
    unsigned char memory [4096];
    // ...
    if (iec_initialize (&context, iec_104, memory, sizeof memory)) {
        // ...
        bool   quit = false;
        HANDLE h [] = { sent_event, recv_event };

        context.callback = callback;

        while (!quit) {
            switch (WaitForMultipleObjects (2, h, false, iec_due (&context)) {

                case 0: {
                    unsigned char * p = iec_send_prepare (&context);
                    if (p) {
                        size_t max = iec_send_max_size (&context);
                        size_t n = FetchDataToBeSent (p, max);
                        if (n)
                            iec_send (&context, target, p, n);
                        else
                            iec_send_cancel (&context, p);
                    } else {
                        // busy, waiting for peer to respond
                    };
                } break;

                case 1: {
                    unsigned char p [1024];
                    size_t n = 0u;
                    while ((n = ReadReceivedRawData (p, sizeof p)))
                        if (!iec_process (&context, p, n)) {
                            // serious configuration or application error
                            quit = true;
                            break;
                        };
                } break;

                case WAIT_TIMEOUT:
                    iec_idle (&context);
                break;
            };
        };

        // ...
        iec_quit (&context);
    };
    return 0;
};

// dispatch table
static const iec_dispatch_table_item table [] = {
    // call specialized functions for selected ASDUs
    { 30u, 31u, 1u, 0x00FFFFFFu, process_binary_input },
    { 33u, 33u, 1u, 0x00FFFFFFu, process_counter_input },
    { 36u, 36u, 1u, 0x00FFFFFFu, process_analog_input },
    // call this function for all remaining ASDUs
    { 0u, 255u, 0u, 0x00FFFFFFu, process_unknown_data }
    // end of table
    { 0u, 0u, 0u, 0u, NULL },
};

void callback (iec_context * context,
               const unsigned char * asdu, size_t size) {
    iec_dispatch (context, table, asdu, size);
    return;
};

Conclusion

I have laid down in code pretty much everything one would need to learn and figure out from standards, and I did my best to make the library easy to use and still remain powerful at the same time, but there are few key functions missing from retail quality, and the programmer is expected to do some work. Mostly binding it to communication interface and implementing the logic, e.g. when 60870-5-5 requires specific response to some request, it is the programmer who needs to actually send such response, the library won't do it automatically. It definitely is not a complete and just-use solution as other companies provide (e.g. Triangle MicroWorks) but their packages are adequately expensive.

Availability

The library is available for licensing. The terms and parameters of a license agreement are determined on an individual basis. For further details and options, see my e-mail contact at Contacts page or contact TRIM CORE SOFTWARE s.r.o. [link leads out of this web] Only if seriously interested, please.

exertion of the imagination, portfolio library and software repository