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: 


BMP280
NodeMCU / WeMos D1 mini
Other ESP8266
VCC
3V3

GND
GND

SCL
D1
GPIO 5
SDA
D2
GPIO 4
CSB
3V3

SDO
3V3




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 thingspeak.com and also update the temperature gauge on this blog.



/**********************************************
 * Catalin Batrinu bcatalin@gmail.com 
 * Read temperature and pressure from BMP280
 * and send it to thingspeaks.com
**********************************************/

#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 = "api.thingspeak.com";
WiFiClient client;


/**************************  
 *   S E T U P
 **************************/
void setup() {
  Serial.begin(9600);
  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.println();
  Serial.println();
  Serial.print("Connecting to ");
  Serial.println(ssid);
  
  WiFi.begin(ssid, password);
  
  while (WiFi.status() != WL_CONNECTED) 
  {
    delay(500);
    Serial.print(".");
  }
  Serial.println("");
  Serial.println("WiFi connected");  
}

  /**************************  
 *  L O O P
 **************************/
void loop() {
    Serial.print("T=");
    Serial.print(bme.readTemperature());
    Serial.print(" *C");
    
    Serial.print(" P=");
    Serial.print(bme.readPressure());
    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))  // "184.106.153.149" or api.thingspeak.com
    {
        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("Host: api.thingspeak.com\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: ");
        client.print(postStr.length());
        client.print("\n\n");
        client.print(postStr);    
    }
    client.stop(); 
    //every 20 sec   
    delay(20000);
}


Temperature logging


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











16 comments:

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

    ReplyDelete
  2. Perfectly working, thank you very much.

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

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

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

    ReplyDelete
  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.

    ReplyDelete
    Replies
    1. Go to thingspeak.com 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 !

      Delete
  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?

    ReplyDelete
    Replies
    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: https://myesp8266.blogspot.nl/2017/11/with-help-of-iotcentral.html

      Delete
  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!
    Erik


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

      Delete
  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?
    Thanks!
    Erik

    ReplyDelete
    Replies
    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:

      start-line
      message-headers
      empty-line
      message-body
      message-trailers

      See the empty-line ?

      Search for HTTP header info. Also you can read this: http://www.tcpipguide.com/free/t_HTTPGenericMessageFormat.htm

      Delete
    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?
      Thks
      Erik

      Delete
    3. The & is mandatory. It is a parameter separator in the HTTP GET command.
      http://api.thingspeak.com/update?api_key=xxxxzzzzzzyyyyy&field1=33&field2=5454&field3=44

      Please see more examples on https://de.mathworks.com/help/thingspeak/examples.html

      Delete
    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.

      Delete
    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:

      loop(){
      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.
      }

      Delete