Closed assegid closed 6 years ago
Sounds like this is a project you should author. You know what you want, your interest and needs would be a driving force.
Chuck.
@assegid: sometimes it is hard to accept, that a missing feature or bug that one suffers from, is not a high priority to others. We chose to use a bleeding edge platform, that his heavily worked on, and there are many, many unresolved issues - so we have to deal with the consequences. and only thing that will improve the situation is contributing.
having said that, maybe the following code helps you:
//#include <esp_task_wdt.h>
#include "log.h"
#define WATCHDOG_TIMEOUT 10
//#define WATCHDOG_LED_GPIO 17
static uint32_t watchdog_counter = 0;
static bool watchdog_active = false;
// a full lock of the ESP will be handled by the HW watchdog
// a soft lock of the SDK will trigger the SW watchdog
// a loop lock will be covered by check_loop_iteration()
static hw_timer_t *timer = NULL;
static void IRAM_ATTR check_loop_iteration(void) {
if (watchdog_counter++ >= 2 * WATCHDOG_TIMEOUT) {
ESP.restart();
}
#ifdef WATCHDOG_LED_GPIO
static bool toggle = false;
if (toggle || (watchdog_counter > WATCHDOG_TIMEOUT + 2)) {
digitalWrite(WATCHDOG_LED_GPIO, !digitalRead(WATCHDOG_LED_GPIO));
}
toggle = !toggle;
#endif // WATCHDOG_LED_GPIO
}
static bool enable_watchdog_timer(void) {
if (watchdog_active) return (false);
#ifdef WATCHDOG_LED_GPIO
pinMode(WATCHDOG_LED_GPIO, OUTPUT);
digitalWrite(WATCHDOG_LED_GPIO, LOW); // LOW => ON
#endif
// use the 1st of 4 HW timers
// 1 tick take 1/(80MHZ/80) = 1us so we set divider 80
// count up
timer = timerBegin(0, 80, true);
// attach callback function
timerAttachInterrupt(timer, &check_loop_iteration, true);
// set alarm for every 500ms (1 tick is 1us)
timerAlarmWrite(timer, 500000, true); // true => auto reload
// start alarm
timerAlarmEnable(timer);
watchdog_active = true;
return (true);
}
static bool disable_watchdog_timer(void) {
if (!watchdog_active) return (false);
timerAlarmDisable(timer);
timerEnd(timer);
timer = NULL;
delay(10);
#ifdef WATCHDOG_LED_GPIO
digitalWrite(WATCHDOG_LED_GPIO, HIGH); // HIGH => OFF
pinMode(WATCHDOG_LED_GPIO, INPUT);
#endif
watchdog_active = false;
return (true);
}
bool watchdog_init(void) {
bool ret = enable_watchdog_timer();
if (ret) {
log_print(F("WD: enabling main loop watchdog"));
}
return (ret);
}
bool watchdog_fini(void) {
bool ret = disable_watchdog_timer();
if (ret) {
log_print(F("WD: disabling main loop watchdog"));
}
// esp_task_wdt_delete();
return (ret);
}
void watchdog_poll(void) {
if (watchdog_active) {
watchdog_counter = 0; // reset watchdog
// esp_task_wdt_feed();
}
}
call watchdog_init() in setup() and watchdog_poll() in loop().
Thank you Clemens for supplying this code. I will see if I can simplify it and adapt it for use with the esp32 in the Arduino IDE. I have no doubt the development team is busy solving a myriad of issues. My point is, for people developing with the Arduino IDE, there are several features of the hardware that are already functional that can make the hardware valuable if it could be made to recover from unknown freezes.
I ran the supplied code and there are no 'module.h', 'log.h', and 'watchdog.h' in my arduino esp32 package.
Assegid
From: Clemens Kirchgatterer notifications@github.com Sent: Friday, November 17, 2017 2:21 AM To: espressif/arduino-esp32 Cc: assegid; Mention Subject: Re: [espressif/arduino-esp32] wdt implementation (#841)
@assegidhttps://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_assegid&d=DwMFaQ&c=l45AxH-kUV29SRQusp9vYR0n1GycN4_2jInuKy6zbqQ&r=tUUNhxjXInPYrZpicyVgl4Wh6jDb9190e7dboFMmfI0&m=NxMqYmHqJtMuctpyASsFOm81nzB-tmrbJzK27G2B2k8&s=AqUHhfDixNu8DCroN8YmbHzkM4phGZRQf0fWX9NJ618&e=: sometimes it is hard to accept, that a missing feature or bug that one suffers from, is not a high priority to others. We chose to use a bleeding edge platform, that his heavily worked on, and there are many, many unresolved issues - so we have to deal with the consequences. and only thing that will improve the situation is contributing.
having said that, maybe the following code helps you:
static uint32_t watchdog_counter = 0; static bool watchdog_active = false;
// a full lock of the ESP will be handled by the HW watchdog // a soft lock of the SDK will trigger the SW watchdog // a loop lock will be covered by check_loop_iteration()
static hw_timer_t *timer = NULL;
static void IRAM_ATTR check_loop_iteration(void) { if (watchdog_counter++ >= 2 * WATCHDOG_TIMEOUT) { ESP.restart(); }
static bool toggle = false;
if (toggle || (watchdog_counter > WATCHDOG_TIMEOUT + 2)) { digitalWrite(WATCHDOG_LED_GPIO, !digitalRead(WATCHDOG_LED_GPIO)); }
toggle = !toggle;
}
static bool enable_watchdog_timer(void) { if (watchdog_active) return (false);
pinMode(WATCHDOG_LED_GPIO, OUTPUT); digitalWrite(WATCHDOG_LED_GPIO, LOW); // LOW => ON
// use the 1st of 4 HW timers // 1 tick take 1/(80MHZ/80) = 1us so we set divider 80 // count up timer = timerBegin(0, 80, true);
// attach callback function timerAttachInterrupt(timer, &check_loop_iteration, true);
// set alarm for every 500ms (1 tick is 1us) timerAlarmWrite(timer, 500000, true); // true => auto reload
// start alarm timerAlarmEnable(timer);
watchdog_active = true;
return (true); }
static bool disable_watchdog_timer(void) { if (!watchdog_active) return (false);
timerAlarmDisable(timer); timerEnd(timer); timer = NULL;
delay(10);
digitalWrite(WATCHDOG_LED_GPIO, HIGH); // HIGH => OFF pinMode(WATCHDOG_LED_GPIO, INPUT);
watchdog_active = false;
return (true); }
static Ticker timer;
static void ICACHE_RAM_ATTR check_loop_iteration(void) { if (watchdog_counter >= WATCHDOG_TIMEOUT) { log_print(F("MAIN: loop() blocked for %i seconds, rebooting ..."), watchdog_counter ); ESP.restart(); // normal reboot //ESP.reset(); // hard reset
return;
}
if (watchdog_counter >= WATCHDOG_TIMEOUT / 2) { log_print(F("MAIN: WARNING! loop() blocked for %i seconds"), watchdog_counter ); }
watchdog_counter++; }
static bool enable_watchdog_timer(void) { if (watchdog_active) return (false);
timer.attach_ms(1000, check_loop_iteration);
watchdog_active = true;
return (true); }
static bool disable_watchdog_timer(void) { if (!watchdog_active) return (false);
timer.detach();
watchdog_active = false;
return (true); }
int watchdog_state(void) { if (watchdog_active) return (MODULE_STATE_ACTIVE);
return (MODULE_STATE_INACTIVE); } bool watchdog_init(void) { bool ret = enable_watchdog_timer();
if (ret) { log_print(F("WD: enabling main loop watchdog")); }
return (ret); }
bool watchdog_fini(void) { bool ret = disable_watchdog_timer();
if (ret) { log_print(F("WD: disabling main loop watchdog")); }
// esp_task_wdt_delete();
return (ret); }
void watchdog_poll(void) { if (watchdog_active) { watchdog_counter = 0; // reset watchdog
// esp_task_wdt_feed();
} }
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_espressif_arduino-2Desp32_issues_841-23issuecomment-2D345189175&d=DwMFaQ&c=l45AxH-kUV29SRQusp9vYR0n1GycN4_2jInuKy6zbqQ&r=tUUNhxjXInPYrZpicyVgl4Wh6jDb9190e7dboFMmfI0&m=NxMqYmHqJtMuctpyASsFOm81nzB-tmrbJzK27G2B2k8&s=a6aaF9JTwBWMvYWXkD72F-vD91yObb602L75-MnLLSg&e=, or mute the threadhttps://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_notifications_unsubscribe-2Dauth_AFYTaSXgU1VEqu-5FzopfbxcvC-2DAfg3e0jks5s3VAtgaJpZM4QhE-2Dr&d=DwMFaQ&c=l45AxH-kUV29SRQusp9vYR0n1GycN4_2jInuKy6zbqQ&r=tUUNhxjXInPYrZpicyVgl4Wh6jDb9190e7dboFMmfI0&m=NxMqYmHqJtMuctpyASsFOm81nzB-tmrbJzK27G2B2k8&s=U3YVOaLe0FD-TeVY2vK6xMXP8TRCf8d_SIlm7BVEyC0&e=.
I will probably find a solution for my needs. But I am a hardware engineer and my solution is unlikely to be a clean general purpose code that will be useful to others.
From: chuck todd notifications@github.com Sent: Thursday, November 16, 2017 7:09 PM To: espressif/arduino-esp32 Cc: assegid; Author Subject: Re: [espressif/arduino-esp32] wdt implementation (#841)
Sounds like this is a project you should author. You know what you want, your interest and needs would be a driving force.
Chuck.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHubhttps://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_espressif_arduino-2Desp32_issues_841-23issuecomment-2D345124153&d=DwMCaQ&c=l45AxH-kUV29SRQusp9vYR0n1GycN4_2jInuKy6zbqQ&r=tUUNhxjXInPYrZpicyVgl4Wh6jDb9190e7dboFMmfI0&m=p6v_Us8n8HI3HisoZm19m729QjS77EKIeZZqHhlSD6Q&s=q43CNjNKUhn0TNtdvlIZtPHme88yVCC-vC3sHMB33zo&e=, or mute the threadhttps://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_notifications_unsubscribe-2Dauth_AFYTaWG76kn44yTmyNaUvrAyM6VP6rpIks5s3OrmgaJpZM4QhE-2Dr&d=DwMCaQ&c=l45AxH-kUV29SRQusp9vYR0n1GycN4_2jInuKy6zbqQ&r=tUUNhxjXInPYrZpicyVgl4Wh6jDb9190e7dboFMmfI0&m=p6v_Us8n8HI3HisoZm19m729QjS77EKIeZZqHhlSD6Q&s=HMmqCh5Pk3r5elBtU-wjD7bvM85NsjyrF8r_iaQ8oLc&e=.
i simplified the example above, you just have to replace log_print() with whatever logging you are using, or just remove it, and of course remove the inclusion of "log.h".
@everslick Thanks so much for your help so far. We are getting close. So the code now indeed halts execution when the set time (10 sec) elapses without calling watchdog_poll(). However, it halts the CPU and gets stuck. How do I get it to run from the beginning as if the boot button is pressed? Code printed when watchdog resets is below. I am intentionally keeping the program in a loop to timeout the watchdog by pressing a button. It prints 'button pressed' while button pressed.
button pressed button pressed abort() was called at PC 0x40081dfd Guru Meditation Error: Core 1 panic'ed (abort)
Backtrace: 0x40081dc2:0x3ffc0750 0x40084965:0x3ffc0770 0x40081f18:0x3ffc07a0 0x400f01e5:0x3ffc07c0 0x400f3420:0x3ffc0ad0 0x40081d44:0x3ffc0b00 0x400d9a94:0x3ffc0b50 0x4008171e:0x3ffc0b80 0x400d0ab2:0x3ffc0ba0 0x40080a7e:0x3ffc0bc0 0x40080e40:0x3ffc0be0 0x40081abd:0x3ffc0c00
CPU halted.
Since a few months the default behavior of the IDF SDK is to reboot on all panics, but I'm not sure about calls to abort(). In any case you have to use the backtrace decoder to decode the stack trace that you see above, to get any useful information out of it. Then you might be able to see what happened right before the crash.
Yeah, why did the CPU halt in that log? Are you using Arduino as component and you have disabled auto reboot on panic?
No, I simply use the Arduino Core, as is, but with the makeEspArduino Makefile. I did not customize the IDF SDK in any way. I use the standard config.
When loop() is blocked and does not call watchdog_poll() for more then 10 seconds, the device reboots. sometimes it does get stuck in Esp.restart(), but then the real HW watchdog on CPU 0 kicks in and reboots the device.
On Sun, Nov 19, 2017 at 11:03 AM, Me No Dev notifications@github.com wrote:
Yeah, why did the CPU halt in that log? Are you using Arduino as component and you have disabled auto reboot on panic?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/espressif/arduino-esp32/issues/841#issuecomment-345505159, or mute the thread https://github.com/notifications/unsubscribe-auth/AB0jLwQ4HuiHtoUJBlLweWtK7045PXMnks5s3_0EgaJpZM4QhE-r .
@everslick question was for @assegid :) his log shows the halt
@me-no-dev: HAHA! sorry, didn't realize it was you, i hastily answered to the email notification by reply. LOL.
@me-no-dev @everslick so I am programming using the Arduino IDE not the IDF. I am trying to get the watchdog function work in the Arduino IDE. What I am finding after trying the code that everslick supplied is the ESP.restart() function works when it is placed in the main loop. i.e.. it resets the board and continues to excute normally. However when it is placed in the interrupt service routine it halts the cpu. I looked at exception decoder output which is below but I am not sure what is going on.
Decoding 9 results 0x400d97cc: esp_vApplicationIdleHook at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/esp32/./freertos_hooks.c line 52 0x40084941: prvIdleTask at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/freertos/./tasks.c line 4421 0x400840a6: xPortGetCoreID at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/freertos/./tasks.c line 4421 : (inlined by) vTaskSwitchContext at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/freertos/./tasks.c line 2732 0x400854e3: _frxt_dispatch at ?? line ? 0x40085574: vPortYieldFromInt at ?? line ? 0x400d07ec: delay at /Users/assegidkidane/Documents/Arduino/hardware/espressif/esp32/cores/esp32/esp32-hal-misc.c line 54 0x400d06ea: resetModule() at /Users/assegidkidane/Documents/Arduino/watchdogEspTest4/watchdogEspTest4.ino line 144 0x40080e1c: __timerISR at /Users/assegidkidane/Documents/Arduino/hardware/espressif/esp32/cores/esp32/esp32-hal-timer.c line 179 0x40081a99: _xt_lowint1 at xtensa_vectors.o line ?
@everslick when you say 'the real HW watchdog kicks in' , after how long? In my tests, it just gets stuck at the 'cpu 1 halted' state. But may be I did not wait long enough so I will try again and wait about 15 minutes.
@everslick I waited over a half hour after the CPU halt and it did not reboot. The ESP exception decoder output follows. Is any information available on how to directly manipulate timer and watchdog config registers?
Decoding 13 results 0x40081e31: lock_acquire_generic at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/newlib/./locks.c line 133 0x40081df6: lock_init_generic at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/newlib/./locks.c line 83 0x40084999: prvIdleTask at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/freertos/./tasks.c line 4421 0x40081f4c: _lock_acquire_recursive at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/newlib/./locks.c line 161 0x400f01f1: _vfprintf_r at /Users/ivan/e/newlib_xtensa-2.2.0-bin/newlib_xtensa-2.2.0/xtensa-esp32-elf/newlib/libc/stdio/../../../.././newlib/libc/stdio/vfprintf.c line 862 0x400f342c: vprintf at /Users/ivan/e/newlib_xtensa-2.2.0-bin/newlib_xtensa-2.2.0/xtensa-esp32-elf/newlib/libc/stdio/../../../.././newlib/libc/stdio/vprintf.c line 40 0x40081d78: esp_log_write at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/log/./log.c line 105 0x400d9aa0: esp_wifi_stop at ?? line ? 0x40081752: esp_restart at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/esp32/./system_api.c line 40 0x400d0abe: EspClass::restart() at ?? line ? 0x40080a81: check_loop_iteration() at /Users/assegidkidane/Documents/Arduino/watchdogEspTest3/watchdogEspTest3.ino line 109 0x40080e74: __timerISR at /Users/assegidkidane/Documents/Arduino/hardware/espressif/esp32/cores/esp32/esp32-hal-timer.c line 179 0x40081af1: _xt_lowint1 at xtensa_vectors.o line ?
No, the hardware watchdog kicks in approximately 5 seconds after the timer IRQ gets stuck.
On Mon, Nov 20, 2017 at 6:36 AM, assegid notifications@github.com wrote:
@everslick https://github.com/everslick I waited over a half hour after the CPU halt and it did not reboot. The ESP exception decoder output follows. Is any information available on how to directly manipulate timer and watchdog config registers?
Decoding 13 results 0x40081e31: lock_acquire_generic at /Users/ficeto/Desktop/ESP32/ ESP32/esp-idf-public/components/newlib/./locks.c line 133 0x40081df6: lock_init_generic at /Users/ficeto/Desktop/ESP32/ ESP32/esp-idf-public/components/newlib/./locks.c line 83 0x40084999: prvIdleTask at /Users/ficeto/Desktop/ESP32/ ESP32/esp-idf-public/components/freertos/./tasks.c line 4421 0x40081f4c: _lock_acquire_recursive at /Users/ficeto/Desktop/ESP32/ ESP32/esp-idf-public/components/newlib/./locks.c line 161 0x400f01f1: _vfprintf_r at /Users/ivan/e/newlib_xtensa-2. 2.0-bin/newlib_xtensa-2.2.0/xtensa-esp32-elf/newlib/libc/ stdio/../../../.././newlib/libc/stdio/vfprintf.c line 862 0x400f342c: vprintf at /Users/ivan/e/newlib_xtensa-2. 2.0-bin/newlib_xtensa-2.2.0/xtensa-esp32-elf/newlib/libc/ stdio/../../../.././newlib/libc/stdio/vprintf.c line 40 0x40081d78: esp_log_write at /Users/ficeto/Desktop/ESP32/ ESP32/esp-idf-public/components/log/./log.c line 105 0x400d9aa0: esp_wifi_stop at ?? line ? 0x40081752: esp_restart at /Users/ficeto/Desktop/ESP32/ ESP32/esp-idf-public/components/esp32/./system_api.c line 40 0x400d0abe: EspClass::restart() at ?? line ? 0x40080a81: check_loop_iteration() at /Users/assegidkidane/ Documents/Arduino/watchdogEspTest3/watchdogEspTest3.ino line 109 0x40080e74: __timerISR at /Users/assegidkidane/Documents/Arduino/hardware/ espressif/esp32/cores/esp32/esp32-hal-timer.c line 179 0x40081af1: _xt_lowint1 at xtensa_vectors.o line ?
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/espressif/arduino-esp32/issues/841#issuecomment-345596278, or mute the thread https://github.com/notifications/unsubscribe-auth/AB0jLxnCmRTAe-Rj5sf_qeUHKBreWp9Eks5s4Q_qgaJpZM4QhE-r .
using esp_restart_noos();
instead of ESP.restart();
fixes the hard lock before the hardware WD resets the CPU.
Hi Clemens, I am getting a 'not declared in this scope' error. Adding 'esp_panic.h' did not help either.
From: Clemens Kirchgatterer notifications@github.com Sent: Tuesday, December 5, 2017 7:12 AM To: espressif/arduino-esp32 Cc: assegid; Mention Subject: Re: [espressif/arduino-esp32] wdt implementation (#841)
using esp_restart_noos(); instead of ESP.restart(); fixes the hard lock before the hardware WD resets the CPU.
— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHubhttps://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_espressif_arduino-2Desp32_issues_841-23issuecomment-2D349315380&d=DwMCaQ&c=l45AxH-kUV29SRQusp9vYR0n1GycN4_2jInuKy6zbqQ&r=tUUNhxjXInPYrZpicyVgl4Wh6jDb9190e7dboFMmfI0&m=rHimyFy4-5zlLW8PIm7WBuJYMXuiimYK2ysWReZdi0U&s=TDE4lqZuf_Mylh5TSwLKVyirsf1xc7Xbm9HU5Bl3E6s&e=, or mute the threadhttps://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_notifications_unsubscribe-2Dauth_AFYTaXi6WLfV8IT3AAR-5FAw4dsc5Vi6gxks5s9U9HgaJpZM4QhE-2Dr&d=DwMCaQ&c=l45AxH-kUV29SRQusp9vYR0n1GycN4_2jInuKy6zbqQ&r=tUUNhxjXInPYrZpicyVgl4Wh6jDb9190e7dboFMmfI0&m=rHimyFy4-5zlLW8PIm7WBuJYMXuiimYK2ysWReZdi0U&s=aVBJ45f7GWBeD7xktDt8Z6FTb41PUAKGqRFdCrJpibw&e=.
@assegid you must be using old version of esp32 arduino ;) Here is some working code (tested last night)
#include "esp_system.h"
const int loopTimeCtl = 0;
hw_timer_t *timer = NULL;
void IRAM_ATTR resetModule(){
ets_printf("reboot\n");
esp_restart_noos();
}
void setup() {
Serial.begin(115200);
pinMode(loopTimeCtl, INPUT_PULLUP);
delay(1000);
Serial.println("running setup");
timer = timerBegin(0, 80, true); //timer 0, div 80
timerAttachInterrupt(timer, &resetModule, true);
timerAlarmWrite(timer, 3000000, false); //set time in us
timerAlarmEnable(timer); //enable interrupt
}
void loop() {
timerWrite(timer, 0); //reset timer (feed watchdog)
long tme = millis();
Serial.println("running mainloop");
while(!digitalRead(loopTimeCtl)){
Serial.println("button pressed");
delay(500);
}
delay(1000);
Serial.print("loop time is = ");
tme = millis() - tme;
Serial.println(tme);
}
Hello,
i tried to implement the example in my own sketch. I also know that this example made it into the examples of the code.
I setup the timer according to this example in setup(). But i do not feed the "watchdog" in loop() to test the function.
timer = timerBegin(3, 240, true); //timer 0, div 240
timerAttachInterrupt(timer, &resetModule, true);
timerAlarmWrite(timer, 6000000, true); //set time in us
timerAlarmEnable(timer); //enable interrupt
Ive set the div to 240, because i think my WROOM-32 operates at 240mhz. Therefore i excpected the whatdog to kick in after 6 seconds, after i started the ESP32. But there is no reaction at all.
Maybe i misssed doing something which is nessesary specially for the WROOM-32.
I'm using PlatformIO to programm the ESP32.
Does anyone has a hint for me, why the example doesn't work for me?
@Kasperdelasopa This issue is closed, because it looks as if it is not a bug or problem with the ESP32 Arduino core or its support libraries. For general API usage questions or help on specific coding challenges, please visit the arduino-esp32 Gitter channel. If you feel this issue was closed in error, reopen it and comment, why you think this is a bug in the Arduino-Core.
Hello
Existing for esp32 similar wdt reset like atmel awr wdt ???
https://tushev.org/articles/arduino/5/arduino-and-watchdog-timer
Hardware:
Board: ?ESP32 Dev Module? Core Installation/update date: ?? IDE name: ?Arduino IDE? Flash Frequency: ?? Upload Speed: ??
Description:
We need a simple watchdog timer implementation in the Arduino IDE. The proposed solution of 'use the IDF' is not acceptable. I am actually surprised why other people are not complaining about this. It should be easy enough to do for the members of the development team who are familiar with the details of the hardware.
Sketch: