0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AtomS3Rを使ったUSB Host CDC ACMデータ送受信

Last updated at Posted at 2025-07-03

概要

以前からマイコンのUSB Host機能を使ってバーコードリーダーなどのCDC-ACM対応
デバイスでデータの送受信を行いたいと考えていました。
今回、生成AIの手を借りて何とかうまく動作させることができましたので記事にします。
何かの参考になれば幸いです。

ホストとしてM5stack社のAtomS3Rマイコン、デバイスとしてM5StickC Plus2を使い、
USBを介して相互にシリアル送受信(テキスト形式)を行いました。
デバイス側から1秒毎に文字列送信しています。ホスト側はUSB-TTL変換デバイスで
PCと接続しシリアルモニターから文字列送信しています。

<接続は下記>
PC <--USB-TTL変換ケーブル--> AtomS3R <--USBケーブル--> M5StickC Plus2 

image.png
image.png

ログの出力について

動作状況はPCのシリアルモニタに出力されます。
[USB] client_async_seq_task loop alive
[USB] client_async_seq_task loop alive
[USB] Device Connected
Trying to open device at address: 1
usb_host_device_open returned: 0x0
dev_hdl after open: 0x3fcee420
[USB] host.open() = Success
Interface 0 class: 0x02
Interface 1 class: 0x0A
CDC ACM endpoints assigned successfully.
USBacmDevice::init() start
usb_host_interface_claim OK for control interface 0
usb_host_interface_claim OK for data interface 1
USBacmDevice::init() success
[USB] ACM Device Initialized
[USB] client_async_seq_task loop alive
[IN] Received 13 bytes
[DATA] CDC Test:59

[USB] client_async_seq_task loop alive
[EVENT] Set Line Coding Complete
[USB] client_async_seq_task loop alive
[USB] client_async_seq_task loop alive
[USB] client_async_seq_task loop alive
[USB] client_async_seq_task loop alive
[USB] client_async_seq_task loop alive
[USB] client_async_seq_task loop alive
[IN] Received 13 bytes
[DATA] CDC Test:60

[USB] client_async_seq_task loop alive
[USB] client_async_seq_task loop alive
[USB] client_async_seq_task loop alive
[USB] client_async_seq_task loop alive
[USB] client_async_seq_task loop alive
[IN] Received 13 bytes
[DATA] CDC Test:61

環境など

<開発環境>
エディタ:VisualStudioCode(PlatformIO IDE)
フレームワーク:Arudino
マイコン:Host側 AtomS3R、デバイス側 M5StickC Plus2
ライブラリ:ESP32TinyUSB https://github.com/chegewara/EspTinyUSB
 ⇒ベースのままではうまく動作しなかったため修正を加えています
USB-TTL変換デバイス(PC-ホスト間通信用)

Host側 plathomeio.ini設定(AtomS3R)

[env:m5stack-atoms3]
platform = espressif32
board = m5stack-atoms3
framework = arduino
build_flags =
-DARDUINO_USB_MODE=2
-DARDUINO_USB_CDC_ON_BOOT=0
lib_deps =
m5stack/M5AtomS3 @ ^1.0.2
m5stack/M5Unified
fastled/FastLED @ ^3.10.1
https://github.com/chegewara/EspTinyUSB.git

lib_extra_dirs =
.pio/libdeps/m5stack-atoms3/ESP32TinyUSB/src

platform_packages =
platformio/framework-espidf@^3.50401.0

Host側 mainコード

#include <Arduino.h>
#include <M5Unified.h>
#include "usb/usb_host.h"
#include "usb_host.hpp"
#include "usb_acm.hpp"

USBhost host;
USBacmDevice* device = nullptr;
bool usbdevice_connected=false;// デバイス接続状態フラグ


// 表示用関数
void disp(int x, int y, uint16_t fcolor, uint16_t bcolor, int size, String msg) {
   if (size == 12) M5.Display.setFont(&fonts::efontJA_12);
   else if (size == 14) M5.Display.setFont(&fonts::efontJA_14);
   else if (size == 16) M5.Display.setFont(&fonts::efontJA_16);
   else if (size == 24) M5.Display.setFont(&fonts::efontJA_24);

   M5.Display.setTextWrap(false);
   M5.Display.setTextColor(fcolor, bcolor);
   int textLength = msg.length() / 3;
   M5.Display.fillRect(x, y, textLength * size, size, BLACK);
   M5.Display.setCursor(x, y);
   M5.Display.print(msg);
}


// ACM受信イベント
void acm_events(int event, void* data, size_t len) {
   switch (event) {
       case CDC_CTRL_SET_CONTROL_LINE_STATE:
           Serial1.println("[EVENT] Set Control Line State");
           if (device) {
               device->setLineCoding(115200, 0, 0, 8);
               delay(100);
               device->submitReadTransfer();  // 最初の受信
           }
           break;

       case CDC_DATA_IN:
           Serial1.printf("[IN] Received %d bytes\n", len);
           if (data && len > 0) {
               // 受信データを安全に表示
               uint8_t* buf = (uint8_t*)data;
               char printbuf[129];  // 最大128文字+終端
               size_t copy_len = (len < 128) ? len : 128;
               memcpy(printbuf, buf, copy_len);
               printbuf[copy_len] = '\0';
               Serial1.printf("[DATA] %s\n", printbuf);
               disp(5, 76, WHITE, BLACK, 16, String(printbuf));
           }

           // 【ここが重要!!】次の受信要求を出す!
           if (device) {
               device->submitReadTransfer();
           }
           break;

       case CDC_DATA_OUT:
           Serial1.println("[OUT] Data sent.");
           break;

       case CDC_CTRL_SET_LINE_CODING:
           Serial1.println("[EVENT] Set Line Coding Complete");
           break;

       default:
           Serial1.printf("[EVENT] Unknown Event: %d\n", event);
           break;
   }
}


// USBデバイス接続/切断コールバック
void client_event_callback(const usb_host_client_event_msg_t* event_msg, void* arg) {

   if (event_msg->event == USB_HOST_CLIENT_EVENT_NEW_DEV) {// デバイス接続時
       Serial1.println("[USB] Device Connected");
       usbdevice_connected=true;
       disp(10, 40, GREEN   , BLACK, 16, "Connected   ");

       bool opened = host.open(event_msg);
       Serial1.printf("[USB] host.open() = %s\n", opened ? "Success" : "Fail");

       const usb_device_desc_t* dev_desc = host.getDeviceDescriptor();
       const usb_config_desc_t* config_desc = host.getConfigurationDescriptor();

       int offset = 0;
       for (size_t i = 0; i < dev_desc->bNumConfigurations; i++) {
           for (size_t n = 0; n < config_desc->bNumInterfaces; n++) {
               const usb_intf_desc_t* intf = usb_parse_interface_descriptor(config_desc, n, 0, &offset);
               if (intf && intf->bInterfaceClass == 0x0A) {  // CDC ACM
                   // USBacmDevice インスタンス生成
                   device = new USBacmDevice(config_desc, &host);
                   if (device && device->init()) {
                       Serial1.println("[USB] ACM Device Initialized");
                       device->onEvent(acm_events);
                       device->setControlLine(1, 1);
                       device->INDATA();
                   } else {
                       Serial1.println("[USB] ACM Device init failed");
                   }
                   return;  // CDC ACM が1つ見つかればここで終了
               }
           }
       }
   }else if (event_msg->event == USB_HOST_CLIENT_EVENT_DEV_GONE) {// デバイス切断時
       Serial1.println("[USB] Device Disconnected");
       usbdevice_connected=false;
       disp(10, 40, MAGENTA , BLACK, 16, "Disconnected");

       if (device) {
           device->cleanup();
           delete device;
           device = nullptr;
       }

       host.close();  // 明示的にデバイスクローズ

       // 再接続に備えてイベントループ再起動
       Serial1.println("[USB] Restarting USB host task for reconnection...");
       host.init(true);  // ← タスク再生成も含めて再初期化
   }
}


void setup() {
   auto cfg = M5.config();
   M5.begin(cfg);
   Serial.begin(115200);
   Serial1.begin(115200, SERIAL_8N1, 1, 2);

   disp(0, 0, GREEN, BLACK, 16, "USB Host CDC-ACM");
   disp(0, 20, WHITE, BLACK, 16, "Status");
   disp(10, 36, MAGENTA , BLACK, 16, "Disconnected");
   disp(0, 60, WHITE , BLACK, 16, "ReceveData");
   
   host.init();
   host.registerClientCb(client_event_callback);
}


void loop() {
   // シリアルデータ受信 ⇒ CDCACMデバイスへ送信
   if(Serial1.available()>0){
       String rev_str = Serial1.readStringUntil('\n'); //終端文字までを取得
       const char* msg = rev_str.c_str();
       device->OUTDATA((uint8_t*)msg, strlen(msg));
   }
   delay(10);
}

ESP32TinyUSBライブラリ修正 (usb_host.cpp)

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "usb/usb_host.h"
#include "usb_host.hpp"
#include "usb/usb_types_stack.h"
#include "tusb.h"
#include "usb_acm.hpp"
#include <Arduino.h>
#include <M5Unified.h>

USBhost usbHost;

void _client_event_callback(const usb_host_client_event_msg_t *event_msg, void *arg)
{
   USBhost *host = (USBhost *)arg;

   ESP_LOGI("", "client event: %d", event_msg->event);

   if (host->_client_event_cb)
   {
       host->_client_event_cb(event_msg, arg);  // ← ユーザー定義のコールバックに渡す
   }
   else
   {
       if (event_msg->event == USB_HOST_CLIENT_EVENT_NEW_DEV)
       {
           host->open(event_msg);
       }
       else if (event_msg->event == USB_HOST_CLIENT_EVENT_DEV_GONE)
       {
           host->close();  // ← 必要なら明示的にクローズ処理
       }
   }
}

static void client_async_seq_task(void *param)
{
   usb_host_client_handle_t client_hdl = *(usb_host_client_handle_t *)param;
   uint32_t event_flags;

   while (1)
   {
       // USBクライアントイベント待ち (timeout = 10ms)
       esp_err_t err = usb_host_client_handle_events(client_hdl, 100);
       if (err != ESP_OK && err != ESP_ERR_TIMEOUT) {
           Serial1.printf("[USB] usb_host_client_handle_events error: 0x%X\n", err);
       }

       // ライブラリイベント (デバイス追加/削除など)
       if (ESP_OK == usb_host_lib_handle_events(100, &event_flags))
       {
           if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS)
           {
               Serial1.println("[USB] No more clients, freeing devices...");
               usb_host_device_free_all();
           }
           if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE)
           {
               Serial1.println("[USB] All resources free. Exiting client task.");
               break;
           }
       }

       // 定期ログで動作確認
       Serial1.println("[USB] client_async_seq_task loop alive");
       vTaskDelay(pdMS_TO_TICKS(10));  // 10msスリープ  // ←暴走防止に必須
   }

   // クライアント解放
   usb_host_client_deregister(client_hdl);
   vTaskDelete(NULL);
}


USBhost::USBhost()
{
}

USBhost::~USBhost()
{
   usb_host_device_close(client_hdl, dev_hdl);
}

bool USBhost::init(bool create_tasks)
{
   const usb_host_config_t config = {
       .intr_flags = ESP_INTR_FLAG_LEVEL1,
   };

   esp_err_t err = usb_host_install(&config);
   if (err == ESP_ERR_INVALID_STATE) {
       ESP_LOGW("", "USB host already installed. Skipping install.");
   } else {
       ESP_LOGI("", "install status: %d", err);
   }

   const usb_host_client_config_t client_config = {
       .is_synchronous = false,
       .max_num_event_msg = 5,
       .async = {
           .client_event_callback = _client_event_callback,
           .callback_arg = this
       }
   };

   err = usb_host_client_register(&client_config, &client_hdl);
   ESP_LOGI("", "client register status: %d", err);

   if (create_tasks)
   {
       xTaskCreate(client_async_seq_task, "async", 4 * 8192, &client_hdl, 20, NULL);
   }

   return true;
}

bool USBhost::close()
{
   if (deviceHandle()) {
       usb_host_device_close(clientHandle(), deviceHandle());
       clearDeviceHandle();
   }

   if (clientHandle()) {
       usb_host_client_deregister(clientHandle());
       client_hdl = nullptr;
   }

   // 少し待ってからアンインストール(100ms程度)
   vTaskDelay(pdMS_TO_TICKS(100));

   esp_err_t err = usb_host_uninstall();
   Serial1.printf("[USB] usb_host_uninstall result: 0x%X\n", err);

   return (err == ESP_OK);
}


bool USBhost::open(const usb_host_client_event_msg_t *event_msg)
{
   
   Serial1.printf("Trying to open device at address: %d\n", event_msg->new_dev.address);

   esp_err_t err = usb_host_device_open(client_hdl, event_msg->new_dev.address, &dev_hdl);
   Serial1.printf("usb_host_device_open returned: 0x%X\n", err);
   Serial1.printf("dev_hdl after open: %p\n", dev_hdl);

   if (err != ESP_OK || dev_hdl == nullptr) {
       Serial1.println("Failed to open device or device handle is null");
       return false;
   }

   parseConfig();
   return true;
}

typedef struct TU_ATTR_PACKED {
 uint8_t bLength;
 uint8_t bDescriptorType;
 uint8_t bInterfaceNumber;
 uint8_t bAlternateSetting;
 uint8_t bNumEndpoints;
 uint8_t bInterfaceClass;
 uint8_t bInterfaceSubClass;
 uint8_t bInterfaceProtocol;
 uint8_t iInterface;
} usb_interface_desc_t;


#define USB_DESC_TYPE_INTERFACE 0x04

void USBhost::parseConfig()
{
   const usb_config_desc_t *config_desc;
   if (usb_host_get_active_config_descriptor(dev_hdl, &config_desc) != ESP_OK) {
       ESP_LOGE("USBhost", "Failed to get config descriptor");
       return;
   }

   const uint8_t *p = reinterpret_cast<const uint8_t *>(config_desc);
   const uint8_t *end = p + config_desc->wTotalLength;

   while (p < end) {
       uint8_t len = p[0];
       uint8_t type = p[1];

       if (type == USB_DESC_TYPE_INTERFACE) {
           const usb_interface_desc_t *itf = reinterpret_cast<const usb_interface_desc_t *>(p);
           ESP_LOGI("USBhost", "Interface %d: class=0x%02x, subclass=0x%02x, protocol=0x%02x",
                    itf->bInterfaceNumber,
                    itf->bInterfaceClass,
                    itf->bInterfaceSubClass,
                    itf->bInterfaceProtocol);

           if (itf->bInterfaceClass == 0x0A) {
               ESP_LOGI("USBhost", "Found CDC Data Interface: %d", itf->bInterfaceNumber);
               // TODO: Claim interface and find endpoints
           } else if (itf->bInterfaceClass == 0x02) {
               ESP_LOGI("USBhost", "Found CDC Control Interface: %d", itf->bInterfaceNumber);
               // Optional: Handle control interface
           }
       }

       p += len;
   }
}

usb_device_info_t USBhost::getDeviceInfo()
{
   usb_host_device_info(dev_hdl, &dev_info);

   return dev_info;
}

const usb_device_desc_t* USBhost::getDeviceDescriptor()
{
   const usb_device_desc_t *device_desc;
   usb_host_get_device_descriptor(dev_hdl, &device_desc);

   return device_desc;
}

const usb_config_desc_t* USBhost::getConfigurationDescriptor()
{
   const usb_config_desc_t *config_desc;
   usb_host_get_active_config_descriptor(dev_hdl, &config_desc);
   return config_desc;
}

uint8_t USBhost::getConfiguration()
{
   return getDeviceInfo().bConfigurationValue;
}

usb_host_client_handle_t USBhost::clientHandle()
{
   return client_hdl;
}

usb_device_handle_t USBhost::deviceHandle()
{
   return dev_hdl;
}

ESP32TinyUSBライブラリ修正 (usb_acm.cpp)

#include "usb_acm.hpp"
#include "esp_log.h"
#include <string.h>
#include <Arduino.h>
#include <M5Unified.h>

#ifndef USB_DESC_TYPE_ENDPOINT
#define USB_DESC_TYPE_ENDPOINT 0x05
#endif

extern HardwareSerial Serial1;

static const char *TAG = "USBacmDevice";
//uint8_t control_interface_number;  // CDC Controlインターフェイス番号
//uint8_t data_interface_number;  // CDC Dataインターフェイス番号

void IRAM_ATTR USBacmDevice::usb_read_cb(usb_transfer_t *transfer) {
   USBacmDevice* dev = (USBacmDevice*)transfer->context;
   dev->transfer_in_progress = false;
   if (transfer->status == USB_TRANSFER_STATUS_COMPLETED && dev->event_cb) {
       dev->event_cb(CDC_DATA_IN, transfer->data_buffer, transfer->actual_num_bytes);
   }
}

void IRAM_ATTR USBacmDevice::usb_write_cb(usb_transfer_t *transfer) {
   USBacmDevice* dev = (USBacmDevice*)transfer->context;
   if (dev->event_cb) {
       dev->event_cb(CDC_DATA_OUT, nullptr, 0);
   }
}

void IRAM_ATTR USBacmDevice::usb_ctrl_cb(usb_transfer_t *transfer) {
   USBacmDevice* dev = (USBacmDevice*)transfer->context;
   if (dev->event_cb) {
       dev->event_cb(CDC_CTRL_SET_LINE_CODING, nullptr, 0);
   }
}



typedef struct {
   uint8_t bLength;
   uint8_t bDescriptorType;
} usb_desc_header_t;

const usb_ep_desc_t* parse_endpoint_descriptor(const usb_config_desc_t* config_desc, int& offset) {
   while (offset < config_desc->wTotalLength) {
       const usb_desc_header_t* header = (const usb_desc_header_t*)((uint8_t*)config_desc + offset);
       if (header->bDescriptorType == USB_DESC_TYPE_ENDPOINT) {
           return (const usb_ep_desc_t*)header;
       }
       offset += header->bLength;
   }
   return nullptr;
}


USBacmDevice::USBacmDevice(const usb_config_desc_t* config_desc, USBhost* host)
   : USBhostDevice(), _host(host), ep_int(nullptr), ep_in(nullptr), ep_out(nullptr),
     xfer_read(nullptr), xfer_write(nullptr), xfer_ctrl(nullptr),
     connected(false), event_cb(nullptr) {

   int offset = 0;
   for (int i = 0; i < config_desc->bNumInterfaces; ++i) {
       const usb_intf_desc_t* intf = usb_parse_interface_descriptor(config_desc, i, 0, &offset);
       if (!intf) continue;

       Serial1.printf("Interface %d class: 0x%02X\n", i, intf->bInterfaceClass);

       if (intf->bInterfaceClass == 0x0A) { // CDC Data
           data_interface_number = intf->bInterfaceNumber;
           const usb_ep_desc_t* ep1 = parse_endpoint_descriptor(config_desc, offset);
           if (ep1) offset += ep1->bLength;

           const usb_ep_desc_t* ep2 = parse_endpoint_descriptor(config_desc, offset);
           if (ep2) offset += ep2->bLength;

           if (ep1 && (ep1->bEndpointAddress & 0x80)) ep_in = ep1;
           else if (ep1) ep_out = ep1;

           if (ep2 && (ep2->bEndpointAddress & 0x80)) ep_in = ep2;
           else if (ep2) ep_out = ep2;

       } else if (intf->bInterfaceClass == 0x02) { // CDC Control
           control_interface_number = intf->bInterfaceNumber;
           const usb_ep_desc_t* ep = parse_endpoint_descriptor(config_desc, offset);
           if (ep) {
               ep_int = ep;
               offset += ep->bLength;
           }
       }
   }

   if (ep_in && ep_out) {
       Serial1.println("CDC ACM endpoints assigned successfully.");
   } else {
       Serial1.println("Error: Could not find required endpoints!");
   }
}

USBacmDevice::~USBacmDevice() {}

void USBacmDevice::cleanup() {
   Serial1.println("[USB] Cleaning up device resources");

   // 転送キャンセル & free
   if (xfer_read) {
       usb_host_transfer_free(xfer_read);
       xfer_read = nullptr;
   }
   if (xfer_write) {
       usb_host_transfer_free(xfer_write);
       xfer_write = nullptr;
   }
   if (xfer_ctrl) {
       usb_host_transfer_free(xfer_ctrl);
       xfer_ctrl = nullptr;
   }

   // インターフェイス解放
   usb_host_interface_release(_host->clientHandle(), _host->deviceHandle(), control_interface_number);
   usb_host_interface_release(_host->clientHandle(), _host->deviceHandle(), data_interface_number);

   connected = false;
}


bool USBacmDevice::init() {
   Serial1.println("USBacmDevice::init() start");

   if (!_host || !_host->deviceHandle()) return false;

   usb_device_info_t info = _host->getDeviceInfo();

   esp_err_t err;
   // Control interface claim
   err = usb_host_interface_claim(_host->clientHandle(), _host->deviceHandle(), 0, 0);
   if (err != ESP_OK) {
       Serial1.printf("usb_host_interface_claim failed for control interface: 0x%X\n", err);
       return false;
   }
   Serial1.println("usb_host_interface_claim OK for control interface 0");

   // Data interface claim
   err = usb_host_interface_claim(_host->clientHandle(), _host->deviceHandle(), data_interface_number, 0);
   if (err != ESP_OK) {
       Serial1.printf("usb_host_interface_claim failed for data interface: 0x%X\n", err);
       return false;
   }
   Serial1.printf("usb_host_interface_claim OK for data interface %d\n", data_interface_number);

   // Allocate transfers
   err = usb_host_transfer_alloc(64, 0, &xfer_write);
   if (err != ESP_OK) return false;
   xfer_write->device_handle = _host->deviceHandle();
   xfer_write->context = this;
   xfer_write->callback = usb_write_cb;
   xfer_write->bEndpointAddress = ep_out->bEndpointAddress;

   err = usb_host_transfer_alloc(64, 0, &xfer_read);
   if (err != ESP_OK) return false;
   xfer_read->device_handle = _host->deviceHandle();
   xfer_read->context = this;
   xfer_read->callback = usb_read_cb;
   xfer_read->bEndpointAddress = ep_in->bEndpointAddress;

   err = usb_host_transfer_alloc(info.bMaxPacketSize0, 0, &xfer_ctrl);
   if (err != ESP_OK) return false;
   xfer_ctrl->device_handle = _host->deviceHandle();
   xfer_ctrl->context = this;
   xfer_ctrl->callback = usb_ctrl_cb;
   xfer_ctrl->bEndpointAddress = 0;

   transfer_in_progress = false;
   connected = true;
   Serial1.println("USBacmDevice::init() success");
   return true;
}

void USBacmDevice::setControlLine(bool dtr, bool rts) {
   uint16_t value = (dtr ? 1 : 0) | (rts ? 2 : 0);
   //USB_CTRL_REQ_CDC_SET_CONTROL_LINE_STATE((usb_setup_packet_t *)xfer_ctrl->data_buffer, 0, value);
   USB_CTRL_REQ_CDC_SET_CONTROL_LINE_STATE((usb_setup_packet_t *)xfer_ctrl->data_buffer, 0, true, true);
   xfer_ctrl->num_bytes = sizeof(usb_setup_packet_t);
   usb_host_transfer_submit_control(_host->clientHandle(), xfer_ctrl);
}

void USBacmDevice::setLineCoding(uint32_t bitrate, uint8_t cf, uint8_t parity, uint8_t bits) {
   line_coding_t data;
   data.dwDTERate = bitrate;
   data.bCharFormat = cf;
   data.bParityType = parity;
   data.bDataBits = bits;

   USB_CTRL_REQ_CDC_SET_LINE_CODING((usb_setup_packet_t *)xfer_ctrl->data_buffer, 0, bitrate, cf, parity, bits);
   memcpy(xfer_ctrl->data_buffer + sizeof(usb_setup_packet_t), &data, sizeof(line_coding_t));
   xfer_ctrl->num_bytes = sizeof(usb_setup_packet_t) + sizeof(line_coding_t);

   esp_err_t err = usb_host_transfer_submit_control(_host->clientHandle(), xfer_ctrl);
   if (err != ESP_OK) {
       Serial1.printf("setLineCoding failed: 0x%X\n", err);
   }
}

void USBacmDevice::getLineCoding() {
   USB_CTRL_REQ_CDC_GET_LINE_CODING((usb_setup_packet_t *)xfer_ctrl->data_buffer, 0);
   xfer_ctrl->num_bytes = sizeof(usb_setup_packet_t);
   usb_host_transfer_submit_control(_host->clientHandle(), xfer_ctrl);
}

void USBacmDevice::INDATA() {
   if (!connected || !xfer_read) return;
   if (transfer_in_progress) return;

   xfer_read->num_bytes = 64;
   esp_err_t err = usb_host_transfer_submit(xfer_read);
   if (err == ESP_OK) transfer_in_progress = true;
}

void USBacmDevice::OUTDATA(uint8_t* data, size_t len) {
   if (!connected || !xfer_write) return;

   memcpy(xfer_write->data_buffer, data, len);
   xfer_write->num_bytes = len;
   usb_host_transfer_submit(xfer_write);
}

void USBacmDevice::onEvent(cdc_event_cb_t _cb) {
   event_cb = _cb;
}

bool USBacmDevice::isConnected() {
   return connected;
}

bool USBacmDevice::submitReadTransfer() {
   if (!connected || !xfer_read || transfer_in_progress) return false;
   xfer_read->num_bytes = 64;
   esp_err_t err = usb_host_transfer_submit(xfer_read);
   if (err == ESP_OK) transfer_in_progress = true;
   return err == ESP_OK;
}

void USBacmDevice::_callback(int event, usb_transfer_t* data) {
   if (event_cb) event_cb(event, data->data_buffer, data->actual_num_bytes);
}

ESP32TinyUSBライブラリ修正 (usb_host.hpp)

#pragma once
#include "usb/usb_host.h"

class USBhost
{
   friend void _client_event_callback(const usb_host_client_event_msg_t *event_msg, void *arg);
   

//protected:
private:
   usb_device_info_t dev_info;
   usb_host_client_handle_t client_hdl;
   usb_device_handle_t dev_hdl;
   
   usb_host_client_event_cb_t _client_event_cb = nullptr;
   uint8_t _dev_addr;
   uint8_t _configs;
   uint8_t _itfs;

public:
   USBhost();
   ~USBhost();

   bool init(bool create_tasks = true);
   bool open(const usb_host_client_event_msg_t *event_msg);
   bool close();
   usb_device_info_t getDeviceInfo();
   const usb_device_desc_t* getDeviceDescriptor();
   const usb_config_desc_t* getConfigurationDescriptor();

   uint8_t getConfiguration();
   bool setConfiguration(uint8_t);
   void parseConfig();

   usb_host_client_handle_t clientHandle();
   usb_device_handle_t deviceHandle();
   
   void registerClientCb(usb_host_client_event_cb_t cb) { _client_event_cb = cb; }

   
   // USBhost.h の public セクションに追加してください
   bool receiveCDCData(uint8_t* buffer, size_t length, size_t* actual_length);

   void onDeviceConnected();// ← これを追加

   void clearDeviceHandle() {
       dev_hdl = nullptr;
   }
};

ESP32TinyUSBライブラリ修正 (usb_acm.hpp)

#pragma once
#include "usb_device.hpp"
#include "usb_requests.hpp"

#define CDC_DATA_IN                             1
#define CDC_DATA_OUT                            2
#define CDC_CTRL_SET_CONTROL_LINE_STATE         3
#define CDC_CTRL_SET_LINE_CODING                4
#define CDC_CTRL_GET_LINE_CODING                5

typedef void (*cdc_event_cb_t)(int, void* data, size_t len);


class USBacmDevice : public USBhostDevice
{
private:
   const usb_ep_desc_t *ep_int;
   const usb_ep_desc_t *ep_in;
   const usb_ep_desc_t *ep_out;
   USBhost *_host;

   usb_transfer_t *xfer_read;
   usb_transfer_t *xfer_write;
   usb_transfer_t *xfer_ctrl;

   bool transfer_in_progress;
   bool connected;

   cdc_event_cb_t event_cb;

public:
   uint8_t control_interface_number;
   uint8_t data_interface_number;

   USBacmDevice(const usb_config_desc_t* config_desc, USBhost* host);
   ~USBacmDevice();

   bool init();
   void setControlLine(bool dtr, bool rts);
   void setLineCoding(uint32_t bitrate, uint8_t cf, uint8_t parity, uint8_t bits);
   void getLineCoding();
   void INDATA();
   void OUTDATA(uint8_t* data, size_t len);
   void onEvent(cdc_event_cb_t _cb);
   bool isConnected();
   void cleanup();

   void _callback(int event, usb_transfer_t* data);
   bool submitReadTransfer();

   static void usb_read_cb(usb_transfer_t* transfer);
   static void usb_write_cb(usb_transfer_t* transfer);
   static void usb_ctrl_cb(usb_transfer_t* transfer);
};

Device側 plathomeio.ini設定(M5StickC Plus2)

[env:m5stick-cplus2]
platform = espressif32
board = m5stick-c
framework = arduino
upload_speed = 1500000
monitor_speed = 115200
board_upload.flash_size = 8MB
board_upload.maximum_size = 8388608
board_build.f_flash = 80000000L
board_build.filesystem = spiffs
build_flags =
-DCORE_DEBUG_LEVEL=0
-DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
-mfix-esp32-psram-cache-strategy=memw
board_build.partitions = max_app_8MB.csv
lib_deps =
m5stack/M5Unified

Device側 mainコード

#include <Arduino.h>
#include <M5Unified.h>

int cnt=0;

// 表示用関数
void disp(int x, int y, uint16_t fcolor, uint16_t bcolor, int size, String msg) {
  if (size == 12) M5.Display.setFont(&fonts::efontJA_12);
  else if (size == 14) M5.Display.setFont(&fonts::efontJA_14);
  else if (size == 16) M5.Display.setFont(&fonts::efontJA_16);
  else if (size == 24) M5.Display.setFont(&fonts::efontJA_24);

  M5.Display.setTextWrap(false);
  M5.Display.setTextColor(fcolor, bcolor);
  int textLength = msg.length() / 3;
  M5.Display.fillRect(x, y, textLength * size, size, BLACK);
  M5.Display.setCursor(x, y);
  M5.Display.print(msg);
}


void setup() {
M5.begin();
Serial.begin(115200);//USBシリアル
disp(0, 10, GREEN, BLACK, 16, "USB Dev. CDC-ACM");
}


void loop() {
// データ受信処理
if(Serial.available()>0){
  String rev_str = Serial.readStringUntil('\n'); //終端文字までを取得
  disp(0, 30, WHITE, BLACK, 16, "ReceveData");
  disp(10, 50, WHITE, BLACK, 16, String(rev_str));
}

// データ送信処理
Serial.print("CDC Test:");
Serial.println(cnt);
cnt++;
delay(1000);
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?