default 0
depends on !ULP_COPROC_ENABLED
+menu "Watchdogs & brown-out detection"
+
+config INT_WDT
+ bool "Interrupt watchdog"
+ default y
+ help
+ This watchdog timer can detect if the FreeRTOS tick interrupt has not been called for a certain time,
+ either because a task turned off interrupts and did not turn them on for a long time, or because an
+ interrupt handler did not return. It will try to invoke the panic handler first and failing that
+ reset the SoC.
+
+config INT_WDT_TIMEOUT_MS_MIN
+ default (2000/CONFIG_FREERTOS_HZ)
+
+config INT_WDT_TIMEOUT_MS
+ int "Interrupt watchdog timeout (ms)"
+ depends on INT_WDT
+ default 100
+ range INT_WDT_TIMEOUT_MS_MIN 10000
+ help
+ The timeout of the watchdog, in miliseconds.
+
+config TASK_WDT
+ bool "Task watchdog"
+ default y
+ help
+ This watchdog timer can be used to make sure individual tasks are still running.
+
+config TASK_WDT_PANIC
+ bool "Invoke panic handler when Task Watchdog is triggered"
+ depends on TASK_WDT
+ default n
+ help
+ Normally, the Task Watchdog will only print out a warning if it detects it has not
+ been fed. If this is enabled, it will invoke the panic handler instead, which
+ can then halt or reboot the chip.
+
+config TASK_WDT_TIMEOUT_S
+ int "Task watchdog timeout (seconds)"
+ depends on TASK_WDT
+ range 1 60
+ default 5
+ help
+ Timeout for the task WDT, in seconds.
+
+config TASK_WDT_CHECK_IDLE_TASK
+ bool "Task watchdog watches idle tasks"
+ depends on TASK_WDT
+ default y
+ help
+ With this turned on, the task WDT can detect if the idle task is not called within the task
+ watchdog timeout period. The idle task not being called usually is a symptom of another
+ task hoarding the CPU. It is also a bad thing because FreeRTOS household tasks depend on the
+ idle task getting some runtime every now and then.
+
+config BROWNOUT_DET
+ bool "Hardware brownout detect & reset"
+ default y
+ help
+ The ESP32 has a built-in brownout detector which can detect if the voltage is lower than
+ a specific value. If this happens, it will reset the chip in order to prevent unintended
+ behaviour.
+
+choice BROWNOUT_DET_LVL_SEL
+ prompt "Brownout voltage level"
+ depends on BROWNOUT_DET
+ default BROWNOUT_DET_LVL_SEL_25
+ help
+ The brownout detector will reset the chip when the supply voltage is below this level.
+
+config BROWNOUT_DET_LVL_SEL_0
+ bool "2.1V"
+config BROWNOUT_DET_LVL_SEL_1
+ bool "2.2V"
+config BROWNOUT_DET_LVL_SEL_2
+ bool "2.3V"
+config BROWNOUT_DET_LVL_SEL_3
+ bool "2.4V"
+config BROWNOUT_DET_LVL_SEL_4
+ bool "2.5V"
+config BROWNOUT_DET_LVL_SEL_5
+ bool "2.6V"
+config BROWNOUT_DET_LVL_SEL_6
+ bool "2.7V"
+config BROWNOUT_DET_LVL_SEL_7
+ bool "2.8V"
+endchoice
+
+config BROWNOUT_DET_LVL
+ int
+ default 0 if BROWNOUT_DET_LVL_SEL_0
+ default 1 if BROWNOUT_DET_LVL_SEL_1
+ default 2 if BROWNOUT_DET_LVL_SEL_2
+ default 3 if BROWNOUT_DET_LVL_SEL_3
+ default 4 if BROWNOUT_DET_LVL_SEL_4
+ default 5 if BROWNOUT_DET_LVL_SEL_5
+ default 6 if BROWNOUT_DET_LVL_SEL_6
+ default 7 if BROWNOUT_DET_LVL_SEL_7
+
+
+config BROWNOUT_DET_RESETDELAY
+ int "Brownout reset delay (in uS)"
+ depends on BROWNOUT_DET
+ range 0 6820
+ default 1000
+ help
+ The brownout detector can reset the chip after a certain delay, in order to make sure e.g. a voltage dip has entirely passed
+ before trying to restart the chip. You can set the delay here.
+
+endmenu
+
+
+
endmenu
--- /dev/null
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include "sdkconfig.h"
+#include "soc/soc.h"
+#include "soc/rtc_cntl_reg.h"
+
+
+void esp_brownout_init() {
+// WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG,
+// RTC_CNTL_BROWN_OUT_ENA | (CONFIG_BROWNOUT_DET_LVL << RTC_CNTL_DBROWN_OUT_THRES_S) |
+// RTC_CNTL_BROWN_OUT_RST_ENA | (((CONFIG_BROWNOUT_DET_RESETDELAY*150)/1000) << RTC_CNTL_BROWN_OUT_RST_WAIT_S) |
+// RTC_CNTL_BROWN_OUT_PD_RF_ENA|RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA);
+
+
+ WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG,
+ RTC_CNTL_BROWN_OUT_ENA | (4 << RTC_CNTL_DBROWN_OUT_THRES_S) |
+ RTC_CNTL_BROWN_OUT_RST_ENA | (0x3FF << RTC_CNTL_BROWN_OUT_RST_WAIT_S) |
+ RTC_CNTL_BROWN_OUT_PD_RF_ENA | RTC_CNTL_BROWN_OUT_CLOSE_FLASH_ENA);
+
+}
\ No newline at end of file
#include "esp_ipc.h"
#include "esp_log.h"
+#include "esp_brownout.h"
+
void start_cpu0(void) __attribute__((weak, alias("start_cpu0_default")));
void start_cpu0_default(void) IRAM_ATTR;
#if !CONFIG_FREERTOS_UNICORE
do_global_ctors();
esp_ipc_init();
spi_flash_init();
+#if CONFIG_BROWNOUT_DET
+ esp_brownout_init();
+#endif
+#if CONFIG_INT_WDT
+ int_wdt_init()
+#endif
+#if CONFIG_TASK_WDT
+ task_wdt_init()
+#endif
+
xTaskCreatePinnedToCore(&main_task, "main",
ESP_TASK_MAIN_STACK, NULL,
ESP_TASK_MAIN_PRIO, NULL, 0);
--- /dev/null
+#ifndef BROWNOUT_H
+#define BROWNOUT_H
+
+void esp_brownout_init();
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef INT_WDT_H
+#define INT_WDT_H
+
+void int_wdt_init();
+
+#endif
\ No newline at end of file
--- /dev/null
+#ifndef TASK_WDT_H
+#define TASK_WDT_H
+
+void task_wdt_feed();
+void task_wdt_delete();
+void task_wdt_init();
+
+#endif
\ No newline at end of file
--- /dev/null
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/*
+This routine enables a watchdog to catch instances of processes disabling
+interrupts for too long, or code within interrupt handlers taking too long.
+It does this by setting up a watchdog which gets fed from the FreeRTOS
+task switch interrupt. When this watchdog times out, initially it will call
+a high-level interrupt routine that will panic FreeRTOS in order to allow
+for forensic examination of the state of the CPU. When this interrupt
+handler is not called and the watchdog times out a second time, it will
+reset the SoC.
+
+This uses the TIMERG1 WDT.
+*/
+
+#include "sdkconfig.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include <esp_types.h>
+#include "esp_err.h"
+#include "esp_intr.h"
+#include "soc/timer_group_struct.h"
+
+#include "esp_int_wdt.h"
+
+#if CONFIG_INT_WDT
+
+
+#define WDT_INT_NUM 24
+
+
+#define WDT_WRITE_KEY 0x50D83AA1
+
+static void esp_int_wdt_isr(void *arg) {
+ abort();
+}
+
+
+void int_wdt_init() {
+ TIMERG1.wdt_wprotect=WDT_WRITE_KEY;
+ TIMERG1.wdt_config0.sys_reset_length=7; //3.2uS
+ TIMERG1.wdt_config0.cpu_reset_length=7; //3.2uS
+ TIMERG1.wdt_config0.level_int_en=1;
+ TIMERG1.wdt_config0.stg0=1; //1st stage timeout: interrupt
+ TIMERG1.wdt_config0.stg1=3; //2nd stage timeout: reset system
+ TIMERG1.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
+ TIMERG1.wdt_config2=CONFIG_INT_WDT_TIMEOUT_MS*2; //Set timeout before interrupt
+ TIMERG1.wdt_config3=CONFIG_INT_WDT_TIMEOUT_MS*4; //Set timeout before reset
+ TIMERG1.wdt_config0.en=1;
+ TIMERG1.wdt_feed=1;
+ TIMERG1.wdt_wprotect=0;
+ ESP_INTR_DISABLE(WDT_INT_NUM);
+ intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, WDT_INT_NUM);
+ xt_set_interrupt_handler(WDT_INT_NUM, int_wdt_isr, NULL);
+ ESP_INTR_ENABLE(WDT_INT_NUM);
+}
+
+
+
+void vApplicationTickHook(void) {
+ TIMERG1.wdt_wprotect=WDT_WRITE_KEY;
+ TIMERG1.wdt_feed=1;
+ TIMERG1.wdt_wprotect=0;
+}
+
+#endif
\ No newline at end of file
--- /dev/null
+// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+
+/*
+This routine enables a more general-purpose task watchdog: tasks can individually
+feed the watchdog and the watchdog will bark if one or more tasks haven't fed the
+watchdog within the specified time. Optionally, the idle tasks can also configured
+to feed the watchdog in a similar fashion, to detect CPU starvation.
+
+This uses the TIMERG0 WDT.
+*/
+
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include "sdkconfig.h"
+#include "freertos/FreeRTOS.h"
+#include "freertos/task.h"
+#include <esp_types.h>
+#include "esp_err.h"
+#include "esp_intr.h"
+#include "soc/timer_group_struct.h"
+#include "esp_log.h"
+
+#include "esp_task_wdt.h"
+
+#if CONFIG_TASK_WDT
+
+static const char* TAG = "task_wdt";
+
+
+typedef struct wdt_task_t wdt_task_t;
+struct wdt_task_t {
+ TaskHandle_t task_handle;
+ bool fed_watchdog;
+ wdt_task_t *next;
+};
+
+
+static wdt_task_t *wdt_task_list=NULL;
+
+#define WDT_INT_NUM 24
+
+
+#define WDT_WRITE_KEY 0x50D83AA1
+
+static void task_wdt_isr(void *arg) {
+ abort();
+}
+
+
+void task_wdt_feed() {
+ wdt_task_t *wdttask=wdt_task_list;
+ bool found_task=false, do_feed_wdt=true;
+ TaskHandle_t handle=xTaskGetCurrentTaskHandle();
+ //Walk the linked list of wdt tasks to find this one, as well as see if we need to feed
+ //the real watchdog timer.
+ while (wdttask!=NULL) {
+ //See if we are at the current task.
+ if (wdttask->task_handle == handle) {
+ wdttask->fed_watchdog=true;
+ found_task=true;
+ }
+ //If even one task in the list doesn't have the do_feed_wdt var set, we do not feed the watchdog.
+ if (!wdttask->fed_watchdog) do_feed_wdt=false;
+ //Next entry.
+ wdttask=wdttask->next;
+ }
+
+ if (!found_task) {
+ //This is the first time the task calls the task_wdt_feed function. Create a new entry for it in
+ //the linked list.
+ wdt_task_t *newtask=malloc(sizeof(wdt_task_t));
+ memset(newtask, 0, sizeof(wdt_task_t));
+ newtask->task_handle=handle;
+ newtask->fed_watchdog=true;
+ if (wdt_task_list == NULL) {
+ wdt_task_list=newtask;
+ } else {
+ wdttask=wdt_task_list;
+ while (!(wdttask->next == NULL)) wdttask=wdttask->next;
+ wdttask->next=wdttask;
+ }
+ }
+ if (do_feed_wdt) {
+ //All tasks have checked in; time to feed the hw watchdog.
+ TIMERG0.wdt_wprotect=WDT_WRITE_KEY;
+ TIMERG0.wdt_feed=1;
+ TIMERG0.wdt_wprotect=0;
+ }
+}
+
+void task_wdt_delete() {
+ TaskHandle_t handle=xTaskGetCurrentTaskHandle();
+ wdt_task_t *wdttask=wdt_task_list;
+ //Wdt task list can't be empty
+ if (!wdt_task_list) {
+ ESP_LOGE(TAG, "task_wdt_delete: No tasks in list?");
+ return;
+ }
+ if (handle==wdt_task_list) {
+ //Current task is first on list.
+ wdt_task_list=wdt_task_list->next;
+ free(wdttask);
+ } else {
+ //Find current task in list
+ while (wdttask->next!=NULL && wdttask->next->task_handle!=handle) wdttask=wdttask->next;
+ if (!wdttask->next) {
+ ESP_LOGE(TAG, "task_wdt_delete: Task never called task_wdt_feed!");
+ return;
+ }
+ wdt_task_t *freeme=wdttask->next;
+ wdttask->next=wdttask->next->next;
+ free(freeme);
+ }
+}
+
+void task_wdt_init() {
+ TIMERG0.wdt_wprotect=WDT_WRITE_KEY;
+ TIMERG0.wdt_config0.sys_reset_length=7; //3.2uS
+ TIMERG0.wdt_config0.cpu_reset_length=7; //3.2uS
+ TIMERG0.wdt_config0.level_int_en=1;
+ TIMERG0.wdt_config0.stg0=1; //1st stage timeout: interrupt
+ TIMERG0.wdt_config0.stg1=3; //2nd stage timeout: reset system
+ TIMERG0.wdt_config1.clk_prescale=80*500; //Prescaler: wdt counts in ticks of 0.5mS
+ TIMERG0.wdt_config2=CONFIG_TASK_WDT_TIMEOUT_S*2000; //Set timeout before interrupt
+ TIMERG0.wdt_config3=CONFIG_TASK_WDT_TIMEOUT_S*4000; //Set timeout before reset
+ TIMERG0.wdt_config0.en=1;
+ TIMERG0.wdt_feed=1;
+ TIMERG0.wdt_wprotect=0;
+ ESP_INTR_DISABLE(ETS_T0_WDT_INUM);
+ intr_matrix_set(xPortGetCoreID(), ETS_TG1_WDT_LEVEL_INTR_SOURCE, ETS_T0_WDT_INUM);
+ xt_set_interrupt_handler(ETS_T0_WDT_INUM, task_wdt_isr, NULL);
+ ESP_INTR_ENABLE(ETS_T0_WDT_INUM);
+}
+
+
+#if CONFIG_TASK_WDT_CHECK_IDLE_TASK
+void vApplicationIdleHook(void) {
+ task_wdt_feed();
+}
+#endif
+
+#endif
\ No newline at end of file
*----------------------------------------------------------*/
#define configUSE_PREEMPTION 1
-#define configUSE_IDLE_HOOK 0
+#define configUSE_IDLE_HOOK ( CONFIG_TASK_WDT_CHECK_IDLE_TASK )
-#define configUSE_TICK_HOOK 0
+#define configUSE_TICK_HOOK ( CONFIG_INT_WDT )
#define configTICK_RATE_HZ ( CONFIG_FREERTOS_HZ )