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