Repo for designs & driver for a TA7642 powered lightning detector

feat: Embassy driver #1

merged opened by sachy.dev targeting main from driver
Labels

None yet.

assignee

None yet.

Participants 1
AT URI
at://did:plc:usjm3ynnir6y4inkcdovrfei/sh.tangled.repo.pull/3mcxaimss3u22
+520
Diff #0
+181
Cargo.lock
··· 1 + # This file is automatically @generated by Cargo. 2 + # It is not intended for manual editing. 3 + version = 4 4 + 5 + [[package]] 6 + name = "byteorder" 7 + version = "1.5.0" 8 + source = "registry+https://github.com/rust-lang/crates.io-index" 9 + checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 10 + 11 + [[package]] 12 + name = "cfg-if" 13 + version = "1.0.4" 14 + source = "registry+https://github.com/rust-lang/crates.io-index" 15 + checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" 16 + 17 + [[package]] 18 + name = "critical-section" 19 + version = "1.2.0" 20 + source = "registry+https://github.com/rust-lang/crates.io-index" 21 + checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" 22 + 23 + [[package]] 24 + name = "document-features" 25 + version = "0.2.12" 26 + source = "registry+https://github.com/rust-lang/crates.io-index" 27 + checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" 28 + dependencies = [ 29 + "litrs", 30 + ] 31 + 32 + [[package]] 33 + name = "embassy-strike-driver" 34 + version = "0.1.0" 35 + dependencies = [ 36 + "embassy-sync", 37 + "embassy-time", 38 + ] 39 + 40 + [[package]] 41 + name = "embassy-sync" 42 + version = "0.7.2" 43 + source = "registry+https://github.com/rust-lang/crates.io-index" 44 + checksum = "73974a3edbd0bd286759b3d483540f0ebef705919a5f56f4fc7709066f71689b" 45 + dependencies = [ 46 + "cfg-if", 47 + "critical-section", 48 + "embedded-io-async", 49 + "futures-core", 50 + "futures-sink", 51 + "heapless", 52 + ] 53 + 54 + [[package]] 55 + name = "embassy-time" 56 + version = "0.5.0" 57 + source = "registry+https://github.com/rust-lang/crates.io-index" 58 + checksum = "f4fa65b9284d974dad7a23bb72835c4ec85c0b540d86af7fc4098c88cff51d65" 59 + dependencies = [ 60 + "cfg-if", 61 + "critical-section", 62 + "document-features", 63 + "embassy-time-driver", 64 + "embedded-hal 0.2.7", 65 + "embedded-hal 1.0.0", 66 + "embedded-hal-async", 67 + "futures-core", 68 + ] 69 + 70 + [[package]] 71 + name = "embassy-time-driver" 72 + version = "0.2.1" 73 + source = "registry+https://github.com/rust-lang/crates.io-index" 74 + checksum = "a0a244c7dc22c8d0289379c8d8830cae06bb93d8f990194d0de5efb3b5ae7ba6" 75 + dependencies = [ 76 + "document-features", 77 + ] 78 + 79 + [[package]] 80 + name = "embedded-hal" 81 + version = "0.2.7" 82 + source = "registry+https://github.com/rust-lang/crates.io-index" 83 + checksum = "35949884794ad573cf46071e41c9b60efb0cb311e3ca01f7af807af1debc66ff" 84 + dependencies = [ 85 + "nb 0.1.3", 86 + "void", 87 + ] 88 + 89 + [[package]] 90 + name = "embedded-hal" 91 + version = "1.0.0" 92 + source = "registry+https://github.com/rust-lang/crates.io-index" 93 + checksum = "361a90feb7004eca4019fb28352a9465666b24f840f5c3cddf0ff13920590b89" 94 + 95 + [[package]] 96 + name = "embedded-hal-async" 97 + version = "1.0.0" 98 + source = "registry+https://github.com/rust-lang/crates.io-index" 99 + checksum = "0c4c685bbef7fe13c3c6dd4da26841ed3980ef33e841cddfa15ce8a8fb3f1884" 100 + dependencies = [ 101 + "embedded-hal 1.0.0", 102 + ] 103 + 104 + [[package]] 105 + name = "embedded-io" 106 + version = "0.6.1" 107 + source = "registry+https://github.com/rust-lang/crates.io-index" 108 + checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d" 109 + 110 + [[package]] 111 + name = "embedded-io-async" 112 + version = "0.6.1" 113 + source = "registry+https://github.com/rust-lang/crates.io-index" 114 + checksum = "3ff09972d4073aa8c299395be75161d582e7629cd663171d62af73c8d50dba3f" 115 + dependencies = [ 116 + "embedded-io", 117 + ] 118 + 119 + [[package]] 120 + name = "futures-core" 121 + version = "0.3.31" 122 + source = "registry+https://github.com/rust-lang/crates.io-index" 123 + checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 124 + 125 + [[package]] 126 + name = "futures-sink" 127 + version = "0.3.31" 128 + source = "registry+https://github.com/rust-lang/crates.io-index" 129 + checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 130 + 131 + [[package]] 132 + name = "hash32" 133 + version = "0.3.1" 134 + source = "registry+https://github.com/rust-lang/crates.io-index" 135 + checksum = "47d60b12902ba28e2730cd37e95b8c9223af2808df9e902d4df49588d1470606" 136 + dependencies = [ 137 + "byteorder", 138 + ] 139 + 140 + [[package]] 141 + name = "heapless" 142 + version = "0.8.0" 143 + source = "registry+https://github.com/rust-lang/crates.io-index" 144 + checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" 145 + dependencies = [ 146 + "hash32", 147 + "stable_deref_trait", 148 + ] 149 + 150 + [[package]] 151 + name = "litrs" 152 + version = "1.0.0" 153 + source = "registry+https://github.com/rust-lang/crates.io-index" 154 + checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" 155 + 156 + [[package]] 157 + name = "nb" 158 + version = "0.1.3" 159 + source = "registry+https://github.com/rust-lang/crates.io-index" 160 + checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" 161 + dependencies = [ 162 + "nb 1.1.0", 163 + ] 164 + 165 + [[package]] 166 + name = "nb" 167 + version = "1.1.0" 168 + source = "registry+https://github.com/rust-lang/crates.io-index" 169 + checksum = "8d5439c4ad607c3c23abf66de8c8bf57ba8adcd1f129e699851a6e43935d339d" 170 + 171 + [[package]] 172 + name = "stable_deref_trait" 173 + version = "1.2.1" 174 + source = "registry+https://github.com/rust-lang/crates.io-index" 175 + checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" 176 + 177 + [[package]] 178 + name = "void" 179 + version = "1.0.2" 180 + source = "registry+https://github.com/rust-lang/crates.io-index" 181 + checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
+16
Cargo.toml
··· 1 + [workspace] 2 + resolver = "3" 3 + members = ["embassy-strike-driver"] 4 + 5 + [workspace.package] 6 + authors = ["Sachy.dev <sachymetsu@tutamail.com>"] 7 + edition = "2024" 8 + repository = "https://tangled.org/sachy.dev/strike-sensor" 9 + version = "0.1.0" 10 + license = "AGPL-3.0-only" 11 + rust-version = "1.92.0" 12 + 13 + [workspace.dependencies] 14 + embassy-time = "0.5" 15 + embassy-sync = "0.7" 16 + defmt = "1"
+4
README.md
··· 4 4 5 5 The sensor designs can be found in the [kicad](./kicad) folder. 6 6 7 + ## Driver 8 + 9 + Currently, the driver needs to be tested and coded according to how the new PCB/circuit behaves, so this will require a bit of work before considered "ready". 10 + 7 11 ## Licenses 8 12 9 13 The sensor hardware designs are licensed under the [CERN Open Hardware Licence Version 2 - Strongly Reciprocal](./kicad/LICENSE-CERN-OHL-S) license.
+12
embassy-strike-driver/Cargo.toml
··· 1 + [package] 2 + name = "embassy-strike-driver" 3 + authors.workspace = true 4 + edition.workspace = true 5 + repository.workspace = true 6 + version.workspace = true 7 + license.workspace = true 8 + rust-version.workspace = true 9 + 10 + [dependencies] 11 + embassy-time.workspace = true 12 + embassy-sync.workspace = true
+307
embassy-strike-driver/src/lib.rs
··· 1 + #![no_std] 2 + 3 + use core::cell::Cell; 4 + 5 + use embassy_sync::{ 6 + blocking_mutex::raw::NoopRawMutex, 7 + zerocopy_channel::{Channel, Receiver, Sender}, 8 + }; 9 + use embassy_time::{Duration, Instant, Ticker, Timer}; 10 + 11 + pub const BLOCK_SIZE: usize = 512; 12 + pub type DmaChannel<'device> = Channel<'device, NoopRawMutex, (i64, [u16; BLOCK_SIZE])>; 13 + 14 + pub trait TimeSource { 15 + fn timestamp(&self) -> i64; 16 + fn get_source() -> Self; 17 + } 18 + 19 + pub trait AdcSource { 20 + fn sample_average(&self, samples: &mut [u16]) -> impl Future<Output = u16>; 21 + fn sample(&self, samples: &mut [u16]) -> impl Future<Output = ()>; 22 + } 23 + 24 + pub trait PwmSource { 25 + fn set_duty(&mut self, duty: u16); 26 + } 27 + 28 + pub trait BufferMut<T> { 29 + fn push(&mut self, value: T); 30 + fn clear(&mut self); 31 + fn as_slice(&self) -> &[T]; 32 + fn len(&self) -> usize; 33 + fn is_empty(&self) -> bool; 34 + } 35 + 36 + #[derive(Debug)] 37 + struct DetectorConfig { 38 + max_duty: Cell<u8>, 39 + duty: Cell<u8>, 40 + avg: Cell<u16>, 41 + strikes: Cell<u16>, 42 + warn_level: Cell<u16>, 43 + blip_threshold: Cell<u16>, 44 + blip_size: Cell<usize>, 45 + } 46 + 47 + impl Default for DetectorConfig { 48 + fn default() -> Self { 49 + Self { 50 + max_duty: Cell::new(0), 51 + duty: Cell::new(0), 52 + avg: Cell::new(0), 53 + strikes: Cell::new(0), 54 + warn_level: Cell::new(255), 55 + blip_threshold: Cell::new(14), 56 + blip_size: Cell::new(2), 57 + } 58 + } 59 + } 60 + 61 + pub enum DetectorUpdate<'a> { 62 + Tick { 63 + timestamp: i64, 64 + level: u16, 65 + }, 66 + Detection { 67 + timestamp: i64, 68 + samples: &'a [u16], 69 + peaks: &'a [usize], 70 + }, 71 + } 72 + 73 + pub struct DetectorDriver<T: TimeSource, P: PwmSource, A: AdcSource> { 74 + config: DetectorConfig, 75 + timer_source: T, 76 + pwm: P, 77 + adc: A, 78 + } 79 + 80 + impl<T, P, A> DetectorDriver<T, P, A> 81 + where 82 + T: TimeSource, 83 + P: PwmSource, 84 + A: AdcSource, 85 + { 86 + pub fn new(timer_source: T, pwm: P, adc: A) -> Self { 87 + Self { 88 + config: Default::default(), 89 + timer_source, 90 + pwm, 91 + adc, 92 + } 93 + } 94 + 95 + pub fn reset_timer_source(&mut self) { 96 + self.timer_source = T::get_source(); 97 + } 98 + 99 + pub fn get_timestamp(&self) -> i64 { 100 + self.timer_source.timestamp() 101 + } 102 + 103 + pub async fn tune(&mut self, samples: &mut [u16]) { 104 + // info!("Tuning Detector for correct voltage settings"); 105 + let mut duty = 0; 106 + self.pwm.set_duty(duty); 107 + Timer::after_secs(2).await; 108 + let mut act_value = self.adc.sample_average(samples).await; 109 + 110 + // info!("initial ACT: {}", act_value); 111 + 112 + while act_value < 1364 { 113 + duty += 2; 114 + if duty >= 256 { 115 + duty = 0; 116 + self.pwm.set_duty(duty); 117 + Timer::after_secs(2).await; 118 + act_value = self.adc.sample_average(samples).await; 119 + // info!("Restarting tuning"); 120 + continue; 121 + } 122 + self.pwm.set_duty(duty); 123 + Timer::after_millis(250).await; 124 + act_value = self.adc.sample_average(samples).await; 125 + // info!("ACT: {}, Duty: {}", act_value, duty); 126 + } 127 + self.config.max_duty.set(duty as u8); 128 + duty = (duty / 3) * 2; 129 + self.pwm.set_duty(duty); 130 + self.config.duty.set(duty as u8); 131 + // info!("Set detection duty to: {}", duty); 132 + // Allow voltage level to stabilize after tuning 133 + Timer::after_secs(2).await; 134 + let avg = self.adc.sample_average(samples).await; 135 + self.config.avg.set(avg); 136 + } 137 + 138 + /// Samples and returns an `i64` timestamp. 139 + pub async fn sample_with_dma<'a>( 140 + &self, 141 + dma: &mut Sender<'a, NoopRawMutex, (i64, [u16; BLOCK_SIZE])>, 142 + ) { 143 + loop { 144 + let (time, samples) = dma.send().await; 145 + *time = self.timer_source.timestamp(); 146 + self.adc.sample(samples).await; 147 + dma.send_done(); 148 + } 149 + } 150 + 151 + pub async fn detect_from_sample<B, F>(&self, timestamp: i64, samples: &[u16], peaks: &mut B, update: F) 152 + where 153 + B: BufferMut<usize>, 154 + F: Fn(DetectorUpdate<'_>), 155 + { 156 + peaks.clear(); 157 + 158 + let new_avg = analyse_buffer_by_stepped_windows( 159 + self.config.blip_threshold.get(), 160 + samples, 161 + self.config.avg.get(), 162 + peaks, 163 + ); 164 + 165 + let blips = peaks.len(); 166 + 167 + if blips >= self.config.blip_size.get() { 168 + self.config 169 + .strikes 170 + .update(|strike| strike.saturating_add(32)); 171 + 172 + update(DetectorUpdate::Detection { 173 + timestamp, 174 + samples, 175 + peaks: peaks.as_slice(), 176 + }); 177 + } 178 + 179 + self.config.avg.set(new_avg); 180 + } 181 + 182 + pub async fn detect_with_dma<'a, 'd, B, F>( 183 + &self, 184 + dma: &mut Receiver<'a, NoopRawMutex, (i64, [u16; BLOCK_SIZE])>, 185 + peaks: &'d mut B, 186 + update: F, 187 + ) where 188 + B: BufferMut<usize>, 189 + F: Fn(DetectorUpdate<'_>), 190 + { 191 + loop { 192 + peaks.clear(); 193 + let (timestamp, samples) = dma.receive().await; 194 + 195 + let new_avg = analyse_buffer_by_stepped_windows( 196 + self.config.blip_threshold.get(), 197 + &*samples, 198 + self.config.avg.get(), 199 + peaks, 200 + ); 201 + 202 + let blips = peaks.len(); 203 + 204 + if blips >= self.config.blip_size.get() { 205 + self.config 206 + .strikes 207 + .update(|strike| strike.saturating_add(32)); 208 + 209 + update(DetectorUpdate::Detection { 210 + timestamp: *timestamp, 211 + samples, 212 + peaks: peaks.as_slice(), 213 + }); 214 + 215 + // info!( 216 + // "Strikes detected at {}s! avg {}, signal strength: {}, blip {}, level {}", 217 + // Instant::now().as_secs(), 218 + // average, 219 + // blips, 220 + // peaks.iter().max(), 221 + // self.state.warn_level.get() 222 + // ); 223 + } 224 + dma.receive_done(); 225 + self.config.avg.set(new_avg); 226 + } 227 + } 228 + 229 + pub async fn tick<F>(&self, update: F) 230 + where 231 + F: Fn(DetectorUpdate), 232 + { 233 + let mut inactive: Option<Instant> = None; 234 + let mut interval = Ticker::every(Duration::from_secs(1)); 235 + 236 + loop { 237 + interval.next().await; 238 + let strikes = self.config.strikes.get(); 239 + let mut warn_level = self.config.warn_level.get(); 240 + 241 + if strikes > 32 { 242 + warn_level = warn_level.saturating_add(strikes); 243 + } 244 + 245 + let decay = warn_level >> 8; 246 + 247 + self.config.strikes.set(0); 248 + self.config.warn_level.set(warn_level - decay); 249 + 250 + match inactive { 251 + Some(_) if decay > 0 => { 252 + inactive = None; 253 + } 254 + Some(val) if val.elapsed() >= Duration::from_secs(3600) => break, 255 + None if decay == 0 => { 256 + inactive = Some(Instant::now()); 257 + } 258 + None => { 259 + update(DetectorUpdate::Tick { 260 + timestamp: self.timer_source.timestamp(), 261 + level: warn_level - 255, 262 + }); 263 + } 264 + _ => continue, 265 + } 266 + } 267 + } 268 + } 269 + 270 + fn analyse_buffer_by_stepped_windows<B: BufferMut<usize>>( 271 + threshold: u16, 272 + buf: &[u16], 273 + average: u16, 274 + peaks: &mut B, 275 + ) -> u16 { 276 + const CHUNK_SIZE: usize = BLOCK_SIZE / 32; 277 + const CHUNK_STEP: usize = CHUNK_SIZE / 2; 278 + 279 + let mut total = 0u32; 280 + let mut len = 0u32; 281 + 282 + for (i, window) in buf.windows(CHUNK_SIZE).enumerate().step_by(CHUNK_STEP) { 283 + let window_total = window.iter().copied().sum::<u16>(); 284 + let window_avg = window_total / CHUNK_SIZE as u16; 285 + let diff = average.saturating_sub(window_avg); 286 + 287 + if diff > threshold { 288 + peaks.push(i); 289 + } else { 290 + total += window_total as u32; 291 + len += CHUNK_SIZE as u32; 292 + } 293 + } 294 + 295 + (total / len) as u16 296 + } 297 + 298 + // #[cfg(test)] 299 + // mod tests { 300 + // use super::*; 301 + 302 + // // #[test] 303 + // // fn it_works() { 304 + // // let result = add(2, 2); 305 + // // assert_eq!(result, 4); 306 + // // } 307 + // }

History

10 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
feat: Embassy driver
1/1 success
expand
expand 0 comments
pull request successfully merged
1 commit
expand
feat: Embassy driver
1/1 success
expand
expand 0 comments
1 commit
expand
feat: Embassy driver
1/1 success
expand
expand 0 comments
1 commit
expand
feat: Embassy driver
1/1 failed
expand
expand 0 comments
1 commit
expand
feat: Embassy driver
1/1 success
expand
expand 0 comments
1 commit
expand
feat: Embassy driver
1/1 success
expand
expand 0 comments
1 commit
expand
feat: Embassy driver
1/1 failed
expand
expand 0 comments
1 commit
expand
feat: Embassy driver
expand 0 comments
1 commit
expand
feat: Embassy driver
expand 0 comments
sachy.dev submitted #0
1 commit
expand
feat: Embassy driver
expand 0 comments