diff --git a/examples/usbnoise.rs b/examples/usbnoise.rs
index 8e6eeaece71a9bb81979ba771a4bd76ed2b17d90..8fcb66d601e5559d18a82be1e7ddfb1ad26a14a4 100644
--- a/examples/usbnoise.rs
+++ b/examples/usbnoise.rs
@@ -136,58 +136,33 @@ async fn run<'d, T: Instance + 'd>(pin: &mut impl Pin, class: &mut CdcAcmClass<'
     // max packet is 64 bytes. we hex encode and add newline, 31*2+1
     const CHUNK: usize = 31;
 
-    let mut buf = [false; CHUNK * 8 * 10];
 
-    let mut a = None;
-    let mut pos = 0u32;
-    let deci = 40u32;
+    let low_cycles = caprand::cap::best_low_time(pin, 10..=90u32).unwrap();
+    trace!("low_cycles = {}", low_cycles);
+    let low_cycles = 54;
+    trace!("low_cycles = {}", low_cycles);
+    let noise = caprand::cap::Noise::new(pin, low_cycles, 5)?
+        .extract();
+    let mut noise = caprand::cap::bits_to_bytes(noise);
+
+
     loop {
+        let mut buf = [0u8; CHUNK];
         let mut b = buf.iter_mut();
-        let low_delay = 30;
-        // XXX should discard first iter or something.
-        caprand::cap::noise(pin, low_delay,
-            |v| {
-                pos = pos.wrapping_add(1);
-                if pos % deci == 0 {
-                    let v = v as u8;
-                    if a.is_none() {
-                        a = Some(v);
-                        true
-                    } else {
-                        let aa = a.take().unwrap();
-                        if aa != v {
-                            if let Some(i) = b.next() {
-                                *i = aa > v;
-                                true
-                            } else {
-                                false
-                            }
-                        } else {
-                            true
-                        }
-                    }
-
-                } else {
-                    // discard
-                    true
-                }
-        }).unwrap();
-
-        for chunk in buf.chunks(CHUNK*8) {
-            // write!(&mut buf, "{:?}", chunk);
-            let mut hex = ['B' as u8; CHUNK*2+1];
-            let mut c = chunk.iter();
-            let mut m = hex.iter_mut();
-            for _ in 0..CHUNK {
-                let mut x = 0u8;
-                for b in 0..8 {
-                    x |= (*c.next().unwrap() as u8) << b;
-                }
-                *m.next().unwrap() = nibble_hex(x >> 4);
-                *m.next().unwrap() = nibble_hex(x & 0xf);
+        loop {
+            match b.next() {
+                Some(b) => *b = noise.next().unwrap()?,
+                None => break
             }
-            *m.next().unwrap() = b'\n';
-            class.write_packet(&hex).await.unwrap();
         }
+
+        let mut hex = ['B' as u8; CHUNK*2+1];
+        let mut m = hex.iter_mut();
+        for c in buf {
+            *m.next().unwrap() = nibble_hex(c >> 4);
+            *m.next().unwrap() = nibble_hex(c & 0xf);
+        }
+        *m.next().unwrap() = b'\n';
+        class.write_packet(&hex).await.unwrap();
     }
 }
diff --git a/src/cap.rs b/src/cap.rs
index fe395ac383414a970354962881d89adc5d085d12..5f13b439da87caa05e62fd3318ba06a1303f095c 100644
--- a/src/cap.rs
+++ b/src/cap.rs
@@ -5,12 +5,13 @@ use log::{debug, info, warn, error, trace};
 use defmt::{error, debug, info, panic, trace};
 
 use core::arch::asm;
+use core::mem::replace;
 
 use embassy_rp::gpio::Pin;
 use embassy_rp::pac;
 
 /// Extra iterations prior to taking output.
-const WARMUP: usize = 16;
+const WARMUP: u8 = 16;
 
 /// Drives a pin low for an exact number of cycles.
 /// Call with interrupts disabled if it's important.
@@ -234,6 +235,104 @@ pub fn lsb(v: u32) -> usize {
     return 32
 }
 
+pub struct Noise<'a, P: Pin> {
+    pin: &'a mut P,
+    low_cycles: u32,
+    skip: u8,
+    _setup: PinSetup,
+}
+
+impl<'a, P: Pin> Noise<'a, P> {
+    pub fn new(pin: &'a mut P, low_cycles: u32, skip: u8) -> Result<Self, ()> {
+        let setup = PinSetup::new(pin.pin());
+        let mut s = Self {
+            pin,
+            low_cycles,
+            skip,
+            _setup: setup,
+        };
+
+        for _ in 0..WARMUP {
+            // OK unwrap: iterator never ends
+            s.next().unwrap()?;
+        }
+        Ok(s)
+    }
+
+    pub fn extract(self) -> impl Iterator<Item = Result<bool, ()>> + 'a {
+        // reduce rate to decorrelate
+        let skip = self.skip as usize;
+        let i = self.step_by(skip);
+
+        // von Neumann extractor
+        Pairs { iter: i }
+            .filter_map(|(a,b)| {
+                if let (Ok(a), Ok(b)) = (a,b) {
+                    if a == b {
+                        None
+                    } else {
+                        // trace!("{} {}", a, b);
+                        Some(Ok(a > b))
+                    }
+                } else {
+                    Some(Err(()))
+                }
+            })
+    }
+}
+
+impl<P: Pin> Iterator for Noise<'_, P> {
+    type Item = Result<u8, ()>;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let r = critical_section::with(|_cs| {
+            let r = time_rise(self.pin, self.low_cycles)?;
+            Ok(r as u8)
+        });
+        Some(r)
+    }
+}
+
+pub fn bits_to_bytes(i: impl Iterator<Item = Result<bool, ()>>) ->
+        impl Iterator<Item = Result<u8, ()>> {
+    let mut pos = 0;
+    let mut acc = 0;
+    i.filter_map(move |r| {
+        if let Ok(r) = r {
+            // trace!("{}", r as u8);
+            acc |= (r as u8) << pos;
+            pos += 1;
+            if pos == 8 {
+                pos = 0;
+                Some(Ok(replace(&mut acc, 0)))
+            } else {
+                None
+            }
+        } else {
+            Some(Err(()))
+        }
+    })
+}
+
+
+pub struct Pairs<I> {
+    iter: I,
+}
+
+impl<I> Iterator for Pairs<I>
+    where I: Iterator<Item = Result<u8, ()>>
+{
+    type Item = (I::Item, I::Item);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let a = self.iter.next()?;
+        let b = self.iter.next()?;
+        // trace!("{} {}", a, b);
+
+        Some((a,b))
+    }
+}
+
 // `f()` is called on each output `u32`.
 pub fn noise<'d, F>(
     pin: &mut impl Pin,