はじめに
Visual Studio/C#でWindows、Macとくれば「次はAndroidからEV3を制御してみよう!」ということでやってみました。
【関連記事】
【EV3 x C#】lejos-server/clientを使ってC#でEV3を制御する(Windows編)
【EV3 x C#】lejos-server/clientを使ってC#でEV3を制御する(Mac編)
本記事は、上記Windows編での準備およびlejos-serverの転送・実行ができている状態を前提とします。
スマホアプリは今回が初めてという私ですら、Xamarin.Formsを使用して簡単にAndroidからEV3の制御ができましたのでご紹介します。
環境
開発PC
- Windows 10 64bit
- Visual Studio 2019
EV3
- leJOS
- 車の基本モデル形
- WiFiドングル付 (ELECOM WDC-150SU2MBK)
Android
- OPPO Reno A (Android 9)
WiFi
- スマホ、EV3を同一APに接続
準備
Visual Studioでモバイル開発を有効にする
Visual Studioインストーラーを実行し、[.NETによるモバイル開発]にチェックしてインストールしておきます。
Androidアプリの作成
「Xamarin を使用して Android 向けの開発を始める」を参考にしながら以下の手順を実施します。
Xamarin.Formsプロジェクトの新規作成
Visual Studioを起動し、新規プロジェクトの作成から[モバイルアプリ(Xamarin.Forms)]を選択して[次へ]
次の画面で任意のプロジェクト名を入力して[作成]をクリック。
[新しいモバイルアプリ]画面が表示されるので、
- [空白]を選択
- 予定している開発対象で[Android]のみにチェック
実機と接続して実行
「Android デバイスまたはエミュレーターでテストする」を参考にしながら、以下の手順を実施します。
(一部文言はOPPOの場合)
- スマホと開発PCをUSB接続し、設定を開く
- [端末情報]を表示する
- [ビルド番号]を7回タップ =>開発者モードになる
- 前の画面に戻り、[その他の設定] -> [開発者オプション] -> [USBデバッグの許可]をセット
ここまでの手順を実施すると、接続した実機名で実行できそうな状態になります。
プログラムは何も変更せずに、とりあえず実行してみます。
おぉ!実機上にこんな画面が表示された!!(もうこの時点で感動)
画面の変更
「Xamarin.Forms XAML ページのレイアウトをカスタマイズする」を参考に、MainPage.xamlファイルの内容を次のように書き換えます。
ラベル部分を「EV3 Controller」に書き換えたのと、3x3のグリッドにボタンを配置しました。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="lejosAndroid.MainPage">
<StackLayout>
<Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
<Label Text="EV3 Controller" HorizontalTextAlignment="Center" TextColor="White" FontSize="36"/>
</Frame>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="1" Text="Forward" Clicked="OnForwardButton_Clicked" />
<Button Grid.Row="1" Grid.Column="0" Text="Left" Clicked="OnLeftButton_Clicked" />
<Button Grid.Row="1" Grid.Column="1" Text="STOP" Clicked="OnStopButton_Clicked" />
<Button Grid.Row="1" Grid.Column="2" Text="Right" Clicked="OnRightButton_Clicked" />
<Button Grid.Row="2" Grid.Column="1" Text="Back" Clicked="OnBackButton_Clicked" />
</Grid>
</StackLayout>
</ContentPage>
NuGetからlejos-clientを追加
- Visual Studioのメインメニューから[ツール] -> [NuGetパッケージマネージャー] -> [パッケージマネージャーコンソール]を選択
- パッケージ マネージャー コンソールで次のように入力し、Enter
PM> Install-Package lejos-client
【補足】
Android開発に合わせて、lejos-clientは.NET Standard2.0にも対応しました。
ロジックの記述
MainPage.xamlを右クリックし、[コードの表示]を選択します。
MainPage.xaml.csに次のように記述します。
ボタンのイベント処理で各EV3のコマンドを実行(パラメータは適当)するシンプルなものです。
using System;
using System.Collections.Generic;
using Xamarin.Forms;
using lejos_client;
namespace lejosAndroid
{
public partial class MainPage : ContentPage
{
//EV3オブジェクト
EV3 _ev3;
public MainPage()
{
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
// アプリ開始時にEV3がWiFiで取得したアドレスに接続
_ev3 = new EV3("192.168.1.107", 6789);
}
private void OnForwardButton_Clicked(object sender, EventArgs e)
{
//前進
_ev3.Wheels.GoForward(150, 720);
}
private void OnLeftButton_Clicked(object sender, EventArgs e)
{
//左に回転
_ev3.Wheels.TurnLeft(100, 100);
}
private void OnRightButton_Clicked(object sender, EventArgs e)
{
//右に回転
_ev3.Wheels.TurnRight(100, 100);
}
private void OnBackButton_Clicked(object sender, EventArgs e)
{
//後進
_ev3.Wheels.GoBackward(150, 720);
}
private void OnStopButton_Clicked(object sender, EventArgs e)
{
//停止
_ev3.Wheels.Stop();
}
}
}
再度デバッグ実行
デバッグ実行の前に、EV3が「READY」の状態になっていることを確認してください。
実行すると、簡易的にグリッドに配置したボタンがこのように表示されました。
ボタンをタップして動きを確認します。
AndroidxC#でEV3を制御してみた。 pic.twitter.com/qd9Lx2m4NT
— teonsen (@teonsen2) December 31, 2020
一度スマホでデバッグ実行すると、アプリがスマホ内に入るのでUSBケーブルを抜いても動作します。
追記:傾きでの制御
スマホの傾きで直感的に操作できるようにしてみました。
「Orientation Mode」を追加し、ONの場合はスマホの傾きがそのまま動きとなるようにします。
MainPage.xamlを次のように変更します。グリッドに2行追加し、SwitchとOrientation表示用のLabelを追加しました。
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="lejosAndroid.MainPage">
<StackLayout>
<Frame BackgroundColor="#2196F3" Padding="24" CornerRadius="0">
<Label Text="EV3 Controller" HorizontalTextAlignment="Center" TextColor="White" FontSize="36"/>
</Frame>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Button Grid.Row="0" Grid.Column="1" Text="Forward" Clicked="OnForwardButton_Clicked" />
<Button Grid.Row="1" Grid.Column="0" Text="Left" Clicked="OnLeftButton_Clicked" />
<Button Grid.Row="1" Grid.Column="1" Text="STOP" Clicked="OnStopButton_Clicked" />
<Button Grid.Row="1" Grid.Column="2" Text="Right" Clicked="OnRightButton_Clicked" />
<Button Grid.Row="2" Grid.Column="1" Text="Back" Clicked="OnBackButton_Clicked" />
<Label Grid.Row="3" Grid.Column="1" Text="Orientation Mode" HorizontalOptions="Center" />
<Switch Grid.Row="3" Grid.Column="2" x:Name = "switch" HorizontalOptions="Start" Toggled = "OnToggled"/>
<Label Grid.Row="4" Grid.Column="0" Grid.ColumnSpan="3" x:Name="LableOrientation" Text="Label Orientation" HorizontalOptions="Center" VerticalOptions="CenterAndExpand" />
</Grid>
</StackLayout>
</ContentPage>
スマホの各種センサー値を取得するには@shuheyさんの記事から「Xamarin.Essentials」を使うと良いらしいことがわかったので、NuGetから「Xamarin.Esssentials」をインストールします。
上記各サイトを参考にしながらMainPage.xaml.csを以下のように修正します。
using System;
using System.Collections.Generic;
using Xamarin.Forms;
using Xamarin.Essentials;
using lejos_client;
namespace lejosAndroid
{
public partial class MainPage : ContentPage
{
//EV3オブジェクト
EV3 _ev3;
bool _OrientationMode = false;
public MainPage()
{
InitializeComponent();
// アプリ開始時にEV3がWiFiで取得したアドレスに接続
_ev3 = new EV3("192.168.1.108", 6789);
}
protected override void OnAppearing()
{
base.OnAppearing();
OrientationSensor.ReadingChanged += OrientationSensor_ReadingChanged;
OrientationSensor.Start(SensorSpeed.UI);
}
void OrientationSensor_ReadingChanged(object sender, OrientationSensorChangedEventArgs e)
{
var data = e.Reading;
// 調整するパラメータ
float paramX = 3;
float paramY = 5.5F;
int paramLR = 140; // 直角に傾けるくらいで左右モードにする
// Xに係数をかけて左右のSpeedとする
int ox = (int)(data.Orientation.X * 100 * paramX);
// Yに係数をかけて前後のSpeedとする
int oy = (int)(data.Orientation.Y * 100 * paramY);
int ow = (int)(data.Orientation.W * 100);
int oz = (int)(data.Orientation.Z * 100);
Device.BeginInvokeOnMainThread(() =>
{
LableOrientation.Text = $"Orientation X={ox}, Y={oy}, Z={oz}, W={ow}";
});
if (_OrientationMode)
{
if (Math.Abs(ox) > paramLR)
{
if (ox < 0)
{
_ev3.Wheels.TurnLeft(oy, 360);
}
else
{
_ev3.Wheels.TurnRight(oy, 360);
}
}
else
{
if (oy >= 0)
{
_ev3.Wheels.GoForward(oy, 720);
}
else
{
_ev3.Wheels.GoBackward(oy, 720);
}
}
}
}
private void OnForwardButton_Clicked(object sender, EventArgs e)
{
_ev3.Wheels.GoForward(150, 720);
}
private void OnLeftButton_Clicked(object sender, EventArgs e)
{
_ev3.Wheels.TurnLeft(100, 100);
}
private void OnRightButton_Clicked(object sender, EventArgs e)
{
_ev3.Wheels.TurnRight(100, 100);
}
private void OnBackButton_Clicked(object sender, EventArgs e)
{
_ev3.Wheels.GoBackward(150, 720);
}
private void OnStopButton_Clicked(object sender, EventArgs e)
{
_ev3.Wheels.Stop();
}
private void OnToggled(object sender, ToggledEventArgs e)
{
_OrientationMode = this.@switch.IsToggled;
if (!_OrientationMode)
{
_ev3.Wheels.Stop();
}
}
}
}
こんな感じで動作します。
スマホの傾きとEV3が連動するようにした。 pic.twitter.com/M6dQMsKcNn
— teonsen (@teonsen2) January 2, 2021
GitHubにアップしましたのでよろしければどうぞ。
https://github.com/teonsen/lejosAndroid
感想
- ありがとう。Xamarin.Forms。
- ドローンをスマホの傾きで制御したとき、どれだけうまくできるのかやってみたくなった。
参考サイト
Xamarin.Formsで各種センサーの値を取得するには
Xamarin.Essentials
Xamarin.Essentials:OrientationSensor