-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Read system configuration from flash partition
- Loading branch information
Showing
8 changed files
with
201 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,4 @@ | ||
/target | ||
|
||
config*.json | ||
!config-example.json |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Note that you must remove all comments in your own configuration file since | ||
// JSON officially doesn't allow comments and parsing will fail with comments. | ||
{ | ||
// SSID and WPA2 password to use for connecting to Wifi network. Wifi must | ||
// provide an IPv4 address via DHCP and internet access to required services. | ||
"wifi-ssid": "My Wifi", | ||
"wifi-password": "12345", | ||
|
||
// Credentials for connecting to the Vereinsflieger API. See Vereinsflieger | ||
// REST Documentation for details. Note that password needs to be given as | ||
// its hex MD5 hash instead of plain text. | ||
"vf-username": "[email protected]", | ||
"vf-password-md5": "00000000000000000000000000000000", | ||
"vf-appkey": "00000000000000000000000000000000", | ||
"vf-cid": 0, | ||
|
||
// Vereinsflieger article id to use for purchases | ||
"vf-article-id": 0 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
use alloc::string::String; | ||
use core::fmt; | ||
use core::ops::Deref; | ||
use embedded_storage::ReadStorage; | ||
use esp_partition_table::{DataPartitionType, PartitionTable, PartitionType}; | ||
use esp_storage::FlashStorage; | ||
use log::{debug, info, warn}; | ||
use serde::Deserialize; | ||
|
||
/// String with sensitive content (debug and display output redacted) | ||
#[derive(Default, Deserialize)] | ||
pub struct SensitiveString(String); | ||
|
||
impl fmt::Debug for SensitiveString { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
if self.0.is_empty() { | ||
self.0.fmt(f) | ||
} else { | ||
"<redacted>".fmt(f) | ||
} | ||
} | ||
} | ||
|
||
impl fmt::Display for SensitiveString { | ||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { | ||
if self.0.is_empty() { | ||
self.0.fmt(f) | ||
} else { | ||
"<redacted>".fmt(f) | ||
} | ||
} | ||
} | ||
|
||
impl Deref for SensitiveString { | ||
type Target = String; | ||
|
||
fn deref(&self) -> &Self::Target { | ||
&self.0 | ||
} | ||
} | ||
|
||
/// System configuration | ||
/// | ||
/// System configuration is stored in the `nvs` flash partition, so it stays unaffected by firmware | ||
/// updates via USB or OTA. Currently, configuration is read-only at runtime, i.e. it needs to be | ||
/// flashed manually once per device. To make this easier, it is expected to be stored in JSON | ||
/// format at the first sector (4 kb) of the `nvs` flash data partition (this is incompatible with | ||
/// the format that IDF nvs functions expect in this flash partition). See README.md for details | ||
/// on how to flash a configuration. | ||
/// | ||
/// If there is no valid JSON or no valid `nvs` data partition, a default configuration is provided | ||
/// (which isn't very useful, but at least doesn't prevent the device from starting). | ||
#[derive(Debug, Default, Deserialize)] | ||
#[serde(default, rename_all = "kebab-case")] | ||
pub struct Config { | ||
/// Wifi SSID to connect to | ||
pub wifi_ssid: String, | ||
/// Wifi password | ||
pub wifi_password: SensitiveString, | ||
} | ||
|
||
impl Config { | ||
/// Read configuration from nvs flash partition | ||
pub fn read() -> Self { | ||
let mut storage = FlashStorage::new(); | ||
|
||
// Read partition table (at 0x8000 by default) | ||
let table = PartitionTable::default(); | ||
debug!("Config: Reading partition table at 0x{:x}", table.addr); | ||
|
||
// Look up nvs data partition (at 0x9000 by default) | ||
let nvs_offset = if let Some(offset) = table | ||
.iter_storage(&mut storage, false) | ||
.flatten() | ||
.find(|partition| partition.type_ == PartitionType::Data(DataPartitionType::Nvs)) | ||
.map(|partition| partition.offset) | ||
{ | ||
debug!("Config: Found nvs data partition at offset 0x{:x}", offset); | ||
offset | ||
} else { | ||
warn!("Config: Unable to find nvs data partition"); | ||
return Self::default(); | ||
}; | ||
|
||
// Read first sector (4 kb) of nvs partition | ||
let mut bytes = [0; FlashStorage::SECTOR_SIZE as usize]; | ||
if let Err(_err) = storage.read(nvs_offset, &mut bytes) { | ||
warn!("Config: Unable to read nvs partition"); | ||
return Self::default(); | ||
} | ||
// Find first non-ascii character and trim to the end. This removes trailing 0xff bytes | ||
// (unused flash bytes), which would otherwise lead to 'trailing characters' serde error | ||
// nightly: let (json, _rest) = bytes.split_once(|b| !b.is_ascii()); | ||
let json = bytes | ||
.split(|b| !b.is_ascii()) | ||
.next() | ||
.unwrap_or(bytes.as_ref()); | ||
|
||
// Parse JSON config | ||
let config = match serde_json::from_slice::<Self>(json) { | ||
Ok(config) => config, | ||
Err(err) => { | ||
warn!( | ||
"Config: Unable to parse configuration in nvs partition: {}", | ||
err | ||
); | ||
return Self::default(); | ||
} | ||
}; | ||
|
||
debug!("Config: System configuration: {:?}", config); | ||
info!("Config: Configuration loaded from nvs partition"); | ||
config | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters