How could I automatically set the heap size for freertos?

I am learning about how to use FreeRTOS and STM32duino. I added the the STM32FreeRTOS library to my project and try to use heap_4.c. I could manually set the heap size by the macro configTOTAL_HEAP_SIZE , but I want it be set automatically.

So I set configAPPLICATION_ALLOCATED_HEAP to 1 and declare a byte array uint8_t ucHeap[ configTOTAL_HEAP_SIZE ]; in my main.cpp . The macro configTOTAL_HEAP_SIZE should be replaced by ((size_t)((uint32_t)&_estack - (uint32_t)&_Min_Stack_Size - (uint32_t)&_end)), And the compiler says that it is not an integral constant-expression.

_estack, _Min_Stack_Size and _end could be found in linker script. And I could print the right value of configTOTAL_HEAP_SIZE, but I can’t use configTOTAL_HEAP_SIZE to declare the heap.

[env:genericSTM32F407VET6]
platform = ststm32
board = genericSTM32F407VET6
framework = arduino
lib_archive = false
build_flags =
-D SERIAL_UART_INSTANCE=1
-D configMEMMANG_HEAP_NB=4
-D configAPPLICATION_ALLOCATED_HEAP=1
-D configSUPPORT_DYNAMIC_ALLOCATION=1
; I want to set heap size automatically
; -D configTOTAL_HEAP_SIZE=96*1024

lib_deps =
stm32duino/STM32duino FreeRTOS@^10.3.1
In file included from .pio/libdeps/genericSTM32F407VET6/STM32duino FreeRTOS/src/FreeRTOSConfig.h:17,
                 from .pio/libdeps/genericSTM32F407VET6/STM32duino FreeRTOS/src/FreeRTOS/Source/include/FreeRTOS.h:56,
                 from .pio/libdeps/genericSTM32F407VET6/STM32duino FreeRTOS/src/FreeRTOS.h:9,
                 from .pio/libdeps/genericSTM32F407VET6/STM32duino FreeRTOS/src/cmsis_os.h:9,
                 from src/main.cpp:3:
.pio/libdeps/genericSTM32F407VET6/STM32duino FreeRTOS/src/FreeRTOSConfig_Default.h:106:44: error: size of array 'ucHeap' is not an integral constant-expression
  106 | #define configTOTAL_HEAP_SIZE             ((size_t)((uint32_t)&_estack - (uint32_t)&_Min_Stack_Size - (uint32_t)&_end))
      |                                           ~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
src/main.cpp:12:17: note: in expansion of macro 'configTOTAL_HEAP_SIZE'
   12 | uint8_t ucHeap[ configTOTAL_HEAP_SIZE ];

thanks

I implement the automatic setting of heap size by heap_5.c. I define a preinit function and initialize FreeRTOS heap in it. Although PlatformIO can not gets the memory of heap in building and uploading and displays it in terminal.

And I redirected functions of newlib to FreeRTOS’s implements to make Arduino work.

But I still not found how to make heap_4.c works automatically. Does anybody knows how to do it ?

main.cpp

#include <Arduino.h>
#include "cmsis_os.h"

#define SECTION(sec) __attribute__((section(sec)))
#define USED __attribute__((used))

typedef void (*void_func_ptr_t)(void);

extern "C" void preinit(void)
{
    size_t start_addr = (uint32_t)&_end;
    size_t end_addr = (uint32_t)&_estack - (uint32_t)&_Min_Stack_Size;

    HeapRegion_t xHeapRegions[] = {
        { (unsigned char *)start_addr, end_addr - start_addr },
        { NULL, 0 }
    };
    vPortDefineHeapRegions(xHeapRegions);
}

// Add USED to make sure the compiler won't optimize it out
// Add SECTION to make sure it's in .preinit_array section
// .preinit_array section is used by GCC to call functions before main and class constructors
// See https://stackoverflow.com/questions/15265295/understanding-the-libc-init-array
extern "C" const void_func_ptr_t preinit_array[] USED SECTION(".preinit_array") =
{
    &preinit
};


void setup() {
    Serial1.setRx(PA10);
    Serial1.setTx(PA9);
    Serial1.begin(115200);
    Serial1.println("Hello World!");
}

void loop(){
    void * ptr = pvPortMalloc(1024);
    // malloc and free need to be redirect to FreeRTOS pvPortMalloc and vPortFree
    // Arudino has lots of implementation that use malloc
    // for example, Serial1.printf may use malloc internally
    // if you don't redirect malloc and free, you will get heap corruption and crash
    Serial1.printf("Current free heap size: %d\n", xPortGetFreeHeapSize());
    vPortFree(ptr);

    Serial1.printf("Total heap size: %d\n", configTOTAL_HEAP_SIZE);
    Serial1.printf("Free heap size: %d\n", xPortGetFreeHeapSize());
    Serial1.printf("Minimum free heap size: %d\n", xPortGetMinimumEverFreeHeapSize());
    delay(1000);
}

It’s necessary and convenient to redirect newlib to FreeRTOS.

freertos_newlib_redirect.cpp

#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include "cmsis_os.h"

#define custom_malloc(size) pvPortMalloc(size)
#define custom_free(ptr) vPortFree(ptr)

extern "C" void* _malloc_r(struct _reent *r, size_t size)
{
    return custom_malloc(size);
}

extern "C" void _free_r(struct _reent *r, void *ptr)
{
    custom_free(ptr);
}

extern "C" void* _realloc_r(struct _reent *r, void *ptr, size_t size)
{
    if (ptr == NULL)
    {
        return custom_malloc(size);
    }
    if (size == 0)
    {
        custom_free(ptr);
        return NULL;
    }

    void *newPtr = custom_malloc(size);
    if (newPtr != NULL)
    {
        memcpy(newPtr, ptr, size);
        custom_free(ptr);
    }
    return newPtr;
}

extern "C" void* _calloc_r(struct _reent *r, size_t nmemb, size_t size)
{
    void *ptr = custom_malloc(nmemb * size);
    if (ptr != NULL)
    {
        memset(ptr, 0, nmemb * size);
    }
    return ptr;
}

extern "C" void* _memalign_r(struct _reent *r, size_t alignment, size_t size)
{
    configASSERT((alignment & (alignment - 1)) == 0);
    return NULL;
}

extern "C" struct mallinfo _mallinfo_r(struct _reent *r)
{
    struct mallinfo info = {0};
    return info;
}

extern "C" void _malloc_stats_r (struct _reent * r)
{
    configASSERT(0);
}

extern "C" int _mallopt_r (struct _reent *r, int param, int value)
{
    configASSERT(0);
    return 0;
}

extern "C" size_t _malloc_usable_size_r(struct _reent *r, void *ptr)
{
    configASSERT(0);
    return 0;
}

extern "C" void *_valloc_r(struct _reent *r, size_t size)
{
    configASSERT(0);
    return NULL;
}

extern "C" void *_pvalloc_r(struct _reent *r, size_t size)
{
    configASSERT(0);
    return NULL;
}

extern "C" int _malloc_trim_r(struct _reent *r, size_t pad)
{
    configASSERT(0);
    return 0;
}

extern "C" void _mstats_r(struct _reent *r, char *s)
{
    configASSERT(0);
}