initial
This commit is contained in:
41
.cargo/config.toml
Normal file
41
.cargo/config.toml
Normal file
@@ -0,0 +1,41 @@
|
||||
[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"
|
||||
runner = "espflash --monitor"
|
||||
#rustflags = ["--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
|
||||
|
||||
[target.xtensa-esp32s2-espidf]
|
||||
linker = "ldproxy"
|
||||
runner = "espflash --monitor"
|
||||
#rustflags = ["--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
|
||||
|
||||
[target.xtensa-esp32s3-espidf]
|
||||
linker = "ldproxy"
|
||||
runner = "espflash --monitor"
|
||||
#rustflags = ["--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
|
||||
|
||||
[target.riscv32imc-esp-espidf]
|
||||
linker = "ldproxy"
|
||||
runner = "espflash --monitor"
|
||||
# Future - necessary for the experimental "native build" of esp-idf-sys with ESP32C3. See also https://github.com/ivmarkov/embuild/issues/16
|
||||
# For ESP-IDF 5 add `espidf_time64` and for earlier versions - remove this flag: https://github.com/esp-rs/rust/issues/110
|
||||
#rustflags = ["-C", "default-linker-libraries"]
|
||||
rustflags = ["--cfg", "espidf_time64", "-C", "default-linker-libraries"]
|
||||
|
||||
[unstable]
|
||||
|
||||
build-std = ["std", "panic_abort"]
|
||||
#build-std-features = ["panic_immediate_abort"] # Required for older ESP-IDF versions without a realpath implementation
|
||||
|
||||
[env]
|
||||
# Note: these variables are not used when using pio builder (`cargo build --features pio`)
|
||||
# Builds against ESP-IDF stable (v4.4)
|
||||
ESP_IDF_VERSION = "release/v5.1"
|
||||
# Builds against ESP-IDF master (mainline)
|
||||
#ESP_IDF_VERSION = "master"
|
||||
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
/.vscode
|
||||
/.embuild
|
||||
/target
|
||||
/Cargo.lock
|
||||
/cfg.toml
|
||||
49
Cargo.toml
Normal file
49
Cargo.toml
Normal file
@@ -0,0 +1,49 @@
|
||||
[package]
|
||||
name = "esp32-c3-rust-std"
|
||||
version = "0.1.0"
|
||||
authors = ["Gerard CL <gerardcl@gmail.com>"]
|
||||
edition = "2021"
|
||||
resolver = "2"
|
||||
|
||||
[profile.release]
|
||||
#opt-level = "s"
|
||||
codegen-units = 1 # LLVM can perform better optimizations using a single thread
|
||||
debug = 2
|
||||
debug-assertions = false
|
||||
incremental = false
|
||||
lto = 'fat'
|
||||
opt-level = 's'
|
||||
overflow-checks = false
|
||||
|
||||
[profile.dev]
|
||||
debug = true # Symbols are nice and they don't increase the size on Flash
|
||||
opt-level = "z"
|
||||
|
||||
[features]
|
||||
default = ["native"]
|
||||
native = ["esp-idf-sys/native"]
|
||||
|
||||
[dependencies]
|
||||
esp-idf-sys = { version = "0.36.1", features = ["binstart"] }
|
||||
esp-idf-svc = { version = "0.51.0", features = ["experimental", "alloc", "std"] }
|
||||
esp-idf-hal = { version = "0.45.2", features = ["critical-section"]}
|
||||
ssd1306 = "0.10.0"
|
||||
display-interface = "0.5.0"
|
||||
display-interface-i2c = "0.5.0"
|
||||
display-interface-spi = "0.5.0"
|
||||
embedded-hal = "1.0.0"
|
||||
embedded-svc = "0.28.1"
|
||||
embedded-graphics = "0.8.1"
|
||||
embedded-graphics-core = "0.4.0"
|
||||
toml-cfg = { version = "0.2.0" }
|
||||
anyhow = "=1.0.69"
|
||||
log = { version = "0.4", features = [] }
|
||||
heapless = {version = "0.8", features = ["serde"]}
|
||||
spin = "0.10.0"
|
||||
#lazy_static = "1.5.0"
|
||||
# smol = "1.3"
|
||||
|
||||
[build-dependencies]
|
||||
embuild = { version = "0.33.1", features = ["elf"] }
|
||||
toml-cfg = { version = "0.2.0" }
|
||||
anyhow = "1"
|
||||
28
build.rs
Normal file
28
build.rs
Normal file
@@ -0,0 +1,28 @@
|
||||
use std::error::Error;
|
||||
|
||||
#[toml_cfg::toml_config]
|
||||
pub struct Config {
|
||||
#[default("")]
|
||||
wifi_ssid: &'static str,
|
||||
#[default("")]
|
||||
wifi_psk: &'static str,
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Box<dyn Error>> {
|
||||
// Check if the `cfg.toml` file exists and has been filled out.
|
||||
if !std::path::Path::new("cfg.toml").exists() {
|
||||
return Err("You need to create a `cfg.toml` file with your Wi-Fi credentials! Start from `cfg.toml.template`.".into());
|
||||
}
|
||||
|
||||
// The constant `CONFIG` is auto-generated by `toml_config`.
|
||||
let app_config = CONFIG;
|
||||
if app_config.wifi_ssid == "SET_ME" || app_config.wifi_psk == "SET_ME" {
|
||||
return Err("You need to set the Wi-Fi credentials in `cfg.toml`!".into());
|
||||
}
|
||||
|
||||
// Necessary because of this issue: https://github.com/rust-lang/cargo/issues/9641
|
||||
embuild::build::CfgArgs::output_propagated("ESP_IDF")?;
|
||||
embuild::build::LinkArgs::output_propagated("ESP_IDF")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
3
cfg.toml.template
Normal file
3
cfg.toml.template
Normal file
@@ -0,0 +1,3 @@
|
||||
[esp32-c3-rust-std]
|
||||
wifi_ssid = "SET_ME"
|
||||
wifi_psk = "SET_ME"
|
||||
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
@@ -0,0 +1,2 @@
|
||||
[toolchain]
|
||||
channel = "nightly"
|
||||
16
sdkconfig.defaults
Normal file
16
sdkconfig.defaults
Normal file
@@ -0,0 +1,16 @@
|
||||
# Rust often needs a bit of an extra main task stack size compared to C (the default is 3K)
|
||||
CONFIG_ESP_MAIN_TASK_STACK_SIZE=7000
|
||||
#CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=4096
|
||||
|
||||
# Use this to set FreeRTOS kernel tick frequency to 1000 Hz (100 Hz by default).
|
||||
# This allows to use 1 ms granuality for thread sleeps (10 ms by default).
|
||||
#CONFIG_FREERTOS_HZ=1000
|
||||
|
||||
# Workaround for https://github.com/espressif/esp-idf/issues/7631
|
||||
#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n
|
||||
#CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n
|
||||
|
||||
# NAPT demo (router)
|
||||
#CONFIG_LWIP_L2_TO_L3_COPY=y
|
||||
#CONFIG_LWIP_IP_FORWARD=y
|
||||
#CONFIG_LWIP_IPV4_NAPT=y
|
||||
3
src/lib.rs
Normal file
3
src/lib.rs
Normal file
@@ -0,0 +1,3 @@
|
||||
pub mod modules;
|
||||
pub mod server;
|
||||
pub mod settings;
|
||||
202
src/main.rs
Normal file
202
src/main.rs
Normal file
@@ -0,0 +1,202 @@
|
||||
use esp_idf_hal::{
|
||||
delay::FreeRtos, gpio::{Gpio8, Gpio9, Gpio5, Gpio6}, i2c::{self, I2C0, I2c, I2cConfig, I2cDriver}, peripheral::{Peripheral, PeripheralRef}, prelude::Peripherals,
|
||||
};
|
||||
use esp_idf_svc::{
|
||||
eventloop::EspSystemEventLoop,
|
||||
nvs::EspDefaultNvsPartition,
|
||||
espnow::{self, EspNow, PeerInfo},
|
||||
};
|
||||
use esp_idf_sys::{self as _, soc_periph_temperature_sensor_clk_src_t_TEMPERATURE_SENSOR_CLK_SRC_DEFAULT};
|
||||
use log::*;
|
||||
use anyhow::{Error, Result};
|
||||
use esp32_c3_rust_std::modules::{display::{DisplayDriver, init_logger}, wifi::WifiDriver};
|
||||
use esp32_c3_rust_std::server::http::HttpServer;
|
||||
const OLED_I2C_ADDRESS: u8 = 0x3C;
|
||||
const BROADCAST_ADDR: [u8; 6] = [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF];
|
||||
|
||||
|
||||
fn setup_i2c(i2c_peripheral: I2C0, sda: Gpio5, scl: Gpio6) -> Result<I2cDriver<'static>, Error> {
|
||||
use esp_idf_hal::units::Hertz;
|
||||
let baudrate: Hertz = Hertz(400_000);
|
||||
let i2c_config: I2cConfig = I2cConfig::new()
|
||||
.baudrate(baudrate);
|
||||
|
||||
let i2c_driver: I2cDriver = I2cDriver::new(
|
||||
i2c_peripheral,
|
||||
sda,
|
||||
scl,
|
||||
&i2c_config,
|
||||
)?;
|
||||
|
||||
Ok(i2c_driver)
|
||||
}
|
||||
|
||||
use std::env;
|
||||
use std::sync::mpsc;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
enum AppEvent {
|
||||
EspNowRecv { mac: [u8; 6], data: Vec<u8> },
|
||||
SendDiscovery
|
||||
}
|
||||
|
||||
fn main() -> Result<(), Error> {
|
||||
esp_idf_sys::link_patches(); //Needed for esp32-rs
|
||||
unsafe { env::set_var("RUST_BACKTRACE", "1");};
|
||||
info!("Starting ESP32 C3 setup");
|
||||
|
||||
let peripherals = Peripherals::take()?;
|
||||
let sys_loop = EspSystemEventLoop::take()?;
|
||||
let nvs = EspDefaultNvsPartition::take()?;
|
||||
//setup OLED
|
||||
let i2c0 = peripherals.i2c0;
|
||||
let scl_pin = peripherals.pins.gpio6;
|
||||
let sda_pin = peripherals.pins.gpio5;
|
||||
|
||||
init_logger();
|
||||
let i2c_driver = setup_i2c(i2c0, sda_pin, scl_pin)?;
|
||||
let mut display_logger = DisplayDriver::new(i2c_driver, OLED_I2C_ADDRESS);
|
||||
|
||||
// wifi module as client
|
||||
let mut wifi = WifiDriver::new(peripherals.modem, sys_loop, nvs);
|
||||
wifi.configure();
|
||||
wifi.start();
|
||||
|
||||
// server example for accepting HTTP requests
|
||||
let mut server = HttpServer::new();
|
||||
server.set_handlers();
|
||||
info!("HTTP Server accepting connections");
|
||||
|
||||
//ESPNOW
|
||||
|
||||
//channel for event
|
||||
let (tx, rx) = mpsc::channel::<AppEvent>();
|
||||
//espnow
|
||||
let esp_now = EspNow::take()?;
|
||||
//esp_now.set_channel(CHANNEL);
|
||||
//thread receiver
|
||||
let tx_callback = tx.clone();
|
||||
esp_now.register_recv_cb(move |mac, data|
|
||||
{
|
||||
let event = AppEvent::EspNowRecv {
|
||||
mac: *mac.src_addr,
|
||||
data: data.to_vec()
|
||||
};
|
||||
let _ = tx_callback.send(event);
|
||||
|
||||
})?;
|
||||
if !esp_now.peer_exists(BROADCAST_ADDR)? {
|
||||
let broadcast_peer = PeerInfo {
|
||||
peer_addr: BROADCAST_ADDR,
|
||||
encrypt: false,
|
||||
..Default::default()
|
||||
};
|
||||
esp_now.add_peer(broadcast_peer)?;
|
||||
info!("Added broadcast peer");
|
||||
}
|
||||
|
||||
//thread discover
|
||||
let tx_timer = tx.clone();
|
||||
thread::spawn(move || {
|
||||
loop {
|
||||
thread::sleep(Duration::from_secs(5));
|
||||
let _ = tx_timer.send(AppEvent::SendDiscovery);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
let mut wifi_static = wifi.driver.ap_netif().get_ip_info().unwrap();
|
||||
// if we reached 10 times, we will print the ip address again
|
||||
let mut count = 0;
|
||||
loop {
|
||||
match rx.recv_timeout(Duration::from_millis(1000)) {
|
||||
Ok(AppEvent::EspNowRecv { mac, data }) => {
|
||||
let msg = String::from_utf8_lossy(&data);
|
||||
info!("RX from {:02X?}", mac);
|
||||
if !esp_now.peer_exists(mac)? {
|
||||
info!("New peer connected: {:02X?}", mac);
|
||||
let new_peer = PeerInfo {
|
||||
peer_addr: mac,
|
||||
encrypt: false,
|
||||
..Default::default()
|
||||
};
|
||||
if let Ok(_) = esp_now.add_peer(new_peer) {
|
||||
info!("Added peer: {:02X?}", mac);
|
||||
let _ = esp_now.send(mac, b"Hello Peer!");
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(AppEvent::SendDiscovery) => {
|
||||
info!("Sending broadcast discovery message");
|
||||
let _ = esp_now.send(BROADCAST_ADDR, b"DISCOVER-ALPHA");
|
||||
}
|
||||
Err(mpsc::RecvTimeoutError::Timeout) => {
|
||||
let wifi_info: esp_idf_svc::ipv4::IpInfo = wifi.driver.sta_netif().get_ip_info().unwrap();
|
||||
if wifi_static != wifi_info || count > 21{
|
||||
wifi_static = wifi_info;
|
||||
info!(
|
||||
"{:?}",
|
||||
wifi_info.ip);
|
||||
info!(
|
||||
"{:?}",
|
||||
wifi_info.subnet.gateway);
|
||||
count = 0;
|
||||
}
|
||||
count += 1;
|
||||
if let Ok(ref mut display_logger) = &mut display_logger{
|
||||
display_logger.draw_logs();
|
||||
} else {
|
||||
info!("Failed to draw_logs")
|
||||
}
|
||||
|
||||
}
|
||||
Err(mpsc::RecvTimeoutError::Disconnected) => {
|
||||
info!("Disconnected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// let wifi_info: esp_idf_svc::ipv4::IpInfo = wifi.driver.sta_netif().get_ip_info().unwrap();
|
||||
// if wifi_static != wifi_info || count > 10{
|
||||
// wifi_static = wifi_info;
|
||||
// info!(
|
||||
// "{:?}",
|
||||
// wifi_info.ip
|
||||
// //wifi.driver.sta_netif().get_ip_info().unwrap()
|
||||
// );
|
||||
// info!(
|
||||
// "{:?}",
|
||||
// wifi_info.subnet.gateway
|
||||
// //wifi.driver.sta_netif().get_ip_info().unwrap()
|
||||
// );
|
||||
// count = 0;
|
||||
// }
|
||||
// count += 1;
|
||||
// // info!(
|
||||
// // "{:?}",
|
||||
// // wifi_info.ip
|
||||
// // //wifi.driver.sta_netif().get_ip_info().unwrap()
|
||||
// // );
|
||||
// // info!(
|
||||
// // "{:?}",
|
||||
// // wifi_info.subnet.gateway
|
||||
// // //wifi.driver.sta_netif().get_ip_info().unwrap()
|
||||
// // );
|
||||
// // if let Some(dns) = wifi_info.dns {
|
||||
// // info!("{:?}", dns);
|
||||
// // }
|
||||
// //display::draw_logs(&mut display);
|
||||
// if let Ok(ref mut display_logger) = &mut display_logger{
|
||||
// display_logger.draw_logs();
|
||||
// } else {
|
||||
// info!("Failed to draw_logs")
|
||||
// }
|
||||
// //display_logger.draw_logs();
|
||||
// FreeRtos::delay_ms(1000);
|
||||
// //sleep(Duration::new(2, 0));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
204
src/modules/display.rs
Normal file
204
src/modules/display.rs
Normal file
@@ -0,0 +1,204 @@
|
||||
use log::{LevelFilter, Metadata, Record, Level};
|
||||
use core::fmt::{Debug, Write};
|
||||
use spin::Mutex;
|
||||
use heapless::{String, Vec};
|
||||
use embedded_hal::i2c::{ErrorType, I2c};
|
||||
|
||||
use ssd1306::{
|
||||
prelude::*,
|
||||
I2CDisplayInterface,
|
||||
Ssd1306,
|
||||
mode::BufferedGraphicsMode,
|
||||
rotation::DisplayRotation,
|
||||
};
|
||||
|
||||
use embedded_graphics::{
|
||||
mono_font::{MonoTextStyle, ascii::{FONT_4X6, FONT_5X7}},//, iso_8859_2::{self, FONT_4X6}},
|
||||
pixelcolor::BinaryColor,
|
||||
prelude::*,
|
||||
text::{Alignment, Text},
|
||||
};
|
||||
|
||||
const MAX_LOG_LINES: usize = 4;
|
||||
//const MAX_CHARS_PER_LINE: usize = 128/6;
|
||||
//const SCROLL_SPEED: u32 = 100;
|
||||
//const SCROLL_POSITIONS: Mutex<[u32; MAX_LOG_LINES]> = Mutex::new([0u32; MAX_LOG_LINES]);
|
||||
//const LAST_SCROLL_TIME: Mutex<u32> = Mutex::new(0);
|
||||
const MAX_LINE_LENGTH: usize = 19;
|
||||
const START_Y_OFFSET: i32 = 8;
|
||||
static LOG_BUFFER: Mutex<Vec<String<MAX_LINE_LENGTH>, MAX_LOG_LINES>> = Mutex::new(Vec::new());
|
||||
|
||||
pub type OledDisplay<I> = Ssd1306<
|
||||
I2CInterface<I>,
|
||||
DisplaySize128x64,
|
||||
BufferedGraphicsMode<DisplaySize128x64>>;
|
||||
|
||||
pub type OledInterface<I> = Ssd1306<
|
||||
I2CInterface<I>,
|
||||
//DisplaySize128x64,
|
||||
//DisplaySize64x32,
|
||||
DisplaySize128x32,
|
||||
BufferedGraphicsMode<DisplaySize128x32>>;
|
||||
|
||||
|
||||
pub struct DisplayDriver<I>
|
||||
where
|
||||
I: I2c + ErrorType,
|
||||
<I as ErrorType>::Error: core::fmt::Debug,
|
||||
{
|
||||
display: OledInterface<I>,
|
||||
}
|
||||
|
||||
impl<I> DisplayDriver<I>
|
||||
where
|
||||
I: I2c + ErrorType,
|
||||
<I as ErrorType>::Error: core::fmt::Debug,
|
||||
{
|
||||
pub fn new(i2c_driver: I, address: u8) -> Result<Self, <I as ErrorType>::Error> {
|
||||
let interface = I2CDisplayInterface::new_custom_address(i2c_driver, address);
|
||||
log::info!("Driver address: {:?} ", address);
|
||||
//let interface = I2CDisplayInterface::new(i2c_driver);
|
||||
let mut display = Ssd1306::new(
|
||||
interface,
|
||||
//DisplaySize128x64,
|
||||
//DisplaySize64x32,
|
||||
DisplaySize128x32,
|
||||
DisplayRotation::Rotate0)
|
||||
.into_buffered_graphics_mode();
|
||||
|
||||
//display.init().expect("display init failed");
|
||||
if let Err(e) = display.init() {
|
||||
log::error!("Display init failed: {:?}", e);
|
||||
//return Err(e);
|
||||
}
|
||||
display.clear(BinaryColor::Off).expect("display clear failed");
|
||||
display.flush().expect("display flush failed");
|
||||
|
||||
Ok(Self { display })
|
||||
}
|
||||
|
||||
pub fn draw_logs(&mut self) {
|
||||
if let Err(e) = self.display.clear(BinaryColor::Off) {
|
||||
log::error!("draw error: failed to clear display: {:?}", e);
|
||||
return;
|
||||
}
|
||||
|
||||
let buffer = LOG_BUFFER.lock();
|
||||
let text_style = MonoTextStyle::new(&FONT_5X7, BinaryColor::On);
|
||||
//let max_chars = MAX_CHARS_PER_LINE;
|
||||
|
||||
for (i, line) in buffer.iter().enumerate() {
|
||||
let y = START_Y_OFFSET + (i as i32) * FONT_5X7.character_size.height as i32;
|
||||
// let substrings = Self::create_scroll_substring(line, max_chars);
|
||||
|
||||
// for (j, substr) in substrings.iter().enumerate() {
|
||||
// let x = 127 - (j * 6) as i32; // Position each substring horizontally
|
||||
// if let Err(e) = Text::with_alignment(substr, Point::new(x, y), text_style, Alignment::Right)
|
||||
// .draw(&mut self.display) {
|
||||
// log::error!("draw error: failed to draw text: {:?}", e);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
if let Err(e) = Text::with_alignment(line, Point::new(99, y), text_style, Alignment::Right)
|
||||
.draw(&mut self.display) {
|
||||
log::error!("draw error: failed to draw text: {:?}",e);
|
||||
break;
|
||||
}
|
||||
if let Err(e) = self.display.flush() {
|
||||
log::error!("draw error: failed to flush display: {:?}",e);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn wrap_text_to_lines(text: &str, max_len: usize) -> Vec<String<MAX_LINE_LENGTH>, MAX_LOG_LINES> {
|
||||
let mut lines: Vec<String<MAX_LINE_LENGTH>, MAX_LOG_LINES> = Vec::<String<MAX_LINE_LENGTH>, MAX_LOG_LINES>::new();
|
||||
let mut current_pos = 0;
|
||||
while current_pos < text.len() {
|
||||
let end_pos = (current_pos + max_len).min(text.len());
|
||||
let slice = &text[current_pos..end_pos];
|
||||
if let Ok(line) = String::try_from(slice) {
|
||||
if lines.push(line).is_err() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
current_pos = end_pos;
|
||||
}
|
||||
lines
|
||||
}
|
||||
fn create_scroll_substring(line: &String<MAX_LINE_LENGTH>, max_chars: usize) -> Vec<String<MAX_LINE_LENGTH>, 10> {
|
||||
let mut substrings: Vec<String<MAX_LINE_LENGTH>, 10> = Vec::<String<MAX_LINE_LENGTH>, 10>::new();
|
||||
|
||||
if line.len() <= max_chars {
|
||||
let mut substr = String::new();
|
||||
let _ = write!(&mut substr, "{}", line);
|
||||
let _ = substrings.push(substr);
|
||||
return substrings;
|
||||
}
|
||||
|
||||
//let line_chars: Vec<char, 21> = line.chars().collect();
|
||||
let mut extended_line: String<MAX_LINE_LENGTH> = String::new();
|
||||
let _ = write!(&mut extended_line, "{} ", line);
|
||||
let total_chars = extended_line.len();
|
||||
for i in 0..(total_chars + max_chars) {
|
||||
let mut substr = String::new();
|
||||
for j in 0..max_chars {
|
||||
let char_index = (i*j) % total_chars;
|
||||
if char_index < extended_line.len() {
|
||||
let ch = extended_line.chars().nth(char_index).unwrap();
|
||||
let _ = write!(&mut substr, "{}", ch);
|
||||
}
|
||||
}
|
||||
let _ = substrings.push(substr);
|
||||
}
|
||||
substrings
|
||||
|
||||
//let mut line_chars: Vec<char,21> = Vec::new();
|
||||
// for ch in line.chars() {
|
||||
// if line_chars.push(ch).is_err() {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// let total_chars = line_chars.len();
|
||||
|
||||
// for i in 0..(total_chars + max_chars) {
|
||||
// let mut substr = String::new();
|
||||
// for j in 0..max_chars {
|
||||
// let char_index = (i+ j) % total_chars;
|
||||
// let _ = write!(&mut substr, "{}", line_chars[char_index]);
|
||||
// }
|
||||
// let _ = substrings.push(substr);
|
||||
// }
|
||||
// substrings
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
pub struct DisplayLogger;
|
||||
|
||||
impl log::Log for DisplayLogger {
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
metadata.level() <= Level::Info
|
||||
}
|
||||
|
||||
fn log(&self, record: &Record) {
|
||||
if self.enabled(record.metadata()) {
|
||||
println!("[{}] {}: {}", record.level(), record.target(), record.args());
|
||||
let mut line: String<MAX_LINE_LENGTH> = String::new();
|
||||
//let _ = write!(&mut line, "[{}] {}", record.level(), record.args());
|
||||
let _ = write!(&mut line, "{:?}", record.args());
|
||||
|
||||
let mut buffer = LOG_BUFFER.lock();
|
||||
if buffer.is_full() {
|
||||
buffer.remove(0);
|
||||
}
|
||||
let _ = buffer.push(line);
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
}
|
||||
pub fn init_logger() {
|
||||
static LOGGER: DisplayLogger = DisplayLogger;
|
||||
log::set_logger(&LOGGER).map(|()| log::set_max_level(LevelFilter::Info)).unwrap();
|
||||
}
|
||||
2
src/modules/mod.rs
Normal file
2
src/modules/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod wifi;
|
||||
pub mod display;
|
||||
65
src/modules/wifi.rs
Normal file
65
src/modules/wifi.rs
Normal file
@@ -0,0 +1,65 @@
|
||||
use embedded_svc::wifi::{ClientConfiguration, Configuration};
|
||||
use esp_idf_hal::{modem};//, peripherals::Peripherals};
|
||||
use esp_idf_svc::{
|
||||
eventloop::{EspEventLoop, System},
|
||||
nvs::{EspNvsPartition, NvsDefault},
|
||||
wifi::{EspWifi, NonBlocking},
|
||||
espnow::{EspNow, PeerInfo, SendStatus, ReceiveInfo, },
|
||||
};
|
||||
//use heapless::String as HString;
|
||||
//use std::str::FromStr;
|
||||
//use crate::{modules::wifi, settings::wifi::WifiConfig};
|
||||
use heapless;
|
||||
use crate::settings::wifi::WIFI_CONFIG;
|
||||
|
||||
pub struct WifiDriver<'a> {
|
||||
pub driver: EspWifi<'a>,
|
||||
}
|
||||
//use esp_idf_hal::modem;
|
||||
impl WifiDriver<'_> {
|
||||
pub fn new(
|
||||
//peripherals: Peripherals,
|
||||
//esp_idf_hal::peripherals::{Modem},
|
||||
mymodem: modem::Modem,
|
||||
sys_loop: EspEventLoop<System>,
|
||||
nvs: EspNvsPartition<NvsDefault>,
|
||||
) -> WifiDriver<'static> {
|
||||
WifiDriver {
|
||||
driver: EspWifi::new(mymodem, sys_loop, Some(nvs)).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn configure(&mut self) {
|
||||
//let wifi_config = WifiConfig::load_or_default("wifi_config.toml").unwrap();
|
||||
//let wifi_config = WifiConfig::Config;
|
||||
let heapless_ssid_str = heapless::String::<32>::try_from(WIFI_CONFIG.wifi_ssid).expect("SSID too long");
|
||||
let heapless_psk_str = heapless::String::<64>::try_from(WIFI_CONFIG.wifi_psk).expect("PSK too long");
|
||||
//let wifi_config = WifiConfig::new(
|
||||
// heapless_ssid_str,
|
||||
// heapless_psk_str,
|
||||
//);
|
||||
self.driver
|
||||
.set_configuration(&Configuration::Client(ClientConfiguration {
|
||||
ssid: heapless_ssid_str,
|
||||
password: heapless_psk_str,
|
||||
..Default::default()
|
||||
}))
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub fn start(&mut self) {
|
||||
self.driver.start().unwrap();
|
||||
self.driver.connect().unwrap();
|
||||
while !self.driver.is_connected().unwrap() {
|
||||
let config = self.driver.get_configuration().unwrap();
|
||||
println!(
|
||||
"Waiting for station SSID: {} on Channel #{:?}, with AuthMethod: {}, on BSSID: {:?}",
|
||||
config.as_client_conf_ref().unwrap().ssid,
|
||||
config.as_client_conf_ref().unwrap().channel,
|
||||
config.as_client_conf_ref().unwrap().auth_method,
|
||||
config.as_client_conf_ref().unwrap().bssid
|
||||
)
|
||||
}
|
||||
println!("Should be connected now");
|
||||
}
|
||||
}
|
||||
47
src/server/http.rs
Normal file
47
src/server/http.rs
Normal file
@@ -0,0 +1,47 @@
|
||||
use embedded_svc::{
|
||||
http::{Headers, Method},
|
||||
io::Write,
|
||||
};
|
||||
use esp_idf_svc::http::server::{Configuration, EspHttpServer};
|
||||
use log::info;
|
||||
|
||||
use super::template::render_html;
|
||||
|
||||
pub struct HttpServer<'a> {
|
||||
server: EspHttpServer<'a>,
|
||||
}
|
||||
|
||||
impl<'a> HttpServer<'a> {
|
||||
// Set the HTTP server
|
||||
pub fn new() -> HttpServer<'a> {
|
||||
info!("Server creating");
|
||||
|
||||
HttpServer {
|
||||
server: EspHttpServer::new(&Configuration::default()).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_handlers(&mut self) {
|
||||
info!("Server handlers setup");
|
||||
|
||||
self.server
|
||||
.fn_handler("/", Method::Get, move |request| -> Result<(), Box<dyn std::fmt::Debug>> {
|
||||
let html = render_html("World Of Hurt");
|
||||
let mut response = request.into_ok_response().map_err(|e| Box::new(e) as Box<dyn std::fmt::Debug>)?;
|
||||
response.write_all(html.as_bytes()).map_err(|e| Box::new(e) as Box<dyn std::fmt::Debug>)?;
|
||||
info!("request for /");
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
self.server
|
||||
.fn_handler("/host", Method::Get, move |request| -> Result<(), Box<dyn std::fmt::Debug>> {
|
||||
let html = render_html(request.host().unwrap_or_default());
|
||||
let mut response = request.into_ok_response().map_err(|e| Box::new(e) as Box<dyn std::fmt::Debug>)?;
|
||||
response.write_all(html.as_bytes()).map_err(|e| Box::new(e) as Box<dyn std::fmt::Debug>)?;
|
||||
info!("request for /host");
|
||||
Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
2
src/server/mod.rs
Normal file
2
src/server/mod.rs
Normal file
@@ -0,0 +1,2 @@
|
||||
pub mod http;
|
||||
mod template;
|
||||
21
src/server/template.rs
Normal file
21
src/server/template.rs
Normal file
@@ -0,0 +1,21 @@
|
||||
fn templated(content: impl AsRef<str>) -> String {
|
||||
format!(
|
||||
r#"
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>esp-rs web server</title>
|
||||
</head>
|
||||
<body>
|
||||
{}
|
||||
</body>
|
||||
</html>
|
||||
"#,
|
||||
content.as_ref()
|
||||
)
|
||||
}
|
||||
|
||||
pub fn render_html(content: impl AsRef<str>) -> String {
|
||||
templated(format!("Hello {}!", content.as_ref()))
|
||||
}
|
||||
1
src/settings/mod.rs
Normal file
1
src/settings/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod wifi;
|
||||
19
src/settings/wifi.rs
Normal file
19
src/settings/wifi.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
#[toml_cfg::toml_config]
|
||||
#[derive(Default)]
|
||||
pub struct WifiConfig {
|
||||
#[default("")]
|
||||
pub wifi_ssid: &'static str,
|
||||
#[default("")]
|
||||
pub wifi_psk: &'static str,
|
||||
}
|
||||
// impl WifiConfig {
|
||||
// pub fn new(
|
||||
// wifi_ssid: impl Into<heapless::String<32>>,
|
||||
// wifi_psk: impl Into<heapless::String<64>>,
|
||||
// ) -> Self {
|
||||
// Self {
|
||||
// wifi_ssid: wifi_ssid.into(),
|
||||
// wifi_psk: wifi_psk.into(),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
Reference in New Issue
Block a user