433MHz RFモジュールとArduinoのガイド:送信機・受信機・アンテナ
433MHz RFモジュールとArduinoのガイドです。FS1000A送信機とXY-MK-5V受信機の配線、17.3 cmのアンテナ、RadioHeadでのメッセージ送信、RCSwitchでのリモコンのコピーを解説します。
433MHz RFモジュールは、Arduinoのプロジェクトに無線通信を加えるいちばん安い方法です。送信機(FS1000A)と受信機(XY-MK-5V)のセットはとても安く、データ線1本で接続でき、17 cmのワイヤーアンテナを付ければ数十メートル先まで信号が届きます。リモコン式のコンセント、ワイヤレスチャイム、シャッターのリモコン、ドアや窓のセンサー、気象ステーションの屋外センサーも、多くが同じ周波数を使っています。そのため、このモジュールを使えば2台のArduino間でデータを送れるだけでなく、家にある既製のリモコンのコードを読み取り、Arduinoから送り直すこともできます。
このガイドでは、モジュールがASK(OOK)変調でどのように動くのか、アンテナを17.3 cmにする理由、Arduino Uno、ESP32、Raspberry Pi Picoへの配線を学びます。続いて、RadioHeadライブラリで2台のArduino間にテキストとセンサーデータを送り、RCSwitchライブラリでコンセントのリモコンのコードを読み取ってコピーし、通信距離を延ばす方法を確認し、nRF24L01と比較します。
433MHz RFモジュールとは?どのように動くのか
セットは2枚の別々のボードでできています。小さな正方形のFS1000A送信機(XY-FSTという名前でも売られています)は、DATAピンに入る信号に合わせて433.92 MHzの搬送波をオン・オフします。細長いXY-MK-5V受信機は、空中のその信号を受け取り、DATAピンから再び0と1として出力します。つまり、モジュール自体にはプロトコルもアドレスもエラーチェックもありません。役割はデジタル信号を無線で相手側に届けることだけです。データをパケットにまとめ、確認し、復号するのはArduino側のライブラリの仕事です。通信は一方向です。送信機は送るだけ、受信機は聞くだけです。
| 項目 | FS1000A(送信機) | XY-MK-5V(受信機) |
|---|---|---|
| 周波数 | 433.92 MHz(315 MHz版もあり) | 433.92 MHz |
| 電源電圧 | 3〜12 V | 5 V |
| 電流 | 送信中は約9〜40 mA(電圧による) | 約4 mA |
| 変調方式 | ASK / OOK | ASK / OOK、超再生方式の受信機 |
| 通信速度 | 最大約10 kbps(実用上は1〜4 kbps) | 送信機と同じ上限 |
| 出力 / 感度 | 約10 mW、電圧が高いほど増加 | 約−105 dBm |
| ピン | DATA、VCC、GND+ANTのはんだ付けパッド | VCC、DATA、DATA、GND+ANTのはんだ付けパッド |
| サイズ | 約19 × 19 mm | 約30 × 14 mm |
受信機の2つのDATAピンはボード上でつながっているので、どちらを使ってもかまいません。XY-MK-5Vは安価ですが選択度の低い超再生方式の受信機です。周波数はボード上の調整用コイルで決まり、近くのノイズの影響を受けやすくなっています。このコイルのネジは回さないでください。工場出荷時の調整がずれると、受信機が送信機の信号を受け取れなくなります。より安定した結果がほしいなら、RXB6のようなスーパーヘテロダイン方式の受信機のほうが感度が高く、3.3 Vでも動作します。セットには315 MHz版もあります。送信機と受信機の周波数は同じでなければならず、送信機ボードの金属製の共振器には、たいてい周波数が印字されています。
ASK(OOK)変調:なぜライブラリが必要なのか
ASK(Amplitude Shift Keying、振幅偏移変調)のいちばん単純な形であるOOK(On-Off Keying)では、1のビットで搬送波をオンにし、0のビットでオフにします。簡単そうですが、受信機には癖があります。内部の自動利得制御(AGC)は、信号がないときに利得を最大まで上げ、空中のノイズをデータと勘違いして、DATAピンからランダムなパルスを出し続けます。そのため、送信機をArduinoのTXピンに、受信機をRXピンに直接つないでSerial.printで通信しようとしても、たいていは文字化けに終わります。
RadioHeadライブラリのRH_ASKドライバは、この問題を次のように解決します。各パケットの前に36ビットのプリアンブルと12ビットのスタートシンボルを送り、受信機の利得を落ち着かせます。次に、各バイトをDCバランスのとれた6ビットのシンボル2つに変換し(4ビットから6ビットへの符号化)、パケットの最後に16ビットのCRCを付けます。受信機はCRCが一致しないパケットを黙って捨てるので、正しいデータだけが届きます。一方、RCSwitchはリモコン用チップが使うパルス幅による符号を解読します。各ビットは、決まった時間だけHIGHとLOWを保つパルスで表されます。
433MHzのアンテナの長さ:なぜ17.3 cmなのか
モジュールにはアンテナが付いておらず、ANTと書かれたはんだ付けパッドがあるだけです。アンテナなしでは、セットは1〜3メートルしか届きません。いちばん効率のよい簡単なアンテナは、波長の4分の1の長さのまっすぐなワイヤーです。λ = c / f = 300,000,000 / 433,920,000 ≈ 0.69 mで、その4分の1は約17.3 cmになります。315 MHzのモジュールでは、同じ計算で23.8 cmです。単線の一端をANTパッドにはんだ付けし、ワイヤーをまっすぐ垂直に保ちます。受信機にも同じ長さのワイヤーをはんだ付けし、2本のアンテナを同じ向きにそろえます。
| アンテナ | 長さ | おおよその通信距離(見通しのよい屋外) | メモ |
|---|---|---|---|
| アンテナなし | ― | 1〜3 m | 机の上でのテスト専用 |
| まっすぐなワイヤー(λ/4)、送信機は5 V | 17.3 cm | 20〜50 m | いちばん簡単で効率のよい方法 |
| まっすぐなワイヤー(λ/4)、送信機は9〜12 V | 17.3 cm | 50〜100 m以上 | 見通しと設置の高さが大きく影響する |
| 既製のヘリカル(スプリング)アンテナ | 長さ2〜4 cm | まっすぐなワイヤーより短い | ケースの中に収まる |
433MHz RFモジュールの配線:Arduino Uno、ESP32、Pico
| モジュールのピン | Arduino Uno(RadioHead) | Arduino Uno(RCSwitch) | ESP32 | Raspberry Pi Pico |
|---|---|---|---|---|
| FS1000A VCC | 5Vまたは外部電源9〜12 V | 5V | VIN(5 V) | VBUS(5 V) |
| FS1000A DATA | D12 | D10 | GPIO 26 | GP15 |
| FS1000A GND | GND | GND | GND | GND |
| XY-MK-5V VCC | 5V | 5V | VIN(5 V) | VBUS(5 V) |
| XY-MK-5V DATA | D11 | D2(割り込みピン) | GPIO 27(分圧回路経由) | GP14(分圧回路経由) |
| XY-MK-5V GND | GND | GND | GND | GND |
送信機はDATAピンに3.3 Vの信号が入っても問題なく動作します。そのため、ESP32とPicoでは送信機を5 Vで動かし、DATAをGPIOに直接つなげます。通信距離にいちばん大きく影響するのは送信機の電圧です。3.3 Vでは距離がはっきり短くなり、9〜12 Vでは数倍に伸びます。受信機のVCCピンとGNDピンのすぐそばに100 nFのセラミックコンデンサと10 µFの電解コンデンサを置くと、電源からのノイズが減り、受信状態が目に見えてよくなります。
RadioHeadで2台のArduino間にメッセージを送る
- Arduino IDEでライブラリマネージャを開き、RadioHeadを検索して、Mike McCauley氏のライブラリをインストールします。詳しくはArduinoにライブラリを追加する方法を参照してください。
- 1台目のArduinoに送信機をつなぎます:DATA → D12、VCC → 5V、GND → GND。
- 2台目のArduinoに受信機をつなぎます:DATA → D11、VCC → 5V、GND → GND。
- 両方のモジュールのANTパッドに、17.3 cmのワイヤーを1本ずつはんだ付けします。
- 送信側のコードを1台目のボードに、受信側のコードを2台目のボードに書き込み、受信側でシリアルモニタを9600ボーで開きます。
1#include <RH_ASK.h>
2#include <SPI.h> // RadioHead derlemesi için / needed for RadioHead to compile
3
4RH_ASK rf(2000, 11, 12); // hız (bps), RX pini, TX pini / speed (bps), RX pin, TX pin
5unsigned int sayac = 0;
6
7void setup() {
8 Serial.begin(9600);
9 if (!rf.init()) Serial.println("RF init hatasi / failed");
10}
11
12void loop() {
13 char mesaj[24];
14 snprintf(mesaj, sizeof(mesaj), "Merhaba %u", sayac++);
15 rf.send((uint8_t *)mesaj, strlen(mesaj));
16 rf.waitPacketSent(); // paket bitene kadar bekle / wait until the packet is sent
17 Serial.print("Gonderildi / sent: ");
18 Serial.println(mesaj);
19 delay(1000);
20}1#include <RH_ASK.h>
2#include <SPI.h> // RadioHead derlemesi için / needed for RadioHead to compile
3
4RH_ASK rf(2000, 11, 12); // vericiyle aynı hız / same speed as the transmitter
5
6void setup() {
7 Serial.begin(9600);
8 if (!rf.init()) Serial.println("RF init hatasi / failed");
9}
10
11void loop() {
12 uint8_t buf[RH_ASK_MAX_MESSAGE_LEN + 1]; // +1: metin sonu için / +1 for the string terminator
13 uint8_t len = RH_ASK_MAX_MESSAGE_LEN; // her turda yeniden ayarla / reset on every pass
14 if (rf.recv(buf, &len)) { // yalnız CRC doğruysa true / true only if the CRC is valid
15 buf[len] = '\0';
16 Serial.print("Alindi / received: ");
17 Serial.println((char *)buf);
18 }
19}| 命令 | 働き |
|---|---|
| RH_ASK rf(2000, 11, 12) | 速度2000 bps、受信機はD11、送信機はD12。速度は送受信の両側で同じにする |
| rf.init()(初期化) | タイマーとピンを準備し、問題があればfalseを返す |
| rf.send(データ, 長さ) | 最大60バイトのパケットの送信を始める |
| rf.waitPacketSent()(送信完了まで待つ) | パケットが完全に送信されるまで待つ |
| rf.recv(buf, &len)(受信) | CRCが正しい新しいパケットがあれば、bufにコピーしてlenを更新し、trueを返す |
| RH_ASK_MAX_MESSAGE_LEN | 1つのパケットで運べる最大のデータ量:60バイト |
受信側のコードでは、len変数を毎回の周回で設定し直すことが重要です。recv()はこの変数に受け取ったパケットのサイズを書き込み、次の呼び出しではその値がバッファの大きさとして扱われます。設定し直さないと、長いメッセージが途中で切れてしまいます。すべて正常なら、シリアルモニタに1秒ごとに「Merhaba 0」「Merhaba 1」(Merhabaはトルコ語で「こんにちは」)という行が表示されます。番号が飛んでいる場合は、途中でいくつかのパケットが失われています。その場合はアンテナと電源を確認してください。
センサーデータを送る:structを使ったパケット
実際のプロジェクトでは、テキストより数値を送るほうが効率的です。測定値をstructにまとめ、そのバイト列をそのまま送れます。下の送信側は、A0につないだLDRまたはポテンショメーターの値と、D2のボタンの状態を1秒に5回送ります。受信側は、離れた場所のボタンが押されるとD13のLEDを点灯し、失われたパケットにも気づきます。
1#include <RH_ASK.h>
2#include <SPI.h>
3
4struct __attribute__((packed)) Paket { // iki tarafta birebir aynı olmalı / must be identical on both sides
5 uint16_t no; // paket numarası / packet number
6 int16_t deger; // analog değer / analog value
7 uint8_t buton; // 1 = basılı / 1 = pressed
8};
9
10RH_ASK rf; // varsayılan: 2000 bps, RX 11, TX 12 / defaults
11Paket p = {0, 0, 0};
12
13void setup() {
14 pinMode(2, INPUT_PULLUP); // buton D2-GND / button D2-GND
15 rf.init();
16}
17
18void loop() {
19 p.no++;
20 p.deger = analogRead(A0); // örneğin LDR ya da potansiyometre / e.g. LDR or potentiometer
21 p.buton = (digitalRead(2) == LOW);
22 rf.send((uint8_t *)&p, sizeof(p));
23 rf.waitPacketSent();
24 delay(200);
25}1#include <RH_ASK.h>
2#include <SPI.h>
3
4struct __attribute__((packed)) Paket {
5 uint16_t no;
6 int16_t deger;
7 uint8_t buton;
8};
9
10RH_ASK rf;
11const int LED = 13;
12uint16_t sonNo = 0;
13
14void setup() {
15 Serial.begin(9600);
16 pinMode(LED, OUTPUT);
17 rf.init();
18}
19
20void loop() {
21 Paket p;
22 uint8_t len = sizeof(p);
23 if (rf.recv((uint8_t *)&p, &len) && len == sizeof(p)) {
24 digitalWrite(LED, p.buton ? HIGH : LOW); // uzaktaki buton LED'i yakar / remote button lights the LED
25 if (sonNo != 0 && p.no != (uint16_t)(sonNo + 1)) {
26 Serial.println("Paket kaybi / packet lost");
27 }
28 sonNo = p.no;
29 Serial.print(p.no);
30 Serial.print(" A0 = ");
31 Serial.println(p.deger);
32 }
33}__attribute__((packed))という指定は、コンパイラがフィールドの間に詰め物(パディング)を入れるのを防ぎます。これは重要です。同じstructでも、Arduino Unoでは5バイト、ESP32では6バイトになることがあり、異なる2種類のボードで通信するとデータがずれてしまいます。パケット番号のおかげで、受信側は途中で失われたパケットを数えられます。433MHzのモジュールには受信確認がないので、重要な命令は何回か繰り返して送る習慣をつけるとよいでしょう。両側に送信機と受信機の両方を付ければ、RadioHeadのRHReliableDatagramクラスで、受信確認と再送のある通信も組めます。
RCSwitchで既製のリモコンのコードを読み取る
リモコン式のコンセント、ワイヤレスチャイム、シャッターや門扉のリモコンの多く、そしてドアや窓、PIRの防犯センサーは、EV1527やPT2262のようなコード用チップを使っています。これらのチップは、ボタンが押されるたびに、同じ12〜24ビットのコードを間に同期用の空白をはさみながら繰り返し送ります。RCSwitchライブラリ(ライブラリマネージャではrc-switchという名前)は、このコードを解読します。Unoでは受信機のDATAピンを割り込みピン、つまりD2かD3につなぎ、次のコードを書き込みます。
1#include <RCSwitch.h>
2
3RCSwitch rf = RCSwitch();
4
5void setup() {
6 Serial.begin(9600);
7 rf.enableReceive(digitalPinToInterrupt(2)); // alıcı DATA -> D2 (kesme pini) / receiver DATA -> D2 (interrupt pin)
8 Serial.println(F("Kumandanin tusuna bas / press a remote button"));
9}
10
11void loop() {
12 if (rf.available()) {
13 Serial.print(F("Kod / code: "));
14 Serial.print(rf.getReceivedValue());
15 Serial.print(F(" Bit: "));
16 Serial.print(rf.getReceivedBitlength());
17 Serial.print(F(" Protokol / protocol: "));
18 Serial.print(rf.getReceivedProtocol());
19 Serial.print(F(" Darbe / pulse: "));
20 Serial.print(rf.getReceivedDelay());
21 Serial.println(F(" us"));
22 rf.resetAvailable(); // bir sonraki koda hazırlan / get ready for the next code
23 }
24}シリアルモニタを開いてリモコンのボタンを押すと、「Kod / code: 5393 Bit: 24 Protokol / protocol: 1 Darbe / pulse: 350 us」のような行が表示されます。各ボタンのコード、ビット数、プロトコル、パルス幅をメモしておきましょう。コピーするときに4つとも必要になります。EV1527を使うリモコンでは、24ビットのうち最初の20ビットがリモコンのID、最後の4ビットが押されたボタンを表します。そのため、同じリモコンのボタンのコードはお互いにとても近い値になります。プロトコル1では1単位が約350 µsです。0のビットは1単位HIGHと3単位LOW、1のビットは3単位HIGHと1単位LOW、同期信号は1単位HIGHと31単位LOWです。
| リモコンのチップ | コードの構成 | RCSwitchでコピーできるか |
|---|---|---|
| EV1527(学習コード) | 20ビットのID+4ビットのボタン、合計24ビット | できる |
| PT2262 / SC2262 | 3値(0、1、F)のビットが12個。アドレスはDIPスイッチで設定 | できる |
| HT6P20B | 固定コード、RCSwitchのプロトコル6 | できる |
| HCS301(KeeLoq)などのローリングコードのチップ | 押すたびに変わる暗号化されたコード | できない |
リモコンのコードをArduinoから送る
読み取ったコードを送るには、送信機のDATAピンをD10に、2つのボタンをD4とD5それぞれとGNDの間につなぎます。ボタンを押すたびに、Arduinoはリモコンと同じようにコードを10回繰り返して送ります。これでコンセントのオンとオフのボタンがArduinoに移りました。
1#include <RCSwitch.h>
2
3RCSwitch rf = RCSwitch();
4const int BTN_AC = 4; // "aç" butonu D4-GND / "on" button D4-GND
5const int BTN_KAPAT = 5; // "kapat" butonu D5-GND / "off" button D5-GND
6
7void setup() {
8 pinMode(BTN_AC, INPUT_PULLUP);
9 pinMode(BTN_KAPAT, INPUT_PULLUP);
10 rf.enableTransmit(10); // verici DATA -> D10 / transmitter DATA -> D10
11 rf.setProtocol(1); // önce protokol... / protocol first...
12 rf.setPulseLength(350); // ...sonra okuduğun darbe süresi / ...then the pulse length you read
13 rf.setRepeatTransmit(10); // her gönderim 10 kez tekrarlanır / each send is repeated 10 times
14}
15
16void loop() {
17 if (digitalRead(BTN_AC) == LOW) {
18 rf.send(5393, 24); // okuduğun "aç" kodu ve bit sayısı / the "on" code and bit length you read
19 delay(300);
20 }
21 if (digitalRead(BTN_KAPAT) == LOW) {
22 rf.send(5396, 24); // okuduğun "kapat" kodu / the "off" code you read
23 delay(300);
24 }
25}setProtocol()はパルス幅をそのプロトコルの初期値に戻してしまうので、setPulseLength()は必ずその後に呼び出します。コンセントが反応しない場合は、まず読み取ったパルス幅(例:320や420 µs)をそのまま入力し、それから繰り返し回数を15に増やします。学習コード式のコンセントには、もう1つ便利な方法があります。コンセントを学習モードにしてArduinoから任意の24ビットのコードを送ると、コンセントがそのコードをメモリに登録します。つまり、リモコンを1つもコピーせずに、自分だけのコードを作れます。
ESP32で433MHzの受信機を使う:リモコンでリレーを操作
ESP32は、433MHzのリモコンをWi-Fiの世界につなぐ橋渡しにぴったりです。RCSwitchはESP32でも動き、ESP32のGPIOはすべて割り込みを使えるので、受信機は好きな安全なピンにつなげます。下のコードは、GPIO 27に(分圧回路経由で)つないだ受信機からコードを読み取ります。自分のリモコンのコードが届くと、GPIO 2のLEDまたはリレーモジュールのオン・オフを切り替えます。リモコンは1回押しただけでコードを何度も送るので、プログラムはコードが600 ms途切れるまで、次の切り替えを行いません。
1#include <RCSwitch.h>
2
3RCSwitch rf = RCSwitch();
4const int RX_PIN = 27; // alıcı DATA, gerilim bölücüyle / receiver DATA, through a divider
5const int ROLE_PIN = 2; // dahili LED ya da röle girişi / onboard LED or relay input
6const unsigned long KOD = 5393; // kendi kumandanın kodu / your own remote's code
7bool durum = false;
8unsigned long sonGorulme = 0;
9
10void setup() {
11 Serial.begin(115200);
12 pinMode(ROLE_PIN, OUTPUT);
13 rf.enableReceive(digitalPinToInterrupt(RX_PIN)); // ESP32'de her GPIO kesme alır / every GPIO takes interrupts on the ESP32
14}
15
16void loop() {
17 if (rf.available()) {
18 unsigned long kod = rf.getReceivedValue();
19 Serial.printf("Kod / code: %lu %u bit protokol %u\n", kod, rf.getReceivedBitlength(), rf.getReceivedProtocol());
20 if (kod == KOD) {
21 // kumanda kodu art arda tekrarlar; yalnız yeni basışta değiştir / the remote repeats the code; toggle only on a new press
22 if (millis() - sonGorulme > 600) {
23 durum = !durum;
24 digitalWrite(ROLE_PIN, durum ? HIGH : LOW);
25 }
26 sonGorulme = millis();
27 }
28 rf.resetAvailable();
29 }
30}この土台の上には、いろいろなものを作れます。受け取ったコードをMQTTでHome Assistantに転送して安価な433MHzのドアセンサーをスマートホームに加えたり、GPIO 26につないだ送信機でコンセントをスマホから操作したりできます。RadioHeadもESP32に対応しています。違いは、コンストラクターでピンを明示的に書くことだけです:RH_ASK rf(2000, 27, 26, 25);(受信機は27、送信機は26、PTT用の空きピンとして25)。ESP32が初めてなら、ESP32とは?を読んでみてください。
Raspberry Pi PicoとMicroPythonで433MHzの送信機を使う
MicroPythonにはRCSwitchに相当する既製のライブラリはありませんが、プロトコル1のタイミングはとても単純なので、数行で自分で書けます。FS1000AのVCCをPicoのVBUS(5 V)ピンに、DATAをGP15に、ボタンをGP16とGNDの間につなぎます。ボタンを押すと、PicoはRCSwitchと同じように24ビットを最上位ビットから順に送り、最後に同期パルスを付け、パケット全体を10回繰り返します。PicoにMicroPythonを入れる方法をまだ知らない場合は、Raspberry Pi Picoとは?から始めてください。
1from machine import Pin
2import time
3
4tx = Pin(15, Pin.OUT, value=0) # FS1000A DATA -> GP15
5buton = Pin(16, Pin.IN, Pin.PULL_UP) # buton GP16-GND / button GP16-GND
6T = 350 # darbe süresi µs (RCSwitch protokol 1) / pulse length µs (RCSwitch protocol 1)
7
8def darbe(yuksek, dusuk): # süreler T cinsinden / durations in units of T
9 tx.value(1)
10 time.sleep_us(yuksek * T)
11 tx.value(0)
12 time.sleep_us(dusuk * T)
13
14def gonder(kod, bit=24, tekrar=10):
15 for _ in range(tekrar):
16 for i in range(bit - 1, -1, -1): # en anlamlı bitten başla / start from the most significant bit
17 if (kod >> i) & 1:
18 darbe(3, 1) # "1" biti: 3T HIGH, 1T LOW / bit "1"
19 else:
20 darbe(1, 3) # "0" biti: 1T HIGH, 3T LOW / bit "0"
21 darbe(1, 31) # senkron: 1T HIGH, 31T LOW / sync
22
23while True:
24 if buton.value() == 0: # butona basıldı / button pressed
25 gonder(5393) # okuduğun kodu yaz / put the code you read here
26 time.sleep_ms(300)Picoの125 MHzのプロセッサにとって、350 µsのパルスは余裕のある速さです。Pythonによる小さな遅れは、RCSwitchの受信側が許容する60%のタイミング誤差よりずっと小さく収まります。RCSwitchの読み取り用コードを書き込んだArduinoを横に置けば、すぐにコードをテストできます。シリアルモニタに、送った数値が表示されるはずです。受信側では、パルスの長さをタイムスタンプで測って解読する必要があります。この作業には、RCSwitchが動くArduinoやESP32のほうが手軽です。
通信距離とノイズ:信号をもっと遠くへ届ける
- アンテナを忘れない:送信側と受信側の両方に、17.3 cmのまっすぐなワイヤーをはんだ付けします。通信距離にいちばん大きく影響するのはこれです。
- 送信機の電圧を上げる:FS1000Aを9〜12 Vで動かすと、出力と通信距離がはっきり伸びます。GNDの共通接続を忘れないでください。
- 速度を下げる:RadioHeadで2000 bpsの代わりに1000 bpsを使うと、1ビットあたりの時間が長くなり、受信機がより弱い信号も解読できるようになります。両側に同じ速度を設定してください。
- 見通しと高さ:鉄筋コンクリートの壁、金属製のケース、地面に近い設置は、通信距離を半分にすることがあります。アンテナは金属ケースの外に出し、できるだけ高い位置に移しましょう。
- 電源をきれいにする:モーター、リレー、スイッチング方式のACアダプターは受信機の感度を奪います。受信機にはフィルターを通した電源を与え、配線は短くしてください。
- よりよい受信機を使う:XY-MK-5Vの代わりにRXB6のようなスーパーヘテロダイン方式の受信機を使うと、同じ送信機でも多くの場合はっきり遠くまで届きます。
- もう一度送る:受信確認の仕組みがないので、重要な命令は3〜5回送り、受信側でパケット番号を使って重複を取り除きます。
433 MHzは共有の周波数帯であることを覚えておきましょう。近所の気象ステーション、車のキー、チャイムも、すべて同じ周波数で電波を出しています。ヨーロッパとトルコでは、433.05〜434.79 MHzの範囲が短距離無線機器向けに免許不要で使えますが、送信出力は10 mW(ERP、実効放射電力)までに制限され、チャンネルを常にふさがないようにする必要があります。ルールは国によって異なり、アメリカではリモコンに315 MHzが使われることが多くなっています。日本では433 MHz帯をこの用途で自由に使うことはできず、技適マークのない無線モジュールの使用は電波法で制限されています(国内のリモコンは主に315 MHz帯の特定小電力無線を使います)。データを送り続けるのではなく、一定の間隔で、または何かが変わったときだけ送るようにすれば、ルールを守れるうえに電池も長持ちします。電池で動く送信機には電池寿命計算ツールを使えます。
433MHz RFモジュールとnRF24L01、どちらを選ぶ?

| 項目 | 433MHz RF(FS1000A + XY-MK-5V) | nRF24L01 |
|---|---|---|
| 周波数 | 433.92 MHz | 2.4 GHz |
| 変調方式 | ASK / OOK | GFSK |
| 通信の方向 | 一方向(送信機と受信機は別のボード) | 双方向(1つのトランシーバーモジュール) |
| 通信速度 | 実用上は1〜4 kbps(RadioHeadの初期値は2 kbps) | 250 kbps〜2 Mbps |
| 接続 | データピン1本 | SPI(配線7本) |
| 電源 | 送信機3〜12 V、受信機5 V | 1.9〜3.6 V |
| エラーチェック | ソフトウェアで行う(RadioHeadのCRC) | ハードウェアのCRC、自動受信確認と再送 |
| 通信距離 | 17.3 cmのアンテナで20〜100 m | 50〜100 m、PA+LNA版で約1 km |
| 壁の透過 | 比較的よい(周波数が低い) | 弱い |
| 既製品との互換性 | コンセント、チャイム、センサーのリモコン | なし |
| 価格 | とても安い | 安い |
まとめると、一方向で、たまにしか送らない小さなデータ(ボタン、温度の値、ドアが開いたという情報)を送りたい場合や、既製のコンセントやセンサーと組み合わせたい場合は、433MHzのモジュールがいちばん安くて簡単な解決策です。高速で双方向、受信確認つきの通信が必要なラジコンカー、ジョイスティック、複数ノードのセンサーネットワークには、nRF24L01のほうが適しています。何キロメートルもの距離が必要ならLoRaモジュールを検討してください。すべての選択肢を並べて比べるには、Arduinoの無線通信モジュールを読んでください。
433MHz RFモジュールを使ったプロジェクトのアイデア
- ワイヤレスチャイム:門のボタンから信号が届いたら、家の中でメロディーを鳴らします。音を出すにはブザーで十分です。
- Arduinoからコンセントを操作:リモコン式のコンセントのコードをコピーし、時刻や明るさに合わせて照明をオン・オフします。
- ワイヤレスのドアセンサー:ドアにリードスイッチと送信機付きの小さなArduinoを取り付け、ドアが開いたら受信側で警報を鳴らします。
- 家庭用の防犯システム:既製の433MHzのPIRセンサーやドアセンサーを1つの親機に集めるArduinoのアラームシステムを作ります。
- 庭のセンサーステーション:電池で動く送信機で、土壌水分や温度を数分ごとに家へ送ります。
- 433MHzとWi-Fiのブリッジ:ESP32を使って、家にあるすべての433MHz機器をスマホやスマートホームのシステムから操作します。
よくある失敗と対処法
| 症状 | 考えられる原因 | 対処法 |
|---|---|---|
| 受信機にパケットがまったく届かない(RadioHead) | 速度やピンが両側で異なる、アンテナがない | 両側で同じコンストラクター(2000, 11, 12)を使い、両方のモジュールに17.3 cmのワイヤーをはんだ付けする |
| 数センチメートルの距離でしか動かない | アンテナなしで使っている、または送信機の電圧が低い | アンテナを付け、送信機を5〜12 Vで動かす |
| エラー:multiple definition of `__vector_11'(多重定義) | RadioHeadとServoライブラリがTimer1を共有できない | RH_ASK_ARDUINO_USE_TIMER2またはServoTimer2を使う |
| D9とD10のPWMが動かない、D10につないだ機器の動きがおかしい | RH_ASKがTimer1とD10(PTT)を使っている | これらのピンを空けておくか、コンストラクターでPTT用に別のピンを選ぶ |
| RCSwitchにコードがまったく表示されない | 受信機のDATAが割り込みのないピンにつながっている | UnoとNanoではD2かD3を使う |
| 見覚えのないコードが表示される | 近所の気象ステーション、センサー、またはノイズ | 既知のコード、ビット数、プロトコルにだけ反応させる |
| コピーしたコードでコンセントが動かない | パルス幅やプロトコルが違う、またはリモコンがローリングコード式 | getReceivedDelay()の値をsetPulseLength()に入れ、繰り返し回数を増やす |
| ESP32がコードを受け取らない、または動作が不安定 | 5 Vの受信機の出力がGPIOに直接つながっている | 分圧回路か3.3 Vの受信機を使う |
| モーターが動くとデータが途切れる | 電源のノイズと電圧降下 | 受信機に別の電源を用意し、100 nF+10 µFのコンデンサを追加する |
433MHz RFモジュールは、わずかな費用でプロジェクトに無線の腕を与えてくれます。家にある既製のリモコン式の機器と通信できる、数少ないモジュールの1つでもあります。無線制御の考え方を理解すれば、赤外線リモコンやBluetoothなど、ほかの方法も簡単に身につけられます。BlueGraysアプリでは、「2台のArduinoで一方向通信」、「リモコンで220V電球を点灯・消灯」(日本の商用電源は100V(50/60Hz)です)、「リードスイッチ式ドアセンサー」、「ESP32の動体検知アラーム」などのプロジェクトを、回路図とステップごとのコードつきで見つけられます。
よくある質問
433MHz RFモジュールの通信距離はどのくらいですか?
アンテナなしでは、セットは1〜3メートルしか届きません。送信側と受信側の両方に17.3 cmのワイヤーアンテナをはんだ付けし、送信機を5 Vで動かすと、見通しのよい屋外で20〜50メートル、送信機を9〜12 Vで動かすと100メートル以上も可能です。屋内では壁やノイズのため、これらの値ははっきり小さくなります。
433MHzのアンテナの長さはどのくらいにすればいいですか?
いちばん効率のよい簡単なアンテナは、1/4波長のまっすぐなワイヤーです。433 MHzでは約17.3 cm、315 MHzでは23.8 cmです。ワイヤーをモジュールのANTパッドにはんだ付けし、まっすぐ垂直に保ちます。受信機と送信機のアンテナは同じ向きにそろえてください。
433MHzの送信機と受信機で双方向の通信はできますか?
1組のペアだけでは、通信は送信機から受信機への一方向だけです。両側に送信機と受信機の両方を付ければ双方向に通信でき、この構成ではRadioHeadのRHReliableDatagramクラスが受信確認と再送も行ってくれます。ただし、この用途には、最初から双方向に対応したnRF24L01やHC-12のようなモジュールのほうが手軽です。
433MHzのモジュールで車やガレージのリモコンをコピーできますか?
車のキーや最近のガレージのリモコンはローリングコードを使っています。押すたびにコードが変わり、記録したコードは拒否されるので、RCSwitchではコピーできません。一方、固定コードのコンセント、チャイム、センサーのリモコンはコピーできます。この方法は自分の機器にだけ使ってください。他人のシステムにアクセスしようとすると、法的な問題になります。
433MHz RFモジュールはESP32で使えますか?
使えます。FS1000A送信機は5 Vで動かし、DATAピンをGPIOに直接つなげます。3.3 Vの信号で十分です。XY-MK-5V受信機の出力は5 Vなので、分圧回路を入れるか、RXB6のように3.3 Vで動作する受信機を選んでください。RCSwitchとRadioHeadのライブラリは、どちらもESP32に対応しています。
433MHzと315MHzのモジュールの違いは何ですか?
どちらも同じ構造で、違うのは動作周波数だけです。ヨーロッパとトルコではリモコンの多くが433.92 MHzを使い、アメリカや一部のアジアの国では315 MHzが一般的です(日本のリモコンも主に315 MHz帯です)。送信機と受信機、またはコピーしたいリモコンの周波数は、完全に同じでなければなりません。
この記事を読んだ後に作れるプロジェクト
全プロジェクトを見る
2台のArduinoで一方向通信
2台のArduinoを1本のワイヤーで通信させます。一方のボタンを押すともう一方のLEDが点灯し、もう一度押すと消灯します。送信側のボードがボタンの押下を短いメッセージで伝え、受信側のボードはそのメッセージを受け取るとLEDの点灯と消灯を切り替えます。機器同士が情報を伝える仕組みを、最もシンプルな形で確かめられます。

2台のArduinoで双方向通信
2台のArduinoをケーブルでつないで通信します。片方のボタンを押すともう片方のライトが点灯し、もう一度押すと消灯します。両方のボードで同じプログラムを動かすので、どちらからでもメッセージを送受信できます。ボード同士でメッセージをやり取りする仕組みを、シンプルな構成で学べます。

リモコンで220V電球を点灯・消灯
ソファに座ったまま、テレビのリモコンで家庭の電球を点灯・消灯します。Arduinoがリモコンから届く目に見えない光の信号を読み取り、電源ボタンを押すとリレーを動かして電球を点灯させ、消音ボタンを押すと消灯させます。家庭用電源を扱うため、配線は大人と一緒に、必ずプラグを抜いた状態で行ってください。

リードスイッチ式ドアセンサー
ライトを見るだけで、ドアが開いているか閉まっているか分かります。ドアに小さな磁石、ドア枠に磁石を検知するスイッチを取り付けます。ドアが閉まっているときは両者が並び、緑のLEDが点灯します。ドアが開いて磁石が離れると、赤いLEDが点灯します。家庭用警報装置のドアセンサーも、こんなシンプルな仕組みです。

IR受信機とリモコンでLEDを点灯・消灯
椅子に座ったまま3つの照明を操作するように、リモコンの1、2、3のボタンで青、赤、緑のLEDを点灯させ、4、5、6のボタンで同じ順に消灯させます。電源ボタンで3つを一斉に点灯させ、消音ボタンですべて消灯させます。リモコンの信号を受け取るのは赤外線受信機です。家の機器をリモコンで操作するプロジェクトへの第一歩です。

ESP32汎用IRリモコン(IR受信機 + リモコン + LED3個 + ブザー + OLED)
ESP32で市販のIRリモコンの信号を読み取り、自作の機器を操作できます!このプロジェクトでは赤外線受信機(NECプロトコル、38 kHz)で20ボタンのIRリモコンの信号を受信します。各ボタンには異なる動作を割り当てています。1、2、3ボタンで3個のLEDをそれぞれオン・オフし、PWRボタンですべて消灯します。4~9の数字ボタンではOLEDに特別な表示を出します。SSD1306 OLEDには最後に押したボタンを大きく表示し、実行した動作の説明とLEDの状態もリアルタイムで表示します。未知のボタンを押すとOLEDにHEXコードを表示します。

BluetoothでスマートフォンからLEDを操作
スマートフォンで部屋の照明をつけたり消したりするための、小さな練習です。Bluetoothモジュールがスマートフォンから送られた文字をArduinoに伝えます。ある文字で赤いLEDが、別の文字で緑のLEDが点灯し、同じ文字を小文字で送ると消灯します。無線操作の仕組みを、ごくシンプルな形で学べます。
ほかの記事
BlueGraysアプリ
この回路をスマホでステップごとに作る
回路図、コード、部品リストを1つの画面で確認できます。Arduino、ESP32、Picoのプロジェクトを200件以上用意しています。

