Breadboard Watch Using Attiny 85, DS 3231 Real Time Clock, a Push Button and the Arduino IDE

by Artful_Dabbler in Circuits > Arduino

1694 Views, 5 Favorites, 0 Comments

Breadboard Watch Using Attiny 85, DS 3231 Real Time Clock, a Push Button and the Arduino IDE

DS3231 Real Time clock with ATTiny85.
IMG_7313.jpeg

I wanted to make my own digital watch/clock using the ATTiny85 chip and the real time clock from Adafruit (the DS 3231). The starting point was to make a breadboard version which is shown below.

Supplies

IMG_7318.jpeg

This project needs the following components:

  • DS3231 Real Time Clock module from Adafruit
  • 1220 coin battery as backup for the DS3231
  • SSD1306 OLED Display I2C (I used a 128X64 and a 64x32 version)
  • Attiny85 chip
  • A push button
  • Jumper leads
  • A Sparkfun AV Programmer
  • A 5V power source

Set Up Breadboard

Watch Sch 3_bb copy.jpg

Set up the breadboard as shown in the fritzing picture.

Note the accelerometer is optional and is used in another version (here)



Download the Following Libraries

For the code, you will meed to download a specific set of libraries to make these components work with the ATTiny85.

These are:

1) u8g2.h library (available within the library manager of the Arduino IDE). This includes the library u8x8lib.h which has all the fonts suitable for the ATTiny85's memory. Tihs is because Text output only (character) device. Only fonts allowed with fit into a 8x8 pixel grid. Writes directly to the display. No buffer in the microcontroller required.

2) TinyWireM.h - this is available in the library manager of the Arduino IDE and provides the interface between the ATTiny85 and the I2C display.

3) DS3231.h for Attiny85 (download here from Joe Young on GitHub)

Note I am not an Arduino coding expert and am just learning and I therefore relied on the help of an experienced coder called KathleenSue on FIVERRs platform. I have no affiliation by the way but thought she was great when I got stuck.

Upload the Code Below and Set the Time

For the following tasks use the code below in the Arduino IDE. To load this onto the ATTiny, use the Sparkfun Tiny Programmer (here) to load the code. The hook up and instruction guide is here. It is really easy to use.

Note - in the arduino IDE - remember to pick your board (ATTiny85), clockspeed (Internal 8mhz) and Programmer (USBTinyISP) before loading. Also Burn Bootloader first so that the ATTiny85 runs at 8mhz.


/*Description:

Screen 1 when button pressed:
22:31
Fri 24 Jun


Screen 2 when button pressed again:
34c


If inactive turn off after set number of seconds (10 seconds)

The components are:

DS3231 RTC from Adafruit and 1220 backup battery
OLED Displays I2C 64x32 and 64x48
ATTiny85
Push button
Powersource

Circuit:
    Arduino        Component        
       3  ------->  button  
      A4  -------> sda DS3231 / sda SSD1306 
      A5  -------> scl DS3231 / scl SSD1306


      Attiny85        Component        
       1  ------->  button    
       0  -------> sda DS3231 / sda SSD1306 
       2  -------> scl DS3231 / scl SSD1306


Details of libraries needed:


https://github.com/olikraus/u8g2/wiki/fntlist8
https://github.com/joeyoung/arduino_usingTinyWire


 */


//Libraries
#include <Arduino.h>
#include <U8x8lib.h>
#include "DS3231.h"      // use whichever library matches clock IC
#include <avr/interrupt.h>
#include<avr/io.h>
#include <avr/sleep.h>
#include <TinyWireM.h>


#define buttonPin 1  


U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ 2, /* data=*/ 0, /* reset=*/ U8X8_PIN_NONE);      //** Digispark ATTiny85


DS3231 clk;
Time time;
Date date;
Temp temp;
byte status[5];


byte Hour=16, Minute=18,Second=50;
uint8_t Day=1,Month=5,Year=22;
uint8_t DoW=2;
uint8_t lastSecond=0;
uint8_t lastMinute=0;


uint16_t temperature=0;
uint8_t screen=0;
uint8_t timeLowPower=0;
uint8_t flagButton=0;


// Variables will change:
uint16_t accelValue=0;
bool flagAccel=0;


void setup() {


  pinMode(buttonPin,INPUT_PULLUP);
 
  TinyWireM.begin();
  
   char commline1[15]={'2','3','x','0','5','x','1','4','x','0','2'};   //12 characters - yy mm dd dw
                                                                       //this example:   22 05 22 02
   for(int i=0;i<11;i++){
    commline1[i] = tolower( commline1[i] );
   }
   
    date.yr = ( ( (commline1[0]&0xf)<<4) | (commline1[1]&0xf) );
    date.mo = ( ( (commline1[3]&0xf)<<4) | (commline1[4]&0xf) );
    date.dom = ( ( (commline1[6]&0xf)<<4) | (commline1[7]&0xf) );
    date.dow = ( ( (commline1[9]&0xf)<<4) | (commline1[10]&0xf) );
    clk.setDate( date );


   char commline[10]={'1','6','x','1','9','x','0','0'};      //9 characters - hh mm ss
                                                             //this example:  16 29 55
   for(int i=0;i<8;i++){
    commline[i] = tolower( commline[i] );
   }
    time.hr = ( ( (commline[0]&0xf)<<4) | (commline[1]&0xf) );
    time.min = ( ( (commline[3]&0xf)<<4) | (commline[4]&0xf) );
    time.sec = ( ( (commline[6]&0xf)<<4) | (commline[7]&0xf) );
    clk.setTime( time );

  u8x8.begin();
  u8x8.setPowerSave(0);
  
  flagButton=1;
  flagAccel=1;

}


void loop() {
  
  if(flagButton==1) {
    flagButton=2;
    screen=1;
    timeLowPower=0;

  }
  else if(flagButton==2) {
      readbutton();  
  }


  if(timeLowPower>=10){   
    screen=0;
 
    u8x8.clearDisplay();
     
    flagButton=0;


    sleepNow(); // sleep function called here
  }

  if(screen==1){
      getRTC();
  
    if(Second!=lastSecond){
      showOledScreenTime(time);
      showOledScreenDate(date);
      lastSecond=Second;
      timeLowPower++;
    }
  }
  else if(screen==2){
    getRTC();


    if(Second!=lastSecond){
      showOledScreenTemp();
      lastSecond=Second;
      timeLowPower++;
    }
  }
    
}


void getRTC(){
 
  TinyWireM.begin( );
  
  byte err = clk.getDate( date );
  Year=date.yr;
  Month=date.mo;
  Day=date.dom;
  DoW=date.dow;


  err = clk.getTime( time );
  Hour=time.hr;
  Minute=time.min;
  Second=time.sec;


  err = clk.getTemp( temp );
  temperature=temp.tmp;

}

void showOledScreenTime(Time &tim){


    u8x8.setFont(u8x8_font_profont29_2x3_n);
    u8x8.setCursor(1,1);
    u8x8.write( ( ( tim.hr & 0xf0 )>>4 ) + 0x30 );
    u8x8.write( ( tim.hr & 0x0f ) + 0x30 );
    u8x8.print(":");


    u8x8.write( ( ( tim.min & 0xf0 )>>4 ) + 0x30 );
    u8x8.write( ( tim.min & 0x0f ) + 0x30 );


    u8x8.setFont(u8x8_font_amstrad_cpc_extended_r);
    u8x8.setCursor(12,1);
    u8x8.write( ( ( tim.sec & 0xf0 )>>4 ) + 0x30 );
    u8x8.write( ( tim.sec & 0x0f ) + 0x30 );
       
}


void showOledScreenDate(Date &dat){


    u8x8.setFont(u8x8_font_amstrad_cpc_extended_r);
    u8x8.setCursor(1,5);
    byte d=dat.dow;
    
    if(d==0) u8x8.print("Mon ");
    else if(d==1) u8x8.print("Tue ");
    else if(d==2) u8x8.print("Wed ");
    else if(d==3) u8x8.print("Thu ");
    else if(d==4) u8x8.print("Fri ");
    else if(d==5) u8x8.print("Sat ");
    else if(d==6) u8x8.print("Sun ");


    u8x8.write( ( ( dat.dom&0x30 )>>4 ) + 0x30 );
    u8x8.write( ( ( dat.dom&0x0f ) + 0x30 ) );


    byte m=(((dat.mo&0x10)>>4)*10 ) + ( date.mo&0x0f);
    
    if(m==0) u8x8.print(" Jan");
    else if(m==1) u8x8.print(" Feb");
    else if(m==2) u8x8.print(" Mar");
    else if(m==3) u8x8.print(" Apr");
    else if(m==4) u8x8.print(" May");
    else if(m==5) u8x8.print(" Jun");
    else if(m==6) u8x8.print(" Jul");
    else if(m==7) u8x8.print(" Aug");
    else if(m==8) u8x8.print(" Sep");
    else if(m==9) u8x8.print(" Oct");
    else if(m==10) u8x8.print(" Nov");
    else if(m==11) u8x8.print(" Dec");
 
}


void showOledScreenTemp(){
    u8x8.setFont(u8x8_font_amstrad_cpc_extended_r);
    u8x8.setCursor(1,1);
    u8x8.print ("Temp (C):");


    u8x8.setFont(u8x8_font_profont29_2x3_n);
    u8x8.setCursor(1,4);
    u8x8.print(temperature);
    
}


void readbutton(){
 
      if (digitalRead(buttonPin) == LOW ) {
           u8x8.clearDisplay();
           
           if(screen==0 ){  
             screen=1; 
             
             timeLowPower=0;
            }
            else if(screen==1){ 
             screen=2;
                
             timeLowPower=0;
           }
            else if(screen==2){ 
             screen=1; 
          
             timeLowPower=0;
           }
          delay(200);
      }


}

void sleepNow(){         
 
    GIMSK |= _BV(PCIE);                     // Enable Pin Change Interrupts
    PCMSK |= _BV(PCINT1);                   // Use PB1 as interrupt pin
    ADCSRA &= ~_BV(ADEN);                   // ADC off
    set_sleep_mode(SLEEP_MODE_PWR_DOWN);    // replaces above statement


    sleep_enable();                         // Sets the Sleep Enable bit in the MCUCR Register (SE BIT)
    sei();                                  // Enable interrupts
    sleep_cpu();                            // sleep


    cli();                                  // Disable interrupts
    PCMSK &= ~_BV(PCINT1);                  // Turn off PB3 as interrupt pin
    sleep_disable();                        // Clear SE bit
    ADCSRA |= _BV(ADEN);                    // ADC on


  
    flagButton=1;
    sei();                                  // Enable interrupts
 } // sleep

ISR(PCINT0_vect) {
    // This is called when the interrupt occurs
  
    flagButton=1;
 }

   

Follow these steps:

1) Remove the backup battery from the Adafruit DS3231 so that it recognises that the time is about to be set.

2) Set a correct time and date for a few minutes in the future in the lines of code shown below and then load the code to the ATTiny 85 (full code is above.)

char commline1[15]={'2','3','x','0','5','x','1','4','x','0','2'};   //12 characters - yy mm dd dw
//this example:   22 05 22 02

Note -

Month 0 is Jan and Month 11 is Dec

Day 0 is Monday and Day 6 Sunday

char commline[10]={'1','6','x','1','9','x','0','0'};      //9 characters - hh mm ss
                                                             //this example:  16 29 55


3) At exactly the correct time put the ATTiny85 back onto the breadboard and power up. Reinsert the battery in to the back of the DS3231. It will now hold the time even if it loses power.

4) Finally rerun the code on the ATTiny 85 but with the section showed below greyed out using /* and */ and return the chip to the board. The time should now be set and the chip will not try to reset the time at each moment it is repowered. See the attached picture.

/*  
  TinyWireM.begin();
  
   char commline1[15]={'2','3','x','0','5','x','1','4','x','0','2'};   //12 characters - yy mm dd dw
                                                                       //this example:   22 05 22 02
   for(int i=0;i<11;i++){
    commline1[i] = tolower( commline1[i] );
   }
   
    date.yr = ( ( (commline1[0]&0xf)<<4) | (commline1[1]&0xf) );
    date.mo = ( ( (commline1[3]&0xf)<<4) | (commline1[4]&0xf) );
    date.dom = ( ( (commline1[6]&0xf)<<4) | (commline1[7]&0xf) );
    date.dow = ( ( (commline1[9]&0xf)<<4) | (commline1[10]&0xf) );
    clk.setDate( date );


   char commline[10]={'1','6','x','1','9','x','0','0'};      //9 characters - hh mm ss
                                                             //this example:  16 29 55
   for(int i=0;i<8;i++){
    commline[i] = tolower( commline[i] );
   }
    time.hr = ( ( (commline[0]&0xf)<<4) | (commline[1]&0xf) );
    time.min = ( ( (commline[3]&0xf)<<4) | (commline[4]&0xf) );
    time.sec = ( ( (commline[6]&0xf)<<4) | (commline[7]&0xf) );
    clk.setTime( time );

*/

Adjust the Code for Your Requirements

If needed, you can adjust the code for your requirements. In particular if you have a different OLED display type and if you want to change the font. See below:


1) Set the correct configuration depending on the display type - there are numerous options but the two I used are this for the 128x64. A complete list is here


U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ 2, /* data=*/ 0, /* reset=*/ U8X8_PIN_NONE); 

Change this on this line of code in the define section:

#define buttonPin 1  
U8X8_SSD1306_128X64_NONAME_SW_I2C u8x8(/* clock=*/ 2, /* data=*/ 0, /* reset=*/ U8X8_PIN_NONE);


2) Choose your font. I chose the two below. Full list is here. Please note that some will require too much memory for the chip with the code but you can play around. You will need to change the font code in all places that these lines show.

u8x8.setFont(u8x8_font_profont29_2x3_n);
u8x8.setFont(u8x8_font_amstrad_cpc_extended_r);