Arduino / ESP8266 の使用できるRAM 領域を再考

ESP8266 ( ESP-WROOM-02 )

こんばんは。

前回の記事では今回はローカル関数間のポインタ渡しをやる予定でしたが、その前に抑えておきたいことが出てきたのでご報告します。
今さらですが、配列宣言のメモリ確保で新たな事実が分かってきたのです。

Arduino UNO や ESP-WROOM-02 ( ESP8266 ) のSRAM で、実際にユーザーが使える記憶領域が、表示されている容量よりも意外に少なかったということです。
これを知らないで、大量の文字列処理のプログラムを組んでいて、予期せぬエラーに悩まされていた方は、これでもしかしたら解決するかも知れません。

ということで、今回はArduino と ESP-WROOM-02 ( ESP8266 ) のSRAMメモリ容量や、コンパイル時の注意点を考察してみます。
以下、ビギナー向けの考察も含んでおりますが、ある程度熟練した方々でも意外な盲点があるかも知れません。

また、何度も言いますが、私はC/C++言語やArduino 言語は全て独学ですので、基本的にアマチュア対象で記事を書いております。
もし誤ったことを書いていたら、コメント等でご連絡いただけると助かります。
以下の記事は予想も含んでおりますので、予めご了承ください。

Arduino IDE を使って考察したボードは以下の2つです。

Arduino UNO

Arduino Uno Rev3 A000066
Arduino (アルドゥイーノ)
¥4,415(2024/10/05 02:35時点)

ESP-WROOM-02 ( ESP8266 )

私はESP-WROOM-02 を単体で使うよりも、動作が安定しているスイッチサイエンス製のESPr Developer ( ESP-WROOM-02 開発ボード )をお勧めしています。

ESP-WROOM-02開発ボード
スイッチサイエンス(Switch Science)

ESPr Developer(ピンソケット実装済)
スイッチサイエンス(Switch Science)

この使い方は以下のページを参照してください。

ESPr Developer ( ESP-WROOM-02 開発ボード )の使い方をザッと紹介

スポンサーリンク

    【目次】

  1. ESPr Developer ( ESP-WROOM-02, ESP8266 ) の初回コンパイルは何故か多量の警告が出ていた?
  2. グローバル変数領域での配列宣言の文字数限界
  3. ローカル関数内での配列宣言の落とし穴
  4. ローカル関数内の String クラスの最大限界文字数
  5. まとめ

ESPr Developer ( ESP-WROOM-02, ESP8266 ) の初回コンパイルは何故か多量の警告が出ていた?

まず、今さら気付いたのですが、面白いことがまた判明しましたので、それを紹介します。

まず、Arduino IDE を使う上で、予期せぬ結果やエラーに見舞われたら、コンパイラ側で詳細な警告内容を見ることで解決する場合があります。
前回の記事でtwitterの北二十四条低音組合さんから教わった、Arduino IDE の環境設定で、コンパイラの警告表示を「全て」にしておくと、コンパイルは通っても、警告が出ているものを見ることが出来ます。
これは便利なので、是非使ってみてください。

ただ、ESP-WROOM-02 ( ESP8266 ) を使う場合には気を付けなければいけないことがあります。

まず、ESPr Developer ( ESP-WROOM-02, ESP8266 ) とパソコンを接続し、以下のようなセットアップ関数とメインloop 関数だけの空のスケッチをコンパイル書き込みしてみてください。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

void setup() {

}

void loop() {

}

すると、コンパイラの警告がズラーっと表示されますが、エラーにならずにコンパイル終了します。
その警告のあまりの多さにこのブログでは載せることができません。
なんじゃこりゃ!! 状態です。
ほとんどESP8266ボードのcore 部分の警告です。
これは、Arduino UNO では表示されません。

これはまだ Arduino core for ESP8266 WiFi chip が未完成なのかな・・・? と思ってしまいます。

実は、これ、USBケーブルを外さず、スケッチを変えずに、もう一度コンパイル書き込みを行うと、次からは2度と警告が出ません。
私はまだコンパイラについてはサッパリ分からない状態で、これが何を意味するのかも解明できていませんが、このことを頭に入れていないと、1回コンパイルしただけで、多量の警告の解決の為に無駄な時間を費やしてしまうことになりますので、皆さん十分ご注意ください。

グローバル変数領域での配列宣言の文字数限界

Arduino や ESP-WROOM-02 ( ESP8266 ) で電子工作および IoT を実現しようとすると、文字列処理が必要なことが多々あります。
String クラスは取扱いが楽なのですが、メモリを多く消費してしまうため、文字列処理はできるだけ char型配列をうまく使っ方が良いわけです。
では、そのchar型配列に的を絞って、SRAM メモリの消費状況を考察してみたいと思います。

まず、グローバル変数について考えてみたいと思います。
グローバル変数は、全ての関数間で共通して使用したり、代入できる便利な変数ですが、これには欠点もあります。
それは、プログラム全てを通して、そのメモリが解放されずに、確保されたままになってしまいます。
ローカル関数内で宣言した場合は、ローカル関数内だけそのメモリ領域が有効で、関数を抜けてしまうと、そのメモリは解放されます。

また、プログラムが大きくなり、ローカル関数が多くなると、グローバル変数がどこで使われているかが分かりにくくなってしまいます。

というわけで、Arduino UNO のような少ないメモリの場合は、極力グローバル変数を使わないか、または割り当てるメモリを少なくした方が良いわけです。

では、このグローバル変数はどれだけのメモリを消費しているのでしょうか?

Arduino UNO の場合

まず、Arduino 言語で絶対必要な、setup関数と、loop関数だけの以下のような空のスケッチをコンパイルしてみてください。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

void setup() {

}

void loop() {

}

すると、コンパイラのメッセージ欄で以下のように表示されます。

最大32256バイトのフラッシュメモリのうち、スケッチが444バイト(1%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が9バイト(0%)を使っていて、ローカル変数で2039バイト使うことができます。

このメッセージでは、Arduino UNO のSRAMメモリ 2KB を存分に使えそうな気がしてきます。

では、グローバル変数領域、つまり、関数の外側でchar 型配列を宣言し、1000文字を予め確保する場合を考えてみます。

例えば以下のスケッチは、ローカル関数内で初期化した文字配列をグローバル変数のchar型配列に代入してシリアルモニターに表示させるものですが、これを Arduino UNO でコンパイルしてみて下さい。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

// Arduino UNO でコンパイルした場合
char ca[1000];

void setup() {
  delay(1000);
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println();

  c_test_func();

  Serial.print("ca = "); Serial.println( ca );
}

void loop() {
}

void c_test_func(){
  char cstr[] = "abcdefghijklmnopqrstuvwxyz";
  
  for(int i=0; i<27; i++){
    ca[i] = cstr[i];
  }
}

Arduino UNO の場合、コンパイルは正常終了して、シリアルモニターには以下のように正常に表示されます。
(※右下隅を 115200 bps にすることを忘れずに)

また、コンパイラのメッセージは以下のように出ます。

最大2048バイトのRAMのうち、グローバル変数が1218バイト(59%)を使っていて、ローカル変数で830バイト使うことができます。

これで分かる通り、グローバル変数の配列 ca で1000文字の領域を確保したということは、ここだけで 1000 byte 使っていることになります。
それにプラスした 218バイトは、その他の SRAM 消費分です。
このことから、スケッチの書き方によっては、配列で確保できる文字数も少なくなってくるというわけです。

私は、Arduino UNOで多くのプログラムを実行してきましたが、70%を超えると動作が不安定になってくるという経験がありました。
とりあえず正しくシリアルモニターに表示されていますが、このスケッチはもう既にギリギリというわけです。

ESP-WROOM-02 ( ESP8266 ) の場合はメモリが圧倒的に大きいので、この程度ではあまり問題になりませんが、それでも塵が積もると山になります。

では、試しに、1行目を

char ca[2000];

として、コンパイルしてみてください。

結果、コンパイルエラーとなり、以下のようなメッセージになります。

最大32256バイトのフラッシュメモリのうち、スケッチが1706バイト(5%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が2218バイト(108%)を使っていて、ローカル変数で-170バイト使うことができます。
スケッチが使用するメモリが足りません。メモリを節約する方法については、以下のURLのページを参照してください。http://www.arduino.cc/en/Guide/Troubleshooting#size
ボードArduino/Genuino Unoに対するコンパイル時にエラーが発生しました。

これはArduino UNO の SRAM 領域を超えて2000文字 ( 2000 byte ) 確保しようとしたらエラーが出るのは当たり前ですね。
では、何文字までOKなのでしょうか?

私がいろいろ探った結果、

char ca[1798];

とすれば、シリアルモニターに

ca = abcdefghijklmnopqrstuvwxyz

と正常に表示されました。
これは、同じArduino UNO でも微妙に異なるかも知れませんので、皆さん探ってみてください。

この場合のコンパイラメッセージは

最大32256バイトのフラッシュメモリのうち、スケッチが1706バイト(5%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が2016バイト(98%)を使っていて、ローカル変数で32バイト使うことができます。
スケッチが使用できるメモリが少なくなっています。動作が不安定になる可能性があります。

となります。
赤文字のところは警告メッセージですね。
シリアルモニターで正常に表示されても、警告が出てしまってはいけません。

試しに、

char ca[1799];

とすると、シリアルモニターにはこんな風に表示されます。

メモリ領域を食ってしまって、一文字だけ文字化けしているのがわかると思います。
つまり、こうなってしまったら、プログラムとして完全に欠陥となってしまうので使えません。

恐らく、ビギナーの方はコンパイラメッセージで RAM が 100% いかなければ正常に動くだろうと思っている場合が多いと思います。
ですが、Arduino UNO の場合、RAM の70~80% 以上使っていくと、上記のように確保したメモリ領域を侵してしまい、動作が不安定になって来るということは頭に入れておいた方が良いですね。
もちろん、スケッチの書き方によって変わって来ますので、その辺をよく考えてプログラミングしていかなければなりません。

ESPr Developer ( ESP-WROOM-02, ESP8266 ) の場合

では、ESPr Developer ( ESP-WROOM-02, ESP8266 ) の場合はどうなるでしょうか?

まず、setup と loop のみの空のスケッチをコンパイルしてみてください。
最初に述べたように、環境設定のコンパイルの警告表示を「全て」にしておくと、プログラムに誤りが無いはずなのに、ズラーっと警告表示が出てきます。
これは初回だけなので、もう一度コンパイルしてください。

すると、メッセージはこのように表示されます。

最大1044464バイトのフラッシュメモリのうち、スケッチが222043バイト(21%)を使っています。
最大81920バイトのRAMのうち、グローバル変数が31568バイト(38%)を使っていて、ローカル変数で50352バイト使うことができます。

空のスケッチで既に38% も使っているということは、裏でWi-Fi関係などの相当なプログラムが動いているのだろうと思われます。
これは仕方ないですね。
つまり、ユーザーが使える RAM サイズは
81920 – 31568 = 50352 byte
となります。
これは前回の記事でも述べた通り、データシートの仕様通り、約50 KB ですね。
(実は、後半で述べますが、ここにも意外な事実があったりします。)

では、Arduino UNO と同じように、グローバル変数を50KB の 50% の 25000 文字として宣言してみましょう。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

// ESPr Developer ( ESP-WROOM-02, ESP8266 ) でコンパイルした場合
char ca[25000];

void setup() {
  delay(1000);
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println();

  c_test_func();

  Serial.print("ca = "); Serial.println( ca );
}

void loop() {
}

void c_test_func(){
  char cstr[] = "abcdefghijklmnopqrstuvwxyz";
  
  for(int i=0; i<27; i++){
    ca[i] = cstr[i];
  }
}

シリアルモニターには以下のように正常に文字列を表示してくれます。

メッセージは以下のように表示されます。

最大1044464バイトのフラッシュメモリのうち、スケッチが224369バイト(21%)を使っています。最大81920バイトのRAMのうち、グローバル変数が56744バイト(69%)を使っていて、ローカル変数で25176バイト使うことができます。

これは全く問題無いですね。
では、正常に表示できる、グローバル変数領域はいったい何文字まで確保できるのでしょうか?

試した結果、ギリギリの限界の文字数は、

char ca[48412];

でした。
ただ、これは個体差があるかも知れませんので、各々試してみて下さい。

この場合は、コンパイルも通って、正常に表示されますが、以下のように警告表示が出ます。

最大1044464バイトのフラッシュメモリのうち、スケッチが224369バイト(21%)を使っています。最大81920バイトのRAMのうち、グローバル変数が80152バイト(97%)を使っていて、ローカル変数で1768バイト使うことができます。
スケッチが使用できるメモリが少なくなっています。動作が不安定になる可能性があります。

それで、これより1文字でも大きく設定すると、どうなるでしょうか?
すると、Arduino UNO と違って、シリアルモニターでは以下のようなメッセージが出て、延々とリセットを繰り返します。

ets Jan  8 2013,rst cause:2, boot mode:(3,6)

load 0x4010f000, len 1384, room 16
tail 8
chksum 0x2d
csum 0x2d
v09f0c112
~ld

これは、私が何度も遭遇した、ESP-WROOM-02 ( ESP8266 ) のエラーです。

ESP8266 ボードの場合、Arduino UNO とはコンパイラやその構成が異なりますので、当然結果も異なります。
Arduino UNO の場合は1文字だけ文字化けするという結果でしたが、ESP8266 の場合は1文字でも多くなると確実にビギナーでも分かるエラーとなります。
Arduino UNO のような中途半端なエラーになるよりは、私個人的には確実にエラーになってもらいたいですね。

以上から、グローバル変数領域の SRAM メモリは、Arduino および ESPr Developer ( ESP-WROOM-02, ESP8266 ) も同様に約97% くらいは使用できるようですが、次に紹介するのはもっと特異です。
ビギナーに関わらず、ある程度プログラミングに慣れた方でもウッカリ陥る罠ですのでそれを紹介します。

ローカル関数内での配列宣言の落とし穴

では、ローカル関数内で配列領域を確保した場合、どうなるでしょうか?
実は、これから紹介することは、お恥ずかしながら、私が今さら気付いたことなんです。

Arduino UNO の場合

では、まず、以下のスケッチの様に、ローカル関数内で配列を宣言し、それに文字配列を代入してシリアルモニターに表示させるというプログラムを考えてみます。

Arduino UNO でコンパイルしてみてください。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

// Arduino UNO でコンパイルした場合

void setup() {
  delay(1000);
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println();

  c_test_func();
}

void loop() {
}

void c_test_func(){
  char cstr1[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  char cstr2[] = "I have a pen. I have a apple. Uh... Apple Pen.";

  char c1[900], c2[900];

  for(uint16_t i=0; i<sizeof(cstr1); i++){ //変数i を int で初期化すると警告が出るので注意
    c1[i] = cstr1[i];
  }  
  
  for(uint16_t ii=0; ii<sizeof(cstr2); ii++){
    c2[ii] = cstr2[ii];
  } 

  Serial.print("c1 = "); Serial.println(c1);
  Serial.print("c2 = "); Serial.println(c2);
}

21行目でc1, c2 それぞれ900文字で合計1800文字の領域を確保してます。

Arduino IDE のコンパイラのメッセージは以下のようになります。

最大32256バイトのフラッシュメモリのうち、スケッチが1902バイト(5%)を使っています。
最大2048バイトのRAMのうち、グローバル変数が298バイト(14%)を使っていて、ローカル変数で1750バイト使うことができます。

これは驚くことに、警告は一切出ないのに、シリアルモニターには何も表示されません。
これはビギナーだったらば、「なんじゃこりゃ!」ってなりますよね。
メモリにも余裕があるのに、何で??? と考え込んでしまいます。

実はこれ、ローカル関数内の21行目の配列宣言を801文字以下にすると正しく表示されるので、それで分かると思います。

つまり、ローカル関数内で宣言しても、そこで900文字×2倍の領域を確保するということは、
1800 byte = 1.8KB
となり、90%もRAM を使ってしまうことになります。
ローカル関数内とはいえ、これでは他のプログラム動作に支障をきたしてしまい、まともにシリアルモニタには表示されません。

これを例えば、c1, c2 とも802文字で宣言すると、シリアルモニターではこうなります。
(※Arduino UNO の個体差があるかも知れません)

1文字だけ文字化けしているということは、配列の文字数が多すぎて、RAMメモリの限界を超えてしまっているので、文字列領域のメモリまで浸食してしまっているということだろうと思います。
結果、このプログラムでは、ローカル関数内の配列宣言の文字数は合計で1600文字が限界のようです。
もちろん、プログラムが変わればまた使用できるメモリ容量は変わってくるわけです。

以上から、Arduino UNOでは、何も警告表示が出てなく、グローバル変数領域も圧倒的に余裕がある表示が出ていても、ローカル関数内で配列領域を多く確保し過ぎてしまうと、予期せぬバグや文字化けが現れて、まともにプログラムが動かないということです。
これはしっかり頭に入れてプログラミングしていきたいですね。

ESPr Developer ( ESP-WROOM-02, ESP8266 ) の場合

では、Arduino UNO と異なり、ESPr Developer ( ESP-WROOM-02, ESP8266 ) の場合はもっと注意しなければなりません。

先ほどのスケッチの21行目を、ESP8266 のSRAM 50KB の約80%使って

char c1[20000], c2[20000];

としてコンパイルした場合、どうなるでしょうか?

例のごとく、コンパイルを2回行った結果、警告表示は一切表示されませんでした。
ただ、グローバル変数は38% しか使っていないのに、シリアルモニターには何も表示されず、ちょっと時間をおいて、以下のエラーを吐き出し、延々と繰り返しました。

ets Jan  8 2013,rst cause:4, boot mode:(3,6)

wdt reset
load 0x4010f000, len 1384, room 16
tail 8
chksum 0x2d
csum 0x2d
v09f0c112
~ld

このメッセージも私は他のプログラミングで何度も遭遇しました。
wdt というのは、ウォッチドッグタイマが作動できなかったというエラーです。
(関連記事)
ESP8266 ( ESP-WROOM-02 ) SPI 通信高速化、その2 ( 複数デバイス、Mode、Watchdog Timer 問題 )

と、いうことは、ESP-WROOM-02 ( ESP8266 ) のSRAM はArduino UNO とはちょっと異なり、もっと余裕を見なければいけないかもしれません。

そして、何回も文字数を変えて試してみて、ようやく境界を見つけました。

文字数を以下のように宣言すると、エラーが出なくなり、安定して正しく表示されました。

char c1[12054], c2[12054];

シリアルモニターの結果は正常にこのように表示されます。

ただ、c1, c2 どちらかでも数値を1つ増やすだけで、コンパイラの警告は一切無いのに、シリアルモニターでは以下のような例外エラーになり、

Exception (9):

epc1=0x40104278 epc2=0x00000000 epc3=0x00000000 excvaddr=0x00050a26 depc=0x00000000

ctx: sys
sp: 3ffffbb0 end: 3fffffb0 offset: 01a0

>>>stack>>>
3ffffd50:  5c5c5c5c 5c5c5c5c 5c5c5c5c 5c5c5c5c
・・・etc

その後は延々と

ets Jan  8 2013,rst cause:2, boot mode:(3,7)

load 0x4010f000, len 1384, room 16
tail 8
chksum 0x2d
csum 0x2d
v09f0c112
~ld

というエラーを繰り返します。
そういえば、こんなエラーもよく遭遇して、原因が分からなかったものでした。

でも、これ、おかしいと思いませんか???

つまり、これは、合計で24108 byte しか使うことができないのです。
先にも述べたように、ESP-WROOM-02 ( ESP8266 )のスペックでは、約50KBのRAMを使用できるはずですよね。
それなのに、ローカル関数内で配列宣言したら、メモリ領域は半分以下の48% しか使うことができないんです。

試しに、c1, c2 をグローバル変数領域で宣言すると、

char c1[24149], c2[24148];

まで設定できました。
合計で、48297 byte まで宣言できます。

つまり、ローカル関数内では、グローバル変数領域よりも更に半分以下しかRAMメモリを確保できないということです。
これではビギナーの方々ならば ??? となってしまいますよね。
いくらなんでもユーザー使用領域が少なすぎませんか?

残念ながら、今の私には詳しく調べている時間が無く、事実の報告だけで申し訳ないのですが、おそらく ESP8266 のメモリ割り当ての仕様でそういう設定にされているからだと思われます。
もし、何か間違えていたらコメント等で教えていただけたら助かります。

しかし、お恥ずかしながら、今までずっとこのデバイスを使っていて、今更これに気付きました。
これでは、ESPr Developer  ( ESP-WROOM-02, ESP8266 ) でプログラミングエラーに遭遇して、原因がなかなか分からないということが起こるわけですよね。
これには参りました・・・。

でも、Wi-Fi デバイスですから、それだけバックグラウンドでいろいろ動いているっていうことなんでしょう。

ローカル関数内の String クラスの最大限界文字数

では、String型変数には、ローカル関数内で何文字まで入れ込めるのでしょうか?
Stringクラスは、C言語のchar型とは異なります。
これはどの様な規格で作られたクラスなのかは、いろいろ調べてみたのですが、正直まだ分かりません。
Arduino IDE にはそれ専用のStringクラスが作られているようで、IDE がバージョンアップすると、そのクラスの仕様や関数が更新されたりしているようです。
ただ、C言語の文字列上のややこしいルールは知らなくても、手軽に扱えるツールなので、これのメモリ最大容量も押さえておきたいところです。

Arduino UNO の場合

では、Arduino UNO の場合、ローカル関数内で String型文字列は最大何文字入れ込めるのでしょうか?
以下のスケッチをコンパイルしてみてください。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

// Arduino UNO でコンパイルした場合

void setup() {
  delay(1000);
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println();
  
  String str;
  for( uint16_t i=0; i<2000; i++ ){
    str += 'a';
  }
  str += '\0';

  Serial.println( str );
  Serial.println( str.length() );
}

void loop() {
}

Arduino UNO のRAM は2KB ですから、12行目で絶対にエラーになると思われる文字数を2000文字としてみました。
シリアルモニターの結果は以下のようになります。

なんと、2000文字を代入するはずが、文字数が 1644 となっています。
しかも、エラーや警告も一切出てなく、正常にコンパイルも通っています。
12行目を3000としても同じでした。

では、試しに、12行目を 1642 とすると、ヌル終端文字 ‘\0’ も含めて、文字数は 1643 となりました。

これから分かる通り、おそらく Stringクラスは、アマチュアやビギナーがとんでもない文字数を割り当てても、限られたRAM メモリ中で最適な数量に割り当てて、後は捨てているのではないかと思います。
これも、簡単に扱えるようにした Arduino のコンセプトなのでしょうか・・・?

ESPr Developer ( ESP-WROOM-02, ESP8266 ) の場合

では、ESPr Developer ( ESP-WROOM-02, ESP8266 ) の場合の Stringクラスを検証してみます。
以下のスケッチをコンパイルしてみてください。
【ソースコード】 (※無保証 ※PCの場合、ダブルクリックすればコード全体を選択できます)

// ESPr Developer ( ESP-WROOM-02, ESP8266 )でコンパイルした場合

void setup() {
  delay(1000);
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
  Serial.println();
  
  String str;
  for( uint16_t i=0; i<25145; i++ ){
    str += 'a';
  }
  str += '\0';

  Serial.println( str );
  Serial.println( str.length() );
}

void loop() {
}

これは個体差があるかも知れませんので、いろいろ試してください。
私の手持ちの ESPr Developer では、12行目の数値は 25145 が限界で、それよりも1文字でも多くすると、エラーになります。
以下は、12行目を 25146 とした場合のシリアルモニターの結果です。

以下のメッセージが表示されて、1回だけリセットされました。

Soft WDT reset

ctx: cont 
sp: 3ffef1e0 end: 3ffef3c0 offset: 01b0

>>>stack>>>
3ffef390:  00000000 00000000 00000001 402022c9  
3ffef3a0:  3fffdad0 00000000 3ffee384 402022fa  
3ffef3b0:  feefeffe feefeffe 3ffee3a0 40100114  
>>>stack>>>

 ets Jan  8 2013,rst cause:2, boot mode:(3,7)

load 0x4010f000, len 1384, room 16 
tail 8
chksum 0x2d
csum 0x2d
v09f0c112
~ld

今度は単なる wdt だけではなく、Soft WDT となりますね。
これは、ソフトウェアの原因によって、ウォッチドッグタイマが動作できなかったことによるエラーのようです。
(WDT関連記事)
ESP8266 ( ESP-WROOM-02 ) SPI 通信高速化、その2 ( 複数デバイス、Mode、Watchdog Timer 問題 )

ということは、ESPr Developer ( ESP-WROOM-02, ESP8266 ) のStringクラスは、Arduino UNO と違った振る舞いをするということを頭にいれておかなければなりません。
良く分からないのですが、Stringクラスは、おそらくコンパイラ依存の関数なんでしょうか・・・。

それともう一つ、ESPr Developer ( ESP-WROOM-02, ESP8266 )では、char型配列でも、String型でも、ローカル関数内の最大文字数の限界はほぼ同じで、約25KB ということが分かりました。

ちょっとこれは残念・・・。

まとめ

以上、ですが、Arduino UNO のメモリ構造については garretlabさんのWEBページの以下の記事に詳しく紹介されていました。

Arduino Unoのメモリ↓
http://garretlab.web.fc2.com/arduino/introduction/memory/

最初は小難しいなぁと思って読むのをパスしていましたが、ある程度熟練してくると、なるほどな~、と納得してしまいますね。
とてもよくまとめられていて、分かりやすいです。
私もまだまだ勉強不足でした。

以上、Arduino および ESPr Developer ( ESP-WROOM-02, ESP8266 ) の RAM メモリ使用や、コンパイルで気を付けなければいけないことをまとめると、

  • コンパイルエラーが出なくても、警告メッセージが出ている場合がある。
    それを見るには、環境設定のコンパイルの警告表示を「全て」にする。
  • ESPr Developer ( ESP-WROOM-02, ESP8266 ) は初回コンパイル時は多量の警告メッセージが出ている。
    2回目以降は出ない。
  • ESPr Developer ( ESP-WROOM-02, ESP8266 ) のローカル関数内の配列宣言では、確保できる領域は意外と少ない。
  • 配列の領域確保が大きすぎた場合、Arduino UNO ではシリアルモニターで文字化けだけの場合があり、 ESPr Developer ( ESP-WROOM-02, ESP8266 )の場合はエラーメッセージを延々と繰り返す。

ということです。

これをよくよく頭に入れてプログラミングしていかないと、思わぬエラーに頭を悩ませ、その原因追及に無駄な時間を費やしてしまいますので十分ご注意ください。
私はこれでどれだけの時間を無駄にしてしまった事か・・・

ということで、もし誤ったことを書いていたらコメント等でご連絡いただけると助かります。
次回こそは関数間のポインタや配列の受け渡しについて考察してみたいと思います。
ではまた・・・。

Amazon.co.jp 当ブログのおすすめ

スイッチサイエンス ESPr Developer 32 Type-C SSCI-063647
スイッチサイエンス
¥2,420(2024/10/04 14:31時点)
ZEROPLUS ロジックアナライザ LAP-C(16032)
ZEROPLUS
¥19,358(2024/10/05 00:18時点)
Excelでわかるディープラーニング超入門
技術評論社
¥2,068(2024/10/05 09:07時点)

コメント

  1. M より:

    グローバル変数とローカル変数は使う領域が違うため、許容サイズが違っても不思議はありません。一般に、ローカル(スタック)領域のほうが狭いですし、関数コールによって許容サイズも変わります。

    • mgo-tec mgo-tec より:

      Mさん

      記事をご覧いただき、そしてコメントいただき有難うございます。

      Mさんのおっしゃる通りのようです。

      この記事を書いた当初は私自身、良く分かっていませんでした。
      手軽な Arduino IDE だけでコーディングしていると、こういう知識をスルーしてしまいますね。

タイトルとURLをコピーしました