I am getting error code "NotSupportedError: GATT operation not permitted."
I am trying to connect to ESP32 bluetooth. I tested using nRF Connect and messages are getting to the hardware. Next I tried to use javascript and web-bluetooth. Unfortunately I am getting error on console.error('Argh! ' + error) line of code. The error is happening on characteristic.writeValue()
The code was run on https.
The code below
$(document).ready(function(){
let bluetoothDevice = null;
let requestDeviceParams = {
filters: [
{name: ["konko"]}
],
optionalServices: ['10001001-0000-1000-8000-00805f9b34fb']
}
let name = document.querySelector('#date-input')
$("#synch-date-time").click(() => {
if (document.querySelector('#date-input').value === '') {
console.error('empty date field, please fill the fields')
return
}
asyncResultNotif();
})
async function asyncResultNotif(){
return await navigator.bluetooth.requestDevice(requestDeviceParams)
.then(device => {
bluetoothDevice = device.gatt;
return device.gatt.connect();
})
.then(server => {
if (bluetoothDevice.connected) {
return server.getPrimaryService('10001001-0000-1000-8000-00805f9b34fb');
} else {
console.log('cant connect to prime server')
}
})
.then(service => {
if (bluetoothDevice.connected) {
return service.getCharacteristic('10001111-0000-1000-8000-00805f9b34fb'); // write one value
} else {
console.log('cant connect to characteristic')
}
})
.then(characteristic => {
if (bluetoothDevice.connected) {
// const resetEnergyExpended = Uint8Array.of(1);
// return characteristic.writeValue(resetEnergyExpended);
let data = '{"ssid": "' +name.value
data +='"}'
console.log(data)
let encoder = new TextEncoder('utf-8');
let val = encoder.encode(data);
// return characteristic.writeValue(val.buffer)
return characteristic.writeValue(new Uint8Array([1]))
} else {
console.log('cant send message over BLE')
}
}). then(() => {
if (bluetoothDevice.connected) {
bluetoothDevice.disconnect();
} else {
console.log('> Bluetooth Device is already disconnected');
}
}).catch(error => {
console.error('Argh! ' + error)
});
}
It could be that the Bluetooth GATT characteristic you're writing to is not writable. Can you share your ESP32 server code as well?
It should look something like below. Note that I use BLECharacteristic::PROPERTY_WRITE.
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string value = pCharacteristic->getValue();
BLECharacteristic *pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_WRITE
);
pCharacteristic->setCallbacks(new MyCallbacks());
pCharacteristic->setValue("Hello World");
I found the answer. The problem was with UUID of characteristic. On ESP32 it was "10001111-0000-1000-8000-00805f9b34fb" and when I changed that to "10000000-0000-1000-8000-00805f9b34fb" it starts working. I somewhere read that it may be something with 128bits in UUID. We can mark with as answered.
Related
I'm trying to send a message through an IPFS network but I keep getting the error TypeError: Cannot read properties of undefined (reading 'publish').
The error occurs at line node.pubsub.publish(topic, msg, (err) => {}).
ipfs.js is the app's main engine and contains the methods needed to interact with the IPFS network.
The 'sendNewMsg' method is used to send messages to a topic where other peers can subscribe and read the messages.
index.js calls and executes the method.
Could you please help me spot and fix the problem?
Thanks in advance!
ipfs.js:
const IPFS = require('ipfs');
const BufferPackage = require('buffer');
const Buffer = BufferPackage.Buffer;
class IPFSEngine {
constructor(){
let node = IPFS.create({
EXPERIMENTAL: { pubsub: true },
repo: (() => `repo-${Math.random()}`)(),
config: {
Addresses: {
Swarm: [
'/dns4/ws-star.discovery.libp2p.io/tcp/443/wss/p2p-websocket-star'
]
}
}
});
this.node = node;
}
....
....
....
sendNewMsg(topic, newMsg) {
let {node} = this;
console.log('Message sent: ', newMsg)
const msg = Buffer.from(newMsg)
node.pubsub.publish(topic, msg, (err) => {
if (err) {
return console.error(`Failed to publish to ${topic}`, err)
}
// msg was broadcasted
console.log(`Published to ${topic}`)
})
}
}
// Export IPFSEngine
module.exports = {IPFSEngine};
index.js:
const {IPFSEngine} = require('./ipfs');
const ipfs = new IPFSEngine();
ipfs.sendNewMsg(`topic2`,`Messages 2 ..!`)
The error states that node.pubsub is undefined while you are trying to access the publish property on the object.
Quickly reading through an example from the IPFS documentation, it appears that IPFS.create is an asynchronous API, which result you are not awaiting. It could explain why you get an undefined pubsub property on your node.
Using an async function, you could write something like:
async function sendNewMsg(topic, newMsg) {
let { node } = this;
console.log('Message sent: ', newMsg);
const msg = Buffer.from(newMsg);
(await node).pubsub.publish(topic, msg, (err) => {
if (err) {
return console.error(`Failed to publish to ${topic}`, err);
}
// msg was broadcasted
console.log(`Published to ${topic}`);
});
}
Or without the async/await syntax:
function sendNewMsg2(topic, newMsg) {
let { node } = this;
console.log('Message sent: ', newMsg);
const msg = Buffer.from(newMsg);
node.then((readyNode) => {
readyNode.pubsub.publish(topic, msg, (err) => {
if (err) {
return console.error(`Failed to publish to ${topic}`, err);
}
// msg was broadcasted
console.log(`Published to ${topic}`);
});
})
}
I have some code like so:
export async function handleRefresh() {
if (!existsSync('postr.toml')) fail('not a postr directory');
const posts = expandGlob('posts/*');
for await (const post of posts) {
if (!post.isDirectory) {
console.warn('warning: non-folder found in posts directory');
continue;
}
let {parsedFrontMatter, contents} = extractFrontMatter(await read(post.path + '/post.md'));
const adapters = parsedFrontMatter.adapters ?? [];
if (!parsedFrontMatter) {
fail('no frontmatter for ' + post.path);
continue;
}
if (!Array.isArray(adapters)) {
fail('adapters is not an array');
continue;
}
if (isValidFrontMatter(parsedFrontMatter)) {
fail('frontmatter is not valid');
continue;
}
adapters.forEach(async (adapter: string) => {
const adapterPlugins = parseToml(await read('postr.toml')).adapterPlugins ?? {};
if (!isObject(adapterPlugins)) {
fail('adapterPlugins in the configuration is not an object');
return;
}
const adapterPath = adapterPlugins[adapter];
if (!adapterPath) {
console.warn('warn: an adapter was set' +
'but the corresponding plugin was not configured in `postr.toml`. Skipping');
return;
}
if (!('path' in <any>adapterPath)) {
fail(`adapter ${adapter} does not have a path`);
return;
}
import((<any>adapterPath).path)
.then(async module => {
const action = getActionForPost(parsedFrontMatter);
if (module[action]) {
await module[action](contents, parsedFrontMatter, (<any>adapterPath).config, {
updateFrontMatter(newData: {[x: string]: any}) {
parsedFrontMatter = Object.assign(parsedFrontMatter, newData);
},
mapID(remote: string | number) {
addMapping(parsedFrontMatter.id as string, remote.toString(), adapter);
}
})
} else {
console.warn(`Adapter ${adapter} does not support action \`${action}\``);
return;
}
writeFinalContents(parsedFrontMatter, contents, post.path)
})
.catch(error => fail(`could not run adapter because of ${error.name}: ${error.message}`));
});
}
}
Huge function.
There are a lot of these necessary if checks. 3/4 of the function is if checks, you could say. I want some advice on how I could refactor these statements.
As you can see the checks are not always the same, there are some different checks going on there.
EDIT: I've added real code.
Please help me figure out the difference in return behaviour between the onCall and onRequest google functions below.
onCall, the problem: returns null on all returns, except at the first return (as commented below). The db entries and rest of the code works fine. Just no returns problem.
onRequest, returns perfectly fine on every return. The db entries and rest of the code also works fine.
Both as you will see compare the same, but I just can't seem to get it to work at all. Any advice on how to get my returns to work for the onCall (and structure it better) would be much appreciated.
I am keen on sticking with async await (as opposed to a promise). Using Node.js 12. I am calling the onCall in Flutter, don't know if that is relevant for the question.
The onCall:
exports.applyUserDiscount = functions.https.onCall(async (data, context) => {
if (!context.auth) return {message: "Authentication Required!", code: 401};
const uid = context.auth.uid;
const discountCode = data["discountCode"];
const cartTotal = data["cartTotal"];
try {
return await db.collection("discountCodes").where("itemID", "==", discountCode).limit(1).get()
.then(async (snapshot) => {
if (snapshot.empty) {
return "doesNotExist"; // The only return that works.
} else { // Everything else from here onwards returns null.
snapshot.forEach(async (doc) => {
if (doc.data().redeemed == true) {
return "codeUsed";
} else {
const newCartTotal = cartTotal - doc.data().discountAmount;
if (newCartTotal < 0) {
return "lessThanTotal";
} else {
doc.ref.update({
redeemed: true,
uid: uid,
redeemDate: fireDateTimeNow,
});
await db.collection("userdata").doc(uid).set({
cartDiscount: admin.firestore.FieldValue.increment(-doc.data().discountAmount),
}, {merge: true});
return doc.data().discountAmount.toString();
}
}
});
}
});
} catch (error) {
console.log("Error:" + error);
return "error";
}
});
The onRequest:
exports.applyUserDiscount = functions.https.onRequest(async (req, res) => {
const uid = req.body.uid;
const discountCode = req.body.discountCode;
const cartTotal = req.body.cartTotal;
try {
return await db.collection("discountCodes").where("itemID", "==", discountCode).limit(1).get()
.then(async (snapshot) => {
if (snapshot.isempty) {
res.send("doesNotExist");
} else {
snapshot.forEach(async (doc) => {
if (doc.data().redeemed == true) {
res.send("codeUsed");
} else {
const newCartTotal = cartTotal - doc.data().discountAmount;
if (newCartTotal < 0) {
res.send("lessThanTotal");
} else {
doc.ref.update({
redeemed: true,
uid: uid,
redeemDate: fireDateTimeNow,
});
await db.collection("userdata").doc(uid).set({
cartDiscount: admin.firestore.FieldValue.increment(-doc.data().discountAmount),
}, {merge: true});
res.send(doc.data().discountAmount.toString());
}
}
});
}
});
} catch (error) {
console.log(error);
res.send("error");
}
});
There are several points to be noted when looking at your code(s):
You should not use async/await within a forEach loop. The problem is that the callback passed to forEach() is not being awaited, see more explanations here or here. HOWEVER, in your case you don't need to loop over the QuerySnapshot since it contains only one doc. You can use the docs property which return an array of all the documents in the QuerySnapshot and take the first (and unique) element.
You mix-up then() with async/await, which is not recommended.
I would advise to throw exceptions for the "error" cases, like doesNotExist, codeUsed or lessThanTotal but it's up to you to choose. The fact that, for example, the lessThanTotal case is an error or a standard business case is debatable... So if you prefer to send a "text" response, I would advise to encapsulate this response in a Object with one property: in your front-end the response will always have the same format.
So, the following should do the trick. Note that I send back on object with a response element, including for the cases that could be considered as errors. As said above you could throw an exception in these cases.
exports.applyUserDiscount = functions.https.onCall(async (data, context) => {
if (!context.auth) ... //See https://firebase.google.com/docs/functions/callable#handle_errors
const uid = context.auth.uid;
const discountCode = data["discountCode"];
const cartTotal = data["cartTotal"];
try {
const snapshot = await db.collection("discountCodes").where("itemID", "==", discountCode).limit(1).get();
if (snapshot.empty) {
//See https://firebase.google.com/docs/functions/callable#handle_errors
} else {
const uniqueDoc = snapshot.docs[0];
if (uniqueDoc.data().redeemed == true) {
return { response: "codeUsed" };
} else {
const newCartTotal = cartTotal - uniqueDoc.data().discountAmount;
if (newCartTotal < 0) {
return { response: "lessThanTotal" };
} else {
await uniqueDoc.ref.update({ // See await here!!
redeemed: true,
uid: uid,
redeemDate: fireDateTimeNow,
});
await db.collection("userdata").doc(uid).set({
cartDiscount: admin.firestore.FieldValue.increment(-uniqueDoc.data().discountAmount),
}, { merge: true });
return {
response: uniqueDoc.data().discountAmount.toString()
}
}
}
}
} catch (error) {
console.log("Error:" + error);
return "error";
}
});
I have an Azure Function that inserts from JSON-LD into my GraphDB - however what I'm finding is my GraphDB keeps crashing, because the function is sending too many insert requests.
I've set "maxConcurrentRequests": 1 and "maxOutstandingRequests": 1 however it still doesn't seem to wait and process one at a time.
Could someone please explain why this so.
export async function onTrigger(context: Context, documents: Resource[] | null): Promise<void> {
if (documents == null) { return }
documents.forEach(async function (value: any) {
if ("metadata" in value) { } else { return; }
let song: MyDocument = value.metadata.songs.song;
// Create the JSON-LD object using the song object from above
let jsonld = ...
let nQuads = await jsonldParser.toRDF(jsonld, {format: 'application/n-quads'});
let insertQuery = "INSERT DATA {" + nQuads + "}";
try {
let res = await axios.post('http://localhost:7200/mygraphdb', "update="+encodeURIComponent(insertQuery))
if (res.status === 204) {
console.log(`All Done!`);
}
} catch (error) {
console.log(`Error! ${song.id}`);
}
});
}
I'm developing a software who will use Web Bluetooth API to connect to a BT to Serial Adapter. It seems to have support for Write and Notify. But I can't get nofity to work.
The event is never fired. Right now I'm testing in Canary on Mac.
Thanks
Anders
My code for search/pair and add events:
var readCharacteristic;
var writeCharacteristic;
var serialBLEService = 'ba920001-d666-4d59-b141-05960b3a4ff7';
var txChar = 'ba920002-d666-4d59-b141-05960b3a4ff7';
var rxChar = 'ba920003-d666-4d59-b141-05960b3a4ff7';
$scope.writeToSerial = function (valueToWrite) {
var tmpValue = $('input').val();
valueToWrite = utf8AbFromStr(tmpValue);
writeCharacteristic.writeValue(valueToWrite)
.then( a => {
alert("Written: " + valueToWrite);
})
.catch(function (error) {
// And of course: error handling!
console.error('Something went wrong!', error);
});
}
function handleCharacteristicValueChanged(event) {
var value = event.target.value;
console.log('Received ' + value);
}
$scope.searchForBTDevices = function() {
navigator.bluetooth.requestDevice({
filters: [{ services: [serialBLEService] }],
optionalServices: [
serialBLEService, rxChar, txChar, configChar
]
})
.then(device => {
return device.gatt.connect();
})
.then(server => {
return server.getPrimaryService(serialBLEService);
})
.then(service => {
return service.getCharacteristics();
})
.then(characteristics => {
$scope.btResult += '>> Found Characteristics!\n';
$scope.$apply();
var queue = Promise.resolve();
characteristics.forEach(characteristic => {
switch (characteristic.uuid) {
case rxChar:
readCharacteristic = characteristic;
readCharacteristic.addEventListener('characteristicvaluechanged',
handleCharacteristicValueChanged);
break;
case txChar:
writeCharacteristic = characteristic;
break;
}
});
})
.catch(error => {
$scope.btResult = '>> Error: ' + error;
$scope.$digest();
});
};
According to https://developers.google.com/web/updates/2015/07/interact-with-ble-devices-on-the-web#receive_gatt_notifications, it looks like you need to call characteristic.startNotifications() to let the browser know you want to receive GATT notifications:
navigator.bluetooth.requestDevice({ filters: [{ services: ['heart_rate'] }] })
.then(device => device.gatt.connect())
.then(server => server.getPrimaryService('heart_rate'))
.then(service => service.getCharacteristic('heart_rate_measurement'))
.then(characteristic => characteristic.startNotifications())
.then(characteristic => {
characteristic.addEventListener('characteristicvaluechanged',
handleCharacteristicValueChanged);
console.log('Notifications have been started.');
})
.catch(error => { console.log(error); });
function handleCharacteristicValueChanged(event) {
var value = event.target.value;
console.log('Received ' + value);
// TODO: Parse Heart Rate Measurement value.
// See https://github.com/WebBluetoothCG/demos/blob/gh-pages/heart-rate-sensor/heartRateSensor.js
}
Yes, you're absolutely right. There was 2 errors first in baud rate against my serial to BT adapter which made that I didn't send what I thought I was. And the other was that startnotifications was not called.
Thanks
Anders