esp32/boards/ARDUINO_NANO_ESP32: Add support for Arduino Nano ESP32.

Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
This commit is contained in:
Luca Burelli 2023-07-04 16:08:23 +02:00 committed by Damien George
parent e0784750aa
commit cc9735ad6a
11 changed files with 412 additions and 0 deletions

View File

@ -0,0 +1,20 @@
{
"deploy": [
"../deploy_s3.md"
],
"docs": "",
"features": [
"BLE",
"WiFi",
"USB-C",
"RGB LED"
],
"images": [
"ABX00092_01.iso_1000x750.jpg"
],
"mcu": "esp32s3",
"product": "Arduino Nano ESP32",
"thumbnail": "",
"url": "https://store.arduino.cc/products/arduino-nano-esp32",
"vendor": "Arduino"
}

View File

@ -0,0 +1,124 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h>
#include "py/mphal.h"
#include <esp_system.h>
#include <esp_ota_ops.h>
#include <esp_partition.h>
#include "double_tap.h"
#include "usb.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"
#define LED_RED GPIO_NUM_46
#define LED_GREEN GPIO_NUM_0
#define LED_BLUE GPIO_NUM_45
#define DELAY_US 60000
static bool _recovery_marker_found; // double tap detected
static bool _recovery_active; // running from factory partition
static void rgb_pulse_delay() {
// initialize RGB signals from weak pinstraps
mp_hal_pin_output(LED_RED);
mp_hal_pin_output(LED_GREEN);
mp_hal_pin_output(LED_BLUE);
static const uint8_t SEQ[] = { 1, 3, 2, 6, 7, 5, 4, 5, 7, 6, 2, 3, 1 };
for (int idx = 0; idx < sizeof(SEQ); ++idx) {
int v = SEQ[idx & 7];
mp_hal_pin_write(LED_RED, !(v & 1));
mp_hal_pin_write(LED_GREEN, !(v & 2));
mp_hal_pin_write(LED_BLUE, !(v & 4));
// busy wait, we can't use task delay yet
mp_hal_delay_us_fast(DELAY_US);
}
// reset pins to digital HIGH before leaving
mp_hal_pin_write(LED_RED, 1);
mp_hal_pin_write(LED_GREEN, 1);
mp_hal_pin_write(LED_BLUE, 1);
}
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();
}
void NANO_ESP32_usb_callback_line_state_changed(int itf, void *event_in) {
extern void mp_usbd_line_state_cb(uint8_t itf, bool dtr, bool rts);
cdcacm_event_t *event = event_in;
mp_usbd_line_state_cb(itf, event->line_state_changed_data.dtr, event->line_state_changed_data.rts);
}
void NANO_ESP32_board_startup(void) {
boardctrl_startup();
// mark current partition as valid
const esp_partition_t *running = esp_ota_get_running_partition();
esp_ota_img_states_t ota_state;
if (esp_ota_get_state_partition(running, &ota_state) == ESP_OK) {
if (ota_state == ESP_OTA_IMG_PENDING_VERIFY) {
esp_ota_mark_app_valid_cancel_rollback();
}
}
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();
}

View File

@ -0,0 +1,8 @@
### Via dfu-util
This board can programmed via DFU bootloader, using e.g. [dfu-util](http://dfu-util.sourceforge.net/).
To enter the DFU bootloader, double tap the reset (blue) button, or you can use `machine.bootloader()` from the MicroPython REPL.
```bash
dfu-util -d 0x2341:0x0070 -R -D build-ARDUINO_NANO_ESP32/micropython.bin
```

View File

@ -0,0 +1,89 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <string.h>
#include "py/mphal.h"
#include <esp32s3/rom/cache.h>
// for get_extram_data_high()
#include <esp_psram.h>
#include <esp_private/esp_psram_extram.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];
// 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.
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();
}
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;
}

View File

@ -0,0 +1,37 @@
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Arduino SA
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_ESP32_DOUBLE_TAP_H
#define MICROPY_INCLUDED_ESP32_DOUBLE_TAP_H
#include <stdint.h>
void double_tap_init(void);
void double_tap_mark(void);
void double_tap_invalidate(void);
bool double_tap_check_match(void);
#endif /* MICROPY_INCLUDED_ESP32_DOUBLE_TAP_H */

View File

@ -0,0 +1,9 @@
include("$(PORT_DIR)/boards/manifest.py")
# Utils
require("time")
require("senml")
require("logging")
# Bluetooth
require("aioble")

View File

@ -0,0 +1,23 @@
if(NOT MICROPY_DIR)
get_filename_component(MICROPY_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../.. ABSOLUTE)
endif()
set(IDF_TARGET esp32s3)
set(SDKCONFIG_DEFAULTS
boards/sdkconfig.base
boards/sdkconfig.usb
boards/sdkconfig.ble
boards/sdkconfig.240mhz
boards/sdkconfig.spiram_sx
boards/sdkconfig.spiram_oct
${MICROPY_BOARD_DIR}/sdkconfig.board
)
set(MICROPY_SOURCE_BOARD
${MICROPY_BOARD_DIR}/board_init.c
${MICROPY_BOARD_DIR}/double_tap.c
${MICROPY_DIR}/shared/tinyusb/mp_cdc_common.c
)
set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py)

View File

@ -0,0 +1,32 @@
#define MICROPY_HW_BOARD_NAME "Arduino Nano ESP32"
#define MICROPY_HW_MCU_NAME "ESP32S3"
#define MICROPY_PY_MACHINE_DAC (0)
#define MICROPY_HW_I2C0_SCL (12)
#define MICROPY_HW_I2C0_SDA (11)
#define MICROPY_HW_I2C1_SCL (8)
#define MICROPY_HW_I2C1_SDA (9)
#define MICROPY_HW_SPI1_MOSI (38)
#define MICROPY_HW_SPI1_MISO (47)
#define MICROPY_HW_SPI1_SCK (48)
#define MICROPY_HW_SPI2_MOSI (10)
#define MICROPY_HW_SPI2_MISO (17)
#define MICROPY_HW_SPI2_SCK (18)
#define MICROPY_HW_ENABLE_USBDEV (1)
#define MICROPY_HW_USB_EXTERNAL_TINYUSB (1)
#define MICROPY_HW_USB_CDC_1200BPS_TOUCH (1)
#define MICROPY_SCHEDULER_STATIC_NODES (1)
#define MICROPY_HW_USB_CUSTOM_LINE_STATE_CB NANO_ESP32_usb_callback_line_state_changed
void NANO_ESP32_usb_callback_line_state_changed(int itf, void *event);
#define MICROPY_BOARD_STARTUP NANO_ESP32_board_startup
void NANO_ESP32_board_startup(void);
#define MICROPY_BOARD_ENTER_BOOTLOADER(nargs, args) NANO_ESP32_enter_bootloader()
void NANO_ESP32_enter_bootloader(void);

View File

@ -0,0 +1,10 @@
# Notes: the offset of the partition table itself is set in
# $IDF_PATH/components/partition_table/Kconfig.projbuild.
# Name, Type, SubType, Offset, Size, Flags
nvs, data, nvs, 0x9000, 0x5000,
otadata, data, ota, 0xe000, 0x2000,
app0, app, ota_0, 0x10000, 0x300000,
app1, app, ota_1, 0x310000, 0x300000,
ffat, data, fat, 0x610000, 0x960000,
factory, app, factory, 0xF70000, 0x80000,
coredump, data, coredump, 0xFF0000, 0x10000,
1 # Notes: the offset of the partition table itself is set in
2 # $IDF_PATH/components/partition_table/Kconfig.projbuild.
3 # Name, Type, SubType, Offset, Size, Flags
4 nvs, data, nvs, 0x9000, 0x5000,
5 otadata, data, ota, 0xe000, 0x2000,
6 app0, app, ota_0, 0x10000, 0x300000,
7 app1, app, ota_1, 0x310000, 0x300000,
8 ffat, data, fat, 0x610000, 0x960000,
9 factory, app, factory, 0xF70000, 0x80000,
10 coredump, data, coredump, 0xFF0000, 0x10000,

View File

@ -0,0 +1,38 @@
D0,GPIO44
D1,GPIO43
D2,GPIO5
D3,GPIO6
D4,GPIO7
D5,GPIO8
D6,GPIO9
D7,GPIO10
D8,GPIO17
D9,GPIO18
D10,GPIO21
D11,GPIO38
D12,GPIO47
D13,GPIO48
LED_RED,GPIO46
LED_GREEN,GPIO0
LED_BLUE,GPIO45
A0,GPIO1
A1,GPIO2
A2,GPIO3
A3,GPIO4
A4,GPIO11
A5,GPIO12
A6,GPIO13
A7,GPIO14
LED_BUILTIN,GPIO48
TX,GPIO43
RX,GPIO44
RTS,GPIO45
CTS,GPIO6
DTR,GPIO1
DSR,GPIO7
SS,GPIO21
MOSI,GPIO38
MISO,GPIO47
SCK,GPIO48
SDA,GPIO11
SCL,GPIO12
1 D0 GPIO44
2 D1 GPIO43
3 D2 GPIO5
4 D3 GPIO6
5 D4 GPIO7
6 D5 GPIO8
7 D6 GPIO9
8 D7 GPIO10
9 D8 GPIO17
10 D9 GPIO18
11 D10 GPIO21
12 D11 GPIO38
13 D12 GPIO47
14 D13 GPIO48
15 LED_RED GPIO46
16 LED_GREEN GPIO0
17 LED_BLUE GPIO45
18 A0 GPIO1
19 A1 GPIO2
20 A2 GPIO3
21 A3 GPIO4
22 A4 GPIO11
23 A5 GPIO12
24 A6 GPIO13
25 A7 GPIO14
26 LED_BUILTIN GPIO48
27 TX GPIO43
28 RX GPIO44
29 RTS GPIO45
30 CTS GPIO6
31 DTR GPIO1
32 DSR GPIO7
33 SS GPIO21
34 MOSI GPIO38
35 MISO GPIO47
36 SCK GPIO48
37 SDA GPIO11
38 SCL GPIO12

View File

@ -0,0 +1,22 @@
CONFIG_ESPTOOLPY_FLASHMODE_QIO=y
CONFIG_ESPTOOLPY_FLASHFREQ_80M=y
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
CONFIG_PARTITION_TABLE_CUSTOM=y
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="boards/ARDUINO_NANO_ESP32/partitions-app3M_fat9M_fact512k_16MiB.csv"
CONFIG_SPIRAM_TYPE_ESPPSRAM64=y
CONFIG_SPIRAM_SPEED_80M=y
CONFIG_SPIRAM_MEMTEST=
CONFIG_SPIRAM_IGNORE_NOTFOUND=
CONFIG_LWIP_LOCAL_HOSTNAME="nano-esp32"
CONFIG_TINYUSB_DESC_CUSTOM_VID=0x2341
CONFIG_TINYUSB_DESC_CUSTOM_PID=0x056B
CONFIG_TINYUSB_DESC_MANUFACTURER_STRING="Arduino"
CONFIG_TINYUSB_DESC_PRODUCT_STRING="Nano ESP32"
# compatibility with Espressif Arduino core
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y
CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP=y
CONFIG_ESP_ENABLE_COREDUMP_TO_FLASH=y