Skip to content

Commit

Permalink
Use o1heap for custom - realtime - memory allocation and in order to …
Browse files Browse the repository at this point in the history
…keep track of the memory situation.
  • Loading branch information
aentinger committed May 16, 2024
1 parent a1925b1 commit 52484ab
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 36 deletions.
144 changes: 110 additions & 34 deletions examples/opcUA_server/opcUA_server.ino
Original file line number Diff line number Diff line change
Expand Up @@ -56,13 +56,17 @@ extern "C"
* GLOBAL VARIABLES
**************************************************************************************/

/* Create a server listening on port 4840 (default) */
UA_Server * opc_ua_server = nullptr;

size_t const OPC_UA_SERVER_THREAD_STACK_SIZE = 65536; /* 64 kB */
static size_t const OPC_UA_SERVER_THREAD_STACK_SIZE = 65536*2; /* 64*2 kB */
static uint8_t alignas(uint32_t) OPC_UA_SERVER_THREAD_STACK[OPC_UA_SERVER_THREAD_STACK_SIZE];
rtos::Thread opc_ua_server_thread(osPriorityNormal, OPC_UA_SERVER_THREAD_STACK_SIZE, OPC_UA_SERVER_THREAD_STACK);

static size_t const OPC_UA_SERVER_THREAD_HEAP_SIZE = 65536*2; /* 64*2 kB */
template <size_t SIZE> struct alignas(O1HEAP_ALIGNMENT) OPC_UA_HEAP final : public std::array<uint8_t, SIZE> {};
static OPC_UA_HEAP<OPC_UA_SERVER_THREAD_HEAP_SIZE> OPC_UA_SERVER_THREAD_HEAP;
O1HeapInstance * o1heap_ins = nullptr;

UA_Int32 myInteger = 42;

REDIRECT_STDOUT_TO(Serial)
Expand All @@ -71,6 +75,61 @@ REDIRECT_STDOUT_TO(Serial)
* LOCAL FUNCTIONS
**************************************************************************************/

extern "C" void * o1heap_malloc(size_t size)
{
if (!o1heapDoInvariantsHold(o1heap_ins))
Serial.println("malloc error");

void * new_ptr = o1heapAllocate(o1heap_ins, size);

char msg[64];
snprintf(msg, sizeof(msg), "malloc: %d (%X)", size, new_ptr);
Serial.println(msg);

return new_ptr;
}

extern "C" void o1heap_free(void * ptr)
{
if (!o1heapDoInvariantsHold(o1heap_ins))
Serial.println("free error");

char msg[64];
snprintf(msg, sizeof(msg), "free: (%X)", ptr);
Serial.println(msg);

o1heapFree(o1heap_ins, ptr);
}

extern "C" void * o1heap_calloc(size_t nelem, size_t elsize)
{
if (!o1heapDoInvariantsHold(o1heap_ins))
Serial.println("calloc error");

char msg[64];
snprintf(msg, sizeof(msg), "calloc: nelem = %d, elsize = %d", nelem, elsize);
Serial.println(msg);

void * ptr = o1heap_malloc(nelem * elsize);
memset(ptr, 0, nelem * elsize);
return ptr;
}

extern "C" void * o1heap_realloc(void * old_ptr, size_t size)
{
if (!o1heapDoInvariantsHold(o1heap_ins))
Serial.println("realloc error");

char msg[64];
snprintf(msg, sizeof(msg), "realloc: old_ptr = %X, size = %d", old_ptr, size);
Serial.println(msg);

void * new_ptr = o1heap_malloc(size);
memcpy(new_ptr, old_ptr, size);
o1heap_free(old_ptr);
return new_ptr;
}

//void updater(UA_Server *server) {
// while (1) {
// delay(1000);
Expand Down Expand Up @@ -99,42 +158,59 @@ void setup()
Serial.print("Our IP is ");
Serial.println(Ethernet.localIP());

/* Create a server listening on port 4840 (default) */
opc_ua_server = UA_Server_new();

/* Add a variable node to the server */

/* 1) Define the variable attributes */
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "the answer");
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_HISTORYREAD;
/* We also set this node to historizing, so the server internals also know from it. */
attr.historizing = true;
UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);

// rtos::Thread t;
// t.start(mbed::callback(updater, opc_ua_server));

/* 2) Define where the node shall be added with which browsename */
UA_NodeId newNodeId = UA_NODEID_STRING(1, "the.answer");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_NodeId variableType = UA_NODEID_NULL; /* take the default variable type */
UA_QualifiedName browseName = UA_QUALIFIEDNAME(1, "the answer");

/* 3) Add the node */
UA_Server_addVariableNode(opc_ua_server,
newNodeId,
parentNodeId,
parentReferenceNodeId,
browseName,
variableType,
attr,
NULL, NULL);
/* Initialize heap memory. */
o1heap_ins = o1heapInit(OPC_UA_SERVER_THREAD_HEAP.data(), OPC_UA_SERVER_THREAD_HEAP.size());
if (o1heap_ins == nullptr) {
Serial.println("o1heap initialisation failed.");
for (;;) { }
}
char o1heap_info[128] = {0};
snprintf(o1heap_info,
sizeof(o1heap_info),
"o1Heap capacity: %d | allocated: %d | peak_allocated: %d",
o1heapGetDiagnostics(o1heap_ins).capacity,
o1heapGetDiagnostics(o1heap_ins).allocated,
o1heapGetDiagnostics(o1heap_ins).peak_allocated);
Serial.println(o1heap_info);

UA_mallocSingleton = o1heap_malloc;
UA_freeSingleton = o1heap_free;
UA_callocSingleton = o1heap_calloc;
UA_reallocSingleton = o1heap_realloc;

opc_ua_server_thread.start(
+[]()
{
/* Create a server listening on port 4840 (default) */
opc_ua_server = UA_Server_new();

/* Add a variable node to the server */

/* 1) Define the variable attributes */
UA_VariableAttributes attr = UA_VariableAttributes_default;
attr.displayName = UA_LOCALIZEDTEXT("en-US", "the answer");
attr.accessLevel = UA_ACCESSLEVELMASK_READ | UA_ACCESSLEVELMASK_WRITE | UA_ACCESSLEVELMASK_HISTORYREAD;
/* We also set this node to historizing, so the server internals also know from it. */
attr.historizing = true;
UA_Variant_setScalar(&attr.value, &myInteger, &UA_TYPES[UA_TYPES_INT32]);

/* 2) Define where the node shall be added with which browsename */
UA_NodeId newNodeId = UA_NODEID_STRING(1, "the.answer");
UA_NodeId parentNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_OBJECTSFOLDER);
UA_NodeId parentReferenceNodeId = UA_NODEID_NUMERIC(0, UA_NS0ID_ORGANIZES);
UA_NodeId variableType = UA_NODEID_NULL; /* take the default variable type */
UA_QualifiedName browseName = UA_QUALIFIEDNAME(1, "the answer");

/* 3) Add the node */
UA_Server_addVariableNode(opc_ua_server,
newNodeId,
parentNodeId,
parentReferenceNodeId,
browseName,
variableType,
attr,
NULL, NULL);

/* Print some threading related message. */
char thd_info_msg[128] = {0};
snprintf(thd_info_msg,
Expand Down
1 change: 1 addition & 0 deletions src/Arduino_open62541.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#pragma once

#include "open62541.h"
#include "o1heap/o1heap.h"

#if !defined(ARDUINO_OPTA)
# error "This library does only support Arduino Opta"
Expand Down
9 changes: 7 additions & 2 deletions src/arch/posix/eventloop_mbed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ UA_EventLoopPOSIX_deregisterFD(UA_EventLoopPOSIX *el, UA_RegisteredFD *rfd) {
}

rtos::EventFlags _events;
#include "../../o1heap/o1heap.h"

UA_StatusCode
UA_EventLoopPOSIX_pollFDs(UA_EventLoopPOSIX *el, UA_DateTime listenTimeout) {
Expand Down Expand Up @@ -118,15 +119,19 @@ UA_EventLoopPOSIX_pollFDs(UA_EventLoopPOSIX *el, UA_DateTime listenTimeout) {
short event = rfd->listenEvents;

extern rtos::Thread opc_ua_server_thread;
extern O1HeapInstance * o1heap_ins;

UA_LOG_INFO(el->eventLoop.logger,
UA_LOGCATEGORY_EVENTLOOP,
"Processing event %u on fd %u (stack: size = %d, free = %d, used = %d)",
"Processing event %u on fd %u (stack: size = %d, free = %d, used = %d) (heap: capacity = %d, allocated = %d, peak_allocated = %d)",
(unsigned)event,
(unsigned)rfd->fd,
opc_ua_server_thread.stack_size(),
opc_ua_server_thread.free_stack(),
opc_ua_server_thread.used_stack());
opc_ua_server_thread.used_stack(),
o1heapGetDiagnostics(o1heap_ins).capacity,
o1heapGetDiagnostics(o1heap_ins).allocated,
o1heapGetDiagnostics(o1heap_ins).peak_allocated);

/* Call the EventSource callback */
rfd->eventSourceCB(rfd->es, rfd, event);
Expand Down

0 comments on commit 52484ab

Please sign in to comment.