diff --git a/ports/espressif/boards/elio_eliobot_s3/board.c b/ports/espressif/boards/elio_eliobot_s3/board.c new file mode 100644 index 00000000000..a83496500f1 --- /dev/null +++ b/ports/espressif/boards/elio_eliobot_s3/board.c @@ -0,0 +1,39 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2025 Romain Boutrois +// +// SPDX-License-Identifier: MIT + +#include "supervisor/board.h" +#include "mpconfigboard.h" +#include "shared-bindings/microcontroller/Pin.h" + +#include "lib/oofatfs/ff.h" +#include "extmod/vfs_fat.h" +#include "py/mpstate.h" +#include "supervisor/filesystem.h" + +void board_init(void) { + mp_import_stat_t stat_b = mp_import_stat("boot.py"); + if (stat_b != MP_IMPORT_STAT_FILE) { + fs_user_mount_t *fs_mount = filesystem_circuitpy(); + FATFS *fatfs = &fs_mount->fatfs; + FIL fs; + UINT char_written = 0; + // Default boot.py: CIRCUITPY is read-only over USB so children can't + // break the robot's files from the drive, while code sent over the + // serial REPL or BLE (Eliobot's web editor) can still write them. + const byte buffer[] = + "import storage\n" + "\n" + "# Eliobot default: the USB drive is read-only, and code sent by\n" + "# the Elioblocs editor can write files. To edit files from the drive\n" + "# instead, comment out the next line.\n" + "storage.remount(\"/\", False)\n"; + f_open(fatfs, &fs, "/boot.py", FA_WRITE | FA_CREATE_ALWAYS); + f_write(&fs, buffer, sizeof(buffer) - 1, &char_written); + f_close(&fs); + } +} + +// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here. diff --git a/ports/espressif/boards/elio_eliobot_s3/mpconfigboard.h b/ports/espressif/boards/elio_eliobot_s3/mpconfigboard.h new file mode 100644 index 00000000000..7919df48932 --- /dev/null +++ b/ports/espressif/boards/elio_eliobot_s3/mpconfigboard.h @@ -0,0 +1,27 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Romain Boutrois +// +// SPDX-License-Identifier: MIT + +#pragma once + +// Micropython setup + +#define MICROPY_HW_BOARD_NAME "Eliobot" +#define MICROPY_HW_MCU_NAME "ESP32S3" +#define CIRCUITPY_DRIVE_LABEL "ELIOBOT" + +// Current boards have one status NeoPixel; the next revision chains eight. +#define MICROPY_HW_NEOPIXEL (&pin_GPIO1) +#define MICROPY_HW_NEOPIXEL_COUNT (8) + +#define DEFAULT_I2C_BUS_SCL (&pin_GPIO9) +#define DEFAULT_I2C_BUS_SDA (&pin_GPIO8) + +#define DEFAULT_SPI_BUS_SCK (&pin_GPIO39) +#define DEFAULT_SPI_BUS_MOSI (&pin_GPIO40) +#define DEFAULT_SPI_BUS_MISO (&pin_GPIO41) + +#define DEFAULT_UART_BUS_RX (&pin_GPIO44) +#define DEFAULT_UART_BUS_TX (&pin_GPIO43) diff --git a/ports/espressif/boards/elio_eliobot_s3/mpconfigboard.mk b/ports/espressif/boards/elio_eliobot_s3/mpconfigboard.mk new file mode 100644 index 00000000000..405b8e872df --- /dev/null +++ b/ports/espressif/boards/elio_eliobot_s3/mpconfigboard.mk @@ -0,0 +1,27 @@ +USB_VID = 0x303a +USB_PID = 0x8384 +USB_PRODUCT = "Eliobot" +USB_MANUFACTURER = "ELIO" +USB_INTERFACE_NAME = "Eliobot" + +IDF_TARGET = esp32s3 + +CIRCUITPY_ESP_FLASH_MODE = qio +CIRCUITPY_ESP_FLASH_FREQ = 80m +CIRCUITPY_ESP_FLASH_SIZE = 4MB + +CIRCUITPY_ESP_PSRAM_SIZE = 2MB +CIRCUITPY_ESP_PSRAM_MODE = qio +CIRCUITPY_ESP_PSRAM_FREQ = 80m + +CIRCUITPY_ESPCAMERA = 0 +CIRCUITPY_BITMAPFILTER = 0 +CIRCUITPY_CODEOP = 0 +CIRCUITPY_PARALLELDISPLAYBUS = 0 + + +# Include these Python libraries in firmware. +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_HTTPServer +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_IRRemote +FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BLE diff --git a/ports/espressif/boards/elio_eliobot_s3/pins.c b/ports/espressif/boards/elio_eliobot_s3/pins.c new file mode 100644 index 00000000000..79f6e83aa61 --- /dev/null +++ b/ports/espressif/boards/elio_eliobot_s3/pins.c @@ -0,0 +1,134 @@ +// This file is part of the CircuitPython project: https://circuitpython.org +// +// SPDX-FileCopyrightText: Copyright (c) 2026 Romain Boutrois +// +// SPDX-License-Identifier: MIT + +#include "shared-bindings/board/__init__.h" + +static const mp_rom_map_elem_t board_module_globals_table[] = { + CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS + + { MP_ROM_QSTR(MP_QSTR_IO0), MP_ROM_PTR(&pin_GPIO0) }, + + // GPIO1 : NeoPixel (WS2812B) + { MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO1) }, + + // GPIO2 : Eyes LED connector (J3) + { MP_ROM_QSTR(MP_QSTR_IO2), MP_ROM_PTR(&pin_GPIO2) }, + { MP_ROM_QSTR(MP_QSTR_EYES), MP_ROM_PTR(&pin_GPIO2) }, + + // GPIO3 : Battery voltage sense (ADC, 1:2 divider) + { MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_BATTERY), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_VBAT_SENSE), MP_ROM_PTR(&pin_GPIO3) }, + { MP_ROM_QSTR(MP_QSTR_VOLTAGE_MONITOR), MP_ROM_PTR(&pin_GPIO3) }, + + // GPIO4-7 : IR obstacle sensor outputs (TSSP77038) + { MP_ROM_QSTR(MP_QSTR_IO4), MP_ROM_PTR(&pin_GPIO4) }, + { MP_ROM_QSTR(MP_QSTR_IR_OUT_1), MP_ROM_PTR(&pin_GPIO4) }, + + { MP_ROM_QSTR(MP_QSTR_IO5), MP_ROM_PTR(&pin_GPIO5) }, + { MP_ROM_QSTR(MP_QSTR_IR_OUT_2), MP_ROM_PTR(&pin_GPIO5) }, + + { MP_ROM_QSTR(MP_QSTR_IO6), MP_ROM_PTR(&pin_GPIO6) }, + { MP_ROM_QSTR(MP_QSTR_IR_OUT_3), MP_ROM_PTR(&pin_GPIO6) }, + + { MP_ROM_QSTR(MP_QSTR_IO7), MP_ROM_PTR(&pin_GPIO7) }, + { MP_ROM_QSTR(MP_QSTR_IR_OUT_4), MP_ROM_PTR(&pin_GPIO7) }, + + // GPIO8-9 : I2C bus + { MP_ROM_QSTR(MP_QSTR_IO8), MP_ROM_PTR(&pin_GPIO8) }, + { MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO8) }, + + { MP_ROM_QSTR(MP_QSTR_IO9), MP_ROM_PTR(&pin_GPIO9) }, + { MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO9) }, + + // GPIO10-14 : Line sensors (J1) + { MP_ROM_QSTR(MP_QSTR_IO10), MP_ROM_PTR(&pin_GPIO10) }, + { MP_ROM_QSTR(MP_QSTR_LINE_1), MP_ROM_PTR(&pin_GPIO10) }, + + { MP_ROM_QSTR(MP_QSTR_IO11), MP_ROM_PTR(&pin_GPIO11) }, + { MP_ROM_QSTR(MP_QSTR_LINE_2), MP_ROM_PTR(&pin_GPIO11) }, + + { MP_ROM_QSTR(MP_QSTR_IO12), MP_ROM_PTR(&pin_GPIO12) }, + { MP_ROM_QSTR(MP_QSTR_LINE_3), MP_ROM_PTR(&pin_GPIO12) }, + + { MP_ROM_QSTR(MP_QSTR_IO13), MP_ROM_PTR(&pin_GPIO13) }, + { MP_ROM_QSTR(MP_QSTR_LINE_4), MP_ROM_PTR(&pin_GPIO13) }, + + { MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) }, + { MP_ROM_QSTR(MP_QSTR_LINE_5), MP_ROM_PTR(&pin_GPIO14) }, + + // GPIO15-16 : free (no 32 kHz crystal fitted) + { MP_ROM_QSTR(MP_QSTR_IO15), MP_ROM_PTR(&pin_GPIO15) }, + { MP_ROM_QSTR(MP_QSTR_IO16), MP_ROM_PTR(&pin_GPIO16) }, + + // GPIO17 : Buzzer + { MP_ROM_QSTR(MP_QSTR_IO17), MP_ROM_PTR(&pin_GPIO17) }, + { MP_ROM_QSTR(MP_QSTR_BUZZER), MP_ROM_PTR(&pin_GPIO17) }, + + // GPIO18 : Line sensor LED enable + { MP_ROM_QSTR(MP_QSTR_IO18), MP_ROM_PTR(&pin_GPIO18) }, + { MP_ROM_QSTR(MP_QSTR_LINE_LED), MP_ROM_PTR(&pin_GPIO18) }, + + // GPIO19 : USB_D- + // GPIO20 : USB_D+ + + // GPIO21 : VBUS sense (5V present, 1:2 divider) + { MP_ROM_QSTR(MP_QSTR_VBUS), MP_ROM_PTR(&pin_GPIO21) }, + { MP_ROM_QSTR(MP_QSTR_VBUS_SENSE), MP_ROM_PTR(&pin_GPIO21) }, + + // GPIO26-32 : internal flash/PSRAM SPI — not available as GPIO + + // GPIO33 : Line sensor command output (J1) + { MP_ROM_QSTR(MP_QSTR_IO33), MP_ROM_PTR(&pin_GPIO33) }, + { MP_ROM_QSTR(MP_QSTR_LINE_CMD), MP_ROM_PTR(&pin_GPIO33) }, + + // GPIO34 : IR obstacle sensor 38kHz modulation output + { MP_ROM_QSTR(MP_QSTR_IO34), MP_ROM_PTR(&pin_GPIO34) }, + { MP_ROM_QSTR(MP_QSTR_IR_CMD), MP_ROM_PTR(&pin_GPIO34) }, + + // GPIO35-38 : Motor driver (DRV8833) + { MP_ROM_QSTR(MP_QSTR_IO35), MP_ROM_PTR(&pin_GPIO35) }, + { MP_ROM_QSTR(MP_QSTR_BIN2), MP_ROM_PTR(&pin_GPIO35) }, + + { MP_ROM_QSTR(MP_QSTR_IO36), MP_ROM_PTR(&pin_GPIO36) }, + { MP_ROM_QSTR(MP_QSTR_AIN1), MP_ROM_PTR(&pin_GPIO36) }, + + { MP_ROM_QSTR(MP_QSTR_IO37), MP_ROM_PTR(&pin_GPIO37) }, + { MP_ROM_QSTR(MP_QSTR_BIN1), MP_ROM_PTR(&pin_GPIO37) }, + + { MP_ROM_QSTR(MP_QSTR_IO38), MP_ROM_PTR(&pin_GPIO38) }, + { MP_ROM_QSTR(MP_QSTR_AIN2), MP_ROM_PTR(&pin_GPIO38) }, + + // GPIO39-41 : SPI header (J8) via JTAG pins MTCK/MTDO/MTDI + { MP_ROM_QSTR(MP_QSTR_IO39), MP_ROM_PTR(&pin_GPIO39) }, + { MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO39) }, + + { MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) }, + { MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO40) }, + + { MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) }, + { MP_ROM_QSTR(MP_QSTR_MISO), MP_ROM_PTR(&pin_GPIO41) }, + + { MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) }, + + // GPIO43-44 : UART (U0TXD / U0RXD) + { MP_ROM_QSTR(MP_QSTR_IO43), MP_ROM_PTR(&pin_GPIO43) }, + { MP_ROM_QSTR(MP_QSTR_TX), MP_ROM_PTR(&pin_GPIO43) }, + + { MP_ROM_QSTR(MP_QSTR_IO44), MP_ROM_PTR(&pin_GPIO44) }, + { MP_ROM_QSTR(MP_QSTR_RX), MP_ROM_PTR(&pin_GPIO44) }, + + // GPIO45 : SPI chip select (J8) + { MP_ROM_QSTR(MP_QSTR_IO45), MP_ROM_PTR(&pin_GPIO45) }, + { MP_ROM_QSTR(MP_QSTR_SS), MP_ROM_PTR(&pin_GPIO45) }, + + { MP_ROM_QSTR(MP_QSTR_IO46), MP_ROM_PTR(&pin_GPIO46) }, + + { MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) }, + { MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) }, + { MP_ROM_QSTR(MP_QSTR_UART), MP_ROM_PTR(&board_uart_obj) }, +}; +MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table); diff --git a/ports/espressif/boards/elio_eliobot_s3/sdkconfig b/ports/espressif/boards/elio_eliobot_s3/sdkconfig new file mode 100644 index 00000000000..253a3984eb1 --- /dev/null +++ b/ports/espressif/boards/elio_eliobot_s3/sdkconfig @@ -0,0 +1,16 @@ +# +# Espressif IoT Development Framework Configuration +# +# +# Component config +# +# +# LWIP +# +CONFIG_LWIP_LOCAL_HOSTNAME="ELIOBOT" + +# end of LWIP + +# end of Component config + +# end of Espressif IoT Development Framework Configuration