This project demonstrates how to use Swift in an embedded environment with ESP32C6, featuring a rotating 3D cube animation on an OLED display.
.
├── CMakeLists.txt # Main CMake configuration
├── main/
│ ├── CMakeLists.txt # Component CMake configuration
│ ├── Package.swift # Swift package manifest
│ └── Source/
│ ├── App/ # Main application code
│ └── Support/ # Support code and fonts
└── sdkconfig # ESP-IDF configuration
- Swift embedded programming on ESP32C6
- OLED display support using U8g2
- 3D graphics rendering
- I2C communication
To integrate Swift Package Manager with CMake in an embedded environment:
- Create a
Package.swift
file in your component directory:
// swift-tools-version: 6.1
import PackageDescription
let package = Package(
name: "App",
products: [
.library(
name: "App",
type: .static,
targets: ["App"]),
],
dependencies: [
.package(url: "https://github.com/CmST0us/U8g2Kit", branch: "main", traits: ["Embedded"]),
],
targets: [
.target(
name: "App",
dependencies: [
"Support",
.product(name: "U8g2Kit", package: "U8g2Kit"),
.product(name: "CU8g2", package: "U8g2Kit")],
swiftSettings: [
.swiftLanguageMode(.v5),
.enableExperimentalFeature("Embedded"),
]),
.target(name: "Support",
swiftSettings: [
.swiftLanguageMode(.v5),
.enableExperimentalFeature("Embedded"),
]),
]
)
- Configure CMakeLists.txt to build Swift code:
Add custom command to run swift build to make embedded libApp.a
add_custom_target(swift-archive
COMMAND
# Remove the archive. Swift does not overwrite this on building but merges them.
# If the file with app_main gets renamed, both objectfiles (old and new) will be in the archive, potentially causing problems
rm -f ${SWIFT_PRODUCT_ARCHIVE}
&&
${SWIFT_TOOLCHAIN}/swift package update
--package-path ${COMPONENT_DIR}
&&
${SWIFT_TOOLCHAIN}/swift build
-c release
--package-path ${COMPONENT_DIR}
--triple ${SWIFT_TARGET}
-Xswiftc -target -Xswiftc riscv32-none-none-eabi
-Xswiftc -enable-experimental-feature -Xswiftc Embedded
-Xswiftc -wmo
-Xswiftc -parse-as-library
-Xswiftc -Osize
-Xswiftc -Xfrontend -Xswiftc -function-sections
-Xswiftc -Xfrontend -Xswiftc -enable-single-module-llvm-emission
-Xlinker -lgcc
-Xlinker -lm
-Xswiftc -pch-output-dir -Xswiftc /tmp
-Xcc ${march_flag}
-Xcc ${mabi_flag}
-Xcc -fno-pic
-Xcc -fno-pie
# Get includes for C-std libraries and extra components
${SWIFT_INCLUDES_LIST}
$$\( echo '$<TARGET_PROPERTY:__idf_main,INCLUDE_DIRECTORIES>' | tr '\;' '\\n' | sed -e 's/\\\(.*\\\)/-Xcc -I\\1/g' \)
BYPRODUCTS
${SWIFT_PRODUCT_ARCHIVE}
)
When working with Swift->C->ESP-IDF symbols (e.g., i2c_driver_install
), you might encounter linking errors. To resolve this:
// Reference to ESP32U8g2Driver.swift
init() {
super.init(u8g2_Setup_ssd1306_i2c_128x64_noname_f, &U8g2Kit.u8g2_cb_r0)
// Mask use this, otherwise i2c_driver_install will not found
let installHandler = i2c_driver_install
}
To reduce binary size when using U8g2 fonts:
- Extract required fonts to a separate C file:
// font.c
#include "font.h"
const uint8_t *default_font_5x7 = "..."; // Font data
- Include only necessary fonts in your build.
Currently, SourceKit-LSP doesn't work properly in the embedded Swift environment. This affects:
- Code completion
- Symbol navigation
- Error reporting
Potential workarounds:
- Use a text editor with basic syntax highlighting
- Maintain a separate Swift package for development
- Use command-line tools for building and debugging
- Install ESP-IDF and Swift toolchain
- Configure the project:
idf.py set-target esp32c6
idf.py menuconfig
- Build and flash:
idf.py build
idf.py -p (PORT) flash
Contributions are welcome! Please feel free to submit a Pull Request.
This project is licensed under the Apache License 2.0 - see the LICENSE file for details.