Visual Studio 2017のJavaScriptクロスプラットフォームプロジェクトはCorodovaの技術を用いてビルドされています。従来のCordovaではNode.jsを導入して、コマンドラインよりゴリゴリビルドするのが定石でしたが、Visual Studioならそういった面倒な手間を一手に担ってくれるので、開発効率が格段に上がります。

UWPのAPIの大部分もJavaScriptとして、そのまま利用することができます。しかしながら、画像やビデオのサムネイル画像を作成してくれるFileThumbnailサンプルなど、ローカルファイルを参照するプログラムをそのまま移植しても、Corodovaプロジェクトではエラーになってしまいます。

これはVisual Studioが作製するHTMLテンプレートにXSSの危険性を抑えるためのメタタグ「Content Security Policy(CSP)」がUWPの仕様を定義していないために起こります。この問題を解決するには、UWPのローカルファイル参照プロトコルである「blob」を許可するコードを追加すれば良いことになります(青色の文字が追加した箇所)。

<meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: blob: 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *">

これにより「URL.createObjectURL」で変換したファイルをHTMLファイルで自由に使うことができるようになります。

 | 2017年7月19日

マウスカーソルのすぐそばでIMEが有効になっているかの状態を一目で確認できるユーティリティ「IMEステータス」のVer 2.17を公開しました。このバージョンでは、半角状態の判定を有効にした場合、64bit OSで識別時にエラーが発生することがある問題を修正しています。

 | 2017年7月19日

ホームセンターの浴室コーナーで知ったネタですが、今時の浴室の壁は断熱材として金属板を使っており、その上から防水壁紙を貼っています。そのため、タイルではない今時の浴室の壁であれば、磁石がひっつきます。既存のよくある吸盤タイプは素材が劣化しやすく、吸着力がすぐ落ちてしまいますが、100円均一で売っているネオジム磁石なら大抵のものを半永久的に固定することができます。DIYの際の参考にいかがでしょうか。

 | 2017年7月12日

マウスカーソルのすぐそばでIMEが有効になっているかの状態を一目で確認できるユーティリティ「IMEステータス」のVer 2.16を公開しました。このバージョンでは、Windows 7で起動しない問題を修正しています。

普段はベクターでの公開に合わせてアップロードするのですが、今回のは深刻な不具合のため、例外的にTNKソフトウェアサイト内にて先行で公開いたしました。

 | 2017年7月5日

モトローラ社のDSDS対応SIMフリー「Moto G4 Plus」の中古を購入したのですが、近所に専用の液晶保護フィルムが売っていなかったので100円均一の保護フィルムを切って自作してみることにしました。結論から言えば、カッターナイフだとボタンの周辺が自然に浮き出てしまい、それが気泡の原因となってしまうので断念しました。おそらくカッティングマシンなどの正確に切断できる機械を使えば、ちゃんと使えると思うのですが、それならネットで市販品を買った方が安いので、供養の意味を込めて私が独自に作成した型紙を無料で配布します。SVG形式なので、InkScapeをはじめとするベクターグラフィック作成ソフトで再編集が可能です。


ダウンロードはこちらから
 | 2017年7月5日

マウスカーソルのすぐそばでIMEが有効になっているかの状態を一目で確認できるユーティリティ「IMEステータス」のVer 2.15の機能限定版となる無料版を「オールフリーソフト」で取り上げていただけました。

詳細な解説を書いていただいたことに、この場を借りてオールフリーソフトの管理人様にお礼を申し上げます。


(※スクリーンショットは完全版のものです)
 | 2017年6月26日

マウスカーソルのすぐそばでIMEが有効になっているかの状態を一目で確認できるユーティリティ「IMEステータス」のVer 2.15を公開しました。このバージョンでは、Windows 8.1より前のOSでは、タスクトレイのアイコングループにおけるアプリのアイコンが見づらいとの指摘がありましたので、Windows 10より前のOSでは従来の黒縁のアイコンを表示するように修正しました。

 
※スクリーンショットはイメージです
 | 2017年6月22日

ボタンやスクリーン、キーボードと言った、操作に必要なレイアウトのデザインが自由にできるリモートデスクトップアプリ「デスクトップPCコントローラー for Windows」および「デスクトップPCコントローラー for Windows 10」のスキン作成ソフトとクライアントソフトをアップデートしました。最新版では、コマンド実行時において、起動引数のためなどの空白文字が含まれていると異常終了する問題を修正しています。


 | 2017年6月3日

大阪日本橋の電子パーツショップ・デジットの200円LCD「AM50288H」。かつては段ボールいっぱいに入っているのを見かけていたのですが、最近では残りもわずかとなってきました。そんなじわじわ売れゆくLCDを見て、昔紹介したプログラミング記事を改良したい気持ちに駆られたので、今回は題名の通りのプログラミングテクニックをご紹介します。このノウハウを使って「これはWindows IoT Coreで作ったライブラリーのソースだけど、Arduinoでも手を加えることなく、すぐに使えるよ」とPRすれば、より多くの開発者の注目を集められるかも!?

まずは、「AM50288H」を制御するC#コードをArduinoライブラリーとして動作することを意識して、C++言語で書き直してみます。VC++独自拡張の「#pragma once」を使用しない、「#include <arduino.h>」を追加する二点を守れば、ArduinoでもWindows IoT Coreでも動作するコードが記述できます。以下のコード例は一部割愛しているので、すべてのコードをご覧になりたい方はこのページの最下部にあるリンクから入手してください。
// libAM50288H.h
#ifndef __AM50288H_H__
#define __AM50288H_H__

#include <arduino.h>

class AM50288H
{
private:
    enum SegmentBit
    {
    }
    const static int Digits = 8;
    const static int Segments = 14;
    const static int DinsLen = 142;
    const SegmentBit segarray[Digits][Segments] = {
        {...},
        {...}
    };

    byte ple, pdin, psck = 0xFF;
    byte dins[DinsLen];

public:
    AM50288H();
    AM50288H(byte pinLE, byte pinDIN, byte pinSCK);
    ~AM50288H();

    void setPins(byte pinLE, byte pinDIN, byte pinSCK);
    void begin();
    void update();
    void clear();
};

#endif __AM50288H_H__

以下の写真はこのC++クラスをArduino IDEのスケッチに書きこみ、実行した例です(このスケッチも"sketch_am50288h.ino"としてサンプルに含まれています)。動作確認ができたら、Windows IoT Coreに組み込んでみましょう。


ArduinoはC++言語がベースなので、Windows IoT CoreではC++/CXランタイムライブラリとしてプロジェクトを作成します。プロジェクトを作ったら、先のクラスをプロジェクトに追加し、ランタイムライブラリの仕様内に収まるようにラッパークラスを作ります。ちなみに、C言語のenumはランタイムライブラリとの互換性がないため、橋渡しのためのC++/CXとしてのenumを必要に応じて用意する必要があるのですが、これにFlags属性を加える際は「 : unsigned int」の一文がないとエラーになる点に注意しましょう。
// AM50288H.h
#pragma once

#include "libAM50288H.h"

namespace Tnksoft {
namespace AM50288H {
    public enum class Mark
    {
    };

    [Platform::Metadata::Flags]
    public enum class SegmentPosition : unsigned int
    {
    };

    public ref class AM50288H sealed
    {
    private:
        ::AM50288H _am; //Arduino向けのオリジナルクラス
    public:
        AM50288H();
        void begin(byte pinLE, byte pinDIN, byte pinSCK);
        void update();
        void clear();
    };
}
}

// AM50288H.cpp
#include "AM50288H.h"

namespace Tnksoft {
namespace AM50288H {
    AM50288H::AM50288H()
    {
    }

    void AM50288H::begin(byte pinLE, byte pinDIN, byte pinSCK)
    {
        _am.setPins(pinLE, pinDIN, pinSCK);
        _am.begin();
    }

    void AM50288H::update()
    {
        _am.update();
    }

    void AM50288H::clear()
    {
        _am.clear();
    }
}
}

今回は「Tnksoft.AM50288」という名前空間でランタイムクラスを定義したので、プロジェクトプロパティの「Windowsメタデータファイル」の項目を変更するがあり、このライブラリーの場合は「$(OutDir)Tnksoft.AM50288H.winmd」となります。

ビルドに成功したら、このランタイムライブラリをDMAPドライバーで稼働するUWPアプリで参照すれば、機器の制御が簡単かつ高速に行えるようになります。


using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml;
using Tnksoft.AM50288H;

namespace AM50288HDemo
{
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();

            amh = new AM50288H();
            // GPIO5 = 29, GPIO6 = 31, GPIO13 = 33 on Raspberry Pi
            amh.begin(29, 31, 33);
        }
    }
}

サンプルソースのダウンロード(IoTプログラミングのページに移動します)
, | 2017年5月30日

過去に何度も取り上げているArduino Wiring on Windows 10 IoT Coreですが、ドライバーの開発における勉強の過程で、「Arduino APIは所詮C++ラッパー。別にバックグラウンドアプリでなくても使えるのではないか」と思い、UWPプロジェクトでArduino関数を使う方法を試してみました。

まずはVisual StudioでUWPアプリプロジェクトを作成します。しかし、メインアプリの開発にC++/CXを使うのは相当な困難が伴いますので、GUI部分はC#言語で開発し、C++/CXランタイムコンポーネントでGPIOを制御することにしました。

そのため、Visual C++ユニバーサルWindowsランタイムライブラリプロジェクトを追加することにします。このプロジェクトに「Windows Desktop Extensions for the UWP」と「Windows IoT Extensions for the UWP」参照を追加し、NuGetより「Microsoft.IoT.Lightning」を登録します。

コンポーネントにはマニフェストの概念がないため、低レベルアクセスのパーミッションはC#アプリのappxmanifestに追加することになります。具体的な追加方法は過去の当該記事をご覧ください。

続いてC++/CXプロジェクトでラッパークラスを作ります。Windows IoT CoreのArduino APIはMicrosoft.IoT.Lightningに含まれる「Arduino.h」をインクルードするだけで、C++/CXファイルからでも簡単に呼び出すことができます。

namespace ArduinoLibrary
{
public enum class GpioPin
{
    P2 = 3, P3 = 5, P4 = 7, ...
};

public ref class Arduino sealed
{
public:
    static void pinMode(GpioPin pin, PinMode mode);
    static PinValue digitalRead(GpioPin pin);
    static void digitalWrite(GpioPin pin, PinValue state);
};
}

#include <arduino.h>
#include "ArduinoFunctions.h"

using namespace Platform;
using namespace ArduinoLibrary;

void Arduino::pinMode(GpioPin pin, PinMode mode)
{
    ::pinMode((byte)pin, (uint8_t)mode);
}

PinValue Arduino::digitalRead(GpioPin pin)
{
    return (PinValue)::digitalRead((byte)pin);
}

void Arduino::digitalWrite(GpioPin pin, PinValue state)
{
    return ::digitalWrite((byte)pin, (uint8_t)state);
}

C#から呼び出す例です。
private async void RuntimeTest()
{
    await Task.Run(() => {
        Arduino.pinMode(GpioPin.P5, PinMode.OUTPUT);
        Arduino.pinMode(GpioPin.P6, PinMode.OUTPUT);
        Arduino.digitalWrite(GpioPin.P5, PinValue.LOW);
        Arduino.digitalWrite(GpioPin.P6, PinValue.LOW);

        while (true) {
            uint m = Arduino.millis();
            Arduino.digitalWrite(GpioPin.P5, PinValue.HIGH);
            Arduino.delay(10);

            while (Arduino.millis() - m < 10000) {
                Arduino.digitalWrite(GpioPin.P6, PinValue.HIGH);
                Arduino.delayMicroseconds(50);
                Arduino.digitalWrite(GpioPin.P6, PinValue.LOW);
            }

            Arduino.delay(10);
            Arduino.digitalWrite(GpioPin.P5, PinValue.LOW);

            Arduino.delay(500);
        }
    });
}

……と、ここまで書いておいてなんですが、この方法だとWindowsランタイムが頻繁に介入するため、実際に動かしてみると、速度やタイミングが不安定で、ダイレクトメモリーマッピングの恩恵をあまり受けられないのが実情です。そのため、以下のようにGPIOを制御するルーチンはできる限りランタイムライブラリにまとめた方が良いでしょう。
void Arduino::frequencyTest1()
{
    ::pinMode(GPIO5, DIRECTION_OUT);
    ::pinMode(GPIO6, DIRECTION_OUT);
    ::digitalWrite(GPIO5, LOW);
    ::digitalWrite(GPIO6, LOW);

    while (true) {
        unsigned long m = millis();
        ::digitalWrite(GPIO5, HIGH);
        ::delay(10);

        while (millis() - m < 10000) {
            ::digitalWrite(GPIO6, HIGH);
            ::delayMicroseconds(50);
            ::digitalWrite(GPIO6, LOW);
        }

        ::delay(10);
        ::digitalWrite(GPIO5, LOW);

        ::delay(500);
    }
}


おまけ:inoファイルをC++/CXプロジェクトに追加する方法
inoファイルをVisual C++にC言語と見なさせることで、ランタイムライブラリでもinoファイルを直接プロジェクトに追加することができます。任意の名前を持つinoファイルをプロジェクトに追加したら、ソリューションエクスプローラーより、inoファイルのプロパティを開きます。ここより「全般」の「項目の種類」に「C/C++コンパイラ」を選択し、「C/C++詳細設定」の「必ず使用されるインクルードファイル」に「Arduino.h」を追加します。

inoファイルのコンパイル方法指定 Arduino.hを自動インクルード
これにより、inoファイルに特別な定義を追加せずとも、Arduino IDE同様の記述ができるようになるため、Arduino向けのプログラムの移植がより容易になります。

// C++/CX file
void setup();
void loop();

void Arduino::frequencyTest2()
{
    setup();
    while(true) loop();
}

// test.ino
void setup()
{
    pinMode(GPIO5, OUTPUT);
    pinMode(GPIO6, OUTPUT);
    digitalWrite(GPIO5, LOW);
    digitalWrite(GPIO6, LOW);
}

void loop()
{
    unsigned long m = millis();
    digitalWrite(GPIO5, HIGH);
    delay(10);

    while (millis() - m < 10000) {
        digitalWrite(GPIO6, HIGH);
        delayMicroseconds(50);
        digitalWrite(GPIO6, LOW);
    }

    delay(10);
    digitalWrite(GPIO5, LOW);

    delay(500);
}

以前紹介したバックグラウンドアプリと通信する方法だと、リソースが不足したときにOSがバックグラウンドアプリを止めてしまう可能性があります。そのため、アプリが動いている間はGPIO制御の動作も保証されるという点を考慮すると、フォアグラウンドアプリとして埋め込む利点は十分にあると思います。

ちなみにArduino UnoとRaspberry Piを接続して簡易的な性能テストをしてみましたが、C++/CXにAPIを埋め込む方法と、inoファイルに記述した関数を呼び出す方法に目立つ性能差はありませんでしたので、どちらを採用するかは開発者の好みでよいかと思います。サンプルプログラムにはテスト内容やinoファイルまで今回紹介した内容一式がすべて含まれているので、オシロスコープなどの高度な計測ができる機器で試されたら、結果を私にも教えてください。

サンプルソースのダウンロード(IoTプログラミングのページに移動します)
, | 2017年5月19日