I am learning use web bluetooth api to write web app and use chrome/chromium run it .
But the notification only response few times, I don't know why and how to debug it(to see what happened).
The bluetooth peripheral is an oximeter, use BLE to send real-time spo2, heart rate, etc. And my browser use Chromium 60.0.3112.78 built on Debian 9.1, running on Debian 9.1 (64 bit) .
Below is my javascript:
var serviceUuid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
characteristicUuid = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" ;
// Sorry, I hide the UUID.
document.querySelector('#button').addEventListener('click', function(event) {
onStartButtonClick();
}});
async function onStartButtonClick(){
let options = {};
options.acceptAllDevices = true;
options.optionalServices = [serviceUuid];
try{
const device = await navigator.bluetooth.requestDevice(options);
device.addEventListener('gattserverdisconnected', onDisconnected);
console.log('Got device:', device.name);
console.log('id:', device.id);
console.log('Connecting to GATT Server...');
const server = await device.gatt.connect();
console.log('Getting Service...');
const service = await server.getPrimaryService(serviceUuid);
console.log('Getting Characteristic...');
myCharacteristic = await service.getCharacteristic(characteristicUuid);
myCharacteristic.addEventListener('characteristicvaluechanged',
handleNotifications);
await myCharacteristic.startNotifications();
console.log('> Notifications started');
} catch(error) {
console.log('Argh! ' + error);
}
}
async function disconnect(){
await myCharacteristic.stopNotifications();
onDisconnected();
}
function onDisconnected(event) {
// Object event.target is Bluetooth Device getting disconnected.
console.log('> Bluetooth Device disconnected');
}
var tmp_count=0;
async function handleNotifications(event) {
// I will read data by Uint8Array.
// var databuf = new Uint8Array(event.target.value.buffer);
tmp_count++;
console.log(tmp_count);
}
Console of chromium display:
03:41:49.893 (index):192 Connecting to GATT Server...
03:41:50.378 (index):195 Getting Service...
03:41:51.237 (index):198 Getting Characteristic...
03:41:51.359 (index):204 > Notifications started
03:41:51.781 (index):228 1
03:41:51.782 (index):228 2
03:42:22.573 (index):217 > Bluetooth Device disconnected
It's no response after 03:41:51.782 (index):228 2 , so I turn turn off oximeter.
What is the problem ? And what can I do ?
Thanks.
First, note that Chrome supports Android, Chrome OS, macOS at this time.
Try using some of the tips on https://www.chromium.org/developers/how-tos/file-web-bluetooth-bugs such as chrome://bluetooth-internals,
nRF Connect for Android or nRF Connect for iOS
If you can receive continuous notifications in those apps, but not in Chrome, there's a bug. Please file details on how to reproduce it, logs you've collected, there: https://crbug.com/new
Related
I am a total newb, I just started looking into this today. I have a a chromebook running chrome Version 96.0.4664.111 (Official Build) (64-bit), and a raspberry pi pico which I have loaded python bootloader on (drag & drop). I am trying to access the pico from my browser serially to load my source code since I cannot install thawny on my chromebook. I have pieced together this javascript function that uses web serial api to connect to the pico.
const filters = [
{ usbVendorId: 0x2E8A, usbProductId: 0x0003 },
{ usbVendorId: 0x2E8A, usbProductId: 0x0005 }
];
// Prompt user to select an Arduino Uno device.
const port = await navigator.serial.requestPort({ filters });
const { usbProductId, usbVendorId } = port.getInfo();
// Wait for the serial port to open.
await port.open({ baudRate: 9600 });
const textDecoder = new TextDecoderStream();
const readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
const reader = textDecoder.readable.getReader();
// Listen to data coming from the serial device.
while (true) {
const { value, done } = await reader.read();
if (done) {
// Allow the serial port to be closed later.
reader.releaseLock();
break;
}
// value is a Uint8Array.
console.log(value);
}
// Listen to data coming from the serial device.
while (true) {
const { value, done } = await reader.read();
if (done) {
// Allow the serial port to be closed later.
reader.releaseLock();
break;
}
// value is a string.
console.log(value);
}
const textEncoder = new TextEncoderStream();
const writableStreamClosed = textEncoder.readable.pipeTo(port.writable);
const writer = textEncoder.writable.getWriter();
await writer.write("hi");
// Allow the serial port to be closed later.
writer.releaseLock();
I cannot find a way to make this program upload a file, I would really appreciate it if someone could help me out.
Please excuse me if I'm being unclear or extremley stupid, I am completley new to this and I am really tired from new-years last night. Thanks!
I have found a suitable solution to my question, tinkerdoodle.cc.
I am trying to build a service worker that retrieves a video from cache if available and fetches from online if it is not available. Here is my code for that:
self.addEventListener("fetch", function (event) {
if (event.request.headers.get("range")) {
caches.match(event.request.url).then(function (res) {
if (!res) {
log.debug(
`Range request NOT found in cache for ${event.request.url}, activating fetch...`
);
return fetch(event.request);
}
returnRangeRequest(event);
});
} else {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
}
});
function returnRangeRequest(event) {
var rangeHeader = event.request.headers.get("range");
var rangeMatch = rangeHeader.match(/^bytes\=(\d+)\-(\d+)?/);
var pos = Number(rangeMatch[1]);
var pos2 = rangeMatch[2];
if (pos2) {
pos2 = Number(pos2);
}
event.respondWith(
caches
.match(event.request.url)
.then(function (res) {
return res.arrayBuffer();
})
.then(function (ab) {
let responseHeaders = {
status: 206,
statusText: "Partial Content",
headers: [
["Content-Type", "video/mp4"],
[
"Content-Range",
"bytes " +
pos +
"-" +
(pos2 || ab.byteLength - 1) +
"/" +
ab.byteLength,
],
],
};
var abSliced = {};
if (pos2 > 0) {
abSliced = ab.slice(pos, pos2 + 1);
} else {
abSliced = ab.slice(pos);
}
log.debug(
`Returning range request response`
);
return new Response(abSliced, responseHeaders);
})
.catch(function (err) {
log.error(err);
})
);
}
When I am online and I try to play the video, it works fine and it prints the debug line Range request NOT found in cache for https://example.com/vid.mp4, activating fetch...
When I have cached the video url using cache.add("https://example.com/vid.mp4");, and I try to play it, the video plays fine.
The problem arises when I turn off the Wifi on the iPad. When I try to play the video after turning of wifi, the video stays at 0:00 with a total length of 0:00.
Some of my findings:
When I have wifi on and I have the video cached, there are two requests made with bytes bytes=0-1 and then bytes=0-4444000.
When I have wifi off, the request for bytes=0-1 is made, but it stops with that.
Where am I going wrong?
Safari appears to have a very strict approach to range requests and similar issues to your problem are sometimes seen with regular online web servers.
In particular, Safari expects to see a '206' response when it sends a request with a byte range. If the server responds with a '200' request it appears Safari cannot handle this. Some other browsers seem to be ok with this - for example Chrome.
Apple provide some info on hoe to check for this:
If you are not sure whether your media server supports byte-range requests, you can open the Terminal application in OS X and use the curl command-line tool to download a short segment from a file on the server:
curl --range 0-99 http://example.com/test.mov -o /dev/null
If the tool reports that it downloaded 100 bytes, the media server correctly handled the byte-range request. If it downloads the entire file, you may need to update the media server. For more information on curl, see OS X Man Pages.
See this answer for more detail and background: https://stackoverflow.com/a/32998689/334402
I use this code to connect to NodeJS web socket:
useEffect(() => {
let futureResponseFeedAddress = "ws://localhost:/endpoint";
const futureResponseClient = new W3CWebSocket(futureResponseFeedAddress);
props.onUpdateOrderbookWebsocket(futureResponseClient);
futureResponseClient.onopen = () => {
console.log("WebSocket Client Connected on " + futureResponseFeedAddress);
};
futureResponseClient.onmessage = (message) => {
..............
};
futureResponseClient.onclose = closeEvent => {
console.log("response messages websocket closed.");
}
return function cleanup() {
futureResponseClient.close();
};
}, []);
But after around 1-2 minutes the messages are not received. Looks like I get timeout. Do you know how I can configure the web socket to be always open?
My suggestion is you should use socket.io for doing any real-time communication . It uses http long polling ,upgrades the connection to websocket if needed and it has also good browser support . It is much realiable too. But there are some drawbacks , your server need to use socket.io server api .
Socket.io-client
Socket.io-server
There is no need to re-invent the wheel .
I have LibNFC working from the Linux terminal recognising my ACR122U Reader, and I wanted to know if there was a method for it to work through Chrome on a Linux Desktop as it is really similar to Android Support, with all the NFC device handling done by libnfc and the browser just has to know about this library instead of every type usb or other device than can do NFC.
I have tried using the WebNFC API to connect it :
document.getElementById("scanButton").addEventListener("click", async () => {
log.innerHTML = "NFC Register started...";
try {
const ndef = new NDEFReader();
await ndef.scan();
log.innerHTML = ("> Scan started");
ndef.addEventListener("readingerror", () => {
log.innerHTML = ("Argh! Cannot read data from the NFC tag. Try another one?");
});
ndef.addEventListener("reading", ({ message, serialNumber }) => {
log.innerHTML = ("ID ${serialNumber} logged #" + dt.toLocaleTimeString()); });
} catch (error) {
log.innerHTML = (error);
}
});
document.getElementById("stopButton").onclick = function(){
log.innerHTML = "NFC Register stopped # " + new Date().toLocaleTimeString();
};
but I'm met with Error: NFC Permission Request denied
and the WebUSB API to connect it:
var usbd = {};
let device;
let deviceEndpoint = 0x02;
let powerUpDevice = new Uint8Array([0x62,0x00, 0x00, 0x00, 0x00,0x00,0x00,0x01,0x00, 0x00]).buffer;
let getCardUID = new Uint8Array([0xff,0xca,0x00,0x00,0x04]).buffer;
(function() {
'use strict';
usbd.authorize = function(){
navigator.usb.requestDevice({ filters: [{ vendorId: 0x072f }] })
.then(selectedDevice => {
device = selectedDevice;
console.log(device.configuration.interfaces[0].interfaceNumber);
console.log(device.manufacturerName);
console.log(device.productName);
console.log(device);
return device.open()
.then(() => {
if (device.configuration === null) {
return device.selectConfiguration(1);
}
});
})
.then(() => device.claimInterface(0))
but I'm met with Error: ...blocked because it implements a protected interface class i.e. not supported, so that's a no go.
Is there a way to incorporate the libusb/libnfc libraries or any other method to directly connect an NFC reader to read into a web browser/application?
Web NFC is supported on Android only as of February 2021. See https://web.dev/nfc/
The WebUSB error suggests you're requesting an interface that implements a protected class (among those below):
// USB Class Codes are defined by the USB-IF:
// https://www.usb.org/defined-class-codes
const uint8_t kProtectedClasses[] = {
0x01, // Audio
0x03, // HID
0x08, // Mass Storage
0x0B, // Smart Card
0x0E, // Video
0x10, // Audio/Video
0xE0, // Wireless Controller (Bluetooth and Wireless USB)
};
I wonder if that's a linux thing though as I was able to communicate with the ACR122U and SCL3711 NFC reader USB devices through WebUSB. See https://github.com/beaufortfrancois/chrome-nfc
Did you give a try to WebHID by any chance first? See https://web.dev/hid
Is it possible to perform a lookup of a Bluetooth device given its address in a Nodejs script?
There are a few packages out there, the main one being Noble. However, they all focus around scanning, and not looking up a known address (as far as i can tell anyway!).
What i want to achieve, is to look up a known address, to see if the device can be found.
Much like PyBluez does for Python:
bluetooth.lookup_name('CC:20:E8:8F:3A:1D', timeout=5)
In Python, this can find the device even if it is undiscoverable, unlike a typical inquiry scan would.
I had this same problem and just found the btwatch lib, but it isn't working for me on the latest raspbian. But the source is just calling l2ping and looking for a string that I'm guessing no longer prints on success, so the modified code below works instead, similar to the lookup_name method, once you have l2ping installed (I think npm bluetooth or pybluez has it)
var Spawn = require('child_process').spawn;
function detectMacAddress(macAddress, callback)
{
//var macAddress = '72:44:56:05:79:A0';
var ls = Spawn('l2ping', ['-c', '1', '-t', '5', macAddress]);
ls.stdout.on('data', function (data) {
console.log("Found device in Range! " + macAddress);
callback(true);
});
ls.on('close', function () {
console.log("Could not find: " + macAddress);
callback(false);
});
}
Or, a synchronous way,
var execSync = require('child_process').execSync;
function detectMacAddressSync(macAddress)
{
var cmd = 'l2ping -c 1 -t 5 ' + macAddress;
try
{
var output = execSync(cmd );
console.log("output : "+ output );
return true;
}
catch(e)
{
console.log("caught: " + e);
return false;
}
}
As far as I have understood the problem you want to connect to the device using address. Then, I would suggest using node-bluetooth-serial-port.
var btSerial = new (require('bluetooth-serialport')).BluetoothSerialPort();
btSerial.on('found', function(address, name) {
btSerial.findSerialPortChannel(address, function(channel) {
btSerial.connect(address, channel, function() {
console.log('connected');
btSerial.write(new Buffer('my data', 'utf-8'), function(err, bytesWritten) {
if (err) console.log(err);
});
btSerial.on('data', function(buffer) {
console.log(buffer.toString('utf-8'));
});
}, function () {
console.log('cannot connect');
});
// close the connection when you're ready
btSerial.close();
}, function() {
console.log('found nothing');
});
});
BluetoothSerialPort.findSerialPortChannel(address, callback[, errorCallback])
Checks if a device has a serial port service running and if it is found it passes the channel id to use for the RFCOMM connection.
callback(channel) - called when finished looking for a serial port on the device.
errorCallback - called the search finished but no serial port channel was found on the device. Connects to a remote bluetooth device.
bluetoothAddress - the address of the remote Bluetooth device.
channel - the channel to connect to.
[successCallback] - called when a connection has been established.
[errorCallback(err)] - called when the connection attempt results in an error. The parameter is an Error object.