Hatena::ブログ(Diary)

滴了庵日録 このページをアンテナに追加 RSSフィード

2016/08/17(Wed)

Android Studioでアプリのバージョン情報指定

Moduleのbuild.gradleに記述する。

versionCodeは整数値、versionNameは文字列を設定する。


【例】

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "net.lipoyang.gppropo"
        minSdkVersion 18
        targetSdkVersion 23
        versionCode 2
        versionName "1.1"
    }
    (略)
}
(略)

2016/08/16(Tue)

Androidアプリでゲームパッドのイベントを取る

ミニ四駆ラジコンのプロポアプリを作って痛感したのは、タッチパネルUIだとスティックの感触がないから画面から目を離して操作しづらい、ということです。これはラジコンやロボットコントローラとしては致命的です。というわけで、ゲームパッドを使えるようにしたいと思いました。

購入したゲームパッド

今回購入したゲームパッドは、ipegaのPG9023という機種。Bluetooth接続のゲームパッドで、スマホタブレットをがっちり挟み込める構造になっています。

PG9023

このゲームパッドはペアリング時のボタン押下でモードが決まります。

  • Xボタン+HOMEボタン → ゲームパッドモード (Android用)
  • Aボタン+HOMEボタン → キーボードモード
  • Bボタン+HOMEボタン → iCadeモード (iOS用)
  • Yボタン+HOMEボタン → SPPモード

今回はゲームパッドモードで使用します。ちなみにHOMEボタン長押しで電源OFF、一度ペアリングした後はHOMEボタン押下で電源ON&再接続できる仕様です。

イベントの取得

  • ボタンのイベントは onKeyDown/onKeyUp/onKeyLongPress
  • アナログスティックのイベントは onGenericMotionEvent

をActivityでオーバーライドすれば取得できます。

ボタンやスティックによってはプライマリ/セカンダリの2個のイベントを発生するものがあります。まずプライマリイベントが発生し、それがアプリによって処理されなかったときにセカンダリイベントが発生します。上記のイベントハンドラでtrueを返すと、イベントは処理されたことになります。

ボタンのイベント

どのボタンが押されたかはonKeyDownの引数のkeyCodeの値で得られます。PG9023で実際の各ボタンのkeyCode値を調べてみました。機種によって細かい違いはあろうかと思いますが、主要なボタンはたぶん互換性があるでしょう。値1/値2はプライマリ/セカンダリの値を示します。

【主なボタン】

ボタンプライマリセカンダリ
十字↑--KEYCODE_DPAD_UP19
十字↓--KEYCODE_DPAD_DOWN20
十字←--KEYCODE_DPAD_LEFT21
十字→--KEYCODE_DPAD_RIGHT22
AKEYCODE_BUTTON_A96KEYCODE_DPAD_CENTER23
BKEYCODE_BUTTON_B97KEYCODE_BACK4
XKEYCODE_BUTTON_X99KEYCODE_DEL67
YKEYCODE_BUTTON_Y100KEYCODE_SPACE62
L1KEYCODE_BUTTON_L1102--
R1KEYCODE_BUTTON_R1103--
L2--KEYCODE_BUTTON_L2104
R2--KEYCODE_BUTTON_R2105

【左ジョイスティック

ボタンプライマリセカンダリ
押し下げKEYCODE_BUTTON_THUMBL106KEYCODE_DPAD_CENTER23
--KEYCODE_DPAD_UP19
--KEYCODE_DPAD_DOWN20
--KEYCODE_DPAD_LEFT21
--KEYCODE_DPAD_RIGHT22

【右ジョイスティック】

ボタンプライマリセカンダリ
押し下げKEYCODE_BUTTON_THUMBR107KEYCODE_DPAD_CENTER23
----
----
----
----

【その他のボタン】

ボタンプライマリセカンダリ
SELECTKEYCODE_BUTTON_SELECT109KEYCODE_MENU82
HOME----
STARTKEYCODE_BUTTON_START108KEYCODE_DPAD_CENTER23
-KEYCODE_VOLUME_DOWN25--
+KEYCODE_VOLUME_UP24--
巻き戻しKEYCODE_MEDIA_PREVIOUS88--
再生/停止KEYCODE_MEDIA_PLAY_PAUSE85--
早送りKEYCODE_MEDIA_NEXT87--

アナログスティックのイベント

各軸の値は、onGenericMotionEventのevent.getAxisValue(MotionEvent.軸名)で得られます。値の型はfloatです。

軸名値の範囲
左ジョイスティック 横AXIS_X-1〜+1
左ジョイスティック 縦AXIS_Y-1〜+1
右ジョイスティック 横AXIS_Z-1〜+1
右ジョイスティック 縦AXIS_RZ-1〜+1
十字 横AXIS_HAT_X-1〜+1
十字 縦AXIS_HAT_Y-1〜+1
Lトリガ(L2)AXIS_LTRIGGER0〜1 ※
Rトリガ(R2)AXIS_RTRIGGER0〜1 ※

※ PG9023のL2,R2はアナログではないただのボタンのためか、アナログ値は常に0でした。

ソースコード

    // ボタンのイベント
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event){
        boolean handled = false; // 処理したらtrueに
        
        // キーコードをログ出力してみる
        String msg = "keyCode:" + keyCode;
        Log.e("GamePad", msg);

        return handled || super.onKeyDown(keyCode, event);
    }
    // アナログのイベント
    @Override
    public boolean onGenericMotionEvent(MotionEvent event){
        boolean handled = false; // 処理したらtrueに

        // 左ジョイスティックの値をログ出力してみる
        float x = event.getAxisValue(MotionEvent.AXIS_X);
        float y = event.getAxisValue(MotionEvent.AXIS_Y);
        String msg = "(x,y)=" + x + "," + y;
        Log.e("GamePad", msg);
        
        return handled || super.onGenericMotionEvent(event);
    }

参考URL

2016/08/14(Sun)

ESP8266のSTAモードでUDP通信

昨日はAPモードでのUDP通信を実験した。

しかし、APモードだとネットに接続できなくなるのが不便である。そこでSTAモードも試してみる。ただしSTAモードの場合、IPアドレスWiFiルータからDHCPで割り振られることが一般的なので、IPアドレスではアクセスしにくい。そこでmDNSを利用する。mDNSはローカルネット内でDNS情報をマルチキャストする仕組みで、ホスト名.local でアクセスできる。

#include <ESP8266WiFi.h> // WiFi
#include <ESP8266mDNS.h> // mDNS
#include <WiFiUDP.h>     // UDP

// SSIDとパスワード (環境に合わせる)
const char *ssid = "hoge";
const char *password = "piyo";

// ホスト名
char hostName[16];
  
// UDPオブジェクト
static WiFiUDP udp;

// mDNSオブジェクト
MDNSResponder mdns;

// ポート番号
#define LOCAL_PORT  0xC000  // 自分のポート番号
#define REMOTE_PORT 0xC001  // 相手のポート番号

// IPアドレス
IPAddress localIP;  // 自分のIPアドレス
IPAddress remoteIP; // 相手のIPアドレス

void setup() {
  Serial.begin(115200);
  delay(100);

  // ホスト名(固有名になるように)
  sprintf(hostName, "esp8266-%06x", ESP.getChipId());
  Serial.print("HostName: ");  Serial.println(hostName);

  // STAの設定
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
}

void loop() {
  static bool isConnected = false;
  char packetBuffer[256];
  
  // APへの接続待ち
  if (WiFi.status() != WL_CONNECTED) {
    if(isConnected){
      isConnected = false;
      // UDPの停止
      udp.stop();
    }
    delay(500);
    Serial.print(".");
    return;
  }
  if(!isConnected){
    isConnected = true;
    
    // IPアドレスの取得
    localIP = WiFi.localIP();
    Serial.println();
    Serial.print("Connected to "); Serial.println(ssid);
    Serial.print("STA IP address: "); Serial.println(localIP);
    
    // UDPの開始
    udp.begin(LOCAL_PORT);
    if ( mdns.begin ( hostName, localIP ) ) {
      Serial.println ( "MDNS responder started" );
    }else{
      Serial.println("Error setting up MDNS responder!");
    }
  }
  mdns.update();
  
  // パケット受信があればデータ取得
  int packetSize = udp.parsePacket();
  if (packetSize) {
    int len = udp.read(packetBuffer, packetSize);
    if (len > 0) packetBuffer[len] = '\0';
    
    // 相手のIPアドレス取得
    remoteIP = udp.remoteIP();
    
    Serial.print(remoteIP);
    Serial.print(" / ");
    Serial.println(packetBuffer);
    
    // パケット送信
    udp.beginPacket(remoteIP, REMOTE_PORT);
    udp.write(packetBuffer);
    udp.endPacket();
  }
}

2016/08/13(Sat)

ESP8266のAPモードでUDP通信

ESP8266のWiFi機能はAPモード(親機)とSTAモード(子機)、およびAP+STAモードがある。

まずは、APモードでPCとUDP通信してみた。

ESP8266側のスケッチ

#include <ESP8266WiFi.h> // WiFi
#include <WiFiUDP.h>     // UDP

// SSIDとパスワード
const char *ssid = "esp8266";
const char *password = "12345678"; // 8文字以上

// UDPオブジェクト
static WiFiUDP udp;

// ポート番号
#define LOCAL_PORT  0xC000  // 自分のポート番号
#define REMOTE_PORT 0xC001  // 相手のポート番号

// IPアドレス
IPAddress localIP;  // 自分のIPアドレス
IPAddress remoteIP; // 相手のIPアドレス

void setup() {
  Serial.begin(115200);
  delay(100);
  
  // APの設定
  WiFi.mode(WIFI_AP);
  WiFi.softAP(ssid, password);
  localIP = WiFi.softAPIP();
  Serial.println();
  Serial.print("AP IP address: ");Serial.println(localIP);
  
  // UDPの開始
  udp.begin(LOCAL_PORT);
}

void loop() {
  char packetBuffer[256];
  
  // パケット受信があればデータ取得
  int packetSize = udp.parsePacket();
  if (packetSize) {
    int len = udp.read(packetBuffer, packetSize);
    if (len > 0) packetBuffer[len] = '\0';
    
    // 相手のIPアドレス取得
    remoteIP = udp.remoteIP();
    
    Serial.print(remoteIP);
    Serial.print(" / ");
    Serial.println(packetBuffer);
    
    // パケット送信
    udp.beginPacket(remoteIP, REMOTE_PORT);
    udp.write(packetBuffer);
    udp.endPacket();
  }
}

PC側のC#アプリ (抜粋)

public partial class Form1 : Form
{
    const int REMOTE_PORT = 0xC000;  // 自分のポート番号
    const int LOCAL_PORT  = 0xC001;  // 相手のポート番号

    // ソケットオブジェクト
    private System.Net.Sockets.UdpClient udp;
    private bool isConnected = false;

    public Form1()
    {
        InitializeComponent();
        timer.Interval = 100;
    }

    // 接続/切断
    private void buttonConnect_Click(object sender, EventArgs e)
    {
        // 接続
        if (!isConnected)
        {
            // 指定されたIPアドレスに接続
            udp = new System.Net.Sockets.UdpClient(0xC001);
            udp.Connect(textIPAddress.Text, REMOTE_PORT);

            timer.Start();
            buttonConnect.Text = "Disconnect";
            isConnected = true;
        }
        // 切断
        else
        {
            udp.Close();

            timer.Stop();
            buttonConnect.Text = "Connect";
            isConnected = false;
        }
    }

    // 送信
    private void buttonSend_Click(object sender, EventArgs e)
    {
        if (isConnected)
        {
            Byte[] data = System.Text.Encoding.ASCII.GetBytes(textCommand.Text);
            udp.Send(data, data.Length);
        }
    }

    // タイマハンドラで受信
    private void timer_Tick(object sender, EventArgs e)
    {
        if (udp.Available > 0)
        {
            // 受信
            System.Net.IPEndPoint ipAny =
                new System.Net.IPEndPoint(System.Net.IPAddress.Any, 0);
            Byte[] data = udp.Receive(ref ipAny);

            textLog.Text += System.Text.Encoding.ASCII.GetString(data) 
                            + Environment.NewLine;
        }
    }
}

2016/08/11(Thu)

Maker Faire Tokyo 2016

8/6〜8/7に開催されたMaker Faire Tokyo 2016に、京都電創庵のメンバーとして出展してきました。出展者側なので満足に見学する時間がありませんでしたが、ロボット関係でいくつかご紹介。


ほげ

やっぱりみんなタチコマ大好きですね。karakuri productsさんの1/2タチコマはすごい目立ってましたし、青葉山技研さんのロジコマはすごい緻密な完成度でした。写真はPartmatonさんのミニタチコマ。外装は3Dプリンタ製。ぼくも歩くミニ四駆の応用でちっちゃいタチコマ作ろうかな。


ほげ

ロボットの会チーム1さんの倒立振子。秋月の格安ステッピングモータを使っています。(プーリーが圧入されてるのでジャンク価格)電源は12V鉛を24Vに昇圧。ドライバはやはり、STマイクロのL6470。1/128マイクロステップができてSPI接続というすぐれものです。マイコンArduino


ほげ

MAENOH!さんのデスクトップロボGRシリーズの倒立振子。中身は僕の倒立振子とほぼ同じような構成ですが、外装がとても可愛らしいです。倒立振子は見た目が地味なので、外装の工夫も大事ですね。あと、テレビのリモコンでコントロールするのが面白い。1個のリモコンで複数台に同じ動きをさせたり。


ほげ

東工大ロボット技術研究会さんの玉乗りロボット。マイコンはSTM32F407。9軸センサはおなじみMPU9250。PololuのエンコーダつきDCモータを使用し、電流センサACS711を使って電流制御しているそうです。オムニホイールはVstoneのなめらかオムニ。


ほげ

電子工作室/yuki-labさんのヤドカリ型歩行メカ。可愛い脚車輪のメカ。無線は920M特小。2.4G帯が混み合うイベント会場では920M特小は有利ですね。2本/4自由度のジョイスティックで4個のサーボを直接操作するので、操縦にはかなり慣れが必要(笑)。そのかわりどんなポーズも2本のジョイスティックだけでできます。


ほげ

Ai.FrameさんのAIF-44-0 Apollo Robot (キット)。独自設計のサーボで、ちっちゃいけど4kg・cmもあるらしい。コントローラはSTM32でBluetooth通信するらしい。(それくらいしか英語聞き取れなかった。)


ほげ

ゲリラ(出展者じゃないけど作品持ち歩いて見学)のかた。ブラケットは3Dペン製というのがある意味すごい(笑)。


ほげ

こちらもゲリラのかた。ロボットを相方にしてる芸人さんらしい。世界は広いな。


ほげ

TKMANさんのお菓子自販機のとなりに展示されてたチロルチョコの4足歩行ロボット。僕の歩くミニ四駆もそうだけど、外装の意外性でウケてる面が大きい。コンテンツ力だいじ。


ESP8266をArduinoとして使う

いまさらだが自分用まとめ

ESP8266とは?

  • 安い技適済みWiFiモジュール (のコアとなるチップ)
  • みんなが使ってるのはESP-WROOM-02 (秋月で550円)
    • なぜかみんなESP-WROOM-02のことをESP8266と呼んでる
  • ファームウェアを書き換えられる
  • ATコマンドで制御できるファームウェアが予め焼かれている
  • Arduino IDEでファームを開発できる (←これが流行り)
  • LuaPythonJavascriptでも開発できるらしい。知らんけど。

ESP-WROOM-02のスペック

  • 32bit CPU 80MHz (ARMではない。懐かしのTensilica Xtensa!)
  • データRAM 80kB (SDKが約30kBを消費するため、ユーザが使えるのは約50kB)
  • SPIフラッシュ 4MB (SDKが約220kBを消費)
  • 3.3V駆動
  • GPIO x 11 (兼用ピン含む。一部制約あり)
  • SPI x 1, I2C x 1 (SPIとI2Cは一部ピン兼用のため同時使用不可)
  • UART x 1 (デバッグプリント用にTxDのみのUARTがもう一個ある)
  • 10ビットADC x 1


ピン表はこちらのものが見やすい。

購入

モジュール単体では試作には使いにくいので、ブレークアウトボードとかを利用する

 【秋月】

 【スイッチサイエンス


ここでは、共立電子の基板を使用した。

チップ抵抗のハンダ付けは初心者向きではないが、基板上でプルアップできる。

Arduino開発環境

配線

配線

PCと接続するためにUSBシリアル変換基板を用い、電源もとりあえずここから取る。

  • 3V3 → 3.3V電源
  • EN → 10kΩでプルアップ
  • RST → 10kΩでプルアップ, タクトスイッチでGND
  • TXD → USBシリアルのRXD
  • RXD → USBシリアルのTXD
  • GND → GND
  • IO0 → 10kΩでプルアップ, タクトスイッチでGNDへ
  • IO2 → 10kΩでプルアップ
  • IO15 → 10kΩでプルダウン

※写真の例ではブレークアウトボード裏面にプルアップ抵抗実装ずみ

※起動時のIO0, IO2, IO15のレベルによってモードが決定する

DIP IO0 IO2 IO15
UART Download Mode 0 1 0
Flash Startup Mode 1 1 0
SD-Card Boot Mode 0 0 1

IO0のスイッチを押したまま、RSTのスイッチを押して離すとファーム書き込みモードになる。

Lチカ

とりあえず、シリアル出力とLチカとサーボ制御とPWM出力を確認してみる。

#include <Servo.h>

Servo servo;

void setup() {
  Serial.begin(115200);
  pinMode(13, OUTPUT);  // IO13にLED
  servo.attach(12);     // IO12にサーボ
  servo.write(90);      // サーボを90°に
  analogWrite(14, 512); // IO14にPWM (10bitであることに注意)
}

void loop() {
  Serial.print("Hello, ");
  digitalWrite(13, HIGH);
  delay(1000);
  Serial.println("world!");
  digitalWrite(13, LOW);
  delay(1000);
}

Arduinoとしてプログラムできることが確認できた。肝心のWiFiはまた今度。

資料