Compare commits
8 Commits
Author | SHA1 | Date | |
---|---|---|---|
6989c365cc | |||
f1321a696a | |||
027f80dce0 | |||
a2c6ffa599 | |||
419a960500 | |||
eed8db4863 | |||
98b81ae274 | |||
ef5cb2c876 |
72
Cargo.lock
generated
72
Cargo.lock
generated
|
@ -187,21 +187,6 @@ dependencies = [
|
||||||
"generic-array 0.14.7",
|
"generic-array 0.14.7",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "bootloader"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"cortex-m",
|
|
||||||
"cortex-m-rt",
|
|
||||||
"defmt",
|
|
||||||
"defmt-rtt",
|
|
||||||
"embassy-boot-rp",
|
|
||||||
"embassy-rp",
|
|
||||||
"embassy-sync",
|
|
||||||
"embassy-time",
|
|
||||||
"embedded-storage",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "buffered-io"
|
name = "buffered-io"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -293,7 +278,6 @@ checksum = "8ec610d8f49840a5b376c69663b6369e71f4b34484b9b2eb29fb918d92516cb9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bare-metal",
|
"bare-metal",
|
||||||
"bitfield",
|
"bitfield",
|
||||||
"critical-section",
|
|
||||||
"embedded-hal 0.2.7",
|
"embedded-hal 0.2.7",
|
||||||
"volatile-register",
|
"volatile-register",
|
||||||
]
|
]
|
||||||
|
@ -634,6 +618,7 @@ name = "embassy-embedded-hal"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
source = "git+https://github.com/embassy-rs/embassy#af7c93abba768057f1d3299c7b4f4aa4501b3e56"
|
source = "git+https://github.com/embassy-rs/embassy#af7c93abba768057f1d3299c7b4f4aa4501b3e56"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"defmt",
|
||||||
"embassy-futures",
|
"embassy-futures",
|
||||||
"embassy-sync",
|
"embassy-sync",
|
||||||
"embassy-time",
|
"embassy-time",
|
||||||
|
@ -1246,6 +1231,30 @@ version = "1.0.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "impl-tools"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d82c305b1081f1a99fda262883c788e50ab57d36c00830bdd7e0a82894ad965c"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg",
|
||||||
|
"impl-tools-lib",
|
||||||
|
"proc-macro-error",
|
||||||
|
"syn 2.0.29",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "impl-tools-lib"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "85d3946d886eaab0702fa0c6585adcced581513223fa9df7ccfabbd9fa331a88"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.29",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indexmap"
|
name = "indexmap"
|
||||||
version = "2.0.0"
|
version = "2.0.0"
|
||||||
|
@ -1758,25 +1767,24 @@ dependencies = [
|
||||||
"dsmr5",
|
"dsmr5",
|
||||||
"embassy-boot",
|
"embassy-boot",
|
||||||
"embassy-boot-rp",
|
"embassy-boot-rp",
|
||||||
|
"embassy-embedded-hal",
|
||||||
"embassy-executor",
|
"embassy-executor",
|
||||||
"embassy-futures",
|
"embassy-futures",
|
||||||
"embassy-net",
|
"embassy-net",
|
||||||
"embassy-rp",
|
"embassy-rp",
|
||||||
"embassy-sync",
|
"embassy-sync",
|
||||||
"embassy-time",
|
"embassy-time",
|
||||||
"embedded-io-async",
|
|
||||||
"embedded-storage",
|
|
||||||
"git-version",
|
"git-version",
|
||||||
"heapless 0.7.16",
|
"heapless 0.7.16",
|
||||||
"nourl",
|
"nourl",
|
||||||
"panic-probe",
|
"panic-probe",
|
||||||
"rand",
|
"rand",
|
||||||
"reqwless",
|
|
||||||
"rust-mqtt",
|
"rust-mqtt",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-json-core",
|
"serde-json-core",
|
||||||
"smoltcp",
|
"smoltcp",
|
||||||
"static_cell",
|
"static_cell",
|
||||||
|
"updater",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2130,6 +2138,32 @@ dependencies = [
|
||||||
"subtle",
|
"subtle",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "updater"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"cortex-m",
|
||||||
|
"cortex-m-rt",
|
||||||
|
"defmt",
|
||||||
|
"defmt-rtt",
|
||||||
|
"embassy-boot",
|
||||||
|
"embassy-futures",
|
||||||
|
"embassy-net",
|
||||||
|
"embassy-time",
|
||||||
|
"embedded-io-async",
|
||||||
|
"embedded-storage",
|
||||||
|
"embedded-tls",
|
||||||
|
"heapless 0.7.16",
|
||||||
|
"impl-tools",
|
||||||
|
"nourl",
|
||||||
|
"rand_core",
|
||||||
|
"reqwless",
|
||||||
|
"rust-mqtt",
|
||||||
|
"serde",
|
||||||
|
"serde-json-core",
|
||||||
|
"static_cell",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vcell"
|
name = "vcell"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
|
|
13
Cargo.toml
13
Cargo.toml
|
@ -3,9 +3,6 @@ name = "rp"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[workspace]
|
|
||||||
members = ["bootloader"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
cortex-m = { version = "0.7", features = ["inline-asm"] }
|
cortex-m = { version = "0.7", features = ["inline-asm"] }
|
||||||
cortex-m-rt = "0.7"
|
cortex-m-rt = "0.7"
|
||||||
|
@ -19,6 +16,7 @@ embassy-executor = { version = "0.3", features = [
|
||||||
"nightly",
|
"nightly",
|
||||||
"integrated-timers",
|
"integrated-timers",
|
||||||
] }
|
] }
|
||||||
|
embassy-embedded-hal = { version = "0.1", features = ["defmt", "nightly"] }
|
||||||
embassy-rp = { version = "0.1", features = [
|
embassy-rp = { version = "0.1", features = [
|
||||||
"defmt",
|
"defmt",
|
||||||
"unstable-traits",
|
"unstable-traits",
|
||||||
|
@ -59,7 +57,6 @@ cyw43-pio = { git = "https://github.com/embassy-rs/embassy", features = [
|
||||||
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
panic-probe = { version = "0.3", features = ["print-defmt"] }
|
||||||
static_cell = { version = "1.1", features = ["nightly"] }
|
static_cell = { version = "1.1", features = ["nightly"] }
|
||||||
heapless = { version = "0.7.16", features = ["defmt", "serde"] }
|
heapless = { version = "0.7.16", features = ["defmt", "serde"] }
|
||||||
embedded-io-async = { version = "0.5", features = ["defmt-03"] }
|
|
||||||
dsmr5 = "0.3"
|
dsmr5 = "0.3"
|
||||||
rust-mqtt = { version = "0.1.5", features = [
|
rust-mqtt = { version = "0.1.5", features = [
|
||||||
"defmt",
|
"defmt",
|
||||||
|
@ -81,13 +78,14 @@ smoltcp = { version = "0.10.0", default-features = false, features = [
|
||||||
"dns-max-server-count-4",
|
"dns-max-server-count-4",
|
||||||
] }
|
] }
|
||||||
nourl = { version = "0.1.1", features = ["defmt"] }
|
nourl = { version = "0.1.1", features = ["defmt"] }
|
||||||
reqwless = { version = "0.5.0", features = ["defmt"] }
|
|
||||||
embedded-storage = "0.3.0"
|
|
||||||
const_format = "0.2.31"
|
const_format = "0.2.31"
|
||||||
git-version = "0.3.5"
|
git-version = "0.3.5"
|
||||||
|
|
||||||
|
updater = { path = "../iot_tools/updater" }
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
embassy-executor = { git = "https://github.com/embassy-rs/embassy" }
|
embassy-executor = { git = "https://github.com/embassy-rs/embassy" }
|
||||||
|
embassy-embedded-hal = { git = "https://github.com/embassy-rs/embassy" }
|
||||||
embassy-rp = { git = "https://github.com/embassy-rs/embassy" }
|
embassy-rp = { git = "https://github.com/embassy-rs/embassy" }
|
||||||
embassy-time = { git = "https://github.com/embassy-rs/embassy" }
|
embassy-time = { git = "https://github.com/embassy-rs/embassy" }
|
||||||
embassy-net = { git = "https://github.com/embassy-rs/embassy" }
|
embassy-net = { git = "https://github.com/embassy-rs/embassy" }
|
||||||
|
@ -110,7 +108,8 @@ dotenvy = "0.15"
|
||||||
[features]
|
[features]
|
||||||
# With this feature enabled the cyw43 fiwmares are not included in the build and need to be manually flashed
|
# With this feature enabled the cyw43 fiwmares are not included in the build and need to be manually flashed
|
||||||
# This helps speed up development
|
# This helps speed up development
|
||||||
exclude_firmwares = []
|
include_firmwares = []
|
||||||
|
invert_rx = []
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
debug = true
|
debug = true
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "bootloader"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2021"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
defmt = { version = "0.3", optional = true }
|
|
||||||
defmt-rtt = { version = "0.4", optional = true }
|
|
||||||
|
|
||||||
embassy-rp = { version = "0.1", features = ["nightly"] }
|
|
||||||
embassy-boot-rp = { version = "0.1" }
|
|
||||||
embassy-sync = { version = "0.2" }
|
|
||||||
embassy-time = { version = "0.1", features = ["nightly"] }
|
|
||||||
|
|
||||||
cortex-m = { version = "0.7", features = [
|
|
||||||
"inline-asm",
|
|
||||||
"critical-section-single-core",
|
|
||||||
] }
|
|
||||||
cortex-m-rt = { version = "0.7" }
|
|
||||||
embedded-storage = "0.3.0"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
defmt = ["dep:defmt", "embassy-boot-rp/defmt", "embassy-rp/defmt"]
|
|
||||||
debug = ["defmt-rtt", "defmt"]
|
|
|
@ -1,36 +0,0 @@
|
||||||
use std::env;
|
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Write;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let out = &PathBuf::from(env::var_os("OUT_DIR").unwrap());
|
|
||||||
|
|
||||||
// By default cortex-m-rt expects memory.x, however this causes issues with workspaces as it
|
|
||||||
// will pick the first file that is found.
|
|
||||||
// In order to get around this we make a dummy memory.x file
|
|
||||||
File::create(out.join("memory.x")).unwrap();
|
|
||||||
|
|
||||||
// Use memory.x.in as a template for the actual memory.x
|
|
||||||
let memory = include_str!("../memory.x.in")
|
|
||||||
.replace("{BOOTLOADER}", "FLASH")
|
|
||||||
.replace("{ACTIVE}", "ACTIVE");
|
|
||||||
|
|
||||||
// And then include it with a unique name
|
|
||||||
File::create(out.join("memory_boot.x"))
|
|
||||||
.unwrap()
|
|
||||||
.write_all(memory.as_bytes())
|
|
||||||
.unwrap();
|
|
||||||
println!("cargo:rustc-link-search={}", out.display());
|
|
||||||
println!("cargo:rerun-if-changed=../memory.x.in");
|
|
||||||
|
|
||||||
// And link with that one
|
|
||||||
println!("cargo:rustc-link-arg-bins=-Tmemory_boot.x");
|
|
||||||
|
|
||||||
println!("cargo:rustc-link-arg-bins=--nmagic");
|
|
||||||
println!("cargo:rustc-link-arg-bins=-Tlink.x");
|
|
||||||
println!("cargo:rustc-link-arg-bins=-Tlink-rp.x");
|
|
||||||
if env::var("CARGO_FEATURE_DEFMT").is_ok() {
|
|
||||||
println!("cargo:rustc-link-arg-bins=-Tdefmt.x");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
#![no_std]
|
|
||||||
#![no_main]
|
|
||||||
|
|
||||||
use core::cell::RefCell;
|
|
||||||
|
|
||||||
use cortex_m_rt::{entry, exception};
|
|
||||||
#[cfg(feature = "defmt")]
|
|
||||||
use defmt_rtt as _;
|
|
||||||
use embassy_boot_rp::*;
|
|
||||||
use embassy_rp::flash::{Blocking, Flash};
|
|
||||||
use embassy_sync::blocking_mutex::Mutex;
|
|
||||||
|
|
||||||
const FLASH_SIZE: usize = 2 * 1024 * 1024;
|
|
||||||
|
|
||||||
#[entry]
|
|
||||||
fn main() -> ! {
|
|
||||||
let p = embassy_rp::init(Default::default());
|
|
||||||
|
|
||||||
let flash = Flash::<_, Blocking, FLASH_SIZE>::new_blocking(p.FLASH);
|
|
||||||
let flash = Mutex::new(RefCell::new(flash));
|
|
||||||
|
|
||||||
let config = BootLoaderConfig::from_linkerfile_blocking(&flash);
|
|
||||||
let active_offset = config.active.offset();
|
|
||||||
let bl: BootLoader = BootLoader::prepare(config);
|
|
||||||
|
|
||||||
unsafe { bl.load(embassy_rp::flash::FLASH_BASE as u32 + active_offset) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
#[cfg_attr(target_os = "none", link_section = ".HardFault.user")]
|
|
||||||
unsafe extern "C" fn HardFault() {
|
|
||||||
cortex_m::peripheral::SCB::sys_reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
#[exception]
|
|
||||||
unsafe fn DefaultHandler(_: i16) -> ! {
|
|
||||||
const SCB_ICSR: *const u32 = 0xE000_ED04 as *const u32;
|
|
||||||
let irqn = core::ptr::read_volatile(SCB_ICSR) as u8 as i16 - 16;
|
|
||||||
|
|
||||||
panic!("DefaultHandler #{:?}", irqn);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[panic_handler]
|
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
|
||||||
cortex_m::asm::udf();
|
|
||||||
}
|
|
|
@ -1,7 +1,7 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
mkdir -p target/firmware
|
mkdir -p target/firmware
|
||||||
cargo objcopy --release --features=exclude_firmwares -- -O binary target/firmware/firmware
|
cargo objcopy --release --features=include_firmwares,invert_rx -- -O binary target/firmware/firmware
|
||||||
shasum -a 512 -b target/firmware/firmware | dd ibs=128 count=1 | xxd -p -r > target/firmware/checksum
|
shasum -a 512 -b target/firmware/firmware | dd ibs=128 count=1 | xxd -p -r > target/firmware/checksum
|
||||||
signify -S -m target/firmware/checksum -s ~/Projects/crypt/R0/private/keys/firmware/pico_p1.sec -x target/firmware/checksum.sig
|
signify -S -m target/firmware/checksum -s ~/Projects/crypt/R0/private/keys/firmware/pico_p1.sec -x target/firmware/checksum.sig
|
||||||
tail -n1 target/firmware/checksum.sig | base64 -d -i | dd ibs=10 skip=1 > target/firmware/signed
|
tail -n1 target/firmware/checksum.sig | base64 -d -i | dd ibs=10 skip=1 > target/firmware/signed
|
||||||
|
|
252
src/main.rs
252
src/main.rs
|
@ -2,11 +2,10 @@
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(type_alias_impl_trait)]
|
#![feature(type_alias_impl_trait)]
|
||||||
|
|
||||||
use core::cell::RefCell;
|
use core::{cell::RefCell, str::from_utf8};
|
||||||
|
|
||||||
use embassy_boot_rp::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig};
|
use embassy_boot_rp::{AlignedBuffer, BlockingFirmwareUpdater, FirmwareUpdaterConfig};
|
||||||
use embedded_storage::nor_flash::NorFlash;
|
use heapless::Vec;
|
||||||
use heapless::{String, Vec};
|
|
||||||
use rand::{
|
use rand::{
|
||||||
rngs::{SmallRng, StdRng},
|
rngs::{SmallRng, StdRng},
|
||||||
RngCore, SeedableRng,
|
RngCore, SeedableRng,
|
||||||
|
@ -34,14 +33,9 @@ use embassy_sync::{
|
||||||
channel::{Channel, Sender},
|
channel::{Channel, Sender},
|
||||||
};
|
};
|
||||||
use embassy_time::{Duration, Ticker, Timer};
|
use embassy_time::{Duration, Ticker, Timer};
|
||||||
use embedded_io_async::Read;
|
|
||||||
|
|
||||||
use dsmr5::Readout;
|
use dsmr5::Readout;
|
||||||
use nourl::Url;
|
use nourl::Url;
|
||||||
use reqwless::{
|
|
||||||
request::{Method, Request, RequestBuilder},
|
|
||||||
response::Response,
|
|
||||||
};
|
|
||||||
use rust_mqtt::{
|
use rust_mqtt::{
|
||||||
client::{
|
client::{
|
||||||
client::MqttClient,
|
client::MqttClient,
|
||||||
|
@ -49,11 +43,10 @@ use rust_mqtt::{
|
||||||
},
|
},
|
||||||
packet::v5::publish_packet::QualityOfService,
|
packet::v5::publish_packet::QualityOfService,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use static_cell::make_static;
|
use static_cell::make_static;
|
||||||
|
|
||||||
use const_format::formatcp;
|
use const_format::formatcp;
|
||||||
use defmt::{debug, error, info, trace, warn, Debug2Format};
|
use defmt::*;
|
||||||
|
|
||||||
use {defmt_rtt as _, panic_probe as _};
|
use {defmt_rtt as _, panic_probe as _};
|
||||||
|
|
||||||
|
@ -69,34 +62,7 @@ const TOPIC_UPDATE: &str = formatcp!("{}/update", TOPIC_BASE);
|
||||||
const VERSION: &str = git_version::git_version!();
|
const VERSION: &str = git_version::git_version!();
|
||||||
const PUBLIC_SIGNING_KEY: &[u8] = include_bytes!("../key.pub");
|
const PUBLIC_SIGNING_KEY: &[u8] = include_bytes!("../key.pub");
|
||||||
|
|
||||||
#[derive(Deserialize)]
|
const FLASH_SIZE: usize = 2 * 1024 * 1024;
|
||||||
struct UpdateMessage<'a> {
|
|
||||||
url: &'a str,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UpdateMessage<'_> {
|
|
||||||
fn get_url(&self) -> String<1024> {
|
|
||||||
self.url.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize)]
|
|
||||||
#[serde(rename_all = "snake_case", tag = "status")]
|
|
||||||
enum Status<'a> {
|
|
||||||
Connected { version: &'a str },
|
|
||||||
Disconnected,
|
|
||||||
PreparingUpdate,
|
|
||||||
Erasing,
|
|
||||||
Writing { progress: u32 },
|
|
||||||
UpdateComplete,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Status<'_> {
|
|
||||||
fn vec(&self) -> Vec<u8, 1024> {
|
|
||||||
serde_json_core::to_vec(self)
|
|
||||||
.expect("The buffer should be large enough to contain all the data")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[embassy_executor::task]
|
#[embassy_executor::task]
|
||||||
async fn wifi_task(
|
async fn wifi_task(
|
||||||
|
@ -175,7 +141,7 @@ async fn uart_rx_task(
|
||||||
/// Get the cyw43 firmware blobs
|
/// Get the cyw43 firmware blobs
|
||||||
///
|
///
|
||||||
/// # Safety
|
/// # Safety
|
||||||
/// When building with `exclude_firmwares` make sure to flash the firmwares using the following
|
/// When building without `include_firmwares` make sure to flash the firmwares using the following
|
||||||
/// commands:
|
/// commands:
|
||||||
/// ```bash
|
/// ```bash
|
||||||
/// probe-rs download firmware/43439A0.bin --format bin --chip RP2040 --base-address 0x101BE000
|
/// probe-rs download firmware/43439A0.bin --format bin --chip RP2040 --base-address 0x101BE000
|
||||||
|
@ -183,7 +149,12 @@ async fn uart_rx_task(
|
||||||
/// ```
|
/// ```
|
||||||
unsafe fn get_firmware() -> (&'static [u8], &'static [u8]) {
|
unsafe fn get_firmware() -> (&'static [u8], &'static [u8]) {
|
||||||
cfg_if::cfg_if! {
|
cfg_if::cfg_if! {
|
||||||
if #[cfg(feature = "exclude_firmwares")] {
|
if #[cfg(feature = "include_firmwares")] {
|
||||||
|
let fw = include_bytes!("../firmware/43439A0.bin");
|
||||||
|
let clm = include_bytes!("../firmware/43439A0_clm.bin");
|
||||||
|
|
||||||
|
(fw, clm)
|
||||||
|
} else {
|
||||||
// TODO: It would be nice if it could automatically get the correct size
|
// TODO: It would be nice if it could automatically get the correct size
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#[link_name = "__fw_start"]
|
#[link_name = "__fw_start"]
|
||||||
|
@ -193,11 +164,6 @@ unsafe fn get_firmware() -> (&'static [u8], &'static [u8]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
(&fw, &clm)
|
(&fw, &clm)
|
||||||
} else {
|
|
||||||
let fw = include_bytes!("../firmware/43439A0.bin");
|
|
||||||
let clm = include_bytes!("../firmware/43439A0_clm.bin");
|
|
||||||
|
|
||||||
(fw, clm)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,10 +175,24 @@ async fn main(spawner: Spawner) {
|
||||||
|
|
||||||
let channel = make_static!(Channel::<NoopRawMutex, _, 1>::new());
|
let channel = make_static!(Channel::<NoopRawMutex, _, 1>::new());
|
||||||
|
|
||||||
|
// TODO: Ideally we use async flash
|
||||||
|
// This has issues with alignment right now
|
||||||
|
let flash = Flash::<_, _, FLASH_SIZE>::new_blocking(p.FLASH);
|
||||||
|
let flash = Mutex::new(RefCell::new(flash));
|
||||||
|
|
||||||
|
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
|
||||||
|
let mut aligned = AlignedBuffer([0; WRITE_SIZE]);
|
||||||
|
let updater = BlockingFirmwareUpdater::new(config, &mut aligned.0);
|
||||||
|
|
||||||
|
let mut updater = updater::Updater::new(updater, TOPIC_STATUS, VERSION, PUBLIC_SIGNING_KEY);
|
||||||
|
|
||||||
// === UART ===
|
// === UART ===
|
||||||
let mut config = uart::Config::default();
|
let mut config = uart::Config::default();
|
||||||
config.parity = Parity::ParityNone;
|
config.parity = Parity::ParityNone;
|
||||||
// config.invert_rx = true;
|
#[cfg(feature = "invert_rx")]
|
||||||
|
{
|
||||||
|
config.invert_rx = true;
|
||||||
|
}
|
||||||
|
|
||||||
let rx = UartRx::new(p.UART0, p.PIN_17, Irqs, p.DMA_CH0, config);
|
let rx = UartRx::new(p.UART0, p.PIN_17, Irqs, p.DMA_CH0, config);
|
||||||
|
|
||||||
|
@ -250,8 +230,7 @@ async fn main(spawner: Spawner) {
|
||||||
|
|
||||||
// Use the Ring Oscillator of the RP2040 as a source of true randomness to seed the
|
// Use the Ring Oscillator of the RP2040 as a source of true randomness to seed the
|
||||||
// cryptographically secure PRNG
|
// cryptographically secure PRNG
|
||||||
let mut rng_rosc = RoscRng;
|
let mut rng = StdRng::from_rng(&mut RoscRng).unwrap();
|
||||||
let mut rng = StdRng::from_rng(&mut rng_rosc).unwrap();
|
|
||||||
|
|
||||||
let stack = make_static!(Stack::new(
|
let stack = make_static!(Stack::new(
|
||||||
net_device,
|
net_device,
|
||||||
|
@ -280,7 +259,7 @@ async fn main(spawner: Spawner) {
|
||||||
info!("IP Address: {}", cfg.address.address());
|
info!("IP Address: {}", cfg.address.address());
|
||||||
|
|
||||||
let mut rx_buffer = [0; 1024];
|
let mut rx_buffer = [0; 1024];
|
||||||
let mut tx_buffer = [0; 4096];
|
let mut tx_buffer = [0; 1024];
|
||||||
|
|
||||||
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
|
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
|
||||||
// socket.set_timeout(Some(Duration::from_secs(10)));
|
// socket.set_timeout(Some(Duration::from_secs(10)));
|
||||||
|
@ -300,19 +279,17 @@ async fn main(spawner: Spawner) {
|
||||||
MqttVersion::MQTTv5,
|
MqttVersion::MQTTv5,
|
||||||
// Use fast and simple PRNG to generate packet identifiers, there is no need for this to be
|
// Use fast and simple PRNG to generate packet identifiers, there is no need for this to be
|
||||||
// cryptographically secure
|
// cryptographically secure
|
||||||
SmallRng::from_rng(&mut rng_rosc).unwrap(),
|
SmallRng::from_rng(&mut RoscRng).unwrap(),
|
||||||
);
|
);
|
||||||
|
|
||||||
config.add_username(env!("MQTT_USERNAME"));
|
config.add_username(env!("MQTT_USERNAME"));
|
||||||
config.add_password(env!("MQTT_PASSWORD"));
|
config.add_password(env!("MQTT_PASSWORD"));
|
||||||
config.add_max_subscribe_qos(QualityOfService::QoS1);
|
config.add_max_subscribe_qos(QualityOfService::QoS1);
|
||||||
config.add_client_id(ID);
|
config.add_client_id(ID);
|
||||||
// Leads to InsufficientBufferSize error
|
updater.add_will(&mut config);
|
||||||
let msg: &Vec<_, 1024> = make_static!(Status::Disconnected.vec());
|
|
||||||
config.add_will(TOPIC_STATUS, &msg, true);
|
|
||||||
|
|
||||||
let mut recv_buffer = [0; 1024];
|
let mut recv_buffer = [0; 1024];
|
||||||
let mut write_buffer = [0; 4096];
|
let mut write_buffer = [0; 1024];
|
||||||
|
|
||||||
let mut client =
|
let mut client =
|
||||||
MqttClient::<_, 5, _>::new(socket, &mut write_buffer, &mut recv_buffer, config);
|
MqttClient::<_, 5, _>::new(socket, &mut write_buffer, &mut recv_buffer, config);
|
||||||
|
@ -321,25 +298,8 @@ async fn main(spawner: Spawner) {
|
||||||
client.connect_to_broker().await.unwrap();
|
client.connect_to_broker().await.unwrap();
|
||||||
info!("MQTT Connected!");
|
info!("MQTT Connected!");
|
||||||
|
|
||||||
// TODO: Ideally we use async flash
|
|
||||||
// This has issues with alignment right now
|
|
||||||
let flash = Flash::<_, _, FLASH_SIZE>::new_blocking(p.FLASH);
|
|
||||||
let flash = Mutex::new(RefCell::new(flash));
|
|
||||||
|
|
||||||
let config = FirmwareUpdaterConfig::from_linkerfile_blocking(&flash);
|
|
||||||
let mut aligned = AlignedBuffer([0; WRITE_SIZE]);
|
|
||||||
let mut updater = BlockingFirmwareUpdater::new(config, &mut aligned.0);
|
|
||||||
|
|
||||||
// We wait with marking as booted until everything is connected
|
|
||||||
updater.mark_booted().unwrap();
|
|
||||||
|
|
||||||
let status = Status::Connected { version: VERSION }.vec();
|
|
||||||
client
|
|
||||||
.send_message(TOPIC_STATUS, &status, QualityOfService::QoS1, true)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
client.subscribe_to_topic(TOPIC_UPDATE).await.unwrap();
|
client.subscribe_to_topic(TOPIC_UPDATE).await.unwrap();
|
||||||
|
updater.ready(&mut client).await.unwrap();
|
||||||
|
|
||||||
// Turn LED off when connected
|
// Turn LED off when connected
|
||||||
control.gpio_set(0, false).await;
|
control.gpio_set(0, false).await;
|
||||||
|
@ -368,26 +328,39 @@ async fn main(spawner: Spawner) {
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
Either3::Third(message) => {
|
Either3::Third(Ok((TOPIC_UPDATE, url))) => {
|
||||||
let message = match message {
|
let url: Vec<_, 256> = match Vec::from_slice(url) {
|
||||||
Ok(message) => message,
|
Ok(url) => url,
|
||||||
Err(err) => {
|
|
||||||
error!("Failed to receive MQTT message: {}", err);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let message = match serde_json_core::from_slice::<UpdateMessage>(message.1) {
|
|
||||||
Ok((message, _)) => message,
|
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!("Unable to parse update message");
|
error!("URL is longer then buffer size");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let url = match from_utf8(&url) {
|
||||||
|
Ok(url) => url,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Url is not valid utf-8 string: {}", Display2Format(&err));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let url = match Url::parse(url) {
|
||||||
|
Ok(url) => url,
|
||||||
|
Err(err) => {
|
||||||
|
error!("Failed to parse url: {}", err);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let url = message.get_url();
|
if let Err(err) = updater.update(url, stack, &mut rng, &mut client).await {
|
||||||
let url = Url::parse(url.as_str()).unwrap();
|
error!("Update failed: {}", err)
|
||||||
attempt_update(stack, &mut updater, &mut client, url).await;
|
}
|
||||||
|
}
|
||||||
|
Either3::Third(Ok(_)) => {
|
||||||
|
warn!("No handler for mqtt message");
|
||||||
|
}
|
||||||
|
Either3::Third(Err(err)) => {
|
||||||
|
error!("Failed to receive MQTT message: {}", err);
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -405,108 +378,3 @@ async fn wait_for_config(
|
||||||
yield_now().await;
|
yield_now().await;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const FLASH_SIZE: usize = 2 * 1024 * 1024;
|
|
||||||
|
|
||||||
async fn attempt_update<T, const MAX_PROPERTIES: usize, R, F>(
|
|
||||||
stack: &'static Stack<cyw43::NetDriver<'static>>,
|
|
||||||
updater: &mut BlockingFirmwareUpdater<'_, F, F>,
|
|
||||||
client: &mut MqttClient<'_, T, MAX_PROPERTIES, R>,
|
|
||||||
url: Url<'_>,
|
|
||||||
) where
|
|
||||||
T: embedded_io_async::Write + embedded_io_async::Read,
|
|
||||||
R: rand::RngCore,
|
|
||||||
F: NorFlash,
|
|
||||||
{
|
|
||||||
info!("Preparing for OTA...");
|
|
||||||
let status = Status::PreparingUpdate.vec();
|
|
||||||
client
|
|
||||||
.send_message(TOPIC_STATUS, &status, QualityOfService::QoS1, false)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let ip = stack.dns_query(url.host(), DnsQueryType::A).await.unwrap()[0];
|
|
||||||
|
|
||||||
let mut rx_buffer = [0; 4096 * 2];
|
|
||||||
let mut tx_buffer = [0; 1024];
|
|
||||||
|
|
||||||
let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
|
|
||||||
let addr = (ip, url.port_or_default());
|
|
||||||
debug!("Addr: {}", addr);
|
|
||||||
socket.connect(addr).await.unwrap();
|
|
||||||
|
|
||||||
debug!("Path: {}", url.path());
|
|
||||||
Request::get(url.path())
|
|
||||||
.build()
|
|
||||||
.write(&mut socket)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut headers = [0; 4096];
|
|
||||||
let resp = Response::read(&mut socket, Method::GET, &mut headers)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let mut body = resp.body().reader();
|
|
||||||
|
|
||||||
debug!("Erasing flash...");
|
|
||||||
let status = Status::Erasing.vec();
|
|
||||||
client
|
|
||||||
.send_message(TOPIC_STATUS, &status, QualityOfService::QoS1, false)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let writer = updater
|
|
||||||
.prepare_update()
|
|
||||||
.map_err(|e| warn!("E: {:?}", Debug2Format(&e)))
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
debug!("Writing...");
|
|
||||||
let status = Status::Writing { progress: 0 }.vec();
|
|
||||||
client
|
|
||||||
.send_message(TOPIC_STATUS, &status, QualityOfService::QoS1, false)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// The first 64 bytes of the file contain the signature
|
|
||||||
let mut signature = [0; 64];
|
|
||||||
body.read_exact(&mut signature).await.unwrap();
|
|
||||||
|
|
||||||
trace!("Signature: {:?}", signature);
|
|
||||||
|
|
||||||
let mut buffer = AlignedBuffer([0; 4096]);
|
|
||||||
let mut size = 0;
|
|
||||||
while let Ok(read) = body.read(&mut buffer.0).await {
|
|
||||||
if read == 0 {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
debug!("Writing chunk: {}", read);
|
|
||||||
writer.write(size, &buffer.0[..read]).unwrap();
|
|
||||||
size += read as u32;
|
|
||||||
|
|
||||||
let status = Status::Writing { progress: size }.vec();
|
|
||||||
client
|
|
||||||
.send_message(TOPIC_STATUS, &status, QualityOfService::QoS1, false)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
debug!("Total size: {}", size);
|
|
||||||
|
|
||||||
updater.verify_and_mark_updated(PUBLIC_SIGNING_KEY, &signature, size).unwrap();
|
|
||||||
|
|
||||||
// Update mqtt message should be send using retain
|
|
||||||
// TODO: Clear the message
|
|
||||||
|
|
||||||
let status = Status::UpdateComplete.vec();
|
|
||||||
client
|
|
||||||
.send_message(TOPIC_STATUS, &status, QualityOfService::QoS1, false)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
client.disconnect().await.unwrap();
|
|
||||||
|
|
||||||
info!("Restarting in 5 seconds...");
|
|
||||||
Timer::after(Duration::from_secs(5)).await;
|
|
||||||
|
|
||||||
cortex_m::peripheral::SCB::sys_reset();
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user