From bef490b4eb3e2151289833d1f47a0dfb0c7ef1b3 Mon Sep 17 00:00:00 2001 From: Jazmin Ferreiro Date: Mon, 4 Oct 2021 21:15:06 -0300 Subject: [PATCH] add edge impulse api --- assets/secrets.json | 3 +- lib/connection/ble/bluetooth_backend.dart | 10 +- .../ble/bluetooth_specification.dart | 3 +- lib/connection/wifi/socket.dart | 3 +- .../measurements_collector.dart | 7 +- lib/datacollection/storage.dart | 11 +- lib/edgeimpulse/api_client.dart | 217 +++++++++++------- lib/pages/ble_data_collection_page.dart | 5 +- lib/pages/ble_devices_connection_page.dart | 22 +- 9 files changed, 164 insertions(+), 117 deletions(-) diff --git a/assets/secrets.json b/assets/secrets.json index 57aa7b8..659ca1f 100644 --- a/assets/secrets.json +++ b/assets/secrets.json @@ -1,3 +1,4 @@ { - "api_key": "ei_7223fabc8d9843d8fff520e22a5b6fc5578444d02ea8b610398089e6cbc175ea" + "RightGloveLSA-ApiKey": "ei_0b21c715bb1b1ecc4a69812a38314280e2f3351eadf562fe", + "LeftGloveLSA-ApiKey": "ei_a6aa9ff39eb518436474eb402235f3babb298fe6ce0e637f" } \ No newline at end of file diff --git a/lib/connection/ble/bluetooth_backend.dart b/lib/connection/ble/bluetooth_backend.dart index 25d3777..37f4041 100644 --- a/lib/connection/ble/bluetooth_backend.dart +++ b/lib/connection/ble/bluetooth_backend.dart @@ -33,8 +33,8 @@ class BluetoothBackend { sendCommandToConnectedDevice(device, BluetoothSpecification.CALIBRATE); } - static void sendStopCommand(List connectedDevices) async { - sendCommandToConnectedDevices( + static Future sendStopCommand(List connectedDevices) async { + await sendCommandToConnectedDevices( connectedDevices, BluetoothSpecification.STOP_ONGOING_TASK); } @@ -58,7 +58,7 @@ class BluetoothBackend { /// /// This method is expected to be used to start and stop the measurement /// readings from the glove as well as the interpretations. - static void sendCommandToConnectedDevices( + static Future sendCommandToConnectedDevices( List connectedDevices, String command) async { List characteristics = await getDevicesControllerCharacteristics(connectedDevices); @@ -153,8 +153,10 @@ class BluetoothBackend { /// Retrieve the deviceName in spanish static String getSpanishGloveName(String deviceName) { switch (deviceName) { - case (BluetoothSpecification.deviceName): + case (BluetoothSpecification.RIGHT_GLOVE_NAME): return RightGlove; + case (BluetoothSpecification.LEFT_GLOVE_NAME): + return LeftGlove; default: return deviceName; } diff --git a/lib/connection/ble/bluetooth_specification.dart b/lib/connection/ble/bluetooth_specification.dart index e04e1ab..720082e 100644 --- a/lib/connection/ble/bluetooth_specification.dart +++ b/lib/connection/ble/bluetooth_specification.dart @@ -3,7 +3,8 @@ /// the service and characteristic UUIDs. class BluetoothSpecification { /// Identifier of the glove. - static const String deviceName = "RightHandSmartGlove"; + static const String RIGHT_GLOVE_NAME = "RightHandSmartGlove"; + static const String LEFT_GLOVE_NAME = "LeftHandSmartGlove"; /// Service for reading measurements or retrieving interpretations from the gloves. static const String LSA_GLOVE_SERVICE_UUID = "7056f14b-02df-4dd8-86fd-0261c7b15c86"; diff --git a/lib/connection/wifi/socket.dart b/lib/connection/wifi/socket.dart index c218048..b6f6628 100644 --- a/lib/connection/wifi/socket.dart +++ b/lib/connection/wifi/socket.dart @@ -6,6 +6,7 @@ import 'dart:developer' as developer; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:lsa_gloves/connection/ble/bluetooth_specification.dart'; import 'package:lsa_gloves/datacollection/storage.dart'; import 'package:lsa_gloves/model/glove_measurement.dart'; import 'package:lsa_gloves/widgets/Dialog.dart'; @@ -218,7 +219,7 @@ class _MovementRecorderWidget extends State { Dialogs.showLoadingDialog(context, _keyLoader, "Guardando..."); var word = fileName; var deviceId = gloveMeasurement.first.deviceId; - var measurementFile = await DeviceMeasurementsFile.create(deviceId, word); + var measurementFile = await DeviceMeasurementsFile.create(BluetoothSpecification.RIGHT_GLOVE_NAME, deviceId, word); for (int i = 0; i < gloveMeasurement.length; i++) { developer.log('saving in file -> ${gloveMeasurement[i].toJson().toString()}'); measurementFile.add(gloveMeasurement[i]); diff --git a/lib/datacollection/measurements_collector.dart b/lib/datacollection/measurements_collector.dart index be21be3..fb0ebf3 100644 --- a/lib/datacollection/measurements_collector.dart +++ b/lib/datacollection/measurements_collector.dart @@ -15,12 +15,13 @@ import 'package:lsa_gloves/model/glove_measurement.dart'; class MeasurementsCollector { final GlobalKey _keyLoader = new GlobalKey(); static const String TAG = "MeasurementsCollector"; + String _deviceName; String _deviceId; BluetoothCharacteristic _characteristic; List _items; StreamSubscription>? _subscription; - MeasurementsCollector(this._deviceId, this._characteristic) + MeasurementsCollector(this._deviceName,this._deviceId, this._characteristic) : _items = []; void readMeasurements(BuildContext context) async { @@ -96,9 +97,9 @@ class MeasurementsCollector { } //open pop up loading Dialogs.showLoadingDialog(context, _keyLoader, "Guardando..."); - var deviceId = gloveMeasurements.first.deviceId; + var measurementFile = - await DeviceMeasurementsFile.create(deviceId, selectedGesture); + await DeviceMeasurementsFile.create(this._deviceName, this._deviceId, selectedGesture); for (int i = 0; i < gloveMeasurements.length; i++) { developer .log('saving in file -> ${gloveMeasurements[i].toJson().toString()}'); diff --git a/lib/datacollection/storage.dart b/lib/datacollection/storage.dart index dcb8a21..8cb8c86 100644 --- a/lib/datacollection/storage.dart +++ b/lib/datacollection/storage.dart @@ -67,10 +67,10 @@ class DeviceMeasurementsFile { this.lastModificationDate, this.fileContent); - static Future create(String deviceId, String word) async { + static Future create(String deviceName, String deviceId, String word) async { var creationDate = DateTime.now(); var values = >[]; - SensorMeasurements json = new SensorMeasurements(deviceId, word, values); + SensorMeasurements json = new SensorMeasurements(deviceName, deviceId, word, values); String datetimeStr = format(creationDate); var filename = "${word}_$datetimeStr"; var file = await new GloveEventsStorage().createFile(filename); @@ -127,7 +127,7 @@ class DeviceMeasurementsFile { Future upload() async { SensorMeasurements measurementsJson = await readJsonContent(); - uploadFile(measurementsJson, lastModificationDate); + EdgeImpulseApiClient.uploadFile(measurementsJson, lastModificationDate); } static String format(DateTime date) { @@ -141,11 +141,12 @@ class DeviceMeasurementsFile { } class SensorMeasurements { + final String deviceName; final String deviceId; final String word; final List> values; - SensorMeasurements(this.deviceId, this.word, this.values); + SensorMeasurements(this.deviceName, this.deviceId, this.word, this.values); bool add(GloveMeasurement gloveMeasurement) { if(gloveMeasurement.deviceId != this.deviceId){ @@ -186,6 +187,7 @@ class SensorMeasurements { }).toList(); } return SensorMeasurements( + json['device_name'] as String, json['device_id'] as String, json['word'] as String, _values, @@ -194,6 +196,7 @@ class SensorMeasurements { Map toJson() { return { + 'device_name': deviceName, 'device_id': deviceId, 'word': word, 'values': values, diff --git a/lib/edgeimpulse/api_client.dart b/lib/edgeimpulse/api_client.dart index 0ebc45e..0ed7688 100644 --- a/lib/edgeimpulse/api_client.dart +++ b/lib/edgeimpulse/api_client.dart @@ -2,76 +2,127 @@ import 'dart:convert'; import 'dart:io'; import 'dart:async' show Future; import 'package:flutter/services.dart' show rootBundle; +import 'package:lsa_gloves/connection/ble/bluetooth_specification.dart'; import 'package:lsa_gloves/datacollection/storage.dart'; import 'dart:developer' as developer; -void uploadFile(SensorMeasurements sensorMeasurements, DateTime datetime) async { - - var fileName = sensorMeasurements.word +'-'+ datetime.toString(); - - var protected = Protected( - ver: "v1", //always v1 - alg: "none", //the algorithm used to sign this file. Either HS256 (HMAC-SHA256) or none (required) - iat: datetime.toUtc().millisecondsSinceEpoch // date when the file was created in seconds since epoch - ); +class EdgeImpulseApiClient { + + static void uploadFile( + SensorMeasurements sensorMeasurements, DateTime datetime) async { + var fileName = sensorMeasurements.word + '-' + datetime.toString(); + + var protected = Protected( + ver: "v1", + //always v1 + alg: "none", + //the algorithm used to sign this file. Either HS256 (HMAC-SHA256) or none (required) + iat: datetime + .toUtc() + .millisecondsSinceEpoch // date when the file was created in seconds since epoch + ); + + var payload = Payload( + deviceName: sensorMeasurements.deviceId, + //globally unique identifier for this device (e.g. MAC address) + deviceType: "ESP32", + // exact model of the device + //the frequency of the data in this file (in milliseconds). E.g. for 100Hz fill in 10 (new data every 10 ms.) + intervalMs: 10, + //mpu6050 Default Internal 8MHz oscillator (register 0x6B = 0) equals to 1.25 milliseconds + sensors: EdgeImpulseApiClient.sensorMeasurementNames, + values: sensorMeasurements.values); + + var edgeImpulseBody = EdgeImpulseBody( + protected: protected, signature: "empty", payload: payload); + developer.log("sending post"); + developer.log("${edgeImpulseBody.toJson()}"); + + Secret secret = + await SecretLoader(secretPath: "assets/secrets.json").load(); + + HttpClient httpClient = new HttpClient(); + HttpClientRequest request = await httpClient.postUrl( + Uri.parse('https://ingestion.edgeimpulse.com/api/training/data')); + request.headers.set('Content-type', 'application/json'); + if(BluetoothSpecification.LEFT_GLOVE_NAME == sensorMeasurements.deviceName){ + request.headers.set('x-api-key', secret.leftGloveApiKey); + }else{ + request.headers.set('x-api-key', secret.rightGloveApiKey); + } + request.headers.set('x-file-name', fileName); + request.headers.set('x-label', sensorMeasurements.word); + request.add(utf8.encode(json.encode(edgeImpulseBody))); + HttpClientResponse response = await request.close(); + String reply = await response.transform(utf8.decoder).join(); + developer.log(reply); + httpClient.close(); + } - var sensor = [ + static const sensorMeasurementNames = [ //thumb - Sensor("thumbAccX","m/s2"),Sensor("thumbAccY","m/s2"), Sensor("thumbAccZ","m/s2"), - Sensor("thumbGyroX","deg"),Sensor("thumbGyroY","deg"), Sensor("thumbGyroZ","deg"), - Sensor("thumbRoll","deg"),Sensor("thumbPitch","deg"), Sensor("thumbYaw","deg"), + SensorParameter("thumbAccX", "m/s2"), + SensorParameter("thumbAccY", "m/s2"), + SensorParameter("thumbAccZ", "m/s2"), + SensorParameter("thumbGyroX", "deg"), + SensorParameter("thumbGyroY", "deg"), + SensorParameter("thumbGyroZ", "deg"), + SensorParameter("thumbRoll", "deg"), + SensorParameter("thumbPitch", "deg"), + SensorParameter("thumbYaw", "deg"), //index - Sensor("indexAccX","m/s2"),Sensor("indexAccY","m/s2"), Sensor("indexAccZ","m/s2"), - Sensor("indexGyroX","deg"),Sensor("indexGyroY","deg"), Sensor("indexGyroZ","deg"), - Sensor("indexRoll","deg"),Sensor("indexPitch","deg"), Sensor("indexYaw","deg"), + SensorParameter("indexAccX", "m/s2"), + SensorParameter("indexAccY", "m/s2"), + SensorParameter("indexAccZ", "m/s2"), + SensorParameter("indexGyroX", "deg"), + SensorParameter("indexGyroY", "deg"), + SensorParameter("indexGyroZ", "deg"), + SensorParameter("indexRoll", "deg"), + SensorParameter("indexPitch", "deg"), + SensorParameter("indexYaw", "deg"), //middle - Sensor("middleAccX","m/s2"),Sensor("middleAccY","m/s2"), Sensor("middleAccZ","m/s2"), - Sensor("middleGyroX","deg"),Sensor("middleGyroY","deg"), Sensor("middleGyroZ","deg"), - Sensor("middleRoll","deg"),Sensor("middlePitch","deg"), Sensor("middleYaw","deg"), + SensorParameter("middleAccX", "m/s2"), + SensorParameter("middleAccY", "m/s2"), + SensorParameter("middleAccZ", "m/s2"), + SensorParameter("middleGyroX", "deg"), + SensorParameter("middleGyroY", "deg"), + SensorParameter("middleGyroZ", "deg"), + SensorParameter("middleRoll", "deg"), + SensorParameter("middlePitch", "deg"), + SensorParameter("middleYaw", "deg"), //ring - Sensor("ringAccX","m/s2"),Sensor("ringAccY","m/s2"), Sensor("ringAccZ","m/s2"), - Sensor("ringGyroX","deg"),Sensor("ringGyroY","deg"), Sensor("ringGyroZ","deg"), - Sensor("ringRoll","deg"),Sensor("ringPitch","deg"), Sensor("ringYaw","deg"), + SensorParameter("ringAccX", "m/s2"), + SensorParameter("ringAccY", "m/s2"), + SensorParameter("ringAccZ", "m/s2"), + SensorParameter("ringGyroX", "deg"), + SensorParameter("ringGyroY", "deg"), + SensorParameter("ringGyroZ", "deg"), + SensorParameter("ringRoll", "deg"), + SensorParameter("ringPitch", "deg"), + SensorParameter("ringYaw", "deg"), //pinky - Sensor("pinkyAccX","m/s2"),Sensor("pinkyAccY","m/s2"), Sensor("pinkyAccZ","m/s2"), - Sensor("pinkyGyroX","deg"),Sensor("pinkyGyroY","deg"), Sensor("pinkyGyroZ","deg"), - Sensor("pinkyRoll","deg"),Sensor("pinkyPitch","deg"), Sensor("pinkyYaw","deg"), - + SensorParameter("pinkyAccX", "m/s2"), + SensorParameter("pinkyAccY", "m/s2"), + SensorParameter("pinkyAccZ", "m/s2"), + SensorParameter("pinkyGyroX", "deg"), + SensorParameter("pinkyGyroY", "deg"), + SensorParameter("pinkyGyroZ", "deg"), + SensorParameter("pinkyRoll", "deg"), + SensorParameter("pinkyPitch", "deg"), + SensorParameter("pinkyYaw", "deg"), ]; - - var payload = Payload( - deviceName: sensorMeasurements.deviceId,//globally unique identifier for this device (e.g. MAC address) - deviceType: "ESP32",// exact model of the device - //the frequency of the data in this file (in milliseconds). E.g. for 100Hz fill in 10 (new data every 10 ms.) - intervalMs: 10, //mpu6050 Default Internal 8MHz oscillator (register 0x6B = 0) equals to 1.25 milliseconds - sensors: sensor, - values: sensorMeasurements.values - ); - - var edgeImpulseBody = EdgeImpulseBody(protected: protected, signature: "empty", payload: payload); - developer.log("sending post"); - developer.log("${edgeImpulseBody.toJson()}"); - - Secret secret = await SecretLoader(secretPath: "assets/secrets.json").load(); - - HttpClient httpClient = new HttpClient(); - HttpClientRequest request = await httpClient.postUrl(Uri.parse('https://ingestion.edgeimpulse.com/api/training/data')); - request.headers.set('Content-type', 'application/json'); - request.headers.set('x-api-key', secret.apiKey); - request.headers.set('x-file-name', fileName); - request.headers.set('x-label', sensorMeasurements.word); - request.add(utf8.encode(json.encode(edgeImpulseBody))); - HttpClientResponse response = await request.close(); - String reply = await response.transform(utf8.decoder).join(); - developer.log(reply ); - httpClient.close(); } class Secret { - final String apiKey; - Secret({this.apiKey = ""}); + final String rightGloveApiKey; + final String leftGloveApiKey; + + Secret({this.rightGloveApiKey = "", this.leftGloveApiKey = ""}); + factory Secret.fromJson(Map jsonMap) { - return new Secret(apiKey: jsonMap["api_key"]); + return new Secret( + rightGloveApiKey: jsonMap["RightGloveLSA-ApiKey"], + leftGloveApiKey: jsonMap["LeftGloveLSA-ApiKey"]); } } @@ -79,16 +130,16 @@ class SecretLoader { final String secretPath; SecretLoader({required this.secretPath}); + Future load() { return rootBundle.loadStructuredData(this.secretPath, - (jsonStr) async { - final secret = Secret.fromJson(json.decode(jsonStr)); - return secret; - }); + (jsonStr) async { + final secret = Secret.fromJson(json.decode(jsonStr)); + return secret; + }); } } - /* * { "protected": { @@ -120,7 +171,11 @@ class EdgeImpulseBody { final Protected protected; final String signature; final Payload payload; - EdgeImpulseBody({required this.protected, required this.signature, required this.payload}); + + EdgeImpulseBody( + {required this.protected, + required this.signature, + required this.payload}); Map toJson() { return { @@ -129,40 +184,33 @@ class EdgeImpulseBody { 'payload': payload.toJson(), }; } - } class Protected { final String ver; final String alg; final int iat; + Protected({required this.ver, required this.alg, required this.iat}); Map toJson() { - return { - 'ver': ver, - 'alg': alg, - 'iat': iat - }; + return {'ver': ver, 'alg': alg, 'iat': iat}; } - } - class Payload { final String deviceName; final String deviceType; final double intervalMs; - final List sensors; + final List sensors; final List> values; - Payload({ - required this.deviceName, - required this.deviceType, - required this.intervalMs, - required this.sensors, - required this.values }); - + Payload( + {required this.deviceName, + required this.deviceType, + required this.intervalMs, + required this.sensors, + required this.values}); Map toJson() { return { @@ -175,15 +223,11 @@ class Payload { } } -class Sensor { - late final String name; - late final String units; - - Sensor(String name, String units){ - this.name = name; - this.units = units; - } +class SensorParameter { + final String name; + final String units; + const SensorParameter(this.name, this.units); Map toJson() { return { @@ -192,4 +236,3 @@ class Sensor { }; } } - diff --git a/lib/pages/ble_data_collection_page.dart b/lib/pages/ble_data_collection_page.dart index 74f4574..d7282d1 100644 --- a/lib/pages/ble_data_collection_page.dart +++ b/lib/pages/ble_data_collection_page.dart @@ -168,8 +168,9 @@ class _BleDataCollectionState extends State this._connectedDevices); // TODO(https://git.io/JEyV4): Process data from more than one device. String deviceId = "${this._connectedDevices.first.id}"; + String deviceName = "${this._connectedDevices.first.name}"; this._measurementsCollector = new MeasurementsCollector( - deviceId, dataCollectionCharacteristics.first); + deviceName, deviceId, dataCollectionCharacteristics.first); this._timerController.start(); this._measurementsCollector!.readMeasurements(context); setState(() { @@ -179,12 +180,12 @@ class _BleDataCollectionState extends State } Future stopRecording() async { + BluetoothBackend.sendStopCommand(this._connectedDevices); developer.log('stopRecording', name: TAG); _timerController.reset(); setState(() { _isRecording = false; }); - BluetoothBackend.sendStopCommand(this._connectedDevices); if (this._measurementsCollector != null) { String gesture = "$selectedCategory-$selectedGesture"; await this._measurementsCollector!.stopReadings(context, gesture); diff --git a/lib/pages/ble_devices_connection_page.dart b/lib/pages/ble_devices_connection_page.dart index 62c6b9d..e2fbb15 100644 --- a/lib/pages/ble_devices_connection_page.dart +++ b/lib/pages/ble_devices_connection_page.dart @@ -67,27 +67,20 @@ class FindDevicesScreen extends StatefulWidget { } class _FindDevicesScreen extends State { - _FindDevicesScreen(this.rightGloveFound, this.leftGloveFound); + _FindDevicesScreen(this.rightGloveConnected, this.leftGloveConnected); - bool rightGloveFound; - bool leftGloveFound; + bool rightGloveConnected; + bool leftGloveConnected; void updateState(BluetoothDevice device) { - if (BluetoothSpecification.deviceName == device.name) { + if (BluetoothSpecification.RIGHT_GLOVE_NAME == device.name || + BluetoothSpecification.LEFT_GLOVE_NAME == device.name) { setState(() { - rightGloveFound = true; + rightGloveConnected = true; }); } } - bool shouldRender(ScanResult scanResult) { - switch (scanResult.device.name) { - case (BluetoothSpecification.deviceName): - return !rightGloveFound; - default: - return false; - } - } @override Widget build(BuildContext context) { @@ -125,7 +118,8 @@ class _FindDevicesScreen extends State { return Column( children: devices .where((d) => - d.name == BluetoothSpecification.deviceName) + d.name == BluetoothSpecification.RIGHT_GLOVE_NAME || + d.name == BluetoothSpecification.LEFT_GLOVE_NAME) .map((device) => ConnectionGloveCard( device: device, updateState: this.updateState,