Introduction
I have seen many cases where there are multiple PLCs using the same program and the only difference between them is their communication configurations. In most of these cases, engineers have to manually change the PLCs’ communication configurations and download the hardware configuration.
The truth is it doesn’t have to be like that. In this article, I’m going to introduce a way to configure the SIEMENS PLC’s communication configuration with user program and recipe so that you can keep the same TIA Portal project and simply update the recipe. I’ll demonstrate using a user program to configure the PLC’s IP address, DNS server and NTP server configurations. There are more configurations to change but similar to my demonstrations.
Let’s go.
Hardware Configuration
To use the proposed approach in this article, the PLC’s hardware configuration must be kept in a certain state: the PLC’s communication configurations should not be set by the TIA Portal project and should be set by itself.
This is achieved by the below settings. Note that I am demonstrating the IP address, DNS server and NTP server configurations.
IP address.

DNS server.

NTP Server.

In addition, one of the ways to use the PLC’s recipe systems to backup and restore the communication configuration is to use the integrated web server. Hence the below configuration must be made.
Enable web server.

Allow web server access from the network interface.

Program the Function Block
In my demonstration, the whole feature is programmed into 1 function block.
The SIEMENS Library used in this function block are:
- RecipeImport: import recipe from the PLC’s load memory.
- RecipeExport: export recipe to the PLC’s load memory.
- CommConfig: configure the PLC’s communication interface.
The usages of these SIEMENS Libraries are detailed in the TIA Portal help files so I’m not going to go into details.
Instead, I’ll share my function block for demonstration below.
FUNCTION_BLOCK "ConfigNetwork"
{ S7_Optimized_Access := 'TRUE' }
VERSION : 0.1
VAR_INPUT
i_CMD : Int;
i_HWID_Interface : HW_INTERFACE;
i_HWID_SubModule : HW_INTERFACE;
END_VAR
VAR
s_CMD : Int;
s_Busy : Bool;
s_RecipeImport {InstructionName := 'RecipeImport'; LibVersion := '1.2'} : RecipeImport;
s_RecipeExport {InstructionName := 'RecipeExport'; LibVersion := '1.2'} : RecipeExport;
s_Recipe : Array[0..1] of "UDT_NetworkConfig";
s_NetworkConfig {InstructionName := 'CommConfig'; LibVersion := '1.0'} : CommConfig;
s_NetworkConfigData : Struct
Index : Int;
Mode : UInt;
DNS {InstructionName := 'Conf_DNS'; LibVersion := '1.0'; S7_SetPoint := 'False'} : Conf_DNS := (1, [()]);
IPAddress {InstructionName := 'Conf_IPSuitev4'; LibVersion := '1.0'; S7_SetPoint := 'False'} : Conf_IPSuitev4 := (2, (), (), ());
NTP {InstructionName := 'Conf_NTP'; LibVersion := '1.0'; S7_SetPoint := 'False'} : Conf_NTP := (1, [()]);
END_STRUCT;
s_Stp_Backup : Int;
s_Stp_Restore : Int;
END_VAR
VAR CONSTANT
CMD_Idle : Int := 998;
CMD_Backup : Int := 100;
CMD_Restore : Int := 200;
Stp_Backup_DNS : Int := 21;
Stp_Backup_IP : Int := 22;
Stp_Backup : Int := 24;
Stp_Backup_Done : Int := 29;
Stp_Restore_DNS : Int := 31;
Stp_Restore_IP : Int := 32;
Stp_Restore_NTP : Int := 33;
Stp_Restore : Int := 34;
Stp_Restore_Done : Int := 39;
END_VAR
BEGIN
REGION CMD_Response
IF #i_CMD <> #s_CMD AND NOT #s_Busy THEN
#s_CMD := #i_CMD;
#s_Busy := TRUE;
CASE #s_CMD OF
#CMD_Backup:
#s_Stp_Backup := #Stp_Backup_DNS;
#CMD_Restore:
#s_Stp_Restore := #Stp_Restore;
#CMD_Idle:
#s_Stp_Backup := #Stp_Backup_Done;
#s_Stp_Restore := #Stp_Restore_Done;
#s_Busy := FALSE;
END_CASE;
END_IF;
END_REGION
REGION Backup Sequence
IF #s_CMD = #CMD_Backup THEN
CASE #s_Stp_Backup OF
#Stp_Backup_DNS:
#s_NetworkConfig(REQ := TRUE,
HW_ID := #i_HWID_SubModule,
MODE := 0,
DATA := #s_NetworkConfigData.DNS);
IF #s_NetworkConfig.DONE THEN
#s_Recipe[0].DNS := #s_NetworkConfigData.DNS.DNS_Server[1];
#s_NetworkConfig(REQ := FALSE);
#s_RecipeExport(REQ := FALSE);
#s_Stp_Backup := #Stp_Backup_IP;
END_IF;
#Stp_Backup_IP:
#s_NetworkConfig(REQ := TRUE,
HW_ID := #i_HWID_Interface,
MODE := 0,
DATA := #s_NetworkConfigData.IPAddress);
IF #s_NetworkConfig.DONE THEN
#s_Recipe[0].IP := #s_NetworkConfigData.IPAddress.IPAddress;
#s_Recipe[0].SubnetMask := #s_NetworkConfigData.IPAddress.SubnetMaskPrefix;
#s_Recipe[0].Gateway := #s_NetworkConfigData.IPAddress.Gateway;
#s_NetworkConfig(REQ := FALSE);
#s_RecipeExport(REQ := FALSE);
#s_Stp_Backup := #Stp_Backup;
END_IF;
#Stp_Backup:
#s_RecipeExport(REQ := TRUE,
RECIPE_DB := #s_Recipe);
IF #s_RecipeExport.DONE THEN
#s_NetworkConfig(REQ := FALSE);
#s_RecipeExport(REQ := FALSE);
#s_Stp_Backup := #Stp_Backup_Done;
END_IF;
#Stp_Backup_Done:
#s_Busy := FALSE;
END_CASE;
END_IF;
END_REGION
REGION Restore Sequence
IF #s_CMD = #CMD_Restore THEN
CASE #s_Stp_Restore OF
#Stp_Restore:
#s_RecipeImport(REQ := TRUE,
RECIPE_DB := #s_Recipe);
IF #s_RecipeImport.DONE THEN
IF #s_Recipe[1].ApplyConfig THEN
#s_NetworkConfigData.DNS.DNS_Server[1] := #s_Recipe[1].DNS;
#s_NetworkConfigData.DNS.Count := 1;
#s_NetworkConfigData.IPAddress.write_mode := 2;
#s_NetworkConfigData.IPAddress.IPAddress := #s_Recipe[1].IP;
#s_NetworkConfigData.IPAddress.SubnetMaskPrefix := #s_Recipe[1].SubnetMask;
#s_NetworkConfigData.IPAddress.Gateway := #s_Recipe[1].Gateway;
#s_NetworkConfigData.NTP.Count := 1;
#s_NetworkConfigData.NTP.NTP_Server[1] := #s_Recipe[1].NTPServer;
#s_NetworkConfig(REQ := FALSE);
#s_RecipeImport(REQ := FALSE);
#s_Stp_Restore := #Stp_Restore_DNS;
ELSE
#s_NetworkConfig(REQ := FALSE);
#s_RecipeImport(REQ := FALSE);
#s_Stp_Restore := #Stp_Restore_Done;
END_IF;
END_IF;
#Stp_Restore_DNS:
#s_NetworkConfig(REQ := TRUE,
HW_ID := #i_HWID_SubModule,
MODE := 1,
DATA := #s_NetworkConfigData.DNS);
IF #s_NetworkConfig.DONE THEN
#s_NetworkConfig(REQ := FALSE);
#s_RecipeImport(REQ := FALSE);
#s_Stp_Restore := #Stp_Restore_IP;
END_IF;
#Stp_Restore_IP:
#s_NetworkConfig(REQ := TRUE,
HW_ID := #i_HWID_Interface,
MODE := 1,
DATA := #s_NetworkConfigData.IPAddress);
IF #s_NetworkConfig.DONE THEN
#s_NetworkConfig(REQ := FALSE);
#s_RecipeImport(REQ := FALSE);
#s_Stp_Restore := #Stp_Restore_NTP;
END_IF;
#Stp_Restore_NTP:
#s_NetworkConfig(REQ := TRUE,
HW_ID := #i_HWID_SubModule,
MODE := 1,
DATA := #s_NetworkConfigData.NTP);
IF #s_NetworkConfig.DONE THEN
#s_NetworkConfig(REQ := FALSE);
#s_RecipeImport(REQ := FALSE);
#s_Stp_Restore := #Stp_Restore_Done;
END_IF;
#Stp_Restore_Done:
#s_Busy := #s_Recipe[1].ApplyConfig := FALSE;
END_CASE;
END_IF;
END_REGION
END_FUNCTION_BLOCK
Demonstration
Below is a demonstration of how the function block backs up and configures the PLC’s communication.
Conclusion
Using the SIEMENS PLC’s recipe system and user program to configure the PLC’s communication interface can allow us to keep a consistent hardware configuration and software. WIth different recipes loaded, the PLC configures itself automatically. In my option, this is a much more elegant way to develop and deploy an application.