Skip to content

Commit

Permalink
esp32/boards/ARDUINO_NANO_ESP32: Add support for Arduino Nano ESP32.
Browse files Browse the repository at this point in the history
Signed-off-by: Luca Burelli <l.burelli@arduino.cc>
  • Loading branch information
pillo79 authored and dpgeorge committed Jul 20, 2023
1 parent e078475 commit cc9735a
Show file tree
Hide file tree
Showing 11 changed files with 412 additions and 0 deletions.
20 changes: 20 additions & 0 deletions ports/esp32/boards/ARDUINO_NANO_ESP32/board.json
Original file line number Diff line number Diff line change
@@ -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"
}
124 changes: 124 additions & 0 deletions ports/esp32/boards/ARDUINO_NANO_ESP32/board_init.c
Original file line number Diff line number Diff line change
@@ -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();
}
8 changes: 8 additions & 0 deletions ports/esp32/boards/ARDUINO_NANO_ESP32/deploy.md
Original file line number Diff line number Diff line change
@@ -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
```
89 changes: 89 additions & 0 deletions ports/esp32/boards/ARDUINO_NANO_ESP32/double_tap.c
Original file line number Diff line number Diff line change
@@ -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;
}
37 changes: 37 additions & 0 deletions ports/esp32/boards/ARDUINO_NANO_ESP32/double_tap.h
Original file line number Diff line number Diff line change
@@ -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 */
9 changes: 9 additions & 0 deletions ports/esp32/boards/ARDUINO_NANO_ESP32/manifest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
include("$(PORT_DIR)/boards/manifest.py")

# Utils
require("time")
require("senml")
require("logging")

# Bluetooth
require("aioble")
23 changes: 23 additions & 0 deletions ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.cmake
Original file line number Diff line number Diff line change
@@ -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)
32 changes: 32 additions & 0 deletions ports/esp32/boards/ARDUINO_NANO_ESP32/mpconfigboard.h
Original file line number Diff line number Diff line change
@@ -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);
Original file line number Diff line number Diff line change
@@ -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,
38 changes: 38 additions & 0 deletions ports/esp32/boards/ARDUINO_NANO_ESP32/pins.csv
Original file line number Diff line number Diff line change
@@ -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
Loading

0 comments on commit cc9735a

Please sign in to comment.