Skip to content

Instantly share code, notes, and snippets.

@dcoles
Created October 21, 2024 04:00
Show Gist options
  • Save dcoles/4929ba2250695d46e076474e1533018b to your computer and use it in GitHub Desktop.
Save dcoles/4929ba2250695d46e076474e1533018b to your computer and use it in GitHub Desktop.
Flipper Zero serial reader (work in progress)
#![no_main]
#![no_std]
// Required for panic handler
extern crate flipperzero_rt;
use core::{ffi::{c_void, CStr}, ops::{Deref, DerefMut}, ptr};
use flipperzero::{furi::{self, stream_buffer::FuriStreamBuffer, thread::{self, ThreadId}, time::Duration}, info, log, println};
use flipperzero_rt::{entry, manifest};
// Required for log macros
use flipperzero::__macro_support::ufmt;
use flipperzero_sys as sys;
// Define the FAP Manifest for this application
manifest!(
name = "Try UART From Rust",
app_version = 1,
has_icon = false,
);
// Define the entry function
entry!(entry);
// Entry point
fn entry(_args: Option<&CStr>) -> i32 {
unsafe { main() }
0
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct WorkerEvent(u32);
impl WorkerEvent {
pub const FLAG_RESERVED: u32 = (1 << 0);
pub const FLAG_STOP: u32 = (1 << 1);
pub const FLAG_DATA: u32 = (1 << 2);
pub const FLAG_IDLE: u32 = (1 << 3);
pub const FLAG_OVERRUN_ERROR: u32 = (1 << 4);
pub const FLAG_FRAMING_ERROR: u32 = (1 << 5);
pub const FLAG_NOISE_ERROR: u32 = (1 << 6);
pub const MASK: u32 = Self::FLAG_STOP | Self::FLAG_DATA | Self::FLAG_IDLE | Self::FLAG_OVERRUN_ERROR | Self::FLAG_FRAMING_ERROR | Self::FLAG_NOISE_ERROR;
pub fn is_stop(self) -> bool {
self.0 & Self::FLAG_STOP != 0
}
pub fn is_rx_data(self) -> bool {
self.0 & Self::FLAG_DATA != 0
}
pub fn is_rx_idle(self) -> bool {
self.0 & Self::FLAG_IDLE != 0
}
pub fn is_error(self) -> bool {
self.0 & (Self::FLAG_OVERRUN_ERROR | Self::FLAG_FRAMING_ERROR | Self::FLAG_NOISE_ERROR) != 0
}
pub fn is_overrun_error(self) -> bool {
self.0 & Self::FLAG_OVERRUN_ERROR != 0
}
pub fn is_framing_error(self) -> bool {
self.0 & Self::FLAG_FRAMING_ERROR != 0
}
pub fn is_noise_error(self) -> bool {
self.0 & Self::FLAG_NOISE_ERROR != 0
}
}
pub struct AsyncSerialReceiver<F>
where F: FnMut(&[u8])
{
handle: *mut sys::FuriHalSerialHandle,
context: FuriBox<Context<F>>,
}
struct Context<F: FnMut(&[u8])> {
rx_stream: FuriStreamBuffer,
on_rx: F,
worker_thread: *mut sys::FuriThread,
}
#[derive(PartialEq, Eq)]
pub struct FuriBox<T>(*mut T);
impl<T> FuriBox<T> {
pub fn new(value: T) -> Self {
let ptr;
unsafe {
ptr = sys::aligned_malloc(size_of::<T>(), align_of::<T>()) as *mut T;
assert!(!ptr.is_null());
ptr.write(value);
};
FuriBox(ptr)
}
pub fn as_ptr(boxed: &FuriBox<T>) -> *mut T {
boxed.0
}
}
impl<T> Drop for FuriBox<T> {
fn drop(&mut self) {
let ptr = self.0;
unsafe { sys::aligned_free(ptr as _); }
}
}
impl<T> AsRef<T> for FuriBox<T> {
fn as_ref(&self) -> &T {
unsafe { & *self.0 }
}
}
impl<T> AsMut<T> for FuriBox<T> {
fn as_mut(&mut self) -> &mut T {
unsafe { &mut *self.0 }
}
}
impl<T> Deref for FuriBox<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
unsafe { & *self.0 }
}
}
impl<T> DerefMut for FuriBox<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.0 }
}
}
impl<F> AsyncSerialReceiver<F>
where F: FnMut(&[u8])
{
pub fn new(serial_handle: *mut sys::FuriHalSerialHandle, on_rx: F) -> Self {
let rx_stream = FuriStreamBuffer::new(2048, 1);
let mut context = FuriBox::new(Context {
rx_stream,
on_rx,
worker_thread: ptr::null_mut(),
});
context.worker_thread = unsafe { sys::furi_thread_alloc_ex(c"AsyncSerialReceiverWorker".as_ptr(), 1024, Some(async_serial_receiver_worker::<F>), FuriBox::as_ptr(&context).cast()) };
unsafe { sys::furi_thread_start(context.worker_thread) };
AsyncSerialReceiver {
handle: serial_handle,
context,
}
}
pub fn start(&mut self) {
unsafe {
sys::furi_hal_serial_async_rx_start(self.handle, Some(async_serial_receiver_rx_callback::<F>), FuriBox::as_ptr(&self.context).cast(), true);
}
}
pub fn stop(&mut self) {
unsafe {
sys::furi_hal_serial_async_rx_stop(self.handle);
}
}
}
impl<F: FnMut(&[u8])> Drop for AsyncSerialReceiver<F> {
fn drop(&mut self) {
if !self.context.worker_thread.is_null() {
let thread_id = unsafe { thread::ThreadId::from_furi_thread(self.context.worker_thread) };
furi::thread::set_flags(thread_id, WorkerEvent::FLAG_STOP).unwrap();
unsafe {
sys::furi_thread_join(self.context.worker_thread);
sys::furi_thread_free(self.context.worker_thread);
}
self.context.worker_thread = ptr::null_mut();
}
}
}
unsafe extern "C" fn async_serial_receiver_rx_callback<F: FnMut(&[u8])>(handle: *mut sys::FuriHalSerialHandle, event: sys::FuriHalSerialRxEvent, context: *mut c_void) {
let context = &mut *context.cast::<Context<F>>();
let mut flags = 0u32;
if event & sys::FuriHalSerialRxEvent_FuriHalSerialRxEventData != 0 {
let data = [ sys::furi_hal_serial_async_rx(handle) ];
(*context).rx_stream.send(&data, Duration::ZERO);
flags |= WorkerEvent::FLAG_DATA;
}
if event & sys::FuriHalSerialRxEvent_FuriHalSerialRxEventIdle != 0 {
flags |= WorkerEvent::FLAG_IDLE;
}
if event & sys::FuriHalSerialRxEvent_FuriHalSerialRxEventNoiseError != 0 {
flags |= WorkerEvent::FLAG_FRAMING_ERROR;
}
if event & sys::FuriHalSerialRxEvent_FuriHalSerialRxEventNoiseError != 0 {
flags |= WorkerEvent::FLAG_FRAMING_ERROR;
}
if event & sys::FuriHalSerialRxEvent_FuriHalSerialRxEventNoiseError != 0 {
flags |= WorkerEvent::FLAG_FRAMING_ERROR;
}
let thread_id = unsafe { ThreadId::from_furi_thread((*context).worker_thread) };
furi::thread::set_flags(thread_id, flags).unwrap();
}
const SERIAL_WORKER_BUFFER_LEN: usize = 64;
unsafe extern "C" fn async_serial_receiver_worker<F: FnMut(&[u8])>(context: *mut c_void) -> i32 {
info!("Starting async worker");
assert!(!context.is_null());
let context: &mut Context<F> = &mut *context.cast();
loop {
let events = WorkerEvent(furi::thread::wait_any_flags(WorkerEvent::MASK, true, Duration::MAX).unwrap_or(0));
info!("WorkerEvent: {}", events.0);
if events.is_stop() {
break;
}
if events.is_rx_data() {
loop {
let mut data = [0u8; SERIAL_WORKER_BUFFER_LEN];
let len = context.rx_stream.receive(&mut data, Duration::ZERO);
if len == 0 {
break;
}
(context.on_rx)(&data[..len])
}
}
if events.is_rx_idle() {
}
if events.is_error() {
if events.is_overrun_error() {
}
if events.is_framing_error() {
}
if events.is_noise_error() {
}
}
}
0
}
unsafe fn main() {
let serial_handle =
sys::furi_hal_serial_control_acquire(sys::FuriHalSerialId_FuriHalSerialIdLpuart);
assert!(!serial_handle.is_null());
sys::furi_hal_serial_init(serial_handle, 9600);
let mut buffer = furi::string::FuriString::new();
let mut serial = AsyncSerialReceiver::new(serial_handle, |data| {
let ch = data[0] as char;
println!("{:?}", data);
if ch == '\r' || ch == '\n' {
println!("> {}", buffer);
buffer.clear();
} else {
buffer.push(ch);
}
});
serial.start();
info!("And now we run");
sys::furi_delay_ms(10_000);
serial.stop();
sys::furi_hal_serial_deinit(serial_handle);
sys::furi_hal_serial_control_release(serial_handle);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment