安いガジェット基板に赤外線通信機能を載せようと思う。IrDA使おうと思っていたけど
http://akizukidenshi.com/catalog/g/gM-00382/
秋月電子で2個で700円。高いなあ。1桁ぐらい安いコストで実現できるものを考えてみます。
赤外線LED + フォトトランジスタ
単にフォトトランジスタと赤外線LEDとフォトトランジスタでつなげてみた
- マイコンボード RobotDyn STM32 Dev.Board(≒Blue Pill≒Maple Mini)
- フォトトランジスタL-51ROPT1D1
- 赤外線LED OSI3CA5111A
フォトトランジスタは安いのをいくつか試したところこれが最も調子がいい感じ。
赤外線LEDは、一般的なピーク波長が940nmではなく850nm
受信側はフォトトランジスタL-51ROPT1D1の信号をトランジスタで増幅してUSBシリアルアダプタ→PCへ。
図の、「OUTPUT W1」にはUSBシリアルアダプタのRXに接続しています。
送受信をぴったりくっつけると通信できた。
ボーレートが低いほど安定するかと思ったけどそうでもないみたい。
2400bpsが最も安定していた。
条件が良いと1cm弱離れた状態で受信できる
電流を増やしてみる
さすがに1cm弱だと部品のばらつきや環境光などで常時使うには不安。
STM32は1ポートあたりのドライブ電流は20mA。赤外線LEDの OSI3CA5111A は50mA流せるので、もう少し流してみようかな。
送信側に、LEDドライブ用のトランジスタを増やしてみた。
数センチ離れてもOKになりました。
本質的な改善のためには
いままでのやりかたは本質的に擾乱に弱い。ゲイン調整や、設置環境など詰めないと実用には難しい。また、送信側にも受信側にも信号増幅用にトランジスタが必要。またクロストークもどうにかしたい・・・
パーツ点数を増やさずに、これらの問題をクリアするには?
赤外線リモコン受光素子を使う
赤外線リモコン受光素子は、秋月電子で50円ぐらいで売られています。また、赤外線リモコンは数メートル離れていても届きますよね。これを使ってみようかな・・・
但し、赤外線リモコンはいくつかフォーマットがあります。それらのフォーマッに沿って通信する場合、送信はそんなに面倒ではありませんが受信側が結構面倒です。
受信の際は取りこぼししないように注意、その後のデコードも、pythonやArduinoのデコードライブラリは受信サイズに制限があることもあります。
今回は全二重通信させるかねあいもあり、赤外線リモコンプロトコルを使わずに、単なるシリアル通信を赤外線リモコン受光素子に流してみました。
赤外線リモコン受光素子
赤外線リモコン受光素子は、内部にアンプ、ゲイン調整、復調器など内蔵されているのでこれをうまく使えば安定した通信ができそうです。
調べてみるとこちらにそのような実験をされている方がおられました。
赤外線リモコンフォーマットに則ってなくても、例えば38kHzで変調した赤外線を送るだけで復調したデジタル信号が出てくることがわかりました。
ということで2400bpsのシリアル通信信号を38kHzで変調し、送ってみます。
まずは、準備としてシリアル通信をSTM32で送信します。TX1に送られた信号のポートのレベルに割り込みトリガをかけて、レベルが変化すると割り込み処理でPB0ポートのHigh/Lowを制御して同じ信号が出るようにしました。
次に、PB0ポートにPWMで38KHzを出力しておき、割り込み処理でPWMのON/OFFをするように変更してみました。
それを赤外線LEDに出力し、赤外線リモコン受光素子で
送信側のスケッチです
STM32F103C8のボードを使いました。
このボードはUpload Methodによってシリアルポート変化するので注意。
#include <STM32Sleep.h>
#include <RTClock.h>
int i,counter=0;
#define LED PC13
#define BOARD_BUTTON_PIN PB10
#define COPY_PIN PA8
#define PWM_PIN0 PB0 // TIMER3
#define PWM_PIN1 PB1 // TIMER3
void setup() {
Serial.begin(2400);
Serial3.begin(2400);
delay(100);
pinMode(LED,OUTPUT);
pinMode(COPY_PIN,OUTPUT);
for ( i=0;i<5;i++){
digitalWrite(LED,HIGH);
delay(40);
digitalWrite(LED,LOW);
delay(40);
}
pinMode(PWM_PIN0, PWM); // LED_PINをPWMに設定
pinMode(PWM_PIN1, PWM); // LED_PINをPWMに設定
Timer3.pause();
Timer3.setPrescaleFactor(947); // システムクロック 72MHzを10kHzに分周
Timer3.setOverflow(2); // wave width = 2 ( 2 * 72MHz / 947 = 38Khz)
pwmWrite(PWM_PIN0, 1); // pulse width = 1 (1/2 duty 50%)
pwmWrite(PWM_PIN1, 1); // pulse width = 1 (1/2 duty 50%)
Timer3.setCount(1);
Timer3.refresh();
Timer3.resume();
// Timer3.resume();
delay(100);
Timer3.pause();
Timer3.setCount(0);
Timer3.refresh();
Serial.println("start ...");
delay(100);
attachInterrupt(BOARD_BUTTON_PIN, action, CHANGE);
delay(100);
Serial.println("Never came here");
}
void loop() {
// sleepAndWakeUp(STANDBY, &rt, alarmDelay);
delay(100);
for ( i=0;i<5;i++){
Serial.println("");
Serial.println("");
Serial.print(counter);
Serial.println(":START:PAYMENT:500AV:REMAIN:123455677:END");
Serial3.println("");
Serial3.println("");
Serial3.print(counter);
Serial3.println(":START:PAYMENT:500AV:REMAIN:123455677:END");
digitalWrite(LED,HIGH);
delay(40);
digitalWrite(LED,LOW);
delay(40);
}
counter=counter+1;
delay(360);
}
void action()
{
int state= digitalRead(BOARD_BUTTON_PIN);
// digitalWrite(PWM_PIN0,state);
if (HIGH == state ){
Timer3.pause();
Timer3.setCount(0);
Timer3.refresh();
} else {
Timer3.resume();
}
}
Serial に送っているのはモニター用。
Serial3 に送っているデータは、実際にはSerial3 のTXポート(PB10)には何も接続されておらず、このポートの値が変化することで割り込みをかけてPB0とPB1のPWMをON/OFFしている。
PB0とPB1を同時に操作しているのはSTM32が1ポートあたりに流せる電流には制限があるため、2ポートを使って電流値を稼いでいる。
信頼性向上のため、同じメッセージを5回送っている。
受信結果。5回の送信を1パケットとして、いくつめのパケットかを行頭に表示している。結構化けているが、パケット中2行程度は受信できていることがわかる。おそらく、微妙なクロックずれによって3回目以降は受信できなくなっているのではないかな?
実際には、タイミングを調整し、チェックサムなどを付加して使うことになる。
低予算(60円ぐらい)で100バイトぐらいならなんとかなりそうというお話でした。