ぷろじぇくと、みすじら。

MicrosoftEdge Documentationを訳してみる

Created at:

GitHubにMicrosoftEdge DocumentationというEdgeについてのドキュメントがあるのですが、 それをふと英語の勉強もかねて訳してみたりしています。

https://github.com/mayuki/MicrosoftEdge-Documentation

まああまりうまくないし間違ってるかもしれませんがそこまで極端には間違ってないと思いたいところ…。

今のところ以下のあたりをやってあります。

Intel Edisonを買ったのでC#でも触りたいのです

Created at:

Intel Edison

最近、Intel Edisonをおすすめされたので買ってみました。

Arduinoなどと比べるとお高いのですが、その分性能も高くてSDカードのひと回り大きいぐらいのサイズで500MHzで動作するAtomとRAMが1GB、ストレージとして4GBを持ち、Wi-FiとBluetooth 4.0も持っていて技適も通っていてLinuxが動くという代物。すごい。ちなみに写真はBreakout Board Kitに乗せた状態のものです。

Edisonのセットアップは簡単で、ちょこちょこと初期設定するとsshで入れるようになり、Node.jsも標準で使えます。EdisonのピンというかIOを操作するためにMRAAというライブラリが用意されていてNode.jsのバインディングも用意されていて簡単に扱えるようになっています。

Intel Edison (Breakout Board Kit)でLチカ - Qiitaといった記事を参考にLチカをするのも簡単です。

C#を書けるようにする

やはりC#書きたいですね。LチカするにもC#でやりたい!…ということで最初はCoreCLRのネイティブコンパイルしたバイナリを持っていくか、CoreCLR自体を持っていこうと思っていたのですがそもそもLinux x86 portはまだのようなのできっとあるであろうMonoを探すことに。

探してみるとMonoのパッケージを提供している方がいらっしゃるのでありがたく利用させていただきます(EdisonのLinuxにはopkgというパッケージマネージャーが入っています)。

…と、簡単に入ります。

Monoがインストールできればあとはmonoコマンドでいろいろ実行できてこっちのものです。ちなみにMono 4.0なので少し古いのですが概ね問題ないでしょう。

C#からピンを操作できるようにする

しかしMonoからいざEdisonのGPIOのようなものを操作するにはsysfs経由 (/sys/class/gpio/)で操作する必要がありあまり使い勝手がよくありません。そこでNode.jsのようにMRAA(libmraa)をC#から使えるようにするライブラリがあれば…!と思ったので作ったのがMraaSharpです。

MraaSharpはだいぶ作りかけですが、libmraaをP/Invokeで呼び出すためMono以外に必要としません。本来であればMRAAはSWIGで各言語のバインディングを生成できるのですが、いろいろと(入れるのもビルドも)面倒なのでピュアC#で完結するものにしました。

使い方は簡単で、MraaSharp.dllを参照したうえで大よそMRAAと同じようなAPIを呼び出せます(ほとんど未実装ですが)。

using MraaSharp;

// 初期化 (mraa_init)
Mraa.Initialize();
// 情報を表示する (mraa_get_version, mraa_get_platform_name)
Console.WriteLine("Version: {0}", Mraa.Version);
Console.WriteLine("PlatformName: {0}", Mraa.PlatformName);

// ピンをGPIOの出力として開く
var gpio = new Gpio(MraaIntelEdisonMiniboard.J17_8, MraaGpioDir.Out);
Console.WriteLine("Gpio Pin: {0}", gpio.Pin);
Console.WriteLine("Gpio PinRaw: {0}", gpio.PinRaw);

while (true)
{
    gpio.Write(MraaGpioValue.High); // 出力をHIGHにする
    Thread.Sleep(1000);
    gpio.Write(MraaGpioValue.Low); // 出力をLOWにする
    Thread.Sleep(1000);
}

なおMonoをインストールするとC# ShellというMonoのC# REPLが一緒に入るので、それを起動してMraaSharpを読み込むとインタラクティブに試すことができます。Node.jsに負けてない!(MraaSharpがすべて実装済みとは言っていない)

というわけでC#でLチカできてよかったのです(1.8Vなので直接つなげない分、若干難易度高かった)。

MRAA + C#

A video posted by Mayuki Sawatari (@misuzilla) on

ILI9340な液晶をNetduinoで使う

Created at:

液晶+Netduino plus 2

aitendoで2.2インチのQVGAなSPI接続のTFT液晶を買いまして、 Arduinoで使おうと思っていろいろやったついでにNetduino plus 2で使えるかどうか試してみました。 Adafruitの2.2インチ液晶と同じコントローラ(ILI9340)を持っているものっぽいです。

ILI9340の利用例はRaspberry PiとかArduinoではちょこちょこあるしArduinoにはライブラリがあるのですが、 Netduinoでの利用例は見当たらなかったので直接コントロールして表示しています。

ちなみにこの液晶はロジックの電圧に3.3Vを要求していて5Vでつなぐとダメで、 Arduino Unoに直結すると動かないのですが(なのでTrinket ProやArduino M0 Proで先に動かした)、 Netduinoは3.3Vなので動かせそうとだなと思ってつないでコードを書いたところちゃんと動作しました。

接続はArduinoのSPIと同じで以下のような感じ。

LCD側ピンNetduino側ピン備考
VCC 3.3V
GND GND
CS D10 SPI / チップセレクト(SS)
RESET D8
D/C D9 コマンド用
SDI/MOSI D11 SPI
SCK D13 SPI / クロック(SCLK)
LED 3.3V
SDO/MISO D12 SPI

実装にはaitendoのデモコードとImageWriter: Raspberry Pi (9) LCD表示 1Adafruitのライブラリを参考にして、 SPIでひたすらコマンドとデータを転送する形になっています。実際のコードは以下の通りです。

なお、Netduino plus 2はRAMが100KBちょっとということもあり、QVGAのピクセルデータを全部保持することもできないので、 線や図形を適宜描画するか、画像はSDカードから読み込んで表示するといったことが必要となります。

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Microsoft.SPOT;
using Microsoft.SPOT.Hardware;
using SecretLabs.NETMF.Hardware;
using SecretLabs.NETMF.Hardware.Netduino;
using System.IO;

namespace NetduinoApplication2
{
    public class Program
    {
        // DC はコマンドを発行するときに操作する用の線
        static OutputPort _dcPort;
        // RST はリセット用の線
        static OutputPort _resetPort;
        // SPI
        static SPI _spi;

        public static void Main()
        {
            _dcPort = new OutputPort(Pins.GPIO_PIN_D9, true);
            _resetPort = new OutputPort(Pins.GPIO_PIN_D8, false);

            // SPIをセットアップ
            _spi = new SPI(new SPI.Configuration(Pins.GPIO_PIN_D10, false, 0, 0, true, true, 8000, SPI_Devices.SPI1));

            // リセットをかける
            _resetPort.Write(false); // LOW (Reset)
            Thread.Sleep(100);
            _resetPort.Write(true); // HIGH (Reset off)
            Thread.Sleep(100);

            // ILI9340を初期化する
            InitializeLcd();

            // ちなみに初期化後は白でも黒でもない中途半端な状態になるので塗りつぶしをしないとしましました風に見える

            if (false)
            { 
                // パターン1: SDカードのファイルから読み込んで画像を表示する
                // ファイルの中身は240x320の RRGGGBB のバイト列

                // まず描画先のアドレスを指定する: (0, 0) -> (239, 319)
                SetAddress(0, 0, 239, 319);

                // ファイルを読んで流し込む
                var path = @"SD\nanikabitmapdata.bin";
                var buffer = new byte[16 * 1024]; // 16KB ごとに読み出す
                using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read))
                {
                    int readLen = 0;

                    while (true)
                    {
                        readLen = stream.Read(buffer, 0, buffer.Length);

                        if (readLen == 0) break;

                        // byte列で送信しないととても遅いので注意
                        if (readLen != buffer.Length)
                        {
                            var tmp = new byte[readLen];
                            Array.Copy(buffer, tmp, readLen);

                            WriteData(tmp);
                        }
                        else
                        {
                            WriteData(buffer);
                        }
                    }
                }
            }
            else
            {
                // パターン2: ピクセルを直接操作して矩形を書く
                ushort x = 100;
                ushort y = 100;
                ushort width = 40;
                ushort height = 40;
                ushort color = 0x07E0; // 緑

                // まず描画先のアドレスを指定する: (100, 100) -> (139, 139)
                SetAddress(x, y, (ushort)(x + width - 1), (ushort)(y + height - 1));
                // ピクセルデータを流し込む
                for (var i = 0; i < width * height; i++)
                {
                    WriteData(color);
                }
            }
        }

        static void InitializeLcd()
        {
            // 診断用コード
            Debug.Print("Display Power Mode: 0x" + ReadCommand(0x0A).ToString("x"));
            Debug.Print("MADCTL Mode: 0x" + ReadCommand(0x0B).ToString("x"));
            Debug.Print("Pixel Format: 0x" + ReadCommand(0x0C).ToString("x"));
            Debug.Print("Image Format: 0x" + ReadCommand(0x0A).ToString("x"));
            Debug.Print("Self Diagnostic: 0x" + ReadCommand(0x0F).ToString("x"));

            // この辺はILI9340の初期化コード
            // コマンドとデータはAdafruitのライブラリを参考にするとてっとり早いです
            // https://github.com/adafruit/Adafruit_ILI9340/blob/master/Adafruit_ILI9340.h
            WriteCommandAndData(0xEF, new byte[] { 0x03, 0x80, 0x02 });
            WriteCommandAndData(0xCF, new byte[] { 0x00, 0xC1, 0x30 });
            WriteCommandAndData(0xED, new byte[] { 0x64, 0x03, 0x12, 0x81 });
            WriteCommandAndData(0xE8, new byte[] { 0x85, 0x00, 0x78 });
            WriteCommandAndData(0xCB, new byte[] { 0x39, 0x2C, 0x00, 0x34, 0x02 });
            WriteCommandAndData(0xF7, new byte[] { 0x20 });
            WriteCommandAndData(0xEA, new byte[] { 0x00, 0x00 });
            WriteCommandAndData(0xC0, new byte[] { 0x23 });
            //Power control
            //SAP[2:0];BT[3:0]
            WriteCommandAndData(0xC1, new byte[] { 0x10 });
            //VCM control
            WriteCommandAndData(0xC5, new byte[] { 0x3e, 0x28 });
            //VCM control2
            WriteCommandAndData(0xC7, new byte[] { 0x86 });
            // Memory Access Control
            WriteCommandAndData(0x36, new byte[] { 0x48 }); // 回転はこの辺
            WriteCommandAndData(0x3A, new byte[] { 0x55 });
            WriteCommandAndData(0xB1, new byte[] { 0x00, 0x18 });
            // Display Function Control
            WriteCommandAndData(0xB6, new byte[] { 0x08, 0x82, 0x27 });
            // 3Gamma Function Disable
            WriteCommandAndData(0xF2, new byte[] { 0x00 });
            //Gamma curve selected 
            WriteCommandAndData(0x26, new byte[] { 0x01 });
            //Set Gamma
            WriteCommandAndData(0xE0, new byte[] { 0x0F, 0x31, 0x2B, 0x0C, 0x0E, 0x08, 0x4E, 0xF1, 0x37, 0x07, 0x10, 0x03, 0x0E, 0x09, 0x00 });
            //Set Gamma
            WriteCommandAndData(0XE1, new byte[] { 0x00, 0x0E, 0x14, 0x03, 0x11, 0x07, 0x31, 0xC1, 0x48, 0x08, 0x0F, 0x0C, 0x31, 0x36, 0x0F });
            //Exit Sleep
            WriteCommand(0x11);

            Thread.Sleep(120);

            //Display on
            WriteCommand(0x29);
        }

        /// <summary>
        /// 描画先アドレスを設定する
        /// </summary>
        /// <param name="x">開始X座標</param>
        /// <param name="y">開始Y座標</param>
        /// <param name="x2">終了X座標</param>
        /// <param name="y2">終了Y座標</param>
        static void SetAddress(ushort x, ushort y, ushort x2, ushort y2)
        {
            WriteCommand(0x2A);
            WriteData(x);
            WriteData(x2);
            WriteCommand(0x2B);
            WriteData(y);
            WriteData(y2);
            WriteCommand(0x2C);
        }

        /// <summary>
        /// コマンドを発行して結果を取得します。
        /// </summary>
        /// <param name="command"></param>
        /// <returns></returns>
        static byte ReadCommand(byte command)
        {
            var buffer = new byte[1];
            buffer[0] = command;

            _dcPort.Write(false);
            _spi.WriteRead(buffer, buffer);

            _dcPort.Write(true);
            buffer[0] = 0x0;
            _spi.WriteRead(buffer, buffer);

            return buffer[0];
        }

        /// <summary>
        /// コマンドを発行します。
        /// </summary>
        /// <param name="command"></param>
        static void WriteCommand(byte command)
        {
            var buffer = new byte[1];
            buffer[0] = command;

            _dcPort.Write(false);
            _spi.WriteRead(buffer, buffer);
        }

        /// <summary>
        /// 符号なし16bit整数データを書き込みます。
        /// </summary>
        /// <param name="data"></param>
        static void WriteData(ushort data)
        {
            var hi = (byte)(data >> 8);
            var lo = (byte)(data & 0xFF);

            WriteData(new[] { hi, lo });
        }

        /// <summary>
        /// バイトデータを書き込みます。
        /// </summary>
        /// <param name="data"></param>
        static void WriteData(byte data)
        {
            WriteData(new[] { data });
        }

        /// <summary>
        /// バイト列データを書き込みます
        /// </summary>
        /// <param name="data"></param>
        static void WriteData(byte[] data)
        {
            _dcPort.Write(true);
            _spi.Write(data);
        }

        /// <summary>
        /// コマンドを発行してデータを書き込みます。
        /// </summary>
        /// <param name="command"></param>
        /// <param name="data"></param>
        static void WriteCommandAndData(byte command, byte[] data)
        {
            WriteCommand(command);
            WriteData(data);
        }
    }
}

液晶で何か表示できると結構面白そうな感じなのです。

Visual Studio Codeのエディター部分(Monaco)のみをビルドする

Created at:

Visual Studio CodeのエディターをWebページに埋め込むVisual Studio CodeのシンタックスハイライトをWebページで使うで Visual Studio Codeのエディター部分いわゆるMonaco EditorをCodeから引きはがして使っていました。

Monacoはそれ単体で配布されていたりリポジトリーがあったりするわけではないのですが、実はCodeのgulpfile.editor.jsにエディター部分(Monaco)だけをビルドするビルドタスクがあります。

まずCodeをビルドできるよう準備して、その後gulpで editor-distro タスクを実行すると以下のディレクトリにMonacoのビルド結果が出力されます。

このディレクトリー以下の一式をアプリケーションやWebサイトに配置して、エディターを組み込むことができます。Mimosaのmonaco-prebuiltはこのout-editor-minを突っ込んだものです。

ちなみにMonacoのライセンスはVisual Studio Codeの一部なのでMIT Licenseとなります。

Visual Studio CodeのエディターをWebページに埋め込む

Created at:

Visual Studio CodeのシンタックスハイライトをWebページで使うという話を少し前に書きましたが、 今回はエディタ部分をWebページに埋め込んでみようという話です。

前にも少し触れましたが、Visual Studio Codeのエディター部分、いわゆるMonacoはVisual Studio Code以前からいろいろなところで埋め込まれて使われています。 Visual Studio Online "Monaco"を始め、TypeScript PlaygroundWinJS Playground、IE/EdgeのF12開発者ツールなどなど、 エディター部分の出来の良さというか埋め込んで使われることが念頭に置かれている感じがします。

動作サンプル

まずは動作するサンプルを貼り付けてみるので、触ってみてください。

適当なところでCtrl/Cmd + スペースを押せばきちんとコードの補完(IntelliSense)が表示されます。 Visual Studio Codeと同じようにJavaScriptモードはTypeScriptモードが元になっているので、型もちゃんと認識されています(helloメソッドの戻り値の型がstringになっています)。

hの後ろでコード補完を表示した

埋め込み方

肝心の埋め込み方ですが、エディター部分を埋め込むだけであればとても簡単です。

最初にエディターのJavaScriptファイルを用意する必要がありますが、 MimosaにあらかじめVisual Studio Codeからエディター部分をビルドしたものを同梱しているのでそれを使うと簡単です。 リポジトリの monaco-prebuilt 以下を引っこ抜いてきてもよいです。

ファイルの準備ができたらHTMLを書きます。エディターの領域をdiv要素で確保してください。

<div id="editor" style="height:200px;"></div>

エディターの表示サイズはエディターにしてほしい部分の要素のサイズによって確定するので、空の要素の場合、高さが0で何も表示できなくなります。 例えばコードの長さに応じた高さ、といったことはできないので注意が必要です。 また、このエディターとなりたい部分の要素の内容は空にしておいてください(エディターが実体化されても残るので)。

次にJavaScriptでMonacoエディターを生成します。

<script>
    require.config({
        baseUrl: '/mimosa/', // これは例によってMonaco(vsディレクトリ)が入っているディレクトリ
    });

    require(["vs/editor/editor.main"], function () {
        // エディター領域を取得する
        var editorE = document.querySelector('#editor');

        // エディターに表示する文字列を組み立てる
        var content = [
            '"use strict";',
            'const message = "コンニチハ!";',
            'class Greeter {',
            '    /** ハロー!! */',
            '    hello() {',
            '        return message;',
            '    }',
            '}',
            'new Greeter().h'
        ].join('\n');

        // エディターを生成する
        var editor = Monaco.Editor.create(editorE, {
            value: content,
            mode: "javascript",
            readOnly: false,
            //theme: "vs-dark",
            scrollBeyondLastLine: false,
            automaticLayout: true,
            autoSize: true,
            scrollbar: {
                handleMouseWheel: true
            }
        });

        // 何かイベントに応じて編集した文字列を取得する
        // console.log(editor.getValue());
    });
</script>

実質的にやることはrequire(["vs/editor/editor.main"])で読み込んで、Monaco.Editor.create メソッドでエディターを生成するということだけです。なんとお手軽。

Monaco.Editor.create メソッドの第二引数はオプションとなるオブジェクトです。 valueが初期文字列、modeが言語(csharpとかjavascriptとか)、readOnlyは編集可能かどうか、それ以外はICommonEditorOptions インターフェースあたりをご覧ください。

これだと表示するだけですので、最終的には編集結果を取得するというのが普通です。 Monaco.Editor.create メソッドの戻り値であるエディターのインスタンスに対して、 getValue メソッドを呼び出すことで編集中の文字列を取得できます。

まとめ

というわけで割と手軽に高性能エディターを自分のアプリケーションやWebページに組み込めるようになりました。 一部Visual Studio Codeの本体側に依存している機能(diagnostics)などもあるのでそういうものは使えなかったりするのですが、 それはそれとしても利用価値はあるのではと思います。

今回は単純に埋め込むところまでなので、次はこのエディターの拡張方法について書くかもしれません。