Merge pull request #11 from adafruit/brain-esp32

Support Brain to program esp32
This commit is contained in:
Ha Thach 2023-02-16 18:26:23 +07:00 committed by GitHub
commit 84329baa36
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 935 additions and 21 deletions

View file

@ -0,0 +1,186 @@
// This sketch program ESP32 by flashing bin file from SD Card
// Hardware wiring:
// - Brain GPIO28 <-> ESP32 IO0
// - Brain Reset <-> ESP32 Enable
// - Brain TX/RX <-> ESP32 RX/TX
// required for Host MSC block device
#include "SdFat.h"
// required for USB host
#include "pio_usb.h"
#include "Adafruit_TinyUSB.h"
#include "Adafruit_TestBed_Brains.h"
#define ESP32_RESET 27
#define ESP32_IO0 28
// false for ESP32 (default)
// true for later chips such as S2, S3, H2, C3
#define ESP32_SUPPORTS_ENCRYPTED_FLASH true
//#define ESP32_BAUDRATE 921600
#define ESP32_BAUDRATE 115200
// CDC Host object
Adafruit_USBH_CDC SerialHost;
// Defined an boot rom object that use UART Serial1
ESP32BootROMClass ESP32BootROM(SerialHost, ESP32_IO0, ESP32_RESET, ESP32_SUPPORTS_ENCRYPTED_FLASH);
// Bin files on SDCard to program
// These files can be found in raspi-tester (or compiled from Arduino sketch)
#define BIN_FEATHER_ESP32S2 0
#define BIN_FEATHER_ESP32S3 1
#define BIN_FILES BIN_FEATHER_ESP32S3 // select which bins to flash either Feather ESP32 S2 or S3
struct {
uint32_t addr;
const char* fpath;
} bin_files [] =
{
#if BIN_FILES == BIN_FEATHER_ESP32S2
{ 0x1000 , "esp32s2/PID5000/esp32s2_feather_test.ino.bootloader.bin" },
{ 0x8000 , "esp32s2/PID5000/esp32s2_feather_test.ino.partitions.bin" },
{ 0xe000 , "esp32s2/PID5000/boot_app0.bin" },
{ 0x10000 , "esp32s2/PID5000/esp32s2_feather_test.ino.bin" },
{ 0x2d0000, "esp32s2/PID5000/tinyuf2.bin" },
#endif
#if BIN_FILES == BIN_FEATHER_ESP32S3
{ 0x0000 , "esp32s3/PID5477/esp32s3_feather_test.ino.bootloader.bin" },
{ 0x8000 , "esp32s3/PID5477/esp32s3_feather_test.ino.partitions.bin" },
{ 0xe000 , "esp32s3/PID5477/boot_app0.bin" },
{ 0x10000 , "esp32s3/PID5477/esp32s3_feather_test.ino.bin" },
{ 0x2d0000, "esp32s3/PID5477/tinyuf2.bin" },
#endif
};
enum {
BIN_FILES_COUNT = sizeof(bin_files)/sizeof(bin_files[0])
};
//--------------------------------------------------------------------+
// Setup and Loop on Core0
//--------------------------------------------------------------------+
void prepare_sd(void) {
if (!Brain.SD_detected()) {
Brain.LCD_printf(0, "No SD Card");
while ( !Brain.SD_detected() ) delay(10);
}
if ( !Brain.SD_begin(SD_SCK_MHZ(16)) ) {
Brain.LCD_printf(0, "SD init failed");
while(1) delay(10);
}
Brain.LCD_printf(0, "SD mounted");
// Print out file on SD if Serial is connected
if (Serial) {
Serial.println();
Serial.println("SD Contents:");
Serial.printf("Card size = %0.1f GB\n", 0.000000512 * Brain.SD.card()->sectorCount());
Brain.SD.ls(LS_R | LS_DATE | LS_SIZE);
}
}
void print_speed(size_t count, uint32_t ms) {
Brain.LCD_printf(0, "%.01fKB %.01fs", count/1000.0F, ms / 1000.0F);
Serial.printf("Completed %u bytes in %.02f seconds.\r\n", count, ms / 1000.0F);
Serial.printf("Speed : %.02f KB/s\r\n", (count / 1000.0F) / (ms / 1000.0F));
}
void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
Serial.println("Tester Brains: Programming ESP32 with UART!");
// sync: wait for Brain.begin() called in core1 before accessing SD or other peripherals
while (!Brain.inited()) delay(10);
// prepare SD Card
prepare_sd();
while ( !Brain.esp32_begin(&ESP32BootROM, ESP32_BAUDRATE) ) {
}
// Writing bin files
size_t total_bytes = 0;
uint32_t ms = millis();
for(size_t i=0; i<BIN_FILES_COUNT; i++) {
Brain.LCD_printf("Flashing file %u", i);
size_t wr_count = Brain.essp32_programFlash(bin_files[i].fpath, bin_files[i].addr);
total_bytes += wr_count;
if (!wr_count) {
Brain.LCD_printf_error("Failed to flash");
}
}
print_speed(total_bytes, millis() - ms);
Brain.esp32_end();
// reset ESP32 to run new firmware
Brain.targetReset();
}
void loop() {
}
//--------------------------------------------------------------------+
// Setup and Loop on Core1
//--------------------------------------------------------------------+
// call usbh_begin() here to make pio usb background task run on core1
// NOTE: Brain.begin() should be called here as well to prevent race condition
void setup1() {
Brain.begin();
Brain.usbh_begin();
// Since we only support 1 CDC interface with Tester (also CFG_TUH_CDC = 1)
// the index will always be 0 for SerialHost
// SerialHost.setInterfaceIndex(0);
SerialHost.begin(115200);
Brain.LCD_printf(0, "No USB attached");
Brain.LCD_printf(1, "Plug your device");
}
// core1's loop: process usb host task on core1
void loop1() {
Brain.USBHost.task();
// periodically flush SerialHost if connected
if ( SerialHost && SerialHost.connected() ) {
SerialHost.flush();
}
}
//--------------------------------------------------------------------+
// TinyUSB Host callbacks
// Note: running in the same core where Brain.USBHost.task() is called
//--------------------------------------------------------------------+
extern "C" {
// Invoked when device is mounted (configured)
void tuh_mount_cb (uint8_t daddr)
{
uint16_t vid, pid;
tuh_vid_pid_get(daddr, &vid, &pid);
Serial.printf("Device attached, address = %d\r\n", daddr);
Brain.LCD_printf("USBID %04x:%04x", vid, pid);
}
/// Invoked when device is unmounted (bus reset/unplugged)
void tuh_umount_cb(uint8_t dev_addr)
{
(void) dev_addr;
Brain.LCD_printf(1, "No USB Device");
}
}

View file

@ -0,0 +1,155 @@
// This sketch program ESP32 by flashing bin file from SD Card
// Hardware wiring:
// - Brain GPIO28 <-> ESP32 IO0
// - Brain Reset <-> ESP32 Enable
// - Brain TX/RX <-> ESP32 RX/TX
// required for Host MSC block device
#include "SdFat.h"
// required for USB host
#include "pio_usb.h"
#include "Adafruit_TinyUSB.h"
#include "Adafruit_TestBed_Brains.h"
#define ESP32_RESET 27
#define ESP32_IO0 28
// false for ESP32 (default)
// true for later chips such as S2, S3, H2, C3
#define ESP32_SUPPORTS_ENCRYPTED_FLASH false
#define ESP32_BAUDRATE 921600
// #define ESP32_BAUDRATE 115200
// Bin files on SDCard to program
// These files can be generated by any of Arduino sketches
// The example use WiFiAccessPoint sketch, which we can verify its running image
// by simply scan for existence of ssid "YourAP"
struct {
uint32_t addr;
const char* fpath;
} bin_files [] = {
{ 0x1000 , "esp32/WiFiAccessPoint/WiFiAccessPoint.ino.bootloader.bin" },
{ 0x8000 , "esp32/WiFiAccessPoint/WiFiAccessPoint.ino.partitions.bin" },
{ 0xe000 , "esp32/WiFiAccessPoint/boot_app0.bin" },
{ 0x10000 , "esp32/WiFiAccessPoint/WiFiAccessPoint.ino.bin" },
};
enum {
BIN_FILES_COUNT = sizeof(bin_files)/sizeof(bin_files[0])
};
// Defined an boot rom object that use UART Serial1
ESP32BootROMClass ESP32BootROM(Serial1, ESP32_IO0, ESP32_RESET, ESP32_SUPPORTS_ENCRYPTED_FLASH);
//--------------------------------------------------------------------+
// Setup and Loop on Core0
//--------------------------------------------------------------------+
void prepare_sd(void) {
if (!Brain.SD_detected()) {
Brain.LCD_printf(0, "No SD Card");
while ( !Brain.SD_detected() ) delay(10);
}
if ( !Brain.SD_begin(SD_SCK_MHZ(16)) ) {
Brain.LCD_printf(0, "SD init failed");
while(1) delay(10);
}
Brain.LCD_printf(0, "SD mounted");
// Print out file on SD if Serial is connected
if (Serial) {
Serial.println();
Serial.println("SD Contents:");
Serial.printf("Card size = %0.1f GB\n", 0.000000512 * Brain.SD.card()->sectorCount());
Brain.SD.ls(LS_R | LS_DATE | LS_SIZE);
}
}
void print_speed(size_t count, uint32_t ms) {
Brain.LCD_printf(0, "%.01fKB %.01fs", count/1000.0F, ms / 1000.0F);
Serial.printf("Completed %u bytes in %.02f seconds.\r\n", count, ms / 1000.0F);
Serial.printf("Speed : %.02f KB/s\r\n", (count / 1000.0F) / (ms / 1000.0F));
}
void setup() {
Serial.begin(115200);
while (!Serial) delay(10);
Serial.println("Tester Brains: Programming ESP32 with UART!");
// sync: wait for Brain.begin() called in core1 before accessing SD or other peripherals
while (!Brain.inited()) delay(10);
// prepare SD Card
prepare_sd();
while ( !Brain.esp32_begin(&ESP32BootROM, ESP32_BAUDRATE) ) {
// retry syncing
delay(1000);
}
// Writing bin files
size_t total_bytes = 0;
uint32_t ms = millis();
for(size_t i=0; i<BIN_FILES_COUNT; i++) {
Brain.LCD_printf("Flashing file %u", i);
size_t wr_count = Brain.essp32_programFlash(bin_files[i].fpath, bin_files[i].addr);
total_bytes += wr_count;
if (!wr_count) {
Brain.LCD_printf_error("Failed to flash");
}
}
print_speed(total_bytes, millis() - ms);
Brain.esp32_end();
// reset ESP32 to run new firmware
Brain.targetReset();
}
void loop() {
}
//--------------------------------------------------------------------+
// Setup and Loop on Core1
//--------------------------------------------------------------------+
// call usbh_begin() here to make pio usb background task run on core1
// NOTE: Brain.begin() should be called here as well to prevent race condition
void setup1() {
Brain.begin();
Brain.usbh_begin();
Brain.LCD_printf(1, "No USB Device");
}
// core1's loop: process usb host task on core1
void loop1() {
Brain.USBHost.task();
}
//--------------------------------------------------------------------+
// TinyUSB Host callbacks
// Note: running in the same core where Brain.USBHost.task() is called
//--------------------------------------------------------------------+
extern "C" {
// Invoked when device is mounted (configured)
void tuh_mount_cb (uint8_t dev_addr)
{
(void) dev_addr;
}
/// Invoked when device is unmounted (bus reset/unplugged)
void tuh_umount_cb(uint8_t dev_addr)
{
(void) dev_addr;
Brain.LCD_printf(1, "No USB Device");
}
}

View file

@ -59,7 +59,8 @@ void setup1() {
// Since we only support 1 CDC interface with Tester (also CFG_TUH_CDC = 1)
// the index will always be 0 for SerialHost
SerialHost.begin(0);
// SerialHost.setInterfaceIndex(0);
SerialHost.begin(115200);
Brain.LCD_printf(0, "No USB attached");
Brain.LCD_printf(1, "Plug your device");

View file

@ -93,6 +93,7 @@ Adafruit_TestBed_Brains::Adafruit_TestBed_Brains() {
_target_swdclk = 3;
dap = NULL;
esp32boot = NULL;
}
void Adafruit_TestBed_Brains::begin(void) {
@ -389,6 +390,114 @@ size_t Adafruit_TestBed_Brains::dap_programFlash(const char *fpath,
return fsize;
}
//--------------------------------------------------------------------+
// ESP32 Target
//--------------------------------------------------------------------+
bool Adafruit_TestBed_Brains::esp32_begin(ESP32BootROMClass *bootrom,
uint32_t baudrate) {
esp32boot = bootrom;
LCD_printf("Syncing ESP32");
bool ret = esp32boot->begin(baudrate);
if (ret) {
LCD_printf("Synced OK");
} else {
LCD_printf_error("Sync failed!");
}
return ret;
}
void Adafruit_TestBed_Brains::esp32_end(void) {
esp32boot->endFlash(false);
esp32boot->end();
}
size_t Adafruit_TestBed_Brains::essp32_programFlash(const char *fpath,
uint32_t addr) {
if (!esp32boot) {
return 0;
}
enum { MAX_PAYLOAD_SIZE = 1024 };
uint8_t *buf = (uint8_t *)malloc(MAX_PAYLOAD_SIZE);
if (!buf) {
LCD_printf_error("No memory %u\n", MAX_PAYLOAD_SIZE);
return 0;
}
File32 fsrc = SD.open(fpath);
if (!fsrc) {
Serial.printf("SD: cannot open file: %s\r\n", fpath);
return 0;
}
uint32_t fsize = fsrc.fileSize();
uint32_t total_count = 0;
if (!esp32boot->beginFlash(addr, fsize, MAX_PAYLOAD_SIZE)) {
LCD_printf_error("beginFlash failed!");
} else {
LCD_printf("#Packets %u", fsize / MAX_PAYLOAD_SIZE);
MD5Builder md5;
md5.begin();
//------------- Flashing -------------//
while (fsrc.available()) {
memset(buf, 0xff, MAX_PAYLOAD_SIZE); // empty it out
uint32_t rd_count = fsrc.read(buf, MAX_PAYLOAD_SIZE);
setLED(HIGH);
Serial.printf("#");
if (!esp32boot->dataFlash(buf, MAX_PAYLOAD_SIZE)) {
LCD_printf_error("Failed to flash");
break;
}
setLED(LOW);
md5.add(buf, rd_count);
total_count += rd_count;
}
Serial.println();
//------------- MD5 verification -------------//
md5.calculate();
Serial.printf("md5 = %s\r\n", md5.toString().c_str());
uint8_t file_md5[16];
md5.getBytes(file_md5);
uint8_t esp_md5[16];
esp32boot->md5Flash(addr, fsize, esp_md5);
if (0 == memcmp(file_md5, esp_md5, 16)) {
LCD_printf("MD5 matched");
} else {
LCD_printf_error("MD5 mismatched!!");
Serial.printf("File: ");
for (size_t i = 0; i < 16; i++) {
Serial.printf("%02X ", file_md5[i]);
}
Serial.println();
Serial.printf("ESP : ");
for (size_t i = 0; i < 16; i++) {
Serial.printf("%02X ", esp_md5[i]);
}
Serial.println();
}
}
free(buf);
fsrc.close();
return total_count;
}
//--------------------------------------------------------------------+
// SD Card
//--------------------------------------------------------------------+
@ -511,27 +620,28 @@ void Adafruit_TestBed_Brains::lcd_write(uint8_t linenum, char linebuf[17]) {
_lcd_line = 1 - linenum;
}
#define _LCD_PRINTF(_line, _format) \
do { \
char linebuf[17]; \
va_list ap; \
va_start(ap, _format); \
vsnprintf(linebuf, sizeof(linebuf), _format, ap); \
va_end(ap); \
lcd_write(_line, linebuf); \
} while (0)
void Adafruit_TestBed_Brains::LCD_printf(uint8_t linenum, const char format[],
...) {
char linebuf[17];
va_list ap;
va_start(ap, format);
vsnprintf(linebuf, sizeof(linebuf), format, ap);
va_end(ap);
lcd_write(linenum, linebuf);
_LCD_PRINTF(linenum, format);
}
void Adafruit_TestBed_Brains::LCD_printf(const char format[], ...) {
char linebuf[17];
_LCD_PRINTF(_lcd_line, format);
}
va_list ap;
va_start(ap, format);
vsnprintf(linebuf, sizeof(linebuf), format, ap);
va_end(ap);
lcd_write(_lcd_line, linebuf);
void Adafruit_TestBed_Brains::LCD_printf_error(const char format[], ...) {
setColor(0xFF0000);
_LCD_PRINTF(_lcd_line, format);
}
void Adafruit_TestBed_Brains::LCD_info(const char *msg1, const char *msg2) {

View file

@ -10,6 +10,9 @@
#include "Adafruit_DAP.h"
#include "Adafruit_TinyUSB.h"
#include "ESP32BootROM.h"
#include "MD5Builder.h"
/**************************************************************************/
/*!
@brief A helper class for making RP2040 "Tester Brains"
@ -21,12 +24,17 @@ public:
void begin(void);
bool inited(void);
// LCD
// printf on specified line
//------------- LCD -------------//
// printf on specific line
void LCD_printf(uint8_t linenum, const char format[], ...);
// printf on next line
void LCD_printf(const char format[], ...);
// printf on error (RGB = RED)
void LCD_printf_error(const char format[], ...);
void LCD_error(const char *errmsg1, const char *errmsg2);
void LCD_info(const char *msg1, const char *msg2);
@ -46,7 +54,9 @@ public:
// Target
void targetReset(uint32_t reset_ms = 20);
//------------- RP2040 target -------------//
//--------------------------------------------------------------------+
// RP2040 Target
//--------------------------------------------------------------------+
// reset rp2040 target to Boot ROM
void rp2040_targetResetBootRom(int bootsel_pin = 28, uint32_t reset_ms = 20);
@ -54,7 +64,9 @@ public:
// return number of copied bytes (typically uf2 file size)
size_t rp2040_programUF2(const char *fpath);
//------------- SAMD21 target -------------//
//--------------------------------------------------------------------+
// DAP (samd21/51, nrf5x, stm32f4 etc..) Target
//--------------------------------------------------------------------+
bool dap_begin(Adafruit_DAP *dp);
bool dap_connect(void);
void dap_disconnect(void);
@ -68,7 +80,21 @@ public:
// return number of programmed bytes
size_t dap_programFlash(const char *fpath, uint32_t addr);
//------------- Public Variables -------------//
//--------------------------------------------------------------------+
// ESP32 Target
//--------------------------------------------------------------------+
bool esp32_begin(ESP32BootROMClass *bootrom, uint32_t baudrate);
void esp32_end(void);
// program esp32 target with file from SDCard
// return number of programmed bytes
size_t essp32_programFlash(const char *fpath, uint32_t addr);
//--------------------------------------------------------------------+
// Public Variables
//--------------------------------------------------------------------+
LiquidCrystal lcd = LiquidCrystal(7, 8, 9, 10, 11, 12);
SdFat SD;
SdSpiConfig SD_CONFIG = SdSpiConfig(17, SHARED_SPI, SD_SCK_MHZ(16));
@ -80,8 +106,12 @@ public:
FatVolume USBH_FS;
Adafruit_USBH_MSC_BlockDevice USBH_BlockDev;
// Dap
Adafruit_DAP *dap;
// ESP32 ROM
ESP32BootROMClass *esp32boot;
private:
bool _inited;
uint8_t _lcd_line;

373
src/ESP32BootROM.cpp Normal file
View file

@ -0,0 +1,373 @@
/*
ESP32BootROM - part of the Firmware Updater for the
Arduino MKR WiFi 1010, Arduino MKR Vidor 4000, and Arduino UNO WiFi Rev.2.
Copyright (c) 2018 Arduino SA. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "ESP32BootROM.h"
#define DEBUG 0
#if DEBUG
#define DBG_PRINTF(...) Serial.printf(__VA_ARGS__)
#else
#define DBG_PRINTF(...)
#endif
enum {
// Commands supported by ESP8266 ROM bootloader
ESP_FLASH_BEGIN = 0x02,
ESP_FLASH_DATA = 0x03,
ESP_FLASH_END = 0x04,
ESP_MEM_BEGIN = 0x05,
ESP_MEM_END = 0x06,
ESP_MEM_DATA = 0x07,
ESP_SYNC = 0x08,
ESP_WRITE_REG = 0x09,
ESP_READ_REG = 0x0A,
// Some comands supported by ESP32 and later chips ROM bootloader (or -8266 w/
// stub)
ESP_SPI_SET_PARAMS = 0x0B,
ESP_SPI_ATTACH = 0x0D,
ESP_READ_FLASH_SLOW = 0x0E, // ROM only, much slower than the stub flash read
ESP_CHANGE_BAUDRATE = 0x0F,
ESP_FLASH_DEFL_BEGIN = 0x10,
ESP_FLASH_DEFL_DATA = 0x11,
ESP_FLASH_DEFL_END = 0x12,
ESP_SPI_FLASH_MD5 = 0x13,
// Commands supported by ESP32-S2 and later chips ROM bootloader only
ESP_GET_SECURITY_INFO = 0x14,
// Some commands supported by stub only
ESP_ERASE_FLASH = 0xD0,
ESP_ERASE_REGION = 0xD1,
ESP_READ_FLASH = 0xD2,
ESP_RUN_USER_CODE = 0xD3,
// Flash encryption encrypted data command
ESP_FLASH_ENCRYPT_DATA = 0xD4,
// Response code(s) sent by ROM
ROM_INVALID_RECV_MSG = 0x05, // response if an invalid message is received
};
enum {
// Maximum block sized for RAM and Flash writes, respectively.
ESP_RAM_BLOCK = 0x1800,
FLASH_WRITE_SIZE = 0x400,
// Default baudrate. The ROM auto-bauds, so we can use more or less whatever
// we want.
ESP_ROM_BAUD = 115200,
// First byte of the application image
ESP_IMAGE_MAGIC = 0xE9,
// Initial state for the checksum routine
ESP_CHECKSUM_MAGIC = 0xEF,
// Flash sector size, minimum unit of erase.
// FLASH_SECTOR_SIZE = 0x1000,
UART_DATE_REG_ADDR = 0x60000078,
// This ROM address has a different value on each chip model
CHIP_DETECT_MAGIC_REG_ADDR = 0x40001000,
UART_CLKDIV_MASK = 0xFFFFF,
// Memory addresses
IROM_MAP_START = 0x40200000,
IROM_MAP_END = 0x40300000,
// The number of bytes in the UART response that signify command status
// STATUS_BYTES_LENGTH = 2,
// Bootloader flashing offset
// BOOTLOADER_FLASH_OFFSET = 0x0,
// ROM supports an encrypted flashing mode
// SUPPORTS_ENCRYPTED_FLASH = False,
// Response to ESP_SYNC might indicate that flasher stub is running
// instead of the ROM bootloader
// sync_stub_detected = False,
// Device PIDs
USB_JTAG_SERIAL_PID = 0x1001
};
ESP32BootROMClass::ESP32BootROMClass(HardwareSerial &serial, int gpio0Pin,
int resetnPin, bool supportsEncryptedFlash)
: _serial(&serial), _gpio0Pin(gpio0Pin), _resetnPin(resetnPin),
_supports_encrypted_flash(supportsEncryptedFlash) {}
int ESP32BootROMClass::begin(unsigned long baudrate) {
_serial->begin(115200);
pinMode(_gpio0Pin, OUTPUT);
pinMode(_resetnPin, OUTPUT);
digitalWrite(_gpio0Pin, LOW);
digitalWrite(_resetnPin, LOW);
delay(10);
digitalWrite(_resetnPin, HIGH);
delay(50);
// Wait for serial, needed if using with SerialHost (host cdc)
while (!_serial) {
delay(10);
}
delay(50); // additional delay for SerialHost connected
digitalWrite(_gpio0Pin, HIGH);
int synced = 0;
for (int retries = 0; !synced && (retries < 10); retries++) {
Serial.println("Trying to sync");
synced = sync();
}
if (!synced) {
return 0;
}
Serial.println("Synced!");
if (baudrate != 115200) {
if (!changeBaudrate(baudrate)) {
Serial.print("Failed to change baudrate");
return 0;
}
// _serial->end();
delay(100);
Serial.println("Updating local Serial baudrate");
_serial->begin(baudrate);
}
while (!spiAttach()) {
Serial.println("Failed to attach SPI");
delay(100);
}
return 1;
}
void ESP32BootROMClass::end() {
//_serial->end();
}
int ESP32BootROMClass::sync() {
const uint8_t data[] = {0x07, 0x07, 0x12, 0x20, 0x55, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55,
0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};
command(ESP_SYNC, data, sizeof(data));
int results[8];
for (int i = 0; i < 8; i++) {
results[i] = response(0x08, 100);
}
return (results[0] == 0);
}
int ESP32BootROMClass::changeBaudrate(unsigned long baudrate) {
const uint32_t data[2] = {baudrate, 0};
command(ESP_CHANGE_BAUDRATE, data, sizeof(data));
return (response(0x0f, 3000) == 0);
}
int ESP32BootROMClass::spiAttach() {
const uint8_t data[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
command(ESP_SPI_ATTACH, data, sizeof(data));
return (response(0x0d, 3000) == 0);
}
int ESP32BootROMClass::beginFlash(uint32_t offset, uint32_t size,
uint32_t chunkSize) {
const uint32_t data[] = {size, size / chunkSize, chunkSize, offset, 0};
uint16_t const len = _supports_encrypted_flash ? 20 : 16;
command(ESP_FLASH_BEGIN, data, len);
_flashSequenceNumber = 0;
_chunkSize = chunkSize;
return (response(0x02, 120000) == 0);
}
int ESP32BootROMClass::dataFlash(const void *data, uint32_t length) {
uint32_t cmdData[4 + (_chunkSize / 4)];
cmdData[0] = length;
cmdData[1] = _flashSequenceNumber++;
cmdData[2] = 0;
cmdData[3] = 0;
memcpy(&cmdData[4], data, length);
if (length < _chunkSize) {
memset(&cmdData[4 + (length / 4)], 0xff, _chunkSize - length);
}
command(ESP_FLASH_DATA, cmdData, sizeof(cmdData));
return (response(0x03, 3000) == 0);
}
int ESP32BootROMClass::endFlash(uint32_t reboot) {
const uint32_t data[1] = {reboot};
command(ESP_FLASH_END, data, sizeof(data));
return (response(0x04, 3000) == 0);
}
int ESP32BootROMClass::md5Flash(uint32_t offset, uint32_t size,
uint8_t *result) {
const uint32_t data[4] = {offset, size, 0, 0};
command(ESP_SPI_FLASH_MD5, data, sizeof(data));
uint8_t asciiResult[32];
if (response(0x13, 3000, asciiResult) != 0) {
return 0;
}
char temp[3] = {0, 0, 0};
for (int i = 0; i < 16; i++) {
temp[0] = asciiResult[i * 2];
temp[1] = asciiResult[i * 2 + 1];
result[i] = strtoul(temp, NULL, 16);
}
return 1;
}
void ESP32BootROMClass::command(int opcode, const void *data, uint16_t length) {
uint32_t checksum = 0;
if (opcode == ESP_FLASH_DATA) {
checksum = 0xef; // seed
for (uint16_t i = 16; i < length; i++) {
checksum ^= ((const uint8_t *)data)[i];
}
}
DBG_PRINTF("=> c0 00 %02x %02x %02x ", opcode, length & 0x00ff, length >> 8);
_serial->write(0xc0);
_serial->write((uint8_t)0x00); // direction
_serial->write(opcode);
_serial->write((uint8_t *)&length, sizeof(length));
writeEscapedBytes((uint8_t *)&checksum, sizeof(checksum));
writeEscapedBytes((uint8_t *)data, length);
_serial->write(0xc0);
_serial->flush();
DBG_PRINTF("c0\r\n");
}
int ESP32BootROMClass::response(int opcode, unsigned long timeout, void *body) {
uint8_t data[10 + 256];
uint16_t index = 0;
uint8_t responseLength = 4;
unsigned long start = millis();
while ((millis() - start) < timeout) {
if (_serial->available()) {
data[index] = _serial->read();
if (index == 3) {
responseLength = data[index];
}
index++;
}
if (index >= (uint16_t)(10 + responseLength)) {
break;
}
}
#if DEBUG
if (index) {
Serial.print("<= ");
for (int i = 0; i < index; i++) {
Serial.printf("%02x ", data[i]);
}
Serial.println();
}
#endif
if (index != (uint16_t)(10 + responseLength)) {
return -1;
}
if (data[0] != 0xc0 || data[1] != 0x01 || data[2] != opcode ||
data[responseLength + 5] != 0x00 || data[responseLength + 6] != 0x00 ||
data[responseLength + 9] != 0xc0) {
return -1;
}
if (body) {
memcpy(body, &data[9], responseLength - 4);
}
return data[responseLength + 5];
}
void ESP32BootROMClass::writeEscapedBytes(const uint8_t *data,
uint16_t length) {
uint16_t written = 0;
while (written < length) {
uint8_t b = data[written++];
if (b == 0xdb) {
_serial->write(0xdb);
_serial->write(0xdd);
DBG_PRINTF("db db ");
} else if (b == 0xc0) {
_serial->write(0xdb);
_serial->write(0xdc);
DBG_PRINTF("db dc ");
} else {
_serial->write(b);
DBG_PRINTF("%02x ", b);
}
}
}

59
src/ESP32BootROM.h Normal file
View file

@ -0,0 +1,59 @@
/*
ESP32BootROM - part of the Firmware Updater for the
Arduino MKR WiFi 1010, Arduino MKR Vidor 4000, and Arduino UNO WiFi Rev.2.
Copyright (c) 2018 Arduino SA. All rights reserved.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <Arduino.h>
class ESP32BootROMClass {
public:
// supportsEncryptedFlash must be set to
// - false for ESP32 (default)
// - true for later chips such as S2, S3, H2, C3
ESP32BootROMClass(HardwareSerial &hwSerial, int gpio0Pin, int resetnPin,
bool supportsEncryptedFlash = false);
int begin(unsigned long baudrate);
void end();
int beginFlash(uint32_t offset, uint32_t size, uint32_t chunkSize);
int dataFlash(const void *data, uint32_t length);
int endFlash(uint32_t reboot);
int md5Flash(uint32_t offset, uint32_t size, uint8_t *result);
private:
int sync();
int changeBaudrate(unsigned long baudrate);
int spiAttach();
void command(int opcode, const void *data, uint16_t length);
int response(int opcode, unsigned long timeout, void *body = NULL);
void writeEscapedBytes(const uint8_t *data, uint16_t length);
private:
HardwareSerial *_serial;
int _gpio0Pin;
int _resetnPin;
bool _supports_encrypted_flash;
uint32_t _flashSequenceNumber;
uint32_t _chunkSize;
};