01/2018

The ESP8266 WiFi barometer with
BMP280 sensor and Arduino software

New look

Barometer Glass and Brass
// Arduino WiFi Barometer as HTTP server -  V5.5 / 2018-03-18
// For ESP8266 and BMP280 sensor
// Copyright 2018 Pavel Janko, www.fancon.cz
// MIT license, http://opensource.org/licenses/MIT
// Tested with ARDUINO IDE 1.8.5, Arduino-ESP8266 2.4.1
//
// Properties:
// - WiFi Protected Setup with PBC ready
// - mDNS responder for connect by name
// - HTTP BASIC authentication
// - WiFi auto reconnect with restart
// - Supports simply OTA from browser with password
//   (with at least 1MB of memory for OTA)
// - Improved barometric formula
// - Minimal and maximal pressure
// - Dynamic trend of pressure in interval three hours
//
// Added libraries:
// Adafruit Unified Sensor by Adafruit 1.0.2
// Adafruit BMP280 Library by Adafruit 1.0.2
// After flash from serial restart the device !!!
//
// Connection :
// ESP8266-12   ------------------------------------------
// Vcc    -> Power 3.3V
// GND    -> Power GND
// CH_PD  -> Vcc
// RESET  -> resistor 10Kohms to Vcc and switch to GND
// GPIO0  -> resistor 10Kohms to Vcc and switch with
//           serial resistor around 330ohms to GND
// GPIO2  -> resistor 10Kohms to Vcc
// GPIO13 -> LED with serial resistor around 330ohms to GND
// GPIO15 -> resistor 10Kohms to GND
// Vcc    -> Vcc BMP280
// GND    -> GND BMP280
// GPIO4  -> SDA BMP280
// GPIO5  -> SCL BMP280
// --------------------------------------------------------
//
// How to:
// For local altitude correction, set LOCALALTUD in meters
//
// To connect to an access point, hold down the button (switch on GPIO0) for about
// three seconds (LED on GPIO13 is lit permanently) and activate WPS
// (Wi-Fi Protected Setup) with PBC (Push Button Configuration) on your Access point.
//
// For mDNS install Bonjour to your Windows - https://support.apple.com/bonjour
// or Avahi for your Linux - https://www.avahi.org/
// mDNS name is defined as "barometer" below here. Just enter http://barometer.local
// into the browser. But in my case, mDNS is sometimes
// slower than a direct IP address.
//
// For web authentication set #define WWWAUTHENTI true and
// your WWWUSERNAME and WWWPASSWORD below only.
//
// For OTA activation set OTAUSER, OTAPASSWORD and then in operation
// write to browser address row http://barometer.local/firmware and upload your *.bin
// file.
// Default users and passwords are all "admin"
//
//   |--------------------------------------------------------------------|
//   |       Display      |                   Value                       |
//   |--------------------------------------------------------------------|
//   |  red marks         | Maximum and minimum measured pressure         |
//   |  yellow mark       | The difference between the yellow mark        |
//   |                    |      on the scale and pointer show trend of   |
//   |                    |      pressure in interval three hours.        |
//   |  olive green dot   | Standard atmospheric pressure 1013.25 hPa     |
//   |--------------------------------------------------------------------|
//
//   |--------------------------------------------------------------------|
//   |  Button - GPIO0    | LED - GPIO13|           Action                |
//   |--------------------------------------------------------------------|
//   | short press        |  ON or OFF  |              ---                |
//   | long press > 3sec. |     ON      | wait for WPS from Access point  |
//   |      ---           | flashes 4Hz | connection to WiFi              |
//   | before power on    |     ---     | flash mode                      |
//   |--------------------------------------------------------------------|
//
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <Wire.h>
#include <Ticker.h>
#include <ESP8266WiFi.h>
#include <ESP8266mDNS.h>
#include <ESP8266WebServer.h>
#include <ESP8266HTTPUpdateServer.h>
#define LOCALALTUD      208.0      // [m] Set your local altitude in meters
#define INSIDE          true       // true = sensor inside, false = sensor outdoor
#define STDTEMP         15.0       // [Celsius] Standard temperature  
#define STDPRESSURE     1013.25    // [hPa] Standard atmosferic pressure 
#define LENGTH          18         // length of string with old pressure data
#define WWWPAGEREFR     30         // [s] Web page refresh time [s]
#define WWWAUTHENTI      false     // Set true for web authentication
#define WWWUSERNAME     "admin"    // Set www user name
#define WWWPASSWORD     "admin"    // Set www user password
#define OTAUSER         "admin"    // Set OTA user
#define OTAPASSWORD     "admin"    // Set OTA password
#define OTAPATH         "/firmware"// Set path for update
#define MDNSNAME        "barometer"// Set local mDNS name
#define SERVERPORT      80         // Server port
#define ROUNDING        1
#define BUTTONPIN       0          // GPIO0   button pin
#define LEDPIN          13         // GPIO13  LED 
#define SDAI2CPIN       4          // GPIO4   I2C SDA bus
#define SCLI2CPIN       5          // GPIO5   I2C SCL bus
#define BMP280ADDR      0x76       // Set BMP280 I2C address
#define BUTTONTIME      0.25       // [s] Periodic time for button read
#define SENSORTIME      10.0       // [s] Periodic time for sensor read
#define WIFITIME        10.0       // [s] Periodic time for WiFi test
#define TRENDTIME       600.0      // [s] Periodic time for calculation of trend
bool    ButtonFlag      = false;
bool    SensorFlag      = false;
bool    WifiFlag        = false;
bool    TrendFlag       = false;
bool    LedState        = false;   // LED off
char    ButtonCount     = 0;
float   MaxPress        = 95700.0;
float   MinPress        = 106300.0;
float   SeaPressure;
float   Temperature;
float   Trend;
float   PointerAngle;
float   TrendAngle;
float   StdPssAngle;
float   MaxAngle;
float   MinAngle;
float   OldPressure[LENGTH];
ESP8266WebServer HttpServer(SERVERPORT);
ESP8266HTTPUpdateServer httpUpdater;
Ticker ButtonTick;           // Preparing for periodic button reading
Ticker SensorTick;           // preparing for periodic sensor reading
Ticker WifiTick;             // preparing for periodic test WiFi connection
Ticker TrendTick;            // preparing for periodic trend calculation
Adafruit_BMP280 bmp280;
//-----------------------------------------------------------------
void setup(void) {
  WiFi.begin();            // Disable this line only if you can not use WPS
  //WiFi.persistent(false);// Enable this three lines only if you can not use WPS
  //WiFi.mode(WIFI_STA);               //               -"-
  //WiFi.begin(YourSSID,YourPassword); //               -"-
  Wire.begin(SDAI2CPIN, SCLI2CPIN);
  bmp280.begin(BMP280ADDR);
  pinMode(BUTTONPIN, INPUT);
  pinMode(LEDPIN, OUTPUT);
  /* wait for WiFi connect */
  while (WiFi.status() != WL_CONNECTED) {
    GetButton();
    delay(250);
    LedSet(!LedState);
  }
  LedSet(false);  //LED OFF
  /* prepare first data for trend calculation */
  for (int i = 0; i < LENGTH; i++)TrendCalc();
  /* position of the standard pressure indicator */
  StdPssAngle = (30.0 + (STDPRESSURE - 960.0) * 3.0);
  /* set interupt timer */
  ButtonTick.attach(BUTTONTIME, ButtonFlagSet);
  SensorTick.attach(SENSORTIME, SensorFlagSet);
  WifiTick.attach(WIFITIME, WifiFlagSet);
  TrendTick.attach(TRENDTIME, TrendFlagSet);
  httpUpdater.setup(&HttpServer, OTAPATH, OTAUSER, OTAPASSWORD);
  MDNS.begin(MDNSNAME);
  HttpServer.on("/", HTTP_GET, ModifySendPage);
  HttpServer.onNotFound(handleNotFound);
  HttpServer.begin();
}
//-----------------------------------------------------------------
void loop(void) {
  HttpServer.handleClient();       // Listen for HTTP requests from clients
  if (ButtonFlag) GetButton();     // Periodic serve button
  if (SensorFlag) SensorRead();    // Periodic sensor  reading
  if (WifiFlag) WifiTest();        // Periodic test WiFi connection
  if (TrendFlag) TrendCalc();      // Periodic save trend data
}//-----------------------------------------------------------------
void ModifySendPage(void) {
  if (WWWAUTHENTI == true) {
    /* request for www user/password from client */
    if (!HttpServer.authenticate(WWWUSERNAME, WWWPASSWORD))
      return HttpServer.requestAuthentication();
  }
  SendPage();
}
void SendPage(void) {
  String buff  = "<!DOCTYPE HTML><html><head><meta charset=\"UTF-8\"><meta http-equiv=\"refresh\" content=\"" + String(WWWPAGEREFR) + "\">\n";
  buff += "<title>Barometer</title></head><body style=\"background:#dcdcdc\">\n";
  buff += "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" width=\"1000\" height=\"1000\">\n";
  buff += "<defs><linearGradient id=\"g1\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"0%\">\n";
  buff += "<stop offset=\"0%\" style=\"stop-color:#888;stop-opacity:1\"/>\n";
  buff += "<stop offset=\"50%\" style=\"stop-color:#ccc;stop-opacity:1\"/>\n";
  buff += "<stop offset=\"100%\" style=\"stop-color:#888;stop-opacity:1\"/></linearGradient>\n";
  buff += "<linearGradient id=\"g2\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"0%\">\n";
  buff += "<stop offset=\"0%\" style=\"stop-color:#b0a080;stop-opacity:1\"/>\n";
  buff += "<stop offset=\"50%\" style=\"stop-color:#f0e0c0;stop-opacity:1\"/>\n";
  buff += "<stop offset=\"100%\" style=\"stop-color:#b0a080;stop-opacity:1\"/></linearGradient>\n";
  buff += "<g id=\"PO\"><path d=\"M495 440A12 12 0 1 1 505 440L505 440 503 710 500 720 497 710z\" fill=\"black\"/>\n";
  buff += "<circle cx=\"500\" cy=\"500\" r=\"10\" stroke=\"black\" stroke-width=\"3\" fill=\"#CD7F32\"/>\n";
  buff += "<circle cx=\"500\" cy=\"500\" r=\"5\" fill=\"#B5A642\"/>\n";
  buff += "<line stroke-width=\"2\" id=\"S\" x1=\"500\" y1=\"725\" x2=\"500\" y2=\"750\"/>\n";
  buff += "<line stroke-width=\"2\" id=\"M\" x1=\"500\" y1=\"720\" x2=\"500\" y2=\"750\"/>\n";
  buff += "<line stroke-width=\"2\" id=\"L\" x1=\"500\" y1=\"705\" x2=\"500\" y2=\"750\"/></g>\n";
  buff += "<g id=\"T\"><use xlink:href=\"#L\" transform=\"rotate(30,500,500)\"/>\n";
  buff += "<g id=\"D\"><use xlink:href=\"#S\" transform=\"rotate(33,500,500)\"/>\n";
  buff += "<use xlink:href=\"#S\" transform=\"rotate(36,500,500)\"/>\n";
  buff += "<use xlink:href=\"#S\" transform=\"rotate(39,500,500)\"/></g>\n";
  buff += "<use xlink:href=\"#S\" transform=\"rotate(42,500,500)\"/>\n";
  buff += "<use xlink:href=\"#M\" transform=\"rotate(45,500,500)\"/>\n";
  buff += "<use xlink:href=\"#S\" transform=\"rotate(48,500,500)\"/>\n";
  buff += "<use xlink:href=\"#D\" transform=\"rotate(18,500,500)\"/></g>\n";
  buff += "<circle id=\"SP\" cx=\"500\" cy=\"756\" r=\"5\" fill=\"#808000\"/>\n";
  buff += "<path id=\"TR\" d=\"M500 750 L495 760 L505 760 Z\"/></defs>\n";
  buff += "<circle cx=\"500\" cy=\"500\" r=\"294\" fill=\"url(#g1)\" transform=\"rotate(-45,500,500)\"/>\n";
  buff += "<circle cx=\"500\" cy=\"500\" r=\"282\" fill=\"url(#g1)\" transform=\"rotate(45,500,500)\"/>\n";
  buff += "<circle cx=\"500\" cy=\"500\" r=\"267\" fill=\"url(#g2)\" transform=\"rotate(45,500,500)\"/>\n";
  buff += "<g stroke=\"black\" stroke-width=\"2\" fill=\"none\">\n";
  buff += "<rect x=\"450\" y=\"590\" rx=\"5\" ry=\"5\" width=\"100\" height=\"40\"/>\n";
  buff += "<rect x=\"450\" y=\"635\" rx=\"5\" ry=\"5\" width=\"100\" height=\"40\"/></g>\n";
  buff += "<g stroke=\"black\"><use xlink:href=\"#D\" transform=\"rotate(-12,500,500)\"/>\n";
  buff += "<g id=\"TT\"><use xlink:href=\"#T\"/>\n";
  buff += "<use xlink:href=\"#T\" transform=\"rotate(30,500,500)\"/>\n";
  buff += "<use xlink:href=\"#T\" transform=\"rotate(60,500,500)\"/>\n";
  buff += "<use xlink:href=\"#T\" transform=\"rotate(90,500,500)\"/>\n";
  buff += "<use xlink:href=\"#T\" transform=\"rotate(120,500,500)\"/></g>\n";
  buff += "<use xlink:href=\"#TT\" transform=\"rotate(150,500,500)\"/>\n";
  buff += "<use xlink:href=\"#L\" transform=\"rotate(330,500,500)\"/>\n";
  buff += "<use xlink:href=\"#D\" transform=\"rotate(300,500,500)\"/></g>\n";
  buff += "<g font-family=\"arial\" font-size=\"28\" font-weight=\"normal\" font-style=\"italic\" fill=\"black\" >\n";
  buff += "<text x=\"330\" y=\"600\">970</text>\n";
  buff += "<text x=\"305\" y=\"500\">980</text>\n";
  buff += "<text x=\"330\" y=\"400\">990</text>\n";
  buff += "<text x=\"390\" y=\"350\">1000</text>\n";
  buff += "<text x=\"465\" y=\"320\">1010</text>\n";
  buff += "<text x=\"540\" y=\"350\">1020</text>\n";
  buff += "<text x=\"605\" y=\"400\">1030</text>\n";
  buff += "<text x=\"635\" y=\"500\">1040</text>\n";
  buff += "<text x=\"610\" y=\"600\">1050</text>\n";
  buff += "<text x=\"470\" y=\"740\">hPa</text>\n";
  buff += "<text x=\"455\" y=\"620\" fill=\"#800000\">" + String(SeaPressure / 100, ROUNDING) + "</text>\n";
  buff += "<text x=\"555\" y=\"620\" font-size=\"24\" >hPa</text>\n";
  buff += "<text x=\"470\" y=\"665\" fill=\"#800000\">" + String(Temperature, ROUNDING) + "</text>\n";
  buff += "<text x=\"560\" y=\"665\" font-size=\"24\">C</text>\n";
  /* grade marker */
  buff += "<circle cx=\"560\" cy=\"645\" r=\"3\" fill=\"transparent\" stroke=\"black\" stroke-width=\"2\"/></g>\n";
  /* mark of standard pressure */
  buff += "<use xlink:href=\"#SP\" fill=\"#8b0000\" transform=\"rotate(" + String(StdPssAngle) + ",500,500)\"/>\n";
  /* minimum mark */
  buff += "<use xlink:href=\"#TR\" fill=\"#8b0000\" transform=\"rotate(" + String(MinAngle) + ",500,500)\"/>\n";
  /* maximum mark */
  buff += "<use xlink:href=\"#TR\" fill=\"#8b0000\" transform=\"rotate(" + String(MaxAngle) + ",500,500)\"/>\n";
  /* trend mark */
  buff += "<use xlink:href=\"#TR\" fill=\"#daa520\" transform=\"rotate(" + String(TrendAngle) + ",500,500)\"/>\n";
  buff += "<use xlink:href=\"#PO\" transform=\"rotate(" + String(PointerAngle) + ",500,500)\"/></svg></body></html>\n";
  HttpServer.send(200, "text/html", buff);
}
/* Send HTTP status 404 Not Found */
void handleNotFound(void) {
  HttpServer.send(404, "text/plain", "404: Not found");
}
/* Read button on GPIO0*/
void GetButton(void) {
  /* short press butoon to change state of LED */
  if (digitalRead(BUTTONPIN) == false ) ++ButtonCount;
  if (digitalRead(BUTTONPIN) == true && ButtonCount > 1 && ButtonCount < 12 ) {
    LedSet(!LedState);      // change LED
    ButtonCount = 0;
  }
  /* long press button - WPS mode */
  if (ButtonCount > 12) {
    LedSet(!LedState);      // LED change
    ButtonTick.detach();    // Stop Tickers
    SensorTick.detach();
    WifiTick.detach();
    TrendTick.detach();
    /* Wait for release button */
    while (!digitalRead(BUTTONPIN))yield();
    delay(100);
    StartWpsWaiting();
  }
  if (digitalRead(BUTTONPIN) == true ) ButtonCount = 0;
  ButtonFlag = false;
}
/* Service Wi-Fi Protected Setup with Push Button Configuration */
void StartWpsWaiting(void) {
  WiFi.persistent(false);
  WiFi.mode(WIFI_STA);
  bool    RafHo = false;
  String  HowDelka = "";
  /* Wait for access point WPS */
  do {
    RafHo = WiFi.beginWPSConfig();
    HowDelka = WiFi.SSID();
  }
  while (RafHo == false || HowDelka.length() == 0);
  delay(500);
  ESP.restart();
}
/* If disconnected WiFi then restart */
void WifiTest(void) {
  if (WiFi.status() != WL_CONNECTED)ESP.restart();
  WifiFlag = false;
}
void SensorRead(void) {
  Temperature = bmp280.readTemperature();
  float Pressure = bmp280.readPressure();
  if (INSIDE) {
    SeaPressure = Pressure / pow(1.0 - 0.0065 * LOCALALTUD / (STDTEMP + 273.15), 5.255); // as BOSCH formula
  }
  else {
    SeaPressure = Pressure / pow(1.0 - 0.0065 * LOCALALTUD / (Temperature + 273.15), 5.255); // ICAO formula
  }
  if (SeaPressure > MaxPress)MaxPress = SeaPressure;
  if (SeaPressure < MinPress)MinPress = SeaPressure;
  PointerAngle = (30.0 + (SeaPressure / 100.0 - 960.0) * 3.0);
  MaxAngle = (30.0 + (MaxPress / 100.0 - 960.0) * 3.0);
  MinAngle = (30.0 + (MinPress / 100.0 - 960.0) * 3.0);
  TrendAngle = (30.0 + (Trend / 100.0 - 960.0) * 3.0);
  SensorFlag = false;
}
void TrendCalc(void) {
  delay(10);
  SensorRead();
  OldPressure[LENGTH - 1] = SeaPressure;
  for (int i = 0; i < (LENGTH - 1); i++) {
    OldPressure[i] = OldPressure[i + 1];
  }
  Trend = OldPressure[0];
  TrendFlag = false;
}
/* set  LED */
void LedSet(bool SetLedState) {
  LedState = SetLedState;
  digitalWrite(LEDPIN, LedState);
}
void ButtonFlagSet(void) {
  ButtonFlag = true;
}
void SensorFlagSet(void) {
  SensorFlag = true;
}
void WifiFlagSet(void) {
  WifiFlag = true;
}
void TrendFlagSet(void) {
  TrendFlag = true;
}

Here is Arduino sketch for download