taro 微信小程序连接树莓派4b蓝牙并写入wifi信息

设备卖给客户,不能通过设备自动连接客户家中的网络,使用微信小程序连接树莓派蓝牙,并发送数据(wifi信息)给树莓派

先点击 ‘搜索蓝牙设备’搜索蓝牙

可通过uuid唯一搜索树莓派(如果不知道,那就不填写,就会导致搜索出来很多蓝牙。下面代码中有备注)
如果不填写local_name(需要在树莓派中的蓝牙配置中修改)会是导致搜索出来很多名称为‘未知设备’的蓝牙


9lr35-ouajf.gif

树莓派上的接收

5c6c4fcdb637de74c4c39660b6baee3.jpg

该程序是使用taro编写,大体上和wx小程序写法一样,低功率蓝牙api都是一样的。

import Taro from '@tarojs/taro';
import React, { Component } from 'react';
import { View, Picker } from '@tarojs/components';
import { AtForm, AtInput, AtButton, AtMessage, AtList, AtListItem } from 'taro-ui';
import { string2buffer, ab2hex, isEmpty } from '../../utils/util';
import './index.scss';


class BlueTooth extends Component {

  state = {
    wifiName: '', // 当前手机连接的wifi信息
    wifiPassWord: '',
    logs: [],
    deviceArray: [],
    currDeviceID: '请选择...'
  }

  componentWillMount() { }

  componentDidMount() {
    const that = this;
    Taro.startWifi({
      success() {
        Taro.getConnectedWifi({
          success: resp => {
            console.log(resp);
            that.setState({
              wifiName: resp.wifi.SSID
            })
          },
          fail: resf => {
            if (resf.errCode == 12006) {
              Taro.showModal({
                title: '请打开GPS定位',
                content: 'Android手机不打开GPS定位,无法搜索到蓝牙设备.',
                showCancel: false
              })
            }
          }
        })
      }
    })
  }

  componentWillUnmount() { }

  componentDidShow() { }

  componentDidHide() { }

  delayTimer = ''; // 用来控制是否持续服务发现
  isFound = false; // 蓝牙是否被发现判断标识
  closeFlag = false; // 蓝牙适配器是否被关闭标识

  onSubmit = () => {
    const { currDeviceID, wifiName, wifiPassWord } = this.state;
    console.log("选中:" + currDeviceID);
    if (isEmpty(currDeviceID) || currDeviceID == "请选择...") {
      Taro.atMessage({
        'message': '请先搜索设备',
        'type': 'warning',
      })
      return;
    }
    if (isEmpty(wifiName)) {
      Taro.atMessage({
        'message': '请输入wifi名称',
        'type': 'warning',
      })
      return;
    }
    if (isEmpty(wifiPassWord)) {
      Taro.atMessage({
        'message': '请输入wifi密码',
        'type': 'warning',
      })
      return;
    }
    const device = currDeviceID.split('[');
    if (device.length <= 1) {
      Taro.atMessage({
        'message': '当前没有蓝牙设备在线',
        'type': 'warning',
      })
      return;
    }
    const id = device[device.length - 1].replace("]", "");
    this.createBLE(id);
  }

  onReset = () => {
    this.initBLE();
  }

  initBLE = () => {
    this.printLog("启动蓝牙适配器, 蓝牙初始化")
    Taro.openBluetoothAdapter({
      success: () => {
        // console.log(res);
        this.findBLE();
      },
      fail: () => {
        Taro.atMessage({
          'message': '请先打开蓝牙',
          'type': 'warning',
        })
      }
    })
  }

  findBLE = () => {
    this.printLog("打开本机蓝牙成功.");
    Taro.startBluetoothDevicesDiscovery({
      services: ['6E400001-B5A3-F393-E0A9-E50E24DCCA9E'], // 这里要注意,填写筒子们自己设备的uuid,如果不知道,可以注释的掉这一行,注释导致会搜索到很多蓝牙设备![9lr35-ouajf.gif](https://upload-images.jianshu.io/upload_images/15221653-c0070aab9e6b6c90.gif?imageMogr2/auto-orient/strip)

      allowDuplicatesKey: false,
      interval: 0,
      success: () => {
        Taro.showLoading({
          title: '正在搜索设备',
        })
        this.delayTimer = setInterval(() => {
          this.discoveryBLE() //3.0 //这里的discovery需要多次调用
        }, 1000);
        setTimeout(() => {
          if (this.isFound) {
            return;
          } else {
            Taro.hideLoading();
            console.log("搜索设备超时");
            Taro.stopBluetoothDevicesDiscovery({
              success: () => {
                console.log('连接蓝牙成功之后关闭蓝牙搜索');
              }
            })
            clearInterval(this.delayTimer)
            Taro.showModal({
              title: '搜索设备超时',
              content: '请检查蓝牙设备是否正常工作,Android手机请打开GPS定位.',
              showCancel: false
            })
            Taro.atMessage({
              'message': '搜索设备超时,请打开GPS定位,再搜索',
              'type': 'warning',
            })
            return
          }
        }, 15000);
      },
      fail: res => {
        this.printLog("蓝牙设备服务发现失败: " + res.errMsg);
      }
    })
  }

  discoveryBLE = () => {
    Taro.getBluetoothDevices({
      success: res => {
        const list = res.devices;
        console.log('蓝牙列表', list);
        if (isEmpty(list)) {
          return;
        }
        const devices = [];
        list.forEach(v => {
          const name = v.name || v.localName;
          if (!isEmpty(name) && v.RSSI != 0) {
            // if (!isEmpty(name) && v.RSSI != 0) {
            // const item = {
            //   RSSI: v.RSSI,
            //   name: v.name,
            //   deviceId: v.deviceId,
            //   mac: Array.prototype.map.call(new Uint8Array(v.advertisData.slice(4, 10)), x => ('00' + x.toString(16)).slice(-2)).join(':').toUpperCase()
            // }
            // devices.push(item);
            devices.push(v);
          }
        })
        console.log('devices -----', devices);
        console.log('总共有' + devices.length + "个设备需要设置")
        if (devices.length <= 0) {
          return;
        }
        this.connectBLE(devices);
      },
      fail: function () {
        Taro.atMessage({
          'message': '搜索蓝牙设备失败',
          'type': 'warning',
        })
      }
    })
  }

  connectBLE = devices => {
    this.printLog('总共有' + devices.length + "个设备需要设置")
    Taro.hideLoading();
    this.isFound = true;
    clearInterval(this.delayTimer);
    Taro.stopBluetoothDevicesDiscovery({
      success: () => {
        this.printLog('连接蓝牙成功之后关闭蓝牙搜索');
      }
    })
    //两个的时候需要选择
    const list = [];
    devices.forEach(v => {
      const name = v.localName || v.name;
      list.push(name + "[" + v.deviceId + "]")
    })
    this.setState({
      deviceArray: list
    })
    //默认选择
    this.setState({
      currDeviceID: list[0]
    })
  }

  createBLE = deviceId => {
    this.printLog("连接: [" + deviceId + "]");
    if (this.closeFlag) {
      Taro.openBluetoothAdapter({
        success: () => {
          Taro.createBLEConnection({
            deviceId: deviceId,
            success: () => {
              this.printLog("设备连接成功");
              this.getBLEServiceId(deviceId);
            },
            fail: resf => {
              this.printLog("设备连接失败" + resf.errMsg);
            }
          })
        },
        fail: () => {
          Taro.atMessage({
            'message': '请先打开蓝牙',
            'type': 'warning',
          })
        }
      })
    } else {
      Taro.createBLEConnection({
        deviceId: deviceId,
        success: () => {
          this.printLog("设备连接成功");
          this.getBLEServiceId(deviceId);
        },
        fail: resf => {
          this.printLog("设备连接失败" + resf.errMsg);
        }
      })
      // this.closeBLE(deviceId, () => {
      //   console.log("预先关闭,再打开");
      //   setTimeout(() => {
      //     Taro.createBLEConnection({
      //       deviceId: deviceId,
      //       success: () => {
      //         this.printLog("设备连接成功");
      //         this.getBLEServiceId(deviceId);
      //       },
      //       fail: resf => {
      //         this.printLog("设备连接失败" + resf.errMsg);
      //       }
      //     })
      //   }, 2000)
      // });
    }
  }

  closeBLE = (deviceId, callback) => {
    Taro.closeBLEConnection({
      deviceId: deviceId,
      success: () => {
        this.printLog("断开设备[" + deviceId + "]成功.");
        // console.log(res)
      },
      fail: () => {
        this.printLog("断开设备[" + deviceId + "]失败.");
      },
      complete: callback
    })
  }

  // 往蓝牙传输数据
  getBLEServiceId = deviceId => {
    this.printLog("获取设备[" + deviceId + "]服务列表")
    Taro.getBLEDeviceServices({
      deviceId: deviceId,
      success: res => {
        const services = res.services;
        if (isEmpty(services)) {
          this.printLog("未找到主服务列表")
          return;
        }
        this.printLog('找到设备服务列表个数: ' + services.length);
        console.log(services);
        // if (services.length == 1){
        const service = services[0];                                                                                                                                                                                                                                         
        this.printLog("服务UUID:[" + service.uuid + "] Primary:" + service.isPrimary);
        this.getBLECharactedId(deviceId, service.uuid);
        // }else{ //多个主服务
        //   //TODO
        // }
      },
      fail: res => {
        this.printLog("获取设备服务列表失败" + res.errMsg);
      }
    })
  }

  getBLECharactedId = (deviceId, serviceId) => {
    this.printLog("获取设备特征值")
    Taro.getBLEDeviceCharacteristics({
      deviceId: deviceId,
      serviceId: serviceId,
      success: res => {
        // console.log(res);
        //这里会获取到两个特征值,一个用来写,一个用来读
        const chars = res.characteristics;
        if (isEmpty(chars)) {
          this.printLog("未找到设备特征值")
          return;
        }
        // console.log('char-------', chars);
        this.printLog("找到设备特征值个数:" + chars.length);
        if (chars.length == 2) {
          for (var i = 0; i < chars.length; i++) {
            var char = chars[i];
            // console.log(char);
            this.printLog("特征值[" + char.uuid + "]")
            const prop = char.properties;
            console.log('-----特征值', char, prop, deviceId, serviceId, char.uuid)
            if (prop.notify == true) {
              this.printLog("该特征值属性: Notify");
              this.recvBLECharacterNotice(deviceId, serviceId, char.uuid);
            } else if (prop.write == true) {
              this.printLog("该特征值属性: Write");
              this.sendBLECharacterNotice(deviceId, serviceId, char.uuid);
            } else {
              this.printLog("该特征值属性: 不支持写操作");
            }
          }
        } else {
          //TODO
        }
      },
      fail: () => {
        this.printLog("获取设备特征值失败");
      }
    })
  }

  recvBLECharacterNotice = (deviceId, serviceId, charId) => {
    //接收设置是否成功
    this.printLog("注册Notice 回调函数");
    Taro.notifyBLECharacteristicValueChange({
      deviceId: deviceId,
      serviceId: serviceId,
      characteristicId: charId,
      state: true, //启用Notify功能
      success: () => {
        Taro.onBLECharacteristicValueChange(res => {
          console.log(res);
          this.printLog("收到Notify数据: " + ab2hex(res.value));
          //关闭蓝牙
          Taro.showModal({
            title: '配网成功',
            content: ab2hex(res.value),
            showCancel: false
          })
        });
      },
      fail: res => {
        console.log(res);
        this.printLog("特征值Notice 接收数据失败: " + res.errMsg);
      }
    })
  }

  sendBLECharacterNotice = (deviceId, serviceId, charId) => {
    //发送ssid/pass
    this.printLog("延时1秒后,发送SSID/PASS");
    const { wifiName, wifiPassWord } = this.state;
    const buffer = string2buffer(JSON.stringify({
      'ssid': wifiName,
      'pass': wifiPassWord
    }));
    setTimeout(() => {
      Taro.writeBLECharacteristicValue({
        deviceId: deviceId,
        serviceId: serviceId,
        characteristicId: charId,
        value: buffer,
        success: () => {
          this.printLog("发送SSID/PASS成功");
        },
        fail: res => {
          this.printLog("发送失败." + res.errMsg);
        },
        complete: () => {
          Taro.closeBluetoothAdapter({
            success: () => {
              this.printLog("发送完毕,关闭蓝牙设备");
              this.closeFlag = true;
            }
          })
        }
      })
    }, 1000);
  }

  printLog = text => {
    const { logs } = this.state;
    logs.unshift(text);
    this.setState({ logs })
  }

  handleChangeName = (e) => {
    this.setState({
      wifiName: e
    })
  }

  handleChangePwd = (e) => {
    this.setState({
      wifiPassWord: e
    })
  }

  onPickerChange = e => {
    const { deviceArray } = this.state;
    this.setState({
      currDeviceID: deviceArray[e.detail.value]
    })
  }

  render() {
    const { wifiName, wifiPassWord, logs, deviceArray, currDeviceID } = this.state;
    return (
      <View className='blue-tooth-index'>
        <AtMessage />
        <View className='blue-title'>wifi信息(请先打开蓝牙,Android用户需打开GPS定位)</View>
        <AtForm className='contain-form'>
          <AtInput
            name='name'
            title='wifi名称'
            type='text'
            placeholder='请输入wifi名称'
            value={wifiName}
            onChange={e => { this.handleChangeName(e) }}
          />
          <AtInput
            name='password'
            title='wifi密码'
            type='text'
            placeholder='请输入wifi密码'
            value={wifiPassWord}
            onChange={e => { this.handleChangePwd(e) }}
          />
        </AtForm>
        <View className='blue-title'>选择蓝牙设备</View>
        <Picker mode='selector' range={deviceArray} onChange={this.onPickerChange}>
          <AtList>
            <AtListItem
              title='蓝牙设备'
              extraText={currDeviceID}
            />
          </AtList>
        </Picker>
        <AtButton className='blue-btn-submit' type='primary' onClick={e => { this.onSubmit(e) }} >蓝牙设备配网</AtButton>
        <AtButton className='blue-btn-reset' onClick={e => { this.onReset(e) }} >搜索蓝牙设备</AtButton>
        <View className='blue-log'>
          {
            logs.map((v, i) => {
              return (
                <View className='at-article__p' key={i}>{v}</View>
              )
            })
          }
        </View>
      </View>
    )
  }
}

export default BlueTooth;

utils.js

import Taro from "@tarojs/taro";

/**
 * 将字符串转换成ArrayBufer
 */
export function string2buffer(str) {
  if (!str) return;
  var val = "";
  for (var i = 0; i < str.length; i++) {
    val += str.charCodeAt(i).toString(16);
  }
  console.log(val);
  str = val;
  val = "";
  let length = str.length;
  let index = 0;
  let array = []
  while (index < length) {
    array.push(str.substring(index, index + 2));
    index = index + 2;
  }
  val = array.join(",");
  // 将16进制转化为ArrayBuffer
  return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function (h) {
    return parseInt(h, 16)
  })).buffer
}
/**
 * 将ArrayBuffer转换成字符串
 */
export function ab2hex(buffer) {
  var hexArr = Array.prototype.map.call(
    new Uint8Array(buffer),
    function (bit) {
      return ('00' + bit.toString(16)).slice(-2)
    }
  )
  return hexArr.join(':');
}
/**
 * 判断传值是否为空、[]、{}
 * @param {*} param 
 * @returns 
 */
export const isEmpty = (param) => {
  if (param == null) {
    return true;
  } else if (typeof param === 'string') {
    return param == '';
  } else if (typeof param === 'object') {
    return JSON.stringify(param) == '[]' || JSON.stringify(param) == '{}';
  }
  return false;
}


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容