#![no_std] #![no_main] #![deny( clippy::mem_forget, reason = "mem::forget is generally not safe to do with esp_hal types, especially those \ holding buffers for the duration of a data transfer." )] use alloc::boxed::Box; use alloc::string::ToString; use esp_hal::clock::CpuClock; use esp_hal::delay::Delay; use esp_hal::uart::{Config, Uart}; use esp_hal::{gpio, main}; use core::fmt::Debug; use esp_hal::gpio::{Event, OutputConfig}; use esp_hal::gpio::{Input, InputConfig}; use esp_hal::handler; use esp_hal::rmt::{Rmt, TxChannelConfig, TxChannelCreator}; use esp_hal::time::{Instant, Rate}; //pid-controller use core::time::Duration; use discrete_pid::pid::PidContext; use discrete_pid::time::InstantLike; use discrete_pid::time::Millis; use discrete_pid::{pid, time}; use core::cell::RefCell; use core::mem::forget; use critical_section::Mutex; use esp_backtrace::{self as _, Backtrace}; use rtt_target::rprintln; mod dc_driver; mod peripherals; use peripherals::nextion::Nextion; //DSHOT via rmt! use dc_driver::dshot::DShotSpeed; use crate::dc_driver::dshot; use crate::peripherals::ErrCommand; use esp_hal::rmt::RxChannelConfig; use esp_hal::rmt::RxChannelCreator; use esp_hal::timer::OneShotTimer; use esp_hal::timer::Timer; use esp_hal::timer::timg::TimerGroup; #[panic_handler] fn panic(panic: &core::panic::PanicInfo) -> ! { rprintln!("{}", panic.message()); let trace = Backtrace::capture(); loop { rprintln!("{}", panic.location().unwrap()) } } use alloc::format; // static EMERGENCY_BUTTON: Mutex>> = Mutex::new(RefCell::new(None)); extern crate alloc; //target RPM const DEFAULT_TARGET_RPM: u32 = 4000; //in seconds const DEFAULT_SPIN_TIME: u32 = 10; // This creates a default app-descriptor required by the esp-idf bootloader. // For more information see: esp_bootloader_esp_idf::esp_app_desc!(); use crate::peripherals::Command; #[derive(Copy, Clone)] struct Time { sec: i32, nsec: i32, } impl InstantLike for Time { fn duration_since(&self, other: Self) -> Duration { let sec = self.sec - other.sec; let nsec = self.nsec - other.nsec; Duration::new(sec as u64, nsec as u32) } } #[main] fn main() -> ! { // generator version: 1.0.1 rtt_target::rtt_init_print!(); let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); let peripherals = esp_hal::init(config); let delay = Delay::new(); esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 65536); //setup RMT let freq = Rate::from_mhz(80); let rmt = Rmt::new(peripherals.RMT, freq).expect("CAN NOT SET FREQUENCY"); let rx_config = RxChannelConfig::default() .with_clk_divider(1) .with_idle_threshold(1000); let tx_config = TxChannelConfig::default() .with_clk_divider(1) .with_idle_output(true) .with_idle_output_level(esp_hal::gpio::Level::High); let oc = OutputConfig::default().with_drive_mode(esp_hal::gpio::DriveMode::OpenDrain); let ic = InputConfig::default(); let rx_pin = gpio::Input::new(unsafe { peripherals.GPIO23.clone_unchecked() }, ic); let tx_pin = gpio::Output::new(peripherals.GPIO23, esp_hal::gpio::Level::High, oc); let mut tx_channel = rmt .channel0 .configure_tx(tx_pin, tx_config) .expect("creation of TX_CHANNEL FAILED!"); let mut rx_channel = rmt.channel3.configure_rx(rx_pin, rx_config).unwrap(); let dshot_esc = dshot::DShot::new( &mut rx_channel, &mut tx_channel, DShotSpeed::DShot600, Some(80_000_000), Some(1), ); let dshot_esc = Box::new(dshot_esc); let dshot_esc = Box::leak(dshot_esc); //dshot_esc.arm(&delay).unwrap(); //PID-Controller let loop_time = Duration::from_micros(10); let cfg = pid::PidConfigBuilder::default() .kp(3.0) .ki(1.0) .output_limits(100.00, 2040.00) .sample_time(loop_time) .filter_tc(0.1) .build() .expect("Failed to build a PID configuration"); let controller = pid::FuncPidController::new(cfg); let mut ctx = PidContext::new(Millis(0), 0.0, 0.0); let set_point = DEFAULT_TARGET_RPM; dshot_esc.arm(); let mut rpm = 0; let mut control: f32 = 200.0; let mut timg0 = TimerGroup::new(peripherals.TIMG0); let coat_timer = timg0.timer0; dshot_esc.set_throttle(0); // let mut esc = AfroEsc::new(&mut pwm_pin);; // esc.set_timestamp(1000); // delay.delay_millis(3000); // esc.set_duty_percent(10); // delay.delay_millis(3000); //1421 is I think the best, makes calculating the throttle easier // esc.set_timestamp(1055); // delay.delay_millis(3000); //gpio 9 is the button let input_config = InputConfig::default().with_pull(esp_hal::gpio::Pull::Up); //let emergency_button = peripherals.GPIO9; let mut emergency_button = Input::new(peripherals.GPIO9, input_config); critical_section::with(|cs| { emergency_button.listen(Event::FallingEdge); EMERGENCY_BUTTON .borrow_ref_mut(cs) .replace(emergency_button) }); let mut io = esp_hal::gpio::Io::new(peripherals.IO_MUX); io.set_interrupt_handler(handler); let mut uart0 = Uart::new( peripherals.UART0, Config::default() .with_baudrate(115200) .with_data_bits(esp_hal::uart::DataBits::_8) .with_stop_bits(esp_hal::uart::StopBits::_1) .with_parity(esp_hal::uart::Parity::None), ) .unwrap() .with_rx(peripherals.GPIO5) .with_tx(peripherals.GPIO7); let mut display = Nextion::new(&mut uart0); //we just set it to page0 to be sure display.send_command(b"page page0"); let mut _rpm = DEFAULT_TARGET_RPM; let mut _timer = DEFAULT_SPIN_TIME; let mut started = false; loop { if display.read_ready() { match display.read_command() { Ok(Command::CommandSuccess) => { rprintln!("COMMAND SUCCESSFULLY executed"); } Ok(Command::Start) => { rprintln!("START"); display.send_command(b"page page2"); started = true; } Ok(Command::Stop) => { rprintln!("STOP"); started = false; // spincoater.stop(); } Ok(Command::SetRpm(x)) => { rprintln!("SET_RPM with {}", x); _rpm = x; } Ok(Command::SetTimer(x)) => { rprintln!("SETTING TIMER {}", x); _timer = x; } Ok(Command::SendConfig) => { rprintln!("SEND CONFIG"); let command = format!("rpm.val={}", DEFAULT_TARGET_RPM); display.send_command(command.to_string().as_bytes()); } Err(ErrCommand::NoValidCmd) => { rprintln!(" NOT A VALID CMD!"); } Err(ErrCommand::ReadError) => { rprintln!("READ FAILED!"); } _ => { rprintln!("This should never happen!") } } // display.send_command(b"page page0"); } if started { rprintln!("STARTING!"); // spincoater.armed(); let _ = coat_timer .load_value(esp_hal::time::Duration::from_secs(10)) .expect("TODO: Could not set timer for coating! "); coat_timer.start(); while !coat_timer.is_interrupt_set() { dshot_esc.process(); //let control_val = dc_driver:: //this bad boy needs floats, this will be fun :) dshot_esc.set_throttle(control as _); rpm = match dshot_esc.get_rpm() { Some(x) => { // rprintln!("GOT RPM {}", x); x } None => { // rprintln!("NO RPM!"); rpm } }; // rprintln!("RPM:{}",rpm); let last_time = esp_hal::time::Instant::now(); let timestamp = esp_hal::time::Instant::now() .duration_since_epoch() .as_millis(); (control, ctx) = controller.compute(ctx, rpm as _, set_point as _, Millis(timestamp), None); let dumped_context = ctx.last_time().expect("LAST TIME!"); // rprintln!("control:{},rpm:{}", control,rpm); //first we send the RPM! let running_rpm = format!("running_rpm.val={}", rpm); display.send_command(running_rpm.to_string().as_bytes()); let counter = format!("counter.val={}", 1234); display.send_command(counter.to_string().as_bytes()); } rprintln!("COATING done!"); started = false; display.send_command(b"page page0"); } } } #[handler] // #[ram] fn handler() { critical_section::with(|cs| { let mut binding = EMERGENCY_BUTTON.borrow_ref_mut(cs); let button = binding.as_mut().unwrap(); if button.is_interrupt_set() { // do the thing rprintln!("BUTTON1 WAS PRESSED!"); button.clear_interrupt(); return; } }); }