Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions ports/espressif/boards/dfrobot_unihiker_k10/README.MD

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for this. Please add a concise summary to circuitpython-org after this is merged instead.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# CircuitPython Port: DFRobot UNIHIKER K10 (ESP32-S3)

**Status**: ✅ Functional — All major hardware features working:
- PMIC (TCA9555 I2C expander: backlight, camera reset, buttons, audio amp)
- Display ILI9341 (240×320, oriented correctly via MADCTL=0x88)
- NeoPixel (single RGB LED)
- Camera GC2145 (2MP, QVGA via espcamera)
- Audio I2S (MAX98357 speaker amp, 440Hz tone)
- Buttons A/B (PMIC-controlled, NOT GPIOs)
- Sensors: SC7A20H (accelerometer), LTR303ALS (light), AHT20 (temp/humidity)
- PSRAM 8MB OPI @ 80MHz ✅
- Flash 16MB DIO @ 80MHz ✅
- USB Serial/JTAG console ✅
- Frozen Python libraries (27 modules pre-loaded)
106 changes: 106 additions & 0 deletions ports/espressif/boards/dfrobot_unihiker_k10/board.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// This file is part of the CircuitPython project: https://circuitpython.org
// SPDX-FileCopyrightText: Copyright (c) 2026 Djair Guilherme
// SPDX-License-Identifier: MIT

#include "supervisor/board.h"
#include "mpconfigboard.h"
#include "shared-bindings/busio/SPI.h"
#include "shared-bindings/fourwire/FourWire.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-module/displayio/__init__.h"
#include "shared-module/displayio/mipi_constants.h"
#include "shared-bindings/board/__init__.h"

#define DELAY 0x80

// ILI9341 init sequence (standard)
uint8_t display_init_sequence[] = {
// sw reset
0x01, 0 | DELAY, 150,
// sleep out
0x11, 0 | DELAY, 255,
// normal display mode on
0x13, 0,
// pixel format: 16-bit RGB565
0x3A, 1, 0x55,
// frame rate control
0xB1, 2, 0x00, 0x1B,
// display function control
0xB6, 4, 0x0A, 0x82, 0x27, 0x00,
// power control 1
0xC0, 1, 0x23,
// power control 2
0xC1, 1, 0x10,
// VCOM control 1
0xC5, 2, 0x3E, 0x28,
// VCOM control 2
0xC7, 1, 0x86,
// memory access control (MV=0, MX=0, MY=0, ML=0, BGR=1)
0x36, 1, 0x88,
// column address set (0..239)
0x2A, 4, 0x00, 0x00, 0x00, 0xEF,
// page address set (0..319)
0x2B, 4, 0x00, 0x00, 0x01, 0x3F,
// positive gamma
0xE0, 15, 0x00, 0x07, 0x10, 0x09, 0x17, 0x0B, 0x41, 0x89, 0x4B, 0x0A, 0x0C, 0x0E, 0x18, 0x1A, 0x0F,
// negative gamma
0xE1, 15, 0x00, 0x17, 0x1D, 0x02, 0x0C, 0x05, 0x23, 0x75, 0x4B, 0x05, 0x0E, 0x11, 0x27, 0x38, 0x0F,
// display on
0x29, 0 | DELAY, 100,
};

void board_init(void) {
// Backlight is controlled by PMIC via init_board() in libunihiker_k10.a
// No direct GPIO backlight control needed

busio_spi_obj_t *spi = common_hal_board_create_spi(0);
fourwire_fourwire_obj_t *bus = &allocate_display_bus()->fourwire_bus;
bus->base.type = &fourwire_fourwire_type;

common_hal_fourwire_fourwire_construct(
bus,
spi,
MP_OBJ_FROM_PTR(&pin_GPIO13), // DC
MP_OBJ_FROM_PTR(&pin_GPIO14), // CS
mp_const_none, // RST (tied to 3.3V)
40000000, // baudrate
0, // polarity
0 // phase
);

busdisplay_busdisplay_obj_t *display = &allocate_display()->display;
display->base.type = &busdisplay_busdisplay_type;

common_hal_busdisplay_busdisplay_construct(
display,
bus,
240, // width
320, // height
0, // column start
0, // row start
0, // rotation (0=default from MADCTL)
16, // color depth
false, // grayscale
false, // pixels in a byte share a row
1, // bytes per cell
false, // reverse_pixels_in_byte
true, // reverse_pixels_in_word
MIPI_COMMAND_SET_COLUMN_ADDRESS,
MIPI_COMMAND_SET_PAGE_ADDRESS,
MIPI_COMMAND_WRITE_MEMORY_START,
display_init_sequence,
sizeof(display_init_sequence),
NULL, // backlight pin (PMIC controlled)
NO_BRIGHTNESS_COMMAND,
1.0f, // brightness
false, // single_byte_bounds
false, // data_as_commands
true, // auto_refresh
60, // native_frames_per_second
true, // backlight_on_high
false, // SH1107_addressing
50000 // backlight pwm frequency
);
}

// Use the MP_WEAK supervisor/shared/board.c versions of routines not defined here.
61 changes: 61 additions & 0 deletions ports/espressif/boards/dfrobot_unihiker_k10/mpconfigboard.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// This file is part of the CircuitPython project: https://circuitpython.org
// SPDX-FileCopyrightText: Copyright (c) 2026 Djair Guilherme
// SPDX-License-Identifier: MIT

#pragma once

#define MICROPY_HW_BOARD_NAME "DFRobot UNIHIKER K10"
#define MICROPY_HW_MCU_NAME "ESP32S3"

// I2C bus - sensors (AHT20, LTR303ALS, SC7A20H)
#define CIRCUITPY_BOARD_I2C (1)
#define CIRCUITPY_BOARD_I2C_PIN {{.scl = &pin_GPIO48, .sda = &pin_GPIO47}}

// SPI bus - Display ILI9341
#define CIRCUITPY_BOARD_SPI (1)
#define CIRCUITPY_BOARD_SPI_PIN {{.clock = &pin_GPIO12, .mosi = &pin_GPIO21, .miso = &pin_GPIO41}}

// Display (ILI9341 via SPI)
#define MICROPY_HW_DISPLAY_SPI_CS (&pin_GPIO14)
#define MICROPY_HW_DISPLAY_SPI_DC (&pin_GPIO13)
//#define MICROPY_HW_DISPLAY_SPI_RST (NULL) // tied to 3.3V, not used - RST handled in board.c
#define MICROPY_HW_DISPLAY_WIDTH (240)
#define MICROPY_HW_DISPLAY_HEIGHT (320)

// Backlight - controlled via PMIC (not direct GPIO)
// eLCD_BLK is handled by init_board() in libunihiker_k10.a

// NeoPixel (3x WS2812)
#define MICROPY_HW_NEOPIXEL (&pin_GPIO46)

// Status LED
#define MICROPY_HW_LED_STATUS (&pin_GPIO46)

// Default UART
//#define DEFAULT_UART_BUS_RX (NULL) // No UART on default pins
//#define DEFAULT_UART_BUS_TX (NULL) // No UART on default pins

// Camera (GC2145) - pins from who_camera.h
#define CIRCUITPY_ESPCAMERA_PWDN (-1)
#define CIRCUITPY_ESPCAMERA_RESET (-1)
#define CIRCUITPY_ESPCAMERA_XCLK (7)
#define CIRCUITPY_ESPCAMERA_SIOD (47)
#define CIRCUITPY_ESPCAMERA_SIOC (48)
#define CIRCUITPY_ESPCAMERA_D7 (6)
#define CIRCUITPY_ESPCAMERA_D6 (15)
#define CIRCUITPY_ESPCAMERA_D5 (16)
#define CIRCUITPY_ESPCAMERA_D4 (18)
#define CIRCUITPY_ESPCAMERA_D3 (9)
#define CIRCUITPY_ESPCAMERA_D2 (11)
#define CIRCUITPY_ESPCAMERA_D1 (10)
#define CIRCUITPY_ESPCAMERA_D0 (8)
#define CIRCUITPY_ESPCAMERA_VSYNC (4)
#define CIRCUITPY_ESPCAMERA_HREF (5)
#define CIRCUITPY_ESPCAMERA_PCLK (17)

// I2S Audio
#define CIRCUITPY_I2S_BCLK (&pin_GPIO0)
#define CIRCUITPY_I2S_LRCLK (&pin_GPIO38)
#define CIRCUITPY_I2S_DIN (&pin_GPIO39)
#define CIRCUITPY_I2S_DOUT (&pin_GPIO45)
#define CIRCUITPY_I2S_MCLK (&pin_GPIO3)
49 changes: 49 additions & 0 deletions ports/espressif/boards/dfrobot_unihiker_k10/mpconfigboard.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# This file is part of the CircuitPython project: https://circuitpython.org
# SPDX-FileCopyrightText: Copyright (c) 2026 Djair Guilherme
# SPDX-License-Identifier: MIT

USB_VID = 0x303A
USB_PID = 0x1001
Comment on lines +5 to +6

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a generic PID. Please get a specific one. Espressif allocates them here: https://github.com/espressif/usb-pids

USB_MANUFACTURER = "DFRobot"
USB_PRODUCT = "UNIHIKER K10"

IDF_TARGET = esp32s3

# Flash configuration - 16MB QSPI Flash
CIRCUITPY_ESP_FLASH_SIZE = 16MB
CIRCUITPY_ESP_FLASH_MODE = dio
CIRCUITPY_ESP_FLASH_FREQ = 80m

# PSRAM configuration - 8MB QSPI PSRAM
CIRCUITPY_ESP_PSRAM_SIZE = 8MB
CIRCUITPY_ESP_PSRAM_MODE = opi
CIRCUITPY_ESP_PSRAM_FREQ = 80m

OPTIMIZATION_FLAGS = -Os

# Display via SPI (ILI9341)
CIRCUITPY_PARALLELDISPLAYBUS = 0

# Camera (GC2145)
CIRCUITPY_ESPCAMERA = 1

# I2S for audio (microphone + speaker)
CIRCUITPY_AUDIOBUSIO = 1

# SD card via SDMMC - disabled until pin mapping verified
CIRCUITPY_SDIOIO = 0

# Frozen Python modules (built-in libraries)
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_NeoPixel
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_ILI9341
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_AHTx0
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SD
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Wave
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Shapes
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Display_Text
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_BusDevice
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_Register
FROZEN_MPY_DIRS += $(TOP)/frozen/Adafruit_CircuitPython_SimpleIO
FROZEN_MPY_DIRS += $(TOP)/frozen/adafruit_tca9555


79 changes: 79 additions & 0 deletions ports/espressif/boards/dfrobot_unihiker_k10/pins.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// This file is part of the CircuitPython project: https://circuitpython.org
// SPDX-FileCopyrightText: Copyright (c) 2026 Djair Guilherme
// SPDX-License-Identifier: MIT

#include "py/obj.h"
#include "py/mphal.h"
#include "shared-bindings/board/__init__.h"
#include "shared-bindings/microcontroller/Pin.h"
#include "shared-module/displayio/__init__.h"

static const mp_rom_map_elem_t board_module_globals_table[] = {
CIRCUITPYTHON_BOARD_DICT_STANDARD_ITEMS

// =================================================================
// ONBOARD PERIPHERALS - Functional Names
// =================================================================

// Buttons are PMIC-controlled (TCA9555), not direct GPIOs
{ MP_ROM_QSTR(MP_QSTR_BOOT), MP_ROM_PTR(&pin_GPIO0) },

// NeoPixel (3x WS2812)
{ MP_ROM_QSTR(MP_QSTR_NEOPIXEL), MP_ROM_PTR(&pin_GPIO46) },
{ MP_ROM_QSTR(MP_QSTR_LED), MP_ROM_PTR(&pin_GPIO46) },

// I2C (sensors: AHT20, LTR303ALS, SC7A20H)
{ MP_ROM_QSTR(MP_QSTR_SDA), MP_ROM_PTR(&pin_GPIO47) },
{ MP_ROM_QSTR(MP_QSTR_SCL), MP_ROM_PTR(&pin_GPIO48) },
{ MP_ROM_QSTR(MP_QSTR_I2C), MP_ROM_PTR(&board_i2c_obj) },

// SPI (Display ILI9341)
{ MP_ROM_QSTR(MP_QSTR_MOSI), MP_ROM_PTR(&pin_GPIO21) },
{ MP_ROM_QSTR(MP_QSTR_SCK), MP_ROM_PTR(&pin_GPIO12) },
{ MP_ROM_QSTR(MP_QSTR_SPI), MP_ROM_PTR(&board_spi_obj) },

// Display control
{ MP_ROM_QSTR(MP_QSTR_TFT_CS), MP_ROM_PTR(&pin_GPIO14) },
{ MP_ROM_QSTR(MP_QSTR_TFT_DC), MP_ROM_PTR(&pin_GPIO13) },
// TFT_RESET is tied to 3.3V (no GPIO)

// Camera (GC2145)
{ MP_ROM_QSTR(MP_QSTR_CAM_XCLK), MP_ROM_PTR(&pin_GPIO7) },
{ MP_ROM_QSTR(MP_QSTR_CAM_SIOD), MP_ROM_PTR(&pin_GPIO47) },
{ MP_ROM_QSTR(MP_QSTR_CAM_SIOC), MP_ROM_PTR(&pin_GPIO48) },
{ MP_ROM_QSTR(MP_QSTR_CAM_D7), MP_ROM_PTR(&pin_GPIO6) },
{ MP_ROM_QSTR(MP_QSTR_CAM_D6), MP_ROM_PTR(&pin_GPIO15) },
{ MP_ROM_QSTR(MP_QSTR_CAM_D5), MP_ROM_PTR(&pin_GPIO16) },
{ MP_ROM_QSTR(MP_QSTR_CAM_D4), MP_ROM_PTR(&pin_GPIO18) },
{ MP_ROM_QSTR(MP_QSTR_CAM_D3), MP_ROM_PTR(&pin_GPIO9) },
{ MP_ROM_QSTR(MP_QSTR_CAM_D2), MP_ROM_PTR(&pin_GPIO11) },
{ MP_ROM_QSTR(MP_QSTR_CAM_D1), MP_ROM_PTR(&pin_GPIO10) },
{ MP_ROM_QSTR(MP_QSTR_CAM_D0), MP_ROM_PTR(&pin_GPIO8) },
{ MP_ROM_QSTR(MP_QSTR_CAM_VSYNC), MP_ROM_PTR(&pin_GPIO4) },
{ MP_ROM_QSTR(MP_QSTR_CAM_HREF), MP_ROM_PTR(&pin_GPIO5) },
{ MP_ROM_QSTR(MP_QSTR_CAM_PCLK), MP_ROM_PTR(&pin_GPIO17) },

// I2S Audio
{ MP_ROM_QSTR(MP_QSTR_I2S_BCLK), MP_ROM_PTR(&pin_GPIO0) },
{ MP_ROM_QSTR(MP_QSTR_I2S_LRCLK), MP_ROM_PTR(&pin_GPIO38) },
{ MP_ROM_QSTR(MP_QSTR_I2S_DIN), MP_ROM_PTR(&pin_GPIO39) },
{ MP_ROM_QSTR(MP_QSTR_I2S_DOUT), MP_ROM_PTR(&pin_GPIO45) },
{ MP_ROM_QSTR(MP_QSTR_I2S_MCLK), MP_ROM_PTR(&pin_GPIO3) },

// SD Card (SDMMC) - internal to SDMMC peripheral, no discrete GPIOs

// =================================================================
// GENERAL PURPOSE I/O
// =================================================================
{ MP_ROM_QSTR(MP_QSTR_IO1), MP_ROM_PTR(&pin_GPIO1) },
{ MP_ROM_QSTR(MP_QSTR_IO3), MP_ROM_PTR(&pin_GPIO3) },
{ MP_ROM_QSTR(MP_QSTR_IO14), MP_ROM_PTR(&pin_GPIO14) },
{ MP_ROM_QSTR(MP_QSTR_IO21), MP_ROM_PTR(&pin_GPIO21) },
{ MP_ROM_QSTR(MP_QSTR_IO40), MP_ROM_PTR(&pin_GPIO40) },
{ MP_ROM_QSTR(MP_QSTR_IO41), MP_ROM_PTR(&pin_GPIO41) },
{ MP_ROM_QSTR(MP_QSTR_IO42), MP_ROM_PTR(&pin_GPIO42) },

// Display
{ MP_ROM_QSTR(MP_QSTR_DISPLAY), MP_ROM_PTR(&displays[0].display) },
};
MP_DEFINE_CONST_DICT(board_module_globals, board_module_globals_table);
57 changes: 57 additions & 0 deletions ports/espressif/boards/dfrobot_unihiker_k10/sdkconfig

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should be able to use circuitpython settings for these instead of your own.

Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#
#
# Debug/Diagnostic settings for K10
#

#
# Bootloader config
#
CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y
# CONFIG_BOOTLOADER_LOG_LEVEL_NONE is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_WARN is not set
# CONFIG_BOOTLOADER_LOG_LEVEL_ERROR is not set
# default:
CONFIG_BOOTLOADER_LOG_LEVEL=3

#
# ESP-STDIO (USB Serial/JTAG console)
#
# CONFIG_ESP_CONSOLE_NONE is not set
CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG=y
# default:
CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG_ENABLED=y
# default:
CONFIG_ESP_CONSOLE_UART_NUM=-1
# default:
CONFIG_ESP_CONSOLE_ROM_SERIAL_PORT_NUM=6

#
# ESP System Settings - panic behavior
#
CONFIG_ESP_SYSTEM_PANIC_PRINT_REBOOT=y
# CONFIG_ESP_SYSTEM_PANIC_SILENT_REBOOT is not set
# CONFIG_ESP_SYSTEM_PANIC_PRINT_HALT is not set

#
# PSRAM - allow boot even if memtest fails
#
CONFIG_SPIRAM_IGNORE_NOTFOUND=y

# Espressif IoT Development Framework Configuration
#
#
# Component config
#
#
# LWIP
#
# end of LWIP

# end of Component config

# end of Espressif IoT Development Framework Configuration

#
# Camera sensor support - GC2145
#
CONFIG_GC2145_SUPPORT=y
Loading