ATTiny85で赤外線送信する

既出すぎる内容な気もしますが、いわゆるArduino端末によく使われる
ATMega328Pではなく、ATTiny85を用いた内容、かつ
irRemoteライブラリを使わない実装としたため記事にします。

この記事は下書きのまま一年以上放置してしまいましたが
編集途中でも公開しておきます。

# 背景

オーム社の照明リモコンCRS-01Wは、壁スイッチでON/OFFすると
OFFから開始されるので、リモコンでONしなければ電気が点かない。
とても不便なので壁スイッチONでリモコンONの操作もさせたいと思います。

シーリングソケット
├  通電した時にだけON信号を出す赤外線送信機
└ シーリング照明

# 実装

## sketch

```c
#include <avr/wdt.h> // wdt_disable()を使うため
#define OUTPUT_PIN  (_BV(4) | _BV(1)) // 4:インジケータ、1:赤外線LED (PWM)

static const unsigned int irSignal[] = { // オーム社の照明リモコンCRS-01W：スイッチON信号
//  ON, OFF,  ON,OFF,  ON,OFF,  ON,OFF,  ON, OFF,  ON, OFF,  ON, OFF,  ON, OFF,  ON,OFF,  ON, OFF
  2550,2700, 800,850, 850,850, 800,850, 850,1900, 800,1900, 850,1850, 850,1900, 800,850, 850,9000
};

/**
 * 赤外線LEDを38kHzで点灯させる（1番ピン）
 */
void pwm_out(int microseconds) {
  PORTB |= _BV(4); // インジケータ点灯：digitalWrite(4, HIGH)と同じ
  // タイマー(TC0)を設定
  OCR0A = 25;  // TOP value :  38kHz --> 26.3us.
  OCR0B = 6;  //  H period  : 6.4us.
  TCCR0A = B00100011; // COM0A: 00, COM0B: 10, WGM01-00: 11
  TCCR0B = B00001001; // FOC:00, WGM02:1, CS02-01: 001 (system clock x1)
  TCNT0 = OCR0A;
  delayMicroseconds(microseconds); // 指定マイクロ秒だけ点灯
  // タイマー(TC0)をリセット
  TCCR0A = 0; // タイマー(TC0)リセット
  TCCR0B = 0; // タイマー(TC0)リセット
  PORTB &= ~_BV(4); // インジケータ消灯：digitalWrite(4, LOW)と同じ
}
 
void setup() {
  /**
   * 初期化
   */
  cli(); // noInterrupts();の中身（割り込み停止）
  DDRB = OUTPUT_PIN; // B010010000　pinMode(PB1, OUTPUT),pinMode(PB4, OUTPUT)
  PORTB = 0; // B00000000　PB1もPB4もLOW
  ACSR |= 0x80; // アナログコンパレータ禁止
  ADCSRA &= 0x7f; // disable ADC
  wdt_disable(); // ウォッチドッグタイマを停止
  TIMSK = 0; // disable Timer0/1 overflow interrupt.
  sei(); // noInterrupts();の中身（割り込み停止）

  /**
   * ループ
   */
  int len = sizeof(irSignal) / sizeof(irSignal[0]); // 送信する信号の数
  int ir_mcs = 0; // マイクロ秒。信号の送信に要する時間
  for (int i = 0; i < len; i++) {
    ir_mcs += irSignal[i]; // 合計：32,650マイクロ秒＝32.65ミリ秒（9000マイクロ秒の赤外線OFFはダミー）
  }
  int interval_ms = 50; // ミリ秒。次の信号を送信するまでの間隔
  int loop_ms = 1000; // ミリ秒。信号を送信し続ける時間
  int loop_cnt = loop_ms / interval_ms; // ループの回数を計算
  // 次の信号を送信するまでの待ち時間（ミリ秒）
  // 　＝インターバル（ミリ秒）-（１回あたりの赤外線送信時間（マイクロ秒）÷1000）
  double wait_ms = interval_ms - (ir_mcs / 1000); // 50 - 32.65 = 17.35
  for (; loop_cnt > 0; loop_cnt--) {
    // このループ全体で一つの赤外線信号
    for (int i = 0; i < len; i += 2) { // １回のループでONとOFFを処理する
      pwm_out(irSignal[i]); // 赤外線ON（38kHzで指定のマイクロ秒だけ点灯）
      delayMicroseconds(irSignal[i+1]); // 赤外線OFF
    }
    // インターバル。delayが使えないのでdelayMicrosecondsを必要ms分ループ
    for (int i = 0; i < wait_ms; i++) { // （小数点切り捨て＝17ミリ秒待つ処理）
      delayMicroseconds(1000); // 1ミリ秒待つ
    }
  }
}

// Arduinoのタイマーは使わないため、loop()では何も処理しない
void loop() {}
```

## 回路図

![スクリーンショット 2020-12-24 12.53.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/127776/957143a4-8654-212e-039c-8ebe499cc6fd.png)

* USB電源
  * [スイッチングＡＣアダプター（ＵＳＢ　ＡＣアダプター）　Ｔｙｐｅ－Ａメス　５Ｖ２Ａ](https://akizukidenshi.com/catalog/g/gM-13658/)
  * [ＵＳＢケーブル　ＵＳＢ２．０　Ａオス－マイクロＢオス　１．５ｍ　Ａ－ｍｉｃｒｏＢ](https://akizukidenshi.com/catalog/g/gC-07607/)
  * [基板用マイクロＵＳＢコネクタ（電源専用）](https://akizukidenshi.com/catalog/g/gC-10398/)
* 基板
  * [両面スルーホールガラスコンポジット・ユニバーサル基板　Ｅタイプ](https://akizukidenshi.com/catalog/g/gP-12725/)
* R1 / R2
  * [金属皮膜抵抗　１／４Ｗ２２０Ω　（１００本入）](https://akizukidenshi.com/catalog/g/gR-03359/)
* ATTiny85
  * [ＡＶＲマイコン　ＡＴＴＩＮＹ８５－２０ＰＵ](https://akizukidenshi.com/catalog/g/gI-09573/)
* LED
  * [５ｍｍ赤外線ＬＥＤ　９４０ｎｍ　ＯＳＩＲ５１１３Ａ　（１００個入）](https://akizukidenshi.com/catalog/g/gI-00656/)

# 解説

## 赤外線信号の仕組み

今回学んだことを羅列します。

* 赤外線信号は **点灯・消灯を繰り返す時間の長さ** で情報を伝えます（モールス信号みたいなもの）
* 一般的に赤外線通信に使う赤外線の波長は **900nm** らしいです（LEDを購入するときに念のため確認しましょう）
* 受信側は太陽光など他の光と区別するために **38kHzという周波数の光だけを受信する** ようにできています
 * つまり発信側も38kHzで発光させる必用があります
 * 38kHzとは **26.3μ秒で一周期** で、この間にON/OFFを１回します（ONは6μ秒、残り時間はOFFで良いみたいです→省電力）
  * デューティー比＝1/3：一周期でONになっている時間は1/3の時間とする（つまり8.7μ秒がON、上記の6μ秒と矛盾してるけどw）
  * 送信機の条件は **38kHzで発光できること** 、 **点灯・消灯時間をμ秒で制御できること** 、になります

ちなみに今回の信号は `irSignal` で定義したとおりです。単位は `μ秒` です。
（最後のOFF継続時間9000μ秒は、信号終了が判断可能な時間を十分に確保しようと、私が勝手に追加したものです）

```c:赤外線信号の定義
static const unsigned int irSignal[] = { // オーム社の照明リモコンCRS-01W：スイッチON信号
//  ON, OFF,  ON,OFF,  ON,OFF,  ON,OFF,  ON, OFF,  ON, OFF,  ON, OFF,  ON, OFF,  ON,OFF,  ON, OFF
  2550,2700, 800,850, 850,850, 800,850, 850,1900, 800,1900, 850,1850, 850,1900, 800,850, 850,9000
};
```

## PWM

ATTiny85の[PWM(パルス幅変調)](https://www.google.com/search?q=PWM)は０番ピン・１番ピンです。
→[データシート](https://akizukidenshi.com/download/ds/atmel/attiny25_attiny45_attiny85.pdf)
今回は１番ピンを使って38kHzの赤外線信号を点灯させます。
なぜ０番ではなく１番ピンを使うのかは後述します。
（irRemoteでも勝手に１番ピンが使われるのはPWMに対応しているからという理由です、他のピンでは代替えできません）

## irRemoteライブラリを使わない理由

いま思えば最後のスイッチOFF時間を十分に取れていなかっただけかも知れませんが
送信した信号がうまく解析してもらえなかったのが理由です。
ON/OFFを繰り返し10回行いますが、3回分しか認識しなかったり15回分だと判断してしまったり。。
それ以外にも `delay()` が使えないとか（使わない方が良い？）
ライブラリに手を加えないと動かない、とか、いくつか問題がありました。


##