[3] Construction of OPCUA server on STM32 platform based on open62541

The following content was compiled again on stm32 platform around September 2020, but it was not released. The code involved in this article may have changed. Please refer to the official code warehouse

Prepare FreeRTOS+LwIP

The previous compilation of single files said that open62541 is built on the system, so before transplantation, you need to prepare a well running STM32 FressRTO+Lwip code. If not, you can use GitHub STM32F4-FreeRTOS-LwIP Download the code. See readme.md It is said in. The IDE for compiling stm32 code. According to my personal preference, I use keil 5.

When FreeRTOS+LwIP is OK, you can start the next step.

Compilation conditions

Add macro definition

  1. Add architecture macro - UA in FreeRTOS+LWIP project_ ARCHITECTURE_ FREERTOSLWIP
  2. Add LwIP macro
    // In ` lwipopts H ` add the following to the document
    #define LWIP_COMPAT_SOCKETS 0 // Don't do name define-transformation in networking function names.
    #define LWIP_SOCKET 1 // Enable Socket API (normally already set)
    #define LWIP_DNS 1 // enable the lwip_getaddrinfo function, struct addrinfo and more.
    #define SO_REUSE 1 // Allows to set the socket as reusable
    #define LWIP_TIMEVAL_PRIVATE 0 // This is optional. Set this flag if you get a compilation error about redefinition of struct timeval
  3. Add FreeRTOS macro
    // In ` freertosconfig H ` add the following to the document
    #define configCHECK_FOR_STACK_OVERFLOW 1
    #define configUSE_MALLOC_FAILED_HOOK 1


Comment out sockets H lines 315 ~ 319, 324, etc. of the file

Before comments:

After comments:

This is because this structure is used in open62541, but the original LwIP is useless, so it needs to be released. Of course, you can also directly release LwIP_ TIMEVAL_ The value of private is changed to 1, but I don't know why I didn't do that at that time @ (black line)

Add source file

The preparatory work is completed. Now put [zero] compile separate open62541 source files and header files supporting STM32 platform based on open62541 project Open62541.0 generated in C and open62541 H add to project engineering

Adding source files and header files should not require me to say more @ (you know)

Modify FreeRTOS source code

Because UA was checked during previous compilation_ ARCH_ FREERTOS_ USE_ OWN_ Memory is to use FreeRTOS's own memory management function

However, among the memory management functions provided by FreeRTOS, there are only pvPortMalloc and vPortFree. There are no pvPortCalloc and pvPortRealloc functions, so we need to implement these two functions ourselves. It is impossible. Foreign netizens have implemented them. Then I used them and found no problems, so I posted them directly below

void *pvPortCalloc( size_t nmemb, size_t size )
	void *pvReturn;
		pvReturn = pvPortMalloc( nmemb*size );

	return pvReturn;

void *pvPortRealloc( void *pv, size_t xWantedSize )
	uint8_t *puc = ( uint8_t * ) pv;
 	BlockLink_t *pxLink;
	int datasize;
 	void *pvReturn = NULL;

    if (xWantedSize == 0) {
        return NULL;
 	if (pv == NULL)
 		return pvPortMalloc(xWantedSize);
 	/* The memory being freed will have an BlockLink_t structure immediately
 	before it. */
 	puc -= xHeapStructSize;
	pxLink = ( BlockLink_t * ) puc;
	datasize = (pxLink->xBlockSize & ~xBlockAllocatedBit) - xHeapStructSize;
 	if (datasize >= xWantedSize) // have enough memory don't need realloc
 		return pv;

    pvReturn = pvPortMalloc(xWantedSize);
    if (pvReturn == NULL) // malloc fail, return NULL, don't free pv.
        return NULL;
    memcpy(pvReturn, pv, xWantedSize);
    vPortFree(pv);// realloc success, copy and free pv.
    return pvReturn;

Then in portable Add the following code to H

void *pvPortCalloc( size_t nmemb, size_t size );
void *pvPortRealloc( void *pv, size_t xWantedSize );

Modify open62541

  • In open62541 Redefine int as ssize in H_ t.
  • Statement OPEN62541_FEERTOS_USE_OWN_MEM, enable FreeRTOS's own memory management function.
  • In open62541 C, AF will be involved_ The contents of inet6 are masked by macro definitions
  • If sockets If the H file does not contain the following, add
    #if !defined(sa_family_t) && !defined(SA_FAMILY_T_DEFINED)
    typedef u8_t sa_family_t;
    struct sockaddr_storage {
      u8_t        s2_len;
      sa_family_t ss_family;
      char        s2_data1[2];
      u32_t       s2_data2[3];
    #if LWIP_IPV6
      u32_t       s2_data3[3];
    #endif /* LWIP_IPV6 */

OPCUA server

Write the following code to establish the server

void opcua_task(void *pvParameter)
	//The default 64KB of memory for sending and receicing buffer caused problems to many users. With the code below, they are reduced to ~16KB
	UA_UInt32 sendBufferSize = 16000;       //64 KB was too much for my platform
	UA_UInt32 recvBufferSize = 16000;       //64 KB was too much for my platform
	UA_UInt16 portNumber = 4840;

	UA_Server* mUaServer = UA_Server_new();
	UA_ServerConfig *uaServerConfig = UA_Server_getConfig(mUaServer);
	UA_ServerConfig_setMinimalCustomBuffer(uaServerConfig, portNumber, 0, sendBufferSize, recvBufferSize);

	//VERY IMPORTANT: Set the hostname with your IP before starting the server
	UA_ServerConfig_setCustomHostname(uaServerConfig, UA_STRING(""));

	//The rest is the same as the example

	UA_Boolean running = true;

//	// add a variable node to the adresspace
//	UA_VariableAttributes attr = UA_VariableAttributes_default;
//	UA_Int32 myInteger = 42;
//	UA_Variant_setScalarCopy(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);
//	attr.description = UA_LOCALIZEDTEXT_ALLOC("en-US","the answer");
//	attr.displayName = UA_LOCALIZEDTEXT_ALLOC("en-US","the answer");
//	UA_NodeId myIntegerNodeId = UA_NODEID_STRING_ALLOC(1, "the.answer");
//	UA_QualifiedName myIntegerName = UA_QUALIFIEDNAME_ALLOC(1, "the answer");
//	UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
//	UA_Server_addVariableNode(mUaServer, myIntegerNodeId, parentNodeId,
//																													parentReferenceNodeId, myIntegerName,
//																													UA_NODEID_NULL, attr, NULL, NULL);

//	/* allocations on the heap need to be freed */
//	UA_VariableAttributes_clear(&attr);
//	UA_NodeId_clear(&myIntegerNodeId);
//	UA_QualifiedName_clear(&myIntegerName);

	UA_StatusCode retval = UA_Server_run(mUaServer, &running);


If the above steps are OK, click this button at this timeAfter that, the compilation will be successful without errors


Use UaExport to connect to the OPCUA server. After the connection is successful, it is shown in the figure

So far, porting open62541 to STM32 has been completed @ (too happy)

Added by thor erik on Tue, 11 Jan 2022 16:10:32 +0200