A simple idea. I would like to show bluetooth devices in range of the computer (using a bluetooth dongle via bluetooth-serial-port NodeJS package) in the web browser served up upon call over AJAX.
The issue I am having is that with the following script, I can't continiously scan for devices and serve up web sites at the same time. Could you explain the workflow in this and why/what would be a good direction to rectify the issue?
var http = require("http"),
url = require("url"),
path = require("path"),
fs = require("fs"),
port = process.argv[2] || 8888,
btSerial = new (require('bluetooth-serial-port')).BluetoothSerialPort();
btSerial.inquire();
// Get the devices
var devices = [];
btSerial.on('found', function(address, name) {
if(typeof devices[address] === 'undefined'){
devices.push({'address': address,'name': name});
console.log("Device Found: "+address+" which is named: "+name);
}
});
// Keep searching
btSerial.on('finished', function() {
console.log("Done searching");
console.log(devices);
//btSerial.inquire();
});
http.createServer(function(request, response) {
var uri = url.parse(request.url).pathname,
filename = path.join(process.cwd(), uri);
if (uri == '/devices') {
response.writeHead(200, {"Content-Type": "application/json"});
response.write(JSON.stringify(devices));
response.end();
return;
}
fs.exists(filename, function(exists) {
if(!exists) {
response.writeHead(404, {"Content-Type": "text/plain"});
response.write("404 Not Found\n");
response.end();
return;
}
if (fs.statSync(filename).isDirectory()) filename += 'public/index.html';
fs.readFile(filename, "binary", function(err, file) {
if(err) {
response.writeHead(500, {"Content-Type": "text/plain"});
response.write(err + "\n");
response.end();
return;
}
response.writeHead(200);
response.write(file, "binary");
response.end();
});
});
}).listen(parseInt(port, 10));
console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown");
The solution was much simpler than expected.
First I created a bluetooth scanner service (running with forever) that opens a socket and delivers updated results to the server service at the find of each device. I set the setTimeout function to 1s (1000ms) otherwise the results would not be delivered to the listening server.
var port = 9838,
devices = {},
socket = require('socket.io-client')('http://localhost:'+port);
btSerial = new (require('bluetooth-serial-port')).BluetoothSerialPort();
var onFinishedFoundDevice = function(message){
// Add new device to object array
if(typeof message.address !== 'undefined' && typeof message.name !== 'undefined')
devices[message.address] = {'address': message.address,'name': message.name};
console.log("Sent new device", message);
};
var sendMessage = function(type, message, callback){
this.message = message;
socket.emit(type, message);
if(callback)
callback(message);
};
var findBluetooths = function () {
// Scan for BT devices in range
btSerial.on('found', function(address, name) {
if(typeof devices[address] === 'undefined'){
var message = {'address': address, 'name': name}; // prepare message
sendMessage('add-device', message, onFinishedFoundDevice); // actually send message to server
console.log("Device Found: "+address+" which is named: "+name);
}
});
// Keep searching
btSerial.on('finished', function() {
console.log("Received Finished... cont'd");
setTimeout(function(){btSerial.inquire();}, 1000);
});
// Scan for devices
console.log("Begin scanning");
btSerial.inquire();
}
// Do the magic
findBluetooths();
Than I created a server service (running in forever as well) that enables HTTP server as well as a listener for socket.io events. It takes in events from the bluetooth scanner as well as the index.html page.
var app = require('http').createServer(handler),
io = require('socket.io')(app),
fs = require('fs'),
port = 9838,
devices = [];
app.listen(port);
function handler (req, res) {
fs.readFile(__dirname + '/public/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Error loading index.html');
}
res.writeHead(200);
res.end(data);
});
}
io.on('connection', function (socket) {
socket.emit('devices', devices);
socket.on('add-device', function(device) {
devices.push({'address': device.address,'name': device.name});
socket.broadcast.emit('device-added', device); // send device to browsers
console.log("Device Found: "+device.address+" which is named: "+device.name);
});
socket.on('get-devices', function(payload){
n });
});
Finally I created a public/index.html file to display the scanning results as well as issue requests for more information from the server.
<!DOCTYPE html>
<html>
<head>
<title>Bluetooth Scanner</title>
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script>
var port = 9838;
var hostname = window.location.hostname;
var socket = io(hostname+':'+port);
socket.on('welcome', function (message) {
console.log(message);
});
socket.on('device-added', function (device) {
$("#no-devices").hide();
$('<li>', {
html: "Device named "+device.name+" with MAC "+ device.address+" is online."
}).appendTo('#devices');
});
socket.on('devices', function(devices){
if(devices.length > 0) $("#no-devices").hide();
$.each(devices, function(key, device){
$('<li>', {
html: "Device named <b>"+device.name+"</b> with MAC <b>"+ device.address+"</b> is online."
}).appendTo('#devices');
});
});
</script>
</head>
<body>
<div>
<span id="no-devices">No devices yet...</span>
<ul id="devices"></ul>
</div>
</body>
</html>
Thank you all for the help. It lacks necessary functionality to really make it usable but it is a good start for my project
To periodically scan for devices in the background, use setInterval
:
setInterval(btSerial.inquire, 10000);
This will call btSerial.inquire
once every 10 seconds. You'll need to decide on the best refresh interval for your application. The trade-off is that you can get more up-to-date device data with a shorter interval, but that gives your server less time for HTTP requests.