● 実験テーマ173

「ESP32S3+SDカード+液晶+MP3デコーダIC MP3 Player製作記録 その5」

(スケッチ更新:① 曲数:16曲→ 256曲に拡張 ② リスト表示:1画面表示→ スクロール表示にて256曲max表示)

■ 2026.9.14
  ・Copilotに聞きながら作業を進めました。

<私>
 ・16曲maxのプレーヤープロジェクトはokとしました。
  ここで256曲maxに拡張したプロジェクトを開始しました。
  ファイル名バッファをconst char MAX_FILES = 16; // 最大、16ファイルから
  const int MAX_FILES = 256; // 最大、256ファイルに拡張しました。
  バッファ構成はchar fileNames[MAX_FILES][MAX_NAME_LEN];です。
  但し、const char MAX_NAME_LEN = 13; // 8.3形式 + 終端文字('\0')です。
  以下のようなスケッチです。

 ・今テストで使用中のSDカードには、mp3ファイルが170ファイル入っています。
  256に満たない場合は、有る分のファイル読込-> バッファへのコピーを行ってルートクローズする流れに
  なっているつもりでスケッチを作成しましたが、現状最初の16ファイルのファイル名リストは表示しますが
  while(file)ループを抜け出してないのか、抜けた後のカーソルマーク描画等の処理に移行してません。
  それを確かめる為に、以下のように抜け出した後に本当に来ているかモニタ出力で確認してみた。

while(file)
{
  ・
  ・
} // end while
root.close();
digitalWrite(SD_CS,HIGH);

/// シリアルモニタ
Serial.println("Root closed");
Serial.print("fileCount: ");
Serial.println(fileCount);
}

 ・やはりここには来てない。while(file)ループを抜け出せてない。
  arduinoでのファイル操作には不慣れなもので基本的なミスがないか等、アドバイスお願いします。

<Copilot>
 ・シリアルモニタに "Root closed" が出力されない原因は、
  MP3以外のファイル(非MP3ファイル)がループ内で処理されたときに、無限ループに陥る構造になっているためです。
  Arduino(ESP32)の openNextFile() を使用する際の典型的な落とし穴ですので、修正方法を分かりやすく解説します。

 ・原因の解説
  現在のスケッチの while(file) ループ内の構造を見ると、以下のようになっています。

if (nameStr.endsWith(".mp3") || nameStr.endsWith(".MP3")) {
// 1. 配列へのコピー処理
// ...
// 2. 次のファイルを読み込む処理(★ここが問題)
fileCount++;
if (fileCount < MAX_FILES) file = root.openNextFile();
else break;
}

 ・この構造だと、「見つかったファイルがMP3ファイルだった場合のみ」次のファイルを読み込む(root.openNextFile()) という動きになります。
  もし、SDカード内にシステムファイルや隠しファイル(例:System Volume Information ディレクトリや .DS_Store、画像ファイルなど)、
  あるいはMP3以外のファイルが1つでも混ざっていると、以下の現象が起きます。
   ① if (nameStr.endsWith(".mp3")) の条件に一致しないため、if文の中身(次のファイルを読み込む処理)が丸ごとスキップされる。
   ② 次のファイルを読み込まないため、変数 file の中身(非MP3ファイルの情報)が全く更新されないまま while(file) の先頭に戻る。
   ③ 再び同じ非MP3ファイルをチェックし、またスキップされる……という無限ループが発生し、プログラムがそこから先に進まなくなります。

 ・改善のポイント
   ① file.close() の追加: openNextFile() で開いたファイルは、次のファイルをオープンする前に毎回必ず .close() で閉じないと、
      ESP32のファイルハンドル(メモリ)が枯渇して動作が不安定になります。
   ② 進める処理を外側へ: file = root.openNextFile(); を if 文の外側(while の一番最後)に配置したことで、
      カード内のすべてのファイルを確実に1つずつスキャンできるようになり、無限ループが解消されます。

<私>
 ・使ってるSDカードにはbmpファイルが含まれていました。

<Copilot>
 ・そのBMPファイルを読み込んだ瞬間に「次のファイルをめくる処理」をスキップしてしまい、
  プログラムがずっとそのBMPファイルを見つめ続ける無限ループに陥っていたわけです。

  今回の原因が「ファイル名がmp3ではないファイル」だったと確定しましたので、
  先ほど紹介した「MP3であってもBMPであっても、
  ループの最後で必ず file.close() して root.openNextFile() で次のファイルに進める」という修正を行えば、100%確実に解決します!

● 上記を踏まえた修正案

<修正前:260912>
/**********************************************************
* SDカードのルートディレクトリにあるファイルを読込み
* 液晶の1画面のファイル名リストを表示する。
* 最初の16ファイル分の表示のみ行う関数
* スクロール表示後のリスト表示は随時、メインで別途行う。
***********************************************************/
void listRootFiles()
{
  /// ルートディレクトリを開く
  root = SD.open("/");

  if (!root) {
    Serial.println("ルートディレクトリが開けません"); // シリアルモニタに出力
    return;
  }
  if (!root.isDirectory()) {
    Serial.println("ディレクトリではありません"); // シリアルモニタに出力
    return;
  }

  /// ループで次々とファイルを取得
  File file = root.openNextFile();

  /// ファイルが見つかるまで読取ったファイル名をバッファに格納する(最大256ファイル)
  /// 256ファイルに満たない場合は、MP3ファイルが見付かった分繰り返す(戻り値のfileで判断)
  while(file) {
    /// ファイル名取得
    const char* filename = file.name();
    String nameStr = String(filename);

    //// 拡張子が ".mp3" 或いは ".MP3"かどうかをチェックし対象ファイルのみ以下を実行
    if (nameStr.endsWith(".mp3") || nameStr.endsWith(".MP3")) {
    /// 2次元配列へコピー・念のため終端文字を保証
    strncpy(fileNames[fileCount], filename, MAX_NAME_LEN-1);
    fileNames[fileCount][MAX_NAME_LEN - 1] = '\0';

    if (fileCount < 16) { // 液晶の1画面分のファイル名リストを表示する(最初の16ファイルのみ)
      /// ファイル名の文字フォントデータを液晶表示関数に渡しファイル名を表示
      digitalWrite(SD_CS,HIGH);
      digitalWrite(D_CS, LOW);
      LCD_DrawString(2, fileCount, fileNames[fileCount], WHITE, BLACK);
      digitalWrite(D_CS, HIGH);
      digitalWrite(SD_CS, LOW);
    }

    file.close(); // 260912

    // 次のファイルへ
    fileCount++;
    Serial.println(fileCount);
    if (fileCount < MAX_FILES) file = root.openNextFile();
    // if (fileCount < 170) file = root.openNextFile();
    else break; // 256ファイル分終了したらループブレークしクローズ
  } // end while
  root.close();
  digitalWrite(SD_CS,HIGH);

  /// シリアルモニタ
  Serial.println("Root closed");
  Serial.print("fileCount: ");
  Serial.println(fileCount);

}

<修正後:260914>
/**********************************************************
* SDカードのルートディレクトリにあるファイルを読込み
* 液晶の1画面のファイル名リストを表示する。
* 最初の16ファイル分の表示のみ行う関数
* スクロール表示後のリスト表示は随時、メインで別途行う。
***********************************************************/
void listRootFiles()
{
  /// ルートディレクトリを開く
  root = SD.open("/");

  if (!root) {
    Serial.println("ルートディレクトリが開けません"); // シリアルモニタに出力
    return;
  }
  if (!root.isDirectory()) {
     Serial.println("ディレクトリではありません"); // シリアルモニタに出力
     return;
  } 

  /// ループで次々とファイルを取得
  File file = root.openNextFile();

  /// ファイルが見つかるまで読取ったファイル名をバッファに格納する(最大256ファイル)
  /// 256ファイルに満たない場合は、MP3ファイルが見付かった分繰り返す(戻り値のfileで判断)
  while(file) {
    /// ファイル名取得
    const char* filename = file.name();
    String nameStr = String(filename);

    //// 拡張子が ".mp3" 或いは ".MP3"かどうかをチェックし対象ファイルのみ以下を実行
    if (nameStr.endsWith(".mp3") || nameStr.endsWith(".MP3")) {
      /// 2次元配列へコピー・念のため終端文字を保証
      strncpy(fileNames[fileCount], filename, MAX_NAME_LEN-1);
      fileNames[fileCount][MAX_NAME_LEN - 1] = '\0';

      if (fileCount < 16) { // 液晶の1画面分のファイル名リストを表示する(最初の16ファイルのみ)
        /// ファイル名の文字フォントデータを液晶表示関数に渡しファイル名を表示
        digitalWrite(SD_CS,HIGH);
        digitalWrite(D_CS, LOW);
        LCD_DrawString(2, fileCount, fileNames[fileCount], WHITE, BLACK);
        digitalWrite(D_CS, HIGH);
        digitalWrite(SD_CS, LOW);
      }
      fileCount++;
    }
    file.close(); // 開いたファイルを閉じて次のファイルへ

    /// 最大数に達したらループを抜ける、そうでなければ次のファイルをオープン
    if (fileCount >= MAX_FILES) {
      break;
    }
    file = root.openNextFile();
  } // end while

  root.close();
  digitalWrite(SD_CS, HIGH);

  /// シリアルモニタでの確認用
  Serial.println("Root closed");
  Serial.print("fileCount: ");
  Serial.println(fileCount);
}

<結果>
Root closed
fileCount: 127

・全170曲と思っていたが、これはbmpも含めた数でmp3は127曲だったのでok
・スクロール表示もしている。
・ただ、17曲目からの最初のスクロールのみOK
・ 18曲目のスクロールから曲がズレる。
・あと2画面からのTop_SWの動きがおかしい。


■ 2026.9.15
<私>

 ・進捗報告。最初の16曲リスト表示後のカーソル+N**表示+V**表示まで上手く行きました。
  また、17曲目以降からのスクロール表示も上手く行っているようです。
  スクロール表示になってからのSW割込みですが、SKIP_SWはOKそうですが、Top_SWとかは
  最初のリスト画面に戻らないなど、未だ怪しい動きがあるようです。暫く不具合点を整理して
  デバッグしてみます。また行き詰まったらお願いします。
  尚、root.close();後のシリアルモニタの結果ですがOKでした。
/// シリアルモニタでの確認用
Serial.println("Root closed");
Serial.print("fileCount: ");
Serial.println(fileCount);

<結果>
Root closed
fileCount: 127

 ・全170曲と思っていたが、これはbmpも含めた数でmp3は127曲だったのでok 


■ 2026.9.16
<不具合点調査デバッグ:以下の修正で解決した>

 (1) 17曲目からの最初のスクロールのみOKだが、何故かV表示が消える(VOL_SWを押すと復帰はする)

<修正前:main>
listRootFiles();
fileCount = 0;

itostring(2, 64-Volume, str_V+1);
digitalWrite(D_CS, LOW);
LCD_DrawString(21, 0, str_V, WHITE, BLACK);// 'Vxx'表示
LCD_DrawChar(0, 0, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示)

while(1) {
  /// ファイルNo.表示
  itostring(2, fileCount+1, str_N+1);
  LCD_DrawString(21, 15, str_N, WHITE, BLACK); // 'Nxx'表示

  /****** 選択されたファイルの再生処理 ***********/
    ・
    ・
 }

<修正後:V**表示をwhileループの中に移動>
listRootFiles();
fileCount = 0;

digitalWrite(D_CS, LOW);
LCD_DrawChar(0, 0, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示)

while(1) {
  /// ファイルNo.表示
  itostring(2, fileCount+1, str_N+1);
  LCD_DrawString(21, 15, str_N, WHITE, BLACK); // 'Nxx'表示

  /// 音量表示
  itostring(2, 64-Volume, str_V+1);
  LCD_DrawString(21, 0, str_V, WHITE, BLACK);// 'Vxx'表示

  /****** 選択されたファイルの再生処理 ***********/
    ・
    ・
}

 (2) スクロール画面で、TOP_SWを押すとカーソルが、その画面の頭に移動するがトップページのリストにならない。
    ただ再生はトップからの音が出てる。
    また押す前のカーソル表示(末尾)を消してない。

<修正前:main>
do {
  ・
  ・
} while (!flagSkip && !flagTop); // 再生途中にSW割込みが無く1曲分の再生が終わった時と
// 音量SW以外のSW割込みが入った場合はループを抜け以下を実行
  ・
  ・

if (!flagSkip && !flagTop) { // 再生中にボタンが押されなかった場合の処理(音量ボタン以外)
  fileCount++;
  MakerDisp_and_Scroll(); // 選択マーカー移動表示
}
else { // 再生中にボタンが押された場合の処理(音量ボタン以外)
  if (flagSkip) {
    flagSkip = 0;
    fileCount++; // 次の再生ファイルNo取得
    MakerDisp_and_Scroll(); // 選択マーカー移動表示
  }
  else {
    if (flagTop) {
      flagTop = 0;
      FN_Top = 1;
      LCD_DrawChar(0, fileCount, 0x20, BLACK, BLACK); // 現在位置の選択キャラクタ'●'をクリアする
      fileCount = 0;
      LCD_DrawChar(0, fileCount, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示
    }
  }
}


<修正後>
do {
  ・
  ・
} while (!flagSkip && !flagTop); // 再生途中にSW割込みが無く1曲分の再生が終わった時と
// 音量SW以外のSW割込みが入った場合はループを抜け以下を実行
  ・
  ・

if (!flagSkip && !flagTop) { // 再生中にボタンが押されなかった場合の処理(音量ボタン以外)
  fileCount++;
  MakerDisp_and_Scroll(); // 選択マーカー移動表示
}
else { // 再生中にボタンが押された場合の処理(音量ボタン以外)
  if (flagSkip) {
    flagSkip = 0;
    fileCount++; // 次の再生ファイルNo取得
    MakerDisp_and_Scroll(); // 選択マーカー移動表示
  }
  else {
    if (flagTop) {
      if (fileCount < 16) { // 最初の1画面分の表示中か?(0 - 15)
        flagTop = 0;
        FN_Top = 1;
        LCD_DrawChar(0, fileCount, 0x20, BLACK, BLACK); // 現在位置の選択キャラクタ'●'をクリアする
        fileCount = 0;
        LCD_DrawChar(0, fileCount, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示
      }
      else { // スクロール中の場合
        flagTop = 0;
        fileCount = 0;
        FN_Top = 1;
        LCD_Fill(BLACK); // 全画面クリア

        /// 最初の16曲分のファイル名の表示
        i = 0;
        while(i < 16) {
          LCD_DrawString(2, i, fileNames[i], WHITE, BLACK);
          i++;
        }
        LCD_DrawChar(0, fileCount, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示
      }
    }
}


■ 2026.9.17
<不具合点調査デバッグ:以下の修正で解決した>

 (1) 17曲目からの最初のスクロールのみOKだが、何故かV表示が消える(VOL_SWを押すと復帰はする)

<最後の127曲再生後に1曲目に戻るか?のチェック時発覚した事>
 ① N** と2桁表示にしているが、最大256曲対応なので、N***と3桁にしないと駄目だった。
 ② N96番目の「OTIBA_KIS.mp3」で止まり、液晶に「File not found」が表示された。
   デバッグ機能の確認にはなったが、これはファイル名の字数が8文字を超えている為と直ぐ分かった。
   「OTIBA_KI.mp3」にpcのエクスプローラでリネームセーブした。
   その結果、N96から最後のN127に移ったようだ。まだ3桁表示に修正してないので、後で確認する。

<修正・改善点>
 ① "N**" -> "N***/127"に変更。127は、ルートクローズ後のファイルカウントの値をアスキー変換して表示
   これで上記②が確認できる。

<修正前>
char str_N[]= "Nxx"; // FileNo表示用

while(1) {
  /// ファイルNo.表示
  itostring(2, fileCount+1, str_N+1);
  LCD_DrawString(21, 15, str_N, WHITE, BLACK); // 'Nxx'表示

--------------------------------------------------------------------------------------

void listRootFiles()
{
   ・
   ・
   ・
  while(file) {
   ・
   ・
  }
} // end while

root.close();
digitalWrite(SD_CS, HIGH);

/// シリアルモニタでの確認用
Serial.println("Root closed");
Serial.print("fileCount: ");
Serial.println(fileCount);

================================================================================

<修正後>
char str_N[]= "Nxxx"; // 曲番表示用
char str_T[]= "/xxx"; // トータル曲数表示用

while(1) {
  /// ファイルNo.表示
  itostring(3, fileCount+1, str_N+1);
  LCD_DrawString(16, 15, str_N, WHITE, BLACK); // 'Nxxx'表示
   ・
   ・
}

---------------------------------------------------------------------

void listRootFiles()
{
   ・
   ・
   ・
  while(file) {
   ・
   ・
  }
} // end while

root.close();
digitalWrite(SD_CS, HIGH);

/// トータル・曲数(ファイル数)の表示
digitalWrite(D_CS, LOW);
itostring(3, fileCount, str_T+1);
LCD_DrawString(20, 15, str_T, WHITE, BLACK);
 


■ 2026.9.18
 ・不具合発覚
  N127再生後トップに戻らなかった。
  無限に先に意味なく進んで行く感じ。


■ 2026.9.19
 ・昨日の症状対策

<修正前>
void listRootFiles()
{
  ・
  ・
  ・
  /// ループで次々とファイルを取得
  File file = root.openNextFile();

  /// ファイルが見つかるまで読取ったファイル名をバッファに格納する(最大256ファイル)
  /// 256ファイルに満たない場合は、MP3ファイルが見付かった分繰り返す(戻り値のfileで判断)
  while(file) {
    /// ファイル名取得
    const char* filename = file.name();
    String nameStr = String(filename);

    //// 拡張子が ".mp3" 或いは ".MP3"かどうかをチェックし対象ファイルのみ以下を実行
    if (nameStr.endsWith(".mp3") || nameStr.endsWith(".MP3")) {
      /// 2次元配列へコピー・念のため終端文字を保証
      strncpy(fileNames[fileCount], filename, MAX_NAME_LEN-1);
      fileNames[fileCount][MAX_NAME_LEN - 1] = '\0';

      if (fileCount < 16) { // 液晶の1画面分のファイル名リストを表示する(最初の16ファイルのみ)
        /// ファイル名の文字フォントデータを液晶表示関数に渡しファイル名を表示
        digitalWrite(SD_CS,HIGH);
        digitalWrite(D_CS, LOW);
        LCD_DrawString(2, fileCount, fileNames[fileCount], WHITE, BLACK);
        digitalWrite(D_CS, HIGH);
        digitalWrite(SD_CS, LOW);
      }
      fileCount++;
    }
    file.close(); // 開いたファイルを閉じて次のファイルへ

    /// 最大数に達したらループを抜ける、そうでなければ次のファイルをオープン
    if (fileCount >= MAX_FILES) {
      break;
    }
    file = root.openNextFile();
  } // end while

  root.close();
  digitalWrite(SD_CS, HIGH);

  /// トータル・曲数(ファイル数)の表示
  digitalWrite(D_CS, LOW);
  itostring(3, fileCount, str_T+1);
  LCD_DrawString(20, 15, str_T, WHITE, BLACK);
}

<修正後>
int TotalMp3Files; // メイン頭のグローバル変数に追加

void listRootFiles()
{
  ・
  ・
  ・
  /// ループで次々とファイルを取得
  File file = root.openNextFile();

  /// ファイルが見つかるまで読取ったファイル名をバッファに格納する(最大256ファイル)
  /// 256ファイルに満たない場合は、MP3ファイルが見付かった分繰り返す(戻り値のfileで判断)
  while(file) {
    /// ファイル名取得
    const char* filename = file.name();
    String nameStr = String(filename);

    //// 拡張子が ".mp3" 或いは ".MP3"かどうかをチェックし対象ファイルのみ以下を実行
    if (nameStr.endsWith(".mp3") || nameStr.endsWith(".MP3")) {
      /// 2次元配列へコピー・念のため終端文字を保証
      strncpy(fileNames[fileCount], filename, MAX_NAME_LEN-1);
      fileNames[fileCount][MAX_NAME_LEN - 1] = '\0';

      if (fileCount < 16) { // 液晶の1画面分のファイル名リストを表示する(最初の16ファイルのみ)
        /// ファイル名の文字フォントデータを液晶表示関数に渡しファイル名を表示
        digitalWrite(SD_CS,HIGH);
        digitalWrite(D_CS, LOW);
        LCD_DrawString(2, fileCount, fileNames[fileCount], WHITE, BLACK);
        digitalWrite(D_CS, HIGH);
        digitalWrite(SD_CS, LOW);
      }
      fileCount++;
    }
    file.close(); // 開いたファイルを閉じて次のファイルへ

    /// 最大数に達したらループを抜ける、そうでなければ次のファイルをオープン
    if (fileCount >= MAX_FILES) {
      break;
    }
    file = root.openNextFile();
  } // end while

  root.close();
  digitalWrite(SD_CS, HIGH);

  /// トータル・曲数(ファイル数)の表示
  digitalWrite(D_CS, LOW);
  itostring(3, fileCount, str_T+1);
  LCD_DrawString(20, 15, str_T, WHITE, BLACK);

 TotalMp3Files = fileCount; // 現在の値をTotalMp3Filesに退避 追加:260919
}

-------------------------------------------------------------------------------------

<修正前>
void MakerDisp_and_Scroll()
{
  if (fileCount < 16) { // 最初の1画面分の表示中か?(0 - 15)
    LCD_DrawChar(0, fileCount-1, 0x20, BLACK, BLACK); // その前に表示されている、選択キャラクタ'●'をクリアする
    LCD_DrawChar(0, fileCount, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示
  }
  else if (fileCount < MAX_FILES) { // 最初の1画面分の表示を終えたか?(スクロール表示の開始)
          LCD_Fill(BLACK); // 全画面クリア

          /// 16曲分のファイル名の表示(FileNoは、スクロール毎に、1つ上にずれる)
          i = 0;
          int FN_Top_Temp = FN_Top; // FN_Topをテンポラリに一時退避
          while(i < 16) {
            LCD_DrawString(2, i, fileNames[FN_Top], WHITE, BLACK);
            FN_Top++;
            i++;
          }
          FN_Top = FN_Top_Temp; // ループ前の値に戻す。
          FN_Top++; // 次のスクロール表示のためにインクリメント
          LCD_DrawChar(0, 15, 0xE4, GREEN, BLACK); // '●'を最終行に表示(スクロール中は常に最終行に表示される)

          /// トータル・曲数(ファイル数)の表示:追加260918
          LCD_DrawString(20, 15, str_T, WHITE, BLACK);
       }
       else{ // 256maxを超えた場合、先頭に戻す
         fileCount = 0;
         FN_Top = 1;
         LCD_DrawChar(0, 15, 0x20, BLACK, BLACK); // その前に表示されている、選択キャラクタ'●'をクリアする
         LCD_DrawChar(0, fileCount, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示
      }
}

<修正後>
void MakerDisp_and_Scroll()
{
  if (fileCount < 16) { // 最初の1画面分の表示中か?(0 - 15)
    LCD_DrawChar(0, fileCount-1, 0x20, BLACK, BLACK); // その前に表示されている、選択キャラクタ'●'をクリアする
    LCD_DrawChar(0, fileCount, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示
  }

  if (fileCount < MAX_FILES) { // 最初の1画面分の表示を終えたか?(スクロール表示の開始)
    LCD_Fill(BLACK); // 全画面クリア

    /// 16曲分のファイル名の表示(FileNoは、スクロール毎に、1つ上にずれる)
    i = 0;
    int FN_Top_Temp = FN_Top; // FN_Topをテンポラリに一時退避
    while(i < 16) {
      LCD_DrawString(2, i, fileNames[FN_Top], WHITE, BLACK);
      FN_Top++;
      i++;
    }
    FN_Top = FN_Top_Temp; // ループ前の値に戻す。
    FN_Top++; // 次のスクロール表示のためにインクリメント
    LCD_DrawChar(0, 15, 0xE4, GREEN, BLACK); // '●'を最終行に表示(スクロール中は常に最終行に表示される)

    /// トータル・曲数(ファイル数)の表示:追加260918
    LCD_DrawString(20, 15, str_T, WHITE, BLACK);
  }

  if (fileCount > TotalMp3Files) { // SDカード収録のMP3ファイル総数を超えた時は先頭に戻す
    fileCount = 0;
    FN_Top = 1;
    LCD_Fill(BLACK); // 全画面クリア

    /// 最初の16曲分のファイル名の表示
    i = 0;
    while(i < 16) {
      LCD_DrawString(2, i, fileNames[i], WHITE, BLACK);
      i++;
    }
    LCD_DrawChar(0, fileCount, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示
  }
}
 


■ 2026.9.22
 ・↑↑--- 試してみた。

  <結果>
   ・コンパイルは通過
   ・skip sw 常に最終行にカーソルが表示されてしまう。

<修正後>
void MakerDisp_and_Scroll()
{
  if (fileCount < 16) { // 最初の1画面分の表示中か?(0 - 15)
    LCD_DrawChar(0, fileCount-1, 0x20, BLACK, BLACK); // その前に表示されている、選択キャラクタ'●'をクリアする
    LCD_DrawChar(0, fileCount, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示
  }
  else if (fileCount < MAX_FILES) { // 最初の1画面分の表示を終えたか?(スクロール表示の開始)
           LCD_Fill(BLACK); // 全画面クリア

           /// 16曲分のファイル名の表示(FileNoは、スクロール毎に、1つ上にずれる)
           i = 0;
           int FN_Top_Temp = FN_Top; // FN_Topをテンポラリに一時退避
           while(i < 16) {
             LCD_DrawString(2, i, fileNames[FN_Top], WHITE, BLACK);
             FN_Top++;
             i++;
           }
           FN_Top = FN_Top_Temp; // ループ前の値に戻す。
           FN_Top++; // 次のスクロール表示のためにインクリメント
           LCD_DrawChar(0, 15, 0xE4, GREEN, BLACK); // '●'を最終行に表示(スクロール中は常に最終行に表示される)
 
           /// トータル・曲数(ファイル数)の表示:追加260918
           LCD_DrawString(20, 15, str_T, WHITE, BLACK);
       }
       else if (fileCount > TotalMp3Files) { // SDカード収録のMP3ファイル総数を超えた時は先頭に戻す
               fileCount = 0;
               FN_Top = 1;
               LCD_Fill(BLACK); // 全画面クリア

               /// 最初の16曲分のファイル名の表示
               i = 0;
               while(i < 16) {
                 LCD_DrawString(2, i, fileNames[i], WHITE, BLACK);
                 i++;
               }
               LCD_DrawChar(0, fileCount, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示
             }
}

  <結果>
   ・これでも駄目同じ。
   → else if (fileCount > TotalMp3Files) { に来てないようだ。
     何時までも、else if (fileCount < MAX_FILES)の条件を通過して先に行っているようだ。

  <修正案:自己解決>
   ・else if (fileCount < MAX_FILES) { // 最初の1画面分の表示を終えたか?(スクロール表示の開始)
    この条件式の条件:MAX_FILES(256)では駄目。128曲目以降もこの条件に当てはまってどんどん先に進んでしまう。
    あくまでも、TotalMp3Filesと比較しないと駄目。
    MAX_FILESで比較するのは、最初の16曲リスト表示のところのループをブレークする時だけでOK。バッファへの保存は256曲までで制限。

void MakerDisp_and_Scroll()
{
  if (fileCount < 16) { // 最初の1画面分の表示中か?(0 - 15)
    LCD_DrawChar(0, fileCount-1, 0x20, BLACK, BLACK); // その前に表示されている、選択キャラクタ'●'をクリアする
    LCD_DrawChar(0, fileCount, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示
  }
  else if (fileCount < TotalMp3Files) { // 最初の1画面分の表示を終えたか?(スクロール表示の開始)
           LCD_Fill(BLACK); // 全画面クリア

           /// 16曲分のファイル名の表示(FileNoは、スクロール毎に、1つ上にずれる)
           i = 0;
           int FN_Top_Temp = FN_Top; // FN_Topをテンポラリに一時退避
           while(i < 16) {
             LCD_DrawString(2, i, fileNames[FN_Top], WHITE, BLACK);
             FN_Top++;
             i++;
          }
          FN_Top = FN_Top_Temp; // ループ前の値に戻す。
          FN_Top++; // 次のスクロール表示のためにインクリメント
          LCD_DrawChar(0, 15, 0xE4, GREEN, BLACK); // '●'を最終行に表示(スクロール中は常に最終行に表示される)

          /// トータル・曲数(ファイル数)の表示:追加260918
          LCD_DrawString(20, 15, str_T, WHITE, BLACK);
       }
       else { // SDカード収録のMP3ファイル総数を超えた時は先頭に戻す
         fileCount = 0;
         FN_Top = 1;
         LCD_Fill(BLACK); // 全画面クリア

         /// 最初の16曲分のファイル名の表示
         i = 0;
         while(i < 16) {
           LCD_DrawString(2, i, fileNames[i], WHITE, BLACK);
           i++;
         }
         LCD_DrawString(20, 15, str_T, WHITE, BLACK); // トータル・曲数(ファイル数)の表示
         LCD_DrawChar(0, fileCount, 0xE4, GREEN, BLACK); // '●'を選択初期位置(先頭)に表示
      }
}

  <結果>
   ・これでやっと通常再生とSKIP_SWでの127曲繰り返し再生OKになる。

 ・次回はジャケット画像の表示を予定してます。 


<最終スケッチとフォントデータ>

・こちらからどうぞ→ ESP32S3_MP3_PLAYER_TEST_2.ino
                                        ASCII12dot.h  


← 実験テーマ1に戻る   TOP PAGEに戻る   実験テーマ174へ