Spec2入門(その6)

これはSmalltalk Advent Calendar 2019の記事です。

Spec2は、Pharo Smalltalk で採用されているUIフレームワークであるSpec の新しいバージョンです。Pharo 7.0 では Spec が使われていましたが、Pharo 8 では(ほぼ?)全てのUIが Spec2 で書き直されるようです。この記事では Spec2 を使った UI の実装方法について簡単に紹介します。

Squeak/Pharo Smalltalk では、UIの実現のために長らく Morphic を用いてきました。Morphic はシンプルで強力なフレームワークですが、複数のコンポーネントを組み合わせていくとアナーキーな実装をしがちで、リファクタリングや再利用の際に泣きたくなるような状況に陥りがちです。Spec/Spec2 はUIの構築に秩序をもたらし、コンポーネントの再利用がしやすく設計されています。

前回の記事では、Scratch 1.4風のファイル選択ダイアログに、親ディレクトリへ移動するボタンと、ディレクトリ階層がわかるドロップリストを追加しました。


前回つくったドロップリストはガワだけで機能がないので、今回はディレクトリ階層を表示したり、途中の親ディレクトリに移動できるような機能を追加します。

ドロップリストの内容を設定する

スクラッチではドロップリストをクリックすると以下のようにディレクトリ階層が表示されます。途中のディレクトリをクリックすると、そのディレクトリ内容がファイル一覧に表示されます。

まず、現在のディレクトから、ディレクトリ階層の文字列を取り出すメソッド directoryHierarchy を作成します。

directoryHierarchy
     ^ directory path segments
         withIndexCollect:
             [ :each :index | (String new: index withAll: Character space) , each ]

directory は FileReference のインスタンスなので、path で AbsolutePath オブジェクトに変換し、segments でパスを部分文字列の配列に変換します。その後、インデックスを使ってインデントの空白を作ります。

次にドロップリストにディレクトリ階層を設定するメソッド listCurrentContents: を作ります。現在のディレクトリ名が表示されるよう、インデックス位置を設定します。

listCurrentContents: aCollection
     listCurrent selectIndex: 0.
     listCurrent items: aCollection.
     listCurrent selectIndex: aCollection size

ディレクトリ階層は、ディレクトリが変更されたタイミングで設定するべきなので、directory: メソッドの中で設定するようにします。

directory: aFileReference
     directory := aFileReference.
     listEntries
         unselectAll;
         items: #();
         items: self getEntries.
     self listCurrentContents: self directoryHierarchy

ドロップリストで選択できるようにする

表示されたディレクトリ階層を選んだら、その親(系列の)ディレクトリに移動するようにしましょう。そのためには、ドロップリストが変更された時の処理を connectPresenters に追加する必要があります。

connectPresenters
     listEntries
         display: [ :m | self showEntry: m ];
         whenActivatedDo: [ :selection | self entriesChanged: selection ].
     buttonParent action: [ self changeParentDirectory ].
     listCurrent
         whenSelectedItemChangedDo: [ :selection | self currentChanged: selection ]

ここでは、ブロック引数をつけて whenSelectedItemChangedDo: メッセージを送っています。これで、ドロップリストの項目が選択されたタイミングで 、選択された項目の文字列を引数として currentChanged: メッセージが送られるようになります。

currentChanged: メソッドは以下のように定義します。

currentChanged: aString
     | dir |
     listCurrent selectedIndex = 0
         ifTrue: [ ^ self ].
     dir := directory.
     directory path segments size - listCurrent selectedIndex
         timesRepeat: [ dir := dir parent ].
     self directory: dir

ディレクトリのパスの個数から選択された位置を引いた回数だけ親ディレクトリをたどって、選ばれたディレクトリを特定しています。不格好なやり方ですが、とりあえず動きます。

ドリップリストの項目を選択すれば親系列のディレクトリに移動できるのですが、ドロップリストにリストを設定するたびに上のメッセージが送られることになるため、初期化などの際にも不用意なメッセージ送信が発生してしまいます。そこで、ドロップリストの生成時に startWithoutSelection を送ることで、これを回避します。

initializePresenters
     listEntries := self newList
         beSingleSelection;
         activateOnDoubleClick;
         items: self getEntries.
     buttonParent := self newButton
         icon: (Smalltalk ui icons iconNamed: #up).
     listCurrent := self newDropList startWithoutSelection.
     (以下略)

初期化のための調整

ドロップリストに関する最後の調整をします。 initialize 時に directory 変数を初期化していますが、これではドロップリストの内容が設定されないので、初期化のタイミングを後にずらすことにします。

まず、最初に作成した initialize メソッドを削除します。その上で、 initializePresenters に以下の変更を加えます。

initializePresenters
     listEntries := self newList
         beSingleSelection;
         activateOnDoubleClick.
     buttonParent := self newButton
         icon: (Smalltalk ui icons iconNamed: #up).
     listCurrent := self newDropList
         startWithoutSelection.
     self directory: FileSystem workingDirectory.
     self focusOrder
         add: listCurrent;
         add: buttonParent;
         add: listEntries

directory: メッセージを使うことで、ファイル一覧もディレクトリ階層もあわせて初期化されることになります。

Spec2入門(その5)

これはSmalltalk Advent Calendar 2019の記事です。

Spec2は、Pharo Smalltalk で採用されているUIフレームワークであるSpec の新しいバージョンです。Pharo 7.0 では Spec が使われていましたが、Pharo 8 では(ほぼ?)全てのUIが Spec2 で書き直されるようです。この記事では Spec2 を使った UI の実装方法について簡単に紹介します。

Squeak/Pharo Smalltalk では、UIの実現のために長らく Morphic を用いてきました。Morphic はシンプルで強力なフレームワークですが、複数のコンポーネントを組み合わせていくとアナーキーな実装をしがちで、リファクタリングや再利用の際に泣きたくなるような状況に陥りがちです。Spec/Spec2 はUIの構築に秩序をもたらし、コンポーネントの再利用がしやすく設計されています。

前回の記事では、Scratch 1.4風のファイル選択ダイアログに、ディレクトリ移動の機能を追加しました。


前回はファイル一覧でディレクトリ名をダブルクリックしたらサブディレクトリに移動する機能を付けましたが、それだと深くディレクトリに入っていくだけなので、今回は親ディレクトリに移動するボタンを追加します。また、現在のディレクトリ階層がわかるようなドロップリストも追加します。

ドロップリストとボタンを追加する

新しくボタンとドロップリストを追加するので、それぞれを格納するインスタンス変数を加えてクラスを再定義します。

SpPresenter subclass: #SpecFileListSample
     instanceVariableNames: 'directory listEntries buttonParent listCurrent'
     classVariableNames: ''
     package: 'SpecSample'

追加したのは buttonParent と listCurrent で、それぞれ親ディレクトリへの移動ボタンとディレクトリ階層を表すドロップリストを格納します。

いつものように initializePresenters で、それぞれのウィジェットを初期化します。

initializePresenters
     listEntries := self newList
         beSingleSelection;
         activateOnDoubleClick;
         items: self getEntries.
     buttonParent := self newButton
         icon: (Smalltalk ui icons iconNamed: #up).
     listCurrent := self newDropList.
     (以下略)

以前に紹介したとおり、ボタンは self newButton で生成します。また、ドロップリストは self newDropList で生成します。ボタンには上矢印のアイコンを付けておきます。

ボタンの機能の設定は、 connectPresenters メソッドで行います。ボタンが押されたら changeParentDirectory メッセージを送って親ディレクトリに移動するようにします。(太字部分を追加します)

ボタンの機能を設定する

connectPresenters
     listEntries
         display: [ :m | self showEntry: m ];
         whenActivatedDo: [ :selection | self entriesChanged: selection ].
     buttonParent action: [ self changeParentDirectory ].

changeParentDirectory のメソッドを実装します。単にルートディレクトリでなければ親ディレクトリに設定しなおすだけです。

changeParentDirectory
     directory isRoot ifTrue: [ ^ self ].
     self directory: directory parent

レイアウトを修正する

レイアウトを Scratch 1.4 のダイアログボックスのように変更します。

上の図では、ファイル一覧の上側にドロップリストとボタンが一列にならんでいます。このようなレイアウトを実現するには、クラスメソッドの defaultSpec を以下のようにします。

defaultSpec
     ^ SpBoxLayout newVertical
         add:
             (SpBoxLayout newHorizontal
                 add: #listCurrent;
                 add: #buttonParent;
                 yourself);
         add: #listEntries;
         yourself

SpBoxLayout newVertical で生成された縦並びのレイアウトオブジェクトに、SpBoxLayout newHorizontal で生成された横並びのレイアウトオブジェクトを追加して、そこでドロップリストとボタンを追加しています。

このレイアウト設定で表示させると、上のように高さや幅が均等な状態になって不格好です。そこで、子ウィジェットの幅や高さを制限するようにします。

defaultSpec
     ^ SpBoxLayout newVertical
         add:
             (SpBoxLayout newHorizontal
                 add: #listCurrent;
                 add: #buttonParent withConstraints: [ :c | c expand: false ];
                 yourself)
             withConstraints: [ :c | c expand: false ];
         add: #listEntries;
         yourself

withConstraints: メッセージをレイアウトオブジェクトに送ると、幅や高さなどを細かく設定できます。width: や height: で直接値を指定することもできますが、ここでは expand: false メッセージを送ることで幅や高さを拡張しないようにしています。

ウィジェット間にスペースを入れる

今度は上限のウィジェットが詰まりすぎなので、間にスペースを入れます。

defaultSpec
     ^ SpBoxLayout newVertical
         spacing: 4;
         add:
             (SpBoxLayout newHorizontal

spacing: メッセージでウィジェット間のスペースを調整できます。これで下図のようにすっきりしました。

右側のボタンを押せば親ディレクトリに移動することも確認できます。

親ディレクトリに移動するボタンの追加は以上です。リストボックスの機能は実現できませんでしたので、次回に回したいと思います。

Spec2入門(その4)

これはSmalltalk Advent Calendar 2019の記事です。

Spec2は、Pharo Smalltalk で採用されているUIフレームワークであるSpec の新しいバージョンです。Pharo 7.0 では Spec が使われていましたが、Pharo 8 では(ほぼ?)全てのUIが Spec2 で書き直されるようです。この記事では Spec2 を使った UI の実装方法について簡単に紹介します。

Squeak/Pharo Smalltalk では、UIの実現のために長らく Morphic を用いてきました。Morphic はシンプルで強力なフレームワークですが、複数のコンポーネントを組み合わせていくとアナーキーな実装をしがちで、リファクタリングや再利用の際に泣きたくなるような状況に陥りがちです。Spec/Spec2 はUIの構築に秩序をもたらし、コンポーネントの再利用がしやすく設計されています。

前回の記事から、Scratch 1.4風のファイル選択ダイアログを作っています。


今回は前回のファイル選択ダイアログに、ディレクトリ移動の機能を付けます。具体的にはファイル一覧でディレクトリ名をダブルクリックしたらサブディレクトリに移動する機能を加えます。

一覧でのダブルクリックに反応する

ファイル一覧は self newList で生成されるリストウィジェットで表示しています。リストウィジェットは、一覧を操作するための様々な機能を備えています。

デフォルトで生成されるリストウィジェットは項目を複数選択できるものですが、今回の例には合わないので項目を1つだけ選択できる(beSingleSelection)ようにします。また、ダブルクリックに反応できる(activateOnDoubleClick)よう、 initializePresenters を太字のように変更します。

initializePresenters
     listCurrent := self newDropList.
     listEntries := self newList
         beSingleSelection;
         activateOnDoubleClick;
         items: self getEntries.

項目をダブルクリックされたときに反応するには、ブロックを引数にしてリストウィジェットへ whenActivatedDo: メッセージを送って設定します。これは connectPresenters で行います。

connectPresenters
     listEntries
         display: [ :m | self showEntry: m ];
         whenActivatedDo: [ :selection | self entriesChanged: selection ].

これで、項目がダブルクリックされたときに entriesChanged: メッセージが送られるようになるので、次は entriesChanged: メソッドを定義します。

entriesChanged: aSelectionMode
     | dir |
     dir := aSelectionMode selectedItem.
     dir isDirectory
         ifFalse: [ ^ self ].
     self directory: dir asFileReference

entriesChanged: の引数は、SpSingleSelectionMode のインスタンスで、ダブルクリックされた項目に関する情報を保持しています。このオブジェクトに対して selectedItem を送れば、項目のオブジェクトが得られるので、ダイアログの新たなディレクトリとして設定します。

最後に directory: メソッドを定義して、作成しているダイアログのディレクトリを変更し、リストウィジェットの内容を再構築します。

directory: aFileReference
     directory := aFileReference.
     listEntries
         unselectAll;
         items: #();
         items: self getEntries

以上で、リストウィジェットのディレクトリ(カッコで囲まれたエントリ)をダブルクリックすると、サブディレクトリが表示されるようになりました。

Spec2入門(その3)

これはSmalltalk Advent Calendar 2019の記事です。

Spec2は、Pharo Smalltalk で採用されているUIフレームワークであるSpec の新しいバージョンです。Pharo 7.0 では Spec が使われていましたが、Pharo 8 では(ほぼ?)全てのUIが Spec2 で書き直されるようです。この記事では Spec2 を使った UI の実装方法について簡単に紹介します。

Squeak/Pharo Smalltalk では、UIの実現のために長らく Morphic を用いてきました。Morphic はシンプルで強力なフレームワークですが、複数のコンポーネントを組み合わせていくとアナーキーな実装をしがちで、リファクタリングや再利用の際に泣きたくなるような状況に陥りがちです。Spec/Spec2 はUIの構築に秩序をもたらし、コンポーネントの再利用がしやすく設計されています。

前回の記事ではボタンへのアイコン追加やウィンドウタイトルの設定方法について紹介しました。今回からはもう少し実用的なUI構築に向けて舵を切り直します。ただし、例によって内容は小出しにしていきます。


今回からファイル選択ダイアログを作っていこうと思います。とりあげた理由は、オレオレプロジェクトで取り組んでいるScratch 1.4風アプリに必要だからです。ちなみにScratch 1.4のファイル選択ダイアログは以下のようなものです。

左側にショートカットボタン、中央にファイル一覧があり、右側にファイルの概要が表示されるというものです。そっくり同じというわけでなくとも、Spec2を使って似たようなものを作ろうと思います。

メイン部のウィジェットを作る

中央に表示されるファイルファイル一覧のウィジェットから手を付けることにします。まずはファイルの一覧が表示されるようにしましょう。メイン部のクラス SpecFileListSample を登録します。

SpPresenter subclass: #SpecFileListSample
     instanceVariableNames: 'directory listEntries '
     classVariableNames: ''
     package: 'SpecSample'

directory は表示するディレクトリ(FileReferenceオブジェクト)を保持します。listEntries はファイルの一覧(Arrayオブジェクト)を保持します。

次に、initialize メソッドで directoryを初期化します。とりあえずPharoイメージのあるディレクトリにしておきます。

initialize
     directory := FileSystem workingDirectory.
     super initialize

メイン部にはファイル一覧を表示したいので、リストウィジェットが表示されるように initializePresenters メソッドを作ります。

initializePresenters
     listEntries := self newList.
     self focusOrder
         add: listEntries

さらに defaultSpec クラスメソッドでレイアウトを設定します。

defaultSpec
     ^ SpBoxLayout newVertical
         add: #listEntries;
         yourself

これでウィジェットの骨格ができました。実際に試してみましょう。

SpecFileListSample new openWithSpec.

ファイル一覧を表示する

リストウィジェットにファイルの一覧を表示させることにします。まず、directory が示すディレクトリ内のファイル一覧を取り出すメソッド getEntries を作ります。

getEntries
     ^ directory entries

リストウィジェットで項目を表示するためのメソッド showEntry: を作ります。項目がディレクトリなら両側をカッコで囲むようにしています。

showEntry: aFileReference
     | base |
     base := aFileReference basename.
     ^ aFileReference isDirectory
         ifTrue: [ '<' , base , '>' ]
         ifFalse: [ base ]

太字部分のように initializePresenters を修正して、ファイル一覧を設定します。

initializePresenters
     listEntries := self newList.
     listEntries items: self getEntries.
     self focusOrder
         add: listEntries

最後に connectPresenters で、設定された一覧を表示するように、リストウィジェットにブロック引数を付けて display: メッセージを送ります。

connectPresenters
     listEntries
         display: [ :m | self showEntry: m ]

これでファイル一覧が表示されました。

Spec2入門(その2)

これはSmalltalk Advent Calendar 2019の記事です。

Spec2は、Pharo Smalltalk で採用されているUIフレームワークであるSpec の新しいバージョンです。Pharo 7.0 では Spec が使われていましたが、Pharo 8 では(ほぼ?)全てのUIが Spec2 で書き直されるようです。この記事では Spec2 を使った UI の実装方法について簡単に紹介します。

Squeak/Pharo Smalltalk では、UIの実現のために長らく Morphic を用いてきました。Morphic はシンプルで強力なフレームワークですが、複数のコンポーネントを組み合わせていくとアナーキーな実装をしがちで、リファクタリングや再利用の際に泣きたくなるような状況に陥りがちです。Spec/Spec2 はUIの構築に秩序をもたらし、コンポーネントの再利用がしやすく設計されています。

前回の記事ではSpec2を用いた簡単なUIの作成方法について紹介しました。今回は前回のUIに対して若干の拡張を行います。今年の Advent Calendar は記事の集まりが悪いので、小出しにしていきます。


前回作成したのは簡単なカウンターで、ボタンを押すと数値が増減する以下のようなものです。さすがに見栄えが悪いので何とかします。

ボタンにアイコンを付ける

UpとDownのボタンに、それぞれ上下の矢印アイコンを付けましょう。アイコンは、 Smalltalk ui icons から適当なものを使うことにします。

ボタンの装飾などは initializePresenters でボタン生成時に行います。SpecSample の initializePresenters でボタンにラベルを設定していた2行を以下のように変更します。

buttonUp
    label: 'Up';
    icon: (Smalltalk ui icons iconNamed: #up).
buttonDown
    label: 'Down';
    icon: (Smalltalk ui icons iconNamed: #down).

ラベルと同様にシンプルなキーワードメッセージ式でアイコン(Formのインスタンス)を設定します。以下の式を Do it して表示してみましょう。

SpecSample new openWithSpec.

ボタンのラベルの左側にアイコンが表示されました。

ウィンドウタイトルを付ける

Untitled Window のままのウィンドウタイトルを別のものにします。Spec2 のウィジェットは別のウィジェットに自由に組み込めるため、initializePresenters や connectPresenters などのタイミングでは、ウィンドウタイトルを指定できません。

その代わり、ウィンドウフレームに組み込まれた時に送られるメッセージを捕まえて、そのタイミングでウィンドウタイトルを設定します。そのためのメソッドが initializeWindow: です。以下のように使います。

initializeWindow: aWindow
aWindow title: 'Spec Sample'

上のコードによって Spec Sample というウィンドウタイトルが設定されます。

なお、ボタン押下時などウィジェットが表示されている状態でウィンドウタイトルを変更する場合には、 self window title: ‘sample’ のようなメッセージを送ることで動的にタイトルを変更できます。

情報を小出しにする関係で、今回の記事内容は以上です。あしからず。

Spec2入門(その1)

これはSmalltalk Advent Calendar 2019の記事です。

Spec2は、Pharo Smalltalk で採用されているUIフレームワークであるSpec の新しいバージョンです。Pharo 7.0 では Spec が使われていましたが、Pharo 8 では(ほぼ?)全てのUIが Spec2 で書き直されるようです。この記事では Spec2 を使った UI の実装方法について簡単に紹介します。

Squeak/Pharo Smalltalk では、UIの実現のために長らく Morphic を用いてきました。Morphic はシンプルで強力なフレームワークですが、複数のコンポーネントを組み合わせていくとアナーキーな実装をしがちで、リファクタリングや再利用の際に泣きたくなるような状況に陥りがちです。Spec/Spec2 はUIの構築に秩序をもたらし、コンポーネントの再利用がしやすく設計されています。では、能書きはこれまでにして、具体的に Spec2 を用いた UIの実装方法を紹介しましょう。


Presenterの作成

Spec/Spec2 はVisual WorksやDolphin Smalltalk で用いられているMVP(Model View Presenter)パターンの影響を受けています。主要なUIコンポーネントを Presenterと呼んでおり、UIにおけるロジックに加えて、アプリケーションドメインとウィジェットとの連携を管理します。

Spec2ではウィジェットである Presenter を作成するのに、SpPresenter クラスを継承したクラスを作成します。Spec2に関連する全てのクラスは Sp という接頭辞が付いています。現状のPharo 8.0は Spec(1.0) と Spec2 が混在しているため、Spec2 でUIを実現するのであれば、Sp接頭辞の付いたクラスだけを用いる必要があります。

SpPresenter を継承したクラスでは、そのクラスが管理する子ウィジェットをインスタンス変数で管理します。例として、2つのボタンと1つのラベルを持つウィジェット SpecSample を作成してみましょう。

SpPresenter subclass: #SpecSample
    instanceVariableNames: 'buttonUp buttonDown labelCounter counter'
    classVariableNames: ''
    package: 'SpecSample'

ウィジェットとなるのは buttonUp と buttonDown、そしてlabelCounterです。上下ボタンを押すとカウンターが増減するため、内部状態として counter を設けています。

まずは counter を初期化するために initialize メソッドを作ります。

initialize
    counter := 0.
    super initialize

ウィジェットを作成する

ウィジェットは initialize で作成するのではなく、initializePresenters というメソッドで作成します。ボタンやラベルを作るためのヘルパーメソッドがあるのでそれらを利用します。また、ボタンラベルなども合わせて設定しておきます。

initializePresenters
    buttonUp := self newButton.
    buttonDown := self newButton.
    labelCounter := self newLabel.

    buttonUp label: 'Up'.
    buttonDown label: 'Down'.
    labelCounter label: counter asString.

    self focusOrder
       add: buttonUp;
       add: buttonDown

メソッドの最後にフォーカス順序を設定するためのコードを加えています。実際には機能しているように見えないのですが、設定するのが推奨されているようです。

レイアウトを設定する

レイアウトはクラス側で定義するため、クラス側のメソッドとして defaultSpec を追加します。

defaultSpec
    ^ SpBoxLayout newVertical
        add: #labelCounter;
        add: #buttonUp;
        add: #buttonDown;
        yourself

ここでは SpBoxLayout のインスタンスを作ってレイアウトしています。 newVertical はウィジェットを縦に並べるためのものです。インスタンス変数のシンボルを add: していくと、その順で上から下に配置されます。

試してみる

ここまでの作業でも実際に動かして試すことができます。Playground で SpecSample new openWithSpec を DoIt すればウィンドウが表示されます。

ボタンの動作を設定する

Upボタンでカウンターを増やし、Downで減らすようにしましょう。そのためにはボタンが押されたらラベルの内容を更新する必要があります。ウィジェット同士を関連づけるには、connectPresenters メソッドを定義します。

connectPresenters
    buttonUp
        action: [ counter := counter + 1.
            labelCounter label: counter asString ].
    buttonDown
        action: [ counter := counter - 1.
            labelCounter label: counter asString ]

ボタンにaction:メソッドを送ってブロックを設定すると、ボタンが押された時にブロックの内容が実行されます。ここではカウンターの変数を増減し、その結果をラベルに設定しています。先ほどと同様にウィンドウを表示してボタンを押すと、カウンターが増減されることが確認できます。

Spec2は簡単

Spec2 の簡単な例について説明しました。まだ、装飾など何もしていないので殺風景ですが、ヘルパーメソッドも多く用意されていたり、細かくレイアウト調整ができるので Spec2 を使って UI を構築するのは簡単です。後の記事でも引き続き Spec2 について説明していくつもりです。

vscode で M5Stack Gray のビルドができない

別の記事ではさらっと書いているけど、最も時間がかかったのは表記の問題によるものだった。

単に自マシン(Ubuntu 18.04)にインストール済の vscode の問題なのかもしれないけど、一応解決したのでその方法を書いておく。(そもそも、M5Stack Gray というデバイスなんだけど Platform IOでは Grey になっているのは置いておく)

通常は、platformio.ini に以下のように書いておけばビルドできるはず。

[env:m5stack-grey]
platform = espressif32
board = m5stack-grey
framework = arduino

ところが、何度ビルドしても m5stack-grey なんていう board は知らないとエラーが出てしまう。

そこで、platformio の設定を確認するために、ホームディレクトリを調べてみた。

[Home]
+ .platformio
   + .cache
   + appstate.json
   + homestate.json
   + lib
   + packages
   + penv
   + platforms

.platformio ディレクトリ内に platforms ディレクトリがあり、その中にインストール済のデバイスの情報がディレクトリごとに分かれて入っている。

[Home]
+ .platformio
   + platforms
      + espressif32
         + boards

M5Stack は espressif32 として提供されており、boards ディレクトリにデバイス情報を定義した json ファイルが格納されている。よく見ると、

+ boards
   (省略)
   m5stack-core-esp32.json
   m5stack-fire.json
   m5stick-c.json
   (省略)

M5Stack Gray 用の json ファイルが見当たらない。

どうやら、これが原因のようなので、該当する json ファイルを探してみたら以下のようなリポジトリ内に見つけた。

https://github.com/platformio/platform-espressif32/blob/master/boards/m5stack-grey.json

これを件のディレクトリにコピーすれば万事解決と思ったのだが、そのままは転送できずに若干の追記が必要だった。

*** /home/itoh/Downloads/m5stack-grey.json      2019-10-16 17:01:01.024886387 +0900
 --- m5stack-grey.json   2019-10-16 17:06:10.509753134 +0900
 
 *** 24,31 ****
       "flash_size": "16MB",
       "maximum_ram_size": 532480,
       "maximum_size": 16777216,
       "require_upload_port": true,
 !     "speed": 460800
     },
     "url": "http://www.m5stack.com",
     "vendor": "M5Stack"
 --- 24,32 ----
       "flash_size": "16MB",
       "maximum_ram_size": 532480,
       "maximum_size": 16777216,
 +     "protocol": "esptool",
       "require_upload_port": true,
 !     "speed": 921600
     },
     "url": "http://www.m5stack.com",
     "vendor": "M5Stack" 

要するに転送方法の指定とボーレートの変更を行うようにすればよい。

以上で vscode で M5Stack Gray のビルドと転送ができるようになった。

ROS2でM5StackとPCをつなぐ

Ubuntu 18.04 なPCとM5Stack GrayとをROS2で結んでみたので忘れないようにメモ。

参考にしたのは以下のサイト

使用環境

PC側

  • 普通のノートPC(本当はあまり普通じゃない GPD MicroPCだけど変わらないはず)
  • Ubuntu 18.04
  • Visual Studio Code
  • vscode の Platform IO 拡張

M5Stack側

  • M5Stack Gray (多分 Basic でも Fire でも同じ)

インストール作業いろいろ

ROS2のインストール

以下のサイトの手順にてROS2 Dashing Diademataをインストールした。「Install additional RMW implementations」以外は全て実施した。(手順は省略)

https://index.ros.org/doc/ros2/Installation/Dashing/Linux-Install-Debians/

python-rosdep2 パッケージをインストールした後で、以下を行った。

sudo rosdep init
rosdep update

サンプルプログラムのインストール

ROS2ではじめよう次世代ロボットプログラミング」で勉強中なので、書籍で説明されていたサンプルコードを使った。(丁寧でわかりやすいです)

あらかじめ working と ros2_ws のフォルダは作成済み。

cd ~/working
git clone https://github.com/youtalk/get-started-ros2.git
cd get-started-ros2
git submodule update --init
cd ~/ros2_ws
ln -s ~/working/get-started-ros2/ros2 src
rosdep install --from-paths src --ignore-src -r -y
colcon build

colcon build が何度か失敗したけど、やり直したら全てビルドできた。不思議。

Micro-XRCE-DDSのインストール

M5Stackは、Micro-XRCE-DDSをインストールしたPCと接続するとROS2でつながれるらしい。そこでMicro-XRCE-DDSをインストールする。以下のページを見ながらインストール。

https://micro-xrce-dds.readthedocs.io/en/latest/installation.html

ほぼ上の通りだが、 https://speakerdeck.com/anoken/m5stack-x-ros2?slide=16 によると、cmake のところでオプション指定しているので、そのように変更した。それがどんな意味を持っているか(まだ)わかっていない。

cd ~/working
git clone https://github.com/eProsima/Micro-XRCE-DDS.git
cd Micro-XRCE-DDS
mkdir build
cd build
cmake -DTHIRDPARTY=ON -DCONFIG_UDP_TRANSPORT_MTU=4096 -DCONFIG_SERIAL_TRANSPORT_MTU=4096 ..
make
sudo make install

ros2arduino のインストール

M5Stack 側のプログラムでは ros2arduino のライブラリが必要となるのでインストールする。vscode + platformio で開発しているので 、PIO Home から Libraries で ros2arduino を検索してインストールする。非常に簡単。

プログラミング

今回はテストとしてchatterプログラムを使用し、PC側をListener 、M5Stack側をPublisherとした。

PC側のプログラミング

PC側については、ふつうにROS2のプログラムを作成する。先ほどビルドしたROS2のサンプルプログラムとして入っているものをそのまま使用した。

. ~/ros2_ws/install/setup.bash
ros2 run hello_world listener

上記のサンプルでは、chatterトピックのメッセージを受け取って画面に表示するものである。別のターミナルを開いて以下を実行すれば、PC内で動作確認できる。

. ~/ros2_ws/install/setup.bash
ros2 run hello_world talker

動作を確認したら talker は止めておく。

M5Stack 側のプログラミング

vscode でPIO HomeからNew Project を選んで新規プロジェクトを作成する。Nameに適当な名前を入力し、Boardで適切なデバイスを選択し(使ったのは M5Stack GREY ESP32)、FrameworkはArduinoとした。

Libraries で Installed を選んで ros2arduino の Examplesを探した。今回は、 publisher_wifi_udp というそれらしいものを選んで内容をコピーし、 src/main.cpp に丸ごと貼り付けた。

サンプルからの変更点としては、以下の通り。(サンプルを見ればわかるので詳細は省略)

  • 先頭に #include <M5Stack.h> を挿入した。
  • SSID, SSID_PW を適切な内容に変更した。
  • AGENT_IP をPCのIPアドレスにした。
  • 27行目付近にある “arduino_chatter” を “chatter” に変更した。
  • setup に、適宜 M5.begin やら M5.print などを入れ、動作しているかわかるようにした。

あとは普通にビルドして、M5Stack に転送。転送すると自動的に起動するが、とりあえず放置。

動作確認

PC側では、ROS2のノード(今回は chatter の listener)と、Micro-XRCE-DDSのエージェントが必要となる。ノードの起動は上で行ったので、Micro-XRCE-DDSを起動させる。

MicroXRCEAgent udp -p 2018

ポート番号は ros2arduino のサンプルに記述されているものを指定した。

次に M5Stack をリセットして起動しなおす。アクセスポイントに接続できれば、Micro-XRCE-DDSのエージェントに接続にいく。(なので先にエージェントを起動しておく必要がある)エージェントのログに、session established と表示されれば無事に接続が済んでいる。

ばっちスクショだが、上のようになればOK。左側にMicroXRCEAgentのログ、右側にM5Stackからのメッセージが表示されている。

環境構築に少し手間取ったが、ROS2でPCとM5Stackが簡単につながるようになったのは嬉しい。これで何を作ろうかな。

micro: Maqueen の解析

micro: Maqueen は micro:bit を載せて走らせることのできるロボットプラットフォームである。DFROBOT社製で、 SWITCH SCIENCE から3,000円強で購入することができる。ちなみに、組み立て方その他はこのページに掲載されている。

中に入っているもの
組み立てたもの

プログラミングには、Scratch 3.0 をベースにした MIND+ というものを使うらしいが、githubにある拡張モジュールを使って、Microsoft MakeCode でプログラミングすることもできる。

とはいえ、MicroPython でも使えないことはなかろうと、いろいろと調べてみた。結果的には(全ての機能ではないものの)使えることがわかり、さっそく micro:witch に取り込んでみた。(いずれ公開予定)

この記事では、micro: Maqueen の各種デバイスをどのように制御するかについて調べたことをまとめる。元ネタは先ほどの拡張モジュールのソースファイル(maqueen.ts)である。


GPIO関係

ありがたいことに Maqueen のボード上には、デバイスの横にP8 のようにシルク印刷されている。まさに、左前の赤いLEDがP8と書かれているのだが、これは micro:bit のpin8に接続されていることを意味するので、プログラムから適切にアクセス(Pythonなら pin8.write_digital(1) のように)すれば、LEDが光る。

裏面にはライントレース用のフォトリフレクタがあるが、左側はP13と書かれており、 pin13 に接続されていることがわかるので、Python ならば pin13.read_digital() で値を取得できる。

項目ピン番号入出力備考
左前LED8出力1でオン
右前LED12出力1でオン
左フォトリフレクタ13入力反射があれば1
右フォトリフレクタ14入力反射があれば1

DCモータ

左右のDCモータはi2cバスを使って接続されている。デバイスのアドレスは 0x10 である。モータを扱うには3バイトのデータを書き込む必要がある。

最初のバイトは左右のモーターの区別で、M1なら0x00、M2なら0x02を指定する。(左右のどちらがM1, M2だったか忘れた。後で記載する)

2番めのバイトは回転方向で、前転なら0x00、後転なら0x01を指定する。(左右のモータは逆向きに付けられているが、同じ値の指定で前進・後退する)

3番めのバイトは回転スピードで、0から255の値を指定する。

バイト位置意味備考
0モータ指定M1=0x00, M2=0x02
1回転方向前転=0x00, 後転=0x01
2回転スピード0から255までの整数値

モータを使って戸惑ったのは、モータを回転させている途中で回転方向を切り替えても反応しないということだった。何らかの理由で回転の指示がキャンセルされてしまう。いったん回転スピードを0にして止めてから100msほど待てば、逆回転させることができた。

サーボ

ボードにサーボを取り付けることができるらしく、これもi2cバスで接続されているらしい。(まだ使ったことがない)資料によると、2バイトのデータを書き込むことでサーボを制御できるようだ。(動作未確認なので注意)

バイト位置意味備考
0サーボ指定S1=0x14, S2=0x15
1角度0から180までの整数値

フルカラーLED

ボード上にはフルカラーLEDが4つある。ただ、先の資料には記載がなく、使い方はよくわからない。NeoPixel として使えるのかも。余裕があれば調べる予定。

Hyper Keyboard pi のタッチパネル補正

ラズパイにキーボードと液晶を一体化させる Hyper Keyboard pi を手に入れた。ちょっと時間があったので作ってみたら、いくつか注意点があったので備忘として残しておく。

いろいろ書いているけど、Hyper Keyboard pi は素晴らしいです。ラズパイの使い勝手が高まります。個人的にはマストバイと思っています。

説明書は最後まで読んで取り組むべし

基本的に説明書を読んで作っていけばいいのだけど、ところどころ先に読んでおけばよかったなあということがあった。例えば、「カプトンテープって何だ?家にはないぞ。買わないと」のようなものだったり、「2枚の基板を組み上げる前に予備ハンダしとかないと後で辛いぞ」みたいなものだったり。これに限らず場当たり的に作るのが問題なのだけれど。

底板をつける前に MicroSD カードを刺す

底板を付けた後だと、ピンセットで差し込まなきゃならないことになる。

起動してもディスプレイが表示されない

HyperPixel4 で表示させるには当然のことながらドライバのインストールが必要。つまり、それまでは外部ディスプレイがないとダメ。事前に用意しよう。

ディスプレイの上下がさかさま

説明書にも書いてあるが、単にドライバインストールしただけではディスプレイの上下が反転してしまう。(正確には180度回転する)そのため、github に書かれた手順を行う必要がある。具体的には大したことなくて、「180 Degree Rotation」の通りにすれば良い。

タッチした場所にカーソルがこない

これにはまいった。画面をタッチするととんでもないところにカーソルが行ってしまう。xinput-calibrator とかインストールして設定してみても効果なし。いろいろググった末に、この issue にたどりついた。

要するに、 github でクローンしたソースファイルの一部を書き換える必要がある。具体的には、 src/hyperpixel4.dts を修正して、

         touchscreen-size-x = <800>;
         touchscreen-size-y = <480>;

         touchscreen-size-x = <480>;
         touchscreen-size-y = <800>;

にしてから、make & copy する必要がある。これでタッチの問題は解決する。

ヒートシンクは早めに設置

ラズパイの熱暴走の予防にヒートシンクをつけるなら底面の配線の前にした方がいい。すっかり忘れてラズパイを外そうとしたら、液晶から外せないくらい配線をきっちりしたせいで、ピンセットで取り付けるハメになった。ラズパイへの配線には余裕を持たそう。