use core::fmt::{Debug, Display};
use core::marker::PhantomData;
use core::mem;
use core::ptr;
use core::result::Result;
use core::time::Duration;
use embedded_svc::service;
use embedded_svc::timer::{self, Timer};
use esp_idf_sys::*;
pub type EspOnce = EspTimerService<Once>;
pub type EspPeriodic = EspTimerService<Periodic>;
pub type EspOnceTimer = EspTimer<Once>;
pub type EspPeriodicTimer = EspTimer<Periodic>;
pub trait EspTimerType {
fn is_periodic() -> bool;
}
pub struct Once(Option<Box<dyn FnOnce() + 'static>>);
pub struct Periodic(Box<dyn FnMut() + 'static>);
impl EspTimerType for Once {
fn is_periodic() -> bool {
false
}
}
impl EspTimerType for Periodic {
fn is_periodic() -> bool {
true
}
}
struct UnsafeCallback<T>(*mut T);
impl<T> UnsafeCallback<T> {
fn from(boxed: &mut Box<T>) -> Self {
Self(boxed.as_mut())
}
unsafe fn from_ptr(ptr: *mut c_types::c_void) -> Self {
Self(ptr as *mut _)
}
fn as_ptr(&self) -> *mut c_types::c_void {
self.0 as *mut _
}
}
impl UnsafeCallback<Once> {
unsafe fn call(&self) {
let reference = self.0.as_mut().unwrap();
if let Some(cb) = mem::replace(&mut reference.0, None) {
cb();
}
}
}
impl UnsafeCallback<Periodic> {
unsafe fn call(&self) {
let reference = self.0.as_mut().unwrap();
(reference.0)();
}
}
pub struct EspTimer<T>
where
T: EspTimerType,
{
handle: esp_timer_handle_t,
duration: Duration,
_callback: Box<T>,
}
impl<T> EspTimer<T>
where
T: EspTimerType,
{
extern "C" fn handle(arg: *mut c_types::c_void) {
unsafe {
if T::is_periodic() {
UnsafeCallback::<Periodic>::from_ptr(arg).call();
} else {
UnsafeCallback::<Once>::from_ptr(arg).call();
}
}
}
}
unsafe impl<T> Send for EspTimer<T> where T: EspTimerType {}
impl<T> Drop for EspTimer<T>
where
T: EspTimerType,
{
fn drop(&mut self) {
let _ = self.cancel();
while unsafe { esp_timer_delete(self.handle) } != ESP_OK {
}
}
}
impl<T> service::Service for EspTimer<T>
where
T: EspTimerType,
{
type Error = EspError;
}
impl<T> timer::Timer for EspTimer<T>
where
T: EspTimerType,
{
fn start(&mut self) -> Result<(), Self::Error> {
let _ = self.cancel();
if T::is_periodic() {
esp!(unsafe { esp_timer_start_periodic(self.handle, self.duration.as_micros() as _) })?;
} else {
esp!(unsafe { esp_timer_start_once(self.handle, self.duration.as_micros() as _) })?;
}
Ok(())
}
fn is_scheduled(&self) -> Result<bool, Self::Error> {
Ok(unsafe { esp_timer_is_active(self.handle) })
}
fn cancel(&mut self) -> Result<bool, Self::Error> {
let res = unsafe { esp_timer_stop(self.handle) };
Ok(res != ESP_OK)
}
}
#[derive(Clone)]
struct PrivateData;
#[derive(Clone)]
pub struct EspTimerService<T>(PhantomData<*const T>);
impl<T> EspTimerService<T>
where
T: EspTimerType,
{
pub fn new() -> Result<Self, EspError> {
Ok(Self(PhantomData))
}
fn timer(&self, duration: Duration, mut callback: Box<T>) -> Result<EspTimer<T>, EspError> {
let mut handle: esp_timer_handle_t = ptr::null_mut();
let unsafe_callback = UnsafeCallback::from(&mut callback);
esp!(unsafe {
esp_timer_create(
&esp_timer_create_args_t {
callback: Some(EspTimer::<T>::handle),
name: b"rust\0" as *const _ as *const _, arg: unsafe_callback.as_ptr(),
dispatch_method: esp_timer_dispatch_t_ESP_TIMER_TASK,
skip_unhandled_events: false, },
&mut handle as *mut _,
)
})?;
Ok(EspTimer {
handle,
duration,
_callback: callback,
})
}
}
impl<T> service::Service for EspTimerService<T> {
type Error = EspError;
}
impl timer::Once for EspTimerService<Once> {
type Timer = EspTimer<Once>;
fn after<E>(
&mut self,
duration: Duration,
callback: impl FnOnce() -> Result<(), E> + Send + 'static,
) -> Result<Self::Timer, Self::Error>
where
E: Display + Debug + Send + Sync + 'static,
{
self.timer(
duration,
Box::new(Once(Some(Box::new(move || callback().unwrap())))),
)
}
}
impl timer::Periodic for EspTimerService<Periodic> {
type Timer = EspTimer<Periodic>;
fn every<E>(
&mut self,
duration: Duration,
mut callback: impl FnMut() -> Result<(), E> + Send + 'static,
) -> Result<Self::Timer, Self::Error>
where
E: Display + Debug + Send + Sync + 'static,
{
self.timer(
duration,
Box::new(Periodic(Box::new(move || callback().unwrap()))),
)
}
}