diff --git a/sdkconfig.defaults b/sdkconfig.defaults index 3ca3b5d..fffa100 100644 --- a/sdkconfig.defaults +++ b/sdkconfig.defaults @@ -8,3 +8,4 @@ CONFIG_ESP_MAIN_TASK_STACK_SIZE=7000 # Workaround for https://github.com/espressif/esp-idf/issues/7631 #CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n #CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n +CONFIG_LWIP_LOCAL_HOSTNAME=ticker-tape-rs diff --git a/src/display.rs b/src/display.rs index 45ebb5d..66c4201 100644 --- a/src/display.rs +++ b/src/display.rs @@ -1,19 +1,19 @@ +use anyhow::{bail, Error, Result}; +use esp_idf_hal::gpio::{Gpio0, Gpio1, Gpio2, Output, PinDriver}; +use font8x8::{UnicodeFonts, BASIC_FONTS}; use log::*; -use anyhow::{Result, Error, bail}; -use esp_idf_hal::gpio::{ - PinDriver, - Gpio0, Gpio1, Gpio2, - Output -}; -use font8x8::{BASIC_FONTS, UnicodeFonts}; use max7219::{connectors::*, *}; -pub type DisplayPins<'a> = - PinConnector, PinDriver<'a, Gpio1, Output>, PinDriver<'a, Gpio2, Output>>; +pub type DisplayPins<'a> = PinConnector< + PinDriver<'a, Gpio0, Output>, + PinDriver<'a, Gpio1, Output>, + PinDriver<'a, Gpio2, Output>, +>; pub struct DotDisplay<'a> { display: MAX7219>, display_is_on: bool, + brightness: u8, } impl DotDisplay<'_> { @@ -21,6 +21,7 @@ impl DotDisplay<'_> { let mut controller = DotDisplay { display, display_is_on: true, + brightness: 0, }; // Start the DotDisplay in a known state @@ -83,6 +84,7 @@ impl DotDisplay<'_> { let brightness = (brightness as f32 * 2.55) as u8; self.display.set_intensity(0, brightness)?; + self.brightness = brightness; Ok(()) } } @@ -101,8 +103,8 @@ impl Ticker<'_> { shift: 0, index: 0, len: 0, - message: [0; 100], - display: display, + message: [0; 100], + display: display, } } @@ -111,7 +113,7 @@ impl Ticker<'_> { if message.len() > self.message.len() { bail!("Message too long"); } - + self.len = message.len(); self.message[..self.len].copy_from_slice(message.as_bytes().try_into()?); Ok(()) @@ -123,18 +125,17 @@ impl Ticker<'_> { let mut glyph = BASIC_FONTS.get(c).unwrap_or(unknown); glyph.iter_mut().for_each(|x| { - *x = x.reverse_bits(); - let mut y = *x as isize; - - if shift < 0 { - y >>= shift.abs(); - } else { - y <<= shift; - } - - *x = y as u8; + *x = x.reverse_bits(); + let mut y = *x as isize; + + if shift < 0 { + y >>= shift.abs(); + } else { + y <<= shift; } - ); + + *x = y as u8; + }); glyph } @@ -145,17 +146,26 @@ impl Ticker<'_> { self.index = (self.index + 1) % self.len; } - let previous = if self.index > 0 {self.message[self.index - 1] as char} else {0 as char}; + let previous = if self.index > 0 { + self.message[self.index - 1] as char + } else { + 0 as char + }; let next = self.message[self.index] as char; - + let mut previous_glyph = Ticker::glyph(previous, self.shift); let next_glyph = Ticker::glyph(next, self.shift - 8); - + // OR two glyphs together - previous_glyph.iter_mut().enumerate().for_each(|(index, el)| *el |= next_glyph[index]); + previous_glyph + .iter_mut() + .enumerate() + .for_each(|(index, el)| *el |= next_glyph[index]); // Write to the display - self.display.write_display(&previous_glyph).expect("Failed to write dot-matrix"); + self.display + .write_display(&previous_glyph) + .expect("Failed to write dot-matrix"); self.shift += 1; } -} \ No newline at end of file +} diff --git a/src/led.rs b/src/led.rs index 1b13afb..8079d24 100644 --- a/src/led.rs +++ b/src/led.rs @@ -2,7 +2,8 @@ use anyhow::Result; // TODO: why use anyhow? use core::time::Duration; use esp_idf_hal::{ gpio::OutputPin, - rmt::{config::TransmitConfig, FixedLengthSignal, PinState, Pulse, TxRmtDriver, RmtChannel}, peripheral::Peripheral, + peripheral::Peripheral, + rmt::{config::TransmitConfig, FixedLengthSignal, PinState, Pulse, RmtChannel, TxRmtDriver}, }; pub use rgb::RGB8; // TODO: Find a better way to expose this @@ -15,8 +16,8 @@ impl<'d> WS2812RMT<'d> { // Rust ESP Board gpio2, ESP32-C3-DevKitC-02 gpio8 TODO: This is the wrong LED / KIT reference code!!! pub fn new( led: impl Peripheral

+ 'd, - channel: impl Peripheral

+ 'd, - )-> Result { + channel: impl Peripheral

+ 'd, + ) -> Result { let config = TransmitConfig::new().clock_divider(2); let tx = TxRmtDriver::new(channel, led, &config)?; Ok(Self { tx_rtm_driver: tx }) diff --git a/src/main.rs b/src/main.rs index 81e2d04..5ac1f36 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,17 @@ -use std::{str, sync::{Arc, Mutex}}; -use esp_idf_hal::{ - prelude::Peripherals, - gpio::PinDriver +use embedded_svc::{ + http::{Headers, Method}, + io::{Read, Write}, }; -use embedded_svc::{http::{Method, Headers}, io::{Write, Read}}; +use esp_idf_hal::{gpio::PinDriver, prelude::Peripherals}; use esp_idf_svc::http::server::{Configuration, EspHttpServer}; use esp_idf_sys as _; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported use log::*; use max7219::*; use serde::Serialize; +use std::{ + str, + sync::{Arc, Mutex}, +}; mod led; use led::{RGB8, WS2812RMT}; @@ -27,7 +30,6 @@ pub struct Config { wifi_psk: &'static str, } - fn main() -> anyhow::Result<()> { // It is necessary to call this function once. Otherwise some patches to the runtime // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 @@ -39,53 +41,72 @@ fn main() -> anyhow::Result<()> { // The constant CONFIG is auto-generated by toml_config let app_config = CONFIG; let mut led = WS2812RMT::new(peripherals.pins.gpio18, peripherals.rmt.channel0)?; - led.set_pixel(RGB8::new(50, 50, 0))?; + + led.set_pixel(RGB8::new(50, 50, 0))?; // TODO... // Associate to network and obtain DHCP IP - info!("Loading with credentials, ssid:{:?} psk:{:?}", app_config.wifi_ssid, app_config.wifi_psk); - let mut wifi = Wifi::init(peripherals.modem, &app_config.wifi_ssid, &app_config.wifi_psk); + info!( + "Loading with credentials, ssid:{:?} psk:{:?}", + app_config.wifi_ssid, app_config.wifi_psk + ); + let mut wifi = Wifi::init( + peripherals.modem, + &app_config.wifi_ssid, + &app_config.wifi_psk, + ); Wifi::start(&mut wifi)?; - - // Setup for the MAX7219 display + // Setup for the Ticker display let data = PinDriver::output(peripherals.pins.gpio0).unwrap(); let cs = PinDriver::output(peripherals.pins.gpio1).unwrap(); let sck = PinDriver::output(peripherals.pins.gpio2).unwrap(); let ticker_main = Arc::new(Mutex::new(Ticker::new( DotDisplay::from(MAX7219::from_pins(1, data, cs, sck).unwrap()).unwrap(), ))); - let ticker = ticker_main.clone(); - ticker.lock().unwrap().set_message("Hello world")?; - ticker.lock().unwrap().display.set_brightness(80).expect("Failed to set brightness"); + let ticker_message_handler = ticker_main.clone(); + let ticker_brightness_handler = ticker_main.clone(); + ticker_main.lock().unwrap().set_message("Hello world")?; + ticker_main + .lock() + .unwrap() + .display + .set_brightness(80) + .expect("Failed to set brightness"); + + let tick_speed_ms = Arc::new(Mutex::new(70 as u64)); + let tick_speed_handler = tick_speed_ms.clone(); // Set the HTTP server let mut server = EspHttpServer::new(&Configuration::default())?; - - // http:/// handler server.fn_handler("/", Method::Get, |request| { #[derive(Serialize)] struct TickerConfig { message: String, speed: u16, - brightness: u8 + brightness: u8, } - let config = TickerConfig{speed: 0, message: String::default(), brightness: 0}; + let config = TickerConfig { + speed: 0, + message: String::default(), + brightness: 0, + }; let mut response = request.into_ok_response()?; response.write_all(serde_json::to_string(&config)?.as_bytes())?; Ok(()) })?; - + // Speed - server.fn_handler("/speed", Method::Put, |mut req| { + server.fn_handler("/speed", Method::Put, move |mut req| { let len = req.content_len().unwrap_or(0) as usize; let mut buf = vec![0; len]; req.read_exact(&mut buf)?; - + if let Ok(as_str) = str::from_utf8(&buf) { - if let Ok(speed) = as_str.parse::() { + if let Ok(speed) = as_str.parse::() { if speed <= 1000 { - // TODO.... + info!("Set scroll speed to {:?}ms", speed); + *tick_speed_handler.lock().unwrap() = speed; req.into_ok_response()?; return Ok(()); } @@ -96,8 +117,29 @@ fn main() -> anyhow::Result<()> { })?; // Brightness - server.fn_handler("/speed", Method::Put, |mut req| { - // TODO + server.fn_handler("/brightness", Method::Put, move |mut req| { + let len = req.content_len().unwrap_or(0) as usize; + let mut buf = vec![0; len]; + req.read_exact(&mut buf)?; + + if let Ok(as_str) = str::from_utf8(&buf) { + if let Ok(brightness) = as_str.parse::() { + match ticker_brightness_handler + .lock() + .unwrap() + .display + .set_brightness(brightness) + { + Ok(_) => { + info!("Set brightness to {:?}%", brightness); + req.into_ok_response()?; + return Ok(()); + } + _ => (), // Fall through + } + } + } + Err(().into()) })?; @@ -106,19 +148,19 @@ fn main() -> anyhow::Result<()> { let len = req.content_len().unwrap_or(0) as usize; let mut buf = vec![0; len]; req.read_exact(&mut buf)?; - + if let core::result::Result::Ok(as_str) = str::from_utf8(&buf) { - // info!("Setting to {:?}", as_str); - ticker.lock().unwrap().set_message(as_str)?; + ticker_message_handler.lock().unwrap().set_message(as_str)?; + debug!("Setting ticker message to: {:?}", as_str); req.into_ok_response()?; return Ok(()); } Err(().into()) })?; - + let mut seed = 0; - loop { + loop { ticker_main.lock().unwrap().tick(); seed += 1; @@ -129,6 +171,8 @@ fn main() -> anyhow::Result<()> { led.set_pixel(RGB8::new(0, 50, 0))?; // Green } - std::thread::sleep(std::time::Duration::from_millis(70)); + std::thread::sleep(std::time::Duration::from_millis( + *tick_speed_ms.lock().unwrap(), + )); } } diff --git a/src/wifi.rs b/src/wifi.rs index bb8e97f..92696b6 100644 --- a/src/wifi.rs +++ b/src/wifi.rs @@ -1,8 +1,12 @@ -use log::*; -use anyhow::{Result, Error}; +use anyhow::{Error, Result}; use embedded_svc::wifi::{AuthMethod, ClientConfiguration, Configuration}; use esp_idf_hal::modem::Modem; -use esp_idf_svc::{eventloop::EspSystemEventLoop, nvs::EspDefaultNvsPartition, wifi::{EspWifi, BlockingWifi}}; +use esp_idf_svc::{ + eventloop::EspSystemEventLoop, + nvs::EspDefaultNvsPartition, + wifi::{BlockingWifi, EspWifi}, +}; +use log::*; pub struct Wifi { pub driver: BlockingWifi>, @@ -11,18 +15,18 @@ pub struct Wifi { impl Wifi { pub fn init(modem: Modem, ssid: &'static str, psk: &'static str) -> Self { let sysloop = EspSystemEventLoop::take().expect("Failed to take system event loop"); - let esp_wifi = EspWifi::new(modem, + let esp_wifi = EspWifi::new( + modem, sysloop.clone(), Some(EspDefaultNvsPartition::take().expect("Failed to take default nvs partition")), // ? Necessary? - ).expect("Failed to create esp wifi device"); - - let mut driver = BlockingWifi::wrap( - esp_wifi, - sysloop.clone() - ).expect("Failed to intialise BlockingWifi object"); + ) + .expect("Failed to create esp wifi device"); + let mut driver = BlockingWifi::wrap(esp_wifi, sysloop.clone()) + .expect("Failed to intialise BlockingWifi object"); - driver.wifi_mut() + driver + .wifi_mut() .set_configuration(&Configuration::Client(ClientConfiguration { ssid: ssid.into(), password: psk.into(), @@ -41,7 +45,7 @@ impl Wifi { let ip = self.driver.wifi().sta_netif().get_ip_info()?; info!("IP info: {:?}", ip); - + Ok(()) } -} \ No newline at end of file +}