LoginSignup
7
2

More than 3 years have passed since last update.

ATtiny85で作るシリアル接続 汎用リモコン送信モジュール

Posted at

皆さん、楽しい赤外線ライフを満喫していますか? 電子工作で赤外線リモコンを作るとき、特に Raspberry Pi や MicroPython では数十μ秒のタイミング制御がうまくいかなかったり、言語やライブラリのお作法に悩まされたり、色々な問題に阻まれることがあります。かといって何でも C や Arduino で書くのは面倒だし。やっぱりイマドキの高級言語で書きたいですよね? ということで、言語やライブラリを問わずPCや各種マイコンに幅広く対応する送信モジュールを開発しました。

まずは今回作ったリモコン送信モジュール。ATTiny85を使い Software Serial で受け取ったコマンドを赤外線で送出します。

IMG_5692.JPG

動機

ESP32 + MicroPython で赤外線リモコンを作ろうと思ったら On/Off のタイミングを思ったように制御できなくて悲しい気持ちになったから (結局このときは ESP32 + Arduino で開発した)。よろしい、ならば抽象化だ。

やりたいこと

  • 言語やフレームワーク・ランタイムを問わない赤外線送信の抽象化
  • ハードウエアを問わず接続できるモジュール化
  • NEC フォーマットとその派生フォーマットへの対応

ハードウエア編

用意したもの

仕様と回路図

  • 電源: 3.3V
  • 通信速度: 9600bps
  • ストップビット: 2
  • パリティ: なし

circuit.png

回路図のATtiny85から出ているRXDは接続先のTXDに、TXDはRXDに接続します。

リモコンのコード解析

過去の拙著をそのまま使いました。今回もrawData配列はそのまま使えます。

ソフトウエア編

Arduinoのスケッチ

東芝のシーリングライト LEDH96040-LC と、そのリモコン FRC-194T(W) は、一見NECフォーマットにも見えるのですが機能によって信号が32bit長だったり64bit長だったりします。またHi/Loの持続時間も通常のNECフォーマットより100〜1000μ秒ほど長くなっています。NECフォーマット専用のプログラムでは対応できないため「Hi/Loの持続時間」をコンマ区切りで渡すことにしました。終端は"."(ピリオド)とし、これを受け取ったら送信します。

今回はNECフォーマットの派生のようですが、搬送波38kHz・1/3デューティであれば他のフォーマットにも対応できるでしょう。

ATtiny85で赤外線リモコン用の波形を作る話 | 東京お気楽カメラを参考に以下のスケッチを作成し、ATtiny85に書き込みます。

#include <avr/wdt.h>
#include <SoftwareSerial.h>
#define COMMAND_BUFFER_SIZE 150
#define OUTPUT_PIN _BV(1)

/**
 * ATTiny85 @ 8MHz を使い、Software Serial で受信したタイミングに
 * 沿って赤外線LEDを制御する。
 *
 */

SoftwareSerial serial(3, 4); // RX, TX
unsigned int commandBuffer[COMMAND_BUFFER_SIZE];
char c;
int index = 0;
int signalSize = 0;
unsigned int t;
boolean end = false;

void setup() {
  serial.begin(9600);

  // IR LED
  // http://okiraku-camera.tokyo/blog/?p=7480 から引用
  noInterrupts();
  DDRB = OUTPUT_PIN; // inputは0
  PORTB = 0; // 他は0
  ACSR |= 0x80; // アナログコンパレータ禁止
  ADCSRA &= 0x7f; // disable ADC
  wdt_disable();
  TIMSK = 0;  // disable Timer0/1 overflow interrupt.
  interrupts();
}

void mark() {
  // 38kHz, 1/3 duty
  // http://okiraku-camera.tokyo/blog/?p=7480 から引用、8MHz
  // に対応するよう改変
  OCR0A = OCR1C = 25;  // TOP value :  38kHz --> 26.3us.
  OCR0B = OCR1B = 6;  //  H period  : 6.4us.
  TCCR0A = B00100011; // COM0A: 00, COM0B: 10, WGM01-00: 11
  TCCR0B = B00001000; // FOC:00, WGM02:1, CS02-01: 000 (stop clock)
  GTCCR = B01100000; // OC1B cleared on compare match. Set when TCNT1 = $00.
  TCCR1 = B10000100; // CTC=1. clear TCNT1 on OCR1C==TCNT1. CS13-10 = 0100B (/8)
  TCNT1 = 0;
  TCCR0B |= B010;  // enable clock (/8).
  TCNT0 = OCR0A ;

}

void space() {
  TCCR0A = 0;
  TCCR0B = 0;
  TCCR1 = 0;
  GTCCR = 0;
}

void loop() {
  if(serial.available()) {
    c = serial.read();
    switch (c) {
      case '0': t = t * 10; break;
      case '1': t = t * 10 + 1; break;
      case '2': t = t * 10 + 2; break;
      case '3': t = t * 10 + 3; break;
      case '4': t = t * 10 + 4; break;
      case '5': t = t * 10 + 5; break;
      case '6': t = t * 10 + 6; break;
      case '7': t = t * 10 + 7; break;
      case '8': t = t * 10 + 8; break;
      case '9': t = t * 10 + 9; break;
      case ',':
        commandBuffer[index] = t;
        index++;
        t = 0;
        break;
      case '.':
        commandBuffer[index] = t;
        signalSize = index+2;
        t = 0;
        end = true;
        break;
    }
    if(COMMAND_BUFFER_SIZE <= index) {
      index = 0; // バッファがあふれたらいったん全て捨てる
    }
  }
  if(end) {
    for(index = 0; index < signalSize; index++) {
      if(0 == index % 2) {
        mark();
      } else {
        space();
      }
      delayMicroseconds(commandBuffer[index]);
    }
    space();
    end = false;
    index = 0;
    signalSize = 0;
  }
}

ATtiny85へのスケッチの書き込み

High-Low Tech – Programming an ATtiny w/ Arduino 1.6 (or 1.0) を参考にスケッチを書き込みます。注意点は以下の通り。

  • ArduinoからATmega328を外さない
  • Programmerは"Arduino as ISP"を使う
  • 内部クロックを使用し、周波数は8MHz
  • まずブートローダを書き込め。話はそれからだ
  • "ISP" といいつつ "In-System Programming"がうまくいかない。面倒でもATtiny85をプログラマに挿したり基板に戻したりする

使用例

東芝のシーリングライト用リモコン FRC-194T(W) の「全灯」ボタン チャネル2のコードを色々な方法で送ってみます。

USB/Serial変換アダプタでMacにつなぎ、echoコマンドで送信

echo '10200,5050, 650,1900, 650,1900, 650,1900, 650,600, 650,600, 700,1900, 650,1850, 700,1850, 700,600, 650,600, 650,1900, 650,1900, 650,600, 700,550, 700,600, 700,600, 650,1850, 700,1900, 650,1850, 700,600, 650,1900, 650,600, 650,650, 650,600, 650,650, 650,600, 650,600, 700,1850, 650,650, 650,1900, 650,1850, 650,1900, 650.' | sudo tee /dev/cu.usbserial-SOMEDEVICE

ESP32のMicroPythonで送信

UART0はREPLおよびWebREPLで使っているので、別のピンを使うことにします。ESP-32とMicroPythonでCO2センサを読んでみた+α - Qiita を参考に TXD, RXD はそれぞれ4番・5番としました。

IMG_5695.JPG

import machine
uart = machine.UART(2, baudrate=9600, rx=5, tx=4)
uart.write("10200,5050, 650,1900, 650,1900, 650,1900, 650,600, 650,600, 700,1900, 650,1850, 700,1850, 700,600, 650,600, 650,1900, 650,1900, 650,600, 700,550, 700,600, 700,600, 650,1850, 700,1900, 650,1850, 700,600, 650,1900, 650,600, 650,650, 650,600, 650,650, 650,600, 650,600, 700,1850, 650,650, 650,1900, 650,1850, 650,1900, 650.")

このコードで無事にシーリングライトを制御できました。ひとまず目的は達成です。

micro:bitから送信

本体のAボタンを押すとシーリングライトが点灯する仕組みにしました。

IMG_5694.JPG

via microbit.png

ちなみに JavaScript だとこんな感じになるらしいです。


input.onButtonPressed(Button.A, function () {
    serial.writeString("10200,5050, 650,1900, 650,1900, 650,1900, 650,600, 650,600, 700,1900, 650,1850, 700,1850, 700,600, 650,600, 650,1900, 650,1900, 650,600, 700,550, 700,600, 700,600, 650,1850, 700,1900, 650,1850, 700,600, 650,1900, 650,600, 650,650, 650,600, 650,650, 650,600, 650,600, 700,1850, 650,650, 650,1900, 650,1850, 650,1900, 650.")
})
serial.redirect(
SerialPin.P0,
SerialPin.P1,
BaudRate.BaudRate9600
)
basic.forever(function () {

})

余談ですが micro:bit は超絶簡単でいいですね。初めて使いましたが一気に好きになりました。

Raspberry Piのシェルから送信

標準の TXD/RXD (14, 15) を使います。
IMG_5696.JPG

事前にUARTを有効にしておく必要があります。

sudo raspi-config nonint do_serial 0  # 0で有効化
sudo shutdown -r now  # 再起動

コマンド自体はMacのときと同様です。

echo '10200,5050, 650,1900, 650,1900, 650,1900, 650,600, 650,600, 700,1900, 650,1850, 700,1850, 700,600, 650,600, 650,1900, 650,1900, 650,600, 700,550, 700,600, 700,600, 650,1850, 700,1900, 650,1850, 700,600, 650,1900, 650,600, 650,650, 650,600, 650,650, 650,600, 650,600, 700,1850, 650,650, 650,1900, 650,1850, 650,1900, 650.' > /dev/serial0

Raspberry PiのPythonで送信

pyserialモジュールを導入。

pip install pyserial

続いて本体のコード。

import serial
s = serial.Serial('/dev/serial0', 9600)
s.write(b"10200,5050, 650,1900, 650,1900, 650,1900, 650,600, 650,600, 700,1900, 650,1850, 700,1850, 700,600, 650,600, 650,1900, 650,1900, 650,600, 700,550, 700,600, 700,600, 650,1850, 700,1900, 650,1850, 700,600, 650,1900, 650,600, 650,650, 650,600, 650,650, 650,600, 650,600, 700,1850, 650,650, 650,1900, 650,1850, 650,1900, 650.")
s.close()

Arduinoから送信

IRremoteという強力なライブラリがあります。ただボードやコントローラによっては対応していない場合もあり、そういった場合に有効かと思います。

IMG_5697.JPG

起動直後に「全灯」のコマンドを送信してみます。

void setup() {
  char signal[] = "10200,5050, 650,1900, 650,1900, 650,1900, 650,600, 650,600, 700,1900, 650,1850, 700,1850, 700,600, 650,600, 650,1900, 650,1900, 650,600, 700,550, 700,600, 700,600, 650,1850, 700,1900, 650,1850, 700,600, 650,1900, 650,600, 650,650, 650,600, 650,650, 650,600, 650,600, 700,1850, 650,650, 650,1900, 650,1850, 650,1900, 650.";
  Serial.begin(9600);
  Serial.print(signal);
}

void loop() {
}

まとめとか今後とか

  • シェルから echo で家電を制御できるって楽しい
  • Windows環境でもCOPYコマンドでCOMnに書き込めば同様のことができるかも。BATで家電を制御できると楽しいですね
  • シリアルの1対1接続と違ってI2Cだと1対nで接続できるので、そのうち I2C slave版も作るかも。ただシリアルの利便性も捨て難い……

謝辞

この工作は調光君2号の開発の過程で生まれました。すぐに何かの役に立つ品物でもないのに、家事を疎かにし実験や開発に没頭していても「仕事で帰りが遅いしストレス発散は大事だよね」と大目に見てくれる妻に感謝します。

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2