Android C++開発 [2] : InstrumentedTestでUIを表示する
InstrumentedTestを使えばデバイス上でUnitTestができますが、状態や映像を表示したりするにはUIが表示できると便利です。
InstrumentedTestにActivity表示のルールを追加する
Activityのテストをするにはテストクラスに ActivityTestRule というルールを追加します。
つまりテスト実行時にこのActivityを表示するように、といったルールの定義です。
ExampleInstrumentedTest.java
@Rule public ActivityTestRule rule = new ActivityTestRule<>(MainActivity.class, true, true);
この記述を行うとビルドエラーが発生します。
なぜならInstrumentedTest実行時にこのクラスを参照する設定がされていないからです。
build.gradle の dependencies 内、 androidTestImplementation の部分に参照を追加するとビルドエラーは解消します。
build.gradle
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:28.0.0' implementation 'com.android.support.constraint:constraint-layout:1.1.3' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:rules:0.4' #コレを追加 androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' }
ActivityにUI要素を追加
実施するテストに応じてActivityに必要なUI要素を追加します。
ここでは文字列を表示する TextView を "textMessage" という名前で追加しました。
テストクラスからUI要素にアクセスする
テストクラスからActivity内のUI要素にアクセスするには、先ほど追加したActivityTestRule からActivityを取得し操作します。
ただUI要素はUIスレッドから操作する必要があるので直接操作することはできず、runOnUiThread()メソッドにアクションを渡し、非同期で実行させる必要があるのですこし面倒でした。
このようなユーティリティメソッドを作成しました。
private void showMessageOnUiThread(String message) { class ShowTextAction implements Runnable { private String message; public ShowTextAction(String message) { this.message = message; } @Override public void run() { TextView messageText = rule.getActivity().findViewById(R.id.textMessage); messageText.setText(this.message); } } this.rule.getActivity().runOnUiThread(new ShowTextAction(message)); }
テストメソッドからの呼び出し
以上の準備を行えばテストメソッド内から画面に文字列を表示することができます。
応用すれば様々な操作を行うことができます。それだけではなくこのRuleの真価はUIの自動テストが行えるところにあります。
ボタンをプッシュするといった人が行うようなアクションもシミュレートさせることができます。
さて、テストメソッドでC++から取得した文字列を表示するためにはこのような実装を行いました。
ExampleInstrumentedTest.java
@Test public void stringFromJNITest() throws InterruptedException { String message = stringFromJNI(); this.showMessageOnUiThread(message); Thread.sleep(5000); assertEquals("Hello from C++ to InstrumentedTest", message); }
実行結果はこのようになります。
C++から取得したメッセージが表示されています。
まとめ
Andoid実機で動作するInstrumentedTestで画面を表示し、UI要素にアクセスする方法をお伝えしました。
プロジェクト一式は下記にアップしています。
Android C++開発 [1] : CMakeプロジェクトの作成
お久しぶりです!
この夏はものすごく忙しくてブログを書く時間がとれませんでした。
何をやっていたかというとAndroidで動作するライブラリをC++で作り、Unityラッパーも作成してUnity上でも利用できるようにする、という事をしていました。
C++でのAndroid開発はかなりレアな体験で、色々と学べたのでシェアします。
レアすぎて需要が無さそうだなあ(笑)
プロジェクトの作成
Android StudioでC++を扱うプロジェクトを作成するにはプロジェクト作成のウィザードの中で "Include C++ support" のチェックを付けます。
生成されるプロジェクトは下記のものを含みます。
名前 | 説明 |
---|---|
java | 画面のActivity等のJavaソースコード類 |
cpp | C++ソースコード |
build.gradle(Module: app) | ビルド設定 |
build.gradle(Module: app)について
Android のビルドシステムにはGradleが使われます。
C++のビルドで使われるCMakeとの関連付けはこのファイルの中で行われます。
"externalNativeBuild"の設定でC++ビルドのオプションとしてC++11を利用すること、CMakeのビルド設定が書かれているCMakeLists.txtへのパスが指定されています。
独自のカスタマイズを行う場合はこれらを編集します。
とりあえず実行
このまま特に変更をせずにアプリを起動するとC++側から"Hello from C++"という文字列を取得して画面に表示するものが起動します。
コードで何が行われているかを解説します。
native-lib.cpp
extern "C" JNIEXPORT jstring JNICALL Java_com_example_satoshi_1maemoto_myapplication_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
C++側ではマネージドのJavaのVM環境と連携をとるためJNIの規約に沿った名前のメソッドが用意されています。
規約としてはJavaのパッケージ名の "." を "_" に置き換え、引数でJavaVMへのポインタを受け取るというのがシンプルな説明です。
実際には様々なかたちのデータを引数でやり取りするため様々なテクニックがあります。
例えば配列の配列を受け取るとか、Java側のコールバックメソッドのアドレスを受け取りC++からJavaをコールバックするなど、、この辺りは次回以降扱いたいと思います。
JNIの規約で作ったC++関数は下記のようにJavaから呼び出すことができます。
MainActivity.java
public class MainActivity extends AppCompatActivity { // C++ライブラリ(native-lib.so)をロード static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // C++側の stringFromJNI()関数を呼び出し文字列を取得 TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(stringFromJNI()); } // C++関数は native 指定で宣言が必要 public native String stringFromJNI(); }
実機でのUnitTest
プロジェクトの何も手を加えない状態でUnitTestのモックも作成されています。
UnitTestには2種類あり、開発環境内で動作させる通常のUnitTestと、デバイス内で動作をさせるInstrumentedTestがあります。
やっぱり事件は現場で起こるのでできるだけ後者を行いたいものです。ビルド時間はかかってしまうのですが。
あらかじめ用意されているモックは特にC++側との連携はされていないものですが、もちろんC++関数のテストも可能です。 次回以降このInstrumentedTest内でUIを表示するにはどうすれば良いかなども扱いたいです。
ExampleInstrumentedTest.java
@RunWith(AndroidJUnit4.class) public class ExampleInstrumentedTest { @Test public void useAppContext() { // Context of the app under test. Context appContext = InstrumentationRegistry.getTargetContext(); assertEquals("com.example.satoshi_maemoto.myapplication", appContext.getPackageName()); } }
InstrumentedTestの実行は、クラスを右クリックしてRunまたはDebugを選択します。
まとめ
MRTKのAppBarで簡単視野追従フローティングメニューを作ってみた
HoloLensアプリを作っていると、ちょっとしたシンプルなメニューを呼び出して処理を選択したいということがよくあります。
そしてそれがあまり頻繁には使わない機能だったり、見栄えをスッキリさせたいという場合、使わないときはメニューを非表示にしておきたいですよね。
でもエアタップを他のアクションに割り当ててしまっている場合、HoloLensにはマウスの右クリックに相当するジェスチャーがないのでどのようにしてメニューを呼び出すか悩んでしまいますよね。
そこでこんなUIを作ってみました。
- 空間を数秒ホールドするとフローティングメニュー(AppBar)が現れる
- メニューは視線を移動しても視野内に追従して付いてくる
- 項目はフォーカス時、選択時に効果音を鳴らし操作感を上げる
このメニューを適用したプロジェクトはGitHubで公開しています。
エアタップはUnityちゃんの呼び出しに割り当ててあり、フローティングメニューでSpatialMapping表示有無の切り替え、BGMのミュート/ミュート解除を用意しています。
AppBarの表示、アクションの設定
メニューはAppBarを利用します。
AppBarを利用することで簡単にボタンが並んだメニューを作ることができます。
ボタンのアクションはInteractiveReceiverにリンクさせることで一つのコンポーネントで集中管理することができます。
- SquareButtonPrefab
指定先のPrefabでボタンの表示などをカスタマイズできます。今回はアイコンなし、テキストを中央表示にしたPrefabを指定しています - Use XXX
デフォルトで用意されている Remove Adjust Hide ボタンを非表示にします - Display Type
独立型のAppBarとします
- Type
Customとします - Name ボタン名、アクションを実際に処理するInteractiveReceiverのコードでアクション種別の識別に利用される識別子です
- Text
ボタンに表示されるテキスト - EventTarget
アクションを実際に処理するInteractiveReceiverの派生コンポーネントを指定します
AppBarの自動移動の設定
視線を移動した際にメニューが視野内に自動追従してくる機能はSolver関連のRadial Viewコンポーネントを利用することで簡単に実現できます。
カスタマイズポイントとしては、追従時に視野のどの程度の範囲内に留まるかの指定があります。
デフォルトではHoloLensの場合ほとんど視野に入ってこないので小さめにしています。
ただImmersive Headsetで見た場合はかなり狭い範囲に収まってしまって逆に不自然だったので双方で不自然でないギリギリの設定をしました。
ここはもう少し丁寧に作りこむ場合、プラットフォームによって設定値を変えた方が良いと思います。
- Max View Degrees
オブジェクトが留まる視野内の範囲を度数で指定します - Aspect V
オブジェクトが留まる範囲の縦横比。横1.0に対する縦の比率。
InteractiveReveiverでのアクション処理
ボタンのタップ処理は指定したInteractiveReceiverに通知され、ボタンのNameを識別子とすることで下記のように振り分け処理を行えます。
protected override void InputClicked(GameObject obj, InputClickedEventData eventData) { switch (obj.name) { case "Close": this.appBar.gameObject.SetActive(false); break; case "Mapping": this.IsDrawSpatialMappingWireframe = !this.IsDrawSpatialMappingWireframe; this.spatialMappingManager.SurfaceMaterial = this.IsDrawSpatialMappingWireframe ? this.spatialMappingMaterialWireframe : this.spatialMappingMaterialOcclusion; break; case "BGM": this.audioSource.volume = (audioSource.volume > 0f) ? 0f : 0.5f; break; default: base.InputClicked(obj, eventData); break; } }
Profileによるボタンの体裁や効果音の変更
Profileを編集することで一括してUIの体裁などを変更することができます。
MRTKにデフォルトで用意されているProfileを変更してしまうと、MRTKのアップデートなどでファイルを上書きした際に設定が戻ってしまうので、コピーを作成しそこに独自の設定を行うことをお勧めします。
また、下記画像で示しているProfileを参照しているHolographicButton Prefab も同様に直接変更せずコピーを作成して編集することをお勧めします。
ここでは独自のProfileを参照することと、アイコン表示をdisableとしています。
ボタンの体裁は AppButtonTextProfile にてテキストを中央揃えとしました。
効果音は AppButtonSoundsProfile にてクリック時の音とフォーカス移動時の音を独自のものに変更しています。
ホールドでのメニュー呼び出し
最後に空間ホールドでのメニュー呼び出しです。
これは、InputManagerにGlobalListnerにハンドラを追加することで実現します。
HoloLensUnityChan/GlobalInputHandler.cs at master · SystemFriend/HoloLensUnityChan · GitHub
public class GlobalInputHandler : MonoBehaviour, IHoldHandler, IInputClickHandler { void Start () { InputManager.Instance.AddGlobalListener(this.gameObject); } void IHoldHandler.OnHoldCompleted(HoldEventData eventData) { this.controller.appBar.gameObject.SetActive(true); }
まとめ
以上で簡単に視野追従型フローティングメニューをアプリに組み込むことができます。
ちょまどさんの筋肉ボイスを動かしてみた
Google Home Miniを貸していただく機会があったので、Microsoftエヴァンジェリストの千代田まどかさんが公開している「筋肉ボイス」を動かしてみました!
#Google Home で野太い声になるアプリ「筋肉ボイス」を作った!筋肉はいいぞ#ActionsOnGoogle 対応した!
— ちょまど@エンジニア兼マンガ家 (@chomado) 2018年3月16日
これで声から起動できる!
Microsoft のサーバーレスのサービス「#AzureFunctions」使って、
言語は C# を使ってプログラミングした!
コードは全部上げたよ!https://t.co/K5zJCJlUyt
ー pic.twitter.com/FssZ6UDw0M
ちょまどさん作成のオリジナルソースに少し手を加えたソース一式はGitHubに公開しています。
ソース以外のAzure FunctionsやActions on Googleの設定を残しておきます。 両方ともほとんどはじめて触ったのでもっと良いやり方があれば教えください。
Azure Functions の設定
Azureへのデプロイ
ソースコードのデプロイはVisual Studioのソリューションエクスプローラーでプロジェクトの右クリックメニュー、「発行...」で行いました。
初めAzure Portalで手動でApp Service等を作ってデプロイしてみたのですが変なエラーが出て動かなかったのですが、ウィザードにおかませしたところ一発でした(笑)
アプリケーション設定
Azure PortalでAzure Functionアプリの「アプリケーション設定」→「アプリケーション設定」にVoiceTextのAPIキーを設定します。
関数URLの控え
Azure Portal で「GoogleHome」関数の「関数のURLの取得」からURLを控えておきます。これはGoogle側に後ほど設定します。
ストレージのアクセス許可の変更
デプロイ時に自動作成されたストレージアカウントのBLOB内にmp3ファイルを格納するコンテナを作成し、アクセス許可を与えておきます。
Actions on Googleの設定
Actions on Googleはほんと初めて触ってなんかできたくらいの感じなのですが、一応設定をぺたぺた張っておきます。
言語を日本語に
起動フレーズを設定
Actionの設定はデフォルトのままで
DialogFlowではデフォルトIntentにfollow-upとしてfallbackのIntentを追加
Contextの設定とWebhookを有効に
Webhookの設定にAzure Functionsの関数URLを設定
うごいた
なにこれめっちゃたのしい
.@chomado さんの筋肉ボイス動いたwhttps://t.co/MnhNwZtT00 pic.twitter.com/Oc56Cypu3Q
— Satoshi Maemoto (@peugeot106s16) 2018年5月10日
うんこボタンとHoloLensを繋げてみた <後編>
うんこボタンはこの度Makuakeでのクラウドファンディングに成功したインターネットボタンです。 前編ではこのうんこボタンに独自のプログラムを送り込むところまでを扱いました。
satoshi-maemoto.hatenablog.com
ソースコードはこちらで公開しています。
うんこサーバーの構築
システム構成図におけるサーバーサイドの構築を始めましょう。
サーバーサイドはASP.Net Core 2.0をフレームワークとし、RESTのAPIとWebSocketを実装します。 VisualStudio2017のプロジェクトの作成では[Visual C#]-[Cloud]-[ASP.NET Core Web アプリケーション]をテンプレートとして選択します。
次のダイアログでは [API] を選択し、REST API用コントローラーを備えたプロジェクトを作成します。
さらに、WebSocketを備えたサービスにしてゆきます。
基本的にはこちらの記事とサンプルを参考にさせて頂きました。
幾つか変更点に言及してゆきしょう。
コントローラーで使いたいSingletonオブジェクトを追加
アプリ全体で共有したいシングルトンなオブジェクトはStartup時のConfigureServices()で追加できます。 うんこぼたんからのRESTリクエストをコントローラーが受けた際、WebSocketのサーバークラス[WSServer]にアクセスし、接続中全クライアントへの一斉通知をさせるためにこのようにしています。
UnkoButton/Startup.cs at master · satoshi-maemoto/UnkoButton · GitHub
public void ConfigureServices(IServiceCollection services) { services.AddMvc(); services.AddSingleton<WSServer>(); }
するとコントローラーのコンストラクタでオブジェクトを受け取ることができます。
UnkoButton/UnkosController.cs at master · satoshi-maemoto/UnkoButton · GitHub
public UnkosController(WSServer wsServer) { this.WSServer = wsServer; }
このUnkosCotrollerのPost()メソッドでWSServerに全クライアントへのdidUnko通知を依頼しています。
public void Post([FromBody]string value) { this.WSServer.BroadcastMessage(new Message() { MessageType = "DidUnko", ClientName = "UnkoButton", What = "Unko", }); }
WebSocketクライアント間のオブザーバーパターン
元々参考にさせて頂いたチャットサンプルがWebSocketクライアント間のイベント通知をReactを使ったオブザーバーパターンで行うようになっており、とてもスマートな感じになっています。
はじめはうんこボタンもWebSocketクライアントの一つとして、双方向リアルタイムにやりとりをしたいと思ったのですが、Wi-Fi接続やコネクションを保持したままスリープしないのは電池消費につながるのでワンショットのREST方式としました。
なので、オブザーバーパターンは現状それほど生きていないのですが、今後HoloLens複数台やその他のリッチなデバイスがうんこクライアントになった際には双方向にうんこを投げ合うなどの楽しいことができそうです。
HoloLens側でうんこを投げたらうんこボタンが光る、というのをまたそのうちやりたいです。
なお、今のコードではうんこを送ると他クライアントすべてにブロードキャストしつつ自分にもエコーバックでうんこが投げ返されてくるクソ仕様となっています(笑)
HoloLensアプリ
HoloLensアプリはうんこサーバーにWebSocket接続を行い、didUnko通知を待ち受けます。 UWPでWebSocketを利用するため、下記のようなクラスを作成しました。 Unityへの依存はないので、どのようなUWPアプリでも利用できます。
UnkoButton/UnkoServiceClient.cs at master · satoshi-maemoto/UnkoButton · GitHub
using System; #if NETFX_CORE using System.Threading.Tasks; using Windows.Networking.Sockets; using Windows.Storage.Streams; #endif namespace UnkoService { public class UnkoServiceClient { public delegate void MessageReceivedEventHandler(object sender, string message); public event MessageReceivedEventHandler OnMessageReceived; #if NETFX_CORE private MessageWebSocket WebSocket { get; set; } public void Initialize() { this.WebSocket = new MessageWebSocket(); this.WebSocket.Control.MessageType = SocketMessageType.Utf8; this.WebSocket.MessageReceived += this.MessageReceived; this.WebSocket.Closed += this.Closed; } public void Connect(Uri uri, string connectedMessage) { Task.Run(async () => { await Task.Run(async () => { await this.WebSocket.ConnectAsync(uri); await this.SendMessage(connectedMessage); }); }); } public async Task SendMessage(string message) { await this.SendMessage(this.WebSocket, message); } private async Task SendMessage(MessageWebSocket webSocket, string message) { var messageWriter = new DataWriter(webSocket.OutputStream); messageWriter.WriteString(message); await messageWriter.StoreAsync(); } private void MessageReceived(MessageWebSocket sender, MessageWebSocketMessageReceivedEventArgs args) { var messageReader = args.GetDataReader(); messageReader.UnicodeEncoding = Windows.Storage.Streams.UnicodeEncoding.Utf8; var messageString = messageReader.ReadString(messageReader.UnconsumedBufferLength); this.OnMessageReceived?.Invoke(this, messageString); } private void Closed(IWebSocket sender, WebSocketClosedEventArgs args) { } #endif } }
リポジトリには入れていませんが、このUnkoServiceClientをUnityのスクリプトからは下記のように利用します。
private UnkoServiceClient unkoServiceClient; void Start() { #if NETFX_CORE var context = SynchronizationContext.Current; this.unkoServiceClient = new UnkoServiceClient(); this.unkoServiceClient.OnMessageReceived += (s, m) => { context.Post((state) => { this.GenerateUnko(); }, null); }; this.unkoServiceClient.Initialize(); this.unkoServiceClient.Connect(new Uri("ws://xxxx.azurewebsites.net/ws"), JsonConvert.SerializeObject(new JoinMessage() { ClientName = "HoloLens", MessageType = "JoinMessage" })); #endif }
まとめ
以上でうんこボタンを押すとうんこが出せるようになりました。 なんかうんこうんこ書きすぎて気分が悪くなってきましたww
IoTデバイスとHoloLensを連携させる際の参考にしていただければ幸いです。
うんこボタンとHoloLensを繋げてみた <前編>
うんこボタンとは
うんこボタン。なんというパワー感あふれるネーミング!
うんこボタンはこの度Makuakeでのクラウドファンディングに成功したインターネットボタンです。
赤ちゃんの排泄情報をボタンを押すだけで記録してくれるデバイス&サービス。
我が家もこの前まで排泄のたびに手帳に手書きで記録をしていて、地味ながら結構大変な作業でした。
でもあとで見返してみるととても良い思い出なんですよね。
そして今回ぼくは「ハッカーコース」を支援し、無事にキットが到着しました!
このキットにはうんこボタン自体の機能は入っていません。
でも普通にESP8266+物理2ボタンのデバイスとしてArduino IDEで自在にプログラミングを行うことができるのです!
なんでもつくれるのはワクワクしますね!
今回作るもの
うんこボタンなので押すとうんこがでるものを作ってみます。
リアルに出ては大変なので、ホログラムのうんこをHoloLensから発射するのがゴールです。
うんちの3Dモデルはうんちマンさんから別プロジェクトでご提供いただいたものを使わせて頂いています。
システム構成はこんな感じ。
- Azure上に中継用のうんこサービスを構築する
- うんこサービスにはうんこボタンからのリクエストを受け付けるRESTインターフェイス、HoloLensに通知を送るためのWebSocketインターフェイスを設ける
- HoloLensはうんこサービスとWebSocketのコネクションを張る
- うんこボタンはボタン押下時にうんこサービスのPOSTを呼び出す(PUTにするか悩んだ)
- うんこサービスは接続中のすべてのWebSocketクライアントにdidUnko命令を通知する
- HoloLensはうんこのホログラムを生成する
ソースコードはこちらに(HoloLens側は全Assetを公開できないため、WebSocketのクライアントスクリプトのみ)
うんこボタン側スケッチ
UnkoButton/unko_btn_trigger.ino at master · satoshi-maemoto/UnkoButton · GitHub
#include <ArduinoJson.h> #include <ESP8266HTTPClient.h> #include <ESP8266WiFi.h> #include <ESP8266WiFiMulti.h> // Wi-Fi接続パラメータ const char* ssid = "xxxx"; const char* password = "xxxx"; const char* uri = "http://xxxx.azurewebsites.net/api/unkos"; // IOピン宣言 #define LEFT_BTN 14 #define RIGHT_BTN 12 #define ACTIVE 16 #define RBOOTED 13 #define LBOOTED 5 #define LEFT_RED_LED 15 #define RIGHT_RED_LED 4 #define LEFT_GREEN_LED 2 #define RIGHT_GREEN_LED 0 bool lBooted; bool rBooted; int batteryLevel; HTTPClient http; ESP8266WiFiMulti wifiMulti; DynamicJsonBuffer jsonBuffer; // 定形初期化処理(理解するまでは変更しないで) void boot() { Serial.begin(74800); // pin setup pinMode(LBOOTED, INPUT_PULLUP); pinMode(RBOOTED, INPUT_PULLUP); pinMode(LEFT_RED_LED, OUTPUT); pinMode(RIGHT_RED_LED, OUTPUT); pinMode(LEFT_BTN, INPUT); pinMode(RIGHT_BTN, INPUT); // スリープから復帰するのに押されてたボタン状態を取得 lBooted = digitalRead(LBOOTED) == LOW; rBooted = digitalRead(RBOOTED) == LOW; // アクティブモードへ移行 digitalWrite(ACTIVE, LOW); pinMode(ACTIVE, OUTPUT); // ACTIVE==LOWのあとでしか緑LEDを使えない pinMode(LEFT_GREEN_LED, OUTPUT); pinMode(RIGHT_GREEN_LED, OUTPUT); // バッテリーレベル取得(アクティブモードでないとダメ) batteryLevel = analogRead(A0) * 3000 / 1024; // [mV] // 電池が消耗していたり、通電のみだった場合はすぐにスリープする if (batteryLevel < 2300 || !lBooted && !rBooted) { ESP.deepSleep(0); delay(500); } } void setup() { boot(); // 必ずsetup関数の最初に実行 if (lBooted) { digitalWrite(LEFT_GREEN_LED, HIGH); // left green on } if (rBooted) { digitalWrite(RIGHT_GREEN_LED, HIGH); // right green on } Serial.println(); Serial.println("left: " + String(lBooted) + " right: " + String(rBooted)); // Wi-Fi connect WiFi.mode(WIFI_STA); wifiMulti.addAP(ssid, password); } void loop() { if (wifiMulti.run() != WL_CONNECTED) { delay(500); return; } String message; if (lBooted && !rBooted) { message = "{ \"message\":\"うんこボタンの左を押した\" }"; } else if (!lBooted && rBooted) { message = "{ \"message\":\"うんこボタンの右を押した\" }"; } else if (lBooted && rBooted) { message = "{ \"message\":\"うんこボタンの両方を押した\" }"; } if (message.length() == 0) { return; } Serial.println(uri); http.begin(uri); http.addHeader("Content-Type", "application/json"); int code = http.POST(message); String content = http.getString(); http.end(); Serial.println(String(code)); Serial.println(content); JsonObject& resp = jsonBuffer.parseObject(content); // タスクが終了したら必ずディープスリープすること。 ESP.deepSleep(0); delay(500); }
- ボタンを押すとデバイスはスリープ復帰する
- 押されたボタン側のLEDを点灯する
- Wi-Fiへ接続する
- うんこサービスへメッセージをPOSTする 一応どのボタンを押したかを通知している
- POSTが終了したらデバイスはスリープする
ボタン押下ごとにWi-Fi接続し、スリープするシンプルな処理なので通知に時間がかかります。
一定時間スリープせずにWi-Fi接続を保持し、押下待ち受けするつくりにすれば応答性が上がりますが、今回はふんばる感があるのでこれでいいかなと思っています(笑)
うんこボタンへのデプロイ
ボードのピンの解説、PCとの接続について書かれているこちらをよく読んで進めれば問題なくできると思います。
https://unkobtn.com/news/press/article/5/
PCのUSBと接続するにはUSB->シリアル変換アダプタが必要なので用意しましょう。
そして規定通りに結線します。
ファームの書き換えモードに入るにはJP2をショートさせ、電池の抜き差しをしてリセットすればOKです。
Arduino IDEはWindowsストアから最新版をインストールしました。
あとはボードに書き込むだけです。
前編まとめ
うんこボタンの紹介とうんこボタン側スケッチの用意についてお伝えしました。
次回、うんこサーバー側とHoloLens側の実装についてお伝えします。
World Tour Report - バルセロナ MWC2018へ行ってきました 02
ワールドツアー・バルセロナ編。続きを書きます。
4YFN スタートアップイベント
Mobile World Congressは豪華でガチエンタープライズな本会場と、少し離れた4YFN会場で開催されていました。4YFNはスタートアップが集まるイベントでした。
今回は(多分後になると時間なくて行かないような気がしたので)別会場から攻めてみることに。
4YFNには日本からもスタートアップやってきており、"JAPAN Rising Startups"ブース群がありました!
久しぶりに日本語で会話ができ、息継ぎができた(笑)!
でもMWC全体を通し、ソニーや富士通といった大企業を除くと日本のブースはとても少ないです。多分ここだけ。
深圳や韓国の企業ブースは本会場にもたくさんあったので勢いの違いを感じさせられました。
Japanese foodは散見されましたけどね(笑)
でもきっとこれは地雷なので日本人は食べないほうがいいかも。
その他色々ありましたが、VIVEを使ったVRものとかもちらほらとありました。
4YFN会場は活気が溢れていて気楽に楽しめる雰囲気で楽しかったです!
さて、4YFN会場のすぐ近くにはオリンピック会場にもなったムンジュイックの丘やカタルーニャ美術館がありました。
小雪の舞う中さくっと寄ってからまたシャトルバスで本会場へ。
本会場 HALL5-8
本会場、全8ホールのうち4ホールは比較的小さな企業や世界中の国々が自国のブースを出しているような感じでした。
HALL1-4が一言さんお断りな感じで中にも入ることさえできないブースが多かったのに比べると気楽で楽しかったですw
幾つか面白かったものを動画で挙げてゆきます。
SMART ROPE
縄跳びのジャンプ回数をカウントし、空間に表示してくれる。
塗るだけでスマホ画面が強くなる魔法の塗り薬
恐らく SAMSUNG Odyssey っぽいWin-MRのゲームコンテンツ
HTC VIVEのワイヤレス
大型ルームでのHTC VIVE クロマキー
HTC VIVI PRO
回転するファンの上にホログラム
サーモカメラ
と、この辺りで体力切れ(笑)
東京ビックサイトが何個もあるわけで一日ではさらっと回るのも無理。
また次回!