3.3.7
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
#include "Arduino.h"
|
||||
|
||||
#include <esp32-hal-tinyusb.h>
|
||||
#include <esp_system.h>
|
||||
|
||||
// defines an "Update" object accessed only by this translation unit
|
||||
// (also, the object requires MD5Builder internally)
|
||||
namespace {
|
||||
// ignore '{anonymous}::MD5Builder::...() defined but not used' warnings
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wunused-function"
|
||||
#include "../../libraries/Update/src/Updater.cpp"
|
||||
#include "../../cores/esp32/HEXBuilder.cpp"
|
||||
#include "../../cores/esp32/MD5Builder.cpp"
|
||||
#pragma GCC diagnostic pop
|
||||
} // namespace
|
||||
|
||||
#define ALT_COUNT 1
|
||||
|
||||
//--------------------------------------------------------------------+
|
||||
// DFU callbacks
|
||||
// Note: alt is used as the partition number, in order to support multiple partitions like FLASH, EEPROM, etc.
|
||||
//--------------------------------------------------------------------+
|
||||
|
||||
uint16_t load_dfu_ota_descriptor(uint8_t *dst, uint8_t *itf) {
|
||||
#define DFU_ATTRS (DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_CAN_UPLOAD | DFU_ATTR_MANIFESTATION_TOLERANT)
|
||||
|
||||
uint8_t str_index = tinyusb_add_string_descriptor("Arduino DFU");
|
||||
uint8_t descriptor[TUD_DFU_DESC_LEN(ALT_COUNT)] = {
|
||||
// Interface number, string index, attributes, detach timeout, transfer size */
|
||||
TUD_DFU_DESCRIPTOR(*itf, ALT_COUNT, str_index, DFU_ATTRS, 100, CFG_TUD_DFU_XFER_BUFSIZE),
|
||||
};
|
||||
*itf += 1;
|
||||
memcpy(dst, descriptor, TUD_DFU_DESC_LEN(ALT_COUNT));
|
||||
return TUD_DFU_DESC_LEN(ALT_COUNT);
|
||||
}
|
||||
|
||||
// Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST)
|
||||
// Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation.
|
||||
// During this period, USB host won't try to communicate with us.
|
||||
uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state) {
|
||||
if (state == DFU_DNBUSY) {
|
||||
// longest delay for Flash writing
|
||||
return 10;
|
||||
} else if (state == DFU_MANIFEST) {
|
||||
// time for esp32_ota_set_boot_partition to check final image
|
||||
return 100;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests
|
||||
// This callback could be returned before flashing op is complete (async).
|
||||
// Once finished flashing, application must call tud_dfu_finish_flashing()
|
||||
void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length) {
|
||||
if (!Update.isRunning()) {
|
||||
// this is the first data block, start update if possible
|
||||
if (!Update.begin()) {
|
||||
tud_dfu_finish_flashing(DFU_STATUS_ERR_TARGET);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// write a block of data to Flash
|
||||
// XXX: Update API is needlessly non-const
|
||||
size_t written = Update.write(const_cast<uint8_t *>(data), length);
|
||||
tud_dfu_finish_flashing((written == length) ? DFU_STATUS_OK : DFU_STATUS_ERR_WRITE);
|
||||
}
|
||||
|
||||
// Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest)
|
||||
// Application can do checksum, or actual flashing if buffered entire image previously.
|
||||
// Once finished flashing, application must call tud_dfu_finish_flashing()
|
||||
void tud_dfu_manifest_cb(uint8_t alt) {
|
||||
(void)alt;
|
||||
bool ok = Update.end(true);
|
||||
|
||||
// flashing op for manifest is complete
|
||||
tud_dfu_finish_flashing(ok ? DFU_STATUS_OK : DFU_STATUS_ERR_VERIFY);
|
||||
}
|
||||
|
||||
// Invoked when received DFU_UPLOAD request
|
||||
// Application must populate data with up to length bytes and
|
||||
// Return the number of written bytes
|
||||
uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t *data, uint16_t length) {
|
||||
(void)alt;
|
||||
(void)block_num;
|
||||
(void)data;
|
||||
(void)length;
|
||||
|
||||
// not implemented
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Invoked when the Host has terminated a download or upload transfer
|
||||
void tud_dfu_abort_cb(uint8_t alt) {
|
||||
(void)alt;
|
||||
// ignore
|
||||
}
|
||||
|
||||
// Invoked when a DFU_DETACH request is received
|
||||
void tud_dfu_detach_cb(void) {
|
||||
// done, reboot
|
||||
esp_restart();
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <esp_system.h>
|
||||
#include <esp32s3/rom/cache.h>
|
||||
#include <esp_heap_caps.h>
|
||||
|
||||
#include "double_tap.h"
|
||||
|
||||
#define NUM_TOKENS 3
|
||||
static const uint32_t MAGIC_TOKENS[NUM_TOKENS] = {
|
||||
0xf01681de,
|
||||
0xbd729b29,
|
||||
0xd359be7a,
|
||||
};
|
||||
|
||||
static void *magic_area;
|
||||
static uint32_t backup_area[NUM_TOKENS];
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
// Current IDF does not map external RAM to a fixed address.
|
||||
// The actual VMA depends on other enabled devices, so the precise
|
||||
// location must be discovered.
|
||||
#include <esp_psram.h>
|
||||
#include <esp_private/esp_psram_extram.h>
|
||||
static uintptr_t get_extram_data_high(void) {
|
||||
// get a pointer into SRAM area (only the address is useful)
|
||||
void *psram_ptr = heap_caps_malloc(16, MALLOC_CAP_SPIRAM);
|
||||
heap_caps_free(psram_ptr);
|
||||
|
||||
// keep moving backwards until leaving PSRAM area
|
||||
uintptr_t psram_base_addr = (uintptr_t)psram_ptr;
|
||||
psram_base_addr &= ~(CONFIG_MMU_PAGE_SIZE - 1); // align to start of page
|
||||
while (esp_psram_check_ptr_addr((void *)psram_base_addr)) {
|
||||
psram_base_addr -= CONFIG_MMU_PAGE_SIZE;
|
||||
}
|
||||
|
||||
// offset is one page from start of PSRAM
|
||||
return psram_base_addr + CONFIG_MMU_PAGE_SIZE + esp_psram_get_size();
|
||||
}
|
||||
#else
|
||||
#include <soc/soc.h>
|
||||
#define get_extram_data_high() ((uintptr_t)SOC_EXTRAM_DATA_HIGH)
|
||||
#endif
|
||||
|
||||
void double_tap_init(void) {
|
||||
// magic location block ends 0x20 bytes from end of PSRAM
|
||||
magic_area = (void *)(get_extram_data_high() - 0x20 - sizeof(MAGIC_TOKENS));
|
||||
}
|
||||
|
||||
void double_tap_mark() {
|
||||
memcpy(backup_area, magic_area, sizeof(MAGIC_TOKENS));
|
||||
memcpy(magic_area, MAGIC_TOKENS, sizeof(MAGIC_TOKENS));
|
||||
Cache_WriteBack_Addr((uintptr_t)magic_area, sizeof(MAGIC_TOKENS));
|
||||
}
|
||||
|
||||
void double_tap_invalidate() {
|
||||
if (memcmp(backup_area, MAGIC_TOKENS, sizeof(MAGIC_TOKENS))) {
|
||||
// different contents: restore backup
|
||||
memcpy(magic_area, backup_area, sizeof(MAGIC_TOKENS));
|
||||
} else {
|
||||
// clear memory
|
||||
memset(magic_area, 0, sizeof(MAGIC_TOKENS));
|
||||
}
|
||||
Cache_WriteBack_Addr((uintptr_t)magic_area, sizeof(MAGIC_TOKENS));
|
||||
}
|
||||
|
||||
bool double_tap_check_match() {
|
||||
return (memcmp(magic_area, MAGIC_TOKENS, sizeof(MAGIC_TOKENS)) == 0);
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#ifndef __DOUBLE_TAP_H__
|
||||
#define __DOUBLE_TAP_H__
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
void double_tap_init(void);
|
||||
void double_tap_mark(void);
|
||||
void double_tap_invalidate(void);
|
||||
bool double_tap_check_match(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __DOUBLE_TAP_H__ */
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
# Arduino Nano Nora Recovery Sketch
|
||||
|
||||
This sketch implements the DFU recovery mode logic, called by all sketches
|
||||
when a double tap on the RESET button is detected. It should not be uploaded
|
||||
as any other sketch; instead, this should be compiled and then flashed in
|
||||
the module's `factory` partition.
|
||||
|
||||
## Compilation
|
||||
|
||||
The binary can be compiled with the Arduino 2.x IDE or CLI using the
|
||||
`nano_nora` variant. In particular, using the CLI the resulting binary
|
||||
can be exported to the `build` directory with the `-e` switch to
|
||||
`arduino-cli compile`.
|
||||
|
||||
## Automatic installation
|
||||
|
||||
By replacing the binary in the current folder, automatic installation
|
||||
can be performed by running the "Upload with Programmer" action on any
|
||||
sketch in the Arduino 2.x IDE or CLI. In particular, using the CLI the
|
||||
binary can be installed via the command:
|
||||
|
||||
```
|
||||
arduino-cli compile -u --programmer esptool
|
||||
```
|
||||
|
||||
## Manual installation
|
||||
|
||||
Once compiled, the binary can also be installed on a board using `esptool.py`
|
||||
with the following command:
|
||||
|
||||
```
|
||||
esptool.py --chip esp32s3 --port "/dev/ttyACM0" --baud 921600 --before default_reset --after hard_reset write_flash -z --flash_mode dio --flash_freq 80m --flash_size 16MB 0xF70000 "nora_recovery.ino.bin"
|
||||
```
|
||||
|
||||
where:
|
||||
- `esptool.py` is located in your core's install path under `tools/esptool_py`;
|
||||
- `/dev/ttyACM0` is the serial port exposed by the board to be used;
|
||||
- `0xF70000` is the factory partition address (make sure it matches the
|
||||
offset in the variant's `{build.partitions}` file);
|
||||
- `nora_recovery.ino.bin` is the compiled sketch image.
|
||||
|
||||
Due to a BSP issue, the first call to `esptool.py` will enter the hardware
|
||||
bootloader for programming, but fail with an "Input/output error". This is
|
||||
a known issue; calling the program again with the same arguments will now
|
||||
work correctly.
|
||||
|
||||
Once flashing is complete, a power cycle (or RESET button tap) is required
|
||||
to leave the `esptool.py` flashing mode and load user sketches.
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "USB.h"
|
||||
|
||||
#define USB_TIMEOUT_MS 15000
|
||||
#define POLL_DELAY_MS 60
|
||||
#define FADESTEP 8
|
||||
|
||||
void pulse_led() {
|
||||
static uint32_t pulse_width = 0;
|
||||
static uint8_t dir = 0;
|
||||
|
||||
if (dir) {
|
||||
pulse_width -= FADESTEP;
|
||||
if (pulse_width < FADESTEP) {
|
||||
dir = 0U;
|
||||
pulse_width = FADESTEP;
|
||||
}
|
||||
} else {
|
||||
pulse_width += FADESTEP;
|
||||
if (pulse_width > 255) {
|
||||
dir = 1U;
|
||||
pulse_width = 255;
|
||||
}
|
||||
}
|
||||
|
||||
analogWrite(LED_GREEN, pulse_width);
|
||||
}
|
||||
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_partition.h>
|
||||
#include <esp_flash_partitions.h>
|
||||
#include <esp_image_format.h>
|
||||
const esp_partition_t *find_previous_firmware() {
|
||||
extern bool _recovery_active;
|
||||
if (!_recovery_active) {
|
||||
// user flashed this recovery sketch to an OTA partition
|
||||
// stay here and wait for a proper firmware
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// booting from factory partition, look for a valid OTA image
|
||||
esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, NULL);
|
||||
for (; it != NULL; it = esp_partition_next(it)) {
|
||||
const esp_partition_t *part = esp_partition_get(it);
|
||||
if (part->subtype != ESP_PARTITION_SUBTYPE_APP_FACTORY) {
|
||||
esp_partition_pos_t candidate = {part->address, part->size};
|
||||
esp_image_metadata_t meta;
|
||||
if (esp_image_verify(ESP_IMAGE_VERIFY_SILENT, &candidate, &meta) == ESP_OK) {
|
||||
// found, use it
|
||||
return part;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const esp_partition_t *user_part = NULL;
|
||||
|
||||
void setup() {
|
||||
user_part = find_previous_firmware();
|
||||
if (user_part) {
|
||||
esp_ota_set_boot_partition(user_part);
|
||||
}
|
||||
|
||||
extern bool _recovery_marker_found;
|
||||
if (!_recovery_marker_found && user_part) {
|
||||
// recovery marker not found, probable cold start
|
||||
// try starting previous firmware immediately
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
// recovery marker found, or nothing else to load
|
||||
printf("Recovery firmware started, waiting for USB\r\n");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
static int elapsed_ms = 0;
|
||||
|
||||
pulse_led();
|
||||
delay(POLL_DELAY_MS);
|
||||
if (USB) {
|
||||
// wait indefinitely for DFU to complete
|
||||
elapsed_ms = 0;
|
||||
} else {
|
||||
// wait for USB connection
|
||||
elapsed_ms += POLL_DELAY_MS;
|
||||
}
|
||||
|
||||
if (elapsed_ms > USB_TIMEOUT_MS) {
|
||||
elapsed_ms = 0;
|
||||
// timed out, try loading previous firmware
|
||||
if (user_part) {
|
||||
// there was a valid FW image, load it
|
||||
analogWrite(LED_GREEN, 255);
|
||||
printf("Leaving recovery firmware\r\n");
|
||||
delay(200);
|
||||
esp_restart(); // does not return
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -0,0 +1,73 @@
|
||||
#if defined(BOARD_HAS_PIN_REMAP) && !defined(ARDUINO_CORE_BUILD)
|
||||
// -DARDUINO_CORE_BUILD must be set for core files only, to avoid extra
|
||||
// remapping steps that would create all sorts of issues in the core.
|
||||
// Removing -DBOARD_HAS_PIN_REMAP at least does correctly restore the
|
||||
// use of GPIO numbers in the API.
|
||||
#error This build system is not supported. Please rebuild without BOARD_HAS_PIN_REMAP.
|
||||
#endif
|
||||
|
||||
#if !defined(BOARD_HAS_PIN_REMAP)
|
||||
// This board uses pin mapping but the build system has disabled it
|
||||
#warning The build system forces the Arduino API to use GPIO numbers on a board that has custom pin mapping.
|
||||
#elif defined(BOARD_USES_HW_GPIO_NUMBERS)
|
||||
// The user has chosen to disable pin mapping.
|
||||
#warning The Arduino API will use GPIO numbers for this build.
|
||||
#endif
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
// NOTE: This must match with the remapped pin sequence in pins_arduino.h
|
||||
static const int8_t TO_GPIO_NUMBER[] = {
|
||||
44, // [ 0] D0, RX
|
||||
43, // [ 1] D1, TX
|
||||
5, // [ 2] D2
|
||||
6, // [ 3] D3, CTS
|
||||
7, // [ 4] D4, DSR
|
||||
8, // [ 5] D5
|
||||
9, // [ 6] D6
|
||||
10, // [ 7] D7
|
||||
17, // [ 8] D8
|
||||
18, // [ 9] D9
|
||||
21, // [10] D10, SS
|
||||
38, // [11] D11, MOSI
|
||||
47, // [12] D12, MISO
|
||||
48, // [13] D13, SCK, LED_BUILTIN
|
||||
46, // [14] LED_RED
|
||||
0, // [15] LED_GREEN
|
||||
45, // [16] LED_BLUE, RTS
|
||||
1, // [17] A0, DTR
|
||||
2, // [18] A1
|
||||
3, // [19] A2
|
||||
4, // [20] A3
|
||||
11, // [21] A4, SDA
|
||||
12, // [22] A5, SCL
|
||||
13, // [23] A6
|
||||
14, // [24] A7
|
||||
};
|
||||
|
||||
#if defined(BOARD_HAS_PIN_REMAP) && !defined(BOARD_USES_HW_GPIO_NUMBERS)
|
||||
|
||||
int8_t digitalPinToGPIONumber(int8_t digitalPin) {
|
||||
if ((digitalPin < 0) || (digitalPin >= NUM_DIGITAL_PINS)) {
|
||||
return -1;
|
||||
}
|
||||
return TO_GPIO_NUMBER[digitalPin];
|
||||
}
|
||||
|
||||
int8_t gpioNumberToDigitalPin(int8_t gpioNumber) {
|
||||
if (gpioNumber < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// slow linear table lookup
|
||||
for (int8_t digitalPin = 0; digitalPin < NUM_DIGITAL_PINS; ++digitalPin) {
|
||||
if (TO_GPIO_NUMBER[digitalPin] == gpioNumber) {
|
||||
return digitalPin;
|
||||
}
|
||||
}
|
||||
|
||||
// not found
|
||||
return -1;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,114 @@
|
||||
#ifndef Pins_Arduino_h
|
||||
#define Pins_Arduino_h
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define USB_VID 0x2341
|
||||
#define USB_PID 0x0070
|
||||
|
||||
#ifndef __cplusplus
|
||||
#define constexpr const
|
||||
#endif
|
||||
|
||||
// primary pin names
|
||||
|
||||
#if defined(BOARD_HAS_PIN_REMAP) && !defined(BOARD_USES_HW_GPIO_NUMBERS)
|
||||
|
||||
// Arduino style definitions (API uses Dx)
|
||||
|
||||
static constexpr uint8_t D0 = 0; // also RX
|
||||
static constexpr uint8_t D1 = 1; // also TX
|
||||
static constexpr uint8_t D2 = 2;
|
||||
static constexpr uint8_t D3 = 3; // also CTS
|
||||
static constexpr uint8_t D4 = 4; // also DSR
|
||||
static constexpr uint8_t D5 = 5;
|
||||
static constexpr uint8_t D6 = 6;
|
||||
static constexpr uint8_t D7 = 7;
|
||||
static constexpr uint8_t D8 = 8;
|
||||
static constexpr uint8_t D9 = 9;
|
||||
static constexpr uint8_t D10 = 10; // also SS
|
||||
static constexpr uint8_t D11 = 11; // also MOSI
|
||||
static constexpr uint8_t D12 = 12; // also MISO
|
||||
static constexpr uint8_t D13 = 13; // also SCK, LED_BUILTIN
|
||||
static constexpr uint8_t LED_RED = 14;
|
||||
static constexpr uint8_t LED_GREEN = 15;
|
||||
static constexpr uint8_t LED_BLUE = 16; // also RTS
|
||||
|
||||
static constexpr uint8_t A0 = 17; // also DTR
|
||||
static constexpr uint8_t A1 = 18;
|
||||
static constexpr uint8_t A2 = 19;
|
||||
static constexpr uint8_t A3 = 20;
|
||||
static constexpr uint8_t A4 = 21; // also SDA
|
||||
static constexpr uint8_t A5 = 22; // also SCL
|
||||
static constexpr uint8_t A6 = 23;
|
||||
static constexpr uint8_t A7 = 24;
|
||||
|
||||
#else
|
||||
|
||||
// ESP32-style definitions (API uses GPIOx)
|
||||
|
||||
static constexpr uint8_t D0 = 44; // also RX
|
||||
static constexpr uint8_t D1 = 43; // also TX
|
||||
static constexpr uint8_t D2 = 5;
|
||||
static constexpr uint8_t D3 = 6; // also CTS
|
||||
static constexpr uint8_t D4 = 7; // also DSR
|
||||
static constexpr uint8_t D5 = 8;
|
||||
static constexpr uint8_t D6 = 9;
|
||||
static constexpr uint8_t D7 = 10;
|
||||
static constexpr uint8_t D8 = 17;
|
||||
static constexpr uint8_t D9 = 18;
|
||||
static constexpr uint8_t D10 = 21; // also SS
|
||||
static constexpr uint8_t D11 = 38; // also MOSI
|
||||
static constexpr uint8_t D12 = 47; // also MISO
|
||||
static constexpr uint8_t D13 = 48; // also SCK, LED_BUILTIN
|
||||
static constexpr uint8_t LED_RED = 46;
|
||||
static constexpr uint8_t LED_GREEN = 0;
|
||||
static constexpr uint8_t LED_BLUE = 45; // also RTS
|
||||
|
||||
static constexpr uint8_t A0 = 1; // also DTR
|
||||
static constexpr uint8_t A1 = 2;
|
||||
static constexpr uint8_t A2 = 3;
|
||||
static constexpr uint8_t A3 = 4;
|
||||
static constexpr uint8_t A4 = 11; // also SDA
|
||||
static constexpr uint8_t A5 = 12; // also SCL
|
||||
static constexpr uint8_t A6 = 13;
|
||||
static constexpr uint8_t A7 = 14;
|
||||
|
||||
#endif
|
||||
|
||||
// Aliases
|
||||
|
||||
static constexpr uint8_t LEDR = LED_RED;
|
||||
static constexpr uint8_t LEDG = LED_GREEN;
|
||||
static constexpr uint8_t LEDB = LED_BLUE;
|
||||
|
||||
// alternate pin functions
|
||||
|
||||
static constexpr uint8_t LED_BUILTIN = D13;
|
||||
|
||||
static constexpr uint8_t TX = D1;
|
||||
static constexpr uint8_t RX = D0;
|
||||
static constexpr uint8_t RTS = LED_BLUE;
|
||||
static constexpr uint8_t CTS = D3;
|
||||
static constexpr uint8_t DTR = A0;
|
||||
static constexpr uint8_t DSR = D4;
|
||||
|
||||
static constexpr uint8_t SS = D10;
|
||||
static constexpr uint8_t MOSI = D11;
|
||||
static constexpr uint8_t MISO = D12;
|
||||
static constexpr uint8_t SCK = D13;
|
||||
|
||||
static constexpr uint8_t SDA = A4;
|
||||
static constexpr uint8_t SCL = A5;
|
||||
|
||||
#define PIN_I2S_SCK D7
|
||||
#define PIN_I2S_FS D8
|
||||
#define PIN_I2S_SD D9
|
||||
#define PIN_I2S_SD_OUT D9 // same as bidir
|
||||
#define PIN_I2S_SD_IN D10
|
||||
|
||||
#ifndef __cplusplus
|
||||
#undef constexpr
|
||||
#endif
|
||||
|
||||
#endif /* Pins_Arduino_h */
|
||||
@@ -0,0 +1,101 @@
|
||||
// Enable pin remapping in this file, so pin constants are meaningful
|
||||
#undef ARDUINO_CORE_BUILD
|
||||
|
||||
#include "Arduino.h"
|
||||
|
||||
#include "double_tap.h"
|
||||
|
||||
#include <esp_system.h>
|
||||
#include <esp_ota_ops.h>
|
||||
#include <esp_partition.h>
|
||||
|
||||
extern "C" {
|
||||
void initVariant() {
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
// global, accessible from recovery sketch
|
||||
bool _recovery_marker_found; // double tap detected
|
||||
bool _recovery_active; // running from factory partition
|
||||
|
||||
#define DELAY_US 10000
|
||||
#define FADESTEP 8
|
||||
static void rgb_pulse_delay(void) {
|
||||
// Bv R^ G x
|
||||
int widths[4] = {192, 64, 0, 0};
|
||||
int dec_led = 0;
|
||||
|
||||
// initialize RGB signals from weak pinstraps
|
||||
pinMode(LED_RED, OUTPUT);
|
||||
pinMode(LED_GREEN, OUTPUT);
|
||||
pinMode(LED_BLUE, OUTPUT);
|
||||
while (dec_led < 3) {
|
||||
widths[dec_led] -= FADESTEP;
|
||||
widths[dec_led + 1] += FADESTEP;
|
||||
if (widths[dec_led] <= 0) {
|
||||
widths[dec_led] = 0;
|
||||
dec_led = dec_led + 1;
|
||||
widths[dec_led] = 255;
|
||||
}
|
||||
|
||||
analogWrite(LED_RED, 255 - widths[1]);
|
||||
analogWrite(LED_GREEN, 255 - widths[2]);
|
||||
analogWrite(LED_BLUE, 255 - widths[0]);
|
||||
delayMicroseconds(DELAY_US);
|
||||
}
|
||||
|
||||
// reset pins to digital HIGH before leaving
|
||||
digitalWrite(LED_RED, HIGH);
|
||||
digitalWrite(LED_GREEN, HIGH);
|
||||
digitalWrite(LED_BLUE, HIGH);
|
||||
}
|
||||
|
||||
static void NANO_ESP32_enter_bootloader(void) {
|
||||
if (!_recovery_active) {
|
||||
// check for valid partition scheme
|
||||
const esp_partition_t *ota_part = esp_ota_get_next_update_partition(NULL);
|
||||
const esp_partition_t *fact_part = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL);
|
||||
if (ota_part && fact_part) {
|
||||
// set tokens so the recovery FW will find them
|
||||
double_tap_mark();
|
||||
// invalidate other OTA image
|
||||
esp_partition_erase_range(ota_part, 0, 4096);
|
||||
// activate factory partition
|
||||
esp_ota_set_boot_partition(fact_part);
|
||||
}
|
||||
}
|
||||
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
static void boot_double_tap_logic() {
|
||||
const esp_partition_t *part = esp_ota_get_running_partition();
|
||||
_recovery_active = (part->subtype == ESP_PARTITION_SUBTYPE_APP_FACTORY);
|
||||
|
||||
double_tap_init();
|
||||
|
||||
_recovery_marker_found = double_tap_check_match();
|
||||
if (_recovery_marker_found && !_recovery_active) {
|
||||
// double tap detected in user application, reboot to factory
|
||||
NANO_ESP32_enter_bootloader();
|
||||
}
|
||||
|
||||
// delay with mark set then proceed
|
||||
// - for normal startup, to detect first double tap
|
||||
// - in recovery mode, to ignore several short presses
|
||||
double_tap_mark();
|
||||
rgb_pulse_delay();
|
||||
double_tap_invalidate();
|
||||
}
|
||||
|
||||
namespace {
|
||||
class DoubleTap {
|
||||
public:
|
||||
DoubleTap() {
|
||||
boot_double_tap_logic();
|
||||
}
|
||||
};
|
||||
|
||||
DoubleTap dt __attribute__((init_priority(101)));
|
||||
} // namespace
|
||||
Reference in New Issue
Block a user