diff --git a/Cargo.lock b/Cargo.lock index 5a8e571..6c7d320 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -38,6 +38,12 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + [[package]] name = "bitfield" version = "0.13.2" @@ -56,6 +62,15 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + [[package]] name = "bytemuck" version = "1.21.0" @@ -68,6 +83,18 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + [[package]] name = "cortex-m" version = "0.7.7" @@ -128,6 +155,15 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "cpufeatures" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3" +dependencies = [ + "libc", +] + [[package]] name = "crc-any" version = "2.5.0" @@ -143,6 +179,28 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "debug-helper" version = "0.3.13" @@ -192,12 +250,52 @@ dependencies = [ "embedded-io", ] +[[package]] +name = "der" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +dependencies = [ + "const-oid", + "zeroize", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + [[package]] name = "either" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest", + "ff", + "generic-array", + "group", + "hkdf", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + [[package]] name = "embedded-dma" version = "0.2.0" @@ -248,6 +346,16 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" +[[package]] +name = "ff" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ded41244b729663b1e574f1b4fb731469f69f79c17667b5d776b16cda0479449" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "frunk" version = "0.4.3" @@ -302,6 +410,28 @@ version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d758ba1b47b00caf47f24925c0074ecb20d6dfcffe7f6d53395c0465674841a" +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "hash32" version = "0.2.1" @@ -349,6 +479,24 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -368,6 +516,12 @@ dependencies = [ "either", ] +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + [[package]] name = "lock_api" version = "0.4.12" @@ -378,6 +532,12 @@ dependencies = [ "scopeguard", ] +[[package]] +name = "log" +version = "0.4.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + [[package]] name = "nb" version = "0.1.3" @@ -413,6 +573,16 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "elliptic-curve", + "primeorder", +] + [[package]] name = "panic-halt" version = "1.0.0" @@ -442,6 +612,15 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "280dc24453071f1b63954171985a0b0d30058d287960968b9b2aca264c8d4ee6" +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -532,12 +711,16 @@ dependencies = [ "defmt-serial", "embedded-hal 1.0.0", "fugit", + "heapless 0.8.0", + "log", "panic-halt", "rp2040-boot2", "rp2040-hal", "smart-leds", "usb-device", "usbd-serial", + "w5500-dhcp", + "w5500-mqtt", "waveshare-rp2040-zero", "ws2812-pio", ] @@ -575,6 +758,7 @@ dependencies = [ "rand_core", "rp2040-hal-macros", "rp2040-pac", + "rtic-monotonic", "usb-device", "vcell", "void", @@ -652,6 +836,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "0.9.0" @@ -673,6 +870,17 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "smart-leds" version = "0.3.0" @@ -706,6 +914,12 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + [[package]] name = "syn" version = "1.0.109" @@ -748,6 +962,12 @@ dependencies = [ "syn 2.0.90", ] +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + [[package]] name = "unicode-ident" version = "1.0.14" @@ -803,6 +1023,63 @@ dependencies = [ "vcell", ] +[[package]] +name = "w5500-dhcp" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a45835364e6e32f610a4aced5eef3d7cdffff94e9dc60bd8a92989e4442b2ea6" +dependencies = [ + "log", + "w5500-hl", +] + +[[package]] +name = "w5500-hl" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acf85c58c338dedab9c75a67f40564a0987356b6b6338335325b7bd51b5c535e" +dependencies = [ + "w5500-ll", +] + +[[package]] +name = "w5500-ll" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef4332c876837525e11b6949300fb7471ec8a2718a7df4fc66084f64feb28ca6" +dependencies = [ + "embedded-hal 0.2.7", +] + +[[package]] +name = "w5500-mqtt" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1e988d3c3e0f50cc618999424ad177e403113fbe1f1dc13600c466f123a46b" +dependencies = [ + "log", + "w5500-hl", + "w5500-tls", +] + +[[package]] +name = "w5500-tls" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3aac13decfc63c93230a2d2c54928ad78affb887ff3e43e48293f01d85179693" +dependencies = [ + "cfg-if", + "heapless 0.8.0", + "hkdf", + "hmac", + "log", + "p256", + "rand_core", + "sha2", + "subtle", + "w5500-hl", +] + [[package]] name = "waveshare-rp2040-zero" version = "0.8.0" @@ -828,3 +1105,9 @@ dependencies = [ "rp2040-hal", "smart-leds-trait", ] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" diff --git a/Cargo.toml b/Cargo.toml index d23881e..1027e2f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,10 +15,10 @@ cortex-m = "0.7" fugit = "0.3.7" cortex-m-rt = "0.7" rp2040-boot2 = { version = "0.3.0", optional = true } -rp2040-hal = "0.10.2" -waveshare-rp2040-zero = "0.8.0" -cortex-m-rtic = "1" -embedded-hal = "1.0.0" +rp2040-hal = {version = "0.10.2", features = ["rtic-monotonic"]} +waveshare-rp2040-zero = { version = "0.8.0", features = ["rt"] } +cortex-m-rtic = { version = "1", features = [] } +embedded-hal = {version = "1.0.0", features = []} usb-device = "0.3.2" @@ -30,3 +30,8 @@ usbd-serial = "0.2.2" smart-leds = "0.3.0" ws2812-pio = "0.8.0" + +w5500-dhcp = { version = "0.7", features = ["eh0", "log"] } +w5500-mqtt = { version = "0.4", features = ["eh0", "log"] } +log = "0.4.22" +heapless = "0.8" \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index c899de3..eead057 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,37 +1,105 @@ #![no_std] #![no_main] - use panic_halt as _; -#[rtic::app(device = waveshare_rp2040_zero::hal::pac, peripherals = true)] +#[rtic::app(device = waveshare_rp2040_zero::hal::pac, peripherals = true, dispatchers = [I2C0_IRQ])] mod app { - use core::iter::once;use fugit::MicrosDurationU32; - use rp2040_hal::Clock; - use rp2040_hal::gpio::{FunctionPio0, PullDown}; - use rp2040_hal::gpio::bank0::Gpio16; - use rp2040_hal::pac::PIO0; + use core::convert::TryInto; + use core::iter::once; + use cortex_m::delay::Delay; + use fugit::MicrosDurationU32; + use fugit::RateExtU32; + use rp2040_hal::gpio::bank0::{Gpio16, Gpio2, Gpio3, Gpio4, Gpio5}; + use rp2040_hal::gpio::{ + FunctionPio0, FunctionSio, FunctionSpi, Pin, PullDown, PullNone, PullUp, SioOutput, + }; use rp2040_hal::pio::SM0; use rp2040_hal::timer::CountDown; + use rp2040_hal::{Clock, Spi}; use smart_leds::{brightness, SmartLedsWrite}; use usbd_serial::SerialPort; + use waveshare_rp2040_zero::hal::prelude::*; + use waveshare_rp2040_zero::hal::spi::Enabled; use waveshare_rp2040_zero::{ - hal::{self, clocks::init_clocks_and_plls, timer::Alarm, watchdog::Watchdog, Sio}, + hal::{ + self, clocks::init_clocks_and_plls, timer::monotonic::Monotonic, timer::Alarm, + watchdog::Watchdog, Sio, + }, + pac::{PIO0, SPI0}, XOSC_CRYSTAL_FREQ, }; use usb_device::{class_prelude::*, prelude::*}; - use ws2812_pio::Ws2812; use waveshare_rp2040_zero::hal::pio::PIOExt; + use ws2812_pio::Ws2812; + + use super::*; + use core::fmt::Write; + use embedded_hal::digital::OutputPin; + use fugit::ExtU32; + use rp2040_hal::timer::Alarm0; + use w5500_dhcp::{ + hl::Hostname, + ll::{ + eh0::vdm_infallible_gpio::W5500, + eh0::MODE as W5500_MODE, + net::{Eui48Addr, Ipv4Addr, SocketAddrV4}, + LinkStatus, OperationMode, PhyCfg, Registers, Sn, + }, + Client as DhcpClient, + }; + use w5500_mqtt::{ + Client as MqttClient, ClientId, Event as MqttEvent, SRC_PORT as MQTT_SRC_PORT, + }; + const SYSCLK_HZ: u32 = 8_000_000; const SCAN_TIME_US: MicrosDurationU32 = MicrosDurationU32::millis(400); + const DHCP_SN: Sn = Sn::Sn0; + const MQTT_SN: Sn = Sn::Sn1; + + const MQTT_SERVER: SocketAddrV4 = SocketAddrV4::new(Ipv4Addr::new(192, 168, 0, 14), 1883); + const NAME: &str = "ambient1"; + const HOSTNAME: Hostname<'static> = Hostname::new_unwrapped(NAME); + const CLIENT_ID: ClientId<'static> = ClientId::new_unwrapped(NAME); + + #[monotonic(binds = TIMER_IRQ_0, default = true)] + type MyMono = Monotonic; + + #[inline(always)] + pub fn delay_ms(ms: u32) { + const CYCLES_PER_MILLIS: u32 = SYSCLK_HZ / 1000; + cortex_m::asm::delay(CYCLES_PER_MILLIS.saturating_mul(ms)); + } + + fn monotonic_secs() -> u32 { + app::monotonics::now() + .duration_since_epoch() + .to_secs() + .try_into() + .unwrap() + } #[shared] struct Shared { - timer: hal::Timer, - alarm: hal::timer::Alarm0, usb_dev: UsbDevice<'static, hal::usb::UsbBus>, serial: SerialPort<'static, hal::usb::UsbBus>, - ws: Ws2812, hal::gpio::Pin> + ws: Ws2812, hal::gpio::Pin>, + w5500: W5500< + Spi< + Enabled, + SPI0, + ( + Pin, + Pin, + Pin, + ), + >, + Pin, PullDown>, + >, + dhcp: DhcpClient<'static>, + mqtt: MqttClient<'static>, + dhcp_spawn_at: Option, + mqtt_spawn_at: Option, } #[local] @@ -55,8 +123,8 @@ mod app { &mut resets, &mut watchdog, ) - .ok() - .unwrap(); + .ok() + .unwrap(); let sio = Sio::new(c.device.SIO); let pins = waveshare_rp2040_zero::Pins::new( @@ -66,29 +134,53 @@ mod app { &mut resets, ); + // Set up our SPI pins into the correct mode + let spi_sclk: hal::gpio::Pin<_, hal::gpio::FunctionSpi, hal::gpio::PullNone> = + pins.gp2.reconfigure(); + let spi_mosi: hal::gpio::Pin<_, hal::gpio::FunctionSpi, hal::gpio::PullNone> = + pins.gp3.reconfigure(); + let spi_miso: hal::gpio::Pin<_, hal::gpio::FunctionSpi, hal::gpio::PullUp> = + pins.gp4.reconfigure(); + let spi_cs = pins.gp5.into_push_pull_output(); + let mut spi_reset = pins.gp7.into_push_pull_output(); + + // Create the SPI driver instance for the SPI0 device + let spi = hal::spi::Spi::<_, _, _, 8>::new(c.device.SPI0, (spi_mosi, spi_miso, spi_sclk)); + // Exchange the uninitialised SPI driver for an initialised one + let spi = spi.init( + &mut resets, + clocks.peripheral_clock.freq(), + 1.MHz(), + W5500_MODE, + ); + let mut w5500 = W5500::new(spi, spi_cs); + + w5500_dhcp::ll::eh0::reset(&mut spi_reset, &mut Delay::new(c.core.SYST, SYSCLK_HZ)) + .unwrap(); + //let timer = Timer::new(c.device.TIMER, &mut c.device.RESETS, &clocks); let dTIMER = c.device.TIMER; - let timer = cortex_m::singleton!(: hal::Timer = hal::Timer::new(dTIMER, &mut resets, &clocks)).unwrap(); + let timer = + cortex_m::singleton!(: hal::Timer = hal::Timer::new(dTIMER, &mut resets, &clocks)) + .unwrap(); let mut alarm = timer.alarm_0().unwrap(); - let count_down: CountDown<'static> = timer.count_down(); - - let (mut pio, sm0, _, _, _) = c.device.PIO0.split(&mut resets); - let ws: Ws2812, hal::gpio::Pin> = Ws2812::new( - // The onboard NeoPixel is attached to GPIO pin #16 on the Waveshare RP2040-Zero. - pins.neopixel.into_function(), - &mut pio, - sm0, - clocks.peripheral_clock.freq(), - count_down, - ); - - - - - - + let count_down: CountDown<'static> = timer.count_down(); + let (mut pio, sm0, _, _, _) = c.device.PIO0.split(&mut resets); + let ws: Ws2812< + PIO0, + SM0, + CountDown<'static>, + hal::gpio::Pin, + > = Ws2812::new( + // The onboard NeoPixel is attached to GPIO pin #16 on the Waveshare RP2040-Zero. + pins.neopixel.into_function(), + &mut pio, + sm0, + clocks.peripheral_clock.freq(), + count_down, + ); // Set up the USB driver let USBCTRL_REGS = c.device.USBCTRL_REGS; @@ -118,37 +210,210 @@ mod app { let _ = serial.write(b"Hello, World!\r\n"); - - let _ = alarm.schedule(SCAN_TIME_US); alarm.enable_interrupt(); - (Shared { timer: *timer, alarm, usb_dev, serial, ws}, Local {}, init::Monotonics()) + let mac: Eui48Addr = Eui48Addr::new(0x00, 0x08, 0xDC, 0x00, 0x00, 0x00); + + timer_irq::spawn().unwrap(); + + // continually initialize the W5500 until we link up + // since we are using power over Ethernet we know that if the device + // has power it also has an Ethernet cable connected. + let _phy_cfg: PhyCfg = 'outer: loop { + // sanity check W5500 communications + assert_eq!(w5500.version().unwrap(), w5500_dhcp::ll::VERSION); + + // load the MAC address we got from EEPROM + w5500.set_shar(&mac).unwrap(); + debug_assert_eq!(w5500.shar().unwrap(), mac); + + // wait for the PHY to indicate the Ethernet link is up + let mut attempts: u32 = 0; + serial.write(b"Polling for link up\r\n"); + const PHY_CFG: PhyCfg = PhyCfg::DEFAULT.set_opmdc(OperationMode::FullDuplex10bt); + w5500.set_phycfgr(PHY_CFG).unwrap(); + + const LINK_UP_POLL_PERIOD_MILLIS: u32 = 100; + const LINK_UP_POLL_ATTEMPTS: u32 = 50; + loop { + let phy_cfg: PhyCfg = w5500.phycfgr().unwrap(); + if phy_cfg.lnk() == LinkStatus::Up { + break 'outer phy_cfg; + } + if attempts >= LINK_UP_POLL_ATTEMPTS { + let mut data: heapless::String<16> = heapless::String::new(); + write!( + &mut data, + "Failed to link up in {} ms\r\n", + attempts * LINK_UP_POLL_PERIOD_MILLIS + ) + .unwrap(); + + serial.write(data.as_bytes()); + break; + } + delay_ms(LINK_UP_POLL_PERIOD_MILLIS); + attempts += 1; + } + + spi_reset.set_low().unwrap(); + delay_ms(1); + spi_reset.set_high().unwrap(); + delay_ms(3); + }; + let mut data: heapless::String<16> = heapless::String::new(); + write!(&mut data, "Done link up\n{}", _phy_cfg).unwrap(); + + serial.write(data.as_bytes()); + + let mut mqtt: MqttClient = MqttClient::new(MQTT_SN, MQTT_SRC_PORT, MQTT_SERVER); + mqtt.set_client_id(CLIENT_ID); + + let seed: u64 = u64::from(cortex_m::peripheral::SYST::get_current()) << 32 + | u64::from(cortex_m::peripheral::SYST::get_current()); + + let dhcp = DhcpClient::new(DHCP_SN, seed, mac, HOSTNAME); + dhcp.setup_socket(&mut w5500).unwrap(); + + // start the DHCP client + //dhcp_sn::spawn().unwrap(); + + // start the timeout tracker + //timeout_tracker::spawn().unwrap(); + + ( + Shared { + usb_dev, + serial, + ws, + w5500, + dhcp, + mqtt, + dhcp_spawn_at: None, + mqtt_spawn_at: None, + }, + Local {}, + init::Monotonics(Monotonic::new(*timer, alarm)), + ) + } + + #[task(shared = [dhcp_spawn_at, mqtt_spawn_at])] + fn timeout_tracker(mut cx: timeout_tracker::Context) { + timeout_tracker::spawn_after(1_u32.secs().into()).unwrap(); + + let now: u32 = monotonic_secs(); + + cx.shared.dhcp_spawn_at.lock(|dhcp_spawn_at| { + if let Some(then) = dhcp_spawn_at { + if now >= *then { + if dhcp_sn::spawn().is_err() { + log::error!("DHCP task is already spawned") + } + *dhcp_spawn_at = None; + } + } + }); + + cx.shared.mqtt_spawn_at.lock(|mqtt_spawn_at| { + if let Some(then) = mqtt_spawn_at { + if now >= *then { + if mqtt_sn::spawn().is_err() { + log::error!("MQTT task is already spawned") + } + *mqtt_spawn_at = None; + } + } + }); + } + + #[task(shared = [w5500, mqtt, mqtt_spawn_at], local = [])] + fn mqtt_sn(cx: mqtt_sn::Context) { + log::info!("[TASK] mqtt_sn"); + + (cx.shared.w5500, cx.shared.mqtt, cx.shared.mqtt_spawn_at).lock( + |w5500, mqtt, mqtt_spawn_at| { + loop { + let now: u32 = monotonic_secs(); + match mqtt.process(w5500, now) { + Ok(MqttEvent::CallAfter(secs)) => { + *mqtt_spawn_at = Some(now + secs); + break; + } + Ok(MqttEvent::ConnAck) => { + log::info!("[MQTT] ConnAck"); + // can subscribe to topics here + // not needed for this + } + Ok(MqttEvent::Publish(reader)) => { + log::warn!("should not get Publish never subscribed"); + reader.done().unwrap(); + } + Ok(MqttEvent::SubAck(_) | MqttEvent::UnSubAck(_)) => { + log::warn!("should not get (Un)SubAck, never (un)subscribed"); + } + Ok(MqttEvent::None) => { + { + let mut data: heapless::String<16> = heapless::String::new(); + write!(&mut data, "{:.1}", 42).unwrap(); + mqtt.publish(w5500, "test", data.as_bytes()).unwrap(); + } + *mqtt_spawn_at = Some(now + 5); + break; + } + Err(e) => { + log::error!("[MQTT] {e:?}"); + *mqtt_spawn_at = Some(now + 10); + break; + } + } + } + }, + ); + } + + #[task(shared = [w5500, dhcp, dhcp_spawn_at])] + fn dhcp_sn(cx: dhcp_sn::Context) { + log::info!("[TASK] dhcp_sn"); + + (cx.shared.w5500, cx.shared.dhcp, cx.shared.dhcp_spawn_at).lock( + |w5500, dhcp, dhcp_spawn_at| { + let leased_before: bool = dhcp.has_lease(); + let now: u32 = monotonic_secs(); + let spawn_after_secs: u32 = dhcp.process(w5500, now).unwrap(); + + let spawn_at: u32 = now + spawn_after_secs; + *dhcp_spawn_at = Some(spawn_at); + log::info!("[DHCP] spawning after {spawn_after_secs} seconds, at {spawn_at}"); + + // spawn MQTT task if bound + if dhcp.has_lease() && !leased_before && mqtt_sn::spawn().is_err() { + log::error!("MQTT task is already spawned") + } + }, + ) } #[task( - binds = TIMER_IRQ_0, priority = 1, - shared = [alarm, serial, ws], + shared = [serial, ws], local = [tog: bool = true], )] fn timer_irq(mut c: timer_irq::Context) { if *c.local.tog { - c.shared.ws.lock(|ws| ws.write(brightness(once((255, 0, 0).into()), 32)).unwrap()); + c.shared + .ws + .lock(|ws| ws.write(brightness(once((255, 0, 0).into()), 32)).unwrap()); c.shared.serial.lock(|serial| _ = serial.write(b"High\r\n")); - } else { - c.shared.ws.lock(|ws| ws.write(brightness(once((0, 0, 0).into()), 32)).unwrap()); + c.shared + .ws + .lock(|ws| ws.write(brightness(once((0, 0, 0).into()), 32)).unwrap()); c.shared.serial.lock(|serial| _ = serial.write(b"LOW\r\n")); } *c.local.tog = !*c.local.tog; - - let mut alarm = c.shared.alarm; - (alarm).lock(|a| { - a.clear_interrupt(); - let _ = a.schedule(SCAN_TIME_US); - }); + timer_irq::spawn_after(500.millis().into()).unwrap(); } #[task( @@ -190,6 +455,14 @@ mod app { } } }); - } -} \ No newline at end of file + + /// This is the W5500 interrupt. + /// + /// The only interrupts we should get are for the DHCP & MQTT sockets. + #[task(binds = SPI0_IRQ, local = [], shared = [w5500])] + #[allow(clippy::collapsible_if)] + fn exti0(mut cx: exti0::Context) { + log::info!("[TASK] exti0"); + } +}