Skip to content

Progress Log: Serial.printlnの使い方ガイド

Task Description

Serial.println(およびSerial.print)の使い方をKurikintonsプロジェクトの文脈で整理しました。

以下の観点から調査・整理しました。

  • Serial.printlnSerial.printの違い
  • 基本的な型と出力方法
  • Fマクロによるメモリ最適化
  • kurikintonsでの実践的な使用例
  • ArduinoJsonとの連携

Outcome

Serial.printlnの包括的な使用ガイドを作成しました。

Serial.print vs Serial.println

違い:

  • Serial.print() - 改行なし
  • Serial.println() - 改行付き(\nを自動追加)
Serial.print("Detection:");
Serial.print(95);
Serial.println();  // 改行

// 出力: Detection:95

出力可能な基本型

// 文字列
Serial.println("Hello, World!");           // 文字列リテラル
Serial.println("Detection complete");

// 数値型
Serial.println(123);                       // 整数
Serial.println(45.67);                     // 浮動小数点数
Serial.println((byte)255);                 // バイト値

// 真偽値
Serial.println(true);                      // "1"
Serial.println(false);                     // "0"

// 変数
int value = 100;
float temp = 25.35;
Serial.println(value);
Serial.println(temp);

char配列とStringオブジェクト

// char配列(C文字列)
char str[] = "Detection";
Serial.println(str);

// Stringオブジェクト
String myString = "ArduinoJson";
Serial.println(myString);

数値の基数指定

// 16進数
Serial.println(0xFF, HEX);                 // FF

// 2進数
Serial.println(255, BIN);                  // 11111111

// 8進数
Serial.println(255, OCT);                  // 377

// 10進数(デフォルト)
Serial.println(255, DEC);                  // 255
Serial.println(255);                       // 255

Fマクロ - メモリ最適化

問題: 文字列リテラルはデフォルトでRAMに格納される

// ❌ 問題:RAMを消費
Serial.println("Detection complete");
Serial.println("Sensor initialized");
Serial.println("Error occurred");

解決: Fマクロを使ってフラッシュメモリに格納

// ✅ 推奨:RAMを節約
Serial.println(F("Detection complete"));
Serial.println(F("Sensor initialized"));
Serial.println(F("Error occurred"));

メモリ削減効果:

  • 固定メッセージ3つ(各30バイト程度): 90バイトのRAM節約
  • ESP32は限られたRAMしかないため、制御メッセージはすべてFマクロを使用すべき

Kurikintonsでの実践例

1. 検出イベントのログ出力

// イベント発生時の通知(Fマクロ使用)
if (detection.detected) {
  Serial.println(F("=== Detection Event ==="));

  // 検出値の出力(動的なので Fマクロ不要)
  Serial.print(F("signal1: "));
  Serial.println(detection.signal1);

  Serial.print(F("signal2: "));
  Serial.println(detection.signal2);

  Serial.print(F("temperature: "));
  Serial.println(temperature);
}

2. JSONL形式での出力

StaticJsonDocument<256> doc;
doc["signal1"] = 95;
doc["signal2"] = 87;
doc["signal3"] = 92;
doc["adc"] = 2048;
doc["temp"] = 25.35;
doc["pressure"] = 1013.25;
doc["humidity"] = 45.67;
doc["uptime_ms"] = 123456;
doc["duration_us"] = 1234567890;

// JSON全体をシリアル出力(JSONL形式)
serializeJson(doc, Serial);
Serial.println();  // 改行で1行を完成

シリアル出力:

{"signal1":95,"signal2":87,"signal3":92,"adc":2048,"temp":25.35,"pressure":1013.25,"humidity":45.67,"uptime_ms":123456,"duration_us":1234567890}

3. センサー初期化ログ

void setup() {
  Serial.begin(115200);
  delay(3000);

  // Fマクロで制御メッセージを出力
  Serial.println(F("Kurikintons System Starting..."));

  // センサー初期化
  if (bme280_init()) {
    Serial.println(F("BME280 initialized"));
  } else {
    Serial.println(F("BME280 initialization failed"));
  }

  cosmic_detector_init();
  Serial.println(F("Cosmic detector initialized"));

  Serial.println(F("System ready"));
}

4. デバッグ出力パターン

// 値と説明を組み合わせた出力
Serial.print(F("Free heap: "));
Serial.print(ESP.getFreeHeap());
Serial.println(F(" bytes"));

// 複数の値を1行で出力
Serial.print(F("Signal: "));
Serial.print(signal1);
Serial.print(F(", "));
Serial.print(signal2);
Serial.print(F(", "));
Serial.println(signal3);

// 結果: Signal: 95, 87, 92

Serial.print/println vs ArduinoJson

Single値の出力:

Serial.println(value);  // 単純で直感的

複合データ(JSON)の出力:

// JSON全体をまとめて出力
serializeJson(doc, Serial);
Serial.println();  // 改行で1行完成

// 結果: {"signal1":95,"signal2":87,...}

推奨パターン:

  • 制御メッセージ → Serial.println(F("..."))
  • センサー値 → Serial.print()Serial.println()
  • 検出データ(複合) → serializeJson(doc, Serial) + Serial.println()

メモリ使用量のチェック

void print_memory_info() {
  // Fマクロでメモリ情報を出力
  Serial.print(F("Free heap: "));
  Serial.print(ESP.getFreeHeap());
  Serial.println(F(" bytes"));

  Serial.print(F("Largest free block: "));
  Serial.print(ESP.getMaxAllocHeap());
  Serial.println(F(" bytes"));

  Serial.print(F("Flash size: "));
  Serial.print(ESP.getFlashChipSize() / 1024);
  Serial.println(F(" KB"));
}

ボーレート設定

// 115200 bps はKurikintons標準
Serial.begin(115200);  // JSONL出力に推奨

転送時間の目安(115200 bps = 9600 バイト/秒):

  • 60バイト(スペース区切り): 約6ms
  • 150バイト(JSONL): 約16ms

Learnings

  1. Fマクロは必須: 固定メッセージにはF()を使ってRAMを節約
  2. print vs println: 用途に応じて使い分ける(改行の有無)
  3. JSON出力との組み合わせ: serializeJson(doc, Serial) + Serial.println()で効率的
  4. メモリ制約を意識: ESP32のRAMは限られているため、ストリーミング出力が有効
  5. デバッグとデータ出力の分離: 制御メッセージ(Fマクロ)と検出データ(JSON)を分ける

Next Steps

  1. Kurikintonsでの統合:
  2. 既存のSerial.printlnをFマクロに置き換え
  3. JSONL形式の検出データ出力へ移行
  4. ログレベル機能の実装(デバッグ/本番)

  5. パフォーマンス測定:

  6. JSONL形式でのシリアル出力速度テスト
  7. メモリ使用量の実測定
  8. バッファオーバーフロー確認

  9. ドキュメント統合:

  10. ArduinoJsonガイドとの組み合わせ例
  11. JSON形式ガイドとの統合