├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── ROADMAP.md ├── examples ├── getStreaming │ └── getStreaming.js ├── getStreamingWiFiDirect │ └── getStreamingWiFiDirect.js └── xstreamCSV │ └── xstreamCSV.js ├── images └── WiFi_front_product.jpg ├── openBCIWifi.js ├── package-lock.json ├── package.json └── test ├── bluebirdChecks.js ├── openBCIWifi-test.js └── timingEventsAsPromises.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Node template 3 | # Logs 4 | logs 5 | *.log 6 | npm-debug.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # node-waf configuration 27 | .lock-wscript 28 | 29 | # Compiled binary addons (http://nodejs.org/api/addons.html) 30 | build/Release 31 | 32 | # Dependency directories 33 | node_modules 34 | jspm_packages 35 | 36 | # Optional npm cache directory 37 | .npm 38 | 39 | # Optional eslint cache 40 | .eslintcache 41 | 42 | # Optional REPL history 43 | .node_repl_history 44 | 45 | # Output of 'npm pack' 46 | *.tgz 47 | 48 | # Yarn Integrity file 49 | .yarn-integrity 50 | 51 | # Webstorm 52 | .idea 53 | .idea/*.xml 54 | .DS_Store 55 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | test_mocks/ 3 | 4 | # MIRRORED FROM .gitignore PLEASE MAINTAIN 5 | logs 6 | *.log 7 | pids 8 | *.pid 9 | *.seed 10 | lib-cov 11 | coverage 12 | .grunt 13 | .lock-wscript 14 | build/Release 15 | node_modules 16 | .idea 17 | .DS_Store 18 | public 19 | myOutput.txt 20 | *.tgz 21 | openBCISerialFormat 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "6.11" 4 | - "8.5" 5 | - "10" 6 | install: 7 | - npm install --all 8 | script: 9 | - npm run test-cov 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v1.0.2 2 | 3 | ### Bug Fixes 4 | 5 | * Get host IP address on Windows machines using valid network interface 6 | 7 | # v1.0.1 8 | 9 | ### Bug Fixes 10 | 11 | * Updated references to .Debug and .Utilities to .debug and .utilities 12 | 13 | # v1.0.0 14 | 15 | ### Breaking Changes 16 | 17 | * Name of package is now @openbci/wifi 18 | 19 | ### Enhancements 20 | 21 | * Include node 10 in testing 22 | 23 | # v0.4.2 24 | 25 | ### Examples 26 | 27 | 1. Refactored examples `getStreaming.js` and `getStreamingWiFiDirect.js` 28 | - Removed dead code 29 | - Renamed variables 30 | - Variables properly classed into `const` and `let` 31 | - Improved readability 32 | - Eliminated functional redundancies 33 | 2. Added example `xstreamCSV.js` 34 | - Direct WiFi 35 | - Pipes data from OpenBCI hardware into an [XStream](https://github.com/staltz/xstream) producer 36 | - Outputs to console, CSV (via WriteStream) using listeners 37 | 38 | # v0.4.1 39 | 40 | ### Bug Fixes 41 | 42 | * DELETE, GET, and POST would resolve even if code was not equal to 200 43 | 44 | # v0.4.0 45 | 46 | ### New Features 47 | 48 | * Add UDP support! New option on connect and Constructor called `protocol` that can be either `'udp'` or `'tcp'` (Default is `tcp`) 49 | * Add burst mode UDP support! New option on connect and Constructor called `burst` that can be either `true` or `false` (default `false`). When `true` and `protocol` option is UDP, will tell WiFi Shield to send every packet three times. The module will automatically only process new data. 50 | * Went through all the docs again and cleaned up! 51 | 52 | ### New Example 53 | 54 | * WiFi direct example! 55 | 56 | ### Bug Fixes 57 | 58 | * Now starting both UDP and TCP systems incase so user can select at `connect` function. 59 | 60 | # v0.3.1 61 | 62 | ### New Features 63 | 64 | * Add UDP support. 65 | 66 | # v0.3.0 67 | 68 | Docs for all!! 69 | 70 | ### New Files 71 | 72 | * `CODE_OF_CONDUCT.md` added to govern community 73 | * `CONTRIBUTING.md` added to describe how people should contribute 74 | * `ROADMAP.md` added to outline a roadmap for project 75 | 76 | ### Bug Fixes 77 | 78 | * Bump utilities to 0.2.7 to get patch for wifi accel data and stop byte 79 | 80 | ### Breaking Changes 81 | 82 | * Removed index.js to conform to other cyton and ganglion modules 83 | 84 | # v0.2.1 85 | 86 | ### Bug Fixes 87 | 88 | * Bumping utility version to 0.2.4 fixes bug in this repo too for ganglion over wifi with no scale. 89 | 90 | # 0.2.0 91 | 92 | ### Breaking changes 93 | 94 | * Update `autoFindAndConnectToWifiShield()` to be called `searchToStream` and upgraded it's power! Checkout the source code for how to use the function. 95 | 96 | ### New Features 97 | 98 | * Added function for telling the WiFi Shield to forget it's credentials and turn back into an access point, aka broad casting it's unique name ready for someone to connect and have it join another network. 99 | 100 | # 0.1.4 101 | 102 | ### Enhancements 103 | 104 | * Bump `openbci-utilities` to v0.2.0 for accelDataCounts support on cyton. 105 | 106 | # 0.1.3 107 | 108 | ### New Features 109 | 110 | * Add `channelSet` function 111 | 112 | # 0.1.2 113 | 114 | ### New Features 115 | 116 | * Add `sdStart`, `sdStop`, and `syncRegisterSettings` 117 | 118 | # 0.1.1 119 | 120 | ### New Features 121 | 122 | * Added function called `autoFindAndConnectToWifiShield()` which will automatically search the local network for wifi shields and connect to the first one it finds. 123 | * Add `latency` setting to default options 124 | 125 | # 0.1.0 126 | 127 | ### Breaking Change 128 | 129 | * Updated `localName` building for wifi v0.2.1+ where name changed to `PTW-0001-XXXX` where the last four are the last two bytes of the mac address. 130 | 131 | # 0.0.5 132 | 133 | ### Enhancements 134 | 135 | * Bump utilities to 0.1.2 136 | 137 | # 0.0.4 138 | 139 | ### New Features 140 | 141 | * Switch `getSampleRate` and `setSampleRate` to regex parseing which adds support for ganglion now. 142 | * Started saving the wifi shields found so the user can pass a `localName` and the module will use the ip address to route to it. mDNS is too router dependent. 143 | 144 | # 0.0.3 145 | 146 | This actually seems to be generally working for cyton and ganglion. Daisy needs more testing. 147 | 148 | ### Enhancements 149 | 150 | * Now working with daisy, cyton and ganglion. 151 | * Bumped `openbci-utilities` to v0.0.8 152 | * Now setting the sample rate it possible checkout the example 153 | * Module will now sync itself with the shield on `connect` which will align board type and channel gains. 154 | 155 | # 0.0.2 156 | 157 | The readme and package.json were horribly wrong in `v0.0.1` 158 | 159 | # 0.0.1 160 | 161 | Initial release 162 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # OpenBCI WiFi NodeJS Code of Conduct 2 | 3 | ## Purpose 4 | 5 | It is our hope that any one is able to contribute to OpenBCI WiFi NodeJS regardless of their background. Thus, we hope to provide a safe, welcoming, and warmly geeky environment for everybody, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof). 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment 10 | include: 11 | 12 | * Using welcoming and inclusive language 13 | * Being respectful of differing viewpoints and experiences 14 | * Gracefully accepting constructive criticism 15 | * Focusing on what is best for the community 16 | * Showing empathy towards other community members 17 | 18 | Examples of unacceptable behavior by participants include: 19 | 20 | * The use of sexualized language or imagery and unwelcome sexual attention or 21 | advances 22 | * Trolling, insulting/derogatory comments, and personal or political attacks 23 | * Public or private harassment 24 | * Publishing others' private information, such as a physical or electronic 25 | address, without explicit permission 26 | * Other conduct which could reasonably be considered inappropriate in a 27 | professional setting 28 | 29 | ## Our Responsibilities 30 | 31 | Project maintainers are responsible for clarifying the standards of acceptable 32 | behavior and are expected to take appropriate and fair corrective action in 33 | response to any instances of unacceptable behavior. 34 | 35 | Project maintainers have the right and responsibility to remove, edit, or 36 | reject comments, commits, code, wiki edits, issues, and other contributions 37 | that are not aligned to this Code of Conduct, or to ban temporarily or 38 | permanently any contributor for other behaviors that they deem inappropriate, 39 | threatening, offensive, or harmful. 40 | 41 | ## Scope 42 | 43 | This Code of Conduct applies both within project spaces and in public spaces 44 | when an individual is representing the project or its community. Examples of 45 | representing a project or community include using an official project e-mail 46 | address, posting via an official social media account, or acting as an appointed 47 | representative at an online or offline event. Representation of a project may be 48 | further defined and clarified by project maintainers. 49 | 50 | ## Enforcement 51 | 52 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 53 | reported by contacting the project team at [info@pushtheworld.us](mailto:info@pushtheworld.us). All 54 | complaints will be reviewed and investigated and will result in a response that 55 | is deemed necessary and appropriate to the circumstances. The project team is 56 | obligated to maintain confidentiality with regard to the reporter of an incident. 57 | Further details of specific enforcement policies may be posted separately. 58 | 59 | Project maintainers who do not follow or enforce the Code of Conduct in good 60 | faith may face temporary or permanent repercussions as determined by other 61 | members of the project's leadership. 62 | 63 | ## Attribution 64 | 65 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 66 | available at [http://contributor-covenant.org/version/1/4][version] 67 | 68 | [homepage]: http://contributor-covenant.org 69 | [version]: http://contributor-covenant.org/version/1/4/ 70 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | :tada::clinking_glasses: First off, thanks for taking the time to contribute! :tada::clinking_glasses: 4 | 5 | Contributions are always welcome, no matter how small. 6 | 7 | The following is a small set of guidelines for how to contribute to the project 8 | 9 | ## Where to start 10 | 11 | ### Code of Conduct 12 | This project adheres to the Contributor Covenant [Code of Conduct](CODE_OF_CONDUCT.md). 13 | By participating you are expected to adhere to these expectations. Please report unacceptable behaviour to [info@pushtheworld.us](mailto:info@pushtheworld.us) 14 | 15 | ### Contributing on Github 16 | 17 | If you're new to Git and want to learn how to fork this repo, make your own additions, and include those additions in the master version of this project, check out this [great tutorial](http://blog.davidecoppola.com/2016/11/howto-contribute-to-open-source-project-on-github/). 18 | 19 | ### Community 20 | 21 | This project is maintained by the [OpenBCI](www.openbci.com) and [NeuroTechX](www.neurotechx.com) community. Join the NeuroTechX Slack to check out our #devices channel, where discussions about OpenBCI takes place. 22 | 23 | ## How can I contribute? 24 | 25 | This is currently a small, humble project so our contribution process is rather casual. If there's a feature you'd be interested in building, go ahead! Let us know by [opening an issue](../../issues/new) and we'll support you as much as we can. When you're finished submit a pull request to the master branch referencing the specific issue you addressed. 26 | 27 | If you find a bug, or have a suggestion on how to improve the project, just fill out a [Github issue](../../issues) 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 OpenBCI 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | **THIS REPOSITORY HAS BEEN DEPRECATED AND IS NO LONGER IN ACTIVE DEVELOPMENT.** 2 | 3 | 4 | # OpenBCI WiFi Shield NodeJS SDK 5 | 6 |
7 |
8 |
10 | Make programming with OpenBCI reliable, easy, research grade and fun! 11 |
12 | 13 | [](https://codecov.io/gh/OpenBCI/OpenBCI_NodeJS_Wifi) 14 | [](https://david-dm.org/OpenBCI/OpenBCI_NodeJS_Wifi) 15 | [](http://npmjs.com/package/@openbci/wifi) 16 | [](https://github.com/Flet/semistandard) 17 | 18 | ## Welcome! 19 | 20 | First and foremost, Welcome! :tada: Willkommen! :confetti_ball: Bienvenue! :balloon::balloon::balloon: 21 | 22 | Thank you for visiting the OpenBCI WiFi Shield NodeJS SDK repository. 23 | 24 | This document (the README file) is a hub to give you some information about the project. Jump straight to one of the sections below, or just scroll down to find out more. 25 | 26 | * [What are we doing? (And why?)](#what-are-we-doing) 27 | * [Who are we?](#who-are-we) 28 | * [What do we need?](#what-do-we-need) 29 | * [How can you get involved?](#get-involved) 30 | * [Get in touch](#contact-us) 31 | * [Find out more](#find-out-more) 32 | * [Understand the jargon](#glossary) 33 | 34 | ## What are we doing? 35 | 36 | ### The problem 37 | 38 | * Users continuously struggle to get prerequisites properly installed to get current OpenBCI Cyton and Ganglion, hours/days/weeks are wasted just _trying to get the data_. 39 | * Bluetooth requires you to stay close to your computer, if you go to far, data is lost and the experiment is over. 40 | * Bluetooth is too slow for transmitting research grade EEG, researchers want 1000Hz (samples per second), bluetooth with 8 channels is limted to 250Hz and with 16 channels limited to 125Hz. 41 | * Bluetooth is unreliable when many other Bluetooth devices are around, demo device or use in busy real life experiment is not reliable. (think grand central station at rush hour) 42 | * Bluetooth requires data to be sent to desktops in raw or compressed form, must use other node modules to parse complex byte streams, prevents from running in browser. 43 | * OpenBCI Cyton (8 and 16 channel) with Bluetooth cannot go to any mobile device because of required Bluetooth-to-USB "Dongle". Must use USB port on Desktop/Laptop computers. 44 | * Bluetooth on Ganglion requires low level drivers to use computers bluetooth hardware. 45 | * The OpenBCI Cyton and Ganglion must transmit data to another computer over Bluetooth before going to the cloud for storage or analytics 46 | * OpenBCI Cyton Dongle FTDI virtual comm port drivers have high latency by default which limits the rate at which new data is made available to your application to twice a second when it should get data as close to 250 times a second. 47 | * Using Cyton or Ganglion NodeJS drivers requires the use of [_native C++ modules_](https://nodejs.org/api/addons.html) which continuously confuse developers of all levels. 48 | 49 | So, if even the very best developers integrate the current easy to use Cyton and Ganglion NodeJS drivers, they are still burdened by the limitations of the physical hardware on the OpenBCI system. 50 | 51 | ### The solution 52 | 53 | The OpenBCI WiFi Shield NodeJS SDK will: 54 | 55 | * Find, connect, sync, and configure (e.g. set sample rate) with OpenBCI WiFi Shield and Carrier board (Ganglion or Cyton or Cyton with Daisy) in a single function call 56 | * Use TCP over WiFi to prevent packet loss 57 | * Relies on **zero** [_native C++ modules_](https://nodejs.org/api/addons.html) 58 | * Enable streaming of high speed (samples rates over 100Hz), low latency (by default, send data every 10ms), research grade EEG (no lost data) directly to any internet connected device (i.e. iPhone, Android, macOS, Windows, Linux, Raspberry Pi 3) 59 | * With WiFi Shield you can now use OpenBCI Ganglion and Cyton anywhere you have a good enough WiFi signal. 60 | * Hotspots create a stable wireless transmission system even in crowded areas 61 | 62 | Using WiFi physically solves limitations with the current state-of-the-art open source bio sensor. The goal for the WiFi Shield firmware was to create a [_one up_](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0ahUKEwjF7pax67PWAhUH6oMKHdfJAcgQFggoMAA&url=https%3A%2F%2Fwww.electroimpact.com%2FWhitePapers%2F2008-01-2297.pdf&usg=AFQjCNHSyVXxRNtkFrmPiRqM5WqHWdO9-g) data pipeline, where scientific data in JSON is sent instead of raw/compressed ADC counts (yuk!) to ***make programming with OpenBCI reliable, easy, research grade and fun!*** 63 | 64 | ## Who are we? 65 | 66 | The founder of the OpenBCI WiFi Shield NodeJS SDK is [AJ Keller][link_aj_keller]. 67 | 68 | 69 |Object
InitializationObject
](#InitializationObject)
313 | * [._accelArray](#Wifi+_accelArray)
314 | * [.curOutputMode](#Wifi+curOutputMode)
315 | * [.bufferRawDataPackets(rawDataPackets)](#Wifi+bufferRawDataPackets) ⇒ Array
316 | * [.channelOff(channelNumber)](#Wifi+channelOff) ⇒ Promise.<T>
317 | * [.channelOn(channelNumber)](#Wifi+channelOn) ⇒ Promise.<T>
\| \*
318 | * [.channelSet(channelNumber, powerDown, gain, inputType, bias, srb2, srb1)](#Wifi+channelSet) ⇒ Promise
319 | * [.impedanceSet(channelNumber, pInputApplied, nInputApplied)](#Wifi+impedanceSet) ⇒ Promise
320 | * [.connect(o)](#Wifi+connect) ⇒ Promise
321 | * [.disconnect()](#Wifi+disconnect) ⇒ Promise
322 | * [.isConnected()](#Wifi+isConnected) ⇒ boolean
323 | * [.isSearching()](#Wifi+isSearching) ⇒ boolean
324 | * [.isStreaming()](#Wifi+isStreaming) ⇒ boolean
325 | * [.getBoardType()](#Wifi+getBoardType) ⇒ \*
326 | * [.getFirmwareVersion()](#Wifi+getFirmwareVersion) ⇒ String
327 | * [.getIpAddress()](#Wifi+getIpAddress) ⇒ null
\| String
328 | * [.getLatency()](#Wifi+getLatency) ⇒ Number
329 | * [.getMacAddress()](#Wifi+getMacAddress) ⇒ null
\| String
330 | * [.getNumberOfChannels()](#Wifi+getNumberOfChannels) ⇒ Number
331 | * [.getSampleRate()](#Wifi+getSampleRate) ⇒ Number
332 | * [.getShieldName()](#Wifi+getShieldName) ⇒ null
\| String
333 | * [.impedanceStart()](#Wifi+impedanceStart) ⇒ global.Promise
\| Promise
334 | * [.impedanceStop()](#Wifi+impedanceStop) ⇒ global.Promise
\| Promise
335 | * [.searchToStream(o)](#Wifi+searchToStream) ⇒ Promise
336 | * [.setSampleRate(sampleRate)](#Wifi+setSampleRate) ⇒ Promise
337 | * [.syncSampleRate()](#Wifi+syncSampleRate) ⇒ Promise
338 | * [.searchStart()](#Wifi+searchStart) ⇒ Promise
339 | * [.searchStop()](#Wifi+searchStop) ⇒ global.Promise
\| Promise
340 | * [.sdStop()](#Wifi+sdStop) ⇒ Promise
341 | * [.syncRegisterSettings()](#Wifi+syncRegisterSettings) ⇒ Promise.<T>
\| \*
342 | * [.softReset()](#Wifi+softReset) ⇒ Promise
343 | * [.eraseWifiCredentials()](#Wifi+eraseWifiCredentials) ⇒ Promise
344 | * [.streamStart()](#Wifi+streamStart) ⇒ Promise
345 | * [.streamStop()](#Wifi+streamStop) ⇒ Promise
346 | * [.syncInfo(o)](#Wifi+syncInfo) ⇒ Promise.<TResult>
347 | * [.write(data)](#Wifi+write) ⇒ Promise
348 | * [.destroy()](#Wifi+destroy)
349 | * [.wifiGetLocalPort()](#Wifi+wifiGetLocalPort) ⇒ number
350 | * [.wifiGetLocalPortUDP()](#Wifi+wifiGetLocalPortUDP) ⇒ number
351 | * [.wifiGetLocalPortTCP()](#Wifi+wifiGetLocalPortTCP) ⇒ number
352 | * [.wifiInitServer()](#Wifi+wifiInitServer)
353 | * [.delete(path)](#Wifi+delete) ⇒ Promise
354 | * [.get(path)](#Wifi+get) ⇒ Promise
355 | * [.post(path, payload)](#Wifi+post) ⇒ Promise
356 | * _inner_
357 | * [~o](#Wifi..o)
358 |
359 |
360 |
361 | ### new Wifi(options)
362 | The initialization method to call first, before any other method.
363 |
364 |
365 | | Param | Type | Description |
366 | | --- | --- | --- |
367 | | options | [InitializationObject
](#InitializationObject) | (optional) - Board optional configurations. |
368 |
369 |
370 |
371 | ### wifi.options : [InitializationObject
](#InitializationObject)
372 | **Kind**: instance property of [Wifi
](#Wifi)
373 |
374 |
375 | ### wifi._accelArray
376 | Private Properties (keep alphabetical)
377 |
378 | **Kind**: instance property of [Wifi
](#Wifi)
379 |
380 |
381 | ### wifi.curOutputMode
382 | Public Properties (keep alphabetical)
383 |
384 | **Kind**: instance property of [Wifi
](#Wifi)
385 |
386 |
387 | ### wifi.bufferRawDataPackets(rawDataPackets) ⇒ Array
388 | This function is for redundancy after a long packet send, the wifi firmware can resend the same
389 | packet again, using this till add redundancy on poor networks.
390 |
391 | **Kind**: instance method of [Wifi
](#Wifi)
392 | **Author**: AJ Keller (@aj-ptw)
393 |
394 | | Param | Description |
395 | | --- | --- |
396 | | rawDataPackets | - |
397 |
398 |
399 |
400 | ### wifi.channelOff(channelNumber) ⇒ Promise.<T>
401 | Send a command to the board to turn a specified channel off
402 |
403 | **Kind**: instance method of [Wifi
](#Wifi)
404 | **Author**: AJ Keller (@aj-ptw)
405 |
406 | | Param |
407 | | --- |
408 | | channelNumber |
409 |
410 |
411 |
412 | ### wifi.channelOn(channelNumber) ⇒ Promise.<T>
\| \*
413 | Send a command to the board to turn a specified channel on
414 |
415 | **Kind**: instance method of [Wifi
](#Wifi)
416 | **Author**: AJ Keller (@aj-ptw)
417 |
418 | | Param |
419 | | --- |
420 | | channelNumber |
421 |
422 |
423 |
424 | ### wifi.channelSet(channelNumber, powerDown, gain, inputType, bias, srb2, srb1) ⇒ Promise
425 | To send a channel setting command to the board
426 |
427 | **Kind**: instance method of [Wifi
](#Wifi)
428 | **Returns**: Promise
- resolves if sent, rejects on bad input or no board
429 | **Author**: AJ Keller (@aj-ptw)
430 |
431 | | Param | Description |
432 | | --- | --- |
433 | | channelNumber | Number (1-16) |
434 | | powerDown | Bool (true -> OFF, false -> ON (default)) turns the channel on or off |
435 | | gain | Number (1,2,4,6,8,12,24(default)) sets the gain for the channel |
436 | | inputType | String (normal,shorted,biasMethod,mvdd,temp,testsig,biasDrp,biasDrn) selects the ADC channel input source |
437 | | bias | Bool (true -> Include in bias (default), false -> remove from bias) selects to include the channel input in bias generation |
438 | | srb2 | Bool (true -> Connect this input to SRB2 (default), false -> Disconnect this input from SRB2) Select to connect (true) this channel's P input to the SRB2 pin. This closes a switch between P input and SRB2 for the given channel, and allows the P input to also remain connected to the ADC. |
439 | | srb1 | Bool (true -> connect all N inputs to SRB1, false -> Disconnect all N inputs from SRB1 (default)) Select to connect (true) all channels' N inputs to SRB1. This effects all pins, and disconnects all N inputs from the ADC. |
440 |
441 |
442 |
443 | ### wifi.impedanceSet(channelNumber, pInputApplied, nInputApplied) ⇒ Promise
444 | To send an impedance setting command to the board
445 |
446 | **Kind**: instance method of [Wifi
](#Wifi)
447 | **Returns**: Promise
- resolves if sent, rejects on bad input or no board
448 | **Author**: AJ Keller (@aj-ptw)
449 |
450 | | Param | Type | Description |
451 | | --- | --- | --- |
452 | | channelNumber | Number
| (1-16) |
453 | | pInputApplied | Boolean
| (true -> ON, false -> OFF (default)) |
454 | | nInputApplied | Boolean
| (true -> ON, false -> OFF (default)) |
455 |
456 |
457 |
458 | ### wifi.connect(o) ⇒ Promise
459 | The essential precursor method to be called initially to establish a
460 | ble connection to the OpenBCI ganglion board.
461 |
462 | **Kind**: instance method of [Wifi
](#Wifi)
463 | **Returns**: Promise
- If the board was able to connect.
464 | **Author**: AJ Keller (@aj-ptw)
465 |
466 | | Param | Type | Description |
467 | | --- | --- | --- |
468 | | o | Object
| |
469 | | o.burst | Boolean
| Set this option true to have UDP do burst mode 3x |
470 | | o.examineMode | Boolean
| Set this option true to connect to the WiFi Shield even if there is no board attached. |
471 | | o.ipAddress | String
| The ip address of the shield if you know it |
472 | | o.latency | Number
| If you want to set the latency of the system you can here too. |
473 | | o.protocol | String
| Either send the data over TCP or UDP. UDP seems better for either a bad router or slow router. Default is TCP |
474 | | o.sampleRate | | The sample rate to set the board connected to the wifi shield |
475 | | o.shieldName | String
| If supplied, will search for a shield by this name, if not supplied, will connect to the first shield found. |
476 | | o.streamStart | Boolean
| Set `true` if you want the board to start streaming. |
477 |
478 |
479 |
480 | ### wifi.disconnect() ⇒ Promise
481 | Closes the connection to the board. Waits for stop streaming command to
482 | be sent if currently streaming.
483 |
484 | **Kind**: instance method of [Wifi
](#Wifi)
485 | **Returns**: Promise
- - fulfilled by a successful close, rejected otherwise.
486 | **Author**: AJ Keller (@aj-ptw)
487 |
488 |
489 | ### wifi.isConnected() ⇒ boolean
490 | Checks if the driver is connected to a board.
491 |
492 | **Kind**: instance method of [Wifi
](#Wifi)
493 | **Returns**: boolean
- - True if connected.
494 | **Author**: AJ Keller (@aj-ptw)
495 |
496 |
497 | ### wifi.isSearching() ⇒ boolean
498 | Checks if noble is currently scanning.
499 |
500 | **Kind**: instance method of [Wifi
](#Wifi)
501 | **Returns**: boolean
- - True if streaming.
502 | **Author**: AJ Keller (@aj-ptw)
503 |
504 |
505 | ### wifi.isStreaming() ⇒ boolean
506 | Checks if the board is currently sending samples.
507 |
508 | **Kind**: instance method of [Wifi
](#Wifi)
509 | **Returns**: boolean
- - True if streaming.
510 | **Author**: AJ Keller (@aj-ptw)
511 |
512 |
513 | ### wifi.getBoardType() ⇒ \*
514 | Get the current board type
515 |
516 | **Kind**: instance method of [Wifi
](#Wifi)
517 | **Author**: AJ Keller (@aj-ptw)
518 |
519 |
520 | ### wifi.getFirmwareVersion() ⇒ String
521 | Get the firmware version of connected and synced wifi shield.
522 |
523 | **Kind**: instance method of [Wifi
](#Wifi)
524 | **Returns**: String
- The version number
525 | Note: This is dependent on if you called connect
526 | **Author**: AJ Keller (@aj-ptw)
527 |
528 |
529 | ### wifi.getIpAddress() ⇒ null
\| String
530 | Return the ip address of the attached WiFi Shield device.
531 |
532 | **Kind**: instance method of [Wifi
](#Wifi)
533 | **Author**: AJ Keller (@aj-ptw)
534 |
535 |
536 | ### wifi.getLatency() ⇒ Number
537 | Return the latency to be set on the WiFi Shield.
538 |
539 | **Kind**: instance method of [Wifi
](#Wifi)
540 | **Author**: AJ Keller (@aj-ptw)
541 |
542 |
543 | ### wifi.getMacAddress() ⇒ null
\| String
544 | Return the MAC address of the attached WiFi Shield device.
545 |
546 | **Kind**: instance method of [Wifi
](#Wifi)
547 | **Author**: AJ Keller (@aj-ptw)
548 |
549 |
550 | ### wifi.getNumberOfChannels() ⇒ Number
551 | This function is used as a convenience method to determine how many
552 | channels the current board is using.
553 | Note: This is dependent on if your wifi shield is attached to another board and how many channels are there.
554 |
555 | **Kind**: instance method of [Wifi
](#Wifi)
556 | **Returns**: Number
- A number
557 | **Author**: AJ Keller (@aj-ptw)
558 |
559 |
560 | ### wifi.getSampleRate() ⇒ Number
561 | Get the the current sample rate is.
562 | Note: This is dependent on if you configured the board correctly on setup options
563 |
564 | **Kind**: instance method of [Wifi
](#Wifi)
565 | **Returns**: Number
- The sample rate
566 | **Author**: AJ Keller (@aj-ptw)
567 |
568 |
569 | ### wifi.getShieldName() ⇒ null
\| String
570 | Return the shield name of the attached WiFi Shield device.
571 |
572 | **Kind**: instance method of [Wifi
](#Wifi)
573 | **Author**: AJ Keller (@aj-ptw)
574 |
575 |
576 | ### wifi.impedanceStart() ⇒ global.Promise
\| Promise
577 | Call to start testing impedance.
578 |
579 | **Kind**: instance method of [Wifi
](#Wifi)
580 | **Author**: AJ Keller (@aj-ptw)
581 |
582 |
583 | ### wifi.impedanceStop() ⇒ global.Promise
\| Promise
584 | Call to stop testing impedance.
585 |
586 | **Kind**: instance method of [Wifi
](#Wifi)
587 | **Author**: AJ Keller (@aj-ptw)
588 |
589 |
590 | ### wifi.searchToStream(o) ⇒ Promise
591 | Used to search for an OpenBCI WiFi Shield. Will connect to the first one if no `shieldName` is supplied.
592 |
593 | **Kind**: instance method of [Wifi
](#Wifi)
594 | **Returns**: Promise
- - Resolves after successful connection, rejects otherwise with Error.
595 | **Author**: AJ Keller (@aj-ptw)
596 |
597 | | Param | Type | Description |
598 | | --- | --- | --- |
599 | | o | Object
| (optional) |
600 | | o.sampleRate | | The sample rate to set the board connected to the wifi shield |
601 | | o.shieldName | String
| If supplied, will search for a shield by this name, if not supplied, will connect to the first shield found. |
602 | | o.streamStart | Boolean
| Set `true` if you want the board to start streaming. |
603 | | o.timeout | Number
| The time in milli seconds to wait for the system to try and auto find and connect to the shield. |
604 |
605 |
606 |
607 | ### wifi.setSampleRate(sampleRate) ⇒ Promise
608 | Set the sample rate of the remote OpenBCI shield
609 |
610 | **Kind**: instance method of [Wifi
](#Wifi)
611 | **Author**: AJ Keller (@aj-ptw)
612 |
613 | | Param | Type | Description |
614 | | --- | --- | --- |
615 | | sampleRate | Number
| the sample rate you want to set to. |
616 |
617 |
618 |
619 | ### wifi.syncSampleRate() ⇒ Promise
620 | Returns the sample rate from the board
621 |
622 | **Kind**: instance method of [Wifi
](#Wifi)
623 | **Author**: AJ Keller (@aj-ptw)
624 |
625 |
626 | ### wifi.searchStart() ⇒ Promise
627 | List available peripherals so the user can choose a device when not
628 | automatically found.
629 |
630 | **Kind**: instance method of [Wifi
](#Wifi)
631 | **Returns**: Promise
- - If scan was started
632 | **Author**: AJ Keller (@aj-ptw)
633 |
634 |
635 | ### wifi.searchStop() ⇒ global.Promise
\| Promise
636 | Called to end a search.
637 |
638 | **Kind**: instance method of [Wifi
](#Wifi)
639 | **Author**: AJ Keller (@aj-ptw)
640 |
641 |
642 | ### wifi.sdStop() ⇒ Promise
643 | Sends the stop SD logging command to the board. If not streaming then `eot` event will be emitted
644 | with request response from the board.
645 |
646 | **Kind**: instance method of [Wifi
](#Wifi)
647 | **Returns**: Promise
- - Resolves when written
648 | **Author**: AJ Keller (@aj-ptw)
649 |
650 |
651 | ### wifi.syncRegisterSettings() ⇒ Promise.<T>
\| \*
652 | Syncs the internal channel settings object with a cyton, this will take about
653 | over a second because there are delays between the register reads in the firmware.
654 |
655 | **Kind**: instance method of [Wifi
](#Wifi)
656 | **Returns**: Promise.<T>
\| \*
- Resolved once synced, rejects on error or 2 second timeout
657 | **Author**: AJ Keller (@aj-ptw)
658 |
659 |
660 | ### wifi.softReset() ⇒ Promise
661 | Sends a soft reset command to the board
662 |
663 | **Kind**: instance method of [Wifi
](#Wifi)
664 | **Returns**: Promise
- - Fulfilled if the command was sent to board.
665 | **Author**: AJ Keller (@aj-ptw)
666 |
667 |
668 | ### wifi.eraseWifiCredentials() ⇒ Promise
669 | Tells the WiFi Shield to forget it's network credentials. This will cause the board to drop all
670 | connections.
671 |
672 | **Kind**: instance method of [Wifi
](#Wifi)
673 | **Returns**: Promise
- Resolves when WiFi Shield has been reset and the module disconnects.
674 | **Author**: AJ Keller (@aj-ptw)
675 |
676 |
677 | ### wifi.streamStart() ⇒ Promise
678 | Sends a start streaming command to the board.
679 |
680 | **Kind**: instance method of [Wifi
](#Wifi)
681 | **Returns**: Promise
- indicating if the signal was able to be sent.
682 | Note: You must have successfully connected to an OpenBCI board using the connect
683 | method. Just because the signal was able to be sent to the board, does not
684 | mean the board will start streaming.
685 | **Author**: AJ Keller (@aj-ptw)
686 |
687 |
688 | ### wifi.streamStop() ⇒ Promise
689 | Sends a stop streaming command to the board.
690 |
691 | **Kind**: instance method of [Wifi
](#Wifi)
692 | **Returns**: Promise
- indicating if the signal was able to be sent.
693 | Note: You must have successfully connected to an OpenBCI board using the connect
694 | method. Just because the signal was able to be sent to the board, does not
695 | mean the board stopped streaming.
696 | **Author**: AJ Keller (@aj-ptw)
697 |
698 |
699 | ### wifi.syncInfo(o) ⇒ Promise.<TResult>
700 | Sync the info of this wifi module
701 |
702 | **Kind**: instance method of [Wifi
](#Wifi)
703 | **Author**: AJ Keller (@aj-ptw)
704 |
705 | | Param | Type | Description |
706 | | --- | --- | --- |
707 | | o | Object
| |
708 | | o.examineMode | Boolean
| Set this option true to connect to the WiFi Shield even if there is no board attached. |
709 |
710 |
711 |
712 | ### wifi.write(data) ⇒ Promise
713 | Used to send data to the board.
714 |
715 | **Kind**: instance method of [Wifi
](#Wifi)
716 | **Returns**: Promise
- - fulfilled if command was able to be sent
717 | **Author**: AJ Keller (@aj-ptw)
718 |
719 | | Param | Type | Description |
720 | | --- | --- | --- |
721 | | data | Array
\| Buffer
\| Number
\| String
| The data to write out |
722 |
723 |
724 |
725 | ### wifi.destroy()
726 | Call this to shut down the servers.
727 |
728 | **Kind**: instance method of [Wifi
](#Wifi)
729 |
730 |
731 | ### wifi.wifiGetLocalPort() ⇒ number
732 | Get the local port number of either the TCP or UDP server. Based on `options.protocol` being set to
733 | either `udp` or `tcp`.
734 |
735 | **Kind**: instance method of [Wifi
](#Wifi)
736 | **Returns**: number
- The port number that was dynamically assigned to this module on startup.
737 |
738 |
739 | ### wifi.wifiGetLocalPortUDP() ⇒ number
740 | Get the local port number of the UDP server.
741 |
742 | **Kind**: instance method of [Wifi
](#Wifi)
743 | **Returns**: number
- The port number that was dynamically assigned to this module on startup.
744 |
745 |
746 | ### wifi.wifiGetLocalPortTCP() ⇒ number
747 | Get the local port number of the UDP server.
748 |
749 | **Kind**: instance method of [Wifi
](#Wifi)
750 | **Returns**: number
- The port number that was dynamically assigned to this module on startup.
751 |
752 |
753 | ### wifi.wifiInitServer()
754 | Initialization function that will start the TCP server and bind the UDP port.
755 |
756 | **Kind**: instance method of [Wifi
](#Wifi)
757 |
758 |
759 | ### wifi.delete(path) ⇒ Promise
760 | Send a delete message to the connected wifi shield.
761 |
762 | **Kind**: instance method of [Wifi
](#Wifi)
763 | **Returns**: Promise
- - Resolves if gets a response from the client/server, rejects with error
764 |
765 | | Param | Type | Description |
766 | | --- | --- | --- |
767 | | path | String
| the path/route to send the delete message to |
768 |
769 |
770 |
771 | ### wifi.get(path) ⇒ Promise
772 | Send a GET message to the connected wifi shield.
773 |
774 | **Kind**: instance method of [Wifi
](#Wifi)
775 | **Returns**: Promise
- - Resolves if gets/with a response from the client/server, rejects with error
776 |
777 | | Param | Type | Description |
778 | | --- | --- | --- |
779 | | path | String
| the path/route to send the GET message to |
780 |
781 |
782 |
783 | ### wifi.post(path, payload) ⇒ Promise
784 | Send a POST message to the connected wifi shield.
785 |
786 | **Kind**: instance method of [Wifi
](#Wifi)
787 | **Returns**: Promise
- - Resolves if gets a response from the client/server, rejects with error
788 |
789 | | Param | Type | Description |
790 | | --- | --- | --- |
791 | | path | String
| the path/route to send the POST message to |
792 | | payload | \*
| can really be anything but should be a JSON object. |
793 |
794 |
795 |
796 | ### Wifi~o
797 | Configuring Options
798 |
799 | **Kind**: inner property of [Wifi
](#Wifi)
800 |
801 |
802 | ## InitializationObject : Object
803 | **Kind**: global typedef
804 | **Properties**
805 |
806 | | Name | Type | Description |
807 | | --- | --- | --- |
808 | | attempts | Number
| The number of times to try and perform an SSDP search before quitting. (Default 10) |
809 | | burst | Boolean
| Only applies for UDP, but the wifi shield will send 3 of the same packets on UDP to increase the chance packets arrive to this module (Default false) |
810 | | debug | Boolean
| Print out a raw dump of bytes sent and received. (Default `false`) |
811 | | latency | Number
| The latency, or amount of time between packet sends, of the WiFi shield. The time is in micro seconds! |
812 | | protocol | String
| Either send the data over TCP or UDP. UDP seems better for either a bad router or slow router. Default is TCP |
813 | | sampleRate | Number
| The sample rate to set the board to. (Default is zero) |
814 | | sendCounts | Boolean
| Send integer raw counts instead of scaled floats. (Default `false`) |
815 | | verbose | Boolean
| Print out useful debugging events. (Default `false`) |
816 |
817 |
818 | [link_aj_keller]: https://github.com/aj-ptw
819 | [link_shop_wifi_shield]: https://shop.openbci.com/collections/frontpage/products/wifi-shield?variant=44534009550
820 | [link_shop_ganglion]: https://shop.openbci.com/collections/frontpage/products/pre-order-ganglion-board
821 | [link_shop_cyton]: https://shop.openbci.com/collections/frontpage/products/cyton-biosensing-board-8-channel
822 | [link_shop_cyton_daisy]: https://shop.openbci.com/collections/frontpage/products/cyton-daisy-biosensing-boards-16-channel
823 | [link_ptw]: https://www.pushtheworldllc.com
824 | [link_openbci]: http://www.openbci.com
825 | [link_mozwow]: http://mozillascience.github.io/working-open-workshop/index.html
826 | [link_wifi_get_streaming]: examples/getStreaming/getStreaming.js
827 | [link_openleaderscohort]: https://medium.com/@MozOpenLeaders
828 | [link_mozsci]: https://science.mozilla.org
829 |
--------------------------------------------------------------------------------
/ROADMAP.md:
--------------------------------------------------------------------------------
1 | # Roadmap
2 |
3 | ## OpenBCI WiFi NodeJS
4 |
5 | Make programming with OpenBCI reliable, easy, research grade and fun!
6 |
7 | ## Short term - what we're working on now
8 |
9 | - Unit tests!
10 |
11 | ## Medium term
12 |
13 | - Make this module work in the browser
14 | - Get the simulator added for the wifi shield
15 |
--------------------------------------------------------------------------------
/examples/getStreaming/getStreaming.js:
--------------------------------------------------------------------------------
1 | /* jslint es6 */
2 | /**
3 | * This is an example from the readme.md
4 | * On windows you should run with PowerShell not git bash.
5 | * Install
6 | * [nodejs](https://nodejs.org/en/)
7 | *
8 | * To run:
9 | * change directory to this file `cd examples/getStreaming`
10 | * do `npm install`
11 | * then `npm start`
12 | */
13 | 'use strict';
14 | const OBCIConsts = require('@openbci/utilities').constants;
15 | const OBCIWifi = require('../../openBCIWifi');
16 |
17 | const wifi = new OBCIWifi({
18 | debug: false, // Pretty print bytes
19 | verbose: true, // Verbose output
20 | sendCounts: false,
21 | latency: 16667,
22 | protocol: 'tcp' // or "udp"
23 | });
24 |
25 | let counter = 0;
26 | let sampleRateCounterInterval = null;
27 | let lastSampleNumber = 0;
28 | let MAX_SAMPLE_NUMBER = 255;
29 | let droppedPacketArray = [];
30 | let sampleRateArray = [];
31 | let droppedPackets = 0;
32 |
33 | const sum = (acc, cur) => acc + cur;
34 |
35 | const sampleFunc = (sample) => {
36 | try {
37 | // console.log(JSON.stringify(sample));
38 | if (sample.valid) {
39 | counter++;
40 | if (sampleRateCounterInterval === null) {
41 | sampleRateCounterInterval = setInterval(() => {
42 | droppedPacketArray.push(droppedPackets);
43 | sampleRateArray.push(counter);
44 |
45 | const dpSum = droppedPacketArray.reduce(sum, 0);
46 | const srSum = sampleRateArray.reduce(sum, 0);
47 |
48 | console.log(`SR: ${counter}`);
49 | console.log(`Dropped ${droppedPackets} packets`);
50 | console.log(`Dropped packet average: ${dpSum / droppedPacketArray.length}`);
51 | console.log(`Sample rate average: ${srSum / sampleRateArray.length}`);
52 |
53 | droppedPackets = 0;
54 | counter = 0;
55 | }, 1000);
56 | }
57 |
58 | let packetDiff = sample.sampleNumber - lastSampleNumber;
59 |
60 | if (packetDiff < 0) packetDiff += MAX_SAMPLE_NUMBER;
61 |
62 | if (packetDiff > 1) {
63 | console.log(`dropped ${packetDiff} packets | cur sn: ${sample.sampleNumber} | last sn: ${lastSampleNumber}`);
64 | droppedPackets += packetDiff;
65 | }
66 |
67 | lastSampleNumber = sample.sampleNumber;
68 | // console.log(JSON.stringify(sample));
69 | }
70 | } catch (err) {
71 | console.error(err);
72 | }
73 | };
74 |
75 | wifi.on(OBCIConsts.OBCIEmitterImpedance, (impedance) => {
76 | console.log(JSON.stringify(impedance));
77 | });
78 |
79 | wifi.on(OBCIConsts.OBCIEmitterSample, sampleFunc);
80 | // wifi.on(OBCIConsts.OBCIEmitterRawDataPacket, console.log);
81 |
82 | wifi.searchToStream({
83 | streamStart: true,
84 | sampleRate: 200
85 | })
86 | .then(() => {
87 | MAX_SAMPLE_NUMBER = wifi.getNumberOfChannels() === 4 ? 200 : 255;
88 | })
89 | .catch((err) => {
90 | console.error(err);
91 | process.exit(0);
92 | });
93 |
94 | function exitHandler (options, err) {
95 | if (options.cleanup) {
96 | if (options.verbose) console.log('clean');
97 | /** Do additional clean up here */
98 | if (wifi.isConnected()) wifi.disconnect().catch(console.log);
99 |
100 | wifi.removeAllListeners('rawDataPacket');
101 | wifi.removeAllListeners('sample');
102 | wifi.destroy();
103 |
104 | if (sampleRateCounterInterval) clearInterval(sampleRateCounterInterval);
105 | }
106 |
107 | if (err) console.error(err.stack);
108 |
109 | if (options.exit) {
110 | if (options.verbose) console.log('exit');
111 |
112 | if (wifi.isStreaming()) {
113 | const _t = setTimeout(() => {
114 | console.log('timeout');
115 | process.exit(0);
116 | }, 1000);
117 |
118 | wifi.streamStop()
119 | .then(() => {
120 | console.log('stream stopped');
121 | if (_t) clearTimeout(_t);
122 | process.exit(0);
123 | }).catch((err) => {
124 | console.error(err);
125 | process.exit(0);
126 | });
127 | } else {
128 | process.exit(0);
129 | }
130 | }
131 | }
132 |
133 | if (process.platform === 'win32') {
134 | const rl = require('readline').createInterface({
135 | input: process.stdin,
136 | output: process.stdout
137 | });
138 |
139 | rl.on('SIGINT', function () {
140 | process.emit('SIGINT');
141 | });
142 | }
143 |
144 | // Perform actions on exit
145 | process.on('exit', exitHandler.bind(null, {
146 | cleanup: true
147 | }));
148 |
149 | // Perform actions on SIGINT
150 | process.on('SIGINT', exitHandler.bind(null, {
151 | exit: true
152 | }));
153 |
154 | // Perform actions on uncaught exceptions
155 | process.on('uncaughtException', exitHandler.bind(null, {
156 | exit: true
157 | }));
158 |
--------------------------------------------------------------------------------
/examples/getStreamingWiFiDirect/getStreamingWiFiDirect.js:
--------------------------------------------------------------------------------
1 | /* jslint es6 */
2 | /**
3 | * This is an example from the readme.md
4 | * On windows you should run with PowerShell not git bash.
5 | * Install
6 | * [nodejs](https://nodejs.org/en/)
7 | *
8 | * To run:
9 | * change directory to this file `cd examples/debug`
10 | * do `npm install`
11 | * then `npm start`
12 | */
13 | 'use strict';
14 | const OpenBCIConsts = require('@openbci/utilities').constants;
15 | const OpenBCIWifi = require('../../openBCIWifi');
16 |
17 | const deviceAddr = '10.0.1.3';
18 |
19 | const wifi = new OpenBCIWifi({
20 | debug: false, // Pretty print bytes
21 | verbose: false, // Verbose output
22 | sendCounts: false,
23 | latency: 16667,
24 | protocol: 'tcp', // or "udp"
25 | burst: true
26 | });
27 |
28 | let counter = 0;
29 | let sampleRateCounterInterval = null;
30 | let lastSampleNumber = 0;
31 | let MAX_SAMPLE_NUMBER = 255;
32 | let droppedPacketArray = [];
33 | let sampleRateArray = [];
34 | let droppedPackets = 0;
35 |
36 | const sum = (acc, cur) => acc + cur;
37 |
38 | const sampleFunc = (sample) => {
39 | try {
40 | // console.log(JSON.stringify(sample));
41 | if (sample.valid) {
42 | counter++;
43 | if (sampleRateCounterInterval === null) {
44 | sampleRateCounterInterval = setInterval(() => {
45 | droppedPacketArray.push(droppedPackets);
46 | sampleRateArray.push(counter);
47 |
48 | const dpSum = droppedPacketArray.reduce(sum, 0);
49 | const srSum = sampleRateArray.reduce(sum, 0);
50 |
51 | console.log(`SR: ${counter}`);
52 | console.log(`Dropped ${droppedPackets} packets`);
53 | console.log(`Dropped packet average: ${dpSum / droppedPacketArray.length}`);
54 | console.log(`Sample rate average: ${srSum / sampleRateArray.length}`);
55 |
56 | droppedPackets = 0;
57 | counter = 0;
58 | }, 1000);
59 | }
60 |
61 | let packetDiff = sample.sampleNumber - lastSampleNumber;
62 |
63 | if (packetDiff < 0) packetDiff += MAX_SAMPLE_NUMBER;
64 |
65 | if (packetDiff > 1) {
66 | console.log(`dropped ${packetDiff} packets | cur sn: ${sample.sampleNumber} | last sn: ${lastSampleNumber}`);
67 | droppedPackets += packetDiff;
68 | }
69 |
70 | lastSampleNumber = sample.sampleNumber;
71 | // console.log(JSON.stringify(sample));
72 | }
73 | } catch (err) {
74 | console.log(err);
75 | }
76 | };
77 |
78 | wifi.on(OpenBCIConsts.OBCIEmitterImpedance, (impedance) => {
79 | console.log(JSON.stringify(impedance));
80 | });
81 |
82 | wifi.on(OpenBCIConsts.OBCIEmitterSample, sampleFunc);
83 | // wifi.on(OpenBCIConsts.OBCIEmitterRawDataPacket, console.log);
84 |
85 | wifi.connect({
86 | sampleRate: 200,
87 | streamStart: true,
88 | ipAddress: deviceAddr
89 | })
90 | .then(() => {
91 | MAX_SAMPLE_NUMBER = wifi.getNumberOfChannels() === 4 ? 200 : 255;
92 | })
93 | .catch((err) => {
94 | console.log(err);
95 | process.exit(0);
96 | });
97 |
98 | function exitHandler (options, err) {
99 | if (options.cleanup) {
100 | if (options.verbose) console.log('clean');
101 | /** Do additional clean up here */
102 | if (wifi.isConnected()) wifi.disconnect().catch(console.log);
103 |
104 | wifi.removeAllListeners('rawDataPacket');
105 | wifi.removeAllListeners('sample');
106 | wifi.destroy();
107 |
108 | if (sampleRateCounterInterval) clearInterval(sampleRateCounterInterval);
109 | }
110 |
111 | if (err) console.log(err.stack);
112 |
113 | if (options.exit) {
114 | if (options.verbose) console.log('exit');
115 |
116 | if (wifi.isStreaming()) {
117 | const _t = setTimeout(() => {
118 | console.log('timeout');
119 | process.exit(0);
120 | }, 1000);
121 |
122 | wifi.streamStop()
123 | .then(() => {
124 | console.log('stream stopped');
125 | if (_t) clearTimeout(_t);
126 | process.exit(0);
127 | }).catch((err) => {
128 | console.log(err);
129 | process.exit(0);
130 | });
131 | } else {
132 | process.exit(0);
133 | }
134 | }
135 | }
136 |
137 | if (process.platform === 'win32') {
138 | const rl = require('readline').createInterface({
139 | input: process.stdin,
140 | output: process.stdout
141 | });
142 |
143 | rl.on('SIGINT', function () {
144 | process.emit('SIGINT');
145 | });
146 | }
147 |
148 | // do something when app is closing
149 | process.on('exit', exitHandler.bind(null, {
150 | cleanup: true
151 | }));
152 |
153 | // catches ctrl+c event
154 | process.on('SIGINT', exitHandler.bind(null, {
155 | exit: true
156 | }));
157 |
158 | // catches uncaught exceptions
159 | process.on('uncaughtException', exitHandler.bind(null, {
160 | exit: true
161 | }));
162 |
--------------------------------------------------------------------------------
/examples/xstreamCSV/xstreamCSV.js:
--------------------------------------------------------------------------------
1 | /* jslint es6 */
2 | 'use strict';
3 | const fs = require('fs');
4 | const OBCIConst = require('@openbci/utilities').constants;
5 | const Wifi = require('../../openBCIWifi');
6 | const xs = require('xstream').Stream;
7 |
8 | // GLOBALS
9 | const deviceAddr = '10.0.1.3';
10 | let f_ = null; // WriteStream, assigned by wifi.connect({...})
11 |
12 | const wifi = new Wifi({
13 | debug: false,
14 | verbose: false,
15 | sendCounts: false,
16 | latency: 20000,
17 | protocol: 'tcp'
18 | });
19 |
20 | // PRODUCERS
21 | const OBCIDataStream = {
22 | start: (listener) => {
23 | wifi.on(OBCIConst.OBCIEmitterSample, (s) => {
24 | listener.next(s);
25 | });
26 | },
27 | stop: () => {
28 | console.log('OBCIDataStream stop');
29 | }
30 | };
31 | const data_ = xs.create(OBCIDataStream);
32 |
33 | // LISTENERS
34 | const CSVWriter = {
35 | next: (v) => {
36 | const row = [v.sampleNumber, v.timestamp, ...v.channelData, ...v.accelData, v.valid].join(',');
37 | f_.write(row + '\n');
38 | },
39 | error: (err) => console.error(err),
40 | complete: () => console.log('CSVWriter complete')
41 | };
42 |
43 | const StreamPrinter = {
44 | next: (v) => console.log(v),
45 | error: (e) => console.error(e),
46 | complete: () => console.log('StreamPrinter complete')
47 | };
48 |
49 | // APPLICATION CODE
50 | wifi.connect({
51 | ipAddress: deviceAddr,
52 | sampleRate: 200,
53 | streamStart: false
54 | }).then(() => {
55 | f_ = fs.createWriteStream(`${Date.now()}-${wifi.getShieldName()}-${wifi.getSampleRate()}hz.csv`);
56 | f_.write('sample,t,ch1,ch2,ch3,ch4,x,y,z,valid\n');
57 | wifi.streamStart().then(() => {
58 | data_.addListener(CSVWriter);
59 | data_.addListener(StreamPrinter);
60 | });
61 | });
62 |
63 | // HANDLERS
64 | const exitHandler = (opt, err) => {
65 | if (opt.cleanup) {
66 | console.log('Disconnect');
67 | if (wifi.isConnected()) wifi.disconnect().catch(console.log);
68 | wifi.removeAllListeners('rawDataPacket');
69 | wifi.removeAllListeners('sample');
70 | wifi.destroy();
71 | }
72 |
73 | if (err) console.error(err);
74 |
75 | if (wifi.isStreaming()) {
76 | const tOut = setTimeout(() => {
77 | console.log('Stream timed out');
78 | process.exit(0);
79 | }, 1000);
80 |
81 | wifi.streamStop()
82 | .then(() => {
83 | console.log('WiFi stream stop');
84 | if (tOut) clearTimeout(tOut);
85 | OBCIDataStream.stop();
86 | f_.end();
87 | process.exit(0);
88 | })
89 | .catch((err) => {
90 | console.error(err);
91 | process.exit(0);
92 | });
93 | } else {
94 | process.exit(0);
95 | }
96 | };
97 |
98 | process.on('exit', exitHandler.bind(null, { cleanup: true }));
99 | process.on('SIGINT', exitHandler.bind(null, { exit: true }));
100 | process.on('uncaughtException', exitHandler.bind(null, { exit: true }));
101 |
--------------------------------------------------------------------------------
/images/WiFi_front_product.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openbci-archive/OpenBCI_NodeJS_Wifi/d6cc78d2223e1e9a3578ab5e126f556ba1e7b455/images/WiFi_front_product.jpg
--------------------------------------------------------------------------------
/openBCIWifi.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 | const EventEmitter = require('events').EventEmitter;
3 | const _ = require('lodash');
4 | const util = require('util');
5 | // Local imports
6 | const OpenBCIUtilities = require('@openbci/utilities');
7 | const obciUtils = OpenBCIUtilities.utilities;
8 | const k = OpenBCIUtilities.constants;
9 | const obciDebug = OpenBCIUtilities.debug;
10 | const clone = require('clone');
11 | const os = require('os');
12 | const ip = require('ip');
13 | const Client = require('node-ssdp').Client;
14 | const net = require('net');
15 | const http = require('http');
16 | const bufferEqual = require('buffer-equal');
17 | const Buffer = require('safe-buffer').Buffer;
18 | const dgram = require('dgram');
19 | const isWin = process.platform === 'win32';
20 |
21 | const wifiOutputModeRaw = 'raw';
22 | const wifiOutputProtocolUDP = 'udp';
23 | const wifiOutputProtocolTCP = 'tcp';
24 | /**
25 | * Options object
26 | * @type {InitializationObject}
27 | * @private
28 | */
29 | const _options = {
30 | attempts: 10,
31 | burst: false,
32 | debug: false,
33 | latency: 10000,
34 | protocol: [wifiOutputProtocolTCP, wifiOutputProtocolUDP],
35 | sampleRate: 0,
36 | sendCounts: false,
37 | verbose: false
38 | };
39 |
40 | /**
41 | * @typedef {Object} InitializationObject
42 | * @property {Number} attempts - The number of times to try and perform an SSDP search before quitting. (Default 10)
43 | *
44 | * @property {Boolean} burst - Only applies for UDP, but the wifi shield will send 3 of the same packets on UDP to
45 | * increase the chance packets arrive to this module (Default false)
46 | *
47 | * @property {Boolean} debug - Print out a raw dump of bytes sent and received. (Default `false`)
48 | *
49 | * @property {Number} latency - The latency, or amount of time between packet sends, of the WiFi shield. The time is in
50 | * micro seconds!
51 | *
52 | * @property {String} protocol - Either send the data over TCP or UDP. UDP seems better for either a bad router or slow
53 | * router. Default is TCP
54 | *
55 | * @property {Number} sampleRate - The sample rate to set the board to. (Default is zero)
56 | *
57 | * @property {Boolean} sendCounts - Send integer raw counts instead of scaled floats.
58 | * (Default `false`)
59 | *
60 | * @property {Boolean} verbose - Print out useful debugging events. (Default `false`)
61 | *
62 | */
63 |
64 | /**
65 | * @description The initialization method to call first, before any other method.
66 | * @param options {InitializationObject} (optional) - Board optional configurations.
67 | * @constructor
68 | * @author AJ Keller (@aj-ptw)
69 | */
70 | function Wifi (options) {
71 | if (!(this instanceof Wifi)) {
72 | return new Wifi(options);
73 | }
74 |
75 | options = ((typeof options !== 'function') && options) || {};
76 | let opts = {};
77 |
78 | /** Configuring Options */
79 | let o;
80 | for (o in _options) {
81 | var userOption = (o in options) ? o : o.toLowerCase();
82 | var userValue = options[userOption];
83 | delete options[userOption];
84 |
85 | if (typeof _options[o] === 'object') {
86 | // an array specifying a list of choices
87 | // if the choice is not in the list, the first one is defaulted to
88 |
89 | if (_options[o].indexOf(userValue) !== -1) {
90 | opts[o] = userValue;
91 | } else {
92 | opts[o] = _options[o][0];
93 | }
94 | } else {
95 | // anything else takes the user value if provided, otherwise is a default
96 |
97 | if (userValue !== undefined) {
98 | opts[o] = userValue;
99 | } else {
100 | opts[o] = _options[o];
101 | }
102 | }
103 | }
104 |
105 | for (o in options) throw new Error('"' + o + '" is not a valid option');
106 |
107 | /**
108 | * @type {InitializationObject}
109 | */
110 | this.options = clone(opts);
111 |
112 | /**
113 | * @type {RawDataToSample}
114 | * @private
115 | */
116 | this._rawDataPacketToSample = k.rawDataToSampleObjectDefault(8);
117 | this._rawDataPacketToSample.scale = !this.options.sendCounts;
118 | this._rawDataPacketToSample.protocol = k.OBCIProtocolWifi;
119 | this._rawDataPacketToSample.verbose = this.options.verbose;
120 |
121 | /** Private Properties (keep alphabetical) */
122 |
123 | this._accelArray = [0, 0, 0];
124 | this._allInfo = null;
125 | this._boardConnected = false;
126 | this._boardInfo = null;
127 | this._boardType = k.OBCIBoardNone;
128 | this._connected = false;
129 | this._droppedPacketCounter = 0;
130 | this._firstPacket = true;
131 | this._ipAddress = null;
132 | this._info = null;
133 | this._latency = this.options.latency;
134 | this._lowerChannelsSampleObject = null;
135 | this._macAddress = null;
136 | this._multiPacketBuffer = null;
137 | this._numberOfChannels = 0;
138 | this._packetCounter = 0;
139 | this._peripheral = null;
140 | this._sampleRate = this.options.sampleRate;
141 | this._scanning = false;
142 | this._shieldName = null;
143 | this._shieldName = null;
144 | this._streaming = false;
145 | this._version = null;
146 | this._lastPacketArrival = 0;
147 |
148 | /** Public Properties (keep alphabetical) */
149 |
150 | this.curOutputMode = wifiOutputModeRaw;
151 | this.internalRawDataPackets = [];
152 | this.wifiShieldArray = [];
153 | this.wifiServerUDPPort = 0;
154 |
155 | /** Initializations */
156 |
157 | this.wifiInitServer();
158 | }
159 |
160 | // This allows us to use the emitter class freely outside of the module
161 | util.inherits(Wifi, EventEmitter);
162 |
163 | /**
164 | * This function is for redundancy after a long packet send, the wifi firmware can resend the same
165 | * packet again, using this till add redundancy on poor networks.
166 | * @param rawDataPackets -
167 | * @returns {Array}
168 | * @author AJ Keller (@aj-ptw)
169 | */
170 | Wifi.prototype.bufferRawDataPackets = function (rawDataPackets) {
171 | if (this.internalRawDataPackets.length === 0) {
172 | this.internalRawDataPackets = rawDataPackets;
173 | return [];
174 | } else {
175 | let out = [];
176 | let coldStorage = [];
177 | _.forEach(rawDataPackets, (newRDP) => {
178 | let found = false;
179 | _.forEach(this.internalRawDataPackets, (oldRDP) => {
180 | if (!found && bufferEqual(new Buffer(newRDP), new Buffer(oldRDP))) {
181 | out.push(oldRDP);
182 | found = true;
183 | }
184 | });
185 | if (!found) {
186 | coldStorage.push(newRDP);
187 | }
188 | });
189 | if (out.length === 0) {
190 | out = this.internalRawDataPackets;
191 | this.internalRawDataPackets = rawDataPackets;
192 | } else {
193 | this.internalRawDataPackets = coldStorage;
194 | }
195 | return out;
196 | }
197 | };
198 |
199 | /**
200 | * @description Send a command to the board to turn a specified channel off
201 | * @param channelNumber
202 | * @returns {Promise.