├── .gitignore ├── LICENSE ├── README.md ├── doc ├── custom_driver.md └── image │ └── gateway.png ├── example ├── ble-acc.js ├── ble.js ├── example-gps.js ├── example-sensor.js ├── example-temperature-get.js ├── example-temperature-humidity-listen.js ├── example-temperature-listen.js ├── future-sensor-2.js └── future-sensor.js ├── index.js ├── lib ├── index.js ├── middleware │ ├── average.js │ ├── filter.js │ ├── queue.js │ ├── websocket.js │ └── websocketServer.js ├── proto.js ├── sense.js └── sensor │ ├── README.md │ ├── driver │ ├── GP2Y1010AU0F │ │ └── index.js │ ├── dht │ │ ├── bin │ │ │ └── Beagle_GPIO_dht22 │ │ ├── index.js │ │ └── src │ │ │ ├── Beagle_GPIO.cc │ │ │ ├── Beagle_GPIO.hh │ │ │ ├── Beagle_GPIO_dht22.cc │ │ │ └── Makefile │ ├── digitalCO2 │ │ ├── X100.js │ │ └── index.js │ ├── digitalHumidity │ │ ├── HTU21D.js │ │ └── index.js │ ├── digitalLight │ │ ├── BH1750.js │ │ └── index.js │ ├── ds18b20 │ │ └── index.js │ ├── fireAlarm │ │ └── index.js │ ├── gps │ │ └── index.js │ ├── index.js │ ├── magneticSwitch │ │ └── index.js │ ├── mg811 │ │ └── index.js │ ├── motionDetector │ │ └── index.js │ ├── onoff.js │ ├── photocell │ │ └── index.js │ ├── powerSource │ │ └── index.js │ ├── powerSwitch │ │ └── index.js │ └── rgbLed │ │ └── index.js │ ├── driver_cfg.json │ ├── index.js │ └── network │ ├── index.js │ └── w1 │ └── index.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .DS_Store 3 | *.swp 4 | *.swo 5 | node_modules 6 | cscope.* 7 | *.log 8 | *.log.* 9 | dump.rdb 10 | client/.idea/* 11 | .idea/* 12 | tags 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2013 Daliworks Inc 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sensorjs 2 | 3 | This module handles all about sensor with powerful and familiar techs. 4 | In other words this sensorjs is not only sensor driver module but also framework like express. 5 | 6 | And it's working on linux boards such as the BeagleBone or Raspberry Pi. 7 | 8 | ![gateway](https://raw.github.com/daliworks/sensorjs/master/doc/image/gateway.png "gateway") 9 | 10 | [Here](https://github.com/daliworks/sensorjs/blob/master/doc/custom_driver.md) is the guide to create your own driver. 11 | [Here](https://github.com/daliworks/sensorjs/blob/master/lib/sensor/README.md) is currently available sensor driver list. 12 | 13 | See also [sensorjs-app](https://github.com/daliworks/sensorjs-app) 14 | 15 | ## Installation 16 | 17 | $ npm install sensorjs 18 | 19 | ## sensor.js URL scheme 20 | ### sensorjs://[{gateway}]/{sensor_network}[:{bus_id}]/{address}/{sensor_model}/{sensor_id}{&query_strings} 21 | - example 22 | - ```sensorjs:///i2c:1/33/dht33/22-000003a7f590``` 23 | - ```sensorjs:///gpio/22/singleled/r222&name=sensingLed``` 24 | - ```sensorjs:///ble/000A3A58F310/proximity/0A3A58F310-1``` 25 | 26 | ## Example 27 | ```js 28 | var connect = require('sensorjs'), 29 | sensorDriver = connect.sensor; 30 | 31 | var app = connect(). 32 | use(connect.filter({$between: [-50, 50]})). // filter: passing between -50 and 50 33 | use(connect.average(20 /*duration*/)). // reduce: values to an average every 20 sec. 34 | use(function (data, next) { // custom middleware 35 | if (Math.max.apply(null, data.queue) < data.value) { 36 | console.log('new record', data.value); 37 | } 38 | next(); 39 | }). 40 | use(connect.queue(100)). // buffering max # of 100. 41 | use('/w1/*/ds18b20', function (data, next) { 42 | if (data.value > 20) { 43 | console.log('getting hot:', data.value); 44 | } 45 | next(); 46 | }). 47 | use('/gpio/*/dht11', function (data, next) { 48 | if (data.value < 30) { 49 | console.log('getting dry:', data.value); 50 | } 51 | next(); 52 | }); 53 | // transport(mqtt, localStorage, websocket and etc) 54 | //use(connect.websocket('http://yourhost.com', 'temperature/{id}'/*topic*/)); 55 | ``` 56 | More examples are [here](https://github.com/daliworks/sensorjs/tree/master/example). 57 | 58 | ## Contributor 59 | 60 | [https://github.com/daliworks/sensorjs/graphs/contributors](https://github.com/daliworks/sensorjs/graphs/contributors) 61 | 62 | ## License 63 | 64 | (The MIT License) 65 | 66 | Copyright (c) 2013 [Daliworks Inc](http://www.daliworks.co.kr) 67 | 68 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 69 | 70 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 71 | 72 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 73 | 74 | 75 | [Facebook](https://www.facebook.com/groups/sensor.js) 76 | [Twitter](https://twitter.com/sensorjs) 77 | -------------------------------------------------------------------------------- /doc/custom_driver.md: -------------------------------------------------------------------------------- 1 | # Custom Sensor Driver 2 | 3 | This guide will introduce you to develop the custom sensor driver. 4 | 5 | ## Structure 6 | 7 | ```js 8 | . 9 | +-- sensor-app.js 10 | +-- node_modules 11 | +-- sensorjs 12 | +-- sensorjs-custom-driver 13 | +-- index.js (1) 14 | +-- driver 15 | | +-- customSensor 16 | | +-- index.js (2) 17 | | +-- customActuator 18 | | +-- index.js (3) 19 | +-- network 20 | | +-- customNetwork 21 | | +-- index.js (4) 22 | +-- package.json 23 | 24 | ``` 25 | 26 | Create a custom sensor driver as node module. An example of custom sensor driver is [sensorjs-ble](https://github.com/daliworks/sensorjs-ble). 27 | 28 | ## Sensor application 29 | 30 | The 'sensor-app.js' is the main application to utilize the sensorjs framework and some custom sensor drivers. The sensor app imports necessary modules and add packages of custom sensor drivers. It can discover and create sensors using the drivers. 31 | 32 | 1. Import modules (sensorjs, sensorjs-custom-driver) 33 | 2. Init the custom sensor driver (addSensorPackage) 34 | 3. If the custom sensor is discoverable, discover the sensors(devices) 35 | 4. Create a sensor using the sensor URL 36 | 5. Add event handlers for the 'data' or 'change' event 37 | 38 | Below is the example code. 39 | 40 | ```js 41 | 42 | var sensorDriver = require('sensorjs').sensor, 43 | customSensor = require('sensorjs-custom-driver'); 44 | 45 | sensorDriver.addSensorPackage(customSensor); 46 | 47 | sensorDriver.discover('customSensor', function (err, devices) { 48 | devices.forEach(function (device) { 49 | device.sensorUrls.forEach(function (sensorUrl) { 50 | var sensor, parsedSensorUrl, props; 51 | 52 | sensor = sensorDriver.createSensor(sensorUrl); 53 | 54 | parsedSensorUrl = sensorDriver.parseSensorUrl(sensorUrl); 55 | 56 | props = sensorDriver.getSensorProperties(parsedSensorUrl.model); 57 | 58 | if (props.onChange) { 59 | sensor.on('change', function (data) { 60 | console.log('[custom sensor] on change - ', data); 61 | }); 62 | 63 | sensor.listen('change'); 64 | } else { 65 | sensor.on('data', function(data) { 66 | console.log('[custom sensor] on data - ', data); 67 | }); 68 | 69 | sensor.listen(); 70 | } 71 | }); 72 | }); 73 | }); 74 | 75 | ``` 76 | 77 | ## Custom sensor driver 78 | 79 | The custom sensor driver consists of three parts. 80 | 81 | 1. Init file 82 | 2. Driver part 83 | 3. Network part 84 | 85 | ### 1. Init file (index.js) 86 | 87 | In the structure tree, (1) is the init file. It initializes the custom sensor driver and network. 88 | 89 | The init file returns object which has the initialization functions and the driver and network configurations. 90 | 91 | #### Exporting 92 | 93 | ```js 94 | 95 | module.exports = { 96 | networks: ['customNetwork'], 97 | drivers: { 98 | customSensor: ['customSensorModelA', 'customSensorModelB'], 99 | customActuator: ['customActuatorModel'] 100 | }, 101 | initNetworks: initNetworks, 102 | initDrivers: initDrivers 103 | }; 104 | 105 | ``` 106 | 107 | #### Initialization functions 108 | 109 | ```js 110 | 111 | function initDrivers() { 112 | var customSensor, customActuator; 113 | 114 | try { 115 | customSensor = require('./driver/customSensor'); 116 | customActuator = require('./driver/customActuator'); 117 | } catch(e) { } 118 | 119 | return { 120 | customSensor: customSensor, 121 | customActuator: customActuator 122 | }; 123 | } 124 | 125 | function initNetworks() { 126 | var customNetwork; 127 | 128 | try { 129 | customNetwork = require('./network/customNetwork'); 130 | } catch (e) { } 131 | 132 | return { 133 | customNetwork: customNetwork 134 | }; 135 | } 136 | 137 | ``` 138 | 139 | ### 2. Driver part 140 | 141 | In the structure tree, (2) and (3) are the driver part. The driver part has two directories which are customSensor and customActuator. 142 | 143 | #### Custom sensor driver (driver/customSensor/index.js) 144 | 145 | The custom sensor function constructor inherits Sensor from the sensorjs. 146 | 147 | Implement the commented parts with number(1~5) below. 148 | 149 | ```js 150 | 151 | var SensorLib = require('../../index'), 152 | Sensor = SensorLib.Sensor, 153 | util = require('util'); 154 | 155 | function CustomSensor(sensorInfo, options) { 156 | Sensor.call(this, sensorInfo, options); 157 | 158 | if (sensorInfo.model) { 159 | this.model = sensorInfo.model; 160 | } 161 | 162 | this.dataType = CustomSensor.properties.dataTypes[this.model][0]; 163 | } 164 | 165 | CustomSensor.properties = { 166 | supportedNetworks: ['customNetwork'], 167 | dataTypes: { 168 | customSensorModelA: ['customTypeA'], 169 | customSensorModelB: ['customTypeB'] 170 | }, 171 | onChange: { 172 | customSensorModelA: false, 173 | customSensorModelB: false 174 | }, 175 | discoverable: true, 176 | addressable: true, 177 | recommendedInterval: { 178 | customSensorModelA: 10000, 179 | customSensorModelB: 60000 180 | }, 181 | maxInstances: 7, 182 | maxRetries: 8, 183 | idTemplate: '{model}-{address}', 184 | models: ['customSensorModelA', 'customSensorModelB'], 185 | category: 'sensor' 186 | }; 187 | 188 | util.inherits(CustomSensor, Sensor); 189 | 190 | // 1. Custom function to get a sensor value 191 | function getSensorValue(cb) { 192 | var error, data; 193 | 194 | // Place codes here to get sensor value. 195 | 196 | return cb && cb(error, data); 197 | } 198 | 199 | // When the 'get' method of created sensor instance is called. 200 | CustomSensor.prototype._get = function (cb) { 201 | var self = this, 202 | rtn; 203 | 204 | getSensorValue(function (error, data) { 205 | if (error) { 206 | rtn = { status: 'error', id : self.id, message: error.toString() }; 207 | } else { 208 | rtn = { status: 'ok', id : self.id, result: {} }; 209 | rtn.result[self.dataType] = data; 210 | } 211 | 212 | if (cb) { 213 | return cb(rtn.message, rtn); 214 | } else { 215 | self.emit('data', rtn); 216 | return; 217 | } 218 | }); 219 | }; 220 | 221 | // 3. Custom function to watch the change of sensor value 222 | function watchSensorValue(cb) { 223 | var error, data; 224 | 225 | // Place codes here to watch sensor value (e.g. on, off) 226 | 227 | return cb && cb(error, data); 228 | } 229 | 230 | // 2. Implement _enableChange function to enable onChange event. 231 | // Remove _enableChange function if onChange event is not necessary 232 | CustomSensor.prototype._enableChange = function () { 233 | var self = this, 234 | rtn, 235 | previousRtn; 236 | 237 | this.enableChange = true; 238 | 239 | watchSensorValue(function (error, data) { 240 | if (error) { 241 | // 4. Place here error handling codes 242 | } else { 243 | rtn = { status: 'ok', id : self.id, result: {} }; 244 | rtn.result[self.dataType] = data; 245 | 246 | self.emit('change', rtn, previousRtn); 247 | 248 | previousRtn = rtn; 249 | } 250 | }); 251 | }; 252 | 253 | // When the 'clear' method of created sensor instance is called. 254 | CustomSensor.prototype._clear = function () { 255 | // 5. Place here the clearing codes. 256 | 257 | return; 258 | }; 259 | 260 | module.exports = CustomSensor; 261 | 262 | ``` 263 | 264 | The variable "sensorInfo" has the information of sensor URL. 265 | 266 | ```js 267 | sensorInfo = { 268 | device: { 269 | sensorNetwork: 'customNetwork', 270 | address: '11' 271 | }, 272 | model: 'customSensorModelA', 273 | id: '1234567890' 274 | } 275 | 276 | ``` 277 | 278 | The properties of sensor are used by sensor application and sensorjs framework. 279 | 280 | ```js 281 | supportedNetworks: array, // array of working networks 282 | dataTypes: object (w/ array) or array, // 'temperature', 'humidity', etc. 283 | onChange: object (w/ boolean) or boolean, // polling type sensor(not interrupt type) 284 | discoverable: boolean, // discoverable via 'customNetwork' network or not 285 | addressable: boolean, // addressable manually or not 286 | recommendedInterval: object (w/ milliseconds) or milliseconds, // recommended data gathering interval 287 | maxInstances: integer, // max # of sensors to be attached 288 | maxRetries: integer, // max # of retries on error 289 | idTemplate: string, // template to generate the sensor ID 290 | models: array, // array of sensor models 291 | category: string // sensor 292 | ``` 293 | 294 | #### Custom actuator driver (driver/customActuator/index.js) 295 | 296 | The custom actuator function constructor inherits Actuator from the sensorjs. 297 | 298 | Implement the commented parts with number(1~5) below. 299 | 300 | ```js 301 | 302 | var SensorLib = require('../../index'), 303 | Actuator = SensorLib.Actuator, 304 | util = require('util'); 305 | 306 | function CustomActuator(sensorInfo, options) { 307 | Actuator.call(this, sensorInfo, options); 308 | 309 | if (sensorInfo.model) { 310 | this.model = sensorInfo.model; 311 | } 312 | 313 | this.dataType = CustomActuator.properties.dataTypes[0]; 314 | } 315 | 316 | CustomActuator.properties = { 317 | supportedNetworks: ['customNetwork'], 318 | dataTypes: ['customTypeC'], 319 | discoverable: false, 320 | addressable: true, 321 | maxInstances: 1, 322 | idTemplate: '{model}-{address}', 323 | models: ['customActuatorModel'], 324 | commands: ['on', 'off'], // 1. Add more commands if necessary 325 | category: 'actuator' 326 | }; 327 | 328 | util.inherits(CustomActuator, Actuator); 329 | 330 | // 2. Implement 'on' function 331 | CustomActuator.prototype.on = function (options, cb) { 332 | var error, rtnMessage; 333 | 334 | // Place here to control the actuator with on command 335 | 336 | return cb && cb(error, rtnMessage); 337 | }; 338 | 339 | // 3. Implement 'off' function 340 | CustomActuator.prototype.off = function (options, cb) { 341 | var error, rtnMessage; 342 | 343 | // Place here to control the actuator with off command 344 | 345 | return cb && cb(error, rtnMessage); 346 | }; 347 | 348 | // 4. Implement more functions with additionalCommand 349 | CustomActuator.prototype.additionalCommand = function (options, cb) { 350 | var error, rtnMessage; 351 | 352 | // Place here to control the actuator with additionalCommand command 353 | 354 | return cb && cb(error, rtnMessage); 355 | }; 356 | 357 | // When the 'clear' method of created sensor instance is called. 358 | CustomActuator.prototype._clear = function () { 359 | // 5. Place here the clearing codes. 360 | 361 | return; 362 | }; 363 | 364 | module.exports = CustomActuator; 365 | 366 | ``` 367 | 368 | The properties of actuator are used by sensor application and sensorjs framework. 369 | 370 | ```js 371 | supportedNetworks: array, // array of working networks 372 | dataTypes: object (w/ array) or array, // 'powerSwitch', 'camera', etc. 373 | discoverable: boolean, // discoverable via 'customNetwork' network or not 374 | addressable: boolean, // addressable manually or not 375 | maxInstances: integer, // max # of actuators to be attached 376 | idTemplate: string, // template to generate the actuator ID 377 | models: array, // array of actuator models 378 | commands: array, // array of commands of actuator 379 | category: string // actuator 380 | ``` 381 | 382 | ### 3. Network part 383 | 384 | In the structure tree, (4) is the driver part. 385 | 386 | #### Custom network (network/customNetwork/index.js) 387 | 388 | The custom network function constructor inherits Network from the sensorjs. 389 | 390 | Implement the commented parts with number(1~4) below. 391 | 392 | ```js 393 | 394 | var sensorDriver = require('../../index'), 395 | Network = sensorDriver.Network, 396 | Device = sensorDriver.Device, 397 | util = require('util'), 398 | _ = require('lodash'), 399 | async = require('async'); 400 | 401 | // 1. Rename the network name 'customNetwork' 402 | function CustomNetwork(options) { 403 | Network.call(this, 'customNetwork', options); 404 | } 405 | 406 | util.inherits(CustomNetwork, Network); 407 | 408 | function template(str, tokens) { 409 | return str && str.replace(/\{(\w+)\}/g, function (x, key) { 410 | return tokens[key]; 411 | }); 412 | } 413 | 414 | // 2. Custom function to discover sensors (address) 415 | function discoverSensors(model, cb) { 416 | var error, addresses = []; 417 | 418 | // Place codes here to discover sensors and get array with addresses 419 | 420 | return cb && cb(error, addresses); 421 | } 422 | 423 | CustomNetwork.prototype.discover = function (driverOrModel, options, cb) { 424 | var self = this, 425 | founds = [], 426 | models, 427 | modelCount; 428 | 429 | if (typeof options === 'function') { 430 | cb = options; 431 | options = undefined; 432 | } 433 | 434 | // Get models from driverName or from model 435 | models = sensorDriver.getDriverConfig()[driverOrModel]; 436 | 437 | if (!models) { //find model 438 | if(_.findKey(sensorDriver.getDriverConfig(), function (models) { 439 | return _.contains(models, driverOrModel); 440 | })) { 441 | models = [driverOrModel]; 442 | } else { 443 | return cb && cb(new Error('model not found')); 444 | } 445 | } 446 | 447 | async.eachSeries(models, function(model, done) { 448 | discoverSensors(model, function (error, addresses) { 449 | _.forEach(addresses, function (address) { 450 | var props = sensorDriver.getSensorProperties(model), 451 | sensorId, 452 | device; 453 | 454 | // 3. sensorId must be globally unique. If not, use a different idTemplate such as '{model}-{macAddress}-{address}' in which 'macAddress' is the address of hosting machine(e.g. Beaglebone Black) 455 | sensorId = template(props.idTemplate, { model: model, address: address }); 456 | 457 | device = new Device(self, address, [{ id: sensorId, model: model }]); 458 | 459 | founds.push(device); 460 | 461 | self.emit('discovered', device); 462 | }); 463 | 464 | done(); 465 | }); 466 | }, 467 | function (error) { 468 | if (error) { 469 | // 4. Do someting to handle error 470 | } 471 | 472 | return cb && cb(error, founds); 473 | }); 474 | }; 475 | 476 | module.exports = new CustomNetwork(); 477 | 478 | 479 | ``` 480 | 481 | ## Additional information 482 | 483 | ### Choose the proper names 484 | 485 | Change the names of network, driver, model, data types, and etc. 486 | 487 | ``` 488 | - network('customNetwork') 489 | - driver('customSensor', 'customActuator') 490 | - model('customSensorModelA', 'customSensorModelB', 'customActuatorModel') 491 | - data types('customTypeA', 'customTypeB', 'customTypeC') 492 | ``` 493 | 494 | ### Node libraries (dependencies) 495 | 496 | ``` 497 | "async": "1.5.2", 498 | "lodash": "2.4.1", 499 | ``` 500 | -------------------------------------------------------------------------------- /doc/image/gateway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daliworks/sensorjs/07ae36740461c6c22ef44524e866c60de61d8aac/doc/image/gateway.png -------------------------------------------------------------------------------- /example/ble-acc.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // NOTE: sensorjs-ble is required (npm install sensorjs-ble) 4 | var connect = require('../'), 5 | sensorApp = connect.sensor, 6 | bleSensor = require('sensorjs-ble'), 7 | ble; 8 | 9 | sensorApp.addSensorPackage(bleSensor); 10 | ble = sensorApp.getNetwork('ble'); 11 | 12 | var app = connect(). 13 | use(function (data, next) { // custom middleware 14 | console.log('accelerometer=', data.value); 15 | next(); 16 | }). 17 | use(connect.queue(100)); // buffering max # of 100. 18 | 19 | ble.discover('sensorTagAcc'/* sensor driver name(or profile name) */, function (err, devices) { 20 | devices.forEach(function (device) { 21 | device.sensorUrls.forEach(function (sensorUrl) { 22 | app.listen(sensorApp.createSensor(sensorUrl)); 23 | }); 24 | }); 25 | }); 26 | -------------------------------------------------------------------------------- /example/ble.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // NOTE: sensorjs-ble is required (npm install sensorjs-ble) 4 | var connect = require('../'), 5 | sensorApp = connect.sensor, 6 | bleSensor = require('sensorjs-ble'), 7 | ble; 8 | 9 | sensorApp.addSensorPackage(bleSensor); 10 | ble = sensorApp.getNetwork('ble'); 11 | 12 | var app = connect(). 13 | use(connect.filter({$between: [-50, 50]})). // filter: passing between -50 and 50 14 | use(connect.average(20 /*duration*/)). // reduce: values to an average every 5 sec. 15 | use(function (data, next) { // custom middleware 16 | if (Math.max.apply(null, data.queue) < data.value) { 17 | console.log('new record', data.value); 18 | } 19 | next(); 20 | }). 21 | use(connect.queue(100)); // buffering max # of 100. 22 | // transport(mqtt, localStorage, websocket and etc) 23 | //use(connect.websocket('http://yourhost.com', 'temperature/{id}'/*topic*/)); 24 | 25 | ble.discover('sensorTagHum'/* sensor driver name(or profile name) */, function (err, devices) { 26 | devices.forEach(function (device) { 27 | device.sensorUrls.forEach(function (sensorUrl) { 28 | app.listen(sensorApp.createSensor(sensorUrl)); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /example/example-gps.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var sensorApp = require('../').sensor; 3 | 4 | var url = 'sensorjs:///uart/1/gps/gps1'; 5 | var loc = sensorApp.createSensor(url, {baudRate: 9600}); 6 | loc.listen(); 7 | loc.on('data', function (data) { 8 | console.log(url, data); 9 | }); 10 | -------------------------------------------------------------------------------- /example/example-sensor.js: -------------------------------------------------------------------------------- 1 | /* jshint strict: true */ 2 | /* global console, require */ 3 | 4 | var connect = require('../'), 5 | sensorApp = connect.sensor, 6 | exampleSensor = require('sensorjs-example-sensor'); 7 | 8 | sensorApp.addSensorPackage(exampleSensor); 9 | 10 | sensorApp.discover('exampleSensor', function (err, devices) { 11 | 'use strict'; 12 | 13 | console.log('discovered devices', devices, err); 14 | 15 | devices.forEach(function (device) { 16 | device.sensorUrls.forEach(function (sensorUrl) { 17 | var sensor, parsedSensorUrl, props; 18 | 19 | sensor = sensorApp.createSensor(sensorUrl); 20 | 21 | parsedSensorUrl = sensorApp.parseSensorUrl(sensorUrl); 22 | 23 | props = sensorApp.getSensorProperties(parsedSensorUrl.model); 24 | 25 | if (props.onChange) { 26 | sensor.on('change', function (data) { 27 | console.log('[[future sensor]] on change - data', data); 28 | }); 29 | 30 | sensor.listen('change'); 31 | } else { 32 | sensor.on('data', function(data) { 33 | console.log('[[future sensor]] sensor data', data); 34 | }); 35 | 36 | sensor.listen(); 37 | } 38 | }); 39 | }); 40 | }); -------------------------------------------------------------------------------- /example/example-temperature-get.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var sensorApp = require('../').sensor; 3 | 4 | sensorApp.discover('ds18b20'/* sensor driver name */, function (err, devices) { 5 | devices.forEach(function (device) { 6 | device.sensorUrls.forEach(function (sensorUrl) { 7 | //device.connect(); //auto 8 | var thermometer = sensorApp.createSensor(sensorUrl); 9 | thermometer.get(function (err, data) { 10 | if (!err) { 11 | console.log(data); 12 | } 13 | }); 14 | }); 15 | }); 16 | }); 17 | 18 | sensorApp.getNetwork('w1').discover(null/* sensor driver name */, function (err, devices) { 19 | devices.forEach(function (device) { 20 | device.sensorUrls.forEach(function (sensorUrl) { 21 | //device.connect(); //auto 22 | var thermometer = sensorApp.createSensor(sensorUrl); 23 | thermometer.get(function (err, data) { 24 | if (!err) { 25 | console.log(data); 26 | } 27 | }); 28 | }); 29 | }); 30 | }); 31 | 32 | var url = 'sensorjs:///w1/28-000003a7f590/ds18b20/28-000003a7f590'; 33 | var therm = sensorApp.createSensor(url); 34 | therm.get(function (err, data) { 35 | console.log(url, data); 36 | }); 37 | 38 | //url 39 | /** 40 | * sensorjs:///{sensor network}[:{bus_id}]/{addr}/{model}/{instance id} 41 | * sensorjs:///gpio/22/dht11 --> uuid: model + gatewayId 42 | * sensorjs:///i2c/222/dht22 --> uuid: model + gatewayId 43 | * sensorjs:///ble/1122334455/PXP --> uuid address+model 44 | * sensorjs:///w1/28-xxx/ds18b20 --> uuid : address 45 | * sensorjs:///uart/1ds18b20 --> uuid : address 46 | */ 47 | -------------------------------------------------------------------------------- /example/example-temperature-humidity-listen.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var connect = require('../'), 3 | sensorApp = connect.sensor; 4 | 5 | var app = connect(). 6 | use(connect.filter({$between: [-50, 50]})). // filter: passing between -50 and 50 7 | use(connect.average(20 /*duration*/)). // reduce: values to an average every 20 sec. 8 | use(function (data, next) { // custom middleware 9 | if (Math.max.apply(null, data.queue) < data.value) { 10 | console.log('new record', data.value); 11 | } 12 | next(); 13 | }). 14 | use(connect.queue(100)). // buffering max # of 100. 15 | use('/w1/*/ds18b20', function (data, next) { 16 | if (data.value > 20) { 17 | console.log('getting hot:', data.value); 18 | } 19 | next(); 20 | }). 21 | use('/gpio/*/dht11', function (data, next) { 22 | if (data.value < 30) { 23 | console.log('getting dry:', data.value); 24 | } 25 | next(); 26 | }); 27 | // transport(mqtt, localStorage, websocket and etc) 28 | //use(connect.websocket('http://yourhost.com', 'temperature/{id}'/*topic*/)); 29 | 30 | sensorApp.discover('ds18b20'/*sensor driver*/, function (err, devices) { 31 | devices.forEach(function (device) { 32 | device.sensorUrls.forEach(function(sensorUrl) { 33 | app.listen(sensorApp.createSensor(sensorUrl)); 34 | }); 35 | }); 36 | }); 37 | // gpio is not discoverable 38 | var dhtUrl = 'sensorjs:///gpio/22/dht11/dht11'; 39 | app.listen(sensorApp.createSensor(dhtUrl)); 40 | -------------------------------------------------------------------------------- /example/example-temperature-listen.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var connect = require('../'), 3 | sensorApp = connect.sensor; 4 | 5 | var app = connect(). 6 | use(connect.filter({$between: [-50, 50]})). // filter: passing between -50 and 50 7 | use(connect.average(20 /*duration*/)). // reduce: values to an average every 5 sec. 8 | use(function (data, next) { // custom middleware 9 | if (Math.max.apply(null, data.queue) < data.value) { 10 | console.log('new record', data.value); 11 | } 12 | next(); 13 | }). 14 | use(connect.queue(100)); // buffering max # of 100. 15 | // transport(mqtt, localStorage, websocket and etc) 16 | //use(connect.websocket('http://yourhost.com', 'temperature/{id}'/*topic*/)); 17 | 18 | sensorApp.discover('ds18b20'/* sensor driver name */, function (err, devices) { 19 | devices.forEach(function (device) { 20 | device.sensorUrls.forEach(function (sensorUrl) { 21 | var thermometer = sensorApp.createSensor(sensorUrl); 22 | 23 | // listen to sensor data for connecting 24 | app.listen(thermometer); 25 | }); 26 | }); 27 | }); 28 | -------------------------------------------------------------------------------- /example/future-sensor-2.js: -------------------------------------------------------------------------------- 1 | /* jshint strict: true */ 2 | /* global console, require */ 3 | 4 | // NOTE: sensorjs-futuretek is required (npm install sensorjs-futuretek) 5 | var connect = require('../'), 6 | sensorApp = connect.sensor, 7 | futureSensor = require('sensorjs-futuretek'); 8 | 9 | sensorApp.addSensorPackage(futureSensor); 10 | 11 | sensorApp.discover('futureSensor', function (err, devices) { 12 | 'use strict'; 13 | 14 | console.log('discovered devices', devices, err); 15 | 16 | devices.forEach(function (device) { 17 | device.sensorUrls.forEach(function (sensorUrl) { 18 | var sensor, parsedSensorUrl, props; 19 | 20 | sensor = sensorApp.createSensor(sensorUrl); 21 | 22 | parsedSensorUrl = sensorApp.parseSensorUrl(sensorUrl); 23 | 24 | props = sensorApp.getSensorProperties(parsedSensorUrl.model); 25 | 26 | if (props.onChange) { 27 | sensor.on('change', function (data) { 28 | console.log('[[future sensor]] on change - data', data); 29 | }); 30 | 31 | sensor.listen('change'); 32 | } else { 33 | sensor.on('data', function(data) { 34 | console.log('[[future sensor]] sensor data', data); 35 | }); 36 | 37 | sensor.listen(); 38 | } 39 | }); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /example/future-sensor.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | // NOTE: sensorjs-futuretek is required (npm install sensorjs-futuretek) 4 | var connect = require('../'), 5 | sensorApp = connect.sensor, 6 | futureSensor = require('sensorjs-futuretek'), 7 | futureNetwork; 8 | 9 | sensorApp.addSensorPackage(futureSensor); 10 | futureNetwork = sensorApp.getNetwork('future'); 11 | 12 | var app = connect(). 13 | use('/future/*/futureTemp',function (data, next) { // custom middleware 14 | console.log('Temp=', data.value); 15 | next(); 16 | }). 17 | use('/future/*/futureHumi',function (data, next) { // custom middleware 18 | console.log('Humi=', data.value); 19 | next(); 20 | }). 21 | use(connect.queue(100)); // buffering max # of 100. 22 | 23 | futureNetwork.discover(function (err, devices) { 24 | devices.forEach(function (device) { 25 | device.sensorUrls.forEach(function (sensorUrl) { 26 | app.listen(sensorApp.createSensor(sensorUrl)); 27 | }); 28 | }); 29 | }); 30 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports =require('./lib/sense'); 2 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Note: most of codes are from connect - https://github.com/senchalabs/connect 3 | */ 4 | -------------------------------------------------------------------------------- /lib/middleware/average.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var _ = require('lodash'); 3 | 4 | //duration in sec 5 | module.exports = function average(duration){ 6 | return function average(req, next){ 7 | var now = Date.now(), 8 | v; 9 | if (!req.average) { 10 | req.average = { 11 | queue: [], 12 | start: now, 13 | }; 14 | } 15 | if (req.average.queue.length > 0 && req.average.start < (now - duration*1000)) { 16 | //FIXME: ignore too old data? 17 | v = req.value; 18 | 19 | req.value = _.reduce(req.average.queue, function(sum, n){return sum + n;}) / 20 | req.average.queue.length; 21 | req.average.start = now; 22 | req.average.queue = []; 23 | return next(); 24 | } 25 | req.average.queue.push(req.value); 26 | return; 27 | }; 28 | }; 29 | -------------------------------------------------------------------------------- /lib/middleware/filter.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var _ = require('lodash'); 3 | require('underscore-query')(_); 4 | 5 | module.exports = function filter(options){ 6 | return function filter(req, next){ 7 | if (!_.isEmpty(_.query([req], {value: options}))) { 8 | next(); 9 | } 10 | }; 11 | }; 12 | -------------------------------------------------------------------------------- /lib/middleware/queue.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var _ = require('lodash'); 3 | require('underscore-query')(_); 4 | 5 | var KEYS = ['id', 'time', 'value', 'path']; 6 | 7 | module.exports = function queue(max){ 8 | return function queue(req, next){ 9 | var data = {}; 10 | if (!req.queue) { 11 | req.queue = []; 12 | } 13 | _.each(KEYS, function (k) { 14 | data[k] = req[k]; 15 | }); 16 | req.queue.unshift(data); 17 | if (req.queue >= max) { 18 | req.queue.splice(req.queue.length - 1, 1); 19 | } 20 | next(); 21 | }; 22 | }; 23 | -------------------------------------------------------------------------------- /lib/middleware/websocket.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var _ = require('lodash'); 3 | 4 | var KEYS = ['id', 'time', 'value', 'path']; 5 | 6 | module.exports = function websocket(url, topic){ 7 | var socket = require('socket.io-client')(url), 8 | connected = false; 9 | 10 | socket.on('connect', function(){ 11 | connected = true; 12 | socket.once('disconnect', function(){ 13 | connected = false; 14 | }); 15 | }); 16 | return function websocket(req, next){ 17 | var data = {}; 18 | 19 | _.each(KEYS, function (k) { 20 | data[k] = req[k]; 21 | }); 22 | if (connected) { 23 | try { socket.emit(topic, data); } catch (e) { } 24 | } 25 | next(); 26 | }; 27 | }; 28 | -------------------------------------------------------------------------------- /lib/middleware/websocketServer.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var _ = require('lodash'), 3 | logger = require('log4js').getLogger('Middleware'); 4 | 5 | var KEYS = ['id', 'type', 'time', 'value', 'path', 'status', 'message']; 6 | 7 | module.exports = function websocketServer(appServer, topic){ 8 | var io; 9 | 10 | if (appServer) { 11 | io = require('socket.io')(appServer); 12 | 13 | io.on('connection', function(socket){ 14 | logger.info('[WebsocketServer] socket is connected', socket.id); 15 | 16 | socket.once('disconnect', function(){ 17 | logger.info('[WebsocketServer] socket is disconnected', socket.id); 18 | }); 19 | }); 20 | } 21 | 22 | return function websocketServer(req, next){ 23 | var data = {}; 24 | 25 | if (io) { 26 | _.each(KEYS, function (k) { 27 | data[k] = req[k]; 28 | }); 29 | 30 | try { 31 | logger.debug('[WebsocketServer] emitting data', data); 32 | io.emit(topic || 'data', data); 33 | } catch (e) { 34 | logger.error('[WebsocketServer] error on emitting', e); 35 | } 36 | } 37 | 38 | next(); 39 | }; 40 | }; 41 | -------------------------------------------------------------------------------- /lib/proto.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var _ = require('lodash'); 3 | 4 | exports = module.exports = {}; 5 | var app = exports; 6 | 7 | app.use = function (route, fn) { 8 | if ('string' !== typeof route) { 9 | fn = route; 10 | route = '/'; 11 | } 12 | if ('function' === typeof fn) { 13 | this.stack.push({route: route, handle: fn}); 14 | } 15 | return this; 16 | }; 17 | 18 | app.handle = function (req, out) { 19 | var stack = this.stack, 20 | index = 0, 21 | path = req.path ? req.path : '/'; 22 | 23 | function next(err) { 24 | var layer = stack[index++]; 25 | 26 | if(!layer) { //done 27 | return out && out(err); 28 | } 29 | 30 | try { 31 | var pathArr = path && path.toLowerCase().split('/'), 32 | routeArr = layer.route !== '/' && layer.route.toLowerCase().split('/'); 33 | 34 | if (routeArr && !_.all(routeArr, function (route, idx) { 35 | return (route === '*' || route === pathArr[idx]); 36 | })) { 37 | return next(err); 38 | } 39 | var arity = layer.handle.length; 40 | if (err) { 41 | if (arity === 3) { 42 | layer.handle(err, req, next); 43 | } else { 44 | next(err); 45 | } 46 | } else if (arity < 3) { 47 | layer.handle(req, next); 48 | } else { 49 | next(); 50 | } 51 | } catch (e) { 52 | next(e); 53 | } 54 | } 55 | next(); 56 | }; 57 | 58 | app._handler = function(sensor, data, cache) { 59 | var info = sensor.info, 60 | req; 61 | 62 | req = { 63 | id: sensor.id, 64 | type: data.result && _.keys(data.result)[0], 65 | time: Date.now(), 66 | value: data.result && _.values(data.result)[0], 67 | status: data.status, 68 | message: data.message, 69 | path: ['', info.device.sensorNetwork, info.device.address, info.model, info.id].join('/') 70 | }; 71 | 72 | if (cache[req.id]) { //Note: id should be unique. 73 | req = _.assign(cache[req.id], req); 74 | } else { 75 | cache[req.id] = req; 76 | } 77 | 78 | this.handle(req, function (err) { 79 | if (err) { 80 | console.error('err', err); 81 | } 82 | }); 83 | }; 84 | 85 | app.listen = function (sensors) { 86 | var self = this; 87 | if (!_.isArray(sensors)) { 88 | sensors = [ sensors ]; 89 | } 90 | _.each(sensors, function (sensor) { 91 | var props = sensor.getProperties(); 92 | if (props && props.onChange) { 93 | sensor.listen('change'); 94 | sensor.on('change', function (data) { 95 | self._handler(this, data, self.cache); 96 | }); 97 | } else { 98 | sensor.listen(); 99 | sensor.on('data', function (data) { 100 | self._handler(this, data, self.cache); 101 | }); 102 | } 103 | }); 104 | }; 105 | -------------------------------------------------------------------------------- /lib/sense.js: -------------------------------------------------------------------------------- 1 | /*jshint camelcase: false*/ 2 | 'use strict'; 3 | var fs = require('fs'), 4 | path = require('path'), 5 | util = require('util'), 6 | _ = require('lodash'), 7 | EventEmitter = require('events').EventEmitter, 8 | proto = require('./proto'), 9 | sensor = require('./sensor'); 10 | 11 | function createSense() { 12 | function app(req, next){ app.handle(req, next); } 13 | 14 | util.inherits(app, EventEmitter); 15 | 16 | _.merge(app, proto); 17 | 18 | app.stack = []; 19 | app.cache = {}; 20 | 21 | for (var i = 0; i < arguments.length; ++i) { 22 | app.use(arguments[i]); 23 | } 24 | 25 | return app; 26 | } 27 | 28 | exports = module.exports = createSense; 29 | exports.proto = proto; 30 | exports.sensor = sensor; 31 | exports.middleware = {}; 32 | 33 | /** 34 | * Auto-load bundled middleware with getters. 35 | */ 36 | fs.readdirSync(__dirname + '/middleware').forEach(function(filename){ 37 | if (!/\.js$/.test(filename)) { return; } 38 | var name = path.basename(filename, '.js'); 39 | function load(){ return require('./middleware/' + name); } 40 | exports.middleware.__defineGetter__(name, load); 41 | exports.__defineGetter__(name, load); 42 | }); 43 | 44 | -------------------------------------------------------------------------------- /lib/sensor/README.md: -------------------------------------------------------------------------------- 1 | ## supported sensors 2 | All sensors and auctuators are tested on Beaglebone Black. 3 | 4 | ### Sensor setup 5 | * Requirement 6 | * Network (Wired or Wireless) connection 7 | 8 | * VDD 5V vs. SYS 5V 9 | * VDD_5V 10 | * 1A 11 | * VDD_5V is the main power supply from the DC input jack. So this voltage is not present when the board is only powered via USB 12 | * SYS_5V 13 | * 0.25A 14 | 15 | * Sensors 16 | 1. [ds18b20 (1-wire)](http://www.ermicro.com/blog/wp-content/uploads/2009/10/picaxe_11.jpg) 17 | : Temperature 18 | * VDD_3V3 19 | * DGND 20 | * GPIO 2 21 | * 4.7K Pull-Up Resistor (between VDD_3V3 and DGND) 22 | 2. [DHT11 Sensor V2](http://www.dfrobot.com/wiki/index.php/DHT11_Temperature_and_Humidity_Sensor_V2_SKU:_DFR0067) 23 | : Humidity 24 | * SYS_5V or VDD_5V 25 | * DGND 26 | * GPIO 27 27 | 3. [htu21d (I2C)](https://www.sparkfun.com/products/12064) 28 | : Humidity 29 | * VDD_3V3 30 | * DGND 31 | * I2C2_SCL 32 | * I2C2_SDA 33 | * Address: 0x40 34 | 4. [photocell (I2C)](http://stackoverflow.com/questions/10611294/reading-analog-in-on-beaglebone-avoiding-segmentation-fault-error) 35 | : Light 36 | * VDD_ADC 37 | * GDNA_ADC 38 | * AIN0 39 | * 10K Pull-Down Resistor (between AIN0 and DGND) / (sensor : between VDD_ADC and AIN0) 40 | 5. [BH1750FVI](http://www.dfrobot.com/index.php?route=product/product&product_id=531) 41 | : Light (digital) 42 | * VDD_3V3 43 | * DGND 44 | * I2C2_SCL 45 | * I2C2_SDA 46 | * (Optional) ADD : if ADD is HIGH then address will be changed to 0x5c from 0x23 47 | * Address: 0x23 (0x5C) 48 | 6. [magnetic switch] 49 | * DGND 50 | * GPIO 46 or 61 or 115 51 | 7. [motion detector] 52 | * SYS_5V or VDD_5V 53 | * DGND 54 | * GPIO 46 or 61 or 115 55 | 8. [noise detector] 56 | * SYS_5V or VDD_5V 57 | * DGND 58 | * GPIO 46 or 61 or 115 59 | 9. [RGB] 60 | * DGND 61 | * GPIO 67 62 | 10. [power switch](http://www.dfrobot.com/index.php?route=product/product&product_id=64) 63 | * SYS_5V or VDD_5V 64 | * DGND 65 | * GPIO 67 66 | 11. sensorTag 67 | * TI sensor tag over Bluetooth LE 68 | * Bluetooth LE network generic support is under way. 69 | 70 | 71 | ## Example 72 | 73 | This example code is to access sensors: ds18b20 and dht22/dht11. ds18b20 uses 1-wire network and dht22/dht11 uses GPIO port thru proprietry protocol. 74 | 75 | ```js 76 | var sensor = require('./sensor/'); 77 | 78 | var targets = [{ 79 | driverName: 'ds18b20', // temperature sensor over 1-wire sensor network 80 | options: {}, 81 | }, { 82 | driverName: 'dht', // temperature/humidity sensor over GPIO 83 | options: {model: 'dht22', macAddress: 'xxxxxx'}, 84 | }]; 85 | 86 | targets.forEach(function (target) { 87 | var props = sensor.getSensorProperties(target.driverName), 88 | sensorId; 89 | 90 | if (props.discoverable) { // discoverable over sensor network 91 | sensor.discover(props.supportedNetworks[0], function (err, foundIDs) { 92 | if (err) { 93 | console.error('err=', err.stack); 94 | } else { 95 | if (foundIDs.length > 0) { //discovered 96 | sensorId = foundIDs[0]; // pick 1st one from the discovered IDs 97 | var snsr = sensor.createSensor(target.driverName, sensorId, target.options); 98 | 99 | // listening sensor data at the recommended interval 100 | snsr.on('data', function (data) { 101 | console.log('data=', data); 102 | }); 103 | snsr.listen(props.recommendedInterval); 104 | } 105 | } 106 | }); 107 | } else { // not discoverable 108 | var snsr = sensor.createSensor(target.driverName, null/*sensorId*/, target.options); 109 | 110 | // listening sensor data at the recommended interval 111 | snsr.on('data', function (data) { 112 | console.log('data=', data); 113 | }); 114 | snsr.listen(props.recommendedInterval); 115 | } 116 | }); 117 | ``` 118 | 119 | ## sensor/actuator properties 120 | 121 | 122 | Ds18b20 example: 123 | ```js 124 | supportedNetworks: ['oneWire'], // working over 1-wire network 125 | sensorType: 'temperature', 126 | onChange: false, // polling type sensor(not interrupt type) 127 | discoverable: true, // discoverable thru 1-wire network 128 | recommendedInterval: 10000, // recommended data gathering interval 129 | maxInstances: 7, // max # of sensors to be attached 130 | model: 'ds18b20', 131 | maxRetries: 8 // max # of retries on error 132 | 133 | ``` 134 | ## sensor data format 135 | 136 | ### ok example 137 | 138 | ```json 139 | { 140 | status: 'ok', 141 | id: 'dht22-xxxxxx', 142 | result: { 143 | temperature: 26.9, 144 | humidity: 51.5 } , 145 | time: 1377605409847 // optional 146 | } 147 | ``` 148 | 149 | ### error example 150 | 151 | ``` 152 | { 153 | status: 'error', 154 | id: 'dht22-xxxxxx', 155 | message: 'Error: Command failed' 156 | } 157 | ``` 158 | -------------------------------------------------------------------------------- /lib/sensor/driver/GP2Y1010AU0F/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Sensor = require('../index').Sensor, 4 | util = require('util'), 5 | fs = require('fs'), 6 | _cached = {retries: 0}, 7 | logger = Sensor.getLogger(); 8 | 9 | var ocpPaths = [ 10 | '/sys/devices/ocp.2/helper.15', 11 | '/sys/devices/ocp.3/helper.16' 12 | ]; 13 | 14 | function getDensity(volMeasured) { 15 | var calcVoltage, dustDensity; 16 | 17 | // 0 ~ 1800(1.8V ADC volt of Beaglebone black) mapped to 0 ~ 1024 integer valeus 18 | calcVoltage = volMeasured * 1024 / 1800; 19 | 20 | // linear eqaution taken from http://www.howmuchsnow.com/arduino/airquality/ 21 | // Chris Nafis (c) 2012 22 | dustDensity = 0.17 * calcVoltage - 0.1; 23 | 24 | return Math.round(dustDensity); 25 | } 26 | 27 | function GP2Y1010AU0F(sensorInfo, options) { 28 | Sensor.call(this, sensorInfo, options); 29 | } 30 | 31 | GP2Y1010AU0F.properties = { 32 | supportedNetworks: ['analog'], 33 | dataTypes: ['dust'], 34 | onChange: false, 35 | discoverable: false, 36 | addressable: true, 37 | recommendedInterval: 60000, 38 | maxInstances: 1, 39 | model: 'GP2Y1010AU0F', 40 | idTemplate: '{model}-{gatewayId}-{deviceAddress}', 41 | maxRetries: 1, 42 | address: 1, 43 | category: 'sensor' 44 | }; 45 | 46 | util.inherits(GP2Y1010AU0F, Sensor); 47 | 48 | /* 49 | * Get the dust of a given sensor 50 | * @param sensor : The sensor ID 51 | * @param callback : callback (err, {id, value}) 52 | */ 53 | GP2Y1010AU0F.prototype._get = function () { 54 | var self = this, rtn; 55 | 56 | if (!_cached.ocpPath) { 57 | ocpPaths.forEach(function (ocpPath) { 58 | if (fs.existsSync(ocpPath)) { 59 | _cached.ocpPath = ocpPath; 60 | return false; 61 | } 62 | }); 63 | } 64 | 65 | fs.readFile(_cached.ocpPath + '/AIN' + (this.info.address || GP2Y1010AU0F.properties.address), 66 | 'utf8', function (err, data) { 67 | var density; 68 | 69 | if (err) { 70 | rtn = {status: 'off', id : self.id, message: err.toString()}; 71 | } else { 72 | density = getDensity(parseInt(data, 10)); 73 | 74 | rtn = {status: 'ok', id : self.id, result: {'dust': density}}; 75 | } 76 | 77 | logger.info('[GP2Y1010AU0F-Dust] data, rtn, info.address, properties.address, ocpPath - ', 78 | data, rtn, self.info.address, GP2Y1010AU0F.properties.address, _cached.ocpPath); 79 | 80 | self.emit('data', rtn); 81 | }); 82 | }; 83 | 84 | module.exports = GP2Y1010AU0F; 85 | -------------------------------------------------------------------------------- /lib/sensor/driver/dht/bin/Beagle_GPIO_dht22: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daliworks/sensorjs/07ae36740461c6c22ef44524e866c60de61d8aac/lib/sensor/driver/dht/bin/Beagle_GPIO_dht22 -------------------------------------------------------------------------------- /lib/sensor/driver/dht/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Sensor = require('../index').Sensor, 3 | util = require('util'), 4 | childProcess = require('child_process'); 5 | 6 | var _cached = { retries: 0}, 7 | inProgress = false, 8 | logger = Sensor.getLogger(); 9 | 10 | // options.model : dht11(default) or dht22 11 | function Dht(sensorInfo, options) { 12 | Sensor.call(this, sensorInfo, options); 13 | if (sensorInfo.device.address) { 14 | this.address = sensorInfo.device.address; 15 | } 16 | if (sensorInfo.model) { 17 | this.model = sensorInfo.model; 18 | } else { 19 | this.model = (options && options.model === 'dht22') ? 'dht22' : 'dht11'; 20 | } 21 | } 22 | 23 | Dht.properties = { 24 | supportedNetworks: ['gpio'], 25 | dataTypes: ['humidity'], 26 | onChange: false, 27 | discoverable: false, 28 | addressable: true, 29 | recommendedInterval: 60000, 30 | validCachedValueTimeout: 25000, 31 | maxInstances: 1, 32 | models: ['dht11', 'dht22'], 33 | idTemplate: '{model}-{gatewayId}-{deviceAddress}', // id generation template 34 | errorRange: '1', 35 | validValueRange: [0, 100], 36 | maxRetries: 4, 37 | category: 'sensor' 38 | }; 39 | 40 | 41 | util.inherits(Dht, Sensor); 42 | 43 | Dht.prototype._get = function () { 44 | var self = this; 45 | setTimeout(self.__get(), 3000); // delay 1sec to avoid busy situation 46 | }; 47 | 48 | Dht.prototype.__get = function () { 49 | var self = this; 50 | 51 | //if (_cached.time && _cached.time + Dht.properties.validCachedValueTimeout > Date.now()) { 52 | // _cached.data.time = _cached.time; 53 | // self.emit('data', _cached.data); 54 | //} 55 | if (inProgress) { 56 | return; 57 | } 58 | inProgress = true; 59 | childProcess.exec([ 60 | __dirname + '/bin/Beagle_GPIO_dht22', 61 | '-s', 62 | self.model, 63 | '-g', 64 | self.address || '27', 65 | ].join(' '), { 66 | timeout: Dht.properties.recommendedInterval, 67 | killSignal: 'SIGKILL' 68 | }, function (err, data) { 69 | 70 | var retry = false, 71 | rtn; 72 | 73 | inProgress = false; 74 | if (err) { 75 | rtn = {status: 'error', id : self.id, message: err.toString()}; 76 | _cached.retries = 0; 77 | self.emit('data', rtn); 78 | } else { 79 | rtn = JSON.parse(data); 80 | } 81 | 82 | rtn.id = self.id; 83 | if (rtn.status === 'ok') { 84 | var now = Date.now(); 85 | if (!_cached.time) { // 1st time init 86 | _cached.retries = 0; 87 | retry = true; 88 | } else { 89 | var cacheExpire = _cached.time + Dht.properties.validCachedValueTimeout * Dht.properties.maxRetries, 90 | diff = Math.abs(rtn.result.humidity - _cached.data.result.humidity); 91 | 92 | // retry if cached value is too old or outof accepted error range 93 | if ((now > cacheExpire) || (diff > Dht.properties.errorRange)) { 94 | retry = true; 95 | } 96 | } 97 | _cached.time = now; 98 | _cached.data = rtn; 99 | } else { // rtn.satus === error 100 | retry = true; 101 | } 102 | 103 | if (retry) { 104 | logger.debug(self.model, 'retry _cached=', _cached, 'due to result=', rtn); 105 | if (_cached.retries > Dht.properties.maxRetries) { 106 | rtn = {status: 'error', id : self.id, 107 | message: 'too many retries due to invalid sensing value value'}; 108 | } else { 109 | _cached.retries++; 110 | process.nextTick(function () {self.__get(); }); 111 | return; 112 | } 113 | } 114 | 115 | _cached.retries = 0; 116 | if (rtn.result) { //keep only humidity, remove temperature 117 | delete rtn.result.temperature; 118 | } 119 | self.emit('data', rtn); 120 | }); 121 | }; 122 | 123 | module.exports = Dht; 124 | -------------------------------------------------------------------------------- /lib/sensor/driver/dht/src/Beagle_GPIO.cc: -------------------------------------------------------------------------------- 1 | /****************************** 2 | ** Beagle Bone GPIO Library ** 3 | ** ** 4 | ** Francois Sugny ** 5 | ** 01/07/12 ** 6 | ** ** 7 | ** v1.0 ** 8 | ******************************/ 9 | 10 | //======================================================= 11 | //======================================================= 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | //======================================================= 22 | //======================================================= 23 | 24 | #include "Beagle_GPIO.hh" 25 | 26 | //======================================================= 27 | //======================================================= 28 | 29 | const int Beagle_GPIO::GPIO_Pin_Bank[] = 30 | { 31 | -1, -1, 1, 1, 1, // P8_1 -> P8_5 32 | 1, 2, 2, 2, 2, // P8_6 -> P8_10 33 | 1, 1, 0, 0, 1, // P8_11 -> P8_15 34 | 1, 0, 2, 0, 1, // P8_16 -> P8_20 35 | 1, 1, 1, 1, 1, // P8_21 -> P8_25 36 | 1, 2, 2, 2, 2, // P8_26 -> P8_30 37 | 0, 0, 0, 2, 0, // P8_31 -> P9_35 38 | 2, 2, 2, 2, 2, // P8_36 -> P8_40 39 | 2, 2, 2, 2, 2, // P8_41 -> P8_45 40 | 2, // P8_46 41 | -1, -1, -1, -1, -1, // P9_1 -> P9_5 42 | -1, -1, -1, -1, -1, // P9_6 -> P9_10 43 | 0, 1, 0, 1, 1, // P9_11 -> P9_15 44 | 1, 0, 0, 0, 0, // P9_16 -> P9_20 45 | 0, 0, 1, 0, 3, // P9_21 -> P9_25 46 | 0, 3, 3, 3, 3, // P9_26 -> P9_30 47 | 3, -1, -1, -1, -1, // P9_31 -> P9_35 48 | -1, -1, -1, -1, -1, // P9_36 -> P9_40 49 | 0, 0, -1, -1, -1, // P9_41 -> P9_45 50 | -1 // P9_46 51 | }; 52 | 53 | //======================================================= 54 | //======================================================= 55 | 56 | const int Beagle_GPIO::GPIO_Pin_Id[] = 57 | { 58 | -1, -1, 6, 7, 2, // P8_1 -> P8_5 59 | 3, 2, 3, 5, 4, // P8_6 -> P8_10 60 | 13, 12, 23, 26, 15, // P8_11 -> P8_15 61 | 14, 27, 1, 22, 31, // P8_16 -> P8_20 62 | 30, 5, 4, 1, 0, // P8_21 -> P8_25 63 | 29, 22, 24, 23, 25, // P8_26 -> P8_30 64 | 10, 11, 9, 17, 8, // P8_31 -> P9_35 65 | 16, 14, 15, 12, 13, // P8_36 -> P8_40 66 | 10, 11, 8, 9, 6, // P8_41 -> P8_45 67 | 7, // P8_46 68 | -1, -1, -1, -1, -1, // P9_1 -> P9_5 69 | -1, -1, -1, -1, -1, // P9_6 -> P9_10 70 | 30, 28, 31, 18, 16, // P9_11 -> P9_15 71 | 19, 5, 4, 13, 12, // P9_16 -> P9_20 72 | 3, 2, 17, 15, 21, // P9_21 -> P9_25 73 | 14, 19, 17, 15, 16, // P9_26 -> P9_30 74 | 14, -1, -1, -1, -1, // P9_31 -> P9_35 75 | -1, -1, -1, -1, -1, // P9_36 -> P9_40 76 | 20, 7, -1, -1, -1, // P9_41 -> P9_45 77 | -1 // P9_46 78 | }; 79 | 80 | //======================================================= 81 | //======================================================= 82 | 83 | // Pad Control Register 84 | const unsigned long Beagle_GPIO::GPIO_Pad_Control[] = 85 | { 86 | 0x0000, 0x0000, 0x0818, 0x081C, 0x0808, // P8_1 -> P8_5 87 | 0x080C, 0x0890, 0x0894, 0x089C, 0x0898, // P8_6 -> P8_10 88 | 0x0834, 0x0830, 0x0824, 0x0828, 0x083C, // P8_11 -> P8_15 89 | 0x0838, 0x082C, 0x088C, 0x0820, 0x0884, // P8_16 -> P8_20 90 | 0x0880, 0x0814, 0x0810, 0x0804, 0x0800, // P8_21 -> P8_25 91 | 0x087C, 0x08E0, 0x08E8, 0x08E4, 0x08EC, // P8_26 -> P8_30 92 | 0x08D8, 0x08DC, 0x08D4, 0x08CC, 0x08D0, // P8_31 -> P8_35 93 | 0x08C8, 0x08C0, 0x08C4, 0x08B8, 0x08BC, // P8_36 -> P8_40 94 | 0x08B0, 0x08B4, 0x08A8, 0x08AC, 0x08A0, // P8_41 -> P8_45 95 | 0x08A4, // P8_46 96 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // P9_1 -> P9_5 97 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // P9_6 -> P9_10 98 | 0x0870, 0x0878, 0x0874, 0x0848, 0x0840, // P9_11 -> P9_15 99 | 0x084C, 0x095C, 0x0958, 0x097C, 0x0978, // P9_16 -> P9_20 100 | 0x0954, 0x0950, 0x0844, 0x0984, 0x09AC, // P9_21 -> P9_25 101 | 0x0980, 0x09A4, 0x099C, 0x0994, 0x0998, // P9_26 -> P9_30 102 | 0x0990, 0x0000, 0x0000, 0x0000, 0x0000, // P9_31 -> P9_35 103 | 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, // P9_36 -> P9_40 104 | 0x09B4, 0x0964, 0x0000, 0x0000, 0x0000, // P9_41 -> P9_45 105 | 0x0000 // P9_46 106 | }; 107 | 108 | //======================================================= 109 | //======================================================= 110 | 111 | const unsigned long Beagle_GPIO::GPIO_Control_Module_Registers = 0x44E10000; 112 | 113 | //======================================================= 114 | //======================================================= 115 | 116 | const unsigned long Beagle_GPIO::GPIO_Base[] = 117 | { 118 | 0x44E07000, // GPIO0 119 | 0x4804C000, // GPIO1 120 | 0x481AC000, // GPIO2 121 | 0x481AE000 // GPIO3 122 | }; 123 | 124 | //======================================================= 125 | //======================================================= 126 | 127 | Beagle_GPIO::Beagle_GPIO() 128 | { 129 | GPIO_PRINT( "Beagle_GPIO::Beagle_GPIO()" ); 130 | 131 | // Not initialized by default 132 | m_active = false; 133 | 134 | // Opening /dev/mem first 135 | GPIO_PRINT( "Opening /dev/mem" ); 136 | m_gpio_fd = open( "/dev/mem", O_RDWR | O_SYNC ); 137 | if ( m_gpio_fd < 0 ) 138 | { 139 | GPIO_ERROR( "Cannot open /dev/mem" ); 140 | return; 141 | } 142 | 143 | // Map Control Module 144 | m_controlModule = (unsigned long *)mmap( NULL, 0x1FFF, PROT_READ | PROT_WRITE, MAP_SHARED, m_gpio_fd, GPIO_Control_Module_Registers ); 145 | if ( m_controlModule == MAP_FAILED ) 146 | { 147 | GPIO_ERROR( "Control Module Mapping failed" ); 148 | return; 149 | } 150 | 151 | // Now mapping the GPIO registers 152 | for ( int i=0; i<4; ++i) 153 | { 154 | // Map a GPIO bank 155 | m_gpio[i] = (unsigned long *)mmap( NULL, 0xFFF, PROT_READ | PROT_WRITE, MAP_SHARED, m_gpio_fd, GPIO_Base[i] ); 156 | if ( m_gpio[i] == MAP_FAILED ) 157 | { 158 | GPIO_ERROR( "GPIO Mapping failed for GPIO Module " << i ); 159 | return; 160 | } 161 | } 162 | 163 | // Init complete and successfull 164 | m_active = true; 165 | 166 | GPIO_PRINT( "Beagle GPIO Initialized" ); 167 | } 168 | 169 | //======================================================= 170 | //======================================================= 171 | 172 | Beagle_GPIO::~Beagle_GPIO() 173 | { 174 | //GPIO_PRINT( "BeAGLe_GPIO::~Beagle_GPIO()" ); 175 | if ( m_active && m_gpio_fd) 176 | close( m_gpio_fd ); 177 | } 178 | 179 | //======================================================= 180 | //======================================================= 181 | 182 | // Configure pin as input/output 183 | Beagle_GPIO::Beagle_GPIO_Status Beagle_GPIO::configurePin( unsigned short _pin, Beagle_GPIO_Direction _direction ) 184 | { 185 | if ( !m_active ) 186 | return kFail; 187 | 188 | assert(GPIO_Pin_Bank[_pin]>=0); 189 | assert(GPIO_Pin_Id[_pin]>=0); 190 | 191 | // Set Pin as GPIO on the pad control 192 | m_controlModule[GPIO_Pad_Control[_pin]/4] |= 0x07; 193 | 194 | unsigned long v = 0x1 << GPIO_Pin_Id[_pin]; 195 | 196 | if ( _direction == kINPUT) 197 | { 198 | m_gpio[GPIO_Pin_Bank[_pin]][kOE/4] |= v; 199 | } 200 | else 201 | { 202 | m_gpio[GPIO_Pin_Bank[_pin]][kOE/4] &= ~v; 203 | } 204 | 205 | // Disable Interrupts by default 206 | m_gpio[GPIO_Pin_Bank[_pin]][kIRQSTATUS_CLR_0/4] |= v; 207 | m_gpio[GPIO_Pin_Bank[_pin]][kIRQSTATUS_CLR_1/4] |= v; 208 | 209 | return kSuccess; 210 | } 211 | 212 | //======================================================= 213 | //======================================================= 214 | 215 | // Enable/Disable interrupts for the pin 216 | Beagle_GPIO::Beagle_GPIO_Status Beagle_GPIO::enablePinInterrupts( unsigned short _pin, bool _enable ) 217 | { 218 | if ( !m_active ) 219 | return kFail; 220 | 221 | assert(GPIO_Pin_Bank[_pin]>=0); 222 | assert(GPIO_Pin_Id[_pin]>=0); 223 | 224 | // Set Pin as GPIO on the pad control 225 | m_controlModule[GPIO_Pad_Control[_pin]/4] |= 0x07; 226 | 227 | unsigned long v = 0x1 << GPIO_Pin_Id[_pin]; 228 | 229 | if ( _enable ) 230 | { 231 | m_gpio[GPIO_Pin_Bank[_pin]][kIRQSTATUS_SET_0/4] |= v; 232 | m_gpio[GPIO_Pin_Bank[_pin]][kIRQSTATUS_SET_1/4] |= v; 233 | } 234 | else 235 | { 236 | m_gpio[GPIO_Pin_Bank[_pin]][kIRQSTATUS_CLR_0/4] |= v; 237 | m_gpio[GPIO_Pin_Bank[_pin]][kIRQSTATUS_CLR_1/4] |= v; 238 | } 239 | 240 | return kSuccess; 241 | 242 | } 243 | 244 | //======================================================= 245 | //======================================================= 246 | 247 | // Write a value to a pin 248 | Beagle_GPIO::Beagle_GPIO_Status Beagle_GPIO::writePin( unsigned short _pin, unsigned char _value ) 249 | { 250 | assert(GPIO_Pin_Bank[_pin]>=0); 251 | assert(GPIO_Pin_Id[_pin]>=0); 252 | 253 | unsigned long v = (_value & 0x01) << GPIO_Pin_Id[_pin]; 254 | unsigned long mask = 0x1 << GPIO_Pin_Id[_pin]; 255 | 256 | // Remove bit 257 | m_gpio[GPIO_Pin_Bank[_pin]][kDATAOUT/4] &= ~mask; 258 | // Assign new bit value 259 | m_gpio[GPIO_Pin_Bank[_pin]][kDATAOUT/4] |= v; 260 | 261 | return kSuccess; 262 | } 263 | 264 | //======================================================= 265 | //======================================================= 266 | 267 | // Read a value from a pin 268 | unsigned char Beagle_GPIO::readPin( unsigned short _pin ) 269 | { 270 | assert(GPIO_Pin_Bank[_pin]>=0); 271 | assert(GPIO_Pin_Id[_pin]>=0); 272 | 273 | unsigned long bit = GPIO_Pin_Id[_pin]; 274 | return (m_gpio[GPIO_Pin_Bank[_pin]][kDATAIN/4] & (0x1 << bit)) >> bit; 275 | } 276 | 277 | //======================================================= 278 | //======================================================= 279 | 280 | // Default SPI Device for the beaglebone 281 | static const char *spi_device = "/dev/spidev2.0"; 282 | 283 | // Open SPI Channel 284 | void Beagle_GPIO::openSPI( unsigned char _mode, 285 | unsigned char _bits, 286 | unsigned long _speed, 287 | unsigned short _delay ) 288 | { 289 | GPIO_PRINT( "Opening SPI Device" ); 290 | m_spi_fd = open( spi_device, O_RDWR ); 291 | if ( m_spi_fd < 0 ) 292 | { 293 | GPIO_ERROR( "Error opening SPI Device" ); 294 | return; 295 | } 296 | 297 | int ret = 0; 298 | 299 | // Save settings 300 | m_spi_mode = _mode; 301 | m_spi_bits = _bits; 302 | m_spi_speed = _speed; 303 | m_spi_delay = _delay; 304 | 305 | m_spi_buffer_rx = new unsigned char[65536]; 306 | 307 | // SPI Mode 308 | ret = ioctl(m_spi_fd, SPI_IOC_WR_MODE, &m_spi_mode); 309 | if (ret == -1) 310 | { 311 | GPIO_ERROR( "Error setting SPI Mode"); 312 | return; 313 | } 314 | 315 | ret = ioctl(m_spi_fd, SPI_IOC_RD_MODE, &m_spi_mode); 316 | if (ret == -1) 317 | { 318 | GPIO_ERROR( "Error getting SPI Mode"); 319 | return; 320 | } 321 | 322 | // SPI Bits Per Word 323 | ret = ioctl(m_spi_fd, SPI_IOC_WR_BITS_PER_WORD, &m_spi_bits); 324 | if (ret == -1) 325 | { 326 | GPIO_ERROR( "Error setting SPI Bits Per Word"); 327 | return; 328 | } 329 | 330 | ret = ioctl(m_spi_fd, SPI_IOC_RD_BITS_PER_WORD, &m_spi_bits); 331 | if (ret == -1) 332 | { 333 | GPIO_ERROR( "Error getting SPI Bits Per Word"); 334 | return; 335 | } 336 | 337 | // SPI Max Speed 338 | ret = ioctl(m_spi_fd, SPI_IOC_WR_MAX_SPEED_HZ, &m_spi_speed); 339 | if (ret == -1) 340 | { 341 | GPIO_ERROR( "Error setting SPI Max Speed"); 342 | return; 343 | } 344 | 345 | ret = ioctl(m_spi_fd, SPI_IOC_RD_MAX_SPEED_HZ, &m_spi_speed); 346 | if (ret == -1) 347 | { 348 | GPIO_ERROR( "Error getting SPI Max Speed"); 349 | return; 350 | } 351 | 352 | GPIO_PRINT( "SPI Mode : " << std::hex << (int)(m_spi_mode) ); 353 | GPIO_PRINT( "SPI Bits Per Word : " << std::dec << (int)(m_spi_bits) ); 354 | GPIO_PRINT( "SPI Max Speed : " << std::dec << m_spi_speed ); 355 | GPIO_PRINT( "SPI Delay : " << std::dec << m_spi_delay ); 356 | GPIO_PRINT( "SPI Opened" ); 357 | } 358 | 359 | //======================================================= 360 | //======================================================= 361 | 362 | // Close SPI Channel 363 | void Beagle_GPIO::closeSPI() 364 | { 365 | if ( m_spi_fd >= 0) 366 | { 367 | GPIO_PRINT( "Closing SPI Device" ); 368 | close( m_spi_fd ); 369 | delete [] m_spi_buffer_rx; 370 | } 371 | } 372 | 373 | //======================================================= 374 | //======================================================= 375 | 376 | // Send SPI Buffer 377 | void Beagle_GPIO::sendSPIBuffer( unsigned long _buffer, int _size ) 378 | { 379 | assert( m_spi_fd >= 0 ); 380 | assert( _buffer > 0 ); 381 | assert( _size > 0 ); 382 | 383 | m_spi_ioc_tr.tx_buf = _buffer; 384 | m_spi_ioc_tr.rx_buf = (unsigned long)(m_spi_buffer_rx); 385 | m_spi_ioc_tr.len = _size; 386 | m_spi_ioc_tr.delay_usecs = m_spi_delay; 387 | m_spi_ioc_tr.speed_hz = m_spi_speed; 388 | m_spi_ioc_tr.bits_per_word = m_spi_bits; 389 | 390 | if ( ioctl( m_spi_fd, SPI_IOC_MESSAGE(1), &m_spi_ioc_tr ) < 1 ) 391 | { 392 | GPIO_ERROR( "Cannot send SPI Buffer, size=" << std::dec << _size ); 393 | return; 394 | } 395 | } 396 | 397 | //======================================================= 398 | //======================================================= 399 | 400 | 401 | -------------------------------------------------------------------------------- /lib/sensor/driver/dht/src/Beagle_GPIO.hh: -------------------------------------------------------------------------------- 1 | /****************************** 2 | ** Beagle Bone GPIO Library ** 3 | ** ** 4 | ** Francois Sugny ** 5 | ** 01/07/12 ** 6 | ** ** 7 | ** v1.0 ** 8 | ******************************/ 9 | 10 | //======================================================= 11 | //======================================================= 12 | 13 | #ifndef beagle_gpio_hh 14 | #define beagle_gpio_hh 15 | 16 | //======================================================= 17 | //======================================================= 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | //======================================================= 25 | //======================================================= 26 | 27 | #define GPIO_ERROR(msg) \ 28 | std::cerr << "[GPIO] Error : " << msg << std::endl; \ 29 | std::cout << "{\"status\": \"error\", \"message\": \"[GPIO] Error : " << msg << "\"}"; \ 30 | exit(1); 31 | 32 | //#define BEAGLE_GPIO_DEBUG 33 | #ifdef BEAGLE_GPIO_DEBUG 34 | #define GPIO_PRINT(msg) std::cerr << "[GPIO] : " << msg << std::endl; 35 | #define assert( condition ) \ 36 | if (!(condition)) \ 37 | { \ 38 | GPIO_ERROR( "Assert Failed in file '" << __FILE__ << "' on line " << __LINE__ ); \ 39 | exit(0); \ 40 | } 41 | 42 | #else 43 | #define GPIO_PRINT(msg) 44 | #define assert( condition ) 45 | #endif 46 | 47 | 48 | //======================================================= 49 | //======================================================= 50 | 51 | class Beagle_GPIO 52 | { 53 | public: 54 | // Return status 55 | typedef enum 56 | { 57 | kFail = 0, 58 | kSuccess = 1 59 | } Beagle_GPIO_Status; 60 | 61 | // Beagle Bone GPIO Register Offsets 62 | enum 63 | { 64 | kREVISION = 0x0, 65 | kSYSCONFIG = 0x10, 66 | kIRQSTATUS_RAW_0 = 0x24, 67 | kIRQSTATUS_RAW_1 = 0x28, 68 | kIRQSTATUS_0 = 0x2C, 69 | kIRQSTATUS_1 = 0x30, 70 | kIRQSTATUS_SET_0 = 0x34, 71 | kIRQSTATUS_SET_1 = 0x38, 72 | kIRQSTATUS_CLR_0 = 0x3C, 73 | kIRQSTATUS_CLR_1 = 0x40, 74 | kIRQWAKEN_0 = 0x44, 75 | kIRQWAKEN_1 = 0x48, 76 | kSYSSTATUS = 0x114, 77 | kCTRL = 0x130, 78 | kOE = 0x134, 79 | kDATAIN = 0x138, 80 | kDATAOUT = 0x13C, 81 | kLEVELDETECT0 = 0x140, 82 | kLEVELDETECT1 = 0x144, 83 | kRISINGDETECT = 0x148, 84 | kFALLINGDETECT = 0x14C, 85 | kDEBOUNCEENABLE = 0x150, 86 | kDEBOUNCINGTIME = 0x154, 87 | kCLEARDATAOUT = 0x190, 88 | kSETDATAOUT = 0x194 89 | } Beagle_GPIO_Registers; 90 | 91 | // Input/Output pin mode 92 | typedef enum 93 | { 94 | kINPUT = 0, 95 | kOUTPUT = 1 96 | } Beagle_GPIO_Direction; 97 | 98 | // GPIO Pins 99 | enum 100 | { 101 | P8_1, P8_2, P8_3, P8_4, P8_5, 102 | P8_6, P8_7, P8_8, P8_9, P8_10, 103 | P8_11, P8_12, P8_13, P8_14, P8_15, 104 | P8_16, P8_17, P8_18, P8_19, P8_20, 105 | P8_21, P8_22, P8_23, P8_24, P8_25, 106 | P8_26, P8_27, P8_28, P8_29, P8_30, 107 | P8_31, P8_32, P8_33, P8_34, P8_35, 108 | P8_36, P8_37, P8_38, P8_39, P8_40, 109 | P8_41, P8_42, P8_43, P8_44, P8_45, 110 | P8_46, 111 | P9_1, P9_2, P9_3, P9_4, P9_5, 112 | P9_6, P9_7, P9_8, P9_9, P9_10, 113 | P9_11, P9_12, P9_13, P9_14, P9_15, 114 | P9_16, P9_17, P9_18, P9_19, P9_20, 115 | P9_21, P9_22, P9_23, P9_24, P9_25, 116 | P9_26, P9_27, P9_28, P9_29, P9_30, 117 | P9_31, P9_32, P9_33, P9_34, P9_35, 118 | P9_36, P9_37, P9_38, P9_39, P9_40, 119 | P9_41, P9_42, P9_43, P9_44, P9_45, 120 | P9_46 121 | } GPIO_Pins; 122 | 123 | // IO Banks for GPIOs 124 | static const int GPIO_Pin_Bank[]; 125 | 126 | // Pin Id for GPIOs 127 | static const int GPIO_Pin_Id[]; 128 | 129 | // Pad Control Register 130 | static const unsigned long GPIO_Pad_Control[]; 131 | 132 | // Base address of Control Module Registers 133 | static const unsigned long GPIO_Control_Module_Registers; 134 | 135 | // Base addresses of GPIO Modules 136 | static const unsigned long GPIO_Base[]; 137 | 138 | public: 139 | Beagle_GPIO(); 140 | ~Beagle_GPIO(); 141 | 142 | public: 143 | // Configure pin as input/output 144 | Beagle_GPIO_Status configurePin( unsigned short _pin, Beagle_GPIO_Direction _direction ); 145 | 146 | // Enable/Disable interrupts for the pin 147 | Beagle_GPIO_Status enablePinInterrupts( unsigned short _pin, bool _enable ); 148 | 149 | // Write a value to a pin 150 | Beagle_GPIO_Status writePin( unsigned short _pin, unsigned char _value ); 151 | 152 | // Read a value from a pin 153 | unsigned char readPin( unsigned short _pin ); 154 | 155 | // Open SPI Channel 156 | void openSPI( unsigned char _mode=0, 157 | unsigned char _bits=8, 158 | unsigned long _speed=4800000, 159 | unsigned short _delay=0 ); 160 | 161 | // Close SPI Channel 162 | void closeSPI(); 163 | 164 | // Send SPI Buffer 165 | void sendSPIBuffer( unsigned long buffer, int size ); 166 | 167 | // Is this Module active ? 168 | bool isActive() { return m_active; } 169 | 170 | private: 171 | bool m_active; 172 | int m_gpio_fd; 173 | unsigned long * m_controlModule; 174 | unsigned long * m_gpio[4]; 175 | 176 | int m_spi_fd; 177 | unsigned char * m_spi_buffer_rx; 178 | unsigned char m_spi_mode; 179 | unsigned char m_spi_bits; 180 | unsigned long m_spi_speed; 181 | unsigned short m_spi_delay; 182 | 183 | struct spi_ioc_transfer m_spi_ioc_tr; 184 | }; 185 | 186 | //======================================================= 187 | //======================================================= 188 | 189 | #endif 190 | 191 | //======================================================= 192 | //======================================================= 193 | 194 | -------------------------------------------------------------------------------- /lib/sensor/driver/dht/src/Beagle_GPIO_dht22.cc: -------------------------------------------------------------------------------- 1 | /* 2 | DHT22 reader for Beaglebone 3 | 4 | Inspired by adafruit code : 5 | https://github.com/adafruit/Adafruit-Raspberry-Pi-Python-Code/tree/master/Adafruit_DHT_Driver 6 | Library used for GPIO access : 7 | https://github.com/majestik666/Beagle_GPIO 8 | 9 | */ 10 | #include "Beagle_GPIO.hh" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define MAXTIMINGS 100 19 | 20 | #define DHT11 1 21 | #define DHT22 2 22 | #define SENSOR_NAME(sensor) ((sensor == DHT11)?"DHT11":"DHT22") 23 | 24 | #define BIG_NUMBER 1000 25 | #define TIMEOUT 10 // 10 sec 26 | 27 | #define ABS(a) (((a)<0)?-(a):(a)) 28 | 29 | #define GPIO_TABLE_SIZE 92 30 | #define MAX_GPIO_ADDRESS 125 31 | 32 | timespec diff(timespec start, timespec end); 33 | 34 | Beagle_GPIO gpio; 35 | struct timespec startTime; 36 | clockid_t clk_id = CLOCK_REALTIME ; 37 | int debug = false; 38 | 39 | const int gpio_address_table[] = 40 | { 41 | -1, -1, 38, 39, 34, // P8_1 -> P8_5 42 | 35, 66, 67, 69, 68, // P8_6 -> P8_10 43 | 45, 44, 23, 26, 47, // P8_11 -> P8_15 13, 14 44 | 46, 27, 65, 22, 63, // P8_16 -> P8_20 17, 19 45 | 62, 37, 36, 33, 32, // P8_21 -> P8_25 46 | 61, 86, 88, 87, 89, // P8_26 -> P8_30 47 | 10, 11, 9, 81, 8, // P8_31 -> P9_35 31, 32, 33, 35 48 | 80, 78, 79, 76, 77, // P8_36 -> P8_40 49 | 74, 75, 72, 73, 70, // P8_41 -> P8_45 50 | 71, // P8_46 51 | -1, -1, -1, -1, -1, // P9_1 -> P9_5 52 | -1, -1, -1, -1, -1, // P9_6 -> P9_10 53 | 30, 60, 31, 40, 48, // P9_11 -> P9_15 11, 13 54 | 51, 4, 5, -1, -1, // P9_16 -> P9_20 55 | 3, 2, 49, 15,117, // P9_21 -> P9_25 21, 22, 24 56 | 14,125,123,121,122, // P9_26 -> P9_30 26 57 | 120, -1, -1, -1, -1, // P9_31 -> P9_35 58 | -1, -1, -1, -1, -1, // P9_36 -> P9_40 59 | 20, 7, -1, -1, -1, // P9_41 -> P9_45 41, 42 60 | -1 // P9_46 61 | }; 62 | 63 | void check_timeout(); 64 | unsigned short get_pin_index_with_gpio_address(int gpio_address); 65 | 66 | int main(int argc, char* argv[]) 67 | { 68 | // std::cerr << "==========================\n"; 69 | // std::cerr << "BeagleBone GPIO DHT22 Test\n"; 70 | // std::cerr << "==========================\n" ; 71 | 72 | int counter = 0; 73 | int laststate = 1; 74 | int j = 0; 75 | int d = 0; 76 | unsigned short pin = Beagle_GPIO::P8_17; // ving: GPIO conf 77 | int bits[250], state[250], data[100]; 78 | struct timespec timestamp[250]; 79 | int bitidx = 0; 80 | float f, h; 81 | int sensor = DHT22; 82 | //clockid_t clk_id = CLOCK_PROCESS_CPUTIME_ID; 83 | int c, index; 84 | char* cvalue; 85 | int gpio_address = 0; 86 | 87 | // argument parsing 88 | opterr = 0; 89 | 90 | while ((c = getopt (argc, argv, "hds:g:")) != -1) 91 | switch (c) 92 | { 93 | case 'd': 94 | debug = true; 95 | break; 96 | case 's': 97 | if (strcmp(optarg, "dht11") == 0) { sensor = DHT11; } 98 | break; 99 | case 'g': 100 | if (strcmp(optarg, "") != 0) { 101 | gpio_address = atoi(optarg); 102 | if (gpio_address < 0 || gpio_address > MAX_GPIO_ADDRESS) { 103 | std::cout << "{\"status\": \"error\", \"message\": \"GPIO address is out of range.\"}"; 104 | exit(0); 105 | } 106 | pin = get_pin_index_with_gpio_address(gpio_address); 107 | if (pin < 0) { 108 | std::cout << "{\"status\": \"error\", \"message\": \"Wrong GPIO address\"}"; 109 | exit(0); 110 | } 111 | } 112 | break; 113 | default: 114 | fprintf(stderr, "Usage:\n\t%s [-d] [-s sensorname] [-g GPIO address]\n\t-d: debug\n\t-s: specify sensor, default dht22\n\t-g: GPIO address, default 27\n", argv[0]); 115 | exit(0); 116 | } 117 | 118 | clock_gettime(clk_id, &startTime); 119 | //check_timeout(); 120 | while(true) { 121 | bitidx = 0; 122 | counter = 0; 123 | laststate = 1; 124 | j = 0; 125 | clk_id = CLOCK_REALTIME ; 126 | data[0] = data[1] = data[2] = data[3] = data[4] = 0; 127 | 128 | //std::cerr << "Configuring Pin P8_4 as Output\n"; 129 | gpio.configurePin( pin, Beagle_GPIO::kOUTPUT ); 130 | gpio.enablePinInterrupts( pin, false ); 131 | 132 | // Initialize DHT22 sensor 133 | gpio.writePin( pin, 0 ); 134 | usleep(20000); // 500 ms 135 | gpio.writePin( pin, 1 ); 136 | usleep(40); 137 | 138 | gpio.configurePin( pin, Beagle_GPIO::kINPUT ); 139 | gpio.enablePinInterrupts( pin, false ); 140 | 141 | if (debug) { std::cerr << "-"; } 142 | while (gpio.readPin(pin) == 0) { 143 | check_timeout(); 144 | usleep(1); 145 | } 146 | if (debug) { std::cerr << "-"; } 147 | while (gpio.readPin(pin) == 0) { 148 | check_timeout(); 149 | usleep(1); 150 | } 151 | // read data! 152 | if (debug) { std::cerr << ">"; } 153 | for (int i=0; i< MAXTIMINGS; i++) { 154 | counter = 0; 155 | while ( gpio.readPin(pin) == laststate) { 156 | counter++; 157 | //usleep(1); 158 | if (counter == 400) 159 | break; 160 | check_timeout(); 161 | } 162 | //laststate = gpio.readPin(pin); 163 | laststate = gpio.readPin(pin); 164 | if (counter == 400) break; 165 | state[bitidx] = laststate; 166 | clock_gettime(clk_id, ×tamp[bitidx]); 167 | bits[bitidx++] = counter; 168 | check_timeout(); 169 | } 170 | 171 | // analyse data and 172 | j = 0; 173 | data[0] = data[1] = data[2] = data[3] = data[4] = 0; 174 | //std::cerr << "bitidx=" << bitidx << "\n"; 175 | for (int i=0; i 1) && (i%2 == 0)) { 177 | // shove each bit into the storage bytes 178 | if (debug) { 179 | if (j%8 == 0) { 180 | std::cerr << "\n" << j/8; 181 | } 182 | if (debug) { 183 | std::cerr << " " << (diff(timestamp[i-1], timestamp[i]).tv_nsec/1000); 184 | } 185 | } 186 | d = diff(timestamp[i-1], timestamp[i]).tv_nsec/1000; 187 | if (d < 20 || d > 80 ){ 188 | bitidx = -1; 189 | break; 190 | } 191 | data[j/8] <<= 1; 192 | if (d > 40) { data[j/8] |= 1; } 193 | j++; 194 | } 195 | check_timeout(); 196 | } 197 | 198 | if (debug) { 199 | if (bitidx > 0) { 200 | std::cerr << "\nbitidx=" << bitidx << "\n"; 201 | } else { 202 | std::cerr << "."; 203 | } 204 | } 205 | 206 | // Compute the checksum 207 | int checksum = (data[0] + data [1] + data [2] + data [3]) & 0xFF; 208 | if (debug) { 209 | if (checksum != 0) { 210 | for (int i=0; i < 5; i++) { 211 | fprintf(stderr, "data[%d]=%0d ", i, data[i]); 212 | } 213 | std::cerr << "Checksum= " << checksum << "\n"; 214 | } 215 | } 216 | if (checksum != data[4] || (checksum == 0)) { 217 | continue; 218 | } 219 | 220 | // Compute the Temp & Hum from data (for RHT3 / DHT22) 221 | if (DHT22 == sensor) { 222 | h = data[0] * 256 + data[1]; 223 | h /= 10; 224 | 225 | f = (data[2] & 0x7F)* 256 + data[3]; 226 | f /= 10.0; 227 | if (data[2] & 0x80) f *= -1; 228 | } else if (DHT11 == sensor) { 229 | h = data[0]; 230 | f = data[2]; 231 | if (data[2] & 0x80) f *= -1; 232 | } 233 | 234 | // Print to console 235 | if (bitidx > 40 && h >= 0 && h <= 100) { // check humidity range 236 | //found 237 | if(debug) { 238 | std::cerr << "Temp = " << h << "°C, Hum = " << f << "% bitidx=" << bitidx << "\n"; 239 | } 240 | printf("{\"status\": \"ok\", \"id\":\"%s\", \"result\":{\"temperature\": %.2f, \"humidity\": %.2f}}", SENSOR_NAME(sensor), f, h); 241 | exit(0); 242 | } else { 243 | usleep(200000); // sleep 200ms 244 | } 245 | check_timeout(); 246 | } 247 | } 248 | 249 | 250 | void check_timeout() { 251 | struct timespec now; 252 | clock_gettime(clk_id, &now); 253 | //printf("%d ", now.tv_sec - startTime.tv_sec); 254 | if ( (now.tv_sec - startTime.tv_sec) > TIMEOUT) { 255 | std::cout << "{\"status\": \"error\", \"message\": \"timeout\"}"; 256 | exit(0); 257 | } 258 | } 259 | /* Compute diff for timespec (from clock_gettime)*/ 260 | timespec diff(timespec start, timespec end) 261 | { 262 | timespec temp; 263 | if ((end.tv_nsec-start.tv_nsec)<0) { 264 | temp.tv_sec = end.tv_sec-start.tv_sec-1; 265 | temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; 266 | } else { 267 | temp.tv_sec = end.tv_sec-start.tv_sec; 268 | temp.tv_nsec = end.tv_nsec-start.tv_nsec; 269 | } 270 | return temp; 271 | } 272 | 273 | unsigned short get_pin_index_with_gpio_address(int gpio_address) 274 | { 275 | int array_size = GPIO_TABLE_SIZE; 276 | unsigned short pin_index = -1; 277 | 278 | for (unsigned short i = 0; i < array_size; i++) { 279 | if (gpio_address == gpio_address_table[i]) { 280 | pin_index = i; 281 | } 282 | } 283 | 284 | return pin_index; 285 | } 286 | -------------------------------------------------------------------------------- /lib/sensor/driver/dht/src/Makefile: -------------------------------------------------------------------------------- 1 | 2 | ###################################### 3 | ###################################### 4 | 5 | CXX=g++ 6 | 7 | #CFLAGS=-Wall -c -O3 8 | CFLAGS=-Wall -c -g 9 | 10 | #LDFLAGS=-Wall -O3 11 | LDFLAGS=-Wall -g 12 | 13 | BIN_DIR=../bin 14 | TARGETS = Beagle_GPIO.o \ 15 | $(BIN_DIR)/Beagle_GPIO_dht22 16 | 17 | all: $(TARGETS) 18 | 19 | Beagle_GPIO.o: Beagle_GPIO.cc Beagle_GPIO.hh 20 | @echo Compiling $< to $@ 21 | @$(CXX) $(CFLAGS) Beagle_GPIO.cc 22 | 23 | $(BIN_DIR)/Beagle_GPIO_dht22: Beagle_GPIO_dht22.cc Beagle_GPIO.o 24 | @echo Compiling $< to $@ 25 | @$(CXX) $(LDLAGS) Beagle_GPIO_dht22.cc Beagle_GPIO.o -lrt -o $@ 26 | 27 | clean: 28 | @rm -rf *~ *.o $(TARGETS) 29 | 30 | 31 | ###################################### 32 | ###################################### 33 | 34 | -------------------------------------------------------------------------------- /lib/sensor/driver/digitalCO2/X100.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Sensor = require('../index').Sensor, 4 | logger = Sensor.getLogger(), 5 | util = require('util'), 6 | I2c = require('i2c'), 7 | serialport = require('serialport'), 8 | events = require('events'), 9 | _ = require('lodash'), 10 | async = require('async'); 11 | 12 | var defaultOptions = { 13 | i2c: { 14 | 'debug': false, 15 | 'address': 0x23, 16 | 'device': '/dev/i2c-1', 17 | 'powerMode': 'powerOn' 18 | }, 19 | uart: { 20 | 'address': 4, 21 | 'device': '/dev/ttyO4' 22 | } 23 | }; 24 | 25 | var X100 = function(opts) { 26 | events.EventEmitter.call(this); 27 | 28 | this.options = _.extend({}, defaultOptions[opts.sensorNetwork], opts); 29 | 30 | switch (this.options.sensorNetwork) { 31 | case 'i2c': 32 | this.i2c = new I2c(this.options.address, { 33 | device: this.options.device, 34 | debug: this.options.debug 35 | }); 36 | 37 | break; 38 | case 'uart': 39 | this.i2c = new serialport.SerialPort('/dev/ttyO' + this.options.address, { 40 | baudrate: this.options.baudrate || 9600, 41 | parser: serialport.parsers.readline('\n\r') 42 | }); 43 | 44 | break; 45 | default: 46 | logger.warn('[DigitalCO2/Driver] ' + this.options.sensorNetwork + ' sensor network is not supported'); 47 | } 48 | }; 49 | 50 | util.inherits(X100, events.EventEmitter); 51 | 52 | X100.powerModes = { 53 | 'powerDown': 0x00, 54 | 'powerOn': 0x01 55 | }; 56 | 57 | X100.commands = { 58 | 'CO2': 0xA2, 59 | 'StatusError': 0xA5, 60 | 'All': 0xA1 61 | }; 62 | 63 | X100.prototype.init = function(callback) { 64 | var self = this; 65 | 66 | switch (this.options.sensorNetwork) { 67 | case 'i2c': 68 | async.series([ 69 | function(cB) { 70 | self.setPowerMode(self.options.powerMode, cB); 71 | } 72 | ], function(err, res) { 73 | var ts = Math.round(+new Date() / 1000); 74 | var evData = { 75 | 'addr': self.options.address, 76 | 'type': 'X100', 77 | 'ts': ts, 78 | 'error': err 79 | }; 80 | if (err) { 81 | self.emit('sensorInitFailed', evData); 82 | if (callback) { callback(err, null); } 83 | } else { 84 | self.emit('sensorInitCompleted', evData); 85 | if (callback) { callback(null, true); } 86 | } 87 | }); 88 | 89 | break; 90 | case 'uart': 91 | logger.info('[DigitalCO2/Driver/X100] init with uart', this.options); 92 | 93 | this.i2c.on('open', function () { 94 | self.i2c.on('data', function(data) { 95 | var rtn, result; 96 | 97 | // # 0471 XXX.X XX Nr Nr 98 | result = data.toString().split(' '); 99 | 100 | if (result.length >= 6) { 101 | if (result[4] === 'Nr' && result[5] === 'Nr') { 102 | rtn = { 103 | status: 'ok', 104 | result: { 105 | 'co2': Number(result[1], 10) 106 | } 107 | }; 108 | } else if (result[4] === 'WU') { 109 | rtn = { 110 | status: 'error', 111 | message: 'Sensor is warming up.' 112 | }; 113 | } else if (result[5] === 'Er') { 114 | rtn = { 115 | status: 'error', 116 | message: 'Sensor is Error. Replace the CO2 Sensor.' 117 | }; 118 | } else { 119 | rtn = { 120 | status: 'error', 121 | message: 'Data from the sensor is not valid values' 122 | }; 123 | } 124 | } else { 125 | rtn = { 126 | status: 'error', 127 | message: 'Data from the sensor is not valid format' 128 | }; 129 | } 130 | 131 | //logger.debug('[DigitalCO2/Driver] on data', data, rtn); 132 | self.emit('data', rtn); 133 | }); 134 | }); 135 | 136 | break; 137 | default: 138 | logger.warn('[DigitalCO2/Driver] ' + this.options.sensorNetwork + ' sensor network is not supported'); 139 | } 140 | }; 141 | 142 | X100.prototype.setPowerMode = function(newMode, callback) { 143 | var self = this; 144 | 145 | if (_.has(X100.powerModes, newMode) === false) { 146 | var err = new Error('wrong powermode value in set powermode command'); 147 | var ts = Math.round(+new Date() / 1000); 148 | var evData = { 149 | 'addr': self.options.address, 150 | 'type': 'X100', 151 | 'setting': 'powerMode', 152 | 'newValue': newMode, 153 | 'ts': ts, 154 | 'error': err 155 | }; 156 | self.emit('sensorSettingFailed', evData); 157 | if (callback) { callback(err, null); } 158 | return; 159 | } 160 | 161 | async.waterfall([ 162 | function(cB) { 163 | var writeVal; 164 | 165 | writeVal = X100.powerModes[newMode]; 166 | 167 | self.i2c.writeByte(writeVal, function(err) { 168 | if (err) { 169 | cB(new Error('powermode not set on write'), 'write'); 170 | } else { 171 | cB(null, 'write'); 172 | } 173 | }); 174 | } 175 | ], 176 | function(err, results) { 177 | var ts = Math.round(+new Date() / 1000); 178 | var evData = { 179 | 'addr': self.options.address, 180 | 'type': 'X100', 181 | 'setting': 'powerMode', 182 | 'newValue': newMode, 183 | 'ts': ts, 184 | 'error': err 185 | }; 186 | if (err) { 187 | self.emit('sensorSettingFailed', evData); 188 | if (callback) { callback(err, null); } 189 | } else { 190 | self.options.powerMode = newMode; 191 | self.emit('sensorSettingChanged', evData); 192 | if (callback) { callback(null, newMode); } 193 | } 194 | }); 195 | }; 196 | 197 | X100.prototype.getCO2 = function(command, callback) { 198 | var self = this; 199 | 200 | var commandCode = X100.commands.command; 201 | 202 | var hi = 0, lo = 0, li = 0, 203 | status = 0, error = 0, 204 | rtn = {}; 205 | 206 | switch (command.toLowerCase()) { 207 | case 'co2': 208 | self.i2c.readBytes(commandCode, 2, function(err, bytes) { 209 | if (err) { 210 | callback(err); 211 | } else { 212 | hi = bytes.readUInt8(0); 213 | lo = bytes.readUInt8(1); 214 | li = (hi << 8) + lo; 215 | 216 | logger.debug('getCO2 - hi, lo, li', hi, lo, li); 217 | 218 | rtn.co2 = li; 219 | 220 | callback(null, rtn); 221 | } 222 | }); 223 | 224 | break; 225 | case 'statuserror': 226 | self.i2c.readBytes(commandCode, 2, function(err, bytes) { 227 | if (err) { 228 | callback(err); 229 | } else { 230 | status = bytes.readUInt8(0); 231 | error = bytes.readUInt8(1); 232 | 233 | logger.debug('getStatusError - status, error', status, error); 234 | 235 | rtn.status = status; 236 | rtn.error = error; 237 | 238 | callback(null, rtn); 239 | } 240 | }); 241 | 242 | break; 243 | case 'all': 244 | self.i2c.readBytes(commandCode, 4, function(err, bytes) { 245 | if (err) { 246 | callback(err); 247 | } else { 248 | hi = bytes.readUInt8(1); 249 | lo = bytes.readUInt8(0); 250 | li = (hi << 8) + lo; 251 | 252 | status = bytes.readUInt8(2); 253 | error = bytes.readUInt8(3); 254 | 255 | logger.debug('getAll - hi, lo, li, status, error', hi, lo, li, status, error); 256 | 257 | rtn.co2 = li; 258 | rtn.status = status; 259 | rtn.error = error; 260 | 261 | callback(null, rtn); 262 | } 263 | }); 264 | 265 | break; 266 | default: 267 | logger.info('command is not found'); 268 | callback(null, rtn); 269 | } 270 | }; 271 | 272 | X100.prototype.getDensity = function(callback) { 273 | var self = this, 274 | command; 275 | 276 | command = self.options.command || 'All'; 277 | 278 | async.series([ 279 | function(cB) { 280 | self.getCO2(command, cB); 281 | } 282 | ], function(err, results) { 283 | //logger.info(results) 284 | var ts = Math.round(+new Date() / 1000); 285 | var evData = { 286 | 'addr': self.options.address, 287 | 'type': 'X100', 288 | 'valType': 'co2', 289 | 'ts': ts, 290 | 'error': err 291 | }; 292 | 293 | if (err) { 294 | self.emit('sensorValueError', evData); 295 | if (callback) { callback(err, null); } 296 | } else if (results[0].co2 === 0) { 297 | var e = new Error('invalid value(s) from sensor'); 298 | evData.error = e; 299 | self.emit('sensorValueError', evData); 300 | if (callback) { callback(e, null); } 301 | } else { 302 | evData.sensVal = results[0].co2; 303 | self.emit('newSensorValue', evData); 304 | if (callback) { callback(null, results[0].co2); } 305 | } 306 | }); 307 | }; 308 | 309 | module.exports = X100; 310 | -------------------------------------------------------------------------------- /lib/sensor/driver/digitalCO2/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Sensor = require('../index').Sensor, 4 | util = require('util'), 5 | _cached = {retries: 0}, 6 | logger = Sensor.getLogger(), 7 | X100 = require('./X100'), 8 | _ = require('lodash'); 9 | 10 | var I2C_DEVICE = '/dev/i2c-', 11 | I2C_BUS_ID = 1, 12 | UART_DEVICE = '/dev/ttyO', 13 | UART_ADDRESS_ID = 4; 14 | 15 | function DigitalCO2(sensorInfo, options) { 16 | var self = this, 17 | device, sensOptions, 18 | latestSuccessResult; 19 | 20 | logger.info('[DigitalCO2/Driver] sensorInfo, options', sensorInfo, options); 21 | 22 | Sensor.call(this, sensorInfo, options); 23 | 24 | if (sensorInfo.model) { 25 | this.model = sensorInfo.model; 26 | } else if (options && options.model) { 27 | this.model = options.model; 28 | } 29 | 30 | if (sensorInfo.device.sensorNetwork && sensorInfo.device.address) { 31 | 32 | if (sensorInfo.device.sensorNetwork === 'i2c') { 33 | device = I2C_DEVICE + (sensorInfo.device.bus || I2C_BUS_ID); 34 | } else if (sensorInfo.device.sensorNetwork === 'uart') { 35 | device = UART_DEVICE + (sensorInfo.device.address || UART_ADDRESS_ID); 36 | } 37 | 38 | switch (this.model) { 39 | case 'X100': 40 | sensOptions = { 41 | address: sensorInfo.device.address, 42 | device: device, 43 | sensorNetwork: sensorInfo.device.sensorNetwork 44 | }; 45 | 46 | this.wire = new X100(sensOptions); 47 | 48 | this.wire.on('sensorSettingChanged', function (e) { 49 | logger.debug('[DigitalCO2/Driver] sensorSettingChanged', e); 50 | }); 51 | this.wire.on('sensorSettingFailed', function (e) { 52 | logger.debug('[DigitalCO2/Driver] sensorSettingFailed', e); 53 | }); 54 | this.wire.on('newSensorValue', function (e) { 55 | logger.debug('[DigitalCO2/Driver] newSensorValue', e); 56 | }); 57 | this.wire.on('sensorValueError', function (e) { 58 | logger.debug('[DigitalCO2/Driver] sensorValueError', e); 59 | }); 60 | 61 | this.wire.on('data', function (result) { 62 | var sentDateGap; 63 | 64 | if (result && _.isObject(result)) { 65 | if (result.status === 'ok') { 66 | latestSuccessResult = result; 67 | } 68 | 69 | sentDateGap = Date.now() - self._sentDate; 70 | 71 | if (!sentDateGap || (sentDateGap >= DigitalCO2.properties.recommendedInterval)) { 72 | result.id = self.id; 73 | 74 | // send the latest success result within recommededInterval or the last fail result 75 | if (latestSuccessResult && latestSuccessResult.status === 'ok') { 76 | result = latestSuccessResult; 77 | } 78 | 79 | self._sentDate = Date.now(); 80 | self.rtn = result; 81 | 82 | latestSuccessResult = null; 83 | 84 | //self.emit('change', result); 85 | 86 | logger.debug('[DigitalCO2/Driver] data event from sensor and send chagne', result); 87 | } 88 | } 89 | }); 90 | 91 | this.wire.init(function (err, val) { 92 | if (err) { 93 | logger.error('[DigitalCO2/Driver] X100 - error on sensor init: ', err); 94 | } else { 95 | logger.debug('[DigitalCO2/Driver] X100 - sensor init completed: ', val); 96 | } 97 | }); 98 | 99 | break; 100 | default: 101 | logger.warn('[DigitalCO2/Driver] ' + this.model + ' model is not supported'); 102 | } 103 | } else { 104 | logger.warn('[DigitalCO2/Driver] sensor network or address is not provided'); 105 | } 106 | } 107 | 108 | DigitalCO2.properties = { 109 | supportedNetworks: ['i2c', 'uart'], 110 | dataTypes: ['co2'], 111 | onChange: false, 112 | discoverable: false, 113 | addressable: true, 114 | recommendedInterval: 30000, 115 | maxInstances: 10, 116 | models: ['X100'], 117 | idTemplate: '{model}-{gatewayId}-{deviceAddress}', 118 | maxRetries: 8, 119 | category: 'sensor' 120 | }; 121 | 122 | util.inherits(DigitalCO2, Sensor); 123 | 124 | /* 125 | * Get the co2 of a given sensor 126 | * @param sensor : The sensor ID 127 | * @param callback : callback (err, {id, value}) 128 | */ 129 | DigitalCO2.prototype._get = function () { 130 | var self = this, rtn; 131 | 132 | try { 133 | switch (this.model) { 134 | case 'X100': 135 | switch (this.wire.options.sensorNetwork) { 136 | case 'i2c': 137 | this.wire.getDensity(function (err, data) { 138 | if (err) { 139 | rtn = {status: 'error', id : self.id, message: err.toString()}; 140 | } else { 141 | rtn = {status: 'ok', id : self.id, result: {'co2': data}}; 142 | } 143 | 144 | logger.debug('[DigitalCO2/Driver] X100/I2C - data, rtn, info: ', data, rtn, self.info); 145 | 146 | self.emit('data', rtn); 147 | }); 148 | 149 | break; 150 | case 'uart': 151 | logger.debug('[DigitalCO2/Driver] X100/UART', this.wire); 152 | 153 | if (this.rtn) { 154 | logger.debug('[DigitalCO2/Driver] X100/UART with rtn - rtn, info: ', this.rtn, this.info); 155 | 156 | this.emit('data', this.rtn); 157 | } else { 158 | this.emit('data', {status: 'off', id : this.id, message: 'CO2 data is not available'}); 159 | } 160 | 161 | break; 162 | default: 163 | logger.warn('[DigitalCO2/Driver] ' + this.wire.options.sensorNetwork + ' sensor network is not supported'); 164 | rtn = { 165 | status: 'error', 166 | id : this.id, 167 | message: this.wire.options.sensorNetwork + ' sensor network is not supported' 168 | }; 169 | 170 | self.emit('data', rtn); 171 | } 172 | 173 | break; 174 | default: 175 | logger.warn('[DigitalCO2/Driver] ' + this.model + ' model is not supported'); 176 | rtn = {status: 'error', id : this.id, message: this.model + ' model is not supported'}; 177 | 178 | self.emit('data', rtn); 179 | } 180 | } catch (e) { 181 | logger.error('[DigitalCO2/Driver] error', e); 182 | } 183 | }; 184 | 185 | module.exports = DigitalCO2; 186 | -------------------------------------------------------------------------------- /lib/sensor/driver/digitalHumidity/HTU21D.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Sensor = require('../index').Sensor, 4 | logger = Sensor.getLogger(), 5 | util = require('util'), 6 | I2c = require('i2c'), 7 | events = require('events'), 8 | _ = require('lodash'), 9 | async = require('async'); 10 | 11 | var defaultOptions = { 12 | 'debug': false, 13 | 'address': 0x40, 14 | 'device': '/dev/i2c-1', 15 | 'powerMode': 'powerOn', 16 | 'command': 'Trigger_Humidity_Hold_Master' 17 | }; 18 | 19 | var HTU21D = function(opts) { 20 | events.EventEmitter.call(this); 21 | 22 | this.options = _.extend({}, defaultOptions, opts); 23 | 24 | this.i2c = new I2c(this.options.address, { 25 | device: this.options.device, 26 | debug: this.options.debug 27 | }); 28 | }; 29 | 30 | util.inherits(HTU21D, events.EventEmitter); 31 | 32 | HTU21D.commands = { 33 | 'Trigger_Temperature_Hold_Master': 0xE3, 34 | 'Trigger_Humidity_Hold_Master': 0xE5, 35 | 'Trigger_Temperature_No_Hold_Master': 0xF3, 36 | 'Trigger_Humidity_No_Hold_Master': 0xF5, 37 | 'Write_User_Register': 0xE6, 38 | 'Read_User_Register': 0xE7, 39 | 'Soft_Reset': 0xFE 40 | }; 41 | 42 | HTU21D.prototype.init = function(callback) { 43 | var self = this; 44 | 45 | async.series([ 46 | function(done) { 47 | self.reset('Soft_Reset', done); 48 | }, 49 | function(done) { 50 | self.begin('Read_User_Register', done); 51 | } 52 | ], function(err, result) { 53 | var ts = Math.round(+new Date() / 1000); 54 | var evData = { 55 | 'addr': self.options.address, 56 | 'type': 'HTU21D', 57 | 'ts': ts, 58 | 'result': result, 59 | 'error': err 60 | }; 61 | if (err) { 62 | self.emit('sensorInitFailed', evData); 63 | if (callback) { callback(err, null); } 64 | } else { 65 | self.emit('sensorInitCompleted', evData); 66 | if (callback) { callback(null, true); } 67 | } 68 | }); 69 | }; 70 | 71 | HTU21D.prototype.reset = function(command, callback) { 72 | var self = this; 73 | 74 | async.series([ 75 | function(done) { 76 | var writeVal; 77 | 78 | writeVal = HTU21D.commands[command]; 79 | 80 | self.i2c.writeByte(writeVal, function(err) { 81 | if (err) { 82 | done(new Error('reset error')); 83 | } else { 84 | done(null); 85 | } 86 | }); 87 | }, 88 | function(done) { 89 | setTimeout(function () { 90 | done(null); 91 | }, 15); 92 | } 93 | ], function(err) { 94 | var ts = Math.round(+new Date() / 1000); 95 | var evData = { 96 | 'addr': self.options.address, 97 | 'type': 'HTU21D', 98 | 'setting': 'reset', 99 | 'newValue': command, 100 | 'ts': ts, 101 | 'error': err 102 | }; 103 | if (err) { 104 | self.emit('sensorSettingFailed', evData); 105 | if (callback) { callback(err); } 106 | } else { 107 | self.emit('sensorSettingChanged', evData); 108 | if (callback) { callback(null, command); } 109 | } 110 | }); 111 | }; 112 | 113 | HTU21D.prototype.begin = function(command, callback) { 114 | var self = this; 115 | 116 | async.series([ 117 | function(done) { 118 | var writeVal; 119 | 120 | writeVal = HTU21D.commands[command]; 121 | 122 | self.i2c.writeByte(writeVal, function(err) { 123 | if (err) { 124 | done(new Error('begin error')); 125 | } else { 126 | done(null); 127 | } 128 | }); 129 | }, 130 | function(done) { 131 | self.i2c.readByte(function(err, res) { 132 | if (err) { 133 | done(new Error('begin error')); 134 | } else { 135 | // after reset should be 0x2 136 | logger.info('[DigitalHumidity/driver/HTU21D] begin success', res); 137 | 138 | // TODO: check res in the init process and reset again if the res is not 0x2 139 | 140 | done(null, res); 141 | } 142 | }); 143 | } 144 | ], function(err, result) { 145 | var ts = Math.round(+new Date() / 1000); 146 | var evData = { 147 | 'addr': self.options.address, 148 | 'type': 'HTU21D', 149 | 'setting': 'begin', 150 | 'newValue': command, 151 | 'ts': ts, 152 | 'error': err 153 | }; 154 | if (err) { 155 | self.emit('sensorSettingFailed', evData); 156 | if (callback) { callback(err); } 157 | } else { 158 | self.emit('sensorSettingChanged', evData); 159 | if (callback) { callback(null, result); } 160 | } 161 | }); 162 | }; 163 | 164 | HTU21D.prototype.getHumidity = function(callback) { 165 | var self = this, 166 | command; 167 | 168 | command = HTU21D.commands[self.options.command] || HTU21D.command.Trigger_Humidity_Hold_Master; 169 | 170 | var hi = 0, lo = 0, li, crc; 171 | 172 | self.i2c.readBytes(command, 3, function(err, bytes) { 173 | if (err) { 174 | callback(err); 175 | } else { 176 | hi = bytes.readUInt8(0); 177 | lo = bytes.readUInt8(1); 178 | li = (hi << 8) + lo; 179 | 180 | li *= 125; 181 | li /= 65536; 182 | li -= 6; 183 | 184 | // TODO: check crc and retry for the DigitalHumidity.properties.maxRetries 185 | crc = bytes.readUInt8(2); 186 | 187 | logger.info('[DigitalHumidity/driver/HTU21D] getHumidity - hi, lo, li, crc', hi, lo, li, crc); 188 | 189 | callback(null, li); 190 | } 191 | }); 192 | }; 193 | 194 | HTU21D.prototype.getPercent = function(callback) { 195 | var self = this; 196 | 197 | async.series([ 198 | function(done) { 199 | self.getHumidity(done); 200 | } 201 | ], function(err, results) { 202 | //logger.info(results) 203 | var ts = Math.round(+new Date() / 1000); 204 | var evData = { 205 | 'addr': self.options.address, 206 | 'type': 'HTU21D', 207 | 'valType': 'light', 208 | 'ts': ts, 209 | 'error': err 210 | }; 211 | 212 | if (err) { 213 | self.emit('sensorValueError', evData); 214 | if (callback) { callback(err, null); } 215 | } else if (_.isUndefined(results[0]) || _.isNull(results[0]) || results[0] < 0) { 216 | var e = new Error('invalid value(s) from sensor'); 217 | evData.error = e; 218 | self.emit('sensorValueError', evData); 219 | if (callback) { callback(e, null); } 220 | } else { 221 | evData.sensVal = results[0]; 222 | self.emit('newSensorValue', evData); 223 | if (callback) { callback(null, results[0]); } 224 | } 225 | }); 226 | }; 227 | 228 | module.exports = HTU21D; 229 | -------------------------------------------------------------------------------- /lib/sensor/driver/digitalHumidity/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Sensor = require('../index').Sensor, 4 | util = require('util'), 5 | _cached = {retries: 0}, 6 | logger = Sensor.getLogger(), 7 | HTU21D = require('./HTU21D'); 8 | 9 | var I2C_DEVICE = '/dev/i2c-', 10 | I2C_BUS_ID = 1, 11 | UART_DEVICE = '/dev/ttyO', 12 | UART_ADDRESS_ID = 4; 13 | 14 | function DigitalHumidity(sensorInfo, options) { 15 | var device, sensOptions; 16 | 17 | logger.info('[DigitalHumidity/Driver] sensorInfo, options', sensorInfo, options); 18 | 19 | Sensor.call(this, sensorInfo, options); 20 | 21 | if (sensorInfo.model) { 22 | this.model = sensorInfo.model; 23 | } else if (options && options.model) { 24 | this.model = options.model; 25 | } 26 | 27 | if (sensorInfo.device.sensorNetwork && sensorInfo.device.address) { 28 | 29 | if (sensorInfo.device.sensorNetwork === 'i2c') { 30 | device = I2C_DEVICE + (sensorInfo.device.bus || I2C_BUS_ID); 31 | } else if (sensorInfo.device.sensorNetwork === 'uart') { 32 | device = UART_DEVICE + (sensorInfo.device.address || UART_ADDRESS_ID); 33 | } 34 | 35 | switch (this.model) { 36 | case 'HTU21D': 37 | sensOptions = { 38 | address: sensorInfo.device.address, 39 | device: device, 40 | command: 'Trigger_Humidity_Hold_Master' 41 | }; 42 | 43 | this.wire = new HTU21D(sensOptions); 44 | 45 | this.wire.on('sensorSettingChanged', function (e) { 46 | logger.debug('[DigitalHumidity/Driver] sensorSettingChanged', e); 47 | }); 48 | this.wire.on('sensorSettingFailed', function (e) { 49 | logger.debug('[DigitalHumidity/Driver] sensorSettingFailed', e); 50 | }); 51 | this.wire.on('newSensorValue', function (e) { 52 | logger.debug('[DigitalHumidity/Driver] newSensorValue', e); 53 | }); 54 | this.wire.on('sensorValueError', function (e) { 55 | logger.debug('[DigitalHumidity/Driver] sensorValueError', e); 56 | }); 57 | 58 | this.wire.init(function (err, val) { 59 | if (err) { 60 | logger.error('[DigitalHumidity/Driver] HTU21D - error on sensor init: ', err); 61 | } else { 62 | logger.debug('[DigitalHumidity/Driver] HTU21D - sensor init completed: ', val); 63 | } 64 | }); 65 | 66 | break; 67 | default: 68 | logger.warn('[DigitalHumidity/Driver] ' + this.model + ' model is not supported'); 69 | } 70 | } else { 71 | logger.warn('[DigitalHumidity/Driver] sensor network or address is not provided'); 72 | } 73 | } 74 | 75 | DigitalHumidity.properties = { 76 | supportedNetworks: ['i2c'], 77 | dataTypes: ['humidity'], 78 | onChange: false, 79 | discoverable: false, 80 | addressable: true, 81 | recommendedInterval: 30000, 82 | maxInstances: 10, 83 | models: ['HTU21D'], 84 | idTemplate: '{model}-{gatewayId}-{deviceAddress}', 85 | maxRetries: 8, 86 | category: 'sensor' 87 | }; 88 | 89 | util.inherits(DigitalHumidity, Sensor); 90 | 91 | /* 92 | * Get the light of a given sensor 93 | * @param sensor : The sensor ID 94 | * @param callback : callback (err, {id, value}) 95 | */ 96 | DigitalHumidity.prototype._get = function () { 97 | var self = this, rtn; 98 | 99 | try { 100 | switch (this.model) { 101 | case 'HTU21D': 102 | this.wire.getPercent(function (err, data) { 103 | if (err) { 104 | rtn = {status: 'error', id : self.id, message: err.toString()}; 105 | } else { 106 | rtn = {status: 'ok', id : self.id, result: {'humidity': data}}; 107 | } 108 | 109 | logger.debug('[DigitalHumidity/Driver] HTU21D/I2C - data, rtn, info: ', data, rtn, self.info); 110 | 111 | self.emit('data', rtn); 112 | }); 113 | 114 | break; 115 | default: 116 | logger.warn('[DigitalHumidity/Driver] ' + this.model + ' model is not supported'); 117 | rtn = {status: 'error', id : this.id, message: this.model + ' model is not supported'}; 118 | 119 | self.emit('data', rtn); 120 | } 121 | } catch (e) { 122 | logger.error('[DigitalHumidity/Driver] error', e); 123 | } 124 | }; 125 | 126 | module.exports = DigitalHumidity; 127 | -------------------------------------------------------------------------------- /lib/sensor/driver/digitalLight/BH1750.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var Sensor = require('../index').Sensor, 4 | logger = Sensor.getLogger(), 5 | util = require('util'), 6 | I2c = require('i2c'), 7 | events = require('events'), 8 | _ = require('lodash'), 9 | async = require('async'); 10 | 11 | var defaultOptions = { 12 | 'debug': false, 13 | 'address': 0x23, 14 | 'device': '/dev/i2c-1', 15 | 'powerMode': 'powerOn', 16 | 'measurementMode': 'Continuous_H_Resolution_Mode' 17 | }; 18 | 19 | var BH1750 = function(opts) { 20 | events.EventEmitter.call(this); 21 | 22 | this.options = _.extend({}, defaultOptions, opts); 23 | 24 | this.i2c = new I2c(this.options.address, { 25 | device: this.options.device, 26 | debug: this.options.debug 27 | }); 28 | }; 29 | 30 | util.inherits(BH1750, events.EventEmitter); 31 | 32 | BH1750.powerModes = { 33 | 'powerDown': 0x00, 34 | 'powerOn': 0x01 35 | }; 36 | 37 | BH1750.measurementModes = { 38 | 'Continuous_H_Resolution_Mode': 0x10, 39 | 'Continuous_H_Resolution_Mode2': 0x11, 40 | 'Continuous_L_Resolution_Mode': 0x13, 41 | 'OneTime_H_Resolution_Mode': 0x20, 42 | 'OneTime_H_Resolution_Mode2': 0x21, 43 | 'OneTime_L_Resolution_Mode': 0x23 44 | }; 45 | 46 | BH1750.prototype.init = function(callback) { 47 | var self = this; 48 | 49 | async.series([ 50 | function(cB) { 51 | self.setPowerMode(self.options.powerMode, cB); 52 | } 53 | ], function(err, res) { 54 | var ts = Math.round(+new Date() / 1000); 55 | var evData = { 56 | 'addr': self.options.address, 57 | 'type': 'BH1750', 58 | 'ts': ts, 59 | 'error': err 60 | }; 61 | if (err) { 62 | self.emit('sensorInitFailed', evData); 63 | if (callback) { callback(err, null); } 64 | } else { 65 | self.emit('sensorInitCompleted', evData); 66 | if (callback) { callback(null, true); } 67 | } 68 | }); 69 | }; 70 | 71 | BH1750.prototype.setPowerMode = function(newMode, callback) { 72 | var self = this; 73 | 74 | if (_.has(BH1750.powerModes, newMode) === false) { 75 | var err = new Error('wrong powermode value in set powermode command'); 76 | var ts = Math.round(+new Date() / 1000); 77 | var evData = { 78 | 'addr': self.options.address, 79 | 'type': 'BH1750', 80 | 'setting': 'powerMode', 81 | 'newValue': newMode, 82 | 'ts': ts, 83 | 'error': err 84 | }; 85 | self.emit('sensorSettingFailed', evData); 86 | if (callback) { callback(err, null); } 87 | return; 88 | } 89 | 90 | async.waterfall([ 91 | function(cB) { 92 | var writeVal; 93 | 94 | writeVal = BH1750.powerModes[newMode]; 95 | 96 | self.i2c.writeByte(writeVal, function(err) { 97 | if (err) { 98 | cB(new Error('powermode not set on write'), 'write'); 99 | } else { 100 | cB(null, 'write'); 101 | } 102 | }); 103 | } 104 | ], 105 | function(err, results) { 106 | var ts = Math.round(+new Date() / 1000); 107 | var evData = { 108 | 'addr': self.options.address, 109 | 'type': 'BH1750', 110 | 'setting': 'powerMode', 111 | 'newValue': newMode, 112 | 'ts': ts, 113 | 'error': err 114 | }; 115 | if (err) { 116 | self.emit('sensorSettingFailed', evData); 117 | if (callback) { callback(err, null); } 118 | } else { 119 | self.options.powerMode = newMode; 120 | self.emit('sensorSettingChanged', evData); 121 | if (callback) { callback(null, newMode); } 122 | } 123 | }); 124 | }; 125 | 126 | BH1750.prototype.getLight = function(callback) { 127 | var self = this; 128 | 129 | var measurementMode = BH1750.measurementModes[self.options.measurementMode] || 130 | BH1750.measurementModes.Continuous_H_Resolution_Mode; 131 | 132 | var hi = 0, lo = 0, li; 133 | 134 | self.i2c.readBytes(measurementMode, 2, function(err, bytes) { 135 | if (err) { 136 | callback(err); 137 | } else { 138 | hi = bytes.readUInt8(0); 139 | lo = bytes.readUInt8(1); 140 | li = (hi << 8) + lo; 141 | li /= 1.2; 142 | 143 | callback(null, li); 144 | } 145 | }); 146 | }; 147 | 148 | BH1750.prototype.getLux = function(callback) { 149 | var self = this; 150 | 151 | async.series([ 152 | function(cB) { 153 | self.getLight(cB); 154 | } 155 | ], function(err, results) { 156 | //logger.info(results) 157 | var ts = Math.round(+new Date() / 1000); 158 | var evData = { 159 | 'addr': self.options.address, 160 | 'type': 'BH1750', 161 | 'valType': 'light', 162 | 'ts': ts, 163 | 'error': err 164 | }; 165 | 166 | if (err) { 167 | self.emit('sensorValueError', evData); 168 | if (callback) { callback(err, null); } 169 | } else if (_.isUndefined(results[0]) || _.isNull(results[0]) || results[0] < 0) { 170 | var e = new Error('invalid value(s) from sensor'); 171 | evData.error = e; 172 | self.emit('sensorValueError', evData); 173 | if (callback) { callback(e, null); } 174 | } else { 175 | evData.sensVal = results[0]; 176 | self.emit('newSensorValue', evData); 177 | if (callback) { callback(null, results[0]); } 178 | } 179 | }); 180 | }; 181 | 182 | module.exports = BH1750; 183 | -------------------------------------------------------------------------------- /lib/sensor/driver/digitalLight/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Sensor = require('../index').Sensor, 4 | util = require('util'), 5 | _cached = {retries: 0}, 6 | logger = Sensor.getLogger(), 7 | TSL2561 = require('sensor_tsl2561'), 8 | BH1750 = require('./BH1750'); 9 | 10 | var I2C_DEVICE = '/dev/i2c-', 11 | I2C_BUS_ID = 1, 12 | UART_DEVICE = '/dev/ttyO', 13 | UART_ADDRESS_ID = 4; 14 | 15 | function DigitalLight(sensorInfo, options) { 16 | var device, sensOptions; 17 | 18 | logger.info('[DigitalLight/Driver] sensorInfo, options', sensorInfo, options); 19 | 20 | Sensor.call(this, sensorInfo, options); 21 | 22 | if (sensorInfo.model) { 23 | this.model = sensorInfo.model; 24 | } else if (options && options.model) { 25 | this.model = options.model; 26 | } 27 | 28 | if (sensorInfo.device.sensorNetwork && sensorInfo.device.address) { 29 | 30 | if (sensorInfo.device.sensorNetwork === 'i2c') { 31 | device = I2C_DEVICE + (sensorInfo.device.bus || I2C_BUS_ID); 32 | } else if (sensorInfo.device.sensorNetwork === 'uart') { 33 | device = UART_DEVICE + (sensorInfo.device.address || UART_ADDRESS_ID); 34 | } 35 | 36 | switch (this.model) { 37 | case 'TSL2561': 38 | sensOptions = { 39 | address: sensorInfo.device.address, 40 | device: device 41 | }; 42 | 43 | this.wire = new TSL2561(sensOptions); 44 | 45 | this.wire.on('sensorSettingChanged', function (e) { 46 | logger.debug('[DigitalLight/Driver] sensorSettingChanged', e); 47 | }); 48 | this.wire.on('sensorSettingFailed', function (e) { 49 | logger.debug('[DigitalLight/Driver] sensorSettingFailed', e); 50 | }); 51 | this.wire.on('newSensorValue', function (e) { 52 | logger.debug('[DigitalLight/Driver] newSensorValue', e); 53 | }); 54 | this.wire.on('sensorValueError', function (e) { 55 | logger.debug('[DigitalLight/Driver] sensorValueError', e); 56 | }); 57 | 58 | this.wire.init(function (err, val) { 59 | if (err) { 60 | logger.error('[DigitalLight/Driver] TSL2561 - error on sensor init: ', err); 61 | } else { 62 | logger.debug('[DigitalLight/Driver] TSL2561 - sensor init completed: ', val); 63 | } 64 | }); 65 | 66 | break; 67 | case 'BH1750': 68 | sensOptions = { 69 | address: sensorInfo.device.address, 70 | device: device 71 | }; 72 | 73 | this.wire = new BH1750(sensOptions); 74 | 75 | this.wire.on('sensorSettingChanged', function (e) { 76 | logger.debug('[DigitalLight/Driver] sensorSettingChanged', e); 77 | }); 78 | this.wire.on('sensorSettingFailed', function (e) { 79 | logger.debug('[DigitalLight/Driver] sensorSettingFailed', e); 80 | }); 81 | this.wire.on('newSensorValue', function (e) { 82 | logger.debug('[DigitalLight/Driver] newSensorValue', e); 83 | }); 84 | this.wire.on('sensorValueError', function (e) { 85 | logger.debug('[DigitalLight/Driver] sensorValueError', e); 86 | }); 87 | 88 | this.wire.init(function (err, val) { 89 | if (err) { 90 | logger.error('[DigitalLight/Driver] BH1750 - error on sensor init: ', err); 91 | } else { 92 | logger.debug('[DigitalLight/Driver] BH1750 - sensor init completed: ', val); 93 | } 94 | }); 95 | 96 | break; 97 | default: 98 | logger.warn('[DigitalLight/Driver] ' + this.model + ' model is not supported'); 99 | } 100 | } else { 101 | logger.warn('[DigitalLight/Driver] sensor network or address is not provided'); 102 | } 103 | } 104 | 105 | DigitalLight.properties = { 106 | supportedNetworks: ['i2c'], 107 | dataTypes: ['light'], 108 | onChange: false, 109 | discoverable: false, 110 | addressable: true, 111 | recommendedInterval: 30000, 112 | maxInstances: 10, 113 | models: ['TSL2561', 'BH1750'], 114 | idTemplate: '{model}-{gatewayId}-{deviceAddress}', 115 | maxRetries: 8, 116 | category: 'sensor' 117 | }; 118 | 119 | util.inherits(DigitalLight, Sensor); 120 | 121 | /* 122 | * Get the light of a given sensor 123 | * @param sensor : The sensor ID 124 | * @param callback : callback (err, {id, value}) 125 | */ 126 | DigitalLight.prototype._get = function () { 127 | var self = this, rtn; 128 | 129 | try { 130 | switch (this.model) { 131 | case 'TSL2561': 132 | this.wire.getLux(function (err, data) { 133 | if (err) { 134 | rtn = {status: 'error', id : self.id, message: err.toString()}; 135 | } else { 136 | rtn = {status: 'ok', id : self.id, result: {'light': data}}; 137 | } 138 | 139 | logger.debug('[DigitalLight/Driver] TSL2561/I2C - data, rtn, info: ', data, rtn, self.info); 140 | 141 | self.emit('data', rtn); 142 | }); 143 | 144 | break; 145 | case 'BH1750': 146 | this.wire.getLux(function (err, data) { 147 | if (err) { 148 | rtn = {status: 'error', id : self.id, message: err.toString()}; 149 | } else { 150 | rtn = {status: 'ok', id : self.id, result: {'light': data}}; 151 | } 152 | 153 | logger.debug('[DigitalLight/Driver] BH1750/I2C - data, rtn, info: ', data, rtn, self.info); 154 | 155 | self.emit('data', rtn); 156 | }); 157 | 158 | break; 159 | default: 160 | logger.warn('[DigitalLight/Driver] ' + this.model + ' model is not supported'); 161 | rtn = {status: 'error', id : this.id, message: this.model + ' model is not supported'}; 162 | 163 | self.emit('data', rtn); 164 | } 165 | } catch (e) { 166 | logger.error('[DigitalLight/Driver] error', e); 167 | } 168 | }; 169 | 170 | module.exports = DigitalLight; 171 | -------------------------------------------------------------------------------- /lib/sensor/driver/ds18b20/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Sensor = require('../index').Sensor, 4 | util = require('util'), 5 | fs = require('fs'), 6 | _cached = {retries: 0}; 7 | 8 | function Ds18b20(sensorInfo, options) { 9 | Sensor.call(this, sensorInfo, options); 10 | } 11 | 12 | Ds18b20.properties = { 13 | supportedNetworks: ['w1'], 14 | dataTypes: ['temperature'], 15 | onChange: false, 16 | discoverable: true, 17 | addressable: false, 18 | recommendedInterval: 60000, 19 | maxInstances: 10, 20 | model: 'ds18b20', 21 | maxRetries: 8, 22 | validValueRange: [-55, 125], 23 | category: 'sensor' 24 | }; 25 | 26 | util.inherits(Ds18b20, Sensor); 27 | 28 | /* 29 | * Get the temperature of a given sensor 30 | * @param sensor : The sensor ID 31 | * @param callback : callback (err, {id, value}) 32 | */ 33 | Ds18b20.prototype._get = function () { 34 | var self = this, rtn; 35 | 36 | fs.readFile('/sys/bus/w1/devices/' + this.id + '/w1_slave', 'utf8', function (err, data) { 37 | if (err) { 38 | rtn = {status: 'off', id : self.id, message: err.toString()}; 39 | } else { 40 | var crcOk = data.match(/YES/g); 41 | 42 | if (crcOk) { 43 | var output = data.match(/t=(\-?\d+)/i); 44 | if (output) { 45 | var degree = output[1] / 1000; 46 | if (parseInt(output[1], 10) === 85000) { // The power-on reset value is +85 degree 47 | rtn = {status: 'error', id : self.id, message: 'power on'}; 48 | } else if (degree < Ds18b20.properties.validValueRange[0] || 49 | degree > Ds18b20.properties.validValueRange[1]) { 50 | rtn = {status: 'error', id : self.id, message: 'invalid range'}; 51 | } else { 52 | rtn = {status: 'ok', id : self.id, result: {'temperature': degree}}; 53 | } 54 | } else { // crc okay but invalid output 55 | rtn = {status: 'error', id : self.id, message: 'invalid output'}; 56 | } 57 | } else { // crc Check failed 58 | if (_cached.retries > Ds18b20.properties.maxRetries) { 59 | rtn = {status: 'error', id : self.id, message: 'crc check failed'}; 60 | } else { 61 | _cached.retries++; 62 | process.nextTick(function () {self._get(); }); 63 | return; 64 | } 65 | } 66 | } 67 | _cached.retries = 0; 68 | self.emit('data', rtn); 69 | }); 70 | }; 71 | 72 | module.exports = Ds18b20; 73 | -------------------------------------------------------------------------------- /lib/sensor/driver/fireAlarm/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Sensor = require('../index').Sensor, 4 | util = require('util'), 5 | fs = require('fs'), 6 | logger = Sensor.getLogger(); 7 | 8 | function FireAlarm(sensorInfo, options) { 9 | Sensor.call(this, sensorInfo, options); 10 | this._cached = {onoff: 0, onoffTime: -1, retries: 0}; 11 | 12 | } 13 | 14 | var THRESHOLD = 45, // in degree 15 | POLLING_INTERVAL = 5000, // 5 secs 16 | REPORT_INTERVAL = 30 * 60000; // 30min 17 | 18 | // report every REPORT_INTERVAL 19 | // polling temp sensor every POLLING_INTERVAL 20 | // 21 | FireAlarm.properties = { 22 | supportedNetworks: ['w1'], 23 | dataTypes: ['onoff'], 24 | onChange: true, 25 | discoverable: true, 26 | addressable: false, 27 | recommendedInterval: 60000, 28 | maxInstances: 10, 29 | model: 'fireAlarm', 30 | maxRetries: 8, 31 | validValueRange: [-55, 155], 32 | category: 'sensor' 33 | }; 34 | 35 | util.inherits(FireAlarm, Sensor); 36 | 37 | FireAlarm.prototype.checkFire = function(err, temp) { 38 | var self = this, 39 | now = Date.now(); 40 | 41 | if (temp && temp.status === 'ok') { 42 | if ( temp.result.temperature >= THRESHOLD) { 43 | temp.result.onoff = 1; 44 | } else { 45 | temp.result.onoff = 0; 46 | } 47 | } else { 48 | if (!temp) { temp = {status: 'error'}; } 49 | } 50 | 51 | if (this._cached.onoff !== (temp.result && temp.result.onoff) || 52 | (this._cached.onoffTime + REPORT_INTERVAL <= now)) { 53 | logger.debug('fireAlarm change', temp); 54 | self.emit('change', temp); 55 | this._cached.onoff = temp.result.onoff; 56 | this._cached.onoffTime = now; 57 | } 58 | }; 59 | 60 | FireAlarm.prototype._enableChange = function () { 61 | var self = this; 62 | 63 | if (!this.timer) { 64 | self.__get(self.checkFire.bind(self)); 65 | this.timer = setInterval(function () { 66 | self.__get(self.checkFire.bind(self)); 67 | }, POLLING_INTERVAL); 68 | } 69 | }; 70 | 71 | FireAlarm.prototype._clear = function () { 72 | if (this.timer) { 73 | clearInterval(this.timer); 74 | delete this.timer; 75 | } 76 | }; 77 | 78 | /* 79 | * Get the temperature of a given sensor 80 | */ 81 | FireAlarm.prototype.__get = function (cb) { 82 | var self = this, rtn; 83 | 84 | fs.readFile('/sys/bus/w1/devices/' + this.id + '/w1_slave', 'utf8', function (err, data) { 85 | if (err) { 86 | rtn = {status: 'off', id : self.id, message: err.toString()}; 87 | } else { 88 | var crcOk = data.match(/YES/g); 89 | 90 | if (crcOk) { 91 | var output = data.match(/t=(\-?\d+)/i); 92 | if (output) { 93 | var degree = output[1] / 1000; 94 | if (parseInt(output[1], 10) === 85000) { // The power-on reset value is +85 degree 95 | rtn = {status: 'error', id : self.id, message: 'power on'}; 96 | } else if (degree < FireAlarm.properties.validValueRange[0] || 97 | degree > FireAlarm.properties.validValueRange[1]) { 98 | rtn = {status: 'error', id : self.id, message: 'invalid range'}; 99 | } else { 100 | rtn = {status: 'ok', id : self.id, result: {'temperature': degree}}; 101 | } 102 | } else { // crc okay but invalid output 103 | rtn = {status: 'error', id : self.id, message: 'invalid output'}; 104 | } 105 | } else { // crc Check failed 106 | if (self._cached.retries > FireAlarm.properties.maxRetries) { 107 | rtn = {status: 'error', id : self.id, message: 'crc check failed'}; 108 | } else { 109 | self._cached.retries++; 110 | process.nextTick(function () {self.__get(); }); 111 | return; 112 | } 113 | } 114 | } 115 | self._cached.retries = 0; 116 | return cb && cb(null, rtn); 117 | }); 118 | }; 119 | 120 | FireAlarm.prototype._get = function () { 121 | var self = this; 122 | self.__get(function(err, rtn) { 123 | self.emit('data', rtn); 124 | }); 125 | }; 126 | 127 | module.exports = FireAlarm; 128 | -------------------------------------------------------------------------------- /lib/sensor/driver/gps/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Sensor = require('../index').Sensor, 3 | serialport = require('serialport'), 4 | util = require('util'), 5 | nmea = require('nmea'), 6 | logger = Sensor.getLogger(); 7 | 8 | function latLngToDecimal(coord){ 9 | if (coord === undefined) { 10 | return ; 11 | } 12 | var negative = (parseInt(coord, 10) < 0), 13 | decimal = null, 14 | match = coord.match(/^-?([0-9]*?)([0-9]{2,2}\.[0-9]*)$/), 15 | deg, min; 16 | 17 | if (match) { 18 | deg = parseInt(match[1], 10); 19 | min = parseFloat(match[2]); 20 | 21 | decimal = deg + (min / 60); 22 | if (negative){ 23 | decimal *= -1; 24 | } 25 | } 26 | 27 | return decimal.toFixed(5); 28 | } 29 | 30 | function Gps(sensorInfo, options) { 31 | var self = this, 32 | addr, port; 33 | 34 | Sensor.call(this, sensorInfo, options); 35 | addr = Number(this.info.device.address, 10); 36 | 37 | if (addr) { 38 | port = new serialport.SerialPort('/dev/ttyO' + addr, { 39 | baudrate: options.baudRate || 9600, 40 | parser: serialport.parsers.readline('\r\n') 41 | }); 42 | port.on('data', function(line) { 43 | var loc; 44 | try { 45 | loc = nmea.parse(line); 46 | } catch (e) { 47 | logger.error('nmea parse err', e); 48 | } 49 | if (loc && loc.lat && loc.lon) { 50 | self.lastLoc = { 51 | status: 'ok', 52 | id: self.id, 53 | result: { 54 | 'location': { 55 | lat: latLngToDecimal(loc.lat), 56 | lng: latLngToDecimal(loc.lon) 57 | } 58 | } 59 | }; 60 | logger.debug('lastLoc', self.lastLoc); 61 | if (self.onChange) { 62 | self.emit('change', self.lastLoc); 63 | } 64 | } 65 | }); 66 | } 67 | } 68 | 69 | Gps.properties = { 70 | supportedNetworks: ['uart'], 71 | dataTypes: ['location'], 72 | onChange: false, 73 | discoverable: false, 74 | addressable: true, 75 | recommendedInterval: 60000, 76 | maxInstances: 1, 77 | model: 'gps', 78 | idTemplate: '{model}-{gatewayId}', 79 | maxRetries: 8, 80 | address: 0, 81 | category: 'sensor' 82 | }; 83 | 84 | util.inherits(Gps, Sensor); 85 | 86 | /* 87 | * Get sensor data 88 | * @param sensor : The sensor ID 89 | * @param callback : callback (err, {id, value}) 90 | */ 91 | Gps.prototype._get = function () { 92 | var self = this; 93 | if (this.lastLoc) { //FIXME: check if too old one 94 | this.emit('data', this.lastLoc); 95 | } else { 96 | this.emit('data', {status: 'error', id : self.id, message: 'gps data is not available'}); 97 | } 98 | return; 99 | }; 100 | 101 | Gps.prototype._enableChange = function () { 102 | this.onChange = true; 103 | }; 104 | 105 | module.exports = Gps; 106 | -------------------------------------------------------------------------------- /lib/sensor/driver/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var EventEmitter = require('events').EventEmitter, 4 | util = require('util'), 5 | _ = require('lodash'), 6 | querystring = require('querystring'), 7 | sensorLib = require('../index'), 8 | logger = require('log4js').getLogger('Sensor'); 9 | 10 | function template(str, tokens) { 11 | return str.replace(/\{(\w+)\}/g, function (x, key) { 12 | return tokens[key]; 13 | }); 14 | } 15 | 16 | /* 17 | * Sensor 18 | * param options {Object} 19 | * getTimeout: timeout in ms, default recommendedInterval*2 20 | * connTimeout: timeout in ms, driver specific default 21 | */ 22 | function Sensor(sensorInfo, options) { 23 | var props = this.properties = this.constructor.properties; 24 | var id = sensorInfo.id; 25 | 26 | if (id) { 27 | this.id = id; 28 | } else { 29 | if (props && props.id) { 30 | this.id = template(props.id, options); 31 | } 32 | } 33 | this.info = sensorInfo; 34 | this.options = _.defaults(options || {}, sensorInfo.options); 35 | this.model = sensorInfo.model; 36 | this.device = sensorInfo.device; 37 | 38 | EventEmitter.call(this); 39 | } 40 | 41 | util.inherits(Sensor, EventEmitter); 42 | 43 | Sensor.properties = {}; 44 | Sensor.prototype.getProperties = function () { 45 | return sensorLib.getSensorProperties(this.model); 46 | }; 47 | 48 | // listen 'data' event once unless interval is provided and not zero. 49 | // listen 'change' event if 'change' is provided 50 | // previous listener stops before enabling new one. 51 | Sensor.prototype.listen = function (intervalOrEvent) { 52 | var self = this, 53 | props = this.getProperties(), 54 | interval, change; 55 | 56 | if (intervalOrEvent !== null && intervalOrEvent !== undefined) { 57 | if (typeof intervalOrEvent === 'number') { 58 | interval = intervalOrEvent; 59 | } else if (intervalOrEvent === 'change') { 60 | change = true; 61 | } 62 | } else { 63 | interval = props.recommendedInterval; 64 | } 65 | 66 | if (interval > 0) { 67 | if (this.listenTimer) { 68 | clearInterval(this.listenTimer); 69 | this.listenTimer = null; 70 | } 71 | this.listenTimer = setInterval(function () { 72 | self._get(); 73 | }, interval); 74 | self._get(); // get as soon as possible 75 | } else if (change) { 76 | self._enableChange(); 77 | } else { 78 | self._get(); 79 | } 80 | }; 81 | 82 | Sensor.prototype.stopListening = function () { 83 | if (this.listenTimer) { 84 | clearInterval(this.listenTimer); 85 | this.listenTimer = null; 86 | } 87 | }; 88 | 89 | Sensor.prototype.get = function (cb) { 90 | if (cb) { 91 | this.once('data', function (data) { 92 | return cb(null, data); 93 | }); 94 | } 95 | this._get(); 96 | }; 97 | 98 | Sensor.prototype.clear = function () { 99 | this.stopListening(); 100 | this.removeAllListeners(); 101 | return this._clear && this._clear(); 102 | }; 103 | 104 | Sensor.prototype.getStatus = function () { 105 | // FIXME: alternatively use event emittor 106 | throw new Error('NOT IMPLEMENTED'); 107 | }; 108 | 109 | Sensor.getLogger = function () { 110 | return logger; 111 | }; 112 | 113 | Sensor.prototype.close = function () {}; 114 | 115 | /* 116 | * Actuator 117 | */ 118 | function Actuator(sensorInfo, options) { 119 | var props = this.properties = this.constructor.properties; 120 | var id = sensorInfo.id; 121 | 122 | if (id) { 123 | this.id = id; 124 | } else { 125 | if (props && props.id) { 126 | this.id = template(props.id, options); 127 | } 128 | } 129 | this.info = sensorInfo; 130 | this.options = options; 131 | this.model = sensorInfo.model; 132 | 133 | EventEmitter.call(this); 134 | } 135 | util.inherits(Actuator, EventEmitter); 136 | 137 | Actuator.properties = {}; 138 | Actuator.prototype.getProperties = function () { 139 | return sensorLib.getSersorProperties(this.model); 140 | }; 141 | 142 | Actuator.prototype.set = function (cmd, options, cb) { 143 | var commands = _.isArray(this.properties.commands) ? 144 | this.properties.commands : (this.properties.commands && this.properties.commands[this.model]); 145 | if (!_.contains(commands, cmd)) { 146 | return cb && cb(new Error('unknown command')); 147 | } 148 | if (_.isFunction(this._set)) { 149 | this._set(cmd, options, cb); 150 | } else { 151 | return this[cmd](options, cb); 152 | } 153 | }; 154 | 155 | Actuator.prototype.clear = function (/*cmd, options, cb*/) { 156 | return this._clear && this._clear(); 157 | }; 158 | 159 | Actuator.prototype.getStatus = function () { 160 | return 'on'; 161 | }; 162 | 163 | Actuator.getLogger = function () { 164 | return logger; 165 | }; 166 | 167 | /* 168 | * Device 169 | */ 170 | function Device(network, addr, modelId, sensorInfos) { 171 | var self = this; 172 | this.network = network; 173 | this.address = addr; 174 | this.modelId = modelId; 175 | this.deviceHandle = {}; 176 | this.url = ['sensorjs://', network.sensorNetwork, addr].join('/'); 177 | this.sensorUrls = []; 178 | _.each(sensorInfos, function(sensorInfo) { 179 | var url = [self.url, sensorInfo.model, sensorInfo.id].join('/'); 180 | if (sensorInfo.options) { 181 | url += '?' + querystring.stringify(sensorInfo.options); 182 | } 183 | self.sensorUrls.push(url); 184 | if (sensorInfo.deviceHandle) { // FIXME: not to manage sensor connection 185 | //ex. futuretek snmp device connection 186 | self.deviceHandle[sensorInfo.id] = sensorInfo.deviceHandle; 187 | } 188 | }); 189 | EventEmitter.call(this); 190 | } 191 | 192 | util.inherits(Device, EventEmitter); 193 | 194 | Device.properties = {}; 195 | Device.prototype.getStatus = function () { 196 | // FIXME: alternatively use event emittor 197 | throw new Error('NOT IMPLEMENTED'); 198 | }; 199 | 200 | exports.Sensor = Sensor; 201 | exports.Actuator = Actuator; 202 | exports.Device = Device; 203 | -------------------------------------------------------------------------------- /lib/sensor/driver/magneticSwitch/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var OnOff = require('../onoff'), 4 | util = require('util'), 5 | _ = require('lodash'); 6 | 7 | var SWITCH_GPIO_NUM = 61; 8 | 9 | // MagneticSwitch constructor 10 | var MagneticSwitch = function(sensorInfo, options) { 11 | var gpioNum; 12 | 13 | if (sensorInfo.model) { 14 | this.model = sensorInfo.model; 15 | } else { 16 | if (options) { 17 | this.model = options.model; 18 | } 19 | } 20 | if (!options) { options = {}; } 21 | 22 | options.gpioPort = SWITCH_GPIO_NUM; 23 | gpioNum = Number(sensorInfo.device.address, 10); 24 | 25 | if (!_.isNaN(gpioNum)) { 26 | options.gpioPort = gpioNum; 27 | } else { 28 | console.warn('MagneticSwitch sensor address is not provided'); 29 | } 30 | 31 | OnOff.call(this, sensorInfo, options, 'onoff'); 32 | }; 33 | 34 | MagneticSwitch.properties = { 35 | supportedNetworks: ['gpio'], 36 | dataTypes: ['onoff'], 37 | onChange: true, 38 | discoverable: false, 39 | addressable: true, 40 | recommendedInterval: 60000, 41 | maxInstances: 1, 42 | models: ['normallyOpen', 'normallyClose'], 43 | idTemplate: '{model}-{gatewayId}-{deviceAddress}', 44 | maxRetries: 1, 45 | category: 'sensor' 46 | }; 47 | 48 | util.inherits(MagneticSwitch, OnOff); 49 | 50 | MagneticSwitch.prototype._convertValue = function (value) { 51 | if (this.model === 'normallyOpen') { 52 | if (value === 0) { value = 1; } 53 | else { value = 0; } 54 | } 55 | 56 | return value; 57 | }; 58 | 59 | module.exports = MagneticSwitch; 60 | 61 | 62 | -------------------------------------------------------------------------------- /lib/sensor/driver/mg811/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Sensor = require('../index').Sensor, 4 | util = require('util'), 5 | fs = require('fs'), 6 | _cached = {retries: 0}, 7 | logger = Sensor.getLogger(); 8 | 9 | var ocpPaths = [ 10 | '/sys/devices/ocp.2/helper.15', 11 | '/sys/devices/ocp.3/helper.16' 12 | ]; 13 | 14 | // TODO: calculate ppm from the measured analog input(0 ~ 1800 in the case of Beaglebone black analog input) 15 | function getPPM(volMeasured) { 16 | var ppm; 17 | 18 | ppm = parseInt(volMeasured, 10); 19 | 20 | return ppm; 21 | } 22 | 23 | function Mg811(sensorInfo, options) { 24 | Sensor.call(this, sensorInfo, options); 25 | } 26 | 27 | Mg811.properties = { 28 | supportedNetworks: ['analog'], 29 | dataTypes: ['co2'], 30 | onChange: false, 31 | discoverable: false, 32 | addressable: true, 33 | recommendedInterval: 60000, 34 | maxInstances: 1, 35 | model: 'mg811', 36 | idTemplate: '{model}-{gatewayId}-{deviceAddress}', 37 | maxRetries: 8, 38 | address: 0, 39 | category: 'sensor' 40 | }; 41 | 42 | util.inherits(Mg811, Sensor); 43 | 44 | /* 45 | * Get the CO2 of a given sensor 46 | * @param sensor : The sensor ID 47 | * @param callback : callback (err, {id, value}) 48 | */ 49 | Mg811.prototype._get = function () { 50 | var self = this, rtn; 51 | 52 | if (!_cached.ocpPath) { 53 | ocpPaths.forEach(function (ocpPath) { 54 | if (fs.existsSync(ocpPath)) { 55 | _cached.ocpPath = ocpPath; 56 | return false; 57 | } 58 | }); 59 | } 60 | 61 | fs.readFile(_cached.ocpPath + '/AIN' + (this.info.address || Mg811.properties.address), 62 | 'utf8', function (err, data) { 63 | if (err) { 64 | rtn = {status: 'off', id : self.id, message: err.toString()}; 65 | } else { 66 | rtn = {status: 'ok', id : self.id, result: {'co2': getPPM(data)}}; 67 | } 68 | 69 | logger.info('[MG811-CO2] data, rtn, info.address, properties.address, ocpPath - ', 70 | data, rtn, self.info.address, Mg811.properties.address, _cached.ocpPath); 71 | 72 | self.emit('data', rtn); 73 | }); 74 | }; 75 | 76 | module.exports = Mg811; 77 | -------------------------------------------------------------------------------- /lib/sensor/driver/motionDetector/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var OnOff = require('../onoff'), 4 | util = require('util'), 5 | _ = require('lodash'); 6 | var logger = require('../index').Sensor.getLogger(); 7 | 8 | //logger.setLevel('debug'); 9 | 10 | var SWITCH_GPIO_NUM = 60; 11 | var GRACE_PERIOD = 10000; 12 | //var MAX_COUNT_IN_GRACE_PERIOD = 10; 13 | 14 | // MotionDetector (model: PassiveInfrared) constructor 15 | var MotionDetector = function(sensorInfo, options) { 16 | var gpioNum; 17 | 18 | if (sensorInfo.model) { 19 | this.model = sensorInfo.model; 20 | } else { 21 | if (options) { 22 | this.model = options.model; 23 | } 24 | } 25 | if (!options) { options = {}; } 26 | 27 | options.gpioPort = SWITCH_GPIO_NUM; 28 | gpioNum = Number(sensorInfo.device.address, 10); 29 | 30 | if (!_.isNaN(gpioNum)) { 31 | options.gpioPort = gpioNum; 32 | } else { 33 | console.warn('MotionDetector sensor address is not provided'); 34 | } 35 | 36 | this.savedValue = 0; 37 | 38 | OnOff.call(this, sensorInfo, options, 'motion'); 39 | }; 40 | 41 | MotionDetector.properties = { 42 | supportedNetworks: ['gpio'], 43 | dataTypes: ['motion'], 44 | onChange: true, 45 | discoverable: false, 46 | addressable: true, 47 | recommendedInterval: 60000, 48 | maxInstances: 1, 49 | models: ['passiveInfrared'], 50 | idTemplate: '{model}-{gatewayId}-{deviceAddress}', 51 | maxRetries: 1, 52 | category: 'sensor' 53 | }; 54 | 55 | util.inherits(MotionDetector, OnOff); 56 | 57 | MotionDetector.prototype.__triggerEvent = function(currValue) { 58 | var self = this; 59 | 60 | if (self.button) { 61 | var previous = self._cached.rtn; 62 | 63 | self._cached.rtn = {status: 'ok', id: self.id, result: {}}; 64 | self._cached.rtn.result[self.type] = currValue; 65 | self._cached.value = currValue; 66 | 67 | logger.debug('[MotionDetector] triggerEvent() - emit change \ncurrent =', self._cached.rtn, 68 | '\nprevious =', previous); 69 | 70 | self.emit('change', self._cached.rtn, previous); 71 | } 72 | }; 73 | 74 | MotionDetector.prototype._enableChange = function() { 75 | var self = this; 76 | 77 | this.emit('change', this._cached.rtn); 78 | 79 | this.button.watch(function(err, currValue) { 80 | if (err) { 81 | logger.error('[MotionDetector] enableChange() watch fail!', self._cached.rtn, self.button); 82 | return; 83 | } 84 | logger.debug('[MotionDetector] saved/currValue', self.savedValue, currValue); 85 | 86 | if (self.timer) { 87 | logger.debug('[MotionDetector] reset timer - value = ', currValue); 88 | clearTimeout(self.timer); 89 | self.timer = setTimeout(function() { 90 | logger.debug('[MotionDetector] timeout'); 91 | self.__triggerEvent(0); 92 | self.timer = null; 93 | }, GRACE_PERIOD); 94 | } else if (currValue === 1) { 95 | self.__triggerEvent(currValue); 96 | self.timer = setTimeout(function() { 97 | logger.debug('[MotionDetector] timeout'); 98 | self.__triggerEvent(0); 99 | self.timer = null; 100 | }, GRACE_PERIOD); 101 | } else { 102 | logger.debug('[MotionDetector] ignore'); 103 | } 104 | }); 105 | }; 106 | 107 | MotionDetector.prototype._clear = function() { 108 | if (this.timer) { 109 | clearTimeout(this.timer); 110 | delete this.timer; 111 | } 112 | }; 113 | 114 | module.exports = MotionDetector; 115 | 116 | 117 | -------------------------------------------------------------------------------- /lib/sensor/driver/onoff.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Sensor = require('./index').Sensor, 4 | util = require('util'); 5 | var logger = Sensor.getLogger(); 6 | 7 | function OnOff(sensorInfo, options, type) { 8 | Sensor.call(this, sensorInfo, options); 9 | this.type = type; 10 | this.init(options.gpioPort); 11 | } 12 | 13 | util.inherits(OnOff, Sensor); 14 | 15 | OnOff.prototype.init = function (gpioPort) { 16 | var Gpio = require('onoff').Gpio; 17 | 18 | this._cached = {value: null, rtn: {}}; 19 | this.button = new Gpio(gpioPort, 'in', 'both'); 20 | 21 | if (this.button.direction() !== 'in' || this.button.edge() !== 'both') { 22 | this.button.unexport(); // delete existing gpio configuration and open again. 23 | 24 | this.button = new Gpio(gpioPort, 'in', 'both'); 25 | if (this.button.direction() !== 'in' || this.button.edge() !== 'both') { 26 | this._cached.rtn = {status: 'error', id: this.id, 27 | message: 'configuration error gpio=' + gpioPort}; 28 | logger.error('[OnOff] init failure ', this._cached.rtn.message); 29 | return; 30 | } 31 | } 32 | 33 | this._readValue(); 34 | 35 | logger.info('[OnOff] init success', this._cached.rtn.result); 36 | }; 37 | 38 | OnOff.prototype._readValue = function () { 39 | var value = this.button.readSync(); 40 | if (value !== 0 && value !== 1) { 41 | logger.error('[OnOff] invalid value=', value); 42 | value = 0; 43 | } 44 | 45 | value = this._convertValue(value); 46 | 47 | this._cached.value = value; 48 | this._cached.rtn = {status: 'ok', id: this.id, result: {}}; 49 | this._cached.rtn.result[this.type] = this._cached.value; 50 | 51 | return value; 52 | }; 53 | 54 | OnOff.prototype._get = function () { 55 | this.emit('data', this._cached.rtn); 56 | }; 57 | 58 | OnOff.prototype._convertValue = function (value) { 59 | return value; 60 | }; 61 | 62 | OnOff.prototype._enableChange = function () { 63 | var self = this; 64 | 65 | logger.info('OnOff._enableChange() change first', this._cached.rtn, this.button); 66 | 67 | this.emit('change', this._cached.rtn); 68 | 69 | this.button.watch(function(err, currValue) { 70 | var previous; 71 | 72 | if (err) { 73 | logger.error('OnOff._enableChange() watch fail!', self._cached.rtn, self.button); 74 | return; 75 | } 76 | 77 | if (self.button) { 78 | currValue = self._convertValue(currValue); 79 | if (self._cached.value !== currValue) { 80 | previous = self._cached.rtn; 81 | self._cached.rtn = {status: 'ok', id: self.id, result: {}}; 82 | self._cached.rtn.result[self.type] = currValue; 83 | self._cached.value = currValue; 84 | 85 | logger.debug('OnOff._enableChange()-watch emit change \ncurren=', self._cached.rtn, 86 | '\nprevious=', previous); 87 | 88 | self.emit('change', self._cached.rtn, previous); 89 | } 90 | } 91 | }); 92 | }; 93 | 94 | module.exports = OnOff; 95 | -------------------------------------------------------------------------------- /lib/sensor/driver/photocell/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Sensor = require('../index').Sensor, 4 | util = require('util'), 5 | fs = require('fs'), 6 | _cached = {retries: 0}, 7 | logger = Sensor.getLogger(); 8 | 9 | var ocpPaths = [ 10 | '/sys/devices/ocp.2/helper.15', 11 | '/sys/devices/ocp.3/helper.16' 12 | ]; 13 | 14 | // TODO: calculate lux from the measured analog input(0 ~ 1800 in the case of Beaglebone black analog input) 15 | function getLux(volMeasured) { 16 | var lux; 17 | 18 | lux = parseInt(volMeasured, 10); 19 | 20 | lux = 2.3 * lux - 3200; 21 | lux = lux >= 0 ? lux : 0; 22 | 23 | return lux; 24 | } 25 | 26 | function Photocell(sensorInfo, options) { 27 | Sensor.call(this, sensorInfo, options); 28 | } 29 | 30 | Photocell.properties = { 31 | supportedNetworks: ['analog'], 32 | dataTypes: ['light'], 33 | onChange: false, 34 | discoverable: false, 35 | addressable: true, 36 | recommendedInterval: 60000, 37 | maxInstances: 1, 38 | model: 'photocell', 39 | idTemplate: '{model}-{gatewayId}-{deviceAddress}', 40 | maxRetries: 8, 41 | address: 2, 42 | category: 'sensor' 43 | }; 44 | 45 | util.inherits(Photocell, Sensor); 46 | 47 | /* 48 | * Get the light of a given sensor 49 | * @param sensor : The sensor ID 50 | * @param callback : callback (err, {id, value}) 51 | */ 52 | Photocell.prototype._get = function () { 53 | var self = this, rtn; 54 | 55 | if (!_cached.ocpPath) { 56 | ocpPaths.forEach(function (ocpPath) { 57 | if (fs.existsSync(ocpPath)) { 58 | _cached.ocpPath = ocpPath; 59 | return false; 60 | } 61 | }); 62 | } 63 | 64 | fs.readFile(_cached.ocpPath + '/AIN' + (this.info.address || Photocell.properties.address), 65 | 'utf8', function (err, data) { 66 | if (err) { 67 | rtn = {status: 'off', id : self.id, message: err.toString()}; 68 | } else { 69 | rtn = {status: 'ok', id : self.id, result: {'light': getLux(data)}}; 70 | } 71 | 72 | logger.info('[photocell] data, rtn, info.address, properties.address, ocpPath - ', 73 | data, rtn, self.info.address, Photocell.properties.address, _cached.ocpPath); 74 | 75 | self.emit('data', rtn); 76 | }); 77 | }; 78 | 79 | module.exports = Photocell; 80 | -------------------------------------------------------------------------------- /lib/sensor/driver/powerSource/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | (function (callback) { 3 | var I2c = require('i2c'); 4 | 5 | var I2C_BUS_0 = '/dev/i2c-0', // TPS65217C is on ic2 bus 0 6 | TPS65217C_I2C_ID = 0x24, // TPS65217C's i2c id 7 | TPS65217C_STATUS_REG = 0x0a, // TPS65217C's status register address 8 | AC_FLAG = 0x08, 9 | USB_FLAG = 0x04; 10 | 11 | var wire = new I2c(TPS65217C_I2C_ID, {device: I2C_BUS_0}); 12 | 13 | wire.readBytes(TPS65217C_STATUS_REG, 1, function (err, buf) { 14 | if (!err && buf.length === 1) { 15 | return callback(null, { 16 | status: 'ok', 17 | result: { 18 | power: { 19 | usb: buf[0] & USB_FLAG ? true : false, 20 | ac: buf[0] & AC_FLAG ? true : false 21 | } 22 | } 23 | }); 24 | } else { 25 | return callback(new Error('i2c read'), { 26 | status: 'error', 27 | message: 'i2c read failure', 28 | }); 29 | } 30 | }); 31 | })(function (err, result) { 32 | console.log("err=", err, "result=", result); 33 | }); 34 | -------------------------------------------------------------------------------- /lib/sensor/driver/powerSwitch/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Gpio = require('onoff').Gpio, // Constructor function for Gpio objects. 3 | util = require('util'), 4 | Actuator = require('../index').Actuator; 5 | 6 | var logger = Actuator.getLogger(); 7 | 8 | var DEFAULT_BLINK_INTERVAL = 5000; 9 | 10 | // PowerSwitch constructor 11 | var PowerSwitch = function (sensorInfo, options) { 12 | Actuator.call(this, sensorInfo, options); 13 | 14 | if (sensorInfo.device.address) { 15 | this.gpio = new Gpio(sensorInfo.device.address, 'out'); 16 | logger.info('powerSwitch sensor is created at driver', sensorInfo); 17 | } else { 18 | logger.warn('powerSwitch sensor address is not provided'); 19 | } 20 | }; 21 | 22 | PowerSwitch.properties = { 23 | supportedNetworks: ['gpio'], 24 | dataTypes: ['powerSwitch'], 25 | discoverable: false, 26 | addressable: true, 27 | maxInstances: 5, 28 | idTemplate: '{model}-{gatewayId}-{deviceAddress}', 29 | model: 'powerSwitch', 30 | commands: ['on', 'off', 'blink'], 31 | category: 'actuator' 32 | }; 33 | util.inherits(PowerSwitch, Actuator); 34 | 35 | /* Turn powerSwitch on */ 36 | /* options.duration: infinite if NaN, null, undefined, zero or minus */ 37 | PowerSwitch.prototype.on = function (options, cb) { 38 | var self = this, 39 | duration = options && Number(options.duration, 10); 40 | 41 | this._clear(); 42 | 43 | if (!duration || duration <= 0) { 44 | duration = 0; 45 | } 46 | 47 | this.gpio.writeSync(1); 48 | 49 | if (duration) { 50 | this.offTimer = setTimeout(function () { 51 | self.offTimer = null; 52 | self.off(); 53 | }, duration); 54 | } 55 | 56 | logger.info('[PowerSwitch] on command with ', options); 57 | 58 | return cb && typeof cb === 'function' && cb(null, 'on'); 59 | }; 60 | /* Turn powerSwitch off */ 61 | PowerSwitch.prototype.off = function (options, cb) { 62 | this._clear(); 63 | this.gpio.writeSync(0); 64 | 65 | logger.info('[PowerSwitch] off command with ', options); 66 | return cb && typeof cb === 'function' && cb(null, 'off'); 67 | }; 68 | /* Blink 69 | * interval: blink interval(default: 5 sec) 70 | * options.duration: infinite if NaN, null, undefined, zero or minus 71 | */ 72 | PowerSwitch.prototype.blink = function (options, cb) { 73 | var self = this, 74 | interval = options && Number(options.interval, 10), 75 | duration = options && Number(options.duration, 10); 76 | 77 | this._clear(); 78 | 79 | if (!interval || interval <= 0) { 80 | interval = DEFAULT_BLINK_INTERVAL; 81 | } 82 | 83 | 84 | if (!duration || duration <= 0) { 85 | duration = 0; 86 | } 87 | 88 | this.blinkTimer = setInterval(function () { 89 | self.gpio.read(function (err, value) { 90 | if (err) { throw err; } 91 | self.gpio.write(value === 0 ? 1 : 0, function (err) {if (err) { throw err; }}); 92 | }); 93 | }, interval || DEFAULT_BLINK_INTERVAL); 94 | 95 | if (duration) { 96 | this.offTimer = setTimeout(function () { 97 | clearInterval(self.blinkTimer); 98 | self.blinkTimer = null; 99 | self.offTimer = null; 100 | self.off(); 101 | }, duration); 102 | } 103 | 104 | return cb && typeof cb === 'function' && cb(null, 'blink'); 105 | }; 106 | /* Clear powerSwitch */ 107 | PowerSwitch.prototype._clear = function () { 108 | if (this.blinkTimer) { 109 | clearInterval(this.blinkTimer); 110 | this.blinkTimer = null; 111 | } 112 | if (this.offTimer) { 113 | clearTimeout(this.offTimer); 114 | this.offTimer = null; 115 | } 116 | }; 117 | 118 | module.exports = PowerSwitch; 119 | -------------------------------------------------------------------------------- /lib/sensor/driver/rgbLed/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var Gpio = require('onoff').Gpio, // Constructor function for Gpio objects. 3 | util = require('util'), 4 | Actuator = require('../index').Actuator; 5 | 6 | var logger = Actuator.getLogger(); 7 | 8 | var DEFAULT_BLINK_INTERVAL = 1000; 9 | 10 | // Led constructor 11 | var Led = function (sensorInfo, options) { 12 | Actuator.call(this, sensorInfo, options); 13 | 14 | if (sensorInfo.device.address) { 15 | this.gpio = new Gpio(sensorInfo.device.address, 'out'); 16 | logger.info('Led sensor is created at driver', sensorInfo); 17 | } else { 18 | logger.warn('Led sensor address is not provided'); 19 | } 20 | }; 21 | 22 | Led.properties = { 23 | supportedNetworks: ['gpio'], 24 | dataTypes: ['led'], 25 | discoverable: false, 26 | addressable: true, 27 | maxInstances: 5, 28 | idTemplate: '{model}-{gatewayId}-{deviceAddress}', 29 | model: 'rgbLed', 30 | commands: ['on', 'off', 'blink'], 31 | category: 'actuator' 32 | }; 33 | util.inherits(Led, Actuator); 34 | 35 | /* Turn LED on */ 36 | /* options.duration: infinite if NaN, null, undefined, zero or minus */ 37 | Led.prototype.on = function (options, cb) { 38 | var self = this, 39 | duration = options && Number(options.duration, 10); 40 | 41 | this._clear(); 42 | 43 | if (!duration || duration <= 0) { 44 | duration = 0; 45 | } 46 | 47 | this.gpio.writeSync(1); 48 | 49 | if (duration) { 50 | this.offTimer = setTimeout(function () { 51 | self.offTimer = null; 52 | self.off(); 53 | }, duration); 54 | } 55 | 56 | logger.info('[rgbLed] on command with ', options); 57 | 58 | return cb && typeof cb === 'function' && cb(null, 'on'); 59 | }; 60 | /* Turn LED off */ 61 | Led.prototype.off = function (options, cb) { 62 | this._clear(); 63 | this.gpio.writeSync(0); 64 | 65 | logger.info('[rgbLed] off command with ', options); 66 | return cb && typeof cb === 'function' && cb(null, 'off'); 67 | }; 68 | /* Blink 69 | * interval: blink interval(default: 5 sec) 70 | * options.duration: infinite if NaN, null, undefined, zero or minus 71 | */ 72 | Led.prototype.blink = function (options, cb) { 73 | var self = this, 74 | interval = options && Number(options.interval, 10), 75 | duration = options && Number(options.duration, 10); 76 | 77 | this._clear(); 78 | 79 | if (!interval || interval <= 0) { 80 | interval = DEFAULT_BLINK_INTERVAL; 81 | } 82 | 83 | if (!duration || duration <= 0) { 84 | duration = 0; 85 | } 86 | 87 | this.blinkTimer = setInterval(function () { 88 | self.gpio.read(function (err, value) { 89 | if (err) { throw err; } 90 | self.gpio.write(value === 0 ? 1 : 0, function (err) {if (err) { throw err; }}); 91 | }); 92 | }, interval || DEFAULT_BLINK_INTERVAL); 93 | 94 | if (duration) { 95 | this.offTimer = setTimeout(function () { 96 | clearInterval(self.blinkTimer); 97 | self.blinkTimer = null; 98 | self.offTimer = null; 99 | self.off(); 100 | }, duration); 101 | } 102 | 103 | return cb && typeof cb === 'function' && cb(null, 'blink'); 104 | }; 105 | /* Clear LED */ 106 | Led.prototype._clear = function () { 107 | if (this.blinkTimer) { 108 | clearInterval(this.blinkTimer); 109 | this.blinkTimer = null; 110 | } 111 | if (this.offTimer) { 112 | clearTimeout(this.offTimer); 113 | this.offTimer = null; 114 | } 115 | }; 116 | 117 | module.exports = Led; 118 | -------------------------------------------------------------------------------- /lib/sensor/driver_cfg.json: -------------------------------------------------------------------------------- 1 | { 2 | "dht": ["dht11", "dht22"], 3 | "digitalLight": ["TSL2561", "BH1750"], 4 | "digitalCO2": ["X100"], 5 | "digitalHumidity": ["HTU21D"], 6 | "magneticSwitch": ["normallyClose", "normallyOpen"], 7 | "motionDetector": ["passiveInfrared"] 8 | } 9 | -------------------------------------------------------------------------------- /lib/sensor/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var url = require('url'), 4 | path = require('path'), 5 | _ = require('lodash'), 6 | querystring = require('querystring'), 7 | logger = require('log4js').getLogger('Sensor'), 8 | Network = require('./network/index'), 9 | Driver = require('./driver/index'); 10 | 11 | var _cachedDrivers = {}, 12 | _cachedNetworks = {}, 13 | DRIVER_CFG_PATH = path.join(__dirname, 'driver_cfg.json'), 14 | DRIVER_CFG = require(DRIVER_CFG_PATH); 15 | 16 | var addSensorPackage = function (sensor) { 17 | var networks, drivers; 18 | 19 | // Step 1. Setup(Assign) super constructors 20 | _.defaults(sensor, exports); 21 | sensor.Network = Network; 22 | sensor.Device = Driver.Device; 23 | sensor.Sensor = Driver.Sensor; 24 | sensor.Actuator = Driver.Actuator; 25 | 26 | try { 27 | // Step 2. Init sensor(child) constructors(network, drivers) 28 | networks = sensor.initNetworks(); 29 | drivers = sensor.initDrivers(); 30 | 31 | // Step 3. Register network and drivers 32 | _.forEach(sensor.networks, function (networkName) { 33 | _cachedNetworks[networkName] = networks[networkName]; 34 | }); 35 | 36 | _.forEach(sensor.drivers, function (models, driverName) { 37 | DRIVER_CFG[driverName] = models; 38 | 39 | _.forEach(models, function (model) { 40 | _cachedDrivers[model] = drivers[driverName]; 41 | }); 42 | 43 | _cachedDrivers[driverName] = drivers[driverName]; 44 | //logger.info('[sensor/index.js] updated DRIVER_CFG[' + driverName + ']', DRIVER_CFG[driverName]); 45 | }); 46 | } catch (e) { 47 | logger.error('[sensor/index.js] addSensorPackage', e); 48 | } 49 | 50 | //log4js log level doesn't apply on init probably 51 | //logger.debug('[sensor/index.js] _cachedNetworks', _cachedNetworks); 52 | //logger.debug('[sensor/index.js] _cachedDrivers', _cachedDrivers); 53 | //logger.debug('[sensor/index.js] added sensor', sensor); 54 | //logger.debug('[sensor/index.js] updated DRIVER_CFG', DRIVER_CFG); 55 | }; 56 | 57 | var getSensorDriver = function (model) { 58 | if (!model) { 59 | return; 60 | } 61 | if (_cachedDrivers[model]) { 62 | return _cachedDrivers[model]; 63 | } 64 | try { 65 | _cachedDrivers[model] = require(path.join(__dirname, 'driver', model)); 66 | return _cachedDrivers[model]; 67 | } catch (e) {//try again after driver lookup 68 | try { 69 | var driver = _.findKey(DRIVER_CFG, function (models) { 70 | return _.contains(models, model); 71 | }); 72 | if (driver) { 73 | _cachedDrivers[model] = require(path.join(__dirname, 'driver', driver)); 74 | } else { 75 | logger.warn('getSensorDriver() driver not found / lookup driver', model); 76 | } 77 | return _cachedDrivers[model]; 78 | } catch (ee) { 79 | logger.warn('getSensorDriver() driver not found / model=', model, 'error=', ee); 80 | throw ee; 81 | } 82 | } 83 | }; 84 | 85 | var setSensorDriver = function(model, driver) { 86 | if (!model || !driver) { 87 | return; 88 | } 89 | if (!_cachedDrivers[model]) { 90 | _cachedDrivers[model] = _cachedDrivers[driver.driverName]; 91 | } 92 | _.merge(_cachedDrivers[model].properties, driver); 93 | }; 94 | 95 | var parseSensorUrl = function(sensorUrl) { 96 | var parsed = url.parse(sensorUrl); 97 | 98 | if (parsed.protocol !== 'sensorjs:' || !parsed.pathname) { return; } 99 | var splits = parsed.pathname.split('/'); 100 | return { 101 | device: { 102 | sensorNetwork: splits[1], 103 | address: splits[2] 104 | // TODO: add device id 105 | }, 106 | model: splits[3], 107 | id: splits[4], 108 | options: parsed.query && querystring.parse(parsed.query) 109 | }; 110 | }; 111 | 112 | var getUrl = function(sensorInfo) { 113 | var baseUrl = ['sensorjs://', sensorInfo.device.sensorNetwork, 114 | sensorInfo.device.address, sensorInfo.model, sensorInfo.id].join('/'); 115 | if (sensorInfo.options) { 116 | baseUrl += '?' + querystring.stringify(sensorInfo.options); 117 | } 118 | return baseUrl; 119 | }; 120 | 121 | var createSensor = function (sensorUrl, options) { 122 | var sensorInfo = parseSensorUrl(sensorUrl); 123 | try { 124 | var Sensor = getSensorDriver(sensorInfo.model); 125 | 126 | return Sensor && new Sensor(sensorInfo, _.defaults(options || {}, sensorInfo.options)); 127 | } catch (e) { 128 | logger.error('err=', e.stack); 129 | return null; 130 | } 131 | }; 132 | 133 | var getSensorProperties = function(model) { 134 | var sensorDriver; 135 | 136 | try { 137 | sensorDriver = getSensorDriver(model); 138 | 139 | if (sensorDriver && sensorDriver.properties) { 140 | var props = _.cloneDeep(sensorDriver.properties); // TODO: cloning on demand 141 | _.each(props, function (v, k) { 142 | if (_.isObject(v) && _.has(v, model)) { 143 | props[k] = v[model]; 144 | } 145 | }); 146 | return props; 147 | } else { 148 | return null; 149 | } 150 | } catch (e) { 151 | logger.error('exp=', e); 152 | return null; 153 | } 154 | }; 155 | 156 | var getNetwork = function(networkName) { 157 | if (_cachedNetworks[networkName]) { 158 | return _cachedNetworks[networkName]; 159 | } 160 | 161 | try { 162 | _cachedNetworks[networkName] = require('./network/' + networkName); 163 | return _cachedNetworks[networkName]; 164 | } catch (e) { 165 | logger.error('error to get network [%s]', networkName, e); 166 | } 167 | 168 | return; 169 | }; 170 | 171 | /** 172 | * Discover sensor/actuator IDs(or detail info Object) from the given sensor netowrk 173 | * 174 | * @param {String} driverName or model 175 | * @param {Object} options(optional) - TBD 176 | * @return {Cabllback} error and {Array} array of discovered IDs 177 | */ 178 | exports.discover = function (driverName/*or model*/, options, cb) { 179 | var networkName, network, props, models; 180 | 181 | if (typeof options === 'function') { 182 | cb = options; 183 | options = undefined; 184 | } 185 | 186 | if (driverName) { 187 | try { 188 | models = DRIVER_CFG[driverName]; 189 | if (!models) { 190 | models = [driverName]; 191 | } 192 | props = getSensorProperties(models[0]); 193 | networkName = props.supportedNetworks[0];//FIXME: cover multiple network 194 | } catch (e) { } 195 | } 196 | 197 | if (!driverName || !props || !networkName) { 198 | return cb && cb(new Error('invalid param')); 199 | } 200 | 201 | if (props.discoverable) { 202 | network = getNetwork(networkName); 203 | 204 | if (network) { 205 | if (options) { 206 | network.discover(driverName, options, cb); 207 | } else { 208 | network.discover(driverName, cb); 209 | } 210 | } else { 211 | return cb && cb(new Error('module not found: ' + networkName)); 212 | } 213 | } else { 214 | return cb && cb(new Error('not discoverable: ' + networkName)); 215 | } 216 | }; 217 | 218 | exports.discoverThingsOnNetwork = function (networkName, options, cb) { 219 | var network; 220 | 221 | if (typeof options === 'function') { 222 | cb = options; 223 | options = undefined; 224 | } 225 | 226 | if (!networkName) { 227 | return cb && cb(new Error('invalid param')); 228 | } 229 | 230 | network = getNetwork(networkName); 231 | 232 | if (network) { 233 | if (options) { 234 | network.discover(networkName, options, cb); 235 | } else { 236 | network.discover(networkName, cb); 237 | } 238 | } else { 239 | return cb && cb(new Error('module not found: ' + networkName)); 240 | } 241 | }; 242 | 243 | exports.getDriverConfig = function () { 244 | return DRIVER_CFG; 245 | }; 246 | 247 | exports.addSensorPackage = addSensorPackage; 248 | exports.getSensorDriver = getSensorDriver; 249 | exports.setSensorDriver = setSensorDriver; 250 | exports.parseSensorUrl = parseSensorUrl; 251 | exports.getUrl = getUrl; 252 | exports.createSensor = createSensor; 253 | exports.getSensorProperties = getSensorProperties; 254 | exports.getNetwork = getNetwork; 255 | -------------------------------------------------------------------------------- /lib/sensor/network/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var EventEmitter = require('events').EventEmitter, 4 | util = require('util'), 5 | _ = require('lodash'), 6 | logger = require('log4js').getLogger('Network'); 7 | 8 | /* 9 | * Network 10 | */ 11 | function Network(networkName, options) { 12 | var props = this.properties = this.constructor.properties; 13 | 14 | if (networkName) { 15 | this.sensorNetwork = networkName; 16 | } else { 17 | if (props && props.sensorNetwork) { 18 | this.sensorNetwork = props.sensorNetwork; 19 | } 20 | } 21 | this.options = options; 22 | 23 | EventEmitter.call(this); 24 | } 25 | 26 | util.inherits(Network, EventEmitter); 27 | 28 | Network.properties = { sensorNetwork: 'none' }; 29 | 30 | Network.prototype.discover = function(driverOfModel, options, cb) { 31 | if (typeof driverOfModel === 'function') { 32 | cb = driverOfModel; 33 | } else if (typeof options === 'function') { 34 | cb = options; 35 | } 36 | return cb && cb(new Error('NOT IMPLEMENTED')); 37 | }; 38 | 39 | Network.prototype.getDevice = function(addr, options, cb) { 40 | if (typeof options === 'function') { 41 | cb = options; 42 | } 43 | return cb && cb(new Error('NOT IMPLEMENTED')); 44 | }; 45 | 46 | Network.getLogger = function() { 47 | return logger; 48 | }; 49 | 50 | Network.prototype.close = function() {}; 51 | 52 | Network.getStatus = function() { 53 | return undefined; 54 | }; 55 | 56 | module.exports = Network; 57 | -------------------------------------------------------------------------------- /lib/sensor/network/w1/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var fs = require('fs'), 4 | util = require('util'), 5 | Network = require('../index'), 6 | Device = require('../../driver/index').Device; 7 | 8 | function W1(options) { 9 | Network.call(this, 'w1', options); 10 | } 11 | 12 | util.inherits(W1, Network); 13 | 14 | W1.prototype.discover = function (driverName, options, cb) { 15 | var self = this; 16 | var model = 'ds18b20'; // supporting only this model 17 | 18 | if (typeof driverName === 'function') { 19 | cb = driverName; 20 | } 21 | 22 | if (typeof options === 'function') { 23 | cb = options; 24 | options = null; 25 | } 26 | 27 | fs.readFile('/sys/bus/w1/devices/w1_bus_master1/w1_master_slaves', 'utf8', 28 | function (err, data) { 29 | if (err) { 30 | if (cb) { 31 | return cb(new Error('network failure')); 32 | } else { 33 | return self.emit('error', err); 34 | } 35 | } else { 36 | var parts = data.split('\n'); 37 | var devices = []; 38 | 39 | parts.pop(); 40 | if (parts.toString() === 'not found.') { 41 | if (cb) { 42 | return cb(new Error('There is no sensors')); 43 | } else { 44 | return self.emit('error', new Error('No sensor')); 45 | } 46 | } else { 47 | parts.forEach(function (addr) { 48 | // FIXME: Device model ID should be received via arguments. 49 | // sensorId is same as address. 50 | var device = new Device(self, addr, 'OneWire', [{ id:addr, model: model }]); 51 | devices.push(device); 52 | }); 53 | if (cb) { 54 | return cb(null, devices); 55 | } else { 56 | self.emit('discovered', devices); 57 | self.emit('done'); 58 | } 59 | } 60 | } 61 | } 62 | ); 63 | }; 64 | 65 | module.exports = new W1(); 66 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "sensorjs", 3 | "description": "sensor framework and beyond for internt of things", 4 | "version": "0.1.3", 5 | "author": { 6 | "name": "daliworks", 7 | "email": "dev@daliworks.net" 8 | }, 9 | "dependencies": { 10 | "async": "1.5.2", 11 | "lodash": "2.4", 12 | "log4js": "0.6", 13 | "i2c": "0.1.4", 14 | "onoff": "0.3.2", 15 | "underscore-query": "~0.4.0", 16 | "socket.io": "1.1", 17 | "socket.io-client": "1.1", 18 | "nmea": "0.0.7", 19 | "serialport": "~1.4.5", 20 | "sensor_tsl2561": "0.2.2" 21 | }, 22 | "repository": { 23 | "type": "git", 24 | "url": "git@github.com:daliworks/sensorjs" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/daliworks/sensorjs/issues" 28 | }, 29 | "main": "index", 30 | "keywords": [ 31 | "sensor", 32 | "sensor.js", 33 | "beaglebone", 34 | "raspberrypi", 35 | "1-wire", 36 | "gpio", 37 | "i2c", 38 | "driver" 39 | ], 40 | "engines": { 41 | "node": ">=0.8.x" 42 | }, 43 | "licenses": [ 44 | { 45 | "type": "MIT", 46 | "url": "https://github.com/daliworks/sensorjs/blob/msater/LICENSE" 47 | } 48 | ] 49 | } 50 | --------------------------------------------------------------------------------