+520
Diff
round #0
+181
Cargo.lock
+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
+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
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
+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
+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
1 commit
expand
collapse
feat: Embassy driver
1/1 success
expand
collapse
expand 0 comments
pull request successfully merged
1 commit
expand
collapse
feat: Embassy driver
1/1 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
feat: Embassy driver
1/1 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
feat: Embassy driver
1/1 failed
expand
collapse
expand 0 comments
1 commit
expand
collapse
feat: Embassy driver
1/1 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
feat: Embassy driver
1/1 success
expand
collapse
expand 0 comments
1 commit
expand
collapse
feat: Embassy driver