ESP8266 Soft WDT Error - esp8266

How can I avoid the Soft WDT reset Error in this loop.
The Error consistently occurs when reaching the number 3190.
unsigned long TimeFrame = 10000;
void setup() {
Serial.begin(9600);
}
void loop() {
unsigned long StartTime = millis();
while (millis() - StartTime <= TimeFrame){
Serial.println(millis() - StartTime);
}
}
I could count 4 times to 2500 but would this be the correct approach to this error?
Thanks for the explanation. I added a delay(10) to the code and it works.
void setup() {
Serial.begin(9600);
}
void loop() {
unsigned long StartTime = millis();
while (millis() - StartTime <= TimeFrame){
Serial.println(millis() - StartTime);
delay(10);
}
}

WDT is the "watchdog timer". Watchdog timers are used to get control back when something goes wrong in a system - say, an infinite loop or some other unexpected condition. When the underlying system gets control back it resets these timers so that they start counting up from zero again. When they hit their maximum value they trigger a hardware reset on the chip.
Your code measures the duration of the ESP8266 software watchdog timer - in this case 3.19 seconds.
The ESP8266 has both hardware and software watchdog timers. loop() isn't intended to run indefinitely - it's intended to do a small amount of work and then return. When it returns, the ESP8266 SDK gets to reset the watchdog timers.
Both the delay() and yield() functions give the SDK a chance to say "things are okay" and reset the timers. If you need to have long running code in loop() you should call one of those occasionally to give the rest of the system a chance to run.
Keeping loop() brief isn't just about the watchdog timer, either. It also gives the network stack a chance to run and do processing that it needs to do.
You should always design your program so that loop() does a small batch of repetitive processing and then returns. It should never contain an infinite loop or long loop of code.
For instance, suppose you need to do something every 20 seconds. This is the wrong way to do it:
void loop() {
unsigned long start = millis();
while(millis() - start < 20*1000) ;
do_something();
}
That breaks the way the software is designed to work - with loop() executing briefly. It doesn't allow any other software to run while it's waiting. The watchdog timer will fire and reset your CPU.
This is better:
void loop() {
delay(20*1000);
do_something();
}
because delay() lets the underlying system get control back, reset the watchdog timer and also do network-related processing.
In my opinion, this is best:
static unsigned long start_time;
void setup() {
start_time = millis();
}
void loop() {
if(millis() - start_time > 20*1000) {
do_something();
start_time = millis();
}
}
because it does the least possible work inside loop(), only when it's time to do the work.

Related

Wifi does not start after light sleep

I have an application that uses an ESP8266 running ESP_RTOS_SDK version 3.4 and an STM8. It is solar powered, so minimising current consumption is crucial. It works in three modes:
between events: the ESP8266 is in deep sleep and the STM8 is collecting data
during an event: the ESP8266 is in light sleep and the STM8 wakes it up every 10 seconds with some data
after an event: the ESP8266 wakes up fully, connects to wifi, sends all of the collected data.
If I disable light sleep, everything works fine. With light sleep enabled, The light sleep itself works fine but the ESP8266 does not connect to wifi.
ESP-IDF light sleep is documented here. This is my light sleep function:
// -------------------------------------------------------------------
// in light sleep, the processor is stopped.
// we wake up on a WAKE=low
void light_sleep (void) {
gpio_wakeup_enable(GPIO_WAKE_PIN, GPIO_INTR_LOW_LEVEL);
esp_sleep_enable_gpio_wakeup();
esp_sleep_enable_timer_wakeup (10000000L);
esp_light_sleep_start ();
vTaskDelay (1);
esp_sleep_disable_wakeup_source (ESP_SLEEP_WAKEUP_GPIO);
esp_sleep_disable_wakeup_source (ESP_SLEEP_WAKEUP_TIMER);
}
This is the code that I use to start the wifi:
// ------------------------------------------------------------------------------
static void app_wifi_start (void) {
wifi_config_t config = {};
ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, &ip_event_handler, NULL));
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
strncpy((char *)&config.sta.ssid, wifi_config.remote_ssid, sizeof (config.sta.ssid));
strncpy((char *)&config.sta.password, wifi_config.remote_password, sizeof (config.sta.password));
if (strlen((char *)config.sta.password)) {
config.sta.threshold.authmode = WIFI_AUTH_WPA2_PSK;
}
config.sta.pmf_cfg.capable = true;
config.sta.pmf_cfg.required = false;
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &config) );
ESP_ERROR_CHECK(esp_wifi_start());
esp_wifi_connect();
}
The return code from esp_wifi_connect () is ESP_OK.
My question is: how do I make wifi start after a light sleep?
Update: this is how I stop the wifi.
// ----------------------------------------------------------
static void app_wifi_stop (void) {
ESP_ERROR_CHECK(esp_event_handler_unregister(IP_EVENT, IP_EVENT_STA_GOT_IP, &ip_event_handler));
ESP_ERROR_CHECK(esp_event_handler_unregister(WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler));
switch (current_mode) {
case WIFI_MODE_STA:
case WIFI_MODE_APSTA:
esp_wifi_disconnect ();
break;
case WIFI_MODE_AP:
break;
default:
break;
}
ESP_ERROR_CHECK(esp_wifi_stop ());
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_NULL));
current_mode = WIFI_MODE_NULL;
sta_connected = false;
ap_connections = 0;
}
I don't know if you solved the problem. It's been a long time. Well... Before going into light sleep, you should put the wifi in power saving mode. Also, you must call esp_wifi_stop() to stop the wifi task. However, you can only call esp_wifi_stop() when you are sure that no service is using wifi in the background. For example, if you are connected to an MQTT broker, you must make sure your MQTT queue is empty and call esp_mqtt_client_stop(). So after that, you're ready to call esp_wifi_stop() and go into light sleep by calling esp_light_sleep_start();.

How to implement ticker callback at 100 micro seconds for ESP8266?

I have an ESP8266 NodeMCU 12E development board and I'm using the Arduino IDE. I'm trying to use a Ticker.h to sample an analog input consistently at a frequency of 10khz, which is one sample every 100us. I noticed that Ticker sampler; sampler.attach(0.0001,callbackfunc); didn't work because attach() won't take the value 0.0001.
So then I wrote the following code based on some guides that I saw:
#include <ESP8266WiFi.h>
#include <Ticker.h>
bool s = true;
void getSample()
{
s = !s;
}
Ticker tickerObject(getSample, 100, 0, MICROS_MICROS);
const char *ssid = "___"; // Change it
const char *pass = "___"; // Change it
void setup()
{
Serial.begin(115200);
Serial.println(0); //start
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pass);
tickerObject.start();
}
void loop()
{
if(s == true)
{
Serial.println("True");
}
else
{
Serial.println("False");
}
}
However, this did not compile because tickerObject.start() method did not exist. So what I did next was:
Download the latest ticker package as a zip file
Unzip the package from point 1
Made a back up of C:\Users\john\Documents\ArduinoData\packages\esp8266\hardware\esp8266\2.5.0-beta2\libraries\Ticker
Replaced the folder mentioned in point 3 with the Ticker folder in point 2.
Restarted my Arduino IDE
Compiled and ran the code
Opened up the Serial Monitor
However, when I inspect the serial monitor, all it prints is "True". I was expecting the value s to toggle between true and false at a 10khz frequency.
What did I do wrong?
From the documentation of this library:
The library use no interupts of the hardware timers and works with the micros() / millis() function.
This library implements timers in software by polling the micros() and millis() functions. It requires the update() method to be called in loop().
So the start of loop() should be:
void loop()
{
tickerObject.update();
if(s == true)
I'm trying to use a Ticker.h to sample an analog input consistently at a frequency of 10khz
It is worth a go but this is a software based solution that is prone to jitter depending on how often the event loop can be called.

What does DispatchWallTime do on iOS?

I thought the difference between DispatchTime and DispatchWallTime had to do with whether the app was suspended or the device screen was locked or something: DispatchTime should pause, whereas DispatchWallTime should keep going because clocks in the real world keep going.
So I wrote a little test app:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
func applicationDidEnterBackground(_ application: UIApplication) {
print("backgrounding the app, starting timers for 60 seconds", Date())
DispatchQueue.main.asyncAfter(deadline: .now() + 60) {
print("deadline 60 seconds ended", Date())
}
DispatchQueue.main.asyncAfter(wallDeadline: .now() + 60) {
print("wallDeadline 60 seconds ended", Date())
}
}
func applicationWillEnterForeground(_ application: UIApplication) {
print("app coming to front", Date())
}
}
I ran the app on my device. I backgrounded the app, waited for a while, then brought the app to the foreground. Sometimes "waited for a while" included switching off the screen. I got results like this:
backgrounding the app, starting timers for 60 seconds 2018-08-15 17:41:18 +0000
app coming to front 2018-08-15 17:41:58 +0000
wallDeadline 60 seconds ended 2018-08-15 17:42:24 +0000
deadline 60 seconds ended 2018-08-15 17:42:24 +0000
backgrounding the app, starting timers for 60 seconds 2018-08-15 17:42:49 +0000
app coming to front 2018-08-15 17:43:21 +0000
wallDeadline 60 seconds ended 2018-08-15 17:43:55 +0000
deadline 60 seconds ended 2018-08-15 17:43:55 +0000
The delay before the deadline timer fires is not as long as I expected: it's 6 seconds over the 60 second deadline, even though I "slept" the app for considerably longer than that. But even more surprising, both timers fire at the same instant.
So what does wallDeadline do on iOS that's different from what deadline does?
There's nothing wrong with The Dreams Wind's answer, but I wanted to understand these APIs more precisely. Here's my analysis.
DispatchTime
Here's the comment above DispatchTime.init:
/// Creates a `DispatchTime` relative to the system clock that
/// ticks since boot.
///
/// - Parameters:
/// - uptimeNanoseconds: The number of nanoseconds since boot, excluding
/// time the system spent asleep
/// - Returns: A new `DispatchTime`
/// - Discussion: This clock is the same as the value returned by
/// `mach_absolute_time` when converted into nanoseconds.
/// On some platforms, the nanosecond value is rounded up to a
/// multiple of the Mach timebase, using the conversion factors
/// returned by `mach_timebase_info()`. The nanosecond equivalent
/// of the rounded result can be obtained by reading the
/// `uptimeNanoseconds` property.
/// Note that `DispatchTime(uptimeNanoseconds: 0)` is
/// equivalent to `DispatchTime.now()`, that is, its value
/// represents the number of nanoseconds since boot (excluding
/// system sleep time), not zero nanoseconds since boot.
So DispatchTime is based on mach_absolute_time. But what is mach_absolute_time? It's defined in mach_absolute_time.s. There is a separate definition for each CPU type, but the key is that it uses rdtsc on x86-like CPUs and reads the CNTPCT_EL0 register on ARMs. In both cases, it's getting a value that increases monotonically, and only increases while the processor is not in a sufficiently deep sleep state.
Note that the CPU is not necessarily sleeping deeply enough even if the device appears to be asleep.
DispatchWallTime
There's no similarly helpful comment in the DispatchWallTime definition, but we can look at the definition of its now method:
public static func now() -> DispatchWallTime {
return DispatchWallTime(rawValue: CDispatch.dispatch_walltime(nil, 0))
}
and then we can consult the definition of dispatch_walltime:
dispatch_time_t
dispatch_walltime(const struct timespec *inval, int64_t delta)
{
int64_t nsec;
if (inval) {
nsec = (int64_t)_dispatch_timespec_to_nano(*inval);
} else {
nsec = (int64_t)_dispatch_get_nanoseconds();
}
nsec += delta;
if (nsec <= 1) {
// -1 is special == DISPATCH_TIME_FOREVER == forever
return delta >= 0 ? DISPATCH_TIME_FOREVER : (dispatch_time_t)-2ll;
}
return (dispatch_time_t)-nsec;
}
When inval is nil, it calls _dispatch_get_nanoseconds, so let's check that out:
static inline uint64_t
_dispatch_get_nanoseconds(void)
{
dispatch_static_assert(sizeof(NSEC_PER_SEC) == 8);
dispatch_static_assert(sizeof(USEC_PER_SEC) == 8);
#if TARGET_OS_MAC
return clock_gettime_nsec_np(CLOCK_REALTIME);
#elif HAVE_DECL_CLOCK_REALTIME
struct timespec ts;
dispatch_assume_zero(clock_gettime(CLOCK_REALTIME, &ts));
return _dispatch_timespec_to_nano(ts);
#elif defined(_WIN32)
static const uint64_t kNTToUNIXBiasAdjustment = 11644473600 * NSEC_PER_SEC;
// FILETIME is 100-nanosecond intervals since January 1, 1601 (UTC).
FILETIME ft;
ULARGE_INTEGER li;
GetSystemTimePreciseAsFileTime(&ft);
li.LowPart = ft.dwLowDateTime;
li.HighPart = ft.dwHighDateTime;
return li.QuadPart * 100ull - kNTToUNIXBiasAdjustment;
#else
struct timeval tv;
dispatch_assert_zero(gettimeofday(&tv, NULL));
return _dispatch_timeval_to_nano(tv);
#endif
}
It consults the POSIX CLOCK_REALTIME clock. So it is based on the common idea of time and will change if you change your device's time in Settings (or System Preferences on a Mac).
The Mysterious Six Seconds
You said your timer fired
6 seconds over the 60 second deadline
so let's see where that came from.
Both asyncAfter(deadline:execute:) and asyncAfter(wallDeadline:execute:) call the same C API, dispatch_after. The kind of deadline (or “clock”) is encoded into a dispatch_time_t along with the time value. The dispatch_after function calls the internal GCD function _dispatch_after, which I quote in part here:
static inline void
_dispatch_after(dispatch_time_t when, dispatch_queue_t dq,
void *ctxt, void *handler, bool block)
{
dispatch_timer_source_refs_t dt;
dispatch_source_t ds;
uint64_t leeway, delta;
snip
delta = _dispatch_timeout(when);
if (delta == 0) {
if (block) {
return dispatch_async(dq, handler);
}
return dispatch_async_f(dq, ctxt, handler);
}
leeway = delta / 10; // <rdar://problem/13447496>
if (leeway < NSEC_PER_MSEC) leeway = NSEC_PER_MSEC;
if (leeway > 60 * NSEC_PER_SEC) leeway = 60 * NSEC_PER_SEC;
snip
dispatch_clock_t clock;
uint64_t target;
_dispatch_time_to_clock_and_value(when, &clock, &target);
if (clock != DISPATCH_CLOCK_WALL) {
leeway = _dispatch_time_nano2mach(leeway);
}
dt->du_timer_flags |= _dispatch_timer_flags_from_clock(clock);
dt->dt_timer.target = target;
dt->dt_timer.interval = UINT64_MAX;
dt->dt_timer.deadline = target + leeway;
dispatch_activate(ds);
}
The _dispatch_timeout function can be found in time.c. Suffice to say it returns the number of nanoseconds between the current time and the time passed to it. It determines the “current time” based on the clock of the time passed to it.
So _dispatch_after gets the number of nanoseconds to wait before executing your block. Then it computes leeway as one tenth of that duration. When it sets the timer's deadline, it adds leeway to the deadline you passed in.
In your case, delta is about 60 seconds (= 60 * 109 nanoseconds), so leeway is about 6 seconds. Hence your block is executed about 66 seconds after you call asyncAfter.
This question has been here for quite a while without any answers, so I'd like to give it a try and point out subtle difference I noticed in practice.
DispatchTime should pause, whereas DispatchWallTime should keep going
because clocks in the real world keep going
You are correct here, at least they are supposed to act this way. However it tends to be really tricky to check, that DispatchTime works as expected. When iOS app is running under Xcode session, it has unlimited background time and isn't getting suspended. I also couldn't achieve that by running application without Xcode connected, so it's still a big question if DispatchTime is paused under whatever conditions. However the main thing to note is that DispatchTime doesn't depend on the system clock.
DispatchWallTime works pretty much the same (it's not being suspended), apart from that it depends on the system clock. In order to see the difference, you can try out a little longer timer, say, 5 minutes. After that go to the system settings and set time 1 hour forward. If you now open the application you can notice, that WallTimer immediately expires whereas DispatchTime will keep waiting its time.

iOS - How to measure thread wakeups?

I have an app that's crashing due to too many "thread wakeups". For example:
45004 wakeups over the last 220 seconds (205 wakeups per
second average), exceeding limit of 150 wakeups per second over 300 seconds
This is difficult to debug because I know of no direct way to measure thread wakeups. The closest I've found is an Instruments template called System Trace that will show you number of blocked thread events. Presumably, this is closely related since a blocked thread means that that thread will sleep and then wake up when it becomes unblocked.
The weird thing about this is that the number of blocked threads is in the 10,000's range per second when the app is running normally and doesn't crash. My assumption is that a blocked, sleeping thread only counts towards your "wakeups" limit in certain circumstances - e.g. I would expect that a thread that is locked due to a mutex lock counts, whereas the OS simply transitioning to other threads in normal operation doesn't.
It would be amazing to me if Instruments had a Thread Wakeups template. The only documentation I can find is here - https://developer.apple.com/library/content/technotes/tn2151/_index.html:
The exception subtype WAKEUPS indicates that threads in the process are being woken up too many times per second, which forces the CPU to wake up very often and consumes battery life.
Typically, this is caused by thread-to-thread communication (generally using peformSelector:onThread: or dispatch_async) that is unwittingly happening far more often than it should be. Because the sort of communication that triggers this exception is happening so frequently, there will usually be multiple background threads with very similar Backtraces - indicating where the communication is originating.
Here’s some Objective-C code based on Ivan’s answer you can copy + paste somewhere into your project (e.g. your applicationDidFinishLaunching: method) to log the number of wakeups per second (works on Mac and iOS):
#include <mach/task.h>
#include <mach/mach.h>
...
__block NSUInteger lastWakeups = 0;
[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
struct task_power_info info = {0};
mach_msg_type_number_t count = TASK_POWER_INFO_COUNT;
kern_return_t ret = task_info(current_task(), TASK_POWER_INFO, (task_info_t)&info, &count);
if (ret == KERN_SUCCESS) {
NSUInteger wakeups = info.task_interrupt_wakeups + info.task_timer_wakeups_bin_1 + info.task_timer_wakeups_bin_2;
NSLog(#"WAKEUPS: %lu per second", (unsigned long)(wakeups - lastWakeups));
lastWakeups = wakeups;
} else {
NSLog(#"Error: unable to get CPU wakeups (%d)", ret);
}
}];
Please, take a look here https://developer.apple.com/forums/thread/124180 There is a description of a code of getting wakeup count in your app, not only in the instrument. May help you:
#include <mach/task.h>
#include <mach/mach.h>
BOOL GetSystemWakeup(NSInteger *interrupt_wakeup, NSInteger *timer_wakeup) {
struct task_power_info info = {0};
mach_msg_type_number_t count = TASK_POWER_INFO_COUNT;
kern_return_t ret = task_info(current_task(), TASK_POWER_INFO, (task_info_t)&info, &count);
if (ret == KERN_SUCCESS) {
if (interrupt_wakeup) {
*interrupt_wakeup = info.task_interrupt_wakeups;
}
if (timer_wakeup) {
*timer_wakeup = info.task_timer_wakeups_bin_1 + info.task_timer_wakeups_bin_2;
}
return true;
}
else {
if (interrupt_wakeup) {
*interrupt_wakeup = 0;
}
if (timer_wakeup) {
*timer_wakeup = 0;
}
return false;
}
}
Also there you can find some reasons why wakeups occur too much times.
The "Energy Efficiency Guide for Mac Apps" at the end mentions a command-line utility called timerfires that can be used to see what is causing wakeups.
However, the utility seems to be outdated on macOS 12 Monterey, as I was getting errors like the following when I first tried to run it:
probe description fbt::thread_dispatch:entry does not match any probes
I had to copy the utility and edit it to remove all the DTrace methods that are no longer available to get the tool working.
Once that was done, the tool will show each timer invocation, which is very helpful to track down timers in order to reduce wakeups.

pthread: locking mutex with timeout

I try to implement following logic (a kind of pseudo-code) using pthread:
pthread_mutex_t mutex;
threadA()
{
lock(mutex);
// do work
timed_lock(mutex, current_abs_time + 1 minute);
}
threadB()
{
// do work in more than 1 minute
unlock(mutex);
}
I do expect threadA to do the work and wait untill threadB signals but not longer than 1 minute. I have done similar a lot of time in Win32 but stuck with pthreads: a timed_lock part returns imediately (not in 1 minute) with code ETIMEDOUT.
Is there a simple way to implement the logic above?
even following code returns ETIMEDOUT immediately
pthread_mutex_t m;
// Thread A
pthread_mutex_init(&m, 0);
pthread_mutex_lock(&m);
// Thread B
struct timespec now;
clock_gettime(CLOCK_MONOTONIC, &now);
struct timespec time = {now.tv_sec + 5, now.tv_nsec};
pthread_mutex_timedlock(&m, &time); // immediately return ETIMEDOUT
Does anyone know why? I have also tried with gettimeofday function
Thanks
I implemented my logic with conditional variables with respect to other rules (using wrapping mutex, bool flag etc.)
Thank you all for comments.
For the second piece of code: AFAIK pthread_mutex_timedlock only works with CLOCK_REALTIME.
CLOCK_REALTIME are seconds since 01/01/1970
CLOCK_MONOTONIC typically since boot
Under these premises, the timeout set is few seconds into 1970 and therefore in the past.
try something like this :
class CmyClass
{
boost::mutex mtxEventWait;
bool WaitForEvent(long milliseconds);
boost::condition cndSignalEvent;
};
bool CmyClass::WaitForEvent(long milliseconds)
{
boost::mutex::scoped_lock mtxWaitLock(mtxEventWait);
boost::posix_time::time_duration wait_duration = boost::posix_time::milliseconds(milliseconds);
boost::system_time const timeout=boost::get_system_time()+wait_duration;
return cndSignalEvent.timed_wait(mtxEventWait,timeout); // wait until signal Event
}
// so inorder to wait then call the WaitForEvent method
WaitForEvent(1000); // it will timeout after 1 second
// this is how an event could be signaled:
cndSignalEvent.notify_one();

Resources