Skip to content

Commit

Permalink
Merge pull request #45 from Jim-Hodapp-Coaching/support_dns
Browse files Browse the repository at this point in the history
Support DNS
  • Loading branch information
calebbourg authored Nov 18, 2022
2 parents 4c5bd70 + 19135b2 commit f4a42b0
Show file tree
Hide file tree
Showing 8 changed files with 365 additions and 27 deletions.
1 change: 1 addition & 0 deletions cross/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
members = [
"get_fw_version",
"join",
"dns"
]

[profile.dev]
Expand Down
40 changes: 40 additions & 0 deletions cross/dns/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[package]
authors = [
"Jim Hodapp",
"Caleb Bourg",
"Glyn Matthews"
]
edition = "2021"
name = "dns"
version = "0.1.0"
description = "Example target application that demonstrates DNS functionality with the Rust-based Espressif ESP32-WROOM WiFi driver crate for RP2040 series microcontroller boards."

# makes `cargo check --all-targets` work
[[bin]]
name = "dns"
bench = false
doctest = false
test = false

[dependencies]
defmt = "0.3.0"
defmt-rtt = "0.3.0"
cortex-m = "0.7"
cortex-m-rt = "0.7"
embedded-hal = { version = "0.2", features=["unproven"] }
esp32-wroom-rp = { path = "../../esp32-wroom-rp" }
panic-probe = { version = "0.3.0", features = ["print-rtt"] }

rp2040-hal = { version = "0.6", features=["rt", "eh1_0_alpha"] }
rp2040-boot2 = { version = "0.2" }
fugit = "0.3"

[features]
default = ['defmt-default']
# these features are required by defmt
defmt-default = []
defmt-trace = []
defmt-debug = []
defmt-info = []
defmt-warn = []
defmt-error = []
158 changes: 158 additions & 0 deletions cross/dns/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
//! # ESP32-WROOM-RP Pico Wireless Example
//!
//! This application demonstrates how to use the ESP32-WROOM-RP crate to perform
//! a DNS hostname lookup after setting what DNS server to use.
//!
//! See the `Cargo.toml` file for Copyright and license details.
#![no_std]
#![no_main]

extern crate esp32_wroom_rp;

include!("secrets/secrets.rs");

// The macro for our start-up function
use cortex_m_rt::entry;

// Needed for debug output symbols to be linked in binary image
use defmt_rtt as _;

use panic_probe as _;

// Alias for our HAL crate
use rp2040_hal as hal;

use embedded_hal::spi::MODE_0;
use fugit::RateExtU32;
use hal::clocks::Clock;
use hal::pac;

use esp32_wroom_rp::network::IpAddress;

/// The linker will place this boot block at the start of our program image. We
/// need this to help the ROM bootloader get our code up and running.
#[link_section = ".boot2"]
#[used]
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;

/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
/// if your board has a different frequency
const XTAL_FREQ_HZ: u32 = 12_000_000u32;

/// Entry point to our bare-metal application.
///
/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
/// as soon as all global variables are initialized.
#[entry]
fn main() -> ! {
// Grab our singleton objects
let mut pac = pac::Peripherals::take().unwrap();
let core = pac::CorePeripherals::take().unwrap();

// Set up the watchdog driver - needed by the clock setup code
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);

// Configure the clocks
let clocks = hal::clocks::init_clocks_and_plls(
XTAL_FREQ_HZ,
pac.XOSC,
pac.CLOCKS,
pac.PLL_SYS,
pac.PLL_USB,
&mut pac.RESETS,
&mut watchdog,
)
.ok()
.unwrap();

let mut delay = cortex_m::delay::Delay::new(core.SYST, clocks.system_clock.freq().to_Hz());

// The single-cycle I/O block controls our GPIO pins
let sio = hal::Sio::new(pac.SIO);

// Set the pins to their default state
let pins = hal::gpio::Pins::new(
pac.IO_BANK0,
pac.PADS_BANK0,
sio.gpio_bank0,
&mut pac.RESETS,
);

defmt::info!("ESP32-WROOM-RP DNS resolve example");

// These are implicitly used by the spi driver if they are in the correct mode
let _spi_miso = pins.gpio16.into_mode::<hal::gpio::FunctionSpi>();
let _spi_sclk = pins.gpio18.into_mode::<hal::gpio::FunctionSpi>();
let _spi_mosi = pins.gpio19.into_mode::<hal::gpio::FunctionSpi>();

let spi = hal::Spi::<_, _, 8>::new(pac.SPI0);

// Exchange the uninitialized SPI driver for an initialized one
let mut spi = spi.init(
&mut pac.RESETS,
clocks.peripheral_clock.freq(),
8.MHz(),
&MODE_0,
);

let mut esp_pins = esp32_wroom_rp::gpio::EspControlPins {
// CS on pin x (GPIO7)
cs: pins.gpio7.into_mode::<hal::gpio::PushPullOutput>(),
// GPIO0 on pin x (GPIO2)
gpio0: pins.gpio2.into_mode::<hal::gpio::PushPullOutput>(),
// RESETn on pin x (GPIO11)
resetn: pins.gpio11.into_mode::<hal::gpio::PushPullOutput>(),
// ACK on pin x (GPIO10)
ack: pins.gpio10.into_mode::<hal::gpio::FloatingInput>(),
};

let mut wifi = esp32_wroom_rp::wifi::Wifi::init(&mut spi, &mut esp_pins, &mut delay).unwrap();

let result = wifi.join(SSID, PASSPHRASE);
defmt::info!("Join Result: {:?}", result);

defmt::info!("Entering main loop");

loop {
match wifi.get_connection_status() {
Ok(byte) => {
defmt::info!("Get Connection Result: {:?}", byte);
let sleep: u32 = 1500;
delay.delay_ms(sleep);

if byte == 3 {
defmt::info!("Connected to Network: {:?}", SSID);

// The IPAddresses of two DNS servers to resolve hostnames with.
// Note that failover from ip1 to ip2 is fully functional.
let ip1: IpAddress = [9, 9, 9, 9];
let ip2: IpAddress = [8, 8, 8, 8];
let dns_result = wifi.set_dns(ip1, Some(ip2));

defmt::info!("set_dns result: {:?}", dns_result);

let hostname = "github.com";
defmt::info!("Doing a DNS resolve for {}", hostname);

match wifi.resolve(hostname) {
Ok(ip) => {
defmt::info!("Server IP: {:?}", ip);
}
Err(e) => {
defmt::error!("Failed to resolve hostname {}", hostname);
defmt::error!("Err: {}", e);
}
}

wifi.leave().ok().unwrap();
} else if byte == 6 {
defmt::info!("Disconnected from Network: {:?}", SSID);
}
}
Err(e) => {
defmt::info!("Failed to Get Connection Result: {:?}", e);
}
}
}
}
7 changes: 7 additions & 0 deletions cross/dns/src/secrets/secrets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
//
// secrets.rs - stores WiFi secrets like SSID, passphrase, etc shared across
// all example applications
//

const SSID: &str = "ssid";
const PASSPHRASE: &str = "passphrase";
31 changes: 27 additions & 4 deletions esp32-wroom-rp/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,14 @@ pub mod gpio;
/// Fundamental interface for controlling a connected ESP32-WROOM NINA firmware-based Wifi board.
pub mod wifi;

/// Responsible for interactions over a WiFi network and also contains related types.
pub mod network;
/// Responsible for interactions with NINA firmware over a data bus.
pub mod protocol;

mod spi;

use network::{IpAddress, NetworkError};
use protocol::{ProtocolError, ProtocolInterface};

use defmt::{write, Format, Formatter};
Expand All @@ -105,6 +110,9 @@ pub enum Error {
Bus,
/// Protocol error in communicating with the ESP32 WiFi target
Protocol(ProtocolError),

/// Network related error
Network(NetworkError),
}

impl Format for Error {
Expand All @@ -116,6 +124,7 @@ impl Format for Error {
"Communication protocol error with ESP32 WiFi target: {}",
e
),
Error::Network(e) => write!(fmt, "Network error: {}", e),
}
}
}
Expand All @@ -126,6 +135,12 @@ impl From<protocol::ProtocolError> for Error {
}
}

impl From<network::NetworkError> for Error {
fn from(err: network::NetworkError) -> Self {
Error::Network(err)
}
}

/// A structured representation of a connected NINA firmware device's version number (e.g. 1.7.4).
#[derive(Debug, Default, Eq, PartialEq)]
pub struct FirmwareVersion {
Expand Down Expand Up @@ -184,19 +199,27 @@ where
}

fn firmware_version(&mut self) -> Result<FirmwareVersion, Error> {
Ok(self.protocol_handler.get_fw_version()?)
self.protocol_handler.get_fw_version()
}

fn join(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> {
Ok(self.protocol_handler.set_passphrase(ssid, passphrase)?)
self.protocol_handler.set_passphrase(ssid, passphrase)
}

fn leave(&mut self) -> Result<(), Error> {
Ok(self.protocol_handler.disconnect()?)
self.protocol_handler.disconnect()
}

fn get_connection_status(&mut self) -> Result<u8, Error> {
Ok(self.protocol_handler.get_conn_status()?)
self.protocol_handler.get_conn_status()
}

fn set_dns(&mut self, dns1: IpAddress, dns2: Option<IpAddress>) -> Result<(), Error> {
self.protocol_handler.set_dns_config(dns1, dns2)
}

fn resolve(&mut self, hostname: &str) -> Result<IpAddress, Error> {
self.protocol_handler.resolve(hostname)
}
}

Expand Down
22 changes: 22 additions & 0 deletions esp32-wroom-rp/src/network.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use defmt::{write, Format, Formatter};

/// A four byte array type alias representing an IP address.
pub type IpAddress = [u8; 4];

/// Errors that occur due to issues involving communication over
/// WiFi network.
#[derive(PartialEq, Eq, Debug)]
pub enum NetworkError {
/// Failed to resolve a hostname for the provided IP address.
DnsResolveFailed,
}

impl Format for NetworkError {
fn format(&self, fmt: Formatter) {
match self {
NetworkError::DnsResolveFailed => {
write!(fmt, "Failed to resolve a hostname for the provided IP address")
}
}
}
}
24 changes: 19 additions & 5 deletions esp32-wroom-rp/src/protocol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ pub(crate) enum NinaCommand {
SetPassphrase = 0x11u8,
GetConnStatus = 0x20u8,
Disconnect = 0x30u8,
SetDNSConfig = 0x15u8,
ReqHostByName = 0x34u8,
GetHostByName = 0x35u8,
}

pub(crate) trait NinaParam {
Expand Down Expand Up @@ -225,10 +228,14 @@ impl NinaParam for NinaLargeArrayParam {
pub(crate) trait ProtocolInterface {
fn init(&mut self);
fn reset<D: DelayMs<u16>>(&mut self, delay: &mut D);
fn get_fw_version(&mut self) -> Result<FirmwareVersion, ProtocolError>;
fn set_passphrase(&mut self, ssid: &str, passphrase: &str) -> Result<(), ProtocolError>;
fn disconnect(&mut self) -> Result<(), ProtocolError>;
fn get_conn_status(&mut self) -> Result<u8, ProtocolError>;
fn get_fw_version(&mut self) -> Result<FirmwareVersion, Error>;
fn set_passphrase(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error>;
fn disconnect(&mut self) -> Result<(), Error>;
fn get_conn_status(&mut self) -> Result<u8, Error>;
fn set_dns_config(&mut self, dns1: IpAddress, dns2: Option<IpAddress>) -> Result<(), Error>;
fn req_host_by_name(&mut self, hostname: &str) -> Result<u8, Error>;
fn get_host_by_name(&mut self) -> Result<[u8; 8], Error>;
fn resolve(&mut self, hostname: &str) -> Result<IpAddress, Error>;
}

#[derive(Debug)]
Expand All @@ -241,12 +248,19 @@ pub(crate) struct NinaProtocolHandler<'a, B, C> {

// TODO: look at Nina Firmware code to understand conditions
// that lead to NinaProtocolVersionMismatch
/// Errors related to communication with NINA firmware
#[derive(Debug, Eq, PartialEq)]
pub enum ProtocolError {
/// TODO: look at Nina Firmware code to understand conditions
/// that lead to NinaProtocolVersionMismatch
NinaProtocolVersionMismatch,
/// A timeout occurred.
CommunicationTimeout,
/// An invalid NINA command has been sent over the data bus.
InvalidCommand,
/// An invalid number of parameters sent over the data bus.
InvalidNumberOfParameters,
/// Too many parameters sent over the data bus.
TooManyParameters,
}

Expand All @@ -257,7 +271,7 @@ impl Format for ProtocolError {
ProtocolError::CommunicationTimeout => write!(fmt, "Communication with ESP32 target timed out."),
ProtocolError::InvalidCommand => write!(fmt, "Encountered an invalid command while communicating with ESP32 target."),
ProtocolError::InvalidNumberOfParameters => write!(fmt, "Encountered an unexpected number of parameters for a NINA command while communicating with ESP32 target."),
ProtocolError::TooManyParameters => write!(fmt, "Encountered too many parameters for a NINA command while communicating with ESP32 target."),
ProtocolError::TooManyParameters => write!(fmt, "Encountered too many parameters for a NINA command while communicating with ESP32 target.")
}
}
}
Loading

0 comments on commit f4a42b0

Please sign in to comment.