以下の内容はhttps://gijin77.blog.jp/archives/2024-11.htmlより取得しました。


◆unit MIDIを入手したのでM5StampS3を使ってUSB MIDI I/Fのキーボードデータを
 DIN-MIDI OUT/INへの変換アダプタを作ってみました。
 USBのキーボードから変換器でDINにしてunit MIDIのDIN入力に繋いでいます。

 usb-din-midi変換1
 usb-din-midi変換3

1.開発環境 2024/11/28時点
 ①Windows11 X64 Pro 24H2 
 ②Arduino IDE 2.3.2(2.3.3はフリーズするのでバージョンを落として使っています)
 ➂ESP32ボードバージョンV3.0.7(esp32s3dev)
 ④M5Stack Arduino ボードバージョンV2.08(M5Stamp-S3)

2.変換アダプタ回路図
 usb-din変換アダプタ3

3.USB-MIDIをESP32S3で読み込むための準備
 ①スケッチ例を下記よりダウンロードします。
  esp32-usb-host-demos
  解凍後、「esp32-usb-host-demos-main\examples\usbhmidi\usbhmidi.ino」
  を別フォルダにコピペして使っています。
  そこに、「esp32-usb-host-demos-main」のフォルダにある二つのファイル
  「show_desc.hpp」usbhhelp.hpp」をコピペします。

  *ESP32S3Debkit用
  フォルダ1
  *M5Stamps3用
  フォルダ2

 ②USB-DIN変換用のスケッチです。先人のスケッチを修正しています。
  *ESP32S3用とM5STAMPS3用とは//コメントで変更しています。
  *他のESP32S3ボードを使用する場合、ここのDIN用シリアルポート番号を
   変更すれば、いけると思います。
  ◆当然ながらスケッチは、保障無しの自己責任で参考程度にお願いします。

************************************************************************************************
161: //Serial2.begin(31250,SERIAL_8N1,18,17); //RXD=18 TXD=17 ESP32S3 Devkit
162: Serial2.begin(31250,SERIAL_8N1,15,13);//RXD:15 TXD:13 M5STAMPS3
163:  delay(500);
164; usbh_setup(show_config_desc_full);
165: //Serial.println("ESP32S3 MIDI USB-IN -> DIN-OUT & DIN-IN -> USB-OUT");
166: Serial.println("M5STAMP MIDI USB-IN -> DIN-OUT & DIN-IN -> USB-OUT");
*************************************************************************************************

/*m5stamp_midi.ino
 * MIT License
 *Copyright (c) 2021 touchgadgetdev@gmail.com
 *2024/11/24 modify By JK1VCK
*/
#include <usb/usb_host.h>
#include "show_desc.hpp"
#include "usbhhelp.hpp"

bool isMIDI = false;
bool isMIDIReady = false;

const size_t MIDI_IN_BUFFERS = 8;
const size_t MIDI_OUT_BUFFERS = 8;
usb_transfer_t *MIDIOut = NULL;
usb_transfer_t *MIDIIn[MIDI_IN_BUFFERS] = {NULL};
// USB MIDI Event Packet Format (always 4 bytes)
//
// Byte 0 |Byte 1 |Byte 2 |Byte 3
// -------|-------|-------|------
// CN+CIN |MIDI_0 |MIDI_1 |MIDI_2
//
// CN = Cable Number (0x0..0xf) specifies virtual MIDI jack/cable
// CIN = Code Index Number (0x0..0xf) classifies the 3 MIDI bytes.
// See Table 4-1 in the MIDI 1.0 spec at usb.org.
//
static void midi_transfer_cb(usb_transfer_t *transfer) {
  ESP_LOGI("", "midi_transfer_cb context: %d", transfer->context);
  if (Device_Handle == transfer->device_handle) {
    int in_xfer = transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK;
    if ((transfer->status == 0) && in_xfer) {
      uint8_t *const p = transfer->data_buffer;
      for (int i = 0; i < transfer->actual_num_bytes; i += 4) {
        if ((p[i] + p[i+1] + p[i+2] + p[i+3]) == 0) break;
        ESP_LOGI("", "midi: %02x %02x %02x %02x",p[i], p[i+1], p[i+2], p[i+3]);
        Serial.printf("USB MIDI-IN: %02X %02X %02X %02X ",p[i], p[i+1], p[i+2], p[i+3]);
        Serial.printf("DIN MIDI-OUT: %02X %02X %02X\n", p[i+1], p[i+2], p[i+3]);
        Serial2.write(p[i+1]);
        Serial2.write(p[i+2]);
        Serial2.write(p[i+3]);
      }
      esp_err_t err = usb_host_transfer_submit(transfer);
      if (err != ESP_OK) {
        ESP_LOGI("", "usb_host_transfer_submit In fail: %x", err);
      }
    } else {
      ESP_LOGI("", "transfer->status %d", transfer->status);
    }
  }
}

void check_interface_desc_MIDI(const void *p) {
  const usb_intf_desc_t *intf = (const usb_intf_desc_t *)p;
  // USB MIDI
  if ((intf->bInterfaceClass == USB_CLASS_AUDIO) &&
      (intf->bInterfaceSubClass == 3) &&
      (intf->bInterfaceProtocol == 0))
  {
    isMIDI = true;
    ESP_LOGI("", "Claiming a MIDI device!");
    esp_err_t err = usb_host_interface_claim(Client_Handle, Device_Handle,
        intf->bInterfaceNumber, intf->bAlternateSetting);
    if (err != ESP_OK) ESP_LOGI("", "usb_host_interface_claim failed: %x", err);
  }
}

void prepare_endpoints(const void *p) {
  const usb_ep_desc_t *endpoint = (const usb_ep_desc_t *)p;
  esp_err_t err;
  // must be bulk for MIDI
  if ((endpoint->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK) != USB_BM_ATTRIBUTES_XFER_BULK) {
    ESP_LOGI("", "Not bulk endpoint: 0x%02x", endpoint->bmAttributes);
    return;
  }
  if (endpoint->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK) {
    for (int i = 0; i < MIDI_IN_BUFFERS; i++) {
      err = usb_host_transfer_alloc(endpoint->wMaxPacketSize, 0, &MIDIIn[i]);
      if (err != ESP_OK) {
        MIDIIn[i] = NULL;
        ESP_LOGI("", "usb_host_transfer_alloc In fail: %x", err);
      } else {
        MIDIIn[i]->device_handle = Device_Handle;
        MIDIIn[i]->bEndpointAddress = endpoint->bEndpointAddress;
        MIDIIn[i]->callback = midi_transfer_cb;
        MIDIIn[i]->context = (void *)i;
        MIDIIn[i]->num_bytes = endpoint->wMaxPacketSize;
        esp_err_t err = usb_host_transfer_submit(MIDIIn[i]);
        if (err != ESP_OK) {
          ESP_LOGI("", "usb_host_transfer_submit In fail: %x", err);
        }
      }
    }
  } else {
    err = usb_host_transfer_alloc(endpoint->wMaxPacketSize, 0, &MIDIOut);
    if (err != ESP_OK) {
      MIDIOut = NULL;
      ESP_LOGI("", "usb_host_transfer_alloc Out fail: %x", err);
      return;
    }
    ESP_LOGI("", "Out data_buffer_size: %d", MIDIOut->data_buffer_size);
    MIDIOut->device_handle = Device_Handle;
    MIDIOut->bEndpointAddress = endpoint->bEndpointAddress;
    MIDIOut->callback = midi_transfer_cb;
    MIDIOut->context = NULL;
//    MIDIOut->flags |= USB_TRANSFER_FLAG_ZERO_PACK;
  }
  isMIDIReady = ((MIDIOut != NULL) && (MIDIIn[0] != NULL));
}

void show_config_desc_full(const usb_config_desc_t *config_desc) {
  // Full decode of config desc.
  const uint8_t *p = &config_desc->val[0];
  uint8_t bLength;
  for (int i = 0; i < config_desc->wTotalLength; i+=bLength, p+=bLength) {
    bLength = *p;
    if ((i + bLength) <= config_desc->wTotalLength) {
      const uint8_t bDescriptorType = *(p + 1);
      switch (bDescriptorType) {
        case USB_B_DESCRIPTOR_TYPE_DEVICE:
          ESP_LOGI("", "USB Device Descriptor should not appear in config");
          break;
        case USB_B_DESCRIPTOR_TYPE_CONFIGURATION:
          show_config_desc(p);
          break;
        case USB_B_DESCRIPTOR_TYPE_STRING:
          ESP_LOGI("", "USB string desc TBD");
          break;
        case USB_B_DESCRIPTOR_TYPE_INTERFACE:
          show_interface_desc(p);
          if (!isMIDI) check_interface_desc_MIDI(p);
          break;
        case USB_B_DESCRIPTOR_TYPE_ENDPOINT:
          show_endpoint_desc(p);
          if (isMIDI && !isMIDIReady) {
            prepare_endpoints(p);
          }
          break;
        case USB_B_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
          ESP_LOGI("", "USB device qual desc TBD");// Should not be in config?
          break;
        case USB_B_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION:
          ESP_LOGI("", "USB Other Speed TBD");// Should not be in config?
          break;
        case USB_B_DESCRIPTOR_TYPE_INTERFACE_POWER:
          ESP_LOGI("", "USB Interface Power TBD");// Should not be in config?
          break;
        default:
          ESP_LOGI("", "Unknown USB Descriptor Type: 0x%x", *p);
          break;
      }
    }
    else {
      ESP_LOGI("", "USB Descriptor invalid");
      return;
    }
  }
}

void setup() {
  Serial.begin(115200);
  //Serial2.begin(31250,SERIAL_8N1,18,17); //RXD=18 TXD=17 ESP32S3 Devkit
  Serial2.begin(31250,SERIAL_8N1,15,13);//RXD:15 TXD:13 M5STAMPS3
  delay(500);
  usbh_setup(show_config_desc_full);
  //Serial.println("ESP32S3 MIDI USB-IN -> DIN-OUT & DIN-IN -> USB-OUT");
  Serial.println("M5STAMP MIDI USB-IN -> DIN-OUT & DIN-IN -> USB-OUT");
}
int ct=0;
uint8_t data[5]={1,2,3,4,5};
void loop() {
uint8_t wk;
  usbh_task();
  if (Serial2.available()) {// DIN MIDIからメッセージを取得
    wk=Serial2.read();      //Serial.printf("ct:%d %02x ",ct,wk);
    if ((wk & 0x80)) ct=0;
    data[ct]=wk;
    ct++;
    if (ct>2) {
      ct=0;
      Serial.print("DIN MIDI-IN: ");
      for(int i=0;i<3;i++) {
        Serial.printf("%02X ",data[i]);
      }
      if (isMIDIReady) {
        ESP_LOGI("", "MIDI send 4 bytes");
        MIDIOut->num_bytes = 4;
        uint8_t *const p = MIDIOut->data_buffer;
        p[0]=(data[0]&0xF0)>>4;
        for(int i=0;i<3;i++) {
          p[i+1]=data[i];
        }
        Serial.print(" USB MIDI-OUT: ");
        for(int i=0;i<4;i++) {
          Serial.printf("%02X ",p[i]);
        }
        esp_err_t err = usb_host_transfer_submit(MIDIOut);
        if (err != ESP_OK) {
          ESP_LOGI("", "usb_host_transfer_submit Out fail: %x", err);
        } else {
          Serial.printf(" USB OUT OK\n");
        }
      } else {
        Serial.println();
      }
    }
  }  
}


4.実行例
 最初は、二つ変換アダプタを作り、相互接続して実験しました。
 下記ブログ内記事を参照してみて下さい。

 ◆トラブル
 ①DINコネクタの4,5番ピンの単純な裏表の勘違いで動作せず分かるまで
  大変でした。
 ②その後、データのサイズが、USBから入出力データが、4バイトで
  DINへのデータが3バイトと違っていたので同じく分かるまで大変でした。

 ◆最後、両方向の変換がうまくいき、音が出た時は感激しました。
  カシオのLK-223を繋ぐと鍵盤からの入力と音源への出力と分離して
  入出力出来ました。

 下図は、デバッグ用に付けたシリアル出力です。
 USB---DINOK

4.DIN MIDIで流れるデータの方式について
 ①詳しくは、下記を参照して下さい。
  M I D I 1.0 規格書

 ②概略を下記に示します。(ChatGPTより)

 ## **1. データの基本構造**
 MIDIデータは**1バイト(8ビット)単位**で送信されます。
 1バイトは以下の形式で構成されています:

 ### **1.1 ステータスバイト**
  **最上位ビット (MSB)**: 常に `1`。
  **下位7ビット**: メッセージの種類やチャネル番号を指定。

 | ステータスバイト | 機能                        
 |--------------------------|-----------------------------------------------
 |     `1000xxxx`        | ノートオフメッセージ 
 |     `1001xxxx`        | ノートオンメッセージ 
 |     `1010xxxx`        | ポリフォニック・アフタータッチ
 |     `1011xxxx`        | コントロールチェンジ  
 |     `1100xxxx`        | プログラムチェンジ 
 |     `1101xxxx`        | チャネル・アフタータッチ 
 |     `1110xxxx`        | ピッチベンド  
 |     `1111xxxx`        | システムメッセージ  
 |--------------------------|-----------------------------------------------
  `xxxx` はチャネル番号(0~15、16チャネル対応)。

 ### **1.2 データバイト**
  **最上位ビット (MSB)**: 常に `0`。
  **下位7ビット**: 各メッセージに必要な追加データ。

 例:
 * ノートオンメッセージには「音のピッチ」と「ベロシティ」が含まれる。
 * コントロールチェンジには「コントローラ番号」と「値」が含まれる。

 ## **2. 主なメッセージの種類**
 以下は、DIN MIDIでやり取りされる代表的なメッセージです。

 ### **2.1 ノートオン/ノートオフ**
  **目的**: ノート(音符)を演奏開始・停止。
  **フォーマット**:
   *ノートオン: `1001cccc`(チャネル番号を含む) + `0kkkkkkk`(ノート番号)
   + `0vvvvvvv`(ベロシティ)
 *ノートオフ: `1000cccc`(チャネル番号を含む) + `0kkkkkkk`(ノート番号)
   + `0vvvvvvv`(ベロシティ)

 | 項目                | 内容                             
 |--------------------|------------------------------------------
 | ノート番号     | 0~127(中央Cは60)
 | ベロシティ     | 0~127(0はノートオフ扱い)
 |--------------------|------------------------------------------


 ### **2.2 コントロールチェンジ**
 **目的**: 音量、パンニング、モジュレーションなどのパラメータを変更。
 **フォーマット**:
  `1011cccc`(チャネル番号を含む) + `0ccccccc`(コントローラ番号) + `0vvvvvvv`(値)

 | コントローラ番号 | 機能例 
 |--------------------------|--------------------------
 | 1                            | モジュレーション 
 | 7                            | メイン音量 
 | 10                          | パンニング 
 | 64                          | ダンパーペダル
 |--------------------------|--------------------------

 ### **2.3 ピッチベンド**
 **目的**: 音程を滑らかに変更。
 **フォーマット**:
  `1110cccc`(チャネル番号を含む) + `0vvvvvvv`(LSB) + `0vvvvvvv`(MSB)

 | 項目                 | 内容 
 |---------------------|--------------------------
 | 範囲                 | -8192~8191         
 | デフォルト値   | 0(中央値)          
 |---------------------|--------------------------
 
 ### **2.4 プログラムチェンジ**
 **目的**: 音色(プログラム)の切り替え。
 **フォーマット**:
  `1100cccc`(チャネル番号を含む) + `0ppppppp`(プログラム番号)

 | 項目                   | 内容  
 |-----------------------|------------------------------
 | プログラム番号  | 0~127(音色番号)
 |-----------------------|------------------------------

 ### **2.5 システムメッセージ**
 **目的**: グローバルな操作(全チャネルに影響)。
 **例**:
  **システムリアルタイムメッセージ**: MIDIクロック(テンポ同期)やスタート/ストップ。
  **システムエクスクルーシブ (SysEx)**: メーカー固有の機能。

 ## **3. 伝送方式**
 DIN MIDIでは、以下の方式でデータが送信されます。

 ### **3.1 物理規格**
 **ケーブル**: 5ピンDINケーブル(通常、ピン2がGND、ピン4とピン5がデータ用)。
 **伝送速度**: 31.25 kbps (非同期)。
 **信号レベル**: 電圧範囲は約5V。

 ### **3.2 データ伝送**
 *MIDIデータはシリアル通信方式で1バイトずつ送信。
 *スタートビット、8ビットデータ(LSB先行)、ストップビットで構成されます。

 | 項目                  | 内容          
 |----------------------|-----------------------
 | スタートビット | 1ビット (0固定) 
 | データビット     | 8ビット  
 | ストップビット | 1ビット (1固定) 
 |----------------------|-----------------------

 ### **3.3 データの順序**
 送信されるデータの順序は以下の通り:
 1. ステータスバイト(メッセージタイプとチャネル)
 2. データバイト(必要に応じて1~2バイト)

例:
  ノートオン (チャネル1、ノート60、ベロシティ127):  
   `10010000`(ステータスバイト)  
   `00111100`(ノート番号60)  
   `01111111`(ベロシティ127)

 ## **4. まとめ**
 DIN MIDIはシンプルかつ堅牢なプロトコルで、音楽デバイス間のコミュニケーションを
 標準化しています。シリアル通信を利用し、音符の演奏、音色の変更、コントローラの
 操作、テンポ同期など、幅広い音楽情報をリアルタイムで伝送可能です。
 
以上 入出力共にうまく変換出来ました。

◆もう一台、作る予定だったので、基板を発注してみました。
 下記ブログ内記事を参照してみてください。
 ・Kicad で書いて JLCPCB へ基板を発注してみた






ブログトップへ

◆USB MIDI-OUTのキーボードデータをESP32S3を使ってDIN-MIDI-OUTへ変換してみました。
 *DINコネクタやケーブルは注文中の為、現在前段階です。
 usb_midi_in-1
1.開発環境 2024/11/23時点
 ①Windows11 X64 Pro 24H2 
 ②Arduino IDE 2.3.2(2.3.3はフリーズするのでバージョンを落として使っています)
 ➂ESP32ボードバージョンV3.0.7(esp32s3dev)

2.USB-MIDIをESP32S3で読み込むための準備
 ①スケッチ例を下記よりダウンロードします。
  esp32-usb-host-demos
  解凍後、「esp32-usb-host-demos-main\examples\usbhmidi\usbhmidi.ino」
  を別フォルダにコピペして使っています。
  そこに、「esp32-usb-host-demos-main」のフォルダにある二つのファイル
  「show_desc.hpp」usbhhelp.hpp」をコピペします。

  *ESP32S3Debkit用
  フォルダ1
  *M5Stamps3用
  フォルダ2

 ②USB-DIN変換用のスケッチです。先人のスケッチを修正しています。
  *ESP32S3用とM5STAMPS3用とは//コメントで変更しています。
************************************************************************************************
160:  Serial2.begin(31250,SERIAL_8N1,18,17); //RXD=18 TXD=17 ESP32S3 Devkit
161: //Serial2.begin(31250,SERIAL_8N1,15,13);//RXD:15 TXD:13 M5STAMPS3
162:  delay(500);
163; usbh_setup(show_config_desc_full);
164:  Serial.println("ESP32S3 MIDI USB-IN -> DIN-OUT & DIN-IN -> USB-OUT");
165:  //Serial.println("M5STAMP MIDI USB-IN -> DIN-OUT & DIN-IN -> USB-OUT");
*************************************************************************************************
/*usbhmidi.ino
 * MIT License
 *Copyright (c) 2021 touchgadgetdev@gmail.com
 *2024/11/24 modify By JK1VCK
*/
#include <usb/usb_host.h>
#include "show_desc.hpp"
#include "usbhhelp.hpp"

bool isMIDI = false;
bool isMIDIReady = false;

const size_t MIDI_IN_BUFFERS = 8;
const size_t MIDI_OUT_BUFFERS = 8;
usb_transfer_t *MIDIOut = NULL;
usb_transfer_t *MIDIIn[MIDI_IN_BUFFERS] = {NULL};
// USB MIDI Event Packet Format (always 4 bytes)
//
// Byte 0 |Byte 1 |Byte 2 |Byte 3
// -------|-------|-------|------
// CN+CIN |MIDI_0 |MIDI_1 |MIDI_2
//
// CN = Cable Number (0x0..0xf) specifies virtual MIDI jack/cable
// CIN = Code Index Number (0x0..0xf) classifies the 3 MIDI bytes.
// See Table 4-1 in the MIDI 1.0 spec at usb.org.
//
static void midi_transfer_cb(usb_transfer_t *transfer) {
  ESP_LOGI("", "midi_transfer_cb context: %d", transfer->context);
  if (Device_Handle == transfer->device_handle) {
    int in_xfer = transfer->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK;
    if ((transfer->status == 0) && in_xfer) {
      uint8_t *const p = transfer->data_buffer;
      for (int i = 0; i < transfer->actual_num_bytes; i += 4) {
        if ((p[i] + p[i+1] + p[i+2] + p[i+3]) == 0) break;
        ESP_LOGI("", "midi: %02x %02x %02x %02x",p[i], p[i+1], p[i+2], p[i+3]);
        Serial.printf("USB MIDI-IN: %02x %02x %02x %02x\n",p[i], p[i+1], p[i+2], p[i+3]);
        for(int j=0;j<4;j++){
          Serial2.write(p[i+j]);
        }
      }
      esp_err_t err = usb_host_transfer_submit(transfer);
      if (err != ESP_OK) {
        ESP_LOGI("", "usb_host_transfer_submit In fail: %x", err);
      }
    } else {
      ESP_LOGI("", "transfer->status %d", transfer->status);
    }
  }
}

void check_interface_desc_MIDI(const void *p) {
  const usb_intf_desc_t *intf = (const usb_intf_desc_t *)p;
  // USB MIDI
  if ((intf->bInterfaceClass == USB_CLASS_AUDIO) &&
      (intf->bInterfaceSubClass == 3) &&
      (intf->bInterfaceProtocol == 0))
  {
    isMIDI = true;
    ESP_LOGI("", "Claiming a MIDI device!");
    esp_err_t err = usb_host_interface_claim(Client_Handle, Device_Handle,
        intf->bInterfaceNumber, intf->bAlternateSetting);
    if (err != ESP_OK) ESP_LOGI("", "usb_host_interface_claim failed: %x", err);
  }
}

void prepare_endpoints(const void *p) {
  const usb_ep_desc_t *endpoint = (const usb_ep_desc_t *)p;
  esp_err_t err;
  // must be bulk for MIDI
  if ((endpoint->bmAttributes & USB_BM_ATTRIBUTES_XFERTYPE_MASK) != USB_BM_ATTRIBUTES_XFER_BULK) {
    ESP_LOGI("", "Not bulk endpoint: 0x%02x", endpoint->bmAttributes);
    return;
  }
  if (endpoint->bEndpointAddress & USB_B_ENDPOINT_ADDRESS_EP_DIR_MASK) {
    for (int i = 0; i < MIDI_IN_BUFFERS; i++) {
      err = usb_host_transfer_alloc(endpoint->wMaxPacketSize, 0, &MIDIIn[i]);
      if (err != ESP_OK) {
        MIDIIn[i] = NULL;
        ESP_LOGI("", "usb_host_transfer_alloc In fail: %x", err);
      } else {
        MIDIIn[i]->device_handle = Device_Handle;
        MIDIIn[i]->bEndpointAddress = endpoint->bEndpointAddress;
        MIDIIn[i]->callback = midi_transfer_cb;
        MIDIIn[i]->context = (void *)i;
        MIDIIn[i]->num_bytes = endpoint->wMaxPacketSize;
        esp_err_t err = usb_host_transfer_submit(MIDIIn[i]);
        if (err != ESP_OK) {
          ESP_LOGI("", "usb_host_transfer_submit In fail: %x", err);
        }
      }
    }
  } else {
    err = usb_host_transfer_alloc(endpoint->wMaxPacketSize, 0, &MIDIOut);
    if (err != ESP_OK) {
      MIDIOut = NULL;
      ESP_LOGI("", "usb_host_transfer_alloc Out fail: %x", err);
      return;
    }
    ESP_LOGI("", "Out data_buffer_size: %d", MIDIOut->data_buffer_size);
    MIDIOut->device_handle = Device_Handle;
    MIDIOut->bEndpointAddress = endpoint->bEndpointAddress;
    MIDIOut->callback = midi_transfer_cb;
    MIDIOut->context = NULL;
//    MIDIOut->flags |= USB_TRANSFER_FLAG_ZERO_PACK;
  }
  isMIDIReady = ((MIDIOut != NULL) && (MIDIIn[0] != NULL));
}

void show_config_desc_full(const usb_config_desc_t *config_desc) {
  // Full decode of config desc.
  const uint8_t *p = &config_desc->val[0];
  uint8_t bLength;
  for (int i = 0; i < config_desc->wTotalLength; i+=bLength, p+=bLength) {
    bLength = *p;
    if ((i + bLength) <= config_desc->wTotalLength) {
      const uint8_t bDescriptorType = *(p + 1);
      switch (bDescriptorType) {
        case USB_B_DESCRIPTOR_TYPE_DEVICE:
          ESP_LOGI("", "USB Device Descriptor should not appear in config");
          break;
        case USB_B_DESCRIPTOR_TYPE_CONFIGURATION:
          show_config_desc(p);
          break;
        case USB_B_DESCRIPTOR_TYPE_STRING:
          ESP_LOGI("", "USB string desc TBD");
          break;
        case USB_B_DESCRIPTOR_TYPE_INTERFACE:
          show_interface_desc(p);
          if (!isMIDI) check_interface_desc_MIDI(p);
          break;
        case USB_B_DESCRIPTOR_TYPE_ENDPOINT:
          show_endpoint_desc(p);
          if (isMIDI && !isMIDIReady) {
            prepare_endpoints(p);
          }
          break;
        case USB_B_DESCRIPTOR_TYPE_DEVICE_QUALIFIER:
          ESP_LOGI("", "USB device qual desc TBD");// Should not be in config?
          break;
        case USB_B_DESCRIPTOR_TYPE_OTHER_SPEED_CONFIGURATION:
          ESP_LOGI("", "USB Other Speed TBD");// Should not be in config?
          break;
        case USB_B_DESCRIPTOR_TYPE_INTERFACE_POWER:
          ESP_LOGI("", "USB Interface Power TBD");// Should not be in config?
          break;
        default:
          ESP_LOGI("", "Unknown USB Descriptor Type: 0x%x", *p);
          break;
      }
    }
    else {
      ESP_LOGI("", "USB Descriptor invalid");
      return;
    }
  }
}

void setup() {
  Serial.begin(115200);
  Serial2.begin(31250,SERIAL_8N1,18,17); //RXD=18 TXD=17 ESP32S3 Devkit
  //Serial2.begin(31250,SERIAL_8N1,15,13);//RXD:15 TXD:13 M5STAMPS3
  delay(500);
  usbh_setup(show_config_desc_full);
  Serial.println("ESP32S3 MIDI USB-IN -> DIN-OUT & DIN-IN -> USB-OUT");
  //Serial.println("M5STAMP MIDI USB-IN -> DIN-OUT & DIN-IN -> USB-OUT");
  //Serial2.println("\n-->Serial2 MIDI OUT");
}
int ct=0;
uint8_t data[5]={1,2,3,4,5};
void loop() {
uint8_t wk;
  usbh_task();
  if (Serial2.available()) {// DIN MIDIからメッセージを取得
    wk=Serial2.read();
    if (!(wk & 0xf0)) ct=0;
    data[ct]=wk;
    ct++;
    if (ct>3) {
      ct=0;
      Serial.print("DIN MIDI-IN: ");
      for(int i=0;i<4;i++) {
        Serial.printf("%02x ",data[i]);
      }
      if (isMIDIReady) {
        ESP_LOGI("", "MIDI send 4 bytes");
        MIDIOut->num_bytes = 4;
        uint8_t *const p = MIDIOut->data_buffer;
        for(int i=0;i<4;i++) {
          p[i]=data[i];
        }
        esp_err_t err = usb_host_transfer_submit(MIDIOut);
        if (err != ESP_OK) {
          ESP_LOGI("", "usb_host_transfer_submit Out fail: %x", err);
        } else {
          Serial.printf(" USB MIDI-OUT OK\n");
        }
      } else {
        Serial.println();
      }
    }
  }  
}
3.実行例
 ①二つ変換機を作り、相互接続しての実験中です。
 ・ESP32S3にカシオのキーボードLK-223(USB MIDI in / out)
 ・M5STAMPS3にKORG nanoKEY2(USM MIDI outのみ)
 この時点では、DIN I/Fは無しでUARTをクロス接続して実験しています。

 *M5STAMPS3をプログラムする時とOTGとして使う時の面倒さを解消する為に
 切替器を使っています。詳しくは、下記ブログ内記事を参照して下さい。
 ・USB Type-C 切替器を作ってみたV2

 esp32s3-m5stanps3

 USB_DIN_MIDI_S3

 ①M5STAMPS3に繋いだKORG nanoKEY2よりUSB入力し、
  (DIN-OUT)MIDIインターフェス(シリアル31250ボー)
  出力しています。
 midi-usb-in

 ②M5STAMPS3からDIN出力したものをDIN入力し
  ESP32S3に繋いだカシオのキーボードLK-223にUSB出力しています。

 midi-din-in

4.MIDIインターフェース回路図(参考)
 midi回路図

5.DIN MIDIで流れるデータの方式について
 ①詳しくは、下記を参照して下さい。
  M I D I 1.0 規格書

 ②概略を下記に示します。(ChatGPTより)

 ## **1. データの基本構造**
 MIDIデータは**1バイト(8ビット)単位**で送信されます。
 1バイトは以下の形式で構成されています:

 ### **1.1 ステータスバイト**
  **最上位ビット (MSB)**: 常に `1`。
  **下位7ビット**: メッセージの種類やチャネル番号を指定。

 | ステータスバイト | 機能                        
 |--------------------------|-----------------------------------------------
 |     `1000xxxx`        | ノートオフメッセージ 
 |     `1001xxxx`        | ノートオンメッセージ 
 |     `1010xxxx`        | ポリフォニック・アフタータッチ
 |     `1011xxxx`        | コントロールチェンジ  
 |     `1100xxxx`        | プログラムチェンジ 
 |     `1101xxxx`        | チャネル・アフタータッチ 
 |     `1110xxxx`        | ピッチベンド  
 |     `1111xxxx`        | システムメッセージ  
 |--------------------------|-----------------------------------------------
  `xxxx` はチャネル番号(0~15、16チャネル対応)。

 ### **1.2 データバイト**
  **最上位ビット (MSB)**: 常に `0`。
  **下位7ビット**: 各メッセージに必要な追加データ。

 例:
 * ノートオンメッセージには「音のピッチ」と「ベロシティ」が含まれる。
 * コントロールチェンジには「コントローラ番号」と「値」が含まれる。

 ## **2. 主なメッセージの種類**
 以下は、DIN MIDIでやり取りされる代表的なメッセージです。

 ### **2.1 ノートオン/ノートオフ**
  **目的**: ノート(音符)を演奏開始・停止。
  **フォーマット**:
   *ノートオン: `1001cccc`(チャネル番号を含む) + `0kkkkkkk`(ノート番号)
   + `0vvvvvvv`(ベロシティ)
 *ノートオフ: `1000cccc`(チャネル番号を含む) + `0kkkkkkk`(ノート番号)
   + `0vvvvvvv`(ベロシティ)

 | 項目                | 内容                             
 |--------------------|------------------------------------------
 | ノート番号     | 0~127(中央Cは60)
 | ベロシティ     | 0~127(0はノートオフ扱い)
 |--------------------|------------------------------------------


 ### **2.2 コントロールチェンジ**
 **目的**: 音量、パンニング、モジュレーションなどのパラメータを変更。
 **フォーマット**:
  `1011cccc`(チャネル番号を含む) + `0ccccccc`(コントローラ番号) + `0vvvvvvv`(値)

 | コントローラ番号 | 機能例 
 |--------------------------|--------------------------
 | 1                            | モジュレーション 
 | 7                            | メイン音量 
 | 10                          | パンニング 
 | 64                          | ダンパーペダル
 |--------------------------|--------------------------

 ### **2.3 ピッチベンド**
 **目的**: 音程を滑らかに変更。
 **フォーマット**:
  `1110cccc`(チャネル番号を含む) + `0vvvvvvv`(LSB) + `0vvvvvvv`(MSB)

 | 項目                 | 内容 
 |---------------------|--------------------------
 | 範囲                 | -8192~8191         
 | デフォルト値   | 0(中央値)          
 |---------------------|--------------------------
 
 ### **2.4 プログラムチェンジ**
 **目的**: 音色(プログラム)の切り替え。
 **フォーマット**:
  `1100cccc`(チャネル番号を含む) + `0ppppppp`(プログラム番号)

 | 項目                   | 内容  
 |-----------------------|------------------------------
 | プログラム番号  | 0~127(音色番号)
 |-----------------------|------------------------------

 ### **2.5 システムメッセージ**
 **目的**: グローバルな操作(全チャネルに影響)。
 **例**:
  **システムリアルタイムメッセージ**: MIDIクロック(テンポ同期)やスタート/ストップ。
  **システムエクスクルーシブ (SysEx)**: メーカー固有の機能。

 ## **3. 伝送方式**
 DIN MIDIでは、以下の方式でデータが送信されます。

 ### **3.1 物理規格**
 **ケーブル**: 5ピンDINケーブル(通常、ピン2がGND、ピン4とピン5がデータ用)。
 **伝送速度**: 31.25 kbps (非同期)。
 **信号レベル**: 電圧範囲は約5V。

 ### **3.2 データ伝送**
 *MIDIデータはシリアル通信方式で1バイトずつ送信。
 *スタートビット、8ビットデータ(LSB先行)、ストップビットで構成されます。

 | 項目                  | 内容          
 |----------------------|-----------------------
 | スタートビット | 1ビット (0固定) 
 | データビット     | 8ビット  
 | ストップビット | 1ビット (1固定) 
 |----------------------|-----------------------

 ### **3.3 データの順序**
 送信されるデータの順序は以下の通り:
 1. ステータスバイト(メッセージタイプとチャネル)
 2. データバイト(必要に応じて1~2バイト)

例:
  ノートオン (チャネル1、ノート60、ベロシティ127):  
   `10010000`(ステータスバイト)  
   `00111100`(ノート番号60)  
   `01111111`(ベロシティ127)

 ## **4. まとめ**
 DIN MIDIはシンプルかつ堅牢なプロトコルで、音楽デバイス間のコミュニケーションを
 標準化しています。シリアル通信を利用し、音符の演奏、音色の変更、コントローラの
 操作、テンポ同期など、幅広い音楽情報をリアルタイムで伝送可能です。
 
今日は、ここまで
2024/11/30追記
部品が来て、完成しましたので、下記ブログ内記事を参照してみてください。
・M5StampS3 にて USB MIDI と DIN MIDI の変換アダプタを作ってみた




ブログトップへ

◆先日のファミコンのエミュレータをPS4コントローラで動かしてみました。
 nescat_ps4

 PS4コントローラをBluetoothで接続するのにメモリ不足でコンパイルエラーと
 なり、何処かのメモリ割り当てを削ればいいか試行錯誤して大変でした。
 最終的にSDカードのゲーム数とファイル名の文字数を妥協してメモリを開放し
 動作出来ました。
 
 1003 #define MAXFILES 24 //256
 1004 #define MAXFILENAME_LENGTH 64 //255
 
 ここの「MAXFILES」と「MAXFILENAME_LENGTH」を減らしてメモリを
 確保しています。

 下記の①②の続編ですので下記を参照して環境を作って下さい。
 ①もとのエミュレータについては、下記ブログ内記事を参照して下さい。
 ・ESP32 NESCAT で ファミコン の エミュレータ を 動かしてみた
 
 ②接続などは下記ブログ内記事を参照して下さい
 ・ESP32 NesCat ファミコンのエミュレータをWiiコントローラで動かしてみた

1.PS4コントローラを使うための準備
  ①PS4コントローラ用のライブラリが必要ですのでインストールします。
 ps4_
 ②あとPS4コントローラにESP32のBluetoothのアドレスを記憶させる必要が
  あります。やり方は、下記ブログ内記事を参照して下さい。
 ・ESP32 (M5StickC) で PS4コントローラ を使ってみた
 
2.スケッチは、前記事①②の続編ですので。
 「NesCat.ino」を差し替えて動作します。
 (保障無しの自己責任で)  nescat_ps4.zip
 
以上 PS4のコントローラをワイヤレスで使えました。


ブログトップへ

◆先日のファミコンのエミュレータをWiiコントローラで動かしてみました。
 nescat-wii

 I2C接続のコントローラのデータ取得に時間がかかり、解決するのに色々
 苦労しましたが、最終的に動作出来ました。
 
 ①もとのエミュレータについては、下記ブログ内記事を参照して下さい。
 ・ESP32 NesCat で ファミコン の エミュレータ を 動かしてみた

 ②Wiiコントローラについては、下記ブログ内記事を参照して下さい。
 ・AtomS3Liteを使いWiiControllerをUSB化してPCで使ってみた

1.前記事でのESP32のピン接続では、I2C用のGPIOを別の目的に使っていましたので
 全体的に、見直しをしてI2C用に確保しました。
 ESP32_pin_wii
 
 ESP32_gpio_wii

 ①WiiコントローラとESP32との配線は、下記の通りです。
 ESP32_wii

 ②全体の接続図を下記に示します。
 ESP32_nescat_wii

2.各部品の入手先
 ①waves NodeMCU-32 開発ボード ESP32 ESP-WROOM-32 WiFi Bluetooth
 ②1.54インチフルカラーLCDディスプレイモジュー240x240 ピクセル ドライバーIC 7789
 ➂マイクロSDカードメモリシールドモジュール
 ④Max98357 I2S 3 ワットクラス D アンプブレークアウトインターフェースボード
 ⑤Wiiコントローラ リサイクルショップ(300円)

3.今回追加のWiiコントローラ関連部分のスケッチ抜粋
 
 ①Wiiコントローラ用グローバル変数
 (324行から)
 //Wiiコントローラの入力処理
 uint8_t wii_value;

 ②Wiiコントローラ部の別タスク処理部分
 (922行から)
 //Wiiコントローラの入力処理
 void Wii_Task(void *parameter) {
   wiiCC.init( ); //Wiiコントローラ追加
   pinMode(LED,OUTPUT);
   uint8_t value;
   while (1) {
     value=0; 
     wiiCC.scan( );
     if (wiiCC.isABtn()) value |= 1; //A
     if (wiiCC.isBBtn()) value |= 2; //B
     if (wiiCC.isSelectBtn()) value |= 4; //SELECT
     if (wiiCC.isStartBtn()) value |= 8; //START
     if (wiiCC.isUp()) value |= 16; //UP
     if (wiiCC.isDown()) value  |= 32; //DOWN
     if (wiiCC.isLeft()) value |= 64; //LEFT
     if (wiiCC.isRight()) value |= 128; //RIGHT
     if (value) digitalWrite(LED, HIGH);
     wii_value=value;
     delay(10);
     digitalWrite(LED, LOW);
   }
 }
 *****
 *****
 
 (961行から) 
   xTaskCreatePinnedToCore(Wii_Task, "Wii_Task", 4096, NULL, 1, NULL, 0);
   if (DEBUG) Serial.println("Wii_Task Pinned To Core 0...");

 ②キー入力部分を別タスクからのキー入力に変更
 (593行から)
 uint8_t get_pad0(void) {
   uint8_t value;
   ///  value = (uint8_t) retrieve_type(INP_JOYPAD0);
   value = 0;
   value = wii_value; //別タスクからのキー入力データ
   if ((((value&0x4)==0x04)) && (((value&0x8)==0x08))) NES_POWER = 0; //EXIT
   if(wii_value) Serial.printf("wii_Key=%02X \n",wii_value);
  *****
  *****

4.スケッチは、まず前記事の環境を作ります。
 ①その中で「NesCatフォルダのNesCat.ino」を修正します。
 ②下記よりWiiコントローラ部のプログラムをダウンロードします。
  WiiController.zip 

 ➂解凍後、「WiiClassicController.h」「WiiClassicController.cpp」を
 「NesCatフォルダ」にコピペします。(先人に感謝です) 
 
 ④手間を省く為に、①➂部のスケッチを下記に「zipファイル」を示します。
 自己責任の下、参考にして下さい。
 (保障無しの自己責任で)  nescat_wii.zip

5.操作部をPS4コントローラに替えてみました。
  下記ブログ内記事も参照してみて下さい。
 ・ESP32 NesCat ファミコンのエミュレータをPS4コントローラで動かしてみた
 
以上 Wiiコントローラを使い快適にレトロゲームを遊ぶことが出来ました。


ブログトップへ

◆下記サイトを参考にESP32でファミコンエミュレータを動かしてみました。
 *参考サイト(先人に感謝です)
 
 最初中々sdカードが読めないとか、コンパイル出来ないとか大変でしたが
 一応ブレッドボード上で動く事を確認出来ました。
 1.54インチLCDが小さく見にくいためビデオ出力も出来るようにしました。
 
 ①スーパーマリオ動作画面
 スーパーマリオ動作画面-1

 ②スーパーマリオ動作画面(LCD)
 スーパーマリオ動作画面-LCD

 ➂ゲーム選択画面
 ゲーム選択画面

 ④ゲーム選択画面(LCD)
 ゲーム選択画面-LCD

1.開発環境 2024/11/10時点
  ①Windows11 X64 Pro 24H2 
  ②Arduino IDE 2.3.2 
  (2.3.3は起動時フリーズするのでバージョンダウンしております)
  ➂ESP32ボードバージョン 2.0.11
  (最新の3.0.5ではコンパイルエラーが発生するので落としてあります。)

  *以下ライブラリ
  ④SdFat by Bill Greiman 1.0.5
  ⑤Adafruit ST7735 and ST7789 Library by Adafruit 1.10.4
  ⑥Adafruit GFX Library by Adafruit 1.11.11

2.参考サイトとスケッチが違うところ
 ◆参考スケッチダウンロードサイト
 【NesCat Emulator(Sketch) zipファイル

 ◆下記は「NesCat.ino」の修正箇所(左:オリジナル 右:変更後)
 
 ①解凍後のフォルダ(NesCatフォルダのNesCat.inoを編集)
 nescat-01

 ②GPIOのピンアサインを変更
 nescat-02

 ➂コンポジットビデオ出力追加に必要
 nescat-03

 ④コンポジットビデオ出力追加に必要
 nescat-04

 ⑤入力ポートのHLロジックの反転(SW入力で「0」)
 nescat-05

 ⑥入力ポートのHLロジックの反転(SW入力で「0」)
 nescat-06

 ⑦setup設定
 1.(5592行~)入力ポートを「INPUT_PULLUP」に変更(抵抗を減らすため)
  但し、GPIO36,39,34,35の入力専用ポートは、「INPUT_PULLUP」が設定できず
  外部に10KΩの抵抗でプルアップしています。
 2.(5614行)コンポジットビデオ出力追加に必要
 3.(5622行)使用のLCD仕様に変更
 4.(5625-5626行)LCDの初期化成功を確認する為に追加
 nescat-07

 ⑧「sdFat」ライブラリの中の下記ファイルを変更する必要があります。
  最初気付くまでコンパイルが通らず大変でした。
 「"C:\Users\user\Documents\Arduino\libraries\SdFat\src\SdFatConfig.h"」
  79行目を「0」から「1」へ変更して「SOFTWARE_SPI」を使えるようにします。

 sdfatconfig_h

 ◆ファミコンエミュレータについては、下記ブログ内記事も参照してみて下さい。
 ・ESP32 esp_8_bit で ファミコン の エミュレータ を 動かしてみた

 ◆操作部をWiiコントローラに替えてみました。下記を参照してみて下さい。
 ・ESP32 NesCat ファミコンのエミュレータをWiiコントローラで動かしてみた

 ◆操作部をPS4コントローラに替えてみました。下記を参照してみて下さい。
 ・ESP32 NesCat ファミコンのエミュレータをPS4コントローラで動かしてみた

以上 ESP32でファミコンエミュレータが動きました。


 

ブログトップへ

◆GAMINJA-X7 4.3インチ携帯ゲーム機を入手したので使ってみました。
 x7-2

 *入手先下記(AliExpress)
 GAMINJA-X7ゲームのポータブルコンソール,ipsスクリーン,HDプレーヤー
 ,10000ゲーム,4.3in,gba,gbc,gbc


 使い方などは、下記の動画が参考になります。
 ①X7 Handheld Game Console (Review)
 ②Quick Update X7 Handheld Running PS1 (DECENTLY)
 ➂ゲームを追加するにはどうすればよいですか? / X7 スイッチ クローン ハンドヘルド
 ④X7ハンドヘルド...それは良いですか??

1.取説原本
 x7取説原本-1
 x7取説原本-2

2.各部名称
 X7_キー配置-2
 ①START (ショートプレス - ゲームスタート)
 ②SELECT キー
 ➂TFカード(最大サポート32G)
 ④Reset キー(画面がダウンして再起動したときにクリックできます)
 ⑤ヘッドフォン インターフェイス / TV-UOT インターフェイス
 ⑥USB Mini-B 充電/データ インターフェイス
 ⑦ESC ゲーム終了キー
 ⑧ジョイステック左 ハンドル操作の主な方向
 ⑨ジョイステック右 同期化10
 ➉ [O key home page] または [game (confirm key)] をダブルクリックするか
  Xキーホームページ(終了キー)をダブルクリックするか
  残りの2つはファンクションキーです。
 ⑪Rキー
 ⑫ + - ボリュームキー
 ⑬ (起動ボタン) ロックボタン(充電ロック解除ボタンをOに押す)
 ⑭シャットダウンキー/保存キー(長押し)
 ⑮Lキー
 ⑯上下左右キー


今日は、ここまで





ブログトップへ

◆JC3248W535を入手したので以前に作っていたパタパタ時計を移植してみました。
 fclock14
 fclock16

 簡単に行けるかと思いましたが、これが色々と大変でした。
 ①LCDのインターフェースが、今までと違ってQSPI仕様。
 ②描画の最後にフラッシュが必要。
 ➂GXFインターフェースで200KB以上メモリを使ってしまう。
 ④ESP32S3の為、シリアルモニタを使うためにコンパイラオプションの設定。
 ⑤内蔵メモリでは、{WiFi」と共存できない。
 ⑥外部USB電源だと起動しない。(パソコンに繋ぐと起動する。)
 などなど苦労したので忘れないために備忘録に残して置きます。
 
1.ハード構成とGPIOマッピング
 ①下記は、コネクタやLCDのGPIOマッピングです。
 JC3248W535_GPIO

 ②回路図
  2025/02/01 追記 (I2CのSDA,SCLの回路図が間違っていました。
  タッチパネルを使ようとしたらSDA IO8,SCL IO4)では、動きませんでした。
  実際は、SDA IO4 ,SCL IO8に接続されているようです。)


 JC3248W535-1
 
 JC3248W535_回路図2

 ➂ボードの裏面
 JC3248W535_裏

2.スケッチとIDEの設定
 esp32s3_psram設定

 (保障無しの自己責任で)  JC3248W535_FlipClockV1.zip


 ◆下記ブログ内記事も参照してみて下さい。
 ・JC3248W535 ESP32S3 で ドラムシンセマシンを動かしてみた


以上

ブログトップへ



以上の内容はhttps://gijin77.blog.jp/archives/2024-11.htmlより取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14