スイッチサイエンスチャンネルで紹介するネタを話し合っていたところ、某妖怪時計の話になりました。ウェアラブルデバイスも最近なにかと話題なので、今回はスイッチサイエンスウォッチを作ってみることにしました!
さて、どんな機能を持たせましょうか。
せっかくなので操作はタッチパネルを使いましょう。
あまり複雑なものにしてしまうと話がややこしくなってしまうので、できるだけシンプルな機能。
追加する部品もできるだけ少なくして、作りやすくします。
色々考えた末、4つの機能に決めました。
1.時計機能
ウォッチと言っているのでやはりこれは欠かせませんね。ただし、部品点数は減らしたいのでRTC(Real Time Clock)やバックアップバッテリーは搭載しません。つまり、電源を切ってしまうと時計が0にリセットされてしまいます。時計と言うよりタイマーですね…
2.お絵かき機能
タッチパネルのサンプルスケッチを試してみて楽しかったので採用しました。
3.TVリモコン
赤外線LEDをつけるだけで簡単に実装できるので採用しました。
4.Rapiroコントローラ
せっかくつける赤外線LEDを一つのことにしか使わないのはもったいない、何かに利用しよう。と言うわけでRapiroのコントローラにしてみました。ただし、この場合Rapiro側に赤外線のレシーバーを搭載しないといけないので今回はインターフェースだけの飾りです。
使用した部品は
・Arduino Uno R3 x 1
・Adafruit 2.8インチ TFTタッチシールド v2(静電容量方式) x 1
・Max Power 赤外線LEDキット x 1
・配線材 少々
配線は次のようにします。
Arduino Uno --- 赤外線LEDキット
3.3V --- VCC
D2 --- CTL
GND --- GND
赤外線LEDが写真のような向きで付くように取り付けます。今回はArduino Unoに直接はんだ付けして固定しました。
バッテリーくらいは搭載しようかとも思いましたが、USBケーブルでの給電にしてしまいました。
サンプルスケッチは下のとおりです。リモコンのボタンコード送信するプログラムをを使用するテレビに合わせて追加する必要があります。
追加でインストールする必要のあるライブラリは
・Adafruit_ILI9341
・Adafruit_FT6206
・Adafruit_GFX
後は時計として手首に固定するバンドをつければスイッチサイエンスウォッチの完成です。
電源はモバイルバッテリーでもポケットに忍ばせておきましょう。
実際の動作はスイッチサイエンスチャンネルの動画でご確認ください。
#include "SPI.h" #include "Wire.h" #include "Adafruit_GFX.h" #include "Adafruit_ILI9341.h" #include "Adafruit_FT6206.h" #define HOME 0 #define MEMO 1 #define ROBO 2 #define TIME 3 #define TV 4 #define BOXSIZE 40 #define TFT_DC 9 #define TFT_CS 10 #define LED 2 Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC); Adafruit_FT6206 ctp = Adafruit_FT6206(); int mode = HOME; int sec,mins,hour,old_sec=0,old_mins=0,old_hour=0; void setup() { Serial.begin(115200); tft.begin(); ctp.begin(40); tft.fillScreen(ILI9341_WHITE); tft.setRotation(1); pinMode(LED,OUTPUT); } void loop() { switch(mode){ case HOME: homeMode(); break; case MEMO: memoMode(); break; case TV: tvMode(); break; case ROBO: roboMode(); break; case TIME: timeMode(); break; } check_touch(); } void check_touch() { int tmp; TS_Point p = ctp.getPoint(); p.y = map(p.y, 0, 320, 320,0); tmp = p.y; p.y = p.x; p.x = tmp; switch(mode){ int i; case HOME: if(p.y > 20 && p.y < 105){ if(p.x > 20 && p.x < 145){ mode = MEMO; tft.fillScreen(ILI9341_WHITE); tft.drawRoundRect(280,200,40,40,10,ILI9341_BLACK); } else if(p.x > 175 && p.x < 300){ mode = TV; tft.fillScreen(ILI9341_WHITE); } } else if(p.y > 135 && p.y < 220){ if(p.x > 20 && p.x < 145){ mode = ROBO; tft.fillScreen(ILI9341_WHITE); } else if(p.x > 175 && p.x < 300){ mode = TIME; tft.fillScreen(ILI9341_WHITE); } } break; case ROBO: if(p.y >= 30 && p.y < 80){ if(p.x >= 75 && p.x < 125){ Serial.println("#M1"); } else if(p.x >= 140 && p.x < 190){ } else if(p.x >= 200 && p.x < 250){ Serial.println("#M5"); } else if(p.x >= 260 && p.x < 310){ Serial.println("#M6"); } } else if(p.y >= 95 && p.y < 145){ if(p.x >= 10 && p.x < 60){ Serial.println("#M3"); } else if(p.x >= 75 && p.x < 125){ Serial.println("#M0"); } else if(p.x >= 140 && p.x < 190){ Serial.println("#M4"); } else if(p.x >= 200 && p.x < 250){ Serial.println("#M7"); } else if(p.x >= 260 && p.x < 310){ Serial.println("#M8"); } } else if(p.y >= 160 && p.y < 210){ if(p.x >= 75 && p.x < 125){ Serial.println("#M2"); } else if(p.x >= 200 && p.x < 250){ Serial.println("#M9"); } else if(p.x >= 260 && p.x < 310){ tft.fillScreen(ILI9341_WHITE); mode = HOME; } } break; case TV: if(p.y >= 10 && p.y < 60){ if(p.x >= 20 && p.x < 70){ Serial.println("1ch"); //リモコンコード送信 } else if(p.x >= 80 && p.x < 130){ Serial.println("2ch"); //リモコンコード送信 } else if(p.x >= 140 && p.x < 190){ Serial.println("3ch"); //リモコンコード送信 } else if(p.x >= 200 && p.x < 250){ Serial.println("4ch"); //リモコンコード送信 } else if(p.x >= 260 && p.x < 310){ Serial.println("5ch"); //リモコンコード送信 } } else if(p.y >= 70 && p.y < 120){ if(p.x >= 20 && p.x < 70){ Serial.println("6ch"); //リモコンコード送信 } else if(p.x >= 80 && p.x < 130){ Serial.println("7ch"); //リモコンコード送信 } else if(p.x >= 140 && p.x < 190){ Serial.println("8ch"); //リモコンコード送信 } else if(p.x >= 200 && p.x < 250){ Serial.println("9ch"); //リモコンコード送信 } else if(p.x >= 260 && p.x < 310){ Serial.println("10ch"); //リモコンコード送信 } } else if(p.y >= 130 && p.y < 180){ if(p.x >= 20 && p.x < 70){ Serial.println("11ch"); //リモコンコード送信 } else if(p.x >= 80 && p.x < 130){ Serial.println("12ch"); //リモコンコード送信 } } else if(p.y >= 190 && p.y < 240){ if(p.x >= 20 && p.x < 130){ Serial.println("POWER"); //リモコンコード送信 } else if(p.x >= 140 && p.x < 190){ Serial.println("VOL UP"); //リモコンコード送信 } else if(p.x >= 200 && p.x < 250){ Serial.println("VOL DOWN"); //リモコンコード送信 } else if(p.x >= 280){ tft.fillScreen(ILI9341_WHITE); mode = HOME; } } break; case MEMO: if(p.x >= 280 && p.y >= 200){ tft.fillScreen(ILI9341_WHITE); mode = HOME; } else{ tft.fillCircle(p.x,p.y,2,ILI9341_BLACK); } break; case TIME: if(p.x >= 260 && p.y >= 160){ tft.fillScreen(ILI9341_WHITE); mode = HOME; } break; } } void homeMode() { tft.setTextColor(ILI9341_BLACK); tft.setTextSize(5); for(int y=0;y<2;y++){ for(int x=0;x<2;x++){ tft.drawRoundRect(x*155+10,y*115+10,145,105,10,ILI9341_BLACK); } } tft.setCursor(25,50); tft.println("MEMO"); tft.setCursor(205,50); tft.println("TV"); tft.setCursor(25,160); tft.println("ROBO"); tft.setCursor(180,160); tft.println("TIME"); } void memoMode() { } void roboMode() { int i=0; char code[6]={'A','B','C','D','E',' '}; tft.drawRoundRect(75,30,50,50,10,ILI9341_BLACK); tft.fillTriangle(100,32,80,78,120,78,ILI9341_BLACK); tft.drawRoundRect(10,95,50,50,10,ILI9341_BLACK); tft.fillTriangle(12,120,58,98,58,143,ILI9341_BLACK); tft.drawRoundRect(75,95,50,50,10,ILI9341_BLACK); tft.drawRoundRect(140,95,50,50,10,ILI9341_BLACK); tft.fillTriangle(188,120,142,98,142,143,ILI9341_BLACK); tft.drawRoundRect(75,160,50,50,10,ILI9341_BLACK); tft.fillTriangle(100,208,80,162,120,162,ILI9341_BLACK); tft.setTextColor(ILI9341_BLACK); tft.setTextSize(3); for(int y=0;y<3;y++){ for(int x=0;x<2;x++){ tft.drawRoundRect(x*60+200,y*65+30,50,50,10,ILI9341_BLACK); tft.setCursor(x*60+215,y*65+45); tft.println(code[i]); i++; } } } void tvMode() { int i=1; tft.fillRoundRect(15,190,110,50,10,ILI9341_RED); //電源ボタン tft.drawRoundRect(135,190,50,50,10,ILI9341_BLACK); //VOL UPボタン tft.fillTriangle(160,200,140,228,180,228,ILI9341_BLACK); tft.drawRoundRect(195,190,50,50,10,ILI9341_BLACK); //VOL DOWNボタン tft.fillTriangle(220,228,200,200,240,200,ILI9341_BLACK); tft.drawRoundRect(280,200,40,40,10,ILI9341_BLACK); //戻るボタン tft.setTextColor(ILI9341_BLACK); tft.setTextSize(3); for(int y = 0;y < 3;y++){ //チャンネルボタン描画 for(int x = 0; x < 5;x++){ if(y==2 && x==2)break; tft.drawRoundRect(x*60+15,y*60+10,50,50,10,ILI9341_BLACK); if(i>=10){ tft.setCursor(x*60+20,y*60+25); tft.println(i); i++; } else{ tft.setCursor(x*60+30,y*60+25); tft.println(i); i++; } } } } void timeMode() { long time; time = millis(); time = time / 1000; sec = time % 60; mins = (time / 60)%60; hour = ((time /60)/60)%60; tft.setTextColor(ILI9341_BLACK); tft.setTextSize(3); if(old_hour != hour){ tft.fillRoundRect(90,100,40,25,1,ILI9341_WHITE); } if(old_mins != mins){ tft.fillRoundRect(150,100,40,25,1,ILI9341_WHITE); } if(old_sec != sec){ tft.fillRoundRect(210,100,40,25,1,ILI9341_WHITE); } tft.setCursor(90,100); tft.println(hour/10); tft.setCursor(110,100); tft.println(hour%10); tft.setCursor(130,100); tft.println(":"); tft.setCursor(150,100); tft.println(mins/10); tft.setCursor(170,100); tft.println(mins%10); tft.setCursor(190,100); tft.println(":"); tft.setCursor(210,100); tft.println(sec/10); tft.setCursor(230,100); tft.println(sec%10); old_hour = hour; old_mins = mins; old_sec = sec; tft.drawRoundRect(280,200,40,40,10,ILI9341_BLACK); }