diff --git a/examples/pizza/README.md b/examples/pizza/README.md new file mode 100644 index 00000000..0b9aaeeb --- /dev/null +++ b/examples/pizza/README.md @@ -0,0 +1,15 @@ +# BLE Pizza Service + +This is an example program demonstrating BLE connectivity between a peripheral running bleno, and a central running noble. + +The service represents a robotic pizza oven, with the following characteristics: + +* crust - read / write. A value representing the type of pizza crust (normal, thin, or deep dish) +* toppings - read / write. A value representing which toppings to include (pepperoni, mushrooms, extra cheese, etc.) +* bake - write / notify. The value written is the temperature at which to bake the pizza. When baking is finished, the central is notified with a bake result (half baked, crispy, burnt, etc.) + +To run the peripheral example: + + node peripheral + +And on another computer, connect as a central from [noble](https://github.com/sandeepmistry/noble/tree/master/examples/pizza). \ No newline at end of file diff --git a/examples/pizza/peripheral.js b/examples/pizza/peripheral.js new file mode 100644 index 00000000..b186a6a8 --- /dev/null +++ b/examples/pizza/peripheral.js @@ -0,0 +1,60 @@ +var util = require('util'); + +// +// Require bleno peripheral library. +// https://github.com/sandeepmistry/bleno +// +var bleno = require('../..'); + +// +// Pizza +// * has crust +// * has toppings +// * can be baked +// +var pizza = require('./pizza'); + +// +// The BLE Pizza Service! +// +var PizzaService = require('./pizza-service'); + +// +// A name to advertise our Pizza Service. +// +var name = 'PizzaSquat'; +var pizzaService = new PizzaService(new pizza.Pizza()); + +// +// Wait until the BLE radio powers on before attempting to advertise. +// If you don't have a BLE radio, then it will never power on! +// +bleno.on('stateChange', function(state) { + if (state === 'poweredOn') { + // + // We will also advertise the service ID in the advertising packet, + // so it's easier to find. + // + bleno.startAdvertising(name, [pizzaService.uuid], function(err) { + if (err) { + console.log(err); + } + }); + } + else { + bleno.stopAdvertising(); + } +}); + +bleno.on('advertisingStart', function(err) { + if (!err) { + console.log('advertising...'); + // + // Once we are advertising, it's time to set up our services, + // along with our characteristics. + // + bleno.setServices([ + pizzaService + ]); + } +}); diff --git a/examples/pizza/pizza-bake-characteristic.js b/examples/pizza/pizza-bake-characteristic.js new file mode 100644 index 00000000..984058af --- /dev/null +++ b/examples/pizza/pizza-bake-characteristic.js @@ -0,0 +1,44 @@ +var util = require('util'); +var bleno = require('../..'); +var pizza = require('./pizza'); + +function PizzaBakeCharacteristic(pizza) { + bleno.Characteristic.call(this, { + uuid: '13333333333333333333333333330003', + properties: ['notify', 'write'], + descriptors: [ + new bleno.Descriptor({ + uuid: '2901', + value: 'Bakes the pizza and notifies when done baking.' + }) + ] + }); + + this.pizza = pizza; +} + +util.inherits(PizzaBakeCharacteristic, bleno.Characteristic); + +PizzaBakeCharacteristic.prototype.onWriteRequest = function(data, offset, withoutResponse, callback) { + if (offset) { + callback(this.RESULT_ATTR_NOT_LONG); + } + else if (data.length !== 2) { + callback(this.RESULT_INVALID_ATTRIBUTE_LENGTH); + } + else { + var temperature = data.readUInt16BE(0); + var self = this; + this.pizza.once('ready', function(result) { + if (self.updateValueCallback) { + var data = new Buffer(1); + data.writeUInt8(result, 0); + self.updateValueCallback(data); + } + }); + this.pizza.bake(temperature); + callback(this.RESULT_SUCCESS); + } +}; + +module.exports = PizzaBakeCharacteristic; \ No newline at end of file diff --git a/examples/pizza/pizza-crust-characteristic.js b/examples/pizza/pizza-crust-characteristic.js new file mode 100644 index 00000000..287db2b8 --- /dev/null +++ b/examples/pizza/pizza-crust-characteristic.js @@ -0,0 +1,56 @@ +var util = require('util'); +var bleno = require('../..'); +var pizza = require('./pizza'); + +function PizzaCrustCharacteristic(pizza) { + bleno.Characteristic.call(this, { + uuid: '13333333333333333333333333330001', + properties: ['read', 'write'], + descriptors: [ + new bleno.Descriptor({ + uuid: '2901', + value: 'Gets or sets the type of pizza crust.' + }) + ] + }); + + this.pizza = pizza; +} + +util.inherits(PizzaCrustCharacteristic, bleno.Characteristic); + +PizzaCrustCharacteristic.prototype.onWriteRequest = function(data, offset, withoutResponse, callback) { + if (offset) { + callback(this.RESULT_ATTR_NOT_LONG); + } + else if (data.length !== 1) { + callback(this.RESULT_INVALID_ATTRIBUTE_LENGTH); + } + else { + var crust = data.readUInt8(0); + switch (crust) { + case pizza.PizzaCrust.NORMAL: + case pizza.PizzaCrust.DEEP_DISH: + case pizza.PizzaCrust.THIN: + this.pizza.crust = crust; + callback(this.RESULT_SUCCESS); + break; + default: + callback(this.RESULT_UNLIKELY_ERROR); + break; + } + } +}; + +PizzaCrustCharacteristic.prototype.onReadRequest = function(offset, callback) { + if (offset) { + callback(this.RESULT_ATTR_NOT_LONG, null); + } + else { + var data = new Buffer(1); + data.writeUInt8(this.pizza.crust, 0); + callback(this.RESULT_SUCCESS, data); + } +}; + +module.exports = PizzaCrustCharacteristic; \ No newline at end of file diff --git a/examples/pizza/pizza-service.js b/examples/pizza/pizza-service.js new file mode 100644 index 00000000..3652e461 --- /dev/null +++ b/examples/pizza/pizza-service.js @@ -0,0 +1,21 @@ +var util = require('util'); +var bleno = require('../..'); + +var PizzaCrustCharacteristic = require('./pizza-crust-characteristic'); +var PizzaToppingsCharacteristic = require('./pizza-toppings-characteristic'); +var PizzaBakeCharacteristic = require('./pizza-bake-characteristic'); + +function PizzaService(pizza) { + bleno.PrimaryService.call(this, { + uuid: '13333333333333333333333333333337', + characteristics: [ + new PizzaCrustCharacteristic(pizza), + new PizzaToppingsCharacteristic(pizza), + new PizzaBakeCharacteristic(pizza) + ] + }); +} + +util.inherits(PizzaService, bleno.PrimaryService); + +module.exports = PizzaService; diff --git a/examples/pizza/pizza-toppings-characteristic.js b/examples/pizza/pizza-toppings-characteristic.js new file mode 100644 index 00000000..cd1f8084 --- /dev/null +++ b/examples/pizza/pizza-toppings-characteristic.js @@ -0,0 +1,46 @@ +var util = require('util'); +var bleno = require('../..'); +var pizza = require('./pizza'); + +function PizzaToppingsCharacteristic(pizza) { + bleno.Characteristic.call(this, { + uuid: '13333333333333333333333333330002', + properties: ['read', 'write'], + descriptors: [ + new bleno.Descriptor({ + uuid: '2901', + value: 'Gets or sets the pizza toppings.' + }) + ] + }); + + this.pizza = pizza; +} + +util.inherits(PizzaToppingsCharacteristic, bleno.Characteristic); + +PizzaToppingsCharacteristic.prototype.onWriteRequest = function(data, offset, withoutResponse, callback) { + if (offset) { + callback(this.RESULT_ATTR_NOT_LONG); + } + else if (data.length !== 2) { + callback(this.RESULT_INVALID_ATTRIBUTE_LENGTH); + } + else { + this.pizza.toppings = data.readUInt16BE(0); + callback(this.RESULT_SUCCESS); + } +}; + +PizzaToppingsCharacteristic.prototype.onReadRequest = function(offset, callback) { + if (offset) { + callback(this.RESULT_ATTR_NOT_LONG, null); + } + else { + var data = new Buffer(2); + data.writeUInt16BE(this.pizza.toppings, 0); + callback(this.RESULT_SUCCESS, data); + } +}; + +module.exports = PizzaToppingsCharacteristic; \ No newline at end of file diff --git a/examples/pizza/pizza.js b/examples/pizza/pizza.js new file mode 100644 index 00000000..59fc0c56 --- /dev/null +++ b/examples/pizza/pizza.js @@ -0,0 +1,56 @@ +var util = require('util'); +var events = require('events'); + +var PizzaCrust = { + NORMAL: 0, + DEEP_DISH: 1, + THIN: 2, +}; + +var PizzaToppings = { + NONE: 0, + PEPPERONI: 1 << 0, + MUSHROOMS: 1 << 1, + EXTRA_CHEESE: 1 << 2, + BLACK_OLIVES: 1 << 3, + CANADIAN_BACON: 1 << 4, + PINEAPPLE: 1 << 5, + BELL_PEPPERS: 1 << 6, + SAUSAGE: 1 << 7, +}; + +var PizzaBakeResult = { + HALF_BAKED: 0, + BAKED: 1, + CRISPY: 2, + BURNT: 3, + ON_FIRE: 4 +} + +function Pizza() { + events.EventEmitter.call(this); + this.toppings = PizzaToppings.NONE; + this.crust = PizzaCrust.NORMAL; +} + +util.inherits(Pizza, events.EventEmitter); + +Pizza.prototype.bake = function(temperature) { + var time = temperature * 10; + var self = this; + console.log('baking pizza at', temperature, 'degrees for', time, 'milliseconds'); + setTimeout(function() { + var result = + (temperature < 350) ? PizzaBakeResult.HALF_BAKED: + (temperature < 450) ? PizzaBakeResult.BAKED: + (temperature < 500) ? PizzaBakeResult.CRISPY: + (temperature < 600) ? PizzaBakeResult.BURNT: + PizzaBakeResult.ON_FIRE; + self.emit('ready', result); + }, time); +} + +module.exports.Pizza = Pizza; +module.exports.PizzaToppings = PizzaToppings; +module.exports.PizzaCrust = PizzaCrust; +module.exports.PizzaBakeResult = PizzaBakeResult;