1
0
mirror of https://github.com/cmur2/miflorad.git synced 2025-06-26 22:30:24 +02:00

munin-miflora: refactor into two implementations using go-ble and gatt libraries, have common package

This commit is contained in:
cn
2018-12-14 09:45:13 +01:00
parent 51bb5a93c5
commit e75c04785a
11 changed files with 476 additions and 232 deletions

View File

@ -1,6 +1,7 @@
package main
import (
"context"
"flag"
"fmt"
"os"
@ -8,74 +9,16 @@ import (
"strings"
"time"
"miflorad/common"
impl "miflorad/common/ble"
"github.com/currantlabs/gatt"
"github.com/currantlabs/gatt/examples/option"
"github.com/go-ble/ble"
"github.com/go-ble/ble/examples/lib/dev"
)
const discoveryTimeout = 4 * time.Second
const connectionTimeout = 4 * time.Second
type DiscoveryResult struct {
p gatt.Peripheral
a *gatt.Advertisement
rssi int
}
var discoveryDone = make(chan DiscoveryResult)
var connectionDone = make(chan struct{})
func onStateChanged(device gatt.Device, state gatt.State) {
fmt.Fprintln(os.Stderr, "State:", state)
switch state {
case gatt.StatePoweredOn:
fmt.Fprintln(os.Stderr, "Scanning...")
device.Scan([]gatt.UUID{}, false)
return
default:
device.StopScanning()
}
}
func onPeriphDiscovered(p gatt.Peripheral, a *gatt.Advertisement, rssi int) {
id := strings.ToUpper(flag.Args()[1])
if strings.ToUpper(p.ID()) != id {
return
}
// Stop scanning once we've got the peripheral we're looking for.
p.Device().StopScanning()
discoveryDone <- DiscoveryResult{p, a, rssi}
}
func onPeriphConnected(p gatt.Peripheral, err error) {
fmt.Fprintln(os.Stderr, "Connected")
// Note: can hang due when device has terminated the connection on it's own already
// defer p.Device().CancelConnection(p)
if err := p.SetMTU(500); err != nil {
fmt.Fprintf(os.Stderr, "Failed to set MTU, err: %s\n", err)
}
// Discover services and characteristics
{
_, err := p.DiscoverServices([]gatt.UUID{common.MifloraServiceUUID})
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to discover services, err: %s\n", err)
return
}
}
for _, service := range p.Services() {
_, err := p.DiscoverCharacteristics(nil, service)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to discover characteristics, err: %s\n", err)
return
}
}
func readData(client ble.Client, profile *ble.Profile) {
prefix := flag.Args()[0]
regexNonAlphaNumeric, err4 := regexp.Compile("[^a-z0-9]+")
@ -84,7 +27,7 @@ func onPeriphConnected(p gatt.Peripheral, err error) {
}
id := regexNonAlphaNumeric.ReplaceAllString(strings.ToLower(flag.Args()[1]), "")
metaData, err := common.MifloraRequestVersionBattery(p)
metaData, err := impl.RequestVersionBattery(client, profile)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to request version battery, err: %s\n", err)
return
@ -93,18 +36,18 @@ func onPeriphConnected(p gatt.Peripheral, err error) {
fmt.Fprintf(os.Stderr, "Firmware version: %s\n", metaData.FirmwareVersion)
fmt.Fprintf(os.Stdout, "%s.miflora.%s.battery_level %d %d\n", prefix, id, metaData.BatteryLevel, time.Now().Unix())
fmt.Fprintf(os.Stdout, "%s.miflora.%s.firmware_version %s %d\n", prefix, id, metaData.NumericFirmwareVersion(), time.Now().Unix())
fmt.Fprintf(os.Stdout, "%s.miflora.%s.firmware_version %d %d\n", prefix, id, metaData.NumericFirmwareVersion(), time.Now().Unix())
// for the newer models a magic number must be written before we can read the current data
if metaData.FirmwareVersion >= "2.6.6" {
err2 := common.MifloraRequestModeChange(p)
err2 := impl.RequestModeChange(client, profile)
if err2 != nil {
fmt.Fprintf(os.Stderr, "Failed to request mode change, err: %s\n", err2)
return
}
}
sensorData, err3 := common.MifloraRequstSensorData(p)
sensorData, err3 := impl.RequestSensorData(client, profile)
if err3 != nil {
fmt.Fprintf(os.Stderr, "Failed to request sensor data, err: %s\n", err3)
return
@ -115,11 +58,6 @@ func onPeriphConnected(p gatt.Peripheral, err error) {
fmt.Fprintf(os.Stdout, "%s.miflora.%s.conductivity %d %d\n", prefix, id, sensorData.Conductivity, time.Now().Unix())
}
func onPeriphDisconnected(p gatt.Peripheral, err error) {
fmt.Fprintln(os.Stderr, "Disconnected")
close(connectionDone)
}
func main() {
flag.Parse()
if len(flag.Args()) != 2 {
@ -127,49 +65,52 @@ func main() {
os.Exit(1)
}
device, err := gatt.NewDevice(option.DefaultClientOptions...)
{
device, err := dev.NewDevice("default")
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to open device, err: %s\n", err)
os.Exit(1)
}
ble.SetDefaultDevice(device)
}
filter := func(adv ble.Advertisement) bool {
return strings.ToUpper(adv.Addr().String()) == strings.ToUpper(flag.Args()[1])
}
fmt.Fprintln(os.Stderr, "Scanning...")
ctx := ble.WithSigHandler(context.WithTimeout(context.Background(), discoveryTimeout))
client, err := ble.Connect(ctx, filter)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to open device, err: %s\n", err)
fmt.Fprintf(os.Stderr, "Failed to connect to %s, err: %s\n", flag.Args()[1], err)
os.Exit(1)
}
// Register discovery handler
device.Handle(gatt.PeripheralDiscovered(onPeriphDiscovered))
// Source: https://github.com/go-ble/ble/blob/master/examples/basic/explorer/main.go#L53
// Make sure we had the chance to print out the message.
done := make(chan struct{})
// Normally, the connection is disconnected by us after our exploration.
// However, it can be asynchronously disconnected by the remote peripheral.
// So we wait(detect) the disconnection in the go routine.
go func() {
<-client.Disconnected()
fmt.Fprintln(os.Stderr, "Disconnected")
close(done)
}()
device.Init(onStateChanged)
fmt.Fprintln(os.Stderr, "Connected")
var discoveryResult DiscoveryResult
select {
case discoveryResult = <-discoveryDone:
fmt.Fprintln(os.Stderr, "Discovery done")
case <-time.After(discoveryTimeout):
fmt.Fprintln(os.Stderr, "Discovery timed out")
device.StopScanning()
device.Stop()
profile, err := client.DiscoverProfile(true)
if err != nil {
fmt.Fprintf(os.Stderr, "Failed to discover profile, err: %s\n", err)
os.Exit(1)
}
fmt.Fprintf(os.Stderr, "Discovered peripheral ID:%s, NAME:(%s), RSSI:%d\n", discoveryResult.p.ID(), discoveryResult.p.Name(), discoveryResult.rssi)
readData(client, profile)
// Register connection handlers
device.Handle(
gatt.PeripheralConnected(onPeriphConnected),
gatt.PeripheralDisconnected(onPeriphDisconnected),
)
fmt.Fprintln(os.Stderr, "Connection done")
device.Connect(discoveryResult.p)
client.CancelConnection()
select {
case <-connectionDone:
fmt.Fprintln(os.Stderr, "Connection done")
case <-time.After(connectionTimeout):
fmt.Fprintln(os.Stderr, "Connection timed out")
// TODO: can hang due when device has terminated the connection on it's own already
// device.CancelConnection(discoveryResult.p)
os.Exit(1)
}
// Note: calls CancelConnetcion() and thus suffers the same problem, kernel will cleanup after our process finishes
// device.Stop()
<-done
}