From 15426c075d01d7e870cea0adb8c3541c1812063a Mon Sep 17 00:00:00 2001 From: Rob Moran Date: Tue, 18 Jun 2019 23:46:09 +0100 Subject: [PATCH] Add IO Pin and DFU services --- README.md | 14 +-- src/index.ts | 6 + src/services/accelerometer.ts | 4 +- src/services/button.ts | 8 +- src/services/dfu-control.ts | 14 +++ src/services/event.ts | 8 +- src/services/io-pin.ts | 199 +++++++++++++++++++++++++++++++++- src/services/led.ts | 4 +- src/services/magnetometer.ts | 4 +- src/services/temperature.ts | 8 +- 10 files changed, 241 insertions(+), 28 deletions(-) diff --git a/README.md b/README.md index ca048ef..5b45f74 100644 --- a/README.md +++ b/README.md @@ -68,12 +68,12 @@ Refer to the [micro:bit Web Bluetooth API Documentation](https://thegecko.github - [x] Client Event ### IO Pin Service -- [ ] Pin Data -- [ ] Pin Data Changed Event -- [ ] Pin AD Configuration -- [ ] Pin IO Configuration -- [ ] PWM Control +- [x] Pin Data +- [x] Pin Data Changed Event +- [x] Pin AD Configuration +- [x] Pin IO Configuration +- [x] PWM Control ### DFU Control Service -- [ ] DFU Control -- [ ] Partial Flashing +- [x] Request DFU +- [x] Request Flash Code diff --git a/src/index.ts b/src/index.ts index d668b9b..7b17b28 100644 --- a/src/index.ts +++ b/src/index.ts @@ -43,6 +43,8 @@ export interface Services { magnetometerService?: MagnetometerService; uartService?: UartService; eventService?: EventService; + dfuControlService?: DfuControlService; + ioPinService?: IoPinService; } /** @@ -116,6 +118,8 @@ export const getServices = async (device: BluetoothDevice): Promise => const magnetometerService = await builder.createService(MagnetometerService); const uartService = await builder.createService(UartService); const eventService = await builder.createService(EventService); + const dfuControlService = await builder.createService(DfuControlService); + const ioPinService = await builder.createService(IoPinService); return { deviceInformationService, @@ -126,5 +130,7 @@ export const getServices = async (device: BluetoothDevice): Promise => magnetometerService, uartService, eventService, + dfuControlService, + ioPinService }; }; diff --git a/src/services/accelerometer.ts b/src/services/accelerometer.ts index 545ad0d..405e173 100644 --- a/src/services/accelerometer.ts +++ b/src/services/accelerometer.ts @@ -120,8 +120,8 @@ export class AccelerometerService extends (EventDispatcher as new() => TypedDisp * Get accelerometer sample period */ public async getAccelerometerPeriod(): Promise { - const value = await this.helper.getCharacteristicValue(AccelerometerCharacteristic.accelerometerPeriod); - return value.getUint16(0, true) as AccelerometerPeriod; + const view = await this.helper.getCharacteristicValue(AccelerometerCharacteristic.accelerometerPeriod); + return view.getUint16(0, true) as AccelerometerPeriod; } /** diff --git a/src/services/button.ts b/src/services/button.ts index cf63679..16919f0 100644 --- a/src/services/button.ts +++ b/src/services/button.ts @@ -112,16 +112,16 @@ export class ButtonService extends (EventDispatcher as new() => TypedDispatcher< * Read state of button A */ public async readButtonAState(): Promise { - const value = await this.helper.getCharacteristicValue(ButtonCharacteristic.buttonAState); - return value.getUint8(0); + const view = await this.helper.getCharacteristicValue(ButtonCharacteristic.buttonAState); + return view.getUint8(0); } /** * Read state of button B */ public async readButtonBState(): Promise { - const value = await this.helper.getCharacteristicValue(ButtonCharacteristic.buttonBState); - return value.getUint8(0); + const view = await this.helper.getCharacteristicValue(ButtonCharacteristic.buttonBState); + return view.getUint8(0); } private buttonAStateChangedHandler(event: Event) { diff --git a/src/services/dfu-control.ts b/src/services/dfu-control.ts index b7de144..f82aed7 100644 --- a/src/services/dfu-control.ts +++ b/src/services/dfu-control.ts @@ -60,4 +60,18 @@ export class DfuControlService { constructor(service: BluetoothRemoteGATTService) { this.helper = new ServiceHelper(service); } + + /** + * Request device switches to DFU mode + */ + public requestDfu(): Promise { + return this.helper.setCharacteristicValue(DfuCharacteristic.dfuControl, new Uint8Array([1])); + } + + /** + * Request flash code + */ + public requestFlashCode(): Promise { + return this.helper.setCharacteristicValue(DfuCharacteristic.dfuControl, new Uint8Array([2])); + } } diff --git a/src/services/event.ts b/src/services/event.ts index e495882..e3667aa 100644 --- a/src/services/event.ts +++ b/src/services/event.ts @@ -110,8 +110,8 @@ export class EventService extends (EventDispatcher as new() => TypedDispatcher { - const value = await this.helper.getCharacteristicValue(EventCharacteristic.microBitRequirements); - return this.viewToMicrobitEvent(value); + const view = await this.helper.getCharacteristicValue(EventCharacteristic.microBitRequirements); + return this.viewToMicrobitEvent(view); } /** @@ -130,8 +130,8 @@ export class EventService extends (EventDispatcher as new() => TypedDispatcher { - const value = await this.helper.getCharacteristicValue(EventCharacteristic.microBitEvent); - return this.viewToMicrobitEvent(value); + const view = await this.helper.getCharacteristicValue(EventCharacteristic.microBitEvent); + return this.viewToMicrobitEvent(view); } /** diff --git a/src/services/io-pin.ts b/src/services/io-pin.ts index df5b7d5..b86cc81 100644 --- a/src/services/io-pin.ts +++ b/src/services/io-pin.ts @@ -24,6 +24,7 @@ */ import { ServiceHelper } from "../service-helper"; +import { EventDispatcher, TypedDispatcher } from "../event-dispatcher"; /** * @hidden @@ -35,10 +36,76 @@ export enum IoPinCharacteristic { pwmControl = "e95dd822-251d-470a-a062-fa1922dfa9a8" } +/** + * Pin data + */ +export interface PinData { + /** + * Pin number + */ + pin: number; + /** + * Pin value + */ + value: number; +} + +/** + * PWM control data + */ +export interface PwmControlData { + /** + * Pin number + */ + pin: number; + /** + * Pin value + */ + value: number; + /** + * Period (in milliseconds) + */ + period: number; +} + +/** + * Analogue/Digital Enum + */ +export enum AD { + Digital = 0, + Analogue = 1 +} + +/** + * Input/Output Enum + */ +export enum IO { + Output = 0, + Input = 1 +} + +/** + * Events raised by the magnetometer service + */ +export interface IoPinEvents { + /** + * @hidden + */ + newListener: keyof IoPinEvents; + /** + * @hidden + */ + removeListener: keyof IoPinEvents; + /** + * Pin data changed event + */ + pindatachanged: PinData[]; +} + /** * @hidden */ -export class IoPinService { +export class IoPinService extends (EventDispatcher as new() => TypedDispatcher) { /** * @hidden @@ -49,7 +116,9 @@ export class IoPinService { * @hidden */ public static async create(service: BluetoothRemoteGATTService): Promise { - return new IoPinService(service); + const bluetoothService = new IoPinService(service); + await bluetoothService.init(); + return bluetoothService; } /** @@ -61,6 +130,130 @@ export class IoPinService { * @hidden */ constructor(service: BluetoothRemoteGATTService) { - this.helper = new ServiceHelper(service); + super(); + this.helper = new ServiceHelper(service, this); + } + + private async init() { + await this.helper.handleListener("pindatachanged", IoPinCharacteristic.pinData, this.pinDataChangedHandler.bind(this)); + } + + /** + * Read pin data + */ + public async readPinData(): Promise { + const view = await this.helper.getCharacteristicValue(IoPinCharacteristic.pinData); + return this.dataViewToPinData(view); + } + + /** + * Write pin data + * @param data The pin data to write + */ + public async writePinData(data: PinData[]): Promise { + const view = this.pinDataToDataView(data); + return this.helper.setCharacteristicValue(IoPinCharacteristic.pinData, view); + } + + /** + * Get pin analogue/digital configuration + */ + public async getAdConfiguration(): Promise { + const view = await this.helper.getCharacteristicValue(IoPinCharacteristic.pinAdConfiguration); + return this.dataViewToConfig(view); + } + + /** + * Set pin analogue/digital configuration + * @param config The analogue/digital configuration to set + */ + public async setAdConfiguration(config: AD[]): Promise { + const view = this.configToDataView(config); + return this.helper.setCharacteristicValue(IoPinCharacteristic.pinAdConfiguration, view); + } + + /** + * Get pin input/output configuration + */ + public async getIoConfiguration(): Promise { + const view = await this.helper.getCharacteristicValue(IoPinCharacteristic.pinIoConfiguration); + return this.dataViewToConfig(view); + } + + /** + * Set pin input/output configuration + * @param config The input/output configuration to set + */ + public async setIoConfiguration(config: IO[]): Promise { + const view = this.configToDataView(config); + return this.helper.setCharacteristicValue(IoPinCharacteristic.pinIoConfiguration, view); + } + + /** + * Set pin PWM control + * @param data The PWM control data to set + */ + public async setPwmControl(data: PwmControlData): Promise { + const view = this.pwmControlDataToDataView(data); + return this.helper.setCharacteristicValue(IoPinCharacteristic.pwmControl, view); + } + + private pinDataChangedHandler(event: Event) { + const view = (event.target as BluetoothRemoteGATTCharacteristic).value!; + const value = this.dataViewToPinData(view); + this.dispatchEvent("pindatachanged", value); + } + + private dataViewToPinData(view: DataView): PinData[] { + const data = []; + for (let i = 0; i < view.byteLength; i += 2) { + data.push({ + pin: view.getUint8(i), + value: view.getUint8(i + 1) + }); + } + return data; + } + + private pinDataToDataView(data: PinData[]): DataView { + const view = new DataView(new ArrayBuffer(data.length * 2)); + data.forEach((pinData, index) => { + view.setUint8(index * 2, pinData.pin); + view.setUint8(index * 2 + 1, pinData.value); + }); + return view; + } + + private dataViewToConfig(view: DataView): number[] { + const result: number[] = []; + const value = (view.getUint16(0) << 8) + view.getUint8(2); + + for (let i = 0; i < 24; i++) { + result.push(value >> i); + } + + return result; + } + + private configToDataView(config: number[]): DataView { + const view = new DataView(new ArrayBuffer(3)); + let value = 0; + + // tslint:disable-next-line:prefer-for-of + for (let i = 0; i < config.length; i++) { + value &= 1 << config[i]; + } + + view.setUint16(0, value >> 8); + view.setUint8(2, value & 0xff); + return view; + } + + private pwmControlDataToDataView(data: PwmControlData): DataView { + const view = new DataView(new ArrayBuffer(7)); + view.setUint8(0, data.pin); + view.setUint16(1, data.value); + view.setUint32(3, data.period); + return view; } } diff --git a/src/services/led.ts b/src/services/led.ts index 16a09db..24ff6b2 100644 --- a/src/services/led.ts +++ b/src/services/led.ts @@ -101,8 +101,8 @@ export class LedService { * Get scrolling delay */ public async getScrollingDelay(): Promise { - const value = await this.helper.getCharacteristicValue(LedCharacteristic.scrollingDelay); - return value.getUint16(0, true); + const view = await this.helper.getCharacteristicValue(LedCharacteristic.scrollingDelay); + return view.getUint16(0, true); } /** diff --git a/src/services/magnetometer.ts b/src/services/magnetometer.ts index 55592c4..bc3f142 100644 --- a/src/services/magnetometer.ts +++ b/src/services/magnetometer.ts @@ -172,8 +172,8 @@ export class MagnetometerService extends (EventDispatcher as new() => TypedDispa * Get magnetometer sample period */ public async getMagnetometerPeriod(): Promise { - const value = await this.helper.getCharacteristicValue(MagnetometerCharacteristic.magnetometerPeriod); - return value.getUint16(0, true) as MagnetometerPeriod; + const view = await this.helper.getCharacteristicValue(MagnetometerCharacteristic.magnetometerPeriod); + return view.getUint16(0, true) as MagnetometerPeriod; } /** diff --git a/src/services/temperature.ts b/src/services/temperature.ts index bf5741c..7670489 100644 --- a/src/services/temperature.ts +++ b/src/services/temperature.ts @@ -89,16 +89,16 @@ export class TemperatureService extends (EventDispatcher as new() => TypedDispat * Read temperature */ public async readTemperature(): Promise { - const value = await this.helper.getCharacteristicValue(TemperatureCharacteristic.temperature); - return value.getInt8(0); + const view = await this.helper.getCharacteristicValue(TemperatureCharacteristic.temperature); + return view.getInt8(0); } /** * Get temperature sample period */ public async getTemperaturePeriod(): Promise { - const value = await this.helper.getCharacteristicValue(TemperatureCharacteristic.temperaturePeriod); - return value.getUint16(0, true); + const view = await this.helper.getCharacteristicValue(TemperatureCharacteristic.temperaturePeriod); + return view.getUint16(0, true); } /**