HC-12モジュールで信頼性の高い長距離Arduinoネットワークを構築する
このArduinoチュートリアルでは、複数のArduinoボード間で最大1.8kmの長距離無線通信を実現できるHC-12ワイヤレスシリアル通信モジュールの使い方を学びます。詳細については、以下のビデオまたは文章チュートリアルをご覧ください。 まず、HC-12ワイヤレスシリアルポート通信モジュールを詳しく見ていきましょう。主な仕様は以下の通りです。 これらの値は、選択したシリアルおよびオーバーザエアボーレートによって変わります(下表参照)。 HC-12モジュールにはマイクロコントローラが搭載されていますが、ユーザーがプログラミングする必要はありません。モジュールの設定にはATコマンドを使用し、Arduino、PC、またはその他のマイクロコントローラからシリアルポート経由で送信できます。ATコマンドモードに入るには、モジュールの「Set」ピンをローレベル(Low)に設定するだけです。 HC-12モジュールをArduinoに接続し、最初の例を作成します。回路図は以下の通りです。モジュールの動作電圧は3.2V〜5.5Vで、より安定した動作のためにデカップリングコンデンサと外部電源の使用が推奨されます。ただし、このチュートリアルの3つの例ではすべてPCのUSB電源を使用しましたが、問題は発生しませんでした。 最初のモジュールをArduino UNOに、2つ目のモジュールをArduino MEGAに接続しましたが、もちろん任意のボードを使用できます。 最初の例として、2つのモジュール間でシリアルモニタを使用した基本通信を行うArduinoコードを示します。同じコードを両方のArduinoにアップロードします。 2台のArduinoを別々のPCに接続しても、1台のPCで2つのArduino IDEを起動してそれぞれ別のCOMポートを選択しても動作します。シリアルモニタで文字を入力して送信すると、一方から他方へ無線でデータが転送されます。 シリアルモニタで文字を入力して送信ボタンを押すと、最初のArduinoで この同じコードでATコマンドを送信し、モジュールのパラメータを設定することもできます。モジュールの「Set」ピンをGNDまたはArduinoのデジタルピンに接続し、ローレベルに設定するだけです。 ATコマンドモードに正しく入れたかテストするには、シリアルモニタで「AT」と入力し、「OK」という応答が返ってくるか確認します。全12種類のATコマンドがあり、ボーレート、チャンネル、送信電力などのパラメータを変更できます。例えば「AT+B38400」と送信すると、ボーレートが38400に設定されます。 2つのプッシュボタンを使って通信チャンネルを切り替え、着信データを文字列変数に格納する別の方法を紹介します。 注意: 両HC-12モジュールの「Set」ピンはそれぞれのArduinoのピン6に接続。最初のArduinoの2つのボタンはピン4と3に接続。 まずピンを定義し、「Set」ピンをHighにして通常(透過)モードで動作させます。最初のwhileループで着信データを1バイトずつ読み取り、String変数 ボタン1が押されると「AT+C001」を送信し、相手側でもATコマンドモードに入って同じコマンドを実行することで、両モジュールのチャンネルを同時に切り替えます。ボタン2でも同様にチャンネル02に切り替え可能です。最後に 3つ目の例では、最初のArduinoに接続した加速度センサ(ADXL345)の傾きに応じて、2つ目のArduinoのステッピングモータ位置を無線制御します。 回路にはステッピングモータの原点(0度)を検出するマイクロスイッチも含まれます。 加速度センサとステッピングモータの詳細な接続・使用方法は別チュートリアルを参照してください。ここではHC-12関連のコード部分のみ解説します。 送信側では加速度センサのX/Y軸値から0〜180度の角度を算出し、100回測定の平均を取ってスムージング。さらに前回値から±2度以上変化した場合のみ、開始マーカー「s」と終了マーカー「e」を付けて送信します。受信側では「s」を検出してから「e」が来るまでデータを蓄積し、整数に変換してステッピングモータを目標位置まで回転させます。A4988ドライバの16分割ステップ設定に合わせ、0〜180度を0〜1600ステップにマッピングしています。 以上でこのArduinoチュートリアルは終了です。ご質問はコメント欄でお気軽にどうぞ。概要
HC-12ワイヤレス通信モジュール

ArduinoとHC-12の接続

必要なコンポーネント
例1:基本通信のArduinoコード
/* Arduino Long Range Wireless Communication using HC-12 Example 01
by Dejan Nedelkovski, www.HowToMechatronics.com */
#include <SoftwareSerial.h>
SoftwareSerial HC12(10, 11); // HC-12 TX Pin, HC-12 RX Pin
void setup() {
Serial.begin(9600); // PCとのシリアル通信
HC12.begin(9600); // HC-12とのシリアル通信
}
void loop() {
while (HC12.available()) { // HC-12からデータを受信
Serial.write(HC12.read()); // シリアルモニタに出力
}
while (Serial.available()) { // シリアルモニタからデータを受信
HC12.write(Serial.read()); // HC-12に送信
}
}
コードの動作原理
Serial.available()がtrueになり、HC12.write()でデータがHC-12モジュールに送られます。このモジュールが無線でデータを転送し、2つ目のArduinoではHC12.available()がtrueになり、Serial.write()でシリアルモニタにデータが表示されます。
主なATコマンド
例2:プッシュボタンでチャンネル切り替え

最初のArduinoコード(ボタン側)
/* Arduino Long Range Wireless Communication using HC-12 Example 02
Changing channels using push buttons - Buttons side
by Dejan Nedelkovski, www.HowToMechatronics.com */
#include <SoftwareSerial.h>
#define setPin 6
#define button1 4
#define button2 3
SoftwareSerial HC12(10, 11);
byte incomingByte;
String readBuffer = "";
int button1State = 0, button1Pressed = 0;
int button2State = 0, button2Pressed = 0;
void setup() {
Serial.begin(9600);
HC12.begin(9600);
pinMode(setPin, OUTPUT);
pinMode(button1, INPUT);
pinMode(button2, INPUT);
digitalWrite(setPin, HIGH); // 通常モード
}
void loop() {
// 着信データをString変数に格納
while (HC12.available()) {
incomingByte = HC12.read();
readBuffer += char(incomingByte);
}
delay(100);
// シリアルモニタからのデータを他方のHC-12へ転送
while (Serial.available()) {
HC12.write(Serial.read());
}
// ボタン1押下でチャンネル01に設定
button1State = digitalRead(button1);
if (button1State == HIGH && button1Pressed == LOW) {
button1Pressed = HIGH;
delay(20);
}
if (button1Pressed == HIGH) {
HC12.print("AT+C001"); // 他方のモジュールへATコマンド送信
delay(100);
digitalWrite(setPin, LOW); // ATコマンドモードへ
delay(100);
HC12.print("AT+C001"); // 自身のHC-12へATコマンド送信
delay(200);
while (HC12.available()) {
Serial.write(HC12.read()); // 応答をシリアルモニタに表示
}
Serial.println("Channel successfully changed");
digitalWrite(setPin, HIGH); // 通常モードに戻る
button1Pressed = LOW;
}
// ボタン2押下でチャンネル02に設定(同様の処理)
button2State = digitalRead(button2);
if (button2State == HIGH && button2Pressed == LOW) {
button2Pressed = HIGH;
delay(100);
}
if (button2Pressed == HIGH) {
HC12.print("AT+C002");
delay(100);
digitalWrite(setPin, LOW);
delay(100);
HC12.print("AT+C002");
delay(200);
while (HC12.available()) {
Serial.write(HC12.read());
}
Serial.println("Channel successfully changed");
digitalWrite(setPin, HIGH);
button2Pressed = LOW;
}
checkATCommand();
readBuffer = "";
}
// シリアルモニタ経由でATコマンドを受信したかチェック
void checkATCommand() {
if (readBuffer.startsWith("AT")) {
digitalWrite(setPin, LOW);
delay(200);
HC12.print(readBuffer);
delay(200);
while (HC12.available()) {
Serial.write(HC12.read());
}
digitalWrite(setPin, HIGH);
}
}2つ目のArduinoコード(受信側)
/* Arduino Long Range Wireless Communication using HC-12 Example 02
Changing channels using push buttons
by Dejan Nedelkovski, www.HowToMechatronics.com */
#include <SoftwareSerial.h>
#define setPin 6
SoftwareSerial HC12(10, 11);
byte incomingByte;
String readBuffer = "";
void setup() {
Serial.begin(9600);
HC12.begin(9600);
pinMode(setPin, OUTPUT);
digitalWrite(setPin, HIGH);
}
void loop() {
while (HC12.available()) {
incomingByte = HC12.read();
readBuffer += char(incomingByte);
}
delay(100);
while (Serial.available()) {
HC12.write(Serial.read());
}
// チャンネル01設定コマンド受信時
if (readBuffer == "AT+C001") {
digitalWrite(setPin, LOW);
delay(100);
HC12.print(readBuffer);
delay(200);
while (HC12.available()) {
Serial.write(HC12.read());
}
Serial.println("Channel successfully changed");
digitalWrite(setPin, HIGH);
readBuffer = "";
}
// チャンネル02設定コマンド受信時
if (readBuffer == "AT+C002") {
digitalWrite(setPin, LOW);
delay(100);
HC12.print(readBuffer);
delay(200);
while (HC12.available()) {
Serial.write(HC12.read());
}
Serial.println("Channel successfully changed");
digitalWrite(setPin, HIGH);
readBuffer = "";
}
checkATCommand();
readBuffer = "";
}
void checkATCommand() {
if (readBuffer.startsWith("AT")) {
digitalWrite(setPin, LOW);
delay(100);
HC12.print(readBuffer);
delay(200);
while (HC12.available()) {
Serial.write(HC12.read());
}
digitalWrite(setPin, HIGH);
}
}コードの説明
readBufferに結合します。これにより「Test123」のような文字列を7回のイテレーションで完全に受信できます。
checkATCommand()関数で、受信文字列が「AT」で始まるATコマンドかどうかを判定し、該当する場合は実行します。例3:加速度センサでステッピングモータを制御


必要なコンポーネント
最初のArduino(送信側・加速度センサ)コード
/* Arduino Long Range Wireless Communication using HC-12 Example 03
Stepper Motor Control using Accelerometer - Transmitter
by Dejan Nedelkovski, www.HowToMechatronics.com */
#include <SoftwareSerial.h>
#include <Wire.h>
SoftwareSerial HC12(10, 11);
float angle;
int lastAngle = 0, count = 0, angleSum = 0;
// ADXL345レジスタアドレス
#define Power_Register 0x2D
#define X_Axis_Register_DATAX0 0x32
#define X_Axis_Register_DATAX1 0x33
#define Y_Axis_Register_DATAY0 0x34
#define Y_Axis_Register_DATAY1 0x35
#define Z_Axis_Register_DATAZ0 0x36
#define Z_Axis_Register_DATAZ1 0x37
int ADXAddress = 0x53;
int X0, X1, X_out, Y0, Y1, Y_out, Z1, Z0, Z_out;
float Xa, Ya, Za;
void setup() {
HC12.begin(9600);
Wire.begin();
Serial.begin(9600);
delay(100);
Wire.beginTransmission(ADXAddress);
Wire.write(Power_Register);
Wire.write(8); // 測定モード有効
Wire.endTransmission();
}
void loop() {
// X軸読み取り
Wire.beginTransmission(ADXAddress);
Wire.write(X_Axis_Register_DATAX0);
Wire.write(X_Axis_Register_DATAX1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress, 2);
if (Wire.available() <= 2) {
X0 = Wire.read();
X1 = Wire.read();
X1 = X1 << 8;
X_out = X0 + X1;
Xa = X_out / 256.0;
}
// Y軸読み取り
Wire.beginTransmission(ADXAddress);
Wire.write(Y_Axis_Register_DATAY0);
Wire.write(Y_Axis_Register_DATAY1);
Wire.endTransmission();
Wire.requestFrom(ADXAddress, 2);
if (Wire.available() <= 2) {
Y0 = Wire.read();
Y1 = Wire.read();
Y1 = Y1 << 8;
Y_out = Y0 + Y1;
Ya = Y_out / 256.0;
}
// X, Yから0-180度の角度を算出
if (Y_out > 0) angle = map(Y_out, 0, 256, 90, 0);
else if (Y_out < 0) { angle = map(Y_out, 256, 0, 90, 0); angle = 90 - angle; }
if (X_out < 0 && Y_out < 0) angle = 180;
if (X_out < 0 && Y_out > 0) angle = 0;
int angleInt = int(angle);
// 100回読み取り平均でスムージング
angleSum += angleInt;
count++;
if (count >= 100) {
angleInt = angleSum / 100;
angleSum = 0; count = 0;
// 前回から±2度以上変化した場合のみ送信
if (angleInt > lastAngle + 2 || angleInt < lastAngle - 2) {
Serial.println(angleInt);
String angleString = String(angleInt);
// 開始マーカー's'と終了マーカー'e'を付加
HC12.print("s" + angleString + "e");
delay(10);
lastAngle = angleInt;
}
}
}2つ目のArduino(受信側・ステッピングモータ)コード
/* Arduino Long Range Wireless Communication using HC-12 Example 03
Stepper Motor Control using Accelerometer - Receiver
by Dejan Nedelkovski, www.HowToMechatronics.com */
#include <SoftwareSerial.h>
SoftwareSerial HC12(10, 11);
char incomingByte;
String readBuffer = "";
const int dirPin = 4, stepPin = 3, button = 2;
int currentAngle = 0, lastAngle = 0, rotate = 0;
void setup() {
Serial.begin(9600);
HC12.begin(9600);
pinMode(dirPin, OUTPUT);
pinMode(stepPin, OUTPUT);
pinMode(button, INPUT_PULLUP); // マイクロスイッチ
delay(10);
digitalWrite(dirPin, HIGH);
// 原点復帰
boolean startingPosition = true;
while (startingPosition) {
digitalWrite(stepPin, HIGH); delayMicroseconds(200);
digitalWrite(stepPin, LOW); delayMicroseconds(200);
if (digitalRead(button) == LOW) startingPosition = false;
}
delay(100);
}
void loop() {
readBuffer = "";
boolean start = false;
// 's'と'e'の間のデータを読み取り
while (HC12.available()) {
incomingByte = HC12.read();
delay(5);
if (start) {
if (incomingByte != 'e') readBuffer += char(incomingByte);
else start = false;
} else if (incomingByte == 's') {
start = true;
}
}
currentAngle = readBuffer.toInt();
if (currentAngle > 0 && currentAngle < 180) {
// 角度をステップ数に変換(16分割ステップ: 180度 = 1600ステップ)
currentAngle = map(currentAngle, 0, 180, 0, 1600);
digitalWrite(dirPin, LOW);
if (currentAngle != lastAngle) {
if (currentAngle > lastAngle) {
rotate = currentAngle - lastAngle;
for (int x = 0; x < rotate; x++) {
digitalWrite(stepPin, HIGH); delayMicroseconds(400);
digitalWrite(stepPin, LOW); delayMicroseconds(400);
}
} else {
rotate = lastAngle - currentAngle;
digitalWrite(dirPin, HIGH); // 逆回転
for (int x = 0; x < rotate; x++) {
digitalWrite(stepPin, HIGH); delayMicroseconds(400);
digitalWrite(stepPin, LOW); delayMicroseconds(400);
}
}
}
lastAngle = currentAngle;
}
}コードのポイント
製造プロセス
- Arduino UNOで作るLCDアニメーション&ゲーム入門
- JX Wave Generator – Arduino互換DDS発振器(OLEDディスプレイ搭載)
- u-blox LEA-6H 02 GPSモジュール: Arduino & Python 統合ガイド
- ワイヤレス非接触温度スキャナー:Arduino Nano+Bluetooth+MLX90614
- Python 3からArduino UNOを制御 – シリアル通信でLED点滅デモ
- Arduino NanoとRDA5807Mで作るコンパクトFMラジオ
- Arduino + Windows IoT + Azure クラウドによる高度な転倒検知システム
- ArduRadio:Arduino自作FMラジオ付き目覚まし時計
- Arduino超音波距離センサー:HC-SR04で測定システム構築
- ワイヤレス通信距離をマスターする:電力、データレート、干渉が接続性をどう形作るか
- 電池駆動の長距離ワイヤレスが接続性をどう変えるか