From 65a0123017584946b27f5eda148ee7cab406fe17 Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Thu, 25 Feb 2021 19:53:49 -0800 Subject: [PATCH 1/5] Add example for using ESP.rebootIntoUartDownloadMode() --- .../examples/UartDownload/UartDownload.ino | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 libraries/esp8266/examples/UartDownload/UartDownload.ino diff --git a/libraries/esp8266/examples/UartDownload/UartDownload.ino b/libraries/esp8266/examples/UartDownload/UartDownload.ino new file mode 100644 index 0000000000..0bac8289d3 --- /dev/null +++ b/libraries/esp8266/examples/UartDownload/UartDownload.ino @@ -0,0 +1,181 @@ +/* + + Example of Booting into UART Download using `ESP.rebootIntoUartDownloadMode()` + + Two methods are presented for starting UART Boot Mode. + 1) From `loop()` call the function `proxyEspSync()`, which peeks for a SLIP + frame marker. Then when present, look for an esptool ESP_SYNC packet on + the Serial port. + 2) A simple hotkey of 'D'. + + After either of these, `ESP.rebootIntoUartDownloadMode()` is called to place + the ESP8266 into UART Flash program mode. + + For a quick test to confirm the ESP8266 is responding to esptool.py, + use this command: + esptool.py --chip esp8266 --before no_reset --after soft_reset flash_id + + + Note with these methods a hard reset is not done, and the esptool.py may not + detect and report the correct Crystal frequency for the ESP Module. If you + need that info, it needs to be gathered after a Power-On or Hard Reset. +*/ + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +// +// Peek at Serial Receive for ESP_SYNC slip packet from esptool.py +// +// If you are already using Serial input for command input, the character '\xC0' +// is not available. We must reserve its use for the SLIP Frame Marker. I am not +// sure which languages if any, would pose a problem. For the non-English +// languages check your character set values to be sure it is not an issue. If +// it is an issue, you will not be able to use this method as presented. The +// '\xC0' character is defined by the SLIP protocol and cannot be changed. + +// If your needs require it, you can add logic to loop() for setting and +// clearing uartDownloadEnable. For example, you could add a push button to a +// GPIO pin and monitor for a 5-second press. Then, set uartDownloadEnable to +// true. In addition to that, you could also set a time-to-live for that state +// and clear it after it elapses. +// +// Change this to false if you do not want ESP_SYNC monitor always on. +bool uartDownloadEnable = true; + +// Buffer size to receive an ESP_SYNC packet into, larger than the expected +// ESP_SYNC packet length. +constexpr size_t pktBufSz = 64; + +// Enough time to receive 115 bytes at 115200bps. +// More than enough to receive an ESP_SYNC packet. +constexpr size_t kSyncTimeoutMs = 10; + +// The SLIP Frame end character, which is also used to start a frame. +constexpr char slipFrameMarker = '\xC0'; + +// General packet format: +// <0xC0><32 bit cksum><0xC0> +// Slip packet for ESP_SYNC, minus the frame markers ('\xC0'). +// Captured from esptool using the `--trace` option. +const char syncPkt[] PROGMEM = + "\x00\x08\x24\x00\x00\x00\x00\x00\x07\x07\x12\x20" + "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU"; + +constexpr size_t syncPktSz = sizeof(syncPkt) - 1; // Don't compare zero terminator char + +// +// Use the discovery of an ESP_SYNC packet, to trigger calling UART Download Mode. +// +void proxyEspSync() { + + if (!uartDownloadEnable || + 0 >= Serial.available() || + slipFrameMarker != Serial.peek()) { + return; + } + + Serial.read(); // Skip (start) Frame marker + + byte buf[pktBufSz]; + + // If it is an ESP_SYNC packet, it will not take long for readBytesUntil() to + // complete. + Serial.setTimeout(kSyncTimeoutMs); + int len = Serial.readBytesUntil(slipFrameMarker, buf, pktBufSz); + + // To avoid a false trigger, only start UART Download Mode when we get an + // exact match to the captured esptool ESP_SYNC packet. + if (syncPktSz == len && 0 == memcmp_P(buf, syncPkt, len)) { + ESP.rebootIntoUartDownloadMode(); + // Does not return + } + + // Assume RX FIFO data is garbled and flush all RX data. + while(0 <= Serial.read()) {} // Clear FIFO +} +// +//////////////////////////////////////////////////////////////////////////////// + +void preinit() { + // (no C++ in function) + // disable wifi + ESP8266WiFiClass::preinitWiFiOff(); +} + +void setup() { + // For `proxyEspSync()` to work, the Serial.begin() speed needs to be + // 115200bps. This is the data rate used by esptool.py. It expects the Boot + // ROM to use its "auto-baud" feature to match up. Since `proxyEspSync()` is + // acting as a proxy we must use 115200. + // + // If on the Arduino IDE Tools menu you use "Upload Speeds" above 115200, it + // will work. When esptool.py is run with the `--baud BAUD` option specified + // above 115200, initial communication with the ESP8266 is done at 115200bps. + // Once esptool.py has synchronize with the ESP8266 and downloaded a short + // stub, then both devices shift their UART speeds to the command line value. + Serial.begin(115200); + + Serial.println(F( + "\r\n\r\n" + "Boot UART Download Demo - initialization started.\r\n" + "\r\n" + "For a quick test to see the UART Download work,\r\n" + "stop your serial terminal APP and run:\r\n" + " esptool.py --chip esp8266 --before no_reset --after soft_reset flash_id\r\n")); + + // ... +} + +void cmdLoop(Stream& oStream) { + if (0 >= oStream.available()) { + return; + } + + switch (oStream.read()) { + case 'e': + oStream.println(F("Enable monitor for detecting ESP_SYNC from esptool.py")); + uartDownloadEnable = true; + break; + + case 'D': + oStream.println(F("Boot into UART download mode ...")); + oStream.flush(); + ESP.rebootIntoUartDownloadMode(); + break; + + case 'R': + oStream.println(F("Restart ...")); + oStream.flush(); + ESP.restart(); + break; + + // ... + + case '?': + oStream.println(F("\r\nHot key help:")); + if (!uartDownloadEnable) { + oStream.println(F(" e - Enable monitor for detecting ESP_SYNC from esptool.py")); + } + oStream.println(F(" D - Boot into UART download mode")); + oStream.println(F(" R - Restart")); + oStream.println(F(" ? - This help message\r\n")); + break; + + default: + break; + } + + oStream.println(); +} + + +void loop() { + + proxyEspSync(); + + cmdLoop(Serial); + + // ... +} From 7e7e774443fca52910d70a18f430cfc5aecfc185 Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Sun, 28 Feb 2021 18:07:30 -0800 Subject: [PATCH 2/5] Style --- .../esp8266/examples/UartDownload/UartDownload.ino | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libraries/esp8266/examples/UartDownload/UartDownload.ino b/libraries/esp8266/examples/UartDownload/UartDownload.ino index 0bac8289d3..a49bb30818 100644 --- a/libraries/esp8266/examples/UartDownload/UartDownload.ino +++ b/libraries/esp8266/examples/UartDownload/UartDownload.ino @@ -93,7 +93,7 @@ void proxyEspSync() { } // Assume RX FIFO data is garbled and flush all RX data. - while(0 <= Serial.read()) {} // Clear FIFO + while (0 <= Serial.read()) {} // Clear FIFO } // //////////////////////////////////////////////////////////////////////////////// @@ -118,12 +118,12 @@ void setup() { Serial.begin(115200); Serial.println(F( - "\r\n\r\n" - "Boot UART Download Demo - initialization started.\r\n" - "\r\n" - "For a quick test to see the UART Download work,\r\n" - "stop your serial terminal APP and run:\r\n" - " esptool.py --chip esp8266 --before no_reset --after soft_reset flash_id\r\n")); + "\r\n\r\n" + "Boot UART Download Demo - initialization started.\r\n" + "\r\n" + "For a quick test to see the UART Download work,\r\n" + "stop your serial terminal APP and run:\r\n" + " esptool.py --chip esp8266 --before no_reset --after soft_reset flash_id\r\n")); // ... } From 843f8ee6e1929781afe35c4a28bd517e69b25968 Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Mon, 1 Mar 2021 13:16:40 -0800 Subject: [PATCH 3/5] Fixed race condition --- .../examples/UartDownload/UartDownload.ino | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/libraries/esp8266/examples/UartDownload/UartDownload.ino b/libraries/esp8266/examples/UartDownload/UartDownload.ino index a49bb30818..7dd1259dfd 100644 --- a/libraries/esp8266/examples/UartDownload/UartDownload.ino +++ b/libraries/esp8266/examples/UartDownload/UartDownload.ino @@ -38,8 +38,8 @@ // If your needs require it, you can add logic to loop() for setting and // clearing uartDownloadEnable. For example, you could add a push button to a // GPIO pin and monitor for a 5-second press. Then, set uartDownloadEnable to -// true. In addition to that, you could also set a time-to-live for that state -// and clear it after it elapses. +// true. In addition to that, you could also define a time-to-live for that +// state and clear it after it elapses. // // Change this to false if you do not want ESP_SYNC monitor always on. bool uartDownloadEnable = true; @@ -66,18 +66,15 @@ const char syncPkt[] PROGMEM = constexpr size_t syncPktSz = sizeof(syncPkt) - 1; // Don't compare zero terminator char // -// Use the discovery of an ESP_SYNC packet, to trigger calling UART Download Mode. +// Use the discovery of an ESP_SYNC packet, to trigger calling UART Download +// Mode. At entry we expect the Serial FIFO to start with the byte following +// the slipFrameMarker. // void proxyEspSync() { - - if (!uartDownloadEnable || - 0 >= Serial.available() || - slipFrameMarker != Serial.peek()) { + if (!uartDownloadEnable) { return; } - Serial.read(); // Skip (start) Frame marker - byte buf[pktBufSz]; // If it is an ESP_SYNC packet, it will not take long for readBytesUntil() to @@ -94,6 +91,9 @@ void proxyEspSync() { // Assume RX FIFO data is garbled and flush all RX data. while (0 <= Serial.read()) {} // Clear FIFO + + // If your Serial requirements need a specific timeout value, you would + // restore those here. } // //////////////////////////////////////////////////////////////////////////////// @@ -134,12 +134,18 @@ void cmdLoop(Stream& oStream) { } switch (oStream.read()) { + case slipFrameMarker: + proxyEspSync(); + break; + case 'e': oStream.println(F("Enable monitor for detecting ESP_SYNC from esptool.py")); uartDownloadEnable = true; break; case 'D': + // This option would be prone to false triggering. It is here for DEMO + // purposes and debugging. oStream.println(F("Boot into UART download mode ...")); oStream.flush(); ESP.rebootIntoUartDownloadMode(); @@ -173,8 +179,6 @@ void cmdLoop(Stream& oStream) { void loop() { - proxyEspSync(); - cmdLoop(Serial); // ... From cd1d4fbd3d6fa3f3e79807a4fe407f2b399a47f8 Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Wed, 3 Mar 2021 14:21:24 -0800 Subject: [PATCH 4/5] Refactored loop() and cmdLoop() --- .../examples/UartDownload/UartDownload.ino | 27 +++++++++++-------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/libraries/esp8266/examples/UartDownload/UartDownload.ino b/libraries/esp8266/examples/UartDownload/UartDownload.ino index 7dd1259dfd..217964b99c 100644 --- a/libraries/esp8266/examples/UartDownload/UartDownload.ino +++ b/libraries/esp8266/examples/UartDownload/UartDownload.ino @@ -128,16 +128,8 @@ void setup() { // ... } -void cmdLoop(Stream& oStream) { - if (0 >= oStream.available()) { - return; - } - - switch (oStream.read()) { - case slipFrameMarker: - proxyEspSync(); - break; - +void cmdLoop(Print& oStream, int key) { + switch (key) { case 'e': oStream.println(F("Enable monitor for detecting ESP_SYNC from esptool.py")); uartDownloadEnable = true; @@ -179,7 +171,20 @@ void cmdLoop(Stream& oStream) { void loop() { - cmdLoop(Serial); + // In this example, we can have Serial data from a user keystroke for our + // command loop or the esptool trying to SYNC up for flashing. If the + // character matches the Slip Frame Marker (the 1st byte of the SYNC packet), + // we intercept it and call our ESP_SYNC proxy to complete the verification + // and reboot into the UART Downloader. Otherwise, process the keystroke as + // normal. + if (0 < Serial.available()) { + int keyPress = Serial.read(); + if (slipFrameMarker == keyPress) { + proxyEspSync(); + } else { + cmdLoop(Serial, keyPress); + } + } // ... } From 7c9039ac585c6a924dc07a13bd6dff7bfb46dc47 Mon Sep 17 00:00:00 2001 From: M Hightower <27247790+mhightower83@users.noreply.github.com> Date: Fri, 5 Mar 2021 09:32:03 -0800 Subject: [PATCH 5/5] Update comments --- libraries/esp8266/examples/UartDownload/UartDownload.ino | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/libraries/esp8266/examples/UartDownload/UartDownload.ino b/libraries/esp8266/examples/UartDownload/UartDownload.ino index 217964b99c..fa8ea0d61e 100644 --- a/libraries/esp8266/examples/UartDownload/UartDownload.ino +++ b/libraries/esp8266/examples/UartDownload/UartDownload.ino @@ -26,7 +26,7 @@ //////////////////////////////////////////////////////////////////////////////// // -// Peek at Serial Receive for ESP_SYNC slip packet from esptool.py +// Check Serial Receive for ESP_SYNC slip packet from esptool.py // // If you are already using Serial input for command input, the character '\xC0' // is not available. We must reserve its use for the SLIP Frame Marker. I am not @@ -49,7 +49,7 @@ bool uartDownloadEnable = true; constexpr size_t pktBufSz = 64; // Enough time to receive 115 bytes at 115200bps. -// More than enough to receive an ESP_SYNC packet. +// More than enough to finish receiving an ESP_SYNC packet. constexpr size_t kSyncTimeoutMs = 10; // The SLIP Frame end character, which is also used to start a frame. @@ -57,8 +57,8 @@ constexpr char slipFrameMarker = '\xC0'; // General packet format: // <0xC0><32 bit cksum><0xC0> -// Slip packet for ESP_SYNC, minus the frame markers ('\xC0'). -// Captured from esptool using the `--trace` option. +// Slip packet for ESP_SYNC, minus the frame markers ('\xC0') captured from +// esptool using the `--trace` option. const char syncPkt[] PROGMEM = "\x00\x08\x24\x00\x00\x00\x00\x00\x07\x07\x12\x20" "UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";