x

Dependents:   20180621_FT813

MCP79412.cpp

Committer:
JackB
Date:
2018-07-23
Revision:
0:04311b121ac4

File content as of revision 0:04311b121ac4:

#include "MCP79412.h"

MCP79412::MCP79412(PinName sda, PinName scl) : _i2c(sda, scl)
{
    _address_RTC = MCP79412_RTC_ADDR << 1;
    squareWave(SQWAVE_1_HZ);
//    setTimeZone(1);
//    dayLightSaving = true;
    setI2Cfrequency(400000);
}

void MCP79412::setI2Cfrequency(int freq)
{
    _i2c.frequency(freq);
}

// get a uint8_t containing just the requested bits
// pass the register address to read, a mask to apply to the register and
// an uint* for the output
// you can test this value directly as true/false for specific bit mask
// of use a mask of 0xff to just return the whole register uint8_t
// returns true to indicate success
    /** Get flag
    * @param reg : register address
    * @param mask : flag mask
    * @return The register content
    */     
bool MCP79412::getFlag(char reg, char mask, char *flag)
{
    char buf[1];
    buf[0] = reg;
    int w = _i2c.write(_address_RTC, buf, 1);
    int r = _i2c.read(_address_RTC, buf, 1);
    _error = ((w != 0) || (r != 0));
    // return only requested flag
    *flag = (buf[0] & mask);
    return flag == 0 ? false : true;
}

// set/clear bits in a uint8_t register, or replace the uint8_t altogether
// pass the register address to modify, a uint8_t to replace the existing
// value with or containing the bits to set/clear and one of
// MCP79412_SET/MCP79412_CLEAR/MCP79412_REPLACE
// returns true to indicate success
    /** Set flag
    * @param reg : register address
    * @param bits : bits to set or reset
    * @param mode : MCP79412_REPLACE, MCP79412_SET, MCP79412_CLEAR
    * @return none
    */     
void MCP79412::setFlag(char reg, char bits, char mode)
{
    char buf[2];
    buf[0] = reg;
    // get status register
    int w = _i2c.write(_address_RTC, buf, 1);
    int r = _i2c.read(_address_RTC, buf+1, 1);
    // clear the flag
    if (mode == MCP79412_REPLACE)
        buf[1] = bits;
    else if (mode == MCP79412_SET)
        buf[1] |= bits;
    else
        buf[1] &= ~bits;
    int w2 = _i2c.write(_address_RTC, buf, 2);
    _error = ((w != 0) || (r != 0) || (w2 != 0));
}

// read a register
int MCP79412::readRegister(char reg)
{
//    int w = _i2c.write(_address_RTC, &reg, 1);
//    char rtn;
//    int r = _i2c.read(_address_RTC, &rtn, 1);
//    _error = ((w != 0) || (r != 0));
//    return rtn;

    char buf[1];
    buf[0] = reg;
    int w = _i2c.write(_address_RTC, buf, 1);
    int r = _i2c.read(_address_RTC, buf, 1);
    _error = ((w != 0) || (r != 0));
    return(buf[0]);
}

// read registers
void MCP79412::readRegisters(char reg, char *outbuf, char length)
{
    char buf[1];
    buf[0] = reg;
    int w = _i2c.write(_address_RTC, buf, 1);
    int r = _i2c.read(_address_RTC, outbuf, length);
    _error = ((w != 0) || (r != 0));
}

// write a register
void MCP79412::writeRegister(int reg, char uint8_t)
{
    char buf[2];
    buf[0] = reg;
    buf[1] = uint8_t;
    int w = _i2c.write(_address_RTC, buf, 2);
    _error = (w != 0);
}

// write registers
void MCP79412::writeRegisters(int reg, char *inbuf, char length)
{
    char buf[32];
    buf[0] = reg;
    for (int i = 1; i <= length; i++) {
        buf[i] = inbuf[i-1];
    }
    int w = _i2c.write(_address_RTC, buf, length+1);
    _error = (w != 0);
}


// Function to read the mac address from the eeprom
void MCP79412::getMacAddress(char *mac_address)
{
    char buf[1];
    buf[0] = MAC_LOCATION;
    int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 1);
    int r = _i2c.read(MCP79412_EEPROM_ADDR, mac_address, 6);
    _error = ((w != 0) || (r != 0));
}

// Unlock the unique id area and write in the mac address
void MCP79412::writeMacAddress(char *mac_address)
{
    char buf[7];
    unlockUniqueID();
    buf[0] = MAC_LOCATION;
    for (int i = 1; i <= 6; i++) {
        buf[i] = mac_address[i-1];
    }
    int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 7);

    _error = (w != 0);
}

// Unlock the unique id area ready for writing
void MCP79412::unlockUniqueID()
{
    // Write 0x55 to the memory location 0x09
    char buf[2];
    buf[0] = UNLOCK_ID_REG;
    buf[1] = UNLOCK_ID_CODE1;
    int w1 = _i2c.write(MCP79412_RTC_ADDR, buf, 2);

    // Write 0xAA to the memory location 0x09
    buf[0] = UNLOCK_ID_REG;
    buf[1] = UNLOCK_ID_CODE2;
    int w2 = _i2c.write(MCP79412_RTC_ADDR, buf, 2);

    _error = ((w1 != 0) || (w2 != 0));
}

// Set the date/time, set to 24hr and enable the clock
// (assumes you're passing in valid numbers)
void MCP79412::setRtcDateTime(
  uint8_t second,        // 0-59
  uint8_t minute,        // 0-59
  uint8_t hour,          // 1-23
  uint8_t dayOfWeek,     // 1-7
  uint8_t dayOfMonth,    // 1-31
  uint8_t month,         // 1-12
  uint8_t year)          // 0-99
{
    char buf[8];
    buf[0] = RTC_LOCATION;
    buf[1] = decToBcd(second) & 0x7f;               // set seconds and disable clock (01111111, Bit 7, ST = 0)
    buf[2] = decToBcd(minute) & 0x7f;               // set minutes (01111111)
    buf[3] = decToBcd(hour) & 0x3f;                 // set hours and to 24hr format (00111111, Bit 6 = 0)
    buf[4] = _BV(VBATEN) | (decToBcd(dayOfWeek) & 0x07);   // set the day and enable battery backup (00000111)|(00001000, Bit 3 = 1)
    buf[5] = decToBcd(dayOfMonth) & 0x3f;           // set the date in month (00111111)
    buf[6] = decToBcd(month) & 0x1f;                // set the month (00011111)
    buf[7] = decToBcd(year);                        // set the year (11111111)
    int w1 = _i2c.write(MCP79412_RTC_ADDR, buf, 8);

    // Start Clock:
    buf[0] = RTC_LOCATION;
    buf[1] = _BV(ST) | decToBcd(second);            // set seconds and enable clock (10000000)
    int w2 = _i2c.write(MCP79412_RTC_ADDR, buf, 2);

    _error = ((w1 != 0) || (w2 != 0));
}

// Get the date/time
void MCP79412::getRtcDateTime(
  uint8_t *second,
  uint8_t *minute,
  uint8_t *hour,
  uint8_t *dayOfWeek,
  uint8_t *dayOfMonth,
  uint8_t *month,
  uint8_t *year)
{
    char buf[8];

    buf[0] = RTC_LOCATION;
    int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
    int r = _i2c.read(MCP79412_RTC_ADDR, buf, 7);

    _error = ((w != 0) || (r != 0));

    // A few of these need masks because certain bits are control bits
    *second     = bcdToDec(buf[0] & 0x7f);  // 01111111 0-59
    *minute     = bcdToDec(buf[1] & 0x7f);  // 01111111 0-59
    *hour       = bcdToDec(buf[2] & 0x3f);  // 00111111 1-23
    *dayOfWeek  = bcdToDec(buf[3] & 0x07);  // 00000111 1-7
    *dayOfMonth = bcdToDec(buf[4] & 0x3f);  // 00111111 1-31
    *month      = bcdToDec(buf[5] & 0x1f);  // 00011111 1-12
    *year       = bcdToDec(buf[6]);         // 11111111 0-99
}

bool MCP79412::checkTimeLost(void)
{
    char buf[8];
    uint8_t second, minute, hour, dayOfWeek, dayOfMonth, month, year;
    buf[0] = RTC_LOCATION;
    int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
    int r = _i2c.read(MCP79412_RTC_ADDR, buf, 7);

    _error = ((w != 0) || (r != 0));

    // A few of these need masks because certain bits are control bits
    second     = bcdToDec(buf[0] & 0x7f);  // 01111111 0-59
    minute     = bcdToDec(buf[1] & 0x7f);  // 01111111 0-59
    hour       = bcdToDec(buf[2] & 0x3f);  // 00111111 1-23
    dayOfWeek  = bcdToDec(buf[3] & 0x07);  // 00000111 1-7
    dayOfMonth = bcdToDec(buf[4] & 0x3f);  // 00111111 1-31
    month      = bcdToDec(buf[5] & 0x1f);  // 00011111 1-12
    year       = bcdToDec(buf[6]);         // 11111111 0-99
    return (year <= 15) ? true : false;
}

// Enable the clock without changing the date/time
void MCP79412::enableClock()
{
    // Get the current seconds value as the enable/disable bit is in the same
    // byte of memory as the seconds value:
    char buf[2];
    buf[0] = RTC_LOCATION;
    int w1 = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
    int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1);

    int second = bcdToDec(buf[0] & 0x7f);  // 01111111

    // Start Clock:
    buf[0] = RTC_LOCATION;
    buf[1] = _BV(ST) | decToBcd(second);     // set seconds and enable clock (10000000, Bit 7, ST = 1)
    int w2 = _i2c.write(MCP79412_RTC_ADDR, buf, 2);

    _error = ((w1 != 0) || (r != 0) || (w2 != 0));
}

// Disable the clock without changing the date/time
void MCP79412::disableClock()
{
    // Get the current seconds value as the enable/disable bit is in the same
    // uint8_t of memory as the seconds value:
    char buf[2];
    buf[0] = RTC_LOCATION;
    int w1 = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
    int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1);
  
    int second = bcdToDec(buf[0] & 0x7f);  // 01111111

    // Stop Clock:
    buf[0] = RTC_LOCATION;
    buf[1] = decToBcd(second);     // set seconds and disable clock (01111111, Bit 7, ST = 0)
    int w2 = _i2c.write(MCP79412_RTC_ADDR, buf, 2);

    _error = ((w1 != 0) || (r != 0) || (w2 != 0));
}

// Enable the battery
void MCP79412::enableBattery()
{
    // Get the current day value as the enable/disable bit is in the same
    // uint8_t of memory as the seconds value:
    char buf[2];
    buf[0] = DAY_REG;
    int w1 = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
    int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1);
  
    int day = bcdToDec(buf[0] & 0x07);  // 00000111

    // Start Clock:
    buf[0] = DAY_REG;
    buf[1] = _BV(VBATEN) | decToBcd(day);     // set day and enable battery (00001000)
    int w2 = _i2c.write(MCP79412_RTC_ADDR, buf, 2);

    _error = ((w1 != 0) || (r != 0) || (w2 != 0));
}


// Write a single byte of data to RAM
void MCP79412::writeRamByte(uint8_t location, uint8_t data)
{
    writeRamBytes(location, &data, 1);
//    char buf[2];
//    if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR))
//    {
//        buf[0] = location;
//        buf[1] = data;
//        int w = _i2c.write(MCP79412_RTC_ADDR, buf, 2);
//        _error = (w != 0);
//    }
}

// Write multiple bytes of data to RAM
uint8_t MCP79412::writeRamBytes(uint8_t location, uint8_t *data, uint8_t length)
{
    uint8_t bytesWritten = 0;
    char buf[1];
    buf[0] = location;
    int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
    _error = (w != 0);
    for (uint8_t i = 0; i < length; i++) {
        buf[0] = data[i];
        int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); // Returns 0 on success (ack), non-0 on failure (nack)
        bytesWritten++;
        if (_error == false) {
            _error = (w != 0);
        }
    }
    return bytesWritten;
}

// Read a single byte of data from RAM
uint8_t MCP79412::readRamByte(uint8_t location)
{
    uint8_t data;
    readRamBytes(location, &data, 1);
    return data;
//    char buf[2];
//    if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR))
//    {
//        buf[0] = location;
//        int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
//        int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1);
//
//        _error = ((w != 0) || (r != 0));
//
//        return buf[0];
//    }
//    return 0;
}

// Read multiple bytes of data from RAM
uint8_t MCP79412::readRamBytes(uint8_t location, uint8_t *data, uint8_t length)
{
    uint8_t bytesRead = 0;
    char buf[1];
    buf[0] = location;
    int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
    _error = (w != 0);
    for (uint8_t i = 0; i < length; i++) {
        int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1);
        bytesRead++;
        data[i] = buf[0];
        if (_error == false) {
            _error = (r != 0);
        }
    }
    return bytesRead;
}

// 64 Bytes SRAM, Battery Backed
// Write a single byte of data to SRAM
void MCP79412::writeSramByte(uint8_t location, uint8_t data)
{
    writeSramBytes(location, &data, 1);
//    char buf[2];
//    if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR))
//    {
//        buf[0] = location;
//        buf[1] = data;
//        int w = _i2c.write(MCP79412_RTC_ADDR, buf, 2);
//        _error = (w != 0);
//    }
}

// 64 Bytes SRAM, Battery Backed
// Write multiple bytes of data to SRAM
uint8_t MCP79412::writeSramBytes(uint8_t location, uint8_t *data, uint8_t length)
{
    uint8_t bytesWritten = 0;
    char buf[1];
    buf[0] = location;
    int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
    _error = (w != 0);
    for (uint8_t i = 0; i < length; i++) {
        if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR))
        {
            buf[0] = data[i];
            int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1); // Returns 0 on success (ack), non-0 on failure (nack)
            bytesWritten++;
            if (_error == false) {
                _error = (w != 0);
            }
        }
        location++;
    }
    return bytesWritten;
}

// 64 Bytes SRAM, Battery Backed
// Read a single byte of data from SRAM
uint8_t MCP79412::readSramByte(uint8_t location)
{
    uint8_t data;
    readSramBytes(location, &data, 1);
    return data;
//    char buf[2];
//    if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR))
//    {
//        buf[0] = location;
//        int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
//        int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1);
//
//        _error = ((w != 0) || (r != 0));
//
//        return buf[0];
//    }
//    return 0;
}

// 64 Bytes SRAM, Battery Backed
// Read multiple bytes of data from SRAM
uint8_t MCP79412::readSramBytes(uint8_t location, uint8_t *data, uint8_t length)
{
    uint8_t bytesRead = 0;
    char buf[1];
    buf[0] = location;
    int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
    _error = (w != 0);
    for (uint8_t i = 0; i < length; i++) {
        if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR))
        {
            int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1);
            bytesRead++;
            data[i] = buf[0];
            if (_error == false) {
                _error = (r != 0);
            }
        }
        location++;
    }
    return bytesRead;
}

// 128 Bytes EEPROM
// Write a single byte of data to EEPROM
void MCP79412::writeEepromByte(uint8_t location, uint8_t data)
{
    writeEepromBytes(location, &data, 1);
//    char buf[2];
//    unlockUniqueID();
//    buf[0] = location & (EEPROM_SIZE - 1);
//    buf[1] = data;
//    int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 2);
//    _error = (w != 0);
}

// 128 Bytes EEPROM
// Unlock the unique id area and write multiple of bytes to EEPROM
uint8_t MCP79412::writeEepromBytes(uint8_t location, uint8_t *data, uint8_t length)
{
    uint8_t bytesWritten = 0;
    char buf[1];
    unlockUniqueID();
    buf[0] = location & (EEPROM_SIZE - 1);  // location & 0x7f
    int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 1);
    _error = (w != 0);
    for (uint8_t i = 0; i < length; i++) {
        if (location < EEPROM_SIZE)
        {
            buf[0] = data[i];
            int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 1); // Returns 0 on success (ack), non-0 on failure (nack)
            bytesWritten++;
            if (_error == false) {
                _error = (w != 0);
            }
        }
        location++;
    }
    return bytesWritten;
}

// 128 Bytes EEPROM
// Read a single byte of data from EEPROM
uint8_t MCP79412::readEepromByte(uint8_t location)
{
    uint8_t data;
    readEepromBytes(location, &data, 1);
    return data;
//    char buf[2];
//    if ((location >= SRAM_START_ADDR) && (location <= SRAM_END_ADDR))
//    {
//        buf[0] = location;
//        int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
//        int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1);
//
//        _error = ((w != 0) || (r != 0));
//
//        return buf[0];
//    }
//    return 0;
}

// 128 Bytes EEPROM
// Read multiple bytes of data from EEPROM
uint8_t MCP79412::readEepromBytes(uint8_t location, uint8_t *data, uint8_t length)
{
    uint8_t bytesRead = 0;
    char buf[1];
    buf[0] = location & (EEPROM_SIZE - 1);  // location & 0x7f
    int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 1);
    _error = (w != 0);
    for (uint8_t i = 0; i < length; i++) {
        if (location < EEPROM_SIZE)
        {
            int r = _i2c.read(MCP79412_EEPROM_ADDR, buf, 1);
            bytesRead++;
            data[i] = buf[0];
            if (_error == false) {
                _error = (r != 0);
            }
        }
        location++;
    }
    return bytesRead;
}

/*----------------------------------------------------------------------*
 * Read the calibration register.                                       *
 * The calibration value is not a twos-complement number. The MSB is    *
 * the sign bit, and the 7 LSBs are an unsigned number, so we convert   *
 * it and return it to the caller as a regular twos-complement integer. *
 *----------------------------------------------------------------------*/
int MCP79412::calibRead(void)
{
    uint8_t val = readRamByte(CALIB_REG);

    if ( val & 0x80 ) {
        return -(val & 0x7F);
    }
    return val;
}

/*----------------------------------------------------------------------*
 * Write the calibration register.                                      *
 * Calibration value must be between -127 and 127, others result        *
 * in no action. See note above on the format of the calibration value. *
 *----------------------------------------------------------------------*/
void MCP79412::calibWrite(int value)
{
    uint8_t calibVal;

    if (value >= -127 && value <= 127) {
        calibVal = abs(value);
        if (value < 0) {
            calibVal += 128;
        }
        writeRamByte(CALIB_REG, calibVal);
    }
}

/*----------------------------------------------------------------------*
 * Read the unique ID.                                                  *
 * User or factory programmable, Protected area                         *
 * For the MCP79411 (EUI-48), the first two bytes will contain 0xFF.    *
 * Caller must provide an 8-byte array to contain the results.          *
 *----------------------------------------------------------------------*/
void MCP79412::readUniqueId(char *uniqueID)
{
    char buf[1];
    buf[0] = UNIQUE_ID_ADDR;
    int w = _i2c.write(MCP79412_EEPROM_ADDR, buf, 1);
    int r = _i2c.read(MCP79412_EEPROM_ADDR, uniqueID, UNIQUE_ID_SIZE);
    _error = ((w != 0) || (r != 0));
}

/*----------------------------------------------------------------------------*
 * Returns an EUI-64 ID. For an MCP79411, the EUI-48 ID is converted to       *
 * EUI-64. For an MCP79412, calling this function is equivalent to            *
 * calling readUniqueId(). For an MCP79412, if the RTC type is known, calling *
 * readUniqueId() will be a bit more efficient.                               *
 * Caller must provide an 8-byte array to contain the results.                *
 *----------------------------------------------------------------------------*/
void MCP79412::getEUI64(char *uniqueID)
{
    char rtcID[8];

    readUniqueId(rtcID);
    if ((rtcID[0] == 0xFF) && (rtcID[1] == 0xFF)) {
        rtcID[0] = rtcID[2];
        rtcID[1] = rtcID[3];
        rtcID[2] = rtcID[4];
        rtcID[3] = 0xFF;
        rtcID[4] = 0xFE;
    }
    for (uint8_t i = 0; i < UNIQUE_ID_SIZE; i++) {
        uniqueID[i] = rtcID[i];
    }
}

/*----------------------------------------------------------------------*
 * Check to see if a power failure has occurred. If so, returns TRUE    *
 * as the function value, and returns the power down and power up       *
 * timestamps. After returning the time stamps, the RTC's timestamp     *
 * registers are cleared and the VBAT bit which indicates a power       *
 * failure is reset.                                                    *
 *                                                                      *
 * Note that the power down and power up timestamp registers do not     *
 * contain values for seconds or for the year. The returned time stamps *
 * will therefore contain the current year from the RTC. However, there *
 * is a chance that a power outage spans from one year to the next.     *
 * If we find the power down timestamp to be later (larger) than the    *
 * power up timestamp, we will assume this has happened, and well       *
 * subtract one year from the power down timestamp.                     *
 *                                                                      *
 * Still, there is an assumption that the timestamps are being read     *
 * in the same year as that when the power up occurred.                 *
 *                                                                      *
 * Finally, note that once the RTC records a power outage, it must be   *
 * cleared before another will be recorded.                             *
 *----------------------------------------------------------------------*/
bool MCP79412::powerFail(time_t *powerDown, time_t *powerUp)
{
    uint8_t day, yr;                //copies of the RTC Day and Year registers
    struct tm dn, up;               //power down and power up times
    char buf[8];

    readRamBytes(DAY_REG, &day, 1);
    readRamBytes(YEAR_REG, &yr, 1);
    yr = y2kYearToTm(bcdToDec(yr));
    if ( day & _BV(VBAT) ) {
        buf[0] = PWRDWN_TS_REG;
        int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1);

        int r = _i2c.read(MCP79412_RTC_ADDR, buf, 8);       //read both timestamp registers, 8 bytes total
        dn.tm_sec     = 0;
        dn.tm_min     = bcdToDec(buf[0]);
        dn.tm_hour    = bcdToDec(buf[1] & ~_BV(HR1224));     //assumes 24hr clock
        dn.tm_mday    = bcdToDec(buf[2]);
        dn.tm_mon     = bcdToDec(buf[3] & 0x1F);             //mask off the day, we don't need it
        dn.tm_year    = yr;                                 //assume current year
        up.tm_sec     = 0;
        up.tm_min     = bcdToDec(buf[4]);
        up.tm_hour    = bcdToDec(buf[5] & ~_BV(HR1224));     //assumes 24hr clock
        up.tm_mday    = bcdToDec(buf[6]);
        up.tm_mon     = bcdToDec(buf[7] & 0x1F);             //mask off the day, we don't need it
        up.tm_year    = yr;                                 //assume current year
        
        *powerDown = mktime(&dn);
        *powerUp   = mktime(&up);

        //clear the VBAT bit, which causes the RTC hardware to clear the timestamps too.
        //I suppose there is a risk here that the day has changed since we read it,
        //but the Day of Week is actually redundant data and the makeTime() function
        //does not use it. This could be an issue if someone is reading the RTC
        //registers directly, but as this library is meant to be used with the Time library,
        //and also because we don't provide a method to read the RTC clock/calendar
        //registers directly, we won't lose any sleep about it at this point unless
        //some issue is actually brought to our attention ;-)
        day &= ~_BV(VBAT);
        writeRamBytes(DAY_REG, &day , 1);

        //adjust the powerDown timestamp if needed (see notes above)
        if (*powerDown > *powerUp) {
            --dn.tm_year;
            *powerDown = mktime(&dn);
        }
        return true;
    }
    return false;
}

/*----------------------------------------------------------------------*
 * Enable or disable the square wave output.                            *
 *----------------------------------------------------------------------*/
void MCP79412::squareWave(Sqwave freq)
{
    uint8_t ctrlReg;

    readRamBytes(CTRL_REG, &ctrlReg, 1);
    if (freq > 3) {
        ctrlReg &= ~_BV(SQWE);
    }
    else {
        ctrlReg = (ctrlReg & 0xF8) | _BV(SQWE) | freq;
    }
    writeRamByte(CTRL_REG, ctrlReg);
}

/*----------------------------------------------------------------------*
 * Set an alarm time. Sets the alarm registers only, does not enable    *
 * the alarm. See enableAlarm().                                        *
 *----------------------------------------------------------------------*/
void MCP79412::setAlarm(uint8_t alarmNumber, time_t alarmTime)
{
    struct tm *t;
    uint8_t day;                        // Need to preserve bits in the day (of week) register

    alarmNumber &= 0x01;                // Ensure a valid alarm number
    readRamBytes(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG) , &day, 1);
    t = localtime(&alarmTime);            // Put the time_t into the tm structure
    
    char buf[7];
    buf[0] = ALM0_REG + alarmNumber * (ALM1_REG - ALM0_REG);
    buf[1] = dec2bcd(t->tm_sec);
    buf[2] = dec2bcd(t->tm_min);
    buf[3] = dec2bcd(t->tm_hour);       // Sets 24 hour format (Bit 6 == 0)
    buf[4] = (day & 0xF8) + t->tm_wday;
    buf[5] = dec2bcd(t->tm_mday);
    buf[6] = dec2bcd(t->tm_mon);
    int w = _i2c.write(MCP79412_RTC_ADDR, buf, 7);
    _error = (w != 0);
}

/*----------------------------------------------------------------------*
 * Enable or disable an alarm, and set the trigger criteria,            *
 * e.g. match only seconds, only minutes, entire time and date, etc.    *
 *----------------------------------------------------------------------*/
void MCP79412::enableAlarm(uint8_t alarmNumber, uint8_t alarmType)
{
    uint8_t day;                //alarm day register has config & flag bits
    uint8_t ctrl;               //control register has alarm enable bits

    alarmNumber &= 0x01;        //ensure a valid alarm number
    readRamBytes(CTRL_REG, &ctrl, 1);
    if (alarmType < ALM_DISABLE) {
        readRamBytes(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1);
        day = ( day & 0x87 ) | alarmType << 4;  //reset interrupt flag, OR in the config bits
        writeRamByte(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), day);
        ctrl |= _BV(ALM0 + alarmNumber);        //enable the alarm
    }
    else {
        ctrl &= ~(_BV(ALM0 + alarmNumber));     //disable the alarm
    }
    writeRamByte(CTRL_REG, ctrl);
}

/*----------------------------------------------------------------------*
 * Returns true or false depending on whether the given alarm has been  *
 * triggered, and resets the alarm "interrupt" flag. This is not a real *
 * interrupt, just a bit that's set when an alarm is triggered.         *
 *----------------------------------------------------------------------*/
bool MCP79412::alarm(uint8_t alarmNumber)
{
    uint8_t day;                //alarm day register has config & flag bits

    alarmNumber &= 0x01;        //ensure a valid alarm number
    readRamBytes( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1);
    if (day & _BV(ALMIF)) {
        day &= ~_BV(ALMIF);     //turn off the alarm "interrupt" flag
        writeRamByte(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), day);
        return true;
    }
    return false;
}

/*----------------------------------------------------------------------*
 * Sets the logic level on the MFP when it's not being used as a        *
 * square wave or alarm output. The default is HIGH.                    *
 *----------------------------------------------------------------------*/
void MCP79412::out(bool level)
{
    uint8_t ctrlReg;

    readRamBytes(CTRL_REG, &ctrlReg, 1);
    if (level)
        ctrlReg |= _BV(OUT);
    else
        ctrlReg &= ~_BV(OUT);
    writeRamByte(CTRL_REG, ctrlReg);
}

/*----------------------------------------------------------------------*
 * Specifies the logic level on the Multi-Function Pin (MFP) when an    *
 * alarm is triggered. The default is LOW. When both alarms are         *
 * active, the two are ORed together to determine the level of the MFP. *
 * With alarm polarity set to LOW (the default), this causes the MFP    *
 * to go low only when BOTH alarms are triggered. With alarm polarity   *
 * set to HIGH, the MFP will go high when EITHER alarm is triggered.    *
 *                                                                      *
 * Note that the state of the MFP is independent of the alarm           *
 * "interrupt" flags, and the alarm() function will indicate when an    *
 * alarm is triggered regardless of the polarity.                       *
 *----------------------------------------------------------------------*/
void MCP79412::alarmPolarity(bool polarity)
{
    uint8_t alm0Day;

    readRamBytes(ALM0_DAY, &alm0Day, 1);
    if (polarity)
        alm0Day |= _BV(OUT);
    else
        alm0Day &= ~_BV(OUT);
    writeRamByte(ALM0_DAY, alm0Day);
}

/*----------------------------------------------------------------------*
 * Check to see if the RTC's oscillator is started (ST bit in seconds   *
 * register). Returns true if started.                                  *
 *----------------------------------------------------------------------*/
bool MCP79412::isRunning(void)
{
    char buf[1];
    buf[0] = (uint8_t)TIME_REG;
    int w = _i2c.write(MCP79412_RTC_ADDR, buf, 1);
    int r = _i2c.read(MCP79412_RTC_ADDR, buf, 1);
    _error = ((w != 0) || (r != 0));
    return buf[0] & _BV(ST);
}

/*----------------------------------------------------------------------*
 * Set or clear the VBATEN bit. Setting the bit powers the clock and    *
 * SRAM from the backup battery when Vcc falls. Note that setting the   *
 * time via set() or write() sets the VBATEN bit.                       *
 *----------------------------------------------------------------------*/
void MCP79412::vbaten(bool enable)
{
    uint8_t day;

    readRamBytes(DAY_REG, &day, 1);
    if (enable)
        day |= _BV(VBATEN);
    else
        day &= ~_BV(VBATEN);

    writeRamByte(DAY_REG, day);
    return;
}



//bool MCP79412::getSummerTime(void)
//{
//    getDateTime(&t);
//
//    time_t secondsEpoch = mktime(&t);   // seconds since the Epoch
//    t = *localtime(&secondsEpoch);
//    strftime(buffer, 32, "%j", localtime(&secondsEpoch));
//    int dayOfYearC = atoi(buffer);
//
//    strftime(buffer, 32, "%Y", localtime(&secondsEpoch));
//    int year = atoi(buffer);
//
//    int index  = (year - 2011) * 5;
//    if (index < 0)
//        index = 0;
//    if (index > 440)                    // (2099 - 2011) * 5 = 440
//        index = 440;
//    
//    int monthS = atoi(SummerTime[index+1]);
//    int dayS   = atoi(SummerTime[index+2]);
//
//    t.tm_mon   = monthS - 1;            // adjust for tm structure required values
//    t.tm_mday  = dayS;
//    secondsEpoch = mktime(&t);   // seconds since the Epoch
//    t = *localtime(&secondsEpoch);
//    strftime(buffer, 32, "%j", localtime(&secondsEpoch));
//    int dayOfYearS = atoi(buffer);
//
//    int monthE = atoi(SummerTime[index+3]);
//    int dayE   = atoi(SummerTime[index+4]);
//
//    t.tm_mon   = monthE - 1;            // adjust for tm structure required values
//    t.tm_mday  = dayE;
//    secondsEpoch = mktime(&t);   // seconds since the Epoch
//    t = *localtime(&secondsEpoch);
//    strftime(buffer, 32, "%j", localtime(&secondsEpoch));
//    int dayOfYearE = atoi(buffer);
//
//    return ((dayOfYearC >= dayOfYearS) && (dayOfYearC < dayOfYearE)) ? true : false;
//}
//
//int MCP79412::dayOfYearC(void)
//{
//    getDateTime(&t);
//
//    time_t secondsEpoch = mktime(&t);   // seconds since the Epoch
//    strftime(buffer, 32, "%j", localtime(&secondsEpoch));
//    return atoi(buffer);
//}
//
//char * MCP79412::getSunRise(void)
//{
//    return (char*) SunRise[dayOfYearC()];
//}
//
//char * MCP79412::getSunSet(void)
//{
//    return (char*) SunSet[dayOfYearC()];
//}
//
//char * MCP79412::getDayLength(void)
//{
//    return (char*) DayLength[dayOfYearC()];
//}
//
//int MCP79412::getSunRiseMinute(void)
//{
//    int doy = dayOfYearC();
//    int h = atoi(substr((char*)SunRise[doy], 0, 2));
//    int m = atoi(substr((char*)SunRise[doy], 3, 2));
//    return h * 60 + m;
//}
//
//int MCP79412::getSunSetMinute(void)
//{
//    int doy = dayOfYearC();
//    int h = atoi(substr((char*)SunSet[doy], 0, 2));
//    int m = atoi(substr((char*)SunSet[doy], 3, 2));
//    return h * 60 + m;
//}
//
//bool MCP79412::checkSunRise(void)
//{
//    int dayOfWeek, mday, month, year, hours, minutes, seconds;
//    readDateTime(&dayOfWeek, &mday, &month, &year, &hours, &minutes, &seconds);
//
//    int absMinute     = hours * 60 + minutes;
//    int SunRiseMinute = getSunRiseMinute();
//    int SunSetMinute  = getSunSetMinute();
//
//    return ((absMinute >= SunRiseMinute) && (absMinute < SunSetMinute)) ? true : false;
//}


void MCP79412::substr(char *s, char *d, int pos, int len)
{
    char *t;
    s = s+pos;
    t = s+len;
    while (s != t) {
        *d=*s;
        s++;
        d++;
    }
    *d='\0';
}

char * MCP79412::substr(char *s, int pos, int len)
{
    char *t;
    char *d;
    d = buffer;
    s = s+pos;
    t = s+len;
    while (s != t) {
        *d=*s;
        s++;
        d++;
    }
    *d='\0';
    return buffer;
}




struct tm MCP79412::setSystemDateTime(
  uint8_t second,        // 0-59
  uint8_t minute,        // 0-59
  uint8_t hour,          // 1-23
  uint8_t dayOfMonth,    // 1-31
  uint8_t month,         // 1-12
  uint8_t year)          // 0-99
{
    uint8_t dayOfWeek = 1;          // Will be determined
    // Convert to unix time structure
    t->tm_sec   = second;           // 0-59
    t->tm_min   = minute;           // 0-59
    t->tm_hour  = hour;             // 0-23
    t->tm_wday  = dayOfWeek - 1;    // 0-6     (0 = Sunday)
    t->tm_mday  = dayOfMonth;       // 1-31
    t->tm_mon   = month - 1;        // 0-11
    t->tm_year  = year + 100;       // 100-199 year since 1900
    t->tm_isdst = 0;
    
//    printf("-> Debug %d %02d-%02d-%03d %02d:%02d:%02d\n", t->tm_wday, t->tm_mday, t->tm_mon, t->tm_year, t->tm_hour, t->tm_min, t->tm_sec);

    secondsEpoch = mktime(t);   // seconds since the Epoch
    set_time(secondsEpoch);

    // Get weekday 0-6, Sunday as 0 for RTC
    t = localtime(&secondsEpoch);
//    printf("-> Debug %d %02d-%02d-%03d %02d:%02d:%02d\n", t->tm_wday, t->tm_mday, t->tm_mon, t->tm_year, t->tm_hour, t->tm_min, t->tm_sec);
    return *t;
}

void MCP79412::getSystemDateTime(
  uint8_t *second,          // 0-59
  uint8_t *minute,          // 0-59
  uint8_t *hour,            // 0-23
  uint8_t *dayOfWeek,       // 1-7     (1 = Sunday) 
  uint8_t *dayOfMonth,      // 1-31
  uint8_t *month,           // 1-12
  uint8_t *year)            // 0-99 year since 2000
{
//    struct tm *t;
//    time_t secondsEpoch;

    // Get system DateTime
    secondsEpoch = time(NULL);
    t = localtime(&secondsEpoch);

    // time/date data
    *second     = (t->tm_sec);          // 0-59
    *minute     = (t->tm_min);          // 0-59
    *hour       = (t->tm_hour);         // 0-23
    *dayOfWeek  = (t->tm_wday + 1);     // 1-7     (1 = Sunday)
    *dayOfMonth = (t->tm_mday);         // 1-31
    *month      = (t->tm_mon + 1);      // 1-12
    *year       = (t->tm_year - 100);   // 0-99 year since 2000
}

void MCP79412::setRtcToSystemDateTime(void)
{
    // Get system DateTime
    secondsEpoch = time(NULL);
    t = localtime(&secondsEpoch);

    // Convert from unix time structure
    uint8_t second     = (t->tm_sec);           // 0-59
    uint8_t minute     = (t->tm_min);           // 0-59
    uint8_t hour       = (t->tm_hour);          // 0-23
    uint8_t dayOfWeek  = (t->tm_wday + 1);      // 1-7     (1 = Sunday)
    uint8_t dayOfMonth = (t->tm_mday);          // 1-31
    uint8_t month      = (t->tm_mon + 1);       // 1-12
    uint8_t year       = (t->tm_year - 100);    // 0-99 year since 2000

    // Set RTC DateTime
    setRtcDateTime(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
}

void MCP79412::setSystemToRtcDateTime(void)
{
    // Get RTC DateTime
    getRtcDateTime(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);

    // Convert to unix time structure
    t->tm_sec   = second;           // 0-59
    t->tm_min   = minute;           // 0-59
    t->tm_hour  = hour;             // 0-23
    t->tm_wday  = dayOfWeek - 1;    // 0-6     (0 = Sunday)
    t->tm_mday  = dayOfMonth;       // 1-31
    t->tm_mon   = month - 1;        // 0-11
    t->tm_year  = year + 100;       // 100-199 year since 1900
    t->tm_isdst = 0;
    
    // Set system DateTime
    secondsEpoch = mktime(t);   // seconds since the Epoch
    set_time(secondsEpoch);
}

void MCP79412::setRtcFromTm(struct tm *t)
{
//    // Get system DateTime
//    secondsEpoch = time(NULL);
//    t = localtime(&secondsEpoch);

    // Convert from unix time structure
    uint8_t second     = (t->tm_sec);           // 0-59
    uint8_t minute     = (t->tm_min);           // 0-59
    uint8_t hour       = (t->tm_hour);          // 0-23
    uint8_t dayOfWeek  = (t->tm_wday + 1);      // 1-7     (1 = Sunday)
    uint8_t dayOfMonth = (t->tm_mday);          // 1-31
    uint8_t month      = (t->tm_mon + 1);       // 1-12
    uint8_t year       = (t->tm_year - 100);    // 0-99 year since 2000

//    printf("setRtcFromTm %d %02d-%02d-%03d %02d:%02d:%02d\n", dayOfWeek, dayOfMonth, month, year, hour, minute, second);

    // Set RTC DateTime
    setRtcDateTime(second, minute, hour, dayOfWeek, dayOfMonth, month, year);
}

struct tm MCP79412::getTmFromRtc(void)
{
    // Get RTC DateTime
    getRtcDateTime(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);

    // Convert to unix time structure
    t->tm_sec   = second;           // 0-59
    t->tm_min   = minute;           // 0-59
    t->tm_hour  = hour;             // 0-23
    t->tm_wday  = dayOfWeek - 1;    // 0-6     (0 = Sunday)
    t->tm_mday  = dayOfMonth;       // 1-31
    t->tm_mon   = month - 1;        // 0-11
    t->tm_year  = year + 100;       // 100-199 year since 1900
    t->tm_isdst = 0;
    
    return *t;
//    // Set system DateTime
//    secondsEpoch = mktime(t);   // seconds since the Epoch
//    set_time(secondsEpoch);
}

time_t MCP79412::getSecondsEpoch(void)
{
    secondsEpoch = time(NULL);
    return secondsEpoch;
}

void MCP79412::setSecondsEpoch(time_t t)
{
    secondsEpoch = t;
    set_time(secondsEpoch);
}

void MCP79412::getRtcDateTimeAsTm(void)
{
    // Get RTC DateTime
    getRtcDateTime(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);

    // Convert to unix time structure
    t->tm_sec   = second;           // 0-59
    t->tm_min   = minute;           // 0-59
    t->tm_hour  = hour;             // 0-23
    t->tm_wday  = dayOfWeek - 1;    // 0-6     (0 = Sunday)
    t->tm_mday  = dayOfMonth;       // 1-31
    t->tm_mon   = month - 1;        // 0-11
    t->tm_year  = year + 100;       // 100-199 year since 1900
    t->tm_isdst = 0;
    
    // Set system DateTime
    secondsEpoch = mktime(t);   // seconds since the Epoch
//    time_t t = time(secondsEpoch);
    
//    set_time(secondsEpoch);
}

time_t MCP79412::convertDateTimeToTimestamp(
  uint8_t second,        // 0-59
  uint8_t minute,        // 0-59
  uint8_t hour,          // 0-23
  uint8_t dayOfMonth,    // 1-31
  uint8_t month,         // 1-12
  uint8_t year)          // 0-99 year since 2000
{
    // setup time structure for Wed, 28 Oct 2009 11:35:37
    struct tm t;
    t.tm_sec  = second;         // 0-59
    t.tm_min  = minute;         // 0-59
    t.tm_hour = hour;           // 0-23
    t.tm_mday = dayOfMonth;     // 1-31
    t.tm_mon  = month - 1;      // 0-11
    t.tm_year = year + 100;     // 100-199 year since 1900

//    printf("Debug %d %02d-%02d-%03d %02d:%02d:%02d\n", t.tm_wday, t.tm_mday, t.tm_mon, t.tm_year, t.tm_hour, t.tm_min, t.tm_sec);

    // convert to timestamp and display (1256729737)
    time_t seconds = mktime(&t);
//    printf("Time as seconds since January 1, 1970 = %d\n", seconds);

//    char buffer[32];
//    strftime(buffer, 32, "%a %d-%m-%Y %H:%M:%S\n", localtime(&seconds));
//    printf("Time: %s", buffer);

    // Get weekday Sunday as 0 (0-6) for RTC
    struct tm *t2;
    t2 = localtime(&seconds);
//    printf("Debug %d %02d-%02d-%03d %02d:%02d:%02d\n", t.tm_wday, t2->tm_mday, t2->tm_mon, t2->tm_year, t2->tm_hour, t2->tm_min, t2->tm_sec);
//    printf("Weekday %d\n", t2->tm_wday);

    return seconds;
}

uint8_t MCP79412::getWeekdayFromDate(uint8_t dayOfMonth, uint8_t month, uint8_t year)          // year 0-99
{
    // setup time structure for Wed, 28 Oct 2009 11:35:37
    struct tm t;
    t.tm_sec  = 0;              // 0-59
    t.tm_min  = 0;              // 0-59
    t.tm_hour = 0;              // 0-23
    t.tm_mday = dayOfMonth;     // 1-31
    t.tm_mon  = month - 1;      // 0-11
    t.tm_year = year + 100;     // 100-199 year since 1900

//    printf("Debug %d %02d:%02d:%02d %02d-%02d-%02d\n", t.tm_wday, t.tm_mday, t.tm_mon, t.tm_year, t.tm_hour, t.tm_min, t.tm_sec);

    // convert to timestamp and display (1256729737)
    time_t seconds = mktime(&t);
//    printf("Time as seconds since January 1, 1970 = %d\n", seconds);

//    char buffer[32];
//    strftime(buffer, 32, "%a %d-%m-%Y %H:%M:%S\n", localtime(&seconds));
//    printf("Time: %s", buffer);

    // Get weekday Sunday as 0 (0-6) for RTC
    struct tm *t2;
    t2 = localtime(&seconds);
//    printf("Debug %d %02d-%02d-%03d %02d:%02d:%02d\n", t2->tm_wday, t2->tm_mday, t2->tm_mon, t2->tm_year, t2->tm_hour, t2->tm_min, t2->tm_sec);
//    printf("Weekday %d\n", t2->tm_wday);

    return t2->tm_wday;
}








//double MCP79412::clamp(double v)
//{
//    const double t = v < 0.0f ? 0.0f : v;
//    return t > 1.0f ? 1.0f : t;
//}


//bool MCP79412::checkTimeLost(void)
//{
////    return (atoi(getFormatedDateTime("%Y")) <= 2015) ? true : false;
//    return false;
//}

//
///*----------------------------------------------------------------------*
// * Read the current time from the RTC and return it in a tmElements_t   *
// * structure. Returns false if RTC not present (I2C I/O error).         *
// *----------------------------------------------------------------------*/
//boolean MCP79412RTC::read(tmElements_t &tm)
//{
//    i2cBeginTransmission(RTC_ADDR);
//    i2cWrite((uint8_t)TIME_REG);
//    if (i2cEndTransmission() != 0) {
//        return false;
//    }
//    else {
//        //request 7 uint8_ts (secs, min, hr, dow, date, mth, yr)
//        i2cRequestFrom(RTC_ADDR, tmNbrFields);
//        tm.Second = bcd2dec(i2cRead() & ~_BV(ST));
//        tm.Minute = bcd2dec(i2cRead());
//        tm.Hour = bcd2dec(i2cRead() & ~_BV(HR1224));    //assumes 24hr clock
//        tm.Wday = i2cRead() & ~(_BV(OSCON) | _BV(VBAT) | _BV(VBATEN));    //mask off OSCON, VBAT, VBATEN bits
//        tm.Day = bcd2dec(i2cRead());
//        tm.Month = bcd2dec(i2cRead() & ~_BV(LP));       //mask off the leap year bit
//        tm.Year = y2kYearToTm(bcd2dec(i2cRead()));
//        return true;
//    }
//}
//
///*----------------------------------------------------------------------*
// * Set the RTC's time from a tmElements_t structure.                    *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::write(tmElements_t &tm)
//{
//    i2cBeginTransmission(RTC_ADDR);
//    i2cWrite((uint8_t)TIME_REG);
//    i2cWrite((uint8_t)0x00);                     //stops the oscillator (Bit 7, ST == 0)
//    i2cWrite(dec2bcd(tm.Minute));
//    i2cWrite(dec2bcd(tm.Hour));                  //sets 24 hour format (Bit 6 == 0)
//    i2cWrite(tm.Wday | _BV(VBATEN));             //enable battery backup operation
//    i2cWrite(dec2bcd(tm.Day));
//    i2cWrite(dec2bcd(tm.Month));
//    i2cWrite(dec2bcd(tmYearToY2k(tm.Year)));
//    i2cEndTransmission();
//
//    i2cBeginTransmission(RTC_ADDR);
//    i2cWrite((uint8_t)TIME_REG);
//    i2cWrite(dec2bcd(tm.Second) | _BV(ST));    //set the seconds and start the oscillator (Bit 7, ST == 1)
//    i2cEndTransmission();
//}
//
///*----------------------------------------------------------------------*
// * Write a single uint8_t to RTC RAM.                                      *
// * Valid address range is 0x00 - 0x5F, no checking.                     *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::ramWrite(uint8_t addr, uint8_t value)
//{
//    ramWrite(addr, &value, 1);
//}
//
///*----------------------------------------------------------------------*
// * Write multiple uint8_ts to RTC RAM.                                     *
// * Valid address range is 0x00 - 0x5F, no checking.                     *
// * Number of uint8_ts (nuint8_ts) must be between 1 and 31 (Wire library      *
// * limitation).                                                         *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::ramWrite(uint8_t addr, uint8_t *values, uint8_t nuint8_ts)
//{
//    i2cBeginTransmission(RTC_ADDR);
//    i2cWrite(addr);
//    for (uint8_t i=0; i<nuint8_ts; i++) i2cWrite(values[i]);
//    i2cEndTransmission();
//}
//
///*----------------------------------------------------------------------*
// * Read a single uint8_t from RTC RAM.                                     *
// * Valid address range is 0x00 - 0x5F, no checking.                     *
// *----------------------------------------------------------------------*/
//uint8_t MCP79412RTC::ramRead(uint8_t addr)
//{
//    uint8_t value;
//
//    ramRead(addr, &value, 1);
//    return value;
//}
//
///*----------------------------------------------------------------------*
// * Read multiple uint8_ts from RTC RAM.                                    *
// * Valid address range is 0x00 - 0x5F, no checking.                     *
// * Number of uint8_ts (nuint8_ts) must be between 1 and 32 (Wire library      *
// * limitation).                                                         *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::ramRead(uint8_t addr, uint8_t *values, uint8_t nuint8_ts)
//{
//    i2cBeginTransmission(RTC_ADDR);
//    i2cWrite(addr);
//    i2cEndTransmission();
//    i2cRequestFrom( (uint8_t)RTC_ADDR, nuint8_ts );
//    for (uint8_t i=0; i<nuint8_ts; i++) values[i] = i2cRead();
//}
//
///*----------------------------------------------------------------------*
// * Write a single uint8_t to Static RAM.                                   *
// * Address (addr) is constrained to the range (0, 63).                  *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::sramWrite(uint8_t addr, uint8_t value)
//{
//    ramWrite( (addr & (SRAM_SIZE - 1) ) + SRAM_START_ADDR, &value, 1 );
//}
//
///*----------------------------------------------------------------------*
// * Write multiple uint8_ts to Static RAM.                                  *
// * Address (addr) is constrained to the range (0, 63).                  *
// * Number of uint8_ts (nuint8_ts) must be between 1 and 31 (Wire library      *
// * limitation).                                                         *
// * Invalid values for nuint8_ts, or combinations of addr and nuint8_ts        *
// * that would result in addressing past the last uint8_t of SRAM will      *
// * result in no action.                                                 *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::sramWrite(uint8_t addr, uint8_t *values, uint8_t nuint8_ts)
//{
//#if defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
//    if (nuint8_ts >= 1 && (addr + nuint8_ts) <= SRAM_SIZE) {
//#else
//    if (nuint8_ts >= 1 && nuint8_ts <= (BUFFER_LENGTH - 1) && (addr + nuint8_ts) <= SRAM_SIZE) {
//#endif
//        ramWrite( (addr & (SRAM_SIZE - 1) ) + SRAM_START_ADDR, values, nuint8_ts );
//    }
//}
//
///*----------------------------------------------------------------------*
// * Read a single uint8_t from Static RAM.                                  *
// * Address (addr) is constrained to the range (0, 63).                  *
// *----------------------------------------------------------------------*/
//uint8_t MCP79412RTC::sramRead(uint8_t addr)
//{
//    uint8_t value;
//
//    ramRead( (addr & (SRAM_SIZE - 1) ) + SRAM_START_ADDR, &value, 1 );
//    return value;
//}
//
///*----------------------------------------------------------------------*
// * Read multiple uint8_ts from Static RAM.                                 *
// * Address (addr) is constrained to the range (0, 63).                  *
// * Number of uint8_ts (nuint8_ts) must be between 1 and 32 (Wire library      *
// * limitation).                                                         *
// * Invalid values for nuint8_ts, or combinations of addr and               *
// * nuint8_ts that would result in addressing past the last uint8_t of SRAM    *
// * result in no action.                                                 *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::sramRead(uint8_t addr, uint8_t *values, uint8_t nuint8_ts)
//{
//#if defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
//    if (nuint8_ts >= 1 && (addr + nuint8_ts) <= SRAM_SIZE) {
//#else
//    if (nuint8_ts >= 1 && nuint8_ts <= BUFFER_LENGTH && (addr + nuint8_ts) <= SRAM_SIZE) {
//#endif
//        ramRead((addr & (SRAM_SIZE - 1) ) + SRAM_START_ADDR, values, nuint8_ts);
//    }
//}
//
///*----------------------------------------------------------------------*
// * Write a single uint8_t to EEPROM.                                       *
// * Address (addr) is constrained to the range (0, 127).                 *
// * Can't leverage page write function because a write can't start       *
// * mid-page.                                                            *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::eepromWrite(uint8_t addr, uint8_t value)
//{
//    i2cBeginTransmission(MCP79412_EEPROM_ADDR);
//    i2cWrite( addr & (EEPROM_SIZE - 1) );
//    i2cWrite(value);
//    i2cEndTransmission();
//    eepromWait();
//}
//
///*----------------------------------------------------------------------*
// * Write a page (or less) to EEPROM. An EEPROM page is 8 uint8_ts.         *
// * Address (addr) should be a page start address (0, 8, ..., 120), but  *
// * is ruthlessly coerced into a valid value.                            *
// * Number of uint8_ts (nuint8_ts) must be between 1 and 8, other values       *
// * result in no action.                                                 *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::eepromWrite(uint8_t addr, uint8_t *values, uint8_t nuint8_ts)
//{
//    if (nuint8_ts >= 1 && nuint8_ts <= EEPROM_PAGE_SIZE) {
//        i2cBeginTransmission(MCP79412_EEPROM_ADDR);
//        i2cWrite( addr & ~(EEPROM_PAGE_SIZE - 1) & (EEPROM_SIZE - 1) );
//        for (uint8_t i=0; i<nuint8_ts; i++) i2cWrite(values[i]);
//        i2cEndTransmission();
//        eepromWait();
//    }
//}
//
///*----------------------------------------------------------------------*
// * Read a single uint8_t from EEPROM.                                      *
// * Address (addr) is constrained to the range (0, 127).                  *
// *----------------------------------------------------------------------*/
//uint8_t MCP79412RTC::eepromRead(uint8_t addr)
//{
//    uint8_t value;
//
//    eepromRead( addr & (EEPROM_SIZE - 1), &value, 1 );
//    return value;
//}
//
///*----------------------------------------------------------------------*
// * Read multiple uint8_ts from EEPROM.                                     *
// * Address (addr) is constrained to the range (0, 127).                 *
// * Number of uint8_ts (nuint8_ts) must be between 1 and 32 (Wire library      *
// * limitation).                                                         *
// * Invalid values for addr or nuint8_ts, or combinations of addr and       *
// * nuint8_ts that would result in addressing past the last uint8_t of EEPROM  *
// * result in no action.                                                 *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::eepromRead(uint8_t addr, uint8_t *values, uint8_t nuint8_ts)
//{
//#if defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__) || defined(__AVR_ATtiny45__) || defined(__AVR_ATtiny85__)
//    if (nuint8_ts >= 1 && (addr + nuint8_ts) <= EEPROM_SIZE) {
//#else
//    if (nuint8_ts >= 1 && nuint8_ts <= BUFFER_LENGTH && (addr + nuint8_ts) <= EEPROM_SIZE) {
//#endif
//        i2cBeginTransmission(MCP79412_EEPROM_ADDR);
//        i2cWrite( addr & (EEPROM_SIZE - 1) );
//        i2cEndTransmission();
//        i2cRequestFrom( (uint8_t)MCP79412_EEPROM_ADDR, nuint8_ts );
//        for (uint8_t i=0; i<nuint8_ts; i++) values[i] = i2cRead();
//    }
//}
//
///*----------------------------------------------------------------------*
// * Wait for EEPROM write to complete.                                   *
// *----------------------------------------------------------------------*/
//uint8_t MCP79412RTC::eepromWait(void)
//{
//    uint8_t waitCount = 0;
//    uint8_t txStatus;
//
//    do
//    {
//        ++waitCount;
//        i2cBeginTransmission(MCP79412_EEPROM_ADDR);
//        i2cWrite((uint8_t)0);
//        txStatus = i2cEndTransmission();
//
//    } while (txStatus != 0);
//
//    return waitCount;
//}
//
///*----------------------------------------------------------------------*
// * Read the calibration register.                                       *
// * The calibration value is not a twos-complement number. The MSB is    *
// * the sign bit, and the 7 LSBs are an unsigned number, so we convert   *
// * it and return it to the caller as a regular twos-complement integer. *
// *----------------------------------------------------------------------*/
//int MCP79412RTC::calibRead(void)
//{
//    uint8_t val = ramRead(CALIB_REG);
//
//    if ( val & 0x80 ) return -(val & 0x7F);
//    else return val;
//}
//
///*----------------------------------------------------------------------*
// * Write the calibration register.                                      *
// * Calibration value must be between -127 and 127, others result        *
// * in no action. See note above on the format of the calibration value. *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::calibWrite(int value)
//{
//    uint8_t calibVal;
//
//    if (value >= -127 && value <= 127) {
//        calibVal = abs(value);
//        if (value < 0) calibVal += 128;
//        ramWrite(CALIB_REG, calibVal);
//    }
//}
//
///*----------------------------------------------------------------------*
// * Read the unique ID.                                                  *
// * For the MCP79411 (EUI-48), the first two uint8_ts will contain 0xFF.    *
// * Caller must provide an 8-uint8_t array to contain the results.          *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::idRead(uint8_t *uniqueID)
//{
//    i2cBeginTransmission(MCP79412_EEPROM_ADDR);
//    i2cWrite(UNIQUE_ID_ADDR);
//    i2cEndTransmission();
//    i2cRequestFrom( MCP79412_EEPROM_ADDR, UNIQUE_ID_SIZE );
//    for (uint8_t i=0; i<UNIQUE_ID_SIZE; i++) uniqueID[i] = i2cRead();
//}
//
///*----------------------------------------------------------------------*
// * Returns an EUI-64 ID. For an MCP79411, the EUI-48 ID is converted to *
// * EUI-64. For an MCP79412, calling this function is equivalent to      *
// * calling idRead(). For an MCP79412, if the RTC type is known, calling *
// * idRead() will be a bit more efficient.                               *
// * Caller must provide an 8-uint8_t array to contain the results.          *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::getEUI64(uint8_t *uniqueID)
//{
//    uint8_t rtcID[8];
//
//    idRead(rtcID);
//    if (rtcID[0] == 0xFF && rtcID[1] == 0xFF) {
//        rtcID[0] = rtcID[2];
//        rtcID[1] = rtcID[3];
//        rtcID[2] = rtcID[4];
//        rtcID[3] = 0xFF;
//        rtcID[4] = 0xFE;
//    }
//    for (uint8_t i=0; i<UNIQUE_ID_SIZE; i++) uniqueID[i] = rtcID[i];
//}
//
///*----------------------------------------------------------------------*
// * Check to see if a power failure has occurred. If so, returns TRUE    *
// * as the function value, and returns the power down and power up       *
// * timestamps. After returning the time stamps, the RTC's timestamp     *
// * registers are cleared and the VBAT bit which indicates a power       *
// * failure is reset.                                                    *
// *                                                                      *
// * Note that the power down and power up timestamp registers do not     *
// * contain values for seconds or for the year. The returned time stamps *
// * will therefore contain the current year from the RTC. However, there *
// * is a chance that a power outage spans from one year to the next.     *
// * If we find the power down timestamp to be later (larger) than the    *
// * power up timestamp, we will assume this has happened, and well       *
// * subtract one year from the power down timestamp.                     *
// *                                                                      *
// * Still, there is an assumption that the timestamps are being read     *
// * in the same year as that when the power up occurred.                 *
// *                                                                      *
// * Finally, note that once the RTC records a power outage, it must be   *
// * cleared before another will be recorded.                             *
// *----------------------------------------------------------------------*/
//boolean MCP79412RTC::powerFail(time_t *powerDown, time_t *powerUp)
//{
//    uint8_t day, yr;                   //copies of the RTC Day and Year registers
//    tmElements_t dn, up;            //power down and power up times
//
//    ramRead(DAY_REG, &day, 1);
//    ramRead(YEAR_REG, &yr, 1);
//    yr = y2kYearToTm(bcd2dec(yr));
//    if ( day & _BV(VBAT) ) {
//        i2cBeginTransmission(RTC_ADDR);
//        i2cWrite(PWRDWN_TS_REG);
//        i2cEndTransmission();
//
//        i2cRequestFrom(RTC_ADDR, TIMESTAMP_SIZE);     //read both timestamp registers, 8 uint8_ts total
//        dn.Second = 0;
//        dn.Minute = bcd2dec(i2cRead());
//        dn.Hour = bcd2dec(i2cRead() & ~_BV(HR1224));    //assumes 24hr clock
//        dn.Day = bcd2dec(i2cRead());
//        dn.Month = bcd2dec(i2cRead() & 0x1F);           //mask off the day, we don't need it
//        dn.Year = yr;                                   //assume current year
//        up.Second = 0;
//        up.Minute = bcd2dec(i2cRead());
//        up.Hour = bcd2dec(i2cRead() & ~_BV(HR1224));    //assumes 24hr clock
//        up.Day = bcd2dec(i2cRead());
//        up.Month = bcd2dec(i2cRead() & 0x1F);           //mask off the day, we don't need it
//        up.Year = yr;                                   //assume current year
//
//        *powerDown = makeTime(dn);
//        *powerUp = makeTime(up);
//
//        //clear the VBAT bit, which causes the RTC hardware to clear the timestamps too.
//        //I suppose there is a risk here that the day has changed since we read it,
//        //but the Day of Week is actually redundant data and the makeTime() function
//        //does not use it. This could be an issue if someone is reading the RTC
//        //registers directly, but as this library is meant to be used with the Time library,
//        //and also because we don't provide a method to read the RTC clock/calendar
//        //registers directly, we won't lose any sleep about it at this point unless
//        //some issue is actually brought to our attention ;-)
//        day &= ~_BV(VBAT);
//        ramWrite(DAY_REG, &day , 1);
//
//        //adjust the powerDown timestamp if needed (see notes above)
//        if (*powerDown > *powerUp) {
//            --dn.Year;
//            *powerDown = makeTime(dn);
//        }
//        return true;
//    }
//    else
//        return false;
//}
//
///*----------------------------------------------------------------------*
// * Enable or disable the square wave output.                            *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::squareWave(uint8_t freq)
//{
//    uint8_t ctrlReg;
//
//    ramRead(CTRL_REG, &ctrlReg, 1);
//    if (freq > 3) {
//        ctrlReg &= ~_BV(SQWE);
//    }
//    else {
//        ctrlReg = (ctrlReg & 0xF8) | _BV(SQWE) | freq;
//    }
//    ramWrite(CTRL_REG, &ctrlReg, 1);
//}
//
///*----------------------------------------------------------------------*
// * Set an alarm time. Sets the alarm registers only, does not enable    *
// * the alarm. See enableAlarm().                                        *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::setAlarm(uint8_t alarmNumber, time_t alarmTime)
//{
//    tmElements_t tm;
//    uint8_t day;        //need to preserve bits in the day (of week) register
//
//    alarmNumber &= 0x01;        //ensure a valid alarm number
//    ramRead( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG) , &day, 1);
//    breakTime(alarmTime, tm);
//    i2cBeginTransmission(RTC_ADDR);
//    i2cWrite( ALM0_REG + alarmNumber * (ALM1_REG - ALM0_REG) );
//    i2cWrite(dec2bcd(tm.Second));
//    i2cWrite(dec2bcd(tm.Minute));
//    i2cWrite(dec2bcd(tm.Hour));                  //sets 24 hour format (Bit 6 == 0)
//    i2cWrite( (day & 0xF8) + tm.Wday );
//    i2cWrite(dec2bcd(tm.Day));
//    i2cWrite(dec2bcd(tm.Month));
//    i2cEndTransmission();
//}
//
///*----------------------------------------------------------------------*
// * Enable or disable an alarm, and set the trigger criteria,            *
// * e.g. match only seconds, only minutes, entire time and date, etc.    *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::enableAlarm(uint8_t alarmNumber, uint8_t alarmType)
//{
//    uint8_t day;                //alarm day register has config & flag bits
//    uint8_t ctrl;               //control register has alarm enable bits
//
//    alarmNumber &= 0x01;        //ensure a valid alarm number
//    ramRead(CTRL_REG, &ctrl, 1);
//    if (alarmType < ALM_DISABLE) {
//        ramRead(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1);
//        day = ( day & 0x87 ) | alarmType << 4;  //reset interrupt flag, OR in the config bits
//        ramWrite(ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1);
//        ctrl |= _BV(ALM0 + alarmNumber);        //enable the alarm
//    }
//    else {
//        ctrl &= ~(_BV(ALM0 + alarmNumber));     //disable the alarm
//    }
//    ramWrite(CTRL_REG, &ctrl, 1);
//}
//
///*----------------------------------------------------------------------*
// * Returns true or false depending on whether the given alarm has been  *
// * triggered, and resets the alarm "interrupt" flag. This is not a real *
// * interrupt, just a bit that's set when an alarm is triggered.         *
// *----------------------------------------------------------------------*/
//boolean MCP79412RTC::alarm(uint8_t alarmNumber)
//{
//    uint8_t day;                //alarm day register has config & flag bits
//
//    alarmNumber &= 0x01;        //ensure a valid alarm number
//    ramRead( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1);
//    if (day & _BV(ALMIF)) {
//        day &= ~_BV(ALMIF);     //turn off the alarm "interrupt" flag
//        ramWrite( ALM0_DAY + alarmNumber * (ALM1_REG - ALM0_REG), &day, 1);
//        return true;
//    }
//    else
//        return false;
//}
//
///*----------------------------------------------------------------------*
// * Sets the logic level on the MFP when it's not being used as a        *
// * square wave or alarm output. The default is HIGH.                    *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::out(boolean level)
//{
//    uint8_t ctrlReg;
//
//    ramRead(CTRL_REG, &ctrlReg, 1);
//    if (level)
//        ctrlReg |= _BV(OUT);
//    else
//        ctrlReg &= ~_BV(OUT);
//    ramWrite(CTRL_REG, &ctrlReg, 1);
//}
//
///*----------------------------------------------------------------------*
// * Specifies the logic level on the Multi-Function Pin (MFP) when an    *
// * alarm is triggered. The default is LOW. When both alarms are         *
// * active, the two are ORed together to determine the level of the MFP. *
// * With alarm polarity set to LOW (the default), this causes the MFP    *
// * to go low only when BOTH alarms are triggered. With alarm polarity   *
// * set to HIGH, the MFP will go high when EITHER alarm is triggered.    *
// *                                                                      *
// * Note that the state of the MFP is independent of the alarm           *
// * "interrupt" flags, and the alarm() function will indicate when an    *
// * alarm is triggered regardless of the polarity.                       *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::alarmPolarity(boolean polarity)
//{
//    uint8_t alm0Day;
//
//    ramRead(ALM0_DAY, &alm0Day, 1);
//    if (polarity)
//        alm0Day |= _BV(OUT);
//    else
//        alm0Day &= ~_BV(OUT);
//    ramWrite(ALM0_DAY, &alm0Day, 1);
//}
//
///*----------------------------------------------------------------------*
// * Check to see if the RTC's oscillator is started (ST bit in seconds   *
// * register). Returns true if started.                                  *
// *----------------------------------------------------------------------*/
//boolean MCP79412RTC::isRunning(void)
//{
//    i2cBeginTransmission(RTC_ADDR);
//    i2cWrite((uint8_t)TIME_REG);
//    i2cEndTransmission();
//
//    //request just the seconds register
//    i2cRequestFrom(RTC_ADDR, 1);
//    return i2cRead() & _BV(ST);
//}
//
///*----------------------------------------------------------------------*
// * Set or clear the VBATEN bit. Setting the bit powers the clock and    *
// * SRAM from the backup battery when Vcc falls. Note that setting the   *
// * time via set() or write() sets the VBATEN bit.                       *
// *----------------------------------------------------------------------*/
//void MCP79412RTC::vbaten(boolean enable)
//{
//    uint8_t day;
//
//    ramRead(DAY_REG, &day, 1);
//    if (enable)
//        day |= _BV(VBATEN);
//    else
//        day &= ~_BV(VBATEN);
//
//    ramWrite(DAY_REG, &day, 1);
//    return;
//}

///*----------------------------------------------------------------------*
// * Decimal-to-BCD conversion                                            *
// *----------------------------------------------------------------------*/
//uint8_t MCP79412RTC::dec2bcd(uint8_t n)
//{
//    return n + 6 * (n / 10);
//}
//
///*----------------------------------------------------------------------*
// * BCD-to-Decimal conversion                                            *
// *----------------------------------------------------------------------*/
//uint8_t __attribute__ ((noinline)) MCP79412RTC::bcd2dec(uint8_t n)
//{
//    return n - 6 * (n >> 4);
//}

// BCD to decimal conversion
int MCP79412::bcd2dec(int bcd)
{
    return(((bcd & 0xF0) >> 4) * 10 + (bcd & 0x0F));
}

// decimal to BCD conversion
int MCP79412::dec2bcd(int dec)
{
    return((dec / 10) * 16 + (dec % 10));
}

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

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