Raspberry Pi PicoでPWM出力

ピン機能の設定

  • gpio_set_function()でピン機能をGPIO_FUNC_PWMに設定する。
  • RP2040には8つのPWMスライスがあり、各スライスにA/Bの2つのPWMチャンネルがある。
  • つまり最大8×2=16チャンネルのPWM出力が可能。
  • PWM周波数や分解能はスライスごとに設定できる。
  • 偶数番(2n)のGPIOはAチャンネル、奇数番(2n+1)のGPIOはBチャンネルで、スライス番号は n % 8
  • スライス番号はpwm_gpio_to_slice_num()でも取得できる。

PWM周波数の設定

  • Raspberry Pi Picoのメインクロックは125MHz。
  • pwm_set_clkdiv()で分周比を設定する。(0~256未満の実数値で設定)
  • pwm_set_wrap()でPWMの分解能を設定する。

メインクロックをsysclock、PWM分解能の設定値をwrap、分周比をclkdiv とすると、
PWM周波数 f = sysclock / ((wrap+1) ⋅ clkdiv) より
clkdiv = sysclock / ((wrap+1) ⋅ f )

例えばPWM周波数を250Hzに、PWMの分解能を11ビット(0~2047)に設定したい場合、
sysclock = 125,000,000Hz、wrap = 2047、f=250Hz より
分周比 clkdiv = 244.140625 となる。ただし、clkdiv は実際には1/16単位に丸められる。

デューティーの設定

  • デューティー(High期間)の設定にはpwm_set_chan_level()を用いる。
  • PWM出力の有効/無効はpwm_set_enabled()で設定する。

サンプルコード (Arduino環境用)

#include <hardware/pwm.h>

void setup()
{
    // GP2とGP3のピン機能をPWMに設定
    gpio_set_function(2, GPIO_FUNC_PWM);
    gpio_set_function(3, GPIO_FUNC_PWM);

    // GP2(とGP3)のPWMスライスを取得
    uint slice_num = pwm_gpio_to_slice_num(2);

    // PWM周期を設定
    pwm_set_clkdiv(slice_num, 244.140625);
    pwm_set_wrap(slice_num, 2047);
    
    // チャンネルA(GP2)とチャンネルB(GP3)のPWMのHigh期間を設定
    pwm_set_chan_level(slice_num, PWM_CHAN_A, 1024);
    pwm_set_chan_level(slice_num, PWM_CHAN_B, 512);
    
    // PWM出力イネーブル
    pwm_set_enabled(slice_num, true);
}

void loop()
{
  
}

参考


GR-ROSEで作るIoT渾天儀

この記事は がじぇるねGR Advent Calendar 2021 の5日目です。
先月開催されたGR-ROSE IoTシステム開発コンテストでのLTをもとに補足情報を加筆したものです。

Introduction

今日は12月5日。暦の上ではまもなく大雪を迎え寒さが日増しに深まるこのごろですが、皆様お変わりなくお過ごしでしょうか?

私も若くて元気なころは季節の移ろいをあまり切実に意識することはありませんでした。基礎代謝が高いと冬の寒さなんてへっちゃらなんですよね。でもだんだん年を取るにつれて、冬の寒さがつらい。季節の変化を意識して体調管理にも気を配らないといけないようになってきました。

f:id:licheng:20211120185758j:plain:w600

IoT渾天儀で太陽の運行を知る

さて、季節の移ろいというのは、つまりは地球から見た太陽の運行のことですよね。
そこで太陽の運行を知るためにこんなメカを作ってみました!名付けてIoT渾天儀です。

f:id:licheng:20211120190508j:plain:w640

メカは100円ショップ(セリア)で売ってるプラダンと木材で作りました。円弧の切り出しには直径30cmまで対応のコンパスカッターを使用しました。手作りのアナログ工作です。CADすら使ってません。紙とペンと定規とコンパスで設計しました。

f:id:licheng:20211121122852j:plain:w640

目盛りはinkscapeで描いてPDF化し、コンビニでA3用紙にプリントアウトしました。分度器のような目盛りはinkscapeのタイルクローン機能を使うと簡単に描けます。コンビニでのプリントアウトでは原寸大で印刷することに注意。ローソンの場合は「用紙に合わせる」を「しない」に設定します。

渾天儀とは?

f:id:licheng:20211121121945p:plain:w250:right

渾天儀とはいったい何かというと、古代のギリシャと中国で発明された天体観測のための機械です。西洋ではArmillary sphere (羅:Sphaera armillaris)、東洋では渾天儀 (こんてんぎ / Hùntiānyí / 혼천의) と呼ばれています。

IoT渾天儀のしくみ

私の作った渾天儀は2個のモータで動き、地球上から見た太陽と星座の動きをシミュレートできます。それと太陽はLEDで光ります。

f:id:licheng:20211121142458j:plain:w640

2個のモータにはステッピングモータを使い、これをGR-ROSEから制御します。24BYJ48はユニポーラ結線のモータなので、バイポーラステッピングモータドライバのDRV8825で駆動するのは不適切な気がするのですが、いちおう回ります。(桃, 橙, 黄, 青をA1, A2, B1, B2に接続、赤は未接続)

連続回転するものに給電するのは意外と難しいことで、スリップリングという部品を利用しています。LEDへの給電のスリップリングは小型化のためにコンタクトプローブ(ポゴピン)を使って自作しました。

f:id:licheng:20211121142651j:plain:w533

天文計算のフローは下図の通り。日時と観測地点の経度・緯度から太陽と星座の位置を計算してモータの回転角に換算します。本当は緯度に応じて天球の傾きもモータ駆動で変化させたかったのですが、機構的に難しいため今回は手動としました。

f:id:licheng:20211121142717j:plain:w533

この天文計算のC++クラス化をGitHubで公開しています。

IoT渾天儀の動作

夏至の日には太陽は双児宮から天蟹宮に入り、もっとも高い軌道を通ります。

f:id:licheng:20211121142745j:plain:w533

冬至の日には太陽は人馬宮から磨羯宮に入り、もっとも低い軌道を通ります。

f:id:licheng:20211121142759j:plain:w533

北緯35度では夏と冬でこれだけ太陽の高さが違うんですね。東京や大阪がだいたいこの北緯35度付近です。

黄道十二宮双児宮、天蟹宮、人馬宮、磨羯宮はそれぞれ、ふたご座、かに座、いて座、やぎ座に相当しますが、実際の星座の位置とは約1月ずれています。これは黄道十二宮が成立した二千数百年前と現代とでは地球の歳差運動によって自転軸の向きが変化しているためです。

IoT対応

肝心のIoTをやる時間があまりありませんでしたが、いちおうAzure IoT Central には対応しました。デバイス定義はコマンド1個だけで、日時と経度・緯度を指定するとそれに従って渾天儀が動き、計算結果の太陽の黄経・赤経赤緯を返します。

f:id:licheng:20211121142917j:plain:w640

それだけではちょっと寂しいので、REST APIを使って自前のWebアプリからもコマンドを呼んでみました。

f:id:licheng:20211121135315p:plain:w324

REST APIの使い方に関しては、公式のドキュメントに解説があります。

しかし、認証処理のところで出てくるトークンていうやつが、何のことやら分からなくて手こずりました。

要するに、IoT CentralのWebページの 管理 → APIトークン → 新規 で生成したトークンなる文字列を、HTTPリクエストヘッダのAuthorizationヘッダで指定しなければならないということらしいです。Web系の人にはこんなの常識なのかもしれませんが、組込み系の自分にはチンプンカンプンでした。

f:id:licheng:20211121160438p:plain

この文字列は秘密の文字列なので、フロントエンドに丸見えでは意味が無いように思われます。なのでAPI中継サーバを作成しました。詳しくは下記の記事に書きました。

今後の展望

というわけで今回はここまでですが、さらにブラッシュアップして、いつの日かリアルイベントが展示できるようにしたいと思ってます。

【課題】

  • モータの初期位置出し、現在は手動 → フォトセンサで自動化
  • メカの強度と精度がプラダンでは足りない → CADで設計してMDF製に
  • IoT対応もうちょっとしっかり → Azureの勉強からします

デモ動画

実際に渾天儀が回転しているデモ動画です。デモ用に4日で1年の動きにしています。
(地球が4回自転する間に1回公転すると仮定した場合の動きです。)

発表資料

Raspberry Pi Picoの2つのArduinoサポートを比較

Raspberry Pi PicoはArduino環境で開発することもできる。Raspberry Pi Pico用のボードサポートパッケージはArduino公式版以外にフィルハワー伯爵版(?)なるものが存在するようだ。

公式版はMbedの上にArduinoの皮かぶせたちょっとやっつけ仕事気味なかんじなのに対して、伯爵版はC/C++SDKベースで作られておりむしろ公式版よりしっかりしてるように感じる。詳しく見てみよう。

f:id:licheng:20211127164900j:plain:w500

Arduino公式版

Arduino公式でサポートしているため、「追加のボードマネージャのURL」を設定する必要は無い。ボードマネージャで pico と入力すれば Arduino Mbed OS RP2040 Boards というのが見つかるので、これをインストールする。するとボードの選択メニューに Arduino Mbed OS RP2040 Boards が追加される。

インストールされたパッケージのソースを覗いてみよう。
Winsowsなら %LOCALAPPDATA%/Arduino15/packages/arduino/hardware/mbed_rp2040 にある。

どうもMbed OSの上にArduino APIのラッパーをかぶせているようだ。ちょっと雑な作りのように感じる。利点としてはMbedのAPIをスケッチから直接呼び出すことも可能だが、それなら最初からMbedで開発すればいいじゃないかと思う。Mbedのマルチスレッド機能(Threadクラス)は使えないようだ。

また、Servo, EEPROM, SoftwareSerial といったおなじみのライブラリが使用できない。特にServoをサポートできないはずがないのでちょっと手抜きではないかと思う。

C/C++SDKAPIをスケッチから直接呼び出すことも可能だ。Mbed OS自体がC/C++SDKをベースに移植されているので、まあ当然と言える。C/C++SDKAPIを使えばデュアルコアを活用した並列処理は可能なようだ。(multicore_launch_core1関数)

Earle Philhower版

もう一つのサポートは Earle F. Philhower, III氏が公開しているものだ。 冒頭でフィルハワー伯爵と書いたが、Earleは伯爵(Earl)ではなくてファーストネームか。アール・F・フィルハワー3世、いったい何者だろう?

こちらは「追加のボードマネージャのURL」にURLを追記する必要がある。

https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json

ボードマネージャで pico と入力すれば Raspberry Pi Pico/RP2040 というのが見つかるので、これをインストールする。するとボードの選択メニューに Raspberry Pi Pico RP2040 Boards が追加される。もちろん、公式版とは共存可能だ。

インストールされたパッケージのソースを覗いてみよう。
Winsowsなら %LOCALAPPDATA%/Arduino15/packages/rp2040/hardware/rp2040 にある。

こちらはC/C++SDKベースに作られている。C/C++SDKAPIをスケッチから直接呼び出すことももちろん可能だ。また、setup1()loop1() という関数を書けばコア1で実行される。setup()loop() はコア0で実行されるので、これによって並列処理が簡単に書ける。

また、公式版では使えなかった Servo と EEPROM がこちらでは使える。SoftwareSerialは使えないが、まあ個人的には要らないと思っている。ちなみにハードウェアシリアル通信は、SerialがUSBシリアルで、Serial1Serial2がUARTとなる。

気になるのが個人による開発という点で、今後ちゃんと保守されるのかという心配はある。しかしすでにかなり広く使われているようである。また、前述の通りそもそも個人の趣味と思えないような仕事である。アール・F・フィルハワー3世、いったい何者だろう?

所感

まだ実際に動かしてみたわけではないのだが、ソースをざっと眺めたかぎりでは伯爵版に軍配を上げたい。(もう伯爵版と呼ぶことにする。) 共存可能なので両方インストールしてしばらく様子を見ることにする。

UARTおじさんの帰還

※ この記事は極めて偏った超保守的思想で書かれています。

BLEとはなんぞやというような話は2010年代にさんざん聞かされたし、その概念が分からないわけではないけども、ゼロベースで素直に考え直せばUART/SPPのほうが適しているケースも多いのです。

しかしそうは言ってもコスト面の問題があります。UARTおじさん御用達のRN42が2000円以上するのに対してBLEモジュールは1000円を切ることもあります。

そこでM5Stamp Picoですよ! こいつはSPPもできて800円台ですから。
UARTおじさんはRN42の代替にM5Stamp PicoでSPPやろうぜ!
(量産に使えるかはさておき。あと消費電力の問題もあるけど。)

f:id:licheng:20211125193935j:plain:w640

サンプル

UARTとSPPをブリッジするArduinoスケッチ

#include "BluetoothSerial.h"

BluetoothSerial SerialBT;

void setup() {
  Serial.begin(115200);
  SerialBT.begin("ESP32");
  delay(500);
}

void loop() {
  if(Serial.available()){
    SerialBT.write(Serial.read());
  }
  if(SerialBT.available()){
    Serial.write(SerialBT.read());
  }
}

補足

WebSocketみたいな迂遠で珍妙なプロトコルを平気で使っていながらBLEで16進文字列を送ることを非難するのはダブルスタンダードだと思ってます。もっとも、ちょっと大きめのデータを送ると破綻するようなオレオレなんちゃってBLEシリアル通信は論外です。若者はJSONが好きなので、SPPでシリアル通信すれば良いと思います。

メカナムホイールのトイラジコンを分解してみた

アマゾンで5000円で売ってるメカナムホイールのトイラジコンを分解してみました。
驚くべきことに各軸にエンコーダ(ただし一相)がついています。

開封の儀

コンパクトでわりと品位の良い箱なので、捨てずに収納ボックスとして使うのがよさそう。

f:id:licheng:20211120153834j:plain:w600

内容物は、ラジコン本体、コントローラ、18500 Li-ion電池 (3.7V 1200mAh) × 2本 (1本で駆動、1本は予備)、充電器、充電ケーブル(電池を本体に入れたまま充電できるUSBケーブル)、説明書(日本語)、+ドライバ。

f:id:licheng:20211120153843j:plain:w600

ちょっと走らせて遊んでみましたが、メカナムホイール楽しいですね。トイラジコンなのでやたらピカピカLEDが光ったり効果音やら謎のBGMが流れたりします。

さっそく分解

さっそく分解しました。メイン基板1枚に電池とモータとLEDとスピーカが接続されています。トイラジコンによくあるかんじです。RFはメイン基板と一体のようです。

f:id:licheng:20211120153852j:plain:w600

さらに分解を進めるとモータユニットがゴロっと取り外せました。おや? 線の数が多いですね。

f:id:licheng:20211120153901j:plain:w600

なんとエンコーダつき!?

なんとフォトインタラプタ式のロータリーエンコーダが入っています。ただし1相。なので回転量は取れますが回転方向は分かりません。まあ、モータを回そうとしている方向に回ってるはずだという推定で使うのでしょう。ケーブルは赤が3.3V、黒がGND、黄色と緑がフォトダイオードのカソード、あるいはフォトトランジスタのコレクタのようです。(動作は未確認です。) あと、青と白がモータです。

f:id:licheng:20211120153920j:plain:w600

メカナムホイール

メカナムホイールは外径6mmの六角シャフトにネジで固定されています。ホイールの直径は実測で約52mm、ホイールベース長は約127mm、トレッド幅は約90mmです。ちなみに製品仕様によると全長225mm、全幅115mm、全高110mmです。

f:id:licheng:20211120153933j:plain:w600

感想

これで5000円はめちゃくちゃ安い。もしもエンコーダがちゃんと2相だったら神でしたが、まあじゅうぶんハックしがいのありそうな内容です。ドライバーのフィギュアをどければ運転席のスペースが空くので、お好みのマイコンボードを載せることができるでしょう。

おまけ

メイン基板に乗ってるICを調べてみる。

f:id:licheng:20211127152427j:plain:w600

  • 右側にある2個の16ピンのICは、DCモータドライバ MX1616。Hブリッジ2回路入り。
  • 中央左寄りにある24ピンのICは、型番刻印なし。マイコンと思われる。
  • 左下のアンテナの近くにある8ピンのICは、2.4GHz GFSKトランシーバ XN297。
  • 中央下寄りにある8ピンのICは、録音IC ZC25P040。スピーカに接続されており効果音用と思われる。
  • 上部中央の8ピンのICは、汎用ロジックの6ビットバッファ TC4056。

マイコンと汎用ロジック以外は謎の中華ICばかり。こういう謎の半導体メーカーがゴロゴロ存在する中国のモノづくりすごいなと思う。

ギリシャ正教1ミリも知らない仏教徒が「アグニ・パルセネ」を和訳してみた

ギリシャ語の聖歌「アグニ・パルセネ」を和訳してみた。先入観を抱かないようになるべく英訳は見ずに難解な箇所のみ参考にした。(そもそもWikipediaにある英訳はぜんぜん原文に忠実でない気がする。)

Αγνή Παρθένε Δέσποινα, Άχραντε Θεοτόκε,
純潔の乙女よ、貴婦人よ ※1、穢れなき神産みの御方よ ※2

Παρθένε Μήτηρ Άνασσα, Πανένδροσε τε πόκε. ※3
乙女にして母なる女王よ、まるごと露を含んだ羊毛よ ※4

Χαίρε Νύμφη Ανύμφευτε.
歓べ 未婚の花嫁よ

Υψηλοτέρα ουρανών, ακτίνων λαμπροτέρα, ※5
天空よりもなお高く、陽射しよりもなお輝かしく

Χαρά παρθενικών χορών αγγέλων υπερτέρα. ※6
天使たちの合唱にもなお勝る乙女の歓びよ

Χαίρε Νύμφη Ανύμφευτε.
歓べ 未婚の花嫁よ

Εκλαμπροτέρα ουρανών, φωτός καθαροτέρα, ※7
天空よりもなお輝きを放ち、陽の光よりもなお澄みきって

Των ουρανίων στρατιών πασών αγιωτέρα.
天空の軍団その全てよりもなお聖なるかな

Χαίρε Νύμφη Ανύμφευτε.
歓べ 未婚の花嫁よ

「アグニ・パルセネ」の動画

ラトビア人の歌手 Aleksandra Spicberga さんによるギリシャ語とラトビア語による「アグニ・パルセネ」の歌唱。前半がギリシャ語だが、発音が現代ギリシャ語音とちょっと違うようだ。でも古典ギリシャ語音でもない。まあ、ラテン語の発音もイタリア式やらドイツ式やらあるし、そんなかんじかな?


※1 カトリックなら「聖母マリア」と訳すが、ここではその訳語は使えない。他の案としては、姫様、ひめみこ様、姫御前様、女領主様など、いずれも苦しい。
※2 正教会ではテオトコスを「生神女」と訳すらしいが、あまりに耳馴染みの無い訳語である。「神の母」という訳もあるが、ここでは原語に沿って「神産みの御方」とした。しかし「神産み」という語はイザナミノミコトを連想させてしまうかもしれない。
※3 「まるごと露を含んだ羊毛」というのは唐突で意味不明に感じるが、これは『旧約聖書』の『士師記』に記された士師ギデオンの故事をふまえたもののようだ。キリスト教では露を含んだ羊毛はマリアの処女受胎を象徴するらしい。
※4 πανένδροσε という語は辞書に見当たらないが、 παν + έν + δροσε で「全く露の中の(=まるごと露を含んだ)」の意味と解釈した。
※5 ~τέρα という語尾が頻出するが、これは形容詞の比較級の語尾。
※6 構文がよく分からない。「Χαρά παρθενικών」を「乙女の喜び」と訳してみたが、παρθενικών が複数形なのが解せない。
※7 εκλαμπροτέρα という語は辞書に見当たらないが、εκ + λαμπροτέρα で 「外に輝く (=輝きを放つ)」の意味と解釈した。

なんかよく分からんけどWebアプリをAzureにデプロイするまで (後編)

前回は開発フローの確立までをやった。今回は肝心のWebアプリの中身を作っていく。

作りたいWebアプリ

f:id:licheng:20211111230348p:plain:w500

静的なHTMLファイルのホスティング

前回はpugという謎のテンプレートからHTMLを生成するアプリを作ったが、ぶっちゃけ今回作りたいものは静的なHTMLファイルのホスティングで事足りるので、pugとかよく分からんものは使いたくない。そのような場合、最初のひな型生成の時の引数で --no-view を指定すればよいらしい。(詳しくはこちらのドキュメント)

npx express-generator myExpressApp --no-view

そうするとpugファイルを含むviewフォルダは生成されず、かわりに /public/index.html ファイルが生成される。エディタで開いてみると、ごくふつうのHTMLファイルである。こういうのでいいんだよ。

このHTMLファイルを自作のフロントエンドの index.html で置き換える。アプリを実行してブラウザで開いてみると、この index.html が表示された。これで静的なHTMLファイルのホスティングはできた。

Expressは何をしているのか?

なんか便利らしいのでExpressを使うことにしたが、こいつが何をやっていてどう使えばいいのかよく分からない。同じような思いを持った先人の記事が参考になった。

さっきの静的ファイルのホスティングに関しては生成された app.js の15行目に該当する記述がある。
(詳しくはこちらの記事)

app.use(express.static(path.join(__dirname, 'public')));

処理1: Web APIリクエストの受け付けとレスポンスの返信

今回はPOSTでJSON形式のリクエストを受け付け、JSON形式のレスポンスを返すAPIとする。

あるエンドポイントへのリクエストに対するレスポンスの返信は次のような形で書ける。(抜粋)

app.use(express.json());
app.use(express.urlencoded({ extended: false}));
app.post('/エンドポイント/', (req, res) => {
  req.body (=リクエストのボディ)を処理;
  var res_obj = レスポンスのオブジェクト;
  res.json(res_obj);
});

返信は文字列を返すなら res.send を、オブジェクトをJSON文字列に変換して返すなら res.json を使う。

またPOSTでなくGETの場合は、パスパラメータを取得するなら req.params を、クエリパラメータを取得するなら req.query を用いる。

処理2: Web APIリクエストの発行とレスポンスの受け取り

こちらも今回はPOSTでJSON形式のリクエストを発行し、JSON形式のレスポンスを受け取るAPIとする。

requestパッケージを用いてWeb APIリクエスト処理を実装する。じつはこの request というのはすでに非推奨のパッケージらしいのだが、ひとまずこれで実装することにする。

インストールは下記のコマンドでおこなう。ちなみに npm はデフォルトではカレントディレクトリの下の node_modules ディレクトリにパッケージをローカルインストールする。グローバルにインストールしたい場合にはオプション -g を指定する。ローカルにインストールしたパッケージに関する情報は package.json と package-lock.json に記録される。

npm install request

Web APIリクエスト処理は次のような形で書ける。(抜粋)

const request = require('request')

var req_obj = リクエストのオブジェクト;
const options = {
    method: 'POST',
    url: 'https://Web APIサーバのURL',
    headers: {
      'Content-type': 'application/json',
      'Authorization': 'API認証キー',
    },
    json: req_obj
}
request(options, (error, response, body) => {
    body (=レスポンスのボディ)を処理;
});

Web APIの中継 = 処理1 + 処理2

前述の処理1と処理2を合体させればWeb APIの中継サーバができる。
これは次のような形になる。(抜粋)

const request = require('request')

app.use(express.json());
app.use(express.urlencoded({ extended: false}));
app.post('/エンドポイント/', (req, res) => {
    const options = {
        method: 'POST',
        url: "https://Web APIサーバのURL",
        headers: {
          'Content-type': 'application/json',
          'Authorization': 'API認証キー',
        },
        json: req.body
    };
    request(options, (error, response, body) => {
        res.send(body);
    });
});

これらのコードは下記の記事を参考にした。

整理

app.js を見て不要そうなものを削除する。

  • public/style.css は使わないなら削除する。
  • indexRouter とか usersRouter とかは今回は不要っぽいので関連する記述を削除し、
    routesフォルダのファイルはフォルダごと削除する。
  • cookieParser も今回はクッキーを使用しないので不要っぽいので関連する記述を削除し、
    下記のコマンドで cookie-parser をアンインストールする。
npm uninstall cookie-parser

これでほぼミニマムな構成になった。

バージョン管理について

前述のように node_modules はnpmのパッケージをローカルインストールするフォルダなのでバージョン管理から除外する。さもないと大量のファイルがリポジトリに入ってしまう。必要なパッケージに関する情報は前述のように package.json と package-lock.json に記録されているので、この2つはバージョン管理に含める。

また、.vscode/settings.json をバージョン管理に含めるか否かは運用しだいかなと思う。Azure App Serviceのフォルダの場合、.vscode/settings.json の中にデプロイ先の情報が記述されているので要注意。

.gitignoreの例

node_modules/
.vscode/

リモート環境へのSSHログイン

デプロイしたAzureのリモート環境にSSHでログインしてコマンド操作したい場合、ブラウザで下記のようなURLにアクセスする。するとブラウザ上でターミナルが開く。(詳しくはこちらのドキュメント)

https://アプリ名.scm.azurewebsites.net/webssh/host

所感

  • 関数型っぽい書き方に慣れが必要。(本質的にはC言語の関数ポインタでコールバック関数を渡すのと同じことなのだけども。)
  • JavaScriptは関数の引数にも戻り値にも型宣言が無いので、どんな形で渡してどんな形で返ってくるか分からず困ることが多い。