paulg - 2024-06-06

I've trimmed down the CAA Serial Codesys example to only listen on one port but, when stepping through the Case structure in debug mode, it jumps out of the structure during a specific point in every scan (I'll point it out below after describing the setup and listing the code).

I'm using a Pi 4 Model B, and I have an Arduino Nano Every plugged in via USB which is streaming the following serial message at 1 Hz:

Time since opening connection: 1 s
Time since opening connection: 2 s

...and so on.

The Pi shows the Nano at /dev/ttyACM0 so I edited CODESYSControl_User.cfg to read:
Linux.Devicefile=/dev/ttyACM

The code in my PLC_PRG is (ignore some of the comments, I hadn't deleted them out from the original example):

PROGRAM PLC_PRG
VAR
    xStartTest : BOOL:= TRUE;
    iState : INT;
    xTestDone : BOOL;(* True, when the test was done succesfully *)

    (* Settings to communicate with the COM Port *)
    aCom1Params : ARRAY [1..7] OF COM.PARAMETER;

    como1 : COM.Open;
    comc1 : COM.Close;
    comw1 : COM.Write;
    comr1 : COM.Read;

    //sWrite : STRING := 'Test String!';
    sRead : STRING(25);
    szRead : CAA.SIZE;

    xCom1OpenError : BOOL;
    xCom1CloseError : BOOL;
    xCom1WriteError : BOOL;
    xCom1ReadError : BOOL;
END_VAR

//This example shows the communication of two COM Ports with each other. 
//The first one writes a string of characters, which is read by the second one. 
//After successful execution, the two COM Ports are closed and the test is done.

IF xStartTest THEN

    CASE iState OF
        0:  //The parameters are set for the COM Port
            aCom1Params[1].udiParameterId := COM.CAA_Parameter_Constants.udiPort;
            aCom1Params[1].udiValue := 1; // the correct Port should be adapted
            aCom1Params[2].udiParameterId := COM.CAA_Parameter_Constants.udiBaudrate;
            aCom1Params[2].udiValue := 115200;
            aCom1Params[3].udiParameterId := COM.CAA_Parameter_Constants.udiParity;
            aCom1Params[3].udiValue := INT_TO_UDINT(COM.PARITY.NONE);
            aCom1Params[4].udiParameterId := COM.CAA_Parameter_Constants.udiStopBits;
            aCom1Params[4].udiValue := INT_TO_UDINT(COM.STOPBIT.ONESTOPBIT);
            aCom1Params[5].udiParameterId := COM.CAA_Parameter_Constants.udiTimeout;
            aCom1Params[5].udiValue := 0;
            aCom1Params[6].udiParameterId := COM.CAA_Parameter_Constants.udiByteSize;
            aCom1Params[6].udiValue := 8;
            aCom1Params[7].udiParameterId := COM.CAA_Parameter_Constants.udiBinary;
            aCom1Params[7].udiValue := 0;

            //The first Port is opened with the given parameters
            como1(xExecute := TRUE, usiListLength:=SIZEOF(aCom1Params)/SIZEOF(COM.PARAMETER),pParameterList:= ADR(aCom1Params));

            IF como1.xError THEN
                xCom1OpenError := TRUE;
                iState := 1000;
            END_IF

            //After a successful opening, the next state is reached
            IF como1.xDone THEN
                iState := 15;
            END_IF

        15: // the reading process is started
            comr1(xExecute := TRUE,hCom:= como1.hCom, pBuffer:= ADR(sRead), szBuffer:= SIZEOF(sRead));

            IF comr1.xError THEN
                xCom1ReadError := TRUE;
            END_IF

            //After completion the size of the written bytes are saved
            IF comr1.xDone OR comr1.xError THEN
                szRead := comr1.szSize;
                iState := 20;
            END_IF

        20: // If everything was successful the ports are closed and the handles are released
            comc1(xExecute := TRUE,hCom:= como1.hCom);
            IF comc1.xError THEN
                xCom1CloseError := TRUE;
            END_IF

            IF comc1.xDone OR comc1.xError THEN
                iState := 25;
            END_IF

        25: // The first port is closed and the used handle released
            xTestDone := TRUE;
            xStartTest := FALSE;
            iState := 0;
            como1(xExecute := FALSE);
            comw1(xExecute := FALSE);
            comc1(xExecute := FALSE);
    ELSE
        iState := 0;    
    END_CASE

END_IF

I realize as I write this that the .udiPort should be 0 and not 1, but that shouldn't be causing the issue I'm seeing.

I'm forcing xStartTest:=TRUE every scan so that I can step into each line and observe what's happening. What I see is that the port parameters are set and the port is opened with no errors, but the code jumps out of the case structure to the last line every time it reaches (and I step into) the iState:=15 line (at the end of the iState:=0 block). So every scan cycle it goes through the block for iState=0 and jumps out at the same spot.

I'm a little new to PLC programming so I may be misunderstanding the flow, but shouldn't this case structure keep moving down in the same scan? If it only handles one case per scan, why doesn't the value of iState persist?

Thanks!


Update: I restarted the Codesys control today and I was then able to see an error for como1.eError of "WRONG_PARAMETER". I tried doing some digging and another post made me think I should add another line to CODESYSControl_User.cfg, so I now have:

[SysCom]
Linux.Devicefile=/dev/ttyACM
portnum := COM.SysCom.SYS_COMPORT1

So now when I set .udiPort to 1, I get "NO_ERROR" but I also don't read anything from the port (i.e. szRead = 0 always). If I try setting the port to 0 (which I'm confused about, because I added a COMPORT1 line but the device shows on the Pi as ACM0), I get the "WRONG_PARAMETER" error again.

Is there an easier way to troubleshoot the Pi and view what ports the Codesys runtime is actually able to see while the Pi is running?

 

Last edit: paulg 2024-06-06