Ingo Lohs
Published © GPL3+

MyLCD20x4 Clock with Value-Added Information - BME280

Use a Particle Photon, a BME280 sensor and a cheap LCD20x4 to start your own clock project.

BeginnerFull instructions provided1 hour1,222
MyLCD20x4 Clock with Value-Added Information - BME280

Things used in this project

Hardware components

Photon
Particle Photon
×1
LCD20x4
×1
I2C Adapter for LCD1602 and 2004
I suggest to solder this adapter to the LCD20x4 to use the I2C communication
×1
BME280
I suggest to solder the headers to the breakout board
×1
Breadboard (generic)
Breadboard (generic)
×1
Jumper wires (generic)
Jumper wires (generic)
×1

Story

Read more

Custom parts and enclosures

LCD 2004 Housing

Sketchfab still processing.

Code

MyLCD20x4 clock with value-added information

C/C++
v1.1
// Ingo Lohs, myclock-bme280-lcd-i2c works with Particle Photon v0.6.2
// a digital watch with BME280 on I2C
// 08.07.2017, v1.0
// 26.08.2017, v1.1 add a check for time to clear the LCD Display - else the Weekname will burn into LCD, example Donnerstag is longer then Freitag

// LIBs
#include <Adafruit_BME280.h>
#include <LiquidCrystal_I2C_Spark.h>

// Objects
LiquidCrystal_I2C *lcd;
Adafruit_BME280 bme; // I2C

// Definitions
const unsigned long UPDATE_PERIOD_MS = 5000;
unsigned long lastUpdate = 0;

// I2C wiring
#define BME_MOSI D0
#define BME_SCK D1

// Druck auf NormalNull - Meerwasser-Spiegel
#define SEALEVELPRESSURE_HPA (1013.25)

// for the values
char buf[64];
String Wochentag;

// *********************************

void setup()
{
    Serial.begin(9600);
    
    Serial.println(F("BME280 + LCDisplay 1602 / 2004"));

    if (bme.begin()) {
        Serial.println("BME280 sensor works!");
    }
    
    // The address is typically 0x27. I2C Address: 0x3F
    // https://www.sainsmart.com/new-sainsmart-iic-i2c-twi-1602-serial-lcd-module-display-for-arduino-uno-mega-r3.html
    lcd = new LiquidCrystal_I2C(0x3F /*address*/, 20 /*columns*/, 4/*rows*/); // > for LCD2004
    lcd->init();
    lcd->backlight();
    lcd->clear();
    Time.zone(+2.00); // setup a time zone, which is part of the ISO6801 format 
}
    
// *********************************

void loop() { 

  lcd->setCursor(0 /*columns*/,0 /*rows*/);
  lcd->print(Time.format(Time.now(), "%d.%m.%y")); // %A für Weekday = English Output
  
  if ((Time.format(Time.now(), "%H:%M:%S")) == "00:00:00")
  {
        lcd->clear();
  }
  
  // https://docs.particle.io/reference/firmware/photon/#weekday-
  int wDayName = Time.weekday();
  
  if (wDayName != wDayName)
  {
      lcd->clear();
  }
  
  if (wDayName == 1) 
  {
      Wochentag = "Sonntag";
  }
  else if (wDayName == 2) 
  {
      Wochentag = "Montag";
  }
  else if (wDayName == 3) 
  {
      Wochentag = "Dienstag";
  }
  else if (wDayName == 4) 
  {
      Wochentag = "Mittwoch";
  }
  else if (wDayName == 5) 
  {
      Wochentag = "Donnerstag";
  }
  else if (wDayName == 6) 
  {
      Wochentag = "Freitag";
  }
   else if (wDayName == 7) 
  {
      Wochentag = "Samstag";
  }
   
  lcd->setCursor(9,0);
  lcd->print(Wochentag);
  
  lcd->setCursor(0,1);
  lcd->print(Time.format(Time.now(), "KW: %W"));
  lcd->setCursor(9,1);
  lcd->print(Time.format(Time.now(), "%H:%M:%S"));

    // read the BME280 sensor
	if (millis() - lastUpdate >= UPDATE_PERIOD_MS) {
		lastUpdate = millis();
		
    	Serial.print("Temperature = ");
    	float temp = bme.readTemperature(); // degrees C
        Serial.print(temp);
        Serial.println(" *C");

        Serial.print("Humidity = ");
        float humidity = bme.readHumidity(); // % 
        Serial.print(humidity);
        Serial.println(" %");
    
        Serial.print("Approx. Altitude = ");
        float altitude = (bme.readAltitude(SEALEVELPRESSURE_HPA)); 
        Serial.print(altitude);
        Serial.println(" m");
    
        Serial.print("Pressure = ");
    	float pressure = (bme.readPressure() / 100.0F); // hPa
        Serial.print(pressure);
        Serial.println(" hPa");

        // check for value
		if (temp != NULL) {
	    
	        // print out to LCD 
			snprintf(buf, sizeof(buf), "%.2f C", temp);
			lcd->setCursor(0,2);
			lcd->print(buf);

			//snprintf(buf, sizeof(buf), "%.1f F", temp * 9.0 / 5.0 + 32.0);

			snprintf(buf, sizeof(buf), "%.2f %%", humidity);
            lcd->setCursor(9,2);
			lcd->print(buf);

			snprintf(buf, sizeof(buf), "%.1f % m", altitude);
			lcd->setCursor(0,3);
			lcd->print(buf);

			snprintf(buf, sizeof(buf), "%.1f %%hPa", pressure);
			lcd->setCursor(9,3);
			lcd->print(buf);
        
        // in case of error during sensor reading
       	}
      	else {
      	    lcd->clear();
		    lcd->setCursor(0,0);
			lcd->print("Problem with BME280!");
      	}
  		
	}
}

Update v1.2

C/C++
Function isDST() to check summer/winter-time
// Ingo Lohs, myclock-bme280-lcd-i2c works with Particle Photon v0.6.2
// a digital watch with BME280 on I2C
// 08.07.2017, v1.0
// 26.08.2017, v1.1 add a check for time to clear the LCD Display - else the Weekname will burn into LCD, example Donnerstag is longer then Freitag
// 08.01.2018, v1.2 add a function isDST() to check the summer/winter-time

// LIBs
#include <Adafruit_BME280.h>
#include <LiquidCrystal_I2C_Spark.h>

// Objects
LiquidCrystal_I2C *lcd;
Adafruit_BME280 bme; // I2C

// Definitions
const unsigned long UPDATE_PERIOD_MS = 5000;
unsigned long lastUpdate = 0;

// I2C wiring
#define BME_MOSI D0
#define BME_SCK D1

// Druck auf NormalNull - Meerwasser-Spiegel
#define SEALEVELPRESSURE_HPA (1013.25)

// for the values
char buf[64];
String Wochentag;

// *********************************

void setup()
{
    Serial.begin(9600);
    
    Serial.println(F("BME280 + LCDisplay 1602 / 2004"));

    if (bme.begin()) {
        Serial.println("BME280 sensor works!");
    }
    
    // The address is typically 0x27. I2C Address: 0x3F
    // https://www.sainsmart.com/new-sainsmart-iic-i2c-twi-1602-serial-lcd-module-display-for-arduino-uno-mega-r3.html
    lcd = new LiquidCrystal_I2C(0x3F /*address*/, 20 /*columns*/, 4/*rows*/); // > for LCD2004
    lcd->init();
    lcd->backlight();
    lcd->clear();
    Time.zone(isDST() ? +2.00 : +1.00);  
}
    
// *********************************

void loop() { 

  lcd->setCursor(0 /*columns*/,0 /*rows*/);
  lcd->print(Time.format(Time.now(), "%d.%m.%y")); // %A für Weekday = English Output
  
  if ((Time.format(Time.now(), "%H:%M:%S")) == "00:00:00")
  {
        lcd->clear();
  }

  // https://docs.particle.io/reference/firmware/photon/#weekday-
  int wDayName = Time.weekday();
  
  if (wDayName != wDayName)
  {
      lcd->clear();
  }
  
  if (wDayName == 1) 
  {
      Wochentag = "Sonntag";
  }
  else if (wDayName == 2) 
  {
      Wochentag = "Montag";
  }
  else if (wDayName == 3) 
  {
      Wochentag = "Dienstag";
  }
  else if (wDayName == 4) 
  {
      Wochentag = "Mittwoch";
  }
  else if (wDayName == 5) 
  {
      Wochentag = "Donnerstag";
  }
  else if (wDayName == 6) 
  {
      Wochentag = "Freitag";
  }
   else if (wDayName == 7) 
  {
      Wochentag = "Samstag";
  }
   
  lcd->setCursor(9,0);
  lcd->print(Wochentag);
  
  lcd->setCursor(0,1);
  lcd->print(Time.format(Time.now(), "KW: %W"));
  lcd->setCursor(9,1);
  lcd->print(Time.format(Time.now(), "%H:%M:%S"));

    // read the BME280 sensor
	if (millis() - lastUpdate >= UPDATE_PERIOD_MS) {
		lastUpdate = millis();
		
    	Serial.print("Temperature = ");
    	float temp = bme.readTemperature(); // degrees C
        Serial.print(temp);
        Serial.println(" *C");

        Serial.print("Humidity = ");
        float humidity = bme.readHumidity(); // % 
        Serial.print(humidity);
        Serial.println(" %");
    
        Serial.print("Approx. Altitude = ");
        float altitude = (bme.readAltitude(SEALEVELPRESSURE_HPA)); 
        Serial.print(altitude);
        Serial.println(" m");
    
        Serial.print("Pressure = ");
    	float pressure = (bme.readPressure() / 100.0F); // hPa
        Serial.print(pressure);
        Serial.println(" hPa");

        // check for value
		if (temp != NULL) {
	    
	        // print out to LCD 
			snprintf(buf, sizeof(buf), "%.2f C", temp);
			lcd->setCursor(0,2);
			lcd->print(buf);

			//snprintf(buf, sizeof(buf), "%.1f F", temp * 9.0 / 5.0 + 32.0);

			snprintf(buf, sizeof(buf), "%.2f %%", humidity);
            lcd->setCursor(9,2);
			lcd->print(buf);

			snprintf(buf, sizeof(buf), "%.1f % m", altitude);
			lcd->setCursor(0,3);
			lcd->print(buf);

			snprintf(buf, sizeof(buf), "%.1f %%hPa", pressure);
			lcd->setCursor(9,3);
			lcd->print(buf);
        
        // in case of error during sensor reading
       	}
      	else {
      	    lcd->clear();
		    lcd->setCursor(0,0);
			lcd->print("Problem with BME280!");
      	}
  		
	}
}

// FUNCTION TO CHECK SUMMER/WINTER-TIME ---------------------

bool isDST()
{ // Central European Summer Timer calculation
  int dayOfMonth = Time.day();
  int month = Time.month();
  int dayOfWeek = Time.weekday() - 1; // make Sunday 0 .. Saturday 6

  if (month >= 4 && month <= 9)
  { // March to September definetly DST
    return true;
  }
  else if (month < 3 || month > 10)
  { // before March or after October is definetly normal time
    return false;
  }

  // March and October need deeper examination
  boolean lastSundayOrAfter = (dayOfMonth - dayOfWeek > 24);
  if (!lastSundayOrAfter)
  { // before switching Sunday
    return (month == 10); // October DST will be true, March not
  }

  if (dayOfWeek)
  { // AFTER the switching Sunday
    return (month == 3); // for March DST is true, for October not
  }

  int secSinceMidnightUTC = Time.now() % 86400;
  boolean dayStartedAs = (month == 10); // DST in October, in March not
                                        // on switching Sunday we need to consider the time
  if (secSinceMidnightUTC >= 1 * 3600)
  { // 1:00 UTC (=2:00 CET/3:00 CEST)
    return !dayStartedAs;
  }

  return dayStartedAs;
}

MyLCD20x4 clock with value-added information

Credits

Ingo Lohs

Ingo Lohs

182 projects • 194 followers
I am well over 50 years and come from the middle of Germany.

Comments