/*
DS3231.cpp: DS3231 Real-Time Clock library
original code by
Eric Ayars
4/1/11

updated to Arduino 1.0 
John Hubert
Feb 7, 2012

Released into the public domain.
*/

#include <myDS3231.h>

#define CLOCK_ADDRESS 0x68

// Constructor
myDS3231::myDS3231() {
	// nothing to do for this constructor.
}

/***************************************** 
	Public Functions
 *****************************************/

void myDS3231::getTime(byte& year, byte& month, byte& date, byte& DoW, byte& hour, byte& minute, byte& second) {
	byte tempBuffer;
	bool PM;
	bool h12;

	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x00));
	Wire.endTransmission();
	
	Wire.requestFrom(CLOCK_ADDRESS, 7);

	second = bcdToDec(Wire.read());
	minute = bcdToDec(Wire.read());
	tempBuffer = bcdToDec(Wire.read());
	h12 = tempBuffer & 0b01000000;
	if (h12) {
		PM = tempBuffer & 0b00100000;
		hour = bcdToDec(tempBuffer & 0b00011111);
	} else {
		hour = bcdToDec(tempBuffer & 0b00111111);
	}
	DoW = bcdToDec(Wire.read());
	date = bcdToDec(Wire.read());
	month = bcdToDec(Wire.read() & 0b01111111);
	year = bcdToDec(Wire.read());
}

byte myDS3231::getSecond() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x00));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

byte myDS3231::getMinute() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x01);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

byte myDS3231::getHour(bool& h12, bool& PM) {
	byte temp_buffer;
	byte hour;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(0x02);
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	temp_buffer = Wire.read();
	h12 = temp_buffer & 0b01000000;
	if (h12) {
		PM = temp_buffer & 0b00100000;
		hour = bcdToDec(temp_buffer & 0b00011111);
	} else {
		hour = bcdToDec(temp_buffer & 0b00111111);
	}
	return hour;
}

byte myDS3231::getDoW() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x03));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

byte myDS3231::getDate() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x04));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

byte myDS3231::getMonth(bool& Century) {
	byte temp_buffer;
	byte hour;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x05));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	temp_buffer = Wire.read();
	Century = temp_buffer & 0b10000000;
	return (bcdToDec(temp_buffer & 0b01111111)) ;
}

byte myDS3231::getYear() {
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x06));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return bcdToDec(Wire.read());
}

void myDS3231::setSecond(byte Second) {
	// Sets the seconds 
	// This function also resets the Oscillator Stop Flag, which is set
	// whenever power is interrupted.
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x00));
	Wire.write(decToBcd(Second));	
	Wire.endTransmission();
	// Clear OSF flag
	byte temp_buffer = readControlByte(1);
	writeControlByte((temp_buffer & 0b01111111), 1);
}

void myDS3231::setMinute(byte Minute) {
	// Sets the minutes 
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x01));
	Wire.write(decToBcd(Minute));	
	Wire.endTransmission();
}

void myDS3231::setHour(byte Hour) {
	// Sets the hour, without changing 12/24h mode.
	// The hour must be in 24h format.

	bool h12;

	// Start by figuring out what the 12/24 mode is
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x02));
	Wire.endTransmission();
	Wire.requestFrom(CLOCK_ADDRESS, 1);
	h12 = (Wire.read() & 0b01000000);
	// if h12 is true, it's 12h mode; false is 24h.

	if (h12) {
		// 12 hour
		if (Hour > 12) {
			Hour = decToBcd(Hour-12) | 0b01100000;
		} else {
			Hour = decToBcd(Hour) & 0b11011111;
		}
	} else {
		// 24 hour
		Hour = decToBcd(Hour) & 0b10111111;
	}

	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x02));
	Wire.write(Hour);
	Wire.endTransmission();
}

void myDS3231::setDoW(byte DoW) {
	// Sets the Day of Week
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x03));
	Wire.write(decToBcd(DoW));	
	Wire.endTransmission();
}

void myDS3231::setDate(byte Date) {
	// Sets the Date
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x04));
	Wire.write(decToBcd(Date));	
	Wire.endTransmission();
}

void myDS3231::setMonth(byte Month) {
	// Sets the month
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x05));
	Wire.write(decToBcd(Month));	
	Wire.endTransmission();
}

void myDS3231::setYear(byte Year) {
	// Sets the year
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x06));
	Wire.write(decToBcd(Year));	
	Wire.endTransmission();
}

void myDS3231::setClockMode(bool h12) {
	// sets the mode to 12-hour (true) or 24-hour (false).
	// One thing that bothers me about how I've written this is that
	// if the read and right happen at the right hourly millisecnd,
	// the clock will be set back an hour. Not sure how to do it better, 
	// though, and as long as one doesn't set the mode frequently it's
	// a very minimal risk. 
	// It's zero risk if you call this BEFORE setting the hour, since
	// the setHour() function doesn't change this mode.
	
	byte temp_buffer;

	// Start by reading byte 0x02.
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x02));
	Wire.endTransmission();
	Wire.requestFrom(CLOCK_ADDRESS, 1);
	temp_buffer = Wire.read();

	// Set the flag to the requested value:
	if (h12) {
		temp_buffer = temp_buffer | 0b01000000;
	} else {
		temp_buffer = temp_buffer & 0b10111111;
	}

	// Write the byte
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x02));
	Wire.write(temp_buffer);
	Wire.endTransmission();
}

float myDS3231::getTemperature() {
	// Checks the internal thermometer on the myDS3231 and returns the 
	// temperature as a floating-point value.
	byte temp;
	Wire.beginTransmission(CLOCK_ADDRESS);
	Wire.write(uint8_t(0x11));
	Wire.endTransmission();

	Wire.requestFrom(CLOCK_ADDRESS, 2);
	temp = Wire.read();	// Here's the MSB
	return float(temp) + 0.25*(Wire.read()>>6);
}

void myDS3231::enableOscillator(bool TF, bool battery, byte frequency) {
	// turns oscillator on or off. True is on, false is off.
	// if battery is true, turns on even for battery-only operation,
	// otherwise turns off if Vcc is off.
	// frequency must be 0, 1, 2, or 3.
	// 0 = 1 Hz
	// 1 = 1.024 kHz
	// 2 = 4.096 kHz
	// 3 = 8.192 kHz (Default if frequency byte is out of range)
	if (frequency > 3) frequency = 3;
	// read control byte in, but zero out current state of RS2 and RS1.
	byte temp_buffer = readControlByte(0) & 0b11100111;
	if (battery) {
		// turn on BBSQW flag
		temp_buffer = temp_buffer | 0b01000000;
	} else {
		// turn off BBSQW flag
		temp_buffer = temp_buffer & 0b10111111;
	}
	if (TF) {
		// set ~EOSC to 0 and INTCN to zero.
		temp_buffer = temp_buffer & 0b01111011;
	} else {
		// set ~EOSC to 1, leave INTCN as is.
		temp_buffer = temp_buffer | 0b10000000;
	}
	// shift frequency into bits 3 and 4 and set.
	frequency = frequency << 3;
	temp_buffer = temp_buffer | frequency;
	// And write the control bits
	writeControlByte(temp_buffer, 0);
}

void myDS3231::enable32kHz(bool TF) {
	// turn 32kHz pin on or off
	byte temp_buffer = readControlByte(1);
	if (TF) {
		// turn on 32kHz pin
		temp_buffer = temp_buffer | 0b00001000;
	} else {
		// turn off 32kHz pin
		temp_buffer = temp_buffer & 0b11110111;
	}
	writeControlByte(temp_buffer, 1);
}

bool myDS3231::oscillatorCheck() {
	// Returns false if the oscillator has been off for some reason.
	// If this is the case, the time is probably not correct.
	byte temp_buffer = readControlByte(1);
	bool result = true;
	if (temp_buffer & 0b10000000) {
		// Oscillator Stop Flag (OSF) is set, so return false.
		result = false;
	}
	return result;
}

/***************************************** 
	Private Functions
 *****************************************/

byte myDS3231::decToBcd(byte val) {
// Convert normal decimal numbers to binary coded decimal
	return ( (val/10*16) + (val%10) );
}

byte myDS3231::bcdToDec(byte val) {
// Convert binary coded decimal to normal decimal numbers
	return ( (val/16*10) + (val%16) );
}

byte myDS3231::readControlByte(bool which) {
	// Read selected control byte
	// first byte (0) is 0x0e, second (1) is 0x0f
	Wire.beginTransmission(CLOCK_ADDRESS);
	if (which) {
		// second control byte
		Wire.write(uint8_t(0x0f));
	} else {
		// first control byte
		Wire.write(uint8_t(0x0e));
	}
	Wire.endTransmission();
	Wire.requestFrom(CLOCK_ADDRESS, 1);
	return Wire.read();	
}

void myDS3231::writeControlByte(byte control, bool which) {
	// Write the selected control byte.
	// which=false -> 0x0e, true->0x0f.
	Wire.beginTransmission(CLOCK_ADDRESS);
	if (which) {
		Wire.write(uint8_t(0x0f));
	} else {
		Wire.write(uint8_t(0x0e));
	}
	Wire.write(control);
	Wire.endTransmission();
}

