夜中に目が覚めた時、もう一寝入りするか、そろそろ起きるか、知りたくなりますね。身動きせずにねぼけた目を開けるだけで時刻がわかる大きなデジタル時計が欲しいと思いました。日中は明るくカラフルで楽しい表示でも、暗くなったらまぶしくない明るさで落ち着いた色で表示してくれる時計があったらいいですね。
アナログの掛け時計は秒まで合っていなくとも気になりませんし、数分違っていてもそのまま放置している人が多いと思います。時刻表示がついている家電製品は身の回りにたくさんありますが、多少時刻がずれていても気にならないものです。でも、大きくて目立つデジタル時計の場合は分単位でキビキビ生活している人でなくとも、テレビの時報や朝の時刻表示とずれていると残念な気持ちになるでしょう。
さて、一般的には数字を表示する方法として7セグメントLEDが使われています。大きな7セグメントLEDも販売されていますが単色でしか光らず高価ですので、手作りで大きな7セグメントLEDを作ってみようというのが私の提案です。
普通のRGBフルカラーLED(例えばOSTA5131A-R/PG/B)を制御するには3本のPWM出力が必要です。1桁分に7個のRGBフルカラーLEDを使うと21本のPWM出力ですから電気回路が大変になります。そこでLEDに制御用マイコンが入っているフルカラーシリアルLEDテープを使用します。NeoPixel Digital RGB LED StripとかWS2812B led strip lightとか色々な名前で販売されています。1セグメントに2個*7セグメント*4桁=56個のLEDを使うと、価格は、例えばスイッチサイエンスで購入したとすると、3000円ぐらいです。AliExpressをうまく使えれば1/5ぐらいの値段で手に入ります。
置き時計のアバウトな仕様
・数字は縦10cmぐらいの大きさ
・時分だけ表示(時刻合わせは秒まで)する
・環境光により夜昼モード切り替えできる
・インターネットや電波(標準電波、GPS)がいらない
・USB給電
ハードウェア
・Arduino (Nano互換機)
(Amazon)HiLetgo Nano V3.0 (380円@170705)
・バッテリーバックアップ付きリアルタイムクロック (DS3231)
(Amazon)HiLetgo DS3231時計モジュール (210円@170705)
・照度センサーと抵抗(22kΩ)
(秋月)照度センサ NJL7502L (100円/2個)
・フルカラーシリアルLEDテープ
・タクトスイッチ2個
配線
Arduino Nano
D2————-Rボタン(他端はGND)
D3————-Lボタン(他端はGND)
D7————-SerialLED信号
5V————-VCC(SerialLED)
GND————-GND(SerialLED)
A0————-NJL7502L(短い足)と抵抗(22kΩ)を通してGND
5V————-NJL7502L(長い足)
A4————-SDA(DS3231モジュール)
A5————-SCL(DS3231モジュール)
5V————-VCC(DS3231モジュール)
GND————-GND(DS3231モジュール)
ソフトウェア
– – – – メイカーフェア東京2017で使用したスケッチ(SimpleClock31)
昼夜モードの判断は照度センサの値を10回測定した平均値で行って、閾値付近でモードの切り替えが頻繁に発生することを防止しています。昼はカラフルなrainbowCycleをベースにしました。rainbowCycleは明度が一定で色相が360度回転する関数でAdafruit_NeoPixelライブラリのサンプルスケッチのstrandtest内で使われています。夜は明度を落としてオレンジ色の光で表示します。
どちらのモードでも時刻の取得、時刻設定ボタンの状態チェック、56個のLEDの点灯設定、点灯、待機(delay)を繰り返します。時刻の取得はRTCライブラリの関数で現在の時刻を調べて、表示している時分と異なる場合は4桁の数値の更新と全てのLEDについて1(ON)か0(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 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
// RTClib::Code by JeeLabs http://news.jeelabs.org/code/ #include <Wire.h> #include "RTClib.h" #include <Adafruit_NeoPixel.h> #define PIN 7 #define NUMPIXELS 56 Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800); RTC_DS3231 rtc; //Customize Parameters float NIGHT=125.0;//Threshold for Night clock const int Night_R=13,Night_G=8,Night_B=2;//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[NUMPIXELS];//Mask array for clock display int ledMask[NUMPIXELS];//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 value_ILL[i]=NIGHT+10;//initial data for illuminance average } Serial.println("SimpleClock31 170626"); } void loop () { float value=Illum(); //Serial.println(value);//for modification BRIGHT=(value > NIGHT); if(BRIGHT){ rainbowCycle(10); } 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*14]=1,ledClock[2+i*14]=1,ledClock[4+i*14]=1,ledClock[6+i*14]=0,ledClock[8+i*14]=1,ledClock[10+i*14]=1,ledClock[12+i*14]=1; ledClock[1+i*14]=1,ledClock[3+i*14]=1,ledClock[5+i*14]=1,ledClock[7+i*14]=0,ledClock[9+i*14]=1,ledClock[11+i*14]=1,ledClock[13+i*14]=1; break; case 1: ledClock[0+i*14]=1,ledClock[2+i*14]=0,ledClock[4+i*14]=0,ledClock[6+i*14]=0,ledClock[8+i*14]=1,ledClock[10+i*14]=0,ledClock[12+i*14]=0; ledClock[1+i*14]=1,ledClock[3+i*14]=0,ledClock[5+i*14]=0,ledClock[7+i*14]=0,ledClock[9+i*14]=1,ledClock[11+i*14]=0,ledClock[13+i*14]=0; break; case 2: ledClock[0+i*14]=1,ledClock[2+i*14]=1,ledClock[4+i*14]=0,ledClock[6+i*14]=1,ledClock[8+i*14]=0,ledClock[10+i*14]=1,ledClock[12+i*14]=1; ledClock[1+i*14]=1,ledClock[3+i*14]=1,ledClock[5+i*14]=0,ledClock[7+i*14]=1,ledClock[9+i*14]=0,ledClock[11+i*14]=1,ledClock[13+i*14]=1; break; case 3: ledClock[0+i*14]=1,ledClock[2+i*14]=1,ledClock[4+i*14]=0,ledClock[6+i*14]=1,ledClock[8+i*14]=1,ledClock[10+i*14]=1,ledClock[12+i*14]=0; ledClock[1+i*14]=1,ledClock[3+i*14]=1,ledClock[5+i*14]=0,ledClock[7+i*14]=1,ledClock[9+i*14]=1,ledClock[11+i*14]=1,ledClock[13+i*14]=0; break; case 4: ledClock[0+i*14]=1,ledClock[2+i*14]=0,ledClock[4+i*14]=1,ledClock[6+i*14]=1,ledClock[8+i*14]=1,ledClock[10+i*14]=0,ledClock[12+i*14]=0; ledClock[1+i*14]=1,ledClock[3+i*14]=0,ledClock[5+i*14]=1,ledClock[7+i*14]=1,ledClock[9+i*14]=1,ledClock[11+i*14]=0,ledClock[13+i*14]=0; break; case 5: ledClock[0+i*14]=0,ledClock[2+i*14]=1,ledClock[4+i*14]=1,ledClock[6+i*14]=1,ledClock[8+i*14]=1,ledClock[10+i*14]=1,ledClock[12+i*14]=0; ledClock[1+i*14]=0,ledClock[3+i*14]=1,ledClock[5+i*14]=1,ledClock[7+i*14]=1,ledClock[9+i*14]=1,ledClock[11+i*14]=1,ledClock[13+i*14]=0; break; case 6: ledClock[0+i*14]=0,ledClock[2+i*14]=1,ledClock[4+i*14]=1,ledClock[6+i*14]=1,ledClock[8+i*14]=1,ledClock[10+i*14]=1,ledClock[12+i*14]=1; ledClock[1+i*14]=0,ledClock[3+i*14]=1,ledClock[5+i*14]=1,ledClock[7+i*14]=1,ledClock[9+i*14]=1,ledClock[11+i*14]=1,ledClock[13+i*14]=1; break; case 7: ledClock[0+i*14]=1,ledClock[2+i*14]=1,ledClock[4+i*14]=0,ledClock[6+i*14]=0,ledClock[8+i*14]=1,ledClock[10+i*14]=0,ledClock[12+i*14]=0; ledClock[1+i*14]=1,ledClock[3+i*14]=1,ledClock[5+i*14]=0,ledClock[7+i*14]=0,ledClock[9+i*14]=1,ledClock[11+i*14]=0,ledClock[13+i*14]=0; break; case 8: ledClock[0+i*14]=1,ledClock[2+i*14]=1,ledClock[4+i*14]=1,ledClock[6+i*14]=1,ledClock[8+i*14]=1,ledClock[10+i*14]=1,ledClock[12+i*14]=1; ledClock[1+i*14]=1,ledClock[3+i*14]=1,ledClock[5+i*14]=1,ledClock[7+i*14]=1,ledClock[9+i*14]=1,ledClock[11+i*14]=1,ledClock[13+i*14]=1; break; case 9: ledClock[0+i*14]=1,ledClock[2+i*14]=1,ledClock[4+i*14]=1,ledClock[6+i*14]=1,ledClock[8+i*14]=1,ledClock[10+i*14]=0,ledClock[12+i*14]=0; ledClock[1+i*14]=1,ledClock[3+i*14]=1,ledClock[5+i*14]=1,ledClock[7+i*14]=1,ledClock[9+i*14]=1,ledClock[11+i*14]=0,ledClock[13+i*14]=0; 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 //Serial.print("adjustDisp=");//for debug //Serial.println(MH);//for debug checkTime(false); if(MH==0){ for(int i=0;i<NUMPIXELS/2;i++) ledMask[i]=1; for(int i=NUMPIXELS/2;i<NUMPIXELS;i++) ledMask[i]=0; } if(MH==1){ for(int i=0;i<NUMPIXELS/2;i++) ledMask[i]=0; for(int i=NUMPIXELS/2;i<NUMPIXELS;i++) ledMask[i]=1; } if(MH==2){ for(int i=0;i<NUMPIXELS;i++) ledMask[i]=1; } for(int i=0; i< 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); } |
時計合わせがプログラムの半分ぐらいを占めていますのでフローチャートを書いてみました。
(170811)