Skip to content

Commit b7289f8

Browse files
authored
Merge pull request #22 from Jim-Hodapp-Coaching/join_a_provided_wifi_network
Join a provided wifi network
2 parents 193ffd2 + 896d764 commit b7289f8

File tree

5 files changed

+373
-16
lines changed

5 files changed

+373
-16
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ embedded-time = "0.12"
2323

2424
defmt = "0.3"
2525
defmt-rtt = "0.3"
26+
heapless = "0.7.16"
2627
panic-probe = { version = "0.3", features = ["print-rtt"] }
2728

2829
rp2040-hal = { version = "0.5", features=["rt", "eh1_0_alpha"] }

examples/join.rs

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
//! # ESP32-WROOM-RP Pico Wireless Example
2+
//!
3+
//! This application demonstrates how to use the ESP32-WROOM-RP crate to request that
4+
//! a remote ESP32 WiFi target connects to a particular SSID given a passphrase.
5+
//!
6+
//! See the `Cargo.toml` file for Copyright and license details.
7+
8+
#![no_std]
9+
#![no_main]
10+
11+
extern crate esp32_wroom_rp;
12+
13+
// The macro for our start-up function
14+
use cortex_m_rt::entry;
15+
16+
// Needed for debug output symbols to be linked in binary image
17+
use defmt_rtt as _;
18+
19+
use panic_probe as _;
20+
21+
// Alias for our HAL crate
22+
use rp2040_hal as hal;
23+
24+
use eh_02::spi::MODE_0;
25+
use embedded_time::fixed_point::FixedPoint;
26+
use embedded_time::rate::Extensions;
27+
use hal::clocks::Clock;
28+
use hal::pac;
29+
30+
use embedded_hal::delay::blocking::DelayUs;
31+
32+
/// The linker will place this boot block at the start of our program image. We
33+
/// need this to help the ROM bootloader get our code up and running.
34+
#[link_section = ".boot2"]
35+
#[used]
36+
pub static BOOT2: [u8; 256] = rp2040_boot2::BOOT_LOADER_W25Q080;
37+
38+
/// External high-speed crystal on the Raspberry Pi Pico board is 12 MHz. Adjust
39+
/// if your board has a different frequency
40+
const XTAL_FREQ_HZ: u32 = 12_000_000u32;
41+
42+
// Until cortex_m implements the DelayUs trait needed for embedded-hal-1.0.0,
43+
// provide a wrapper around it
44+
pub struct DelayWrap(cortex_m::delay::Delay);
45+
46+
impl embedded_hal::delay::blocking::DelayUs for DelayWrap {
47+
type Error = core::convert::Infallible;
48+
49+
fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> {
50+
self.0.delay_us(us);
51+
Ok(())
52+
}
53+
54+
fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> {
55+
self.0.delay_ms(ms);
56+
Ok(())
57+
}
58+
}
59+
60+
/// Entry point to our bare-metal application.
61+
///
62+
/// The `#[entry]` macro ensures the Cortex-M start-up code calls this function
63+
/// as soon as all global variables are initialized.
64+
#[entry]
65+
fn main() -> ! {
66+
// Grab our singleton objects
67+
let mut pac = pac::Peripherals::take().unwrap();
68+
let core = pac::CorePeripherals::take().unwrap();
69+
70+
// Set up the watchdog driver - needed by the clock setup code
71+
let mut watchdog = hal::Watchdog::new(pac.WATCHDOG);
72+
73+
// Configure the clocks
74+
let clocks = hal::clocks::init_clocks_and_plls(
75+
XTAL_FREQ_HZ,
76+
pac.XOSC,
77+
pac.CLOCKS,
78+
pac.PLL_SYS,
79+
pac.PLL_USB,
80+
&mut pac.RESETS,
81+
&mut watchdog,
82+
)
83+
.ok()
84+
.unwrap();
85+
86+
let mut delay = DelayWrap(cortex_m::delay::Delay::new(
87+
core.SYST,
88+
clocks.system_clock.freq().integer(),
89+
));
90+
91+
// The single-cycle I/O block controls our GPIO pins
92+
let sio = hal::Sio::new(pac.SIO);
93+
94+
// Set the pins to their default state
95+
let pins = hal::gpio::Pins::new(
96+
pac.IO_BANK0,
97+
pac.PADS_BANK0,
98+
sio.gpio_bank0,
99+
&mut pac.RESETS,
100+
);
101+
102+
defmt::info!("ESP32-WROOM-RP get NINA firmware version example");
103+
104+
// These are implicitly used by the spi driver if they are in the correct mode
105+
let spi_miso = pins.gpio16.into_mode::<hal::gpio::FunctionSpi>();
106+
let spi_sclk = pins.gpio18.into_mode::<hal::gpio::FunctionSpi>();
107+
let spi_mosi = pins.gpio19.into_mode::<hal::gpio::FunctionSpi>();
108+
109+
let spi = hal::Spi::<_, _, 8>::new(pac.SPI0);
110+
111+
// Exchange the uninitialized SPI driver for an initialized one
112+
let spi = spi.init(
113+
&mut pac.RESETS,
114+
clocks.peripheral_clock.freq(),
115+
8_000_000u32.Hz(),
116+
&MODE_0,
117+
);
118+
119+
let esp_pins = esp32_wroom_rp::gpio::EspControlPins {
120+
// CS on pin x (GPIO7)
121+
cs: pins.gpio7.into_mode::<hal::gpio::PushPullOutput>(),
122+
// GPIO0 on pin x (GPIO2)
123+
gpio0: pins.gpio2.into_mode::<hal::gpio::PushPullOutput>(),
124+
// RESETn on pin x (GPIO11)
125+
resetn: pins.gpio11.into_mode::<hal::gpio::PushPullOutput>(),
126+
// ACK on pin x (GPIO10)
127+
ack: pins.gpio10.into_mode::<hal::gpio::FloatingInput>(),
128+
};
129+
let ssid: &str = "SSID";
130+
let passphrase: &str = "Passphrase";
131+
132+
let mut wifi = esp32_wroom_rp::wifi::Wifi::init(spi, esp_pins, &mut delay).unwrap();
133+
let result = wifi.join(ssid, passphrase);
134+
defmt::info!("Join Result: {:?}", result);
135+
136+
defmt::info!("Entering main loop");
137+
138+
loop {
139+
match wifi.get_connection_status() {
140+
Ok(byte) => {
141+
defmt::info!("Get Connection Result: {:?}", byte);
142+
let sleep: u32 = 1500;
143+
delay.delay_ms(sleep).ok().unwrap();
144+
145+
if byte == 3 {
146+
defmt::info!("Connected to Network: {:?}", ssid);
147+
}
148+
}
149+
Err(e) => {
150+
defmt::info!("Failed to Get Connection Result: {:?}", e);
151+
}
152+
}
153+
}
154+
}

src/lib.rs

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,7 @@ use protocol::ProtocolInterface;
110110
use defmt::{write, Format, Formatter};
111111
use embedded_hal::delay::blocking::DelayUs;
112112

113-
// This is just a placeholder for now.
114-
type Params = [u8; 5];
113+
const ARRAY_LENGTH_PLACEHOLDER: usize = 8;
115114

116115
#[derive(Debug)]
117116
pub enum Error {
@@ -135,12 +134,12 @@ pub struct FirmwareVersion {
135134
}
136135

137136
impl FirmwareVersion {
138-
fn new(version: [u8; 8]) -> FirmwareVersion {
137+
fn new(version: [u8; ARRAY_LENGTH_PLACEHOLDER]) -> FirmwareVersion {
139138
Self::parse(version)
140139
}
141140

142141
// Takes in 8 bytes (e.g. 1.7.4) and returns a FirmwareVersion instance
143-
fn parse(version: [u8; 8]) -> FirmwareVersion {
142+
fn parse(version: [u8; ARRAY_LENGTH_PLACEHOLDER]) -> FirmwareVersion {
144143
let major: u8;
145144
let minor: u8;
146145
let patch: u8;
@@ -179,15 +178,21 @@ where
179178
self.reset(delay);
180179
}
181180

182-
fn configure() {}
183-
184181
fn reset<D: DelayUs>(&mut self, delay: &mut D) {
185182
self.protocol_handler.reset(delay)
186183
}
187184

188185
fn firmware_version(&mut self) -> Result<FirmwareVersion, self::Error> {
189186
self.protocol_handler.get_fw_version()
190187
}
188+
189+
fn join(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error> {
190+
self.protocol_handler.set_passphrase(ssid, passphrase)
191+
}
192+
193+
fn get_connection_status(&mut self) -> Result<u8, self::Error> {
194+
self.protocol_handler.get_conn_status()
195+
}
191196
}
192197

193198
#[cfg(test)]

src/protocol.rs

Lines changed: 129 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,159 @@
11
use super::*;
22

3-
use eh_02::blocking::spi::Transfer;
43
use embedded_hal::delay::blocking::DelayUs;
54

6-
pub const PARAMS_ARRAY_LEN: usize = 8;
5+
use heapless::{String, Vec};
6+
7+
pub const MAX_NINA_PARAM_LENGTH: usize = 255;
78

89
#[repr(u8)]
910
#[derive(Debug)]
1011
pub enum NinaCommand {
11-
StartClientTcp = 0x2Du8,
1212
GetFwVersion = 0x37u8,
13+
SetPassphrase = 0x11u8,
14+
GetConnStatus = 0x20u8,
15+
}
16+
17+
pub trait NinaParam {
18+
// Length of parameter in bytes
19+
type LengthAsBytes: IntoIterator<Item = u8>;
20+
21+
fn new(data: &str) -> Self;
22+
23+
fn data(&mut self) -> &[u8];
24+
25+
fn length_as_bytes(&mut self) -> Self::LengthAsBytes;
26+
}
27+
28+
// Used for single byte params
29+
pub struct NinaByteParam {
30+
length: u8,
31+
data: Vec<u8, 1>,
32+
}
33+
34+
// Used for 2-byte params
35+
pub struct NinaWordParam {
36+
length: u8,
37+
data: Vec<u8, 2>,
38+
}
39+
40+
// Used for params that are smaller than 255 bytes
41+
pub struct NinaSmallArrayParam {
42+
length: u8,
43+
data: Vec<u8, MAX_NINA_PARAM_LENGTH>,
44+
}
45+
46+
// Used for params that can be larger than 255 bytes up to MAX_NINA_PARAM_LENGTH
47+
pub struct NinaLargeArrayParam {
48+
length: u16,
49+
data: Vec<u8, MAX_NINA_PARAM_LENGTH>,
50+
}
51+
52+
impl NinaParam for NinaByteParam {
53+
type LengthAsBytes = [u8; 1];
54+
55+
fn new(data: &str) -> Self {
56+
let data_as_bytes: Vec<u8, 1> = String::from(data).into_bytes();
57+
Self {
58+
length: data_as_bytes.len() as u8,
59+
data: data_as_bytes,
60+
}
61+
}
62+
63+
fn data(&mut self) -> &[u8] {
64+
self.data.as_slice()
65+
}
66+
67+
fn length_as_bytes(&mut self) -> Self::LengthAsBytes {
68+
[self.length as u8]
69+
}
70+
}
71+
72+
impl NinaParam for NinaWordParam {
73+
type LengthAsBytes = [u8; 1];
74+
75+
fn new(data: &str) -> Self {
76+
let data_as_bytes: Vec<u8, 2> = String::from(data).into_bytes();
77+
Self {
78+
length: data_as_bytes.len() as u8,
79+
data: data_as_bytes,
80+
}
81+
}
82+
83+
fn data(&mut self) -> &[u8] {
84+
self.data.as_slice()
85+
}
86+
87+
fn length_as_bytes(&mut self) -> Self::LengthAsBytes {
88+
[self.length as u8]
89+
}
90+
}
91+
92+
impl NinaParam for NinaSmallArrayParam {
93+
type LengthAsBytes = [u8; 1];
94+
95+
fn new(data: &str) -> Self {
96+
let data_as_bytes: Vec<u8, MAX_NINA_PARAM_LENGTH> = String::from(data).into_bytes();
97+
Self {
98+
length: data_as_bytes.len() as u8,
99+
data: data_as_bytes,
100+
}
101+
}
102+
103+
fn data(&mut self) -> &[u8] {
104+
self.data.as_slice()
105+
}
106+
107+
fn length_as_bytes(&mut self) -> Self::LengthAsBytes {
108+
[self.length as u8]
109+
}
110+
}
111+
112+
impl NinaParam for NinaLargeArrayParam {
113+
type LengthAsBytes = [u8; 2];
114+
115+
fn new(data: &str) -> Self {
116+
let data_as_bytes: Vec<u8, MAX_NINA_PARAM_LENGTH> = String::from(data).into_bytes();
117+
Self {
118+
length: data_as_bytes.len() as u16,
119+
data: data_as_bytes,
120+
}
121+
}
122+
123+
fn data(&mut self) -> &[u8] {
124+
self.data.as_slice()
125+
}
126+
127+
fn length_as_bytes(&mut self) -> Self::LengthAsBytes {
128+
[
129+
((self.length & 0xff00) >> 8) as u8,
130+
(self.length & 0xff) as u8,
131+
]
132+
}
13133
}
14134

15135
pub trait ProtocolInterface {
16136
fn init(&mut self);
17137
fn reset<D: DelayUs>(&mut self, delay: &mut D);
18138
fn get_fw_version(&mut self) -> Result<FirmwareVersion, self::Error>;
139+
fn set_passphrase(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error>;
140+
fn get_conn_status(&mut self) -> Result<u8, self::Error>;
19141

20142
fn send_cmd(&mut self, cmd: NinaCommand, num_params: u8) -> Result<(), self::Error>;
21143
fn wait_response_cmd(
22144
&mut self,
23145
cmd: NinaCommand,
24146
num_params: u8,
25-
) -> Result<[u8; PARAMS_ARRAY_LEN], self::Error>;
147+
) -> Result<[u8; ARRAY_LENGTH_PLACEHOLDER], self::Error>;
26148
fn send_end_cmd(&mut self) -> Result<(), self::Error>;
27149

28150
fn get_param(&mut self) -> Result<u8, self::Error>;
29151
fn wait_for_byte(&mut self, wait_byte: u8) -> Result<bool, self::Error>;
30152
fn check_start_cmd(&mut self) -> Result<bool, self::Error>;
31153
fn read_and_check_byte(&mut self, check_byte: u8) -> Result<bool, self::Error>;
154+
fn send_param<P: NinaParam>(&mut self, param: P) -> Result<(), self::Error>;
155+
fn send_param_length<P: NinaParam>(&mut self, param: &mut P) -> Result<(), self::Error>;
156+
fn pad_to_multiple_of_4(&mut self, command_size: u16);
32157
}
33158

34159
#[derive(Debug, Default)]
@@ -38,4 +163,3 @@ pub struct NinaProtocolHandler<B, C> {
38163
/// A EspControlPins instance
39164
pub control_pins: C,
40165
}
41-

0 commit comments

Comments
 (0)