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


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,635评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,628评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,971评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,986评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,006评论 6 394
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,784评论 1 307
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,475评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,364评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,860评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,008评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,152评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,829评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,490评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,035评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,156评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,428评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,127评论 2 356

推荐阅读更多精彩内容