SNSへはこちら

ESP32で遊んでみる(5) - タッチセンサ

お次はタッチセンサ機能です。静電容量の変化を検知するセンサで、多分 ADC を使っている API な気がします。ワイヤ1本で出来てしまうのがすごい。あと割り込みに関しては若干注意です。

参考

ソースコード

割り込みを用いたコードを示します。別に割り込まなくても使えます。
動作としては、端子に差したワイヤを手で触れると LED が点きます。一度離してまた触れると消えます。

しきい値の設定が適当なので、設定部分のみ改良した物を本記事下部にて提示しています。こちらも参考にどうぞ。

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/touch_pad.h"
#include "driver/gpio.h"
#include "sdkconfig.h"

#define LED_NUM GPIO_NUM_2

void touch_isr(void *parameters) {
    static int last_tick = 0;
    touch_pad_clear_status();
    if( xTaskGetTickCount() - last_tick < 10 ) {
        last_tick = xTaskGetTickCount();
        return;
    }
    last_tick = xTaskGetTickCount();
    int level = GPIO.out & (1 << LED_NUM);
    gpio_set_level(LED_NUM, !level);
}

void vTouchSensor(void *pvParameters) {
    gpio_set_direction(LED_NUM, GPIO_MODE_OUTPUT);


    touch_pad_init();
    touch_pad_config(TOUCH_PAD_NUM0, 400); // GPIO4
    touch_pad_set_fsm_mode(TOUCH_FSM_MODE_SW);
    touch_pad_isr_register(touch_isr, NULL);
    touch_pad_intr_enable();
    touch_pad_sw_start();
    touch_pad_filter_start(10);

    while(1) {
        vTaskDelay(100 / portTICK_PERIOD_MS);

        uint16_t value;
        touch_pad_read_filtered(TOUCH_PAD_NUM0, &value);
        printf("%d\n", value);
    }
}

void app_main(void) {
    xTaskCreatePinnedToCore(vTouchSensor, "Touch Sensing using a wire", 1024 * 2, NULL, 1, NULL, 1);
}

設定

特に言うこと無いんですよね。ドキュメンテーション読めば。一応言っておきます

まず使用ピンですが、データシートで指定されたピンしか使えないです。ESP-WROOM-32 では、ADC2 のピンに一致しています。詳しくはデータシートの Pin Definitions をご覧あれ。
そして touch_pad_config ではタッチセンサを有効化するピンと、割り込みのしきい値を設定しています。ここでは決め打ちなのであんまり良くはないですね。環境によってここらへんは変わってくると思います。

特筆すべき点として、フィルタ機能が便利です。上のコードの場合は 10msec 静電容量を測定して、値をフィルターしているようです(おそらくローパスフィルターの類)。値のグラつきが減ります。

割り込み

あまり良くないなあと思いますが、割り込みはレベル検知です。すなわち、コード中に記載の 400 という A/D 値を下回っている限り(手で触れると値が下がる)、無限に割り込み関数が呼ばれます。なので単に「割り込み関数が呼ばれたら LED をトグルする」とだけ書くと、LED くんは目にも見えぬ速さで点滅し続けるわけです。

ですので、ちょっと工夫しています。

static int last_tick = 0;
touch_pad_clear_status();
if( xTaskGetTickCount() - last_tick < 10 ) {
    last_tick = xTaskGetTickCount();
    return;
}
last_tick = xTaskGetTickCount();

この部分です。一見何をやっているか分かりづらいですが、最後にこの割り込み関数が呼ばれたのはどのくらい前かを調べています。それが基準値(今回は10)よりも短い、すなわちレベル検知で呼ばれ続けている場合は、特に何もしないという処理(return の部分)です。こうすることで見かけ上エッジ検出にすることが出来ます。

しきい値の改良

上の例ではしきい値を 400 として決め打ちしていました。しかし先述したようにあまりこれは良くありません。最初に値を読んで、それに対する割合で割り込みしきい値を決定するのが得策と考えられます。
起動時に一度値を読んでから割り込み関数の設定を行う、そんな構成に改良しました。その設定部分が以下です。

    touch_pad_init();
    touch_pad_set_fsm_mode(TOUCH_FSM_MODE_SW);
    touch_pad_sw_start();
    touch_pad_filter_start(10);

    uint16_t callib;
    touch_pad_config(TOUCH_PAD_NUM0, 0); // GPIO4
    vTaskDelay(100 / portTICK_PERIOD_MS);
    touch_pad_read_filtered(TOUCH_PAD_NUM0, &callib);
    printf("Filtered: %d\n", callib);

    touch_pad_config(TOUCH_PAD_NUM0, raw * 0.7); // GPIO4
    touch_pad_isr_register(touch_isr, NULL);
    touch_pad_intr_enable();
    touch_pad_sw_start();

真ん中の段落でそれをしています。callib がキャリブレーション値で、ここに一旦値をリードしています。touch_pad_config はドキュメンテーションを読む限り割り込み関数のしきい値のみの設定かと思いきや、そうではないようです。割り込みを使わない場合も、これを呼ぶ必要があるみたい。嘘つき!

一度読んだら、その約 70% をしきい値としています。これでかなり順調に動いています。