/*
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));
}
Comments