ちょっとした時間に気軽に子供と遊べるおもちゃがないかと思い、Amazonでそこそこ評価がよかったマリオのエアホッケーを買ってみた。
まぁまぁ面白いのだが、まだ小さい子供は騒がしく遊ぶ方が好きなので、最初は点数を付けてゆっくり遊んでいてもすぐに複数のパックを投入して点数を気にせずどんどん落としては戻してを繰り返す様になり飽きてしまう結果になる。
Arduinoで使えるモジュールの一つにレーザーセンサーがあり、気になっていたものの使いどころがなかったため放置していたが、エアホッケーのパック検知に使えそうなのでエアホッケーの得点カウンターを作ってみる事にした。得点表示にはOLEDディスプレイを使う。勝ち負けをグラフィカル表示してもまた楽しさが増しそうである。
使用するのはArduinoで使える5V駆動のレーザーとセンサーモジュール。
レーザー発信器側は5Vを給電するだけ、受光器はVCC/GNDとシグナル線一本をArduinoにつなぐだけ。loop()内で状態をチェックし、シグナルに変化があった場合にカウンタを上げるだけの単純コードで十分動く。
エアホッケーのゴール付近、パックが通過するあたりにレーザーモジュールを取り付ける。こんなモジュールでもかなりまぶしいので、少し奥に配置する。レーザーと受光器は向きを調整しながらホットボンドで固定。
これを両側に取り付ける。
赤く光ってかっこいい。
レーザー発振器のモジュールはKY-008というモデルを中国から安く購入したが、最初うまく動かなかった。単純にS端子に5V、-端子にGNDを接続するだけのはずなのに全くレーザーが点灯しない。情報を検索しているとある掲示板で「中国から購入したが、端子が逆になっていた」の書き込みが。SをGND、-を5Vと接続すると動作したらしい。
いくら中華製でもプリント基板の+と-表記は逆にしないだろう。
と思いながら接続をひっくり返すと・・・ 動いた。相変わらず中華製品はあなどれない。
エアホッケーはファンで空気を送り込み風圧でパックを浮かすのだが、モーターがおもちゃで良くあるミニ四駆に入っているタイプのモーターだった為、改造ついでにミニ四駆用に売られているパワーアップ系のモーターに差し替えてみた。
結果風圧は上がったが、騒音も上がってしまった。将来ファンと電池もPCケース用の強力静音ファンに変えるかもしれない。
使う電子パーツはArduino本体、センサー2組とOLEDディスプレイ、電源用の電池のみの単純構成。ArduinoはArduino nanoと小さいブレッドボードを使ってお手軽工作にする。こうしておけばArduino nanoのプログラム書き換えもブレッドボードから抜いてさくっと行える。電池は9V電池とホームセンターで購入した9V電池ホルダーを使用。
電源オフオン用のスイッチを取り付ける為、9V電池のケーブルを途中でカットし、スイッチ用に延長する。
Arduino nanoと電池ホルダー、ケーブルもろもろは両面テープと結束バンドと普通のテープで簡単固定。
スイッチは本体スイッチ横にドリルとルーターとヤスリを使って穴を空け取り付け。
黒い小さいスイッチが得点カウンタArduino用の電源スイッチ。
最後にOLEDディスプレイを取り付け。使用したモジュールは白色OLED I2C接続128×64のディスプレイ。本体中央端に穴を空け、ケーブルを通す。
ディスプレイのホルダーは3Dプリンターでさくっと作成。最近はほぼ一発で欲しい形状の物が作れるようになってきた。
両面テープでホッケー台端に取り付けたら配線を綺麗にしてArduino nanoにプログラムを書き込んで完成!
とりあえず得点と顔文字を表示するだけのもの。Hockeyのスペルをミスっているのはご愛嬌。
最近は電子回路の知識が無くてもモジュールを繋いでちょこっとプログラムを書くだけでこれだけの物が作れるので凄い時代である。
配線はレーザー、センサー、OLEDのVCCとGNDをArduinoの5VとGNDに接続、センサーのシグナルをD3とD4へ、OLEDのSDA/SCLをA4/A5へ接続するのみ。コードは以下の通り。ディスプレイのライブラリにはu8gを使用している。
(2015/11/26)得点表示を少し進化
ただ、欠点もありセンサーをゴール口近くにしたため防御のためマレット(手に持つやつ)がレーザーに触れてもカウントしてしまう。また、パックが少しゴールに入った状態で止めてもカウントしてしまう。センサーの位置や遮断時間である程度は回避出来そうだが、まぁ子供と遊ぶおもちゃなのでとりあえず良しとしよう。
#include <Wire.h> #include<stdlib.h> #include "U8glib.h" U8GLIB_SSD1306_128X64 u8g(U8G_I2C_OPT_NONE|U8G_I2C_OPT_DEV_0); // I2C / TWI int DetectorA = 3,DetectorB = 4; int lastflagA,lastflagB; int countA = 0,countB=0; char buff[32]; boolean checker; void u8g_prepare(void) { u8g.setFont(u8g_font_6x10); u8g.setFontRefHeightExtendedText(); u8g.setDefaultForegroundColor(); u8g.setFontPosTop(); } void draw(int page){ u8g.firstPage(); do { u8g_prepare(); if (page == 0) clearpage(); else if (page == 1) drawpage(); } while( u8g.nextPage() ); } void clearpage(){ u8g.setColorIndex(1); u8g.drawBox( 0, 0, u8g.getWidth(), u8g.getHeight() ); } void drawpage(){ u8g.setColorIndex(1); u8g.setFont(u8g_font_6x10); u8g.drawStr(0,0,"Air Hockey!"); if (countB < countA) u8g.drawStr(17,55,"(>_<)"); else u8g.drawStr(17,55,"(^_^)"); if (countA < countB) u8g.drawStr(86,55,"(>_<)"); else u8g.drawStr(86,55,"(^_^)"); u8g.setFont(u8g_font_10x20); u8g.setScale2x2(); sprintf(buff,"%.2d",countB); u8g.drawStr(6,25,buff); sprintf(buff,"%.2d",countA); u8g.drawStr(40,25,buff); u8g.undoScale(); } void setup() { // Serial.begin (9600); pinMode(DetectorA, INPUT); pinMode(DetectorB, INPUT); lastflagA = 0; lastflagB = 0; draw(0); delay(200); draw(1); } void loop() { checker = digitalRead(DetectorA); if (!checker){ if (lastflagA == 0){ // Serial.print("detectA "); // Serial.println(countA); countA++; draw(1); } lastflagA = 1; }else{ lastflagA = 0; } checker = digitalRead(DetectorB); if (!checker){ if (lastflagB == 0){ // Serial.print("detectB "); // Serial.println(countB); countB++; draw(1); } lastflagB = 1; }else{ lastflagB = 0; } }
特定の点数に達したら勝敗をグラフィック表示したり、Arduinoにはmp3の再生モジュールなんてのもあるのでBGMを鳴らし、得点の状況により再生する音楽を変えたり音声ナレーションを付けても盛り上がりそうである。