2025/05/25 電子書籍「Markdown記法大全+Pandoc変換マスター: 初心者から脱却!HTML・PDF・電子書籍を自在に出力する方法」を出版

ESP32のウォッチドッグタイマー(WDT)

ESP32

マイコンで組み込み用のプログラムを開発する際、ウォッチドッグタイマー(WDT)は重要な機能の一つです。

組み込みのプログラムでは(どのプログラムでもでしょうが)、プログラムが暴走したり、無限ループに陥ったりすることは避けなければなりません。しかし、実機においては、静電気や部品の故障など、何らかの要因によってプログラムが正常に動作しなくなる可能性を完全に排除することはできません。

そのため、組み込みシステムの設計では、「万一、異常が発生した場合にどのように安全な状態へ移行させるか」というフェイルセーフ(fail-safe)の考え方が重要になります。

WDTを搭載しておけば、万が一プログラムが暴走したり、無限ループに陥ったりした場合でも、一定時間内に正常な処理が行われないことを検知して、システムをリセットすることができます。これにより、プログラムを再起動させ、異常状態が継続してしまうことを防ぐための仕組みとして機能します。

このように、WDTは予期せぬプログラムの異常に対する「最後の安全策」の一つとして、組み込みシステムの信頼性を高める上で重要な役割を果たします。

esp_task_wdt_init関数を使用したサンプル

ESP-IDFのWDT用の関数である、esp_task_wdt_init 関数ですが、Arduino Core for ESP32の3.0系では、引数の仕様が変更されています。

以下のコードは、Arduino Core for ESP32の2.0系と3.0系の両方に対応したサンプルコードです。3.0系でのesp_task_wdt_init 関数の引数は、esp_task_wdt_config_t構造体(ポインタ)となっています。

詳細は、Watchdogs(V4.4)、 Watchdogs(latest)を参照ください。(バージョンを指定することで、各バージョンの関数仕様を確認することができます。)

関数一覧

WDT用の初期化&スタート陽関数と、WDTをリセットする関数です。

関数引数説明
void InitWDT(int wdtTimeout)リセットがかかるまでの時間(単位:ms)ウォッチドッグタイマーの初期化及びスタートする関数。引数:リセットがかかるまでの時間 単位:ms
void ResetWDT(void)なしウォッチドッグタイマーを、リセットする

ソースコード

Arduino Core for ESP32の2.0系と3.0系の両方に対応したソースコードです。

ソースコード

  • WatchdogTimer.cpp
#include <Arduino.h>
#include <esp_task_wdt.h>

#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
	// Fixes complier error “invalid conversion from ‘int’ to ‘const esp_task_wdt_config_t*'”:
	esp_task_wdt_config_t twdt_config = 
	{
		.timeout_ms		= 1000,
		.idle_core_mask	= 1,    // Bitmask of cores
		.trigger_panic	= true,
	};
#endif

void InitWDT(int wdtTimeout)
{
	esp_task_wdt_deinit();				//wdt is enabled by default, so we need to 'deinit' it first
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
	twdt_config.timeout_ms	= wdtTimeout;
	esp_task_wdt_init(&twdt_config);	//enable panic so ESP32 restarts
#else
	esp_task_wdt_init(wdtTimeout, true);//enable panic so ESP32 restarts
#endif
	esp_task_wdt_add(NULL);				//add current thread to WDT watch
}

void ResetWDT(void)
{
	esp_task_wdt_reset();	// ***** This is the Only WDT Reset line required for your code *****
}
  • WatchdogTimer.h
#ifndef Watchdog_Timer_H
#define Watchdog_Timer_H

void InitWDT(int wdtTimeout);
void ResetWDT(void);

#endif
  • 上記の関数を使用したサンプルコードです。(main.cpp)
#include <Arduino.h>
#include "WatchdogTimer.h"

int i;
int last;

void setup() 
{
	Serial.begin(115200);
	Serial.println("");
	Serial.println("Configuring WDT...");
	Serial.println("");

	// --------------------------
	// Start Watchdog Timer
	// --------------------------
	InitWDT(1000);		// set 1000ms(1sec) WDT

	i = 0;
	last = millis();
}

void loop() 
{
	// Reset the WDT every 0.5s, 20 times only
	if (millis() - last >= 500 && i < 20) 
	{
		Serial.print("MiliSeconds elapsed from last reset = "); Serial.println(millis() - last);
		last = millis();
		i++;
		Serial.print("WDT Reset # "); Serial.println(i);

		// --------------------------
		// Reset Watchdog Timer
		// --------------------------
		ResetWDT();
	}
	if (i == 20) 
	{
		Serial.println("");
		Serial.println("Stopping WDT reset. CPU should reboot in 1 second");
		Serial.println("");
		i=30; // (simply to Negate both 'if...' statements)
	}
}

Timerライブラリを使用したWDT

Timerライブラリを使用してもWDTを実装することができます。

ESP32のTimerライブラリ で公開されている「Watchdog timer example」をベースに、Arduino Core for ESP32の2.0系と3.0系の両方に対応したWDT用の初期化&スタート陽関数と、WDTをリセットする関数です。

関数一覧

上記と同じ関数を作成しました。

関数引数説明
void InitWDT(int wdtTimeout)リセットがかかるまでの時間(単位:ms)ウォッチドッグタイマーの初期化及びスタートする関数。引数:リセットがかかるまでの時間 単位:ms
void ResetWDT(void)なしウォッチドッグタイマーを、リセットする

ソースコード

  • WatchdogTimer.cpp
#include <Arduino.h>
#include "esp_system.h"
#include "rom/ets_sys.h"

hw_timer_t *timer = NULL;

void ARDUINO_ISR_ATTR resetModule() 
{
	// ets_printf("reboot\n");
	esp_restart();
}

// wdtTimeout : ウオッチドックのタイマー時間 単位:ms
void InitWDT(int wdtTimeout)
{
// バージョンが Ver 3.0.0 以上かどうかを判定
// Ver2とVer3で関数仕様が変更となっている
#if ESP_ARDUINO_VERSION >= ESP_ARDUINO_VERSION_VAL(3, 0, 0)
	timer = timerBegin(1000000);					//timer 1Mhz resolution
	timerAttachInterrupt(timer, &resetModule);		//attach callback
	timerAlarm(timer, wdtTimeout * 1000, false, 0);	//set time in us, false:1回のみ実行
#else
    timer = timerBegin(0, 80, true);				// タイマー番号:0 を使用
    timerAttachInterrupt(timer, &resetModule, true);// Attach onTimer function to our timer.
    timerAlarmWrite(timer, wdtTimeout*1000, false); // wdtTimeout*1000us, 自動リロードなし
    timerAlarmEnable(timer);						// Start an alarm
#endif
}

// reset timer (feed watchdog)
void ResetWDT(void)
{
	timerWrite(timer, 0);  //reset timer (feed watchdog)
}
  • WatchdogTimer.h と、maincppは、共通です。

タイトルとURLをコピーしました