From cc9735ad6a5c3e198eabb406ee8d8ec687d5ec59 Mon Sep 17 00:00:00 2001 From: Luca Burelli Date: Tue, 4 Jul 2023 16:08:23 +0200 Subject: [PATCH] esp32/boards/ARDUINO_NANO_ESP32: Add support for Arduino Nano ESP32. Signed-off-by: Luca Burelli --- .../boards/ARDUINO_NANO_ESP32/board.json | 20 +++ .../boards/ARDUINO_NANO_ESP32/board_init.c | 124 ++++++++++++++++++ .../esp32/boards/ARDUINO_NANO_ESP32/deploy.md | 8 ++ .../boards/ARDUINO_NANO_ESP32/double_tap.c | 89 +++++++++++++ .../boards/ARDUINO_NANO_ESP32/double_tap.h | 37 ++++++ .../boards/ARDUINO_NANO_ESP32/manifest.py | 9 ++ .../ARDUINO_NANO_ESP32/mpconfigboard.cmake | 23 ++++ .../boards/ARDUINO_NANO_ESP32/mpconfigboard.h | 32 +++++ .../partitions-app3M_fat9M_fact512k_16MiB.csv | 10 ++ .../esp32/boards/ARDUINO_NANO_ESP32/pins.csv | 38 ++++++ .../boards/ARDUINO_NANO_ESP32/sdkconfig.board | 22 ++++ 11 files changed, 412 insertions(+) create mode 100644 ports/esp32/boards/ARDUINO_NANO_ESP32/board.json create mode 100644 ports/esp32/boards/ARDUINO_NANO_ESP32/board_init.c create mode 100644 ports/esp32/boards/ARDUINO_NANO_ESP32/deploy.md create mode 100644 ports/esp32/boards/ARDUINO_NANO_ESP32/double_tap.c create mode 100644 ports/esp32/boards/ARDUINO_NANO_ESP32/double_tap.h create mode 100644 ports/esp32/boards/ARDUINO_NANO_ESP32/manifest.py create mode 100644 ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake create mode 100644 ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h create mode 100644 ports/esp32/boards/ARDUINO_NANO_ESP32/partitions-app3M_fat9M_fact512k_16MiB.csv create mode 100644 ports/esp32/boards/ARDUINO_NANO_ESP32/pins.csv create mode 100644 ports/esp32/boards/ARDUINO_NANO_ESP32/sdkconfig.board diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json new file mode 100644 index 0000000000..031ee17415 --- /dev/null +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json @@ -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" +} diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/board_init.c b/ports/esp32/boards/ARDUINO_NANO_ESP32/board_init.c new file mode 100644 index 0000000000..ab03139d90 --- /dev/null +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/board_init.c @@ -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 +#include "py/mphal.h" + +#include +#include +#include + +#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(); +} diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/deploy.md b/ports/esp32/boards/ARDUINO_NANO_ESP32/deploy.md new file mode 100644 index 0000000000..b600a55b64 --- /dev/null +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/deploy.md @@ -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 +``` diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/double_tap.c b/ports/esp32/boards/ARDUINO_NANO_ESP32/double_tap.c new file mode 100644 index 0000000000..8f50a32722 --- /dev/null +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/double_tap.c @@ -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 +#include "py/mphal.h" + +#include + +// for get_extram_data_high() +#include +#include + +#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; +} diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/double_tap.h b/ports/esp32/boards/ARDUINO_NANO_ESP32/double_tap.h new file mode 100644 index 0000000000..9aa738f2d9 --- /dev/null +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/double_tap.h @@ -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 + +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 */ diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/manifest.py b/ports/esp32/boards/ARDUINO_NANO_ESP32/manifest.py new file mode 100644 index 0000000000..e7fc7fe161 --- /dev/null +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/manifest.py @@ -0,0 +1,9 @@ +include("$(PORT_DIR)/boards/manifest.py") + +# Utils +require("time") +require("senml") +require("logging") + +# Bluetooth +require("aioble") diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake new file mode 100644 index 0000000000..7ae4b38185 --- /dev/null +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake @@ -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) diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h new file mode 100644 index 0000000000..3e98bb99d7 --- /dev/null +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h @@ -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); diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/partitions-app3M_fat9M_fact512k_16MiB.csv b/ports/esp32/boards/ARDUINO_NANO_ESP32/partitions-app3M_fat9M_fact512k_16MiB.csv new file mode 100644 index 0000000000..13b4414b1d --- /dev/null +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/partitions-app3M_fat9M_fact512k_16MiB.csv @@ -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, diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/pins.csv b/ports/esp32/boards/ARDUINO_NANO_ESP32/pins.csv new file mode 100644 index 0000000000..b891608d5d --- /dev/null +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/pins.csv @@ -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 diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/sdkconfig.board b/ports/esp32/boards/ARDUINO_NANO_ESP32/sdkconfig.board new file mode 100644 index 0000000000..4d175f7ed7 --- /dev/null +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/sdkconfig.board @@ -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