Yet another Arduino clock... but a special one
A very easy to build buttonless clock. It uses the DCF77 signal to set the date and time. It has four different displays and the brightness can be adjusted.
Components and supplies
1
DCF77 receiver
1
1,3" OLED Display SH1106 128x64 I2C
1
Arduino Nano 33 BLE
Tools and machines
1
Soldering iron (generic)
Apps and platforms
1
Arduino IDE
Project description
Code
DCFclock_english
cpp
English version (for date)
1/******* DCF clock *******/ 2/**** english version ****/ 3 4#include <U8g2lib.h> 5#include <TimeLib.h> 6#include <Arduino_LSM9DS1.h> 7 8U8G2_SH1106_128X64_NONAME_F_HW_I2C 9 u8g2(U8G2_R0); 10 11unsigned long displayTime, motionTime; 12int previousSecond; 13enum {timeDisp, timeDateDisp, timeSecondDisp, 14 analogDisp} displayType=timeDisp; 15int brightness=20; 16 17#define redLED 22 18#define blueLED 23 19#define greenLED 24 20#define DCFPin 9 21#define DCFPowerPin 11 22#define DCFGndPin 10 23#define DCFEnablePin 8 24#define rejectionTime 700 25#define rejectPulseWidth 50 26#define DCFSplitTime 150 27#define DCFSyncTime 1200 28 29volatile bool bitAvailable=false; 30volatile int receivedBit; 31volatile bool lastBit=false; 32 33bool DCFMsg[60]; 34 35int DCFHour, DCFMinute, DCFDay, DCFDayOfWeek, 36 DCFMonth, DCFYear; 37 38time_t DCFMemo[10]; 39 40void DCFHandler(void) 41/* Interrupt routine called each time the DCF 42 signal state changes */ 43{ 44 static unsigned long leadingEdge; 45 static unsigned long fallingEdge; 46 static unsigned long pulseWidth; 47 static unsigned long lowStateWidth; 48 unsigned long flankTime=millis(); 49 byte DCFValue=digitalRead(DCFPin); 50 51 if(DCFValue==HIGH) 52 { 53 if((flankTime-leadingEdge)<rejectionTime) 54 return; 55 else 56 { 57 leadingEdge=flankTime; 58 lowStateWidth=flankTime-fallingEdge; 59 bitAvailable=true; 60 if(pulseWidth<DCFSplitTime) 61 receivedBit=0; 62 else 63 receivedBit=1; 64 if(lowStateWidth<DCFSyncTime) 65 lastBit=false; 66 else 67 lastBit=true; 68 } 69 } 70 71 else 72 if((flankTime-leadingEdge)< 73 rejectPulseWidth) 74 return; 75 else 76 { 77 pulseWidth=flankTime-leadingEdge; 78 fallingEdge=flankTime; 79 } 80} 81 82time_t makeDCFTime(void) 83{ 84 TimeElements DCFElements; 85 86 DCFElements.Second=0; 87 DCFElements.Minute=DCFMinute; 88 DCFElements.Hour=DCFHour; 89 DCFElements.Day=DCFDay; 90 DCFElements.Month=DCFMonth; 91 DCFElements.Year=DCFYear+30; 92 return makeTime(DCFElements); 93} 94 95bool decodeDCFMsg() 96/* Once a complete DCF signal has been 97 received, this function decodes it and 98 verifies its validity. */ 99{ 100 bool parity=false; 101 for(int i=21; i<=28; i++) 102 parity^=DCFMsg[i]; 103 if(parity) 104 { 105 Serial.println("Parity error (minutes)"); 106 return false; 107 } 108 parity=false; 109 for(int i=29; i<=35; i++) 110 parity^=DCFMsg[i]; 111 if(parity) 112 { 113 Serial.println("Parity error (hour)"); 114 return false; 115 } 116 parity=false; 117 for(int i=36; i<=58; i++) 118 parity^=DCFMsg[i]; 119 if(parity) 120 { 121 Serial.println("Parity error (date)"); 122 return false;; 123 } 124 DCFMinute=(DCFMsg[21]?1:0) + 125 (DCFMsg[22]?2:0) + 126 (DCFMsg[23]?4:0) + 127 (DCFMsg[24]?8:0) + 128 (DCFMsg[25]?10:0) + 129 (DCFMsg[26]?20:0) + 130 (DCFMsg[27]?40:0); 131 DCFHour= (DCFMsg[29]?1:0) + 132 (DCFMsg[30]?2:0) + 133 (DCFMsg[31]?4:0) + 134 (DCFMsg[32]?8:0) + 135 (DCFMsg[33]?10:0) + 136 (DCFMsg[34]?20:0); 137 DCFDay= (DCFMsg[36]?1:0) + 138 (DCFMsg[37]?2:0) + 139 (DCFMsg[38]?4:0) + 140 (DCFMsg[39]?8:0) + 141 (DCFMsg[40]?10:0) + 142 (DCFMsg[41]?20:0); 143 DCFDayOfWeek= 144 (DCFMsg[42]?1:0) + 145 (DCFMsg[43]?2:0) + 146 (DCFMsg[44]?4:0); 147 DCFMonth= (DCFMsg[45]?1:0) + 148 (DCFMsg[46]?2:0) + 149 (DCFMsg[47]?4:0) + 150 (DCFMsg[48]?8:0) + 151 (DCFMsg[49]?10:0); 152 DCFYear= (DCFMsg[50]?1:0) + 153 (DCFMsg[51]?2:0) + 154 (DCFMsg[52]?4:0) + 155 (DCFMsg[53]?8:0) + 156 (DCFMsg[54]?10:0) + 157 (DCFMsg[55]?20:0) + 158 (DCFMsg[56]?40:0) + 159 (DCFMsg[57]?80:0); 160 if((DCFHour>23) || (DCFMinute>60) || 161 (DCFYear>99) || (DCFMonth>12) || 162 (DCFDay>31) || (DCFDayOfWeek>7) || 163 (((DCFDayOfWeek%7)+1)!= 164 weekday(makeDCFTime()))) 165 return false; 166 Serial.print(DCFMonth); 167 Serial.print("/"); 168 Serial.print(DCFDay); 169 Serial.print("/"); 170 Serial.print(DCFYear); 171 Serial.print(" "); 172 Serial.print(DCFHour); 173 Serial.print(":"); 174 Serial.println(DCFMinute); 175 return true; 176} 177 178bool filterDCFMsg(void) 179/* This function compares the last receveid 180 time and date to the 9 previous and returns 181 true only if one of these comparisons 182 gives a result less than an hour. */ 183{ 184 for(int i=9; i>0; i--) 185 DCFMemo[i]=DCFMemo[i-1]; 186 DCFMemo[0]=makeDCFTime(); 187 188 for(int i=1; i<=9; i++) 189 if((DCFMemo[0]-DCFMemo[i])<3600) 190 return true; 191 return false; 192} 193 194void centerCursor(char * s, int y) 195/* This function sets the display cursor at 196 the place where the string s will be 197 centered horizontally and at y position 198 vertically. */ 199{ 200 u8g2.setCursor((u8g2.getDisplayWidth()- 201 u8g2.getStrWidth(s))/2, y); 202} 203 204void timeDateScreen(void) 205/* This function displays the complete date 206 and time. */ 207{ 208 char stringToDisplay[25]; 209 210 u8g2.clearBuffer(); 211 u8g2.setDrawColor(1); 212 u8g2.setFont(u8g2_font_6x13_mf); 213 switch(weekday()) 214 { 215 case 1 : 216 sprintf(stringToDisplay, "Sunday"); 217 break; 218 case 2 : 219 sprintf(stringToDisplay, "Monday"); 220 break; 221 case 3 : 222 sprintf(stringToDisplay, "Tuesday"); 223 break; 224 case 4 : 225 sprintf(stringToDisplay, "Wednesday"); 226 break; 227 case 5 : 228 sprintf(stringToDisplay, "Thursday"); 229 break; 230 case 6 : 231 sprintf(stringToDisplay, "Friday"); 232 break; 233 case 7 : 234 sprintf(stringToDisplay, "Saturday"); 235 break; 236 } 237 centerCursor(stringToDisplay, 45); 238 u8g2.print(stringToDisplay); 239 240 u8g2.setFont(u8g2_font_9x15B_mf); 241 sprintf(stringToDisplay, "%d", day()); 242 centerCursor(stringToDisplay, 29); 243 u8g2.print(stringToDisplay); 244 u8g2.setFont(u8g2_font_6x13_mf); 245 switch(month()) 246 { 247 case 1 : 248 sprintf(stringToDisplay, "January"); 249 break; 250 case 2 : 251 sprintf(stringToDisplay, "February"); 252 break; 253 case 3 : 254 sprintf(stringToDisplay, "March"); 255 break; 256 case 4 : 257 sprintf(stringToDisplay, "April"); 258 break; 259 case 5 : 260 sprintf(stringToDisplay, "May"); 261 break; 262 case 6 : 263 sprintf(stringToDisplay, "June"); 264 break; 265 case 7 : 266 sprintf(stringToDisplay, "July"); 267 break; 268 case 8 : 269 sprintf(stringToDisplay, "August"); 270 break; 271 case 9 : 272 sprintf(stringToDisplay, "September"); 273 break; 274 case 10 : 275 sprintf(stringToDisplay, "October"); 276 break; 277 case 11 : 278 sprintf(stringToDisplay, "November"); 279 break; 280 case 12 : 281 sprintf(stringToDisplay, "December"); 282 break; 283 } 284 sprintf(stringToDisplay, "%s %d", 285 stringToDisplay, year()); 286 centerCursor(stringToDisplay, 10); 287 u8g2.print(stringToDisplay); 288 289 u8g2.setFont(u8g2_font_9x15B_mf); 290 sprintf(stringToDisplay, "%d:%02d:%02d", 291 hour(), minute(), second()); 292 centerCursor(stringToDisplay, 63); 293 u8g2.print(stringToDisplay); 294 u8g2.sendBuffer(); 295} 296 297void timeScreen(bool column) 298/* This functions displays current time. The 299 boolean "column" is used to make the 300 column blink between the hours and the 301 minutes. */ 302{ 303 char stringToDisplay[10]; 304 305 u8g2.clearBuffer(); 306 u8g2.setFont(u8g2_font_fur35_tr); 307 if(column) 308 sprintf(stringToDisplay, "%d:%02d", hour(), 309 minute()); 310 else 311 sprintf(stringToDisplay, "%d.%02d", hour(), 312 minute()); 313 centerCursor(stringToDisplay, 50); 314 u8g2.print(stringToDisplay); 315 u8g2.sendBuffer(); 316} 317 318void timeSecondScreen(void) 319/* This function draws a digital clock on a 320 vertical screen. The secnds are displayed 321 at the bottom of the screen. */ 322{ 323 char stringToDisplay[5]; 324 325 u8g2.clearBuffer(); 326 u8g2.setFont(u8g2_font_fur35_tr); 327 sprintf(stringToDisplay, "%02d", hour()); 328 centerCursor(stringToDisplay, 45); 329 u8g2.print(stringToDisplay); 330 sprintf(stringToDisplay, "%02d", minute()); 331 centerCursor(stringToDisplay, 90); 332 u8g2.print(stringToDisplay); 333 u8g2.setFont(u8g2_font_fur20_tr); 334 sprintf(stringToDisplay, "%02d", second()); 335 centerCursor(stringToDisplay, 120); 336 u8g2.print(stringToDisplay); 337 u8g2.sendBuffer(); 338} 339 340void analogScreen(void) 341/* This function draws an analog circular 342 clock. */ 343{ 344 float hourAngle, minuteAngle, secondAngle; 345 const int hourHandLength=20; 346 const int minuteHandLength=28; 347 const int secondHandLength=28; 348 const int radius=29; 349 int centerH, centerV; 350 const int hourDots[]= 351 {1, 2, 4, 5, 7, 8, 10, 11}; 352 353 centerH=u8g2.getDisplayWidth()/2; 354 centerV=u8g2.getDisplayHeight()/2; 355 u8g2.clearBuffer(); 356 u8g2.setFont(u8g2_font_9x15B_tr); 357 u8g2.setFontPosCenter(); 358 u8g2.drawStr(centerH-radius-4, centerV+2, 359 "9"); 360 u8g2.drawStr(centerH+radius-4, centerV+2, 361 "3"); 362 u8g2.drawStr(centerH-4, centerV+radius, 363 "6"); 364 u8g2.drawStr(centerH-9, centerV-radius+4, 365 "12"); 366 u8g2.setFontPosBaseline(); 367 u8g2.drawDisc(centerH, centerV, 3); 368 369 for(int i=0; i<=7; i++) 370 u8g2.drawDisc( 371 centerH+radius*sin(hourDots[i]*PI/6), 372 centerV-radius*cos(hourDots[i]*PI/6), 373 2); 374 375 secondAngle=second()*2*PI/60; 376 minuteAngle=(minute()*60+second())*2*PI/3600; 377 hourAngle=(hour()*60+minute())*2*PI/720; 378 379 u8g2.drawLine(centerH, centerV, 380 centerH+ 381 (int)secondHandLength*sin(secondAngle), 382 centerV- 383 (int)secondHandLength*cos(secondAngle)); 384 385 u8g2.drawLine(centerH+ 386 (int)3*sin(minuteAngle+PI/2), 387 centerV- 388 (int)3*cos(minuteAngle+PI/2), 389 centerH+ 390 (int)minuteHandLength* 391 sin(minuteAngle), 392 centerV- 393 (int)minuteHandLength* 394 cos(minuteAngle)); 395 u8g2.drawLine(centerH+ 396 (int)3*sin(minuteAngle-PI/2), 397 centerV- 398 (int)3*cos(minuteAngle-PI/2), 399 centerH+ 400 (int)minuteHandLength* 401 sin(minuteAngle), 402 centerV- 403 (int)minuteHandLength* 404 cos(minuteAngle)); 405 406 u8g2.drawTriangle(centerH+ 407 (int)3*sin(hourAngle+PI/2), 408 centerV- 409 (int)3*cos(hourAngle+PI/2), 410 centerH+ 411 (int)3*sin(hourAngle-PI/2), 412 centerV- 413 (int)3*cos(hourAngle-PI/2), 414 centerH+ 415 (int)hourHandLength* 416 sin(hourAngle), 417 centerV- 418 (int)hourHandLength* 419 cos(hourAngle)); 420 421 u8g2.sendBuffer(); 422} 423 424void motionDetection(void) 425/* This function detects the position of the 426 box. According to the face on which it 427 is placed, one of the four clock is 428 displayed. 429 If the box is tilted forward, the 430 brighness increases, if it is tilted 431 backwards, the brightness decreases. */ 432{ 433 float x, y, z; 434 435 if (IMU.accelerationAvailable()) 436 { 437 IMU.readAcceleration(x, y, z); 438 if(z<-0.5) 439 { 440 u8g2.setDisplayRotation(U8G2_R0); 441 displayType=timeDateDisp; 442 } 443 else if(z>0.5) 444 { 445 u8g2.setDisplayRotation(U8G2_R2); 446 displayType=timeDisp; 447 } 448 if(y<-0.5) 449 { 450 u8g2.setDisplayRotation(U8G2_R3); 451 displayType=analogDisp; 452 } 453 else if(y>0.5) 454 { 455 u8g2.setDisplayRotation(U8G2_R1); 456 displayType=timeSecondDisp; 457 } 458 if((x<-0.2) && (brightness<255)) 459 { 460 brightness++; 461 u8g2.setContrast(brightness); 462 } 463 if((x>0.2) && (brightness>0)) 464 { 465 brightness--; 466 u8g2.setContrast(brightness); 467 } 468 } 469} 470 471void setup() 472{ 473 pinMode(DCFPowerPin, OUTPUT); 474 pinMode(DCFGndPin, OUTPUT); 475 pinMode(DCFEnablePin, OUTPUT); 476 pinMode(LED_BUILTIN, OUTPUT); 477 pinMode(redLED, OUTPUT); 478 pinMode(greenLED, OUTPUT); 479 pinMode(blueLED, OUTPUT); 480 digitalWrite(DCFGndPin, LOW); 481 digitalWrite(DCFEnablePin, LOW); 482 digitalWrite(DCFPowerPin, HIGH); 483 digitalWrite(redLED, HIGH); 484 digitalWrite(greenLED, HIGH); 485 digitalWrite(blueLED, HIGH); 486 Serial.begin(9600); 487 attachInterrupt(digitalPinToInterrupt 488 (DCFPin), DCFHandler, CHANGE); 489 u8g2.begin(); 490 u8g2.setFont(u8g2_font_6x13_mf); 491 u8g2.setContrast(brightness); 492 setTime(0); 493 displayTime=millis(); 494 motionTime=millis(); 495 IMU.begin(); 496} 497 498void loop() 499{ 500 static int cpt=0; 501 502 if(bitAvailable) 503 { 504 analogWrite(LED_PWR, brightness/2); 505 Serial.print(receivedBit); 506 if((cpt==20) || (cpt==27) || (cpt==28) || 507 (cpt==34) || (cpt==35) || (cpt==41) || 508 (cpt==44) || (cpt==49) || (cpt==57)) 509 Serial.print(" "); 510 if(cpt<60) 511 DCFMsg[cpt++]=receivedBit; 512 if(lastBit) 513 { 514 Serial.println(); 515 cpt=0; 516 if(decodeDCFMsg()) 517 { 518 if(filterDCFMsg()) 519 { 520 setTime(DCFHour, DCFMinute, 0, 521 DCFDay, DCFMonth, DCFYear); 522 analogWrite(LED_PWR, 0); 523 } 524 } 525 Serial.println("0-------------------1 " 526 " min P hour P " 527 " MDay WD month " 528 " year P"); 529 } 530 bitAvailable=false; 531 analogWrite(greenLED, 255- 532 cpt*brightness/2/60); 533 analogWrite(redLED, 255- 534 (60-cpt)*brightness/2/60); 535 analogWrite(LED_BUILTIN, brightness/2); 536 delay(10); 537 analogWrite(LED_BUILTIN, 0); 538 } 539 if(second()!=previousSecond) 540 { 541 previousSecond=second(); 542 displayTime=millis(); 543 switch(displayType) 544 { 545 case timeDisp : 546 timeScreen(true); 547 break; 548 case timeDateDisp : 549 timeDateScreen(); 550 break; 551 case timeSecondDisp : 552 timeSecondScreen(); 553 break; 554 case analogDisp: 555 analogScreen(); 556 break; 557 } 558 } 559 if(((millis()-displayTime>520) || 560 (millis()<displayTime)) && 561 (displayType==timeDisp)) 562 { 563 displayTime=millis(); 564 timeScreen(false); 565 } 566 if((millis()-motionTime)>20) 567 { 568 motionTime=millis(); 569 motionDetection(); 570 } 571}
DCFclock_french
cpp
French version (for date)
1/******* DCF clock ********/ 2/***** french version *****/ 3 4#include <U8g2lib.h> 5#include <TimeLib.h> 6#include <Arduino_LSM9DS1.h> 7 8U8G2_SH1106_128X64_NONAME_F_HW_I2C 9 u8g2(U8G2_R0); 10 11unsigned long displayTime, motionTime; 12int previousSecond; 13enum {timeDisp, timeDateDisp, timeSecondDisp, 14 analogDisp} displayType=timeDisp; 15int brightness=20; 16 17#define redLED 22 18#define blueLED 23 19#define greenLED 24 20#define DCFPin 9 21#define DCFPowerPin 11 22#define DCFGndPin 10 23#define DCFEnablePin 8 24#define rejectionTime 700 25#define rejectPulseWidth 50 26#define DCFSplitTime 150 27#define DCFSyncTime 1200 28 29volatile bool bitAvailable=false; 30volatile int receivedBit; 31volatile bool lastBit=false; 32 33bool DCFMsg[60]; 34 35int DCFHour, DCFMinute, DCFDay, DCFDayOfWeek, 36 DCFMonth, DCFYear; 37 38time_t DCFMemo[10]; 39 40void DCFHandler(void) 41/* Interrupt routine called each time the DCF 42 signal state changes */ 43{ 44 static unsigned long leadingEdge; 45 static unsigned long fallingEdge; 46 static unsigned long pulseWidth; 47 static unsigned long lowStateWidth; 48 unsigned long flankTime=millis(); 49 byte DCFValue=digitalRead(DCFPin); 50 51 if(DCFValue==HIGH) 52 { 53 if((flankTime-leadingEdge)<rejectionTime) 54 return; 55 else 56 { 57 leadingEdge=flankTime; 58 lowStateWidth=flankTime-fallingEdge; 59 bitAvailable=true; 60 if(pulseWidth<DCFSplitTime) 61 receivedBit=0; 62 else 63 receivedBit=1; 64 if(lowStateWidth<DCFSyncTime) 65 lastBit=false; 66 else 67 lastBit=true; 68 } 69 } 70 71 else 72 if((flankTime-leadingEdge)< 73 rejectPulseWidth) 74 return; 75 else 76 { 77 pulseWidth=flankTime-leadingEdge; 78 fallingEdge=flankTime; 79 } 80} 81 82time_t makeDCFTime(void) 83{ 84 TimeElements DCFElements; 85 86 DCFElements.Second=0; 87 DCFElements.Minute=DCFMinute; 88 DCFElements.Hour=DCFHour; 89 DCFElements.Day=DCFDay; 90 DCFElements.Month=DCFMonth; 91 DCFElements.Year=DCFYear+30; 92 return makeTime(DCFElements); 93} 94 95bool decodeDCFMsg() 96/* Once a complete DCF signal has been 97 received, this function decodes it and 98 verifies its validity. */ 99{ 100 bool parity=false; 101 for(int i=21; i<=28; i++) 102 parity^=DCFMsg[i]; 103 if(parity) 104 { 105 Serial.println("Parity error (minutes)"); 106 return false; 107 } 108 parity=false; 109 for(int i=29; i<=35; i++) 110 parity^=DCFMsg[i]; 111 if(parity) 112 { 113 Serial.println("Parity error (hour)"); 114 return false; 115 } 116 parity=false; 117 for(int i=36; i<=58; i++) 118 parity^=DCFMsg[i]; 119 if(parity) 120 { 121 Serial.println("Parity error (date)"); 122 return false;; 123 } 124 DCFMinute=(DCFMsg[21]?1:0) + 125 (DCFMsg[22]?2:0) + 126 (DCFMsg[23]?4:0) + 127 (DCFMsg[24]?8:0) + 128 (DCFMsg[25]?10:0) + 129 (DCFMsg[26]?20:0) + 130 (DCFMsg[27]?40:0); 131 DCFHour= (DCFMsg[29]?1:0) + 132 (DCFMsg[30]?2:0) + 133 (DCFMsg[31]?4:0) + 134 (DCFMsg[32]?8:0) + 135 (DCFMsg[33]?10:0) + 136 (DCFMsg[34]?20:0); 137 DCFDay= (DCFMsg[36]?1:0) + 138 (DCFMsg[37]?2:0) + 139 (DCFMsg[38]?4:0) + 140 (DCFMsg[39]?8:0) + 141 (DCFMsg[40]?10:0) + 142 (DCFMsg[41]?20:0); 143 DCFDayOfWeek= 144 (DCFMsg[42]?1:0) + 145 (DCFMsg[43]?2:0) + 146 (DCFMsg[44]?4:0); 147 DCFMonth= (DCFMsg[45]?1:0) + 148 (DCFMsg[46]?2:0) + 149 (DCFMsg[47]?4:0) + 150 (DCFMsg[48]?8:0) + 151 (DCFMsg[49]?10:0); 152 DCFYear= (DCFMsg[50]?1:0) + 153 (DCFMsg[51]?2:0) + 154 (DCFMsg[52]?4:0) + 155 (DCFMsg[53]?8:0) + 156 (DCFMsg[54]?10:0) + 157 (DCFMsg[55]?20:0) + 158 (DCFMsg[56]?40:0) + 159 (DCFMsg[57]?80:0); 160 if((DCFHour>23) || (DCFMinute>60) || 161 (DCFYear>99) || (DCFMonth>12) || 162 (DCFDay>31) || (DCFDayOfWeek>7) || 163 (((DCFDayOfWeek%7)+1)!= 164 weekday(makeDCFTime()))) 165 return false; 166 Serial.print(DCFDay); 167 Serial.print("/"); 168 Serial.print(DCFMonth); 169 Serial.print("/"); 170 Serial.print(DCFYear); 171 Serial.print(" "); 172 Serial.print(DCFHour); 173 Serial.print(":"); 174 Serial.println(DCFMinute); 175 return true; 176} 177 178bool filterDCFMsg(void) 179/* This function compares the last receveid 180 time and date to the 9 previous and returns 181 true only if one of these comparisons 182 gives a result less than an hour. */ 183{ 184 for(int i=9; i>0; i--) 185 DCFMemo[i]=DCFMemo[i-1]; 186 DCFMemo[0]=makeDCFTime(); 187 188 for(int i=1; i<=9; i++) 189 if((DCFMemo[0]-DCFMemo[i])<3600) 190 return true; 191 return false; 192} 193 194void centerCursor(char * s, int y) 195/* This function sets the display cursor at 196 the place where the string s will be 197 centered horizontally and at y position 198 vertically. */ 199{ 200 u8g2.setCursor((u8g2.getDisplayWidth()- 201 u8g2.getStrWidth(s))/2, y); 202} 203 204void timeDateScreen(void) 205/* This function displays the complete date 206 and time. */ 207{ 208 char stringToDisplay[25]; 209 210 u8g2.clearBuffer(); 211 u8g2.setDrawColor(1); 212 u8g2.setFont(u8g2_font_6x13_mf); 213 switch(weekday()) 214 { 215 case 1 : 216 sprintf(stringToDisplay, "Dimanche"); 217 break; 218 case 2 : 219 sprintf(stringToDisplay, "Lundi"); 220 break; 221 case 3 : 222 sprintf(stringToDisplay, "Mardi"); 223 break; 224 case 4 : 225 sprintf(stringToDisplay, "Mercredi"); 226 break; 227 case 5 : 228 sprintf(stringToDisplay, "Jeudi"); 229 break; 230 case 6 : 231 sprintf(stringToDisplay, "Vendredi"); 232 break; 233 case 7 : 234 sprintf(stringToDisplay, "Samedi"); 235 break; 236 } 237 centerCursor(stringToDisplay, 10); 238 u8g2.print(stringToDisplay); 239 240 u8g2.setFont(u8g2_font_9x15B_mf); 241 sprintf(stringToDisplay, "%d", day()); 242 centerCursor(stringToDisplay, 29); 243 u8g2.print(stringToDisplay); 244 u8g2.setFont(u8g2_font_6x13_mf); 245 switch(month()) 246 { 247 case 1 : 248 sprintf(stringToDisplay, "janvier"); 249 break; 250 case 2 : 251 sprintf(stringToDisplay, "f%cvrier", 252 char(233)); 253 break; 254 case 3 : 255 sprintf(stringToDisplay, "mars"); 256 break; 257 case 4 : 258 sprintf(stringToDisplay, "avril"); 259 break; 260 case 5 : 261 sprintf(stringToDisplay, "mai"); 262 break; 263 case 6 : 264 sprintf(stringToDisplay, "juin"); 265 break; 266 case 7 : 267 sprintf(stringToDisplay, "juillet"); 268 break; 269 case 8 : 270 sprintf(stringToDisplay, "ao%ct", 271 char(251)); 272 break; 273 case 9 : 274 sprintf(stringToDisplay, "septembre"); 275 break; 276 case 10 : 277 sprintf(stringToDisplay, "octobre"); 278 break; 279 case 11 : 280 sprintf(stringToDisplay, "novembre"); 281 break; 282 case 12 : 283 sprintf(stringToDisplay, "d%ccembre", 284 char(233)); 285 break; 286 } 287 sprintf(stringToDisplay, "%s %d", 288 stringToDisplay, year()); 289 centerCursor(stringToDisplay, 45); 290 u8g2.print(stringToDisplay); 291 292 u8g2.setFont(u8g2_font_9x15B_mf); 293 sprintf(stringToDisplay, "%d:%02d:%02d", 294 hour(), minute(), second()); 295 centerCursor(stringToDisplay, 63); 296 u8g2.print(stringToDisplay); 297 u8g2.sendBuffer(); 298} 299 300void timeScreen(bool column) 301/* This functions displays current time. The 302 boolean "column" is used to make the 303 column blink between the hours and the 304 minutes. */ 305{ 306 char stringToDisplay[10]; 307 308 u8g2.clearBuffer(); 309 u8g2.setFont(u8g2_font_fur35_tr); 310 if(column) 311 sprintf(stringToDisplay, "%d:%02d", hour(), 312 minute()); 313 else 314 sprintf(stringToDisplay, "%d.%02d", hour(), 315 minute()); 316 centerCursor(stringToDisplay, 50); 317 u8g2.print(stringToDisplay); 318 u8g2.sendBuffer(); 319} 320 321void timeSecondScreen(void) 322/* This function draws a digital clock on a 323 vertical screen. The secnds are displayed 324 at the bottom of the screen. */ 325{ 326 char stringToDisplay[5]; 327 328 u8g2.clearBuffer(); 329 u8g2.setFont(u8g2_font_fur35_tr); 330 sprintf(stringToDisplay, "%02d", hour()); 331 centerCursor(stringToDisplay, 45); 332 u8g2.print(stringToDisplay); 333 sprintf(stringToDisplay, "%02d", minute()); 334 centerCursor(stringToDisplay, 90); 335 u8g2.print(stringToDisplay); 336 u8g2.setFont(u8g2_font_fur20_tr); 337 sprintf(stringToDisplay, "%02d", second()); 338 centerCursor(stringToDisplay, 120); 339 u8g2.print(stringToDisplay); 340 u8g2.sendBuffer(); 341} 342 343void analogScreen(void) 344/* This function draws an analog circular 345 clock. */ 346{ 347 float hourAngle, minuteAngle, secondAngle; 348 const int hourHandLength=20; 349 const int minuteHandLength=28; 350 const int secondHandLength=28; 351 const int radius=29; 352 int centerH, centerV; 353 const int hourDots[]= 354 {1, 2, 4, 5, 7, 8, 10, 11}; 355 356 centerH=u8g2.getDisplayWidth()/2; 357 centerV=u8g2.getDisplayHeight()/2; 358 u8g2.clearBuffer(); 359 u8g2.setFont(u8g2_font_9x15B_tr); 360 u8g2.setFontPosCenter(); 361 u8g2.drawStr(centerH-radius-4, centerV+2, 362 "9"); 363 u8g2.drawStr(centerH+radius-4, centerV+2, 364 "3"); 365 u8g2.drawStr(centerH-4, centerV+radius, 366 "6"); 367 u8g2.drawStr(centerH-9, centerV-radius+4, 368 "12"); 369 u8g2.setFontPosBaseline(); 370 u8g2.drawDisc(centerH, centerV, 3); 371 372 for(int i=0; i<=7; i++) 373 u8g2.drawDisc( 374 centerH+radius*sin(hourDots[i]*PI/6), 375 centerV-radius*cos(hourDots[i]*PI/6), 376 2); 377 378 secondAngle=second()*2*PI/60; 379 minuteAngle=(minute()*60+second())*2*PI/3600; 380 hourAngle=(hour()*60+minute())*2*PI/720; 381 382 u8g2.drawLine(centerH, centerV, 383 centerH+ 384 (int)secondHandLength*sin(secondAngle), 385 centerV- 386 (int)secondHandLength*cos(secondAngle)); 387 388 u8g2.drawLine(centerH+ 389 (int)3*sin(minuteAngle+PI/2), 390 centerV- 391 (int)3*cos(minuteAngle+PI/2), 392 centerH+ 393 (int)minuteHandLength* 394 sin(minuteAngle), 395 centerV- 396 (int)minuteHandLength* 397 cos(minuteAngle)); 398 u8g2.drawLine(centerH+ 399 (int)3*sin(minuteAngle-PI/2), 400 centerV- 401 (int)3*cos(minuteAngle-PI/2), 402 centerH+ 403 (int)minuteHandLength* 404 sin(minuteAngle), 405 centerV- 406 (int)minuteHandLength* 407 cos(minuteAngle)); 408 409 u8g2.drawTriangle(centerH+ 410 (int)3*sin(hourAngle+PI/2), 411 centerV- 412 (int)3*cos(hourAngle+PI/2), 413 centerH+ 414 (int)3*sin(hourAngle-PI/2), 415 centerV- 416 (int)3*cos(hourAngle-PI/2), 417 centerH+ 418 (int)hourHandLength* 419 sin(hourAngle), 420 centerV- 421 (int)hourHandLength* 422 cos(hourAngle)); 423 424 u8g2.sendBuffer(); 425} 426 427void motionDetection(void) 428/* This function detects the position of the 429 box. According to the face on which it 430 is placed, one of the four clock is 431 displayed. 432 If the box is tilted forward, the 433 brighness increases, if it is tilted 434 backwards, the brightness decreases. */ 435{ 436 float x, y, z; 437 438 if (IMU.accelerationAvailable()) 439 { 440 IMU.readAcceleration(x, y, z); 441 if(z<-0.5) 442 { 443 u8g2.setDisplayRotation(U8G2_R0); 444 displayType=timeDateDisp; 445 } 446 else if(z>0.5) 447 { 448 u8g2.setDisplayRotation(U8G2_R2); 449 displayType=timeDisp; 450 } 451 if(y<-0.5) 452 { 453 u8g2.setDisplayRotation(U8G2_R3); 454 displayType=analogDisp; 455 } 456 else if(y>0.5) 457 { 458 u8g2.setDisplayRotation(U8G2_R1); 459 displayType=timeSecondDisp; 460 } 461 if((x<-0.2) && (brightness<255)) 462 { 463 brightness++; 464 u8g2.setContrast(brightness); 465 } 466 if((x>0.2) && (brightness>0)) 467 { 468 brightness--; 469 u8g2.setContrast(brightness); 470 } 471 } 472} 473 474void setup() 475{ 476 pinMode(DCFPowerPin, OUTPUT); 477 pinMode(DCFGndPin, OUTPUT); 478 pinMode(DCFEnablePin, OUTPUT); 479 pinMode(LED_BUILTIN, OUTPUT); 480 pinMode(redLED, OUTPUT); 481 pinMode(greenLED, OUTPUT); 482 pinMode(blueLED, OUTPUT); 483 digitalWrite(DCFGndPin, LOW); 484 digitalWrite(DCFEnablePin, LOW); 485 digitalWrite(DCFPowerPin, HIGH); 486 digitalWrite(redLED, HIGH); 487 digitalWrite(greenLED, HIGH); 488 digitalWrite(blueLED, HIGH); 489 Serial.begin(9600); 490 attachInterrupt(digitalPinToInterrupt 491 (DCFPin), DCFHandler, CHANGE); 492 u8g2.begin(); 493 u8g2.setFont(u8g2_font_6x13_mf); 494 u8g2.setContrast(brightness); 495 setTime(0); 496 displayTime=millis(); 497 motionTime=millis(); 498 IMU.begin(); 499} 500 501void loop() 502{ 503 static int cpt=0; 504 505 if(bitAvailable) 506 { 507 analogWrite(LED_PWR, brightness/2); 508 Serial.print(receivedBit); 509 if((cpt==20) || (cpt==27) || (cpt==28) || 510 (cpt==34) || (cpt==35) || (cpt==41) || 511 (cpt==44) || (cpt==49) || (cpt==57)) 512 Serial.print(" "); 513 if(cpt<60) 514 DCFMsg[cpt++]=receivedBit; 515 if(lastBit) 516 { 517 Serial.println(); 518 cpt=0; 519 if(decodeDCFMsg()) 520 { 521 if(filterDCFMsg()) 522 { 523 setTime(DCFHour, DCFMinute, 0, 524 DCFDay, DCFMonth, DCFYear); 525 analogWrite(LED_PWR, 0); 526 } 527 } 528 Serial.println("0-------------------1 " 529 " min P hour P " 530 " MDay WD month " 531 " year P"); 532 } 533 bitAvailable=false; 534 analogWrite(greenLED, 255- 535 cpt*brightness/2/60); 536 analogWrite(redLED, 255- 537 (60-cpt)*brightness/2/60); 538 analogWrite(LED_BUILTIN, brightness/2); 539 delay(10); 540 analogWrite(LED_BUILTIN, 0); 541 } 542 if(second()!=previousSecond) 543 { 544 previousSecond=second(); 545 displayTime=millis(); 546 switch(displayType) 547 { 548 case timeDisp : 549 timeScreen(true); 550 break; 551 case timeDateDisp : 552 timeDateScreen(); 553 break; 554 case timeSecondDisp : 555 timeSecondScreen(); 556 break; 557 case analogDisp: 558 analogScreen(); 559 break; 560 } 561 } 562 if(((millis()-displayTime>520) || 563 (millis()<displayTime)) && 564 (displayType==timeDisp)) 565 { 566 displayTime=millis(); 567 timeScreen(false); 568 } 569 if((millis()-motionTime)>20) 570 { 571 motionTime=millis(); 572 motionDetection(); 573 } 574}
Downloadable files
DCF clock schematics
Only 3 components and 4 wires
Schematics.jpg

Comments
Only logged in users can leave comments