Thursday, December 1, 2016

BMP280 and ESP8266

The BMP280 is the next generation sensor from Bosch and follows its predecessors BMP085 - BMP180. Price for it is now under $2 with free shipping.

Key parameters

  •  Pressure range 300 … 1100 hPa (equiv. to +9000…-500 m above/below sea level)
  •  Package 8-pin LGA metal-lid
  • Footprint : 2.0 × 2.5 mm², height: 0.95 mm
  •  Relative accuracy ±0.12 hPa, equiv. to ±1 m
  •  (950 … 1050hPa @25°C)
  •  Absolute accuracy typ. ±1 hPa  (950 ...1050 hPa, 0 ...+40 °C)
  •  Temperature coefficient offset 1.5 Pa/K, equiv. to 12.6 cm/K  (25 ... 40°C @900hPa)
  •  Digital interfaces I²C (up to 3.4 MHz) SPI (3 and 4 wire, up to 10 MHz)
  •  Current consumption 2.7µA @ 1 Hz sampling rate
  •  Temperature range -40 … +85 °C

Typical applications

  • Enhancement of GPS navigation
  •  (e.g. time-to-first-fix improvement, dead-reckoning, slope detection)
  •  Indoor navigation (floor detection, elevator detection)
  •  Outdoor navigation, leisure and sports applications
  •  Weather forecast
  •  Health care applications (e.g. spirometry)
  •  Vertical velocity indication (e.g. rise/sink speed)

For connecting the BMP280 to ESP8266 the following pins need to be connected: 

NodeMCU / WeMos D1 mini
Other ESP8266




The BMP280 supports the I²C and SPI digital interfaces; it acts as a slave for both protocols. 

The I²C interface supports the Standard, Fast and High Speed modes. 

The SPI interface supports both SPI mode ‘00’ (CPOL = CPHA = ‘0’) and mode ‘11’ (CPOL = CPHA = ‘1’) in 4- wire and 3-wire configuration. The following transactions are supported: Single byte write  multiple byte write (using pairs of register addresses and register data) single byte read multiple byte read (using a single register address which is auto-incremented) 

Connect the CSB pin to GND to have SPI and to VCC(3V3) for I2C.

The 7-bit device address is 111011x. The 6 MSB bits are fixed. The last bit is changeable by SDO value and can be changed during operation. 

Connecting SDO to GND results in slave address 1110110 (0x76), connecting it to VCC results in slave address 1110111 (0x77), which is the same as BMP180’s I²C address. 

The SDO pin cannot be left floating, if left floating, the I²C address will be undefined.

In my setup I've connected CSB and SDO to VCC to have I2C and 0x77 as address.

To run a quick test I've installed the Adafruit Sensor library and Adafruit BMP280 library .

The code to test the BMP280 is the example code from library.

This code assume that the SDA and SCL are connected on GPIO 4 and GPIO 5. If you need to assign new pins to your BMP280 use the Wire.begin(2,0) where the GPIO 2 is connected to SDA and GPIO 0 is connected to  SCL.

Now its time to add some code to read the temperature and pressure, post them to the and also update the temperature gauge on this blog.

 * Catalin Batrinu 
 * Read temperature and pressure from BMP280
 * and send it to

#include <Wire.h>
#include <SPI.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_BMP280.h>
#include <ESP8266WiFi.h>

#define BMP_SCK 13
#define BMP_MISO 12
#define BMP_MOSI 11 
#define BMP_CS 10

Adafruit_BMP280 bme; // I2C
// replace with your channel’s thingspeak API key,
String apiKey = "YOUR-API-KEY";
const char* ssid = "YOUR-SSID";
const char* password = "YOUR-ROUTER-PASSWORD";
const char* server = "";
WiFiClient client;

 *   S E T U P
void setup() {
  Serial.println(F("BMP280 test"));
  if (!bme.begin()) {  
    Serial.println("Could not find a valid BMP280 sensor, check wiring!");
    while (1);
  WiFi.begin(ssid, password);
  Serial.print("Connecting to ");
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) 
  Serial.println("WiFi connected");  

 *  L O O P
void loop() {
    Serial.print(" *C");
    Serial.print(" P=");
    Serial.print(" Pa");

    Serial.print(" A= ");
    Serial.print(bme.readAltitude(1013.25)); // this should be adjusted to your local forcase
    Serial.println(" m");

    if (client.connect(server,80))  // "" or
        String postStr = apiKey;
        postStr +="&field1=";
        postStr += String(bme.readTemperature());
        postStr +="&field2=";
        postStr += String(bme.readPressure());
        postStr += "\r\n\r\n";
        client.print("POST /update HTTP/1.1\n");
        client.print("Connection: close\n");
        client.print("X-THINGSPEAKAPIKEY: "+apiKey+"\n");
        client.print("Content-Type: application/x-www-form-urlencoded\n");
        client.print("Content-Length: ");
    //every 20 sec   

Temperature logging

A post about BMP180 attached to a battery shield can be found here.


  1. I follow your procedure but it doesn't work.

  2. Perfectly working, thank you very much.

  3. thank you so much... this really help me...

  4. funciona realmente bien, estoy muy agradecido por tu aporte.

    thank you so much... this sketch its great ...

  5. Thank you very much. It works perfectly.

    Could you include some instructions for the gauge meter ? I don't know how to make it.

    1. Go to and then go to Apps. After that click on Plugins then to New.
      Select Google Gauge and click Create. After that use the JavaScript code to add your API_KEY and be sure to check the "Create a public URL". Have fun !

  6. Hi, very nice use of ESP8266 without thingspeak library. What needs to be added/changed if HTU21 moisture detector is to be read and uploaded? Are GPIO4 and GPIO5 standard I2C pins?

    1. You can use any pin for I2C. I like to use the 4 and 5 in my projects, but you can use any pin.
      To add humidity I've used a DHT22. Check it here:

  7. Hi, thanks for that very fast answer. A few more questions:
    1. why do you use "\r\n\r\n" instead of "\r\n" as last postStr descriptor?
    2. why do you use bme reads instead of bmp reads? BMP280 is the sensor used, right?
    3. if I want to add another field (say HTU21), I assume that the following lines are needed to be added to the string to be uploaded to thingspeak:

    postStr +="&field3=";
    postStr += String(htu.readHumidity());

    4. what is the purpose of the client.print("\n\n"); statement at the end of the thingspeak upload?

    Thanks a lot!

    1. 1. HTML header
      2. BME280 is an enhanced version of BMP280. Same code works with both.
      3. Correct
      4. HTML header format.

  8. Hi Catalin,
    thanks for your fast answer!
    Here a few more questions:
    1. should postStr +="&field1="; not be postStr +="field1="; for the first field?
    2. should postStr += String(bme.redaTemperature()); not be postStr += String(bme.redaTemperature(), DEC); so that a decimal is used for this value? The examples I see on this subject in these cases use DEC; is that superfluous?
    3. where can I find information on the HTML header use of \n and \r for thingspeak? I know \n = new line and \r = carriage return but I have no clue why they are used here?

    1. 1. The desired URI is ti have fields as parameters delimited by &. like field1=33&field2=44&fieldX=YY. Thingspeak accept up to 8 fields.
      2. Output from readTemp is DEC not OCT or HEX so no need to convert it
      3. Structure of the HTTP heder is:

      The HTTP generic message format is as follows:


      See the empty-line ?

      Search for HTTP header info. Also you can read this:

    2. Hi Catalin, thank you for your fast reply!
      My first question refers to the & sign in front of field1: I see most examples on internet do not use & in front of the first field definition. Is it better to use it in the first field definition?

    3. The & is mandatory. It is a parameter separator in the HTTP GET command.

      Please see more examples on

    4. ok, thanks for your input. I find this one of the best examples of code (and instructions) for the use of ESP8266 + sensor + thingspeak available on internet. And the use of code without library adds to its interest.

      It would be perfect if you could post this code for use with a DS3231 RTC (without library) to get sensor readings sent at fixed times instead of a delay function. I am writing some code for that too but I am interested in seeing your way of doing it.

    5. If your esp8266 need to stay in low power and you want to log data to an sd card and you don't have wifi connection than is ok to us ds3231. If your code need to do more and not wait in delay you ca use something like this in your loop function:

      unsigned long currentMillis = millis();

      if (currentMillis - previousMillis >= interval) {
      // save the last
      previousMillis = currentMillis;

      // Call your function to read and post your data

      // Here you can call other function.

  9. I do no understand why I cannot get connected to wifi with your code, but when using below it works:
    ESP8266WiFiMulti WiFiMulti;
    In setup I use following code instead of yours for wifi connect:
    WiFiMulti.addAP(ssid, password);
    Serial.print("Wait for WiFi... ");

    while( != WL_CONNECTED) {

    1. Strange, I've just tested it and is working fine. I've added a new module so I can get the outside temperature with Alexa. Don't see any reason for using WiFiMulti if you are seeting just one SSID. AFAIK WiFiMulti is just calling WiFi.begin. Post some output please.

  10. Hi Catalin, I found the issue. The ESP8266 remained stuck in the loop checking for the BMP280. For some reason this module is not found. When removing this check wifi does get connected.
    The I2C wiring seems correct to me: pull-ups to 3.3V (500R), SDA to GPIO4, SCL to GPIO5. A HTU21 I added (incl library Adafruit_HTU21DF.h and Adafruit_HTU21DF htu = Adafruit_HTU21DF(); statements).
    When using the BMP280 only I get values of 0, when adding the HTU21 the program stops when trying to read Serial.print(htu.readTemperature());
    Would you maybe have a hint?

    1. Hi Erik,

      Try first the example from here:

      and if is working it means that you connections between HTU21 and ESP are correct.

      After that try the code from here:

      If this example is working then also the connections for the BMP280 are ok.

      Run both examples with both sensors (BMP280 and HTU21) attached to the ESP8266. If you have the shield modules, I guess that both have pull-up resistors and there is no need to add more.

      I am sure that both sensors are not sharing the same address.

      To figure it out I need more info about what shields are you using and the entire code.

    2. Hi Catalin, the BMP280 is this one: , the HTU-21:
      Both sensors use a different I2C address.
      I will test your proposal and feed back. May be a few days..

    3. HTU21 is ok, BMP280 defective.
      Program working perfect (BMP280 code commented).
      Thank you Catalin!

    4. Erik, I hope you connected the CSB and SDO to 3V3 on the BMP280 module.

    5. Good point; I had SDO to 3.3V; changed to GND (I2C address 0x76), working now, in your program too. Thank you for all your help!!

    6. Hi Catalin, why do you use the F in Serial.println(F("BMP280 test")); there only?

    7. String to be printed will be saved in flash and not in RAM. Is good if you have a lot of strings to be printed, as in debug mode.

    8. But why do you use it only once (and not for example in the other related lines)?

    9. Got today some BME280 sensors. All are working nice.

  11. I wrote code for your program to be executed once a minute (or once an hour, once a day, twice, more etc..) using a DS3231 without library. I tried to include her ebut it does not accept it. I would like you to have a critical look at it, how can i send it to you?
    Thank you, Erik

    1. Sure, I can take a look on it. Send me an email with it. Email address is here:

  12. Auto-reset and auto-program using DTR, on bare-bones 12E/F modules: connect DTR with 2.2uF cap in series to RESET. Add 10k pull-up between RST and 3.3V, add small signal diode (1N4148) in parallel (anode to RST).
    Auto-program: connect DTR to GPIO0 with second small signal diode in series, anode to GPIO0.
    Works with any USB-to-serial adapter where DTR is broken out (beware: use 3.3V on adapter connections to ESP8266).