It is with great disappointment that I must report continued failure to break through the sub 20 minute frozen croissant barrier.
Through more than a dozen bakes, I’ve established that my 1 kW air fryer can deliver more than enough power. The trouble has been sufficiently baking the inside before burning the outside — over the last 3 months I’ve scorched many croissant tops.
This is likely because the heating element is suspended just a few centimeters above the croissant, which rests in a 2 liter basket:
To test whether the burnt tops are from airflow (the top being the first surface the hot air hits, and thus where it transfers most of its energy) or from infrared radiation (direct line-of-sight to the heating element), I baked several croissants underneath a tin-foil “shade”.
Unfortunately, these shaded croissants turned out under-cooked (even at the full 20 minute cooking time) — my best guess is that, given the small basket size, any tin foil sufficient to fully shade a croissant ends up also significantly impeding the flow of warm air around it.
I also tested raising the humidity of the baking chamber by opening it every few minutes to spritz in water. However, this merely yielded soggy croissants with burnt tops.
All-together, these results leave me discouraged: Perhaps modifying air fryer geometry could get from 20 minutes to 15 minutes, but I don’t see a route to the truly life-changing sub 5 minute bake times using just convective airflow.
After the last newsletter, a reader informed me about metal baking rods which conduct heat to the center of large cakes. The same principle could be employed, though piercing frozen dough poses a more difficult physical interface than liquid batter — a croissant-shrike mechanism would need to overcome that.
Microwaves, even putting aside the safety and complexity reservations discussed last time, are poorly suited to defrosting — frozen water molecules are fixed in a lattice, so won’t readily absorb microwave energy, which finds its way to any available liquid water. This feedback loop results in the painfully-familiar “my burrito is still mostly frozen but now has pockets of lava” effect.
After pondering all this for a while, I stepped back to consider my overall goal: Conjure a deliciously, warm, coffee-complementing snack in the same time it takes to brew a cup.
Then it occurred to me: Rather than optimize a baking mechanism for a fixed croissant, what if I optimized a croissant for a fixed baking mechanism? That is, design a pastry which can be baked from frozen in under 5 minutes.
But that’ll have to wait until later. (In the mean time, please send in any ideas or favorite recipes!)
While my research failed to uncover a superior baking mechanism, it did prove to be a fertile problem domain for exploring time series visualization tools.
Here’s a graphic of the air fryer basket temperature:
Note that when it heats an empty basket it overshoots the setpoint temperature by about 20°C, but after adding the ~60g pain au chocolat it’s pretty accurate, with the temperature oscillating by only about 15°C or 10% of the setpoint — seems pretty good to me for an all-analog, cheapest-on-Amazon special.
My measurement apparatus consists of a Raspberry Pi running a Rust program (enclosed at end of newsletter) which reads the oven air temperature via a MAX6675 Module + K Type Thermocouple and stores to a SQLite database, which is then visualized in a browser using Grafana.
For my use case of live time series visualization, I found Grafana just OK:
I didn’t need or want an extra running service or to maintain a “database” of temperatures.
Really all I wanted was something that felt as lightweight as a println
statement which I could add to my small measurement program, which would spin up a webpage with a live-updating time series plot.
When I mentioned this last time, readers suggested:
However, since I wanted to stick with the language I had the most embedded experience with, Rust, I ended up making splot (“streaming plot”), a lil’ Rust library to do exactly what I want =D
On the browser-side, it relies on the wonderfully lightweight and fast uPlot JavaScript plotting library for visualization. On the server-side, while it’s not entirely allocation-free (the underlying axum web framework will allocate when new browsers connect), I did my best to keep things lightweight:
In addition to numbers, it also allows streaming lines of text — imagine that, being able to view time series and logs in a browser. With that kind of advanced 1990’s technology, will you even need a terminal window anymore!?!
Transform cameras in buttons and joysticks with silicone lenses
“Sans anesthetic, electroporation elicited the remark, ‘On a POW that would be a war crime.’ Available literature greatly understated sensations.” This Hormone Gene Therapy Experiment
A high-level programming language for generative protein design
“Tech debt, in its simplest form, is the time you didn’t spend making tasks more efficient.”
Activated Charcoal for Hangover Prevention: Way more than you wanted to know
HydraRaptor: Buried nuts and hanging holes (designing for 3d printing)
“I survived living in LA and commuting to Cal by plane over the past academic year to save on rent, AMA”
“WebGPU caters to the kind of person who thinks it might be fun to write their own raymarcher, without requiring every programmer to be the kind of person who thinks it would be fun to write their own implementation of malloc.”
“Imagine losing your rudder out at sea and sending out a distress call. And then the largest ocean-going wooden sailing ship in the world comes to your rescue”
“a synthetic plastic created by Bell chemists to replace the existing telephone cable sheathing saved Bell “more than the total research budget of Bell Labs for the decade in which the innovation was worked out.” This was an operationally boring problem that could have been hidden in some maintenance budget, away from the eyes of normal researchers. A system engineer exists to identify opportunities just like that.”
Tested: Where Does The Tone Come From In An Electric Guitar?
“This is a classic case of Ask Culture meets Guess Culture.”
“I wonder why no app has figured rich text editing yet. It’s really simple: you allow two positions on block boundary, one inside and one outside.”
How I Used Stable Diffusion and Dreambooth to Create A Painted Portrait of My Dog
// Build for raspberry pi via:
// cross build --release --features pi --bin croissantron --target aarch64-unknown-linux-gnu
use log::*;
fn now_millis() -> u64 {
use std::time::{SystemTime, UNIX_EPOCH};
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis() as u64
}
fn main() -> anyhow::Result<()> {
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("info"))
.format_timestamp(None)
.init();
let db = rusqlite::Connection::open("observations.db")?;
db.execute(
"CREATE TABLE IF NOT EXISTS observation (
t REAL NOT NULL,
temp REAL NOT NULL
)",
(),
)?;
let insert = |temp| {
db.execute(
"INSERT INTO observation (t, temp) VALUES (?1, ?2)",
// grafana struggles with human readable string (even after sqlite datetime fn); so just store unix timestamp integer
(now_millis(), temp),
)
.unwrap();
};
#[cfg(feature = "pi")]
let mut read_temp = {
// Wire to spi0 pins as described on https://pinout.xyz/pinout/spi
let mut spi = {
use rppal::spi::{Bus, Mode, SlaveSelect};
rppal::spi::Spi::new(Bus::Spi0, SlaveSelect::Ss0, 8_000_000, Mode::Mode0)?
};
move || {
let mut buf = [0u8; 2];
spi.read(&mut buf).unwrap();
debug!("{:04x?}", buf);
// https://www.analog.com/media/en/technical-documentation/data-sheets/MAX6675.pdf
0.25 * (u16::from_be_bytes(buf) >> 3) as f32
}
};
#[cfg(not(feature = "pi"))]
let read_temp = || 100.0;
loop {
let temp = read_temp();
info!("{} degC", temp);
insert(temp);
std::thread::sleep(std::time::Duration::from_millis(500));
}
//Ok(())
}