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.
![]() | 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.

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):

