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