You need to log in to create posts and topics.

373_BMP180 PRESSURE SENSOR

The BMP180 is a digital atmospheric pressure and temperature sensor. It communicates via the I2C bus and allows you to measure pressure, temperature, and calculate an approximate altitude.

Main features:
Type: Digital barometric sensor
Pressure: 300–1100 hPa
Temperature: −40 to +85 °C
Pressure resolution: Up to 0.01 hPa
Absolute accuracy: ±1 hPa typical
I2C address: 0x77 (fixed)
Chip supply voltage: 1.8–3.6 V
Outputs: Digital pressure and temperature

In our simulated component BMP180_PRESSURE, Arduino requests measurements via the I2C bus, using address 0x77. The data is displayed on an SSD1306 OLED screen, showing atmospheric pressure in hPa, temperature in °C, and estimated altitude in meters. Applications: weather stations, barometers, altimeters, and environmental monitoring systems. It is designed for the latest versions of Simulide 1 and 2.

SUBCIRCUIT SCRIPT AND INO PROGRAM:

Both are shown separately below.

// BMP180s SCRIPT. DEFRAN26A
// Direccion I2C fija: 0x77 (escritura 0xEE, lectura 0xEF)
// VP: 0..5 V = 300..1100 hPa; VT: 0..5 V = -20..60 C
// SDA open-drain; ACK sobre el noveno pulso SCL.

IoPin@ SDA = component.getPin("SDA");
IoPin@ SCL = component.getPin("SCL");
IoPin@ VP  = component.getPin("VP");
IoPin@ VT  = component.getPin("VT");
const int ESPERA = 0;
const int RECIBIR = 1;
const int ACK_ESCLAVO = 2;
const int TRANSMITIR = 3;
const int ACK_MASTER = 4;
const int FIN = 5;
array<uint> reg(256, 0);
int fase = ESPERA;
int bits = 0;
uint recibido = 0;
uint pointer = 0;
uint txDato = 0;
int txBit = 7;
bool primerDato = true;
bool direccionCorrecta = false;
bool direccionLectura = false;
bool novenoAlto = false;
bool oldSCL = true;
bool oldSDA = true;
bool procesando = false;
bool sdaForzada = false;
bool iniciarTxTrasAck = false;
bool siguienteEsValor = false;
bool masterConfirmo = false;

void releaseSDA()
{
    sdaForzada = false;
    SDA.setStateZ(true);
}

void pullSDA()
{
    sdaForzada = true;
    SDA.setOutState(false);
    SDA.setStateZ(false);
}

void set16(uint a, int v)
{
    uint w = uint(v) & 0xFFFF;
    reg[a] = (w >> 8) & 255;
    reg[a+1] = w & 255;
}

void loadCalibration()
{
    set16(0xAA, 408);   // AC1
    set16(0xAC, -72);   // AC2
    set16(0xAE, -14383);// AC3
    set16(0xB0, 32741); // AC4
    set16(0xB2, 32757); // AC5
    set16(0xB4, 23153); // AC6
    set16(0xB6, 6190);  // B1
    set16(0xB8, 4);     // B2
    set16(0xBA, -32768);// MB
    set16(0xBC, -8711); // MC
    set16(0xBE, 2868);  // MD
}

void updateMeasurement(uint cmd)
{
    double vP = VP.getVoltage();
    double vT = VT.getVoltage();
    if (vP < 0) vP = 0;
    if (vP > 5) vP = 5;
    if (vT < 0) vT = 0;
    if (vT > 5) vT = 5;
    double pressurePa = 30000.0 + vP*16000.0;
    double tempC = -20.0 + vT*16.0;
    if (cmd == 0x2E)
    {
        // UT aproximado: 28525 corresponde a unos 20 C con esta calibracion
        int ut = int(28525.0 + (tempC-20.0)*125.0);
        if (ut < 0) ut = 0;
        if (ut > 65535) ut = 65535;
        reg[0xF6] = (uint(ut) >> 8) & 255;
        reg[0xF7] = uint(ut) & 255;
        reg[0xF8] = 0;
    }
    else if ((cmd & 0x3F) == 0x34)
    {
        uint oss = (cmd >> 6) & 3;
        // Inversion lineal aproximada de la compensacion a 20 C.
        int baseUP = int(34000.0 + (pressurePa-101465.0)/3.028);
        if (baseUP < 0) baseUP = 0;
        uint up = uint(baseUP) << oss;
        uint adc = up << (8-oss);
        reg[0xF6] = (adc >> 16) & 255;
        reg[0xF7] = (adc >> 8) & 255;
        reg[0xF8] = adc & 255;
    }
    reg[0xF4] = cmd & 0xDF; // SCO=0: conversion inmediata
}

void resetRegisters()
{
    for (uint i=0; i<256; i++) reg[i] = 0;
    loadCalibration();
    reg[0xD0] = 0x55;
    reg[0xD1] = 0x01;
    reg[0xF6] = 0x80;
    pointer = 0;
}

void escribirRegistro(uint direccion, uint valor)
{
    direccion &= 0xFF;
    if (direccion == 0xE0 && valor == 0xB6)
    {
        resetRegisters();
        return;
    }
    if (direccion == 0xF4)
    {
        updateMeasurement(valor);
        return;
    }
    if (direccion == 0xD0 || direccion == 0xD1) return;
    if (direccion >= 0xAA && direccion <= 0xBF) return;
    reg[direccion] = valor & 0xFF;
}

void ponerBitTx()
{
    if (((txDato >> txBit) & 1) != 0) releaseSDA();
    else pullSDA();
}

void comenzarTx()
{
    txDato = reg[pointer & 0xFF];
    txBit = 7;
    bits = 0;
    fase = TRANSMITIR;
    ponerBitTx();
}

void reposo()
{
    fase = ESPERA;
    bits = 0;
    recibido = 0;
    novenoAlto = false;
    releaseSDA();
}

void comenzar()
{
    // El puntero se conserva ante un START repetido.
    fase = RECIBIR;
    bits = 0;
    recibido = 0;
    direccionCorrecta = false;
    direccionLectura = false;
    primerDato = true;
    siguienteEsValor = false;
    iniciarTxTrasAck = false;
    novenoAlto = false;
    releaseSDA();
}

void procesarByte(uint valor)
{
    if (primerDato)
    {
        direccionCorrecta = ((valor >> 1) == 0x77);
        direccionLectura = (valor & 1) != 0;
        iniciarTxTrasAck = direccionCorrecta && direccionLectura;
        primerDato = false;
        siguienteEsValor = false;
    }
    else if (direccionCorrecta && !direccionLectura)
    {
        if (!siguienteEsValor)
        {
            pointer = valor & 0xFF;
            siguienteEsValor = true;
        }
        else
        {
            escribirRegistro(pointer, valor);
            pointer = (pointer + 1) & 0xFF;
        }
        iniciarTxTrasAck = false;
    }
    else direccionCorrecta = false;
}

void setup()
{
    // inicializacion en reset().
}

void reset()
{
    SCL.setPinMode(1);
    VP.setPinMode(1);
    VT.setPinMode(1);
    SDA.setPinMode(3);
    SDA.setOutHighV(5.0);
    SDA.setOutState(false);
    SDA.setStateZ(true);
    procesando = false;
    sdaForzada = false;
    resetRegisters();
    fase = ESPERA;
    bits = 0;
    recibido = 0;
    primerDato = true;
    siguienteEsValor = false;
    iniciarTxTrasAck = false;
    masterConfirmo = false;
    direccionCorrecta = false;
    direccionLectura = false;
    novenoAlto = false;
    oldSCL = SCL.getInpState();
    oldSDA = SDA.getInpState();
    SCL.changeCallBack(element, true);
    SDA.changeCallBack(element, true);
}

void voltChanged()
{
    if (procesando) return;
    procesando = true;
    bool scl = SCL.getInpState();
    bool sda = SDA.getInpState();
    bool subidaSCL = !oldSCL && scl;
    bool bajadaSCL = oldSCL && !scl;
    bool bajadaSDA = oldSDA && !sda;
    bool subidaSDA = !oldSDA && sda;
    oldSCL = scl;  // actualizar ANTES de cambiar SDA.
    oldSDA = sda;
    if (!subidaSCL && !bajadaSCL && scl && !sdaForzada)
    {
        if (bajadaSDA) comenzar();
        else if (subidaSDA) reposo();
    }
    if (fase == RECIBIR)
    {
        if (subidaSCL)
        {
            recibido = ((recibido << 1) | (sda ? 1 : 0)) & 0xFF;
            bits++;
            if (bits == 8) procesarByte(recibido);
        }
        else if (bajadaSCL && bits == 8)
        {
            if (direccionCorrecta)
            {
                fase = ACK_ESCLAVO;
                novenoAlto = false;
                pullSDA();
            }
            else fase = FIN;
        }
    }
    else if (fase == ACK_ESCLAVO)
    {
        if (subidaSCL) novenoAlto = true;
        else if (bajadaSCL && novenoAlto)
        {
            novenoAlto = false;
            fase = FIN;
            releaseSDA();
            if (iniciarTxTrasAck) comenzarTx();
            else
            {
                fase = RECIBIR;
                bits = 0;
                recibido = 0;
            }
        }
    }
    else if (fase == TRANSMITIR)
    {
        if (subidaSCL) bits++;
        else if (bajadaSCL)
        {
            if (bits == 8)
            {
                releaseSDA();
                fase = ACK_MASTER;
                novenoAlto = false;
            }
            else if (bits > 0 && bits < 8)
            {
                txBit = 7 - bits;
                ponerBitTx();
            }
        }
    }
    else if (fase == ACK_MASTER)
    {
        if (subidaSCL)
        {
            novenoAlto = true;
            masterConfirmo = !sda;
        }
        else if (bajadaSCL && novenoAlto)
        {
            fase = FIN;
            if (masterConfirmo)
            {
                pointer = (pointer + 1) & 0xFF;
                comenzarTx();
            }
            else releaseSDA();
        }
    }
    procesando = false;
}
// BMP180 PRESSURE SENSOR. DEFRAN26A
// BMP180: 0x77   OLED: 0x3C
// Librerias: Adafruit BMP085 Library, Adafruit GFX, Adafruit SSD1306

#include <Wire.h>
#include <Adafruit_BMP085.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define OLED_ANCHO 128
#define OLED_ALTO  64
#define OLED_RESET -1
#define OLED_ADDR  0x3C
Adafruit_SSD1306 oled(OLED_ANCHO, OLED_ALTO, &Wire, OLED_RESET);
Adafruit_BMP085 bmp;
const unsigned long PERIODO_MS = 700;
const float PRESION_MAR_HPA = 1013.25;
const byte GRAF_PUNTOS = 29;
float historial[GRAF_PUNTOS];
byte muestras = 0;
byte siguiente = 0;
unsigned long anterior = 0;
bool sensorOK = false;

void setup()
{
  Wire.begin();
  Wire.setClock(100000);  // 100 kHz.
  if (!oled.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {}
  oled.clearDisplay();
  oled.setTextColor(SSD1306_WHITE);
  oled.setTextSize(1);
  oled.setCursor(10, 24); oled.print(F("STARTING BMP180..."));
  oled.display();
  sensorOK = bmp.begin(BMP085_ULTRALOWPOWER);
  if (!sensorOK) {mostrarError(F("BMP180 NOT PRESENT")); return;}
  anterior = millis() - PERIODO_MS;
}

void loop()
{
  if (!sensorOK) return;
  unsigned long ahora = millis();
  if (ahora - anterior < PERIODO_MS) return;
  anterior = ahora;
  float presion = bmp.readPressure() / 100.0;   // Pa -> hPa
  float temperatura = bmp.readTemperature();    // grados Celsius
  float altitud = bmp.readAltitude(PRESION_MAR_HPA * 100.0); // metros
  if (presion < 100 || presion > 1300 || isnan(presion) || isnan(temperatura))
  {
    mostrarError(F("READING ERROR"));
    return;
  }
  guardarPresion(presion);
  mostrarDatos(presion, temperatura, altitud);
}

void mostrarError(const __FlashStringHelper *mensaje)
{
  oled.clearDisplay();
  oled.setTextColor(SSD1306_WHITE);
  oled.setTextSize(1);
  oled.drawRoundRect(0, 0, 128, 64, 5, SSD1306_WHITE);
  oled.setCursor(4, 13); oled.print(F("METEO STATION"));
  oled.drawFastHLine(12, 27, 104, SSD1306_WHITE);
  oled.setCursor(13, 37);
  oled.print(mensaje);
  oled.display();
}

void guardarPresion(float hpa)
{
  historial[siguiente] = hpa;
  siguiente = (siguiente + 1) % GRAF_PUNTOS;
  if (muestras < GRAF_PUNTOS) muestras++;
}

void dibujarGrafica()
{
  const int x = 96, y = 46, w = 29, h = 16;
  oled.drawRect(x - 2, y - 2, w + 4, h + 4, SSD1306_WHITE);
  if (muestras < 2) return;
  float minimo = historial[0];
  float maximo = historial[0];
  for (byte i = 1; i < muestras; i++)
  {
    if (historial[i] < minimo) minimo = historial[i];
    if (historial[i] > maximo) maximo = historial[i];
  }
  if (maximo - minimo < 0.6)
  {
    float medio = (maximo + minimo) * 0.5;
    minimo = medio - 0.3;
    maximo = medio + 0.3;
  }
  byte inicio = (muestras == GRAF_PUNTOS) ? siguiente : 0;
  for (byte i = 1; i < muestras; i++)
  {
    byte ia = (inicio + i - 1) % GRAF_PUNTOS;
    byte ib = (inicio + i) % GRAF_PUNTOS;
    int xa = x + (long)(i - 1) * (w - 1) / (muestras - 1);
    int xb = x + (long)i * (w - 1) / (muestras - 1);
    int ya = y + h - 1 - (int)((historial[ia] - minimo) * (h - 1) / (maximo - minimo));
    int yb = y + h - 1 - (int)((historial[ib] - minimo) * (h - 1) / (maximo - minimo));
    oled.drawLine(xa, ya, xb, yb, SSD1306_WHITE);
  }
}

void mostrarDatos(float presion, float temperatura, float altitud)
{
  oled.clearDisplay();
  oled.setTextColor(SSD1306_WHITE);
  oled.fillRoundRect(0, 0, 128, 13, 3, SSD1306_WHITE);  // Banda superior invertida
  oled.setTextColor(SSD1306_BLACK);
  oled.setTextSize(1);
  oled.setCursor(6, 3); oled.print(F("BMP180  |  BAROMETER"));
  oled.setTextColor(SSD1306_WHITE);
  oled.setCursor(4, 18);  // Presion grande, sin parpadeos: solo se actualiza cada 700 ms
  oled.setTextSize(3);
  if (presion >= 1000.0) {oled.print((int)(presion + 0.5));}
  else {oled.print(presion, 0);}
  oled.setTextSize(1);
  oled.setCursor(89, 30); oled.print(F("hPa"));
  oled.drawFastHLine(4, 42, 120, SSD1306_WHITE);
  oled.setTextSize(1);
  oled.setCursor(4, 47); oled.print(F("TEMP"));
  oled.setCursor(4, 56); oled.print(temperatura, 1);
  oled.write((uint8_t)247);  // Simbolo de grado de la fuente integrada
  oled.print(F("C"));
  oled.setCursor(53, 47); oled.print(F("ALT"));
  oled.setCursor(53, 56);
  oled.print((int)(altitud + (altitud >= 0 ? 0.5 : -0.5)));
  oled.print(F("m"));
  dibujarGrafica();
  oled.display();
}

PROVIDED FILES:

The ZIP file contains all parts of the example, hardware and software. The subcircuit is included as a inner extraZIP for the user's discretionary use.

P. de Francisco

Uploaded files: