#![no_std]
pub mod audio;
#[cfg(feature = "ble")]
pub mod ble;
pub mod bsp;
pub mod display;
#[cfg(feature = "env")]
pub mod env;
pub mod imu;
pub mod input;
#[cfg(feature = "lora")]
pub mod lora;
pub mod motion;
pub mod power;
#[cfg(any(feature = "wifi", feature = "ble"))]
pub mod runtime;
pub mod sprite;
pub mod storage;
pub mod touch;
pub mod ui;
#[cfg(feature = "wifi")]
pub mod wifi;
#[cfg(feature = "ble")]
use crate::ble::Ble;
#[cfg(any(feature = "wifi", feature = "ble"))]
use crate::bsp::RadioRuntimeResources;
#[cfg(feature = "wifi")]
use crate::bsp::WifiResources;
use crate::bsp::{
BoardInitError, ButtonLevels, NessoBuzzer, NessoDisplay, NessoI2c, NessoN1, NessoN1Board,
};
use crate::imu::{Acceleration, Bmi270, Gyroscope};
use crate::input::{BoardButtons, ButtonTiming};
#[cfg(feature = "lora")]
use crate::lora::NessoLora;
use crate::power::{BatteryStatus, ChargeStatus, ChargingConfig, Power};
use crate::storage::{EspFlashSettingsStore, SettingsPartition};
use crate::touch::{Touch, TouchEvent, TouchState};
#[cfg(feature = "wifi")]
use crate::wifi::EspRadioWifi;
use esp_hal::delay::Delay;
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum NessoError {
Board(BoardInitError),
Touch,
Imu,
Power,
FlashUnavailable,
Storage,
#[cfg(feature = "wifi")]
WifiUnavailable,
#[cfg(feature = "ble")]
BleUnavailable,
#[cfg(any(feature = "wifi", feature = "ble"))]
RadioRuntimeUnavailable,
ImuNotInitialized,
Input,
#[cfg(feature = "lora")]
LoraUnavailable,
#[cfg(feature = "lora")]
Lora,
}
pub struct Nesso {
pub display: NessoDisplay,
pub audio: NessoBuzzer,
#[cfg(feature = "lora")]
pub lora: NessoLora,
i2c: NessoI2c,
#[cfg(feature = "wifi")]
wifi: Option<WifiResources>,
#[cfg(feature = "ble")]
ble: Option<crate::bsp::BleResources>,
#[cfg(any(feature = "wifi", feature = "ble"))]
radio_runtime: Option<RadioRuntimeResources>,
#[cfg(any(feature = "wifi", feature = "ble"))]
radio_runtime_started: bool,
flash: Option<esp_hal::peripherals::FLASH<'static>>,
imu_initialized: bool,
previous_touch: TouchState,
}
impl Nesso {
pub fn new(peripherals: esp_hal::peripherals::Peripherals) -> Result<Self, NessoError> {
let parts = NessoN1Board::new(peripherals)
.into_core_parts()
.map_err(NessoError::Board)?;
let nesso = Self {
display: parts.display,
audio: parts.buzzer,
#[cfg(feature = "lora")]
lora: parts.lora,
i2c: parts.i2c,
#[cfg(feature = "wifi")]
wifi: Some(parts.wifi),
#[cfg(feature = "ble")]
ble: Some(parts.ble),
#[cfg(any(feature = "wifi", feature = "ble"))]
radio_runtime: Some(parts.radio_runtime),
#[cfg(any(feature = "wifi", feature = "ble"))]
radio_runtime_started: false,
flash: Some(parts.flash),
imu_initialized: false,
previous_touch: TouchState::default(),
};
Ok(nesso)
}
pub fn init_imu(&mut self) -> Result<(), NessoError> {
Bmi270::new(&mut self.i2c, Delay::new())
.init()
.map_err(|_| NessoError::Imu)?;
self.imu_initialized = true;
Ok(())
}
pub fn touch_state(&mut self) -> Result<TouchState, NessoError> {
Touch::new(&mut self.i2c)
.read_state()
.map_err(|_| NessoError::Touch)
}
pub fn touch_event(&mut self) -> Result<TouchEvent, NessoError> {
let current = self.touch_state()?;
let event = match (self.previous_touch.primary(), current.primary()) {
(None, Some(point)) => TouchEvent::Pressed(point),
(Some(_), None) => TouchEvent::Released,
(Some(previous), Some(point)) if previous != point => TouchEvent::Moved(point),
_ => TouchEvent::Idle,
};
self.previous_touch = current;
Ok(event)
}
pub fn init_buttons(&mut self) -> Result<(), NessoError> {
NessoN1::init_button_inputs(&mut self.i2c).map_err(|_| NessoError::Input)
}
pub fn init_button_events(&mut self) -> Result<BoardButtons, NessoError> {
self.init_button_events_with_timing(ButtonTiming::default())
}
pub fn init_button_events_with_timing(
&mut self,
timing: ButtonTiming,
) -> Result<BoardButtons, NessoError> {
self.init_buttons()?;
Ok(BoardButtons::new(timing))
}
pub fn button_levels(&mut self) -> Result<ButtonLevels, NessoError> {
NessoN1::read_button_levels(&mut self.i2c).map_err(|_| NessoError::Input)
}
pub fn with_sensors<R>(
&mut self,
f: impl FnOnce(&mut NessoSensors<'_>) -> Result<R, NessoError>,
) -> Result<R, NessoError> {
let mut sensors = NessoSensors {
i2c: &mut self.i2c,
imu_initialized: self.imu_initialized,
previous_touch: &mut self.previous_touch,
};
f(&mut sensors)
}
pub fn acceleration(&mut self) -> Result<Acceleration, NessoError> {
if !self.imu_initialized {
return Err(NessoError::ImuNotInitialized);
}
Bmi270::new(&mut self.i2c, Delay::new())
.acceleration()
.map_err(|_| NessoError::Imu)
}
pub fn gyroscope(&mut self) -> Result<Gyroscope, NessoError> {
if !self.imu_initialized {
return Err(NessoError::ImuNotInitialized);
}
Bmi270::new(&mut self.i2c, Delay::new())
.gyroscope()
.map_err(|_| NessoError::Imu)
}
pub fn battery_status(&mut self) -> Result<BatteryStatus, NessoError> {
Power::new(&mut self.i2c)
.battery_status()
.map_err(|_| NessoError::Power)
}
pub fn enable_battery_charging(&mut self) -> Result<(), NessoError> {
Power::new(&mut self.i2c)
.begin_charging()
.map_err(|_| NessoError::Power)
}
pub fn configure_battery_charging(&mut self, config: ChargingConfig) -> Result<(), NessoError> {
Power::new(&mut self.i2c)
.configure_charging(config)
.map_err(|_| NessoError::Power)
}
#[cfg(feature = "wifi")]
pub fn init_wifi(&mut self) -> Result<EspRadioWifi, NessoError> {
let wifi = self.wifi.take().ok_or(NessoError::WifiUnavailable)?;
if self.radio_runtime_started {
return Ok(EspRadioWifi::new_started(wifi));
}
let runtime = self
.radio_runtime
.take()
.ok_or(NessoError::RadioRuntimeUnavailable)?;
Ok(EspRadioWifi::new(wifi, runtime))
}
#[cfg(feature = "ble")]
pub fn init_ble(&mut self) -> Result<Ble, NessoError> {
let ble = self.ble.take().ok_or(NessoError::BleUnavailable)?;
if self.radio_runtime_started {
return Ok(Ble::new_started(ble));
}
let runtime = self
.radio_runtime
.take()
.ok_or(NessoError::RadioRuntimeUnavailable)?;
Ok(Ble::new(ble, runtime))
}
#[cfg(any(feature = "wifi", feature = "ble"))]
pub fn start_async_runtime(&mut self) -> Result<(), NessoError> {
if self.radio_runtime_started {
return Ok(());
}
let runtime = self
.radio_runtime
.take()
.ok_or(NessoError::RadioRuntimeUnavailable)?;
runtime::start_radio_runtime(runtime);
self.radio_runtime_started = true;
Ok(())
}
pub fn take_default_flash_settings(
&mut self,
) -> Result<EspFlashSettingsStore<'static>, NessoError> {
self.take_flash_settings_partition(SettingsPartition::DEFAULT)
}
pub fn take_flash_settings_partition(
&mut self,
partition: SettingsPartition,
) -> Result<EspFlashSettingsStore<'static>, NessoError> {
let flash = self.flash.take().ok_or(NessoError::FlashUnavailable)?;
EspFlashSettingsStore::from_partition(flash, partition).map_err(|_| NessoError::Storage)
}
pub fn take_flash_settings(
&mut self,
offset: u32,
) -> Result<EspFlashSettingsStore<'static>, NessoError> {
let flash = self.flash.take().ok_or(NessoError::FlashUnavailable)?;
Ok(EspFlashSettingsStore::from_flash(flash, offset))
}
}
pub struct NessoSensors<'a> {
i2c: &'a mut NessoI2c,
imu_initialized: bool,
previous_touch: &'a mut TouchState,
}
impl NessoSensors<'_> {
pub fn touch_state(&mut self) -> Result<TouchState, NessoError> {
Touch::new(&mut *self.i2c)
.read_state()
.map_err(|_| NessoError::Touch)
}
pub fn touch_event(&mut self) -> Result<TouchEvent, NessoError> {
let current = self.touch_state()?;
let event = match (self.previous_touch.primary(), current.primary()) {
(None, Some(point)) => TouchEvent::Pressed(point),
(Some(_), None) => TouchEvent::Released,
(Some(previous), Some(point)) if previous != point => TouchEvent::Moved(point),
_ => TouchEvent::Idle,
};
*self.previous_touch = current;
Ok(event)
}
pub fn acceleration(&mut self) -> Result<Acceleration, NessoError> {
if !self.imu_initialized {
return Err(NessoError::ImuNotInitialized);
}
Bmi270::new(&mut *self.i2c, Delay::new())
.acceleration()
.map_err(|_| NessoError::Imu)
}
pub fn gyroscope(&mut self) -> Result<Gyroscope, NessoError> {
if !self.imu_initialized {
return Err(NessoError::ImuNotInitialized);
}
Bmi270::new(&mut *self.i2c, Delay::new())
.gyroscope()
.map_err(|_| NessoError::Imu)
}
pub fn battery_status(&mut self) -> Result<BatteryStatus, NessoError> {
Power::new(&mut *self.i2c)
.battery_status()
.map_err(|_| NessoError::Power)
}
pub fn charge_status(&mut self) -> Result<ChargeStatus, NessoError> {
Power::new(&mut *self.i2c)
.charge_status()
.map_err(|_| NessoError::Power)
}
}