ハザードトリガーをカッコ良く発光させる

久々の一品モノの電子工作です。今回のお題(?)は『仮面ライダービルド DXハザードトリガー』です。

ただいま(2018/3/2現在)劇中で絶賛活躍中のハザードトリガー。玩具としてのギミックは、音声が鳴る他、ビルドドライバーのハンドル操作と連動してメーターがピョコピョコ動きます。

個人的にはこういうメカニカルなギミックを搭載したおもちゃは好きなのですが、Amazonのレビューとか見ていると評価はイマイチな様子。どうも価格の割には音声が少ない、発光がない、成型色、などがその理由のようです。うーん、みんなおもちゃに求めるハードルが高いなあ。。。私も本業は(別業界ですが)モノづくりなので、こういうレビューはなかなか見ていて胃が痛くなります。

私は工作が趣味の人間なので、こういう「隙がある」玩具を見ると、「ここをこうしたらもっとカッコ良くなる!」と思って喜んでしまいます。そして、ビルドの玩具は全体的に「隙がある」「惜しい」ものが多くて、色々弄りがいがあって楽しいです。。。世間一般的にはこれを「残念」というのかもしれませんが。

まあとにもかくにもこのハザードトリガー、素材としては大変魅力的なので、せっかくなので改造してもっとカッコ良くしてみたいと思います。「さぁ、実験を始めようか。」です。

 

改造ポイントは、やっぱりメーター部分の発光が一番みんなが期待していて、かつ実際カッコ良くなるところだと思います。トライされている方々はやはり既にいらっしゃって、色々探してみた中では、フルボトルの発光改造やフルボトルのマスタープレート作成のときにも参照させて頂いた、特撮玩具好きの部屋のクレーンの丈様の改造が一番完成度が高いと思います。

私もハザードトリガーを入手した直後はこのレベルのものを作ろうとしたのですが、先にやられてしまいましたので、これ以上のものを作らざるを得なくなってしまいました。記事として公開する以上は、何かしら目新しい内容を入れないと、ただの二番煎じになってしまいますので。

 

ということで、自分なりにもう一捻りして完成させたハザートトリガーがこちらです。

th_hazard_trigger_28

パッと見はただのハザードトリガーです。

th_hazard_trigger_25

背面も特に変わりはありません。 ちなみに、一応メタリックレッドのスプレーはふっているのですが、見た目あんまり変わっていません。

th_hazard_trigger_26

底面も、ビルドドライバーとの接続部分に小さい穴が2つほど開いているぐらいで、他に変わりはありません。

th_hazard_trigger_27

正面から見ると、メーター部分が白っぽい色で常に表示された状態になっています。元々のハザードトリガーはこの部分が完全に透明で、ギア連動でピョコピョコとメーターが顔を出すようになっています。一方、この改修版ハザートドリガーでは、ギア連動で動くメーターは取り除いてしまっていて、代わりにフルカラーLEDリングの光の動きでメーターの動きを表現しています。

実際に組み込んだものとはプログラムの内容が異なりますが、動作イメージとしてはこんな感じです。

 

これを実現するために、メーター部分には、内側から透明ラベルに印刷したメーターのデザイン(?)を貼り付けています。

th_hazard_trigger_19

これに対して、裏からLEDリングをあてがうのですが、微妙にサイズが大きくて中に収まってくれません。

th_hazard_trigger_10

そのため、パーツの下部分を大きくカットしてしまって、

th_hazard_trigger_29

こんな感じにすると、上手くはまってくれました。

th_hazard_trigger_11

ちなみに、透明ラベルだけでメーター部分を表現してしまうと、中のLEDが一個一個独立して光っているように見えてあまり綺麗ではないので、

th_hazard_trigger_20

実際にはメーターとLEDリングの間に上のように白い紙を一枚挟んで、光が綺麗に混ざるようにしています。

 

th_hazard_trigger_21

続いて、電子回路部分です。こっちから見ると、元々の状態からあまり変わっていないように見えるかもしれませんが、色々線が増えています。

基板部分を外して裏返してみると、このとおり。

th_hazard_trigger_16

元々ギア連動のメーターパーツが入っていた部分に、小型のマイコン基板(Trinket 5V)を仕込んでいます。

半田付けが空中配線でメチャメチャなのはご了承ください。ここで、LEDリングの発光パターンをプログラミングしています。ソースコードは最後にご紹介します。

今回は、自分としては珍しく、元々玩具に組み込まれている基板やスイッチを極力そのまま活かせるような形で改造しています(←ガシャットのときは中身を総入れ替えしていました)。そのため、事前にこんな感じでテスターでスイッチ部分の電位差を調べました。

th_hazard_trigger_2

th_hazard_trigger_3

上記の写真で黒いテスター棒が当たっている方が、通常はGND(0V)に落ちていますが、それぞれスイッチが押されているときだけ、電位が4.3Vぐらいまで上がります。これを先ほど追加したマイコン基板の入力ピンに繋いでやって電圧の変化(HIGH-LOW)を監視してやれば、元々ハザードトリガーに仕込まれているスイッチに連動して、自分が追加したマイコン基板の動作を制御できるようになります。あ、それから元々の音声基板と追加したマイコン基板のGNDを繋いでおくのと、入力ピンにプルダウン抵抗を挟んでおくのを忘れずに。

 

さて、次が個人的には一番気に入っている改造ポイントなのですが、

th_hazard_trigger_30

マイコン(と、マイコンに接続されているLEDリング)の電源ラインはこれですが、 これがどこに伸びているかというと、実はハザードトリガーに元々搭載されているボタン電池x3には繋がっていません。

それじゃあどこに繋がっているのかと言いますと、

th_hazard_trigger_13

ハザードトリガーのビルドドライバーとの接続部の中を通って、ここに繋がっています。これが実は、最初に外観をお見せしたときに開いていた、小さな二つの穴の正体です。

th_hazard_trigger_31

つまり、今回追加したマイコンとLEDリングの電源は、ハザードトリガーのボタン電池x3ではなく、ビルドドライバーの単四電池x3からとっているのです。ハザードトリガーをビルドドライバーに挿すと、自動的にハザードトリガー内の追加マイコンに電力が供給されて、発光ギミックが有効になるという仕組みです。この形にすることで、

  • マイコンとLEDを安定駆動させるための電源をハザードトリガーのどこに仕込むか(※元々あるボタン電池では心許無いため)
  • マイコンON/OFF切り替えのための追加スイッチをどこに仕込むか

という二つの問題を解決することができました。また、「ビルドドライバーに挿すことによって真価を発揮するアイテム」という特徴をより強く出すことができるようになったと思います。

th_hazard_trigger_6

ビルドドライバー側は、こんな感じで穴を開けて、

th_hazard_trigger_32

こんな感じで電源を引っ張ってきています。この位置なら、ややこしそうなギヤ部分には手を触れずに改造可能です。追加したピンの部分は、ハザードトリガーを挿したときに下に押し込まれないように、ホットボンドでガチガチに固めています。

th_hazard_trigger_5

ちなみに、ビルドドライバーを開けるときに、どうしてもこの位置のネジだけ外れてくれなくて、泣く泣くこの部分だけニッパーで切り離しました。。。

 

物理的な改造ポイントは以上です。最後に、どういう発光プログラムになっているかのご紹介です。ソースコードはあんまり綺麗じゃないですが、こんな感じです。

#include <string.h>
#include <Adafruit_NeoPixel.h>

#define PIN_MODE  0  
#define PIN_COUNT 2
#define PIN_LED   3
#define NUM_OF_LED 12
#define NUM_OF_USE_LED 6
#define INTERVAL 30
#define SHORT_INTERVAL 15

#define COUNT_MAX 5
#define RESET_MAX 1000

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUM_OF_LED, PIN_LED, NEO_GRB);

uint8_t last_mode_state = LOW;
uint8_t mode_state      = LOW;
uint8_t last_count_state = LOW;
uint8_t count_state      = LOW;
boolean isMaxHazard = false;
boolean isAttack    = false;
uint8_t count = 0;
int reset_count = 0;

void setup () {
  pinMode(PIN_MODE, INPUT);
  pinMode(PIN_COUNT, INPUT);
  pixels.begin();
  for(int8_t i=0;i<NUM_OF_LED;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  pixels.show();
}

void loop(){
  mode_state = digitalRead(PIN_MODE);
  if(last_mode_state == LOW && mode_state == HIGH){
    isMaxHazard = !isMaxHazard;
    count = 0;
    reset_count = 0;
    isAttack = false;
    if(isMaxHazard){
      for(int8_t i=0;i<=10;i++){
        led_pattern_max_hazard(6,i); delay(INTERVAL);
      }
      for(int8_t i=10;i>=0;i--){
        led_pattern_max_hazard(6,i); delay(INTERVAL);
      }
    }else{
      for(int8_t i=10;i>=0;i--){
        led_pattern_max_hazard(6,i); delay(INTERVAL);
      }
    }
  }
  last_mode_state = mode_state;

  count_state = digitalRead(PIN_COUNT);
  if(last_count_state == LOW && count_state == HIGH){
    uint8_t lv = isAttack ? 10:5;
    switch(count){
    case 0:    
      if(isMaxHazard){      
        for(uint8_t i=1;i<7;i++){
          led_pattern_max_hazard(i,lv); delay(INTERVAL);
        }
        for(uint8_t i=5;i>0;i--){
          led_pattern_max_hazard(i,lv); delay(INTERVAL);
        }
      }else{
        led_pattern_normal_lv1(lv); delay(INTERVAL);
        led_pattern_normal_lv2(lv); delay(INTERVAL);
        led_pattern_normal_lv3(lv); delay(INTERVAL);
        led_pattern_normal_lv4(lv); delay(INTERVAL);
        led_pattern_normal_lv5(lv); delay(INTERVAL);
        led_pattern_normal_lv6(lv); delay(INTERVAL);
        led_pattern_normal_lv5(lv); delay(INTERVAL);
        led_pattern_normal_lv4(lv); delay(INTERVAL);
        led_pattern_normal_lv3(lv); delay(INTERVAL);
        led_pattern_normal_lv2(lv); delay(INTERVAL);
        led_pattern_normal_lv1(lv); delay(INTERVAL);
      }
      break;
    case 1: 
      if(isMaxHazard){
        for(uint8_t i=2;i<7;i++){
          led_pattern_max_hazard(i,lv); delay(INTERVAL);
        }
        for(uint8_t i=5;i>1;i--){
          led_pattern_max_hazard(i,lv); delay(INTERVAL);
        }
      }else{
        led_pattern_normal_lv2(lv); delay(INTERVAL);
        led_pattern_normal_lv3(lv); delay(INTERVAL);
        led_pattern_normal_lv4(lv); delay(INTERVAL);
        led_pattern_normal_lv5(lv); delay(INTERVAL);
        led_pattern_normal_lv6(lv); delay(INTERVAL);
        led_pattern_normal_lv5(lv); delay(INTERVAL);
        led_pattern_normal_lv4(lv); delay(INTERVAL);
        led_pattern_normal_lv3(lv); delay(INTERVAL);
        led_pattern_normal_lv2(lv); delay(INTERVAL);
      }
      break;
    case 2:
      if(isMaxHazard){
        for(uint8_t i=3;i<7;i++){
          led_pattern_max_hazard(i,lv); delay(INTERVAL);
        }
        for(uint8_t i=5;i>2;i--){
          led_pattern_max_hazard(i,lv); delay(INTERVAL);
        }
      }else{
        led_pattern_normal_lv3(lv); delay(INTERVAL);
        led_pattern_normal_lv4(lv); delay(INTERVAL);
        led_pattern_normal_lv5(lv); delay(INTERVAL);
        led_pattern_normal_lv6(lv); delay(INTERVAL);
        led_pattern_normal_lv5(lv); delay(INTERVAL);
        led_pattern_normal_lv4(lv); delay(INTERVAL);
        led_pattern_normal_lv3(lv); delay(INTERVAL);
      }
      break;
    case 3: 
      if(isMaxHazard){
        for(uint8_t i=4;i<7;i++){
          led_pattern_max_hazard(i,lv); delay(INTERVAL);
        }
        for(uint8_t i=5;i>3;i--){
          led_pattern_max_hazard(i,lv); delay(INTERVAL);
        }
      }else{
        led_pattern_normal_lv4(lv); delay(INTERVAL);
        led_pattern_normal_lv5(lv); delay(INTERVAL);
        led_pattern_normal_lv6(lv); delay(INTERVAL);
        led_pattern_normal_lv5(lv); delay(INTERVAL);
        led_pattern_normal_lv4(lv); delay(INTERVAL);
      }
      break;
    case 4: 
      if(isMaxHazard){
        for(uint8_t i=5;i<7;i++){
          led_pattern_max_hazard(i,lv); delay(INTERVAL);
        }
        for(uint8_t i=5;i>4;i--){
          led_pattern_max_hazard(i,lv); delay(INTERVAL);
        }
      }else{
        led_pattern_normal_lv5(lv); delay(INTERVAL);
        led_pattern_normal_lv6(lv); delay(INTERVAL);
        led_pattern_normal_lv5(lv); delay(INTERVAL);
      }
      break;
    case 5: 
      if(isMaxHazard){
        for(int8_t i=lv;i>=0;i--){
          led_pattern_max_hazard(6,i); delay(SHORT_INTERVAL);
        }
        for(int8_t i=1;i<=lv;i++){
          led_pattern_max_hazard(6,i); delay(SHORT_INTERVAL);
        }      
      }else{
        for(int8_t i=lv;i>=0;i--){
          led_pattern_normal_lv6(i); delay(SHORT_INTERVAL);
        }
        for(int8_t i=1;i<=lv;i++){
          led_pattern_normal_lv6(i); delay(SHORT_INTERVAL);
        }
      }
      break; 
    default: ;
    }

    if(count < COUNT_MAX){
      count++;  
    }
  }
  last_count_state = count_state;

  if(count == COUNT_MAX){
    reset_count++;
    if(reset_count > RESET_MAX){
      count = 0;
      reset_count = 0;

      if(!isAttack){
        isAttack = true;
      }
    }
  }

  delay(10);
}

void led_pattern_normal_lv1(uint8_t lv){
  uint8_t value = 24*lv;
  pixels.setPixelColor(0, pixels.Color(value,0,0));
  for(uint8_t i=1;i<NUM_OF_USE_LED;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  pixels.show();
}

void led_pattern_normal_lv2(uint8_t lv){
  uint8_t value = 24*lv;
  pixels.setPixelColor(0, pixels.Color(value,0,0));
  pixels.setPixelColor(1, pixels.Color(value,value,0));
  for(uint8_t i=2;i<NUM_OF_USE_LED;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  pixels.show();
}

void led_pattern_normal_lv3(uint8_t lv){
  uint8_t value = 24*lv;
  pixels.setPixelColor(0, pixels.Color(value,0,0));
  pixels.setPixelColor(1, pixels.Color(value,value,0));
  pixels.setPixelColor(2, pixels.Color(0,value,0));
  for(uint8_t i=3;i<NUM_OF_USE_LED;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  pixels.show();
}

void led_pattern_normal_lv4(uint8_t lv){
  uint8_t value = 24*lv;
  pixels.setPixelColor(0, pixels.Color(value,0,0));
  pixels.setPixelColor(1, pixels.Color(value,value,0));
  pixels.setPixelColor(2, pixels.Color(0,value,0));
  pixels.setPixelColor(3, pixels.Color(0,value,value));
  for(uint8_t i=4;i<NUM_OF_USE_LED;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  pixels.show();
}

void led_pattern_normal_lv5(uint8_t lv){
  uint8_t value = 24*lv;
  pixels.setPixelColor(0, pixels.Color(value,0,0));
  pixels.setPixelColor(1, pixels.Color(value,value,0));
  pixels.setPixelColor(2, pixels.Color(0,value,0));
  pixels.setPixelColor(3, pixels.Color(0,value,value));
  pixels.setPixelColor(4, pixels.Color(0,0,value));
  pixels.setPixelColor(5, pixels.Color(0,0,0));
  pixels.show();
}

void led_pattern_normal_lv6(uint8_t lv){
  uint8_t value = 24*lv;
  pixels.setPixelColor(0, pixels.Color(value,0,0));
  pixels.setPixelColor(1, pixels.Color(value,value,0));
  pixels.setPixelColor(2, pixels.Color(0,value,0));
  pixels.setPixelColor(3, pixels.Color(0,value,value));
  pixels.setPixelColor(4, pixels.Color(0,0,value));
  pixels.setPixelColor(5, pixels.Color(0,0,value));
  pixels.show();
}

void led_pattern_max_hazard(uint8_t pattern, uint8_t lv){
  uint8_t value = 24*lv;
  for(uint8_t i=0;i<pattern;i++){
    pixels.setPixelColor(i, pixels.Color(value,0,0));
  }
  for(uint8_t i=pattern;i<NUM_OF_USE_LED;i++){
    pixels.setPixelColor(i, pixels.Color(0,0,0));
  }
  pixels.show();
}

実際の動きとしては、ビルドドライバーのハンドル回転によってボタンが押されるたびに、ちょっとずつゲージが伸びていくような感じにしてみました。

th_hazard_trigger_43

毎度一瞬だけゲージがMaxまで伸びるようになっているのは、元々の玩具のギミックを意識してそうしています。

th_hazard_trigger_44

あとはアレンジとして、「マックスハザードオン!」の状態になったときには、より「ヤベーイ!」感を強くするために、ゲージの色を赤一色に変更してみました。

あと地味に、「ハザードアタック!」と「ハザードフィニッシュ!」のときは、発光の強さを2倍にしています。ちょっとわかりにくいですが。

 

以上が今回の改造の全てになります。結果は記事冒頭の動画をご確認くださいませ。

 

というわけで、ハザードトリガーの発光改造でした。今回はガシャットのときのようなオリジナリティはあまりないですが、発光はかなり綺麗に出すことができたと思いますし、また電力をビルドドライバーから供給することで全体的におもちゃとして上手くまとめられたと思いますので、個人的には結構お気に入りの作品になりました。


「ハザードトリガーをカッコ良く発光させる」への7件のフィードバック

  1. もう何て言うか、自分の
    ちゃちゃっとLED追加しただけ
    の改造が恥ずかしくなる位、
    めっちゃすげー∑(゚Д゚;)です!
    しかも、発光色の変化まで!

    ただ、装填前のボタンの発光も考えると、
    電源はハザードトリガー単体で取れたら理想ですね^^

    1. そうですね、理想的には電源もハザードトリガー側で取れれば、話としてはわかりやすくなると思います。特に装填前のボタンの発光を実装する場合は必須です。

      装填前のボタンの発光については、実装するかどうかはちょっと迷ったところです。確かに劇中では発光しているのですが、なんとなく、必然性でもって光るというより演出的に光っているような感じがして、自分の中ではなしでも問題ないかな、という気持ちでオミットしました。これについては、作り手の人が何を重要視するか次第かなと、思います。劇中再現度を最重要視するのであれば、ここは実装しておくべきポイントだったと思います。

      今回の私の方式の場合、LED制御用マイコンの電力はボタン電池x3からの供給では厳しいので、外部から引っ張らざるを得なくなっています。しかし装填前のボタンの発光部分だけを、制御マイコンを介さずに丈様と同様のボタン直結にしてあげれば、こちらは電力供給がハザードトリガー自身からになりますので、メーター部の複雑なLED制御と装填前のボタンの発光を両立できそうですね。ハイブリッド方式です。

  2. 要素的には光らすだけですけど、電源をビルドドライバーの方から取ってるのがめちゃくちゃグッときますね・・・

    1. ありがとうございます◎ そこはあんまり評価されていませんが、 機能美としてはかなりうまく処理できたのではないかと自画自賛しておりますので、評価頂いて嬉しいです(笑)

  3. 初めまして、いつも楽しくサイトを見させてもらってます。
    このサイトのオリジナルガシャットを見て、私もこういった作品を作ってみたいと思い、電子工作をはじめました。
    それで、ハザードトリガーの中身をArduinoProMiniとDFPlayerMiniを使って音を変更した作品を作りたいと思っているのですが、電池を用いた場合にDFPlayerMiniから音が出なくて困っております。
    当サイトの解説を見て、ArduinoProminiから電源を供給した場合に音声を出力することは実際に確認できました。(同様に途中からノイズになっていましたが)
    配線も画像に従って配置したため、間違っていないと思います。電池から電源を供給する場合には、何かしらの工夫や手順が必要なのでしょうか?
    使ってる電池はハザードトリガー購入時に使われていたボタン電池3つです。
    お手数をかけますが、どうかよろしくおねがいします。

    1. IWC様

      ご連絡が遅くなり申し訳ありません。本業の方の出張でしばらく家を不在にしていました。

      私自身も電子回路が専門分野というわけではないので正しい説明はおそらくできないのですが、少なくとも、私自身の経験としても、ボタン電池x3でArduinoProMiniとDFPlayerMiniを駆動させるのに成功したことはありません。それは、オリジナルガシャットを作るときに一番初めに試してみたことで、どうにも動いてくれないので、止むを得ず(取り扱い注意にも関わらず)電源としてリチウムイオンポリマー充電池を採用したという経緯があります。

      これは私の推測でしかないのですが、ボタン電池x3だと電圧は4.5Vで足りていたとしても、流せる電流の量が、ArduinoとDFPlayerMiniを動かすには足りていないのではないかと考えています。今回の私のハザードトリガーの改造でも、TrinketとフルカラーLEDの電源を元々のボタン電池x3からとらずにビルドドライバー(単四電池x3)からとっているのでは、先のガシャットのときの経験があったので「おそらくボタン電池x3だとうまく動いてくれないだろうな」となんとなく感じていたからです。

      そのため、私自身の意見としては、ボタン電池x3ではない別の電源供給方法を模索した方が早道かもしれないな、と思っています。もしボタン電池x3でうまく動かす方法が見つかれば、それは逆に、ぜひ教えてほしいと思っています。

IWC にコメントする コメントをキャンセル

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

次のHTML タグと属性が使えます: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>