下記の記事で、ソーラーパネルの出力電力を長期間記録できるデータロガーを製作したが、OLEDディスプレイに測定値を表示するコードと、microSDカードに測定値を記録するコードは、それぞれ正常に動作するのに、OLEDディスプレイへの表示と、microSDカードへの記録の両方を行うコードは正常に動作しなかった。その後、改良を行い、両方の機能を1つのコードで実現することができたので報告する。
両方の機能を1つのコードで実現することができなかった原因について、ChatGPT 4oとPerplexity Proに質問したところ、原因の可能性がある事項として下記の内容などが回答された。
・SPIとI2Cの競合
SDカードモジュールはSPIを使用し、OLEDディスプレイはI2Cを使用しています。
これらの通信プロトコルが適切に管理されていないと、競合が発生する可能性があります。
・電源供給の問題
OLEDディスプレイが十分な電力を受け取っていない、または電圧が不安定。
・プルアップ抵抗の不足
I2Cバスに必要なプルアップ抵抗が不足している。
・メモリ不足
Arduino UnoやNanoなどのマイコンボードでは、OLEDライブラリとSDカードライブラリを同時に使用すると、
RAMが不足する可能性があります。
・ピンの競合
SPIピンとI2Cピンが重複している可能性があります。-
・Wire.begin()
を setup()
関数内で呼び出していないWire.begin()
はI2Cバスを初期化するために必要で、I2Cデバイス(OLEDディスプレイやRTCモジュール)を使用する場合は必須です。
OLEDディスプレイに表示するコードと、microSDカードに記録するコードは、それぞれ単独で正常動作しているので、「電源供給の問題」や「プルアップ抵抗の不足」という原因は除外してよさようである。
下記のような対策も回答された。
・初期化順序の変更
SDカードの初期化を先に行い、その後OLEDディスプレイを初期化してみてください。
・ハードウェアSPIの使用
可能であれば、SDカードモジュールにハードウェアSPIを使用し、OLEDディスプレイにはソフトウェアI2Cを使用することで、競合を減らせる可能性があります
・ライブラリの最適化
U8G2ライブラリなど、メモリ使用量が少ないOLEDドライバーライブラリを使用することを検討してください。
また、Perplexity Proの回答のリンク先には、下記の投稿(質問)が掲載されていた。
・投稿タイトル:I2C and SPI conflict(Arduino.ccのForum)
(要約)
AdafruitのI2CバックパックをSPIモードで使用してLCDパネルを駆動しています。また、SDA/SCLに接続されたI2Cデバイス、デフォルトアドレス0x68であるDS1307 RTCモジュールも使用しています。RTCはRTClibを使用して完璧に動作します。問題は、コードが結合されたときです。RTC は 時刻を正しく報告するが、LCD は空白のまま。
(投稿に対する回答の一部)
MEGAにしたら正常に動作した。
・質問タイトル:OLED (I2C) and Micro SD card module not working together in Arduino(tackoverflow.comサイトでの質問)
(要約)
SD カードと OLED は別々に動作しますが、組み合わせると相互に干渉するようです。私は Arduino SD と Adafruit OLED ライブラリを使用しました。さらに閲覧して実験してみると、SD の MISO または MOSI ピンが OLED の SDA/SCL ピンに干渉している可能性があることがわかりました。
(質問に対する回答の一部)
・2kB の RAM を搭載した Arduino Uno では問題が発生する可能性があります。個々のコードと結合されたコードの両方の RAM 要件を確認してください。OLED ライブラリは 1024 バイトのメモリを動的に割り当てます。ローカル変数用に 745 バイトが残っているということですが、それでは不十分です。
・両方のライブラリを合わせると、RAM (メインメモリ) が不足します。
以上の調査結果から、Arduino UnoからArduino MEGA(正確には安価なMEGA互換ボード)に変更することにした。
Arduino Unoでは色々なエラーが出たが、その中には、「コードが大きいため書き込めない」というものもあり、ChatGPT 4oにコードのサイズを小さくするように依頼したこともあった。また、さらに拡張することを考慮してこの変更を行うことにした。
ChatGPT 4oにArduino UnoとArduino MEGAの基本仕様の比較表を作ってもらった。使わない端子が大量に増えるが、メモリも増える。
項目 | Arduino Uno | Arduino MEGA(互換ボード) |
---|
マイクロコントローラ | ATmega328P | ATmega2560 |
デジタルI/Oピン数 | 14 (うちPWM出力6ピン) | 54 (うちPWM出力15ピン) |
アナログ入力ピン数 | 6 | 16 |
PWMピン数 | 6 | 15 |
フラッシュメモリ | 32 KB (0.5 KBはブートローダー用) | 256 KB (8 KBはブートローダー用) |
SRAM | 2 KB | 8 KB |
EEPROM | 1 KB | 4 KB |
動作電圧 | 5 V | 5 V |
入力電圧 (推奨) | 7-12 V | 7-12 V |
クロック周波数 | 16 MHz | 16 MHz |
UART (シリアル通信) | 1 | 4 |
SPIピン数 | 1セット | 1セット |
I2Cピン数 | 1セット | 1セット |
サイズ | 約68.6 mm × 53.4 mm | 約101.52 mm × 53.3 mm |
価格 | 比較的安価 | 若干高価 |
また、Arduino UnoとArduino MEGAにおけるSPI通信関連およびI2C通信関連の端子の比較表を作ってもらった。
通信方式 | Arduino Uno | Arduino MEGA |
---|---|---|
SPI通信 | ||
MISO | ピン12 | ピン50 |
MOSI | ピン11 | ピン51 |
SCK | ピン13 | ピン52 |
SS (スレーブ選択) | 通常はピン10(手動で変更可能) | 通常はピン53(手動でピン10に変更可能) |
SPI専用端子 | ICSPヘッダー(MISO, MOSI, SCKが含まれる) | ICSPヘッダー(MISO, MOSI, SCKが含まれる) |
I2C通信 | ||
SDA (データライン) | ピンA4 | ピン20 |
SCL (クロックライン) | ピンA5 | ピン21 |
上記比較表により、SPI通信の端子(MISO、MOSI、SCK)と、I2C通信の端子(SDA、SCL)を変更するだけでボードを変更できることが分かった。
下記に改良した回路を示す。ただし、下記回路は作成途中のものであり最終構成とは異なる。
Arduino UnoをArduino MEGA(互換ボード)に変更した装置は下図の通り。OLEDディスプレイの表示と、microSDカードへの書き込みを1つのコードで実現できた。
OLEDディスプレイの表示は下記の通り(温度は室温)。
コードをMEGA互換ボードに書き込んだところ、Arduino IDEの「出力」には下記の表示が現れた。
Arduino Unoの場合は、80%とか90%とかの値が出ていたように思う。だいぶメモリに余裕ができた。
ハードウェアの変更は完了したので、microSDカードに記録されたデータを確認したところ、記録された時刻が1秒間隔ではなく、2秒毎に記録されることが多く不安定な状態だったので、ChatGPT 4oに解決する方法を質問した。
提案されたコードは、正確に1秒毎に計測を行いますか
提案されたコードは、正確に1秒毎に計測を行う設計ではありますが、実際には以下の理由で正確に1秒とはならない場合があります:
1. 処理時間の影響loop()
内では、以下のような処理が行われます:
RTCからの時刻取得。
温度、電圧、電流の測定と計算。
SDカードへのデータ書き込み。
OLEDディスプレイへの描画。
これらの処理にかかる時間がdelay(1000)
に追加されるため、実際のループ時間は1秒を超える可能性があります。
2. SDカードへの書き込み時間
SDカードへのデータ書き込み (dataFile.close()
など) は、特にカードの速度やファイルシステムの状態によって遅延が発生することがあります。
3. I2C通信の遅延
RTCモジュール、温度センサー、OLEDディスプレイはすべてI2C通信を使用します。I2C通信には若干の遅延があり、これも正確な1秒の計測を妨げる可能性があります。
解決方法
以下の方法で、正確に1秒間隔での計測を行うように調整できます。
ミリ秒単位の時間管理を使用するmillis()
関数を使って、次の計測タイミングを管理します。以下はコードの一部を修正した例です:
(修正されたコードの表示省略)
このコードの特徴millis()
で経過時間を管理するため、処理時間の影響を最小化できます。
処理に時間がかかっても、次の測定は正確なタイミングで行われます。
これにより、正確な1秒間隔の計測を実現できます。問題があればさらに調整を行いますのでお知らせください!
修正前は、繰り返し処理にかかる時間とdelay(1000)にかかる時間の合計時間毎にmicroSDカードに記録されていたが、修正されたコードによって、microSDカードに記録された時刻は1秒毎の時刻となった。
「millis()を使用して正確に1秒ごとに処理を実行します」とのことですが、具体的には、どのような処理が行われるということですか
millis()
を使用することで、Arduinoがリセットされてから経過したミリ秒を取得できます。この値を利用して、一定の間隔で処理を実行することが可能です。
以下に、millis()
を使用して「正確に1秒ごとに処理を実行する」とは具体的にどのようなことかを説明します。
1. millis()の仕組みmillis()
は、Arduinoがリセットされてからの経過時間(ミリ秒)を整数型で返します。
例:
Arduino起動から5000ミリ秒後にmillis()
を呼び出すと、5000が返されます。
2. 1秒ごとに処理を実行するロジック
Arduinoの標準的なloop()
関数では、非常に高速で繰り返し処理が行われます。これではタイミングが不正確になりがちですが、millis()
を使用することで次のように一定の間隔で処理を実行できます。
(コード表示省略)
3. このコードの流れ
現在時刻の取得:millis()
で現在の経過時間を取得します(currentMillis
)。
前回の処理時間との差を計算:currentMillis - previousMillis
を計算し、指定した間隔(interval
)以上になったかを確認します。
処理の実行:
指定された間隔が経過していれば、処理を実行し、previousMillis
を現在の時刻に更新します。
間隔が経過していない場合はスキップ:
まだ間隔に満たない場合、loop()
の他の部分を実行し、再び次のチェックまで待機します。
4. millis()の利点
精度:delay()
を使う場合、処理中もArduinoが待機状態になるため、正確な間隔での処理が難しくなります。millis()
は非ブロッキングで処理できるため、時間管理が精密です。
並列処理:millis()
を使うと、1秒間隔で処理を行いながら、他のタスク(例: ボタンの入力やLED制御など)を並行して実行できます。
まとめ
「1秒ごとに処理を実行する」とは、millis()
を用いて1秒間隔を管理し、次のような一連の操作を一定間隔で繰り返し行うことを指します:
センサーの値を取得。
SDカードにデータを書き込み。
OLEDにデータを表示。
繰り返し処理にかかる時間も表示するようにChatGPT 4oに要求したところ、修正コードが示され、下図のようにパソコンのArduino IDEのシリアルモニタに1秒毎に処理時間が表示されるようになった。
間隔時間1秒に対して、まだ余裕がある処理時間。さらに照度測定などを追加しても、十分1秒以内には収まる。
(ChatGPT 4oから提案された追加質問)
SDカードの使用状況を分析できますか
SDカードの使用状況を分析するため、以下のようなポイントを考慮します:
1. データサイズの見積もり
1回の記録(CSV形式)でどれだけのデータが書き込まれるかを計算します。
列構成: 日付、時刻、温度、電圧、電流、電力。
1行のサイズ: 各値を10バイトと仮定し、区切り文字や改行を含めて約90バイトと見積もります。
例:
ログ間隔が1秒の場合:
1日のデータ量: 90バイト * 86,400秒 ≈ 7.8MB/日
ログ間隔が10秒の場合:
1日のデータ量: 90バイト * 8,640秒 ≈ 0.78MB/日
2. SDカード容量との比較
SDカードの容量を考慮して、データがどれだけ保存できるかを計算します。
例:
2GB(2,000MB)のSDカード:
ログ間隔1秒: 2,000MB / 7.8MB ≈ 256日間
。
ログ間隔10秒: 2,000MB / 0.78MB ≈ 約2,564日間
。
3. ファイルシステムの制約
SDカードのファイルシステム(FAT16やFAT32)によって以下の制限があります:
最大ファイルサイズ:
FAT16では1ファイル最大2GB。
FAT32では1ファイル最大4GB。
ファイル数:
ルートディレクトリでは最大512ファイル(FAT16の場合)。
対策:
日付ごとにファイルを分割(例: data_YYYYMMDD.csv
)。
ファイルサイズが大きくなりすぎないよう管理。
4. 書き込み速度の確認
データ書き込みが遅いとログ間隔に影響を与える可能性があります。
書き込み速度を測定するには、SDカードのdataFile.println()
処理の実行時間を測定します。
計測方法:
(コードの表示省略)
許容範囲:
書き込み速度がログ間隔より速い(例えば1秒未満)場合、問題なく動作します。
5. SDカードの寿命(書き込み回数の制限)
SDカードには書き込み回数の制限があります(一般的に10万回程度)。
書き込み頻度が高い場合はカードの寿命が短くなります。
対策:
ログ間隔を増やす(例: 1秒→10秒)。
書き込み時にデータを一時的にRAMに蓄積し、まとめて書き込む。
6. 効率的なデータ構造
CSV形式は可読性が高いですが、バイナリ形式で保存することで容量を節約できます。
例: フロート型データを直接バイト列として保存する。
必要情報
さらに具体的な分析を行うために、以下の情報を教えてください:
SDカードの容量(例: 2GB)。
ログ間隔(例: 1秒、10秒など)。
現在の1行あたりのデータサイズ(例: 90バイト程度)。
SDカードの書き込み速度や種類(例: Class 10)。
これらの情報をもとに、より精密な分析や改善提案を行います!
・実際は2秒ぐらいの間隔でデータを記録するのがよいように思われるので、この場合、1日のデータ量は約3.89MBであり、2GB(2,000MB)のSDカードには約514日間記録できることになる。
・長期間にわたって記録する場合、日付ごとにファイルを分割するようにコードをChatGPT 4oに要求予定。
・SDカードの書き込み回数の制限があるが、書き込み不良などが発生すれば対策を検討する。
ソーラーパネルの出力電圧をSDカードに1秒毎に記録する装置で、1回の書き込みは90バイトであるとして、何時間で寿命が来ると考えられますか。SDカードは2Gであるとします。
SDカードの寿命に関する計算を行います。
前提条件
1回の書き込みサイズ: 90バイト
書き込み頻度: 1秒に1回
SDカード容量: 2GB = ( 2*10^9 ) バイト
SDカードの書き込み寿命: 10万回(1セクタあたり)
計算
1秒間の書き込み量
90バイト/秒
1日(24時間)の書き込み回数
24*60*60 = 86,400 回
書き込み寿命に基づく時間
SDカードの書き込み寿命をセクタあたり10万回と仮定。1つのセクタ(通常512バイト)は寿命が尽きるまでに以下の時間を持つと考えられる。
書き込み回数: 10^5/86,400 = 1.16 日
1セクタあたり約28時間の寿命となります。
寿命を迎える時間
SDカードの全容量が満たされるまで、データはカード内の他のセクタに書き込まれます。
2GBのSDカードに全てのセクタが均等に書き込まれると仮定。
2*10^9/90 →約2.22*10^7 書き込み回数(全体)。
総寿命の時間:2.22 *10^7 /86,400 (約257日)
結果
2GBのSDカードで1秒ごとに90バイト書き込む場合、SDカード全体が寿命を迎えるまでの時間は約257日(約8.5ヶ月)です。
延命方法
ログ間隔の変更:
1秒→10秒にすれば寿命は約10倍(約7年)になります。
データのバッファリング:
RAMに1分(60秒分)蓄積してからまとめて書き込むと、書き込み回数を大幅に削減できます。
耐久性の高いSDカードの使用:
「High Endurance」SDカードを使用すると、書き込み寿命が数倍に向上します。
Arduino MEGAで、RAMに1分(60秒分)蓄積してからまとめて書き込む方法が使えますか
Arduino MEGAでRAMにデータを1分(60秒分)蓄積してからまとめてSDカードに書き込む方法は可能です。ただし、Arduino MEGAのRAM容量には制限があるため、以下の点を考慮する必要があります。
Arduino MEGAのRAM容量
SRAM(RAM)容量: 8 KB
このRAMはすべての動的データ(変数や配列など)に使用されます。
RAMの使用量の計算
1回のデータサイズ:
1回の書き込みデータサイズ: 90バイト
1分間(60秒分)のデータサイズ:
90バイト*60秒= 5400バイト
余裕を考慮:
Arduino MEGAのRAM(8 KB = 8192バイト)の一部は、システムや他の変数に使用されるため、RAMの半分(約4 KB)は余裕を見て確保する必要があります。
5400バイト(60秒分)はギリギリ収まりますが、慎重なメモリ管理が必要です。
実装可能な手法
1. データを配列に蓄積
データを配列に格納し、60秒後に一括してSDカードに書き込む方法。
(コードの表示省略)
注意点
RAMの制約:
5400バイトを確保する場合、他の変数やライブラリがRAMを使いすぎないよう注意してください。
SDカード書き込み時間:
データを書き込む間、システムが一時的に停止する可能性があります。書き込み中に他のタスクを実行する場合は、RTOS(リアルタイムOS)などの使用を検討してください。
エラーハンドリング:
SDカードが接続されていない場合やエラーが発生した場合に備えた処理を追加すると信頼性が向上します。
最適化のヒント
ログフォーマットの工夫:
90バイトが固定でない場合、フォーマットを見直してデータ量を削減可能。
耐久性の高いSDカードを使用:
「High Endurance」SDカードを使用することで、書き込み寿命が延びます。
これで1分間のデータ蓄積とまとめ書き込みが実現できます。必要に応じてメモリ使用量を調整してください!
・RAMに1分(60秒分)蓄積してからまとめて書き込む方法は、書き込み時に処理が遅くなるので、今回は実施しない。
・「High Endurance」SDカードを使用することとし、書き込みエラーが発生するなどの不具合が見られれば、対応を検討する。