Mirko Pavleski
Published © GPL3+

Arduino Barometer + Barograph with BME280 Sensor

Barometer + Barograph that presents the results in graphic form on a 20x4 LCD display.

BeginnerFull instructions provided2 hours1,064
Arduino Barometer + Barograph with BME280 Sensor

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
16x4 LCD Display
×1
Gravity: I2C BME280 Environmental Sensor
DFRobot Gravity: I2C BME280 Environmental Sensor
×1
Gravity:Digital Push Button (Yellow)
DFRobot Gravity:Digital Push Button (Yellow)
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free

Story

Read more

Schematics

Schematic diagram

==

Code

Arduino code

Arduino
==
/*
Barometer with digital and graphic display of atmospheric pressure data.
Components: Screen text LCD 2004, platform: arduinio nano/uno (Atmega328 or more), pressure sensor: BME 280.
Amatroskin 2022.
*/

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
#include "GyverButton.h"
#include <Adafruit_Sensor.h>                 
#include <Adafruit_BME280.h>                 

#define SEALEVELPRESSURE_HPA (933,17)       // Set the height
#define BTN_PIN 3                            //Button Pin
#define BASE_PERIOD 675000                   //Main array acquisition period 675000ms = 11.25 min (*16 bars = 3 hours) maximum graph resolution
#define MIN_VAL 990                          //The minimum value displayed on the chart
#define MAX_VAL 1035                          //The maximum value displayed on the graph
LiquidCrystal_I2C lcd(0x27, 20, 4);          //We create the necessary objects
GButton butt1(BTN_PIN);
Adafruit_BME280 bme;

uint32_t tmr1, tmr2;                          //timer variables
uint32_t  set_period = BASE_PERIOD;           //Display period, changes in multiples of 3 hours (3, 6, 12, 24)
int16_t plot_array[20];                       //Data array for drawing a chart
uint16_t base_array [128];                    //The base array stores all measurements for the last 24 hours (128 cells * 11.25 minutes = 1440 minutes = 24 hours)
int16_t value, delta;                         //Current (taken) readings, difference in readings for a selected period of time
byte interval = 1;                            //Interval displayed on the screen (time difference between adjacent chart bars)
                                              // 11.25 min * 16 = 3 hours, 22.5 min - 6 hours, 45 min - 12 hours, 90 min - 24 hours
void setup() {
  read_all ();
  //  Serial.begin(9600);
  attachInterrupt(1, isr, CHANGE);
  butt1.setDebounce(80);                      // anti-bounce setting (default 80 ms)
  butt1.setTimeout(300);                      // hold timeout setting (default 500ms)
  lcd.init();
  lcd.backlight();
  lcd.clear();
  if (!bme.begin(0x76)) {                              //Initialization of the BME280 sensor at address 0x76 (default)
    //Serial.println("Could not find a valid BME280!");//Printing an Error Message to the Port Monitor
    lcd.setCursor(3, 1);
    lcd.print(F("No connection"));                     //___________________________or on the screen
    lcd.setCursor(5, 2);
    lcd.print(F("to sensor"));
    while (1);                                          //won't go without a sensor.
  }

  if (!digitalRead(BTN_PIN)) {                          //Reset settings when turned on with the button held down
    for (byte i = 0; i < 128; i++) base_array [i] = 0;  //Erase data
    update_all ();                                      //Save
    lcd.setCursor(5, 1);                                //Report it
    lcd.print(F("Reset data"));
    lcd.setCursor(9, 2);
    lcd.print(F("OK"));
  }
  while (!digitalRead(BTN_PIN));
  lcd.clear();
    
  initPlot();                                           //Initializing Symbols for Rendering
  
  value = round ((bme.seaLevelForAltitude(700, bme.readPressure())/100));//We take the current readings, convert to mm Hg.
  base_array[0] = value;
  get_data ();
}

void isr() {                                              //We poll the button in the interrupt to catch the click anyway
   butt1.tick();
}

void loop() {
  butt1.tick();                                           //Poll button
  if (butt1.isClick()) {                                  //When you press:
    interval *= 2;                                        //Switching the chart scale
    if (interval > 8) interval = 1;
    set_period = BASE_PERIOD * interval;                  //Recalculate the interval for updating data from the sensor
    get_data ();                                          //Update information on the display
  }
                            
  if (millis() - tmr1 >= BASE_PERIOD) {                   //We collect the basic array of data
    tmr1 = millis();                                      //Every 11.25 minutes we take readings from the sensor
    for (int i = 126; i >= 0; i--) {                      //Shift the entire array by one point
      base_array[i + 1] = base_array[i];
    }
    value = round ((bme.seaLevelForAltitude(700, bme.readPressure())/100));//Convert to mm/Hg.
    base_array[0] = value;                                 //Write the latest readings to an array
    update_all ();                                         //And remember in EEPROM
  }

  if (millis() - tmr2 >= set_period) {                     //After a period of time set_period (milliseconds)
    tmr2 = millis();                                       //Refreshing the data on the screen
    get_data ();
  }
}

void get_data () {                                          //We draw 16 columns of the diagram on the screen
  for (int i = 15; i >= 0; i--) {
    drawPlot(0, 3, 16, 4, MIN_VAL, MAX_VAL, (base_array[i * interval]));
  }
  delta = ((base_array[0]) - (base_array[15 * interval]));  //We calculate the delta (pressure change) for the selected interval
  screen_data (value, delta, (interval * 3));               //Displaying text information on the screen
}

void screen_data (int value, int delta, byte interval) {    //The function of displaying text information on the screen (everything except for the chart bars) 
  lcd.setCursor(16, 0);
  lcd.print(value);
  lcd.setCursor(17, 2);
  if (delta == value) delta = 0;
  if (delta > 0) {
    lcd.print("+");
  } else if (delta < 0) {
    lcd.print("-");
  } else if (delta == 0) {
    lcd.print(" ");
  }
  lcd.setCursor(18, 2);
  lcd.print(abs(delta));
  if (abs(delta) < 10) {
    lcd.setCursor(19, 2);
    lcd.print(" ");
  }
  lcd.setCursor(17, 1);
  lcd.print("hPa");
  lcd.setCursor(17, 3);
  lcd.print(interval);
  (interval < 10) ? lcd.print("h ") : lcd.print("h");
}

void initPlot() {
  // necessary symbols for work
  // created in http://maxpromer.github.io/LCD-Character-Creator/
  byte row8[8] = {0b11111,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111};
  byte row7[8] = {0b00000,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111};
  byte row6[8] = {0b00000,  0b00000,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111};
  byte row5[8] = {0b00000,  0b00000,  0b00000,  0b11111,  0b11111,  0b11111,  0b11111,  0b11111};
  byte row4[8] = {0b00000,  0b00000,  0b00000,  0b00000,  0b11111,  0b11111,  0b11111,  0b11111};
  byte row3[8] = {0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b11111,  0b11111,  0b11111};
  byte row2[8] = {0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b11111,  0b11111};
  byte row1[8] = {0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b00000,  0b11111};
  lcd.createChar(0, row8);
  lcd.createChar(1, row1);
  lcd.createChar(2, row2);
  lcd.createChar(3, row3);
  lcd.createChar(4, row4);
  lcd.createChar(5, row5);
  lcd.createChar(6, row6);
  lcd.createChar(7, row7);
}

                                              //Diagram drawing, code taken from Guyver https://alexgyver.ru/lcd-plots-and-bars/
void drawPlot(byte pos, byte row, byte width, byte height, int min_val, int max_val, int fill_val) {

  for (byte i = 0; i < width; i++) {
    plot_array[i] = plot_array[i + 1];
  }
  fill_val = constrain(fill_val, min_val, max_val);
  plot_array[width - 1] = fill_val;
  for (byte i = 0; i < width; i++) {         // each parameter column
    int infill, fract;
    
                                             // find the number of whole blocks, taking into account the minimum and maximum, to display on the chart
    infill = floor((float)(plot_array[i] - min_val) / (max_val - min_val) * height * 10);
    fract = (infill % 10) * 8 / 10;                          // find the number of remaining stripes
    infill = infill / 10;
    for (byte n = 0; n < height; n++) {     // for all graph lines
      if (n < infill && infill > 0) {       // while we're below the level
        lcd.setCursor(i, (row - n));        // fill in cells
        lcd.write(0);
      }
      if (n >= infill) {                          // if you reach the level
        lcd.setCursor(i, (row - n));
        if (fract > 0) lcd.write(fract);          // заполняем дробные ячейки
        else lcd.write(16);                       // if fractional == 0, fill empty
        for (byte k = n + 1; k < height; k++) {   // everything that is on top is filled with empty
          lcd.setCursor(i, (row - k));
          lcd.write(16);
        }
        break;
      }
    }
  }
}

void update_all () {                             //Обновляем данные в EEPROM
  eeprom_update_block((void*)&base_array, 0, sizeof(base_array));
}

void read_all () {                              //Reading data from EEPROM
  eeprom_read_block((void*)&base_array, 0, sizeof(base_array));
}

Credits

Mirko Pavleski

Mirko Pavleski

116 projects • 1163 followers

Comments