ソースに絡まるエスカルゴ

貧弱プログラマの外部記憶装置です。

【ESP32】data.sparkfun.comにESP32から値を送る方法

data.sparkfun.com公式

「data.sparkfun.com」というIoT用の無料で使えるサイトを知ったのでESP32から値を送ってみました。今回はその方法をまとめていきます。

1:data.sparkfun.comでストリームを作成する
 data.sparkfun.comにアクセスし、createボタンを押します。移動先のページで各項目を入力することで値を送るストリームを作成できます。

項目名 内容 必須
Title 作成するストリームのページに表示されるタイトル
Description 作成するストリームのページの表示される詳細な説明
Show in Public Stream List? Visibleの場合は全体リストに公開、Hiddenの場合は非公開
Fields 値を受け取る変数名
Stream Alias エイリアスの設定。sensorと入力するとhttp://data.sparkfun.com/sensorでアクセスできる ×
Tags タグの設定 ×
Location 場所の設定 ×

上記の入力が終了しSaveボタンを押すと、必要なキーや値を送る例を表示したページに移動します。各値は忘れないようにメモしておきます。

ちなみに各項目の内容は以下の通りです。

項目名 内容
Public URL 作成されたストリームのURL。ここで送られた値を確認する
Public Key ストリームからデータを取り出す時に必要なキー
Private Key ストリームにデータを送る時に必要なキー
Delete Key ストリームを削除する時に必要なキー
Format ストリームにデータを送る時のフォーマット
Example Formatに合わせた具体例


2:ESP32をネット接続してストリームに値を送信する
1でストリームが完成したら、FormatにしたがってESP32からネット経由で値を送信します。具体的にはESP32に以下のソースを書き込めば実現できます。

・Sparkfun.ino

#include <WiFi.h>

const char* ssid     = "***";       // インターネットに接続できるWiFiのSSID
const char* password = "***";  // SSIDのパスワード

const char* publicKey = "*****";  // ストリーム作成時に払い出されたPublic Key
const char* privateKey = "*****"; // ストリーム作成時に払い出されたPrivate Key
const char* fields = "field";          // ストリーム作成時に設定したFields

const char* host = "data.sparkfun.com";  // host

WiFiClient client;

void setup() {
  Serial.begin(115200);
  
  // WiFiに接続
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)  {
    Serial.print(".");
    delay(1000);
  }
  Serial.print("Connected to ");
  Serial.println(ssid);

  if (client.connect(host, 80)) {
    int val = 100;  // 今回は100という数値を送信

    // Formatに合わせたURLを作成
    String url = String("/input/")
                  + publicKey + "?private_key="
                  + privateKey + "&" + fields + "=" + val;

    // URLを発行
    client.print(String("GET ") + url + " HTTP/1.1\r\n" +
                 "Host: " + host + "\r\n\r\n");
    delay(1000);

    while (client.available()) {
      Serial.print(client.readStringUntil('¥r'));  // 結果のhtmlを表示
    }
  } else {
    Serial.println("Connection failed!");
  }
}

void loop() {
}

WiFiに接続した後に100という値を送信するURLを作成し発行します。その結果のhtmlをシリアルモニタに表示させています。


以上の方法で値をストリームに送信できます。ストリームから値を取得したり削除したりはまだ試していませんが、いわゆるIoTっぽいことがこれでできるようになります。ストリームを使って色々値のやりとりできたら楽しいですね。


※追記:ストリームからの値取得
ストリームからの値を取得することもできたので修正したソースを追記しておきます。

・Sparkfun2.ino

#include <WiFi.h>

const char* ssid     = "****";
const char* password = "****";
const char* publicKey = "*****";
const char* privateKey = "*****";
const char* fields = "field";
const char* host = "data.sparkfun.com";

WiFiClient client;

// 引数の値を送信する関数(全角文字はストリームに送信できない。失敗する)
void sendDataToSparkfun(String val) {
  String url = String("/input/")
                + publicKey + "?private_key="
                + privateKey + "&" + fields + "=" + val;
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
             "Host: " + host + "\r\n\r\n");
  // 結果を表示
  while (client.available()) {
    Serial.print(client.readStringUntil('¥r'));
  }
}

// ストリームの値を取得する関数
void getDataFromSparkfun() {
  String url = String("/output/") + publicKey + ".csv?page=1";
  client.print(String("GET ") + url + " HTTP/1.1\r\n" +
                "Host: " + host + "\r\n\r\n");
  // 結果を表示
  while (client.available()) {
    Serial.print(client.readStringUntil('¥r'));
  }
}

void setup() {
  Serial.begin(115200);
  
  // WiFiに接続
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED)  {
    Serial.print(".");
    delay(1000);
  }
  Serial.print("Connected to ");
  Serial.println(ssid);

  if (client.connect(host, 80)) {
    String value = "1111"; // 1111を送信
    sendDataToSparkfun(value);
    delay(1000);
    getDataFromSparkfun();
  } else {
    Serial.println("Connection failed!");
  }
}

void loop() {
}

Arduino IDEでのコンパイル時に特におかしなところがないのに「"関数名" was not declared in this scope」というエラーが発生した場合、setup関数よりも前にエラーが出た関数を移動させるとコンパイルが通るという現象もあるようです。

【ESP32】ESP32とスマホを接続してWiFiUDP通信する方法

rikoubou.hatenablog.com

↑の前回の記事でESP32を使って以降、ESP32をいじっています。今回はタイトル通り「ESP32とスマホを繋いでUDP通信する方法」をやってみたので記録しておきます。

1:ESP32とUDP通信できるようにする
基本的にはWiFiをアクセスポイント(APモード)で使用するプログラムを少し変更するだけでできます。以下がプログラムになります。

・ESP32_WiFiUDP.ino

#include <WiFi.h>
#include <WiFiUdp.h>

const char ssid[] = "ESP32_wifi"; // SSID
const char pass[] = "esp32pass";  // password
const int localPort = 10000;      // ポート番号

const IPAddress ip(192, 168, 4, 1);       // IPアドレス(ゲートウェイも兼ねる)
const IPAddress subnet(255, 255, 255, 0); // サブネットマスク

WiFiUDP udp;

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

  WiFi.softAP(ssid, pass);           // SSIDとパスの設定
  delay(100);                        // 追記:このdelayを入れないと失敗する場合がある
  WiFi.softAPConfig(ip, ip, subnet); // IPアドレス、ゲートウェイ、サブネットマスクの設定

  Serial.print("AP IP address: ");
  IPAddress myIP = WiFi.softAPIP();
  Serial.println(myIP);

  Serial.println("Starting UDP");
  udp.begin(localPort);  // UDP通信の開始(引数はポート番号)

  Serial.print("Local port: ");
  Serial.println(localPort);
}

void loop() {
  if (udp.parsePacket()) {
    Serial.println(udp.read()); // UDP通信で来た値を表示
  }
}

WiFiUdp.hを読み込んで使用します。ポート番号とIPアドレスは固定させていた方が良いと思います。

上記のプログラムを書き込むとSSIDで指定した名前でWiFiが飛ぶようになります。passwordで設定したパスを入力すれば接続できます。


2:スマホにアプリを入れてUDPコントローラを作成する
次にスマホUDPコントローラとして使うためのアプリ「WiFi TCP/UDP Controller」を以下のリンクからインストールします。
play.google.com


インストール後にWiFi TCP/UDP Controllerを立ち上げるとDEFと書かれた多くのボタンが表示されます。最初は何も設定されていないので、右上にある「PREFERENCE」をタップして各ボタンの設定を行います。

f:id:rikoubou:20170531180529p:plain

・IP or Domain Name
 IPアドレスドメイン名を設定します。1のプログラムそのままの場合は「192.168.4.1」と設定します。

・Port
 ポート番号を設定します。1のプログラムそのままの場合は「10000」と設定します。

・BUTTON Name
 画面に表示されるボタン名を設定します。設定画面に移動したときに出てくる画像通りの並び順なので、それぞれのボタンに表示させたい名前を設定します。自分の場合は以下のように設定しました。

ボタン名 表示名
button2
button4 ◀︎
button6 ▶︎
button8
button19 B
button21 A

・BUTTON Command
 ボタンを押した際に送信する値を設定します。設定画面に移動したときに出てくる画像通りの並び順なので、それぞれのボタンに表示させたい名前を設定します。ASCII-HEXでは文字列を送信する場合は「ASCII」、数値を送信する場合は「HEX」を選びます。

自分の場合は以下のように設定しました。

項目名 設定値
ASCII-HEX ASCII
button2 0
button4 1
button6 2
button8 3
button19 4
button21 5

 STOP COMMANDではボタンを離したときに送信される値を設定します。自分の場合は以下のように設定しました。

項目名 設定値
button2 6
button4 6
button6 6
button8 6
button19 6
button21 6

・BUTTON Visibility
 ボタンの表示/非表示を設定します。デフォルトでは全てのボタンが表示されているので、不要なボタンは非表示にします。自分の場合は以下のように設定しました。

項目名 チェック
button2
button4
button6
button8
button19
button21

上記以外のチェックは全て外しています。

・BUTTON Repeatable
 ボタンを押している間の送信時間間隔を設定します。個別のボタンごとに設定もできますが、自分の場合は以下のように設定しました。

項目名 設定値
Individual Intervals OFF
Interval(ms) 200

・Send Data
 送った値を画面下に表示させるかどうかを設定します。自分の場合はONにしています。

・設定完了例
 自分が作成したコントローラは以下のようになります。各ボタンを押したときに上にある数字が送信されます。全てのボタンにおいて離したときに6が送信されるようにしています。
f:id:rikoubou:20170531181547p:plain


3:ESP32とスマホを接続して通信を確認する
1のプログラムを書き込んだESP32をUSBで接続し、シリアルモニタを立ち上げます。ESP32のSSIDWiFiスマホで接続し、2で作成したコントローラのボタンを押すとその値がシリアルモニタに表示されます。(今回はASCIIで送信しており、数字の文字列が送信されているので、ASCIIの対応表で数字を変換してください)

・このような形で押したボタンの値が表示されていきます。
f:id:rikoubou:20170531185704p:plain


以上でESP32とスマホ間でのUDP通信ができました。
ESP32にディスプレイを繋げば簡単なゲームも作成できる(している方もいる)ので色々と面白いことができそうです。


・参考資料
github.com

www.nicovideo.jp

【ESP32】ゼルダエフェクトーンを自作してみた

www.bpnavi.jp

ツイッター上で話題になったゼルダエフェクトーン。
見た瞬間に「これ作れそう」と思って材料を購入し、勢いで自作しました。
使った材料や回路図を記録する意味でも記事にしておきます。

※電子回路もプログラムも知識が浅いため、ツッコミどころが多いと思います。注意してください


・追記:回路とプログラムの改良版ができました
rikoubou.hatenablog.com


1:材料
以下が今回使用した材料です。合計2500円ぐらいです。

・ESP32-DevKitC(以下記事内ではESP32と記述) 商品ページ
・20ピン基板用ピンソケット[20ピン×1列]【21601X20GSE】×2 商品ページ
 (ESP32の片側の足は19本なので一つ余る)
・ユニバーサル基盤(ICB288) 商品ページ
・電池ケース 単4×2本(BH421A) 商品ページ
・圧電ブザー(buz-1.5-9.5mm) 商品ページ
・ケース入りリードスイッチ(磁石付セット)MC-14AG 商品ページ
・基板用スライドスイッチ(横型)【GB-SSW-SPDT-RAGL】 商品ページ
・SW型プラスチックケース【SW85S】 商品ページ
・3mmプラスチックネジ(7mm)+ナットセット 商品ページ

※あと半田ごてとか接着剤とか電動ドリルとか単4電池とかもろもろ


2:回路図
f:id:rikoubou:20170529173141p:plain

ものすごく単純な回路です。
単4ケースは、ユニバーサル基盤にドリルで穴を開けてプラスチックのネジとナットで基盤自体に固定させています。


3:ケースの加工
電源スイッチをケースの外に出す必要があるので、以下の写真のように側面に穴を開けます。
f:id:rikoubou:20170529174251p:plain


4:ESP32へプログラムを書き込む
前提としてESP32が使用できる環境が必要です。
【備忘録】ESP-32 DevKitCの環境構築方法 - ソースに絡まるエスカルゴ

ESP32に書き込むプログラムは以下の通りです。

・Effectone.ino

#define BUZZER_PIN 23       // ブザーを鳴らすためのピン
#define MAGNET_PIN 12       // 磁石スイッチのHIGHを出力するピン
#define JUDGE_PIN 14        // 磁石スイッチの判断をするピン

#define BEAT 150            // 一つの音を鳴らす時間
#define LEDC_CHANNEL 0      // チャンネル
#define LEDC_TIMER_BIT 13
#define LEDC_BASE_FREQ 5000

bool soundFlg = false; // 音を鳴らしたかどうかのフラグ

void setup() {
  // マグネットスイッチに関係するピンの準備
  pinMode(MAGNET_PIN, OUTPUT);
  pinMode(JUDGE_PIN, INPUT);

  // マグネットスイッチのをHIGHにする
  digitalWrite(MAGNET_PIN, HIGH);
  
  // 音を鳴らす準備
  ledcSetup(LEDC_CHANNEL, LEDC_BASE_FREQ, LEDC_TIMER_BIT);
  ledcAttachPin(BUZZER_PIN, LEDC_CHANNEL);
}

void loop() {
  int val = digitalRead(JUDGE_PIN);
  if (!soundFlg && val == LOW) {
    callZeldaSound();   // ゼルダの謎解き音を鳴らす
    soundFlg = true;
  } else if (val == HIGH) {
    soundFlg = false;
  }
  delay(100);
}

/**
 * ゼルダの謎解き音
 */
void callZeldaSound() {
  ledcWriteTone(LEDC_CHANNEL, 3136); // ソ
  delay(BEAT);
  ledcWriteTone(LEDC_CHANNEL, 2960); // ♯ファ
  delay(BEAT);
  ledcWriteTone(LEDC_CHANNEL, 2489); // ♯レ
  delay(BEAT);
  ledcWriteTone(LEDC_CHANNEL, 1760); // ラ
  delay(BEAT);
  ledcWriteTone(LEDC_CHANNEL, 1661); // ♯ソ
  delay(BEAT);
  ledcWriteTone(LEDC_CHANNEL, 2637); // ミ
  delay(BEAT);
  ledcWriteTone(LEDC_CHANNEL, 3322); // ♯ソ
  delay(BEAT);
  ledcWriteTone(LEDC_CHANNEL, 4186); // ド
  delay(BEAT);
  ledcWriteTone(LEDC_CHANNEL, 0);    // 音を止める
}

ESP32ではブザーを鳴らす関数としてledcWriteToneを使います。

・参考記事
ESP-WROOM-32


5:ケースにつめる
基盤の実装とESP32へのプログラムの書き込みが終了したら、次はケースに詰めます。SW85Sにはネジ穴はついてないので、接着剤でナットをケースの底に固定させています。位置が確定したら、フタを被せてブザーが来る位置に穴を開けます。
f:id:rikoubou:20170529175520j:plain


6:完成したので遊ぶ
動かすとこんな感じになります。

リードスイッチは振動に弱いらしく、現状のプログラムのままだとケースを振るだけで音が鳴ります。また音も小さいので、穴を増やしたり大きくしたりした方が良いかもしれません。


以上が今回作ってみた自作ゼルダエフェクトーンになります。プログラムを修正すれば他の音も鳴らせますし、ESP32はWifiBluetooth接続もできるのでさらに遊べると思います。(正直今回程度のことを実現するならオーバースペックすぎますし……)

ブザーを増やして和音を鳴らせたりするのも面白いかもしれません。

【備忘録】KKHMF LM393 IR赤外線障害物回避センサモジュール Arduino用について

amazonで見つけた怪しいぐらいに安い距離センサー(KKHMF LM393 IR赤外線障害物回避センサモジュール Arduino用)についての備忘録です。

下記商品ページのレビューにある内容と実際のものがちょっと違っていたので記録しておきます。
Amazon CAPTCHA


■大体の仕様
結論部分だけまとめると以下のようになります。

物理的距離 OUTの出力 輸出表示のLED
設定距離より遠い HIGH 消灯
設定距離より近い LOW 点灯

可変抵抗を回して設定距離を変更可能。
判定はOUTのデジタル出力HIGH or LOWで判断。

  • OUTがHIGH : 設定距離より遠い
  • OUTがLOW  : 設定距離より近い

「輸出表示」と文字のある部分のLEDが光っている時は近い、光っていない時は遠いと見た目でもわかるようになっている。可変抵抗を反時計回りに回すと設定距離が短く時計回りに回すと設定距離が長くなる。

※ただし、どちらの場合でも可変抵抗を限界まで回すと検知できなくなるので注意


単に近いかどうかを判定するだけなら十分使えるセンサーだと思います。
これで何かを作りたいですね。

【備忘録】ESP-32 DevKitCでのWiFi接続方法(APモード)

APモードのESP-32にWiFi接続する方法を残しておこうと思った次第です。

というのも、

  • esp8266で使っていたソースが流用できない。
  • サンプルにAPモードでの接続のものが見当たらない。

という状況だったので、まとめようと決意しました。

では以下に方法をまとめていきます。
※一つ前の記事で環境構築が済んでいることが前提です。

■ESP-32でAPモードでWiFiを起動するソース

結論から言うと、以下のソースでいけます。

ESP32_WiFi.ino

#include <WiFi.h>

WiFiServer server(80);

const char ssid[] = "ESP32-WiFi";  // SSID
const char pass[] = "esp32wifi";   // password

const IPAddress ip(192, 168, 20, 2);      // IPアドレス
const IPAddress subnet(255, 255, 255, 0); // サブネットマスク

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

  WiFi.softAP(ssid, pass);           // SSIDとパスの設定
  delay(100);                        // 追記:このdelayを入れないと失敗する場合がある
  WiFi.softAPConfig(ip, ip, subnet); // IPアドレス、ゲートウェイ、サブネットマスクの設定
  
  IPAddress myIP = WiFi.softAPIP();  // WiFi.softAPIP()でWiFi起動
  server.begin();                    // サーバーを起動(htmlを表示させるため)

  /* 各種情報を表示 */
  Serial.print("SSID: ");
  Serial.println(ssid);
  Serial.print("AP IP address: ");
  Serial.println(myIP);

  Serial.println("Server start!");
}

void loop() {
  WiFiClient client = server.available();
  
  if (client) {
    String currentLine = "";
    Serial.println("new client!");
    while (client.connected()) {
      if (client.available()) {  
        if (currentLine.length() == 0) {
          client.println("HTTP/1.1 200 OK");
          client.println("Content-type:text/html");
          client.println();
          client.println("hello!");
          break;
        } else {
          currentLine = "";
        }
      }
    }

    // 接続が切れた場合
    client.stop();
    Serial.println("client disonnected");
  }
}

上記のプログラムをESP-32 DevKitCに書き込むと、ESP32-WiFiというWiFiが飛ぶようになります。
それを選択し、passで設定したパスワードを入力すると接続完了です。

その状態でブラウザを開いて、IPアドレスに設定した192.168.20.2に接続すると「hello!」と表示されます。

SSID、pass、ipなどは定数になっているので任意に変更することができます。


・2017/07/18 追記
 久々にWifiのAPモードをESP32で実行しようとしたところ、なぜか起動しないという不具合が確認できました。色々調べて行くと「WiFi.softAPのあとにdelayを入れないと失敗する場合がある」ことがわかりました。
 なので上記プログラムにdelayを追記しています。

 抜粋すると以下の部分になります。

WiFi.softAP(ssid, pass);           // SSIDとパスの設定
delay(100);                        // 追記:このdelayを入れないと失敗する場合がある
WiFi.softAPConfig(ip, ip, subnet); // IPアドレス、ゲートウェイ、サブネットマスクの設定


・参考ページ
github.com

【備忘録】ESP-32 DevKitCの環境構築方法

ESP32をのせた開発ボードESP-32 DevKitCが秋月で売られていることに気がついたので、秋葉原に行ったついでに購入してきました。
akizukidenshi.com

ということで、早速環境構築をやってみたのでまとめます。
MacOSでの環境構築になります。


1:Arduino IDEをインストールする
以下のサイトでOSを選択してインストールします。
Arduino - Software


2:Pythonをインストールする(Windows以外のOSの場合に必要)
途中でpythonのファイルを実行する場面があるので、以下のページからダウンロードしてPythonをインストールします。
www.python.org


3:必要なファイルをダウンロードして適切な場所に配置する
基本的には以下のページにある方法に沿っていけば大丈夫です。
github.com

・gitがすでにインストールされている場合
すでに記述した手順のArduino IDEpythonに加えて、gitもインストールされている場合は、ターミナルを開いて以下のコマンドを1行ずつコピペして実行するだけでOK。

mkdir -p ~/Documents/Arduino/hardware/espressif
cd ~/Documents/Arduino/hardware/espressif
git clone https://github.com/espressif/arduino-esp32.git esp32
cd esp32/tools/
python get.py


・gitがインストールされていない場合
gitがインストールされてない場合は上記URLの左側にある「Clone or download」から「Download ZIP」を選択して.zipファイルをダウンロードします。
f:id:rikoubou:20170428140459p:plain

ダウンロードが完了したら.zipファイルを解凍します。解凍すると「arduino-esp32-master」というフォルダが作成されるので、このフォルダの名前を「esp32」に変更します。

その後Arduinoのスケッチが保存されるフォルダ(ユーザ名/Documents/Arduinoのフォルダ)に「hardware」という名前のフォルダを作成します。

作成した「hardware」フォルダの中にさらに「espressif」という名前のフォルダを作成します。

「espressif」の中に先ほど解凍して名称を変更した「esp32」というフォルダを移動させます。

構成図は以下のようになります。

Arduinoフォルダ
 |- hardware # 作成
   |- espressif # 作成
     |- esp32 # .zipファイルを解凍したフォルダ(arduino-esp32-masterの名称を変更したもの)

ここまで完了したらターミナルを立ち上げて、esp32の中にあるtoolsフォルダに移動し、以下のコマンドを実行します。

python get.py


4:Arduno IDEを立ち上げて各種設定を行う
1〜3の手順が終わったらArduino IDEを立ち上げます。
ツール -> ボード -> ESP32 Dev Moduleが追加されているのでESP32 Dev Moduleを選択します。
f:id:rikoubou:20170428142414p:plain

選択すると諸々の設定などができるようになるので各種設定を行います。
自分の場合は以下のような設定にしています。
f:id:rikoubou:20170428142725p:plain


5:ESP-32 DevKitCとの接続確認を行う
設定まで終わったらUSBにESP-32 DevKitCを繋いでシリアルポートに認識されるかを確認します。自分の場合は特に問題なく認識しましたが、ドライバを入れないと認識しない場合もあるそうです。(要検証)

あとは普通のArduinoを扱うようにスケッチを書き込めば動くようになります。


以上、ざっと環境構築手順をまとめてみました。
ESP-32 DevKitCを使ってこれから色々遊んでみたいです。


・参考ページ(こちらの方がわかりやすいかも)
blog.boochow.com
www.mgo-tec.com

【Android Studio】備忘録#2 ScrollView内でListViewを動作させる方法

Android Studioで適当なアプリ開発を進めていますが
デフォルトの設定だと色々できなくて「ふんがー!」となっている日々です。

その「ふんがー!」となった内の一つが今回のタイトルにある
「ScrollView内でListViewを動作させる方法」です。

何も設定せずにScrollView内にListViewを配置させると
「一行分しか表示されない上にListView自体のスクロールができない」
という謎の仕様。

解消するために調べても簡単な方法がなかなかみつからなかったのですが
ようやく見つけたので備忘録として残しておきます。

■ScrollView内でListViewを動作させる方法

結論から言うと、以下のページの内容にある通りのことをすれば実現できます。
android-note.open-memo.net

ただこれだとScrollViewにListViewを使う画面に
毎回メソッドを実装しなくてはならないので
以下のようにstaticなクラスに実装しました。

Common.java

public class Common {
    /**
     * ScrollViewにListViewを入れ込む時に使う関数
     * @param listView ListView
     */
    public static void setListViewOnTouchListener(ListView listView) {
        listView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                v.getParent().requestDisallowInterceptTouchEvent(true);
                return false;
            }
        });
    }
}

ScrollViewを表示させる画面のActivityから
上記のメソッドを呼び出します。

■呼び出し記述方法

ListView listView = (ListView) findViewById(R.id.[ListViewのID]);    // ListView
Common.setListViewOnTouchListener(listView);

これでScrollView内でListViewが正常に動くようになります。