diff --git a/.travis.yml b/.travis.yml index fbd4500..f0cc8f3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,7 @@ rust: - stable os: linux -dist: bionic +dist: focal addons: apt: @@ -23,7 +23,7 @@ script: - if [ -n "$TRAVIS_TAG" ]; then sed -i "s/version = \"0.0.0-snapshot\"/version = \"${TRAVIS_TAG#v}\"/g" Cargo.toml; fi - cargo install cargo-deb - cargo build --release --target=armv7-unknown-linux-musleabihf - - wget -O _releases/tc2-firmware https://github.com/TheCacophonyProject/tc2-firmware/releases/download/v0.1.2/tc2-firmware + - wget -O _releases/tc2-firmware https://github.com/TheCacophonyProject/tc2-firmware/releases/download/v0.1.3/tc2-firmware - cargo deb --target=armv7-unknown-linux-musleabihf --output tc2-agent_${TRAVIS_TAG#v}_armhf.deb deploy: diff --git a/Cargo.toml b/Cargo.toml index 3dc61fa..b6a262f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,3 +47,4 @@ assets = [ ["_releases/tc2-firmware", "/etc/cacophony/rp2040-firmware.elf", "664"], ] maintainer-scripts = "_releases/scripts" +revision = "" diff --git a/_releases/scripts/postinst b/_releases/scripts/postinst index faeaa66..a83035a 100644 --- a/_releases/scripts/postinst +++ b/_releases/scripts/postinst @@ -1,6 +1,4 @@ #!/bin/bash systemctl daemon-reload systemctl enable tc2-agent.service -systemctl stop tc2-agent.service -tc2-hat-rp2040 --elf /etc/cacophony/rp2040-firmware.elf -systemctl start tc2-agent.service +systemctl restart tc2-agent.service diff --git a/_releases/tc2-agent.service b/_releases/tc2-agent.service index fef5fbc..9954be9 100644 --- a/_releases/tc2-agent.service +++ b/_releases/tc2-agent.service @@ -6,7 +6,7 @@ After=multi-user.target [Service] Type=simple ExecStart=/usr/bin/tc2-agent -Restart=on-failure +Restart=always RestartSec=5s # Give real-time priority diff --git a/src/main.rs b/src/main.rs index 0a298f5..3c6b1ac 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,8 +16,11 @@ use rppal::{ spi::{Bus, Mode, Polarity, SlaveSelect, Spi}, }; use std::fs; +use std::io; use std::ops::Not; use std::path::Path; +use std::process; +use std::process::Command; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::mpsc::{channel, TryRecvError}; use std::sync::Arc; @@ -51,7 +54,7 @@ use simplelog::*; const AUDIO_SHEBANG: u16 = 1; -const EXPECTED_RP2040_FIRMWARE_VERSION: u32 = 9; +const EXPECTED_RP2040_FIRMWARE_VERSION: u32 = 10; const EXPECTED_ATTINY_FIRMWARE_VERSION: u8 = 12; const SEGMENT_LENGTH: usize = 9760; const FRAME_LENGTH: usize = SEGMENT_LENGTH * 4; @@ -399,7 +402,10 @@ fn main() { pin.set_interrupt(Trigger::RisingEdge).expect("Unable to set pi ping interrupt"); let (tx, rx) = channel(); let (restart_tx, restart_rx) = channel(); + let cross_thread_signal: Arc = Arc::new(AtomicBool::new(false)); if frame_acquire{ + let cross_thread_signal_2 = cross_thread_signal.clone(); + let _ = thread::Builder::new().name("frame-socket".to_string()).spawn_with_priority(ThreadPriority::Max, move |result| { // Spawn a thread which can output the frames, converted to rgb grayscale // This is printed out from within the spawned thread. @@ -408,10 +414,10 @@ fn main() { info!("Connecting to frame socket {}", address); let mut reconnects = 0; let mut prev_frame_num = None; - loop { info!("Entering frame-socket loop"); if let Ok(_) = restart_rx.try_recv() { + cross_thread_signal_2.store(true, Ordering::Relaxed); info!("Restarting rp2040"); if !run_pin.is_set_high() { run_pin.set_high(); @@ -430,6 +436,7 @@ fn main() { 'send_loop: loop { // Check if we need to reset rp2040 because of a config change if let Ok(_) = restart_rx.try_recv() { + cross_thread_signal_2.store(true, Ordering::Relaxed); loop { info!("Restarting rp2040"); if !run_pin.is_set_high() { @@ -502,27 +509,18 @@ fn main() { ms_elapsed = 0; }, _ => { - const NUM_ATTEMPTS_BEFORE_RESET: usize = 10; + const NUM_ATTEMPTS_BEFORE_REPROGRAM: usize = 20; ms_elapsed += recv_timeout_ms; - if ms_elapsed > 5000 { + if ms_elapsed > 10000 { ms_elapsed = 0; reconnects += 1; - if reconnects == NUM_ATTEMPTS_BEFORE_RESET { - let date = chrono::Local::now(); - warn!("Resetting rp2040 at {}", date.format("%Y-%m-%d--%H:%M:%S")); - reconnects = 0; - prev_frame_num = None; - - if !run_pin.is_set_high() { - run_pin.set_high(); - sleep(Duration::from_millis(1000)); + if reconnects == NUM_ATTEMPTS_BEFORE_REPROGRAM { + let e = program_rp2040(); + if e.is_err() { + warn!("Failed to reprogram RP2040: {}", e.unwrap_err()); } - - run_pin.set_low(); - sleep(Duration::from_millis(1000)); - run_pin.set_high(); } else { - info!("-- #{reconnects} waiting for frames from rp2040 (resetting rp2040 after {} more attempts)", NUM_ATTEMPTS_BEFORE_RESET - reconnects); + info!("-- #{reconnects} waiting to connect to rp2040 (reprogram RP2040 after {} more attempts)", NUM_ATTEMPTS_BEFORE_REPROGRAM - reconnects); } } } @@ -746,8 +744,13 @@ fn main() { info!("Got startup info: radiometry enabled: {}, firmware version: {}, lepton serial #{}", radiometry_enabled, firmware_version, lepton_serial_number); if firmware_version != EXPECTED_RP2040_FIRMWARE_VERSION { exit_cleanly(&mut attiny_i2c_interface); - error!("Unsupported firmware version, expected {}, got {}", EXPECTED_RP2040_FIRMWARE_VERSION, firmware_version); - panic!("Exit"); + info!("Unsupported firmware version, expected {}, got {}. Will reprogram RP2040.", EXPECTED_RP2040_FIRMWARE_VERSION, firmware_version); + let e = program_rp2040(); + if e.is_err() { + warn!("Failed to reprogram rp2040: {}", e.unwrap_err()); + panic!("Exit"); + } + process::exit(0); } if device_config.use_low_power_mode() && !radiometry_enabled && !device_config.is_audio_device().unwrap_or_default(){ exit_cleanly(&mut attiny_i2c_interface); @@ -916,9 +919,12 @@ fn main() { } if !sent_reset_request { sent_reset_request = true; - info!("RESETTING"); let _ = restart_tx.send(true); } + if cross_thread_signal.load(Ordering::Relaxed) { + sent_reset_request = false; + cross_thread_signal.store(false, Ordering::Relaxed); + } } FRAME_BUFFER.swap(); let _ = tx.send((Some((radiometry_enabled, is_recording, firmware_version, lepton_serial_number.clone())), None)); @@ -946,6 +952,23 @@ pub const CRC_AUG_CCITT: Algorithm = Algorithm { residue: 0x0000, }; +fn program_rp2040() -> io::Result<()> { + let status = Command::new("tc2-hat-rp2040") + .arg("--elf") + .arg("/etc/cacophony/rp2040-firmware.elf") + .status()?; + + if !status.success() { + return Err(io::Error::new( + io::ErrorKind::Other, + "Command execution failed", + )); + } + + println!("Updated RP2040 firmware."); + Ok(()) +} + fn write_attiny_command(attiny_i2c: &mut I2c, command: u8, value: u8) -> Result<(), &'static str> { let mut payload = [command, value, 0x00, 0x00]; let crc = Crc::::new(&CRC_AUG_CCITT).checksum(&payload[0..=1]);