The 3.7 kB Alarm: A Zero-Bloat Edge AI Smoke Detector in Pure C A developer built a 3.7 kB edge AI smoke detector in pure C, using a neural network that runs on ultra-low-power microcontrollers like the ESP32. The model monitors 12 environmental variables in parallel to detect non-linear chemical signatures of imminent fires, requiring no TensorFlow Lite or other heavy frameworks. The project achieved a zero-bloat design by quantizing weights to 8-bit integers, fitting the entire core model in just 96 bytes. You are building a critical IoT safety device — a smart smoke and fire detector. It monitors 12 environmental variables in parallel: temperature, humidity, TVOC, eCO2, raw hydrogen, ethanol, barometric pressure, and five particulate matter metrics. You want a neural network to catch the non-linear chemical signatures of an imminent fire before the flames start. Then you look at the industry standard for Edge AI. TensorFlow Lite for Microcontrollers demands megabytes of Flash, custom memory allocators, a dynamic runtime, and a dependency chain long enough to make you reconsider the whole project. On a cheap, ultra-low-power microcontroller — an ESP32, an ATtiny — those frameworks eat the silicon and leave nothing for the WiFi stack or peripheral control. The hardware isn't the problem. No synthetic data here. We used the Smoke Detection Dataset — originally collected in the field at 1 Hz by German researcher Stefan Blattmann for his project Real-time Smoke Detection with AI-based Sensor Fusion , and later curated and published on Kaggle by Dataset Grandmaster Deep Contractor. Blattmann's setup captured real environmental readings under real conditions: normal rooms, controlled wood-burning tests, outdoor air, active indoor smoke formation. His goal was the same as ours: prove that sensor fusion is more reliable for saving lives than a single optical or ionization detector that trips on kitchen smoke. The dataset provides 12 environmental features: A note on hardware: 12 features does not mean 12 pins. In a real deployment, Temperature + Humidity come from a single DHT22 or SHT31 1 pin . PM1.0 through NC2.5 come from one particle sensor over UART. eCO₂ + TVOC from a single SGP30 over I2C. The full sensor stack runs on 4–6 physical pins on an ESP32, sharing I2C and UART buses. Raw data is a problem for a micro-network. The original CSV also contains a UTC timestamp, a row index, and a CNT column — a sequential counter. We dropped all three. Timestamps and sequence counters have zero correlation with real chemistry; they cause networks to memorize ordering rather than learn physics. No Jupyter notebooks. No Pandas. A fast, standalone parser in pure C++. Three things it does: The Cleanup. UTC, row index, and CNT are dropped. They are position artifacts that cause instant overfitting. Min-Max Scaling. All 12 sensor readings mapped to a strict 0.0, 1.0 boundary. This prevents high-magnitude features like eCO₂ from drowning out small signals like temperature fluctuations. Target Lock. A critical bug surfaced during early testing: the binary classification column 0 = clean air, 1 = alarm was accidentally swept into the normalization pool and corrupted into floating-point fractions. We locked the label column to ensure it stayed as hard integers. Class Balance. The dataset is skewed — 44,757 fire events vs. 17,873 clean air samples. Without correction, the network takes the easy path: predict "Alarm" constantly and collect 71% accuracy for free. We undersampled the majority class to 17,873, producing a balanced 35,746-row dataset. The output: a clean, normalized CSV with exactly 12 inputs and 1 binary label. We fed 12 features into a funnel architecture: 12 → 8 → 4 → 1 Input Layer 12 Neurons Normalized Sensor Features | Hidden Layer 1 8 Neurons ReLU | Hidden Layer 2 4 Neurons ReLU | Output Layer 1 Neuron Sigmoid → Probability 0.0 to 1.0 By quantizing weights from 32-bit floats down to 8-bit signed integers int8 t , the core weights fit in exactly 96 bytes. First training run looked great on paper. Wrong. During validation, the model output the exact same value for every input. Because the raw dataset was skewed 3:1 toward fire events, the network found the easy path: freeze the biases, say "Alarm" constantly, collect the accuracy applause from the loss function. The fix: aggressive data shuffle, class balancing in the preprocessor, and a lower learning rate to keep weights out of the dead zones of the Sigmoid activation. A subtler problem took longer to catch. The CNT column — a sequential row counter — was included as a feature in early runs. The network learned to use position in the dataset as a proxy for class membership. The results looked excellent. They were meaningless. CNT was dropped and the entire preprocessing pipeline was rebuilt from scratch. Training was done with Hasaki 刃先 v3.2.0 — a CLI C++ tool for desktop neural network training that exports standalone C headers for firmware deployment. No Python, no runtime, no dependencies. Configuration: The model converged at epoch 2,013 . Final validation loss: 0.000008 . After training, we evaluated the model against a held-out test set of 7,150 samples — data the model never touched during training or early stopping. Confusion Matrix: 3547 4 1 3598 Accuracy: 99.9301% Breaking this down: That last number. One missed fire event out of 3,599. 99.97% sensitivity. In a smoke detector, a false positive wakes someone up at 2 AM. A false negative lets a fire go undetected. This network achieved 99.97% sensitivity on data it had never seen, running in 3.7 kB of Flash. The end product is a single, self-contained C header: alarm model.h . The entire file — weights, biases, quantization scales, activation functions, and inference code — is 3.7 kB on disk. Generated by Hasaki v3.2.0: ifndef ALARM MODEL H define ALARM MODEL H include