/**
 ** 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

GND5VSDASCLSQWRTCDS1307+