Join Nostr
2026-08-03 01:03:08 UTC

wono on Nostr: Arduino/ESP32の String ...

Arduino/ESP32の String 連結で問題になるのは、文字列を書き換えるたびにメモリ確保・コピー・解放が発生することがあるためです。

例えばこのコード:

message += "🌡️ 温度: " + String(currentTemperature, 1) + "°C\n";

内部では単純に「後ろに文字を足す」だけではありません。

概念的には以下のような処理になります。

"🌡️ 温度: "

String(currentTemperature, 1)

"🌡️ 温度: 25.3"

"🌡️ 温度: 25.3°C\n"

messageへコピー

途中で一時的なStringが作られます。

さらに、このような書き方:

message += "abc";
message += "def";
message += "ghi";

なら比較的安全です。

しかし、

message += "abc" + String(value) + "def" + String(value2);

のような式では、

String(value)

一時String生成

+ "def"

別の一時String生成

+ String(value2)

さらに一時String生成

という流れになります。

ESP32ではRAMはこのように使われます。

ヒープ領域

┌──────────────┐
│ message │ ← 本体
├──────────────┤
│ 一時String A │
├──────────────┤
│ 一時String B │
├──────────────┤
│ 一時String C │
└──────────────┘

処理終了後、一時Stringは解放されます。

問題はESP32のような小さいRAMでは、

使う
解放する
また確保する

を繰り返すと、**メモリの断片化(heap fragmentation)**が起きることです。

例:

最初:

空き 100KB
████████████████

小さいStringを作る:

████□□████████

解放:

████ □□ ████

合計では空きがあるのに、大きな連続領域が取れない、という状態になります。

対策が reserve() です。

現在:

String message = "";
message += "温度";
message += "湿度";
message += "気圧";

の場合、最初は小さい領域しか確保されません。

例えば:

message
容量 16byte
↓ 足りない
容量 32byteへ移動
↓ 足りない
容量 64byteへ移動

となります。

これを:

String message;
message.reserve(200);

にすると、

最初から:

message
容量 200byte

を確保します。

ただし、今回のコードの場合、もっと重要なのはここです。

String noteString = nostr.getNote(nsecHex, npubHex, now, message);

この時点で、

message
+
NostrイベントJSON
+
署名処理用データ
+
noteString

が同時に存在します。

つまり、投稿文を100文字増やしただけでも、実際にはその数倍のRAM消費増加になる可能性があります。

Nostrのような署名付き通信では、普通の「長い文章を送る」よりメモリ負荷が大きくなります。

Arduino/ESP32の String 連結で問題になるのは、文字列を書き換えるたびにメモリ確保・コピー・解放が発生することがある

発生することがある
って絶対ではないの?

はい、「発生することがある」と書いたのは、毎回必ず再確保が起きるわけではないためです。

ArduinoのStringは内部で容量(capacity)を持っています。

例:

String message;
message += "abc";

最初は内部バッファを確保します。

message
[abc.............]

その後:

message += "def";

追加分が現在の空き容量内なら、

[abcdef.........]

となり、再確保は発生しません。

しかし:

String message;
message += "abcdefghijklmnopqrstuvwxyz";

のように容量を超えると、

新しい大きいメモリ領域を確保
古い内容をコピー
古い領域を解放

が発生します。

古い領域
[abc]



新しい領域
[abcdefghijklmnopqrstuvwxyz]



古い領域解放

です。

今回のコードの場合:

String message = "";

message += "🌡️ 温度: " + String(currentTemperature, 1) + "°C\n";
message += "💧 湿度: " + String(currentHumidity, 1) + "%\n";
message += "📊 気圧: " + String(currentPressure, 1) + "hPa";

では、再確保が起きる可能性は高めです。

理由は、

最初の容量がほぼ0
追加する文字列が複数回ある
日本語・絵文字はUTF-8で1文字数バイトになる
String(float)で一時オブジェクトが作られる

ためです。

ただし、ESP32のArduino String実装では容量拡張時にある程度余裕を持って確保するため、毎回1文字追加ごとに確保するわけではありません。

ちなみにもっと正確に言うと、

message += "固定文字"
→ 容量不足時だけ再確保
"文字列" + String(数値)
→ 一時的なString生成が発生する可能性が高い
message.reserve(200)済み
→ 200バイト以内なら再確保なし

です。

なので問題の中心は「+=を使うこと」ではなく、容量を見積もらずに何度も拡張させることと、一時Stringを大量に作る書き方です。