復活の日 ― 2020年11月11日 18:23
そうだ、アサブロが有ったんだ。
久しぶりに投稿してみるか。
ESP32でラジコンやってみるか ― 2020年11月11日 18:25
ESP32で遊んでみたが、結構使える。
WiFiやBTが使えるので、ラジコンが作れるではないか。
PS3のコントローラが遊んでいるので、これを利用しよう。
WiFiやBTが使えるので、ラジコンが作れるではないか。
PS3のコントローラが遊んでいるので、これを利用しよう。
ESP32とPS3コントローラのペアリング ― 2020年11月11日 18:44
ESP32でPS3コントローラを使うには、esp32-ps3ライブラリを使うと良いらしい。
先ずはペアリングが必要だが、素のESP32ではUSB接続できないのでPC用のツールでペアリングしてやることになるが、SixaxisPairToolを使用するのが良いらしい。
でも我が家にはWindows環境が無いので、sixaxispairerをUbuntuで使用した。
SixaxisPairToolはHID APIに依存しているので、先にインストールしておく。
sixaxispairerをビルドしてroot権限で実行する。
最後の2行が実行で、その1行目は現在設定されているMACアドレスの参照、2行目がペアリングするESP32のMACアドレスの設定。
mkdir ps3controller cd ps3controller git clone git://github.com/libusb/hidapi.git sudo apt-get install libudev-dev libusb-1.0-0-dev libfox-1.6-dev sudo apt-get install autotools-dev autoconf automake libtool cd hidapi ./bootstrap ./configure --prefix=/usr make sudo make install cd .. git clone https://github.com/user-none/sixaxispairer.git cd sixaxispairer mkdir build cd build cmake .. -DHDIAPI_INCLUDE_DIRS=/usr/include/hidapi -DHIDAPI_LIBRARIES=hidapi-libusb sudo ./bin/sixaxispairer sudo ./bin/sixaxispairer xx:xx:xx:xx:xx:xx
ESP32にPS3コントローラをBT接続 ― 2020年11月12日 01:04
ESP32にPS3コントローラをBT接続してみる。
以下のスケッチを実行するとESP32のMACアドレスが表示されるので、PCにPS3コントローラをUSB接続し、前の記事でビルドしたsixaxispairerにESP32のMACアドレスを指定してペアリングする。
PS3コントローラのUSBを抜いて、PSボタンを押すとESP32に接続する。ちゃんとイベントが発生するか、□△○×ボタンを押して確認する。
/******************************************************************************* デバグ用マクロ *******************************************************************************/ // DEBUGマクロを定義するとデバグ情報をシリアルポートへ出力する #define DEBUG #if defined(DEBUG) #define DSTART() Serial.begin(115200) #define DPRINTF(...) Serial.printf(__VA_ARGS__) #else #define DSTART() #define DPRINTF(...) #endif /******************************************************************************* 使用ライブラリのヘッダファイル *******************************************************************************/ #include <Ps3Controller.h> /******************************************************************************* PS3コントローライベントハンドラ *******************************************************************************/ void notify() { // □ボタンプレス/リリース if (Ps3.event.button_down.square) DPRINTF("□ button down\n"); if (Ps3.event.button_up.square) DPRINTF("□ button release\n"); // △ボタンプレス/リリース if (Ps3.event.button_down.triangle) DPRINTF("△ button down\n"); if (Ps3.event.button_up.triangle) DPRINTF("△ button release\n"); // ○ボタンプレス/リリース if (Ps3.event.button_down.circle) DPRINTF("○ button down\n"); if (Ps3.event.button_up.circle) DPRINTF("○ button release\n"); // ☓ボタンプレス/リリース if (Ps3.event.button_down.cross) DPRINTF("☓ button down\n"); if (Ps3.event.button_up.cross) DPRINTF("☓ button release\n"); } /******************************************************************************* PS3コントローラ接続完了イベント *******************************************************************************/ void onConnect() { DPRINTF("Connected!\n"); } /******************************************************************************* PS3コントローラ接続 *******************************************************************************/ void connect_controller() { // ESP32のMACアドレス(バイナリ、文字列) uint8_t btmac[6]; char btmac_str[20]; // PS3コントローラを接続する Ps3.attach(notify); Ps3.attachOnConnect(onConnect); esp_read_mac(btmac, ESP_MAC_BT); sprintf(btmac_str, "%02x:%02x:%02x:%02x:%02x:%02x", btmac[0], btmac[1], btmac[2], btmac[3], btmac[4], btmac[5]); DPRINTF("MAC: %s\n", btmac_str); Ps3.begin(btmac_str); DPRINTF("Ready...\n"); } /******************************************************************************* セットアップ *******************************************************************************/ void setup() { // デバグ情報を有効化する DSTART(); // PS3コントローラを接続する connect_controller(); } /******************************************************************************* メインループ *******************************************************************************/ void loop() { }うまく行けば、シリアルモニタにはこんな内容が表示される。
rst:0x1 (POWERON_RESET),boot:0x13 (SPI_FAST_FLASH_BOOT) configsip: 0, SPIWP:0xee clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 mode:DIO, clock div:1 load:0x3fff0018,len:4 load:0x3fff001c,len:1216 ho 0 tail 12 room 4 load:0x40078000,len:10864 load:0x40080400,len:6432 entry 0x400806b8 MAC: xx:xx:xx:xx:xx:xx Ready... Connected! □ button down □ button release △ button down △ button release ○ button down ○ button release ☓ button down ☓ button release
PS3コントローラでサーボ制御 ― 2020年11月12日 02:59
PS3コントローラを接続できたので、サーボを動かしてみる。
ESP32は16個までのサーボが制御可能な様だが、そんなにいらないので6ch仕様にする。4ch分はアナログスティックを使用するが、残り2ch分はトリガーを使用することにした。
トリガーが返す値は0〜255でニュートラルがないので、ショルダーとの組み合わせでアナログスティックと同様に-128〜127を得る(ショルダープレスでマイナス、トリガーリリースでニュートラルとなる)様にする。
ESP32でサーボを制御するには、ESP32Servoを使用するらしい。Servo::writeを使うと分解能が1度になってしまいもったいないので、Servo::writeMicrosecondsを使用する。パルス幅は使用するサーボの諸元に応じて定義する。
後々、ニュートラルや舵角調整、反転を調整可能としたいが、取り敢えず初期値で定義しておく。
アナログスティックはニュートラルが安定しないので、マージンを取るようにした。
ESP32にサーボを適切に接続して、以下のスケッチを実行してPS3コントローラと接続すると、6ch分のサーボ制御が出来た。
/******************************************************************************* デバグ用マクロ *******************************************************************************/ // DEBUGマクロを定義するとデバグ情報をシリアルポートへ出力する #define DEBUG #if defined(DEBUG) #define DSTART() Serial.begin(115200) #define DPRINTF(...) Serial.printf(__VA_ARGS__) #else #define DSTART() #define DPRINTF(...) #endif /******************************************************************************* 使用ライブラリのヘッダファイル *******************************************************************************/ #include <Ps3Controller.h> #include <ESP32Servo.h> /******************************************************************************* 定数定義 *******************************************************************************/ // 最大チャンネル数 const int MAX_SERVO_NUM = 16; // PS3コントローラのアナログコントロールスティック/トリガーとサーボチャンネルの対応 enum SERVO_CH {LX_CH, LY_CH, RX_CH, RY_CH, L1L2_CH, R1R2_CH}; /******************************************************************************* 型定義 *******************************************************************************/ // ピンアサイン typedef struct { uint8_t servo[MAX_SERVO_NUM]; } pin_asign_t; // サーボ諸元 typedef struct { uint16_t min_pulse_width; uint16_t max_pulse_width; int8_t neutral_deg; } servo_factor_t; // 全体構成 typedef struct { servo_factor_t servo_factor; uint8_t neutral_margin; uint8_t servo_num; pin_asign_t pin; } conf_t; // サーボ調整 typedef struct { int8_t neutral[MAX_SERVO_NUM]; uint8_t range[MAX_SERVO_NUM]; uint16_t reverse; // (ビット配列) } servo_trim_t; /******************************************************************************* グローバル変数定義 *******************************************************************************/ // 全体構成 conf_t conf = {{500, 2500, 90} /* (SG92Rの場合) */, 20, 6, {{32, 33, 25, 26, 27, 14}} }; // サーボ調整 servo_trim_t trim = {{0, 0, 0, 0, 0, 0}, {45, 45, 45, 45, 45, 45}, 0}; // サーボインスタンス、ショルダーボタン状態(ビット配列) Servo servo[MAX_SERVO_NUM]; uint8_t lr1_bn = 0; /******************************************************************************* サーボ制御 *******************************************************************************/ void servo_op(int ch, int val) { // リバースが必要な場合値を反転する if (bitRead(trim.reverse, ch)) val = -val; // ニュートラル調整と舵角調整で設定された範囲のパルス幅を求める int neutral = conf.servo_factor.neutral_deg + trim.neutral[ch]; int pwm_min = map(neutral - trim.range[ch], 0, conf.servo_factor.neutral_deg * 2, conf.servo_factor.min_pulse_width, conf.servo_factor.max_pulse_width); int pwm_max = map(neutral + trim.range[ch], 0, conf.servo_factor.neutral_deg * 2, conf.servo_factor.min_pulse_width, conf.servo_factor.max_pulse_width); // コントローラのニュートラル付近にマージンを取る if (abs(val) < conf.neutral_margin) val = 0; else val -= (val >= conf.neutral_margin) ? conf.neutral_margin : -conf.neutral_margin; // マージン除外後の範囲に再マッピングしてサーボを動作させる servo[ch].writeMicroseconds( map(val, -128 + conf.neutral_margin, 127 + conf.neutral_margin, pwm_min, pwm_max)); } /******************************************************************************* サーボ初期化 *******************************************************************************/ void servo_init() { // 全サーボをアタッチしてニュートラルにする for (int ch = 0; ch < conf.servo_num; ++ch) { servo[ch].attach(conf.pin.servo[ch]); servo_op(ch, 0); } } /******************************************************************************* PS3コントローライベントハンドラ *******************************************************************************/ void notify() { // アナログスティックイベント // アナログスティックLXチェンジ(LX_CH) if (Ps3.event.analog_changed.stick.lx) servo_op(LX_CH, Ps3.data.analog.stick.lx); // アナログスティックLYチェンジ(LY_CH) if (Ps3.event.analog_changed.stick.ly) servo_op(LY_CH, Ps3.data.analog.stick.ly); // アナログスティックRXチェンジ(RX_CH) if (Ps3.event.analog_changed.stick.rx) servo_op(RX_CH, Ps3.data.analog.stick.rx); // アナログスティックRYチェンジ(RY_CH) if (Ps3.event.analog_changed.stick.ry) servo_op(RY_CH, Ps3.data.analog.stick.ry); // ショルダー/トリガーボタンイベント // ショルダーL1プレスとトリガーL2チェンジの組み合わせ(L1L2_CH) if (Ps3.event.button_down.l1) bitSet(lr1_bn, 0); if (Ps3.event.button_up.l1) bitClear(lr1_bn, 0); if (Ps3.event.analog_changed.button.l2) servo_op(L1L2_CH, Ps3.data.analog.button.l2 / (bitRead(lr1_bn, 0) ? -2 : 2)); // ショルダーR1プレスとトリガーR2チェンジの組み合わせ(R1R2_CH) if (Ps3.event.button_down.r1) bitSet(lr1_bn, 1); if (Ps3.event.button_up.r1) bitClear(lr1_bn, 1); if (Ps3.event.analog_changed.button.r2) servo_op(R1R2_CH, Ps3.data.analog.button.r2 / (bitRead(lr1_bn, 1) ? -2 : 2)); } /******************************************************************************* PS3コントローラ接続完了イベント *******************************************************************************/ void onConnect() { DPRINTF("Connected!\n"); } /******************************************************************************* PS3コントローラ接続 *******************************************************************************/ void connect_controller() { // ESP32のMACアドレス(バイナリ、文字列) uint8_t btmac[6]; char btmac_str[20]; // PS3コントローラを接続する Ps3.attach(notify); Ps3.attachOnConnect(onConnect); esp_read_mac(btmac, ESP_MAC_BT); sprintf(btmac_str, "%02x:%02x:%02x:%02x:%02x:%02x", btmac[0], btmac[1], btmac[2], btmac[3], btmac[4], btmac[5]); DPRINTF("MAC: %s\n", btmac_str); Ps3.begin(btmac_str); DPRINTF("Ready...\n"); } /******************************************************************************* セットアップ *******************************************************************************/ void setup() { // デバグ情報を有効化する DSTART(); // サーボを初期化する servo_init(); // PS3コントローラを接続する connect_controller(); } /******************************************************************************* メインループ *******************************************************************************/ void loop() { }