├── BME280.cpp
├── BME280.h
├── BME280I2C.cpp
├── BME280I2C.h
├── LoRaWAN_TTN_Env_Node.ino
├── README.md
├── adcvcc.cpp
└── adcvcc.h
/BME280.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | BME280.cpp
3 | This code records data from the BME280 sensor and provides an API.
4 | This file is part of the Arduino BME280 library.
5 | Copyright (C) 2016 Tyler Glenn
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 |
20 | Written: Dec 30 2015.
21 | Last Updated: Jan 1 2016. - Happy New year!
22 |
23 | This header must be included in any derived code or copies of the code.
24 |
25 | Based on the data sheet provided by Bosch for the Bme280 environmental sensor,
26 | calibration code based on algorithms providedBosch, some unit conversations courtesy
27 | of www.endmemo.com, altitude equation courtesy of NOAA, and dew point equation
28 | courtesy of Brian McNoldy at http://andrew.rsmas.miami.edu.
29 | */
30 |
31 |
32 | /* ==== Includes ==== */
33 | #include
34 | #include "BME280.h"
35 | /* ==== END Includes ==== */
36 |
37 | /* ==== Methods ==== */
38 |
39 | float BME280::CalculateTemperature(int32_t raw, int32_t& t_fine, bool celsius)
40 | {
41 | // Code based on calibration algorthim provided by Bosch.
42 | int32_t var1, var2, final;
43 | uint16_t dig_T1 = (dig[1] << 8) | dig[0];
44 | int16_t dig_T2 = (dig[3] << 8) | dig[2];
45 | int16_t dig_T3 = (dig[5] << 8) | dig[4];
46 | var1 = ((((raw >> 3) - ((int32_t)dig_T1 << 1))) * ((int32_t)dig_T2)) >> 11;
47 | var2 = (((((raw >> 4) - ((int32_t)dig_T1)) * ((raw >> 4) - ((int32_t)dig_T1))) >> 12) * ((int32_t)dig_T3)) >> 14;
48 | t_fine = var1 + var2;
49 | final = (t_fine * 5 + 128) >> 8;
50 | return celsius ? final/100.0 : final/100.0*9.0/5.0 + 32.0;
51 | }
52 |
53 | float BME280::CalculateHumidity(int32_t raw, int32_t t_fine){
54 | // Code based on calibration algorthim provided by Bosch.
55 | int32_t var1;
56 | int8_t dig_H1 = dig[24];
57 | int16_t dig_H2 = (dig[26] << 8) | dig[25];
58 | int8_t dig_H3 = dig[27];
59 | int16_t dig_H4 = (dig[28] << 4) | (0x0F & dig[29]);
60 | int16_t dig_H5 = (dig[30] << 4) | ((dig[29] >> 4) & 0x0F);
61 | int8_t dig_H6 = dig[31];
62 |
63 | var1 = (t_fine - ((int32_t)76800));
64 | var1 = (((((raw << 14) - (((int32_t)dig_H4) << 20) - (((int32_t)dig_H5) * var1)) +
65 | ((int32_t)16384)) >> 15) * (((((((var1 * ((int32_t)dig_H6)) >> 10) * (((var1 *
66 | ((int32_t)dig_H3)) >> 11) + ((int32_t)32768))) >> 10) + ((int32_t)2097152)) *
67 | ((int32_t)dig_H2) + 8192) >> 14));
68 | var1 = (var1 - (((((var1 >> 15) * (var1 >> 15)) >> 7) * ((int32_t)dig_H1)) >> 4));
69 | var1 = (var1 < 0 ? 0 : var1);
70 | var1 = (var1 > 419430400 ? 419430400 : var1);
71 | return ((uint32_t)(var1 >> 12))/1024.0;
72 | }
73 |
74 | float BME280::CalculatePressure(int32_t raw, int32_t t_fine, uint8_t unit){
75 | // Code based on calibration algorthim provided by Bosch.
76 | int64_t var1, var2, pressure;
77 | float final;
78 |
79 | uint16_t dig_P1 = (dig[7] << 8) | dig[6];
80 | int16_t dig_P2 = (dig[9] << 8) | dig[8];
81 | int16_t dig_P3 = (dig[11] << 8) | dig[10];
82 | int16_t dig_P4 = (dig[13] << 8) | dig[12];
83 | int16_t dig_P5 = (dig[15] << 8) | dig[14];
84 | int16_t dig_P6 = (dig[17] << 8) | dig[16];
85 | int16_t dig_P7 = (dig[19] << 8) | dig[18];
86 | int16_t dig_P8 = (dig[21] << 8) | dig[20];
87 | int16_t dig_P9 = (dig[23] << 8) | dig[22];
88 |
89 | var1 = (int64_t)t_fine - 128000;
90 | var2 = var1 * var1 * (int64_t)dig_P6;
91 | var2 = var2 + ((var1 * (int64_t)dig_P5) << 17);
92 | var2 = var2 + (((int64_t)dig_P4) << 35);
93 | var1 = ((var1 * var1 * (int64_t)dig_P3) >> 8) + ((var1 * (int64_t)dig_P2) << 12);
94 | var1 = (((((int64_t)1) << 47) + var1)) * ((int64_t)dig_P1) >> 33;
95 | if (var1 == 0) { return NAN; } // Don't divide by zero.
96 | pressure = 1048576 - raw;
97 | pressure = (((pressure << 31) - var2) * 3125)/var1;
98 | var1 = (((int64_t)dig_P9) * (pressure >> 13) * (pressure >> 13)) >> 25;
99 | var2 = (((int64_t)dig_P8) * pressure) >> 19;
100 | pressure = ((pressure + var1 + var2) >> 8) + (((int64_t)dig_P7) << 4);
101 |
102 | final = ((uint32_t)pressure)/256.0;
103 |
104 | // Conversion units courtesy of www.endmemo.com.
105 | switch(unit){
106 | case 0x1: /* hPa */
107 | final /= 100.0;
108 | break;
109 | case 0x2: /* inHg */
110 | final /= 3386.3752577878; /* final pa * 1inHg/3386.3752577878Pa */
111 | break;
112 | case 0x3: /* atm */
113 | final /= 101324.99766353; /* final pa * 1 atm/101324.99766353Pa */
114 | break;
115 | case 0x4: /* bar */
116 | final /= 100000.0; /* final pa * 1 bar/100kPa */
117 | break;
118 | case 0x5: /* torr */
119 | final /= 133.32236534674; /* final pa * 1 torr/133.32236534674Pa */
120 | break;
121 | case 0x6: /* N/m^2 */
122 | break; /* 1Pa / N/m^2 */
123 | case 0x7: /* psi */
124 | final /= 6894.744825494; /* final pa * 1psi/6894.744825494Pa */
125 | break;
126 | default: /* Pa (case: 0) */
127 | break;
128 | }
129 | return final;
130 | }
131 |
132 | BME280::BME280(uint8_t tosr, uint8_t hosr, uint8_t posr, uint8_t mode, uint8_t st, uint8_t filter,
133 | bool spiEnable):
134 | tempOversamplingRate(tosr), humidityOversamplingRate(hosr),
135 | pressureOversamplingRate(posr), mode(mode), standbyTime(st), filter(filter),
136 | spiEnable(spiEnable)
137 | {
138 | // ctrl_hum register. (ctrl_hum[2:0] = Humidity oversampling rate.)
139 | controlHumidity = humidityOversamplingRate;
140 | // ctrl_meas register. (ctrl_meas[7:5] = temperature oversampling rate, ctrl_meas[4:2] = pressure oversampling rate, ctrl_meas[1:0] = mode.)
141 | controlMeasure = (tempOversamplingRate << 5) | (pressureOversamplingRate << 2) | mode;
142 | // config register. (config[7:5] = standby time, config[4:2] = filter, ctrl_meas[0] = spi enable.)
143 | config = (standbyTime << 5) | (filter << 2) | spiEnable;
144 | }
145 |
146 | void BME280::setMode(uint8_t mode){
147 | controlMeasure = controlMeasure | mode;
148 | WriteRegister(CTRL_MEAS_ADDR, controlMeasure);
149 | }
150 |
151 | float BME280::temp(bool celsius){
152 | int32_t data[8];
153 | int32_t t_fine;
154 | if(!ReadData(data)){ return NAN; }
155 | uint32_t rawTemp = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
156 | return CalculateTemperature(rawTemp, t_fine, celsius);
157 | }
158 |
159 | float BME280::pres(uint8_t unit){
160 | int32_t data[8];
161 | int32_t t_fine;
162 | if(!ReadData(data)){ return NAN; }
163 | uint32_t rawTemp = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
164 | uint32_t rawPressure = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
165 | CalculateTemperature(rawTemp, t_fine);
166 | return CalculatePressure(rawPressure, t_fine, unit);
167 | }
168 |
169 | float BME280::hum(){
170 | int32_t data[8];
171 | int32_t t_fine;
172 | if(!ReadData(data)){ return NAN; }
173 | uint32_t rawTemp = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
174 | uint32_t rawHumidity = (data[6] << 8) | data[7];
175 | CalculateTemperature(rawTemp, t_fine);
176 | return CalculateHumidity(rawHumidity, t_fine);
177 | }
178 |
179 | void BME280::read(float& pressure, float& temp, float& humidity, bool metric, uint8_t p_unit){
180 | int32_t data[8];
181 | int32_t t_fine;
182 | if(!ReadData(data)){
183 | pressure = temp = humidity = NAN;
184 | return;
185 | }
186 | uint32_t rawPressure = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4);
187 | uint32_t rawTemp = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4);
188 | uint32_t rawHumidity = (data[6] << 8) | data[7];
189 | temp = CalculateTemperature(rawTemp, t_fine, metric);
190 | pressure = CalculatePressure(rawPressure, t_fine, p_unit);
191 | humidity = CalculateHumidity(rawHumidity, t_fine);
192 | }
193 |
194 | float BME280::alt(bool metric, float seaLevelPressure){
195 | float temp, hum, pres;
196 | read(pres, temp, hum, metric);
197 | return alt(pres, metric, seaLevelPressure);
198 | }
199 |
200 | float BME280::alt(float pressure, bool metric, float seaLevelPressure){
201 | // Equations courtesy of NOAA;
202 | float altitude = NAN;
203 | if (!isnan(pressure) && !isnan(seaLevelPressure)){
204 | altitude = 1000.0 * ( seaLevelPressure - pressure ) / 3386.3752577878;
205 | }
206 | return metric ? altitude * 0.3048 : altitude;
207 | }
208 |
209 | float BME280::sealevel(float A) // Altitude A (in meters), temperature T in Celsius, pressure P in mb, return the equivalent pressure (in mb) at sea level.
210 | {
211 | float T(NAN), P(NAN);
212 | T = temp(true);
213 | P = pres(1);
214 |
215 | return(P / pow(1-((0.0065 *A) / (T + (0.0065 *A) + 273.15)),5.257));
216 | }
217 |
218 | float BME280::dew(bool metric){
219 | float temp, hum, pres;
220 | read(pres, temp, hum, metric);
221 | return dew(temp, hum, metric);
222 | }
223 |
224 | float BME280::dew(float temp, float hum, bool metric){
225 | // Equations courtesy of Brian McNoldy from http://andrew.rsmas.miami.edu;
226 | float dewPoint = NAN;
227 | if (metric && !isnan(temp) && !isnan(hum)){
228 | dewPoint = 243.04 * (log(hum/100.0) + ((17.625 * temp)/(243.04 + temp)))
229 | /(17.625 - log(hum/100.0) - ((17.625 * temp)/(243.04 + temp)));
230 | }else if (!isnan(temp) && !isnan(hum)){
231 | float ctemp = (temp - 32.0) * 5.0/9.0;
232 | dewPoint = 243.04 * (log(hum/100.0) + ((17.625 * ctemp)/(243.04 + ctemp)))
233 | /(17.625 - log(hum/100.0) - ((17.625 * ctemp)/(243.04 + ctemp)));
234 | dewPoint = dewPoint * 9.0/5.0 + 32.0;
235 | }
236 | return dewPoint;
237 | }
238 |
239 | /* ==== END Methods ==== */
240 |
--------------------------------------------------------------------------------
/BME280.h:
--------------------------------------------------------------------------------
1 | /*
2 | BME280.h
3 | This code records data from the BME280 sensor and provides an API.
4 | This file is part of the Arduino BME280 library.
5 | Copyright (C) 2016 Tyler Glenn
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 |
20 | Written: Dec 30 2015.
21 | Last Updated: Jan 1 2016. - Happy New year!
22 | This code is licensed under the GNU LGPL and is open for ditrbution
23 | and copying in accordance with the license.
24 | This header must be included in any derived code or copies of the code.
25 |
26 | Based on the data sheet provided by Bosch for the Bme280 environmental sensor.
27 | */
28 | #ifndef TG_BME_280_H
29 | #define TG_BME_280_H
30 |
31 | /* ==== Includes ==== */
32 | #include "Arduino.h"
33 | /* ==== END Includes ==== */
34 |
35 | /* ==== Defines ==== */
36 | #define CTRL_HUM_ADDR 0xF2
37 | #define CTRL_MEAS_ADDR 0xF4
38 | #define CONFIG_ADDR 0xF5
39 | #define PRESS_ADDR 0xF7
40 | #define TEMP_ADDR 0xFA
41 | #define HUM_ADDR 0xFD
42 | #define TEMP_DIG_ADDR 0x88
43 | #define PRESS_DIG_ADDR 0x8E
44 | #define HUM_DIG_ADDR1 0xA1
45 | #define HUM_DIG_ADDR2 0xE1
46 | #define ID_ADDR 0xD0
47 | #define BME_ID 0x60
48 | #define BMP_ID 0x58
49 | /* ==== END Defines ==== */
50 |
51 |
52 | class BME280{
53 | protected:
54 | uint8_t tempOversamplingRate, humidityOversamplingRate, pressureOversamplingRate; // B000 = Skipped, B001 = x1, B010 = x2, B011 = x4, B100 = x8, B101/other = x16
55 | uint8_t mode; // Sleep = B00, Normal = B01 and B10, Forced = B11
56 | uint8_t standbyTime; // B000 = 0.5ms, B001 = 62.5ms, B010 = 125ms, B011 = 250ms, B100 = 250ms, B101 = 1000ms, B110 = 10ms, B111 = 20ms
57 | uint8_t filter; // B000 = off, B001 = 2, B010 = 4, B011 = 8, B100/other = 16
58 | uint8_t controlHumidity; // ctrl_hum register. (ctrl_hum[2:0] = Humidity oversampling rate.)
59 | uint8_t controlMeasure; // ctrl_meas register. (ctrl_meas[7:5] = temperature oversampling rate, ctrl_meas[4:2] = pressure oversampling rate, ctrl_meas[1:0] = mode.)
60 | uint8_t config; // config register. (config[7:5] = standby time, config[4:2] = filter, ctrl_meas[0] = spi enable.)
61 | uint8_t dig[32];
62 | bool spiEnable;
63 |
64 |
65 | /* ==== Write values to BME280 registers. ==== */
66 | virtual void WriteRegister(uint8_t addr, uint8_t data)=0;
67 |
68 | /* ==== Read the the trim data from the BME280, return true if successful. ==== */
69 | virtual bool ReadTrim()=0;
70 |
71 | /* ==== Read the raw data from the BME280 into an array and return true if successful. ==== */
72 | virtual bool ReadData(int32_t data[8])=0;
73 |
74 | /* ==== Calculate the temperature from the BME280 raw data and BME280 trim, return a float. ==== */
75 | float CalculateTemperature(int32_t raw, int32_t& t_fine, bool celsius = true);
76 |
77 | /* ==== Calculate the humidity from the BME280 raw data and BME280 trim, return a float. ==== */
78 | float CalculateHumidity(int32_t raw, int32_t t_fine);
79 |
80 | /* ==== Calculate the pressure from the BME280 raw data and BME280 trim, return a float. ==== */
81 | float CalculatePressure(int32_t raw, int32_t t_fine, uint8_t unit = 0x0); // unit: B000/other = Pa, B001 = hPa, B010 = inHg, B011 = atm, B100 = bar, B101 = torr, B110 = N/m^2, B111 = psi
82 |
83 | public:
84 | /* ==== Constructor used to create the class. All parameters have default values. ==== */
85 | BME280(uint8_t tosr = 0x1, uint8_t hosr = 0x1, uint8_t posr = 0x1, uint8_t mode = 0x1,
86 | uint8_t st = 0x5, uint8_t filter = 0x0, bool spiEnable = false); // Oversampling = 1, mode = forced, standby time = 1000ms, filter = none.
87 |
88 | /* ==== Method used at start up to initialize the class. ==== */
89 | virtual bool begin()=0;
90 |
91 | void setMode(uint8_t mode);
92 |
93 | /* ==== Read the temperature from the BME280 and return a float. ==== */
94 | float temp(bool celsius = true);
95 |
96 | /* ==== Read the pressure from the BME280 and return a float with the specified unit. ==== */
97 | float pres(uint8_t unit = 0x0); // unit: B000 = Pa, B001 = hPa, B010 = Hg, B011 = atm, B100 = bar, B101 = torr, B110 = N/m^2, B111 = psi
98 |
99 | /* ==== Read the humidity from the BME280 and return a percentage as a float. ==== */
100 | float hum();
101 |
102 | /* ==== Read the data from the BME280 with the specified units. ==== */
103 | void read(float& pressure, float& temp, float& humidity, bool metric = true, uint8_t p_unit = 0x0);
104 |
105 |
106 | /* ==== Read the data from the BME280 with the specified units and then calculate the altitude. ==== */
107 | float alt(bool metric = true, float seaLevelPressure = 101325);
108 |
109 | /* ==== Calculate the altitude based on the pressure with the specified units. ==== */
110 | float alt(float pressure, bool metric = true, float seaLevelPressure = 101325); // Pressure given in Pa.
111 |
112 | /* convert current pressure to sea-level pressure, returns sealevel pressure in mbar*/
113 | float sealevel(float A); // A: current altitude (meters).
114 |
115 | /* ==== Read BME280 data and calculate the dew point with the specified units. ==== */
116 | float dew(bool metric = true);
117 |
118 | /* ==== Calculate the dew point based on the temperature and humidity with the specified units. ==== */
119 | float dew(float temp, float hum, bool metric = true);
120 |
121 | };
122 | #endif // TG_BME_280_H
123 |
--------------------------------------------------------------------------------
/BME280I2C.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | BME280I2CI2C.cpp
3 | This code records data from the BME280I2C sensor and provides an API.
4 | This file is part of the Arduino BME280I2C library.
5 | Copyright (C) 2016 Tyler Glenn
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 |
20 | Written: Dec 30 2015.
21 | Last Updated: Jan 1 2016. - Happy New year!
22 |
23 | This header must be included in any derived code or copies of the code.
24 |
25 | Based on the data sheet provided by Bosch for the BME280I2C environmental sensor,
26 | calibration code based on algorithms providedBosch, some unit conversations courtesy
27 | of www.endmemo.com, altitude equation courtesy of NOAA, and dew point equation
28 | courtesy of Brian McNoldy at http://andrew.rsmas.miami.edu.
29 | */
30 |
31 |
32 | /* ==== Includes ==== */
33 | #include
34 | #include "BME280I2C.h"
35 | /* ==== END Includes ==== */
36 |
37 | /* ==== Methods ==== */
38 |
39 | bool BME280I2C::Initialize() {
40 | WriteRegister(CTRL_HUM_ADDR, controlHumidity);
41 | WriteRegister(CTRL_MEAS_ADDR, controlMeasure);
42 | WriteRegister(CONFIG_ADDR, config);
43 | return ReadTrim();
44 | }
45 |
46 |
47 | void BME280I2C::WriteRegister(uint8_t addr, uint8_t data)
48 | {
49 | Wire.beginTransmission(bme_280_addr);
50 | Wire.write(addr);
51 | Wire.write(data);
52 | Wire.endTransmission();
53 | }
54 |
55 | bool BME280I2C::ReadTrim()
56 | {
57 | uint8_t ord(0);
58 |
59 | // Temp. Dig
60 | Wire.beginTransmission(bme_280_addr);
61 | Wire.write(TEMP_DIG_ADDR);
62 | Wire.endTransmission();
63 |
64 | Wire.requestFrom(bme_280_addr, (uint8_t)6);
65 | while(Wire.available()){
66 | dig[ord++] = Wire.read();
67 | }
68 |
69 | // Pressure Dig
70 | Wire.beginTransmission(bme_280_addr);
71 | Wire.write(PRESS_DIG_ADDR);
72 | Wire.endTransmission();
73 |
74 | Wire.requestFrom(bme_280_addr, (uint8_t)18);
75 | while(Wire.available()){
76 | dig[ord++] = Wire.read();
77 | }
78 |
79 | // Humidity Dig 1
80 | Wire.beginTransmission(bme_280_addr);
81 | Wire.write(HUM_DIG_ADDR1);
82 | Wire.endTransmission();
83 |
84 | Wire.requestFrom(bme_280_addr, (uint8_t)1);
85 | while(Wire.available()){
86 | dig[ord++] = Wire.read();
87 | }
88 |
89 | // Humidity Dig 2
90 | Wire.beginTransmission(bme_280_addr);
91 | Wire.write(HUM_DIG_ADDR2);
92 | Wire.endTransmission();
93 |
94 | Wire.requestFrom(bme_280_addr, (uint8_t)7);
95 | while(Wire.available()){
96 | dig[ord++] = Wire.read();
97 | }
98 |
99 | #ifdef DEBUG_ON
100 | Serial.print("Dig: ");
101 | for(int i = 0; i < 32; ++i)
102 | {
103 | Serial.print(dig[i], HEX);
104 | Serial.print(" ");
105 | }
106 | Serial.println();
107 | #endif
108 |
109 | return ord == 32;
110 | }
111 |
112 | bool BME280I2C::ReadData(int32_t data[8]){
113 | uint8_t ord = 0;
114 |
115 | // for forced mode we need to write the mode to BME280 register before reading
116 | if ( (mode == 0x01) || (mode == 0x10) )
117 | setMode(mode);
118 |
119 | // Registers are in order. So we can start at the pressure register and read 8 bytes.
120 | Wire.beginTransmission(bme_280_addr);
121 | Wire.write(PRESS_ADDR);
122 | Wire.endTransmission();
123 |
124 | Wire.requestFrom(bme_280_addr, (uint8_t)8);
125 | while(Wire.available()){
126 | data[ord++] = Wire.read();
127 | }
128 |
129 | #ifdef DEBUG_ON
130 | Serial.print("Data: ");
131 | for(int i = 0; i < 8; ++i)
132 | {
133 | Serial.print(data[i], HEX);
134 | Serial.print(" ");
135 | }
136 | Serial.println();
137 | #endif
138 |
139 | return ord == 8;
140 | }
141 |
142 |
143 | BME280I2C::BME280I2C(uint8_t tosr, uint8_t hosr, uint8_t posr, uint8_t mode, uint8_t st, uint8_t filter,
144 | bool spiEnable, uint8_t bme_280_addr):
145 | BME280(tosr, hosr, posr, mode, st, filter, spiEnable), bme_280_addr(bme_280_addr)
146 | {
147 | // ctrl_hum register. (ctrl_hum[2:0] = Humidity oversampling rate.)
148 | controlHumidity = humidityOversamplingRate;
149 | // ctrl_meas register. (ctrl_meas[7:5] = temperature oversampling rate, ctrl_meas[4:2] = pressure oversampling rate, ctrl_meas[1:0] = mode.)
150 | controlMeasure = (tempOversamplingRate << 5) | (pressureOversamplingRate << 2) | mode;
151 | // config register. (config[7:5] = standby time, config[4:2] = filter, ctrl_meas[0] = spi enable.)
152 | config = (standbyTime << 5) | (filter << 2) | spiEnable;
153 | }
154 |
155 | #if defined(ARDUINO_ARCH_ESP8266)
156 | bool BME280I2C::begin(int SDA, int SCL) {
157 | // allow config of pins
158 | Wire.begin(SDA,SCL);
159 | return Initialize();
160 | }
161 | #endif
162 |
163 | bool BME280I2C::begin(){
164 | Wire.begin();
165 | return Initialize();
166 | }
167 |
168 | /* ==== END Methods ==== */
169 |
--------------------------------------------------------------------------------
/BME280I2C.h:
--------------------------------------------------------------------------------
1 | /*
2 | BME280I2C.h
3 | This code records data from the BME280 sensor and provides an API.
4 | This file is part of the Arduino BME280 library.
5 | Copyright (C) 2016 Tyler Glenn
6 |
7 | This program is free software: you can redistribute it and/or modify
8 | it under the terms of the GNU General Public License as published by
9 | the Free Software Foundation, either version 3 of the License, or
10 | (at your option) any later version.
11 |
12 | This program is distributed in the hope that it will be useful,
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 | GNU General Public License for more details.
16 |
17 | You should have received a copy of the GNU General Public License
18 | along with this program. If not, see .
19 |
20 | Written: Sep 19 2016.
21 | Last Updated: Sep 19 2016. - Happy Fall! <5
22 | This code is licensed under the GNU LGPL and is open for ditrbution
23 | and copying in accordance with the license.
24 | This header must be included in any derived code or copies of the code.
25 |
26 | Based on the data sheet provided by Bosch for the Bme280 environmental sensor.
27 | */
28 | #ifndef TG_BME_280_I2C_H
29 | #define TG_BME_280_I2C_H
30 |
31 | /* ==== Includes ==== */
32 | #include "BME280.h"
33 | /* ==== END Includes ==== */
34 |
35 | /* ==== Defines ==== */
36 |
37 | /* ==== END Defines ==== */
38 |
39 |
40 | class BME280I2C: public BME280{
41 | uint8_t bme_280_addr;
42 |
43 | /* ==== Write configuration to BME280, return true if successful. ==== */
44 | bool Initialize();
45 |
46 | /* ==== Write values to BME280 registers. ==== */
47 | virtual void WriteRegister(uint8_t addr, uint8_t data);
48 |
49 | /* ==== Read the the trim data from the BME280, return true if successful. ==== */
50 | virtual bool ReadTrim();
51 |
52 | /* ==== Read the raw data from the BME280 into an array and return true if successful. ==== */
53 | virtual bool ReadData(int32_t data[8]);
54 |
55 | public:
56 | /* ==== Constructor used to create the class. All parameters have default values. ==== */
57 | BME280I2C(uint8_t tosr = 0x1, uint8_t hosr = 0x1, uint8_t posr = 0x1, uint8_t mode = 0x1,
58 | uint8_t st = 0x5, uint8_t filter = 0x0, bool spiEnable = false,
59 | uint8_t bme_280_addr = 0x76); // Oversampling = 1, mode = forced, standby time = 1000ms, filter = none.
60 |
61 | /* ==== Method used at start up to initialize the class. Starts the I2C interface. ==== */
62 | virtual bool begin();
63 | #if defined(ARDUINO_ARCH_ESP8266)
64 | /* ==== On esp8266 it is possible to define I2C pins ==== */
65 | bool begin(int SDA, int SCL);
66 | #endif
67 |
68 | };
69 | #endif // TG_BME_280_I2C_H
70 |
--------------------------------------------------------------------------------
/LoRaWAN_TTN_Env_Node.ino:
--------------------------------------------------------------------------------
1 | /*
2 | * ------------------------------------------------------------------------
3 | * "PH2LB LICENSE" (Revision 1) : (based on "THE BEER-WARE LICENSE" Rev 42)
4 | * wrote this file. As long as you retain this notice
5 | * you can do modify it as you please. It's Free for non commercial usage
6 | * and education and if we meet some day, and you think this stuff is
7 | * worth it, you can buy me a beer in return
8 | * Lex Bolkesteijn
9 | * ------------------------------------------------------------------------
10 | * Filename : LoRaWAN_TTN_Env_Node.ino
11 | * Version : 1.1 (BETA)
12 | * ------------------------------------------------------------------------
13 | * Description : A low power BME280 based datalogger for the ThingsNetwork.
14 | * with deepsleep support and variable interval
15 | * ------------------------------------------------------------------------
16 | * Revision :
17 | * - 2018-jan-04 1.2 reset status detection (on brownout detect skip Tx on startup)
18 | * - 2017-dec-05 1.1 minor code updates
19 | * - 2017-jul-17 1.0 first "beta"
20 | * ------------------------------------------------------------------------
21 | * Hardware used :
22 | * - Arduino Pro-Mini 3.3V
23 | * - RFM95W
24 | * - BME280 sensor.
25 | * ------------------------------------------------------------------------
26 | * Software used :
27 | * - LMIC https://github.com/matthijskooijman/arduino-lmic
28 | * - LowPower library https://github.com/rocketscream/Low-Power
29 | * - MiniCore loader https://forum.arduino.cc/index.php?topic=412070 https://github.com/MCUdude/MiniCore
30 | * To use a brownout detection of 1.8V.
31 | * - special adcvcc library from Charles (see : https://www.thethingsnetwork.org/forum/t/full-arduino-mini-lorawan-and-1-3ua-sleep-mode/8059/32?u=lex_ph2lb )
32 | * - BME280 library https://github.com/finitespace/BME280
33 | *
34 | * For licenses of the used libraries, check the links above.
35 | * ------------------------------------------------------------------------
36 | * Aditional note :
37 | *
38 | * I use a HTTP integration on the TTN with the decoder below.
39 | * ------------------------------------------------------------------------
40 | * TODO LIST :
41 | * - add more sourcode comment
42 | *
43 | *
44 | ****************************************************************************************
45 | * TheThingsNetwork Payload functions :
46 | function Decoder(bytes, port)
47 | {
48 | var retValue = {
49 | bytes: bytes
50 | };
51 |
52 | retValue.batt = bytes[0] / 10.0;
53 | if (retValue.batt === 0)
54 | delete retValue.batt;
55 |
56 | if (bytes.length >= 2)
57 | {
58 | retValue.humidity = bytes[1];
59 | if (retValue.humidity === 0)
60 | delete retValue.humidity;
61 | }
62 | if (bytes.length >= 3)
63 | {
64 | retValue.temperature = (((bytes[2] << 8) | bytes[3]) / 10.0) - 40.0;
65 | }
66 | if (bytes.length >= 5)
67 | {
68 | retValue.pressure = ((bytes[4] << 8) | bytes[5]);
69 | if (retValue.pressure === 0)
70 | delete retValue.pressure;
71 | }
72 |
73 | return retValue;
74 | }
75 | */
76 |
77 |
78 | /*
79 | *****************************************************************************************
80 | * INCLUDE FILES
81 | *****************************************************************************************
82 | */
83 | #include
84 | #include
85 | #include
86 | #include
87 | #include
88 | #include "adcvcc.h"
89 | #include "BME280I2C.h"
90 |
91 | /*
92 | * Defines
93 | */
94 |
95 | #define debugSerial Serial
96 | #define SHOW_DEBUGINFO
97 | #define debugPrintLn(...) { if (debugSerial) debugSerial.println(__VA_ARGS__); }
98 | #define debugPrint(...) { if (debugSerial) debugSerial.print(__VA_ARGS__); }
99 | #define debugFlush() { if (debugSerial) debugSerial.flush(); }
100 |
101 | // When enable BROWNOUTDISABLED be sure that you have BOD fused.
102 | // #define BROWNOUTDISABLED
103 |
104 | #define NORMALINTERVAL 900 // 15 minutes (normal)
105 | //#define NORMALINTERVAL 32 // 32 second (test highspeed)
106 |
107 | // Restrict to channel0 if uncommented; otherwise all channels (allways use SF7)
108 | // #define CHANNEL0
109 |
110 | //// Choice on of the LMIC pinnings below.
111 |
112 | //// Pin mapping for TTN Enschede board
113 | //#define LMIC_NSS 10
114 | //#define LMIC_RXTX 6
115 | //#define LMIC_RST LMIC_UNUSED_PIN
116 | //#define LMIC_DIO0 4
117 | //#define LMIC_DIO1 5
118 | //#define LMIC_DIO2 7
119 |
120 | // Pin mapping CH2I (check out : https://www.thethingsnetwork.org/forum/t/full-arduino-mini-lorawan-and-1-3ua-sleep-mode/8059 )
121 | #define LMIC_NSS 10
122 | #define LMIC_RXTX LMIC_UNUSED_PIN
123 | #define LMIC_RST LMIC_UNUSED_PIN
124 | #define LMIC_DIO0 2
125 | #define LMIC_DIO1 7
126 | #define LMIC_DIO2 8
127 |
128 |
129 | const lmic_pinmap lmic_pins = {
130 | .nss = LMIC_NSS,
131 | .rxtx = LMIC_RXTX,
132 | .rst = LMIC_RST,
133 | .dio = {LMIC_DIO0, LMIC_DIO1, LMIC_DIO2},
134 | };
135 |
136 | // UPDATE WITH YOURE TTN KEYS AND ADDR
137 | static const PROGMEM u1_t NWKSKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; // LoRaWAN NwkSKey, network session key
138 | static const u1_t PROGMEM APPSKEY[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00A6, 0x00 }; // LoRaWAN AppSKey, application session key
139 | static const u4_t DEVADDR = 0x00000000 ; // LoRaWAN end-device address (DevAddr)
140 |
141 |
142 | // These callbacks are only used in over-the-air activation, so they are
143 | // left empty here (we cannot leave them out completely unless
144 | // DISABLE_JOIN is set in config.h, otherwise the linker will complain).
145 | void os_getArtEui (u1_t* buf) { }
146 | void os_getDevEui (u1_t* buf) { }
147 | void os_getDevKey (u1_t* buf) { }
148 |
149 | static osjob_t sendjob;
150 |
151 | // global enviromental parameters
152 | static float temp = 0.0;
153 | static float pressure = 0.0;
154 | static float humidity = 0.0;
155 |
156 | int interval = NORMALINTERVAL;
157 |
158 | byte LMIC_transmitted = 0;
159 | int LMIC_event_Timeout = 0;
160 |
161 | // Schedule TX every this many seconds (might become longer due to duty
162 | // cycle limitations).
163 | // const unsigned TX_INTERVAL = 60;
164 |
165 | BME280I2C bme; // Default : forced mode, standby time = 1000 ms
166 | // Oversampling = pressure ×1, temperature ×1, humidity ×1, filter off,
167 |
168 | /* ======================================================================
169 | Function: ADC_vect
170 | Purpose : IRQ Handler for ADC
171 | Input : -
172 | Output : -
173 | Comments: used for measuring 8 samples low power mode, ADC is then in
174 | free running mode for 8 samples
175 | ====================================================================== */
176 | ISR(ADC_vect)
177 | {
178 | // Increment ADC counter
179 | _adc_irq_cnt++;
180 | }
181 |
182 |
183 | void updateEnvParameters()
184 | {
185 | delay(1000);
186 | temp = bme.temp(true);
187 | pressure = bme.pres(1); // 1 = hPa (milliBar)
188 | humidity = bme.hum();
189 | }
190 |
191 | void onEvent (ev_t ev)
192 | {
193 | debugPrint(os_getTime());
194 | debugPrint(": ");
195 | debugPrintLn(ev);
196 | switch(ev)
197 | {
198 | case EV_SCAN_TIMEOUT:
199 | //debugPrintLn(F("EV_SCAN_TIMEOUT"));
200 | break;
201 | case EV_BEACON_FOUND:
202 | //debugPrintLn(F("EV_BEACON_FOUND"));
203 | break;
204 | case EV_BEACON_MISSED:
205 | //debugPrintLn(F("EV_BEACON_MISSED"));
206 | break;
207 | case EV_BEACON_TRACKED:
208 | //debugPrintLn(F("EV_BEACON_TRACKED"));
209 | break;
210 | case EV_JOINING:
211 | //debugPrintLn(F("EV_JOINING"));
212 | break;
213 | case EV_JOINED:
214 | //debugPrintLn(F("EV_JOINED"));
215 | break;
216 | case EV_RFU1:
217 | //debugPrintLn(F("EV_RFU1"));
218 | break;
219 | case EV_JOIN_FAILED:
220 | //debugPrintLn(F("EV_JOIN_FAILED"));
221 | break;
222 | case EV_REJOIN_FAILED:
223 | //debugPrintLn(F("EV_REJOIN_FAILED"));
224 | break;
225 | case EV_TXCOMPLETE:
226 | debugPrintLn(F("EV_TXC"));
227 | if (LMIC.txrxFlags & TXRX_ACK)
228 | debugPrintLn(F("R ACK")); // Received ack
229 | if (LMIC.dataLen)
230 | {
231 | debugPrintLn(F("R "));
232 | debugPrintLn(LMIC.dataLen);
233 | debugPrintLn(F(" bytes")); // of payload
234 | }
235 | // Schedule next transmission
236 | // os_setTimedCallback(&sendjob, os_getTime()+sec2osticks(TX_INTERVAL), do_send);
237 | LMIC_transmitted = 1;
238 | break;
239 | case EV_LOST_TSYNC:
240 | //debugPrintLn(F("EV_LOST_TSYNC"));
241 | break;
242 | case EV_RESET:
243 | //debugPrintLn(F("EV_RESET"));
244 | break;
245 | case EV_RXCOMPLETE:
246 | // data received in ping slot
247 | //debugPrintLn(F("EV_RXCOMPLETE"));
248 | break;
249 | case EV_LINK_DEAD:
250 | //debugPrintLn(F("EV_LINK_DEAD"));
251 | break;
252 | case EV_LINK_ALIVE:
253 | //debugPrintLn(F("EV_LINK_ALIVE"));
254 | break;
255 | default:
256 | //debugPrintLn(F("Unknown event"));
257 | break;
258 | }
259 | }
260 |
261 | void do_send(osjob_t* j)
262 | {
263 | // Check if there is not a current TX/RX job running
264 | if (LMIC.opmode & OP_TXRXPEND)
265 | {
266 | debugPrintLn(F("OP_TXRXPEND")); //P_TXRXPEND, not sending
267 | }
268 | else
269 | {
270 | // Prepare upstream data transmission at the next possible time.
271 |
272 | // Here the sensor information should be retrieved
273 | // Pressure: 300...1100 hPa
274 | // Temperature: -40…85°C
275 | updateEnvParameters();
276 | updateEnvParameters();
277 |
278 | int batt = (int)(readVcc() / 100); // readVCC returns mVolt need just 100mVolt steps
279 | byte batvalue = (byte)batt; // no problem putting it into a int.
280 |
281 | #ifdef SHOW_DEBUGINFO
282 | debugPrint(F("T="));
283 | debugPrintLn(temp);
284 |
285 | debugPrint(F("P="));
286 | debugPrintLn(pressure);
287 |
288 | debugPrint(F("H="));
289 | debugPrintLn(humidity);
290 |
291 | debugPrint(F("B="));
292 | debugPrintLn(batt);
293 | debugPrint(F("BV="));
294 | debugPrintLn(batvalue);
295 | #endif
296 | int t = (int)((temp + 40.0) * 10.0);
297 | // t = t + 40; => t [-40..+85] => [0..125] => t = t * 10; => t [0..125] => [0..1250]
298 | int p = (int)(pressure); // p [300..1100]
299 | int h = (int)(humidity);
300 |
301 | unsigned char mydata[6];
302 | mydata[0] = batvalue;
303 | mydata[1] = h & 0xFF;
304 | mydata[2] = t >> 8;
305 | mydata[3] = t & 0xFF;
306 | mydata[4] = p >> 8;
307 | mydata[5] = p & 0xFF;
308 |
309 | LMIC_setTxData2(1, mydata, sizeof(mydata), 0);
310 | debugPrintLn(F("PQ")); //Packet queued
311 | }
312 | // Next TX is scheduled after TX_COMPLETE event.
313 | }
314 |
315 |
316 | bool bootFromBrownOut = false;
317 |
318 | // shows the bootstatus on serial output and set bootFromBrownout flag.
319 | void showBootStatus(uint8_t _mcusr)
320 | {
321 | debugPrint(F("mcusr = "));
322 | debugPrint(_mcusr, HEX);
323 | debugPrint(F(" > "));
324 | if (_mcusr & (1<= 2)
40 | {
41 | retValue.humidity = bytes[1];
42 | if (retValue.humidity === 0)
43 | delete retValue.humidity;
44 | }
45 | if (bytes.length >= 3)
46 | {
47 | retValue.temperature = (((bytes[2] << 8) | bytes[3]) / 10.0) - 40.0;
48 | }
49 | if (bytes.length >= 5)
50 | {
51 | retValue.pressure = ((bytes[4] << 8) | bytes[5]);
52 | if (retValue.pressure === 0)
53 | delete retValue.pressure;
54 | }
55 |
56 | return retValue;
57 | }
58 |
--------------------------------------------------------------------------------
/adcvcc.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "Arduino.h"
5 |
6 |
7 | volatile uint8_t _adc_irq_cnt;
8 |
9 |
10 | // add this to the INO of you project.
11 | ///* ======================================================================
12 | //Function: ADC_vect
13 | //Purpose : IRQ Handler for ADC
14 | //Input : -
15 | //Output : -
16 | //Comments: used for measuring 8 samples low power mode, ADC is then in
17 | // free running mode for 8 samples
18 | //====================================================================== */
19 | //ISR(ADC_vect)
20 | //{
21 | // // Increment ADC counter
22 | // _adc_irq_cnt++;
23 | //}
24 |
25 |
26 | /* ======================================================================
27 | Function: readADCLowNoise
28 | Purpose : Read Analog Value with reducing noise
29 | Input : true return the average value, false return only the sum
30 | Output : average value read
31 | Comments: hard coded to read 8 samples each time
32 | AD MUX Channel and ADC must have been set before this call
33 | ====================================================================== */
34 | uint16_t readADCLowNoise(boolean average)
35 | {
36 | uint8_t low, high;
37 | uint16_t sum = 0;
38 |
39 | // Start 1st Conversion, but ignore it, can be hazardous
40 | ADCSRA |= _BV(ADSC);
41 |
42 | // wait for first dummy conversion
43 | while (bit_is_set(ADCSRA,ADSC));
44 |
45 | // Init our measure counter
46 | _adc_irq_cnt = 0;
47 |
48 | // We want to have a interrupt when the conversion is done
49 | ADCSRA |= _BV( ADIE );
50 |
51 | // Loop thru samples
52 | // 8 samples (we don't take the 1st one)
53 | do {
54 | // Enable Noise Reduction Sleep Mode
55 | set_sleep_mode( SLEEP_MODE_ADC );
56 | sleep_enable();
57 |
58 | // Wait until conversion is finished
59 | do {
60 | // enabled IRQ before sleeping
61 | sei();
62 | sleep_cpu();
63 | cli();
64 | }
65 | // Check is done with interrupts disabled to avoid a race condition
66 | while (bit_is_set(ADCSRA,ADSC));
67 |
68 | // No more sleeping
69 | sleep_disable();
70 | sei();
71 |
72 | // read low first
73 | low = ADCL;
74 | high = ADCH;
75 |
76 | // Sum the total
77 | sum += ((high << 8) | low);
78 | }
79 | while (_adc_irq_cnt<8);
80 |
81 | // No more interrupts needed for this
82 | // we finished the job
83 | ADCSRA &= ~ _BV( ADIE );
84 |
85 | // Return the average divided by 8 (8 samples)
86 | return ( average ? sum >> 3 : sum );
87 | }
88 |
89 | /* ======================================================================
90 | Function: readVcc
91 | Purpose : Read and Calculate V powered, the Voltage on Arduino VCC pin
92 | Input : -
93 | Output : value readed
94 | Comments: ADC Channel input is modified
95 | ====================================================================== */
96 | uint16_t readVcc()
97 | {
98 | uint16_t value, _vcc;
99 |
100 | // Enable ADC (just in case going out of low power)
101 | power_adc_enable();
102 | ADCSRA |= _BV(ADEN) ;
103 |
104 | // Read 1.1V reference against AVcc
105 | // REFS1 REFS0 --> 0 1, AVcc internal ref. -Selects AVcc external reference
106 | // MUX3 MUX2 MUX1 MUX0 --> 1110 1.1V (VBG) -Selects channel 14, bandgap voltage, to measure
107 | ADMUX = (0< 5500 ) _vcc = 5500 ;
124 |
125 | // Vcc in millivolts
126 | return ( _vcc );
127 | }
128 |
--------------------------------------------------------------------------------
/adcvcc.h:
--------------------------------------------------------------------------------
1 |
2 | extern uint16_t readVcc();
3 |
4 | extern volatile uint8_t _adc_irq_cnt;
5 |
--------------------------------------------------------------------------------