/**
** 2 line LCD 1602 via I2C
** DS3231 RTC clock
** DHT11 Temperature meter
** PIR sensor
** WS2812B Segment display
**
** Arduino analog input 5 - I2C SCL CLOCK ( BROWN WIRE ) <- note for me
** Arduino analog input 4 - I2C SDA DATA ( WHITE WIRE ) <- note for me
**
** I2C scanner output:
** 0x23 BH1750 light level meter sensor
** 0x27 I2C to LCD
** 0x68 DS3231 RTC clock
** 0x57 ?? temperature?
**/
#include <Wire.h>
#include <FastLED.h>
#include "DHT.h"
#include <Time.h>
#include "LiquidCrystal_I2C.h"
#include "DS3231.h"
#include "BH1750.h"
#define Sprintln(a) (Serial.println(a))
#define Sprint(a) (Serial.print(a))
//#define Sprintln(a)
//#define Sprint(a)
#define seconds() ((unsigned long)millis()/1000)
#define minutes() ((unsigned int)seconds()/60)
#define hours() ((unsigned int)minutes()/60)
#define DATA_PIN 8
#define BTN1_PIN 11
#define BTN2_PIN 10
#define BTN3_PIN 9
// PIR Sensor
#define PIR_PIN A7
#define LED_PIN 13 // show activity
unsigned int pir_active = 0;
const char *WeekDay[7] = { "Nedela", "Pondelok", "Utorok", "Streda", "Stvrtok", "Piatok", "Sobota" };
// DHT11 Temperature
DHT dht(3, DHT11);
byte hum, temp; // Not able to measure outside temperature below zero
// BH1750 Light Meter
BH1750 lightMeter;
uint16_t lux;
// WS2812b RGB LED STRIP
// fire + clock 14 * 4 + 3 * 4 = 68 + 72 = 140
int COLOR_INDEX = 128;
int BRIGHTNESS = 2; // variable
int BRIGHTNESS_ADD = 0;
#define BRIGHTNESS_ON 8
#define BRIGHTNESS_MIN 2
#define BRIGHTNESS_MAX 50
#define NUM_LEDS 68 // fire: 3*24 = 72, seg disp: 4 * 14 + 3 * 4 = 68
#define SEG_DOTS 33 // 33 and 34
#define SEG_DECIMAL 32 // 24.5°C
#define SEG_DEGREE 53 // 24.5°C
CRGB leds[NUM_LEDS];
CRGBPalette16 gPal;
// LCD 1602
LiquidCrystal_I2C lcd(0x27, 16, 2); // <<----- Add your address here. Find it from I2C Scanner, 0x27, 0x3F
// custom chars names
#define BYTE_Lx 0
#define BYTE_Left 127 // standard arrow
#define BYTE_Right 126 // standard arrow
#define BYTE_DEGREE 223 // degree celsius
// --- Define their own characters for the LCD:
byte charLx[8] = {0x00, 0x10, 0x10, 0x10, 0x15, 0x12, 0x1d, 0x00 };
// not used byte charC[8] = {0x18,0x1a,0x05,0x04,0x04,0x05,0x02,0x00};
// SEGMENT clock
#define SEG_SEGMENTS 4 // 0,1,2,3
#define SEG_SEG_SIZE 18 // leds from one seg char to another
boolean SEG_FONT[][14] = {
// 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, // 0
{0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, // 1
{1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1}, // 2
{1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1}, // 3
{0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1}, // 4
{1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1}, // 5
{1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 6
{1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, // 7
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, // 8
{1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1}, // 9
{1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0}, // C
{1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1}, // % left half
{1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1} // % right half
};
time_t t;
int i;
/**1234567890123456**
* Ne 13.11. 10:30 * 1.
* 2016 22.5°C *
********************
* Ne 13.11. 10:30 * 2.
* 123456x 22.5°C *
********************
* Pondelok 12:12 * 3.
* 13.11.16 22.5°C *
********************/
void lcd_display(byte screen) {
lcd.clear();
switch(screen) {
case 1:
lcd.print( (char *) WeekDay[weekday(t) - 1][0]);
lcd.print( (char *) WeekDay[weekday(t) - 1][1]);
lcd.print(" ");
lcd_number(day(t), 2, '-');
lcd.print(".");
lcd_number(month(t), 2, '-');
lcd.print(".");
lcd_number(hour(t), 2, '-');
lcd.print(":");
lcd_number(minute(t), 2, '-');
lcd.setCursor(1,4);
lcd.print(year(t) - 2000, DEC);
break;
case 2:
break;
case 3:
lcd.print( (char *) WeekDay[weekday(t) - 1]);
lcd.setCursor(0, 1);
lcd.print(day(t), DEC);
lcd.print(".");
lcd.print(month(t), DEC);
lcd.print(".");
lcd.print(year(t) - 2000, DEC);
break;
default:
break;
}
}
/***** ****** ****** ** ** *****
** ** ** ** ** ** **
**** ***** ** ** ** *****
** ** ** ** ** **
***** ****** ** **** **/
void setup() {
byte sec, min, hr, wd, dy, mon, yr;
pinMode(PIR_PIN, INPUT);
pinMode(DATA_PIN, OUTPUT);
Serial.begin(9600);
lcd.init();
Wire.begin();
dht.begin();
BRIGHTNESS = BRIGHTNESS_ON;
lcd.createChar (BYTE_Lx, charLx);
// Switch on the backlight
lcd.backlight();
lcd.clear();
lcd.setCursor(4, 0); lcd.print(F("Arduino")); delay(600);
readDS3231time(&sec, &min, &hr, &wd, &dy, &mon, &yr);
setTime(hr, min, sec, dy, mon, 2000 + yr);
// set the initial time here:
// if (yr < 1) {
// // DS3231 seconds, minutes, hours, wday, date, month, year
// setDS3231time( 0, 27, 23, 7, 12, 6, 16);
// }
lcd.setCursor(2, 1); lcd.print(F("WS2812 Clock")); delay(600);
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(leds, NUM_LEDS);
FastLED.clear();
lcd.setCursor(4, 0); lcd.print(F(__TIME__)); delay(600);
FastLED.setBrightness( BRIGHTNESS );
fill_solid(leds, NUM_LEDS, CRGB::White);
FastLED.show();
lcd.setCursor(2, 1); lcd.print(F(__DATE__));lcd.print(" "); delay(100);
FastLED.delay(500); // Sanity before start
gPal = RainbowColors_p;
// show leds on start
for ( i = 0 ; i < NUM_LEDS ; i++ ) {
leds[i] = ColorFromPalette( gPal, (255 / NUM_LEDS) * i, 250, LINEARBLEND);
FastLED.show();
FastLED.delay(30);
}
for ( i = 0 ; i < NUM_LEDS ; i++ ) {
leds[i] = CRGB::Black;
FastLED.show();
FastLED.delay(30);
}
FastLED.show();
FastLED.delay(500);
delay(500);
lcd.clear();
// Set clock palette
//gPal = CRGBPalette16( CRGB::Black, CRGB(255,0,0), CRGB::Orange, CRGB::Yellow);
pir_active = 30;
Sprintln(F("Setup finished."));
}
/** **** **** *****
** ** ** ** ** ** **
** ** ** ** ** *****
** ** ** ** ** **
****** **** **** **/
void loop() {
static int current_delay = 150;
static int last_millis;
static int last_seconds; boolean secondely = false;
static int last_minutes; boolean minutely = false;
static int last_hours; boolean hourly = false;
int h, m, ct, ch; // current temperature, humidity
static byte buttons;
/** Check PIR (every 2 seconds)
* check time (hourly)
* check temp, hum (minute)
* check luxmeter (minute)
* if PIR active, turn display ON, turn leds HIGH
* adjust BRIGHTNESS according to ambient light
**/
secondely = (last_seconds < seconds() ? true : false);
minutely = (last_minutes < minutes() ? true : false);
hourly = (last_hours < hours() ? true : false);
buttons = read_buttons(BTN1_PIN,BTN2_PIN,BTN3_PIN);
t = now();
fill_solid(leds, NUM_LEDS, CRGB::Black);
// PIR
if ( secondely ) { // once per every second
if ( analogRead(PIR_PIN) > 400 ) {
pir_active = ( pir_active > 0 ? (pir_active < 60 ? pir_active : 60) : (1000/current_delay)*10 ) + 1; // increase from 10 to 60
} else {
pir_active = ( pir_active > 0 ? pir_active - 1 : 0 ); // decrease to 0
}
// Sprint("["); Sprint( millis() ); Sprintln("]");
}
if( buttons > 0 ) {
Sprint(seconds());
Sprint(F(" Buttons: "));
Sprintln(buttons);
if (buttons == 100) {
COLOR_INDEX += random(0,30);
}
if (buttons == 10) {
COLOR_INDEX+=2;
}
if (buttons == 1) {
COLOR_INDEX-=2;
}
}
if ( minutely ) {
lcd.clear(); // clear screen:-(
// Date & Time:
lcd.setCursor(0, 0);
lcd.print( (char *) WeekDay[weekday(t) - 1]);
lcd.print(" ");
lcd.setCursor(8, 0);
lcd.print(day(t), DEC);
lcd.print(".");
lcd.print(month(t), DEC);
lcd.print(".");
lcd.print(year(t) - 2000, DEC);
dht.begin();
for (i=0;i<10;i++) {
ct = ct + (int) dht.readTemperature();
ct = ct / 2; // average
readDS3231temperature(&hum);
temp = (ct + hum) /2; // average
hum = (int) dht.readHumidity();
}
//temp = (abs(ct - temp) > 10 ? ct : (ct+temp)/2);
Sprint(" B: ");
Sprint(BRIGHTNESS);
Sprint(" T: ");
Sprint(temp);
Sprint(" H: ");
Sprintln(hum);
lightMeter.begin();
lux = lightMeter.readLightLevel();
}
// Pir status
lcd.setCursor(9, 0);
lcd.print( pir_active ? "*" : "-");
if ( secondely ) {
// Time
lcd.setCursor(8, 1); lcd_number(hour(t), 2, ' '); // Hours
lcd.setCursor(11, 1); lcd_number(minute(t), 2, '0'); // Minutes
Sprint(" B: ");
Sprint(BRIGHTNESS);
Sprint(" BA: ");
Sprintln(BRIGHTNESS_ADD);
}
if ( minutes() % 2 == 0 ) {
// DOTS : :
// if ( millis() % 1000 < 500 ) { // Every half second
// // turn dots on
// lcd.setCursor(10,1); lcd.print(":");
// lcd.setCursor(13,1); lcd.print(":");
// lcd_number(second(t), 2, '0'); // Seconds
// leds[SEG_DOTS] = ColorFromPalette( gPal, COLOR_INDEX, 255, LINEARBLEND);
// leds[SEG_DOTS+1] = ColorFromPalette( gPal, COLOR_INDEX, 255, LINEARBLEND);
// } else {
// // turn dots off
// lcd.setCursor(10,1); lcd.print(" ");
// lcd.setCursor(13,1); lcd.print(" ");
// leds[SEG_DOTS] = CRGB::Black;
// leds[SEG_DOTS+1] = CRGB::Black;
// }
// 14,32,50
// Sprint(" ");
// Sprint( round( (int) millis() % 1000 / 250 + 0.5F) );
leds[14] = leds[15] = leds[16] = leds[17] = CRGB::Black;
leds[32] = leds[33] = leds[34] = leds[35] = CRGB::Black;
leds[50] = leds[51] = leds[52] = leds[53] = CRGB::Black;
switch( round( (float) ( millis() % 1000 ) / 250 + 0.5F) ) {
case 1:
leds[15] = leds[16] = ColorFromPalette( gPal, COLOR_INDEX, 255, LINEARBLEND);
break;
case 2:
leds[33] = leds[34] = ColorFromPalette( gPal, COLOR_INDEX, 255, LINEARBLEND);
break;
case 3:
leds[51] = leds[52] = ColorFromPalette( gPal, COLOR_INDEX, 255, LINEARBLEND);
break;
}
// display TIME:
h = hour(t);
m = minute(t);
lcd.setCursor(0, 1); lcd.print(lux, DEC); lcd.write(BYTE_Lx); lcd.print(" "); // Light level
seg_write( 0, (int) h / 10, CRGB::Black );
seg_write( 1, (int) h % 10, CRGB::Black );
seg_write( 2, (int) m / 10, CRGB::Black );
seg_write( 3, (int) m % 10, CRGB::Black );
} else {
// Turn off the dots:
leds[SEG_DOTS] = CRGB::Black;
leds[SEG_DOTS+1] = CRGB::Black;
if (seconds() % 60 < 30) {
// display TEMPERATURE
lcd.setCursor(0, 1); lcd_number(temp, 4, ' '); lcd.write(BYTE_DEGREE); lcd.print("C"); // Temperature
if ( temp >= 100 ) seg_write( 0, (int) temp / 100, (COLOR_INDEX == 128 ? CRGB::Green : CRGB::Black) );
if ( temp >= 10 ) seg_write( 1, (int) temp / 10, (COLOR_INDEX == 128 ? CRGB::Green : CRGB::Black) );
if ( temp >= 1 ) seg_write( 2, (int) temp % 10, (COLOR_INDEX == 128 ? CRGB::Green : CRGB::Black) );
seg_write( 3, 10, CRGB::White ); // C
leds[SEG_DEGREE] = CRGB::White;
} else {
// display HUMIDITY
lcd.setCursor(0, 1); lcd_number(hum, 4, ' '); lcd.print("%"); // Humidity
if ( hum >= 10 ) seg_write( 0, (int) hum / 10, (COLOR_INDEX == 128 ? CRGB::Blue : CRGB::Black) );
if ( hum >= 1 ) seg_write( 1, (int) hum % 10, (COLOR_INDEX == 128 ? CRGB::Blue : CRGB::Black) );
seg_write( 2, 11, CRGB::White ); // % - left half
seg_write( 3, 12, CRGB::White ); // % - right half
}
}
// Light saver:
if ( pir_active > 0 ) {
lcd.backlight();
BRIGHTNESS_ADD = 1;
} else {
lcd.noBacklight();
BRIGHTNESS_ADD = -1;
}
if ( BRIGHTNESS + BRIGHTNESS_ADD >= BRIGHTNESS_MIN && BRIGHTNESS + BRIGHTNESS_ADD <= BRIGHTNESS_MAX ) {
BRIGHTNESS = BRIGHTNESS + BRIGHTNESS_ADD;
} else {
BRIGHTNESS_ADD = 0;
}
if ( secondely ) {
last_seconds = seconds();
last_minutes = minutes();
last_hours = hours();
}
last_millis = millis();
FastLED.setBrightness( BRIGHTNESS );
FastLED.show();
FastLED.delay( current_delay );
// delay(current_delay);
}
// Write character at position
// use "leds" array
void seg_write(byte position, byte font_char, CRGB color) {
byte j, strip_position;
// strip pos = position * 18
// for each of 7 segment display (14)
for ( j = 0 ; j < 14 ; j++ ) {
strip_position = position * SEG_SEG_SIZE + j;
if ( strip_position < NUM_LEDS ) {
if ( SEG_FONT[font_char][j] == 1 ) {
if ( color.r == 0 && color.g == 0 && color.b == 0 ) {
leds[strip_position] = ColorFromPalette( gPal, COLOR_INDEX, 255, LINEARBLEND);
} else {
leds[strip_position] = color;
}
} else {
leds[strip_position] = CRGB::Black;
}
}
}
}
// read_buttons(PIN1,PIN2,PIN3) and return 3digit number
// debounce button state
byte read_buttons( byte BTN1, byte BTN2, byte BTN3) {
static byte last_buttons; // button 000, 100, 010, 001, 011, 110, 101
byte buttons = 0;
boolean b1_state = digitalRead(BTN1);
boolean b2_state = digitalRead(BTN2);
boolean b3_state = digitalRead(BTN3);
buttons += (b1_state ? 100 : 0);
buttons += (b2_state ? 10 : 0);
buttons += (b3_state ? 1 : 0);
if ( last_buttons == buttons ) {
return buttons;
}
last_buttons = buttons;
return (0);
}
// countDigits( num) -- Count digits in a number
byte countDigits(int num) {
byte count = 0;
if (num < 10) return 1;
if (num < 100) return 2;
if (num < 1000) return 3;
while(num) {
num = num / 10;
count++;
}
return count;
}
// lcd_number( number, number_size, pad) -- print ' ' padded number at cursor
void lcd_number(long number, uint8_t number_size, char pad) {
while (number_size > countDigits(number)) {
number_size--;
lcd.write(pad);
}
lcd.print(number);
}
// End of file WS2812 Clock.ino