
ナイトライダーのK.I.T.T.をご存知ですか?
私と同年代の方々は、頭の中でテーマソングの演奏が始まってしまったのではないでしょうか。


おさらいしておくと、ナイトライダーは80年代のアメリカのドラマで、主人公マイケル.ナイトと彼に仕える人工知能を搭載した車の話です。
K.I.T.T.は人工知能の名前で「キット」と呼ばれていました。調べてみてわかったのですが、Knight Industries Two Thousand(ナイト2000)の頭文字をとったものだったそうです。
今回再現するのは、キットが喋る時に連動するボイスインジケータ の光り方だけで、ディテールにはこだわりません。
ドラマでは「了解です、マイケル」などたくさん喋っていましたが、私は運転していて話しかけられても照れるので、キットの声は再現しません。
かわりに、車内の音の大きさに反応するようにして、音楽や人の声にあわせて光るようにします。
市販品があった!
今回Arduinoで自作しようと決意する前に、市販品で同じようなものがないか探してみたところ、過去に販売されていました。


シガーソケットに差し込むタイプのUSB電源(2個)ですが、いくつかのパターンで喋るようです。
しかし、もう生産されていないようで、日本でもアメリカでもオークションサイトで高値で取引きされています。
簡単に入手できれば、ボックスだけ使って光り方はArduinoで制御したかったのですが、残念です。
Arduinoと接続するモジュール
ボイスインジケータ を再現するにあたり、WS2812Bを8x8個並べたカラーLEDモジュールと、サウンドセンサーモジュールを使います。
LEDはバーグラフタイプのモジュールの方が実物に近いのですが、ちょうどいい大きさのものが見つからなかったので、使いやすいWS2812Bで進めます。
サウンドセンサーで検知した音の大きさに応じて、キットのように上下方向にグラフを立てて、カラーLEDモジュールに表示します。
表示イメージとパターン
8x8マトリックスLEDのうち6x8個を使って、ボイスインジケータを再現します。
実物同様の赤色だけで光るパターンの他に、他の色で光ったり、余っている2x8個を違う光り方で装飾したりするパターンにも切り替えられるようにします。
パターン① KITTを再現します(赤色)。
パターン② KITT風で、色を徐々に変化させます。
パターン③ パターン①に加え、3本のインジケーターの間の余っているスペースに光を流します。今回は上下ですが左右に流れる光こそナイトライダーのイメージそのものですね。
パターン④ パターン②に加え、3本のインジケーターの間の余っているスペースに光を流します。
パターン⑤ KARRも再現します(本当はアンバーですが、私の趣味で緑色)。
パターン⑥ KARR風で、色を徐々に変化させます。
パターン⑤(左)と、パターン⑥(右)
パターン⑦ パターン⑤に加え、3本のインジケーターの間の余っているスペースに光を流します。
回路図

Fritzingで回路図を作成しました。
マイクモジュールは、MAX4466を搭載したものを使っています。
可変抵抗(ボリューム)を使い、表示7パターンを切り替えられるようにします。
スケッチ(プログラム)は、記事の最後に掲載しておきます。
完成しました!
まずブレッドボード上で回路を組んで、ipodタッチで出した音をマイクで拾いながら、マイク感度とLED残像調整をしてスケッチを完成させました。

完成後の作動状況は動画で確認できます。
7種類の光り方だけ見たい場合は、2:37くらいから確認してみてください。
Arduino project #3 「サウンドレベルメーターをWS2812Bでつくる」Sound level meter with WS2812B
今回作ったものは、車のオーバーヘッドコンソールに組み込んで光らせますが、その場所には既に、以前の記事で紹介した8x8LEDが搭載されていて、レインボーカラーなどで光っています。
www.solocamptouring.com
今回のナイトライダー化は、既に搭載されているArduinoにマイクモジュールを追加して、スケッチ(プログラム)を書き換えるだけなので、車での作業はあっという間に終了しました。

スケッチを紹介します
簡単なスケッチ(プログラム)で思い通りの光らせ方ができるGitHubのFastLEDライブラリを使います。
LED光の残像効果は、スケッチ内のfadeToBlackBy()の部分で調整しています。
#include "FastLED.h"
#define DATA_PIN 4
#define LED_TYPE WS2812
#define COLOR_ORDER GRB
#define HEIGHT 8
#define WIDTH 8
#define NUM_LEDS HEIGHT*WIDTH
CRGB leds[NUM_LEDS];
#define BRIGHTNESS 3
const int sampleWindow = 20;
unsigned int sample;
int volts;
int micPin = 2;
int selectPin = 5;
int val = 0;
int numLedsToLight = 0;
int numLedsSide = 0;
void setup() {
delay(3000);
FastLED.addLeds<LED_TYPE,DATA_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
FastLED.setBrightness(BRIGHTNESS);
}
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { kittOriginal, kittColor, kittOriginalWithSinelon, kittColorWithSinelon, karrOriginal, karrColor, karrOriginalWithSinelon };
uint8_t gCurrentPatternNumber = 0;
uint8_t gHue = 96;
void loop()
{
unsigned long startMillis= millis();
unsigned int peakToPeak = 0;
unsigned int signalMax = 0;
unsigned int signalMin = 1024;
while (millis() - startMillis < sampleWindow)
{
sample = analogRead(micPin);
if (sample < 1024)
{
if (sample > signalMax)
{
signalMax = sample;
}
else if (sample < signalMin)
{
signalMin = sample;
}
}
}
peakToPeak = signalMax - signalMin;
volts = (peakToPeak*5/130);
val = analogRead(selectPin);
if (val < 150) {
gCurrentPatternNumber = 0;
} else if (val < 300){
gCurrentPatternNumber = 1;
} else if (val < 450){
gCurrentPatternNumber = 2;
} else if (val < 600){
gCurrentPatternNumber = 3;
} else if (val < 750){
gCurrentPatternNumber = 4;
} else if (val < 900){
gCurrentPatternNumber = 5;
} else {
gCurrentPatternNumber = 6;
}
gPatterns[gCurrentPatternNumber]();
FastLED.show();
EVERY_N_MILLISECONDS( 300 ) { gHue++; }
}
void sinelon()
{
fadeToBlackBy( leds, NUM_LEDS, 8);
int pos = beatsin16(13,16,23);
leds[pos] += CHSV( gHue, 255, 192);
}
void sinelonTwo()
{
fadeToBlackBy( leds, NUM_LEDS, 8);
int pos = beatsin16(13,40,47);
leds[pos] += CHSV( gHue, 255, 192);
}
void onecolorsinelon()
{
fadeToBlackBy( leds, NUM_LEDS, 8);
int pos = beatsin16(13,16,23);
leds[pos] += CRGB::MidnightBlue;
}
void onecolorsinelonTwo()
{
fadeToBlackBy( leds, NUM_LEDS, 8);
int pos = beatsin16(13,40,47);
leds[pos] += CRGB::MidnightBlue;
}
void kittOriginal()
{
if(volts < 4){
numLedsToLight = volts;
if(volts < 1){
numLedsSide = 0;
}else{
numLedsSide = numLedsToLight-1;
}
}else{
numLedsToLight = 4;
numLedsSide = 3;
}
fadeToBlackBy( leds, NUM_LEDS, 20);
for(int led = 4-numLedsSide; led < 4; led++) {
leds[led] += CRGB::Red;
}
for(int led = 4; led < 4+numLedsSide; led++) {
leds[led] += CRGB::Red;
}
for(int led = 12-numLedsSide; led < 12; led++) {
leds[led] += CRGB::Red;
}
for(int led = 12; led < 12+numLedsSide; led++) {
leds[led] += CRGB::Red;
}
for(int led = 28-numLedsToLight; led < 28; led++) {
leds[led] += CRGB::Red;
}
for(int led = 28; led < 28+numLedsToLight; led++) {
leds[led] += CRGB::Red;
}
for(int led = 36-numLedsToLight; led < 36; led++) {
leds[led] += CRGB::Red;
}
for(int led = 36; led < 36+numLedsToLight; led++) {
leds[led] += CRGB::Red;
}
for(int led = 52-numLedsSide; led < 52; led++) {
leds[led] += CRGB::Red;
}
for(int led = 52; led < 52+numLedsSide; led++) {
leds[led] += CRGB::Red;
}
for(int led = 60-numLedsSide; led < 60; led++) {
leds[led] += CRGB::Red;
}
for(int led = 60; led < 60+numLedsSide; led++) {
leds[led] += CRGB::Red;
}
}
void kittColor()
{
if(volts < 4){
numLedsToLight = volts;
if(volts < 1){
numLedsSide = 0;
}else{
numLedsSide = numLedsToLight-1;
}
}else{
numLedsToLight = 4;
numLedsSide = 3;
}
fadeToBlackBy( leds, NUM_LEDS, 20);
for(int led = 4-numLedsSide; led < 4; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 4; led < 4+numLedsSide; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 12-numLedsSide; led < 12; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 12; led < 12+numLedsSide; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 28-numLedsToLight; led < 28; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 28; led < 28+numLedsToLight; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 36-numLedsToLight; led < 36; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 36; led < 36+numLedsToLight; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 52-numLedsSide; led < 52; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 52; led < 52+numLedsSide; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 60-numLedsSide; led < 60; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 60; led < 60+numLedsSide; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
}
void kittOriginalWithSinelon()
{
sinelon() ;
sinelonTwo() ;
kittOriginal() ;
}
void kittColorWithSinelon()
{
onecolorsinelon() ;
onecolorsinelonTwo() ;
kittColor() ;
}
void karrOriginal()
{
if(volts < 4){
numLedsToLight = volts;
numLedsSide = volts;
}else{
numLedsToLight = 4;
numLedsSide = 4;
}
fadeToBlackBy( leds, NUM_LEDS, 20);
for(int led = 8-numLedsSide; led < 8; led++) {
leds[led] += CRGB::Green;
}
for(int led = 0; led < 0+numLedsSide; led++) {
leds[led] += CRGB::Green;
}
for(int led = 16-numLedsSide; led < 16; led++) {
leds[led] += CRGB::Green;
}
for(int led = 8; led < 8+numLedsSide; led++) {
leds[led] += CRGB::Green;
}
for(int led = 28-numLedsToLight; led < 28; led++) {
leds[led] += CRGB::Green;
}
for(int led = 28; led < 28+numLedsToLight; led++) {
leds[led] += CRGB::Green;
}
for(int led = 36-numLedsToLight; led < 36; led++) {
leds[led] += CRGB::Green;
}
for(int led = 36; led < 36+numLedsToLight; led++) {
leds[led] += CRGB::Green;
}
for(int led = 56-numLedsSide; led < 56; led++) {
leds[led] += CRGB::Green;
}
for(int led = 48; led < 48+numLedsSide; led++) {
leds[led] += CRGB::Green;
}
for(int led = 64-numLedsSide; led < 64; led++) {
leds[led] += CRGB::Green;
}
for(int led = 56; led < 56+numLedsSide; led++) {
leds[led] += CRGB::Green;
}
}
void karrColor()
{
if(volts < 4){
numLedsToLight = volts;
numLedsSide = volts;
}else{
numLedsToLight = 4;
numLedsSide = 4;
}
fadeToBlackBy( leds, NUM_LEDS, 20);
for(int led = 8-numLedsSide; led < 8; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 0; led < 0+numLedsSide; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 16-numLedsSide; led < 16; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 8; led < 8+numLedsSide; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 28-numLedsToLight; led < 28; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 28; led < 28+numLedsToLight; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 36-numLedsToLight; led < 36; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 36; led < 36+numLedsToLight; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 56-numLedsSide; led < 56; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 48; led < 48+numLedsSide; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 64-numLedsSide; led < 64; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
for(int led = 56; led < 56+numLedsSide; led++) {
leds[led] += CHSV( gHue, 255, 192);
}
}
void karrOriginalWithSinelon()
{
sinelon() ;
sinelonTwo() ;
karrOriginal() ;
}