├── .npmignore ├── .gitignore ├── docs ├── DS-HCHO.jpg ├── DS-HCHO.pdf ├── PMS5003.jpg └── PMS5003.pdf ├── package.json ├── example └── index.js ├── bin └── dsensor ├── index.js ├── README.md ├── LICENSE ├── AQI.js ├── PMS5003.js └── HCHO.js /.npmignore: -------------------------------------------------------------------------------- 1 | 2 | *.log 3 | 4 | docs/ 5 | node_modules/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | *.log 3 | .DS_Store 4 | yarn.lock 5 | 6 | node_modules/ -------------------------------------------------------------------------------- /docs/DS-HCHO.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsongdev/dsensor/HEAD/docs/DS-HCHO.jpg -------------------------------------------------------------------------------- /docs/DS-HCHO.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsongdev/dsensor/HEAD/docs/DS-HCHO.pdf -------------------------------------------------------------------------------- /docs/PMS5003.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsongdev/dsensor/HEAD/docs/PMS5003.jpg -------------------------------------------------------------------------------- /docs/PMS5003.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lsongdev/dsensor/HEAD/docs/PMS5003.pdf -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "dsensor", 3 | "version": "0.0.6", 4 | "main": "index.js", 5 | "bin": { 6 | "dsensor": "bin/dsensor" 7 | }, 8 | "repository": { 9 | "url": "git@github.com:song940/dsensor.git", 10 | "type": "git" 11 | }, 12 | "author": "Lsong ", 13 | "license": "MIT", 14 | "dependencies": { 15 | "kelp-cli": "*", 16 | "serialport": "8" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | const PMS5003 = require('../PMS5003'); 2 | const HCHO = require('../HCHO'); 3 | /* 4 | const hc = new HCHO("/dev/cu.SLAB_USBtoUART"); 5 | 6 | hc.on('message', message => { 7 | console.log(message.toString()); 8 | }); 9 | 10 | hc.on("open", () => { 11 | setInterval(() => { 12 | hc.send(0x01, 0x00); 13 | }, 3000) 14 | }); 15 | */ 16 | 17 | const pm = new PMS5003('/dev/cu.usbserial-21220'); 18 | 19 | pm.on('message', data => { 20 | console.log(data); 21 | }); 22 | -------------------------------------------------------------------------------- /bin/dsensor: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const program = require('kelp-cli'); 4 | 5 | program() 6 | .command('help', () => { 7 | console.log(); 8 | console.log('~$ dsensor [options]'); 9 | console.log('~$ dsensor send --type=HCHO --device=/dev/cu.SLAB_USBtoUART --command=0x01 --data=0x00'); 10 | }) 11 | .command('send', ({ type, device, command, data, watch }) => { 12 | const Adapter = require(`../${type}`); 13 | device = new Adapter(device); 14 | device.on('message', message => { 15 | console.log("->", message); 16 | if(!watch) device.close(); 17 | }); 18 | device.on('open', () => { 19 | if(watch){ 20 | setInterval(() => { 21 | device.send(command, data); 22 | }, 3000); 23 | }else{ 24 | device.send(command, data); 25 | } 26 | }); 27 | }) 28 | .parse(); 29 | 30 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | const SerialPort = require('serialport'); 2 | 3 | class DSensor extends SerialPort { 4 | constructor(dev, reader){ 5 | super(dev); 6 | this.on('data', reader(data => { 7 | const message = this.parse(data); 8 | this.emit('message', message, data); 9 | })); 10 | } 11 | send(cmd, data = 0x00){ 12 | let checksum = 0; 13 | if(typeof cmd === 'undefined') 14 | throw new TypeError('cmd must be a number or string'); 15 | if(typeof data === 'undefined') 16 | throw new TypeError('data must be a number or string'); 17 | cmd = parseInt(cmd); 18 | data = parseInt(data); 19 | const packet = Buffer.alloc(7); 20 | packet.writeUIntBE(0x42, 0, 1); checksum+=0x42; 21 | packet.writeUIntBE(0x4d, 1, 1); checksum+=0x4d; 22 | packet.writeUIntBE(cmd, 2, 1); checksum+=cmd; 23 | packet.writeUInt16BE(data, 3); checksum+=data; 24 | packet.writeUInt16BE(checksum, 5); 25 | return this.write(packet); 26 | } 27 | } 28 | 29 | module.exports = DSensor; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## dsensor ![dsensor](https://img.shields.io/npm/v/dsensor.svg) 2 | 3 | > Digital universal particle concentration sensor 4 | 5 | ### Installation 6 | 7 | ```bash 8 | $ npm i [-g] dsensor 9 | ``` 10 | 11 | ### Example 12 | 13 | ```js 14 | const HCHO = require('dsensor/HCHO'); 15 | 16 | const sensor = new HCHO("/dev/cu.SLAB_USBtoUART"); 17 | 18 | sensor.on('message', message => { 19 | console.log(message.toString()); // outputs: "HCHO: 0.001Mg/m3" 20 | }); 21 | 22 | sensor.on("open", () => { 23 | setInterval(() => { 24 | sensor.send('query'); 25 | }, 3000); 26 | }); 27 | ``` 28 | 29 | ![](docs/PMS5003.jpg) 30 | ![](docs/DS-HCHO.jpg) 31 | 32 | ### Golang Version 33 | 34 | 35 | 36 | ### Contributing 37 | - Fork this Repo first 38 | - Clone your Repo 39 | - Install dependencies by `$ npm install` 40 | - Checkout a feature branch 41 | - Feel free to add your features 42 | - Make sure your features are fully tested 43 | - Publish your local branch, Open a pull request 44 | - Enjoy hacking <3 45 | 46 | ### MIT 47 | 48 | --- 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2016 Lsong <song940@gmail.com> 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. -------------------------------------------------------------------------------- /AQI.js: -------------------------------------------------------------------------------- 1 | function Linear(AQIhigh, AQIlow, Conchigh, Conclow, Concentration) { 2 | var linear; 3 | var Conc = parseFloat(Concentration); 4 | var a; 5 | a = ((Conc - Conclow) / (Conchigh - Conclow)) * (AQIhigh - AQIlow) + AQIlow; 6 | linear = Math.round(a); 7 | return linear; 8 | } 9 | 10 | function AQIPM25(concentration) { 11 | concentration = parseFloat(concentration); 12 | concentration = (Math.floor(10 * concentration)) / 10; 13 | if (concentration >= 0 && concentration < 12.1) { 14 | return Linear(50, 0, 12, 0, concentration); 15 | } else if (concentration >= 12.1 && concentration < 35.5) { 16 | return Linear(100, 51, 35.4, 12.1, concentration); 17 | } else if (concentration >= 35.5 && concentration < 55.5) { 18 | return Linear(150, 101, 55.4, 35.5, concentration); 19 | } else if (concentration >= 55.5 && concentration < 150.5) { 20 | return Linear(200, 151, 150.4, 55.5, concentration); 21 | } else if (concentration >= 150.5 && concentration < 250.5) { 22 | return Linear(300, 201, 250.4, 150.5, concentration); 23 | } else if (concentration >= 250.5 && concentration < 350.5) { 24 | return Linear(400, 301, 350.4, 250.5, concentration); 25 | } else if (concentration >= 350.5 && concentration < 500.5) { 26 | return Linear(500, 401, 500.4, 350.5, concentration); 27 | } else { 28 | return "Out of Range"; 29 | } 30 | } 31 | 32 | module.exports = AQIPM25; -------------------------------------------------------------------------------- /PMS5003.js: -------------------------------------------------------------------------------- 1 | const DSensor = require('.'); 2 | const assert = require('assert'); 3 | 4 | /** 5 | * DSensor 6 | */ 7 | class PMS5003 extends DSensor { 8 | constructor(dev){ 9 | super(dev, createReader); 10 | return this; 11 | } 12 | /** 13 | * parse sensor data 14 | */ 15 | parse(buffer){ 16 | const data = { 17 | HEAD : buffer.readUInt16BE(0), 18 | LENGTH : buffer.readUInt16BE(2), 19 | PM1_0 : buffer.readUInt16BE(4), 20 | PM2_5 : buffer.readUInt16BE(6), 21 | PM10 : buffer.readUInt16BE(8), 22 | PM1_0_ : buffer.readUInt16BE(10), 23 | PM2_5_ : buffer.readUInt16BE(12), 24 | PM10_ : buffer.readUInt16BE(14), 25 | UM0_3 : buffer.readUInt16BE(16), 26 | UM0_5 : buffer.readUInt16BE(18), 27 | UM1_0 : buffer.readUInt16BE(20), 28 | UM2_5 : buffer.readUInt16BE(22), 29 | UM5_0 : buffer.readUInt16BE(24), 30 | UM10 : buffer.readUInt16BE(26), 31 | VERSION : buffer.readUInt8(28), 32 | ERRCODE : buffer.readUInt8(29), 33 | CHECKSUM: buffer.readUInt16BE(30) 34 | }; 35 | var checksum = 0; 36 | for(var i=0;i= LEN_LENGTH){ 69 | len = buffer.readUInt16BE(index); 70 | flag = STATE_DATA; 71 | continue; 72 | } 73 | if(flag === STATE_DATA && buffer.length - index >= len + LEN_CHECKSUM){ 74 | fn(buffer.slice(index - LEN_HEADER, index + len + LEN_CHECKSUM)); 75 | buffer = buffer.slice(index + len + LEN_CHECKSUM); 76 | flag = STATE_HEADER_1; 77 | } 78 | } 79 | }; 80 | } -------------------------------------------------------------------------------- /HCHO.js: -------------------------------------------------------------------------------- 1 | const DSensor = require('.'); 2 | const assert = require('assert'); 3 | 4 | const TYPES = { 5 | 0x00: 'NoSensor', 6 | 0x01: 'CO', 7 | 0x02: 'H2S', 8 | 0x03: 'CH4', 9 | 0x04: 'CL2', 10 | 0x05: 'HCL', 11 | 0x06: 'F2', 12 | 0x07: 'HF', 13 | 0x08: 'NH3', 14 | 0x09: 'HCN', 15 | 0x0a: 'PH3', 16 | 0x0b: 'NO', 17 | 0x0c: 'NO2', 18 | 0x0d: 'O3', 19 | 0x0e: 'O2', 20 | 0x0f: 'SO2', 21 | 0x10: 'CLO2', 22 | 0x11: 'COCL2', 23 | 0x12: 'PH3', 24 | 0x13: 'SiH4', 25 | 0x14: 'HCHO', 26 | 0x15: 'CO2', 27 | 0x16: 'VOC', 28 | 0x17: 'ETO', 29 | 0x18: 'C2H4', 30 | 0x19: 'C2H2', 31 | 0x1a: 'SF6', 32 | 0x1b: 'AsH3', 33 | 0x1c: 'H2', 34 | 0x1d: 'TOX1', 35 | 0x1e: 'TOX2', 36 | 0x1f: 'L/M', 37 | 0x20: 'BatteryLevel', 38 | }; 39 | 40 | const UNITS = { 41 | 0x01: 'ppm', 42 | 0x02: 'VOL', 43 | 0x03: 'LEL', 44 | 0x04: 'Ppb', 45 | 0x05: 'Mg/m3', 46 | }; 47 | 48 | class HCHO extends DSensor { 49 | constructor(dev){ 50 | super(dev, createReader); 51 | } 52 | parse(raw){ 53 | const data = { 54 | HEAD : raw.readUInt16BE(0), 55 | LENGTH : raw.readUIntBE(2, 1), 56 | TYPE : raw.readUIntBE(3, 1), 57 | UNIT : raw.readUIntBE(4, 1), 58 | VH : raw.readUIntBE(5, 1), 59 | VALUE : raw.readUInt16BE(6), 60 | CHECKSUM: raw.readUInt16BE(8), 61 | }; 62 | var checksum = 0; 63 | for(var i=0;i= LEN_LENGTH){ 104 | len = buffer.readUIntBE(index, 1); 105 | flag = STATE_DATA; 106 | continue; 107 | } 108 | if(flag === STATE_DATA && buffer.length - index >= len){ 109 | fn(buffer.slice(index - LEN_HEADER, index + len)); 110 | buffer = buffer.slice(index + len); 111 | flag = STATE_HEADER_1; 112 | } 113 | } 114 | }; 115 | } --------------------------------------------------------------------------------