リソースに余裕があるマイコン向けのコンソールっぽい機能

[STM32CubeMX:RTOSでLチカしてみる(v4.26.1) - Qiita](https://qiita.com/amutou/items/3eb772dfdde83f922574)の続きっぽいやつです。

シングルトンでFreeRTOS_tasks/Consoleのインスタンスを管理しています。
またソースの最後にFreeRTOSのタスクの関数(`extern "C" void console_func(void const *argument)`)を宣言し、処理を実行します。

サンプルに`tasklist`と`water`と`sum `を実装しています。

tasklistはFreeRTOSのvTaskListを実行します。

waterはuxTaskGetStackHighWaterMarkの結果を表示します。
CubeMXでHighWaterMarkを有効化しておく必要があります。
またfreertos.cに以下のような宣言が必要になります。

```c
osThreadId *const thread_list[] = {
    &defaultTaskHandle,
    &consoleHandle,
    0};
```

sumは整数の加算です。strncmpの動作確認のために追加。

```c:Inc/FreeRTOS_tasks/Console.h
#ifndef FreeRTOS_tasks__Console__H
#define FreeRTOS_tasks__Console__H

#include <stdint.h>

namespace FreeRTOS_tasks
{
class Console
{
  protected: // portable
    static constexpr uint16_t line_buff_len = 100;
    static constexpr uint8_t format_buff_len = 20;

  public:
    void task_loop(void);

    static Console &get_instance(void)
    {
        return (instance);
    }

  protected:
  private:
    const char *const format;
    char line_buff[line_buff_len];

    static Console instance;

    void parse_command(void);

    void read_line(void);

    bool equal(const char *expected);
    bool equal_n(const char *expected);

    static const char *format_alloc_and_generate(void);

    Console(void);
    ~Console(void);
    Console(const Console &);
    Console &operator=(const Console &);
};
} // namespace FreeRTOS_tasks

#endif
```

```c:Src/FreeRTOS_tasks/Console.h
#include <FreeRTOS_tasks/Console.h>
#include <cmsis_os.h>
#include <stdio.h>
#include <string.h>
#include <math.h>

extern osThreadId *thread_list[];

namespace FreeRTOS_tasks
{
Console Console::instance;

void Console::task_loop(void)
{
    osDelay(500);

    printf("hello world\n");
    printf("sqrt(2):%f\n", sqrtf(2));

    while (1)
    {
        read_line();
        parse_command();
    }
}

void Console::parse_command(void)
{
    if (equal("tasklist"))
    {
        char buff[10 * 45];
        osThreadList((uint8_t *)buff);
        printf(
            "Name            State   Priorty Stack   Num\n"
            "*******************************************\n"
            "%s"
            "*******************************************\n",
            buff);
    }

    if (equal("water"))
    {
        printf("*** high water mark *****\n");

        for (uint16_t i = 0; thread_list[i]; i++)
        {
            const uint32_t water_mark = uxTaskGetStackHighWaterMark(*thread_list[i]);
            const char *const task_name = pcTaskGetName(*thread_list[i]);

            printf("%2hu %-*s %4lu\n", i + 1, configMAX_TASK_NAME_LEN, task_name, water_mark);
        }

        printf("*************************\n");
    }

    if (equal_n("sum "))
    {
        int a = 0;
        int b = 0;

        if (2 != sscanf(line_buff, "sum %d %d", &a, &b))
        {
            printf("format error: \"%s\"\n", line_buff);
        }
        else
        {
            printf("%d + %d = %d\n", a, b, a + b);
        }
    }
}

bool Console::equal(const char *const expected)
{
    return (0 == strcmp(expected, line_buff));
}

bool Console::equal_n(const char *const expected)
{
    return (0 == strncmp(expected, line_buff, strlen(expected)));
}

void Console::read_line(void)
{
    scanf(format, line_buff);
    getchar();

    char *const p = strchr(line_buff, '\r');

    if (p)
    {
        *p = '\0';
    }
}

const char *Console::format_alloc_and_generate(void)
{
    char *const format = new char[format_buff_len]();

    sprintf(format, "%%%hhu[^\n]%%*[^\n]", (sizeof(line_buff) / sizeof(line_buff[0])) - 1);

    return (format);
}

Console::Console(void)
    : format(format_alloc_and_generate()),
      line_buff()
{
}

Console::~Console(void)
{
    delete[] format;
}
} // namespace FreeRTOS_tasks

extern "C" void console_func(void const *argument)
{
    FreeRTOS_tasks::Console::get_instance().task_loop();
}
```

本来はコマンド管理するための構造体や配列を使って処理したほうがいいと思います。
そうすればコマンドのヘルプとかも作れるし。
もっとも、マイコンのコードでそこまで必要か、と言われると微妙なところ。
ただ、作ったものを他の人に使わせる可能性があるなら「わからなかったらターミナルソフトで"?"を叩けばヘルプが表示されるからそれを見て」と言えるのは楽でいい。

たとえば「`sum `関数ってどんな機能だ?」と疑問が湧いたときに、ヘルプを表示して`sum %d %d`と表示されれば、このコマンドは整数が2つ必要だな、とわかります。

***

formatをconstにするためにnew[]/delete[]を使っているけど、これなんとかならないかなー。
今の所、プロジェクト全体でも動的メモリ確保を使ってるのはこの部分だけ。