Created
November 12, 2020 13:22
-
-
Save parasquid/808cf63ee6c5e9865df32e3ddb8c795a to your computer and use it in GitHub Desktop.
airquality.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const getUuid = (i) => { | |
const num = i.toString(); | |
const id = ("0").repeat((4 - num.length) > 0 ? (4 - num.length) : 0) + num; | |
return 'd7e5' + id + '-0109-4306-956f-2f725ba7a85d'; | |
}; | |
var s = new Serial(); | |
s.setup(9600,{rx: D33, tx: D34}); | |
let buffer = ''; | |
const header = String.fromCharCode(0x42) + String.fromCharCode(0x4d); | |
let pmData = {}; | |
const calculateChecksum = (arr) => { | |
return arr.reduce((acc, cur) => (acc + cur), 0) - arr[30] - arr[31]; | |
}; | |
s.on('data', function (data) { | |
buffer = buffer + data; | |
if(buffer.length < 32) { | |
// get at least 32 bytes | |
} else { | |
// find header and discard any previous bytes in buffer | |
const index = buffer.indexOf(header); | |
if(index != -1) { // found the header | |
buffer = buffer.substr(index); // discard previous bytes until header | |
if(buffer.length >= 32) { | |
buffer = buffer.substr(0, 32); // get a complete packet | |
const arrayBuffer = E.toArrayBuffer(buffer); | |
buffer = buffer.substr(32); // set buffer to leftover bytes | |
const dataView = new DataView(arrayBuffer); | |
data = { | |
header: dataView.getUint16(0), | |
length: dataView.getUint16(2), | |
pm25: dataView.getUint8(6 + 1), | |
pm10: dataView.getUint8(8 + 1), | |
checksum: dataView.getUint16(30), | |
}; | |
const calculatedChecksum = calculateChecksum(new Uint8Array(arrayBuffer)); | |
// early return on bad data | |
if (data.length != 28) { | |
// print(data.length); | |
return; | |
} | |
if (calculatedChecksum != data.checksum) { | |
print(calculatedChecksum, data.checksum, calculatedChecksum - data.checksum, arrayBuffer); | |
// we're getting too many invalid checksums so comment out | |
// the early return for now ... | |
//return; | |
} | |
pmData = {pm25: data.pm25, pm10: data.pm10, t: Date.now()}; | |
digitalPulse(LED1, true, 50); | |
} | |
} else { // header not found | |
} | |
} | |
}); | |
const SDA = 30; | |
const SCL = 31; | |
I2C1.setup({scl:SCL,sda:SDA}); | |
var exports={}; | |
var C = { | |
I2C_ADDRESS : 0x40, | |
}; | |
function HDC1080(i2c, deviceAddress) { | |
this.i2c = i2c; | |
this.deviceAddress = deviceAddress; | |
} | |
HDC1080.prototype.read = function() { | |
this.i2c.writeTo(this.deviceAddress, 0); | |
return new Promise((resolve, reject) => { | |
setTimeout(() => { | |
const data = this.i2c.readFrom(this.deviceAddress, 4); | |
const temp = data[1] | data[0] << 8; | |
const hum = data[3] | data[2] << 8; | |
resolve({ | |
temperature: (temp / 65536) * 165.0 - 40.0, | |
humidity: (hum / 65536) * 100.0, | |
}); | |
}, 25); | |
}); | |
}; | |
exports.connect = function (i2c, deviceAddress) { | |
return new HDC1080(i2c, deviceAddress || C.I2C_ADDRESS); | |
}; | |
var gas = require("CCS811").connectI2C(I2C1, {int : 29}); | |
var hdc = exports.connect(I2C1); | |
var temperature = 25; | |
var humidity = 50; | |
const start = () => { | |
g.clear(); | |
g.setFont("6x8"); | |
g.drawString("Hello World!",0,0); | |
g.flip(); | |
}; | |
var g = require("SSD1306").connect(I2C1, start); | |
require("Font6x8").add(Graphics); | |
const bleService = (value, description) => { | |
return { | |
value: [value], | |
readable: true, | |
maxLen: 8, | |
notify : true, | |
description: description, | |
}; | |
}; | |
const defaultServices = { | |
0x181A: { // org.bluetooth.descriptor.es_measurement | |
0x2A6E: bleService(temperature), | |
0x2A6F: bleService(humidity), | |
} | |
}; | |
defaultServices[0x181A][getUuid(1)] = bleService(400, 'eCO2'); | |
defaultServices[0x181A][getUuid(2)] = bleService(0, 'TVOC'); | |
defaultServices[0x181A][getUuid(3)] = bleService(0, 'pm 2.5'); | |
defaultServices[0x181A][getUuid(4)] = bleService(0, 'pm 10'); | |
defaultServices[0x181A][getUuid(5)] = bleService(0, 'beat'); | |
NRF.setServices(defaultServices, { advertise: [ '0x181A' ] }); | |
const littleEndian = (value) => { | |
return [value&255, value>>8]; | |
}; | |
gas.on('data', (data) => { | |
hdc.read().then((e) => { | |
temperature = e.temperature; | |
humidity = e.humidity; | |
}); | |
gas.setEnvData(humidity, temperature); | |
print(data, temperature, humidity, pmData); | |
g.clear(); | |
g.setFont("6x8"); | |
g.drawString('eCO2 : ' + data.eCO2, 0, 0); | |
g.drawString('TVOC : ' + data.TVOC, 0, 10); | |
g.drawString('temp : ' + temperature, 0, 20); | |
g.drawString('hmdty: ' + humidity, 0, 30); | |
g.drawString('pm2.5: ' + pmData.pm25, 0, 40); | |
g.drawString('pm10 : ' + pmData.pm10, 0, 50); | |
g.flip(); | |
// Bluetooth spec says data is 16 bits, 0.01/unit - so x100 | |
const t = Math.round(temperature*100); | |
const h = Math.round(humidity*100); | |
const advertData = { | |
0x2A6E: littleEndian(t), | |
0x2A6F: littleEndian(h), | |
}; | |
NRF.setAdvertising(advertData); | |
const serviceData = { | |
0x181A: { // org.bluetooth.descriptor.es_measurement | |
0x2A6E: { // temperature | |
value: littleEndian(t), | |
notify: true, | |
}, | |
0x2A6F: { // humidity | |
value: littleEndian(h), | |
notify: true, | |
}, | |
} | |
}; | |
serviceData[0x181A][getUuid(1)] = { value: littleEndian(data.eCO2), notify: true }; | |
serviceData[0x181A][getUuid(2)] = { value: littleEndian(data.TVOC), notify: true }; | |
serviceData[0x181A][getUuid(3)] = { value: littleEndian(pmData.pm25), notify: true }; | |
serviceData[0x181A][getUuid(4)] = { value: littleEndian(pmData.pm10), notify: true }; | |
serviceData[0x181A][getUuid(5)] = { value: [pmData.t], notify: true }; | |
NRF.updateServices(serviceData, { advertise: [ '0x181A' ] }); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment