forked from adafruit/Adafruit_PM25AQI
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAdafruit_PM25AQI.cpp
149 lines (133 loc) · 3.85 KB
/
Adafruit_PM25AQI.cpp
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
147
148
149
/*!
* @file Adafruit_PM25AQI.cpp
*
* @mainpage Adafruit PM2.5 air quality sensor driver
*
* @section intro_sec Introduction
*
* This is the documentation for Adafruit's PM2.5 AQI driver for the
* Arduino platform. It is designed specifically to work with the
* Adafruit PM2.5 Air quality sensors: http://www.adafruit.com/products/4632
*
* These sensors use I2C or UART to communicate.
*
* Adafruit invests time and resources providing this open source code,
* please support Adafruit and open-source hardware by purchasing
* products from Adafruit!
*
*
* @section author Author
* Written by Ladyada for Adafruit Industries.
*
* @section license License
* BSD license, all text here must be included in any redistribution.
*
*/
#include "Adafruit_PM25AQI.h"
/*!
* @brief Instantiates a new PM25AQI class
*/
Adafruit_PM25AQI::Adafruit_PM25AQI() {}
/*!
* @brief Setups the hardware and detects a valid PMSA003I. Initializes I2C.
* @param theWire
* Optional pointer to I2C interface, otherwise use Wire
* @return True if PMSA003I found on I2C, False if something went wrong!
*/
bool Adafruit_PM25AQI::begin_I2C(TwoWire *theWire) {
if (!i2c_dev) {
i2c_dev = new Adafruit_I2CDevice(PMSA003I_I2CADDR_DEFAULT, theWire);
}
if (!i2c_dev->begin()) {
return false;
}
return true;
}
/*!
* @brief Setups the hardware and detects a valid UART PM2.5
* @param theSerial
* Pointer to Stream (HardwareSerial/SoftwareSerial) interface
* @return True
*/
bool Adafruit_PM25AQI::begin_UART(Stream *theSerial) {
serial_dev = theSerial;
return true;
}
/*!
* @brief Setups the hardware and detects a valid UART PM2.5
* @param data
* Pointer to PM25_AQI_Data that will be filled by read()ing
* @return True on successful read, false if timed out or bad data
*/
bool Adafruit_PM25AQI::read(PM25_AQI_Data *data) {
uint8_t buffer[32];
uint16_t sum = 0;
if (!data) {
return false;
}
if (i2c_dev) { // ok using i2c?
if (!i2c_dev->read(buffer, 32)) {
return false;
}
} else if (serial_dev) { // ok using uart
if (!serial_dev->available()) {
return false;
}
int skipped = 0;
while ((skipped < 32) && (serial_dev->peek() != 0x42)) {
serial_dev->read();
skipped++;
if (!serial_dev->available()) {
return false;
}
}
if (serial_dev->read() != 0x42) { // read assuming you got the first byte
return false;
}
// now catching 1 in 256 edge-case where last byte of checksum is 0x42 too,
// being mistaken for first start byte, leading to byte shift and checksum
// mismatch
if (serial_dev->peek() ==
0x42) { // peek if next byte might match first start byte
serial_dev->read(); // if yes last assumption was wrong, read again
// assuming you now got the start
}
if (serial_dev->read() != 0x4d) { // read & check the second byte
return false;
}
// Now the first two bytes are guaranteed to be 0x42 0x4d but have already
// been read
buffer[0] = 0x42;
buffer[1] = 0x4d;
// Now read remaining 30 bytes
if (serial_dev->available() < 30) {
return false;
}
for (uint8_t i = 2; i < 32; i++) {
buffer[i] = serial_dev->read();
}
} else {
return false;
}
// Check that start byte is correct!
if (buffer[0] != 0x42) {
return false;
}
// get checksum ready
for (uint8_t i = 0; i < 30; i++) {
sum += buffer[i];
}
// The data comes in endian'd, this solves it so it works on all platforms
uint16_t buffer_u16[15];
for (uint8_t i = 0; i < 15; i++) {
buffer_u16[i] = buffer[2 + i * 2 + 1];
buffer_u16[i] += (buffer[2 + i * 2] << 8);
}
// put it into a nice struct :)
memcpy((void *)data, (void *)buffer_u16, 30);
if (sum != data->checksum) {
return false;
}
// success!
return true;
}