Potential for LCD clock, time pulled from network
This commit is contained in:
+98
-21
@@ -1,14 +1,47 @@
|
||||
use std::ffi::CString;
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
use esp_idf_svc::eth::{BlockingEth, EspEth, EthDriver, SpiEthChipset};
|
||||
use esp_idf_svc::eventloop::EspSystemEventLoop;
|
||||
use esp_idf_svc::hal::delay::Ets;
|
||||
use esp_idf_svc::hal::i2c::{I2cConfig, I2cDriver};
|
||||
use esp_idf_svc::hal::peripherals::Peripherals;
|
||||
use esp_idf_svc::hal::prelude::*;
|
||||
use esp_idf_svc::hal::spi::{config::DriverConfig, Dma, SpiDriver};
|
||||
use esp_idf_svc::handle::RawHandle; // brings .handle() into scope for EspNetif
|
||||
use esp_idf_svc::handle::RawHandle;
|
||||
use esp_idf_svc::sntp::{EspSntp, SyncStatus};
|
||||
use hd44780_driver::HD44780;
|
||||
use log::info;
|
||||
use std::ffi::CString;
|
||||
use std::thread;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
// Row start addresses for 24x4 HD44780-compatible displays.
|
||||
// Common split, but NOT universal — check your display's datasheet.
|
||||
const ROW_ADDR: [u8; 4] = [0x00, 0x40, 0x18, 0x58];
|
||||
|
||||
fn lcd_set_cursor(
|
||||
lcd: &mut HD44780<impl hd44780_driver::bus::DataBus>,
|
||||
row: u8,
|
||||
col: u8,
|
||||
delay: &mut Ets,
|
||||
) {
|
||||
let addr = ROW_ADDR[row as usize] + col;
|
||||
lcd.set_cursor_pos(addr, delay).ok();
|
||||
}
|
||||
|
||||
fn lcd_print_line(
|
||||
lcd: &mut HD44780<impl hd44780_driver::bus::DataBus>,
|
||||
row: u8,
|
||||
text: &str,
|
||||
delay: &mut Ets,
|
||||
) {
|
||||
lcd_set_cursor(lcd, row, 0, delay);
|
||||
// pad/truncate to 24 chars so stale characters don't linger
|
||||
let mut line = text.to_string();
|
||||
line.truncate(24);
|
||||
while line.len() < 24 {
|
||||
line.push(' ');
|
||||
}
|
||||
lcd.write_str(&line, delay).ok();
|
||||
}
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
esp_idf_svc::sys::link_patches();
|
||||
@@ -20,49 +53,93 @@ fn main() -> anyhow::Result<()> {
|
||||
|
||||
let spi_driver = SpiDriver::new(
|
||||
peripherals.spi2,
|
||||
pins.gpio13, // SCLK
|
||||
pins.gpio11, // MOSI
|
||||
Some(pins.gpio12), // MISO
|
||||
pins.gpio13,
|
||||
pins.gpio11,
|
||||
Some(pins.gpio12),
|
||||
&DriverConfig {
|
||||
dma: Dma::Auto(4096), // required — default (no DMA) can't move full ethernet frames
|
||||
dma: Dma::Auto(4096),
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let eth_driver = EthDriver::new_spi(
|
||||
spi_driver,
|
||||
pins.gpio10, // INT
|
||||
Some(pins.gpio14), // CS
|
||||
Some(pins.gpio9), // RST
|
||||
pins.gpio10,
|
||||
Some(pins.gpio14),
|
||||
Some(pins.gpio9),
|
||||
SpiEthChipset::W5500,
|
||||
20.MHz().into(),
|
||||
Some(&[0x02, 0x00, 0x00, 0x12, 0x34, 0x56]), // locally-administered MAC
|
||||
None, // default PHY address
|
||||
Some(&[0x02, 0x00, 0x00, 0x12, 0x34, 0x56]),
|
||||
None,
|
||||
sysloop.clone(),
|
||||
)?;
|
||||
|
||||
let eth = EspEth::wrap(eth_driver)?;
|
||||
|
||||
// Set hostname BEFORE starting — esp-idf-svc's safe NetifConfiguration has no
|
||||
// hostname field, so this goes through the raw ESP-IDF binding. It must happen
|
||||
// before eth.start(), otherwise it won't be included in the DHCP request and
|
||||
// your router/OS will show a generic name instead (max 32 bytes).
|
||||
let hostname = CString::new("esp32-eth-device")?;
|
||||
unsafe {
|
||||
esp_idf_svc::sys::esp_netif_set_hostname(eth.netif().handle(), hostname.as_ptr());
|
||||
}
|
||||
|
||||
let mut eth = BlockingEth::wrap(eth, sysloop)?;
|
||||
|
||||
info!("Starting eth...");
|
||||
eth.start()?;
|
||||
info!("Waiting for DHCP lease...");
|
||||
eth.wait_netif_up()?;
|
||||
|
||||
let ip_info = eth.eth().netif().get_ip_info()?;
|
||||
info!("Got IP: {:?}", ip_info);
|
||||
|
||||
// --- SNTP time sync ---
|
||||
info!("Starting SNTP sync...");
|
||||
let sntp = EspSntp::new_default()?;
|
||||
while sntp.get_sync_status() != SyncStatus::Completed {
|
||||
thread::sleep(Duration::from_millis(200));
|
||||
}
|
||||
info!("Time synced.");
|
||||
|
||||
// --- I2C LCD setup (SDA=GPIO17, SCL=GPIO16, adjust addr if needed) ---
|
||||
let i2c_config = I2cConfig::new().baudrate(100.kHz().into());
|
||||
let i2c = I2cDriver::new(peripherals.i2c0, pins.gpio17, pins.gpio16, &i2c_config)?;
|
||||
let mut delay = Ets;
|
||||
let mut lcd = HD44780::new_i2c(i2c, 0x27, &mut delay) // 0x27 is common PCF8574 default; some boards use 0x3F
|
||||
.map_err(|_| anyhow::anyhow!("LCD init failed"))?;
|
||||
lcd.reset(&mut delay).ok();
|
||||
lcd.clear(&mut delay).ok();
|
||||
|
||||
loop {
|
||||
let now = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs();
|
||||
let (date_str, time_str) = format_utc(now);
|
||||
|
||||
lcd_print_line(&mut lcd, 0, "ESP32-S3-ETH", &mut delay);
|
||||
lcd_print_line(&mut lcd, 1, &date_str, &mut delay);
|
||||
lcd_print_line(&mut lcd, 2, &time_str, &mut delay);
|
||||
lcd_print_line(&mut lcd, 3, "", &mut delay);
|
||||
|
||||
thread::sleep(Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
|
||||
// Minimal UTC breakdown without pulling in chrono — good enough for display.
|
||||
fn format_utc(unix_secs: u64) -> (String, String) {
|
||||
let days_since_epoch = unix_secs / 86400;
|
||||
let secs_of_day = unix_secs % 86400;
|
||||
let h = secs_of_day / 3600;
|
||||
let m = (secs_of_day % 3600) / 60;
|
||||
let s = secs_of_day % 60;
|
||||
|
||||
// Civil-from-days algorithm (Howard Hinnant), epoch = 1970-01-01
|
||||
let z = days_since_epoch as i64 + 719468;
|
||||
let era = if z >= 0 { z } else { z - 146096 } / 146097;
|
||||
let doe = (z - era * 146097) as u64;
|
||||
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
||||
let y = yoe as i64 + era * 400;
|
||||
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
||||
let mp = (5 * doy + 2) / 153;
|
||||
let d = doy - (153 * mp + 2) / 5 + 1;
|
||||
let mth = if mp < 10 { mp + 3 } else { mp - 9 };
|
||||
let y = if mth <= 2 { y + 1 } else { y };
|
||||
|
||||
(
|
||||
format!("{:04}-{:02}-{:02} UTC", y, mth, d),
|
||||
format!("{:02}:{:02}:{:02}", h, m, s),
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user