把 Android 手机的传感器数据实时同步到其他设备上,并在其他设备端消费这些数据(绘图、记录、 分析等)。
架构速览
- 服务端 = 手机端 APP:一个跑在 Android 手机上的独立可执行程序 (
server/RemoteSensorServer.cpp,单文件)。它枚举手机上的物理传感器, 监听 TCP 端口,把传感器事件流式推送给客户端。- 客户端 = 封装好的 C++ 库:一个跨平台 C++17 静态库(
RemoteSensor), 封装了局域网发现、TCP 连接、命令/应答协议与传感器事件分发。你只需要调用几个类和方法即可拿到传感器数据,无需关心协议与套接字细节。
传感器数据
Android 手机 (APP) ───────────► 其他设备 (客户端)
UDP传输
-
在手机上启动服务端 APP(见 服务端(手机端 APP))。
-
在电脑上构建并运行测试程序(见 电脑端测试程序):
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release cmake --build build ./build/RemoteSensorTest.exe # 自动发现并连接手机
-
程序会自动在局域网发现服务端并连接,然后以图表实时展示各传感器数据。
RemoteSensor/
├── server/
│ └── RemoteSensorServer.cpp # 手机端服务端(APP,单文件,Android Bionic 编译)
│
├── include/RemoteSensor/ # 客户端库公共头文件(你 include 的头都在这)
│ ├── RemoteSensor.hpp # 统一入口:一行 #include 即可
│ ├── Types.hpp # 类型定义:SensorType / SensorInfo / SensorEvent
│ ├── Discovery.hpp # 局域网发现服务端
│ ├── Device.hpp # 设备连接:connect / openSensor / 信息查询
│ └── Sensor.hpp # 传感器体系:基类 + 各类型子类
│
├── src/RemoteSensor/ # 客户端库实现(Windows/Winsock)
│ ├── internal/Protocol.hpp # 内部:网络协议常量 + 序列化工具
│ ├── internal/WinSocket.hpp # 内部:Winsock 套接字封装
│ ├── RemoteSensor.cpp
│ ├── Device.cpp
│ ├── Sensor.cpp
│ └── Discovery.cpp
│
├── examples/ # 面向库使用者的完整示例(每种传感器都有)
│ ├── README.md
│ ├── 01_discovery_connect.cpp
│ ├── 02_callback_mode.cpp
│ ├── 03_queue_mode.cpp
│ └── 04_read_every_sensor.cpp
│
├── test/
│ ├── main.cpp # 电脑端测试程序(SDL2 实时图表)
│ └── protocol_test.cpp # 无 SDL 的协议自检(配合 tools/mock_server.py)
│
├── include/DebugText.hpp # 中文点阵字体绘制(测试程序用)
├── include/fusionpixel_12px_mono.hpp # 中文字体数据(测试程序用)
├── tools/mock_server.py # Python 模拟服务端(端到端验证用)
├── SDL2-2.32.10/ # SDL2(测试程序用,项目内置)
└── CMakeLists.txt
- TCP 数据端口:
45999(可用--port修改),支持 IPv6 双栈(IPv4/IPv6 均可连接) - UDP 发现端口:
45998 - 二进制帧:
magic(4) + version(1) + type(1) + length(4) + payload,小端序。 - 客户端命令:HELLO / GET_DEVICE_INFO / LIST_SENSORS / ENABLE_SENSOR / DISABLE_SENSOR / PING
- 服务端消息:HELLO_ACK / DEVICE_INFO / SENSOR_LIST / CMD_ACK / SENSOR_EVENT / PONG / ERROR
- 发现协议:客户端向
255.255.255.255:45998(及各网卡子网广播地址)发送 IPv4 UDP 探测包,并向ff02::114:45998组播发送 IPv6 探测包;服务端单播回复通告, 也可周期广播/组播通告。
客户端库已把这些细节全部封装好;除非你要自己实现协议两端,否则无需关心。
- 服务端:优先以 IPv6 双栈监听(
IPV6_V6ONLY=0),同时接受 IPv4 与 IPv6 连接; 若系统无 IPv6 则回退仅 IPv4。启动时打印本机 IPv4 与 IPv6 地址。 - 客户端:
Device::connect()通过getaddrinfo(AF_UNSPEC)解析并依次尝试所有 地址,支持 IPv4/IPv6 地址与主机名;Discovery::search()同时发送 IPv4 广播与 IPv6 组播探测。 - IPv6 链路本地地址(
fe80::…)连接时需带接口名,例如:RemoteSensorTest fe80::a1b2:c3d4%wlan0 45999(Windows 下也可用接口索引%N)。
-
单文件
server/RemoteSensorServer.cpp,使用 Android NDK 的 Bionic 工具链编译。 -
运行参数:
--port <N>TCP 监听端口(默认 45999)--no-discovery关闭局域网发现--announce-interval <S>周期广播间隔秒(默认 3,0 关闭周期广播但应答探测)
-
示例编译(请替换为你的 NDK 路径):
NDK=/path/to/android-ndk-rXX TC=$NDK/toolchains/llvm/prebuilt/linux-x86_64/bin $TC/aarch64-linux-android26-clang++ \ -std=c++17 -O2 -fPIE -pie \ -o RemoteSensorServer server/RemoteSensorServer.cpp -landroid -llog
-
说明:可用C4Droid等工具把RemoteSensorServer.cpp打包成APK
使用 mingw64(GNU g++)编译的静态库,通过 CMake 集成。接口分三大板块:
通过 UDP 广播/组播探测局域网内的服务端,返回 rs::DiscoveredServer 列表。
std::vector<rs::DiscoveredServer> servers = rs::Discovery::search(45998, 2500);
for (auto& s : servers) printf("%s\n", s.toString().c_str());便捷函数:rs::discoverFirstServer(out, timeoutMs) 直接返回第一台。
connect() 建立 TCP 连接(可指定超时),之后可查询设备信息、枚举/打开传感器。
rs::Device dev;
dev.connect("192.168.1.100", 45999); // 直连,可传 DiscoveredServer
auto info = dev.getDeviceInfo(); // 手机型号/系统版本等
auto list = dev.getSensorList(); // 该设备上所有传感器
auto acc = dev.openSensor(rs::SensorType::Accelerometer);openSensor() 会根据传感器类型自动返回对应的子类(AccelerometerSensor、
GyroscopeSensor、LightSensor、StepCounterSensor、HeartRateSensor 等)。
取数有两种方式:
- 回调模式:
setEventCallback(),数据到达时在接收线程中回调。 - 队列模式:
waitForEvents()/readEvents()拉取有界环形缓冲。
start(periodUs) 以指定采样周期(微秒)开始数据流,stop() 停止。具体每种
传感器的读取示例见下一节。
完整、可直接编译的示例放在 examples/ 目录。下面按
传感器类别给出核心代码片段。
返回 X/Y/Z 三个分量,Vec3Sensor::x()/y()/z() 或事件 values[0..2]。
auto acc = dev.openSensor(rs::SensorType::Accelerometer); // 单位: m/s²
acc->setEventCallback([](const rs::SensorEvent& e) {
printf("acc x=%.3f y=%.3f z=%.3f\n", e.value(0), e.value(1), e.value(2));
});
acc->start(20000); // 20ms 周期前三项为 X/Y/Z,第 4 项(w())为四元数 w 分量或旋转精度(取决于设备)。
auto rv = dev.openSensor(rs::SensorType::RotationVector);
rv->setEventCallback([](const rs::SensorEvent& e) {
printf("rv x=%.4f y=%.4f z=%.4f w=%.4f\n",
e.value(0), e.value(1), e.value(2),
e.valueCount > 3 ? e.value(3) : 0.0f);
});
rv->start(20000);每事件只有一个有效数值,用 SingleValueSensor::value() 或 e.value(0)。
auto light = dev.openSensor(rs::SensorType::Light); // 单位: lux
light->setEventCallback([](const rs::SensorEvent& e) {
printf("light=%.1f lux\n", e.value(0));
});
light->start(100000); // 光照变化慢,用 100ms 周期values[0] 为 float 形式的累计步数,用 stepCountU32() 取整型步数。
auto step = dev.openSensor(rs::SensorType::StepCounter);
step->setEventCallback([](const rs::SensorEvent& e) {
printf("steps=%u\n", e.stepCountU32());
});
step->start(200000); // 200ms 周期value() / bpm() 返回每分钟心跳数;-1 表示当前未测到(无皮肤接触)。
hasContact() 判断是否正在测量(等价于 value() >= 0)。
auto hr = dev.openSensor(rs::SensorType::HeartRate);
hr->setEventCallback([](const rs::SensorEvent& e) {
printf("bpm=%.1f contact=%s\n", e.value(0),
e.value(0) >= 0.0f ? "yes" : "no");
});
hr->start(500000); // 500ms 周期完整示例(包含连接、所有 17 种传感器逐个读取、两种取数模式)见
examples/04_read_every_sensor.cpp。
使用 CMake + Ninja + mingw64 构建:
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build生成 build/RemoteSensorTest.exe(SDL2.dll 已自动复制到旁边)。运行:
RemoteSensorTest # 自动发现并连接第一台服务端
RemoteSensorTest 192.168.1.100 # 直连指定 IP
RemoteSensorTest 192.168.1.100 45999按键:
| 按键 | 功能 |
|---|---|
← / → |
切换采样窗口时长(1s~60s) |
空格 |
暂停/继续全部传感器 |
R |
清空图表数据 |
ESC |
退出 |
加速度计、陀螺仪、磁场、旋转矢量、游戏旋转矢量、地磁旋转矢量、重力、线性加速度、
光照、距离、气压、相对湿度、环境温度、计步器、计步检测、显著运动、心率
(对应 Android ASENSOR_TYPE_* 的 1/4/2/11/15/20/9/10/5/8/6/12/13/19/18/17/21)。
| SensorType | 含义 | 数值个数 | 典型单位 |
|---|---|---|---|
Accelerometer |
加速度计 | 3 (x,y,z) | m/s² |
MagneticField |
磁场 | 3 | µT |
Gyroscope |
陀螺仪 | 3 | rad/s |
Light |
光照 | 1 | lux |
Pressure |
气压 | 1 | hPa |
Proximity |
距离 | 1 | cm |
Gravity |
重力 | 3 | m/s² |
LinearAcceleration |
线性加速度 | 3 | m/s² |
RotationVector |
旋转矢量 | 4 (x,y,z,w) | 无量纲 |
RelativeHumidity |
相对湿度 | 1 | % |
AmbientTemperature |
环境温度 | 1 | °C |
MagneticFieldUncalibrated |
磁场(未校准) | 3 | µT |
GameRotationVector |
游戏旋转矢量 | 4 | 无量纲 |
GyroscopeUncalibrated |
陀螺仪(未校准) | 3 | rad/s |
SignificantMotion |
显著运动 | 1 (事件) | — |
StepDetector |
计步检测 | 1 (事件) | — |
StepCounter |
计步器 | 1 (累计步数) | 步 |
GeomagneticRotationVector |
地磁旋转矢量 | 4 | 无量纲 |
HeartRate |
心率 | 1 | bpm |