diff --git a/fan-controller/src/dhcp/mod.rs b/fan-controller/src/dhcp/mod.rs index 77a1276..faca796 100644 --- a/fan-controller/src/dhcp/mod.rs +++ b/fan-controller/src/dhcp/mod.rs @@ -1,5 +1,7 @@ use defmt::{info, Format}; -use embassy_net::EthernetAddress; +use embassy_net::{EthernetAddress, Ipv4Address}; + +use crate::encoding::{Encode, TryDecode}; #[derive(Debug, Format)] pub(crate) enum MessageType { @@ -18,23 +20,6 @@ pub(crate) enum ReplyType { Broadcast = 0x01, } -#[derive(Debug, Format)] -pub(crate) struct Packet<'a> { - pub(crate) option: MessageType, - pub(crate) address_type: HardwareAddressType, - pub(crate) hardware_address_length: u8, - pub(crate) hops_count: u8, - pub(crate) transaction_id: u32, - pub(crate) seconds_elapsed: u16, - pub(crate) flags: ReplyType, - pub(crate) client_address: embassy_net::Ipv4Address, - pub(crate) your_address: embassy_net::Ipv4Address, - pub(crate) server_address: embassy_net::Ipv4Address, - pub(crate) gateway_address: embassy_net::Ipv4Address, - pub(crate) client_hardware_address: EthernetAddress, - pub(crate) options: Options<'a>, -} - #[derive(Debug, Format)] pub(crate) enum DhcpMessageType { Discover = 1, @@ -83,6 +68,12 @@ pub(crate) enum MaybeString<'a> { #[derive(Debug, Format, Default)] pub(crate) struct Options<'a> { + // Option 1 + pub(crate) subnet_mask: Option, + // Option 3 + pub(crate) router: Option, + // Option 51 + pub(crate) address_time: Option, pub(crate) host_name: Option>, pub(crate) message_type: Option, pub(crate) parameter_request_list: Option<&'a [u8]>, @@ -93,6 +84,12 @@ pub(crate) struct Options<'a> { pub(crate) lease_seconds: Option, } +impl Encode for Options<'_> { + fn encode(&self, buffer: &mut [u8], offset: &mut usize) { + todo!() + } +} + #[derive(Debug, Format)] pub(crate) enum DecodeError { UnkownMessageType(u8), @@ -109,6 +106,23 @@ pub(crate) enum DecodeError { }, } +#[derive(Debug, Format)] +pub(crate) struct Packet<'a> { + pub(crate) option: MessageType, + pub(crate) address_type: HardwareAddressType, + pub(crate) hardware_address_length: u8, + pub(crate) hops_count: u8, + pub(crate) transaction_id: u32, + pub(crate) seconds_elapsed: u16, + pub(crate) flags: ReplyType, + pub(crate) client_address: Ipv4Address, + pub(crate) your_address: Ipv4Address, + pub(crate) server_address: Ipv4Address, + pub(crate) gateway_address: Ipv4Address, + pub(crate) client_hardware_address: EthernetAddress, + pub(crate) options: Options<'a>, +} + impl<'a> Packet<'a> { pub(crate) fn try_decode(buffer: &'a [u8]) -> Result { let option = match buffer[0] { @@ -302,4 +316,58 @@ impl<'a> Packet<'a> { options, }) } + + fn encode(&self, buffer: &mut [u8], offset: &mut usize) { + buffer[*offset] = match self.option { + MessageType::Request => 0, + MessageType::Reply => 1, + }; + *offset += 1; + + buffer[*offset] = match self.address_type { + HardwareAddressType::Ethernet => 0x01, + }; + *offset += 1; + + buffer[*offset] = self.hardware_address_length; + *offset += 1; + + buffer[*offset] = self.hops_count; + *offset += 1; + + let bytes = self.transaction_id.to_be_bytes(); + buffer[*offset..*offset + 4].copy_from_slice(&bytes); + *offset += 4; + + // buffer[offset] = self.seconds_elapsed; + buffer[*offset..*offset + 2].copy_from_slice(&self.seconds_elapsed.to_be_bytes()); + *offset += 2; + + buffer[*offset] = match self.flags { + ReplyType::Unicast => 0, + ReplyType::Broadcast => 0b1000_0000, + }; + *offset += 1; + let bytes = self.client_address.as_bytes(); + buffer[*offset..*offset + 4].copy_from_slice(&bytes); + *offset += 4; + + let bytes = self.your_address.as_bytes(); + buffer[*offset..*offset + 4].copy_from_slice(&bytes); + *offset += 4; + + let bytes = self.server_address.as_bytes(); + buffer[*offset..*offset + 4].copy_from_slice(&bytes); + *offset += 4; + + let bytes = self.gateway_address.as_bytes(); + buffer[*offset..*offset + 4].copy_from_slice(&bytes); + *offset += 4; + + let bytes = self.client_hardware_address.0; + buffer[*offset..*offset + 6].copy_from_slice(&bytes); + *offset += 6; + + self.options.encode(buffer, offset); + } } diff --git a/fan-controller/src/encoding.rs b/fan-controller/src/encoding.rs new file mode 100644 index 0000000..6a24cbe --- /dev/null +++ b/fan-controller/src/encoding.rs @@ -0,0 +1,44 @@ +use core::convert::Infallible; + +pub(crate) trait Encode { + fn encode(&self, buffer: &mut [u8], offset: &mut usize); +} + +pub(crate) trait TryEncode { + type Error; + fn try_encode(&self, buffer: &mut [u8], offset: &mut usize) -> Result<(), Self::Error>; +} + +impl TryEncode for T { + type Error = Infallible; + + fn try_encode(&self, buffer: &mut [u8], offset: &mut usize) -> Result<(), Self::Error> { + self.encode(buffer, offset); + Ok(()) + } +} + +pub(crate) trait Decode<'a> { + fn decode(flags: u8, variable_header_and_payload: &'a [u8]) -> Self + where + Self: Sized; +} + +pub(crate) trait TryDecode<'a> { + type Error; + fn try_decode(flags: u8, variable_header_and_payload: &'a [u8]) -> Result + where + Self: Sized; +} + +impl<'a, T: Decode<'a>> TryDecode<'a> for T { + type Error = Infallible; + + fn try_decode(flags: u8, variable_header_and_payload: &'a [u8]) -> Result + where + Self: Sized, + { + let value = T::decode(flags, variable_header_and_payload); + Ok(value) + } +} diff --git a/fan-controller/src/main.rs b/fan-controller/src/main.rs index 2627474..e1a2568 100644 --- a/fan-controller/src/main.rs +++ b/fan-controller/src/main.rs @@ -1,7 +1,5 @@ #![no_std] #![no_main] -//TODO remove this 🥴 -#![allow(warnings)] use configuration::DISCOVERY_TOPIC; use core::convert::Infallible; @@ -49,8 +47,8 @@ use embassy_sync::watch::Watch; use embassy_time::{with_deadline, with_timeout, Duration, Instant, Ticker, TimeoutError, Timer}; use embedded_io_async::{Read, Write}; use embedded_nal_async::{AddrType, Dns, SocketAddr, TcpConnect}; +use encoding::{Encode, TryDecode}; use mqtt::packet::disconnect::Disconnect; -use mqtt::{Encode, TryDecode}; use rand::RngCore; use reqwless::client::{TlsConfig, TlsVerify}; use static_cell::StaticCell; @@ -61,6 +59,7 @@ use {defmt_rtt as _, panic_probe as _}; use self::mqtt::packet; use self::mqtt::packet::Packet; use crate::async_callback::AsyncCallback; +use crate::encoding::TryEncode; use crate::mqtt::non_zero_u16; use crate::mqtt::packet::connect::Connect; use crate::mqtt::packet::connect_acknowledgement::ConnectReasonCode; @@ -73,12 +72,12 @@ use crate::mqtt::packet::{connect, publish, subscribe}; use crate::mqtt::packet::{get_parts, FromPublish, FromSubscribeAcknowledgement}; use crate::mqtt::task::send; use crate::mqtt::QualityOfService; -use crate::mqtt::TryEncode; mod async_callback; mod configuration; mod debounce; mod dhcp; +mod encoding; mod fan; mod modbus; mod mqtt; @@ -1415,7 +1414,6 @@ async fn main(spawner: Spawner) { unwrap!(spawner.spawn(network_task(stack))); // Use it // Try out DHCP - let mut transmit_buffer = [0; 4096]; let mut receive_buffer = [0; 4096]; let mut buffer = [0; 4096]; @@ -1487,7 +1485,14 @@ async fn main(spawner: Spawner) { gateway_address: Ipv4Address::default(), client_hardware_address: packet.client_hardware_address, //TODO - options: Options::default(), + options: Options { + message_type: Some(DhcpMessageType::Offer), + subnet_mask: Some(Ipv4Address::new(255, 255, 255, 0)), + router: Some(DEVICE_ADDRESS), + address_time: Some(86400), + //TODO try without DNS server + ..Default::default() + }, }; } other => warn!("Received unsupported DHCP message type: {:?}", other), diff --git a/fan-controller/src/mqtt/mod.rs b/fan-controller/src/mqtt/mod.rs index 0ed9172..6afbc14 100644 --- a/fan-controller/src/mqtt/mod.rs +++ b/fan-controller/src/mqtt/mod.rs @@ -62,49 +62,6 @@ impl QualityOfService { } } -pub(crate) trait Encode { - fn encode(&self, buffer: &mut [u8], offset: &mut usize); -} - -pub(crate) trait TryEncode { - type Error; - fn try_encode(&self, buffer: &mut [u8], offset: &mut usize) -> Result<(), Self::Error>; -} - -impl TryEncode for T { - type Error = Infallible; - - fn try_encode(&self, buffer: &mut [u8], offset: &mut usize) -> Result<(), Self::Error> { - self.encode(buffer, offset); - Ok(()) - } -} - -pub(crate) trait Decode<'a> { - fn decode(flags: u8, variable_header_and_payload: &'a [u8]) -> Self - where - Self: Sized; -} - -pub(crate) trait TryDecode<'a> { - type Error; - fn try_decode(flags: u8, variable_header_and_payload: &'a [u8]) -> Result - where - Self: Sized; -} - -impl<'a, T: Decode<'a>> TryDecode<'a> for T { - type Error = Infallible; - - fn try_decode(flags: u8, variable_header_and_payload: &'a [u8]) -> Result - where - Self: Sized, - { - let value = T::decode(flags, variable_header_and_payload); - Ok(value) - } -} - /// Helps to mark an integer as a non zero integer when you know at compile time that it is not zero macro_rules! non_zero_u16 { (0) => { diff --git a/fan-controller/src/mqtt/packet/connect.rs b/fan-controller/src/mqtt/packet/connect.rs index 61ca10f..5ebd842 100644 --- a/fan-controller/src/mqtt/packet/connect.rs +++ b/fan-controller/src/mqtt/packet/connect.rs @@ -1,6 +1,6 @@ use crate::mqtt::variable_byte_integer; use crate::mqtt::variable_byte_integer::VariableByteIntegerEncodeError; -use crate::mqtt::TryEncode; +use crate::encoding::TryEncode; use defmt::Format; pub(crate) struct Connect<'a> { diff --git a/fan-controller/src/mqtt/packet/disconnect.rs b/fan-controller/src/mqtt/packet/disconnect.rs index 1d92328..708766f 100644 --- a/fan-controller/src/mqtt/packet/disconnect.rs +++ b/fan-controller/src/mqtt/packet/disconnect.rs @@ -1,4 +1,4 @@ -use crate::mqtt::TryDecode; +use crate::encoding::TryDecode; use defmt::Format; #[derive(Debug, Format)] diff --git a/fan-controller/src/mqtt/packet/mod.rs b/fan-controller/src/mqtt/packet/mod.rs index 981e905..857f5b5 100644 --- a/fan-controller/src/mqtt/packet/mod.rs +++ b/fan-controller/src/mqtt/packet/mod.rs @@ -1,3 +1,4 @@ +use crate::encoding::TryDecode; use crate::mqtt::packet::connect::Connect; use crate::mqtt::packet::connect_acknowledgement::ConnectAcknowledgement; use crate::mqtt::packet::publish::Publish; @@ -6,11 +7,8 @@ use crate::mqtt::packet::subscribe_acknowledgement::{ SubscribeAcknowledgement, SubscribeAcknowledgementError, }; use crate::mqtt::variable_byte_integer; -use crate::mqtt::DecodeError; use defmt::{info, Format}; -use super::TryDecode; - pub(crate) mod connect; pub(crate) mod connect_acknowledgement; pub(crate) mod disconnect; diff --git a/fan-controller/src/mqtt/packet/ping_request.rs b/fan-controller/src/mqtt/packet/ping_request.rs index 8c9942a..4bf95eb 100644 --- a/fan-controller/src/mqtt/packet/ping_request.rs +++ b/fan-controller/src/mqtt/packet/ping_request.rs @@ -1,4 +1,4 @@ -use crate::mqtt::Encode; +use crate::encoding::Encode; use core::convert::Infallible; use core::pin::Pin; diff --git a/fan-controller/src/mqtt/packet/ping_response.rs b/fan-controller/src/mqtt/packet/ping_response.rs index 2fc6a64..0b8e932 100644 --- a/fan-controller/src/mqtt/packet/ping_response.rs +++ b/fan-controller/src/mqtt/packet/ping_response.rs @@ -1,6 +1,6 @@ use core::convert::Infallible; -use crate::mqtt::Decode; +use crate::encoding::Decode; pub(crate) struct PingResponse; diff --git a/fan-controller/src/mqtt/packet/publish.rs b/fan-controller/src/mqtt/packet/publish.rs index c318dab..a08d27d 100644 --- a/fan-controller/src/mqtt/packet/publish.rs +++ b/fan-controller/src/mqtt/packet/publish.rs @@ -1,8 +1,8 @@ use core::str::Utf8Error; +use crate::encoding::{TryDecode, TryEncode}; +use crate::mqtt::variable_byte_integer; use crate::mqtt::variable_byte_integer::VariableByteIntegerEncodeError; -use crate::mqtt::TryEncode; -use crate::mqtt::{variable_byte_integer, TryDecode}; use defmt::{debug, info, write, Debug2Format, Format, Formatter}; #[derive(Format, Clone)] diff --git a/fan-controller/src/mqtt/packet/subscribe.rs b/fan-controller/src/mqtt/packet/subscribe.rs index 2088f72..ac6f6c7 100644 --- a/fan-controller/src/mqtt/packet/subscribe.rs +++ b/fan-controller/src/mqtt/packet/subscribe.rs @@ -2,7 +2,7 @@ use core::num::NonZeroU16; use defmt::{info, Format}; -use crate::mqtt::TryEncode; +use crate::encoding::TryEncode; use crate::mqtt::{variable_byte_integer, QualityOfService}; #[derive(Debug)] diff --git a/fan-controller/src/mqtt/task.rs b/fan-controller/src/mqtt/task.rs index 2267921..c39d061 100644 --- a/fan-controller/src/mqtt/task.rs +++ b/fan-controller/src/mqtt/task.rs @@ -1,8 +1,8 @@ +use crate::encoding::{TryDecode, TryEncode}; use crate::mqtt::packet::connect::{Connect, EncodeError}; use crate::mqtt::packet::connect_acknowledgement::{ConnectAcknowledgement, ConnectReasonCode}; use crate::mqtt::packet::GetPartsError; use crate::mqtt::{packet, ConnectErrorReasonCode, DecodeError}; -use crate::mqtt::{TryDecode, TryEncode}; use core::fmt::Debug; use defmt::{info, warn, Format}; use embassy_net::tcp;