diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..8762107 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,55 @@ +[build] +# Uncomment the relevant target for your chip here (ESP32, ESP32-S2, ESP32-S3 or ESP32-C3) +target = "xtensa-esp32-espidf" +#target = "xtensa-esp32s2-espidf" +#target = "xtensa-esp32s3-espidf" +#target = "riscv32imc-esp-espidf" + +[target.xtensa-esp32-espidf] +linker = "ldproxy" +# Uncomment for ESP-IDF 5 and later. Don't forget to also uncomment the `ESP_IDF_VERSION = "release/v5.0"`parameter in the `[env]` section below +#rustflags = ["--cfg", "espidf_time64"] + +[target.xtensa-esp32s2-espidf] +linker = "ldproxy" +# Uncomment for ESP-IDF 5 and later. Don't forget to also uncomment the `ESP_IDF_VERSION = "release/v5.0"`parameter in the `[env]` section below +#rustflags = ["--cfg", "espidf_time64"] + +[target.xtensa-esp32s3-espidf] +linker = "ldproxy" +# Uncomment for ESP-IDF 5 and later. Don't forget to also uncomment the `ESP_IDF_VERSION = "release/v5.0"`parameter in the `[env]` section below +#rustflags = ["--cfg", "espidf_time64"] + +[target.riscv32imc-esp-espidf] +linker = "ldproxy" +# Future - necessary for the experimental "native build" of esp-idf-sys with ESP32C3 +# See also https://github.com/ivmarkov/embuild/issues/16 +rustflags = ["-C", "default-linker-libraries"] +# Uncomment for ESP-IDF 5 and later. Don't forget to also uncomment the `ESP_IDF_VERSION = "release/v5.0"`parameter in the `[env]` section below +#rustflags = ["--cfg", "espidf_time64", "-C", "default-linker-libraries"] + +[unstable] +build-std = ["std", "panic_abort"] +#build-std-features = ["panic_immediate_abort"] # Only necessary if building against ESP-IDF tag `v4.3.2` (the minimum supported version) + +[env] +# Select ESP IDF version in embuild's format described here: +# https://github.com/esp-rs/esp-idf-sys/blob/master/README.md#esp_idf_version-esp_idf_version-native-builder-only +# +# Uncomment this to build against ESP-IDF master (currently unreleased ESP IDF 5.1) +#ESP_IDF_VERSION = "master" +# Don't forget to uncomment also the `rustflags` parameter in your "target" section above +# +# Uncomment this to build against ESP-IDF 5.0 +# Don't forget to uncomment also the `rustflags` parameter in your "target" section above +#ESP_IDF_VERSION = "release/v5.0" +# +# Comment out this when using the PlatformIO build, i.e. `cargo build --features pio` (it only supports `v4.3.2`) +ESP_IDF_VERSION = "release/v4.4" + +# These configurations will pick up your custom "sdkconfig.release", "sdkconfig.debug" or "sdkconfig.defaults[.*]" files +# that you might put in the root of the project +# The easiest way to generate a full "sdkconfig" configuration (as opposed to manually enabling only the necessary flags via "sdkconfig.defaults[.*]" +# is by running "cargo pio espidf menuconfig" (that is, if using the pio builder) +#ESP_IDF_SDKCONFIG = "sdkconfig.release;sdkconfig.debug" +ESP_IDF_SDKCONFIG_DEFAULTS = "sdkconfig.defaults;sdkconfig.defaults.esp32;sdkconfig.defaults.esp32s2" \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..101a548 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +/target +/Cargo.lock +/.embuild \ No newline at end of file diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..02204c5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "esp32-camera"] + path = esp32-camera + url = https://github.com/espressif/esp32-camera.git + branch = master diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..0747acd --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "esp-camera-rs" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +esp-idf-hal = "0.40.1" +esp-idf-sys = "0.32.1" + +[patch.crates-io] +esp-idf-sys = {git = "https://github.com/esp-rs/esp-idf-sys", branch = "master"} + +[[package.metadata.esp-idf-sys.extra_components]] +component_dirs = "esp32-camera" +bindings_header = "bindings.h" +bindings_module = "camera" diff --git a/bindings.h b/bindings.h new file mode 100644 index 0000000..a4c4232 --- /dev/null +++ b/bindings.h @@ -0,0 +1 @@ +#include "esp_camera.h" diff --git a/esp32-camera b/esp32-camera new file mode 160000 index 0000000..e689c3b --- /dev/null +++ b/esp32-camera @@ -0,0 +1 @@ +Subproject commit e689c3b082985ee7b90198be32d330ce51ac5367 diff --git a/sdkconfig.defaults b/sdkconfig.defaults new file mode 100644 index 0000000..753473e --- /dev/null +++ b/sdkconfig.defaults @@ -0,0 +1 @@ +CONFIG_ESP32_SPIRAM_SUPPORT=y \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..dbf345e --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,294 @@ +use std::marker::PhantomData; + +use esp_idf_hal::gpio::*; +use esp_idf_hal::peripheral::Peripheral; +use esp_idf_sys::*; + +pub struct FrameBuffer<'a> { + fb: *mut camera::camera_fb_t, + _p: PhantomData<&'a camera::camera_fb_t>, +} + +impl<'a> FrameBuffer<'a> { + pub fn data(&self) -> &'a [u8] { + unsafe { std::slice::from_raw_parts((*self.fb).buf, (*self.fb).len) } + } + + pub fn width(&self) -> usize { + unsafe { (*self.fb).width } + } + + pub fn height(&self) -> usize { + unsafe { (*self.fb).height } + } + + pub fn format(&self) -> camera::pixformat_t { + unsafe { (*self.fb).format } + } + + pub fn timestamp(&self) -> camera::timeval { + unsafe { (*self.fb).timestamp } + } +} + +pub struct CameraSensor<'a> { + sensor: *mut camera::sensor_t, + _p: PhantomData<&'a camera::sensor_t>, +} + +impl<'a> CameraSensor<'a> { + pub fn init_status(&self) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).init_status.unwrap()(self.sensor) }) + } + pub fn reset(&self) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).reset.unwrap()(self.sensor) }) + } + pub fn set_pixformat(&self, format: camera::pixformat_t) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_pixformat.unwrap()(self.sensor, format) }) + } + pub fn set_framesize(&self, framesize: camera::framesize_t) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_framesize.unwrap()(self.sensor, framesize) }) + } + pub fn set_contrast(&self, level: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_contrast.unwrap()(self.sensor, level) }) + } + pub fn set_brightness(&self, level: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_brightness.unwrap()(self.sensor, level) }) + } + pub fn set_saturation(&self, level: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_saturation.unwrap()(self.sensor, level) }) + } + pub fn set_sharpness(&self, level: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_sharpness.unwrap()(self.sensor, level) }) + } + pub fn set_denoise(&self, level: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_denoise.unwrap()(self.sensor, level) }) + } + pub fn set_gainceiling(&self, gainceiling: camera::gainceiling_t) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_gainceiling.unwrap()(self.sensor, gainceiling) }) + } + pub fn set_quality(&self, quality: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_quality.unwrap()(self.sensor, quality) }) + } + pub fn set_colorbar(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { + (*self.sensor).set_colorbar.unwrap()(self.sensor, if enable { 1 } else { 0 }) + }) + } + pub fn set_whitebal(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { + (*self.sensor).set_whitebal.unwrap()(self.sensor, if enable { 1 } else { 0 }) + }) + } + pub fn set_gain_ctrl(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { + (*self.sensor).set_gain_ctrl.unwrap()(self.sensor, if enable { 1 } else { 0 }) + }) + } + pub fn set_exposure_ctrl(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { + (*self.sensor).set_exposure_ctrl.unwrap()(self.sensor, if enable { 1 } else { 0 }) + }) + } + pub fn set_hmirror(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { + (*self.sensor).set_hmirror.unwrap()(self.sensor, if enable { 1 } else { 0 }) + }) + } + pub fn set_vflip(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_vflip.unwrap()(self.sensor, if enable { 1 } else { 0 }) }) + } + pub fn set_aec2(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_aec2.unwrap()(self.sensor, if enable { 1 } else { 0 }) }) + } + pub fn set_awb_gain(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { + (*self.sensor).set_awb_gain.unwrap()(self.sensor, if enable { 1 } else { 0 }) + }) + } + pub fn set_agc_gain(&self, gain: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_agc_gain.unwrap()(self.sensor, gain) }) + } + pub fn set_aec_value(&self, gain: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_aec_value.unwrap()(self.sensor, gain) }) + } + pub fn set_special_effect(&self, effect: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_special_effect.unwrap()(self.sensor, effect) }) + } + pub fn set_wb_mode(&self, mode: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_wb_mode.unwrap()(self.sensor, mode) }) + } + pub fn set_ae_level(&self, level: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_ae_level.unwrap()(self.sensor, level) }) + } + pub fn set_dcw(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_dcw.unwrap()(self.sensor, if enable { 1 } else { 0 }) }) + } + pub fn set_bpc(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_bpc.unwrap()(self.sensor, if enable { 1 } else { 0 }) }) + } + pub fn set_wpc(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_wpc.unwrap()(self.sensor, if enable { 1 } else { 0 }) }) + } + pub fn set_raw_gma(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { + (*self.sensor).set_raw_gma.unwrap()(self.sensor, if enable { 1 } else { 0 }) + }) + } + pub fn set_lenc(&self, enable: bool) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_lenc.unwrap()(self.sensor, if enable { 1 } else { 0 }) }) + } + pub fn get_reg(&self, reg: i32, mask: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).get_reg.unwrap()(self.sensor, reg, mask) }) + } + pub fn set_reg(&self, reg: i32, mask: i32, value: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_reg.unwrap()(self.sensor, reg, mask, value) }) + } + pub fn set_res_raw( + &self, + start_x: i32, + start_y: i32, + end_x: i32, + end_y: i32, + offset_x: i32, + offset_y: i32, + total_x: i32, + total_y: i32, + output_x: i32, + output_y: i32, + scale: bool, + binning: bool, + ) -> Result<(), EspError> { + esp!(unsafe { + (*self.sensor).set_res_raw.unwrap()( + self.sensor, + start_x, + start_y, + end_x, + end_y, + offset_x, + offset_y, + total_x, + total_y, + output_x, + output_y, + scale, + binning, + ) + }) + } + pub fn set_pll( + &self, + bypass: i32, + mul: i32, + sys: i32, + root: i32, + pre: i32, + seld5: i32, + pclken: i32, + pclk: i32, + ) -> Result<(), EspError> { + esp!(unsafe { + (*self.sensor).set_pll.unwrap()( + self.sensor, + bypass, + mul, + sys, + root, + pre, + seld5, + pclken, + pclk, + ) + }) + } + pub fn set_xclk(&self, timer: i32, xclk: i32) -> Result<(), EspError> { + esp!(unsafe { (*self.sensor).set_xclk.unwrap()(self.sensor, timer, xclk) }) + } +} + +pub struct Camera<'a> { + _p: PhantomData<&'a ()>, +} + +impl<'a> Camera<'a> { + pub fn new( + pin_pwdn: impl Peripheral
+ 'a, + pin_reset: impl Peripheral
+ 'a, + pin_xclk: impl Peripheral
+ 'a, + pin_d0: impl Peripheral
+ 'a, + pin_d1: impl Peripheral
+ 'a, + pin_d2: impl Peripheral
+ 'a, + pin_d3: impl Peripheral
+ 'a, + pin_d4: impl Peripheral
+ 'a, + pin_d5: impl Peripheral
+ 'a, + pin_d6: impl Peripheral
+ 'a, + pin_d7: impl Peripheral
+ 'a, + pin_vsync: impl Peripheral
+ 'a, + pin_href: impl Peripheral
+ 'a, + pin_pclk: impl Peripheral
+ 'a,
+ ) -> Result