Skip to content

Commit 711db23

Browse files
author
AJ Keller
authored
Merge pull request #17 from aj-ptw/udp
Udp
2 parents e89989a + ac0dd2e commit 711db23

File tree

5 files changed

+268
-27
lines changed

5 files changed

+268
-27
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# v0.3.1
2+
3+
### New Features
4+
5+
* Add UDP support.
6+
17
# v0.3.0
28

39
Docs for all!!

examples/getStreaming/getStreaming.js

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,60 @@
99
* do `npm install`
1010
* then `npm start`
1111
*/
12-
let debug = false; // Pretty print any bytes in and out... it's amazing...
12+
let debug = true; // Pretty print any bytes in and out... it's amazing...
1313
let verbose = true; // Adds verbosity to functions
14+
const protocol = 'tcp'; // or 'udp'
1415

1516
const k = require('openbci-utilities').Constants;
1617
let Wifi = require('../../openBCIWifi');
1718
let wifi = new Wifi({
1819
debug: debug,
1920
verbose: verbose,
2021
sendCounts: false,
21-
latency: 20000
22+
latency: 16667,
23+
protocol: protocol
2224
});
2325

2426
let counter = 0;
2527
let sampleRateCounterInterval = null;
2628
let lastSampleNumber = 0;
2729
let MAX_SAMPLE_NUMBER = 255;
28-
30+
let droppedPacketArray = [];
31+
let sampleRateArray = [];
32+
let droppedPackets = 0;
2933
const sampleFunc = (sample) => {
3034
try {
31-
// console.log(JSON.stringify(sample));
35+
console.log(JSON.stringify(sample));
3236
if (sample.valid) {
3337
counter++;
3438
if (sampleRateCounterInterval === null) {
3539
sampleRateCounterInterval = setInterval(() => {
36-
console.log(`SR: ${counter}`);
40+
console.log(`\nSR: ${counter}`);
41+
console.log(`Dropped ${droppedPackets} packets`);
42+
droppedPacketArray.push(droppedPackets);
43+
let sum = 0;
44+
droppedPacketArray.map((droppedPacketCnt) => {
45+
sum += droppedPacketCnt;
46+
});
47+
console.log(`Dropped packet average: ${sum / droppedPacketArray.length}`);
48+
49+
sampleRateArray.push(counter);
50+
sum = 0;
51+
sampleRateArray.map((counter) => {
52+
sum += counter;
53+
});
54+
droppedPackets = 0;
3755
counter = 0;
56+
3857
}, 1000);
3958
}
4059

4160
let packetDiff = sample.sampleNumber - lastSampleNumber;
4261
if (packetDiff < 0) packetDiff += MAX_SAMPLE_NUMBER;
43-
if (packetDiff > 1) console.log(`dropped ${packetDiff} packets | cur sn: ${sample.sampleNumber} | last sn: ${lastSampleNumber}`);
62+
if (packetDiff > 1) {
63+
console.log(`dropped ${packetDiff} packets | cur sn: ${sample.sampleNumber} | last sn: ${lastSampleNumber}`);
64+
droppedPackets += packetDiff;
65+
}
4466
lastSampleNumber = sample.sampleNumber;
4567
// console.log(JSON.stringify(sample));
4668
}
@@ -56,7 +78,8 @@ wifi.on(k.OBCIEmitterSample, sampleFunc);
5678
// wifi.on(k.OBCIEmitterRawDataPacket, console.log);
5779

5880
wifi.searchToStream({
59-
streamStart: true
81+
streamStart: true,
82+
sampleRate: 1000
6083
})
6184
.then(() => {
6285
if (wifi.getNumberOfChannels() === 4) {

openBCIWifi.js

Lines changed: 111 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,23 @@ const net = require('net');
1414
const http = require('http');
1515
const bufferEqual = require('buffer-equal');
1616
const Buffer = require('safe-buffer').Buffer;
17+
const dgram = require('dgram');
1718

1819
const wifiOutputModeJSON = 'json';
1920
const wifiOutputModeRaw = 'raw';
20-
21+
const wifiOutputProtocolUDP = 'udp';
22+
const wifiOutputProtocolTCP = 'tcp';
2123
/**
2224
* Options object
2325
* @type {InitializationObject}
2426
* @private
2527
*/
2628
const _options = {
2729
attempts: 10,
30+
burst: false,
2831
debug: false,
2932
latency: 10000,
33+
protocol: [wifiOutputProtocolTCP, wifiOutputProtocolUDP],
3034
sampleRate: 0,
3135
sendCounts: false,
3236
simulate: false,
@@ -43,11 +47,17 @@ const _options = {
4347
* @typedef {Object} InitializationObject
4448
* @property {Number} attempts - The number of times to try and perform an SSDP search before quitting. (Default 10)
4549
*
50+
* @property {Boolean} burst - Only applies for UDP, but the wifi shield will send 3 of the same packets on UDP to
51+
* increase the chance packets arrive to this module (Default false)
52+
*
4653
* @property {Boolean} debug - Print out a raw dump of bytes sent and received. (Default `false`)
4754
*
4855
* @property {Number} latency - The latency, or amount of time between packet sends, of the WiFi shield. The time is in
4956
* micro seconds!
5057
*
58+
* @property {String} protocol - Either send the data over TCP or UDP. UDP seems better for either a bad router or slow
59+
* router. Default is TCP
60+
*
5161
* @property {Number} sampleRate - The sample rate to set the board to. (Default is zero)
5262
*
5363
* @property {Boolean} sendCounts - Send integer raw counts instead of scaled floats.
@@ -159,11 +169,14 @@ function Wifi (options) {
159169
this._shieldName = null;
160170
this._streaming = false;
161171
this._version = null;
172+
this._lastPacketArrival = 0;
162173

163174
/** Public Properties (keep alphabetical) */
164175

165176
this.curOutputMode = wifiOutputModeRaw;
177+
this.internalRawDataPackets = [];
166178
this.wifiShieldArray = [];
179+
this.wifiServerUDPPort = 0;
167180

168181
/** Initializations */
169182

@@ -173,6 +186,41 @@ function Wifi (options) {
173186
// This allows us to use the emitter class freely outside of the module
174187
util.inherits(Wifi, EventEmitter);
175188

189+
/**
190+
* This function is for redundancy after a long packet send, the wifi firmware can resend the same
191+
* packet again, using this till add redundancy on poor networks.
192+
* @param rawDataPackets -
193+
* @returns {Array}
194+
*/
195+
Wifi.prototype.bufferRawDataPackets = function (rawDataPackets) {
196+
if (this.internalRawDataPackets.length === 0) {
197+
this.internalRawDataPackets = rawDataPackets;
198+
return [];
199+
} else {
200+
let out = [];
201+
let coldStorage = [];
202+
_.forEach(rawDataPackets, (newRDP) => {
203+
let found = false;
204+
_.forEach(this.internalRawDataPackets, (oldRDP) => {
205+
if (!found && bufferEqual(newRDP, oldRDP)) {
206+
out.push(oldRDP);
207+
found = true;
208+
}
209+
});
210+
if (!found) {
211+
coldStorage.push(newRDP);
212+
}
213+
});
214+
if (out.length === 0) {
215+
out = this.internalRawDataPackets;
216+
this.internalRawDataPackets = rawDataPackets;
217+
} else {
218+
this.internalRawDataPackets = coldStorage;
219+
}
220+
return out;
221+
}
222+
};
223+
176224
/**
177225
* @description Send a command to the board to turn a specified channel off
178226
* @param channelNumber
@@ -334,10 +382,6 @@ Wifi.prototype.disconnect = function () {
334382
return Promise.resolve();
335383
};
336384

337-
Wifi.prototype.eraseCredentials = function () {
338-
this.delete
339-
};
340-
341385
/**
342386
* @description Checks if the driver is connected to a board.
343387
* @returns {boolean} - True if connected.
@@ -717,6 +761,7 @@ Wifi.prototype.streamStart = function () {
717761
return new Promise((resolve, reject) => {
718762
if (this.isStreaming()) return reject('Error [.streamStart()]: Already streaming');
719763
this._streaming = true;
764+
this._lastPacketArrival = 0;
720765
this.write(k.OBCIStreamStart)
721766
.then(() => {
722767
if (this.options.verbose) console.log('Sent stream start to board.');
@@ -859,6 +904,11 @@ Wifi.prototype._processBytes = function (data) {
859904

860905
this.buffer = output.buffer;
861906

907+
// if (this.options.redundancy) {
908+
// this._rawDataPacketToSample.rawDataPackets = this.bufferRawDataPackets(output.rawDataPackets);
909+
// } else {
910+
// this._rawDataPacketToSample.rawDataPackets = output.rawDataPackets;
911+
// }
862912
this._rawDataPacketToSample.rawDataPackets = output.rawDataPackets;
863913

864914
_.forEach(this._rawDataPacketToSample.rawDataPackets, (rawDataPacket) => {
@@ -928,13 +978,24 @@ Wifi.prototype._finalizeNewSampleForDaisy = function (sampleObject) {
928978
* @private
929979
*/
930980
Wifi.prototype._connectSocket = function () {
931-
return this.post('/tcp', {
932-
ip: ip.address(),
933-
output: this.curOutputMode,
934-
port: this.wifiGetLocalPort(),
935-
delimiter: false,
936-
latency: this._latency
937-
});
981+
if (this.options.protocol === 'udp') {
982+
return this.post(`/${this.options.protocol}`, {
983+
ip: ip.address(),
984+
output: this.curOutputMode,
985+
port: this.wifiGetLocalPort(),
986+
delimiter: false,
987+
latency: this._latency,
988+
redundancy: this.options.burst
989+
});
990+
} else {
991+
return this.post(`/${this.options.protocol}`, {
992+
ip: ip.address(),
993+
output: this.curOutputMode,
994+
port: this.wifiGetLocalPort(),
995+
delimiter: false,
996+
latency: this._latency
997+
});
998+
}
938999
};
9391000

9401001
/**
@@ -945,28 +1006,59 @@ Wifi.prototype.destroy = function () {
9451006
if (this.wifiClient) {
9461007
this.wifiClient.stop();
9471008
}
1009+
if (this.wifiServerUDP) {
1010+
this.wifiServerUDP.removeAllListeners('error');
1011+
this.wifiServerUDP.removeAllListeners('message');
1012+
this.wifiServerUDP.removeAllListeners('listening');
1013+
this.wifiServerUDP.close();
1014+
}
9481015
this.wifiClient = null;
9491016
};
9501017

9511018
Wifi.prototype.wifiGetLocalPort = function () {
952-
return this.wifiServer.address().port;
1019+
if (this.options.protocol === wifiOutputProtocolUDP) {
1020+
return this.wifiServerUDPPort;
1021+
} else {
1022+
return this.wifiServer.address().port;
1023+
}
9531024
};
9541025

9551026
Wifi.prototype.wifiInitServer = function () {
956-
let persistentBuffer = null;
957-
const delimBuf = new Buffer("\r\n");
9581027
this.wifiServer = net.createServer((socket) => {
959-
// streamJSON.on("data", (sample) => {
960-
// console.log(sample);
961-
// });
9621028
socket.on('data', (data) => {
9631029
this._processBytes(data);
9641030
});
9651031
socket.on('error', (err) => {
9661032
if (this.options.verbose) console.log('SSDP:',err);
9671033
});
9681034
}).listen();
969-
if (this.options.verbose) console.log("Server on port: ", this.wifiGetLocalPort());
1035+
if (this.options.verbose) console.log("TCP: on port: ", this.wifiGetLocalPort());
1036+
1037+
this.wifiServerUDP = dgram.createSocket('udp4');
1038+
1039+
this.wifiServerUDP.on('error', (err) => {
1040+
if (this.options.verbose) console.log(`server error:\n${err.stack}`);
1041+
this.wifiServerUDP.close();
1042+
});
1043+
1044+
let lastMsg = null;
1045+
1046+
this.wifiServerUDP.on('message', (msg) => {
1047+
if (!bufferEqual(lastMsg, msg)) {
1048+
this._processBytes(msg);
1049+
}
1050+
lastMsg = msg;
1051+
});
1052+
1053+
this.wifiServerUDP.on('listening', () => {
1054+
const address = this.wifiServerUDP.address();
1055+
this.wifiServerUDPPort = address.port;
1056+
if (this.options.verbose) console.log(`server listening ${address.address}:${address.port}`);
1057+
});
1058+
1059+
this.wifiServerUDP.bind({
1060+
address: ip.address()
1061+
});
9701062
};
9711063

9721064
Wifi.prototype.processResponse = function (res, cb) {

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "openbci-wifi",
3-
"version": "0.3.0",
3+
"version": "0.3.1",
44
"description": "The official SDK for the OpenBCI/Push The World Wifi Shield connected to either OpenBCI Cytons or Ganglions.",
55
"main": "openBCIWifi.js",
66
"scripts": {

0 commit comments

Comments
 (0)