Skip to content

Commit

Permalink
Add example for using ESP.rebootIntoUartDownloadMode() (#7897)
Browse files Browse the repository at this point in the history
  • Loading branch information
mhightower83 authored Mar 15, 2021
1 parent 47b8947 commit 9d82ebe
Showing 1 changed file with 190 additions and 0 deletions.
190 changes: 190 additions & 0 deletions libraries/esp8266/examples/UartDownload/UartDownload.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
/*
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 <Arduino.h>
#include <ESP8266WiFi.h>

////////////////////////////////////////////////////////////////////////////////
//
// 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
// 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 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;

// 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 finish receiving 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><cmd><payload length><32 bit cksum><payload data ...><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. At entry we expect the Serial FIFO to start with the byte following
// the slipFrameMarker.
//
void proxyEspSync() {
if (!uartDownloadEnable) {
return;
}

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

// If your Serial requirements need a specific timeout value, you would
// restore those here.
}
//
////////////////////////////////////////////////////////////////////////////////

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(Print& oStream, int key) {
switch (key) {
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();
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() {

// 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);
}
}

// ...
}

0 comments on commit 9d82ebe

Please sign in to comment.