打印

蓝牙耳机丢了,我花几分钟写了一个小程序,找到了!​

[复制链接]
489|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
科叼|  楼主 | 2024-11-19 18:41 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
作者:老码小张

你是否曾经经历过蓝牙耳机不知道丢到哪里去的困扰?特别是忙碌的早晨,准备出门时才发现耳机不见了,整个心情都被影响。幸运的是,随着技术的进步,我们可以利用一些简单的小程序和蓝牙技术轻松找到丢失的耳机。今天,我要分享的是我如何通过一个自制的小程序,利用蓝牙发现功能,成功定位自己的耳机。这不仅是一次有趣的技术尝试,更是对日常生活中类似问题的一个智能化解决方案。



(顺便内推几个技术大厂的机会,前、后端or测试,东莞/深圳等地急招,感兴趣可以试试https://jinshuju.net/f/o38ijj)!

1. 蓝牙耳机丢失的困扰

现代生活中,蓝牙耳机几乎是每个人的必备品。然而,耳机的体积小、颜色常常与周围环境融为一体,导致丢失的情况时有发生。传统的寻找方式依赖于我们对耳机放置地点的**,但往往不尽人意。这时候,如果耳机还保持在开机状态,我们就可以借助蓝牙技术进行定位。然而,市场上大部分设备并没有自带这类功能,而我们完全可以通过编写小程序实现。

2. 蓝牙发现功能的原理

蓝牙发现功能是通过设备之间的信号传输进行连接和识别的。当一个蓝牙设备处于开机状态时,它会周期性地广播自己的信号,周围的蓝牙设备可以接收到这些信号并进行配对。这个过程的背后其实是信号的强度和距离的关系。当我们在手机或其他设备上扫描时,能够检测到耳机的存在,但并不能直接告诉我们耳机的具体位置。此时,我们可以通过信号强弱来推测耳机的大概位置。

3. 实现步骤:从构想到实践

有了这个想法后,我决定动手实践。首先,我使用微信小程序作为开发平台,利用其内置的蓝牙接口实现设备扫描功能。具体步骤如下:

• 环境搭建:选择微信小程序作为平台主要因为其开发简便且自带蓝牙接口支持。

• 蓝牙接口调用:调用wx.openBluetoothAdapter初始化蓝牙模块,确保设备的蓝牙功能开启。

• 设备扫描:通过wx.startBluetoothDevicesDiscovery函数启动设备扫描,并使用wx.onBluetoothDeviceFound监听扫描结果。

• 信号强度分析:通过读取蓝牙信号强度(RSSI),结合多次扫描的数据变化,推测设备的距离,最终帮助定位耳机。

在代码的实现过程中,信号强度的变化尤为重要。根据RSSI值的波动,我们可以判断耳机是在靠近还是远离,并通过走动测试信号的变化,逐渐缩小搜索范围。

下面是我使用 Taro 实现的全部代码:

import React, { useState, useEffect } from "react";
import Taro, { useReady } from "@tarojs/taro";
import { View, Text } from "@tarojs/components";
import { AtButton, AtIcon, AtProgress, AtList, AtListItem } from "taro-ui";
import "./index.scss";

const BluetoothEarphoneFinder = () => {
  const [isSearching, setIsSearching] = useState(false);
  const [devices, setDevices] = useState([]);
  const [nearestDevice, setNearestDevice] = useState(null);
  const [isBluetoothAvailable, setIsBluetoothAvailable] = useState(false);
  const [trackedDevice, setTrackedDevice] = useState(null);

  useEffect(() => {
    if (isSearching) {
      startSearch();
    } else {
      stopSearch();
    }
  }, [isSearching]);

  useEffect(() => {
    if (devices.length > 0) {
      const nearest = trackedDevice
        ? devices.find((d) => d.deviceId === trackedDevice.deviceId)
        : devices[0];
      setNearestDevice(nearest || null);
    } else {
      setNearestDevice(null);
    }
  }, [devices, trackedDevice]);

  const startSearch = () => {
    const startDiscovery = () => {
      setIsBluetoothAvailable(true);
      Taro.startBluetoothDevicesDiscovery({
        success: () => {
          Taro.onBluetoothDeviceFound((res) => {
            const newDevices = res.devices.map((device) => ({
              name: device.name || "未知设备",
              deviceId: device.deviceId,
              rssi: device.RSSI,
            }));
            setDevices((prevDevices) => {
              const updatedDevices = [...prevDevices];
              newDevices.forEach((newDevice) => {
                const index = updatedDevices.findIndex(
                  (d) => d.deviceId === newDevice.deviceId
                );
                if (index !== -1) {
                  updatedDevices[index] = newDevice;
                } else {
                  updatedDevices.push(newDevice);
                }
              });
              return updatedDevices.sort((a, b) => b.rssi - a.rssi);
            });
          });
        },
        fail: (error) => {
          console.error("启动蓝牙设备搜索失败:", error);
          Taro.showToast({
            title: "搜索失败,请重试",
            icon: "none",
          });
          setIsSearching(false);
        },
      });
    };

    Taro.openBluetoothAdapter({
      success: startDiscovery,
      fail: (error) => {
        if (error.errMsg.includes("already opened")) {
          startDiscovery();
        } else {
          console.error("初始化蓝牙适配器失败:", error);
          Taro.showToast({
            title: "蓝牙初始化失败,请检查蓝牙是否开启",
            icon: "none",
          });
          setIsSearching(false);
          setIsBluetoothAvailable(false);
        }
      },
    });
  };

  const stopSearch = () => {
    if (isBluetoothAvailable) {
      Taro.stopBluetoothDevicesDiscovery({
        complete: () => {
          Taro.closeBluetoothAdapter({
            complete: () => {
              setIsBluetoothAvailable(false);
            },
          });
        },
      });
    }
  };

  const getSignalStrength = (rssi) => {
    if (rssi >= -50) return 100;
    if (rssi <= -100) return 0;
    return Math.round(((rssi + 100) / 50) * 100);
  };

  const getDirectionGuide = (rssi) => {
    if (rssi >= -50) return "非常接近!你已经找到了!";
    if (rssi >= -70) return "很近了,继续朝这个方向移动!";
    if (rssi >= -90) return "正确方向,但还需要继续寻找。";
    return "信号较弱,尝试改变方向。";
  };

  const handleDeviceSelect = (device) => {
    setTrackedDevice(device);
    Taro.showToast({
      title: `正在跟踪: ${device.name}`,
      icon: "success",
      duration: 2000,
    });
  };

  return (
    <View className="bluetooth-finder">
      {isSearching && (
        <View className="loading-indicator">
          <AtIcon value="loading-3" size="30" color="#6190E8" />
          <Text className="loading-text">搜索中...</Text>
        </View>
      )}
      {nearestDevice && (
        <View className="nearest-device">
          <Text className="device-name">{nearestDevice.name}</Text>
          <AtProgress
            percent={getSignalStrength(nearestDevice.rssi)}
            status="progress"
            isHidePercent
          />
          <Text className="direction-guide">
            {getDirectionGuide(nearestDevice.rssi)}
          </Text>
        </View>
      )}
      <View className="device-list">
        <AtList>
          {devices.map((device) => (
            <AtListItem
              key={device.deviceId}
              title={device.name}
              note={`${device.rssi} dBm`}
              extraText={
                trackedDevice && trackedDevice.deviceId === device.deviceId
                  ? "跟踪中"
                  : ""
              }
              arrow="right"
              onClick={() => handleDeviceSelect(device)}
            />
          ))}
        </AtList>
      </View>
      <View className="action-button">
        <AtButton
          type="primary"
          circle
          onClick={() => setIsSearching(!isSearching)}
        >
          {isSearching ? "停止搜索" : "开始搜索"}
        </AtButton>
      </View>
    </View>
  );
};

export default BluetoothEarphoneFinder;


嘿嘿,功夫不负苦心人,我最终通过自己的小程序找到了我的蓝牙耳机。

我将我的小程序发布到了微信小程序上,目前已经通过审核,可以直接使用了。搜索老码宝箱 即可体验。

顺带还加了非常多的小工具,而且里面还有非常多日常可能会用到的工具,有些还非常有意思。

比如

绘制函数图

每日一言

汇率转换(实时)

BMI 计算

简易钢琴

算一卦

这还不是最重要的

最重要的是,这里的工具是会不断增加的,而且,更牛皮的是,你还可以给作者提需求,增加你想要的小工具,作者是非常欢迎一起讨论的。有朝一日,你也希望你的工具也出现在这个小程序上,被千万人使用吧。

4. 实际应用与优化空间

这个小程序的实际效果超出了我的预期。我能够通过它快速找到丢失的耳机,整个过程不到几分钟时间。然而,值得注意的是,由于蓝牙信号会受到环境干扰,例如墙体、金属物等,导致信号强度并不总是精确。在后续的优化中,我计划加入更多的信号处理算法,例如利用三角定位技术,结合多个信号源来提高定位精度。此外,还可以考虑在小程序中加入可视化的信号强度图,帮助用户更直观地了解耳机的大致方位。

一些思考:

蓝牙耳机定位这个小程序的开发,展示了技术在日常生活中的强大应用潜力。虽然这个项目看似简单,但背后的原理和实现过程非常具有教育意义。通过这次尝试,我们可以看到,借助开源技术和简单的编程能力,我们能够解决许多日常生活中的实际问题。

参考资料:

微信小程序官方文档:developers.weixin.qq.com

蓝牙信号强度(RSSI)与距离关系的研究:www.bluetooth.com

个人开发者经验分享: 利用蓝牙发现功能定位设备

使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

120

主题

129

帖子

1

粉丝