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.
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
Last edited: