#[macro_use]
extern crate dotenv_codegen;
use esp_idf_sys as _;
const BUF_SIZE: u32 = 1024;
const TXD: i32 = 21;
const RXD: i32 = 20;
const RTS: i32 = esp_idf_sys::UART_PIN_NO_CHANGE;
const CTS: i32 = esp_idf_sys::UART_PIN_NO_CHANGE;
const UART_PORT_NUM: i32 = 0;
const UART_BAUD_RATE: i32 = 115200;
const UART_DATA_BITS: u32 = esp_idf_sys::uart_word_length_t_UART_DATA_8_BITS;
const USERNAME: &[u8] = b"root\n";
const PASSWORD: &[u8] = b"starfive\n";
const HTOP: &[u8] = b"htop\n";
static mut FBUFF: embedded_graphics_framebuf::FrameBuf<
embedded_graphics::pixelcolor::Rgb565,
320,
240,
{ 320 * 240 },
> = embedded_graphics_framebuf::FrameBuf(
[<embedded_graphics::pixelcolor::Rgb565 as embedded_graphics::prelude::RgbColor>::BLACK; {
320 * 240
}],
);
static mut UBUFF: [u8; BUF_SIZE as usize] = [0u8; BUF_SIZE as usize];
fn main() {
use display::*;
use embedded_graphics::{mono_font::MonoTextStyle, pixelcolor::Rgb565, prelude::*, text::Text};
use esp_idf_svc::{
eventloop::EspSystemEventLoop,
nvs::{EspNvsPartition, NvsDefault},
};
use profont::{PROFONT_12_POINT, PROFONT_14_POINT, PROFONT_24_POINT, PROFONT_7_POINT};
use server::*;
use std::collections::VecDeque;
use wifi::*;
let mut lines: VecDeque<String> = VecDeque::with_capacity(20);
esp_idf_sys::link_patches();
let sysloop = EspSystemEventLoop::take().unwrap();
let nvs = EspNvsPartition::<NvsDefault>::take().unwrap();
let peripherals =
esp_idf_hal::prelude::Peripherals::take().expect("Failed to take esp peripherals");
let mut display = display(
peripherals.pins.gpio7.into(),
peripherals.pins.gpio2.into(),
peripherals.pins.gpio10.into(),
peripherals.pins.gpio3.into(),
peripherals.pins.gpio6.into(),
peripherals.pins.gpio4.into(),
peripherals.spi2,
);
display.clear(Rgb565::WHITE).unwrap();
Text::new(
"Hello, spi world!",
display.bounding_box().center() - Size::new(display.bounding_box().size.width / 2 - 10, 24)
+ Size::new(0, 0),
MonoTextStyle::new(&PROFONT_24_POINT, Rgb565::YELLOW),
)
.draw(&mut display)
.unwrap();
Text::new(
"Connecting to wifi...",
display.bounding_box().center() - Size::new(display.bounding_box().size.width / 2 - 10, 0)
+ Size::new(0, 24),
MonoTextStyle::new(&PROFONT_12_POINT, Rgb565::BLUE),
)
.draw(&mut display)
.unwrap();
let (_, scan_results, ip_info) = wifi(peripherals.modem, sysloop, nvs).unwrap();
println!("{:?}", &scan_results);
Text::new(
"Connecting to wifi...",
display.bounding_box().center() - Size::new(display.bounding_box().size.width / 2 - 10, 0)
+ Size::new(0, 24),
MonoTextStyle::new(&PROFONT_12_POINT, Rgb565::WHITE),
)
.draw(&mut display)
.unwrap();
Text::new(
format!("WIFI Connected! IP: {:?}", ip_info.ip).as_str(),
display.bounding_box().center() - Size::new(display.bounding_box().size.width / 2 - 10, 0)
+ Size::new(0, 24),
MonoTextStyle::new(&PROFONT_12_POINT, Rgb565::BLACK),
)
.draw(&mut display)
.unwrap();
Text::new(
"What next?",
display.bounding_box().center() - Size::new(display.bounding_box().size.width / 2 - 10, 0)
+ Size::new(0, 44),
MonoTextStyle::new(&PROFONT_14_POINT, Rgb565::YELLOW),
)
.draw(&mut display)
.unwrap();
let _ = server().unwrap();
let uart_config = esp_idf_sys::uart_config_t {
baud_rate: UART_BAUD_RATE,
data_bits: UART_DATA_BITS,
parity: 0,
stop_bits: 1,
flow_ctrl: 0,
rx_flow_ctrl_thresh: 122,
__bindgen_anon_1: Default::default(),
};
esp_idf_sys::esp!(unsafe {
esp_idf_sys::uart_driver_install(
UART_PORT_NUM,
(BUF_SIZE * 2).try_into().unwrap(),
0,
0,
std::ptr::null_mut(),
0,
)
})
.unwrap();
esp_idf_sys::esp!(unsafe { esp_idf_sys::uart_param_config(UART_PORT_NUM, &uart_config) })
.unwrap();
esp_idf_sys::esp!(unsafe { esp_idf_sys::uart_set_pin(UART_PORT_NUM, TXD, RXD, RTS, CTS) })
.unwrap();
let mut len;
let mut fps = fps_counter::FPSCounter::default();
let fbuff = unsafe { &mut FBUFF };
let ubuff = unsafe { &mut UBUFF };
let mut tick;
loop {
tick = fps.tick();
esp_idf_sys::esp!(unsafe {
len = esp_idf_sys::uart_read_bytes(
UART_PORT_NUM,
ubuff.as_mut_ptr() as *mut std::ffi::c_void,
BUF_SIZE,
1,
);
0
})
.unwrap();
if len > 0 {
let data = std::str::from_utf8(&ubuff[0..usize::try_from(len).unwrap()]).unwrap();
for line in data.split("\n") {
if lines.len() >= 19 {
lines.pop_front();
}
lines.push_back(line.to_string());
}
if data.to_ascii_lowercase().contains("starfive login:") {
let username = std::ffi::CString::new(USERNAME).unwrap();
esp_idf_sys::esp!(unsafe {
esp_idf_sys::uart_write_bytes(
UART_PORT_NUM,
username.as_ptr() as *const std::ffi::c_void,
USERNAME.len(),
);
0
})
.unwrap();
}
if data.to_ascii_lowercase().contains("password:") {
let password = std::ffi::CString::new(PASSWORD).unwrap();
esp_idf_sys::esp!(unsafe {
esp_idf_sys::uart_write_bytes(
UART_PORT_NUM,
password.as_ptr() as *const std::ffi::c_void,
PASSWORD.len(),
);
0
})
.unwrap();
}
if data.to_ascii_lowercase().contains("root@starfive:") {
let htop = std::ffi::CString::new(HTOP).unwrap();
esp_idf_sys::esp!(unsafe {
esp_idf_sys::uart_write_bytes(
UART_PORT_NUM,
htop.as_ptr() as *const std::ffi::c_void,
HTOP.len(),
);
0
})
.unwrap();
}
}
if lines.len() > 0 {
fbuff.clear(Rgb565::WHITE).unwrap();
for (i, line) in lines.iter().enumerate() {
Text::new(
line.as_str(),
embedded_graphics::prelude::Point::new(2, (19 + i * 12).try_into().unwrap()),
MonoTextStyle::new(&PROFONT_7_POINT, Rgb565::BLACK),
)
.draw(fbuff)
.unwrap();
}
Text::new(
format!("{:?} | {:?} fps", ip_info.ip, tick).as_str(),
Point::new(2, 7),
MonoTextStyle::new(&PROFONT_7_POINT, Rgb565::YELLOW),
)
.draw(fbuff)
.unwrap();
display
.write_raw(
0,
0,
320,
240,
embedded_graphics_framebuf::AsWords::as_words(fbuff),
)
.unwrap();
} else {
std::thread::sleep(std::time::Duration::from_millis(20));
}
}
}
mod server {
use anyhow::Error;
use embedded_svc::http::server::Method;
use embedded_svc::io::Write;
use esp_idf_svc::http::server::EspHttpServer;
pub fn server() -> Result<EspHttpServer, Error> {
let mut server = EspHttpServer::new(&Default::default())?;
server.fn_handler("/", Method::Get, |req| {
req
.into_ok_response()?
.write_all("Hello from Rust!".as_bytes())?;
Ok(())
})?;
Ok(server)
}
}
mod wifi {
use embedded_svc::ipv4::IpInfo;
use embedded_svc::wifi::{AccessPointInfo, ClientConfiguration, Configuration, Wifi};
use esp_idf_hal::{modem::Modem, peripheral::Peripheral};
use esp_idf_svc::netif::{EspNetif, EspNetifWait};
use esp_idf_svc::ping::EspPing;
use esp_idf_svc::sntp::EspSntp;
use esp_idf_svc::wifi::{EspWifi, WifiWait};
use esp_idf_svc::{
eventloop::EspSystemEventLoop,
nvs::{EspNvsPartition, NvsDefault},
};
use std::net::Ipv4Addr;
use std::time::Duration;
fn ping(gateway: &Ipv4Addr) -> anyhow::Result<()> {
println!("About to do some pings for {:?}", gateway);
let ping_summary = EspPing::default().ping(*gateway, &Default::default())?;
if ping_summary.transmitted != ping_summary.received {
println!("Pinging gateway {} resulted in timeouts", gateway);
}
println!("Pinging done");
Ok(())
}
pub fn wifi<'a>(
modem: impl Peripheral<P = Modem> + 'a,
sysloop: EspSystemEventLoop,
nvs: EspNvsPartition<NvsDefault>,
) -> anyhow::Result<(Box<EspWifi<'a>>, Vec<AccessPointInfo>, IpInfo)> {
let mut wifi = Box::new(EspWifi::new(modem, sysloop.clone(), Some(nvs))?);
log::info!("Wifi created, about to scan");
let ap_infos = wifi.scan()?;
let config = ClientConfiguration {
ssid: dotenv!("ESP32C3_VF2_SSID").into(),
password: dotenv!("ESP32C3_VF2_PASS").into(),
..Default::default()
};
log::info!("Starting wifi...");
wifi.set_configuration(&Configuration::Client(config))?;
wifi.start()?;
if !WifiWait::new(&sysloop)?
.wait_with_timeout(Duration::from_secs(20), || wifi.is_started().unwrap())
{
anyhow::bail!("Wifi did not start");
}
log::info!("Connecting wifi...");
wifi.connect()?;
if !EspNetifWait::new::<EspNetif>(wifi.sta_netif(), &sysloop)?.wait_with_timeout(
Duration::from_secs(20),
|| {
wifi.is_connected().unwrap()
&& wifi.sta_netif().get_ip_info().unwrap().ip != Ipv4Addr::new(0, 0, 0, 0)
},
) {
anyhow::bail!("Wifi did not connect or did not receive a DHCP lease");
}
let ip_info = wifi.sta_netif().get_ip_info()?;
log::info!("Wifi DHCP info: {:?}", ip_info);
ping(&ip_info.subnet.gateway)?;
EspSntp::new_default()?;
Ok((wifi, ap_infos, ip_info))
}
}
mod display {
use display_interface_spi::SPIInterfaceNoCS;
use embedded_hal::delay::DelayUs;
use esp_idf_hal::{delay, gpio, prelude::*, spi};
use mipidsi::{ColorOrder, Display as DisplayDriver, DisplayOptions, Orientation};
pub type Display<'a> = DisplayDriver<
SPIInterfaceNoCS<
spi::SpiDeviceDriver<'a, spi::SpiDriver<'a>>,
gpio::PinDriver<'a, gpio::AnyOutputPin, gpio::Output>,
>,
gpio::PinDriver<'a, gpio::AnyOutputPin, gpio::Output>,
mipidsi::models::ST7789,
>;
pub fn display<'a>(
mosi: gpio::AnyOutputPin,
cs: gpio::AnyOutputPin,
rst: gpio::AnyOutputPin,
dc: gpio::AnyOutputPin,
sck: gpio::AnyOutputPin,
backlight: gpio::AnyOutputPin,
spi_bus: spi::SPI2,
) -> Display<'a> {
let mut delay = delay::Ets {};
let di = SPIInterfaceNoCS::new(
spi::SpiDeviceDriver::new_single(
spi_bus,
sck,
mosi,
Option::<gpio::AnyIOPin>::None,
spi::Dma::Disabled,
Some(cs),
&spi::SpiConfig::new().baudrate(60.MHz().into()),
)
.unwrap(),
gpio::PinDriver::output(dc).unwrap(),
);
let reset = gpio::PinDriver::output(rst).unwrap();
let mut display = Display::st7789(di, reset);
delay.delay_us(0_u32).unwrap();
display
.init(
&mut delay,
DisplayOptions {
color_order: ColorOrder::Bgr,
orientation: Orientation::Landscape(true),
..Default::default()
},
)
.unwrap();
let mut backlight = gpio::PinDriver::output(backlight).unwrap();
backlight.set_high().unwrap();
display
}
}