Skip to content

Latest commit

 

History

History
300 lines (234 loc) · 11.7 KB

File metadata and controls

300 lines (234 loc) · 11.7 KB

RemoteSensor — 手机传感器同步到其他设备

把 Android 手机的传感器数据实时同步到其他设备上,并在其他设备端消费这些数据(绘图、记录、 分析等)。

架构速览

  • 服务端 = 手机端 APP:一个跑在 Android 手机上的独立可执行程序 (server/RemoteSensorServer.cpp,单文件)。它枚举手机上的物理传感器, 监听 TCP 端口,把传感器事件流式推送给客户端。
  • 客户端 = 封装好的 C++ 库:一个跨平台 C++17 静态库(RemoteSensor), 封装了局域网发现、TCP 连接、命令/应答协议与传感器事件分发。你只需要调用几个类和方法即可拿到传感器数据,无需关心协议与套接字细节。
                    传感器数据
Android 手机 (APP) ───────────► 其他设备 (客户端)
                     UDP传输


目录


快速开始

  1. 在手机上启动服务端 APP(见 服务端(手机端 APP))。

  2. 在电脑上构建并运行测试程序(见 电脑端测试程序):

    cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Release
    cmake --build build
    ./build/RemoteSensorTest.exe        # 自动发现并连接手机
  3. 程序会自动在局域网发现服务端并连接,然后以图表实时展示各传感器数据。


目录结构

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 双栈监听(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)。

服务端(手机端 APP)

  • 单文件 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

客户端库(RemoteSensor C++ 库)

使用 mingw64(GNU g++)编译的静态库,通过 CMake 集成。接口分三大板块:

1. 发现服务端 — rs::Discovery

通过 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) 直接返回第一台。

2. 连接设备 — rs::Device

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);

3. 读取传感器 — rs::Sensor 体系

openSensor() 会根据传感器类型自动返回对应的子类(AccelerometerSensorGyroscopeSensorLightSensorStepCounterSensorHeartRateSensor 等)。 取数有两种方式:

  • 回调模式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 周期

计步器(StepCounter)

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 周期

心率(HeartRate)

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