ASP.NET CoreでArduinoとシリアル通信してみる
ASP.NET Core serial communication with Arduino
Arduinoなどのマイコンを汎用性の高いブラウザからリモート操作したいと思ったことはないでしょうか。Google ChromeにはChrome App APIとしてシリアル通信を直接操作できる命令が含まれていましたが、Chromeアプリは今後廃止されるため、将来性は保証されません。
ArduinoならWebUSBを使っての通信もできないことはありませんが、USBシリアル変換アダプターのチップごとにプログラムを組まなければならず、互換品では動かない可能性が高いため、こちらも応用が利きません。
つまるところ、幅広い環境で使おうとなると、ローカルサーバーを経由してのリモート操作が今のところ最善のようです。
今回紹介するのは、ASP.NET Coreで開発したサーバーアプリで通信する方法です。.NET Coreがベースなので、C#でプログラミングできる、Node.js(JavaScript)やPythonよりも高速、Linuxでも動かせると多様なメリットがあります。コンパイルされた実行ファイルには簡易サーバーも含まれているため、追加のフレームワークやライブラリーも基本的に不要です。
まずはプロジェクトファイルを作ります。Visual Studio 2019を立ち上げたら、新規プロジェクトより「ASP.NET Core Webアプリケーション」を作成します。
アプリケーションの種類には「API」を選びます。
プロジェクトが作成されたら、既存のAPI関連ファイルは削除しましょう。
.NET Core向けのシリアルポートライブラリーを導入します。NuGetパッケージマネージャーより「System.IO.Ports」を探して、それをインストールします。
「Startup.cs」ファイルを開き、以下のテキストを追加します。これにより、「wwwroot」フォルダー以下に配置されたHTMLファイルなどを、直に読み込むようになります。
startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
// Add those
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
});
}
ブラウザとのやりとりはXMLHttpRequestなどからのPOSTで行うことにします。プロジェクトフォルダーの「Controller」内に「SerialController.cs」を作成します。
POSTの受信は、対象のメソッドに「HttpPost」属性を追加することで実装できます。URLも指定する場合は「Route」属性も追加します。
以下の例では「http://localhost/serial」へ送られてきたKey-Value形式によるJSONテキストをDictionaryクラスで受け取ります。SerialPort.Read()は同期処理なので、ブロッキングを防ぐため、非同期でJsonオブジェクトを返しています。
ControllerBaseは通信のたびに新しく作られるため、SerialPortクラスはstaticにするなどで共有しておく必要があります。
task.cs
public class JsonObject : Dictionary<string, object>
{
public string AsString(string key)
{
object o;
if (TryGetValue(key, out o) == false) return string.Empty;
return o.ToString();
}
}
[ApiController]
public class TestController : ControllerBase
{
private static SerialPort sp = null;
private Task<string> Read()
{
return Task.Run(() => {
string s = sp.ReadTo("\n");
s = s.Trim('\r', '\n');
return s;
});
}
[HttpPost]
[Route("serial/")]
public async Task<JsonResult> Post(JsonObject v)
{
var r = new JsonObject();
string action = v.AsString("action");
r["action"] = action;
r["error"] = false;
r["value"] = string.Empty;
try {
switch (action) {
case "open":
Open(v);
break;
case "close":
Close();
break;
case "read":
r["value"] = await Read();
break;
case "write":
Write(v);
break;
}
} catch(Exception ex) {
r["error"] = true;
r["value"] = ex.Message;
}
return new JsonResult(r);
}
}
これらを踏まえたサンプルプロジェクトを
GitHubで配布しています。このサンプルでは、Arudinoがシリアル通信で改行コードを受け取ると、それまでの文字列をそのまま返してきます(非同期テストのため、返信は一定回数ごと)。受け取った文字列はjQuery/Ajaxによって、ほぼリアルタイムで内容が更新されます。
2019/12/04