• Category Archives Automation
  • DS3231_Driver.h

    An example of a RTC driver based on the DS3231 RTC chip. The idea behind the driver is to replicate the chips register structure in part of the micro controller RAM memory in the form of a struct.

    /*
    Name		:	DS3231RTC_driver.ino
    Created		:	14-Jan-19 20:46:08
    Author		:	Oscar Goos
    Code		:	C++, Flash: 3944B, RAM: 413B
    Controller	:	ATmega328
    Last tested	:	14-Jan-2019
    Notes		:   Only Read Write Time and date registers implemented
    .			:	Copy DS3231 to RTC and the other way around could be done with a memcopy
    .			:	YinC must be uint16_t for both data sets
    .			:	Temperature not implemented
    */
    #include "ds3231_driver.h"
    RTC_t*	RTC;
    RTC_t	Timebuf={ 27, 20, 10, 6, 11, 12, 2019, 0 };
    DS3231	clock;
    
    void setup(void)
    {
    Serial.begin(115200);
    Wire.begin();
    //	RTC1.RTC = Timebuf;
    //	RTC1.Driver(INIT);
    delay(100);
    clock.Driver(SYNC);
    }
    
    void loop(void)
    {
    RTC1.Driver(SYNC);
    Serial.print(clock.RTC.sec); Serial.print("\t"), Serial.println(clock.RTC.SinD);
    delay(1000);
    
    }
    

    Here the C++ code of the library to be included in the code. It is the smallest and the simplest I have seen so far. The functions are called based on one class member with the name “Driver” and a command parameters tells the driver what to do. Three commands are implemented

    Sync: Copy time and date DS3231 registers to controller ram.
    INIT: Copy the RAM image to the DS3231 registers.
    TMDT2STR: Convert the RAM time and date registers to a time and date c-string.

    #ifndef		DS3231_H
    #define		DS3231_H
    
    #include	"wire.h"
    
    #define		INIT					0
    #define		SYNC					1
    #define		TMDT2STR				2
    
    #define		I2C_DS3231_ADR 			0x68
    #define		STOP					true
    
    #define		BCD2DEC(bcdval)			(((bcd_val>>4)*10) + (bcd_val & 0x0F))
    #define		DEC2BCD(decval)			(((dec_val/10)<<4) + (dec_val%10))
    
    typedef struct RTC_t {
    uint8_t						id;		// Struct identifier for future use
    uint8_t						sec;	// seconds,			00..59
    uint8_t						min;	// minuts,			00..59
    uint8_t						hr;		// hours,			00..23
    uint8_t						DinW;	// Day in week,		01..07
    uint8_t						DinM;	// Day in month,	01..31
    uint8_t						MinY;	// Month in year,	01..12
    uint16_t					year;	// Year				2000..2100
    uint32_t					SinD;	// seconds in day,	00..86399
    uint32_t					epoch;	// epoch time in sec since 1/1/1970
    int16_t						error;	// Time error in usec/sec
    char						tmstr[9] ="00:00:00";	// buffer for time string
    char						dtstr[11]="00/00/0000";	// buffer for date string
    } RTC_t;
    
    class DS3231 {
    
    typedef struct reg {
    uint8_t						sec;	// seconds,			00..59
    uint8_t						min;	// minuts,			00..59
    uint8_t						hr;		// hours,			00..23
    uint8_t						DinW;	// Day in week,		01..07
    uint8_t						DinM;	// Day in month,	01..31
    uint8_t						MinY;	// Month in year,	01..12
    uint8_t						YinC;	// Year in century,	00..99 respresenting 2000..2099)
    }__attribute__((packed, aligned(1))) reg_t;
    
    public:
    DS3231();
    RTC_t	RTC;
    void	Driver (uint8_t);
    
    private:
    reg_t	DSreg;
    
    protected:
    };
    
    DS3231::DS3231() {
    }
    
    void DS3231::Driver(uint8_t cmd) {
    
    uint8_t		n, bcd_val, dec_val;
    uint8_t*	DSptr = (uint8_t*)&DSreg;
    String		TDstr;
    
    switch (cmd) {
    case INIT: {
    DSreg.sec	= RTC.sec;
    DSreg.min	= RTC.min;
    DSreg.hr	= RTC.hr;
    DSreg.DinM	= RTC.DinM;
    DSreg.MinY	= RTC.MinY;
    DSreg.YinC	= RTC.year - 2000;
    DSreg.DinW	= RTC.DinW;
    RTC.SinD	= (uint32_t)RTC.hr * 3600 + (uint32_t)RTC.min * 60 + (uint32_t)RTC.sec;
    Wire.beginTransmission(I2C_DS3231_ADR);						// Start I2C and send Address
    Wire.write(0);													// set DS3231 register pointer to 0
    for (n = 0; n < sizeof(DSreg); n++) {
    dec_val = *DSptr++;											// Read nth data element from DS3231
    Wire.write(DEC2BCD(dec_val));								// Convert nth element dec2bcd and set all 7 RTC  Time date registers
    }
    Wire.endTransmission();											// Send I2C STOP and release the bus
    } break;
    
    case SYNC: {
    Wire.beginTransmission(I2C_DS3231_ADR);							// Start I2C and send Address
    Wire.write(0);													// set RTC  register address pointer to 0
    Wire.endTransmission();
    Wire.requestFrom((uint8_t)I2C_DS3231_ADR, (uint8_t)sizeof(DSreg),(uint8_t)STOP);
    for (n = 0; n < sizeof(DSreg); n++) {
    bcd_val = Wire.read();
    *DSptr++ = BCD2DEC(bcd_val);									// Read all 7 RTC  registers, convert bcd2dec in
    }
    RTC.sec		= DSreg.sec;
    RTC.min		= DSreg.min;
    RTC.hr		= DSreg.hr;
    RTC.DinM	= DSreg.DinM;
    RTC.MinY	= DSreg.MinY;
    RTC.year	= DSreg.YinC + 2000;
    RTC.DinW	= DSreg.DinW;
    RTC.SinD = (uint32_t)RTC.hr * 3600 + (uint32_t)RTC.min * 60 + (uint32_t)RTC.sec;
    DS3231::Driver(TMDT2STR);
    } break;
    
    case TMDT2STR: {
    TDstr = "";
    if (RTC.hr <= 9) TDstr += F("0");
    TDstr += String(RTC.hr);
    TDstr += F(":");
    if (RTC.min <= 9) TDstr += F("0");
    TDstr += String(RTC.min);
    TDstr += F(":");
    if (RTC.sec <= 9) TDstr += F("0");
    TDstr += String(RTC.sec);
    //		tmstr=(char*)TDstr.c_str();
    strcpy(RTC.tmstr, TDstr.c_str());
    
    TDstr = "";
    if (RTC.DinM <= 9) TDstr += F("0");
    TDstr += String(RTC.DinM);
    TDstr += F("/");
    if (RTC.MinY <= 9) TDstr += F("0");
    TDstr += String(RTC.MinY);
    TDstr += F("/");
    if (RTC.year <= 9) TDstr += F("0");
    TDstr += String(RTC.year-2000);
    //		dTDstr= (char*)TDstr.c_str();
    strcpy(RTC.dtstr, TDstr.c_str());
    } break;
    
    }
    
    }
    #endif // !DS3231_H
    

    Code is free for use.


  • Read and Write to EEPROM memory

    I was programming for a CFE network monitor to check on the quality of the electricity grid in Mexico

    This program and hardware can start up generators and electricity backups in case the grid is out of specifications or specified electric limits. It also will reconnect to the grid under controlled conditions. So this Grid Guard protects your house or business form mall functioning grid conditions.

    The “grid  guard”  has a display showing voltage current, Power, Energy,  Cos phi, frequency and network status.  More options are possible but that is planned for future releases.

    One of the functions I needed was to secure data in case of disconnecting or changing the software in the Grid guard. To do this  use the EEProm memory. The EEPROM.h library doesn’t have a general function that can write and update any desired variables to EEPROM. I wrote the two functions for this reason and you can use them without including the EEPROM.h libraries.

    After compiling by using the arduino IDE it indicated that the two functions use 444 byte of program memory and 9 byte of RAM.

    For the ones interested copy and past the code in your sketch and have fun.

    //******************************************************************************************//
    // This function is independent of data type. It copies all standard types but also string  //
    // and structs. This function reads [n] bytes from RAM pointed by ptr and writes them to    //
    // EEmemory at location EEaddress. This function checks if EEmemory contains the value to be// 
    // written. If it has it skips the programming section and runs for the next byte.          //
    //******************************************************************************************//
    
    void MEM2EE (unsigned int EEaddress, byte *ptr, unsigned int n) {
        n=n&0xFF    ;                        //safety statement can be removed
        while (n != 0) {
            while(EECR & (1<<EEPE));         // Wait for completion of previous write 
            EEAR  = EEaddress;               // Set up address register 
            EECR |= (1<<EERE);               // Start eeprom read by writing EERE 
            if (EEDR != *ptr) {              // skip write if ptr value is equal to EEmem
                EEDR  = *ptr;                // load data register with pointed byte
                EECR |= (1<<EEMPE);          // Write logical one to EEMPE
                EECR |= (1<<EEPE);           // Start eeprom write by setting EEPE 
            }
    //      else Serial.println("EQD");      // test if data ptr and EEmem equal print msg
            ptr++;
            EEaddress++;
            n--;
        } 
    }
    
    //******************************************************************************************//
    // This function is independent of data type. It copies all standard types but also string  //
    // and structs. This function reads n bytes from EEaddress in EEmemory and writes these to  //
    // pointed location ptr in RAM.                                                                 //              
    //******************************************************************************************//
    
    void EE2MEM (unsigned int EEaddress, byte *ptr, unsigned int n) {
         while (n != 0) {
                while(EECR & (1<<EEPE));       // Wait for completion of previous write
                EEAR  = EEaddress;             // Set up address register
                EECR |= (1<<EERE);             // Start eeprom read by writing EERE
                *ptr  = EEDR;                  // Assign EEDR data to memory location
                ptr++;
                EEaddress++;
                n--;
         } 
    }
    

    Examples of how to use these functions.
    Assume you want to write to EEPROM memory addresses 540 and 542

    You have the following variables :
    float            fvar1=3.1416,       fvar2;
    byte            bvar1=66,              bvar2;

    These are the calls

    MEM2EE( 540, (byte*)&bvar1, sizeof(bvar1));
    EE2MEM( 540, (byte*)&bvar2, sizeof(bvar2));
    Serial.println(bvar2);
    MEM2EE( 542, (byte*)&fvar1, sizeof(fvar1));
    EE2MEM( 542, (byte*)&fvar2, sizeof(fvar2));
    Serial.println(fvar2);

    Your can write and read any variable, array or  struct in this way to and from EEPROM memory

    Write a comment if you like it or want to contribute to this code.