ESP-IDFを使ってみる

タスク状態の監視

こちらでesp-open-rtosのタスクの監視を 紹介していますが、esp-idfでは少し挙動が違います。
まずは、esp-open-rtosと同じコードを試してみます。
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

static const char *TAG = "MAIN";

static void test_task(void *pvParameters)
{
    ESP_LOGI(pcTaskGetTaskName(0), "Start");
    for(int i=0;i<5;i++) {
        vTaskDelay(100);
    }
    ESP_LOGI(pcTaskGetTaskName(0), "Finish");
    vTaskDelete(NULL);
}


void app_main(void)
{
    TaskHandle_t xHandle = NULL;
    xTaskCreate(&test_task, "TEST", 8192, NULL, 3, &xHandle);
    while(1) {
        eTaskState taskState = eTaskGetState( xHandle );
        ESP_LOGI(TAG, "taskState=%d",taskState);
        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}

TESTタスクが終了すると、一瞬タスク状態はeDeletedになりますが、直ぐにeReadyに変わります。
I (304) MAIN: taskState=1
I (304) TEST: Start
I (1314) MAIN: taskState=2
I (2314) MAIN: taskState=2
I (3314) MAIN: taskState=2
I (4314) MAIN: taskState=2
I (5314) TEST: Finish
I (5314) MAIN: taskState=4
I (6314) MAIN: taskState=1
I (7314) MAIN: taskState=1
I (8314) MAIN: taskState=1
I (9314) MAIN: taskState=1

EspressIFに確認したら以下の回答でした。
Checking task status after a task is deleted might be valid for a task with statically allocated TCB.

But in your case the TCB is originally allocated from the heap, so the memory is released back to the heap when the task is deleted.
By passing the task handle (which in FreeRTOS is a pointer to TCB) to the eTaskGetState function, you are essentially causing a "use after free", reading some value from a now-deallocated heap block.

This behavior is the same in upstream FreeRTOS.

Google翻訳君にお願いします。
タスクが削除された後のタスクステータスのチェックは、TCBが静的に割り当てられ たタスクに対して有効な場合があります。

しかし、あなたの場合、TCBはもともとヒープから割り当てられているため、タスクが削除されると、メモリはヒープに解放さ れます。
タスクハンドル(FreeRTOSではTCBへのポインター)をeTaskGetState関数に渡すことにより、 本質的に「解放後使用」を引き起こし、現在割り当て解除されているヒープブロックから値を読み取ります。

この動作は、上流のFreeRTOSでも同じです。

要するにvTaskDelete()でdeleteしたタスクのタスクハンドルは無効になるので、正しくタスク状態を取ることができないみたいで す。



そこで、以下のコードで試してみました。
このコードをビルドするためには、menuconfigでCompoent config→FreeRTOS→Kernel→configUSE_TRACE_FACILITYを有効にする必要が有ります。
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

static const char *TAG = "MAIN";

static void test_task(void *pvParameters)
{
        ESP_LOGI(pcTaskGetTaskName(0), "Start");
        for(int i=0;i<5;i++) {
                vTaskDelay(100);
        }
        ESP_LOGI(pcTaskGetTaskName(0), "Finish");
        vTaskDelete(NULL);
}


void app_main(void)
{
    TaskHandle_t xHandle = NULL;
    xTaskCreate(&test_task, "TEST", 8192, NULL, 3, &xHandle);

    UBaseType_t uxArraySize = uxTaskGetNumberOfTasks();
    ESP_LOGI(TAG, "uxArraySize=%d", uxArraySize);
    TaskStatus_t *pxTaskStatusArray;
    pxTaskStatusArray = pvPortMalloc( uxArraySize * sizeof( TaskStatus_t ) );
    if( pxTaskStatusArray == NULL ) {
        ESP_LOGE(TAG, "pvPortMalloc fail");
    }
    while(1) {
        eTaskState taskState = eTaskGetState( xHandle );
        ESP_LOGI(TAG, "taskState=%d",taskState);

        /* Generate raw status information about each task. */
        uint32_t ulTotalRunTime;
        uxArraySize = uxTaskGetSystemState( pxTaskStatusArray, uxArraySize, &ulTotalRunTime );
        for( int x = 0; x < uxArraySize; x++ ) {
            ESP_LOGI(TAG, "pcTaskName=%s eCurrentState=%d xTaskNumber=%lu, uxCurrentPriority=%lu",
                pxTaskStatusArray[ x ].pcTaskName,
                pxTaskStatusArray[ x ].eCurrentState,
                (unsigned long)pxTaskStatusArray[ x ].xTaskNumber,
                (unsigned long)pxTaskStatusArray[ x ].uxCurrentPriority);
        }

        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }

}

5313TickでTESTタスクが終了すると、TCB(Task Control Block)からこのタスクのエントリが消えます。
I (303) MAIN: uxArraySize=9
I (303) TEST: Start
I (313) MAIN: taskState=2
I (313) MAIN: pcTaskName=main eCurrentState=1 xTaskNumber=5, uxCurrentPriority=1
I (323) MAIN: pcTaskName=IDLE1 eCurrentState=1 xTaskNumber=7, uxCurrentPriority=0
I (333) MAIN: pcTaskName=IDLE0 eCurrentState=1 xTaskNumber=6, uxCurrentPriority=0
I (333) MAIN: pcTaskName=TEST eCurrentState=2 xTaskNumber=12, uxCurrentPriority=3
I (343) MAIN: pcTaskName=Tmr Svc eCurrentState=2 xTaskNumber=8, uxCurrentPriority=1
I (353) MAIN: pcTaskName=dport eCurrentState=4 xTaskNumber=4, uxCurrentPriority=5
I (363) MAIN: pcTaskName=esp_timer eCurrentState=2 xTaskNumber=1, uxCurrentPriority=22
I (373) MAIN: pcTaskName=ipc1 eCurrentState=2 xTaskNumber=3, uxCurrentPriority=24
I (383) MAIN: pcTaskName=ipc0 eCurrentState=2 xTaskNumber=2, uxCurrentPriority=24
I (1383) MAIN: taskState=2
I (1383) MAIN: pcTaskName=main eCurrentState=1 xTaskNumber=5, uxCurrentPriority=1
I (1383) MAIN: pcTaskName=IDLE1 eCurrentState=1 xTaskNumber=7, uxCurrentPriority=0
I (1383) MAIN: pcTaskName=IDLE0 eCurrentState=1 xTaskNumber=6, uxCurrentPriority=0
I (1393) MAIN: pcTaskName=TEST eCurrentState=2 xTaskNumber=12, uxCurrentPriority=3
I (1403) MAIN: pcTaskName=Tmr Svc eCurrentState=2 xTaskNumber=8, uxCurrentPriority=1
I (1413) MAIN: pcTaskName=ipc1 eCurrentState=2 xTaskNumber=3, uxCurrentPriority=24
I (1423) MAIN: pcTaskName=ipc0 eCurrentState=2 xTaskNumber=2, uxCurrentPriority=24
I (1433) MAIN: pcTaskName=esp_timer eCurrentState=2 xTaskNumber=1, uxCurrentPriority=22
I (2433) MAIN: taskState=2
I (2433) MAIN: pcTaskName=main eCurrentState=1 xTaskNumber=5, uxCurrentPriority=1
I (2433) MAIN: pcTaskName=IDLE1 eCurrentState=1 xTaskNumber=7, uxCurrentPriority=0
I (2433) MAIN: pcTaskName=IDLE0 eCurrentState=1 xTaskNumber=6, uxCurrentPriority=0
I (2443) MAIN: pcTaskName=TEST eCurrentState=2 xTaskNumber=12, uxCurrentPriority=3
I (2453) MAIN: pcTaskName=Tmr Svc eCurrentState=2 xTaskNumber=8, uxCurrentPriority=1
I (2463) MAIN: pcTaskName=ipc0 eCurrentState=2 xTaskNumber=2, uxCurrentPriority=24
I (2473) MAIN: pcTaskName=esp_timer eCurrentState=2 xTaskNumber=1, uxCurrentPriority=22
I (2483) MAIN: pcTaskName=ipc1 eCurrentState=2 xTaskNumber=3, uxCurrentPriority=24
I (3483) MAIN: taskState=2
I (3483) MAIN: pcTaskName=main eCurrentState=1 xTaskNumber=5, uxCurrentPriority=1
I (3483) MAIN: pcTaskName=IDLE1 eCurrentState=1 xTaskNumber=7, uxCurrentPriority=0
I (3483) MAIN: pcTaskName=IDLE0 eCurrentState=1 xTaskNumber=6, uxCurrentPriority=0
I (3493) MAIN: pcTaskName=TEST eCurrentState=2 xTaskNumber=12, uxCurrentPriority=3
I (3503) MAIN: pcTaskName=Tmr Svc eCurrentState=2 xTaskNumber=8, uxCurrentPriority=1
I (3513) MAIN: pcTaskName=esp_timer eCurrentState=2 xTaskNumber=1, uxCurrentPriority=22
I (3523) MAIN: pcTaskName=ipc1 eCurrentState=2 xTaskNumber=3, uxCurrentPriority=24
I (3533) MAIN: pcTaskName=ipc0 eCurrentState=2 xTaskNumber=2, uxCurrentPriority=24
I (4533) MAIN: taskState=2
I (4533) MAIN: pcTaskName=main eCurrentState=1 xTaskNumber=5, uxCurrentPriority=1
I (4533) MAIN: pcTaskName=IDLE1 eCurrentState=1 xTaskNumber=7, uxCurrentPriority=0
I (4533) MAIN: pcTaskName=IDLE0 eCurrentState=1 xTaskNumber=6, uxCurrentPriority=0
I (4543) MAIN: pcTaskName=TEST eCurrentState=2 xTaskNumber=12, uxCurrentPriority=3
I (4553) MAIN: pcTaskName=Tmr Svc eCurrentState=2 xTaskNumber=8, uxCurrentPriority=1
I (4563) MAIN: pcTaskName=ipc1 eCurrentState=2 xTaskNumber=3, uxCurrentPriority=24
I (4573) MAIN: pcTaskName=ipc0 eCurrentState=2 xTaskNumber=2, uxCurrentPriority=24
I (4583) MAIN: pcTaskName=esp_timer eCurrentState=2 xTaskNumber=1, uxCurrentPriority=22
I (5313) TEST: Finish
I (5583) MAIN: taskState=1
I (5583) MAIN: pcTaskName=main eCurrentState=1 xTaskNumber=5, uxCurrentPriority=1
I (5583) MAIN: pcTaskName=IDLE1 eCurrentState=1 xTaskNumber=7, uxCurrentPriority=0
I (5583) MAIN: pcTaskName=IDLE0 eCurrentState=1 xTaskNumber=6, uxCurrentPriority=0
I (5593) MAIN: pcTaskName=Tmr Svc eCurrentState=2 xTaskNumber=8, uxCurrentPriority=1
I (5603) MAIN: pcTaskName=ipc0 eCurrentState=2 xTaskNumber=2, uxCurrentPriority=24
I (5613) MAIN: pcTaskName=esp_timer eCurrentState=2 xTaskNumber=1, uxCurrentPriority=22
I (5623) MAIN: pcTaskName=ipc1 eCurrentState=2 xTaskNumber=3, uxCurrentPriority=24
I (6633) MAIN: taskState=1
I (6633) MAIN: pcTaskName=main eCurrentState=1 xTaskNumber=5, uxCurrentPriority=1
I (6633) MAIN: pcTaskName=IDLE1 eCurrentState=1 xTaskNumber=7, uxCurrentPriority=0
I (6633) MAIN: pcTaskName=IDLE0 eCurrentState=1 xTaskNumber=6, uxCurrentPriority=0
I (6643) MAIN: pcTaskName=Tmr Svc eCurrentState=2 xTaskNumber=8, uxCurrentPriority=1
I (6653) MAIN: pcTaskName=esp_timer eCurrentState=2 xTaskNumber=1, uxCurrentPriority=22
I (6663) MAIN: pcTaskName=ipc1 eCurrentState=2 xTaskNumber=3, uxCurrentPriority=24
I (6673) MAIN: pcTaskName=ipc0 eCurrentState=2 xTaskNumber=2, uxCurrentPriority=24
I (7683) MAIN: taskState=1
I (7683) MAIN: pcTaskName=main eCurrentState=1 xTaskNumber=5, uxCurrentPriority=1
I (7683) MAIN: pcTaskName=IDLE1 eCurrentState=1 xTaskNumber=7, uxCurrentPriority=0
I (7683) MAIN: pcTaskName=IDLE0 eCurrentState=1 xTaskNumber=6, uxCurrentPriority=0
I (7693) MAIN: pcTaskName=Tmr Svc eCurrentState=2 xTaskNumber=8, uxCurrentPriority=1
I (7703) MAIN: pcTaskName=ipc1 eCurrentState=2 xTaskNumber=3, uxCurrentPriority=24
I (7713) MAIN: pcTaskName=ipc0 eCurrentState=2 xTaskNumber=2, uxCurrentPriority=24
I (7723) MAIN: pcTaskName=esp_timer eCurrentState=2 xTaskNumber=1, uxCurrentPriority=22

EspressIFのエンジニアは vTaskSetThreadLocalStoragePointerAndDelCallback() のAPIを使えば、
vTaskDelete()イベントを取れると教えてくれましたがまだ試していません。
FreeRTOSの関数を使って自前でタスクの終了を通知するようにした方が簡単です。



vTaskList()でフォーマットされたタスクの状態を文字列変数に取り出すことができます。
vTaskList()を使う場合、これらの項目を有効にする必要が有ります。


これがWiFiが動いていないときのタスクの一覧です。
左から、タスク名、タスク状態、タスク優先度、現在のスタックサイズ、タスク番号、実行中のコア番号です。
main            X       1       1288    3       0
IDLE1           R       0       1024    5       1
IDLE0           R       0       1236    4       0
ipc1            S       24      496     2       1
ipc0            S       24      504     1       0

WiFiを有効にすると一気にバックグラウンドのタスクが増えます。
tiTはTCP-IPスタック(LwIP)のメインタスクで、TCP-IP パケットを処理するタスクです。
sys_evtは(おそらく)Wi-Fi や TCP-IP スタック イベントなどのシステム イベントを処理するタスクです。
SoftAPモードの時はDHCP serverが有効になりますが、タスクではなくtiTのモジュールとして動作するみたいです。

tiTタスク(TCP-IP Stack Task)のコア番号は-1(Not pinned)になっています。
これはtiTタスクが動くコアが固定されていないことを意味しています。
tiTタスクの優先度は結構高いので、WiFiを有効にするだけで、コア#1で動いているタスクは影響を受けます。
TCP-IP通信の影響を受けないようにするためには、優先度を19以上で起動する必要が有ります。
なお、システムバックグラウンドタスクは全てコア0で動くと誤認して、アプリケーションタスクをコア1固定で起動しているケースをよく目にしま す。
以下の様に幾つかのシステムバックグラウンドタスクの動作コアは不定なので、アプリケーションタスクをコア1固定で起動することに意味は有りませ ん。
コア0が空いていて、コア1がBusyの時に、アプリケーションが待たされるだけです。
main            X       1       452     4       0
IDLE1           R       0       1036    6       1
IDLE0           R       0       912     5       0
tiT             B       18      2208    8       -1
Tmr Svc         B       1       1544    7       -1
ipc1            S       24      500     2       1
esp_timer       S       22      3240    3       0
wifi            B       23      3552    10      0
sys_evt         B       20      744     9       0
ipc0            S       24      508     1       0



タスクの終了を通知する方法は幾つか有ります。
タスクの結果を文字列などで起動元に戻すときはQueueやRingBufferを使います。
タスクの結果を24Bitの数値で起動元に戻すときはEventGroupを使います。
ただ単に、タスクが終わったことを起動元に通知するときにはTask Notifications機能を使います。
MutexやSemaphoreを使って、タスクの終了を通知することもできますが、これらはどちらかというと、資源の排他制御のために使われま す。

以下はxTaskNotifyGiveを使った例です。
子タスク起動時に親タスクのタスクハンドルをパラメータで渡しています。
子タスクは終了時に xTaskNotifyGive で親タスクに終了を通知します。
親タスクは ulTaskNotifyTake で子タスクの終了を待ちます。
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

void task(void *pvParameters)
{
    TaskHandle_t ParentTaskHandle = (TaskHandle_t)pvParameters;
    ESP_LOGI(pcTaskGetName(NULL), "START tick:%"PRIu32, xTaskGetTickCount());
    vTaskDelay(500);
    xTaskNotifyGive( ParentTaskHandle );
    //xTaskNotify( ParentTaskHandle, 0x10, eSetValueWithOverwrite );
    ESP_LOGI(pcTaskGetName(NULL), "END tick:%"PRIu32, xTaskGetTickCount());
    vTaskDelete( NULL );
}

void app_main()
{
    TaskHandle_t ParentTaskHandle = xTaskGetCurrentTaskHandle();
    UBaseType_t my_prio = uxTaskPriorityGet(NULL);
    ESP_LOGI(pcTaskGetName(NULL), "my_prio=%d", my_prio);

    xTaskCreate(&task, "Task", 2048, (void *)ParentTaskHandle, my_prio+1, NULL);
    uint32_t value = ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
    ESP_LOGI(pcTaskGetName(NULL), "ulTaskNotifyTake value=0x%"PRIx32, value);
}

実行すると以下の様にmain側の ulTaskNotifyTake で子タスクの終了を検出できます。
ulTaskNotifyTakeの正常時の戻り値は1、タイムアウト発生時は0です。
I (303) main_task: Started on CPU0
I (313) main_task: Calling app_main()
I (313) main: my_prio=1
I (313) Task: START tick:1
I (5313) Task: END tick:501
I (5313) main: ulTaskNotifyTake value=0x1
I (5313) main_task: Returned from app_main()



xTaskNotifyGiveの代わりに、xTaskNotifyを使う方法も有ります。
xTaskNotifyでは32Bitの戻り値を設定することができます。
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

void task(void *pvParameters)
{
    TaskHandle_t ParentTaskHandle = (TaskHandle_t)pvParameters;
    ESP_LOGI(pcTaskGetName(NULL), "START tick:%"PRIu32, xTaskGetTickCount());
    vTaskDelay(500);
    //xTaskNotifyGive( ParentTaskHandle );
    xTaskNotify( ParentTaskHandle, 0x10, eSetValueWithOverwrite );
    ESP_LOGI(pcTaskGetName(NULL), "END tick:%"PRIu32, xTaskGetTickCount());
    vTaskDelete( NULL );
}

void app_main()
{
    TaskHandle_t ParentTaskHandle = xTaskGetCurrentTaskHandle();
    UBaseType_t my_prio = uxTaskPriorityGet(NULL);
    ESP_LOGI(pcTaskGetName(NULL), "my_prio=%d", my_prio);

    xTaskCreate(&task, "Task", 2048, (void *)ParentTaskHandle, my_prio+1, NULL);
    uint32_t value = ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
    ESP_LOGI(pcTaskGetName(NULL), "ulTaskNotifyTake value=0x%"PRIx32, value);
}

実行すると以下の様にmain側の ulTaskNotifyTake で子タスクの終了と終了コードを検出できます。
タイムアウト発生時の終了コードは0なので、終了コードに0は使えません
xQueueOverwrite()の軽量な代替手段として使用することができます。
I (302) main_task: Started on CPU0
I (312) main_task: Calling app_main()
I (312) main: my_prio=1
I (312) Task: START tick:1
I (5312) Task: END tick:501
I (5312) main: ulTaskNotifyTake value=0x10
I (5312) main_task: Returned from app_main()



xTaskNotifyGiveの代わりに、vTaskResumeを使う方法も有ります。
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

void task(void *pvParameters)
{
    TaskHandle_t taskHandle = (TaskHandle_t)pvParameters;
    ESP_LOGI(pcTaskGetName(NULL), "START tick:%"PRIu32, xTaskGetTickCount());
    vTaskDelay(500);
    //xTaskNotifyGive(taskHandle);
    vTaskResume(taskHandle);
    ESP_LOGI(pcTaskGetName(NULL), "END tick:%"PRIu32, xTaskGetTickCount());
    vTaskDelete( NULL );
}

void app_main()
{
    TaskHandle_t taskHandle = xTaskGetCurrentTaskHandle();
    UBaseType_t my_prio = uxTaskPriorityGet(NULL);
    ESP_LOGI(pcTaskGetName(NULL), "my_prio=%d", my_prio);

    xTaskCreate(&task, "Task", 2048, (void *)taskHandle, my_prio+1, NULL);
    //ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
    vTaskSuspend(NULL);
    ESP_LOGI(pcTaskGetName(NULL), "vTaskSuspend");
}

ulTaskNotifyTakeとvTaskSuspendの違いは、ulTaskNotifyTakeはタスク状態ををBlockedにし、
vTaskSuspendはタスク状態をSuspendedにします。
こ ちらにBlcokedとSuspendedの違いについて書かれた資料が有ります。

基本的な違いは、ブロックされた状態のタスクには常にタイムアウトがあることです。
中断状態のタスクは無期限に中断され、タイムアウトはありません。
portMAXDELAY のブロック時間を使用すると、タスクは無期限にブロックされますが、その状態は Suspended として報告されます。
一方、( portMAXDELAY - 1 ) のブロック時間を使用すると、タスクはその状態を Blocked として報告します。

と書かれています。
つまり、
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);

vTaskSuspend(NULL);
は、どちらもタスク状態をSuspendedにします。





xTaskNotifyGive()やxTaskNotify()はfreeRTOSのDirect to task notificationsと呼ばれる機能ですが、タスクの待ち合わせを行う機能です。
複数のタスクで簡単に同期を取ることができます。
#include <stdio.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"

static const char *TAG = "MAIN";

void keyout(void* pvParameters)
{
        TaskHandle_t MyTaskHandle = xTaskGetCurrentTaskHandle();
        ESP_LOGI(pcTaskGetName(NULL), "START MyTaskHandle=%"PRIx32, (uint32_t)MyTaskHandle);
        while(1) {
                uint32_t value = ulTaskNotifyTake( pdTRUE, portMAX_DELAY );
                ESP_LOGI(pcTaskGetName(NULL), "ulTaskNotifyTake value=0x%"PRIx32, value);
        }

        vTaskDelete(NULL);
}

void keyin(void *pvParameters)
{
        TaskHandle_t NotifyTaskHandle = (TaskHandle_t)pvParameters;
        ESP_LOGI(pcTaskGetName(NULL), "Start NotifyTaskHandle=%"PRIx32, (uint32_t)NotifyTaskHandle);

        uint16_t c;
        while (1) {
                c = fgetc(stdin);
                if (c == 0xffff) {
                        vTaskDelay(10);
                        continue;
                }
                ESP_LOGI(pcTaskGetName(NULL), "c=%x", c);
                xTaskNotify( NotifyTaskHandle, c, eSetValueWithOverwrite );
#if 0
                if (c == 0x0a) {
                        ESP_LOGI(pcTaskGetName(NULL), "Push Enter");
                        xTaskNotifyGive( NotifyTaskHandle );
                }
#endif
        }

        vTaskDelete( NULL );
}

void app_main()
{
        // Start task
        TaskHandle_t pxCreatedTask;
        xTaskCreate(&keyout, "HTTP", 1024*4, NULL, 5, &pxCreatedTask);
        ESP_LOGI(TAG, "pxCreatedTask1=%"PRIx32, (uint32_t)pxCreatedTask);

        xTaskCreate(&keyin, "KEYIN", 1024*2, (void *)pxCreatedTask, 5, NULL);
}

このコードを実行するとキーボードから入力した文字が、keyoutタスクに通知されます。
I (307) main_task: Started on CPU0
I (317) main_task: Calling app_main()
I (317) HTTP: START MyTaskHandle=3ffb5bb0
I (317) MAIN: pxCreatedTask1=3ffb5bb0
I (317) KEYIN: Start NotifyTaskHandle=3ffb5bb0
I (327) main_task: Returned from app_main()
I (5727) KEYIN: c=61
I (5727) HTTP: ulTaskNotifyTake value=0x61
I (7127) KEYIN: c=62
I (7127) HTTP: ulTaskNotifyTake value=0x62
I (7527) KEYIN: c=63
I (7527) HTTP: ulTaskNotifyTake value=0x63
I (7827) KEYIN: c=64
I (7827) HTTP: ulTaskNotifyTake value=0x64
I (8027) KEYIN: c=65
I (8027) HTTP: ulTaskNotifyTake value=0x65
I (8327) KEYIN: c=66
I (8327) HTTP: ulTaskNotifyTake value=0x66

続く....