-
Notifications
You must be signed in to change notification settings - Fork 1
/
mh.js
146 lines (119 loc) · 3.48 KB
/
mh.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/**
* mh z19b sensor uart client
* polls sensor each N seconsds
* and stores received amount of co2 ppm into persistent database
*
* launch with `yarn mh`
*
* specification for mh-z19b
* http://www.winsen-sensor.com/d/files/infrared-gas-sensor/mh-z19b-co2-ver1_0.pdf
*
* sensor:
* https://www.aliexpress.com/item/1PCS-module-MH-Z19-infrared-co2-sensor-for-co2-monitor-MH-Z19B-Free-shipping-new-stock/32371956420.html
*
* https://mysku.ru/blog/aliexpress/59397.html
*/
const SerialPort = require('serialport');
const Datastore = require('nedb');
const ipc = require('node-ipc');
const internalBus = new (require('events'))();
const {
APP_PORT,
STORAGE_FILENAME,
PUBLIC_PATH,
UART_ADAPTER,
MH_REQUEST_INTERVAL,
IPC_ID_HTTP_SERVER,
IPC_ID_MH,
MESSAGE_NAME,
} = require('./const');
const GET_CO2_REQUEST = Buffer.from([
0xFF, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79
]);
// init IPC client
ipc.config.id = IPC_ID_MH;
ipc.config.retry = 1500;
ipc.config.silent = true;
ipc.connectTo(IPC_ID_HTTP_SERVER, () => {
console.log(`ipc client id=${IPC_ID_MH} is ready to communicate with server id=${IPC_ID_HTTP_SERVER}`);
const ipcClient = ipc.of[IPC_ID_HTTP_SERVER];
internalBus.on(MESSAGE_NAME, point => ipcClient.emit(MESSAGE_NAME, point));
});
// init serial port
const port = new SerialPort(
UART_ADAPTER, {
baudRate: 9600,
}
);
port.on('data', onPortData);
port.on('open', onPortOpen);
port.on('error', onPortError);
// init database
const db = new Datastore({
filename: STORAGE_FILENAME,
autoload: true
});
function calculateCrc(buffer) {
const sumOfBytes1to7 = buffer
.slice(1, 8)
.reduce((sum, intVal) => sum + intVal, 0);
const sumWithInvertedBits = parseInt(
sumOfBytes1to7
.toString(2)
.split('')
.map(ch => ch === '1' ? '0' : '1')
.join(''),
2
);
return sumWithInvertedBits + 1;
}
function sendCO2Request() {
console.log('sent bytes: ', GET_CO2_REQUEST);
port.write(GET_CO2_REQUEST, function(err) {
if (err) {
console.log('write error: ', err);
}
});
}
function onPortData(buffer) {
console.log('received bytes: ', buffer);
const crc = calculateCrc(buffer);
const receivedCrc = buffer[8];
const startByte = buffer[0];
const sensorNum = buffer[1];
const co2HighByte = buffer[2];
const co2LowByte = buffer[3];
const temperatureRaw = buffer[4];
if (startByte === 0xFF && sensorNum === 0x86) {
if (receivedCrc === crc) {
const ppm = (256 * co2HighByte) + co2LowByte;
const temperature = temperatureRaw - 40;
console.log(`measured ppm: ${ppm} and temperature: ${temperature}`);
const timestamp = new Date();
db.insert({
timestamp,
ppm,
temperature,
});
internalBus.emit(MESSAGE_NAME, {
timestamp: timestamp.getTime(),
ppm,
temperature,
});
} else {
console.log(`checksum error`);
console.log('crc', crc);
console.log('receivedCrc', receivedCrc);
}
} else {
console.log(`unexpexted first two bytes of response`);
}
}
function onPortOpen() {
console.log(`serial port was opened`);
sendCO2Request();
setInterval(sendCO2Request, MH_REQUEST_INTERVAL);
}
function onPortError(err) {
console.error(err);
}