LEGOで作るBluetooth遠隔操作ラジコン(Arduinoコマンド、ESP32系マイコンATOM使用)

LEGOで作るBluetoothラジコンカー

Bluetooth通信でジョイスティックを使って操作するラジコンの作り方を詳しく紹介します。

ラジコン本体はLEGOブロックで4輪の車を組み立てて、動力には360°回転サーボを使用しています。
左右の操作もLEGOブロックで実際の車と同じようなステアリング機構を組み立ててRCサーボで駆動させます。
コントローラはジョイスティックを使ってこれもLEGOで組み立てています。


操作部に使用していた「M5StickC Plus」が生産中止のため、後継機の「M5StickC Plus2」用に書き換えたものを以下のリンクで紹介しています。

LEGOで作るBluetoothラジコンカー M5StickC Plus2 + ATOM Lite
M5StickC Plus2を使用してBluetoothでジョイスティック操作のLEGOラジコンの作り方を紹介。駆動はRCサーボでATOM Liteで制御します。

LEGOなので駆動部を流用すれば、いろいろなラジコンを作ることができます。
自分好みのラジコンを作って走らせてみましょう♪

組み立て方等は以下のYouTube「ロジカラチャンネル」でも詳しく紹介しています。

今回の内容は今まで紹介したプログラミングの集大成的な内容となります。
プログラムや構成部品の使い方については過去に紹介していますのでその都度リンクを貼っています。
プログラミングをやったことがない方でも、過去の記事を見ていただければ今回のラジコンも作れるようになると思いますので一緒に作っていきましょう♪
スポンサーリンク

1.作ったラジコンの紹介

今回製作したラジコン本体は下写真のようになります。

LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー

コントローラも下写真のようにLEGOで組み立ててジョイスティックで操作します。

<通信接続開始>

LEGOで作るBluetoothラジコンカー

<通信接続完了>

LEGOで作るBluetoothラジコンカー

<操作可能>

LEGOで作るBluetoothラジコンカー

<通信遮断>

LEGOで作るBluetoothラジコンカー

ラジコンカー本体「ATOM Tailbat」の電源を入れて(ボタンを1回押す、2回押しで電源OFF)操作部「M5StickC Plus」の電源を入れる(左下側面ボタン1回押す、6秒以上長押しで電源OFF)とラジコンカー本体とのBluetooth接続が開始されます。

接続が完了すると青文字で「Connected!」が表示され、画面が切り換わってラジコン操作可能状態になります。
操作中はジョイスティックのX,Y座標が送信され、ラジコンカー側からサーボの動作角度、回転速度の値を受信し続けます。
Bluetooth接続が遮断されると「LOST!」が表示されるので、ラジコンカーの電源が入っていることを確認して「M5StickC Plus」の電源ボタンを押すと再接続が開始されます。

ラジコンカーは2つのギヤの組み合わせを変えることで、パワータイプとスピードタイプに切り換えられます。

ジョイスティックを押し込むと「Press:」が「1」に離すと「0」になります。
「M5StickC Plus」のボタンAを押すと「BtnA:」が「1」に、もう一度ボタンAを押すと「0」になります。
今回は使用していませんが、ラジコン側へ情報は送信されているため、ライトを点灯させる等機能を追加するときに使用できます。
スポンサーリンク

2.ラジコンの全体構成

ラジコンの全体構成について紹介します。

コントローラ部は「M5StickC Plus」に「JoyStick(ジョイスティック)」を接続して使用します。
どちらも「M5Stack」社製です。

「M5Stack」社の製品はLEGOブロックと組み合わせられるものが多くあり、プログラミングの動作確認だけでなく最終の完成形もイメージできるので、アイデアを形にするにはもってこいです。

LEGOで作るBluetoothラジコンカー

「M5StickC Plus」と「JoyStick(ジョイスティック)」については以下のリンクで詳しく紹介しています。

M5StickC Plusの使い方、初期設定、サンプルプログラム、M5StickCとの違い等を詳しく紹介
M5StickC PlusをArduino IDEやPlatformIOで使うための初期設定やサンプルプログラムでの動作確認の方法です。ビジュアルプログラミングのUiFlowの初期設定についても紹介します。
ジョイスティック(JoyStick)の使い方、つなぎ方、動作原理等を回路図も使って詳しく解説
ジョイスティックの仕組みから、使用方法をカメラ等を固定して縦横に操作するパンチルト機構(RCサーボ2個使用)の動作でサンプルプログラムを使って詳しく紹介します。

ラジコン本体は「M5Stack」社製の「ATOM Matrix」にバッテリーとして「ATOM TailBat」を組み合わせて制御部として使用します。

「ATOM Matrix」ではなくても「ATOM LITE」でも同じプログラムで動作可能です。
今回は「ATOM Matrix」の25個のフルカラードットマトリクスLEDを使用したかったので「ATOM Matrix」を使用しています。
見た目だけなのでどちらでもお好きな方を使用してください。

バッテリーに使用している「ATOM TaiBat」は容量が190mAしかありません。
このため動作時間は数分です。長時間遊ぶにはモバイルバッテリーの搭載を検討しましょう。
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー

「ATOM Matrix」については以下のリンクで紹介しています。初期設定については「ATOM LITE」と同じです。

ドットマトリクスLED簡単制御方法、点灯位置も色も思いのままに♪ATOM Matrix
ATOM Matrixには5×5の25個のフルカラーLEDで構成されたドットマトリクスLEDがあり、2重配列と「FastLED」ライブラリを使用することで簡単に制御できます。
ATOM LITE の初期設定。プログラミング初心者におすすめ
Platform IOを使用した ATOM LITE の初期設定です。ファイルを作成、必要なライブラリの準備、初期設定方法を紹介。実際に「Lチカ」プログラムを「コピペ」で書き込み動作確認まで行います。

駆動部には同じく「M5Stack」社製の360°回転サーボキット「Servo Kit 360°」を使用します。
「Servo Kit 360°」はLEGOとの組み合わせに特化しており、LEGO互換のブラケット付きで、LEGOのギアや車輪を接続できるアクスルコネクタもセットになっています。

2個セットですが、今回は1個使用し、もう1個はLEGO互換のブラケットのみ使用します。
このブラケットには「Tower Pro」社製の「RCサーボ(180°)」を取り付けてステアリング駆動用として使用します。

LEGOで作るBluetoothラジコンカー

上写真が回転駆動(後輪)用の360°回転サーボ「Servo Kit 360°」のサーボです。
付属のアクスルコネクターでLEGOの十字軸用ギアを取り付けています。

LEGOで作るBluetoothラジコンカー

上写真がステアリング(前輪)用のRCサーボ「SG90」です。
「Servo Kit 360°」のもう片方のサーボと入れ換えてLEGO用のブラケットを流用しています。

「Servo Kit 360°」や「RCサーボ」については以下のリンクで詳しく紹介しています。

RCサーボモータとは、使い方と簡単制御方法(Arduinoコマンド、ライブラリ不要)の紹介
ラジコンの駆動部等に使用されるRCサーボについての簡単な制御方法をサンプルプログラムで詳しく紹介します。ATOM LITE使用(M5Stack社製ESP32系マイコンボード)
RCサーボモータの使い方(M5Stack系Arduinoコマンド)ライブラリ「ESP32Servo」使用
RCサーボモータの制御方法にはPWM制御の理解が必要ですが、今回はライブラリ使用で角度指定して簡単に制御する方法をATOM LITEを使用したサンプルプログラムで詳しく紹介します。
スポンサーリンク

3.LEGOブロックでのラジコンカーの作り方

LEGOブロックを使用したラジコンカーの組み立て方を各部位ごとに紹介します。

後輪、リアフレーム部

LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー

エンジン部(360°回転サーボ)

LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー

後輪駆動部(後輪、リアフレーム部 + エンジン部)

LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー

前輪、ステアリング部(RCサーボSG90)

LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー

前輪、ステアリング部 + 後輪駆動部

LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー

ラジコンカー本体完成

LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー

サーボ接続端子(5V, 0V分岐)L型ピンヘッダー改造

LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
L型ピンヘッダー2列を改造してはんだ付けしたものです。
ピンを引き抜いて2段目の長い方が下になるように1段目に差し込みます。
2段目に短い方を差し込んで切ったり曲げたりしてからハンダ付けしています。
※加工の際はケガや火傷にご注意ください。

ATOM Matrix + ATOM Tail Bat接続

LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー

操作部(コントローラー)

LEGOで作るBluetoothラジコンカー

パワータイプ から スピードタイプへ組み換え

初期状態ではギア比を減速(パワータイプ)するように組んでますが、下写真のようにギアを入れ換えることでギア比を増速(スピードタイプ)するように組み換えることができます。
速度は4倍になるのでコッチの方が速くて遊んでて楽しいです♪

LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー

見た目がちょっとイマイチ(見方によってはカッコイイ?)なので初期状態はパワータイプにしました。

おまけ:ウイリーユニット

最後に「おまけ」になりますがラジコンといえばウイリー!(私のイメージw)なので、下写真のようなウイリーユニットもLEGOなら作れます。いろいろ改造して自分好みにカスタマイズしてみましょう♪

LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー
LEGOで作るBluetoothラジコンカー

4.配線図

ラジコンの操作部と駆動部の配線は以下のようになります。
配線図では「ATOM LITE」の画像を使ってますが「ATOM Matrix」でも同じ配線です。

操作部(コントローラー)

Bluetoothラジコン コントローラ配線

駆動部(360°回転サーボ+180°RCサーボSG90)

Bluetoothラジコン コントローラ駆動部(RCサーボ)

操作部は「M5StickC Plus」と「JoyStick」を「JoyStick」に付属の「GROVEコネクタ配線」で接続するだけです。

駆動部は「360°回転サーボ」の信号線を「ATOM」の「G21」に接続し、「180°RCサーボSG90」の信号線を「G25」に接続します。

サーボの「5V」と「0V」を「ATOM」の「5V」と「G」を分岐(L型ピンヘッダー改造またはジャンパー線やブレッドボード等を使用)させてそれぞれに接続します。

安定動作のためには「5V-0V」間に100μF程度の電解コンデンサと0.1μFのセラミックコンデンサを取り付けた方が良いですが今回は省略しています。

5.サンプルプログラム(コピペ)

プログラムは以下に準備しましたので「コピペ」して書き込んでください。
操作部(コントローラー)はこの下で、ラジコン本体はさらに下にあります。
※コピーは下コード(黒枠)内の右上角にある小さなアイコンのクリックでもできます。

・操作部(コントローラー)

#include <M5StickCPlus.h>     // ヘッダーファイル
#include "BluetoothSerial.h"
BluetoothSerial SerialBT;

// 変数宣言
String slave_name = "RC_CAR";       // スレーブ側の接続名
String master_name = "CONTROLLER";  // マスターの接続名
bool connected = 0;     // 接続状態格納用
int connect_count = 3;  // 再接続実行回数
String data = "";       // 受信データ格納用
int btn_pw = 0;         // 電源ボタン状態取得用
float battery;          //バッテリー残量表示用
float x_data;           // ジョイスティックX軸(角度サーボ)
float y_data;           // ジョイスティックY軸(回転サーボ)
int press;              // ジョイスティック押し込みボタン
int btn_a = 0;          // 本体ボタンA動作確認用
/********************** 再起動(リスタート)処理 **********************/
void restart() {
  // 電源ボタン状態取得(1秒以下のONで「2」1秒以上で「1」すぐに「0」に戻る)
  btn_pw = M5.Axp.GetBtnPress();
  if (btn_pw == 2) {  // 電源ボタン短押し(1秒以下)なら
    ESP.restart();    // 再起動
  }
}
// 初期設定 ----------------------------------------------------------
void setup(){
  M5.begin();         // 本体初期化
  Serial.begin(9600); // シリアル出力初期化
  Wire.begin(32, 33); // Grove端子をI2C設定(SDA, SCL)

  // LCD表示設定
  M5.Axp.ScreenBreath(9); // 液晶画面明るさレベル(標準12)
  M5.Lcd.setTextFont(4);  // フォント設定

  // Bluetooth接続開始
  SerialBT.begin(master_name, true);  // マスターとして初期化。trueを書くとマスター、省略かfalseを指定でスレーブ
  M5.Lcd.print("Bluetooth\nSearch!\nRC_CAR\n.");
  Serial.print("Bluetooth\nSearch!\nRC_CAR\n.");

  // Bluetooth接続実行
  while (connected == 0) {    // connectedが0(未接続)なら接続実行を繰り返す
    if (connect_count != 0) { // 再接続実行回数カウントが0でなければ
      connected = SerialBT.connect(slave_name); // 接続実行(接続成功で1を返す)
      M5.Lcd.print(".");
      connect_count--;        // 再接続実行回数カウント -1
    } else {                  // 再接続実行回数カウントが0なら接続失敗
      M5.Lcd.setTextColor(RED, BLACK);
      M5.Lcd.print("\nFailed!");
      Serial.print("\nFailed!");
      while (1) {restart();}  // 無限ループ(再起動待機)
    }
  }
  // 接続確認
  M5.Lcd.setTextColor(WHITE, BLACK);
  if (connected) {                    // 接続成功なら
    M5.Lcd.setTextColor(CYAN, BLACK);
    M5.Lcd.println("\nConnected!");   // 「Connected!」表示
    Serial.println("\nConnected!");
  } else {                            // 接続失敗なら
    M5.Lcd.setTextColor(RED, BLACK);
    M5.Lcd.println("\nFailed!!");     // 「Failed!!」表示
    Serial.println("\nFailed!!");
    while (1) {restart();}            // 無限ループ(再起動待機)
  }
  delay(1000);                        // 接続結果確認画面表示時間
  // LCD表示リセット
  M5.Lcd.fillScreen(BLACK);
  M5.Lcd.setTextColor(WHITE, BLACK);
}
// メイン処理 --------------------------------------------------------
void loop() {
  M5.update();  // ボタン状態更新
  // ジョイスティックI2Cデータ取得
  Wire.requestFrom(0x52, 3);  // I2Cデータ取得(アドレス、バイト数)
  while (Wire.available()) {  // 通信データがあれば
    x_data = Wire.read();     // X軸アナログ値取得(0〜255)
    y_data = Wire.read();     // Y軸アナログ値取得(0〜255)
    press = Wire.read();      // 押し込み時ボタン状態取得(true/false)
  }
  // ボタン操作処理(未使用)
  if (M5.BtnA.wasPressed()) { // ボタンAが押されていれば
    btn_a = !btn_a;           // ボタン状態反転
  }
  // Bluetoothデータ送信(「CR」区切り文字)
  if (connected == 1) {       // connectedが1(接続済)ならデータ送信
    SerialBT.printf("%3.0f,%3.0f,%d,%d\r", x_data, y_data, press, btn_a); // X軸アナログ値, Y軸アナログ値, 押し込み時ボタン状態, 本体ボタンA操作状態
  }
  // 液晶表示
  M5.Lcd.setCursor(0, 0);                     // 座標指定
  M5.Lcd.setTextFont(4);                      // フォント設定
  M5.Lcd.println("Controller");               // 項目表示
  M5.Lcd.printf("X : %3.0f      \n", x_data); // X軸アナログ値
  M5.Lcd.printf("Y : %3.0f      \n", y_data); // Y軸アナログ値
  M5.Lcd.printf("Press : %d      \n", press); // 押し込みボタン状態表示
  M5.Lcd.printf("BtnA : %d      \n", btn_a);  // 本体ボタン操作状態表示

  // スレーブ側からの受信データをLCDに表示
  if (SerialBT.available()) {               // Bluetoothデータ受信で
    data = SerialBT.readStringUntil('\r');  // 区切り文字「CR」までデータ取得
    M5.Lcd.print("BT: ");                   // 受信データ液晶表示
    M5.Lcd.println(data + "  ");            // 液晶表示は改行あり
    Serial.print(data + "\r");              // シリアル出力(「CR」付きで先頭復帰し常時表示)
  } else {                                  // Bluetoothデータ受信なしで
    M5.Lcd.setTextColor(ORANGE, BLACK);
    M5.Lcd.println("LOST!             ");   // 警告表示
    M5.Lcd.setTextColor(WHITE, BLACK);
  }
  // バッテリー残量(MAX約4.2V、限界電圧3V)パーセント換算表示
  battery = (M5.Axp.GetBatVoltage() - 3.1) * 100; //バッテリー残量取得、換算
  if (battery > 100) {                            //換算値が100以上なら
    battery = 100;                                //100にする
  }
  M5.Lcd.setTextFont(2);                // フォント設定
  M5.Lcd.setCursor(100, 220);           // 座標
  M5.Lcd.printf("%3.0f%%\n", battery);  //バッテリー残量表示

  restart();  // 再起動(リスタート)処理
  delay(100); // 遅延時間
}

・ラジコン本体

#include <M5Atom.h>
#include "BluetoothSerial.h"
BluetoothSerial SerialBT;
//FastLED(CRGB構造体)設定
CRGB dispColor(uint8_t r, uint8_t g, uint8_t b) {
  return (CRGB)((r << 16) | (g << 8) | b);
}
// 変数宣言
float x_data;           // ジョイスティックX軸(角度サーボ)
float y_data;           // ジョイスティックY軸(回転サーボ)
float speed;            // 回転サーボ速度値換算
int press;              // ジョイスティック押し込みボタン
int btn_a;              // コントローラ側本体ボタンA操作状態
float setting[16][5];   // チャンネル[0〜15]ごとの[周波数, bit数, 0゜パルス時間ms, 最大角度パルス時間ms, 最大動作角度]格納配列
float x_angle;          // X軸角度指示値
float y_round;          // Y軸角度指示値
float duty_angle;       // X軸角度初期値
float duty_y_angle;     // Y軸角度初期値
float read_angle_data;  // X軸角度現在値
float read_round_data;  // Y軸角度現在値
int cnt;                // シリアル出力カウント用
/******************* PWM出力、サーボ仕様設定関数 *******************/
void servo_set(int pin, int channel, float freq, float bit_num, float time_min, float time_max, float angle_max) {
  // サーボ設定値をチャンネルごとの配列に格納
  setting[channel][0] = freq;       // 周波数
  setting[channel][1] = bit_num;    // bit数
  setting[channel][2] = time_min;   // 0゜パルス時間ms
  setting[channel][3] = time_max;   // 最大角度パルス時間ms
  setting[channel][4] = angle_max;  // 最大動作角度
  // PWM設定
  pinMode(pin, OUTPUT);               // PWM用ピンを出力設定
  ledcSetup(channel, freq, bit_num);  // PWM設定(チャンネル, 周波数, ビット数)
  ledcAttachPin(pin, channel);        // PWMチャンネル割り当て(ピン番号, チャンネル)
}
/********************** サーボ動作実行関数 **********************/
float servo(int channel, float angle) {
  float resolution;   // 分解能
  float period;       // 周期ms
  float duty_min;     // 0゜Duty計算値
  float duty_max;     // 最大角度Duty計算値
  float duty_active;  // 動作範囲のDuty計算値
  int duty_value;     // PWM出力Duty値(High状態)※整数型として小数点以下切り捨て
  float read_angle;   // PWM出力Duty値からの角度換算

  // サーボ定数算出
  resolution = pow(2, setting[channel][1]);                 // 分解能(2のbit数乗)
  period = 1000 / setting[channel][0];                      // 周期(1000ms / 周波数) 
  duty_min = (resolution / period) * (setting[channel][2]); // 0゜Duty計算値((分解能/周期)*0゜パルス時間μs)
  duty_max = (resolution / period) * (setting[channel][3]); // 最大角度Duty計算値((分解能/周期)*最大角度パルス時間μs)
  duty_active = duty_max - duty_min;                        // 動作範囲のDuty計算値
  duty_value = (angle * (duty_active / setting[channel][4])) + duty_min;  // 角度指定値(浮動小数点数) → PWM出力Duty値(整数)換算

  if (duty_value <= duty_min) { // PWM出力Duty値が0゜Duty計算値以下なら
    duty_value = duty_min + 1;  // PWM出力Duty値+1
  }
  // PWM出力実行
  ledcWrite(channel, duty_value); // 指定したチャンネルにDuty値を指定してPWM出力実行
  read_angle = (ledcRead(channel) - duty_min) * setting[channel][4] / duty_active;  // PWM出力Duty値を取得して角度に換算
  return read_angle;  // 角度を返す
}
// 初期設定 ----------------------------------------------------------
void setup(){
  M5.begin(false, false, true); // 本体初期化(UART, I2C, LED)
  Serial.begin(9600);           // シリアル出力初期化
  // Bluetooth接続開始
  SerialBT.begin("RC_CAR");     // 接続名を指定して初期化。第2引数にtrueを書くとマスター、省略かfalseでスレーブ

  // PWM出力、サーボ仕様設定(サーボごとに使用する端子、チャンネル等を設定)
  // PWM出力のチャンネルは 0,1/ 2,3/ 4,5/ 6,7/ 8,9/ 10,11 /12,13 /14,15でペア ※周波数と分解能はペアで同じにする必要があります。
  // (出力端子, チャンネル, 周波数, bit数, 0゜パルス時間ms, 最大角度動作時間ms, 最大動作角度)
  servo_set(25, 1, 50, 12, 0.5, 2.4, 180); // チャンネル1(ステアリング):PWM出力、サーボ仕様設定関数呼び出し
  servo_set(21, 2, 50, 12, 0.5, 2.5, 255); // チャンネル2(回転駆動):PWM出力、サーボ仕様設定関数呼び出し
}
// メイン処理 --------------------------------------------------------
void loop() {
  M5.update();  // ボタン状態更新
  // Bluetooth通信データ受信処理
  if (SerialBT.available()) { // コントローラからデータ受信で
    for (int i = 0; i < 25; i++) {              // ドットマトリクスLED25個分繰り返し
      M5.dis.drawpix(i, dispColor(0, 50, 50));  // LED点灯色指定
    }
    // ローカル変数宣言
    int i = 0;    // 取得データカウント用
    String data;  // 受信データ格納用
    data = "";    // データ初期化
    char c;       // 受信データ文字取得用
    // 受信データの文字列をint型に変換して変数に格納
    while (SerialBT.available()) {  // 受信データがある間実行
      c = SerialBT.read();          // Bluetoothデータを文字変数「c」へ格納
      if (c != ',' || c != '\r') {  // 受信データがカンマ「,」でも「CR」でなければ
        data = data + c;            // 文字変数「c」を文字列変数「data」に加算
      }
      if (c == ',') {               // カンマ「,」なら
        switch (i) {                // 「i」で分岐
          case 0:                   // 「0」ならX軸データ
            x_data = data.toInt();  // ジョイスティックX軸データ取得
            break;
          case 1:                   // 「1」ならY軸データ
            y_data = data.toInt();  // ジョイスティックY軸データ取得
            break;
          case 2:                   // 「2」ならスティック押し込みボタン状態
            press = data.toInt();   // ジョイスティック押し込みボタン状態取得(未使用)
            break;
        }
        i++;                        // i +1
        data = "";                  // 文字列変数初期化
      }
      if (c == '\r') {              // 「CR」なら最終データ(コントローラ側本体ボタンA)まで取得完了
        btn_a = data.toInt();       // コントローラ側本体ボタンA操作状態データ取得(未使用)
      } 
    }
    // ジョイスティックX軸受信データをステアリング角度サーボ指示値に換算
    x_angle = 65 * (x_data / 255) + 5;    // X軸ステアリング角度(65°)換算 + 微調整角度

    // ジョイスティックY軸受信データを回転駆動サーボ指示値に換算
    if (y_data >= 115 && y_data <= 139) { // 回転駆動不動領域設定、Y軸がこの範囲なら
      speed = 127;                        // 回転駆動停止(Duty比50%)
    } else if (y_data > 139) {            // +動作領域なら
      speed = ((y_data - 127) * 2) + 127; // 中間値(停止状態)からの増加分を2倍
    } else if (y_data < 115) {            // ー動作領域なら
      speed = y_data / 2;                 // 減少分を1/2倍
    }
    if (speed >= 255) {                   // +領域の増加分が255以上なら
      speed = 255;                        // 回転サーボ速度値は255(最大値)
    }
    y_round = 255 - speed;                //ジョイスティックY軸 - 回転駆動指示値換算(動作反転指示)

    // ステアリング(角度)サーボ動作実行
    read_angle_data = servo(1, x_angle);  // チャンネル1:サーボ動作実行関数呼び出し(チャンネル, 角度値)

    // 駆動(360゜回転)サーボ動作実行
    read_round_data = servo(2, y_round);  // チャンネル2:サーボ動作実行関数呼び出し(チャンネル, 回転指示値)

    // Bluetoothデータ送信
    SerialBT.printf("%3.0f,%3.0f\r", read_angle_data, read_round_data); // 区切り文字「CR」をつけてマスター側へ送信
  } else {
    for (int i = 0; i < 25; i++) {
      M5.dis.drawpix(i, dispColor(100, 30, 0)); // LED点灯
    }
  }
  // シリアル出力
  if (cnt == 10) {  // シリアル出力カウントが10なら
    Serial.printf("X: %3.0f Y: %3.0f P: %d B: %d A: %5.1f R: %5.1f\n", x_data, y_data, press, btn_a, read_angle_data, read_round_data); // X軸アナログ値, Y軸アナログ値, 押し込み時ボタン状態取得, コントローラ側本体ボタンA操作状態, 回転サーボDutyオン幅, 角度サーボDutyオン幅
    cnt = 0;  // シリアル出力カウントリセット
  }
  cnt++;      // シリアル出力カウント加算
  delay(100); // 遅延時間
}

6.使用したもの

ラジコンの製作に使用したものは以下になります。

・操作部(コントローラー)

操作部には「M5Stack」社製の「M5StickC Plus」を使用しますが、LEGO用のブラケットは以下の「腕時計アクセサリー付き」にしかついていません。


LEGOパーツはAmazonで探してみましたが無いものがあったり、タイヤが高いようです。
楽天では1個から購入できて全て揃ったので以下に楽天でリンクを集めました。

購入の際は念の為、購入先の写真や情報を確認してからご購入ください。

リフトアーム 3 x 5 – Hシェイプ :1個(ラジコン本体でさらに3個使用)

リフトアーム 2 x 4 – L字 :2個

コネクター ペグ(滑り止め):2個(ラジコン本体でさらに14個使用)

軸 – コネクターペグ Blue :2個(ラジコン本体でさらに9個使用)

コネクター ペグ 3M(滑り止め):4個(ラジコン本体でさらに12個使用)

・ラジコン本体

ラジコン本体は「ATOM Matrix」に「ATOM TailBat」の組み合わせで使用しましたが「ATOM LITE」でも大丈夫です。

「M5Stack」社製の360°回転サーボキット「Servo Kit 360°」はAmazon、楽天等では見つかりませんでした・・・
こちらは「スイッチサイエンス」で購入可能です。

または、以下の「Servo Kit 180°」をステアリング用に使用して、360°回転サーボ「SG90-HV 360」の組み合わせでも同じものが作れます。

配線には2列のL型ピンヘッダーが便利ですが、加工とハンダ付けが必要です。

ハンダ付けが苦手な方はジャンパー線とブレッドボードで配線図のように接続すれば動作可能です。


LEGOパーツは部品点数多いので長くなりますが、楽天で1個から買えて全て揃います。
同じ形状で色がいろいろありますので自分好みにカスタマイズしてみましょう。

ホイール:4個

タイヤ :4個

ギア – 40歯 :1個

ダブル べベルギア – 20歯 :1個

12M十字軸95mm :1本

5M 十字軸 39mm :2本

4M 十字軸 31mm :3本

コネクター ペグ(滑り止め無し):2個

ピンホール :4個

十字軸用ブッシュRed :4個

十字軸用ブッシュGray :2個

十字軸用1/2ブッシュ :6個

ピンコネクター ラウンド 1L :5個

リフトアーム 1 x 2 :2個

リフトアーム 3 x 5 – L字 :4個

リフトアーム 3 x 5 – L字 Red :2個

軸/ピンコネクター – 垂直 3L 2穴 :4個

軸/ピンコネクター – 垂直 3L:6個

リフトアーム 1 x 2 – 軸/ピン穴 :2個

リフトアーム 1 x 3 :1個

リフトアーム 1 x 5 :1個

リフトアーム 1 x 7 :3個

リフトアーム 1 x 9 :1個

全部揃えるとLEGOで約¥5,000-、「M5StickC Plus」や「ATOM Matrix」「ジョイスティック」「バッテリー」「サーボ」等で約¥10,000-です。
ラジコンとしては高いように思いますが「M5StickC Plus」や「ATOM Matrix」はそれ単体でプログラミング学習に最適なツールです。
当ブログでもこれらを使ったプログラミング方法について詳しく紹介しています。
LEGOブロックは想像力を膨らませれば形を変えていろいろなものが作れます。
プログラミングに挫折しそうになったり、飽きてきたらまたラジコンに組み換えて遊びましょう♪

LEGO互換品について

ガッツリLEGOをやってみようって方にはLEGO互換品が安くて便利です。
私はギヤとタイヤの詰め合わせとリフトアームの詰め合わせを買いました。

おすすめかというとおすすめはしませんwお金に余裕のある方は正規品を買った方がいいです。
ギヤやタイヤ、リフトアーム等大きな部品は特に問題を感じませんでしたが、小さな部品のコネクターペグ等は組み立てる時のはめ込みが硬いです。取り外すときかなり硬くて指がヒリヒリしてきます。
逆に滑り止め付は緩かったり、抜けやすかったりしました。
互換品が何種類存在しているかはわかりませんので、私が買ったものの印象になります。

ただ最初はどんな部品があってどう使うかもわからなかったので、一度にたくさんの種類のパーツをお試しできるので個人的にはかなりいい買い物でした。

アイデアを互換品で試して、確定したら正規品で完成させる。そんな使い方をしています。

リンクの都合上Amazon、楽天のリンクを貼っていますが、価格的にはYahoo!ショッピングが安かったりします。ご参考までに。

7.まとめ

LEGOブロックを使って、Bluetooth通信とジョイスティックで操作するラジコンカーの作り方を紹介しました。

今回はいつものプログラミングの紹介というより、LEGOがメインの内容となりました。
ラジコンはいろいろ作りましたがLEGOが一番自由度が高くて改造も簡単なのでこれからもLEGOでいろんなものを作っていきたいと思います。

プログラムについて今回は詳しく触れていませんが、事前に公開済みの以下の記事で「Bluetooth通信」や「PWM制御」による「RCサーボ」の使い方「ジョイスティック」の使い方を詳しく紹介しています。

M5StickC Plusで簡単Bluetooth相互通信、遠隔操作する方法
M5StickC PlusとM5StickC(両方M5StickC Plusやパソコンでも可)を使用して、Bluetooth(ブルートゥース)相互通信、遠隔操作(Arduinoコマンド使用)する方法を紹介します。
PWM制御とは?Arduino(ESP32)コマンドで使い方を詳しく紹介
PWM制御の基本動作からArduinoコマンドを使ったプログラミング方法、サンプルプログラムを使用して音(ブザー)と光(LED)で動作確認しながら使い方を詳しく紹介します。
RCサーボモータとは、使い方と簡単制御方法(Arduinoコマンド、ライブラリ不要)の紹介
ラジコンの駆動部等に使用されるRCサーボについての簡単な制御方法をサンプルプログラムで詳しく紹介します。ATOM LITE使用(M5Stack社製ESP32系マイコンボード)
ジョイスティック(JoyStick)の使い方、つなぎ方、動作原理等を回路図も使って詳しく解説
ジョイスティックの仕組みから、使用方法をカメラ等を固定して縦横に操作するパンチルト機構(RCサーボ2個使用)の動作でサンプルプログラムを使って詳しく紹介します。

ラジコンの制御に使用したマイコンボード「M5StickC Plus」や「ATOM Matrix(ATOM LITE)」については以下のリンクで初期設定から使い方まで詳しく紹介しています。

M5StickC Plusの使い方、初期設定、サンプルプログラム、M5StickCとの違い等を詳しく紹介
M5StickC PlusをArduino IDEやPlatformIOで使うための初期設定やサンプルプログラムでの動作確認の方法です。ビジュアルプログラミングのUiFlowの初期設定についても紹介します。
ATOM LITE の初期設定。プログラミング初心者におすすめ
Platform IOを使用した ATOM LITE の初期設定です。ファイルを作成、必要なライブラリの準備、初期設定方法を紹介。実際に「Lチカ」プログラムを「コピペ」で書き込み動作確認まで行います。

プログラミングの書き込み環境についても、インストールから初期設定まで以下のリンクで詳しく紹介しています。

お試しで書き込みするなら「ArduinoIDE」が手軽でおすすめ

M5StackシリーズのためのArduino IDEのインストール方法と初期設定、使い方紹介
ArduinoIDEバージョン2のインストール方法から初期設定、スケッチ例の書き込み、コピペでの使い方まで詳しく紹介します。インストールはArduinoでも同じです。

本格的に始めるなら準備は大変ですが高機能エディタ「VSCode」上で動作する「PlatformIO」が書き込み速度も早くておすすめです。
インストールには以下の4つのリンクで「VSCode」のインストールから順番に紹介しています。

プログラミング
Visual Studio Code (VSCode)のインストールと日本語化から基本設定まで紹介
プログラムを書くために必要なエディタのインストールをします。他のプログラミングにも便利な定番エディタです。
pythonのインストール
pythonのダウンロードからインストール方法の紹介
次にインストールするPlatformIOをインストールするために必要なプログラム言語のpythonをインストールします。
PlatformIOでプログラミング
PlatformIO のダウンロードからインストールの紹介。Arduino IDEより速い!高性能!
プログラミングするためのIDE(統合開発環境)のインストールです。
ATOM LITE本体
ATOM LITE の初期設定。プログラミング初心者におすすめ
ATOM LITEにプログラムを書き込むための初期設定とプログラミングの書込みから動作確認まで行います。

プログラミング言語は「C言語」がメインになりますが、「Arduino」のコマンドが使えるので、まずはコピペで書き込んで実際に動かしているうちに自然と「C言語」が身についていくと思います。

「Arduino」コマンドと「C言語」については以下のリンクで詳しく紹介しています。

Arduinoコマンドの使い方一覧
Arduinoコマンドを使用したプログラミングについて詳しく紹介。まずはコピペで書き込み、動作確認しながら少しづつ理解を深めましょう♪C言語の理解にもつながるのでお勧めです!
C言語プログラミングの記事一覧
C言語のプログラミング記事一覧です。安価なマイコンボードを使用して動かしながら学んでみましょう♪ プログラム例を使って、コピペで書き込み、動作確認しながら少しづつ理解を深めましょう。

ブログを始めて、なんだかんだで半年が過ぎました。
思いつきで始めたブログですがなんとか形になり、探り探りですが閲覧数も増えてきて、おかげさまでモチベーションを維持するどころか上がってきてます^^

見てくださる方には本当に感謝です!最近記事が長くなりがちで・・・更新頻度はイマイチですが、これからも少しでもお役に立てる情報を発信できるよう頑張って書いていきたいと思いますのでチラチラ覗きにきてください。

「普段、街を歩いている時に何気なく思いついた、そんな発想を大切にしたい」という「路地からThinking」の精神を忘れずに、全く定着しませんが・・・久しぶりに言いましょう♪

楽しくプログラミングを学んでもらえる方が一人でも増えることを願って!

それではこれからも一緒に行きましょう♪

「Let’s 路地からThinking!!」

コメント

  1. masa より:

    是非頑張って作ってみます。
    何かありましたら、フォローお願いします。
    最高に面白そうですね。

    • logikara より:

      いらっしゃいませ♪
      是非!チャレンジしてみてください。
      わからないところがあればお気軽にどうぞ^^

      車体の方は参考までに、オリジナルのマシンを作ってみてください!

  2. masa より:

    // シリアル出力
    if (cnt == 10) { // シリアル出力カウントが10なら
    Serial.printf(“X: %3.0f Y: %3.0f P: %d B: %d A: %5.1f R: %5.1f\n”, x_data, y_data, press, btn_a, read_angle_data, read_round_data); // X軸アナログ値, Y軸アナログ値, 押し込み時ボタン状態取得, コントローラ側本体ボタンA操作状態, 回転サーボDutyオン幅, 角度サーボDutyオン幅
    cnt = 0; // シリアル出力カウントリセット
    }
    cnt++; // シリアル出力カウント加算

    でcntを10にしたらシリアルプリントするこのプログラムの意味を教えていただけると助かります。何のためにあるのか知りたいです。
    お手数をおかけします。

    • logikara より:

      この部分は「ArduinoIDE」等のシリアルモニタで、コントローラからの情報を表示させて確認するためものです。
      プログラム全体の遅延時間を100msにしてあるので、cntを10にしてカウントすることで10回に1回(約1秒)ごと表示するようにしています。

      cnt == 1 にすると約0.1秒ごとに情報が表示されて見にくくなるため、
      cnt == 10 として約1秒ごとの表示にしています。

      動作自体には関係ない部分なので無くても動作はしますが、コントローラの情報が正しく受信されているかを確認するために使用するものです。
      表示タイミンングは好みもあるので約0.5秒ごとに表示なら「cnt == 5」、約2秒ごとなら「cnt == 20」等に変更してみてください^^

  3. masa より:

    ありがとうございます。勉強になります。

タイトルとURLをコピーしました