RTIC Integration
The ferrite-rtic crate provides a blocking upload helper and a shared-resource wrapper for RTIC v2 applications.
Overview
RTIC uses priority-based preemptive scheduling. The ferrite-sdk RTIC integration provides two tools:
upload_blocking()-- a thin wrapper aroundUploadManager::upload()for use in software tasks.RticTransportResource<T>-- a shared resource that holds your transport and a pending flag, enabling the request/poll pattern common in RTIC applications.
Dependencies
[dependencies]
ferrite-sdk = { git = "https://github.com/mighty840/ferrite-sdk", features = ["cortex-m", "defmt"] }
ferrite-rtic = { git = "https://github.com/mighty840/ferrite-sdk" }
rtic = { version = "2", features = ["thumbv7-backend"] }Note: the embassy feature is not needed for RTIC. The RTIC integration uses the blocking ChunkTransport trait.
Simple pattern: upload_blocking
If you have a dedicated software task for uploads and can pass the transport directly:
#[rtic::app(device = nrf52840_hal::pac, dispatchers = [SWI0_EGU0])]
mod app {
use ferrite_rtic::upload_blocking;
#[shared]
struct Shared {}
#[local]
struct Local {
transport: MyUartTransport,
}
#[init]
fn init(cx: init::Context) -> (Shared, Local) {
// ... init SDK, configure peripherals ...
let transport = MyUartTransport::new(uart);
upload_task::spawn_after(60.secs()).ok();
(Shared {}, Local { transport })
}
#[task(local = [transport])]
async fn upload_task(cx: upload_task::Context) {
match upload_blocking(cx.local.transport) {
Ok(stats) => {
defmt::info!("upload ok: {} chunks", stats.chunks_sent);
}
Err(e) => {
defmt::warn!("upload failed: {}", defmt::Debug2Format(&e));
}
}
// Re-schedule
upload_task::spawn_after(60.secs()).ok();
}
}Advanced pattern: RticTransportResource
When the transport is a shared resource (e.g., a UART that is also used for other purposes), use RticTransportResource:
#[rtic::app(device = nrf52840_hal::pac, dispatchers = [SWI0_EGU0, SWI1_EGU1])]
mod app {
use ferrite_rtic::RticTransportResource;
#[shared]
struct Shared {
uploader: RticTransportResource<MyUartTransport>,
}
#[local]
struct Local {}
#[init]
fn init(cx: init::Context) -> (Shared, Local) {
let transport = MyUartTransport::new(uart);
let uploader = RticTransportResource::new(transport);
// Schedule periodic upload requests
periodic::spawn_after(60.secs()).ok();
(Shared { uploader }, Local {})
}
/// Higher-priority task: requests an upload and pends the upload task.
#[task(shared = [uploader], priority = 2)]
async fn periodic(mut cx: periodic::Context) {
cx.shared.uploader.lock(|u| u.request_upload());
do_upload::spawn().ok();
// Re-schedule
periodic::spawn_after(60.secs()).ok();
}
/// Lower-priority software task: performs the actual upload.
#[task(shared = [uploader], priority = 1)]
async fn do_upload(mut cx: do_upload::Context) {
cx.shared.uploader.lock(|u| {
if let Some(result) = u.poll() {
match result {
Ok(stats) => {
defmt::info!("upload ok: {} chunks", stats.chunks_sent);
}
Err(e) => {
defmt::warn!("upload failed: {}", defmt::Debug2Format(&e));
}
}
}
});
}
}How RticTransportResource works
request_upload()sets an internalpendingflag. This is cheap and can be called from any priority level.poll()checks the pending flag. If set, it callsUploadManager::upload()with the inner transport and returnsSome(result). If not set, it returnsNone.- On success, the pending flag is cleared.
- On
TransportUnavailable, the pending flag is kept so the upload retries on the next poll. - On other errors (encoding, transport failure mid-session), the pending flag is cleared to avoid tight retry loops. The caller can re-request if desired.
Accessing the underlying transport
If you need the transport for non-upload purposes:
cx.shared.uploader.lock(|u| {
let transport = u.transport_mut();
// Use transport for other UART operations
});You can also consume the resource to recover the transport:
let transport = uploader.into_inner();Implementing ChunkTransport for RTIC
The blocking ChunkTransport trait is simpler than the async variant:
use ferrite_sdk::transport::ChunkTransport;
use nrf52840_hal::uarte::{self, Uarte};
pub struct MyUartTransport {
uart: Uarte<nrf52840_hal::pac::UARTE0>,
}
impl ChunkTransport for MyUartTransport {
type Error = uarte::Error;
fn send_chunk(&mut self, chunk: &[u8]) -> Result<(), Self::Error> {
self.uart.write(chunk)?;
Ok(())
}
}Triggering an immediate upload
Unlike the Embassy integration (which has a built-in trigger_upload_now() channel), the RTIC pattern uses RTIC's own task spawning:
// From any task or interrupt handler:
do_upload::spawn().ok();
// Or from a shared resource context:
cx.shared.uploader.lock(|u| u.request_upload());
do_upload::spawn().ok();