├── .cargo └── config.toml ├── .gitignore ├── Cargo.toml ├── README.md ├── build.rs ├── rust-toolchain.toml ├── sdkconfig.defaults └── src └── main.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | # Uncomment the relevant target for your chip here (ESP32, ESP32-S2, ESP32-S3 or ESP32-C3) 3 | #target = "xtensa-esp32-espidf" 4 | #target = "xtensa-esp32s2-espidf" 5 | #target = "xtensa-esp32s3-espidf" 6 | target = "riscv32imc-esp-espidf" 7 | 8 | [target.xtensa-esp32-espidf] 9 | linker = "ldproxy" 10 | 11 | [target.xtensa-esp32s2-espidf] 12 | linker = "ldproxy" 13 | 14 | [target.xtensa-esp32s3-espidf] 15 | linker = "ldproxy" 16 | 17 | [target.riscv32imc-esp-espidf] 18 | linker = "ldproxy" 19 | 20 | # Future - necessary for the experimental "native build" of esp-idf-sys with ESP32C3 21 | # See also https://github.com/ivmarkov/embuild/issues/16 22 | rustflags = ["-C", "default-linker-libraries"] 23 | 24 | [unstable] 25 | 26 | build-std = ["std", "panic_abort"] 27 | build-std-features = ["panic_immediate_abort"] 28 | 29 | [env] 30 | # Enables the esp-idf-sys "native" build feature (`cargo build --features native`) to build against ESP-IDF stable (v4.4) 31 | ESP_IDF_VERSION = { value = "branch:release/v4.4" } 32 | # Enables the esp-idf-sys "native" build feature (`cargo build --features native`) to build against ESP-IDF master (v5.0) 33 | #ESP_IDF_VERSION = { value = "master" } 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | /.embuild 3 | /target 4 | /Cargo.lock 5 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "by-template" 3 | version = "0.1.0" 4 | authors = ["Andrei Litvin "] 5 | edition = "2018" 6 | resolver = "2" 7 | 8 | [profile.release] 9 | opt-level = "s" 10 | 11 | [profile.dev] 12 | debug = true # Symbols are nice and they don't increase the size on Flash 13 | opt-level = "z" 14 | 15 | [features] 16 | default = ["native"] 17 | native = ["esp-idf-sys/native"] 18 | 19 | [dependencies] 20 | anyhow = "1" 21 | esp-idf-sys = { version = "0.30.6", features = ["binstart"] } 22 | esp-idf-svc = "0.37.2" 23 | esp-idf-hal = "0.33.1" 24 | embedded-graphics = "0.7" 25 | embedded-svc = "*" 26 | ssd1306 = "0.7" 27 | 28 | 29 | [build-dependencies] 30 | embuild = "0.28" 31 | anyhow = "1" 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Sample project 2 | 3 | Sample project for a ESP32-C3 embedded program, shows how to use: 4 | - A LCD display based on SSD1036, connected to GPIO4 and GPIO5 5 | - Connecting to a client wifi 6 | 7 | ## Getting started 8 | 9 | Project is based on the [esp-rs/esp-idf-template](https://github.com/esp-rs/esp-idf-template) 10 | project. Read its documentation for getting started. I was generally started as 11 | 12 | ```console 13 | $ rustup install nightly 14 | $ rustup component add rust-src --toolchain nightly 15 | 16 | $ cargo install cargo-generate 17 | $ cargo install ldproxy 18 | $ cargo install espflash 19 | $ cargo install espmonitor 20 | 21 | $ cargo generate --vcs none --git https://github.com/esp-rs/esp-idf-template cargo 22 | ``` 23 | 24 | ## Hardware 25 | 26 | This is based on a [EPS32 C3 Series](https://www.espressif.com/en/products/devkits) device: 27 | 28 | - C3 Risc-V boards from [AliExpress](https://www.aliexpress.com/wholesale?SearchText=ESP32-C3) 29 | - SSD1306-based OLED display from [AliExpress](https://www.aliexpress.com/wholesale?SearchText=ssd1306+oled+128x64) 30 | 31 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | // Necessary because of this issue: https://github.com/rust-lang/cargo/issues/9641 2 | fn main() -> anyhow::Result<()> { 3 | embuild::build::CfgArgs::output_propagated("ESP_IDF")?; 4 | embuild::build::LinkArgs::output_propagated("ESP_IDF") 5 | } 6 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | 3 | 4 | channel = "nightly" 5 | 6 | -------------------------------------------------------------------------------- /sdkconfig.defaults: -------------------------------------------------------------------------------- 1 | # Rust often needs a bit of an extra main task stack size compared to C (the default is 3K) 2 | CONFIG_ESP_MAIN_TASK_STACK_SIZE=7000 3 | 4 | # Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default). 5 | # This allows to use 1 ms granuality for thread sleeps (10 ms by default). 6 | #CONFIG_FREERTOS_HZ=1000 7 | 8 | # Workaround for https://github.com/espressif/esp-idf/issues/7631 9 | CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n 10 | CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n 11 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | use core::time::Duration; 2 | use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported 3 | 4 | use esp_idf_hal::gpio::{Gpio4, Gpio5, Unknown}; 5 | use esp_idf_hal::i2c; 6 | use esp_idf_hal::peripherals::Peripherals; 7 | use esp_idf_hal::units::FromValueType; 8 | use esp_idf_svc::netif::EspNetifStack; 9 | use esp_idf_svc::nvs::EspDefaultNvs; 10 | use esp_idf_svc::sysloop::EspSysLoopStack; 11 | use esp_idf_svc::wifi::EspWifi; 12 | use std::sync::Arc; 13 | 14 | use embedded_svc::wifi::{ 15 | ClientConfiguration, ClientConnectionStatus, ClientIpStatus, ClientStatus, Configuration, Wifi, 16 | }; 17 | 18 | use anyhow::Result; 19 | 20 | use embedded_graphics::{ 21 | mono_font::{ascii::FONT_6X10, MonoTextStyleBuilder}, 22 | pixelcolor::BinaryColor, 23 | prelude::*, 24 | text::{Baseline, Text}, 25 | }; 26 | use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306}; 27 | 28 | fn display_test(i2c0: i2c::I2C0, scl: Gpio4, sda: Gpio5, ip: &str) -> Result<()> { 29 | let i2c = i2c::Master::new( 30 | i2c0, 31 | i2c::MasterPins { 32 | scl: scl.into_output().unwrap(), // O 33 | sda: sda.into_input_output().unwrap(), // I+O 34 | }, 35 | i2c::config::MasterConfig::new().baudrate(400.kHz().into()), 36 | )?; 37 | 38 | let interface = I2CDisplayInterface::new(i2c); 39 | 40 | let mut display = Ssd1306::new(interface, DisplaySize128x64, DisplayRotation::Rotate0) 41 | .into_buffered_graphics_mode(); 42 | display 43 | .init() 44 | .map_err(|e| anyhow::anyhow!("Init error: {:?}", e))?; 45 | 46 | let text_style = MonoTextStyleBuilder::new() 47 | .font(&FONT_6X10) 48 | .text_color(BinaryColor::On) 49 | .build(); 50 | 51 | Text::with_baseline("Hello world!", Point::zero(), text_style, Baseline::Top) 52 | .draw(&mut display) 53 | .map_err(|e| anyhow::anyhow!("Txt error: {:?}", e))?; 54 | 55 | Text::with_baseline( 56 | &format!("IP: {}", ip), 57 | Point::new(0, 16), 58 | text_style, 59 | Baseline::Top, 60 | ) 61 | .draw(&mut display) 62 | .map_err(|e| anyhow::anyhow!("Txt2 error: {:?}", e))?; 63 | 64 | display 65 | .flush() 66 | .map_err(|e| anyhow::anyhow!("Flush error: {:?}", e))?; 67 | 68 | Ok(()) 69 | } 70 | 71 | fn test_wifi() -> Result { 72 | let netif_stack = Arc::new(EspNetifStack::new()?); 73 | let sys_look_stack = Arc::new(EspSysLoopStack::new()?); 74 | let nvs = Arc::new(EspDefaultNvs::new()?); 75 | 76 | let mut wifi = EspWifi::new(netif_stack, sys_look_stack, nvs)?; 77 | 78 | wifi.set_configuration(&Configuration::Client(ClientConfiguration { 79 | ssid: "iot-test".into(), 80 | password: "test1234".into(), 81 | ..Default::default() 82 | }))?; 83 | 84 | wifi.wait_status_with_timeout(Duration::from_secs(30), |s| !s.is_transitional()) 85 | .map_err(|e| anyhow::anyhow!("Wait timeout: {:?}", e))?; 86 | 87 | let status = wifi.get_status(); 88 | 89 | println!("Status: {:?}", status); 90 | 91 | if let ClientStatus::Started(ClientConnectionStatus::Connected(ClientIpStatus::Done( 92 | client_settings, 93 | ))) = status.0 94 | { 95 | Ok(format!("{:?}", client_settings.ip)) 96 | } else { 97 | Err(anyhow::anyhow!("Failed to connect in time.")) 98 | } 99 | } 100 | 101 | fn main() { 102 | // Temporary. Will disappear once ESP-IDF 4.4 is released, but for now it is necessary to call this function once, 103 | // or else some patches to the runtime implemented by esp-idf-sys might not link properly. 104 | esp_idf_sys::link_patches(); 105 | 106 | println!("Hello, world from a ESP32 C3 test!"); 107 | 108 | let peripherals = Peripherals::take().unwrap(); 109 | 110 | let wifi = test_wifi(); 111 | let ip = match wifi { 112 | Err(e) => { 113 | println!("Wifi error: {:?}", e); 114 | format!("ERR: {:?}", e) 115 | } 116 | Ok(s) => s, 117 | }; 118 | 119 | if let Err(e) = display_test( 120 | peripherals.i2c0, 121 | peripherals.pins.gpio4, 122 | peripherals.pins.gpio5, 123 | &ip, 124 | ) { 125 | println!("Display error: {:?}", e) 126 | } else { 127 | println!("Display ok"); 128 | } 129 | } 130 | --------------------------------------------------------------------------------