DIY Digital Barograph with BME280 and ESP32 - 24 Hour Pressure Trends
This is a modern digital interpretation of a traditional barograph that displays real-time atmospheric pressure trends over a 24-hour period using a high-precision BME280 sensor and ESP32 microcontroller.
Components and supplies
1
DOIT ESP32 DevKit v1
1
10 10K trimmer
3
Grove - Button (P)
1
Graphic LCD 128x64 STN LED Backlight
1
Grove - Temp & Humi & Barometer Sensor (BME280)
Tools and machines
1
Soldering kit
Apps and platforms
1
Arduino IDE
Project description
Code
Code Baro
cpp
..
1/* 2***Barometer/Barograph*** 3Components: 4- ESP32 microcontroller (esp32 devkit v1) 5- 128x64 LCD display 6- BME280 pressure, temperature and humidity sensor 7 8Features: 9* Pressure change graph for the last 24 hours (measurement interval: 11 minutes 15 seconds) 10* Temperature display 11* Humidity display 12* Pressure measurement history storage (to protect against graph reset during power loss) 13* Ability to check all sensor readings 14* Ability to adjust the pressure range displayed on screen (min and max) 15*/ 16 17#include <U8g2lib.h> // U8g2 library for graphical displays 18#include <Wire.h> // Wire library for I2C 19#include <Adafruit_Sensor.h> // Common sensor library 20#include <Adafruit_BME280.h> // BME280 sensor library 21#include <Preferences.h> // For saving settings to memory (pressure array) 22#include <EncButton.h> // Button library by Gyver 23 24#define BUFFER_SIZE 128 // Pressure buffer size 25#define MIN_PRES 985 // Minimum pressure in hPa 26#define PRESSURE_RANGE 50 // Pressure range in hPa (max = MIN_PRES + PRESSURE_RANGE) 27#define ALTITUDE 700.0 // Set your location's altitude in meters here 28#define EEPROM_MARKER 0xABCE // Unique marker (for detecting first save) 29#define BUTTON1_PIN 13 30#define BUTTON2_PIN 12 31#define BUTTON3_PIN 14 32#define BUTTON_PIN 16 // Button pin 33#define USE_MEMORY 34 35// Use memory to store pressure array 36 37Button b1(BUTTON1_PIN); 38Button b2(BUTTON2_PIN); 39Button b3(BUTTON3_PIN); 40 41char pressureDiff1h[10]; // For -1h difference 42char pressureDiff3h[10]; // For -3h difference 43 44float hourlyPressureBuffer[6]; // Store actual pressure values for the last hour 45int pressureBufferIndex = 0; 46unsigned long lastPressureStoreTime = 0; 47 48 49/*Required objects*/ 50Preferences preferences; // Object for working with persistent memory 51class RingBuffer { // Ring buffer object 52 private: 53 int buffer[BUFFER_SIZE]; // The buffer itself 54 int head = 0; // Pointer to data start 55 int tail = 0; // Pointer to data end 56 bool full = false; // Buffer full flag 57 58 public: 59 void push(int value) { 60 value = constrain(value, 0, PRESSURE_RANGE); 61 buffer[tail] = value; 62 tail = (tail + 1) % BUFFER_SIZE; 63 64 if (full) { 65 head = (head + 1) % BUFFER_SIZE; // Overwrite old data when full 66 } 67 68 full = (tail == head); 69 } 70 71 float getPressureValue(int index) { 72 if (index >= size()) return -1; 73 int pos = (head + index) % BUFFER_SIZE; 74 return MIN_PRES + (float)buffer[pos] * PRESSURE_RANGE / PRESSURE_RANGE; 75 } 76 bool pop(int &value) { 77 if (isEmpty()) return false; 78 79 value = buffer[head]; 80 head = (head + 1) % BUFFER_SIZE; 81 full = false; 82 83 return true; 84 } 85 86 int peek(int index) { 87 if (index >= size()) return -1; // Check index validity 88 89 int pos = (head + index) % BUFFER_SIZE; 90 return buffer[pos]; 91 } 92 93 bool isEmpty() { 94 return (!full && (head == tail)); 95 } 96 97 bool isFull() { 98 return full; 99 } 100 101 int size() { 102 if (full) return BUFFER_SIZE; 103 if (tail >= head) return tail - head; 104 return BUFFER_SIZE - head + tail; 105 } 106 107 void clear() { 108 head = tail = 0; 109 full = false; 110 } 111 112 void saveBufferToEEPROM() { 113 preferences.begin("ring_buffer", false); // Open memory area 114 preferences.putUShort("marker", EEPROM_MARKER); 115 preferences.putBytes("data", buffer, sizeof(buffer)); 116 preferences.putInt("head", head); 117 preferences.putInt("tail", tail); 118 preferences.putBool("full", full); 119 preferences.end(); // Close memory area 120 } 121 122 void loadBufferFromEEPROM() { 123 preferences.begin("ring_buffer", true); // Open memory area in read mode 124 if (preferences.getUShort("marker", 0) != EEPROM_MARKER) { 125 clear(); 126 for (int i = 0; i < 128; i++) { // Fill buffer with maximum pressure if memory is empty 127 push(PRESSURE_RANGE); 128 } 129 } else { 130 if (preferences.isKey("data")) { 131 preferences.getBytes("data", buffer, sizeof(buffer)); 132 head = preferences.getInt("head", 0); 133 tail = preferences.getInt("tail", 0); 134 full = preferences.getBool("full", false); 135 } 136 } 137 preferences.end(); // Close memory area 138 } 139}; 140 141Button b(BUTTON_PIN); // Button on pin 13 142RingBuffer ringBuffer; // Create ring buffer for pressure array 143U8G2_ST7920_128X64_F_SW_SPI u8g2(U8G2_R0,/* SCLK = */18,/* MOSI = */23,/* CS = */4); // Display initialization via software SPI 144Adafruit_BME280 bme; // Object for working with BME280 sensor 145 146/*Variables*/ 147uint32_t myTimer1, myTimer3; // Timer variables 148 149char pressure[7], // Strings for displaying data on screen 150 pressureDiff[6], 151 pres_scale_1[7], 152 pres_scale_2[7], 153 pres_scale_3[7], 154 pressureDiffSign[2] = " ", 155 temperature[6], 156 humidity[6]; 157 158uint16_t varPress; 159float varPressNorm; 160uint8_t varTemp; 161uint8_t varHum; 162int varPressureDiff; 163 164float getRelativePressure() { 165 float absolute_pressure = bme.readPressure() / 100.0f; // Convert Pa to hPa 166 return absolute_pressure * pow(1.0 - (ALTITUDE / 44330.0), -5.255); 167} 168 169void drawCurrentConditions() { 170 u8g2.clearBuffer(); 171 u8g2.drawFrame(0,0,128,64); 172 173 // Title with smaller font 174 u8g2.setFont(u8g2_font_04b_03_tr); // Smaller font for title 175 u8g2.drawStr(5, 8, "Current Conditions:"); 176 177 // Values with large font 178 u8g2.setFont(u8g2_font_9x18B_tf); // Larger font for readings 179 180 char tempStr[20]; 181 182 // Pressure 183 sprintf(tempStr, "P: %.1f hPa", varPressNorm); 184 u8g2.drawStr(5, 23, tempStr); 185 186 // Temperature 187 sprintf(tempStr, "T: %d C", varTemp); 188 u8g2.drawStr(5, 41, tempStr); 189 190 // Humidity 191 sprintf(tempStr, "H: %d %%", varHum); 192 u8g2.drawStr(5, 59, tempStr); 193 194 u8g2.sendBuffer(); 195} 196 197void firstScreen() { // Sensor check screen 198 u8g2.clearBuffer(); 199 u8g2.drawFrame(0,0,128,64); 200 u8g2.setFont(u8g2_font_7x14B_tf); 201 u8g2.drawStr(7, 35, "Checking sensors"); 202 u8g2.sendBuffer(); 203 delay(2000); 204 205 bool bme280_ok = bme.begin(0x76); 206 207 if (bme280_ok) { 208 varPressNorm = getRelativePressure(); 209 varTemp = round(bme.readTemperature()); 210 varHum = round(bme.readHumidity()); 211 } 212 u8g2.clearBuffer(); 213 u8g2.drawFrame(0,0,128,64); 214 u8g2.setFont(u8g2_font_7x14B_tf); 215 u8g2.drawStr(30, 35, "BME280:"); 216 u8g2.drawStr(78, 35, bme280_ok ? "Ok" : "Err"); 217 u8g2.sendBuffer(); 218 delay(500); 219 if (!bme280_ok) { 220 while (1); // Freeze if BME280 isn't working 221 } 222 delay(2000); 223} 224 225void drawScreen() { 226 u8g2.clearBuffer(); 227 const char* errStr = "---"; 228 229 if ((varTemp > 100)||(varHum > 100)) { 230 sprintf(pressure, "%s", errStr); 231 sprintf(temperature, "%s", errStr); 232 sprintf(humidity, "%s", errStr); 233 } else { 234 sprintf(pressure, "%.1f", varPressNorm); 235 sprintf(temperature, "%d", varTemp); 236 sprintf(humidity, "%d", varHum); 237 } 238 239 // Calculate pressure difference using the hourly buffer 240 if (pressureBufferIndex >= 5) { // We have enough readings 241 float currentPressure = varPressNorm; 242 float oldPressure = hourlyPressureBuffer[(pressureBufferIndex - 5) % 6]; 243 float pressureDiffFloat = currentPressure - oldPressure; 244 245 if (fabs(pressureDiffFloat) < 0.1) { 246 sprintf(pressureDiffSign, " "); 247 sprintf(pressureDiff, "0.0"); 248 } else { 249 if (pressureDiffFloat > 0) { 250 sprintf(pressureDiffSign, "+"); 251 } else { 252 sprintf(pressureDiffSign, "-"); 253 } 254 sprintf(pressureDiff, "%.1f", fabs(pressureDiffFloat)); 255 } 256 } else { 257 sprintf(pressureDiffSign, " "); 258 sprintf(pressureDiff, "---"); 259 } 260 261 sprintf(pres_scale_3, "%d", MIN_PRES); 262 sprintf(pres_scale_2, "%d", MIN_PRES + (PRESSURE_RANGE/2)); 263 sprintf(pres_scale_1, "%d", MIN_PRES + PRESSURE_RANGE); 264 265 for (uint8_t i = 0; i < 128; i++) { // Draw pressure graph (flip array and limit upper/lower bounds) 266 uint8_t gr = ringBuffer.peek(i); 267 268 //gr = map(gr, PRESSURE_RANGE, 0, 63, 23); // Old version - inverted 269 gr = map(gr, 0, PRESSURE_RANGE, 63, 23); // New version - correct 270 271 u8g2.drawBox(i, gr, 1, (64 - gr)); 272 } 273 274 u8g2.setFont(u8g2_font_7x14B_tf); // Set font and display data (pressure, humidity, temperature) 275 u8g2.drawStr(2, 19, pressure); 276 u8g2.drawStr(60, 19, humidity); 277 u8g2.drawStr(90, 19, temperature); 278 279 u8g2.setFont(u8g2_font_04b_03_tr ); // Pressure change over 24 hours 280 u8g2.drawStr(111, 19, pressureDiffSign); 281 u8g2.drawStr(115, 19, pressureDiff); 282 u8g2.drawStr(15, 5, "hPa H% C -1h"); 283 u8g2.drawCircle(92, 1, 1); 284 285 u8g2.setDrawColor(0); // Draw light areas for pressure scale numbers 286 u8g2.drawBox(0, 58, 15, 6); 287 u8g2.drawBox(0, 40, 15, 7); 288 u8g2.drawBox(0, 23, 15, 6); 289 290 u8g2.setDrawColor(1); // Draw pressure scale numbers 291 u8g2.drawStr(0, 64, pres_scale_3); 292 u8g2.drawStr(0, 46, pres_scale_2); 293 u8g2.drawStr(0, 28, pres_scale_1); 294 295 drawDashedLine(15, 43, 128); // Draw dashed lines 296 drawDashedLine(1, 53, 128); 297 drawDashedLine(1, 33, 128); 298 299 drawVerticalDashedLine(32, 24, 64); 300 drawVerticalDashedLine(64, 24, 64); 301 drawVerticalDashedLine(96, 24, 64); 302 303 u8g2.sendBuffer(); 304} 305 306void drawDashedLine(uint8_t x_start, uint8_t y, uint8_t length) { // Draw horizontal line 307 for (uint8_t i = x_start; i < length; i += 2) { 308 u8g2.setDrawColor(1); // Dark pixel 309 u8g2.drawPixel(i, y); 310 u8g2.setDrawColor(0); // Light pixel 311 u8g2.drawPixel(i + 1, y); 312 } 313 u8g2.setDrawColor(1); // Restore normal color for other elements 314} 315 316void drawVerticalDashedLine(uint8_t x, uint8_t y_start, uint8_t y_end) { // Draw vertical line 317 for (uint8_t i = y_start; i < y_end; i += 2) { 318 u8g2.setDrawColor(1); // Dark pixel 319 u8g2.drawPixel(x, i); 320 u8g2.setDrawColor(0); // Light pixel 321 u8g2.drawPixel(x, i + 1); 322 } 323 u8g2.setDrawColor(1); // Restore normal color 324} 325 326void getData() { // Periodic sensor data reading during operation 327 bme.begin(0x76); 328 varPressNorm = getRelativePressure(); // Get relative pressure in hPa 329 varTemp = round(bme.readTemperature()); 330 varHum = round(bme.readHumidity()); 331 varPressureDiff = ringBuffer.peek(0) - ringBuffer.peek(127); // Calculate pressure difference 332 switch (varPressureDiff) { // Difference with + or - sign 333 case -64 ... -1: sprintf(pressureDiffSign, "-"); break; 334 case 0: sprintf(pressureDiffSign, " "); break; 335 case 1 ... 64: sprintf(pressureDiffSign, "+"); break; 336 } 337} 338 339void resetBuffer() { 340 float initialPressure = getRelativePressure(); 341 int pressureForGraph = round(initialPressure - MIN_PRES); 342 pressureForGraph = constrain(pressureForGraph, 0, PRESSURE_RANGE); 343 344 for (int i = 0; i < 128; i++) { 345 ringBuffer.push(pressureForGraph); // Initialize all slots with current pressure 346 } 347} 348 349void drawPressureDiffScreen1h() { 350 u8g2.clearBuffer(); 351 u8g2.drawFrame(0,0,128,64); 352 353 // Title 354 u8g2.setFont(u8g2_font_7x14B_tf); 355 u8g2.drawStr(10, 15, "Pressure Change"); 356 u8g2.drawStr(30, 30, "-1 hour"); 357 358 // Value in large font 359 u8g2.setFont(u8g2_font_10x20_tf); 360 if (pressureBufferIndex >= 5) { // We have enough readings for -1h 361 float currentPressure = varPressNorm; 362 float oldPressure = hourlyPressureBuffer[(pressureBufferIndex - 5) % 6]; 363 float pressureDiffFloat = currentPressure - oldPressure; 364 365 char sign[2] = " "; 366 char value[10]; 367 368 if (fabs(pressureDiffFloat) < 0.1) { 369 sprintf(sign, " "); 370 sprintf(value, "0.0"); 371 } else { 372 if (pressureDiffFloat > 0) { 373 sprintf(sign, "+"); 374 } else { 375 sprintf(sign, "-"); 376 } 377 sprintf(value, "%.1f", fabs(pressureDiffFloat)); 378 } 379 380 u8g2.drawStr(20, 55, sign); 381 u8g2.drawStr(30, 55, value); 382 u8g2.drawStr(70, 55, "hPa"); 383 } else { 384 u8g2.drawStr(50, 55, "N/A"); 385 } 386 387 u8g2.sendBuffer(); 388} 389 390void drawPressureDiffScreen3h() { 391 u8g2.clearBuffer(); 392 393 u8g2.drawFrame(0,0,128,64); 394 395 // Title 396 u8g2.setFont(u8g2_font_7x14B_tf); 397 u8g2.drawStr(10, 15, "Pressure Change"); 398 u8g2.drawStr(30, 30, "-3 hours"); 399 400 // Calculate 3-hour difference 401 if (pressureBufferIndex >= 15) { // We need at least 15 readings (3 hours) 402 float currentPressure = varPressNorm; 403 float oldPressure = hourlyPressureBuffer[(pressureBufferIndex - 15) % 6]; 404 float pressureDiffFloat = currentPressure - oldPressure; 405 406 char sign[2] = " "; 407 char value[10]; 408 409 if (fabs(pressureDiffFloat) < 0.1) { 410 sprintf(sign, " "); 411 sprintf(value, "0.0"); 412 } else { 413 if (pressureDiffFloat > 0) { 414 sprintf(sign, "+"); 415 } else { 416 sprintf(sign, "-"); 417 } 418 sprintf(value, "%.1f", fabs(pressureDiffFloat)); 419 } 420 421 // Value in large font 422 u8g2.setFont(u8g2_font_10x20_tf); 423 u8g2.drawStr(20, 55, sign); 424 u8g2.drawStr(30, 55, value); 425 u8g2.drawStr(70, 55, "hPa"); 426 } else { 427 u8g2.setFont(u8g2_font_10x20_tf); 428 u8g2.drawStr(50, 55, "N/A"); 429 } 430 431 u8g2.sendBuffer(); 432} 433void setup() { 434 pinMode(BUTTON1_PIN, INPUT_PULLUP); 435 pinMode(BUTTON2_PIN, INPUT_PULLUP); 436 pinMode(BUTTON3_PIN, INPUT_PULLUP); 437 438 Wire.begin(); 439 u8g2.begin(); 440 delay(20); 441 442 resetBuffer(); // Clear pressure buffer 443 444 firstScreen(); // Check sensors before startup 445 446 float initialPressure = getRelativePressure(); 447 for (int i = 0; i < 6; i++) { 448 hourlyPressureBuffer[i] = initialPressure; 449 } 450 451#ifdef USE_MEMORY // If using persistent memory 452 ringBuffer.loadBufferFromEEPROM(); // Restore data from memory 453 454 if (!(digitalRead(BUTTON_PIN))) { // If button is pressed, reset pressure array 455 resetBuffer(); 456 ringBuffer.saveBufferToEEPROM(); // Save data to memory 457 u8g2.clearBuffer(); 458 u8g2.setFont(u8g2_font_7x14B_tf); 459 u8g2.drawStr(23, 19, "Buffer Reset"); // Report success 460 u8g2.sendBuffer(); 461 delay(2000); 462 } 463#endif 464} 465 466void loop() { 467 b1.tick(); 468 b2.tick(); 469 b3.tick(); 470 471 static bool showingCurrentConditions = false; 472 static bool showingDiff1h = false; 473 static bool showingDiff3h = false; 474 475 if (b1.click()) { 476 showingCurrentConditions = !showingCurrentConditions; 477 showingDiff1h = false; 478 showingDiff3h = false; 479 getData(); 480 } 481 482 if (b2.click()) { 483 showingDiff1h = !showingDiff1h; 484 showingCurrentConditions = false; 485 showingDiff3h = false; 486 getData(); 487 } 488 489 if (b3.click()) { 490 showingDiff3h = !showingDiff3h; 491 showingCurrentConditions = false; 492 showingDiff1h = false; 493 getData(); 494 } 495 496 if (showingCurrentConditions) { 497 drawCurrentConditions(); 498 } else if (showingDiff1h) { 499 drawPressureDiffScreen1h(); 500 } else if (showingDiff3h) { 501 drawPressureDiffScreen3h(); 502 } else { 503 drawScreen(); 504 } 505 506 // Store pressure readings every 11.25 minutes 507 if (millis() - myTimer1 >= 675000) { 508 myTimer1 = millis(); 509 float relPressure = getRelativePressure(); 510 varPressNorm = relPressure; 511 512 // Store actual pressure value in hourly buffer 513 hourlyPressureBuffer[pressureBufferIndex % 6] = relPressure; 514 pressureBufferIndex++; 515 516 // Store mapped value for graph 517 int pressureForGraph = round(relPressure - MIN_PRES); 518 pressureForGraph = constrain(pressureForGraph, 0, PRESSURE_RANGE); 519 ringBuffer.push(pressureForGraph); 520 } 521 522 if (millis() - myTimer3 >= 3600000) { 523 myTimer3 = millis(); 524#ifdef USE_MEMORY 525 ringBuffer.saveBufferToEEPROM(); 526#endif 527 } 528}
Documentation
Schematic
..
Schematic.jpg

Comments
Only logged in users can leave comments