基于Linux与蓝牙的出入门检测Golang实现

最近买的小米家的产品越来越多,发现小米支持在内网通过组播与设备通信,于是想让智能设备联动起来,做到人走灯灭人来灯亮的效果。但是人体传感器不但贵,还要换电池,还不适合搁宿舍墙上,打一开始就不在我的考虑范围内。我打算用我的斐讯N1盒子带的蓝牙解决这个问题。

然后折腾了半天,不是搜索速度太慢,就是无法判断断连,或者蓝牙广播停滞。后来思路一转,我不是有个小米手环吗,小米手环不是能解锁电脑吗,那个和这个原理岂不是差不多。然后我就 did it 了。期间还尝试了使用tasker(一种app)和路由器来替代,都不够好,差点放弃了。

正文开始

所需材料:

  • 支持解锁电脑或手机的智能手环
  • 支持蓝牙的Linux设备例如斐讯N1

一、将手环与设备配对

首先需要打开手环的笔记本解锁功能

在Linux中依次输入

bluetoothctl
power on
agent on

然后等待手环被扫描到,手环的蓝牙ID可以在APP中看到

当手环被扫描到后,执行

trust xx:xx:xx:xx:xx
pair xx:xx:xx:xx:xx

然后输入yes,即配对完成

二、编写物联代码

只要循环连接,就能检测回家,监听断连,就是出门

以下是一个简单示例,需要把蓝牙ID中的冒号改为下划线后填入device.NewDevice1函数中

如果你的设备默认蓝牙设备不是hci0,就要把它一起改了

package main

import (
	"github.com/muka/go-bluetooth/api"
	"github.com/muka/go-bluetooth/bluez/profile/device"
	"log"
	"time"
)

func Run() error {
   defer api.Exit()

   dev,e:=device.NewDevice1("/org/bluez/hci0/dev_xx_xx_xx_xx_xx_xx")
   if e!=nil {
      panic(e)
   }

   for {
      for dev.Connect()!=nil {
         log.Println("try failed")
         time.Sleep(time.Second*5)
      }

      log.Println("connected")

      c,e:=dev.WatchProperties()
      if e!=nil {
         panic(e)
      }

      for {
         d:=<-c
         if d.Name=="Connected" && d.Value.(bool) ==false {
            log.Println("disconnected")
            break
         }
      }
   }

   return nil
}

func main() {
	if e := Run(); e != nil {
		panic(e)
	}
}

运行结果:

然后是一个完整的更智能的亮灯示例

package modules

import (
	"github.com/godbus/dbus/v5"
	"github.com/muka/go-bluetooth/api"
	"github.com/muka/go-bluetooth/bluez/profile/adapter"
	"github.com/muka/go-bluetooth/bluez/profile/device"
	"log"
	"sync"
	"time"
)

type ble struct {
	path       dbus.ObjectPath
	dev        *device.Device1
	connected  bool
	connLock   sync.Mutex
	running    bool
	runnerLock sync.Mutex
	runnerChan chan bool
	autoClosed bool
}

var BLE = ble{
	path:       "/org/bluez/hci0/dev_xx_xx_xx_xx_xx_xx",
	runnerChan: make(chan bool, 1),
}

func init() {
	var e error
	BLE.dev, e = device.NewDevice1(BLE.path)
	if e != nil {
		panic(e)
	}
	go BLE.Run()
}

func (s *ble) discovery() {
	a, e := api.GetDefaultAdapter()
	if e != nil {
		panic(e)
	}
	discovery, cancel, e := api.Discover(a, nil)
	if e != nil {
		panic(e)
	}

	defer cancel()

	for ev := range discovery {
		s.runnerLock.Lock()
		if ev.Type == adapter.DeviceRemoved {
			if s.running && !s.connected && ev.Path == s.path {
				if len(s.runnerChan) == 1 {
					<-s.runnerChan
				}
				s.runnerChan <- false
			}
			continue
		}

		if !s.running && ev.Path == s.path {
			go s.Run()
		}

		s.runnerLock.Unlock()
	}
}

func (s *ble) Run() {
	s.runnerLock.Lock()
	s.running = true
	s.runnerLock.Unlock()

	for {
		var end bool
		for {
			s.connLock.Lock()
			if s.dev.Connect() == nil {
				s.connected = true
				s.connLock.Unlock()
				break
			}
			s.connLock.Unlock()
			select {
			case <-time.After(time.Second * 5):
			case <-s.runnerChan:
				s.runnerLock.Lock()
				s.running = false
				s.runnerLock.Unlock()
				end = true
			}
		}
		if end {
			break
		}

		log.Println("mmx connected")

		if s.autoClosed || time.Now().Hour() > 17 {
			s.autoClosed = false
			_ = MI.OpenLamp()
		}

		c, e := s.dev.WatchProperties()
		if e != nil {
			panic(e)
		}

		for {
			d := <-c
			if d.Name == "Connected" && d.Value.(bool) == false && s.dev.Connect() != nil {
				if open, e := MI.LampStatus(); e == nil && open {
					s.autoClosed = true
				}
				_ = MI.CloseLamp()
				log.Println("mmx disconnected")
				s.connLock.Lock()
				s.connected = false
				s.connLock.Unlock()
				break
			}
		}
	}
}

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注