Quick Start

The following chapter provides a quick start to the TwinCAT 3 IoT OCPP function. In these instructions, you will set up an OCPP client and an OCPP server and allow them to communicate with each other in a simplified form. Further functionalities can be found in the other sample projects.

Quick Start 1:

Exemplary implementation

For reasons of simplicity, security is not taken into account in this Quick Start. In real applications, secure implementation should always be a central component of the considerations.

Initialization OCPP client

During initialization, either the function block can be used directly or an existing TcCOM object can be linked. In this Getting Started, initialization is carried out via the function block.

    // Client param
    stClientParam      : ST_OCPP1_ClientParam :=(sHost:='127.0.0.1', nPort:=8080, sIdentity:='TestIdent', sPath:='ocpp', eDebugLevel:=E_OCPP_DebugLevel.MessageLogFile, eTraceLevel:=TcTraceLevel.tlVerbose); 
    fbClient           : FB_OCPP1_Client(stClientParam);
    eAction            : E_OCPP1_Action;
    hMessageId         : T_OCPP_MessageId;

In this case, the OCPP client connects to the OCPP server also running on the same device at port 8080. For demonstration purposes, both the logfile is created and the Trace Level is increased. This is not absolutely necessary in normal operation. Details on logging can be found in the chapter Logging.

Initialization OCPP server

    // Server param
    stServerParam       : ST_OCPP1_ServerParam :=
                           (nPort:=8080,
                            eAuthMode:=E_OCPP1_AuthenticationMode.None,
                            eEncryptionMode := E_OCPP_EncryptionMode.None,
                            eDebugLevel:=E_OCPP_DebugLevel.MessageLogFile,
                            eTraceLevel:=TcTraceLevel.tlVerbose);
    fbOcppServer        : FB_OCPP1_Server(stServerParam);
    eAction             : E_OCPP1_Action;
    hMessageId          : T_OCPP_MessageId;
    hStationId          : UDINT;

This OCPP server also runs on the local device, on port 8080. The logfile and Trace Level are also set here for demonstration purposes.

Basic structure sample project

In this Getting Started, a TwinCAT project is used in which both the OCPP client and the OCPP server are operated in a separate PLC project. The actions are initiated by the OCPP client. In a real scenario, actions are of course also triggered on the server side.

Quick Start 2:

Implementation OCPP client

The sample includes two different processes on the client side. On the one hand, a simple StatusNotification can be sent:

// Send StatusNotification
IF bSendStatus THEN
    bSendStatus:=FALSE;
    eError:=E_OCPP1_ChargePointError.HighTemperature;
    eStatus:=E_OCPP1_ChargePointStatus.SuspendedEV;
    eAction:=E_OCPP1_Action.StatusNotification;
END_IF

The values sent with the StatusNotification can be adjusted using the eError and eStatus parameters. On the other hand, a streamlined process for simulated charging is implemented:

// Charging sample
CASE nState OF
    0: 
        IF bStartCharging THEN
            bStartCharging:=FALSE;
            eAction:=E_OCPP1_Action.Authorize;
            nState:=nState+1;
        END_IF
    1:    
        IF bAuthorized THEN
            bAuthorized:=FALSE;
            nMeterStart:=nMeterStop;
            eAction:=E_OCPP1_Action.StartTransaction;
            nState:=nState+1;
        END_IF
    2:
        IF bChargingStarted THEN
            //10 seconds charging simulation
            timerCharge(IN:=TRUE);
            IF timerCharge.Q THEN
                bChargingStarted:=FALSE;
                timerCharge(IN:=FALSE);
                nMeterStop:=nMeterStart+10;
                eAction:=E_OCPP1_Action.StopTransaction;
                nState:=nState+1;
            END_IF
        END_IF
    3:     
        IF bChargingStopped THEN
            bChargingStopped:=FALSE;
            nState:=0;
        END_IF
END_CASE

If the Charging Sample is started via bStartCharging, the client first calls the Authorize method. Following a successful Authorize, the StartTransaction method is then executed. After a successful StartTransaction, the system "loads" for 10 seconds and then executes a StopTransaction.

The specific OCPP communication is implemented via a CASE statement. The individual OCPP commands are implemented in this CASE statement:

// Different OCPP commands implemented for OCPP client CASE eAction OF
    E_OCPP1_Action.None:
        IF NOT fbClient.bError AND fbClient.bValid THEN 
            fbClient.PollRequest(hMessageId=> hMessageId, eAction=> eAction );
        ELSE 
            // Implement error handling here        
        END_IF        
    E_OCPP1_Action.Authorize:
        IF fbClient.SendAuthorize(sIdTag:=sIdTag, eStatus => stIdTagInfo.eStatus) THEN
            IF stIdTagInfo.eStatus = E_OCPP1_AuthorizationStatus.Accepted THEN
                bAuthorized := TRUE;
                eAction    := E_OCPP1_Action.None;
            END_IF
        END_IF
    E_OCPP1_Action.StartTransaction:
        IF fbClient.SendStartTransaction(sIdTag:=sIdTag, nConnectorId:=nConnectorId, nMeterStart:=nMeterStart, eStatus => stIdTagInfo.eStatus, nTransactionId => nTransactionId) THEN
            IF stIdTagInfo.eStatus = E_OCPP1_AuthorizationStatus.Accepted THEN
                bChargingStarted := TRUE;
                eAction    := E_OCPP1_Action.None;
            END_IF
        END_IF
    E_OCPP1_Action.StopTransaction:
        IF fbClient.SendStopTransaction(nConnectorId:=nConnectorId, nMeterStop:=nMeterStop, eStatus => stIdTagInfo.eStatus) THEN
            bChargingStopped := TRUE;
            eAction    := E_OCPP1_Action.None;
        END_IF
    E_OCPP1_Action.StatusNotification:
        IF fbClient.SendStatusNotification(nConnectorId:=nConnectorId, eError, eStatus) THEN
            eAction    := E_OCPP1_Action.None;
        END_IF
    // Implement here more functionalities like: 
    // E_OCPP1_Action.TriggerMessage:
ELSE
    eAction := E_OCPP1_Action.None;
    // not implemented... 
END_CASE

Only the functions used for the sample are implemented here. Further functions would mean an extension of the CASE statement.

Implementation OCPP server

On the OCPP server side, the commands sent by the client are received and the appropriate response is sent:

CASE eAction OF
    E_OCPP1_Action.None:
        IF NOT fbOcppServer.bError AND fbOcppServer.bValid THEN
            fbOcppServer.PollRequest(eAction => eAction, hMessageId => hMessageId, hStationId => hStationId);
        ELSE
            // Implement error handling here
        END_IF
    E_OCPP1_Action.BootNotification:
        IF fbOcppServer.RecvBootNotification(hStationId, hMessageId=>hMessageId,             
            sModel                => stBootNotification.sModel,
            sVendor                => stBootNotification.sVendor,
            sChargeBoxSerial    => stBootNotification.sChargeBoxSerial,
            sChargePointSerial    => stBootNotification.sChargePointSerial,
            sFirmwareVersion    => stBootNotification.sFirmwareVersion,
            sMeterSerial        => stBootNotification.sMeterSerial,
            sMeterType            => stBootNotification.sMeterType)
        THEN
            fbOcppServer.RespBootNotification(hStationId, hMessageId, E_OCPP1_RegistrationStatus.Accepted);
            eAction := E_OCPP1_Action.None;
        END_IF
    E_OCPP1_Action.Heartbeat:
        IF fbOcppServer.RecvHeartbeat(hStationId) THEN
            fbOcppServer.RespHeartbeat(hStationId:=hStationId,hMessageId:=hMessageId);
            eAction := E_OCPP1_Action.None;
        END_IF
    E_OCPP1_Action.Authorize:
        IF fbOcppServer.RecvAuthorize(hStationId, sIdTag => sIdTag) THEN
            IF sIdTag='Test123' THEN
                fbOcppServer.RespAuthorize(hStationId:=hStationId, hMessageId:=hMessageId, E_OCPP1_AuthorizationStatus.Accepted);
                eAction := E_OCPP1_Action.None;
            END_IF
        END_IF
    E_OCPP1_Action.StartTransaction:
        IF fbOcppServer.RecvStartTransaction(hStationId, hMessageId => hMessageId, sIdTag => sIdTag, nConnectorId => nConnectorId, nMeterStart => nMeterStart, nTimestamp => nTimestamp) THEN
            nTransactionId:=nTransactionId+1;
            fbOcppServer.RespStartTransaction(hStationId, hMessageId, E_OCPP1_AuthorizationStatus.Accepted, nTransactionId:=nTransactionId);
            eAction := E_OCPP1_Action.None;
        END_IF
    E_OCPP1_Action.StopTransaction:
        IF fbOcppServer.RecvStopTransaction(hStationId, hMessageId => hMessageId, sIdTag => sIdTag, nTransactionId => nTransactionIdRecv, nConnectorId => nConnectorId, nMeterStop => nMeterStop, nTimestamp => nTimestamp, eReason => eReason) THEN
            fbOcppServer.RespStopTransaction(hStationId, hMessageId, E_OCPP1_AuthorizationStatus.Accepted);
            eAction := E_OCPP1_Action.None;
        END_IF
    E_OCPP1_Action.StatusNotification:
        IF fbOcppServer.RecvStatusNotification(hStationId, hMessageId => hMessageId, nConnectorId => nConnectorIdStatus, eError => eError, eStatus => eStatus) THEN
            fbOcppServer.RespStatusNotification(hStationId, hMessageId);
            eAction := E_OCPP1_Action.None;
        END_IF
END_CASE

Compared to the client sample, two more functions are implemented here. The Heartbeat is sent internally by the client and can be configured via a Property on the function block. The BootNotification is sent automatically when the client starts up and must therefore also be processed in the server.

OCPP messages in server and client can also be implemented in the other direction. The server can send messages to the client. Conversely, the client can then receive these messages from the server.

The logfile looks as follows after the client sample has been executed once (sending a StatusNotification and executing the charging sample):

Quick Start 3:
Quick Start 4: