First of all I’m not sure if it is a bug I think some programming mistakes from my side
What I’m trying to do is create a small web application for android, for a test to send phone’s accelerometer data (Use case is a bit like your phone app). So it can have training mode and classification mode. In classification mode, after successful classification, it can send some info over MQTT to my other system. That is the intention! But we are not there yet.
I’m right now programming the training phase.
Why I’m re-inventing the wheel? (you may ask…)
Although the web-app hosted by Edge-Impulse is great for quick testing but I need some extra features and a specific user experience, about training, specifically uploading data part.
Also everything will happen from browser / client side and nothing from the web-server side of the app, meaning things like encoding the data with signature, making https calls and web-socket calls need to happen from browser itself.
How far have I succeed so far I so far?
- Hosting the webapp from a https basic python server. DONE.
- Remote management with wss (secure websocket). DONE.
- Sample the accelerometer sensor data. DONE.
- Creating proper encrypted signed data format for upload. DONE (from my inspection), Requesting your insight here.
- Uploading. FAILING! (Trying to use
fetch.POST()
from browser. More details below…)
Data generation and encoding in browser (My method):
PSEUDO_CODE:
when in training mode:
Make the device available on Edge-Impulse Studio by websocket handshake
if sampling request received from Edge-Impulse studio over websockets.
sample data for required length
when sampling finished:
[1] notify the studio through the designated websocket message
[2] in the data structure (with other details) push the returned data array
[3] encode the data structure (SHA-256 and hmacKey using https://github.com/Caligatio/jsSHA on the browser side as crypto lib is for nodejs)
[4] [FAILING] Upload the data using fetch()
( I looked in to the Edge-Impulse's code for the mobile app and it was using XMLHttpRequest(). IS there there a specific reason for not using fetch()?)
So how does that look like in a bit more code? (as there might be some coding issue as well)
I have 3 scripts loaded in browser:
remote_manager.js
: as the name suggests does it’s job
data_forwarder.js
: mainly responsible for holding the functions for encoded data structure creation and uploading.
script.js
: for harnessing phone’s sensor data
For now, to test, in the remote manager
, I modified the code here a bit to test the uploading data functionality with fetch()
. This script is then loaded in my HTML file and hosted by a python https server
document.addEventListener('DOMContentLoaded', function () {
console.log("[data_forwarder_tester].js] :\t", "loaded");
function createDataStruct(_emptySignature, _deviceName, _deviceType, _intervalHZ, _dataArrayHolder, _hmac_key, _api_key){
return new Promise((resolve, reject) => {
let _data = {
protected: {
ver: "v1",
alg: "HS256",
iat: Math.floor(Date.now() / 1000) // epoch time, seconds since 1970 (Imp for Edge-Impulse Studio)
},
signature: _emptySignature,
payload: {
device_name: _deviceName,
device_type: _deviceType,
interval_ms: _intervalHZ,
sensors: [
{ name: "accX", units: "m/s2" },
{ name: "accY", units: "m/s2" },
{ name: "accZ", units: "m/s2" }
],
values: _dataArrayHolder
}
};
resolve([_data, _hmac_key, _api_key]);
});
}
function encodeData([_data, _hmac_key, _api_key]){
return new Promise((resolve, reject) => {
let encoded = JSON.stringify(_data);
// now calculate the HMAC and fill in the signature
// using: https://github.com/Caligatio/jsSHA
// load it in the html file, in the head section: <script src="your path to sha.js"></script>
let hmac = new jsSHA("SHA-256", "TEXT", {
hmacKey: {
value: _hmac_key,
format: "TEXT" },
});
hmac.update(encoded);
let signature = hmac.getHash("HEX");
// update the signature in the message and re-encode
_data.signature = signature;
encoded = JSON.stringify(_data);
resolve([encoded, _api_key]);
});
}
function uploadData([_encoded, _api_key]){
// console.log("Encoded data to be uploaded:\n");
// console.log(_encoded);
const todo = {
title: 'Some really important work to finish'
};
// --- *** test code for fetch()
// fetch('https://jsonplaceholder.typicode.com/todos', {
// method: 'POST',
// body: JSON.stringify(todo),
// headers: {
// 'Content-type': 'application/json; charset=UTF-8'
// }
// })
// .then(response => console.log(response))
// .catch(err => console.error(err));
// TBD: labels: training/test/validation data here are to be set dynamically later
fetch('https://ingestion.edgeimpulse.com/api/training/data', {
method: 'POST',
body: _encoded,
encoding: 'binary',
headers: {
'x-api-key': _api_key,
'x-file-name': 'test.01',
'x-label': 'idle',
'Content-type': 'application/json; charset=UTF-8'
}
})
.then(response => console.log(response))
.catch(err => console.error(err));
}
var API_KEY = <YOUR API KEY>;
var hmac_key = <YOUR HAMC KEY>;
// empty signature (all zeros). HS256 gives 32 byte signature, and we encode in hex, so we need 64 characters here
let emptySignature = Array(64).fill('0').join('');
let deviceName = "Test_device_name";
let deviceType = "Test_device_type";
let intervalHZ = 25;
let dataHolderArray = [[0, 0, 0], [0, 0, 0], [0, 0, 0]];
createDataStruct(emptySignature, deviceName, deviceType, intervalHZ, dataHolderArray, hmac_key)
.then(encodeData)
.then(uploadData)
.catch(console.log);
});
I get this error:
Notes:
- The commented out other test fetch part runs successfully.
- The node js code from the guide runs smoothly and uploads data to the studio successfully
- The python code from the guide runs smoothly and uploads data to the studio successfully
Am I doing any silly mistake or any key information I’m missing ? Any pointers would help