リアルタイムクロックDS3231を使用したデジタル壁時計を作成します。
使う主な部品
Arduino Nano互換機、DS3231、シリアルRGB LEDで作成した時計表示板、照度センサー、押しボタンスイッチ2個、22kΩ抵抗1本(照度センサー用)
購入したRTC基板はコイン型リチウムイオン電池(LIR2032)の充電回路が組まれているのですが、購入時に入っているのは充電できないCR2032なので私は充電のパターンをカッターでカットしました。
機能の説明
GPSやNTPサーバーの正確な時計に定期的にアクセスすれば、手動で時刻合わせをする必要がありません。市販の時計のようにボタンで時刻を合わせるのはプログラムを考えるのが大変なので避けてきたのです。以前使ったリアルタイムクロック(RTC)DS1302はひどく精度が悪くて一晩で数分ずれていましたので、RTCは諦めてGPSに頼り切った方が楽だなと考えていました。GPSでも我が家のような木造で窓際ならば問題ありませんが、ビルの中でも使えるかというとさすがに無理な場面も出てくるでしょう。
そんなことを考えながらDS1302よりも高精度と言われているDS3231を試しに購入したところ、カタログ性能は文句のつけようもなく良くて、数日使ってもDS1302とは比べ物にならないほど正確に時を刻んでいます。
しょうがないのでボタン2個で時刻を合わせるプログラムを書くことにしました。ついでに機能も追加して時計としての魅力をUPさせました。部屋の明るさを照度センサーで検知して、暗い時には明かりを抑えたナイトモード、明るい時には華やかなフルカラーモードにしました。表示した数字の色が徐々に変化するようにプログラムしたのですが、AdafruitのシリアルLEDのデモプログラムのRainbowに数字表示のマスクをかけて比較的シンプルに実現させました。アナログの壁時計なら数分ずれていても許せるのですが、デジタルの場合は、朝のテレビ画面の隅で表示している時刻とつい比べてしまうので、秒の表示は無くても同じタイミングで切り替わって欲しいものだと感じます。数字の色の変化はゆっくりなので、時刻表示が切り替わる時にはR→G→Bでフラッシュしてちょっと注意を引くような小細工も入れました。
ボタン2個の同時押しで、時刻合わせモードに入ります。モードが変わったことが直感的に分かるように時刻合わせモードの時は緑です。時→分→秒の順でセットするのですが、ボタンを押さずにいると自動的に時刻合わせモードから抜けます。
時刻合わせモードに入ると”時”だけを緑色で表示します。右ボタンで1アップ、左ボタンで1ダウンします。”時”が合わせ終わったら、ボタン2個の同時押しで、”分”だけを表示します。アップダウンは同様ですが、PCなどの秒まで分かる正確な画面を見て1分先に合わせます。設定したら2個の同時押しで、時分を表示します。PCの秒表示が”00″になる時にどれかボタンを押すとPCの時刻と運動神経レベルで一致させることができます。
回路図
Eagleを勉強中なのでそのうち練習を兼ねて書こうと思います。
照度センサーの短い足を22kΩの抵抗でGNDに落としています。長い足は5Vです。短い足の電圧をA0ピンで測定します。LEDの信号はD7、スイッチはD2とD3、RTCのSDAがA4でSCLがA5です。
プログラム
RTCのライブラリはRTClibを使いました。半分以上は時刻合わせのために書かれています。rainbowCycle()とnightClock()が通常の時計機能でリストの下の端に書かれています。大部分の時間それらの関数の中で動作しているので、時刻合わせモードに入る関数wantAdjust()はそれらの関数の中に置かれていて、”2個の同時押し”を検知するとadjustClock()に入って時刻合わせが始まります。
表示に関してはint ledClock[28]とint ledMask[28]でLEDのON/OFFを行っているので比較的シンプルにできているのではないかと思います。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
// RTClib::Code by JeeLabs http://news.jeelabs.org/code/ #include <Wire.h> #include "RTClib.h" #include <Adafruit_NeoPixel.h> #define PIN 7 #define NUMPIXELS 28 Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); RTC_DS3231 rtc; //Customize Parameters float NIGHT=50.0;//Threshold for Night clock const int Night_R=25,Night_G=17,Night_B=4;//LED color for Night clock const int ILL_Pin = A0;//Illuminance sensor int value_ILL[10];//for averaging int count_ILL=0; boolean BRIGHT=true; int digit[4];//HHMM int ledClock[28];//Mask array for clock display int ledMask[28];//Mask array for Hour digits and Minutes digits int lastMin, lastHour;//for rewrite display const int R_Pin = 2;//push switch for addition const int L_Pin = 3;//push switch for subtruction void setup () { Serial.begin(9600); pinMode(R_Pin, INPUT_PULLUP); pinMode(L_Pin, INPUT_PULLUP); pixels.begin(); // This initializes the NeoPixel library. rtc.begin(); for(int i=0;i<10;i++){ value_ILL[i]=100;//initial data for illuminance average } } void loop () { float value=Illum(); //Serial.println(value);for modification BRIGHT=(value > NIGHT); if(BRIGHT){ rainbowCycle(20); } else{ nightClock(); } } // check button state for the time adjusutment during rainbowCycle or nightClock function void wantAdjust(){ if(!digitalRead(R_Pin)||!digitalRead(L_Pin)){ int STATE=buttonState(); if(STATE==3) adjustClock(); } } int adjustClock(){ if(setHour()==0) return; if(setMin()==0) return; setSec00(); } void setComplete(){ //do nothing now } int setHour(){ int adjusting=1; dispHour(); unsigned long TimeModeIN=millis(); while(adjusting==1){ if((millis()-TimeModeIN)>5000) adjusting=0; int STATE=buttonState(); if(STATE==1) addHour(),TimeModeIN=millis(); if(STATE==2) subHour(),TimeModeIN=millis(); if(STATE==3) adjusting=2; } return adjusting; } void addHour(){ DateTime now = rtc.now()+TimeSpan(0,1,0,0); rtc.adjust(now); dispHour(); } void subHour(){ DateTime now = rtc.now()+TimeSpan(0,-1,0,0); rtc.adjust(now); dispHour(); } int setMin(){ int adjusting=1; dispMin(); unsigned long TimeModeIN=millis(); while(adjusting==1){ if((millis()-TimeModeIN)>5000) adjusting=0; int STATE=buttonState(); if(STATE==1) addMin(),TimeModeIN=millis(); if(STATE==2) subMin(),TimeModeIN=millis(); if(STATE==3) adjusting=2; } return adjusting; } void addMin(){ DateTime now = rtc.now()+TimeSpan(0,0,1,0); rtc.adjust(now); dispMin(); } void subMin(){ DateTime now = rtc.now()+TimeSpan(0,0,-1,0); rtc.adjust(now); dispMin(); } int setSec00(){ int adjusting=1; dispSec00(); unsigned long TimeModeIN=millis(); while(adjusting==1){ if((millis()-TimeModeIN)>60000) adjusting=0; int STATE=buttonState(); if(STATE!=0) adjSec00(),adjusting=2; } } void adjSec00(){ DateTime now = rtc.now(); int Sec00=now.second(); now = now-TimeSpan(0,0,0,Sec00); rtc.adjust(now); } void dispMin(){ adjustDisp(0); } void dispHour(){ adjustDisp(1); } void dispSec00(){ adjustDisp(2); } int buttonState(){ boolean BUTTON=true; int bState,last_bState; int i=0; while(BUTTON){ int rs=0,ls=0; if(!digitalRead(R_Pin)) rs=1; if(!digitalRead(L_Pin)) ls=2; bState=rs+ls; if(i==0) last_bState = bState; if(bState != last_bState) BUTTON=false, bState=0; if(i>5) BUTTON=false; i++; if (bState==3) delay(100); else delay(20); } return bState; } float Illum(){ count_ILL=(count_ILL +1)%10; value_ILL[count_ILL]=analogRead(ILL_Pin); //Serial.print(value_ILL[count_ILL]);//for debug //Serial.print("--");//for debug float sum=0.0; for(int i=0;i<10;i++){ sum=sum+value_ILL[i]; } float average=sum/10.0; //Serial.print(average);//for debug //Serial.print("--");//for debug return average; } void checkTime(boolean WIPE){ DateTime now = rtc.now(); if((now.minute()!=lastMin)||(now.hour()!=lastHour)){ if(WIPE) rgbWipe(255, 5); clockPixels(now.hour(), now.minute()); if(WIPE) rgbWipe(255, 15); } lastMin=now.minute(); lastHour=now.hour(); } void clockPixels(int ledHour, int ledMin){ digit[0]=ledMin%10; digit[1]=ledMin/10; digit[2]=ledHour%10; digit[3]=ledHour/10; for(int i=0;i<4;i++){ switch(digit[i]){ case 0: ledClock[0+i*7]=1,ledClock[1+i*7]=1,ledClock[2+i*7]=1,ledClock[3+i*7]=1,ledClock[4+i*7]=1,ledClock[5+i*7]=1,ledClock[6+i*7]=0; break; case 1: ledClock[0+i*7]=0,ledClock[1+i*7]=1,ledClock[2+i*7]=1,ledClock[3+i*7]=0,ledClock[4+i*7]=0,ledClock[5+i*7]=0,ledClock[6+i*7]=0; break; case 2: ledClock[0+i*7]=1,ledClock[1+i*7]=1,ledClock[2+i*7]=0,ledClock[3+i*7]=1,ledClock[4+i*7]=1,ledClock[5+i*7]=0,ledClock[6+i*7]=1; break; case 3: ledClock[0+i*7]=1,ledClock[1+i*7]=1,ledClock[2+i*7]=1,ledClock[3+i*7]=1,ledClock[4+i*7]=0,ledClock[5+i*7]=0,ledClock[6+i*7]=1; break; case 4: ledClock[0+i*7]=0,ledClock[1+i*7]=1,ledClock[2+i*7]=1,ledClock[3+i*7]=0,ledClock[4+i*7]=0,ledClock[5+i*7]=1,ledClock[6+i*7]=1; break; case 5: ledClock[0+i*7]=1,ledClock[1+i*7]=0,ledClock[2+i*7]=1,ledClock[3+i*7]=1,ledClock[4+i*7]=0,ledClock[5+i*7]=1,ledClock[6+i*7]=1; break; case 6: ledClock[0+i*7]=1,ledClock[1+i*7]=0,ledClock[2+i*7]=1,ledClock[3+i*7]=1,ledClock[4+i*7]=1,ledClock[5+i*7]=1,ledClock[6+i*7]=1; break; case 7: ledClock[0+i*7]=1,ledClock[1+i*7]=1,ledClock[2+i*7]=1,ledClock[3+i*7]=0,ledClock[4+i*7]=0,ledClock[5+i*7]=0,ledClock[6+i*7]=0; break; case 8: ledClock[0+i*7]=1,ledClock[1+i*7]=1,ledClock[2+i*7]=1,ledClock[3+i*7]=1,ledClock[4+i*7]=1,ledClock[5+i*7]=1,ledClock[6+i*7]=1; break; case 9: ledClock[0+i*7]=1,ledClock[1+i*7]=1,ledClock[2+i*7]=1,ledClock[3+i*7]=1,ledClock[4+i*7]=0,ledClock[5+i*7]=1,ledClock[6+i*7]=1; break; } } } void rgbWipe(uint32_t c, uint8_t wait) { colorWipe(pixels.Color(c, 0, 0), wait); // Red colorWipe(pixels.Color(0, c, 0), wait); // Green colorWipe(pixels.Color(0, 0, c), wait); // Blue } void colorWipe(uint32_t c, uint8_t wait) { for(uint16_t i=0; i<pixels.numPixels(); i++) { pixels.setPixelColor(i, c*ledClock[i]); pixels.show(); delay(wait); } } // rainbow equally distributed throughout void rainbowCycle(uint8_t wait) { uint16_t i, j; for(j=0; j<256; j++) { checkTime(true); wantAdjust();//adjustTime for(i=0; i< pixels.numPixels(); i++) { pixels.setPixelColor(i, (Wheel(((i * 256 / pixels.numPixels()) + j) & 255))*ledClock[i]); } pixels.show(); delay(wait); } } void nightClock(){ checkTime(false); wantAdjust();//adjustTime for(int i=0; i< pixels.numPixels(); i++) { pixels.setPixelColor(i, pixels.Color(Night_R, Night_G, Night_B)*ledClock[i]); } pixels.show(); delay(2000); } void adjustDisp(int MH){ //0:--MM,1:HH--,2:HHMM checkTime(false); if(MH==0){ for(int i=0;i<14;i++) ledMask[i]=1; for(int i=14;i<28;i++) ledMask[i]=0; } if(MH==1){ for(int i=0;i<14;i++) ledMask[i]=0; for(int i=14;i<28;i++) ledMask[i]=1; } if(MH==2){ for(int i=0;i<28;i++) ledMask[i]=1; } for(int i=0; i< pixels.numPixels(); i++) { pixels.setPixelColor(i, pixels.Color(0, 100, 0)*ledClock[i]*ledMask[i]); //Display Color is Green } pixels.show(); } // Input a value 0 to 255 to get a color value. // The colours are a transition r - g - b - back to r. uint32_t Wheel(byte WheelPos) { WheelPos = 255 - WheelPos; if(WheelPos < 85) { return pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3); } if(WheelPos < 170) { WheelPos -= 85; return pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3); } WheelPos -= 170; return pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0); } |
(2017.03.30)