Example #1
0
func (d *Device) conn(m msg) *conn {
	// Convert xpc.UUID to ble.UUID.
	a := ble.MustParse(m.deviceUUID().String())
	c, ok := d.conns[a.String()]
	if !ok {
		c = newConn(d, a)
		d.conns[a.String()] = c
	}
	return c
}
Example #2
0
// DiscoverDescriptors finds all the descriptors within a characteristic. [Vol 3, Part G, 4.7.1]
// If filter is specified, only filtered descriptors are returned.
func (cln *Client) DiscoverDescriptors(ds []ble.UUID, c *ble.Characteristic) ([]*ble.Descriptor, error) {
	rsp := cln.conn.sendReq(70, xpc.Dict{
		"kCBMsgArgDeviceUUID":                cln.id,
		"kCBMsgArgCharacteristicHandle":      c.Handle,
		"kCBMsgArgCharacteristicValueHandle": c.ValueHandle,
		"kCBMsgArgUUIDs":                     uuidSlice(ds),
	})
	for _, xds := range rsp.descriptors() {
		xd := msg(xds.(xpc.Dict))
		c.Descriptors = append(c.Descriptors, &ble.Descriptor{
			UUID:   ble.MustParse(xd.uuid()),
			Handle: uint16(xd.descriptorHandle()),
		})
	}
	return c.Descriptors, nil
}
Example #3
0
// DiscoverCharacteristics finds all the characteristics within a service. [Vol 3, Part G, 4.6.1]
// If filter is specified, only filtered characteristics are returned.
func (cln *Client) DiscoverCharacteristics(cs []ble.UUID, s *ble.Service) ([]*ble.Characteristic, error) {
	rsp := cln.conn.sendReq(62, xpc.Dict{
		"kCBMsgArgDeviceUUID":         cln.id,
		"kCBMsgArgServiceStartHandle": s.Handle,
		"kCBMsgArgServiceEndHandle":   s.EndHandle,
		"kCBMsgArgUUIDs":              uuidSlice(cs),
	})
	if rsp.err() != nil {
		return nil, rsp.err()
	}
	for _, xcs := range rsp.characteristics() {
		xc := msg(xcs.(xpc.Dict))
		s.Characteristics = append(s.Characteristics, &ble.Characteristic{
			UUID:        ble.MustParse(xc.uuid()),
			Property:    ble.Property(xc.characteristicProperties()),
			Handle:      uint16(xc.characteristicHandle()),
			ValueHandle: uint16(xc.characteristicValueHandle()),
		})
	}
	return s.Characteristics, nil
}
Example #4
0
// DiscoverServices finds all the primary services on a server. [Vol 3, Part G, 4.4.1]
// If filter is specified, only filtered services are returned.
func (cln *Client) DiscoverServices(ss []ble.UUID) ([]*ble.Service, error) {
	rsp := cln.conn.sendReq(45, xpc.Dict{
		"kCBMsgArgDeviceUUID": cln.id,
		"kCBMsgArgUUIDs":      uuidSlice(ss),
	})
	if rsp.err() != nil {
		return nil, rsp.err()
	}
	svcs := []*ble.Service{}
	for _, xss := range rsp.services() {
		xs := msg(xss.(xpc.Dict))
		svcs = append(svcs, &ble.Service{
			UUID:      ble.MustParse(xs.uuid()),
			Handle:    uint16(xs.serviceStartHandle()),
			EndHandle: uint16(xs.serviceEndHandle()),
		})
	}
	if cln.profile == nil {
		cln.profile = &ble.Profile{Services: svcs}
	}
	return svcs, nil
}
Example #5
0
func explore(cln ble.Client, p *ble.Profile) error {
	for _, s := range p.Services {
		fmt.Printf("    Service: %s %s, Handle (0x%02X)\n", s.UUID, ble.Name(s.UUID), s.Handle)

		for _, c := range s.Characteristics {
			fmt.Printf("      Characteristic: %s %s, Property: 0x%02X (%s), Handle(0x%02X), VHandle(0x%02X)\n",
				c.UUID, ble.Name(c.UUID), c.Property, propString(c.Property), c.Handle, c.ValueHandle)
			if (c.Property & ble.CharRead) != 0 {
				b, err := cln.ReadCharacteristic(c)
				if err != nil {
					fmt.Printf("Failed to read characteristic: %s\n", err)
					continue
				}
				fmt.Printf("        Value         %x | %q\n", b, b)
			}

			for _, d := range c.Descriptors {
				fmt.Printf("        Descriptor: %s %s, Handle(0x%02x)\n", d.UUID, ble.Name(d.UUID), d.Handle)
				b, err := cln.ReadDescriptor(d)
				if err != nil {
					fmt.Printf("Failed to read descriptor: %s\n", err)
					continue
				}
				fmt.Printf("        Value         %x | %q\n", b, b)
			}

			if *sub != 0 {
				// Don't bother to subscribe the Service Changed characteristics.
				if c.UUID.Equal(ble.ServiceChangedUUID) {
					continue
				}

				// Don't touch the Apple-specific Service/Characteristic.
				// Service: D0611E78BBB44591A5F8487910AE4366
				// Characteristic: 8667556C9A374C9184ED54EE27D90049, Property: 0x18 (WN),
				//   Descriptor: 2902, Client Characteristic Configuration
				//   Value         0000 | "\x00\x00"
				if c.UUID.Equal(ble.MustParse("8667556C9A374C9184ED54EE27D90049")) {
					continue
				}

				if (c.Property & ble.CharNotify) != 0 {
					fmt.Printf("\n-- Subscribe to notification for %s --\n", *sub)
					h := func(req []byte) { fmt.Printf("Notified: %q [ % X ]\n", string(req), req) }
					if err := cln.Subscribe(c, false, h); err != nil {
						log.Fatalf("subscribe failed: %s", err)
					}
					time.Sleep(*sub)
					if err := cln.Unsubscribe(c, false); err != nil {
						log.Fatalf("unsubscribe failed: %s", err)
					}
					fmt.Printf("-- Unsubscribe to notification --\n")
				}
				if (c.Property & ble.CharIndicate) != 0 {
					fmt.Printf("\n-- Subscribe to indication of %s --\n", *sub)
					h := func(req []byte) { fmt.Printf("Indicated: %q [ % X ]\n", string(req), req) }
					if err := cln.Subscribe(c, true, h); err != nil {
						log.Fatalf("subscribe failed: %s", err)
					}
					time.Sleep(*sub)
					if err := cln.Unsubscribe(c, true); err != nil {
						log.Fatalf("unsubscribe failed: %s", err)
					}
					fmt.Printf("-- Unsubscribe to indication --\n")
				}
			}
		}
		fmt.Printf("\n")
	}
	return nil
}
Example #6
0
package lib

import "github.com/currantlabs/ble"

// Private 128-bit UUIDs, which avoids the base of pre-defined 16/32-bits UUIDS
// xxxxxxxx-0000-1000-8000-00805F9B34FB [Vol 3, Part B, 2.5.1].
var (
	TestSvcUUID   = ble.MustParse("00010000-0001-1000-8000-00805F9B34FB")
	CountCharUUID = ble.MustParse("00010000-0002-1000-8000-00805F9B34FB")
	EchoCharUUID  = ble.MustParse("00020000-0002-1000-8000-00805F9B34FB")
)