UniPi Neuron for CODESYS – available now

Available Now

Cozens Software Solutions are pleased to announce that UniPi Neuron for CODESYS is available now for download from the CODESYS store.

UniPi Neuron for CODESYS provides CODESYS driver support for the full range of UniPi Neuron PLCs and expansion modules.

  • UniPi Neuron L20x, L30x, L40x, L50x, L51x, M10x, M20x, M30x, M40x, M50x and S10x
  • UniPi Neuron Expansion modules xS10, xS30, xS40 and xS50

The UniPi Neuron is a product line of PLC (Programmable Logic Controller) units built to be universal and used in both Smart Home and Business applications and automation systems. 

CODESYS is the leading hardware independent IEC 61131-3 development system under windows for developing and engineering controller applications

Developing the CODESYS runtime with TDD

Introduction

 

I was recently working in the CODESYS runtime again, developing some components for a client and I thought the experience wold make the basis of a good post on bringing legacy code into a test environment, to enable Test Driven Development (TDD)

The CODESYS runtime is a component based system, and for most device manufacturers is delivered as a binary for their target system and a collection of header files and interface definitions. Much of the interface is generic, however there are platform specific headers that abstract the underlying RTOS. Device manufacturers often develop bespoke runtime components, to access proprietary IO for example. To help with this the delivered software package includes template components as a starting point for development. This means that, according to Michael Feathers definition of legacy code (code without tests), the starting point when developing a CODESYS component is legacy code. In this example the starting point was a partially developed component, legacy code.

 

The Plan

I tend to follow a fairly standard process when bringing legacy code under test. The basic process is well described in TDD How-to: Get your legacy C into a test harness on James Grenning's blog.  I follow roughly the same process, with minor changes, my process can be summarised as follows

  • Select appropriate tools
  • Create a test harness with no reference to the code to be tested and a dummy failing test. Observe it fail. Fix the test and observe it pass.
  • Decide the boundaries of the code I want to test, and include this source in the test harness build.
  • Make the test harness compile (not link)
  • Make the code Link using exploding fakes.
  • Ensure the dummy test still passes
  • Add the first test of the code under test (expect it to crash or fail)
  • Make the test pass by adding initialisation, and using better fakes.
  • Add more tests, always observe them fail (force a failure if needs be - to check that the error output is meaningful), factor out common code into helper functions. Keep the tests small and testing one thing.
  • Add profiling, I like to be able to observe which parts of the code are under test before I make any changes. Particularly if the code under test has large complex functions it is the only way that I trust I have sufficient coverage before making code changes.

Tools

The development build of the component uses a gcc cross compiler on linux. The build is controlled by a makefile and there is already an eclipse project.

I will use the native gcc compiler to run the tests

For the testing framework I'm using googletest 1.8, my preferred test framework for C and C++

To help with creating fakes and mocks I will use Mike Long's Fake Function Framework (fff).

I will add plugins to eclipse so that the whole process can happen in a single environment.

 

The first test

There are two ways of using googletest, one is to build it as a library and link it to the tests, the other is to fuse the source into a single file, and then include the fused source in the tests. On linux I tend to just build the library with default settings.

I've created a new folder called UnitTests to which I've added a makefile and a single source file with this content

#include "gtest/gtest.h"
namespace 
{
TEST(FirstTest, ShouldPass)
{
    ASSERT_EQ(1,0);
}
} // namespace

The makefile, references just this source file, the include path has the path to googletest/include. The link line is shown below (I've omitted the paths for simplicity)

g++ FirstTest.o gtest_main.a -lpthread -o UnitTest

This builds, and when run fails as below

screen-shot-2016-09-08-at-11-06-57

Change the the ASSERT_EQ so that the test passes, rebuild and re-run the tests. 

Compiling with the UUT

The CODESYS component that that I'm working on consists of a single source file (The Unit Under Test UUT), and it links into a target specific library

To get the test application to compile I had to add three directories to the include path

-I$(CODESYS)/Components
-I$(CODESYS)/Platforms/Linux
-I$(TARGET_LIB_SRC)/include

NOTE: If the CODESYS runtime delivery is for a different operating system to the development system then it may be necessary to create fake versions of the headers in the Platforms directory. It may also be necessary to fake some of the RTOS header files.

Linking - Exploding Fakes

Having resolved the includes there are lots of unresolved symbols. A good starting point is to generate a file of exploding fakes, the idea here is to ensure that you know when you are faking code. Have a look at James' exploding fake generator, this can easily be adapted to any linker and any test framework. Save the output of your failed link into a file, execute gen-exploding-fakes-from-linker-output.sh to generate a file of exploding fakes which you include into your build.

make >& make.out
gen-exploding-fakes-from-linker-output.sh make.out explodingfake.c

The only other change required is to copy explodingfakes.h somewhere on the include path for the tests and adapt it to work with gtest as shown.

#ifndef EXPLODING_FAKE_INCLUDED
#define EXPLODING_FAKE_INCLUDED
#include "gtest/gtest.h"
#define EXPLODING_FAKE_FOR(f) void f() { FAIL() << "go write a proper stub for " #f; }
#endif

Now the test application should run and pass again, none of the UUT is yet being executed.

 

 

Testing - Part 1

CODESYS components have well defined interfaces, and I find it pays to test from those interfaces rather then exposing internals of the component wherever possible. Taking this approach tends to lead to less fragile tests that are testing the functionality rather than the implementation.

All components implement CmpItf, an interface that allows the component to be registered and initialised. CmpItf requires a single extern function ComponentEntry to be declared, all other functions in the interface are accessed through function pointers returned by this function call. So my starting point is to write tests that test this interface.

The first tests are straight forward, and soon the ComponentEntry call itself is factored out into the test constructor.

#include "gtest/gtest.h"
extern "C"
{
#include "CmpMyComponentDep.h"
DLL_DECL RTS_INT CDECL ComponentEntry(INIT_STRUCT *pInitStruct);
}
namespace
{
class CmpItfTest: public ::testing::Test
{
public:
    CmpItfTest():m_rResult(ERR_OK),m_InitStruct()
    {
        m_rResult = ComponentEntry(&m_InitStruct);
    }
    RTS_RESULT m_rResult;
    INIT_STRUCT m_InitStruct;
};
TEST_F(CmpItfTest, ComponentEntryShouldSucceed)
{
    ASSERT_EQ(ERR_OK, m_rResult);
}
TEST_F(CmpItfTest, ComponentEntryShouldSetComponentID)
{
    ASSERT_EQ(0x166B2002, m_InitStruct.CmpId);
}
TEST_F(CmpItfTest, CmpGetVersionShouldReturnCorrectVersion)
{
    ASSERT_EQ(0x03050800, m_InitStruct.pfGetVersion());
}

Fairly soon I am testing code that calls into other CODESYS components, as soon as I do, the exploding fakes show up in the tests.

screen-shot-2016-09-11-at-08-53-13

 

Using The Fake Function Framework

Now I need a more powerful fake, this is where the fake function framework comes in to it's own. Creating a fake for EventOpen can be as simple as adding the following to the test source file, and making sure fff.h is on the include path

#include "fff.h"
#include "CmpEventMgrItf.h"
DEFINE_FFF_GLOBALS;
FAKE_VALUE_FUNC( RTS_HANDLE, EventOpen , EVENTID , CMPID , RTS_RESULT *);

Having added this the link will fail with a message like

CmpEventMgrItf.fff.c:7: multiple definition of `EventOpen'

Remove the line from explodingfakes.c for EventOpen, and the tests should now run again.

It is then possible to write a simple test to prove that the EventOpen function has been called.

TEST_F(CmpItfTest, HookCH_INIT3ShouldOpenEvent)
{
    m_InitStruct.pfHookFunction(CH_INIT3,0,0);
    ASSERT_EQ(1, EventOpen_fake.call_count);
}

The Fake Function Framework includes facilities for recording a history of argument calls, setting return values and the ability to provide a custom fake. It makes a very powerful tool for testing C code, I'm not going to cover all of the features here there are plenty of other examples on the web. Do note though that fakes need to be reset for each new test. The constructor for my test fixture looks like this

CmpItfTest():m_rResult(ERR_OK),m_InitStruct()
{
    m_rResult = ComponentEntry(&m_InitStruct);
    RESET_FAKE(EventOpen);
    FFF_RESET_HISTORY();
}

As tests grow and there become multiple test files using the same fakes it makes sense to pull the fakes out into separate files,. I follow a pattern, if I am faking functions defined in a file called XXX.h, I create XXX.fff.h and XXX.fff.c and define my fakes in these files. Most of the time I take the approach of generating each fake manually, one by one as required.

CODESYS specifies the interface to all components in .m4 files, in the delivery I have there are 164 interface files specified. I know that over time these interfaces will be extended, and more interfaces added. I have generated a tool to process the interface definitions and automatically generate fff fakes for each API function in each of the interfaces. I then build these fakes into a static library that can be linked with any component I develop.

There is a danger in automating fake generation, it becomes very easy to not realise when you are using a fake. Most API functions in CODESYS return an RTS_RESULT, ERR_OK means success. ERR_OK has the value of zero which is also the default value returned by fff fakes. If developing new code this isn't a problem. But when bringing a legacy component under test it can lead to code appearing to be tested when it isn't. This can be avoided by still using exploding fakes within fff. 

To achieve all of this using the test library all I need in the tests is an include of the appropriate fake header file,

#include "CmpEventMgrItf.fff.h"

and the test constructor is changed to reset all of the CmpEventMgrItf fakes, set all of the fakes to explode, and then for the two functions that I want to fake I can disable the exploding behaviour.

CmpItfTest():m_rResult(ERR_OK),m_InitStruct()
{
    m_rResult = ComponentEntry(&m_InitStruct);
    FFF_CmpEventMgrItf_FAKES_LIST(RESET_FAKE);
    FFF_RESET_HISTORY();
    FFF_CmpEventMgrItf_FAKES_LIST(FFF_EXPLODE);
    // Allow normal fake operation for these functions, all others in the interface will explode if called.
    EventOpen_fake.custom_fake = NULL; 
    EventRegisterCallbackFunction_fake.custom_fake = NULL;
}

 

What does the fakes library look like?

To show what is included in the library of fakes, for those who are interested below is the content of the CmpEventMgrItf fakes cut down to show just the two functions that have been used.

CmpEventMgrItf.fff.h

#ifndef __CmpEventMgrItf__FFF_H__
#define __CmpEventMgrItf__FFF_H__
#include "fff.h"
#include <string.h>
#include "fff_explode.h"
#include "CmpEventMgrItf.h"
DECLARE_FAKE_VALUE_FUNC3( RTS_HANDLE, EventOpen , EVENTID , CMPID , RTS_RESULT * );
DECLARE_FAKE_VALUE_FUNC2( RTS_RESULT, EventRegisterCallback , RTS_HANDLE , ICmpEventCallback * );
RTS_HANDLE EventOpen_explode( EVENTID , CMPID , RTS_RESULT * );
RTS_RESULT EventRegisterCallback_explode( RTS_HANDLE , ICmpEventCallback * );
#define FFF_CmpEventMgrItf_FAKES_LIST(FAKE) 
FAKE(EventOpen)
FAKE(EventRegisterCallback)

#endif /* __CmpEventMgrItf__FFF_H__ */

Other than including headers three things are happening in this file. Firstly the fff fakes are declared, secondly prototypes for exploding functions are declared and finally a list of all faked functions is created allowing operations to be done on all fakes in one statement.

CmpEventMgrItf.fff.cpp

#include "CmpEventMgrItf.fff.h"
DEFINE_FAKE_VALUE_FUNC3( RTS_HANDLE, EventOpen , EVENTID , CMPID , RTS_RESULT * );
DEFINE_FAKE_VALUE_FUNC2( RTS_RESULT, EventRegisterCallback , RTS_HANDLE , ICmpEventCallback * );
RTS_HANDLE EventOpen_explode( EVENTID  a, CMPID  b, RTS_RESULT * z ){ fff_explode("EventOpen"); return (RTS_HANDLE)0; }
RTS_RESULT EventRegisterCallback_explode( RTS_HANDLE  a, ICmpEventCallback * z ){ fff_explode("EventRegisterCallback"); return (RTS_RESULT)0; }

The fff fakes are defined along with definitions of the exploding fakes. Each exploding fake calls fff_explode, which is declared in a separate module allowing the way it explodes to be changed for a different testing tool..

fff_explode.h

#ifndef __FFF_EXPLODE_H__
#define __FFF_EXPLODE_H__
#define FFF_EXPLODE(a) a##_fake.custom_fake = a##_explode;
#ifdef __cplusplus
extern "C"
{
#endif
void fff_explode(const char * func);
#ifdef __cplusplus
}
#endif
#endif /* __FFF_EXPLODE_H__ */

The macro FFF_EXPLODE(a)  sets the custom_fake variable in an fff fake to point to the exploding fake.

fff_explode.cpp

#include "fff_explode.h"
#include "gtest/gtest.h"
#ifdef __cplusplus
extern "C"
{
#endif
void fff_explode(const char * func)
{
    FAIL()<<"Time to use fake for "<<func;
}
#ifdef __cplusplus
}
#endif

 

Keeping it fast

As I mentioned in the tools section the production code is being built in eclipse. I want to build the test code in eclipse as well, and I want everything to work seamlessly.

I added a second Build Configuration to the production code build, and made this build the unit tests. Having done this I want to run the tests every time I build (Or rather I want to run the tests after every code change, and have the code rebuilt if required). This requires an optional component to be installed in eclipse. Go to Help->Install New Software…, choose to Work with: –All Available Sites– and then under Programming Languages select C/C++ Unit Testing Support, click Next>, Next>, Finish and wait for the install to complete. Restart eclipse when prompted.

Now right click on your project in eclipse and selectRun As->Run Configurations... Create a new C/C++ Unit Test configuration. Use Search Project to find your Unit Test application, then on the C/C++ Testing tab, select Google Tests Runner.

screen-shot-2016-09-08-at-16-14-25

When you run this configuration, it should force your tests to be built and then display the results graphically. Clicking on any failures will take you to the failing tests.

screen-shot-2016-09-10-at-13-20-46

Profiling

Particularly when bringing legacy code under test, I like to be able to visualise what is being tested and what isn't. If you are using gcc then this becomes very easy.

Add these compiler flags to the compilation of the unit under test, and to the link line.

-fprofile-arcs -ftest-coverage

Building and then running with profiling generates .gcda and .gcno files, these are specific to a particular build, so to ensure there are no mismatches in versions add to the link rule in the makefile an action to remove all .gcda and .gcno files from the object directory.

Now having run your tests look in the object directory in eclipse and you will see .gcda and .gcno files, double click one of them. In the dialog that pops up, ensure that your unit test executable is selected, and choose "Show coverage for the whole selected binary".

For me the key is not the amount of code covered, much more, what has been covered by my tests. Each file can be inspected and it is very clear what was run by the tests and what wasn't. This helps me decide if I have sufficient coverage before making changes. For example, the bars below show that my tests don't cover all of the initialisation functions.

screen-shot-2016-09-10-at-13-30-15

ExportFunctions is a standard function that is part of all components, the implementation shouldn't change. The image below shows that the test suite invokes it, but there must be a return statement inside the EXPORT_STMT. Without code coverage I may never have known that some of the code wasn't being exercised. Inspecting the code will then tell me if I need to add tests or not. This may be a trivial example but I hope it shows why inspecting test coverage helps you understand what is being tested. You can then make informed decisions about increasing the coverage, or accepting that you have gone far enough.

screen-shot-2016-09-10-at-13-30-46

Once I'm happy with the coverage in an area I want to change I can start more traditional TDD development. Having started TDD, I tend not to use code coverage checks very often. Being rigorous about TDD tends to lead to 100% coverage, the main time I re-use the coverage checks is if I have refactored the UUT, it helps to show not just that the existing functionality still passes, but that I haven't inadvertently added some untested functionality.

Summary and next steps

Investing the time to get the component under test has given me a re-useable test harness that allows me to extend and refactor the code with confidence. Future development can happen much faster than it would otherwise, as much of the functionality can be proven before taking the software anywhere near the embedded target.

Some components it is worth investing the time to create pre-canned functionality through custom_fakes. Consider these components

SysMem

With no further work fff can be used to simulate failures, check the sizes being allocated and return fixed data structures on allocations. However in some tests we just want the memory allocation to work, so having a simple set of custom fakes that can be used to delegate these calls functional equivalents is worth while. Another useful extension can be to track allocation and freeing of memory, then in a test fixture setup tracking can be enabled, and in the teardown it can be checked.

CMUtils

This component provides string manipulation and other utility functions, in most cases it is preferable to have a working double than the standard fff fake. If you have a source code distribution of the runtime code I would attempt to link this with the tests.

SysTime and SysTimeRTC

One of the great advantages of Unit Testing in embedded systems is being able to run tests faster than realtime. Develop custom_fakes that allow you to take control of the progress of time. 

Continuous Integration

Tests are only useful when they are run. Setting up a continuous integration system to build and test each component every time there is a change to the source code is the way to go.

Continuous Delivery

How far can you go towards continuous delivery? Using a combination of free tools, and the CODESYS Test Manager I have set up delivery pipelines that build the embedded code, run unit tests, performed static analysis, generated documentation, package up instrument firmware packages, build and test CODESYS libraries, automated version number management, create CODESYS packages, deploy the code into test systems and invoke automated testing (integration and system). If the tests all pass then the packages can be promoted to potential release candidates ready for final human validation as required.

 

Starting with CODESYS on the Raspberry Pi – Update

Last May I blogged about Starting with CODESYS on the Raspberry Pi. Since then I have found it a great teaching platform to help others learn CODESYS. My getting started tutorial is now a little out of date, so I thought I would reproduce it using current versions of software. This blog walks through the steps of installing CODESYS under Windows 10, installing the CODESYS for Raspberry Pi plugin, Installing the CODESYS runtime Raspbian and establishing communications.
Thanks to 3S the process is now much easier.

Installing CODESYS on Windows 10

Download CODESYS from https://www.codesys.com/download/  (You need to register, but then the download is free). Run the installer accepting all defaults

Installing CODESYS for Raspberry Pi

Download CODESYS for Raspberry Pi from the CODESYS Store http://store.codesys.com/codesys-control-for-raspberry-pi-sl.html. Again you have to register first, everything is contained within a single .package file
Installation is a two stage process, firstly installing the package within CODESYS, and then subsequently installing CODESYS on the Raspberry Pi.

Installing the CODESYS Package

Launch the CODESYS IDE, Tools->Package Manager… then Install…, browse to the Package previously downloaded. Accept the license and then accept the default on every menu in the installer. The package should install as shown, and CODESYS then needs to be restarted.

Preparing the Raspberry Pi

Ensure that you have the latest version of Raspbian installed on your Raspberry Pi.

New Install

3S give links on their download page http://store.codesys.com/codesys-control-for-raspberry-pi-sl.html and the First Steps pdf tells you how to setup the RPi for various different peripherals

Upgrade Raspbian

If you choose to upgrade, be careful of anything important, perform backups before you start. On the Raspberry Pi issue these commands to upgrade, then continue to install CODESYS as instructed below.

sudo apt-get updatesudo apt-get dist-upgradesudo rpi-update

Installing CODESYS on the Raspberry Pi

Launch CODESYS and then select Tools->Update RaspberryPi, enter the password for the Raspberry Pi, click scan.
If your RPi is found then select the IP Address from the dialog and click OK.
If your RPi is not found, it is possible to enter the IP Address manually, then click OK on the Update Raspberry Pi dialog – this will install the CODESYS runtime on your Raspberry Pi. Progress is reported in the bottom left corner of the CODESYS window.
The update only takes a few seconds, on completion check for messages

There should be a single message saying the Update has finished

Login the the Raspberry Pi and reboot it – now you are ready to test your installation.

Testing The Installation

Launch CODESYS and select New Project…
Select Standard Project and give the project a name, click OK.
Select the Device: as CODESYS Control for Raspberry Pi and the programming language to use, click OK. A project is then created with this content.
Double click on PLC_PRG (PRG) and create a simple program
Now try to login (Online->Login),
Click Yes to start a network scan. (For me this didn’t work – probably because I am running Windows in a virtual machine, if that happens manually type the IP address of your RPi into the box representing the device and hit Enter, your device should then be found).
Select Online->Login again, this time login and download should succeed, the program will be in the STOP state. Switch to the PLC_PRG tab and you should see your program and Variables at their starting values.
Select Debug->Start and the application will start running, you will ten see the variables changing.
Have fun…

Using PiFace Control and Display with CODESYS

This post is intended for CODESYS beginners, it aims to give a step by step guide to creating a CODESYS project for the Raspberry Pi with the PiFace Control and Display add on board. (I am actually using PiFace Control and Display 2, but it appears to be compatible).

Start by creating a new standard CODESYS project.

Select the CODESYS Control for Raspberry Pi as the Device and ST as the programming language
The PiFace Control and Display module is interfaced with using SPI, so we need to add two devices to the device tree, first an SPI Master and then the PiFace Control and Display device. To do this right click on SPI in the device tree and select Add Device
Choose the SPI Master
Click Add Device, then without closing the dialog select SPI_master in the device tree, and then in the dialog select PiFace Control&Display
Click Add Device and close the dialog.
IMPORTANT: PiFace Control and Display operates on SPI port 1, and so the SPI_master device needs to be reconfigured to use this port. To set the port open the SPI_master device in the device tree and change the Value of SPI port as shown to ‘/dev/spidev0.1’.
At this point we have a CODESYS project with all of the drivers installed to operate this display and to read the buttons. Adding the IO device has added a function block called PiFace_Control_Display of type PiFaceCaD. Unfortunately it can be very hard to work out how to use this function block with the version of the library containing this block no documentation is installed and commenting of the function block is hidden. We can find some information by switching to the Predefined Professional Feature Set (Tools->Options->Features->Predefined feature sets…->Professional). Now when the Library Manager is opened in the project additional libraries are visible, in particular we are interested in Raspberry SPI PiFace CaD.

PiFaceCaD

In the Raspberry SPI PiFace CaD library we can see the definition of the PiFaceCaD function block type. The function block has just a single VAR OUTPUT, bySwitches.
The rest of the features of the function block require the use of the Object Oriented features added to IEC61131-3 third edition. The documentation below is extracted directly from the Raspberry SPI PiFace CaD library (With a few corrections)

METHOD Clear

This method is used to clear the display
return value: none 

METHOD DefineCustomBitmap

This method is used to define a custom bitmap, up to 8 custom bitmaps can be used simultaneously return value: none 

Name
Type
Comment
byLocation
BYTE (0..7)
storage positions of the bitmap (0..7)
abyBitmap
ARRAY [0..7] OF BYTE
bitmap definition: each byte (0 (top)..7 (bottom)) represents on line; each bit (0 (right)..4 (left)) of a byte represents one pixel in the line

METHOD Home

This method is used to home the cursor to the upper left corner
return value: none 

METHOD MoveLeft

This method shifts the content of the display about one char to the left
return value: none 

METHOD MoveRight

This method shifts the content of the display about one char to the right
return value: none 

METHOD SetCursor

This method sets the cursor to a specified position
return value: none 
Name
Type
Comment
usiColumn
USINT
column to place the cursor (0..15)
usiRow
USINT
row to place the cursor (0,1)

METHOD SetText

This method sets the content of the display (unspecified characters are left unchanged)
return value: none 
Name
Type
Comment
sLine1
STRING
first line
sLine2
STRING
second line

METHOD Write

This method writes text at the current position
return value: none 
Name
Type
Comment
sText
STRING
string to write

METHOD WriteCustomBitmap

This method writes the specified custom bitmap at the current position
return value: none 
Name
Type
Comment
byLocation
BYTE (0..7)
storage positions of the bitmap (0..7)

PROPERTY Backlight: BOOL (SET)

Sets the backlight on or off 

PROPERTY Blink: BOOL (SET)

Displays or hides the blinking rectangle 

PROPERTY Cursor: BOOL (SET)

Displays or hides the cursor 

PROPERTY Display: BOOL (SET)

Switches the display on or off 

SPI

The PiFaceCad function block Extends the SPI function block, and so we need to understand a little about this function block too. This documentation is lifted from the Raspberry Pi Peripherals library.
This function block is the base class for SPI devices controlled via the SPI device /dev/spidev0.0. It is meant to be extended by other function blocks that overload the body and the following methods/properties and replace it with their specific implementation, always including a call of the base implementation with super^.<MethodName>() :
body (general handling, start-up)
AfterReadInputs (reading input data)
BeforeWriteOutputs (writing output data)
Initialize [optional] (used to read parameters from the configuration)
Operational [optional] (used to signal the status of the device)
The body of this FB is called by the methods AfterReadInputs and BeforeWriteOutputs, where _xAfterReadInputs indicates the caller. Use _iState to control your statemachine. A value of 10 by default indicates that the device is operational. Do not forget to call the base implementation with super^(), where the diagnosis indicators are set according to the Operational property.
 

Hello World

So now we know something about the methods on the PiFaceCaD Function Block we should be able to write a Hello World program.
PROGRAM PLC_PRG
VAR
xUpdate: BOOL := TRUE;
END_VAR
 
IF PiFace_Control_Display.Operational AND xUpdate THEN
PiFace_Control_Display.Clear();
PiFace_Control_Display.SetText(sLine1 := ‘Hello World’, sline2 := ”);
xUpdate := FALSE;
END_IF

Go online and run the application, you should see this

Reading Switches

All of the switches on the PiFace Control and Display board are output as bits within a single byte on the PiFaceCaD function block. The bits are numbered as shown
The sample code below demonstrates reading the switches
PROGRAM PLC_PRG
VAR
xUpdate: BOOL := TRUE;
byLastSwitches : BYTE;
bySwitches : BYTE;
END_VAR
 
bySwitches := PiFace_Control_Display.bySwitches;
IF bySwitches <> byLastSwitches THEN
xUpdate := TRUE;
END_IF
IF PiFace_Control_Display.Operational AND xUpdate THEN
PiFace_Control_Display.Clear();
PiFace_Control_Display.Backlight := TRUE;
PiFace_Control_Display.SetText(sLine1 := ‘Hello World’, sline2 := BYTE_TO_STRING(bySwitches));
xUpdate := FALSE;
byLastSwitches := bySwitches;
END_IF

 

Using the Camera with CODESYS on the Raspberry Pi

This post is a primer on how to get started with using the Camera in CODESYS on the Raspberry Pi. The content of the post is taken from a mix of the CODESYS documentation and from reverse engineering sample projects.

Configuring the Raspberry Pi

Plug the Camera in to the connector labelled CAMERA on the Raspberry Pi.
Connect using SSH and run sudo raspi-config to enable the camera, then reboot
Now we need to install camera interface, these instructions are taken from the CODESYS documentation, which appears to have got them from http://elinux.org/RPi-Cam-Web-Interface
sudo apt-get update
sudo apt-get dist-upgrade
sudo rpi-update
At this point I rebooted again.
Clone the code from github and run the installer with the following commands:
git clone https://github.com/silvanmelchior/RPi_Cam_Web_Interface.git
cd RPi_Cam_Web_Interface
chmod u+x RPi_Cam_Web_Interface_Installer.sh
./RPi_Cam_Web_Interface_Installer.sh install
When running the installer you get prompted for th name of the install folder – I accepted the default. You also get prompted for the Port to use, I stayed with the default port 80. I chose not to enable web server security. I then rebooted.
Then I browsed to http://192.168.0.38 (the IP address  of my RPi) to verify that everything was working.

Capturing a JPG image

Create a new CODESYS project (standard project) selecting the target device as CODESYS Control for Raspberry Pi. Beneath the Camera device right click over the <Empty> device, select plug device
and then select Raspberry Pi Camera, close the dialog.
You should end up with a Camera in the devices tree like this.
You also have a new variable in IoConfig_Globals called Raspberry_Pi_Camera of type Camera.PiCamera. The methods on this function block can be used to save a picture, or video. For documentation check out the online help in CODESYS.
At a minimum you need to specify the name of an output file, the sample below can be driven using the CODESYS debugger, set xTakeStill TRUE and a picture will be saved.
NOTE: On my system when I originally ran this udiResult was set to 70, and no picture was taken. I had to stop the Camera in the RPi Cam Control Application as warned in the CODESYS documentation, by browsing to the IP address of my Pi and selecting Stop Camera
Once I had stopped the Camera the function returned 0 (Errors.ERR_OK), and the picture was taken. Using the device file browser I was able to locate the Picture at the root of the CODESYS file system on my RPi.
NOTE: The function call Raspberry_Pi_Camera.Still takes a long time to execute, by default around 5 seconds. During this time the Program is hung. If you need to make use of this code I strongly suggest having a dedicated, low priority task to execute a program dedicated to taking the picture. I experimented with a timeout of 0, and the program hung for longer than I was prepared to wait. A timeout of 1 still took a long time to complete.

Using PiFace Digital 2 with CODESYS

In this post I look at how to drive the PiFace Digital 2 card with CODESYS. The post is aimed at CODESYS beginners.

The PiFace Digital 2 is provides 8 open collector digital outputs with LED indicators (two of which also drive changeover relays) and 8 digital inputs, four of the inputs are in parallel with on board switches. Fundamentally the guts of the board is a MCP23S17 16 bit I/O expander, so we will need to configure appropriate SPI devices in CODESYS.

First create a new project in CODESYS with ST as the selected language. The PiFace Digital 2 module is based around an SPI latch, so we need to configure the required SPI devices. In the Devices pane right click on the SPI device and select Add Device…

This brings up the Add Device dialog, select SPI master, and click the Add Device button.

Then in the Devices pane select the newly added SPI_master device (do this without closing the Add Device dialog). The Add Device dialog will update to show SPI slave devices that can be added, select the device named PiFace IO driver, click the Add Device button and then close the dialog.
It is possible to connect up to four of these boards to a single Raspberry Pi, to identify individual boards the address is set with a pair of links. If the board is in its default configuration then we should have nothing to do, but we should check, so inspect where JP1 and JP2 are connected, the table below shows what address this represents.

JP1 1-2
JP1 2-3
JP2 1-2
0
1
JP2 2-3
2
3

Open the PiFace_IO_driver device in CODESYS and verify that the Hardware Address parameter matches the value from the table.

We are now all set to test that our PiFace Digital board is working in CODESYS. The easiest way to do this is to create a simple program to copy the Inputs to the Outputs.

The program just copies one VAR of type BYTE to another, to use the actual inputs and outputs change we need to map these VARs onto the IO. To do this use the mapping tab (SPI devices I/O mapping) in the PiFace_IO_driver pane. Double click in the Variable column, and either type in the fully qualified variable name, or click on the elipsis and use the Input Assistant to find the variable.
If you now go online and download this application to your Raspberry PI you should be able to change the state of the outputs by triggering the inputs on the PiFace Digital. You can test the least significant four bits using the switches on the board.

CODESYS with Edimax EW-7811UN on Raspberry PI

Up to this point I have been using wired ethernet, tonight I decided to try with a wireless adapter.

First I followed the instructions here to get the Raspberry PI on my home network. https://www.raspberrypi.org/documentation/configuration/wireless/

Then I tried scanning for the device from CODESYS with no luck. I couldn’t ping the Raspberry PI from my PC, until I pinged the PC from the Raspberry PI. I suspected the Green power saving features of the Edimax adapter.

A quick google found https://www.raspberrypi.org/forums/viewtopic.php?t=61665 I followed the instructions, and created a file /etc/modprobe.d/8192cu.conf with the following content

# Disable power management
options 8192cu rtw_power_mgnt=0 rtw_enusbss=0

and then rebooted.

Having rebooted a Scan Network… from CODESYS found the device.

Getting started with CODESYS visualisation on the Raspberry Pi

This post is intended for those who have never generated any graphics using CODESYS.
CODESYS has a built in fully integrated visualization system. 
I have started by creating a very simple new CODESYS program using ST as the programming language.
Next step is to add a blank visualization as shown below
I name the new visualization Demo.
Adding the Demo visualisation object actually causes three objects to be added to the application.

Before progressing to actually create the visualisation, let’s have a quick look at each of these objects.

Visualization Manager

The Visualization Manager holds global settings such as the initial language, User Management, Hot Keys and language specific fonts. The Visualisation Manager provides a home to add visualization devices underneath. CODEYS supports a number of different visualization device types, the Raspberry Pi adaptation of CODESYS only supports WebVisualization and CODESYS HMI, we will only concern ourselves with WebVisualization in this post.

Web Visualization

The WebVisualization object adds a web server to the application. It also specifies which Visualisation to show when a browser first connects to a specified URL. The defaults are shown below.
Scaling Options are to do with how the visualisation is changed if the size of the window showing them changes. I personally find that the visualisations look best with a Fixed scaling. As I will be testing the visualisations using computers with keyboards and not touchscreen I also change the Default test input to Keyboard.
NOTE: It is possible to have multiple Web Visualization objects each with distinct URLs that can be mapped to different Visualizations. It is equally possible to have multiple Visualizations and access them all through a single URL.

Vizualisation (Demo)

In simplistic terms this object represents a ‘Page’ on which we can add Visualization Elements. These Elements can range from simple lines and rectangles to complex objects such as gauges, alarm tables etc. Visualizations can also be embedded in a Frame element on another Visualisation, or even used to define custom keyboards or dialogs.

Demo Visualization

To create our simple application we will add a Meter to out Demo Visualization and get it to indicate the value of the VAR a in the simple program. Double click on the Demo object in the Devices tree to open up the Visualization. Find the Meter Element under Measurement Controls in the Toolbox.
Then Left Click and drag a Meter from the Toolbox onto the top left of the Visualisation area.
At tis point we have a Visualisation including a Meter element, but it is not associated with any variable in our application, so next we must associate a variable from the program with the Meter. In the Properties box with the Meter still selected click in the Value column against the Value Property, click again and then click the ellipsis. 
This will bring up the Input Assistant, select Application->PLC_PRG->a and click OK.
That’s it, we should now be able to go online and verify that our application runs and the visualisation is working. So select Online->Login from the main CODESYS menu, and Login with download. At this pint the Demo Visualization shows as message stating The online visualization is waiting for a connection. Please start the application. Start the application running (Debug->Start, or F5) and the Meter should start to move rapidly.
We should also be able to see the same in a web browser. To do this you need to know the IP address of the Raspberry Pi that you are using. If you can see the console it may have a message that stays something like My IP address is 192.168.0.34, or in CODESYS open the Device object, open the log tab, click the refresh button (shown below), scroll down through the log messages and the IP address will be shown.
or on the Raspberry Pi console type ifconfig, the ip address is shown as inet addr:
The URL to access the Web Visualisation is http://192.168.0.34:8080/webvisu.htm, the captures below show how the meter looks in Internet Explorer and on the browser on the Raspberry Pi.


Starting with CODESYS on the Raspberry Pi

NOTE THIS POST IS OUT OF DATE. Please see starting-with-codesys-on-raspberry-pi

I’ve finally decided to give CODESYS a try on the Raspberry PI. This post tracks what I had to do to get CODESYS functioning, I’ll post more on sample projects in the future.

First of all, my setup: I’m running a fresh install of Windows 8.1 x64 in a virtual machine hosted on vmware Fusion on a Macbook Pro. I have a Raspberry Pi 2 connected by ethernet on my local network, the virtual machine is bridged so that it is on the same subnet as the Raspberry Pi. I am using an 8Gb SanDisk Ultra microSDHC card.

Installing CODESYS on Windows

I downloaded CODESYS V3.5 SP6 Patch 3 from CODESYS download area (You need to register, but then the download is free). I then ran the installer accepting all defaults.

CODESYS on the Raspberry PI

CODESYS for the Raspberry PI is available as a free download from the CODEYS store, again you have to register to get access, and again this is free. I downloaded  CODESYS_Control_for_Raspberry_PI.zip, and unzipped it to give me a folder with the following content.

Installation basically consists of two parts, firstly updating the CODESYS IDE with a package to add support for the Raspberry Pi, and secondly loading a runtime image onto an SDCard. 

Installing the package

I launched CODESYS V3.5 SP6 Patch 3 from the link on the windows desktop, selected the Standard environment when prompted. I then launched the Package Manager from Tools/PackageManager… I then selected Install… and browsed to the CODESYS_Control_for_Raspberry_PI package,

I then accepted the license agreement and all defaults

(Take a note of the target directory), Allow UAC to make changes.
I clicked Finish

Then clicked Close before exiting CODESYS.

Loading the Image

Following the advice in the product data sheet I downloaded and installed Win32diskimager 0.9.5 from http://sourceforge.net/projects/win32diskimager/. I plugged the microSDHC card into an SDCard carrier and then plugged this into a USB SDCard reader (The internal SDXC slot on my MacBook is not recognised by vmware Fusion, see vmware kb1035825). I then launched Win32DiskImager, and browsed to codesysrasp35620.img in the directory I had previously unzipped, selected device G: which corresponds to the SDCard, and clicked Write.

Programming the card took a few minutes, I then clicked Exit. Using Windows Explorer I was able to see the content of the SDCard,

still using Windows Explorer I right clicked on the device and selected Eject, before physically removing the card from the reader.

I then plugged the card into my Raspberry Pi and powered it on. Notice in the startup messages
codesyscontrol started
At the login prompt I entered the default credentials, username pi, password raspberry.
I chose at this point to expand the filesystem to fill the available space on the card, so at the command prompt
sudo raspi-config


I selected the option to Expand Filesystem, then tabbed to Finish and rebooted.

Testing the Installation

I launched CODESYS V3.5 SP6 Patch 3 and then from the Start Page selected New Project…

I selected the default type of a Standard project gave it a name and clicked OK.

I then selected the Device and programming language I wished to use.
This created a project with this content below

I then double clicked the Device (CODESYS Control for Raspberry Pi) which brought up the Communications Settings tab for the device.

I selected Scan Network, this found the device, which I selected and then clicked OK.

Now we are connected to the Raspberry Pi.