Windows IoT CoreのArduinoアプリとGUIを連携する

Cooperate with WinIoT Arduino app and UWP app
ほぼ毎日に渡ってお送りしてきたWindows IoT CoreによるGPIO制御プログラミング。今回は総仕上げとして、GUIベースのアプリからArduinoアプリで赤外線信号を受信するテクニックを紹介します。

ソリューションを作成し、フォアグラウンドアプリはC#のユニバーサルアプリ、バックグラウンドアプリはC++のArduino Wireアプリを追加します。具体的な作成方法についてはこちらの記事をご覧ください。

「StartupTask.cpp」を開きます。初期状態のコードを見ると、setup()を読んだ後、loop()を永遠に回すという、Arduino IDEでのコンパイル前のコード変換と同じことをしており、また、このファイルにArduinoの関数を宣言することでinoファイルの関数を呼び出せることがわかります。

そこで、loop()の直後に信号が受信されたかをチェックして、情報があればメモリーの内容を複製して、フォアグラウンドアプリに向けてイベントを発生し、最後に信号を初期化するようにしています。ちなみにValueSetクラスはC#やVBとの互換性はあるものの、継承されているインターフェイスが違うこともあり、メソッドなどの仕様が大幅に異なっている点に注意してください。
startup.cs
#include <ppltasks.h>
using namespace concurrency;

using namespace Platform;
using namespace Windows::Foundation;
using namespace Windows::Foundation::Collections;
using namespace Windows::ApplicationModel::Background;
using namespace Windows::ApplicationModel::AppService;

// These functions should be defined in the sketch file
void setup();
void loop();

bool getSignal(unsigned int **sdata, int *dataLen);
void resetSignal();

namespace BgApp
{
    [Windows::Foundation::Metadata::WebHostHidden]
    public ref class StartupTask sealed : public IBackgroundTask
    {
    private:
        AppServiceConnection^ appcon = nullptr;

    public:
        virtual void Run(IBackgroundTaskInstance^ taskInstance) 
        {
            auto deferral = taskInstance->GetDeferral();

            AppServiceTriggerDetails^ triggerDetails = dynamic_cast<AppServiceTriggerDetails^>(taskInstance->TriggerDetails);
            appcon = triggerDetails->AppServiceConnection;
            // フォアグラウンドからデータをを受け取りたいときのサンプル
            //appcon->RequestReceived += ref new TypedEventHandler<AppServiceConnection^, AppServiceRequestReceivedEventArgs^>
            //    (this, &StartupTask::OnConnectionReceived);

            unsigned int *data;
            int dataLen;

            setup();
            while (true)
            {
                loop();

                if (getSignal(&data, &dataLen) == true) {
                    auto sendData = ref new Array<unsigned int>(data, dataLen);
                    auto messages = ref new ValueSet();
                    messages->Insert("infrared", sendData);
                    try {
                        create_task(appcon->SendMessageAsync(messages)).wait();
                    }catch(Exception ^ex){ }
                    resetSignal();
                }
            }
        }
    };
}
inoファイルにも手を加えます。
signal.ino
#ifdef _WIN_IOT
// For communicating to C++/CX
#undef boolean
#define boolean bool
#endif

#define MAX_LEN 512
unsigned int data[MAX_LEN];
int dataPos;

boolean needSend = false;

boolean getSignal(unsigned int **sdata, int *dataLen)
{
    if (needSend == true) {
        *sdata = data;
        *dataLen = dataPos;
    } else {
        *sdata = NULL;
        *dataLen = 0;
    }
    return needSend;
}

// --------------

const int modulePin = GPIO_17;
const long waitMicros = 1000000;

unsigned long lastMicros;
int moduleStatus, totalMS;

void resetSignal()
{
    dataPos = 0;
    totalMS = 0;
    needSend = false;
    lastMicros = micros();
}

void setup() {
    pinMode(modulePin, INPUT);
    moduleStatus = HIGH;

    resetSignal();
}

boolean waitSignal()
{
    unsigned long waitStartMicros = micros();
    while (digitalRead(modulePin) == moduleStatus) {
        if (micros() - waitStartMicros > waitMicros) return true;
        delayMicroseconds(100);
    }
    return false;
}

void loop() {
    if (dataPos >= MAX_LEN || waitSignal() == true) {
        if (totalMS > 4000) {    //45msecが基準
            needSend = true;
        }
        return;
    }

    unsigned long now = micros();

    int sc = (now - lastMicros) / 10;
    if (dataPos > 0) totalMS += sc;
    data[dataPos++] = sc;

    lastMicros = now;

    moduleStatus = !moduleStatus;
}
フルプロジェクトはこちらよりダウンロードできます。ほかに必要なテクニックはこれまでの記事でさんざん語り尽くしているので、最終回は短い内容になってしまいましたが、Windows 10 IoT Coreでも電子工作のためのOSして実用的であることを理解していただけたならば幸いです。
2016/11/05