构建一个 Android 控制台:通过独立的 M5Stack Tab5,可使用 USB 或 Wi-Fi 镜像、控制和调试安卓设备
Tab5-ADB 是一款固件,可将 M5Stack Tab5 转换为独立的 ADB 主机和安卓远程控制台。它通过 USB 或 TCP/IP 与安卓设备连接,让您无需电脑即可实现屏幕镜像、触控操作、查看设备信息、运行 shell 命令、传输文件、管理应用等功能。
若使用 USB 连接,需在安卓设备的开发者选项中开启 USB 调试,并允许 Tab5 进行 ADB 连接;若使用 TCP/IP 连接,则需提前在安卓设备上启用 TCP/IP ADB。
设备信息
您可以查看所连接设备的 SoC、CPU 核心数量、内存、存储、电池、网络、Android 版本、安全补丁级别等信息。实时监控整体及每核 CPU 使用情况、内存使用率、负载平均值、电池电量和温度。
屏幕镜像
轻点实时预览即可启动全屏镜像。您可以通过 Tab5 的触摸面板直接控制安卓设备,支持手势导航和多点触控功能。屏幕上还会显示返回、主页、最近使用、音量和电源键等操作按钮。Fit(适配)、Fill(填充)和 Adapt(自适应)显示模式可让您根据 Tab5 的屏幕比例调整画面宽高比、填充屏幕或缩放设备的逻辑分辨率。
通过 USB 连接时,镜像传输具有高质量和低延迟,通常在 30–50 帧/秒之间运行,具体取决于设备和屏幕内容;通过 TCP/IP 传输时,则会根据可用带宽进行缩放。您还可以通过 Tab5 的扬声器或耳机播放安卓设备的音频(适用于 Android 12 及更高版本)。
ADB 工具
ADB Shell:一个交互式终端,支持 VT100/xterm 的核心功能。您可以使用屏幕键盘输入命令,键盘包含 Ctrl、Esc、Tab、方向键等按键。
文件管理器:浏览安卓设备的内部存储和系统区域,以及 Tab5 的 SD 卡。可预览图片和 APK 信息,并在安卓设备与 SD 卡之间双向复制文件。
应用管理:列出用户应用和系统应用,并查看版本等详细信息。你可以启动、强制停止、清除数据、启用/禁用以及卸载应用程序,并将存储在 Tab5 SD 卡上的 APK 安装到安卓设备上。
Logcat:实时显示 Android 日志。支持按日志级别和文本进行筛选,可暂停、清除日志,并将捕获的日志保存为文本文件至 SD 卡。
截图:从安卓设备截取全分辨率的 PNG 截图,预览并保存到 Tab5 的 SD 卡中。
电源菜单:通过 Tab5 控制屏幕的睡眠与唤醒、正常重启、进入恢复模式或引导加载程序,以及关机操作。
媒体控制:显示当前曲目标题、艺术家、专辑封面及播放状态,并可控制上一首歌曲、播放/暂停以及下一首歌曲。
动机
我之前曾使用 Tab5 构建过多个项目,包括 HDMI 视频预览和 PC 屏幕流传输功能。通过这些工作,我了解到 ESP32-P4 的硬件 JPEG 编码器能够以高帧率输出 MJPEG。
我也知道,scrcpy 这款通过 PC 控制安卓设备的工具,会借助 ADB 将服务器程序传输到安卓设备,启动程序后,再与设备交换屏幕和输入数据。这让我想到,如果我在ESP32-P4上实现一个ADB主机,并采用与scrcpy相同的方法从安卓设备流式传输MJPEG,那么我就可以仅用Tab5来控制安卓设备。除此之外,我决定不再将ADB连接仅用于镜像功能,而是将ADB所能实现的所有功能——包括shell、文件传输、应用管理、Logcat等——整合到一个统一的触控界面中。
工作原理
ESP32-P4上的ADB主机
通过USB的ADB使用了基于厂商级批量传输的自定义协议,用于多路逻辑流的复用。我参考AOSP ADB源码,在ESP-IDF的usb_host框架上实现了ESP32-P4的主机端ADB协议、认证和流管理功能。同时支持TCP/IP传输,因此相同的高层API可在USB和无线连接之间通用。
Android端代理
固件中嵌入了一个运行在Android系统上的Java代理程序。一旦建立ADB连接,Tab5便会将该代理程序复制到Android设备的/data/local/tmp目录,并通过app_process启动。由于它并非常规的APK文件,因此无需安装步骤或应用权限对话框。
该代理程序监听一个抽象的UNIX域套接字。Tab5通过ADB连接此套接字,并将视频、音频、输入和控制消息通过单个ADB流进行复用。Tab5上的触摸坐标以多点触控快照形式发送,代理程序将其注入为Android输入事件。
镜像流水线
在Tab5端对每一帧进行旋转和缩放会增加延迟并降低帧率,这不利于保持低延迟和高帧率。相反,我使用 Android 的显示合成器预先将屏幕旋转并缩放到 720×1280 的尺寸——这与 Tab5 屏幕的尺寸相匹配——然后将其压缩为 JPEG 格式。
视频被分割成若干水平条带。在 Tab5 端,通过绕过 LVGL,每条带由 ESP32-P4 的硬件 JPEG 编解码器进行解码,并直接写入帧缓冲区的对应位置。通过避免额外的重采样和帧复制,以及在独立任务中并行处理接收与解码,即使在 CPU 性能和内存带宽有限的情况下,也能实现流畅的显示效果。
开发环境
从一开始我就意识到,这将是一个庞大的嵌入式软件项目,涵盖 ADB、Android 代理、视频音频处理以及 LVGL 用户界面。因此,为了使 Claude Code 能够高效地支持代理式编码,我首先搭建了一个主机开发环境,在该环境中大部分开发和自动化验证工作可以在没有物理硬件的情况下完成。我通过将大量实现、重构、测试和自动化 UI 检查任务委托给 Claude Code,最后在真实设备上完成最终验证和性能调优。使用 Nix 构建可复现的工具链
我通过一个 Nix flake 定义了开发环境。只需运行 `nix develop`,即可获得项目所需的精确版本工具——包括 ESP-IDF 工具链、CMake、SDL2、libusb、Android 构建工具等。其主要优势在于,它能保持主机环境的整洁,同时让开发者和代码代理都能使用相同的可复现环境。
我使用 Zed 作为编辑器。由于该项目的 Nix 环境与 Espressif 的 VS Code 扩展不兼容,而 Zed 可以通过 direnv 加载环境,并在标准配置下使用 clangd 提供代码补全功能。
基于 SDL 的 Tab5 模拟器
LVGL 本身具有 SDL 驱动程序,但本项目通过镜像方式绕过 LVGL,直接从 ESP32-P4 的硬件 JPEG 编码器获取数据并绘制到显示帧缓冲区中。因此,仅使用标准的 LVGL SDL 驱动无法重现真实设备的渲染路径。
为此,我在自定义 BSP 的 API 边界抽象了显示处理逻辑,并在模拟器中使用 SDL 纹理来还原 MIPI DSI 帧缓冲区的行为。我还为FreeRTOS、JPEG解码器以及ESP-IDF的主要组件提供了主机兼容层,使得Tab5应用程序代码几乎无需修改即可在PC上运行。
此外,我还在模拟器中添加了屏幕截图和脚本化触控功能。这使得编码代理不仅能启动用户界面,还能以图像形式验证自动化交互的结果。无需人工点击屏幕,整个流程——从实现到可视化验证——都可以一次性完成。
使用libusb进行ADB开发
虽然我可以为ESP-IDF的主要API提供主机兼容层,但USB主机栈高度依赖操作系统和硬件,因此在模拟器上直接复现ESP-IDF的usb_host API并不现实。
因此,我将ADB传输层设计为可切换:实际的Tab5使用ESP-IDF的usb_host,而在PC端则使用libusb。无论在哪个环境中,高层的ADB协议、Android代理、文件传输等功能都运行在相同的代码之上。通过这种设置,我可以在连接到电脑的安卓设备上开发和测试大部分ADB实现功能,从而将设备端验证的范围缩小到ESP32-P4特有的USB主机行为和性能调优。
固件
预构建的固件可以从M5Burner下载,并直接刷入M5Stack Tab5。
本文编译自hackster.io





