Controller Reset and Serial Connections

Hello, Community!

I’ve been working on a project to be distributed to largely unskilled end-users and a means to easily reset WiFi configuration is desirable. I’ve taken the approach that variety is better, and I have provided a means to reset WiFi on boot with a pin held low, via the software (if they plan ahead), and found the jenscski/DoubleResetDetect project which seemed like another I wanted to look at adding.

I hit a challenge with developing that into my project which I originally thought was an issue with DRD, but I found it’s actually an offshoot of the IDE/Serial Monitor/Controller communication. Specifically, when flashing the firmware, of course, that’s one “reset”. Then, there’s apparently another one when the serial monitor reconnects to the controller. That kicks in the DRD detection and I’m left wondering where my credentials went.

This is not a complaint/problem per se, I’m just looking for a way through this.

I thought I understood that a serial connection is made resets controllers like the Uno, but that this was not the case with the ESP8266. What I find is different so I am not sure if that’s a “feature” of the controller or specific to how PlatformIO/VSCode creates that connection.

So, if anyone has ideas about how I can avoid this/properly test this, I’m all ears. I’m also interested to hear how other people have designed in field reset functionality.

Thanks!

Opening the serial port can only reset the microcontroller when you have a pin from the serial adapter (e.g., DTR or RST) connected to a cuircuit that pulses reset. It’s done in hardware, triggered from software (opening the serial port). See e.g. the NodeMCU circuit

Settting DTR=1 activates the NPN transistor VT1 and effectively connects nRST to the RTS pin. When DTR=0, RST is pulled high by pullup resistor R15. Analogous for VT2. (see here).

This also means that we can open a miniterm console in such a way that the processor in RESET all the time, not doing anything.

Considering the sketch

#include <Arduino.h>

void setup() {
	delay(1000);
	Serial.begin(115200);
	Serial.println("Boot!");
}
void loop() {
	static int i = 0;
	Serial.println("Loop " + String(i++));
	delay(2000);
}

When the ESP resets, we excpect Boot! Loop Loop Loop.... When we open miniterm normally and reset the ESP8266 we see

C:\Users\Maxi>miniterm.py --exit-char=50 COM12 115200
--- Miniterm on COM12  115200,8,N,1 ---
--- Quit: u'2' | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
;l␀d��|␀�$�|␃␄␌␄�␌d�␄c|ǃ␂�␛�{�c�␌b��og�l'o���␌#␜x��${l{$p�'�␘␃␄␌�␄l␄��␄␌␄c␄o�<␃l�␄␌�c��gn�␀d��$`␃�␛␒'o␌d`␃␎␃gs���o␌␄b␄�␏$␏r��g␄␌c␄�␇l�␃�␃dl�␛�d`␃��g�␃Boot!
Loop 0
Loop 1
Loop 2

--- exit ---

(UART garbage from bootlaoder messages at different baut)

However by using miniterm.py’s --dtr and --rts options (and using inverted values for my CH340G usb converter chip apparently?) we get

C:\Users\Maxi>miniterm.py --rts 1 --dtr 0 --exit-char=50 COM12 115200
--- forcing DTR inactive
--- forcing RTS active
--- Miniterm on COM12  115200,8,N,1 ---
--- Quit: u'2' | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---

Nothing, because RESET = 0 all the time (reset is low active, Reset=0 means “do reset” and 1 means “do not reset”)

However we can also observe that if we let the sketch run for a while and connect to it using normal flags

C:\Users\Maxi>miniterm.py --exit-char=50 COM12 115200
--- Miniterm on COM12  115200,8,N,1 ---
--- Quit: u'2' | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
Loop 33
Loop 34
Loop 35
--- exit ---

Then no reset it done. Also not with Arduino IDE

…So I somehow can’t reproduce that my serial programs, huh? But the serial monitor program can definitely toggle the RTS and DTR pins to do a reset, just like the flasher tool itself does

Writing at 0x00028000... (91 %)
Writing at 0x0002c000... (100 %)
Wrote 267168 bytes (194724 compressed) at 0x00000000 in 17.1 seconds (effective 124.6 kbit/s)...
Hash of data verified.

Leaving...

Hard resetting via RTS pin...

So with the sketch above, do you experience a reset when connecting with which serial monitor exactly?

Note: There is also a thread about it on the Arduino forums.

2 Likes

Well, this is strange but here’s what I am finding. Here’s my sketch:

#include <DoubleResetDetect.h>
#include <Arduino.h>

DoubleResetDetect drd(2.0, 0x00);

void setup()
{
    delay(1000);
    Serial.begin(74880);
    Serial.println("Boot.");
    if (drd.detect()) {
        Serial.println("Double reset detected.");
    } else {
        Serial.println("Normal boot detected.");
    }
}

void loop() {
    static int i = 0;
    Serial.println("Loop " + String(i++));
    delay(1000);
}
> Executing task: platformio.exe device monitor <

--- Miniterm on COM6  74880,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---

 ets Jan  8 2013,rst cause:2, boot mode:(3,6)

load 0x4010f000, len 1384, room 16
tail 8
chksum 0x2d
csum 0x2d
v8b899c12
~ld
Boot.
Double reset detected.
Loop 0
Loop 1
Loop 2

When I restart the serial monitor, the count continues without restarting. When I reset the device via button, a normal boot is detected. It’s when I am running the serial monitor (using the Windows IDE by the way) and upload a sketch, I consistently get the double-reset detected.

If I use the Arduino IDE, leave the serial monitor running, and upload the sketch - get a normal startup:

18:20:52.619 -> SDK:2.2.1(cfd48f3)/Core:2.5.2=20502000/lwIP:STABLE-2_1_2_RELEASE/glue:1.1-7-g82abda3/BearSSL:a143020
18:20:52.654 -> Boot.
18:20:52.654 -> Normal boot detected.
18:20:52.654 -> Loop 0
18:20:53.642 -> Loop 1
18:20:54.641 -> Loop 2

So it seems unique to the way PlatformIO is doing things. Not quite sure which direction to go with this.

I did submit a PR to the author of the Library where I use a check for initial program load with this function:

bool ipl() { // Determine if this is the first start after loading image
    char thisver[20] = __DATE__ __TIME__; // Sets at compile-time
    char savever[20] = "";
    bool _ipl = false;

    EEPROM.begin(20);
    EEPROM.get(EEPROM_ADDRESS, savever);
    if (strcmp (thisver, savever) != 0) {
        EEPROM.put(EEPROM_ADDRESS, thisver);
        EEPROM.commit();
        _ipl = true;
    }
    EEPROM.end();
    return _ipl;
}

The combination let’s me code around it, but it’s just wasted program space if there’s an external way to address it.

I just had to try this, as it was a very elegant solution to issue of how to reset wifi credentials… thank you!

So, if I have the serial monitor open in VSCode, and upload to the board (a Wemos D1 Mini in my case), the code uploads, the controller resets, and then it gets reset again when the serial terminal reattaches.

--- Miniterm on COM8  74880,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---

 ets Jan  8 2013,rst cause:2, boot mode:(3,6)

load 0x4010f000, len 1384, room 16
tail 8     
chksum 0x2d
csum 0x2d
v8b899c12
~ld

Boot.
Normal boot detected.
Loop 0
Loop 1
Loop 2
Loop 3

--- exit ---

If I do an ‘upload and monitor’, it gets the double reset

--- Miniterm on COM8  74880,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---

 ets Jan  8 2013,rst cause:2, boot mode:(3,7)

load 0x4010f000, len 1384, room 16
tail 8
chksum 0x2d
csum 0x2d
v8b899c12
~ld

Boot.
Double reset detected.
Loop 0
Loop 1
Loop 2
Loop 3

--- exit ---

Since @maxgerhardt mentioned CH340 RTS/DTR seemed to be inverted, I tried setting inverted values for the monitor in platformio… and now the output is correct when the serial monitor reattaches after an upload… whether it was already running and was an automatic reattach, or via the upload and monitor command

monitor_rts = 0
monitor_dtr = 1
Hard resetting via RTS pin...
==================== [SUCCESS] Took 14.08 seconds ====================
--- forcing DTR active
--- forcing RTS inactive
--- Miniterm on COM8  74880,8,N,1 ---
--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---
Loop 1
Loop 2
Loop 3

Full platformio.ini for completeness…

[env:d1_mini]
platform = espressif8266
board = d1_mini
framework = arduino
lib_deps = DoubleResetDetect
upload_speed = 460800
monitor_speed = 74880
monitor_rts = 0
monitor_dtr = 1
1 Like

That’s perfect, thank you! I though it might be along those lines, however figuring out the proper syntax in the .ini file seems to be arcane magic. :slight_smile:

I’ll give that a try on my side when I fully wake up.

1 Like

Coffee will help with that :laughing:

Some black magic, random cursing/muttering, and trawling of the docs re: platformio.ini usually does the trick. But yeah, it was a group effort… probably wouldn’t have picked it up if Max hadn’t mentioned the inversion…

The trouble with documentation is that it’s almost always written by people who already know what they are doing - or worse, developers! :wink:

I find it challenging to write good documentation personally. Without being able to see the look in the other person’s eyes to gauge whether or not you need to alter your delivery is quite a handicap. I encourage people to tell me when I’m not being clear since I consider that a bug. I’m sure there’s 20 people banging their head for every one person who contacts me.

In the case of the ini file syntax, I know that it can control those other things like miniterm in this case, but sometimes there are subtle differences between telling PlatformIO and telling miniterm directly. I don’t remember what the last one was I was trying to figure out, but suffice it to say I squirrel those examples away when I find them.

Coffee is brewing!

2 Likes

Hello - I still have some questions:

all I need to do is changing the ini file? But what does the “lib_deps = DoubleResetDetect” if I don’t put in your sketch example to detect normal boot or double reset? Or do I have to take your example?

I am trying to set up a test scenario: I want to power the D1 Mini Pro externally so it running all the time. Over the next one to two weeks I want to attach my computer several times during the day to check the serial monitor to see if everything works. Right now it is booting every time I hit “Monitore” in PlatformIO (Some time it is booting more than once ?!) and I loose all current values like e.g available heap memory.

What about the CP210x USB to Uart Bridge? Does the same apply here?

Many thanks in advanced!

I don’t think this is going to help you in that case. This discussion revolves around the use of the DoubleResetDetect library to detect a double reset to wipe configuration values (such as user saved wifi SSID and password) without needing any extra buttons.

It sounds like when you connected the USB, a reset pulse is being sent whilst the USB UART is being initialised (similar thing can happen with Arduino Uno/Nano when connected to USB if externally powered). What you may want to consider doing is to connect a USB serial adapter to the GND and TX pins on the D1 Mini (or kludge one up via another D1 Mini or an Arduino) so that the device isn’t reset.

Thank you very much for your quick response. This raises up more questions but I understood this discussion is not not the right place. I will check somewhere else. Thanks again!

No worries. Feel free to open up a new thread and we can work through the issue with you if you’re still stuck on something. :wink:

Thank you, you are ace! (coffe)