Git Product home page Git Product logo

mac-ble-peripheral-objc-cli's Introduction

Introduction

This is a demonstration of my experimentation with running a Bluetooth LE peripheral on my Mac, as a proof of concept and part of my endeavour to learn about BLE. Hopefully you will find it useful for the same reasons, or maybe will help kickstart your next BLE peripheral implementation.

Protocol flow

Without going too deeply into the BLE stack's protocol, a subject for entire books, here's the flow for this command-line POC:

  • Set up a run-loop that allocates CPU to our routines
  • Start up Mac OS X BLE service
  • Set up a service and associated characteristic of the POC
  • Start advertizing the service
  • Handle incoming I/O requests

Implementation

Set up a run-loop that allocates CPU to our routines

I did not want to be distracted by coding a UI for this POC, so I chose to implement this POC as a command-line-tool. This means we need to give ourselves CPU time using a loop in main.m:

while([manager running] && [runLoop runMode:NSDefaultRunLoopMode beforeDate:distantFuture]) {
[manager advertize];
}

Without this loop, nothing will work.

Start up Mac OS X BLE service

By instantiating CBPeripheralManager, Mac OS loads and starts the BLE framework, allowing us to interact with it as a peripheral.

This is done in the constructor of manager.m:

_peripheralManager = [[CBPeripheralManager alloc] initWithDelegate:self queue:nil];

Set up a service and associated characteristic of the POC

To serve data, we need to create a service that will be advertised to potential clients. Doing so is a matter of creating a characteristic, which will expose the peripheral's data, and adding it to a service that clients can look for. Both these objects (service and characteristic) need a UUID to distinguish them from other vendors and their services. I used the Mac's built-in UUID generator (uuidgen) to create both for the POC and assigned them to instance variables:

_charUUID = [CBUUID UUIDWithString:@"DDCA9B49-A6F5-462F-A89A-C2144083CA7F"];
_myUUID = [CBUUID UUIDWithString:@"BD0F6577-4A38-4D71-AF1B-4E8F57708080"];

I wanted to be able to read and write the data value of the peripheral, to test out those functions, and used the following flags when creating the characteristic:

CBMutableCharacteristic *myCharacteristic = [[CBMutableCharacteristic alloc]
initWithType:_charUUID properties:CBCharacteristicPropertyRead|CBCharacteristicPropertyIndicate|CBCharacteristicPropertyWriteWithoutResponse value:nil permissions:CBAttributePermissionsReadable|CBAttributePermissionsWriteable];

An important point is not to set the initial value (see value:nil); if you set the initial value, it's taken as a static characteristic, and writes won't be routed to our callback.

After adding the characteristic to the service, we add the service to the list of services the peripheral supports to the manager object, putting us in a state where we can adverise:

Start advertising the service

To have Centrals connect to our Peripheral, it needs to adverise itself:

[_peripheralManager startAdvertising:@{
CBAdvertisementDataLocalNameKey: @"ITAMAR-MAC-BOOK-PRO",
CBAdvertisementDataServiceUUIDsKey: @[_myUUID]
}];

The name 'ITAMAR-MAC-BOOK-PRO' is the one your Bluetooth scanner might display. I used BLE Scanner.

Handle incoming I/O requests

At this stage, your scanner should pick up the device (your mac) and service (itamar etc). Characteristics can be read or written only if the Central is connected to them. This is not pairing, but connecting. Once connected, your scanner can read the value exposed by querying the specific charactersitic's UUID, which, in our case, is

DDCA9B49-A6F5-462F-A89A-C2144083CA7F

Reading, as well as writing, will trigger our callbacks to be called by CBPeripheralManager:

Read callback:

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request
{
if ([request.characteristic.UUID isEqual:_charUUID]) {
request.value = [_peripheralData dataUsingEncoding:NSUTF8StringEncoding];
[peripheral respondToRequest:request withResult:CBATTErrorSuccess];
NSLog(@"didReceiveReadRequest: %@ %@. Returning %@", request.central, request.characteristic.UUID, _peripheralData);
} else
{
NSLog(@"didReceiveReadRequest: %@ %@. Ignoring!", request.central, request.characteristic.UUID);
}
}

Write callback:

- (void)peripheralManager:(CBPeripheralManager *)peripheral didReceiveWriteRequests:(NSArray<CBATTRequest *> *)requests
{
CBATTRequest *request = requests[0];
if ([request.characteristic.UUID isEqual:_charUUID]) {
_peripheralData =[NSString stringWithUTF8String:[request.value bytes]];
NSLog(@"didReceiveWriteRequest: Wrote: %@", _peripheralData);
} else
{
NSLog(@"didReceiveWriteRequest: %@ %@. Ignoring!", request.central, request.characteristic.UUID);
}
}

In this POC, I do some basic checking and handle the request, but most of the code is to translate NSData to NSString and vice versa.

Access the example

Please feel free to use, fork and improve this snippet, posted on github.

I hope you find the example useful!

Happy hacking!

Build & Run Instructions

  1. Build it using Xcode

    xcodebuild
  2. Run it

    ./build/Release/mac-ble-peripheral-objc-cli.app/Contents/MacOS/mac-ble-peripheral-objc-cli

mac-ble-peripheral-objc-cli's People

Contributors

ihassin avatar dfrankland avatar

Watchers

James Cloos avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.