Beacon Game

My strategy for the beacon game was essentially to build a smart enough program that I woudln’t have to do any work during the game, except walk around. The overall structure of my program was to first send a login request, then start scanning for beacons using the provided service. Once a beacon was found, the program would connect, read it’s values, then save that value in an internal data structure. Once the beacon was saved, the program would start posting to the server, and increment the rssi by 10 each post until it claimed the beacon.

That was the idea anyway -> you can see all the code here:


let noble = require('noble');
let axios = require('axios');
let process = require('process');

let readline = require('readline');


let app = {
    init: function(socket, discoverCb) {
        axios.get('http://104.236.102.241:8080/clearUser/jmc1207')
            .then(this.login.bind(this));

        this.beacons = {};
        this.serviceUuids = ["A495FF20-C5B1-4B44-B512-1370F02D74DE"];
        this.characteristicUuid = ["A495FF25-C5B1-4B44-B512-1370F02D74DE"];
        this.socket = socket;


        // Discovered a new peripheral
        noble.on('discover', this.discover.bind(this));
    },

    discover: function(peripheral) {
        console.log(peripheral.advertisement.localName);

        if (peripheral.advertisement.localName === undefined || peripheral.advertisement.localName === 'iPhone') {
            return;
        }

        if (this.beacons[peripheral.advertisement.localName]) {
            console.log('already connected to ', peripheral.advertisement.localName);
            return;
        } else {
            console.log('have not connected to this beacon before');
            noble.stopScanning();
            this.beacons[peripheral.advertisement.localName] = peripheral;
        }

        let self = this;
        // Only try to connect to one peripheral at a time
        // console.log('attempting to connect');
        peripheral.connect((err) => {
            // console.log('connected');
            if (err) {
                return;
            }
            peripheral.discoverServices([self.service], (err, services) => {
                if (err) {
                    return;
                }
                // console.log(services);
                services.forEach(function(service) {

                    service.discoverCharacteristics([self.characteristic], (err, chars) => {
                        if (err) {
                            return;
                        }
                        // console.log(chars);
                        if (chars.length) {
                            chars[0].read(function(err, data) {
                                if (err) {
                                    return;
                                }
                                // console.log('data', data.readInt8());
                                self.beacons[peripheral.advertisement.localName] = data.readInt8();
                                console.log('saved ', peripheral.advertisement.localName, ' with value ', data.readInt8());
                                peripheral.disconnect();
                                self.submitBeacon(peripheral.advertisement.localName, -10);
                                noble.startScanning([self.service], false);
                            });
                        }
                    });
                });
            });
        });
        // console.log(peripheral);
    },

    submitBeacon: function(beaconName, rssi) {
        console.log('submitting beacon', beaconName)
        let beaconValue = this.beacons[beaconName];

        if (rssi < -100) {
            return;
        }

        let post = {
            "token": this.token,
            "localname": beaconName,
            "rssi": rssi,
            "characteristic": this.characteristic,
            "points": beaconValue
        };

        let self = this;
        axios.post('http://104.236.102.241:8080/beacon', post)
            .then(response => {
                console.log(response.data);
                if (response.data.error === 'beacon already claimed') {
                    return;
                }
                if (response.data.error === 'not in range to claim beacon') {
                    console.log('not in range', 'submitting again');
                    self.submitBeacon(beaconName, rssi - 10);
                }
            });
    },

    login: function() {
        axios.post('http://104.236.102.241:8080/login', {
            username: 'jmc1207'
        }).then((response) => {
            console.log('start scanning');
            console.log(response.data);
            this.service = response.data.service.replace(/-/g, '');
            this.characteristic = response.data.characteristic.replace(/-/g, '');
            this.token = response.data.token;
            this.scan(this.service, this.characteristic);
        });
    },

    scan: function(service, char, cb) {
        this.beacons = {};
        console.log('beacon should scan for service', service);
        // let s = service.replace(/-/g, '');
        noble.startScanning([this.service], false);
    },

    connect: function(peripheral) {
        peripheral.connect(() => {
            console.log('periphal connected');
        });
    },

    stop: function() {
        console.log('should stop scanning');
        noble.stopScanning();
    }
};

noble.on('stateChange', (state) => {
    app.init();
});

Problem is, nothing worked as I’d hoped. I didn’t realize that if I logged out, or cleared the user, that all my potential points would be lost. I was having login issues so I threw in a line of code that would clear myself from the user database everytime the program ran, trouble is that in doing so I was sabotaging myself without realizing it. The way I played the game was that when the scan had been running for a while, I killed the program and refreshed it to ensure noble would start a fresh scan, but each time I did that I wiped myself from the game. Oops.

A few things I needed to have my program actually work:

  1. A readline function to take stdin and use that to kick start a new scan
  2. A persistent store for my token and the beacon uuids so I wouldn’t need to clear myself if something went wrong
  3. More time spent testing with the beacons

Leave a Reply

Your email address will not be published. Required fields are marked *