Share My Creation Talking Clock

The clock uses ATMEGA328 internal timers to get 1 minute time base which is as accurate as the 16MHz crystal. The code works for Arduino Nano and Uno only. Code has inline C for setting the timers, and wire lib for the OLED.
The DF Player plays many sound formats. The talking clock can be used in other languages. In languages that numbers are read in different order, like Arabic and Hebrew, section of the code has to be adapted and number of tracks changed too. MP3 files in English are included.
The clock reads the tracks from the SD card in the order they are placed in the root directory, not by name of track, the tracks have to be copied one at a time.
Volume pushbuttons are optional.
Display is OLED 0.96", SSD1306, I2C.
DF Player is a mini MP3 player from Ebay.
B4X:
Sub Process_Globals
    Public Serial1 As Serial
    Private AStream As AsyncStreams
  
    Private master As WireMaster
    Private const addr As Byte = 0x3C
    Private font() As Byte = Array As Byte(0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x72, 0x49, 0x49, 0x49, 0x46, 0x21, 0x41, 0x49, 0x4D, 0x33,0x18, 0x14, 0x12, 0x7F, 0x10,0x27, 0x45, 0x45, 0x45, 0x39,0x3C, 0x4A, 0x49, 0x49, 0x31,0x41, 0x21, 0x11, 0x09, 0x07,0x36, 0x49, 0x49, 0x49, 0x36,0x46, 0x49, 0x49, 0x29, 0x1E,0x00, 0x36, 0x36, 0x00, 0x00,0x7E, 0x11, 0x11, 0x11, 0x7E,0x7F, 0x09, 0x09, 0x09, 0x06,0x7F, 0x02, 0x04, 0x02, 0x7F)    ':,A,P,M '11,12,13,14
    Private tmr1 As Timer
    Private busy As Pin
    Private minute=0, hour=12 As Byte
    Private pulse As Byte
    Private minSW, hourSW, talkSW, volUp, volDown As Pin
End Sub

Private Sub AppStart
    Serial1.Initialize(9600)
    AStream.Initialize(Serial1.Stream,Null , Null)    '"Astream_NewData"
    tmr1.Initialize("tmr1_Tick", 500)
    tmr1.Enabled = True
    busy.Initialize(2, busy.MODE_INPUT)
    minSW.Initialize(6, minSW.MODE_INPUT_PULLUP)
    hourSW.Initialize(7, hourSW.MODE_INPUT_PULLUP)
    talkSW.Initialize(8, talkSW.MODE_INPUT_PULLUP)
    talkSW.AddListener("talkSW_StateChanged")
    volUp.Initialize(9, volUp.MODE_INPUT_PULLUP)
    volDown.Initialize(10, volDown.MODE_INPUT_PULLUP)
    master.Initialize
    oled_init
    RunNative ("set_clock",Null)
    clr_screen

End Sub

Sub talkSW_StateChanged (State As Boolean)
    If State=False Then
        play_df
    End If
End Sub

Sub tmr1_Tick
    Dim volu() As Byte = Array As Byte(0x7E,0xff,0x06,0x04,0x00,0x00,0x00,0xEF)    'volume up command
    Dim vold() As Byte = Array As Byte(0x7E,0xff,0x06,0x05,0x00,0x00,0x00,0xEF)    'volume down command
  
    If volUp.DigitalRead=False Then AStream.Write(volu)
    If volDown.DigitalRead=False Then AStream.Write(vold)
  
    If minSW.DigitalRead=False Then
        minute=minute+1
        If minute>59 Then minute=0
    End If
    If hourSW.DigitalRead=False Then
        hour=hour+1
        If hour>24 Then hour=0
    End If
  
    RunNative ("clock",Null)
    If pulse=1 Then
        minute=minute+1
        pulse=0
    End If

    If minute>59 Then
        minute=0
        hour=hour+1
        play_df    'comment line to stop hourly talk
    End If
    If hour>23 Then hour=0
        'update oled
    drawChar2(minute Mod 10,1,5)
    drawChar2(minute/10,1,4)
    drawChar2(10,1,3)
    drawChar2(hour Mod 10,1,2)
    drawChar2(hour/10,1,1)
  
End Sub

Sub play_df
        'select track 
    If hour>20 Then
        cmnd(21)
        cmnd(hour - 19)
        Else
        cmnd(hour + 1)
    End If
    cmnd(25)    'hour

    If minute<21 Then
        cmnd(minute + 1)    'minutes
    else If minute>20 And minute<30 Then
        cmnd(21)    '20
        cmnd(minute - 19)    'minutes
    else If minute=30 Then
    cmnd(22)    '30
    else If minute>30 And minute<40 Then
        cmnd(22)    '30
        cmnd(minute - 29)    'minutes
    else If minute=40 Then
    cmnd(23)    '40
    else If minute>40 And minute<50 Then
        cmnd(23)    '40
        cmnd(minute - 39)    'minutes
    else If minute=50 Then
    cmnd(24)    '50
    else If minute>50 And minute<60 Then
        cmnd(24)    '50
        cmnd(minute - 49)    'minutes
        Else
    End If 
  
    cmnd(26)    'minutes
    cmnd(27)    'message

End Sub

Sub cmnd (track As Byte)
    Dim ps() As Byte = Array As Byte(0x7E,0xff,0x06,0x03,0x00,0x00,track,0xEF)
      
    Do While busy.DigitalRead=False    'wait For prev track To complete, !busy
    Loop
    AStream.Write(ps)    'a request To play a track, without CRC
   ' Delay(100)
End Sub

    'size 1 chars
Sub drawChar(fig As Byte, y As Byte, x As Byte)
    Dim fb(6), i As Byte

    command(0x21)    'col addr
    command(7 * x)    'col start
    command(7 * x + 4)    'col End
    command(0x22)
    command(y)    ' Page start
    command(y+1)    ' Page End
    For i=0 To 4
        fb(i+1)=(font(5*fig+i))
    Next
    fb(0)=0x40
    master.WriteTo(addr,fb)
End Sub

   'size 2 chars
Sub drawChar2(fig As Byte, y As Byte, x As Byte)
    Dim i, line As Byte
    Dim btm, top As Byte
       Dim fb(31) As Byte
  
    command(0x20)    ' vert mode
    command(0x01)

    command(0x21)    'col addr
    command(19 * x)    'col start
    command(19 * x + 14)    'col End
    command(0x22)
    command(y)    ' Page start
    command(y+1)    ' Page End

    For    i=0 To 4
        line=font(5*fig+i)
        btm=0
        top=0

        If line Mod 2=1 Then top=top+12
        line=line/2
        If line Mod 2=1 Then top=top+48
        line=line/2
        If line Mod 2=1 Then top=top+192
        line=line/2
        If line Mod 2=1 Then btm=btm+3
        line=line/2
        If line Mod 2=1 Then btm=btm+12
        line=line/2
        If line Mod 2=1 Then btm=btm+48
        line=line/2
        If line Mod 2=1 Then btm=btm+192
      
        fb(1+i*6)=top
        fb(2+i*6)=btm
        fb(3+i*6)=top
        fb(4+i*6)=btm
        fb(5+i*6)=top
        fb(6+i*6)=btm
     Next
    fb(0)=0x40
    master.WriteTo(addr,fb)

    command(0x20)    'horizontal mode
    command(0x00)
End Sub

Sub command(cmndO As Byte)
    Dim cb(2) As Byte
    cb(0)=0
    cb(1)=cmndO
    master.WriteTo(addr,cb)
End Sub

Sub oled_init
    command(0xAE)   'DISPLAYOFF
    command(0x8D)   '       CHARGEPUMP *
    command(0x14)    ' 0x14-pump on
    command(0x20)    '      MEMORYMODE
    command(0x0)     ' 0x0=horizontal, 0x01=vertical, 0x02=page
    command(0xA1)    '    SEGREMAP * A0/A1=top/bottom
    command(0xC8)    ' COMSCANDEC * C0/C8=left/right
    command(0xDA)     '     SETCOMPINS *
    command(0x22)   '0x22=4rows, 0x12=8rows
    command(0x81)     '    SETCONTRAST
    command(0xCF)     '0x8F
  
    command(0xAF)       '   DISPLAYON
End Sub

Sub clr_screen
    Dim x, y As Byte
    Dim c() As Byte = Array As Byte(0x40,0,0,0,0,0,0,0,0)
  
    For  y = 0 To 7
        For x = 0 To 15
            command(0x21)     'col addr
            command(8 * x) 'col start
            command(8 * x + 7)  'col End
            command(0x22)    '0x22
            command(y) ' Page start
            command(y) ' Page End
            master.WriteTo(addr,c)

        Next
    Next
End Sub

#if C
    void set_clock(B4R::Object* o)
    {
        //1000 Hz - timer2
      OCR2A =249;
      OCR2B = 125;
      TCCR2A = _BV(COM2B1) | _BV(COM2B0) | _BV(WGM21) | _BV(WGM20);  //output B in phase, fast PWM mode
      TCCR2B = _BV(WGM22) | _BV(CS22); // set prescaler To 64 And start the timer
      pinMode(3, OUTPUT);
    
          //60 sec - timer1
      TCCR1A = _BV(WGM10) | _BV(WGM11) | _BV(COM1A0); // 
      TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS11) | _BV(CS12);  // input T1, PWM mode
   }
#End if

#if C
    void clock(B4R::Object* o)
    {
          if(TIFR1 & _BV(OCF1A))   //1 minute elapsed
         {
              OCR1A = 0xEA5F;  //59999 counts = 1 minute
              TIFR1 = _BV(OCF1A);  //TMR1 interrupt reset
              b4r_main::_pulse = 1;    // Return received data from buffer
         }
    }
#End if
 

Attachments

  • talking clock.zip
    395.6 KB · Views: 56
  • talking_clock400.jpg
    talking_clock400.jpg
    33.6 KB · Views: 124
  • DFPlayer.jpg
    DFPlayer.jpg
    29.2 KB · Views: 8
  • talking_clk_df_arduino.png
    talking_clk_df_arduino.png
    9.5 KB · Views: 8
Last edited:
Top