チラシのうら

レゴとか、工作とか。

8pinoで圧電スピーカーを鳴らす その3

午後の部は

午前の部 で宣言した通り

PWMを使う方式で、深堀してみます。

結果から言いますと

できました!カエルの合唱(輪唱)をお楽しみ下さい。 輪唱の担当別にLEDを光らせています!

なんでしょう、この、俺はいったい何をしているんだ感は。

まぁ、まだ(あまり)誰もやっていない事だからと、胸を張ることで、自分をごまかします。

以下は、自分の備忘録的に。

音階と周波数の関係

こちらのサイトにありました。
「ドは261.6Hz」とかを学ぶ。

PWMが使えるPin

Trinketのサイト に、記載がありました。

The 3 independent IO pins have 1 analog input and 2 PWM output as well. The 2 shared IO pins have 2 more analog inputs and one more PWM output.

もっと、具体的には、Pin0, Pin1, Pin4がPWMとして使用可能なようです。 私の環境では、常にPin0をNeopixel Ringにアサインしているので、今回はPin1, Pin4を使うことにします。

PWMの周波数の変更

PWMへの周波数の指定は、0-255の整数値でしかできません。

8pinoのデフォルトの周波数は8MHzな様ですので、これを256分割したところで、ドとかレとかの数百Hzレベルの細かい周波数指定はできません。うーん。

色々調べてみると、8pinoで使われている、ATTiny85には、PWMの基準の周波数(上の8MHz)を変更できる仕組みが入っているようです。こいつを使います。 ATtiny85 Datasheet によると、Pin1に関しては、TCCR0A及び、TCCR0BのWGMにFast PWMモードを設定し、TCCR0BのCS0*で基準の周波数を変更出来ることがわかります。

f:id:tetunori_lego:20150525185315j:plain 色々、選択肢があるので、ドレミを最もよく表現してくれるオプションを探すため、下記のような表を作りました。
(OCR0AはPWMに設定する0-255の値で、
frequency = 8 MHz/xxx/(OCR0A+1) です。
) f:id:tetunori_lego:20150525185508j:plain これにより、8MHz/256 = 32KHzが一番丁度良さそうであることがわかりました。
(上の表で言うと、真ん中の列を採用するという事)

結局、下記のような表を得ます。

音階 OCR0A
118
106
94

長くなりましたが、Pin1から、PWMでドレミ波形を出すには、下記の設定が必要となります。

// Configure Registers for PWM of PB1(PIN No.1)
// 1. Waveform Generation Mode -> Fast PWM
// 2. Clock Select -> 8MHz/256 = 32KHz.
// 3. Compare Match Output B Mode
// -> Clear OC0A/OC0B on Compare Match, 
// set OC0A/OC0B at BOTTOM
TCCR0A = _BV (COM0B1) | _BV (WGM01) | _BV (WGM00);
TCCR0B = _BV (WGM02) | _BV (CS02);

やっと音が鳴るが

ド、レ、ミと鳴るコードを書いて、音が出たものの、今度は何故か、delay関数が上手く動かない。 高い音になると、delayが短くなるように聞こえる。

どうやら、delayのタイマーは、OCR0Aが絡んでいるようだ。

もう、デバッグする気力もなく、直感で

delay( xxx / OCR0A );

としたら、いい感じにwww。直感大事。 正直調べていないので、理由はわかりませんwww

というわけで、Pin1に関しては、音を出せるようになりました。

Pin4も似た感じ

Pin1との違いは、扱うレジスタが違う位です。 基準の周波数の設定はTCCR1とGTCCRで行います (勿論、Pin1と合わせて32KHzに設定します)。 また、音階の指定はOCR1Cで行います。

まとめると、Pin4から音を出すには、下記の設定が必要です。

// Configure Registers for PWM of PB4(PIN No.4)
// 1. Comparator A Mode -> Set the OC1A output line.
// 2. Time/Counter1 Prescale Select -> 8MHz/256 = 32KHz.
// 3. Enable Pulse Width Modulator B.
// 4. Comparator B Mode -> Set the OC1B output line
TCCR1 = _BV (COM1A1) | _BV (COM1A0) | 
_BV (CS13) | _BV (CS10);
GTCCR = _BV (PWM1B) | _BV (COM1B1) | _BV (COM1B0);

カエルの合唱のコード

#include <Adafruit_NeoPixel.h>
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
 #include <avr/power.h>
#endif

#define NEOPIXEL_IN_PIN 0
#define NUM_OF_PIXELS 16
#define NEOPIXEL_BRIGHTNESS 30
Adafruit_NeoPixel pixels = Adafruit_NeoPixel( NUM_OF_PIXELS, NEOPIXEL_IN_PIN );

// Definitions
#define _8PINO_TONE_START ( 0x00 )
#define _8PINO_TONE_STOP  ( 0x01 )

#define _8PINO_TONE_NOT_USED ( 0 )

#define _8PINO_TONE_SO_2 ( 158 )
#define _8PINO_TONE_RA_2 ( 141 )
#define _8PINO_TONE_SI_2 ( 126 )
#define _8PINO_TONE_DO_3 ( 118 )
#define _8PINO_TONE_RE_3 ( 106 )
#define _8PINO_TONE_MI_3 (  94 )
#define _8PINO_TONE_FA_3 (  89 )
#define _8PINO_TONE_SO_3 (  79 )
#define _8PINO_TONE_RA_3 (  70 )
#define _8PINO_TONE_SI_3 (  62 )
#define _8PINO_TONE_DO_4 (  59 )

// Global variables
#define _8PINO_TONE_SPEAKER_PIN_1 ( 1 )
#define _8PINO_TONE_SPEAKER_PIN_2 ( 4 )

#define SIZE_OF_TONE_ARRAY 192
PROGMEM prog_uint16_t noteArray[] =
{
  
  // Wait...
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  
  // Ka-E-Ru-No-U-Ta-Ga
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_RE_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_MI_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_FA_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP,
  _8PINO_TONE_MI_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_RE_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  
  // Ki-Ko-E-Te-Ku-Ru-Yo
  _8PINO_TONE_MI_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_FA_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP,
  _8PINO_TONE_SO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP,
  _8PINO_TONE_RA_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP,
  _8PINO_TONE_SO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP,
  _8PINO_TONE_FA_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP,
  _8PINO_TONE_MI_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  
  // Qua-Qua-Qua-Qua
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  
  // Kero-Kero-Kero-Kero
  _8PINO_TONE_DO_3, _8PINO_TONE_STOP, _8PINO_TONE_DO_3, _8PINO_TONE_STOP, 
  _8PINO_TONE_RE_3, _8PINO_TONE_STOP, _8PINO_TONE_RE_3, _8PINO_TONE_STOP, 
  _8PINO_TONE_MI_3, _8PINO_TONE_STOP, _8PINO_TONE_MI_3, _8PINO_TONE_STOP, 
  _8PINO_TONE_FA_3, _8PINO_TONE_STOP, _8PINO_TONE_FA_3, _8PINO_TONE_STOP, 
  
    // Qua-Qua-Qua
  _8PINO_TONE_MI_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_RE_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  _8PINO_TONE_DO_3, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, 
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP, 
  
  // Wait...
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED, _8PINO_TONE_NOT_USED,
  

};

void setup(){
  
  // Initialize PIN mode.
  pinMode( _8PINO_TONE_SPEAKER_PIN_1, OUTPUT );
  pinMode( _8PINO_TONE_SPEAKER_PIN_2, OUTPUT );
  
  // Configure Registers for PWM of PB1(PIN No.1)
  //  1. Waveform Generation Mode -> Fast PWM
  //  2. Clock Select -> 8MHz/256 = 32KHz.
  //  3. Compare Match Output B Mode
  //    -> Clear OC0A/OC0B on Compare Match, 
  //        set OC0A/OC0B at BOTTOM
  TCCR0A = _BV (COM0B1) | _BV (WGM01) | _BV (WGM00);
  TCCR0B = _BV (WGM02) | _BV (CS02);
  
  // Configure Registers for PWM of PB4(PIN No.4)
  //  1. Comparator A Mode -> Set the OC1A output line.
  //  2. Time/Counter1 Prescale Select -> 8MHz/256 = 32KHz.
  //  3. Enable Pulse Width Modulator B.
  //  4. Comparator B Mode -> Set the OC1B output line
  TCCR1 = _BV (COM1A1) | _BV (COM1A0) | 
             _BV (CS13) | _BV (CS10);
  GTCCR = _BV (PWM1B) | _BV (COM1B1) | _BV (COM1B0);
  
  // NeoPixel LED
  pinMode( NEOPIXEL_IN_PIN, OUTPUT );
  
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
  if( F_CPU == 16000000 ) clock_prescale_set( clock_div_1 );
#endif
  pixels.begin();
  pixels.setBrightness( NEOPIXEL_BRIGHTNESS );
  pixels.show(); // Initialize all pixels to "off"
  
}

void loop(){
  
  for( int count = 32; count < SIZE_OF_TONE_ARRAY; count++ )
  {
    if( pgm_read_word( &noteArray[count] ) == _8PINO_TONE_NOT_USED ){
      // Do Nothing.
    }else if( pgm_read_word( &noteArray[count] ) == _8PINO_TONE_STOP ){
      _8pinoTone( _8PINO_TONE_SPEAKER_PIN_1, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP );
      setNeopixelColor( _8PINO_TONE_SPEAKER_PIN_1, _8PINO_TONE_STOP );
    }else{
      _8pinoTone( _8PINO_TONE_SPEAKER_PIN_1, pgm_read_word( &noteArray[count] ), _8PINO_TONE_START );
      setNeopixelColor( _8PINO_TONE_SPEAKER_PIN_1, pgm_read_word( &noteArray[count] ) );
    }
    
    if( pgm_read_word( &noteArray[count-32] ) == _8PINO_TONE_NOT_USED ){
      // Do Nothing.
    }else if( pgm_read_word( &noteArray[count-32] ) == _8PINO_TONE_STOP ){
      _8pinoTone( _8PINO_TONE_SPEAKER_PIN_2, _8PINO_TONE_NOT_USED, _8PINO_TONE_STOP );
      setNeopixelColor( _8PINO_TONE_SPEAKER_PIN_2, _8PINO_TONE_STOP );
    }else{
      _8pinoTone( _8PINO_TONE_SPEAKER_PIN_2, pgm_read_word( &noteArray[count-32] ), _8PINO_TONE_START );
      setNeopixelColor( _8PINO_TONE_SPEAKER_PIN_2, pgm_read_word( &noteArray[count-32] ) );
    }
    
    pixels.show();
    delay( 8000 / OCR0A );
  }
  
}

void _8pinoTone( int pin_speaker, uint8_t freq, uint8_t startstop ){
  if( startstop == _8PINO_TONE_START ){
    pinMode( pin_speaker, OUTPUT );
    if( pin_speaker == _8PINO_TONE_SPEAKER_PIN_1 ){
      OCR0A = freq;
      OCR0B = OCR0A - 1;
    }else{
      OCR1C = freq;
      OCR1B = OCR1C - 1;
    }
  }else{
     pinMode( pin_speaker, INPUT );
  }
}

void setNeopixelColor( int speaker_id, int tone ){
  
  uint8_t led_index = 0;
  int i = 0;
  
  if( speaker_id == _8PINO_TONE_SPEAKER_PIN_1 ){
    switch( tone ){
      case _8PINO_TONE_DO_3:
        led_index = 12;
        break;
      case _8PINO_TONE_RE_3:
        led_index = 13;
        break;
      case _8PINO_TONE_MI_3:
        led_index = 14;
        break;
      case _8PINO_TONE_FA_3:
        led_index = 15;
        break;
      case _8PINO_TONE_SO_3:
        led_index = 0;
        break;
      case _8PINO_TONE_RA_3:
        led_index = 1;
        break;
    }
    
    if( tone == _8PINO_TONE_STOP ){
      for( i = 0; i < 2; i++ ){
        pixels.setPixelColor( i, 0, 0, 0 );
      }
      for( i = 12; i < 16; i++ ){
        pixels.setPixelColor( i, 0, 0, 0 );
      }
    }else{
      for( i = 0; i < 2; i++ ){
        pixels.setPixelColor( i, 0, 0, 0 );
      }
      for( i = 12; i < 16; i++ ){
        pixels.setPixelColor( i, 0, 0, 0 );
      }
      if( led_index < 2 ){
        for( i = 0; i < led_index + 1; i++ ){
          pixels.setPixelColor( i, 255, 0, 0 );
        }
        for( i = 12; i < 16; i++ ){
          pixels.setPixelColor( i, 255, 0, 0 );
        }
        
      }else{
        for( i = 12; i < led_index + 1; i++ ){
          pixels.setPixelColor( i, 255, 0, 0 );
        }
      }
    }
    
  }else{
    switch( tone ){
      case _8PINO_TONE_DO_3:
        led_index = 11;
        break;
      case _8PINO_TONE_RE_3:
        led_index = 10;
        break;
      case _8PINO_TONE_MI_3:
        led_index = 9;
        break;
      case _8PINO_TONE_FA_3:
        led_index = 8;
        break;
      case _8PINO_TONE_SO_3:
        led_index = 7;
        break;
      case _8PINO_TONE_RA_3:
        led_index = 6;
        break;
    }
    
    if( tone == _8PINO_TONE_STOP ){
      for( i = 6; i < 12; i++ ){
        pixels.setPixelColor( i, 0, 0, 0 );
      }
    }else{
      for( i = 6; i < 12; i++ ){
        pixels.setPixelColor( i, 0, 0, 0 );
      }
      for( i = led_index; i < 12; i++ ){
        pixels.setPixelColor( i, 0, 255, 0);
      }
    }
    
  }
  
}

音階のdefine名、長すぎた…

読んでくれた方も

お疲れさまでした。 とりあえず、アウトプットを出せてホッとしております。

次回以降はなるべく軽い投稿を目指します…

8pinoで圧電スピーカーを鳴らす その2

前回の投稿で

せっかくスピーカーが2つあるわけですし、8pinoのポートもまだ空きがあるので、2つ同時出力や和音が出来ないか、トライしてみます。

なんて気軽に発言してしまったことを、大変後悔しております。 まさか、土日の自由時間の大半を食い潰すとは、夢にも思いませんでした…

今回の目的

さて、今回は、8pinoと2つの圧電スピーカーを使って、「カエルの合唱」とか「静かな湖畔」あたりを1人輪唱させたいです。

まずは、結線。

前回の奴にスピーカーを一個追加するだけです。 f:id:tetunori_lego:20150525065042j:plain

前例探し

意外にも、どれだけ検索しても、2つのブザーを使って何かやってるものは見当たりませんでした。
自分でソリューションを考えださなくてはいけません。

アプローチ1 → 失敗

前の投稿 で使ったTrinketToneを改造して、2つのピンに対して波形の出力が出来ないか試しました。

ATTiny85のデータシート
を合わせて見ると、TrinketToneはPin番号1(正確にはOC1A)固定の実装になっているので、他のPin(今回はOC1B)の方でも同じようにできるかやってみました。

結果

  • 別のPinからでも、TrinketToneが動く様になった!
  • 2つ同時に鳴らそうとすると、何も音が出てこなくなる…

というのも、良く良くみると、TrinketToneのロジックは、Pin番号と関係ないレジスタ、TCCRを使っており、この人が双方からアクセスされることで、不整合が起きていると思われます。(予想)

時間的にも、精神的にも深手を負ってしまったので、このアプローチは諦めます…

アプローチ2 → 失敗

Trinketのサンプルコードにあるように、GPIO H/Lを自力でパタパタさせて音を出す方法(例↓)を工夫できないか?

void loop() {
   digitalWrite(speakerPin, HIGH);
   delayMicroseconds(1000);
   digitalWrite(speakerPin, LOW);
   delayMicroseconds(1000);
}

具体的には、数usec(欲しい和音の周波数の最大公約数的な数)のdelayを入れて、各PinをH/Lにするべきか否かを判定する

結果

ぶつぶつとノイズしかでてこない。

1usecで起きた時の処理時間あるから、出力波形乱れるのかなぁ。 もう、デバッグする気力はありません…

アプローチ3 → 成功?

arduinoのPWM

pulse width modulation : Pinから、指定した通りの波形を出力し続けてくれる機能。LEDの明るさを可変にする時とかに使う。と研修で習った。

を使って圧電スピーカー鳴らしている人がいました。
これを改変出来ないだろうか?

まずは原理確認するために、上記で実装されている踏切音コードを8pino向けにポーティングしてみます。

んー、なんか、参照先の動画に比べて、音がおかしい気がするけど、出来た。音が鳴ってはいる。

PWMを使う方式で、深堀してみます。

午前の部、終わり

失敗が続くと、かなり体力を使います。 夕方から、予定があるので、それまでにアウトプット出したい…

8pinoで圧電スピーカーを鳴らす その1

スピーカー買いました

8pinoの再購入の際、送料無料まであと少しだったので、100円位の品を物色していたら、こちらを見つけました。

今までの経緯から、壊してしまうことも考え、二個、購入していました。

到着したので、早速ピンヘッダをハンダ付け。 f:id:tetunori_lego:20150521064945j:plain

鳴らしてみる

Arduinoには、ブザー用に音の波形を出力してくれるAPIがあります。便利。

Arduino - Tone

tone(ピン番号, 音程, 長さ) 

な感じで、音が出るそう。 ビルド→オッケー! 焼きこみます、、、が何も起きず。

何回か試行錯誤した末にググると、ありました。 ImageWriter: 8pinoではtone()関数は使えない。The tone library does not work for the 8pino(Trinket).

8pino(Trinket)では、toneライブラリが使えません。 スケッチでtone()を使ってもコンパイルエラーは出ませんが、動作しません。

まじか。

代替えのbeep()関数がありましたので、早速実験してみました。

ふむふむ、では、わたしも。

鳴りました。 「ビーーーー

うん、こういうんじゃない

何とか曲を奏でたい

trinketでも困っている人がいるはずだ!と色々探してみると、やはり、いるものです。

http://w8bh.net/avr/TrinketTone.pdf

こちらの関数を使えば、tone()っぽく使えます。参照先の方が、バッハを実装しているので、そちらをコピペします。

それだけだとつまらないので、演奏しながら、音の高さをNeopixel上に表示してみます。色を紫→黄色と、スムーズにグラデーションさせているのが、無用なこだわりです。

動画はこちら。

※ 注意! つまらない上に長いですww

コードはこちら。

#include <avr/pgmspace.h> // needed for PROGMEM

/*************************************************
* For Adafruit NeoPixel
*************************************************/
#include <Adafruit_NeoPixel.h>
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
 #include <avr/power.h>
#endif

#define NEOPIXEL_IN_PIN 0
#define NUM_OF_PIXELS 16

Adafruit_NeoPixel pixels = Adafruit_NeoPixel( NUM_OF_PIXELS, NEOPIXEL_IN_PIN );

/*************************************************
* Note Pitch Constants
*************************************************/
#define B0 31
#define C1 33
#define CS1 35
#define D1 37
#define DS1 39
#define E1 41
#define F1 44
#define FS1 46
#define G1 49
#define GS1 52
#define A1 55
#define AS1 58
#define B1 62
#define C2 65
#define CS2 69
#define D2 73
#define DS2 78
#define E2 82
#define F2 87
#define FS2 93
#define G2 98
#define GS2 104
#define A2 110
#define AS2 117
#define B2 123
#define C3 131
#define CS3 139
#define D3 147
#define DS3 156
#define E3 165
#define F3 175
#define FS3 185
#define G3 196
#define GS3 208
#define A3 220
#define AS3 233
#define B3 247
#define C4 262
#define CS4 277
#define D4 294
#define DS4 311
#define E4 330
#define F4 349
#define FS4 370
#define G4 392
#define GS4 415
#define A4 440
#define AS4 466
#define B4 494
#define C5 523
#define CS5 554
#define D5 587
#define DS5 622
#define E5 659
#define F5 698
#define FS5 740
#define G5 784
#define GS5 831
#define A5 880
#define AS5 932
#define B5 988
#define C6 1047
#define CS6 1109
#define D6 1175
#define DS6 1245
#define E6 1319
#define F6 1397
#define FS6 1480
#define G6 1568
#define GS6 1661
#define A6 1760
#define AS6 1865
#define B6 1976
#define C7 2093
#define CS7 2217
#define D7 2349
#define DS7 2489
#define E7 2637
#define F7 2794
#define FS7 2960
#define G7 3136
#define GS7 3322
#define A7 3520
#define AS7 3729
#define B7 3951
#define C8 4186
#define CS8 4435
#define D8 4699
#define DS8 4978
/*************************************************
* Note Duration Constants (not used in this app)
*************************************************/
#define FN 1 // 64th note
#define TN 2 // 32nd note
#define DTN 3 // dotted 32nd note
#define SN 4 // 16th note
#define DSN 6 // dotted 16th note
#define EN 8 // 8th note
#define DEN 12 // dotted 8th note
#define QN 16 // quarter note
#define DQN 24 // dotted quarter note
#define HN 32 // half note
#define DHN 48 // dotted half note
#define WN 64 // whole note
#define DWN 96 // dotted whole note

#define SIZE_OF_LED_INDEX_ARRAY 32
PROGMEM prog_uint16_t ledIndexArray[] =
{
  C2, F2, FS2, G2, GS2, B2, C3, D3, DS3, E3, F3, G3, GS3, A3, AS3, B3, C4, D4, DS4, E4, F4, FS4, G4, A4, B4, C5, CS5, D5, E5, F5, G5, A5
};

/*************************************************
* Music Content is stored in the following array.
* PROGMEM is needed because of the large data size.
* "Prelude in C" Well-Tempered Clavier, J.S.Bach
*************************************************/
#define NOTECOUNT 544 // length of array
PROGMEM prog_uint16_t noteArray[] =
{
  C4, E4, G4, C5, E5, G4, C5, E5, //measures 1-2, notes 1-32
  C4, E4, G4, C5, E5, G4, C5, E5,
  C4, D4, A4, D5, F5, A4, D5, F5,
  C4, D4, A4, D5, F5, A4, D5, F5,
  B3, D4, G4, D5, F5, G4, D5, F5, //measures 3-4, notes 33-64
  B3, D4, G4, D5, F5, G4, D5, F5,
  C4, E4, G4, C5, E5, G4, C5, E5,
  C4, E4, G4, C5, E5, G4, C5, E5,
  
  C4, E4, A4, E5, A5, A4, E5, A5, //measures 5-6, notes 65-96
  C4, E4, A4, E5, A5, A4, E5, A5,
  C4, D4, FS4,A4, D5, FS4,A4, D5,
  C4, D4, FS4,A4, D5, FS4,A4, D5,
  
  B3, D4, G4, D5, G5, G4, D5, G5, //measures 7-8, notes 97-128
  B3, D4, G4, D5, G5, G4, D5, G5,
  B3, C4, E4, G4, C5, E4, G4, C5,
  B3, C4, E4, G4, C5, E4, G4, C5,
  
  A3, C4, E4, G4, C5, E4, G4, C5, //measures 9-10, notes 129-160
  A3, C4, E4, G4, C5, E4, G4, C5,
  D3, A3, D4, FS4,C5, D4, FS4,C5,
  D3, A3, D4, FS4,C5, D4, FS4,C5,
  
  G3, B3, D4, G4, B4, D4, G4, B4, //measures 11-12, notes 161-192
  G3, B3, D4, G4, B4, D4, G4, B4, 
  G3, AS3,E4, G4, CS5,E4, G4, CS5,
  G3, AS3,E4, G4, CS5,E4, G4, CS5,
  F3, A3, D4, A4, D5, D4, A4, D5, //measures 13-14, notes 193-224
  F3, A3, D4, A4, D5, D4, A4, D5,
  F3, GS3,D4, F4, B4, D4, F4, B4,
  F3, GS3,D4, F4, B4, D4, F4, B4,
  
  E3, G3, C4, G4, C5, C4, G4, C5, //measures 15-16, notes 225-256
  E3, G3, C4, G4, C5, C4, G4, C5,
  E3, F3, A3, C4, F4, A3, C4, F4,
  E3, F3, A3, C4, F4, A3, C4, F4,
  
  D3, F3, A3, C4, F4, A3, C4, F4, //measures 17-18, notes 257-288
  D3, F3, A3, C4, F4, A3, C4, F4,
  G2, D3, G3, B3, F4, G3, B3, F4,
  G2, D3, G3, B3, F4, G3, B3, F4,
  C3, E3, G3, C4, E4, G3, C4, E4, //measures 19-20, notes 289-320
  C3, E3, G3, C4, E4, G3, C4, E4,
  C3, G3, AS3,C4, E4, AS3,C4, E4,
  C3, G3, AS3,C4, E4, AS3,C4, E4,
  F2, F3, A3, C4, E4, A3, C4, E4, //measures 21-22, notes 321-352
  F2, F3, A3, C4, E4, A3, C4, E4,
  FS2,C3, A3, C4, DS4,A3, C4, DS4,
  FS2,C3, A3, C4, DS4,A3, C4, DS4,
  GS2,F3, B3, C4, D4, B3, C4, D4, //measures 23-24, notes 353-384
  GS2,F3, B3, C4, D4, B3, C4, D4,
  G2, F3, G3, B3, D4, G3, B3, D4,
  G2, F3, G3, B3, D4, G3, B3, D4,
  
  G2, E3, G3, C4, E4, G3, C4, E4, //meaures 25-26, notes 385-416
  G2, E3, G3, C4, E4, G3, C4, E4,
  G2, D3, G3, C4, F4, G3, C4, F4,
  G2, D3, G3, C4, F4, G3, C4, F4,
  G2, D3, G3, B3, F4, G3, B3, F4, //measures 27-28, notes 417-448
  G2, D3, G3, B3, F4, G3, B3, F4,
  G2, DS3,A3, C4, FS4,A3, C4, FS4,
  G2, DS3,A3, C4, FS4,A3, C4, FS4,
  G2, E3, G3, C4, G4, G3, C4, G4, //measures 29-30, notes 449-480
  G2, E3, G3, C4, G4, G3, C4, G4,
  
  G2, F3, G3, C4, F4, G3, C4, F4,
  G2, F3, G3, C4, F4, G3, C4, F4,
  G2, F3, G3, B3, F4, G3, B3, F4, //measures 31-32, notes 481-512
  G2, F3, G3, B3, F4, G3, B3, F4,
  C2, C3, G3, AS3,E4, G3, AS3,E4,
  C2, C3, G3, AS3,E4, G3, AS3,E4,
  C2, C3, F3, A3, C4, F4, C4, A3, //measures 33-34, notes 513-544
  C4, A3, F3, A3, F3, D3, F3, D3,
  C2, B2, G4, B4, D5, F5, D5, B4,
  D5, B4, G4, B4, D4, F4, E4, D4,
  
};

volatile uint32_t toggle_count;
// TrinketTone:
// Generate a square wave on a given frequency & duration
// Call with frequency (in hertz) and duration (in milliseconds).
// Uses Timer1 in CTC mode. Assumes PB1 already in OUPUT mode.
// Generated tone is non-blocking, so routine immediately
// returns while tone is playing.
void TrinketTone(uint16_t frequency, uint32_t duration)
{
  // scan through prescalars to find the best fit
  uint32_t ocr = F_CPU/frequency/2;
  uint8_t prescalarBits = 1;
  while (ocr>255)
  {
    prescalarBits++;
    ocr /= 2;
  }
  // CTC mode; toggle OC1A pin; set prescalar
  TCCR1 = 0x90 | prescalarBits;

  // Calculate note duration in terms of toggle count
  // Duration will be tracked by timer1 ISR
  toggle_count = frequency * duration / 500;
  OCR1C = ocr-1; // Set the OCR
  bitWrite(TIMSK, OCIE1A, 1); // enable interrupt
}

// Timer1 Interrupt Service Routine:
// Keeps track of note duration via toggle counter
// When correct time has elapsed, counter is disabled
ISR(TIMER1_COMPA_vect)
{
  
  if (toggle_count != 0) // done yet?
  toggle_count--; // no, keep counting
  else // yes,
  TCCR1 = 0x90; // stop the counter
  
}

void light_NeopixelRing( uint16_t note )
{
  
  int i, j = 0;
  for(i = 0; i < SIZE_OF_LED_INDEX_ARRAY; i++ ){
    if( note == pgm_read_word( &ledIndexArray[i] ) ){
      // Found!
      break;
    }
  }
  
  // Initialize pixels
  for(j = 0; j < NUM_OF_PIXELS; j++){
    pixels.setPixelColor(j, 0, 0, 0);
  }
  
  for(j = 0; j < i / 2; j++){
    pixels.setPixelColor(j, Wheel((j * 172 / pixels.numPixels() + 140) & 255));
  }
  pixels.show();
  
}

void turnOffNeopixel()
{
  
  int i = 0;
  
  // Initialize pixels
  for(i = 0; i < NUM_OF_PIXELS; i++){
    pixels.setPixelColor(i, 0, 0, 0);
  }
  
  pixels.show();
  
}

// PlayBach:
// Plays "Prelude in C", which is held in noteArray
// Uses PROGMEM to store array, due to large size
void PlayBach()
{
  
  int len = 150; // modify for speed
  int dly = 190; // modify for articulation
  for (int count=0; count<NOTECOUNT; count++)
  {
    
    light_NeopixelRing( pgm_read_word( &noteArray[count] ) );
    TrinketTone( pgm_read_word( &noteArray[count] ), len);
    delay(dly);
    if ((count>512) and (count<540)) // slow down (rit.) at end.
    {
      len += 3;
      dly += 5;
    }
  
  }
  
  TrinketTone(C4,1500); // final note
  turnOffNeopixel();
  delay(1500);
  
}

uint32_t Wheel(byte WheelPos) {
  
  if(WheelPos < 85) {
   return pixels.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return pixels.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return pixels.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  
}

void setup()
{
  
  pinMode( NEOPIXEL_IN_PIN, OUTPUT );
  
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
  if( F_CPU == 16000000 ) clock_prescale_set( clock_div_1 );
#endif
  pixels.begin();
  pixels.setBrightness(30);
  pixels.show(); // Initialize all pixels to "off"
  
  pinMode(1, OUTPUT); // enable OUTPUT (PB1, #1)
  PlayBach(); // Music!
  pinMode(1, INPUT); // disable OUTPUT
  
}

void loop()
{
  
  // once is enough. Really.
  
}

次は

基本動作は確認出来たのですが、せっかくスピーカーが2つあるわけですし、8pinoのポートもまだ空きがあるので、2つ同時出力や和音が出来ないか、トライしてみます。

8pino リベンジ。の続き

前回までのお話

回路までは組めたはずだが…

早速光らせます。 f:id:tetunori_lego:20150520194450j:plain 成功!

それだけだと、つまらないので

Neopixelのコードを1から書いて見ることにしました。

結果、こんな感じ。 色と明るさを制御してみました。

コードはこちら。

#include <Adafruit_NeoPixel.h>
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
 #include <avr/power.h>
#endif

#define NEOPIXEL_IN_PIN 0
#define NUM_OF_PIXELS 16
#define BRIGHTNESS_MAX 30
#define DELAY_TIME_FOR_GRADATION 70

Adafruit_NeoPixel pixels = Adafruit_NeoPixel( NUM_OF_PIXELS, NEOPIXEL_IN_PIN );

void setup() {
  
  pinMode( NEOPIXEL_IN_PIN, OUTPUT );
  
#ifdef __AVR_ATtiny85__ // Trinket, Gemma, etc.
  if( F_CPU == 16000000 ) clock_prescale_set( clock_div_1 );
#endif
  pixels.begin();
  pixels.show(); // Initialize all pixels to "off"
  
}

void loop() {
  
  uint8_t  i = 0;
  
  for( i = 0; i < NUM_OF_PIXELS; i++ ){
    if( i < ( NUM_OF_PIXELS / 4 ) ){
      pixels.setPixelColor(i, 255, 0, 0);
    }else if( i < ( ( 2 * NUM_OF_PIXELS ) / 4 ) ){
      pixels.setPixelColor(i, 0, 255, 0);
    }else if( i < ( ( 3 * NUM_OF_PIXELS ) / 4 ) ){
      pixels.setPixelColor(i, 0, 0, 255);
    }else if( i < NUM_OF_PIXELS ){
      pixels.setPixelColor(i, 255, 255, 255);
    }
  }
  
  for( i = 1; i < BRIGHTNESS_MAX; i++ ){
    
    pixels.setBrightness(i);
    
    // Reflect some settings and light them.
    pixels.show();
    delay( DELAY_TIME_FOR_GRADATION );
    
  }
  
}

わかったこと

  • 明るさは0-255まで代入可能だが、50以上は眩しすぎる
  • 明るさを一度0にしてしまうと、以後、値を設定してもうんすん
  • 明るさは個々のLEDには設定できない
  • センスがないと、光っても全然かっこ良くない

とても気になること

この回路にしてから、手持ちのモバイルバッテリーからの給電では、Neopixelが光らなくなってしまいました。 (8pinoは動作している)
PCからの給電だと、LEDも光るのですが… たぶん、僕の回路が悪いのだろうなぁ。

とりあえず無事、リベンジは果たせたので、満足です。

8pino リベンジ。

リベンジの狼煙

以前、こんなことになって、 しょぼくれていましたが、早速、リベンジします。

Switch science様で、8pinoを複数枚購入しました。

人間とは、失敗から学べるところが素晴らしい生き物です。 こういうものも、頼んでみました。

こいつを使えば、8pinoには直接USBを挿さなくても、updateが可能になるはずです! (Adafruit trinketにしないのは、意地です。)

回路を確認してみる

8pinoはOpen Hardwareな思想なので、回路図が公開されています。 f:id:tetunori_lego:20150520192359j:plain 要は、あたかも、コネクタが挿されたかの様に、ATTinyをだまくらかせば良いのだよね?

とすると、 VBUSとGNDはブレットボードの+と-に繋げばよいですね。 あと、Data線は何か抵抗が要りそうです。

秋月にて、1.5kΩと、68Ωの抵抗を仕入れました。66.5Ωというものは見当たらなかった…

さて、結線

マウント図?もUpされています。 f:id:tetunori_lego:20150520190921j:plain やべぇ、こっちは見方わかんないぞ。 何かプラスって書いてある方をなぞると、2番目のピンの近くに辿り着くので、こっちがD+でしょう!(結果、アタリ)

8pino側を結線してみた結果はこちら。 8pino+化にちょっと失敗して、ハンダ付け汚いのは、スルーしてください。 f:id:tetunori_lego:20150520192826j:plain

USBの変換基板もハンダ付けして、結線。 f:id:tetunori_lego:20150520193211j:plain f:id:tetunori_lego:20150520191614j:plain

ところで

USBのデータ線、普通はD+の方にプルアップ抵抗があるのに、なんで8pinoはD-の方を吊るんだろうと、結構悩みました。

ググって解決。 Low Speedデバイスとして認識させるためですね。

えっ、Low Speedなの? Full Speedが使えないのはATTinyの仕様なのでしょうか?

ここで、力尽きる

また、次回。

8pino、壊れる。

ふりかえると

前回、こんなこと書いていましたが、

ところで、色々なLED点灯firmを焼いていると、8pinoがPCに認識されなくなったり、電源が入らない現象が多発しました。
micro USBの口をぎゅっと押し付けてあげると、認識するので、まぁ、誤魔化しつつ、開発を続けます。

やっぱりダメになってしまいました。

原因はUSBの接点部分だと思います。 気を付けてはいたのですが、抜き差しする度に、各接点の金属がびろーんって伸びてきました。

壊れる直前、びろーんがついに広がりすぎて、VBusとデータ線がショートしたようでした。

あれっ、あれっ、と確認していたら、このザマ。接点の金属が剥がれてきました。 f:id:tetunori_lego:20150519193551j:plain

それでも、と、試しにUSB 5Vを入れて見ましたが、Vccから電圧が出てきません(涙)

8pino説明書によると、

8pinoには逆流防止ダイオードが搭載されていませんので、USBと別電源・バッテリ等の同時使用は絶対におやめください(必ず片側をOFF/切り離す)

とありますので、これはつまり、二度とfirmwareを焼けない、と言うことですので、ご臨終となります。 実働2日と短い命でしたが、楽しませてもらいました…

ところで

その説明書には、こうも記載がありました。

8pinoのUSBポートの端子の挿抜寿命は数百回程度ですので、デバッグの際は必ずPC側のUSBコネクタを抜き差ししてください

気を付けていたので、8pino側は30回も抜き差ししてないと思います。使っていたUSBケーブルがきつくてダメだったのかなぁ。

8pinoでAdafruit Neopixel Ringを光らせる その2

おさらい

前回の投稿では、とりあえずLEDを光らせただけです。

今回は、是非ともレインボーカラーを実現したいです。 といっても、githubをうろうろするだけですが。

見つけた

どこで見つけたかは、失念。

LEDの数と、pin番号あわせて、あと何か変更をいれた気がするが、それも忘れました。すべてはコードに聞いてください。

当初、頭に描いていた制御になりました。

コードはこちら。

#include <Adafruit_NeoPixel.h>

#define PIN 0

int minLEDNum = 0;
int maxLEDNum = 16;

int animStartLEDNum = 0;

int ranLEDNum = 0;
int lastRanLEDNum = 0;

Adafruit_NeoPixel strip = Adafruit_NeoPixel(maxLEDNum, PIN, NEO_GRB + NEO_KHZ800);

uint32_t cExelonBlueBright = strip.Color(0, 0, 204); 
uint32_t cExelonOrangeBright = strip.Color(255, 128, 0); 
uint32_t cExelonGreenBright = strip.Color(0, 153, 0);

uint32_t cLampBright = strip.Color(255, 255, 0); 
//uint32_t cWhiteBright = strip.Color(255, 255, 255); 
uint32_t cOff = strip.Color(0, 0, 0);

void setup(){
  strip.begin();
  strip.setBrightness(50);
  lamp(cLampBright);  // Turn on the lamp
  strip.show();
}

void loop(){
  animationController(random(1,4),random(5));
}

// the main animation controller
void animationController(uint8_t animationNum, uint8_t repeatCount){
   switch (animationNum){
     case 1:
       pulseWave(cExelonBlueBright, 15, 0.80, repeatCount, 20);
       pulseWave(cExelonOrangeBright, 15, 0.80, repeatCount, 20);
       pulseWave(cExelonGreenBright, 15, 0.80, repeatCount, 20);
       break;
     case 2:
       rainbowCycle(repeatCount, 5);
       break;
     case 3:
       colorWipe(cExelonBlueBright, repeatCount, 40);
       colorWipe(cExelonOrangeBright, repeatCount, 40);
       colorWipe(cExelonGreenBright, repeatCount, 40);
       break;
   }
}

// Set the lamp pixels for the light-bulb 
void lamp(uint32_t c){
  for(int j=minLEDNum; j<animStartLEDNum; j++){
    strip.setPixelColor(j, c);
  }
  strip.show();
}

// Bright head followed by a tail
void pulseWave(uint32_t c, uint8_t tailLength, double fadePercent, uint8_t repeatCount, uint8_t wait){
  for( uint8_t r=0; r<repeatCount; r++){  // how many times to cycle
    for( uint8_t i=animStartLEDNum; i<strip.numPixels(); i++){ // cycle through the strip
      animationOff(); // turn off animation pixels
      strip.setPixelColor(i, c); // set head color
      for( uint8_t z=1; z<tailLength; z++){
        if(i-z >= animStartLEDNum){
          strip.setPixelColor(i-z, strip.Color(  getPixelColorComponent(i-z+1,1) * fadePercent,
                                                 getPixelColorComponent(i-z+1,2) * fadePercent,
                                                 getPixelColorComponent(i-z+1,3) * fadePercent));
        }
      }
      strip.show();
      delay(wait);
    }
  }
}

// Fill the dots one after the other with a color
void colorWipe(uint32_t c, uint8_t repeatCount, uint8_t wait) {
  for(uint8_t r=0; r<repeatCount; r++) { // how many times to cycle
   for(uint16_t i=animStartLEDNum; i<strip.numPixels(); i++) {
      strip.setPixelColor(i, c);
      strip.show();
      delay(wait);
    }
    animationOff(); // turn off animation pixels
    strip.show();
  }
}

// Slightly different, this makes the rainbow equally distributed throughout
void rainbowCycle(uint8_t repeatCount, uint8_t wait) {
  uint16_t i, j;
  for(j=0; j<256*repeatCount; j++) { // repeatCount cycles of all colors on wheel
    for(i=animStartLEDNum; i< strip.numPixels(); i++) {
      strip.setPixelColor(i, Wheel(((i * 256 / strip.numPixels()) + j) & 255));
    }
    strip.show();
    delay(wait);
  }
}

// turn off the animation pixels
void animationOff(){
  for( uint8_t x=animStartLEDNum; x<strip.numPixels(); x++){
    strip.setPixelColor(x, cOff);
  }
  strip.show();
}

// Get a color value by segment (R=1, G=2, B=3) 
uint8_t getPixelColorComponent(uint8_t pixelNum, uint8_t segment){
  uint8_t segmentColorValue;
  uint32_t c = strip.getPixelColor(pixelNum);
  switch( segment ){
    case 1:
      segmentColorValue = ( c >> 16) & 255;
      break;
    case 2:
      segmentColorValue = ( c >> 8) & 255;
      break;
    case 3:
      segmentColorValue = c & 255;
      break;
  }
  return segmentColorValue;
}

// 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) {
  if(WheelPos < 85) {
   return strip.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
  } else if(WheelPos < 170) {
   WheelPos -= 85;
   return strip.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  } else {
   WheelPos -= 170;
   return strip.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
}

コードを見ていると、なんとなく制御方法が見えてきたので、色々な点灯方法を試してみようかと思います。

ところで

色々なLED点灯firmを焼いていると、8pinoがPCに認識されなくなったり、電源が入らない現象が多発しました。

micro USBの口をぎゅっと押し付けてあげると、認識するので、まぁ、誤魔化しつつ、開発を続けます。