Skip to content

Instantly share code, notes, and snippets.

@GOROman
Created May 12, 2026 07:53
Show Gist options
  • Select an option

  • Save GOROman/d4aa7895e9c7fbb0f6f49973f58a8776 to your computer and use it in GitHub Desktop.

Select an option

Save GOROman/d4aa7895e9c7fbb0f6f49973f58a8776 to your computer and use it in GitHub Desktop.
🦐 ナル先生の屁ロガー - M5Stack ATOM Lite + SGP30 (M5 Unit Mini TVOC/eCO2) で屁を検出する Arduinoサンプル。H2/TVOC二重検知

🦐 ナル先生の屁ロガー (Fart Logger)

M5Stack ATOM Lite + SGP30 (M5 Unit Mini TVOC/eCO2) で屁を検出するサンプル。

仕組み

屁の主成分:

  • 窒素 78%、CO2 9%、水素H2 9%、メタン3%、酸素3%
  • 臭気成分: 硫化水素・スカトール・インドール(微量)

SGP30は H2濃度を内部で測定 → そこから eCO2 (equivalent CO2) を算出。 つまり屁を浴びると eCO2値が400ppm → 数千ppm に爆上がり。 さらに臭気成分は TVOC として総量カウントされる。

必要なもの

部品 型番 入手
マイコン M5Stack ATOM Lite スイッチサイエンス ¥1,650
センサー M5 Unit Mini TVOC/eCO2 (U088) 秋月電子 ¥2,380
Groveケーブル - 付属

合計 約¥4,000。

セットアップ

  1. Arduino IDE / PlatformIO で開く
  2. ライブラリ追加:
    • M5Atom
    • Adafruit SGP30 Sensor
    • FastLED
  3. Board: ESP32 Pico Kit
  4. 配線: Grove I2C (SDA=GPIO26, SCL=GPIO32)
  5. 書き込み → Serial 115200 で観測

LED表示

  • 🔵 青: ベースライン取得中(30秒)
  • 🟢 緑微弱: 通常
  • 🔴 赤フラッシュ: 屁検出!(eCO2≧1500ppm or TVOC≧2000ppb)

閾値調整

fart_logger.ino の以下を環境に合わせて:

const uint16_t ECO2_THRESHOLD_PPM = 1500;   // 通常400ppm
const uint16_t TVOC_THRESHOLD_PPB = 2000;   // 臭気

注意 ⚠️

人類の約1/3はメタン産生型腸内細菌でH2をほぼ出しません。 SGP30の前でも証拠不十分の可能性あり。完全犯罪が可能な人もいます。

TODO

  • Telegram Bot API への通知(屁検出時)
  • Adafruit IO フィードに記録(時系列)
  • 自家製キャリブレーション(個人差対応)
  • 「肩乗りナルエビ」連動:StackChan が屁検出時に「💩」表示

ライセンス

MIT。自由に改造・配布OK 🦐

// ===================================================================
// 🦐 ナル先生の屁ロガー (Fart Logger) - SGP30 + M5Stack ATOM Lite
// ===================================================================
// 主成分H2(9%) + 硫化水素/スカトール(TVOC) を SGP30 で検出
// eCO2 が閾値を超えたら ATOM Lite LED 赤フラッシュ+Serial通知
//
// 配線: M5Stack ATOM Lite + M5 Unit Mini TVOC/eCO2 (U088)
// ATOM Lite Grove → I2C: SDA=GPIO26, SCL=GPIO32, 5V, GND
// LED: GPIO27 (内蔵 SK6812 1個)
//
// 依存:
// - M5Atom (M5Stack 公式)
// - Adafruit SGP30 Sensor Library
// - FastLED (M5Atomの内蔵LED制御)
//
// 注意:
// 人類の約1/3はメタン産生型腸内細菌でH2をほぼ出さない(false negativeあり)
// 完璧な屁検出はできません 💦
// ===================================================================
#include <M5Atom.h>
#include <Wire.h>
#include <Adafruit_SGP30.h>
Adafruit_SGP30 sgp;
// 閾値
const uint16_t ECO2_THRESHOLD_PPM = 1500; // 通常400ppm、屁で跳ね上がる
const uint16_t TVOC_THRESHOLD_PPB = 2000; // 臭気成分閾値
const unsigned long BASELINE_MS = 30000; // 30秒のベースライン取得
unsigned long bootMs = 0;
int detectCount = 0;
void setup() {
M5.begin(true, false, true); // Serial, I2C, LED
delay(50);
Serial.begin(115200);
Serial.println("🦐 ナル先生の屁ロガー起動");
// SGP30 init
if (!sgp.begin()) {
Serial.println("❌ SGP30 not found! 配線確認");
while (1) {
M5.dis.drawpix(0, 0xff0000); // 赤
delay(500);
M5.dis.drawpix(0, 0x000000);
delay(500);
}
}
Serial.print("✅ SGP30 OK, serial: ");
Serial.print(sgp.serialnumber[0], HEX);
Serial.print(sgp.serialnumber[1], HEX);
Serial.println(sgp.serialnumber[2], HEX);
M5.dis.drawpix(0, 0x0000ff); // 青 = ベースライン取得中
bootMs = millis();
}
void loop() {
M5.update();
if (!sgp.IAQmeasure()) {
Serial.println("❌ measurement failed");
delay(500);
return;
}
uint16_t tvoc = sgp.TVOC; // ppb
uint16_t eco2 = sgp.eCO2; // ppm (H2から算出)
// raw H2 / Ethanol も取れる
if (!sgp.IAQmeasureRaw()) {
Serial.println("❌ raw measurement failed");
}
uint16_t rawH2 = sgp.rawH2;
uint16_t rawEth = sgp.rawEthanol;
unsigned long elapsed = millis() - bootMs;
bool inBaseline = (elapsed < BASELINE_MS);
if (inBaseline) {
// ベースライン中 = 青LED
M5.dis.drawpix(0, 0x0000ff);
} else if (eco2 >= ECO2_THRESHOLD_PPM || tvoc >= TVOC_THRESHOLD_PPB) {
// 検出 = 赤LED フラッシュ
detectCount++;
Serial.printf("⚠️ [%d] 屁検出! TVOC=%u ppb / eCO2=%u ppm / H2raw=%u / Ethraw=%u\n",
detectCount, tvoc, eco2, rawH2, rawEth);
M5.dis.drawpix(0, 0xff0000);
delay(200);
M5.dis.drawpix(0, 0x000000);
delay(100);
M5.dis.drawpix(0, 0xff0000);
delay(200);
M5.dis.drawpix(0, 0x000000);
// TODO: ここで Telegram Bot API or Adafruit IO へ POST
// notifyTelegram(eco2, tvoc, rawH2);
} else {
// 通常 = 緑LED 微弱
M5.dis.drawpix(0, 0x002000);
Serial.printf("[%lus] TVOC=%u eCO2=%u H2=%u Eth=%u\n",
elapsed / 1000, tvoc, eco2, rawH2, rawEth);
}
delay(100); // 100ms間隔(SGP30推奨1秒・短縮はキャリブレ精度落ち)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment