Skip to content

Commit 9d82ebe

Browse files
authored
Add example for using ESP.rebootIntoUartDownloadMode() (#7897)
1 parent 47b8947 commit 9d82ebe

File tree

1 file changed

+190
-0
lines changed

1 file changed

+190
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
/*
2+
3+
Example of Booting into UART Download using `ESP.rebootIntoUartDownloadMode()`
4+
5+
Two methods are presented for starting UART Boot Mode.
6+
1) From `loop()` call the function `proxyEspSync()`, which peeks for a SLIP
7+
frame marker. Then when present, look for an esptool ESP_SYNC packet on
8+
the Serial port.
9+
2) A simple hotkey of 'D'.
10+
11+
After either of these, `ESP.rebootIntoUartDownloadMode()` is called to place
12+
the ESP8266 into UART Flash program mode.
13+
14+
For a quick test to confirm the ESP8266 is responding to esptool.py,
15+
use this command:
16+
esptool.py --chip esp8266 --before no_reset --after soft_reset flash_id
17+
18+
19+
Note with these methods a hard reset is not done, and the esptool.py may not
20+
detect and report the correct Crystal frequency for the ESP Module. If you
21+
need that info, it needs to be gathered after a Power-On or Hard Reset.
22+
*/
23+
24+
#include <Arduino.h>
25+
#include <ESP8266WiFi.h>
26+
27+
////////////////////////////////////////////////////////////////////////////////
28+
//
29+
// Check Serial Receive for ESP_SYNC slip packet from esptool.py
30+
//
31+
// If you are already using Serial input for command input, the character '\xC0'
32+
// is not available. We must reserve its use for the SLIP Frame Marker. I am not
33+
// sure which languages if any, would pose a problem. For the non-English
34+
// languages check your character set values to be sure it is not an issue. If
35+
// it is an issue, you will not be able to use this method as presented. The
36+
// '\xC0' character is defined by the SLIP protocol and cannot be changed.
37+
38+
// If your needs require it, you can add logic to loop() for setting and
39+
// clearing uartDownloadEnable. For example, you could add a push button to a
40+
// GPIO pin and monitor for a 5-second press. Then, set uartDownloadEnable to
41+
// true. In addition to that, you could also define a time-to-live for that
42+
// state and clear it after it elapses.
43+
//
44+
// Change this to false if you do not want ESP_SYNC monitor always on.
45+
bool uartDownloadEnable = true;
46+
47+
// Buffer size to receive an ESP_SYNC packet into, larger than the expected
48+
// ESP_SYNC packet length.
49+
constexpr size_t pktBufSz = 64;
50+
51+
// Enough time to receive 115 bytes at 115200bps.
52+
// More than enough to finish receiving an ESP_SYNC packet.
53+
constexpr size_t kSyncTimeoutMs = 10;
54+
55+
// The SLIP Frame end character, which is also used to start a frame.
56+
constexpr char slipFrameMarker = '\xC0';
57+
58+
// General packet format:
59+
// <0xC0><cmd><payload length><32 bit cksum><payload data ...><0xC0>
60+
// Slip packet for ESP_SYNC, minus the frame markers ('\xC0') captured from
61+
// esptool using the `--trace` option.
62+
const char syncPkt[] PROGMEM =
63+
"\x00\x08\x24\x00\x00\x00\x00\x00\x07\x07\x12\x20"
64+
"UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU";
65+
66+
constexpr size_t syncPktSz = sizeof(syncPkt) - 1; // Don't compare zero terminator char
67+
68+
//
69+
// Use the discovery of an ESP_SYNC packet, to trigger calling UART Download
70+
// Mode. At entry we expect the Serial FIFO to start with the byte following
71+
// the slipFrameMarker.
72+
//
73+
void proxyEspSync() {
74+
if (!uartDownloadEnable) {
75+
return;
76+
}
77+
78+
byte buf[pktBufSz];
79+
80+
// If it is an ESP_SYNC packet, it will not take long for readBytesUntil() to
81+
// complete.
82+
Serial.setTimeout(kSyncTimeoutMs);
83+
int len = Serial.readBytesUntil(slipFrameMarker, buf, pktBufSz);
84+
85+
// To avoid a false trigger, only start UART Download Mode when we get an
86+
// exact match to the captured esptool ESP_SYNC packet.
87+
if (syncPktSz == len && 0 == memcmp_P(buf, syncPkt, len)) {
88+
ESP.rebootIntoUartDownloadMode();
89+
// Does not return
90+
}
91+
92+
// Assume RX FIFO data is garbled and flush all RX data.
93+
while (0 <= Serial.read()) {} // Clear FIFO
94+
95+
// If your Serial requirements need a specific timeout value, you would
96+
// restore those here.
97+
}
98+
//
99+
////////////////////////////////////////////////////////////////////////////////
100+
101+
void preinit() {
102+
// (no C++ in function)
103+
// disable wifi
104+
ESP8266WiFiClass::preinitWiFiOff();
105+
}
106+
107+
void setup() {
108+
// For `proxyEspSync()` to work, the Serial.begin() speed needs to be
109+
// 115200bps. This is the data rate used by esptool.py. It expects the Boot
110+
// ROM to use its "auto-baud" feature to match up. Since `proxyEspSync()` is
111+
// acting as a proxy we must use 115200.
112+
//
113+
// If on the Arduino IDE Tools menu you use "Upload Speeds" above 115200, it
114+
// will work. When esptool.py is run with the `--baud BAUD` option specified
115+
// above 115200, initial communication with the ESP8266 is done at 115200bps.
116+
// Once esptool.py has synchronize with the ESP8266 and downloaded a short
117+
// stub, then both devices shift their UART speeds to the command line value.
118+
Serial.begin(115200);
119+
120+
Serial.println(F(
121+
"\r\n\r\n"
122+
"Boot UART Download Demo - initialization started.\r\n"
123+
"\r\n"
124+
"For a quick test to see the UART Download work,\r\n"
125+
"stop your serial terminal APP and run:\r\n"
126+
" esptool.py --chip esp8266 --before no_reset --after soft_reset flash_id\r\n"));
127+
128+
// ...
129+
}
130+
131+
void cmdLoop(Print& oStream, int key) {
132+
switch (key) {
133+
case 'e':
134+
oStream.println(F("Enable monitor for detecting ESP_SYNC from esptool.py"));
135+
uartDownloadEnable = true;
136+
break;
137+
138+
case 'D':
139+
// This option would be prone to false triggering. It is here for DEMO
140+
// purposes and debugging.
141+
oStream.println(F("Boot into UART download mode ..."));
142+
oStream.flush();
143+
ESP.rebootIntoUartDownloadMode();
144+
break;
145+
146+
case 'R':
147+
oStream.println(F("Restart ..."));
148+
oStream.flush();
149+
ESP.restart();
150+
break;
151+
152+
// ...
153+
154+
case '?':
155+
oStream.println(F("\r\nHot key help:"));
156+
if (!uartDownloadEnable) {
157+
oStream.println(F(" e - Enable monitor for detecting ESP_SYNC from esptool.py"));
158+
}
159+
oStream.println(F(" D - Boot into UART download mode"));
160+
oStream.println(F(" R - Restart"));
161+
oStream.println(F(" ? - This help message\r\n"));
162+
break;
163+
164+
default:
165+
break;
166+
}
167+
168+
oStream.println();
169+
}
170+
171+
172+
void loop() {
173+
174+
// In this example, we can have Serial data from a user keystroke for our
175+
// command loop or the esptool trying to SYNC up for flashing. If the
176+
// character matches the Slip Frame Marker (the 1st byte of the SYNC packet),
177+
// we intercept it and call our ESP_SYNC proxy to complete the verification
178+
// and reboot into the UART Downloader. Otherwise, process the keystroke as
179+
// normal.
180+
if (0 < Serial.available()) {
181+
int keyPress = Serial.read();
182+
if (slipFrameMarker == keyPress) {
183+
proxyEspSync();
184+
} else {
185+
cmdLoop(Serial, keyPress);
186+
}
187+
}
188+
189+
// ...
190+
}

0 commit comments

Comments
 (0)