start of rust + c journey

This commit is contained in:
franchioping 2026-03-16 00:49:02 +00:00
parent b4bba1e5cc
commit 76f1651736
13 changed files with 57 additions and 240 deletions

1
.envrc Normal file
View File

@ -0,0 +1 @@
use flake

2
.gitignore vendored
View File

@ -2,6 +2,8 @@
build/ build/
target/ target/
.direnv
sdkconfig.old sdkconfig.old
sdkconfig sdkconfig
debug/ debug/

@ -1 +1 @@
Subproject commit ede403613d96b3233e2c8f8be04dc4cb9efd7569 Subproject commit 8ae1b54c6100a96a670255e35960f9372c2b4dfb

View File

@ -1,62 +0,0 @@
idf_component_register(
SRCS "placeholder.c"
INCLUDE_DIRS ""
PRIV_REQUIRES "${RUST_DEPS}"
)
set(CARGO_BUILD_TYPE "release")
set(CARGO_BUILD_ARG "--release")
if(CONFIG_IDF_TARGET_ARCH_RISCV)
set(CARGO_TARGET "riscv32imc-esp-espidf")
set(CARGO_FEATURES_ARG "")
elseif(CONFIG_IDF_TARGET_ARCH_XTENSA)
set(CARGO_TARGET "xtensa-esp32-espidf")
set(CARGO_FEATURES_ARG "--features=std")
endif()
set(RUST_PROJECT_DIR "${CMAKE_CURRENT_LIST_DIR}")
set(RUST_BUILD_DIR "${CMAKE_CURRENT_BINARY_DIR}")
set(RUST_TARGET_DIR "${RUST_BUILD_DIR}/target")
set(RUST_INCLUDE_DIR "${RUST_TARGET_DIR}")
set(RUST_INCLUDE_HEADER "${RUST_INCLUDE_DIR}/RustApi.h")
set(RUST_STATIC_LIBRARY "${RUST_TARGET_DIR}/${CARGO_TARGET}/${CARGO_BUILD_TYPE}/librustlib.a")
idf_build_get_property(sdkconfig SDKCONFIG)
ExternalProject_Add(
rustlib_project
PREFIX "${RUST_PROJECT_DIR}"
DOWNLOAD_COMMAND ""
CONFIGURE_COMMAND ${CMAKE_COMMAND} -E env
CARGO_BUILD_TARGET=${CARGO_TARGET}
CARGO_BUILD_TARGET_DIR=${RUST_TARGET_DIR}
cargo clean
BUILD_COMMAND ${CMAKE_COMMAND} -E env
CARGO_BUILD_TARGET=${CARGO_TARGET}
CARGO_BUILD_TARGET_DIR=${RUST_TARGET_DIR}
CARGO_CMAKE_BUILD_INCLUDES=$<TARGET_PROPERTY:${COMPONENT_LIB},INCLUDE_DIRECTORIES>
CARGO_CMAKE_BUILD_LINK_LIBRARIES=$<TARGET_PROPERTY:${COMPONENT_LIB},LINK_LIBRARIES>
CARGO_CMAKE_BUILD_SDKCONFIG=${sdkconfig}
CARGO_CMAKE_BUILD_COMPILER=${CMAKE_C_COMPILER}
cargo build ${CARGO_BUILD_ARG} ${CARGO_FEATURES_ARG}
INSTALL_COMMAND ""
BUILD_ALWAYS TRUE
TMP_DIR "${RUST_BUILD_DIR}/tmp"
STAMP_DIR "${RUST_BUILD_DIR}/stamp"
DOWNLOAD_DIR "${RUST_BUILD_DIR}"
SOURCE_DIR "${RUST_PROJECT_DIR}"
BINARY_DIR "${RUST_PROJECT_DIR}"
INSTALL_DIR "${RUST_BUILD_DIR}"
BUILD_BYPRODUCTS
"${RUST_INCLUDE_HEADER}"
"${RUST_STATIC_LIBRARY}"
)
set_source_files_properties("${RUST_INCLUDE_HEADER}" PROPERTIES GENERATED true)
add_prebuilt_library(rustlib_lib "${RUST_STATIC_LIBRARY}" PRIV_REQUIRES "${RUST_DEPS}")
add_dependencies(rustlib_lib rustlib_project)
target_include_directories(${COMPONENT_LIB} PUBLIC "${RUST_INCLUDE_DIR}")
target_link_libraries(${COMPONENT_LIB} PRIVATE rustlib_lib)

View File

@ -1,30 +0,0 @@
[package]
name = "rustlib"
version = "0.1.0"
authors = ["Espressif Systems CO LTD", "June Life Inc"]
edition = "2018"
[lib]
crate-type = ["staticlib"]
[build-dependencies]
bindgen = "0.58"
cbindgen = "0.19"
[features]
std = []
[profile.dev]
# Optimization level 1 is similar to "g" in gcc/clang, although for some reason
# Cargo doesn't support that flag.
#opt-level = 1
opt-level = "s"
[profile.release]
debug = true
opt-level = "z"
#debug = true
#lto = true
#opt-level = "z"
#codegen-units = 1
#panic = "abort"

View File

@ -1,13 +0,0 @@
# rustlib
This component generates a static library from Rust source code and exposes functions that will be callable from the main IDF application from C.
This component is responsible for three separate functions:
* Export C bindings for Rust functions with `cbindgen`.
* Import Rust binding for C functions with `bindgen`.
* Build a top level static library from Rust code. The `cargo` build command is wrapped in a CMake `ExternalProject_Add` command.
Currently the `build.rs` script expects the `clib` component to export a header with the name `CApi.h`. If the name of the include file changes or if more files are added, the build script must be updated to process the latest headers.
Depending on the complexity of the project, the `cbindgen` and `bindgen` steps could be broken out into one or more IDF components or Rust crates. However, to prevent redundant duplication of the Rust runtime, all Rust crates should be compiled into a single static library that will be linked into the final IDF application.

View File

@ -1,22 +0,0 @@
use std::env;
use std::path::{Path, PathBuf};
fn main() {
let cargo_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let target_dir = PathBuf::from(env::var("CARGO_BUILD_TARGET_DIR").unwrap());
run_cbindgen(&cargo_dir, &target_dir);
}
fn run_cbindgen(cargo_dir: &Path, target_dir: &Path) {
let out = target_dir.join("RustApi.h");
cbindgen::Builder::new()
.with_crate(cargo_dir)
.with_language(cbindgen::Language::C)
.generate()
.expect("Unable to generate bindings")
.write_to_file(&out);
println!("cargo:rerun-if-changed={}", out.display());
}

View File

@ -1,13 +0,0 @@
/* Hello World Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
/* This is an empty source file to force the build of a CMake library that
can participate in the CMake dependency graph. This placeholder library
will depend on the actual library generated by external Rust build.
*/

View File

@ -1,3 +0,0 @@
[toolchain]
channel = "esp"

View File

@ -1,31 +0,0 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(not(version("1.59")), feature(asm))]
#![cfg_attr(
all(version("1.58"), target_arch = "xtensa"),
feature(asm_experimental_arch)
)]
#![feature(cfg_version)]
use core::arch::asm;
#[cfg(not(feature = "std"))]
use core::panic::PanicInfo;
#[no_mangle]
pub extern "C" fn add_in_rust(x: i32, y: i32) -> i32 {
x + y
}
#[no_mangle]
pub extern "C" fn add_in_rust_inline_asm(mut x: i32, y: i32) -> i32 {
unsafe {
// more detail available: https://doc.rust-lang.org/beta/unstable-book/library-features/asm.html
asm!("add {0}, {0}, {1}", inout(reg) x, in(reg) y);
}
x
}
#[cfg(not(feature = "std"))]
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}

View File

@ -1,2 +1,2 @@
idf_component_register(SRCS "main.c" idf_component_register(SRCS "main.cpp"
INCLUDE_DIRS "") INCLUDE_DIRS "")

View File

@ -1,64 +0,0 @@
/* Hello World Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <stdio.h>
#include <inttypes.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_system.h"
#include "esp_flash.h"
#include "esp_err.h"
#include "esp_idf_version.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
#include "esp_chip_info.h"
#endif
#include "RustApi.h"
void app_main(void)
{
uint32_t flash_size;
printf("Hello world!\n");
/* Print chip information */
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
printf("This is %s chip with %d CPU cores, WiFi%s%s, ",
CONFIG_IDF_TARGET,
chip_info.cores,
(chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
printf("silicon revision %d, ", chip_info.revision);
ESP_ERROR_CHECK( esp_flash_get_size(NULL, &flash_size) );
printf("%"PRIu32"MB %s flash\n", flash_size / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded" : "external");
printf("Free heap: %"PRIu32"\n", esp_get_free_heap_size());
int x = 2;
int y = 3;
int sum = add_in_rust(x, y);
printf("Rust calculated %d + %d = %d\n\n", x, y, sum);
x = 9;
y = 10;
sum = add_in_rust_inline_asm(x, y);
printf("Rust calculated (using asm!) %d + %d = %d\n\n", x, y, sum);
for (int i = 10; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
}

52
main/main.cpp Normal file
View File

@ -0,0 +1,52 @@
/* Hello World Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include "esp_err.h"
#include "esp_flash.h"
#include "esp_idf_version.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "sdkconfig.h"
#include <inttypes.h>
#include <stdio.h>
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
#include "esp_chip_info.h"
#endif
#include "drone_controller.h"
extern "C" void app_main(void) {
uint32_t flash_size;
printf("Hello world!\n");
/* Print chip information */
esp_chip_info_t chip_info;
esp_chip_info(&chip_info);
printf("This is %s chip with %d CPU cores, WiFi%s%s, ", CONFIG_IDF_TARGET,
chip_info.cores, (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
(chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");
printf("silicon revision %d, ", chip_info.revision);
ESP_ERROR_CHECK(esp_flash_get_size(NULL, &flash_size));
printf("%" PRIu32 "MB %s flash\n", flash_size / (1024 * 1024),
(chip_info.features & CHIP_FEATURE_EMB_FLASH) ? "embedded"
: "external");
printf("Free heap: %" PRIu32 "\n", esp_get_free_heap_size());
for (int i = 10; i >= 0; i--) {
printf("Restarting in %d seconds...\n", i);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
printf("Restarting now.\n");
fflush(stdout);
esp_restart();
}