Skip to content

Commit c070022

Browse files
jhodappcalebbourg
authored andcommitted
A first pass at implementing multi-layered more mature error types and error handling.
1 parent de25160 commit c070022

File tree

3 files changed

+86
-77
lines changed

3 files changed

+86
-77
lines changed

esp32-wroom-rp/src/lib.rs

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ pub mod wifi;
9191
mod protocol;
9292
mod spi;
9393

94-
use protocol::ProtocolInterface;
94+
use protocol::{ProtocolInterface, ProtocolError};
9595

9696
use defmt::{write, Format, Formatter};
9797
use embedded_hal::blocking::delay::DelayMs;
@@ -103,13 +103,28 @@ const ARRAY_LENGTH_PLACEHOLDER: usize = 8;
103103
pub enum Error {
104104
/// SPI/I2C related communications error with the ESP32 WiFi target
105105
Bus,
106-
/// Timeout in communicating with the ESP32 WiFi target
107-
TimeOut,
106+
/// Protocol error in communicating with the ESP32 WiFi target
107+
Protocol(ProtocolError),
108108
}
109109

110110
impl Format for Error {
111111
fn format(&self, fmt: Formatter) {
112-
write!(fmt, "Generic ESP32-WROOM-RP Error")
112+
match self {
113+
Error::Bus => write!(fmt, "Bus error"),
114+
Error::Protocol(e) => write!(fmt, "Communication protocol error with ESP32 WiFi target: {}", e),
115+
}
116+
}
117+
}
118+
119+
impl From<protocol::ProtocolError> for Error {
120+
fn from(err: protocol::ProtocolError) -> Self {
121+
match err {
122+
protocol::ProtocolError::CommunicationTimeout => Error::Protocol(err),
123+
protocol::ProtocolError::InvalidCommand => Error::Protocol(err),
124+
protocol::ProtocolError::InvalidNumberOfParameters => Error::Protocol(err),
125+
protocol::ProtocolError::NinaProtocolVersionMismatch => Error::Protocol(err),
126+
protocol::ProtocolError::TooManyParameters => Error::Protocol(err),
127+
}
113128
}
114129
}
115130

@@ -170,20 +185,20 @@ where
170185
self.protocol_handler.reset(delay)
171186
}
172187

173-
fn firmware_version(&mut self) -> Result<FirmwareVersion, self::Error> {
174-
self.protocol_handler.get_fw_version()
188+
fn firmware_version(&mut self) -> Result<FirmwareVersion, Error> {
189+
Ok(self.protocol_handler.get_fw_version()?)
175190
}
176191

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

181196
fn leave(&mut self) -> Result<(), Error> {
182-
self.protocol_handler.disconnect()
197+
Ok(self.protocol_handler.disconnect()?)
183198
}
184199

185-
fn get_connection_status(&mut self) -> Result<u8, self::Error> {
186-
self.protocol_handler.get_conn_status()
200+
fn get_connection_status(&mut self) -> Result<u8, Error> {
201+
Ok(self.protocol_handler.get_conn_status()?)
187202
}
188203
}
189204

esp32-wroom-rp/src/protocol.rs

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ use super::*;
44

55
use embedded_hal::blocking::delay::DelayMs;
66

7+
use defmt::{write, Format, Formatter};
8+
79
use heapless::{String, Vec};
810

911
pub const MAX_NINA_PARAM_LENGTH: usize = 255;
@@ -223,10 +225,10 @@ impl NinaParam for NinaLargeArrayParam {
223225
pub(crate) trait ProtocolInterface {
224226
fn init(&mut self);
225227
fn reset<D: DelayMs<u16>>(&mut self, delay: &mut D);
226-
fn get_fw_version(&mut self) -> Result<FirmwareVersion, self::Error>;
227-
fn set_passphrase(&mut self, ssid: &str, passphrase: &str) -> Result<(), Error>;
228-
fn disconnect(&mut self) -> Result<(), self::Error>;
229-
fn get_conn_status(&mut self) -> Result<u8, self::Error>;
228+
fn get_fw_version(&mut self) -> Result<FirmwareVersion, ProtocolError>;
229+
fn set_passphrase(&mut self, ssid: &str, passphrase: &str) -> Result<(), ProtocolError>;
230+
fn disconnect(&mut self) -> Result<(), ProtocolError>;
231+
fn get_conn_status(&mut self) -> Result<u8, ProtocolError>;
230232
}
231233

232234
#[derive(Debug, Default)]
@@ -237,7 +239,23 @@ pub(crate) struct NinaProtocolHandler<B, C> {
237239
pub control_pins: C,
238240
}
239241

240-
pub(crate) enum Error {
242+
#[derive(Debug)]
243+
pub enum ProtocolError {
241244
NinaProtocolVersionMismatch,
242-
Timeout,
245+
CommunicationTimeout,
246+
InvalidCommand,
247+
InvalidNumberOfParameters,
248+
TooManyParameters
249+
}
250+
251+
impl Format for ProtocolError {
252+
fn format(&self, fmt: Formatter) {
253+
match self {
254+
ProtocolError::NinaProtocolVersionMismatch => write!(fmt, "Encountered an unsupported version of the NINA protocol."),
255+
ProtocolError::CommunicationTimeout => write!(fmt, "Communication with ESP32 target timed out."),
256+
ProtocolError::InvalidCommand => write!(fmt, "Encountered an invalid command while communicating with ESP32 target."),
257+
ProtocolError::InvalidNumberOfParameters => write!(fmt, "Encountered an unexpected number of parameters for a NINA command while communicating with ESP32 target."),
258+
ProtocolError::TooManyParameters => write!(fmt, "Encountered too many parameters for a NINA command while communicating with ESP32 target."),
259+
}
260+
}
243261
}

esp32-wroom-rp/src/spi.rs

Lines changed: 37 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use super::protocol::{
77
};
88

99
use super::protocol::operation::Operation;
10-
use super::protocol::Error as ProtocolError;
10+
use super::protocol::ProtocolError;
1111
use super::{Error, FirmwareVersion, WifiCommon, ARRAY_LENGTH_PLACEHOLDER};
1212

1313
use embedded_hal::blocking::delay::DelayMs;
@@ -93,22 +93,22 @@ where
9393
}
9494

9595
fn reset<D: DelayMs<u16>>(&mut self, delay: &mut D) {
96-
self.control_pins.reset(delay)
96+
self.control_pins.reset(delay);
9797
}
9898

99-
fn get_fw_version(&mut self) -> Result<FirmwareVersion, self::Error> {
99+
fn get_fw_version(&mut self) -> Result<FirmwareVersion, ProtocolError> {
100100
// TODO: improve the ergonomics around with_no_params()
101101
let operation =
102102
Operation::new(NinaCommand::GetFwVersion, 1).with_no_params(NinaNoParams::new(""));
103103

104-
self.execute(&operation).ok().unwrap();
104+
self.execute(&operation)?;
105105

106106
let result = self.receive(&operation)?;
107107

108108
Ok(FirmwareVersion::new(result)) // e.g. 1.7.4
109109
}
110110

111-
fn set_passphrase(&mut self, ssid: &str, passphrase: &str) -> Result<(), self::Error> {
111+
fn set_passphrase(&mut self, ssid: &str, passphrase: &str) -> Result<(), ProtocolError> {
112112
let operation = Operation::new(NinaCommand::SetPassphrase, 1)
113113
.param(NinaSmallArrayParam::new(ssid))
114114
.param(NinaSmallArrayParam::new(passphrase));
@@ -119,7 +119,7 @@ where
119119
Ok(())
120120
}
121121

122-
fn get_conn_status(&mut self) -> Result<u8, self::Error> {
122+
fn get_conn_status(&mut self) -> Result<u8, ProtocolError> {
123123
let operation =
124124
Operation::new(NinaCommand::GetConnStatus, 1).with_no_params(NinaNoParams::new(""));
125125

@@ -130,7 +130,7 @@ where
130130
Ok(result[0])
131131
}
132132

133-
fn disconnect(&mut self) -> Result<(), self::Error> {
133+
fn disconnect(&mut self) -> Result<(), ProtocolError> {
134134
let dummy_param = NinaByteParam::from_bytes(&[ControlByte::Dummy as u8]);
135135
let operation = Operation::new(NinaCommand::Disconnect, 1).param(dummy_param);
136136

@@ -147,26 +147,24 @@ where
147147
S: Transfer<u8>,
148148
C: EspControlInterface,
149149
{
150-
fn execute<P: NinaParam>(&mut self, operation: &Operation<P>) -> Result<(), Error> {
150+
fn execute<P: NinaParam>(&mut self, operation: &Operation<P>) -> Result<(), ProtocolError> {
151151
let mut param_size: u16 = 0;
152152
self.control_pins.wait_for_esp_select();
153153
let number_of_params: u8 = if operation.has_params {
154154
operation.params.len() as u8
155155
} else {
156156
0
157157
};
158-
self.send_cmd(&operation.command, number_of_params)
159-
.ok()
160-
.unwrap();
158+
let result = self.send_cmd(&operation.command, number_of_params);
161159

162160
// Only send params if they are present
163161
if operation.has_params {
164162
operation.params.iter().for_each(|param| {
165-
self.send_param(param).ok().unwrap();
163+
self.send_param(param).ok();
166164
param_size += param.length();
167165
});
168166

169-
self.send_end_cmd().ok().unwrap();
167+
self.send_end_cmd().ok();
170168

171169
// This is to make sure we align correctly
172170
// 4 (start byte, command byte, reply byte, end byte) + the sum of all param lengths
@@ -175,13 +173,13 @@ where
175173
}
176174
self.control_pins.esp_deselect();
177175

178-
Ok(())
176+
result
179177
}
180178

181179
fn receive<P: NinaParam>(
182180
&mut self,
183181
operation: &Operation<P>,
184-
) -> Result<[u8; ARRAY_LENGTH_PLACEHOLDER], Error> {
182+
) -> Result<[u8; ARRAY_LENGTH_PLACEHOLDER], ProtocolError> {
185183
self.control_pins.wait_for_esp_select();
186184

187185
let result =
@@ -192,7 +190,7 @@ where
192190
result
193191
}
194192

195-
fn send_cmd(&mut self, cmd: &NinaCommand, num_params: u8) -> Result<(), self::Error> {
193+
fn send_cmd(&mut self, cmd: &NinaCommand, num_params: u8) -> Result<(), ProtocolError> {
196194
let buf: [u8; 3] = [
197195
ControlByte::Start as u8,
198196
(*cmd as u8) & !(ControlByte::Reply as u8),
@@ -201,11 +199,11 @@ where
201199

202200
for byte in buf {
203201
let write_buf = &mut [byte];
204-
self.bus.transfer(write_buf).ok().unwrap();
202+
self.bus.transfer(write_buf).ok();
205203
}
206204

207205
if num_params == 0 {
208-
self.send_end_cmd().ok().unwrap();
206+
self.send_end_cmd().ok();
209207
}
210208
Ok(())
211209
}
@@ -214,43 +212,41 @@ where
214212
&mut self,
215213
cmd: &NinaCommand,
216214
num_params: u8,
217-
) -> Result<[u8; ARRAY_LENGTH_PLACEHOLDER], self::Error> {
215+
) -> Result<[u8; ARRAY_LENGTH_PLACEHOLDER], ProtocolError> {
218216
self.check_start_cmd().ok().unwrap();
219217
let byte_to_check: u8 = *cmd as u8 | ControlByte::Reply as u8;
220-
let result = self.read_and_check_byte(&byte_to_check)?;
218+
let result = self.read_and_check_byte(&byte_to_check).ok().unwrap();
221219
// Ensure we see a cmd byte
222220
if !result {
223-
return Ok([0x31, 0x2e, 0x37, 0x2e, 0x34, 0x0, 0x0, 0x0]);
224-
//return Err(SPIError::Misc);
221+
return Err(ProtocolError::InvalidCommand);
225222
}
226223

227-
let result = self.read_and_check_byte(&num_params)?;
224+
let result = self.read_and_check_byte(&num_params).unwrap();
228225
// Ensure we see the number of params we expected to receive back
229226
if !result {
230-
return Ok([0x31, 0x2e, 0x37, 0x2e, 0x34, 0x0, 0x0, 0x0]);
231-
//return Err(SPIError::Misc);
227+
return Err(ProtocolError::InvalidNumberOfParameters);
232228
}
233229

234-
let num_params_to_read = self.get_byte()? as usize;
230+
let num_params_to_read = self.get_byte().ok().unwrap() as usize;
235231

232+
// TODO: use a constant instead of inline params max == 8
236233
if num_params_to_read > 8 {
237-
return Ok([0x31, 0x2e, 0x37, 0x2e, 0x34, 0x0, 0x0, 0x0]);
238-
//return Err(SPIError::Misc);
234+
return Err(ProtocolError::TooManyParameters);
239235
}
240236

241237
let mut params: [u8; ARRAY_LENGTH_PLACEHOLDER] = [0; 8];
242238
for (index, _param) in params.into_iter().enumerate() {
243239
params[index] = self.get_byte().ok().unwrap()
244240
}
245241
let control_byte: u8 = ControlByte::End as u8;
246-
self.read_and_check_byte(&control_byte)?;
242+
self.read_and_check_byte(&control_byte).ok();
247243

248244
Ok(params)
249245
}
250246

251-
fn send_end_cmd(&mut self) -> Result<(), self::Error> {
247+
fn send_end_cmd(&mut self) -> Result<(), Infallible> {
252248
let end_command: &mut [u8] = &mut [ControlByte::End as u8];
253-
self.bus.transfer(end_command).ok().unwrap();
249+
self.bus.transfer(end_command).ok();
254250
Ok(())
255251
}
256252

@@ -271,58 +267,38 @@ where
271267
return Ok(true);
272268
}
273269
}
274-
Err(ProtocolError::Timeout)
270+
Err(ProtocolError::CommunicationTimeout)
275271
}
276272

277-
fn check_start_cmd(&mut self) -> Result<bool, self::Error> {
273+
fn check_start_cmd(&mut self) -> Result<bool, ProtocolError> {
278274
self.wait_for_byte(ControlByte::Start as u8)
279275
}
280276

281-
fn read_and_check_byte(&mut self, check_byte: &u8) -> Result<bool, self::Error> {
282-
match self.get_byte() {
283-
Ok(byte_out) => {
284-
// Question: does comparing two &u8s work the way we would think?
285-
return Ok(&byte_out == check_byte);
286-
}
287-
Err(e) => {
288-
return Err(e);
289-
}
290-
}
277+
fn read_and_check_byte(&mut self, check_byte: &u8) -> Result<bool, Infallible> {
278+
let byte = self.get_byte().ok().unwrap();
279+
Ok(&byte == check_byte)
291280
}
292281

293-
fn send_param<P: NinaParam>(&mut self, param: &P) -> Result<(), self::Error> {
282+
fn send_param<P: NinaParam>(&mut self, param: &P) -> Result<(), Infallible> {
294283
self.send_param_length(param)?;
295284

296285
for byte in param.data().iter() {
297-
self.bus.transfer(&mut [*byte]).ok().unwrap();
286+
self.bus.transfer(&mut [*byte]).ok();
298287
}
299288
Ok(())
300289
}
301290

302-
fn send_param_length<P: NinaParam>(&mut self, param: &P) -> Result<(), self::Error> {
291+
fn send_param_length<P: NinaParam>(&mut self, param: &P) -> Result<(), Infallible> {
303292
for byte in param.length_as_bytes().into_iter() {
304-
self.bus.transfer(&mut [byte]).ok().unwrap();
293+
self.bus.transfer(&mut [byte]).ok();
305294
}
306295
Ok(())
307296
}
308297

309298
fn pad_to_multiple_of_4(&mut self, mut command_size: u16) {
310299
while command_size % 4 == 0 {
311-
self.get_byte().ok().unwrap();
300+
self.get_byte().ok();
312301
command_size += 1;
313302
}
314303
}
315304
}
316-
317-
#[allow(dead_code)]
318-
/// Error which occurred during a SPI transaction with a target ESP32 device
319-
#[derive(Clone, Copy, Debug)]
320-
pub enum SPIError<SPIE, IOE> {
321-
/// The SPI implementation returned an error
322-
SPI(SPIE),
323-
/// The GPIO implementation returned an error when changing the chip-select pin state
324-
IO(IOE),
325-
/// Timeout
326-
Timeout,
327-
Misc,
328-
}

0 commit comments

Comments
 (0)