ENose Lab is a single-page web app that turns an Arduino Uno plus six MQ gas sensors into a trainable smell classifier. Everything — data collection, neural network training, confusion matrix, live inference — happens in your browser over WebSerial. When you're happy with the model, one click exports a self-contained Arduino sketch that runs inference directly on the Uno, with weights baked into flash via `PROGMEM`. No PC, no cloud, no retraining.
**Try it now:** https://cavyiot.com/enose (Chrome or Edge on desktop required — the app uses WebSerial)
## Why I built thisMost electronic-nose tutorials stop at "read the sensors." The interesting part — teaching the thing to tell coffee from ethanol — is usually punted to Python, Colab notebooks, TensorFlow Lite conversion, and a pile of toolchain setup that nobody actually wants to install. I wanted the whole loop (collect → train → deploy) to work in one tab, and for the final artifact to be a plain `.ino` file that anyone can flash from the Arduino IDE.
The neural network runs on `mashtishk`, a small JavaScript NN library I wrote for exactly this kind of on-device ML work. Training uses a Web Worker so the UI stays responsive. Cross-validation is built in (k-fold) so the accuracy number you see is actually honest.
## What you'll buildA standalone smell classifier that:
- Reads six MQ gas sensors continuously
- Runs a trained neural network on the Uno itself (no PC tethered after upload)
- Prints predicted class + confidence over Serial at 9600 baud
- Uses `PROGMEM` so weights live in flash — SRAM stays free for inference
## Hardware- Arduino Uno (ATmega328P)
- MQ-2 (LPG/smoke) → A0
- MQ-3 (alcohol) → A1
- MQ-4 (methane) → A2
- MQ-5 (natural gas) → A3
- MQ-7 (CO) → A4
- MQ-135 (air quality / NH3/NOx/benzene) → A5
- Breadboard, jumpers, USB cable
All sensors share `VCC → 5V` and `GND → GND`. Give them 2–3 minutes to warm up before collecting data — cold MQ readings are nonsense.
## The full workflow (browser side)1. **Connect.** Upload the provided sketch, plug in the Uno, click Connect.
2. **Define classes.** Pick 2–3 smells (e.g. clean air, coffee, ethanol). Set readings per session.
3. **Collect.** Hit Collect near each smell. Do 3+ sessions per class — the app merges them.
4. **AutoConfig.** App picks the architecture and hyperparameters.
5. **Train.** Runs in a Web Worker with live loss chart and k-fold cross-validation.
6. **Results.** Confusion matrix + live inference at 4 Hz.
7. **Export.** One click → `enose_uno.zip` with the sketch and inference header. Flash and go.
## How the on-Uno inference worksWeights and biases are generated as `const float PROGMEM` arrays, read from flash at inference time with `pgm_read_float()`. Two 64-float ping-pong buffers hold activations, so SRAM usage stays around 512 bytes regardless of model size. A typical 6→12→8→3 network (~230 params) runs in single-digit milliseconds.
## Tips that took me way too long to learn
- **Warm up the sensors for 2–3 minutes** before collecting. MQ heaters need time to stabilize; cold readings drift by hundreds of ADC counts.
- **Collect across sessions, not in one sitting.** Three collections of 20 readings each, spaced by a minute or two, beats one collection of 60 every time. The model learns the smell, not the specific moment you sampled.
- **Include "clean air"** as a class. Without a null class, the model will always predict *something* and confidently misclassify nothing-at-all as coffee.
- **Keep the network small.** 6 → 12 → 8 → N is a sensible starting point. Anything bigger than ~800 params gets tight on Uno SRAM.
- **Serial Monitor at 9600 baud** for the generated sketch. The browser-side collection sketch is also 9600.
## Limitations, honestly- MQ sensors are crude. They respond to broad gas families, not specific molecules. Don't expect to tell cabernet from merlot.
- Cross-sensitivity is real. MQ-135 sees everything. This is a feature for pattern-based classification, a bug if you want absolute concentrations.
- Humidity and temperature affect readings. If you collect training data in winter and test in summer, accuracy will tank. Collect in the conditions you'll deploy in.
- Browser support: Chrome or Edge desktop only. WebSerial isn't in Firefox or Safari, and the File System Access API isn't on mobile.
## What's nextI'm working on an ESP32 export target (12-bit ADC, 3.3V, Wi-Fi for remote inference) and a variant for the Grove Multichannel V2 I2C sensor. Also thinking about adding temperature/humidity compensation as an optional preprocessing step.
## Links- **Live app:** https://cavyiot.com/enose
- **mashtishk library:** [https://github.com/CavyAgrotronics/mashtishk]
- **CavyIoT:** https://cavyiot.com
Feedback and PRs welcome. If you build something with this, I'd love to see what smells you classified.


_ztBMuBhMHo.jpg?auto=compress%2Cformat&w=48&h=48&fit=fill&bg=ffffff)












Comments