-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathSI7021.cpp
170 lines (137 loc) · 4.18 KB
/
SI7021.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
#include "SI7021.h"
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include <linux/i2c-dev.h>
#include <sys/ioctl.h>
#include <iostream>
#include <chrono>
#include <thread>
#undef DBG
SI7021::SI7021(std::string i2c_dev_name, uint8_t ccs811_addr)
: i2c_dev_name(std::move(i2c_dev_name)),
ccs811_addr(ccs811_addr) {
open_device();
init();
}
SI7021::~SI7021() {
close_device();
}
void SI7021::close_device() { if (i2c_fd >= 0) close(i2c_fd); }
void SI7021::init() {
std::cout << "Resetting Si7021..." << std::endl;
reset();
std::this_thread::sleep_for(std::chrono::seconds(1));
read_serial();
read_fw_rev();
}
void SI7021::open_device() {
i2c_fd = open(i2c_dev_name.c_str(), O_RDWR);
if (i2c_fd < 0) {
std::cerr << "Unable to open" << i2c_dev_name << ". " << strerror(errno) << std::endl;
throw 1;
}
if (ioctl(i2c_fd, I2C_SLAVE, ccs811_addr) < 0) {
std::cerr << "Failed to communicate with the device. " << strerror(errno) << std::endl;
throw 1;
}
}
uint64_t SI7021::get_serial() {
return serial_no;
}
void SI7021::write_data(uint8_t *buffer, size_t buffer_len) {
#ifdef DBG
std::cout << "Write: ";
for (size_t i = 0; i < buffer_len; i++) {
std::cout << "0x" << hex << (int) buffer[i] << " ";
}
std::cout << std::endl;
#endif
auto write_c = write(i2c_fd, buffer, buffer_len);
if (write_c < 0) {
std::cerr << "Unable to send command." << std::endl;
// TODO - Have better exceptions.
throw 1;
}
#ifdef DBG
std::cout << " ... wrote " << write_c << " bytes." << std::endl;
#endif
}
std::unique_ptr<std::vector<uint8_t>> SI7021::read_data(size_t buffer_size) {
auto *read_buffer = new uint8_t[buffer_size];
auto bytes_read = read(i2c_fd, read_buffer, buffer_size);
#ifdef DBG
std::cout << "Read " << std::dec << bytes_read << " bytes" << std::endl;
#endif
if (bytes_read < 0) {
// return an empty vector if we can't read anything.
return std::make_unique<std::vector<uint8_t>>();
}
auto result = std::make_unique<std::vector<uint8_t>>(read_buffer, read_buffer + bytes_read);
delete[] read_buffer;
#ifdef DBG
std::cout << "Read: ";
for (auto e : *result) {
std::cout << std::hex << (int) e << " ";
}
std::cout << std::endl;
#endif
return result;
}
void SI7021::reset() {
uint8_t cmd[] = {RESET};
write_data(cmd, 1);
}
// TODO - Implement the CRC logic
// From the documentation: "The checksum byte is calculated using a CRC generator polynomial of
// x^8 + x^5 + x^4 + 1, with an initialization of 0x00."
uint8_t SI7021::crc(uint8_t in) {
return 0;
}
void SI7021::read_serial() {
uint64_t serial = 0;
uint8_t cmd[] = {0xfa, 0x0f};
write_data(cmd, 2);
auto response = read_data(8);
// Skip the crc bytes
for (size_t i = 0; i < response->size(); i += 2) {
serial = (serial << 8) | response->at(i);
}
// Read the second part
cmd[0] = 0xfc;
cmd[1] = 0xc9;
write_data(cmd, 2);
response = read_data(6);
serial = (serial << 8) | response->at(0);
serial = (serial << 8) | response->at(1);
serial = (serial << 8) | response->at(3);
serial = (serial << 8) | response->at(4);
serial_no = serial;
}
void SI7021::read_fw_rev() {
uint8_t cmd[] = {0x84, 0x88};
write_data(cmd, 2);
auto response = read_data(1);
fw_rev = response->front();
}
uint8_t SI7021::get_fw_rev() {
return fw_rev;
}
float SI7021::measure_humidity() {
uint8_t cmd[] = {MEAS_REL_HUM};
write_data(cmd, 1);
std::this_thread::sleep_for(std::chrono::seconds(1));
auto response = read_data(2);
if (response->empty()) return 0;
uint16_t rh_code = (response->at(0) << 8) | response->at(1);
return static_cast<float>(((125.0 * rh_code) / 65536) - 6);
}
float SI7021::measure_temperature() {
uint8_t cmd[] = {MEAS_TEMP};
write_data(cmd, 1);
std::this_thread::sleep_for(std::chrono::seconds(1));
auto response = read_data(2);
if (response->empty()) return 0;
uint16_t temp_code = (response->at(0) << 8) | response->at(1);
return static_cast<float>(((175.72 * temp_code) / 65536) - 46.85);
}