Привет, Хабр.

Наверное каждый разработчик на определенном этапе задумывался о собственном IoT-проекте. Internet of Things сейчас поистине вездесущ и многим из нас хочется попробовать свои силы. Но не все знают, с чего начать и за что браться в первую очередь. Сегодня давайте посмотрим, как легко и непринужденно запустить свой собственный IoT-проект под Raspberry Pi 2, используя Windows 10 IoT Core и DeviceHive.

Деплоим Windows 10 приложения на Raspberry Pi 2


Для начала давайте установим Windows 10 IoT Core на Raspberry Pi. Для этого нам потребуется Windows 10 IoT Core Dashboard, который можно взять вот здесь. Там же можно при желании скачать отдельно ISO-образ, но особого смысла в этом нет — инструмент сделает это за вас.

Затем мы загружаем образ на misroSD-флешку.



Подключаем флешку к Raspberry и включаем. Первую загрузку ОС придется подождать, мгновенной она, конечно, не будет. Когда устройство «оживет» — подключаем Raspberry к локальной сети по Ethernet. Снова открываем Windows 10 IoT Core Dashboard и видим в списке «Мои устройства» заветную строчку. К слову, можно обойтись и без проводного подключения – список WiFi-донглов, поддерживаемых Windows 10 IoT Core, находится тут.

Далее нам понадобится Visual Studio 2015. Если она у вас все еще не установлена (хотя вы бы вряд ли читали эту статью в таком случае), можно скачать Community Edition.

Создаем новый или же открываем существующий Windows Universal проект. Кстати, если в проекте не нужен UI, можно создать Headless Application, выбрав тип проекта Windows IoT Core Background Application.



Выбираем деплой на Remote Machine.



Вводим адрес Raspberry. Посмотреть его можно на стартовом экране Win10 IoT Core или в Windows 10 IoT Core Dashboard.



Собственно, Internet of Things



Раз уж у нас статья о embedded — «моргать светодиодами» придется в любом случае. Хорошо, что мы имеем дело с DeviceHive, у которого заготовлены инструменты на все случаи жизни и все платформы. Поэтому светодиод будет виртуальный и тоже на .NET.

Клонируем master-ветку DeviceHive.NET репозитория с GitHub. На момент написания статьи рабочие примеры для Win10 IoT были именно там.

Открываем solution DeviceHive.Device и в файле Program.cs проекта VirtualLed настраиваем доступ к песочнице DeviceHive.

using (var service = new RestfulDeviceService("http://playground.devicehive.com/api/rest"))
{
    // create a DeviceHive network where our device will reside
    var network = new Network("Network WPNBEP", "Playground Network", "%NETWORK_KEY%");

    //...
}


Если вы интересуетесь IoT, но по какой-то немыслимой причине еще не обзавелись DeviceHive Playground – это можно сделать здесь.



А управлять нашим «светодиодом» будет… Нет, пока не Raspberry, а клиент виртуального светодиода. Пример находится в проекте VirtualLedClient солюшена DeviceHive.Client. Его тоже нужно настроить в файле Program.cs:

var connectionInfo = new DeviceHiveConnectionInfo("http://playground.devicehive.com/api/rest", "%ACCESS_KEY%");


Самое интересное



Наше приложение на Raspberry Pi будет не просто кнопочкой включения/выключения светодиода, а практически полноценной админкой всех IoT-устройств нашей DeviceHive-сети. При желании, конечно, можно упростить его до той самой «кнопочки» или наоборот расширить, например, до клиента, управляющего роботом телеприсутствия.

Готовое приложение находится в том же репозитории, в solution DeviceHive.WindowsManager.Universal. Не будем останавливаться на нюансах гайдлайнов Win10 – корни приложения растут еще из Win8. Не будет тут и MVVM – все и так знают, как его применять. Давайте сосредоточимся на главном: нам нужна консоль мониторинга и управления устройствами, подключенными к DeviceHive, под Windows 10 на Raspberry Pi2.



Для DeviceHive реализовано три клиентских библиотеки:
  • DeviceHive.Client – для «большого» .NET 4.5 и выше. Использует WebSocket4Net.
  • DeviceHive.Client.Portable – для Windows 8.1 и Windows Phone 8.1. Использует нативные WebSockets.
  • DeviceHive.Client.Universal – для всех редакций Windows 10, в том числе для Win10 IoT Core. Именно она используется в нашем приложении.

Наследуем ClientService от DeviceHiveClient и инициализируем его сеттингами:

DeviceHiveConnectionInfo connInfo;
if (!String.IsNullOrEmpty(Settings.Instance.CloudAccessKey))
{
    connInfo = new DeviceHiveConnectionInfo(Settings.Instance.CloudServerUrl, Settings.Instance.CloudAccessKey);
}
else
{
    connInfo = new DeviceHiveConnectionInfo(Settings.Instance.CloudServerUrl, Settings.Instance.CloudUsername, Settings.Instance.CloudPassword);
}
current = new ClientService(connInfo, new RestClient(connInfo));


А также указываем не использовать LongPolling, а только WebSocket, дабы не упираться в лимит одновременных HTTP-запросов:

SetAvailableChannels(new Channel[] {
    new WebSocketChannel(connectionInfo, restClient)
});



Загружаем список девайсов и группируем их по сетям в MainPage:

var deviceList = await ClientService.Current.GetDevicesAsync();
var networkList = (await ClientService.Current.GetNetworksAsync()).FindAll(n => n.Id != null);
foreach (Network network in networkList)
{
    var devices = deviceList.FindAll(d => d.Network?.Id == network.Id);
    if (devices.Count > 0)
    {
        networkWithDevicesList.Add(new NetworkViewModel(network) { Devices = devices });
    }
}

А вот и наш виртуальный светодиод:



Переходим на DevicePage, подгружаем информацию о нем:

Device = await ClientService.Current.GetDeviceAsync(deviceId);



Переключаемся на вкладку с уведомлениями. Уведомления отправляются от управляемого устройства к управляющему устройству. В нашем случае – от VirtualLedClient к VirtualLed.
Инициализируем автоподгружающийся список с «бесконечным» скроллом:

NotificationFilter filter = new NotificationFilter()
{
    End = filterNotificationsEnd,
    Start = filterNotificationsStart,
    SortOrder = SortOrder.DESC
};
var list = new IncrementalLoadingCollection<Notification>(async (take, skip) =>
{
    filter.Skip = (int)skip;
    filter.Take = (int)take;
    return await ClientService.Current.GetNotificationsAsync(deviceId, filter);
}, 20);


Если не определена конечная дата фильтрации списка нотификаций, подписываемся на новые уведомления, которые будут приходить через вебсокет:

notificationsSubscription = await ClientService.Current.AddNotificationSubscriptionAsync(new[] { deviceId }, null, async (notificationReceived) =>
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        lock (NotificationsObservable)
        {
            if (!NotificationsObservable.Any(c => c.Id == notificationReceived.Notification.Id))
            {
                NotificationsObservable.Insert(0, notificationReceived.Notification);
            }
        }
    });
});

Если попробовать переключать наш виртуальный светодиод, то уведомления о его новом состоянии тут же отобразятся в списке.



Если поменять настройки фильтрации, то автоподгружающийся список заново инициализируется с новым фильтром.



Теперь пришла очередь вкладки команд. Команды похожи на нотификации, но направлены от управляющего устройства к управляемому, а также могут иметь статус и результат выполнения.

CommandFilter filter = new CommandFilter()
{
    End = filterCommandsEnd,
    Start = filterCommandsStart,
    SortOrder = SortOrder.DESC
};
var list = new IncrementalLoadingCollection<Command>(async (take, skip) =>
{
    filter.Skip = (int)skip;
    filter.Take = (int)take;
    return await ClientService.Current.GetCommandsAsync(deviceId, filter);
}, 20);


Аналогично подписываемся на новые команды:

commandsSubscription = await ClientService.Current.AddCommandSubscriptionAsync(new[] { deviceId }, null, async (commandReceived) =>
{
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        lock (CommandsObservable)
        {
            if (!CommandsObservable.Any(c => c.Id == commandReceived.Command.Id))
            {
                CommandsObservable.Insert(0, commandReceived.Command);
            }
        }
    });
});




Поскольку мы делаем инструмент не только для мониторинга, но и для управления устройствами в DeviceHive сети, нужно реализовать возможность отправки команд:

var parameters = commandParams.Text != "" ? JObject.Parse(commandParams.Text) : null;
var command = new Command(commandName.Text, parameters);
await ClientService.Current.SendCommandAsync(deviceId, command, CommandResultCallback);


При отправке команды мы подписались на ее обновление методом CommandResultCallback. Обрабатываем результат выполнения команды:

foreach (Command cmd in CommandsObservable)
{
  if (command.Id == cmd.Id)  
  {
        // Command class doesn't implement INotifyPropertyChanded to update its result,
        // so old command is replaced by command with result:
        var index = commandsObservable.IndexOf(cmd);
        commandsObservable.RemoveAt(index);
        commandsObservable.Insert(index, command);
        break;
    }
}




Чтобы не копировать команды вручную, нужно предусмотреть клонирование команд. Выделяем, клонируем, если нужно – редактируем, отправляем.



Задача выполнена! Как видите, Raspberry Pi 2 c Windows 10 IoT Core и DeviceHive – отличное решение для практически любой задачи в контексте Internet of Things. Прикрутите пару кнопок, dashboard и подключите Raspberry Pi к телевизору в гостиной – мониторинг и управление умным домом готово. Купили лишних Raspberry? Не вопрос, библиотека DeviceHive.Client умеет работать не только в качестве управляющего клиента, но и в качестве управляемого девайса – реализуем Headless Application, подключаем датчики/реле и устанавливаем Raspberry Pi по дому. Ограничивает вас лишь ваша фантазия.

Заключение



Появление Windows 10 IoT Core – это именно то, чего ждали embedded-разработчики. Когда ресурсов даже самого мощного микроконтроллера на .NET Micro Framework (для которого, кстати, тоже есть реализация DeviceHive) не хватает, а ставить полноценный компьютер на Windows – все равно, что стрелять из пушки по воробьям, то Windows 10 IoT Core – настоящее спасение. И пусть пока есть нюансы с аппаратным ускорением графики и недостатком драйверов для некоторых USB-устройств – это всё простительно. Ведь еще недавно мы только мечтали, чтобы Windows-приложения, работающие на настольных ПК и планшетах запускались не только на телефонах, но и на микрокомпьютерах. А теперь – это реальность, добро пожаловать в «сегодня».




Об авторе


Антон Седышев — Senior .NET-разработчик «DataArt »

В IT работает с далекого 2003, к команде DataArt присоединился в 2012. Ранее занимался разработкой веб- и мобильных проектов, автоматизицией логистических процессов на складах крупной международной компании. Сейчас выступает в роли ведущего .NET-разработчика и идеолога Microsoft-сообщества DataArt. Занимается разработкой приложений на Windows Phone и Windows 10, сервисом DeviceHive и embedded-технологиями вообще. В свободное время работает над собственным OpenSource embedded-проектом по интеграции .NET Micro Framework устройства в автомобили BMW.

Комментарии (8)


  1. raptor
    11.04.2016 12:03
    +1

     if (command.Id == command.Id) ...


    Уверены? может

     if (command.Id == cmd.Id) ... 


    1. toxsedyshev
      11.04.2016 13:37
      +1

      Спасибо! Фикс закоммитил. Статью не я постил, отредактировать не могу, к сожалению.


  1. Nomad1
    11.04.2016 14:57
    +1

    Все, что относится к графике уже год в таком статусе:
    «is not currently supported, as it requires GPU services that are not currently available on the Raspberry Pi because the DirectX driver has not been implemented»
    UI (читай WPF) через программную эмуляцию там все же есть и работает, но пользоваться им толко нельзя, потому и остаются только безголовые решения или с веб-интефейсом управления. Ждем выхода Pi 3 (или сразу 8?), может тогда что-то по софту изменится и можно будет хоть медиа-плееры или центры видеонаблюдения делать.


  1. cyber_genius
    11.04.2016 16:45

    а hardware floating point поддержку сделали?


  1. jkjkjirf
    12.04.2016 11:12
    -1

    >Сегодня давайте посмотрим, как легко и непринужденно запустить свой собственный IoT-проект под Raspberry Pi 2, используя Windows 10 IoT Core и DeviceHive.

    И компьютер с обычной Windows 10. У меня такого нет. Поставить не могу, т.к. SSD 16Gb, да и есть сомнения, что драйвера для Chromebook имеются.

    Получается мне нужно потратить деньги на новое устройство и лицензию операционной системы. А так как я бедный студент мне их придется ещё сначала заработать. И вот уже 2 дня только на подготовку к тому, чтобы начать что-то делать. Выглядит как-то грустно.

    А вот за DeviceHive спасибо. Репозитории с Go и Python вселяют надежду, что бесплатная малина есть и для меня.


    1. IgorGolov
      13.04.2016 13:24

      Лицензия на Windows 10 ещё бесплатная. Скачивайте образ или обновляйтесь через Центр Обновлений. Касательно драйверов: если у Вас есть дрова на Ваш ChromeBook для Windows 7 — Ваша проблема решена. У меня на чипсет дрова от Windows 7 стоят, ОС — Windows 10. Полёт нормальный косяков не замечено.


  1. hexes
    15.04.2016 18:56

    Ребята из DataArt бесспорно молодцы… но…
    https://hub.docker.com/r/devicehive/devicehive-standalone/

    kmcdonald 4 months ago
    Can't seem to get the login credentials to work on the admin console

    martijnw 2 months ago
    login=dhadmin
    password=dhadmin_#911
    login credentials don't work on http://ipaddres/admin Please advice.

    24 мне написали
    Патч в пути, и обновленные docker образы будут скоро доступны.


    Видимо по каким то соображениям standalone версию «задвигают»… жаль…


    1. toxsedyshev
      21.04.2016 15:43

      Коллеги говорят, что пофиксили этот баг и залили на докер два дня назад.