在树莓派上实现模拟USB设备的功能,并通过USB线与电脑进行通信

AIGC by:通议

全文摘要

本文介绍了一种在树莓派上实现模拟USB设备功能并与其进行通信的方法。该方法使用了gadgetfs和gousb库,其中gadgetfs允许树莓派模拟USB设备,而gousb则用于处理USB通信。具体步骤包括配置树莓派作为USB设备、编写服务端代码和客户端代码等。实验结果表明,该方法能够成功地实现在树莓派上模拟USB设备并与电脑进行通信。需要注意的是,在实际操作中需注意设备权限、设备ID以及端点地址等问题。

简介

要在树莓派上实现模拟 USB 设备的功能,并通过 USB 线与电脑进行通信,可以使用 gadgetfsgousb 库。gadgetfs 允许树莓派模拟 USB 设备,而 gousb 则用于处理 USB 通信。

步骤概述


  1. 配置树莓派作为 USB 设备:使用 gadgetfs 配置树莓派模拟 USB 设备。

  2. 编写服务端代码:在树莓派上编写服务端代码,接收来自电脑客户端的数据。

  3. 编写客户端代码:在电脑上编写客户端代码,发送数据到树莓派。


配置树莓派作为 USB 设备


  1. 启用 USB Gadget 模式
    • 编辑 /boot/config.txt 文件,添加以下内容以启用 USB Gadget 模式:
      dtoverlay=dwc2
      
    • 重启树莓派:
      sudo reboot
      

  1. 配置 USB Gadget
    • 创建一个配置文件,例如 /etc/udev/rules.d/99-gadget.rules,添加以下内容:
      SUBSYSTEM=="usb", ATTR{idVendor}=="1234", ATTR{idProduct}=="5678", MODE="0666"
      
    • 创建一个 gadget 配置文件,例如 /etc/gadget-config.conf,添加以下内容:
      ATTRS{idVendor}="1234"
      ATTRS{idProduct}="5678"
      ATTRS{bcdDevice}="0100"
      ATTRS{bDeviceClass}="02"
      ATTRS{bDeviceSubClass}="00"
      ATTRS{bDeviceProtocol}="00"
      ATTRS{bMaxPacketSize0}="64"
      ATTRS{bNumConfigurations}="1"
      ATTRS{bmAttributes}="80"
      ATTRS{bMaxPower}="50"
      

  1. 加载 USB Gadget 模块
    • 创建一个启动脚本,例如 /usr/local/bin/start-gadget.sh,添加以下内容:
      #!/bin/bash
      modprobe libcomposite
      cd /sys/kernel/config/usb_gadget/
      mkdir -p mygadget
      cd mygadget
      echo 0x1234 > idVendor
      echo 0x5678 > idProduct
      echo 0x0100 > bcdDevice
      echo 0x02 > bDeviceClass
      echo 0x00 > bDeviceSubClass
      echo 0x00 > bDeviceProtocol
      echo 0x40 > bMaxPacketSize0
      echo 0x01 > bNumConfigurations
      mkdir -p strings/0x409
      echo "fedcba9876543210" > strings/0x409/serialnumber
      echo "Raspberry Pi" > strings/0x409/manufacturer
      echo "My USB Gadget" > strings/0x409/product
      mkdir -p configs/c.1/strings/0x409
      echo "Config 1" > configs/c.1/strings/0x409/configuration
      echo 250 > configs/c.1/MaxPower
      mkdir -p functions/ncm.usb0
      ln -s functions/ncm.usb0 configs/c.1/
      ls /sys/class/udc > UDC
      
    • 使脚本可执行:
      sudo chmod +x /usr/local/bin/start-gadget.sh
      
    • 运行脚本:
      sudo /usr/local/bin/start-gadget.sh
      

服务端代码(树莓派)


在树莓派上编写服务端代码,接收来自电脑客户端的数据。

package main

import (
	"fmt"
	"github.com/google/gousb"
	"log"
	"time"
)

const (
	vendorID  = 0x1234 // 替换为你的设备的厂商 ID
	productID = 0x5678 // 替换为你的设备的产品 ID
)

func main() {
	ctx := gousb.NewContext()
	defer ctx.Close()

	dev, err := ctx.OpenDeviceWithVIDPID(vendorID, productID)
	if err != nil {
		log.Fatalf("Failed to open device: %v", err)
	}
	defer dev.Close()

	handle, err := dev.Open()
	if err != nil {
		log.Fatalf("Failed to open device handle: %v", err)
	}
	defer handle.Close()

	intf, err := handle.ClaimInterface(0)
	if err != nil {
		log.Fatalf("Failed to claim interface: %v", err)
	}
	defer intf.Release()

	// 设置端点
	inEndpoint := &gousb.InEndpointInfo{Address: 0x81} // 替换为你的输入端点地址
	outEndpoint := &gousb.OutEndpointInfo{Address: 0x02} // 替换为你的输出端点地址

	buffer := make([]byte, 64) // 缓冲区大小可以根据需要调整

	for {
		n, err := inEndpoint.Read(buffer)
		if err != nil {
			log.Printf("Read error: %v", err)
			continue
		}

		fmt.Printf("Received %d bytes: %x\n", n, buffer[:n])
	}

	// 保持程序运行
	select {}
}

客户端代码(电脑)


在电脑上编写客户端代码,发送数据到树莓派。

package main

import (
	"fmt"
	"github.com/google/gousb"
	"log"
	"time"
)

const (
	vendorID  = 0x1234 // 替换为你的设备的厂商 ID
	productID = 0x5678 // 替换为你的设备的产品 ID
)

func main() {
	ctx := gousb.NewContext()
	defer ctx.Close()

	dev, err := ctx.OpenDeviceWithVIDPID(vendorID, productID)
	if err != nil {
		log.Fatalf("Failed to open device: %v", err)
	}
	defer dev.Close()

	handle, err := dev.Open()
	if err != nil {
		log.Fatalf("Failed to open device handle: %v", err)
	}
	defer handle.Close()

	intf, err := handle.ClaimInterface(0)
	if err != nil {
		log.Fatalf("Failed to claim interface: %v", err)
	}
	defer intf.Release()

	// 设置端点
	outEndpoint := &gousb.OutEndpointInfo{Address: 0x02} // 替换为你的输出端点地址

	data := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08}

	for {
		n, err := outEndpoint.Write(data)
		if err != nil {
			log.Printf("Write error: %v", err)
			continue
		}

		fmt.Printf("Sent %d bytes: %x\n", n, data)

		time.Sleep(1 * time.Second) // 每秒发送一次
	}
}

运行示例


  1. 配置树莓派
    • 按照上述步骤配置树莓派,使其模拟 USB 设备。
    • 将服务端代码保存为 server.go 并上传到树莓派。
    • 在树莓派上运行服务端程序:
      go run server.go
      

  1. 运行客户端
    • 将客户端代码保存为 client.go
    • 在电脑上运行客户端程序:
      go run client.go
      

  1. 查看输出
    • 服务端会输出接收到的数据,客户端会输出发送的数据。

注意事项


希望这个示例能帮助你实现树莓派作为 USB 设备与电脑进行通信。如果你有更多问题或需要进一步的帮助,请随时提问。