M5Stackの方は放置でいいので他のソフトウェアを書く気分になったから初めてAndroidアプリを作ってみることにする。
それそのままの名前のリポジトリをここにおいておく。
ほしい人は持っていって。
https://github.com/ak1211/MyFirstApplication
動機
Androidアプリの作り方を学ぶのが目的だから, お題そのものがむつかしいモノは避けて以前に作ったことのある赤外線リモコン信号を記録して送信できるものを作ってみる。
自分のAndroidスマホには赤外線入出力機能がないので, 外部にハードウエアを用意してソフトとハードで互いに協調動作させるとしよう。
赤外線リモコン信号送受信用ハードウエア
「ダイキンエアコンのリモコン信号を解析する」と
「M5Stack Core2とVSCode + PlatformIOとでM5Stackプログラミングを始めてみた。」
との時に使った
また登場してもらう。
ブレッドボードを出してくるのが面倒だったので
Seeeduino XIAO Groveシールド
を使うことにする。
接続はこうした。
Android Studio + Kotlin
最近はKotlin推しがつよいので何もしなくてもインストールしてすぐにKotlinでアプリを作れる。
https://developer.android.com/kotlin
Kotlin で Android アプリを開発する
Kotlin では、より質の高い Android アプリをより速く記述できます。Kotlin は最新の静的型付けのプログラミング言語で、プロフェッショナルな Android デベロッパーの 60% 以上に使用されており、生産性、デベロッパーの満足度、コードの安全性の向上に貢献しています。
Androidアプリのお手本
最初はお手本をマネするのが定石なので Android Views and Widgets Samples Repository
リポジトリの中からそれっぽいのを探した結果,
Android RecyclerView Sample (Kotlin) をマネする。
スマホを開発者モードにして(連続してタップするやつ)
開発者向けオプションからワイヤレスデバッグをONにして
Android Studioのターミナルから
1
| $ adb pair 192.168.1.21:45351
|
ペア設定コードを入力
1
| $ adb connect 192.168.1.21:45351
|
こんな感じでワイヤレスデバッグする。
お手本が動いたから, さわりながら休憩。
AndroidでUSBシリアル通信
AndroidスマホからSeeeduino XIAOの間はお互いのUSB-C端子をUSBケーブルでつないでいる。
Android OSとusb-serial-for-androidライブラリの支援を受けて, USB接続の上でお互いに「メッセージ」を通す。
https://developer.android.com/guide/topics/connectivity/usb?hl=ja
USB ホストとアクセサリの概要
Android は、さまざまな USB 周辺機器と Android USB アクセサリ(Android アクセサリ プロトコルを実装したハードウェア)を、USB アクセサリと USB ホストの 2 つのモードでサポートしています。USB アクセサリ モードでは、外部 USB ハードウェアが USB ホストとして動作します。アクセサリの例としては、ロボット コントローラ、ドッキング ステーション、診断機器や楽器、キオスク、カードリーダーなどがあります。これにより、ホスト機能を持たない Android 搭載デバイスが USB ハードウェアと情報をやり取りできるようになります。Android USB アクセサリは、Android 搭載デバイスと連携するように設計されている必要があり、また Android アクセサリ通信プロトコルに準拠している必要があります。USB ホストモードでは、Android デバイスがホストとして動作します。デバイスの例としては、デジタルカメラ、キーボード、マウス、ゲーム コントローラなどがあります。幅広い用途や環境向けに設計された USB デバイスが、そうしたデバイスと正しく通信できる Android アプリと情報をやり取りすることが可能です。
図 1 は 2 つのモードの違いを示しています。Android 搭載デバイスをホストモードにすると、Android デバイスは USB ホストとして動作し、バスに電源を供給します。Android 搭載デバイスを USB アクセサリ モードにすると、接続された USB ハードウェア(この場合は Android USB アクセサリ)がホストとして動作し、バスに電源を供給します。
AndroidスマホからSeeeduino XIAOに電源を供給するんで 「ホストモード」になる。
https://developer.android.com/guide/topics/connectivity/usb/host?hl=ja
USB ホストの概要
Android 搭載デバイスを USB ホストモードにすると、Android 搭載デバイスは USB ホストとして動作し、バスに電源を供給して、接続されている USB デバイスを列挙します。USB ホストモードは Android 3.1 以降でサポートされています。
usb-serial-for-androidライブラリ
Android用USBシリアル通信ライブラリを使わせてもらう。
https://github.com/mik3y/usb-serial-for-android
READMEの指示通り
USBView
Vendor ID/Device ID が突然出て来たけど, WindowsにあるUSBViewというアプリケーションで得たもの。
https://docs.microsoft.com/ja-jp/windows-hardware/drivers/debugger/usbview
ユニバーサル シリアル バス ビューアー (USBView) または USBView.exe は、ご使用のコンピューター上のすべての USB コントローラーと接続されている USB デバイスを参照するために使用できる Windows グラフィカル UI アプリです。 USBView は、すべてのバージョンの Windows で動作します。
USBViewを使って (Circuit Pythonが動作している)Seeeduino XIAO を参照するとこんな情報を得られる。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
| [Port3] : USB Composite Device
Is Port User Connectable: yes
Is Port Debug Capable: no
Companion Port Number: 7
Companion Hub Symbolic Link Name: USB#ROOT_HUB30#5&1b0850fa&0&0#{f18a0e88-c30c-11d0-8815-00a0c906bed8}
Protocols Supported:
USB 1.1: yes
USB 2.0: yes
USB 3.0: no
Device Power State: PowerDeviceD0
---===>Device Information<===---
English product name: "Seeeduino XIAO"
ConnectionStatus:
Current Config Value: 0x01 -> Device Bus Speed: Full (is not SuperSpeed or higher capable)
Device Address: 0x05
Open Pipes: 9
===>Device Descriptor<===
bLength: 0x12
bDescriptorType: 0x01
bcdUSB: 0x0200
bDeviceClass: 0x00
*!*ERROR: device class should be Multi-interface Function 0xEF
When IAD descriptor is used
bDeviceSubClass: 0x00
*!*ERROR: device SubClass should be USB Common Sub Class 2
When IAD descriptor is used
bDeviceProtocol: 0x00
*!*ERROR: device Protocol should be USB IAD Protocol 1
When IAD descriptor is used
bMaxPacketSize0: 0x40 = (64) Bytes
idVendor: 0x2886 = Seeed Technology Co., Ltd.
idProduct: 0x002F
bcdDevice: 0x0100
iManufacturer: 0x02
English (United States) "Seeed"
iProduct: 0x03
English (United States) "Seeeduino XIAO"
iSerialNumber: 0x01
English (United States) "E502920D45B435050213E233A22280FF"
bNumConfigurations: 0x01
---===>Open Pipes<===---
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x81 -> Direction: IN - EndpointID: 1
bmAttributes: 0x03 -> Interrupt Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x10
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x84 -> Direction: IN - EndpointID: 4
bmAttributes: 0x03 -> Interrupt Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x08
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x04 -> Direction: OUT - EndpointID: 4
bmAttributes: 0x03 -> Interrupt Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x08
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x02 -> Direction: OUT - EndpointID: 2
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x82 -> Direction: IN - EndpointID: 2
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x83 -> Direction: IN - EndpointID: 3
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x03 -> Direction: OUT - EndpointID: 3
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x05 -> Direction: OUT - EndpointID: 5
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x85 -> Direction: IN - EndpointID: 5
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
---===>Full Configuration Descriptor<===---
===>Configuration Descriptor<===
bLength: 0x09
bDescriptorType: 0x02
wTotalLength: 0x00DA -> Validated
bNumInterfaces: 0x06
bConfigurationValue: 0x01
iConfiguration: 0x00
bmAttributes: 0x80 -> Bus Powered
MaxPower: 0x32 = 100 mA
===>IAD Descriptor<===
bLength: 0x08
bDescriptorType: 0x0B
bFirstInterface: 0x00
bInterfaceCount: 0x02
bFunctionClass: 0x02 -> This is Communications (CDC Control) USB Device Interface Class
bFunctionSubClass: 0x02
bFunctionProtocol: 0x00
iFunction: 0x00
===>Interface Descriptor<===
bLength: 0x09
bDescriptorType: 0x04
bInterfaceNumber: 0x00
bAlternateSetting: 0x00
bNumEndpoints: 0x01
bInterfaceClass: 0x02 -> This is Communications (CDC Control) USB Device Interface Class
bInterfaceSubClass: 0x02
bInterfaceProtocol: 0x00
iInterface: 0x04
English (United States) "CircuitPython CDC control"
-> This is a Communications (CDC Control) USB Device Interface Class
===>Descriptor Hex Dump<===
bLength: 0x05
bDescriptorType: 0x24
05 24 00 10 01
-> This is a Communications (CDC Control) USB Device Interface Class
===>Descriptor Hex Dump<===
bLength: 0x05
bDescriptorType: 0x24
05 24 01 01 01
-> This is a Communications (CDC Control) USB Device Interface Class
===>Descriptor Hex Dump<===
bLength: 0x04
bDescriptorType: 0x24
04 24 02 02
-> This is a Communications (CDC Control) USB Device Interface Class
===>Descriptor Hex Dump<===
bLength: 0x05
bDescriptorType: 0x24
05 24 06 00 01
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x81 -> Direction: IN - EndpointID: 1
bmAttributes: 0x03 -> Interrupt Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x10
===>Interface Descriptor<===
bLength: 0x09
bDescriptorType: 0x04
bInterfaceNumber: 0x01
bAlternateSetting: 0x00
bNumEndpoints: 0x02
bInterfaceClass: 0x0A -> This is a CDC Data USB Device Interface Class
bInterfaceSubClass: 0x00
bInterfaceProtocol: 0x00
iInterface: 0x05
English (United States) "CircuitPython CDC data"
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x02 -> Direction: OUT - EndpointID: 2
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x82 -> Direction: IN - EndpointID: 2
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
===>Interface Descriptor<===
bLength: 0x09
bDescriptorType: 0x04
bInterfaceNumber: 0x02
bAlternateSetting: 0x00
bNumEndpoints: 0x02
bInterfaceClass: 0x08 -> This is a Mass Storage USB Device Interface Class
bInterfaceSubClass: 0x06
bInterfaceProtocol: 0x50
iInterface: 0x06
English (United States) "CircuitPython Mass Storage"
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x83 -> Direction: IN - EndpointID: 3
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x03 -> Direction: OUT - EndpointID: 3
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
===>Interface Descriptor<===
bLength: 0x09
bDescriptorType: 0x04
bInterfaceNumber: 0x03
bAlternateSetting: 0x00
bNumEndpoints: 0x02
bInterfaceClass: 0x03 -> HID Interface Class
bInterfaceSubClass: 0x00
bInterfaceProtocol: 0x00
iInterface: 0x07
English (United States) "CircuitPython HID"
===>HID Descriptor<===
bLength: 0x09
bDescriptorType: 0x21
bcdHID: 0x0111
bCountryCode: 0x00
bNumDescriptors: 0x01
bDescriptorType: 0x22 (Report Descriptor)
wDescriptorLength: 0x00C3
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x84 -> Direction: IN - EndpointID: 4
bmAttributes: 0x03 -> Interrupt Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x08
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x04 -> Direction: OUT - EndpointID: 4
bmAttributes: 0x03 -> Interrupt Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x08
===>Interface Descriptor<===
bLength: 0x09
bDescriptorType: 0x04
bInterfaceNumber: 0x04
bAlternateSetting: 0x00
bNumEndpoints: 0x00
bInterfaceClass: 0x01 -> Audio Interface Class
bInterfaceSubClass: 0x01 -> Audio Control Interface SubClass
bInterfaceProtocol: 0x00
iInterface: 0x0B
English (United States) "CircuitPython Audio"
===>Audio Control Interface Header Descriptor<===
bLength: 0x09
bDescriptorType: 0x24 (CS_INTERFACE)
bDescriptorSubtype: 0x01 (HEADER)
bcdADC: 0x0100
wTotalLength: 0x0009
bInCollection: 0x01
baInterfaceNr[1]: 0x05
===>Interface Descriptor<===
bLength: 0x09
bDescriptorType: 0x04
bInterfaceNumber: 0x05
bAlternateSetting: 0x00
bNumEndpoints: 0x02
bInterfaceClass: 0x01 -> Audio Interface Class
bInterfaceSubClass: 0x03 -> MIDI Streaming Interface SubClass
bInterfaceProtocol: 0x00
iInterface: 0x0A
English (United States) "CircuitPython MIDI"
===>Descriptor Hex Dump<===
bLength: 0x07
bDescriptorType: 0x24
07 24 01 00 01 25 00
===>Descriptor Hex Dump<===
bLength: 0x06
bDescriptorType: 0x24
06 24 02 01 01 08
===>Descriptor Hex Dump<===
bLength: 0x06
bDescriptorType: 0x24
06 24 02 02 02 00
===>Descriptor Hex Dump<===
bLength: 0x09
bDescriptorType: 0x24
09 24 03 01 03 01 02 01 09
===>Descriptor Hex Dump<===
bLength: 0x09
bDescriptorType: 0x24
09 24 03 02 04 01 01 01 00
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x05 -> Direction: OUT - EndpointID: 5
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
===>Descriptor Hex Dump<===
bLength: 0x05
bDescriptorType: 0x25
05 25 01 01 01
===>Endpoint Descriptor<===
bLength: 0x07
bDescriptorType: 0x05
bEndpointAddress: 0x85 -> Direction: IN - EndpointID: 5
bmAttributes: 0x02 -> Bulk Transfer Type
wMaxPacketSize: 0x0040 = 0x40 bytes
bInterval: 0x00
===>Descriptor Hex Dump<===
bLength: 0x05
bDescriptorType: 0x25
05 25 01 01 03
|
これこれ
1
2
3
| ===>Device Descriptor<===
idVendor: 0x2886 = Seeed Technology Co., Ltd.
idProduct: 0x002F
|
参考
USB-シリアル変換器のデバイスファイル名を固定する
Communications Device Class
ライブラリにまかせる領域でも一応確認しておく。
1
| [Port3] : USB Composite Device
|
(Circuit Pythonが動作している) Seeeduino XIAOはOSからUSB複合デバイスとして見えている。そのうちのこれ。
This is a Communications (CDC Control) USB Device Interface Class
Communications Device Class (CDCクラス)インターフェースと接続する。
1
2
3
4
5
6
7
8
9
10
11
12
| ===>Interface Descriptor<===
bLength: 0x09
bDescriptorType: 0x04
bInterfaceNumber: 0x00
bAlternateSetting: 0x00
bNumEndpoints: 0x01
bInterfaceClass: 0x02 -> This is Communications (CDC Control) USB Device Interface Class
bInterfaceSubClass: 0x02
bInterfaceProtocol: 0x00
iInterface: 0x04
English (United States) "CircuitPython CDC control"
-> This is a Communications (CDC Control) USB Device Interface Class
|
1
2
3
4
5
6
7
8
9
10
11
| ===>Interface Descriptor<===
bLength: 0x09
bDescriptorType: 0x04
bInterfaceNumber: 0x01
bAlternateSetting: 0x00
bNumEndpoints: 0x02
bInterfaceClass: 0x0A -> This is a CDC Data USB Device Interface Class
bInterfaceSubClass: 0x00
bInterfaceProtocol: 0x00
iInterface: 0x05
English (United States) "CircuitPython CDC data"
|
確認のためusb.orgから資料をダウンロードしてきた。
bInterfaceClass: 0x02
で bInterfaceSubClass: 0x02
であるため
Communications Interface Class のサブクラスであるAbstract Control Modelサブクラスの存在を確認できる。
bInterfaceClass: 0x0A
であるので Data Interface Class の存在も確認できる。
参考
USB CDCについて
このUSB接続は PSTN Device Model のサブクラスである ACMプロトコルを通じてアプリケーションからは(アナログ)モデムとして見えている。
つまり仮想COMポート。
モデムを接続してホストコンピュータに電話をかけて通信する端末みたいなもん。
以前にWindowsのRLoginと Seeeduino XIAO上のCurcuit Pythonとの間ですでに疎通確認をすましているので, あとはAndroidスマホ側を実装すれば通信できる。
リモコン信号解析器
赤外線リモコン信号の解析アプリケーション
で作った解析器をPureScriptからkotlinに持ってくる。
Maybe(Optional), Either, map, fold, foldMap, traverse … が必要なので
ライブラリを探したらあったので使わしてもらいました。
なぜかREADME通りに入れられなかったので, こんなふうに1.0.0-SNAPSHOTを入れた。
https://github.com/ak1211/MyFirstApplication/blob/main/build.gradle
Kotlinで書く
もともとはこうで
https://github.com/ak1211/pi_hvac/blob/master/frontend/src/InfraredRemoteCode/Internal.purs
Kotlinではこんな風になった。
https://github.com/ak1211/MyFirstApplication/blob/main/infraredremote/src/main/java/com/example/infraredremote/InfraredRemote.kt
ついでにテストも付けておいた。
https://github.com/ak1211/MyFirstApplication/blob/main/infraredremote/src/test/java/DecodeTest.kt
赤外線リモコンコードは
なので, ありがたく頂戴した。Curcuit Python プログラム
Androidアプリケーションの都合でちょっと改造してmain.pyをこうした。
使ってみようAndroidアプリ
ここまでで Androidスマホ から USB-CDC ACMプロトコルで Seeeduino XIAO までつながった。
3144, 1654 (単位はマイクロ秒) これは 8T, 4T なので T=413.5μs (425μs typ.)
AEHAだとわかる。
参考
赤外線リモコンの通信フォーマット
何もしなくてもリモコン信号を受信してリストに追加するし, リストをタップすることで赤外線リモコン信号を送信する。
この動作は要約すると
Androidスマホ <—> Seeeeduino XIAO <—> エアコンとか
この3者で動いているマイクロコントローラとソフトウェアの間でメッセージを通している。
天井照明が反応しないのはパワー不足
天井の照明が赤外線リモコン信号に反応しない。
M5Stack用赤外線送受信ユニット は名前の通りM5Stack用なので。
https://docs.m5stack.com/en/unit/ir
5V
Seeeduino XIAO Groveシールド は
3V3 = 3.3V
5Vを100%とすると3.3Vは67%しかないわけで, 赤外線LEDの出力は物足りなくなる。
天井に近づけたら反応するけれども,
このページ「文ばっかやな」とおもったからバランスをとるために部品を集めて回路を作りましょうか。
送受信回路
パワーが物足りないのでブレッドボードを出してきた。
「赤外線リモコン信号を記録して送信する学習リモコンをラズパイゼロでつくる。」でしたことと被っているけどまあええねん。
こんなん何度やってもいいですからね。
Seeeduino XAIOのMPUであるATSAMD21G18のデータシートより
各クラスタあたり46mA流せるのでI/Oポートから5mA位引っ張っても余裕かな。
赤外線LEDは同じもの(OSI5FU5111C-40)を4個つかってNPNトランジスタにはUTC製の2SC2655を使います。
これでいいか。