2025年4月19日土曜日

ESP32-S3をドライバ不要でWinUSBデバイスにしてみた。

 ESP32-S3にはUSB機能がついてるのでTinyUSBを使って独自のUSBデバイスが作れるらしい。デバッグでよく使うUSB CDCで仮想シリアルポートとしてしかUSB機能を使っていなかったが、まずはWinUSBデバイスとしてPCで認識できるかやってみた。

Microsoft OS 1.0 DescriptorsのWCID機能を使ってやればWindows 8以降でUSBドライバも不要ならしいのでまずはWCID機能で自動でドライバがインストールされるかどうかを試してみることに。Microsoft OS 2.0 Descriptorsはちょっと難しそうだったのでとりあえず1.0で…

#include <Arduino.h>
#include "USB.h"
#include "tusb.h"

static constexpr uint16_t MY_USB_VID = 0x303A;
static constexpr uint16_t MY_USB_PID = 0x4004;

static constexpr uint8_t MS_OS_VENDOR_CODE = 0x20;
static constexpr uint8_t MS_OS_STRING_INDEX = 0xEE;


// ============================================================
// Microsoft OS 1.0 String Descriptor
// Index = 0xEE
//
// "MSFT100"
// VendorCode = 0x20
// ============================================================

static const uint16_t ms_os_string[] = {
  0x0312,

  0x004D,  // M
  0x0053,  // S
  0x0046,  // F
  0x0054,  // T
  0x0031,  // 1
  0x0030,  // 0
  0x0030,  // 0

  0x0020  // VendorCode = 0x20
};

static_assert(
  sizeof(ms_os_string) == 18,
  "MS OS String Descriptor must be exactly 18 bytes");


// ============================================================
// Microsoft OS 1.0 Extended Compatible ID Descriptor
//
// CompatibleID = WINUSB
// Interface 0
// ============================================================

static const uint8_t ms_os_compatible_id[] = {

  // dwLength = 40
  0x28, 0x00, 0x00, 0x00,

  // bcdVersion = 1.00
  0x00, 0x01,

  // wIndex = 0x0004
  0x04, 0x00,

  // bCount = 1
  0x01,

  // reserved[7]
  0x00,
  0x00,
  0x00,
  0x00,
  0x00,
  0x00,
  0x00,

  // bFirstInterfaceNumber = 0
  0x00,

  // reserved
  0x00,

  // CompatibleID = "WINUSB"
  'W',
  'I',
  'N',
  'U',
  'S',
  'B',
  0x00,
  0x00,

  // SubCompatibleID
  0x00,
  0x00,
  0x00,
  0x00,
  0x00,
  0x00,
  0x00,
  0x00,

  // reserved[6]
  0x00,
  0x00,
  0x00,
  0x00,
  0x00,
  0x00
};

static_assert(
  sizeof(ms_os_compatible_id) == 40,
  "MS OS Compatible ID Descriptor must be exactly 40 bytes");


// ============================================================
// Microsoft OS 1.0 Vendor Control Request
// ============================================================

extern "C" bool tinyusb_vendor_control_request_cb(
  uint8_t rhport,
  uint8_t stage,
  tusb_control_request_t const *request) {
  if (request == nullptr) {
    return false;
  }

  if (stage != CONTROL_STAGE_SETUP) {
    return true;
  }

  const uint8_t bmRequestType = request->bmRequestType;
  const uint8_t bRequest = request->bRequest;
  const uint16_t wValue = request->wValue;
  const uint16_t wIndex = request->wIndex;
  const uint16_t wLength = request->wLength;

  if (bmRequestType == 0xC0 && bRequest == MS_OS_VENDOR_CODE && wValue == 0x0000 && wIndex == 0x0004) {

    uint16_t responseLength =
      sizeof(ms_os_compatible_id);

    if (wLength < responseLength) {
      responseLength = wLength;
    }

    return tud_control_xfer(
      rhport,
      request,
      (void *)ms_os_compatible_id,
      responseLength);
  }

  return false;
}


// ============================================================
// USB String Descriptor
// ============================================================

extern "C" const uint16_t *tud_descriptor_string_cb(
  uint8_t index,
  uint16_t langid) {
  static uint16_t desc[64];

  // Microsoft OS 1.0
  if (index == MS_OS_STRING_INDEX) {
    return ms_os_string;
  }

  // LANGID
  if (index == 0) {
    desc[0] = 0x0304;
    desc[1] = 0x0409;
    return desc;
  }

  // Manufacturer
  if (index == 1) {

    const char *s = "M5Stack";
    uint8_t n = strlen(s);

    desc[0] =
      (uint16_t)(0x0300 | (2 + n * 2));

    for (uint8_t i = 0; i < n; i++) {
      desc[1 + i] = s[i];
    }

    return desc;
  }

  // Product
  if (index == 2) {

    const char *s = "M5AtomS3 WinUSB";
    uint8_t n = strlen(s);

    desc[0] =
      (uint16_t)(0x0300 | (2 + n * 2));

    for (uint8_t i = 0; i < n; i++) {
      desc[1 + i] = s[i];
    }

    return desc;
  }

  // Serial
  if (index == 3) {

    const char *s = "ATOM-S3-WINUSB";
    uint8_t n = strlen(s);

    desc[0] =
      (uint16_t)(0x0300 | (2 + n * 2));

    for (uint8_t i = 0; i < n; i++) {
      desc[1 + i] = s[i];
    }

    return desc;
  }

  // Interface
  if (index == 4) {

    const char *s = "WinUSB";
    uint8_t n = strlen(s);

    desc[0] =
      (uint16_t)(0x0300 | (2 + n * 2));

    for (uint8_t i = 0; i < n; i++) {
      desc[1 + i] = s[i];
    }

    return desc;
  }

  return nullptr;
}


void setup() {
  USB.VID(MY_USB_VID);
  USB.PID(MY_USB_PID);

  USB.begin();
}


void loop() {
  delay(10);
}

とりあえずM5AtomS3 LiteがあったのでCDC on BootをDisableにして、USB ModeをTinyUSBに設定してこのスケッチを書き込めば動くと思う。

途中色々試してようやくZadigでWCIDが✓になり、デバイスマネージャーで勝手に認識されるように。ミスったデバイスはいちいちデバイスマネージャーで削除してからやらないとうまく行っていても認識されないので、ZadigのWCIDの欄を確認しながら試したほうがいいかも。

CDC on BootをDisableにして、TinyUSBを使うとCDCデバイスが出なくなるので書き換えの際はリセットボタンを緑色のLEDがつくまで長押しして離すとCOMポートが出てくるのでそれを選んで書き込めばOK。

デバイスマネージャーで見てもちゃんとドライバが勝手にインストールされていた。Zadig不要でWinUSBドライバ勝手に入るの便利。管理者権限とかも要らないしね。

USB\MS_COMP_WINUSBがちゃんと追加されてる。

とりあえず認識するようになったのであとはエンドポイントを追加してPythonツールからデータ送受信ができるかどうか…

ちなみにHIDデバイスとかでもPythonツール作ってデータの送受信ができるみたいだけどWinUSBだとバルク転送も使えるのでより高速らしい。
こういったデバイスはZadigでlibusbとかWinUSBのドライバを強制的にインストールしないといけないのが面倒だったんだけど、勝手にドライバがインストールされるのは便利かもしれない。

2025年4月6日日曜日

Latitude E5470にWindows 11を入れてみた。

 知り合いがPCが未だにWindows 8.1だったのでWindows 11にアップグレードしてみた。DELLのLatitude E5470で、Core i7 6600UなのでDELL公式でもWindows 11はサポートしていないっぽい。TPMも1.2だったし。

しかしTPMを2.0にアップグレードできれば新規インストールでWindows 11が普通に使えるはず…ということでやってみた。

まずはBIOS更新から。DELLのPCは裏面にサービスタグがあるのでDELLの公式ページにサービスタグを入力してBIOSを更新してみた。OSはクリーンインストールするのでBIOSとTPMファームウェアアップデート以外はとりあえず今はやらなくても良さそう。

そこでTPMのファームウェアなんだけど、1.2は表示されてるのに2.0はリストにない…(このLatitude E5470は非対応ってこと?

でもDell TPM 2.0ファームウェア アップデート ユーティリティー 1.3.2.8, A03のリストにはE5470が乗っていたのでとりあえずこちらで更新してみることに。DellTpm2.0_Fw1.3.2.8_V3_64.exeをダウンロードしてきて実行してみると…一応再起動までするけど何も起こらず。

気を取り直して、BIOSの設定に入りTPMをいったんdisableにしてからクリアしてみる。(機能自体のオフではない)そしてもう一度実行してみると…

今度は再起動したあとにファームウェア更新中の画面に!

再起動後、デバイスマネージャーからTPM2.0になっていることを確認。

これでTPM2.0になったのでWindows 11が動かせる。Windows 11のインストールUSBメモリを作成して再起動してF12連打でブートメニューが出るのでそこからWindows 11をインストールすることができた。

TPM2.0がなくてもインストールUSBメモリに細工をすればインストールできるらしいけど今回はTPM2.0対応ということで正規にインストールできた。アップグレード非対応のCPUだったけども、新規インストールなら対応しているしサクサク動いているのでまだしばらく使えそう。SSDもM.2のSSDが入っていたし…

DELL公式では非対応ってなっているけども、DELL純正ソフトウェアもWindows 10のをインストールして使うことができた。ちなみにプロダクトキーは入力しなくても勝手にライセンス認証された。おそらくこのPC自体にWindows 10のキーも含まれているやつだったのかな?

2025年4月5日土曜日

DC電源タイプのPD充電ボードを買ってみた。

 USB PDに対応した充電器って言うとコンセントに直刺しタイプが多いけども、DC入力タイプの充電ボードが売っていたので購入してみた。

CKCS U1Eってやつの互換品かな?CKCSというロゴは入っていないけどU1Eと書いてある。降圧DCDCコンバータとUSB PDのネゴシエーションがワンチップになってる感じかな?搭載されているチップはレーザで刻印が消されているようだけども、どうやらXPM52CというICらしい。今回はDCジャック付のDC5521バージョンを購入してみた。外径5.5内径2.1の略らしい。

裏面には仕様が書いてあって、8V-30Vまでの対応らしい。一応最大出力65W(20V時かな?)。

降圧しか対応していないのでPCの充電とかで20Vの65Wを使用する際は20V以上の電圧を入力する必要があるっぽい。

試しに12Vを入力してモバイルバッテリーやiPhoneを充電してみた。デバイスに給電開始するとLEDが青く光るっぽい。デバイスを取り外すとLEDが消えるようになっていた。

65Wフルに使ったりする場合はICにヒートシンクを取り付けられるようにしたほうがいいかも。車の12Vで65W供給のPCとかで使う場合は一旦20V以上に昇圧するか、昇降圧対応のSW2303を買わないとだめかも。

シガーソケット用のUSB PD充電器で65Wとか100Wの製品は存在するんだけど、安いやつは中身が上の基板と同じようなやつなんだろうな…
パソコンを充電する場合は20V出力が必要だったりするので、AUKEY CC-Y24とかだと12V車でも昇圧で20V出力も使えますって明記されてたり、UGREEN 130Wは結構いろんなところのレビューでPDテスタで20V出てそうな感じなので100W出力ポートに関しては昇圧してそう。
Essager 100W F698もYoutubeでテストしていた人がいたので昇降圧対応してそう。 

昇圧しないタイプだと12Vで3AとかなのでPC側でエラーが出て充電できないかも。車でPCを充電したい人は12V車でもちゃんと20V出力まで対応しているのか確認してから買ったほうが良さそう。