X軸、Y軸の操作ができる2軸のジョイスティック(JoyStick)の仕組みから、実際の使用方法等をサンプルプログラムを使用して詳しく紹介します。
動作確認では「RCサーボモータ」を2個使用して、カメラ等を固定して縦横に動作するパンチルト機構をジョイスティックで操作するものを実際に作ってみます。
マイコンボードには「Arduino」より機能豊富な「M5Stack」社製の「M5StickC Plus」を使用します。
「M5StickC Plus」については以下のリンクで詳しく紹介しています。
「RCサーボ」を使用したラジコンの作り方も、以下のリンクで詳しく紹介しています。
1.ジョイスティックとは
2.外観写真
3.動作原理と構造
・アナログ出力仕様
・シリアル通信(I2C)仕様
4.パンチルト機構について
5.配線図と動作確認
・安定動作用コンデンサについて
・アナログ出力仕様
・シリアル出力仕様
6.サンプルプログラム(コピペ)
・アナログ出力仕様
・シリアル通信(I2C)仕様
7.サンプルプログラムの詳細
・サーボの制御
・ジョイスティックの制御(アナログ出力仕様)
・ジョイスティックの制御(シリアル通信(I2C)仕様)
8.まとめ
1.ジョイスティックとは
「ジョイスティック」とはゲームのコントローラーや、ドローンの操作等に使用され、上下左右の移動を操作するために使用される2軸(X軸、Y軸)のコントローラーです。
1つの「ジョイスティック」で上下左右を操作でき、左右を「X軸」、上下を「Y軸」として使用することが多く、それぞれに操作した時の座標情報を取得できます。
多くの「ジョイスティック」がスティックを押し込むことで「ON/OFF」スイッチとしても機能します。
2.外観写真
ジョイスティックの外観は以下のようになります。
・アナログ出力仕様
下写真はスティックの座標をアナログ電圧出力で取得するタイプで、いろいろなメーカーから同じようなものが販売されています。
・シリアル通信(I2C)仕様
下写真は「M5Stack」社製のジョイスティックで、スティックの操作データをシリアル通信(I2C)で取得するタイプです。
I2C通信用として「GROVE」コネクタがあり、ケーブルも付属しています。
3.動作原理と構造
「ジョイスティック」の動作原理について、内部には「X軸」「Y軸」方向に操作した時に操作量、方向に応じて変動する2つのボリューム(可変抵抗器)があります。
スティックを操作することで各軸の位置によって「0V〜ボリュームの両端電圧」までの電圧が出力され、この電圧を読み取ることでスティックの操作位置を取得することができます。
回路図に表すと以下のようになります。
電源電圧を「3.3V」とすると各軸で操作量に応じて「0〜3.3V」の電圧が出力されます。
ジョイスティックには「アナログ出力仕様」と「シリアル通信仕様」があり、それぞれで出力されるデータや使用方法が異なります。
実際に操作した時の出力データと構造は以下のようになります。
・アナログ出力仕様
アナログ出力仕様は下写真のように、スティックの操作量に応じてX,Y各軸で「0〜最大V(電源電圧)」の電圧を出力します。
電圧を読み取るため、操作精度は処理する側のA/D変換の分解能(M5StickC Plusは12bit「0〜4095」)によって決まります。
矢印部にX,Y軸の操作と連動するボリューム(可変抵抗器)があります。
矢印部にスティックを押し込んだ時に動作するタクトスイッチがあります。
・シリアル通信(I2C)仕様
シリアル通信(I2C)仕様は下写真のように、スティックの操作量に応じてX,Y各軸の座標データ「0〜255」を1バイトづつ順番に出力します。
3バイト目にはスティクを押し込んだ時に動作するスイッチの状態を「1/0(ON/OFF)」で出力します。
操作精度は操作範囲を256分割したものになります。
矢印部にX,Y軸の操作と連動するボリューム(可変抵抗器)があります。
矢印部にスティックを押し込んだ時に動作するタクトスイッチがあります。
「ボリューム(可変抵抗器)」や「押しボタンスイッチ」については以下のリンクで詳しく紹介しています。
4.パンチルト機構について
「パンチルト機構」について紹介します。
カメラを左右に動かすことを「パン」と言い、上下に動かすことを「チルト」と言います。
これらを合わせてカメラを上下に動かすことを「パンチルト」と言います。
安価な「RCサーボ」SG90 を使用した「パンチルト」機構が下写真になります。
「パンチルト」機構のマウント部の部品だけのものや、「RCサーボ」が2個セットになったものが販売されています。
組み立ては自分でする必要があり、はめ込み部分や稼働部の精度はあまり良くないですが、動作確認程度には十分と思います。(ネジがいっぱい付いてますが、いっぱい余りますw)
土台の部分を固定するためにサーボの付属品を使いますが、これをかなり加工しないと組み立てられません。ニッパーやヤスリ等を使って下写真のように加工して組み立てます。
5.配線図と動作確認
配線図と動作について紹介します。
アナログ出力仕様とシリアル通信(I2C)仕様で若干異なります。
・安定動作用コンデンサの接続
まずは「RCサーボ」をマイコンボードで安定して動作させるためコンデンサについて紹介します。
なくても動作はしますが、安定動作のために「セラミックコンデンサ(0.1μF)」と「電解コンデンサ(100μF)」を接続します。
これがないと「RCサーボ」激しく操作した時に動かなくなる時があります。
・アナログ出力仕様
アナログ出力仕様のジョイスティックを接続した写真は以下のようになります。
配線図
配線図は以下のようになります。
ブレッドボードやL型(直角)ピンヘッダー、ジャンパー線、GROVEコネクタ付配線を使用して接続します。
動作確認
ジョイスティックを操作すると液晶画面に各軸のA/D変換値「X:, Y:」の「0〜4095」と動作角度「Xr:, Yr:」の「0〜180°」が表示されます。
スティックを押し込むと「Press:」が0から1になります。
・シリアル出力(I2C)仕様
シリアル出力(I2C)仕様のジョイスティックを接続した写真は以下のようになります。
配線図
配線図は以下のようになります。
ブレッドボードやL型(直角)ピンヘッダー、GROVEコネクタ付配線を使用して接続します。
動作確認
ジョイスティックを操作すると液晶画面に各軸の座標データ「X:, Y:」の「0〜255」と動作角度「Xr:, Yr:」の「0〜180°」が表示されます。
スティックを押し込むと「Press:」が0から1になります。
6.サンプルプログラム(コピペ)
プログラムは以下に準備しましたので「コピペ」して書き込んでください。
アナログ出力仕様はこの下、シリアル通信(I2C)仕様はさらに下にあります。
※コピーは下コード(黒枠)内の右上角にある小さなアイコンのクリックでもできます。
・アナログ出力仕様
#include <M5StickCPlus.h> // ヘッダーファイル
// 変数宣言
float x_data; // ジョイスティックX軸
float y_data; // ジョイスティックY軸
int press; // ジョイスティック押し込みボタン
float setting[16][5]; // チャンネル[0〜15]ごとの[周波数, bit数, 0゜パルス時間ms, 最大角度パルス時間ms, 最大動作角度]格納配列
float x_angle; // X軸角度指示値
float y_angle; // Y軸角度指示値
float duty_angle; // X軸角度初期値
float duty_y_angle; // Y軸角度初期値
float read_x_angle; // X軸角度現在値
float read_y_angle; // Y軸角度現在値
int cnt = 0; // カウント用
/******************* 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(); // 本体初期化
// アナログ入力設定
pinMode(32, ANALOG); //アナログ入力
pinMode(33, ANALOG); //アナログ入力
// G36とG25は同時使用不可。使っていない方は以下のようにフローティング入力にする
gpio_pulldown_dis(GPIO_NUM_36);
gpio_pullup_dis(GPIO_NUM_36);
// 入力設定
pinMode(0, INPUT_PULLUP); //入力設定(プルアップ)
// PWM出力、サーボ仕様設定(サーボごとに使用する端子、チャンネル等を設定)
// PWM出力のチャンネルは 0,1/ 2,3/ 4,5/ 6,7/ 8,9/ 10,11 /12,13 /14,15でペア ※周波数と分解能はペアで同じにする必要があります。
// (出力端子, チャンネル, 周波数, bit数, 0゜パルス時間ms, 最大角度動作時間ms, 最大動作角度)
servo_set(26, 1, 50, 12, 0.5, 2.4, 180); // チャンネル1(X軸):PWM出力、サーボ仕様設定関数呼び出し
servo_set(25, 2, 50, 12, 0.5, 2.4, 180); // チャンネル2(Y軸):PWM出力、サーボ仕様設定関数呼び出し
// LCD表示設定
M5.Lcd.setTextFont(4); // フォント設定
M5.Lcd.fillScreen(BLACK); M5.Lcd.setTextColor(WHITE, BLACK); // 背景、文字色
}
// メイン処理 --------------------------------------------------------
void loop() {
M5.update(); // ボタン状態更新
// A/D変換、角度換算処理
x_data = analogRead(32); // アナログ電圧をデジタル値に変換して取得(0〜4095)
x_angle = 180 - (x_data * (180.0 / 4095.0)); //角度へ換算(動作反転)
y_data = analogRead(33); // アナログ電圧をデジタル値に変換して取得(0〜4095)
y_angle = y_data * (180.0 / 4095.0); //角度へ換算
// サーボ動作指定(戻り値として角度を取得)
read_x_angle = servo(1, x_angle); // チャンネル1:サーボ動作実行関数呼び出し(チャンネル, 角度値)
read_y_angle = servo(2, y_angle); // チャンネル2:サーボ動作実行関数呼び出し(チャンネル, 角度値)
// スティック押し込みボタン検出
if (digitalRead(0)) { // スティックが押されたら
press = 0; // pressを1
} else { // 押されてなければ
press = 1; // pressを2
}
// 液晶表示
if (cnt == 30) { // カウント30ごとに液晶表示更新
M5.Lcd.setCursor(0, 0); // 座標指定
M5.Lcd.println(" JoyStick"); // 項目表示
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\n", press); // 押し込みボタン状態表示
M5.Lcd.printf("Xr : %3.1f' \n", read_x_angle); // x軸角度
M5.Lcd.printf("Yr : %3.1f' \n", read_y_angle); // y軸角度
cnt = 0; // カウントリセット
}
cnt++; // カウント +1
delay(10); // 遅延時間
}
・シリアル通信(I2C)仕様
#include <M5StickCPlus.h> // ヘッダーファイル
// 変数宣言
float x_data; // ジョイスティックX軸
float y_data; // ジョイスティックY軸
int press; // ジョイスティック押し込みボタン
float setting[16][5]; // チャンネル[0〜15]ごとの[周波数, bit数, 0゜パルス時間ms, 最大角度パルス時間ms, 最大動作角度]格納配列
float x_angle; // X軸角度指示値
float y_angle; // Y軸角度指示値
float duty_angle; // X軸角度初期値
float duty_y_angle; // Y軸角度初期値
float read_x_angle; // X軸角度現在値
float read_y_angle; // Y軸角度現在値
int cnt = 0; // カウント用
/******************* 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(); // 本体初期化
Wire.begin(32, 33); // Grove端子をI2C設定(SDA, SCL)
// PWM出力、サーボ仕様設定(サーボごとに使用する端子、チャンネル等を設定)
// PWM出力のチャンネルは 0,1/ 2,3/ 4,5/ 6,7/ 8,9/ 10,11 /12,13 /14,15でペア ※周波数と分解能はペアで同じにする必要があります。
// (出力端子, チャンネル, 周波数, bit数, 0゜パルス時間ms, 最大角度動作時間ms, 最大動作角度)
servo_set(26, 1, 50, 12, 0.5, 2.4, 180); // チャンネル1(X軸):PWM出力、サーボ仕様設定関数呼び出し
servo_set(25, 2, 50, 12, 0.5, 2.4, 180); // チャンネル2(Y軸):PWM出力、サーボ仕様設定関数呼び出し
// LCD表示設定
M5.Axp.ScreenBreath(9); // 液晶画面明るさレベル(標準12)
M5.Lcd.setTextFont(4); // フォント設定
M5.Lcd.fillScreen(BLACK); M5.Lcd.setTextColor(WHITE, BLACK); // 背景、文字色
}
// メイン処理 --------------------------------------------------------
void loop() {
// ジョイスティック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)
}
// ジョイスティック座標-角度換算
x_angle = map(x_data, 0, 255, 0, 180); // X軸 - 角度換算
y_angle = map(y_data, 0, 255, 0, 180); // Y軸 - 角度換算
// サーボ動作指定(戻り値として角度を取得)
read_x_angle = servo(1, x_angle); // チャンネル1:サーボ動作実行関数呼び出し(チャンネル, 角度値)
read_y_angle = servo(2, y_angle); // チャンネル2:サーボ動作実行関数呼び出し(チャンネル, 角度値)
// 液晶表示
if (cnt == 30) { // カウント30ごとに液晶表示更新
M5.Lcd.setCursor(0, 0); // 座標指定
M5.Lcd.println(" JoyStick"); // 項目表示
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\n", press); // 押し込みボタン状態
M5.Lcd.printf("Xr : %3.0f' \n", read_x_angle); // x軸角度
M5.Lcd.printf("Yr : %3.0f' \n", read_y_angle); // y軸角度
cnt = 0; // カウント0リセット
}
cnt++; // カウント +1
delay(10); // 遅延時間
}
7.サンプルプログラムの詳細
サンプルプログラムについて詳しく紹介します。
・サーボの制御
「RCサーボ」の制御については以前も紹介しましたが、PWM制御とサーボの設定を行う「servo_set()」関数、サーボ動作を実行する「servo()」関数を準備して制御しています。
15行目 〜 26行目が「servo_set()」関数です。
ここで、チャンネルごとに各サーボの仕様に合わせたPWM制御の初期設定を行っています。
28行目 〜 52行目が「servo()」関数です。
ここで、指定されたサーボのチャンネルと動作角度からPWM制御の「Duty比」を算出してPWM制御でサーボの動作を実行します。
さらに、戻り値として角度を返すようにしているため、メイン処理内で指定した変数に角度の値を格納して使用できます。
「PWM制御」についてと「RCサーボ」の制御方法については以下のリンクで詳しく紹介しています。
・ジョイスティックの制御(アナログ出力仕様)
アナログ出力仕様のサンプルプログラムの 56行目 〜 63行目でジョイスティックからのアナログ出力を接続するためのアナログ入力端子の設定と、スティックを押し込んだ時のON/OFFスイッチを接続する入力端子の設定を行なっています。
78行目 〜 82行目でジョイスティックからのアナログ電圧をデジタル値に変換して取得(0〜4095)しサーボの動作角度に換算しています。
85行目 〜 86行目で、使用するサーボの「チャンネル」と「動作角度」を指定して「servo_set()」関数を呼び出し、サーボの動作を実行します。
88行目 〜 92行目ではスティックを押し込んだ時のボタンのON/OFF状態を取得しています。
・ジョイスティックの制御(シリアル通信(I2C)仕様)
シリアル通信(I2C)仕様のサンプルプログラムの 56行目でGROVEコネクタの端子「32(SDA), 33(SCL)」をシリアル通信用端子として設定します。
71行目〜77行目でジョイスティックとの通信を行なっています。
シリアル通信(I2C)でX軸、Y軸の座標をそれぞれ「0〜255」で取得し、スティックを押し込んだ時のスイッチの「ON/OFF」状態を「 1 / 0 」で取得します。
シリアル通信部を以下に抜粋し詳しく紹介します。
// ジョイスティック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)
}
「Wire.requestFrom(0x52, 3)」で通信アドレス「0x52」に「3バイト」のデータ取得をリクエストするとジョイステックからデータが送られてきます。
「while文」の中で「Wire.available()」を実行して、受信データがある間「Wire.read()」を実行してデータを各「変数」に格納します。
受信データは 1バイト目が「X軸」、2バイト目が「Y軸」、3バイト目が「スイッチの状態」です。
79, 80行目で「map関数」を使用してX,Y軸の座標データ「0〜255」をサーボの動作角度「0〜180(° )」に換算し各「変数」に格納しています。
座標データを、角度に換算するには「Aruduino」コマンドの「map関数」が便利なのでここで紹介しておきます。
map ( 変換対象, 変換対象の最小値, 変換対象の最大値, 変換後の最小値, 変換後の最大値 ) ;
・変換対象:変換対象の変数等を指定(サンプルプログラムでは「x_data」又は「y_data」)
・変換対象の最小値:変換対象の最小値を指定(サンプルプログラムでは0)
・変換対象の最大値:変換対象の最大値を指定(サンプルプログラムでは255)
・変換後の最小値:変換後の最小値を指定(サンプルプログラムでは0)
・変換後の最大値:変換後の最大値を指定(サンプルプログラムでは180)
最後に 83, 84行目で、使用するサーボの「チャンネル」と「動作角度」を指定して「servo_set()」関数を呼び出し、サーボの動作を実行します。
7.準備するもの
今回使用した部品は以下になります。
「M5StickC Plus」は入出力端子は少ないですが液晶表示付で、2個のボタンやブザー、振動センサ、Wi-Fi、Bluetooth通信等機能が豊富でいろいろなアイデアを試すには最適です。
パンチルト機構は「RCサーボSG90」を2個お持ちの方はカメラマウントのみで購入できます。
「RCサーボ」をお持ちでない方はサーボ付きもあります。
このセットのサーボは「SG90R」で「SG90」のトルクアップ品です。
ジョイスティックは種類が豊富で迷いますが、今回使用したものは以下になります。
アナログ出力仕様
シリアル通信(I2C)仕様
コンデンサは安心の日本製(村田製作所、ニチコン、ケミコン等)がおすすめ
その他、配線に便利なブレッドボードやL型(直角)ピンヘッダー、ジャンパー線、GROVEコネクタ付配線です。
8.まとめ
パンチルト機構を使用して、「ジョイスティック」について詳しく紹介しました。
パンチルトとは、カメラを左右に動かすことを「パン」上下に動かすことを「チルト」と言うことに由来し、これらを合わせてカメラを上下左右に動かすことを「パンチルト」と言います。
「ジョイスティック」はパンチルトのような上下左右に動作するものをコントロールするのに最適で、ゲームのコントローラ部等にも使用されます。
「ジョイスティック」の動作原理は、内部に「X軸」「Y軸」方向に操作した時にそれぞれの操作量、方向に応じて変動する2つのボリューム(可変抵抗器)があり、「アナログ出力仕様」は「0V〜ボリュームの両端電圧」までの電圧が出力され、「シリアル通信仕様」は「0〜255」等の座標データを「1バイト」等のデータで出力します。
これらの出力を読み取ることでスティックの操作位置を取得することができます。
スティック部を押し込むと「ON/OFFスイッチ」として機能するものが多いです。
今回使用した「M5StickC Plus」等、「M5Stack」社製のマイコンボードにはWi-FiやBluetooth機能が搭載されているため「ジョイスティック」で遠隔操作するものも手軽に製作することができます。
次回はBluetooth通信を使用して、「ジョイスティック」で操作できるラジコンの作り方を紹介します。
3Dプリンターを使用して、今回使用した基板タイプの「ジョイスティック」用ケースの作り方も紹介しています。
コメント