この記事のポイント
- nanoFrameworkについて触れているよ
- nanoFrameworkでスタックチャンの顔を作った話を書いているよ
はじめに
この記事ではnanoFrameworkでスタックチャンの顔を書くまでの話を説明しています。
前回の話はココ
M5StackとnanoFrameworkに少し触ってみたい人向けに書いていますのでちょっと上級者向けです。今回はどのようにしてnanoFrameworkでnanoFramework再現したかについて説明しつつ、工夫した点をハンズオンを通して説明します。まとめでは今後の話を書こうと思います。
完成品の動作
まずは完成品の動作をご覧ください。
※この顔はMicrosoftの開発したランタイムと言語で動いています。
nanoFrameworkとは
リソースが限られた組み込みデバイス(マイコン)上で、C#および.NETコードを直接実行できるようにしたオープンソースのフレームワークです。
主な特徴は以下のとおりです。
- C#とVisual StudioおよびVisual Studio Codeで開発可能
- Visual Studioではデバッグ(ブレークポイント、ステップ実行)が可能
- C#の構文(クラス、インターフェース、async/awaitなど)でマイコンの制御が可能
- 軽量なランタイム(Execution Engine)
- ESP32やSTM32といった、RAMが数百KB〜数MB、Flash ROMが数MB程度の安価なマイコンで動作するように軽量化
- ガベージコレクション(GC)のサポート
- 豊富なNuGetパッケージ
- C# ではおなじみのパッケージがNuGetから利用可能
GPIO、PWM、I2C、SPIなどのハードウェア制御はもちろん、Wi-Fi/Bluetooth通信、MQTT、JSONシリアライズなどの処理をNuGet経由で簡単に導入できます。
ちなみに余談
C#でFrameworkと聞くとMainエントリから書かないといけないように思えますが、トップレベルから書き始めることが可能です。既存のnanoFrameworkサンプルはMainエントリから書いていますが、Top Level Statementで記述可能です。
構成と開発環境
このスタックチャンを作るための構成と環境について説明します。
| 項番 | 製品名 | 値段 |
|---|---|---|
| 1 | M5Stack FIRE IoT開発キット(PSRAM) V2.7 | ¥9,800 |
| 2 | 【PLA+】【Ver.2.5】スタックチャン タカオ版 ケースセット【SG90用】 | ¥3,000 |
| 3 | Servo Kit 180 | ¥2,400 |
| 4 | Servo Kit 360 | ¥1,800 |
スタックチャン タカオ版ではSG90を接続して使いますが、サーボモーターが勝手に回転する不具合(後述)を防ぐためにM5Stack公式の180°サーボと360°サーボにつけかえています。
開発環境は以下のとおりです。
- Visual Studio 2022
- nanoFrameworkの拡張機能を導入できる一番新しいVisual Studio
- Visual Studio Code
- Surface Book 3
- nanoff
MacBookでもやれますが、ビルドで詰まる可能性があるのとnanoffコマンドを使いこなさないといけない点があるので注意です。
補足:nanoFrameworkの拡張機能
Visual StudioでnanoFrameworkをビルド/デバッグ/デプロイする場合は以下の拡張機能をインストールする必要があります。
nanoffについて
M5Stackに限らず、nanoFrameworkを扱う場合は最初にdotnetでnanoffをインストールしてください。インストールコマンドは以下のとおりです。
dotnet tool install -g nanoff
nanoFrameworkはこのnanoffを使ったビルドデプロイが基本となります。
※Visual Studio 2022やVisual Studio Codeの拡張機能ではこのnanoffを実行しています
M5StackでnanoFrameworkを使えるようにする準備
nanoffをインストールしたあと、初めてM5StackをnanoFrameworkで動かす場合はファームウェアを書き込む必要があります。
書き込みの順番
1.M5Stackをパソコンに接続する(USB TypeCかTypeA)
2.シリアルポートを確認する
3.ファームウェアを書き込む
シリアルポートを確認する
M5Stackをパソコンに接続したらシリアルポートを探すために以下のコマンドを実行してください。
nanoff --listports
シリアルポートの番号が判明したらメモしておきます。今回はCOM6でした。
ファームウェアを書き込む
nanoFrameworkで作成したプログラムを動かすためにファームウェアを書き込みます。
次のコマンドではM5Core2の1.14.0.179をシリアルポートCOM6から転送レート115200で実行する例です。
nanoff --update --target M5Core2 --fwversion 1.14.0.179 --serialport COM6 --baud 115200 --masserase
書き込みには時間がかかりますのでケーブルには一切触らず、書き込みが完了するまで待ちましょう。
Visual StudioでnanoFrameworkのプログラムを書き込む
まずはリポジトリをcloneします。
git clone https://github.com/ymd65536/NFApp2.git
CloneしたリポジトリをVisual Studio 2022で開きます。
すでに拡張機能をインストールされている人はDevice Explorerに接続したM5Stackが認識されています。
※認識されていない場合は再度接続を試みてください。
そのままF5キーでプログラムをビルドしてM5Stackにデプロイします。
うまくいくと画像のように顔が描画され、側面にあるLEDが光るはずです。
各ファイルとソースコードの説明
ここからはファイルとコードの説明になります。Visual Studio経験者の方は直感でわかるところが多いところですが、念のため説明します。
プロジェクトを構成する主なファイル
- NFApp2.nfproj
- NFApp2.sln
- packages.config
- Program.cs
スタックチャンの顔を構成するプログラムはAvatarというディレクトリにまとめています。
なお、このAvatarのディレクトリはnfprojで指定しています。
<ItemGroup>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Avatar\Core\Avatar.cs" />
<Compile Include="Avatar\Core\BoundingRect.cs" />
<Compile Include="Avatar\Core\DrawContext.cs" />
<Compile Include="Avatar\Core\Enums.cs" />
<Compile Include="Avatar\Core\Face.cs" />
<Compile Include="Avatar\Core\Gaze.cs" />
<Compile Include="Avatar\Drawables\Eye.cs" />
<Compile Include="Avatar\Drawables\Eyebrow.cs" />
<Compile Include="Avatar\Drawables\IDrawable.cs" />
<Compile Include="Avatar\Drawables\Mouth.cs" />
<Compile Include="Avatar\Faces\CustomFaces.cs" />
<Compile Include="Avatar\Palettes\ColorPalette.cs" />
</ItemGroup>
メインプログラムの説明
中身はいたって簡単ですが、M5Stackのパッケージを呼び出す必要があるのとFireの仕様を理解しないといけないのでC# ユーザーだとしてもちょっとした慣れが必要です。
また、一部は名前空間がかぶっていることもあるので名前空間を変更してあげないといけない場合があります。
※以下のコードではConsoleの名前空間が被っています。
using dotNETM5StackAvatar;
using nanoFramework.Hardware.Esp32;
using nanoFramework.M5Stack;
using System.Drawing;
using System.Threading;
using Console = nanoFramework.M5Stack.Console;
using Math = System.Math;
Fire.InitializeScreen();
Console.Clear();
Console.WriteLine("Fast Mouth Animation Example");
Thread.Sleep(2000);
var ledBar = Fire.LedBar;
// Initialize the avatar with default face
var avatar = new Avatar();
// Use rectangular mouth (keep square/rect style) and make it open/close faster
avatar.GetFace().SetMouth(new RectMouth(50, 90, 4, 60));
// Start avatar drawing/animation
avatar.Init();
// Fast continuous mouth animation with periodic counter reset
int i = 0;
while (true)
{
// 速い振動: sin の係数を大きくし、Sleep を短くする
float ratio = (float)((Math.Sin(i * 0.5) + 1.0) / 2.0); // 0..1 に正規化
avatar.SetMouthOpenRatio(ratio);
i++;
if (i >= 100) // 定期的に初期化
{
i = 0;
Console.Clear();
}
Thread.Sleep(50); // 50ms ごとに更新 → 高速な開閉
var red = Color.FromArgb(255, 255, 0, 0);
var green = Color.FromArgb(255, 0, 255, 0);
var blue = Color.FromArgb(255, 0, 0, 255);
ledBar.Image.SetPixel(0, 0, red);
ledBar.Image.SetPixel(1, 0, green);
ledBar.Image.SetPixel(2, 0, blue);
ledBar.Update();
}
また、nanoFramework.Graphicsを使うことでLEDの点灯を実現しています。
var red = Color.FromArgb(255, 255, 0, 0);
var green = Color.FromArgb(255, 0, 255, 0);
var blue = Color.FromArgb(255, 0, 0, 255);
ledBar.Image.SetPixel(0, 0, red);
ledBar.Image.SetPixel(1, 0, green);
ledBar.Image.SetPixel(2, 0, blue);
ledBar.Update();
補足:コードの注意点
今回紹介したコードではnanoFramework.M5Stackというパッケージを利用していますが、すでに廃止予定のパッケージとなっているため、これから開発される方はnanoFramework.M5Coreの利用を推奨します。
スタックチャンの顔をnanoFrameworkで実現するには
実現方法ですが、今回はGitHub Copilot のAgentセッションにスタックチャンのアバターリポジトリを渡してお任せで作ってしまったので実は筆者も細かいところはあまり理解できていないところがあります。
※ちなみに一発で作成はできなかった。
とはいえ、それでは今後困るので作成当時のリポジトリを前提にざっくり説明するとオリジナルのC++版 m5stack-avatarをベースにC# で開発しています。
ですので構造はC++のものをC#で書き直したものになります。C++版を理解していてC# がわかる人なら触れるような代物です。
遭遇した不具合
作り方は以上です。
ここからは遭遇した不具合について書きます。百聞は一見にしかずということでまずはどのような不具合なのかご覧ください
何かというと電源投入時に勝手にサーボモーターが動き出すというものです。
最初はnanoFrameworkで作った場合の問題あるいはM5Stack Fireの問題かなと思って解決不可のように思えたのですが、実際は利用しているサーボモーターとM5Stackの仕様が噛み合っていないだけでした。
これは単に筆者がサーボモーターについて疎かったというだけのことなんですが、どうやらサーボモーターには動き出すための最小電力というものがあり、これをデッドバンドというそうです。
このデッドバンドが小さすぎるとちょっとの電力で過敏に回転を始めたり、「ジジジッ」と音を出して常に動き出すなどの不具合につながります。
つまり、SG90とM5Stack公式のServo Kit 180° ではこのデッドバンドに大きな違いあるのでM5Stack公式のServo Kitを使うことで解決するということです。
まとめ
今回は以上です。思ったより顔だけはいっちょ前にスタックチャンしているのでこれが完成で良いかなと思いつつも、Azure接続とかやれたらもっとおもしろいだろうなと想像しています。
幸いにもnugetのパッケージが利用できるのとサンプルコードもあるので触ってみてスタックチャンをアップグレードしたいと思いました。
今回はM5Stack Fireでしたが、手元にはほかのM5Stackもあるので持っているM5StackをすべてnanoFrameworkでハックしてみるのもおもしろそうだなと考えています。
というのも最近になってnanoFrameworkもアップデートがあり、最新の機種でもいけそうでしたので次のハックに向けて準備中です。今回は以上です。
