Tales along the way in my quest to Integrate Everything.

Omron FINS Protocol

I’ve been trying to create a proof-of-concept program that makes use of the open source FINS library I came across here: https://www.libfins.org/

The first obstacle I encountered was that there does not appear to be any examples on the Internet that use this library. In my experience, this means one of two things:

  1. What I am trying to do is so simple that no one else has bothered writing it up, or
  2. I am going about it entirely the wrong way and am so far off the right course that no one else has gotten to this place.

I looked into the source code itself, for I had noticed some discrepancies between the published documentation and actual function calls. Since the source code is almost always correct, it seemed like a good place to start.

I got a skeleton program written and it seemed able to connect to the PLC, in this case a CP1L-EM30DR-D on loan from Omron. However, any subsequent command I tried to execute, such as finslib_cpu_unit_status_read() or finslib_cpu_unit_data_read() or finslib_memory_area_read_uint16() all failed with the same error message indicating that too many errors had occurred and that the connection had been terminated.

tcpdump showed that the test program was in fact connecting to the PLC, but little more could be gleaned from the hex dumps it spat out. I began to walk through the library code, inserting some console chatter to try to find out exactly where each call was failing. I corrected a few mistakes I’d made. One thing that became apparent was that some of the initialization only takes place if you pass it a null pointer for the fins_sys_tp* parameter. I was creating a fins_sys_tp struct, initializing it to zeroes and passing the address of this structure to finslib_tcp_connect(), which of course then skipped all the initialization. Because of this, the connection type was set to “unknown” by default, so the communication functions all would error out immediately. Passing finslib_tcp_connect() a null pointer instead yielded the correct result.

The next thing I needed to correct was that my model of PLC was not known to the library, so it left another variable, plc_mode, set to FINS_MODE_UNKNOWN. This too, caused communication functions to fail to send data, since they did not know what format to use. As a test, I modified the library to try using CV mode on my model of CPU, which it seemed to like as all the functions invoked after that returned much more meaningful return values, and tcpdump got a lot more excited.

After that, the rest of my proof-of-concept tests proceeded much more to my liking. I was able to read and write directly to the CIO memory on the PLC, which in turn activated and de-activated outputs and LEDs on the unit. Next up I will try to read some input values (the PLC is about 30 km away from me at the moment) but I expect the remainder of the tests will go much more smoothly. Thus, we now have a much clearer path to developing the FINS Buscom plugin!

Here is my test code, in case it may help anyone else save several days of head scratching and Googling:

/*
 * Program: libfins Test Program
 * File:    test.c
 * Author:  John Finlay
 *
 * This file is licensed under the MIT License as stated below
 *
 * Copyright (c) 2019 John Finlay
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

#include "include/fins.h"
#include 
#include 


int main() {

	/* Initialize */

	int err = 0, err_max = 10;
	struct fins_sys_tp *c = NULL;

	// memset(&c, 0, sizeof(struct fins_sys_tp)); // Not needed - finslib_tcp_connect() only sets up default variables if it is passed a NULL pointer.
	// init_system(&c, err_max);

	/* Connect to the PLC */

	struct fins_sys_tp *sys = finslib_tcp_connect(c, "192.168.250.10", 9600, 1,
			5, 0, 1, 10, 0, &err, err_max);
	printf("Connection Call:   Error Code was [%u]\n", err);

	char err_msg[64];
	finslib_errmsg(err, err_msg, 64);
	printf("Connection Call:   Error Message Was: [%s]\n", err_msg);

	/* Find out what kind of PLC we are talking to */

	struct fins_cpudata_tp cpudata;

	int cuer = finslib_cpu_unit_data_read(sys, &cpudata);

	finslib_errmsg(cuer, err_msg, 64);
	printf(
			"Read CPU Data:     Error Message Was: [%d] [%s] - sys->error_count = [%u] sockfd: [%u]\n",
			cuer, err_msg, sys->error_count, sys->sockfd);

	/* Read CPU Status */

	struct fins_cpustatus_tp cpustat;
	int cpustat_ret = finslib_cpu_unit_status_read(sys, &cpustat);
	finslib_errmsg(cpustat_ret, err_msg, 64);
	printf("CPU Unit Stat Read Error Message Was: [%s]\n", err_msg);

	/* Read Memory Area */

	uint16_t arr[2048];
	int i;
	for (i = 0; i < 2048; i++) {
		arr[i] = 0;
	} // Could use memset...

	int num = 16;
	int read_ret = finslib_memory_area_read_uint16(sys, "CIO100.0", arr, num);

	finslib_errmsg(read_ret, err_msg, 64);
	printf("Memory Area Read Error Message Was: [%s]\n", err_msg);

	for (i = 0; i < num; i++) {
		printf("arr[%u] = [%u]\n", i, arr[i]);
	}

	/* Write Memory Area */

	for (i = 0; i < 2048; i++) {
		arr[i] = 0;
	} // Again, could use memset...

	num = 16;
	int write_ret = finslib_memory_area_write_uint16(sys, "CIO100.0", arr, num);
	finslib_errmsg(write_ret, err_msg, 64);
	printf("Memory Area Write Error Message Was: [%s]\n", err_msg);

	for (i = 0; i < num; i++) {
		printf("arr[%u] = [%u]\n", i, arr[i]);
	}

	/* Read Error Log */

	struct fins_errordata_tp errordat;
	size_t num_to_read = 1;
	size_t num_read = 0;
	int err_ret = finslib_error_log_read(sys, &errordat, 0, &num_to_read,
			&num_read);

	finslib_errmsg(err_ret, err_msg, 64);
	printf(
			"Read Error Log:    Error Message Was: [%s] - Requested: [%zu] Records That Were Read: [%zu]\n",
			err_msg, num_to_read, num_read);

	/* Read CPU Data */

	struct fins_cpudata_tp cpuinfo;

	int cpu_ret = finslib_cpu_unit_data_read(sys, &cpuinfo);

	finslib_errmsg(cpu_ret, err_msg, 64);
	printf("CPU Unit Data Read Error Message Was: [%s]\n", err_msg);

	/* Disconnect */

	finslib_disconnect(sys);
	printf("Connection closed.\n");
	return 0;
}

Compile with the following command:

gcc -Wall test.c -o test -Llib -lfins

Comments on: "Omron FINS Protocol" (1)

  1. Hmm, it appears that this theme does not have a great way to format (preformatted) code.

Leave a comment for: "Omron FINS Protocol"

You must be logged in to post a comment.