Showing bluetooth devices in range via browser

2019-07-26 15:20发布

问题:

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");

回答1:

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



回答2:

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.