taro 微信小程序連接樹莓派4b藍牙并寫入wifi信息

設(shè)備賣給客戶,不能通過設(shè)備自動連接客戶家中的網(wǎng)絡(luò)乞旦,使用微信小程序連接樹莓派藍牙贼穆,并發(fā)送數(shù)據(jù)(wifi信息)給樹莓派

先點擊 ‘搜索藍牙設(shè)備’搜索藍牙

可通過uuid唯一搜索樹莓派(如果不知道,那就不填寫兰粉,就會導(dǎo)致搜索出來很多藍牙故痊。下面代碼中有備注)
如果不填寫local_name(需要在樹莓派中的藍牙配置中修改)會是導(dǎo)致搜索出來很多名稱為‘未知設(shè)備’的藍牙


9lr35-ouajf.gif

樹莓派上的接收

5c6c4fcdb637de74c4c39660b6baee3.jpg

該程序是使用taro編寫,大體上和wx小程序?qū)懛ㄒ粯泳凉茫凸β仕{牙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定位,無法搜索到藍牙設(shè)備.',
                showCancel: false
              })
            }
          }
        })
      }
    })
  }

  componentWillUnmount() { }

  componentDidShow() { }

  componentDidHide() { }

  delayTimer = ''; // 用來控制是否持續(xù)服務(wù)發(fā)現(xiàn)
  isFound = false; // 藍牙是否被發(fā)現(xiàn)判斷標識
  closeFlag = false; // 藍牙適配器是否被關(guān)閉標識

  onSubmit = () => {
    const { currDeviceID, wifiName, wifiPassWord } = this.state;
    console.log("選中:" + currDeviceID);
    if (isEmpty(currDeviceID) || currDeviceID == "請選擇...") {
      Taro.atMessage({
        'message': '請先搜索設(shè)備',
        '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': '當前沒有藍牙設(shè)備在線',
        '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'], // 這里要注意焰络,填寫筒子們自己設(shè)備的uuid戴甩,如果不知道,可以注釋的掉這一行闪彼,注釋導(dǎo)致會搜索到很多藍牙設(shè)備![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: '正在搜索設(shè)備',
        })
        this.delayTimer = setInterval(() => {
          this.discoveryBLE() //3.0 //這里的discovery需要多次調(diào)用
        }, 1000);
        setTimeout(() => {
          if (this.isFound) {
            return;
          } else {
            Taro.hideLoading();
            console.log("搜索設(shè)備超時");
            Taro.stopBluetoothDevicesDiscovery({
              success: () => {
                console.log('連接藍牙成功之后關(guān)閉藍牙搜索');
              }
            })
            clearInterval(this.delayTimer)
            Taro.showModal({
              title: '搜索設(shè)備超時',
              content: '請檢查藍牙設(shè)備是否正常工作甜孤,Android手機請打開GPS定位.',
              showCancel: false
            })
            Taro.atMessage({
              'message': '搜索設(shè)備超時,請打開GPS定位畏腕,再搜索',
              'type': 'warning',
            })
            return
          }
        }, 15000);
      },
      fail: res => {
        this.printLog("藍牙設(shè)備服務(wù)發(fā)現(xiàn)失敗: " + 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 + "個設(shè)備需要設(shè)置")
        if (devices.length <= 0) {
          return;
        }
        this.connectBLE(devices);
      },
      fail: function () {
        Taro.atMessage({
          'message': '搜索藍牙設(shè)備失敗',
          'type': 'warning',
        })
      }
    })
  }

  connectBLE = devices => {
    this.printLog('總共有' + devices.length + "個設(shè)備需要設(shè)置")
    Taro.hideLoading();
    this.isFound = true;
    clearInterval(this.delayTimer);
    Taro.stopBluetoothDevicesDiscovery({
      success: () => {
        this.printLog('連接藍牙成功之后關(guān)閉藍牙搜索');
      }
    })
    //兩個的時候需要選擇
    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("設(shè)備連接成功");
              this.getBLEServiceId(deviceId);
            },
            fail: resf => {
              this.printLog("設(shè)備連接失敗" + resf.errMsg);
            }
          })
        },
        fail: () => {
          Taro.atMessage({
            'message': '請先打開藍牙',
            'type': 'warning',
          })
        }
      })
    } else {
      Taro.createBLEConnection({
        deviceId: deviceId,
        success: () => {
          this.printLog("設(shè)備連接成功");
          this.getBLEServiceId(deviceId);
        },
        fail: resf => {
          this.printLog("設(shè)備連接失敗" + resf.errMsg);
        }
      })
      // this.closeBLE(deviceId, () => {
      //   console.log("預(yù)先關(guān)閉缴川,再打開");
      //   setTimeout(() => {
      //     Taro.createBLEConnection({
      //       deviceId: deviceId,
      //       success: () => {
      //         this.printLog("設(shè)備連接成功");
      //         this.getBLEServiceId(deviceId);
      //       },
      //       fail: resf => {
      //         this.printLog("設(shè)備連接失敗" + resf.errMsg);
      //       }
      //     })
      //   }, 2000)
      // });
    }
  }

  closeBLE = (deviceId, callback) => {
    Taro.closeBLEConnection({
      deviceId: deviceId,
      success: () => {
        this.printLog("斷開設(shè)備[" + deviceId + "]成功.");
        // console.log(res)
      },
      fail: () => {
        this.printLog("斷開設(shè)備[" + deviceId + "]失敗.");
      },
      complete: callback
    })
  }

  // 往藍牙傳輸數(shù)據(jù)
  getBLEServiceId = deviceId => {
    this.printLog("獲取設(shè)備[" + deviceId + "]服務(wù)列表")
    Taro.getBLEDeviceServices({
      deviceId: deviceId,
      success: res => {
        const services = res.services;
        if (isEmpty(services)) {
          this.printLog("未找到主服務(wù)列表")
          return;
        }
        this.printLog('找到設(shè)備服務(wù)列表個數(shù): ' + services.length);
        console.log(services);
        // if (services.length == 1){
        const service = services[0];                                                                                                                                                                                                                                         
        this.printLog("服務(wù)UUID:[" + service.uuid + "] Primary:" + service.isPrimary);
        this.getBLECharactedId(deviceId, service.uuid);
        // }else{ //多個主服務(wù)
        //   //TODO
        // }
      },
      fail: res => {
        this.printLog("獲取設(shè)備服務(wù)列表失敗" + res.errMsg);
      }
    })
  }

  getBLECharactedId = (deviceId, serviceId) => {
    this.printLog("獲取設(shè)備特征值")
    Taro.getBLEDeviceCharacteristics({
      deviceId: deviceId,
      serviceId: serviceId,
      success: res => {
        // console.log(res);
        //這里會獲取到兩個特征值,一個用來寫描馅,一個用來讀
        const chars = res.characteristics;
        if (isEmpty(chars)) {
          this.printLog("未找到設(shè)備特征值")
          return;
        }
        // console.log('char-------', chars);
        this.printLog("找到設(shè)備特征值個數(shù):" + 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("獲取設(shè)備特征值失敗");
      }
    })
  }

  recvBLECharacterNotice = (deviceId, serviceId, charId) => {
    //接收設(shè)置是否成功
    this.printLog("注冊Notice 回調(diào)函數(shù)");
    Taro.notifyBLECharacteristicValueChange({
      deviceId: deviceId,
      serviceId: serviceId,
      characteristicId: charId,
      state: true, //啟用Notify功能
      success: () => {
        Taro.onBLECharacteristicValueChange(res => {
          console.log(res);
          this.printLog("收到Notify數(shù)據(jù): " + ab2hex(res.value));
          //關(guān)閉藍牙
          Taro.showModal({
            title: '配網(wǎng)成功',
            content: ab2hex(res.value),
            showCancel: false
          })
        });
      },
      fail: res => {
        console.log(res);
        this.printLog("特征值Notice 接收數(shù)據(jù)失敗: " + res.errMsg);
      }
    })
  }

  sendBLECharacterNotice = (deviceId, serviceId, charId) => {
    //發(fā)送ssid/pass
    this.printLog("延時1秒后把夸,發(fā)送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("發(fā)送SSID/PASS成功");
        },
        fail: res => {
          this.printLog("發(fā)送失敗." + res.errMsg);
        },
        complete: () => {
          Taro.closeBluetoothAdapter({
            success: () => {
              this.printLog("發(fā)送完畢,關(guān)閉藍牙設(shè)備");
              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'>選擇藍牙設(shè)備</View>
        <Picker mode='selector' range={deviceArray} onChange={this.onPickerChange}>
          <AtList>
            <AtListItem
              title='藍牙設(shè)備'
              extraText={currDeviceID}
            />
          </AtList>
        </Picker>
        <AtButton className='blue-btn-submit' type='primary' onClick={e => { this.onSubmit(e) }} >藍牙設(shè)備配網(wǎng)</AtButton>
        <AtButton className='blue-btn-reset' onClick={e => { this.onReset(e) }} >搜索藍牙設(shè)備</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";

/**
 * 將字符串轉(zhuǎn)換成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進制轉(zhuǎn)化為ArrayBuffer
  return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function (h) {
    return parseInt(h, 16)
  })).buffer
}
/**
 * 將ArrayBuffer轉(zhuǎn)換成字符串
 */
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;
}


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末况凉,一起剝皮案震驚了整個濱河市谚鄙,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌刁绒,老刑警劉巖闷营,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡傻盟,警方通過查閱死者的電腦和手機速蕊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來娘赴,“玉大人规哲,你說我怎么就攤上這事》瘫恚” “怎么了唉锌?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長竿奏。 經(jīng)常有香客問我袄简,道長,這世上最難降的妖魔是什么泛啸? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任绿语,我火速辦了婚禮,結(jié)果婚禮上候址,老公的妹妹穿的比我還像新娘吕粹。我一直安慰自己,他們只是感情好宗雇,可當我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布昂芜。 她就那樣靜靜地躺著,像睡著了一般赔蒲。 火紅的嫁衣襯著肌膚如雪泌神。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天舞虱,我揣著相機與錄音欢际,去河邊找鬼。 笑死矾兜,一個胖子當著我的面吹牛损趋,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播椅寺,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼浑槽,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了返帕?” 一聲冷哼從身側(cè)響起桐玻,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎荆萤,沒想到半個月后镊靴,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體以躯,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡届榄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了晕换。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片启搂。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡穗酥,死狀恐怖立由,靈堂內(nèi)的尸體忽然破棺而出后裸,到底是詐尸還是另有隱情,我是刑警寧澤褪子,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布量淌,位于F島的核電站,受9級特大地震影響嫌褪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜胚股,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一笼痛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧琅拌,春花似錦缨伊、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至党晋,卻和暖如春谭胚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背未玻。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工灾而, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扳剿。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓旁趟,卻偏偏與公主長得像,于是被迫代替她去往敵國和親庇绽。 傳聞我的和親對象是個殘疾皇子锡搜,可洞房花燭夜當晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容