<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="http://www.w3.org/2005/Atom" xmlns:sy="http://purl.org/rss/1.0/modules/syndication/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Дмитрий Сошников, академический евангелист</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/sos/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/sos/feed.xml</link>
    <description />
    <lastBuildDate>Tue, 16 Apr 2019 05:29:59 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Создание виртуальной машины для анализа данных в облаке Microsoft Azure</title>
      <link>https://docs.microsoft.com/archive/blogs/sos/%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D0%BD%D0%B8%D0%B5-%D0%B2%D0%B8%D1%80%D1%82%D1%83%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D0%B9-%D0%BC%D0%B0%D1%88%D0%B8%D0%BD%D1%8B-%D0%B4%D0%BB%D1%8F-%D0%B0%D0%BD%D0%B0%D0%BB</link>
      <pubDate>Tue, 30 May 2017 21:13:27 GMT</pubDate>
      <dc:creator><![CDATA[Dmitry Soshnikov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sos/?p=1065</guid>
      <description><![CDATA[[Оригинал записи находится в моём личном блоге] Для задач машинного обучения как правило требуются...]]></description>
      <content:encoded><![CDATA[<p>[Оригинал записи находится <a href="http://blog.soshnikov.com/2017/05/31/%d1%81%d0%be%d0%b7%d0%b4%d0%b0%d0%bd%d0%b8%d0%b5-%d0%b2%d0%b8%d1%80%d1%82%d1%83%d0%b0%d0%bb%d1%8c%d0%bd%d0%be%d0%b9-%d0%bc%d0%b0%d1%88%d0%b8%d0%bd%d1%8b-%d0%b4%d0%bb%d1%8f-%d0%b0%d0%bd%d0%b0%d0%bb/">в моём личном блоге</a>] <p>Для задач машинного обучения как правило требуются достаточно большие ресурсы: как вычислительные, так и для хранения данных. Поэтому всё большее количество специалистов по анализу данных (датасайнтистов) обращают свой взгляд в сторону облачных ресурсов.  <p>Для всех, кто хочет использовать облако для машинного обучения или глубокого обучения нейросетей, у нас есть хорошая новость – облако Microsoft Azure прекрасно вам подойдёт! И вот почему: <ul> <li>В Azure есть готовый образ <strong>Data Science Virtual Machine</strong>, как под управлением Windows, так и под Linux, на которых уже установлено всё необходимое вам программное обеспечение!  <li>В нашем облаке вам доступны <strong>компьютеры с графическими процессорами </strong>NVidia (т.н. <strong>N-Series VMs</strong>), что сильно ускоряет процесс обучения нейросетей. </li></ul> <p>В этом посте я рассмотрю инструкцию по созданию и настройке виртуальной машины для глубокого обучения в облаке. При этом я буду предполагать, что у вас уже есть облачная подписка Microsoft Azure, привязанная в вашему Microsoft Account – если это не так, то можно обзавестись <a href="http://azure.microsoft.com/ru-RU/free">пробной подпиской</a>. <h3>Data Science Virtual Machine</h3> <p>Для задач обработки данных хорошо подходит специализированная виртуальная машина Data Science Virtual Machine. На ней изначально уже установлены: <ul> <li><a href="https://www.visualstudio.com/">Visual Studio</a> и Visual Studio Code, с поддержкой R и Python  <li><a href="https://anaconda.org/">Anaconda</a> – самая популярная cреда для поддержки различных окружений на Python и не только  <li>Все необходимые фреймворки для обучения нейросетей: Microsoft Cognitive Toolkit (<strong>CNTK</strong>), <strong>Tensorflow</strong>, <strong>MXNet </strong>и др. Причем если вы используете виртуалку с GPU – все утилиты и библиотеки для работы с GPU (драйвера NVidia, CUDA, cuDNN и др.) будут уже настроены.  <li><a href="http://jupyter.org">Jupyter Notebooks</a> <li><a href="https://www.microsoft.com/ru-ru/cloud-platform/r-server">Microsoft R</a> <li>Все необходимые библиотеки и утилиты для доступа к Azure  <li>Много полезных утилит (github, AzCopy, …) и прочих вкусностей </li></ul> <p>Виртуальные машины доступны как для Windows, так и для Linux: <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb611.png"><img title="image_thumb[6]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[6]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb6_thumb1.png" width="400" height="178"></a> <p>При этом есть тонкости: <ul> <li>Data Science Virtual Machine for Windows 2016 – это самый лучший вариант! Может ставится как на GPU (причем как NC, так и NV – подробнее об этом ниже), так и на обычную VM.  <li>Deep Learning Toolkit for DSVM – это более старая версия виртуальной машины для глубокого обучения на базе Windows Server, которая ставится только на виртуалки типа NC (которых иногда может не хватать).  <li>Из Linux-вариантов поддержка GPU есть только в Ubuntu, но зато и выбор фреймворков там чуть шире. </li></ul> <h3>Виртуалки с GPU</h3> <p>В Azure есть 2 типа виртуалок с GPU (т.н. N-Series VMs): <ul> <li>NC (Compute) – предназначены для вычислений и глубокого обучения. Поскольку такие виртуалки пользуются значительной популярностью, их может иногда не хватать в тех или иных регионах.  <li>NV (Visualisation) – предназначены для запуска приложений графической визуализации. Тем не менее, на этих виртуалках теоретически можно считать нейросети, хотя делать это не рекомендуется. И стоят они чуть дороже. </li></ul> <p>Виртуальные машины с GPU доступны лишь в некоторых регионах, поэтому если вам не предлагают NC или NV в качестве опции при выборе типа виртуалки – попробуйте выбрать другой регион. Начинать стоит с США (South-Central US, East US) или Европы (North Europe). <a href="https://azure.microsoft.com/en-us/pricing/details/virtual-machines/">Вот на этой странице</a> можно посмотреть доступность в разных регионах, выбирая их из drop-down вверху страницы. <h3>Процесс создания VM</h3> <p>Ниже описывается процесс создания и настройки Data Science VM на GPU. <p>1. Заходим на <a href="http://portal.azure.com/">Azure Portal</a> и выбираем вверху пункт “создать”: <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb711.png"><img title="image_thumb[7]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[7]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb7_thumb1.png" width="600" height="242"></a> <p>2. В строке поиска пишем Data Science и получаем на выбор несколько вариантов, приведенных на рисунке выше. <p>3. Выбираем нужны вариант и нажимаем “Создать”. После этого переходим вот к такому экрану конфигурации: <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb2011.png"><img title="image_thumb[20]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[20]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb20_thumb2.png" width="400" height="292"></a> <p>4. При установке машины с GPU в качестве диска <strong>обязательно выбрать HDD</strong>, поскольку N-Series VM не бывают с SSD, и если выбрать SSD-потом не будет доступна опция выбора N-Series. <p>5. Выбираем имя пользователя, пароль и т.д. Также выбираем имя новой ресурсной группы, куда будут помещены все облачные объекты, привязанные в этой виртуальной машине – хранилище для дисков, сама виртуалка, сетевые интерфейсы и т.д. <p>6. В качестве региона выбираем подходящий регион по принципу, описанному выше. Если в выбранном регионе не окажется доступных VM с GPU – потом можно будет вернуться и сменить его. <p>7. Следующим пунктом выбираем размер и тип виртуалки. NC6 – это самая простая виртуалка с графическим процессором, и как видим на текущий момент она стоит около 60000 руб. в месяц. <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb2311.png"><img title="image_thumb[23]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[23]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb23_thumb1.png" width="400" height="282"></a> <p>8. В разделе “дополнительные параметры” в принципе можно ничего не настраивать, если вы просто создаете одну вирталку. Если же у вас уже есть какая-то виртуальная подсеть, или вы настраивали ранее сетевой доступ для другой VM – здесь есть некоторый простор для оптимизации. <p>9. На последнем экране читаем ещё раз выбранную конфигурацию и нажимаем “Купить”. <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb2611.png"><img title="image_thumb[26]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[26]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb26_thumb1.png" width="400" height="292"></a> <p>10. После этого машина долго и мучительно (более 10 минут) создается. <h3>Процесс настройки VM</h3> <p>На этом этапе вы уже можете зайти в свою виртуалку, нажав кнопку “Connect” на страничке настроек (выделено красным): <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb3511.png"><img title="image_thumb[35]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[35]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb35_thumb1.png" width="400" height="203"></a> <p>Однако для комфортной работы с виртуальной машиной нужно сделать ещё несколько настроек: <p>1. Присвоить машине удобное DNS-имя, чтобы обращаться к ней по имени. Для этого надо нажать на IP-адрес на страничке выше (выделено синим), на открывшейся странице ввести DNS-имя и нажать “Сохранить”: <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb3811.png"><img title="image_thumb[38]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[38]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb38_thumb1.png" width="400" height="124"></a> <p>Также запомните окончание адреса, который зависит от региона. Для данной машины полный DNS-адрес будет smart8.southcentralus.cloudapp.azure.com. Теперь вы сможете просто подключаться к машине с помощью удалённого рабочего стола, используя DNS-адрес и введя указанные ранее данные о пользователе и пароль: <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb4711.png"><img title="image_thumb[47]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[47]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb47_thumb1.png" width="400" height="123"></a> <p>2. Очень удобно проводить обучение нейросетей не через удалённый терминал, а через браузер, используя Jupyter Notebook. Для этого рекомендуется настроить автоматический запуск Jupyter на виртуалке, а также пароль для доступа. Для этого войдите на машину через удалённый рабочий стол и запустите ярлык, показанный ниже: <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb5011.png"><img title="image_thumb[50]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[50]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb50_thumb1.png" width="400" height="299"></a> <p>После этого в появившемся текстовом окне необходимо дважды ввести пароль и нажать ENTER. <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb5311.png"><img title="image_thumb[53]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[53]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb53_thumb1.png" width="400" height="133"></a> <p>После этого необходимо немного подождать, и можно попробовать войти в Jupyter из локального браузера по адресу <a href="https://localhost:9999"><strong>https://localhost:9999</strong></a>. При этом игнорируйте сообщения о том, что https-сертификат неверный – смело переходите на страницу, вводите установленный ранее пароль и начинайте работать! <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb5611.png"><img title="image_thumb[56]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[56]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb56_thumb1.png" width="400" height="302"></a> <p>3. Для того, чтобы Jupyter Notebook был доступен извне по порту 9999, необходимо добавить запись в Network Security Group для нашей машины (если это не было сделано по умолчанию). В панели управления Azure переходим в группу ресурсов нашей виртуалки и выбираем нечто, заканчивающееся на –nsg: <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb4111.png"><img title="image_thumb[41]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[41]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb41_thumb1.png" width="400" height="179"></a> <p>В открывшемся окне мы должны увидеть примерно такие правила: <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb4411.png"><img title="image_thumb[44]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[44]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb44_thumb1.png" width="400" height="128"></a> <p>Если правила Jupyter с портом 9999 нет, то его необходимо добавить вручную, указав протокол TCP и порт 9999. <p>После этого вы должны быть в состоянии зайти на Jupyter Notebook вашей машины, просто указав в браузере адрес <a href="https://smart8.southcentralus.cloudapp.azure.com:9999">https://smart8.southcentralus.cloudapp.azure.com:9999</a> и введя пароль, установленный на предыдущем шаге. <h3>Простой запуск и остановка VM</h3> <p>Поскольку машины с GPU дорогие, то рекомендуется останавливать виртуалки, когда вы их не используете. Так вы сможете снизить расходы с $1000 в месяц, до $70-150 (таков на данный момент мой типовой счет за облако, если я не делаю чего-то экстраординарного). Чтобы каждый раз не заходить на Azure Portal, очень удобно настроить специального <strong>Azure Bot</strong> в скайпе, который позволит вам запускать и останавливать машины простыми командами.  <p>Процесс настройки бота такой: <p>1. Заходите на <a href="http://botframework.com">http://botframework.com</a> в раздел Bot Directory. <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb2911.png"><img title="image_thumb[29]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[29]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb29_thumb1.png" width="400" height="271"></a> <p>2. Как правило, Azure Bot будет доступен на первой странице. Если нет – найдите его! <p>3. Нажимаете на бота и устанавливаете его себе в Skype. <p>4. При первом диалоге с ботом надо будет аутентифицироваться – перейти по ссылке, залогиниться в Azure и скопировать код с сайта в чат с ботом. После этого бот вас запомнит, и делать это будет не нужно. <p>5. Для остановки и запуска виртуальных машин используйте команды start vm &lt;имя&gt;, stop vm &lt;имя&gt; <p><a href="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb3211.png"><img title="image_thumb[32]" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image_thumb[32]" src="https://msdnshared.blob.core.windows.net/media/2017/05/image_thumb32_thumb1.png" width="400" height="372"></a> <p>Обратите внимание, что только такое выключение машины (DEALLOCATION) ведет к тому, что с вас перестают списывать деньги за её использование. Если вы просто в удалённом терминале скажете Shutdown – машина будет остановлена, но не деаллоцирована, и деньги продолжат списываться. При деаллокации машины освобождаются лишние ресурсы, включая IP-адреса и временные диски. <h6>Хранение данных</h6> <p>В заключение хотелось бы сказать несколько слов о хранении данных. Вместе с созданной машиной автоматически было создано два диска: <ul> <li>Диск C: – системный. На нем хранятся все программы и системные файлы, и даже есть немного места. Содержимое этого диска сохраняется при деаллокации машины. Однако захламлять его вашими данными – не слишком хорошая идея.  <li>Диск D: – это быстрый диск для <strong>временных </strong>данных, его содержимое между запусками машины будет теряться. </li></ul> <p>Для долгосрочного хранения данных, а также для разделения их между машинами, можно использовать следующие подходы: <ul> <li>Создать <strong>файловое хранилище </strong>внутри Azure Storage и затем подключить его к своей машине командой <strong>net use</strong>. Такой подход имеет тот плюс, что вы сможете также подключить то же хранилище к своей основной машине, и тем самым получите простой способ обмениваться данными. Однако файловое хранилище относительно медленное, а если оно вдруг расположено в другом регионе – то совсем медленное. Кроме того, за пересылку данных между регионами взимается какая-то плата.  <li>Создать виртуальный диск (Managed Disk) и подключить его к виртуальной машине. Виртуальные диски быстрее, чем файловые хранилища, но их сложнее подключить к внешним компьютерам. </li></ul> <h3>Best Practice</h3> <p>В последнее время я делаю несколько проектов по анализу данных и обучению нейросетей, поэтому хочу поделиться с вами полезным приёмом использования виртуальных машин с GPU. Как известно, 80% времени обычно тратится на подготовку данных, и лишь 20% – на собственно запуск алгоритмов обучения. Поэтому я обычно имею в своём арсенале две виртуальные машины, подключенные к общему файловому хранилищу: <ul> <li>Виртуальная машина для подготовки данных – это обычная виртуалка, без GPU, достаточно мощная, чтобы с ней было комфортно работать по удалённому доступу. Обычно я использую что-то типа DS2.  <li>Виртуальная машина для обучения – это NC6. </li></ul> <p>В каждый момент времени я запускаю ту машину, с которой планирую работать. При этом в обоих случаях все данные и скрипты располагаются на файловом хранилище, т.е. я имею к ним доступ отовсюду. <p>Однако для ускорения обучения я иногда копирую данные с внешнего хранилища на локальный диск (C: или D:, в зависимости от того, насколько долгосрочны должны быть эти данные).  <h3>Заключение</h3> <p>Data Science Virtual Machine – это очень простой и быстрый способ начать использовать инструменты анализа данных и глубокого обучения. Я надеюсь, что мой опыт поможет вам использовать эти виртуальные машины в своей работе или исследованиях. Если у вас есть свой опыт использования виртуальных машин с GPU для анализа данных – делитесь опытом в комментариях!]]></content:encoded>
    </item>
    <item>
      <title>Начинается второй этап Imagine Cup 2017</title>
      <link>https://docs.microsoft.com/archive/blogs/sos/%D0%BD%D0%B0%D1%87%D0%B8%D0%BD%D0%B0%D0%B5%D1%82%D1%81%D1%8F-%D0%B2%D1%82%D0%BE%D1%80%D0%BE%D0%B9-%D1%8D%D1%82%D0%B0%D0%BF-imagine-cup-2017</link>
      <pubDate>Sat, 18 Mar 2017 13:52:49 GMT</pubDate>
      <dc:creator><![CDATA[Dmitry Soshnikov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sos/?p=825</guid>
      <description><![CDATA[Друзья! Закончился приём заявок на конкурс Imagine Cup этого года. Мне приятно сообщить, что...]]></description>
      <content:encoded><![CDATA[<p>Друзья!</p> <p>Закончился приём заявок на конкурс <a href="http://imaginecup.ru">Imagine Cup</a> этого года. Мне приятно сообщить, что несмотря на несколько снизившуюся нашу активность в регионах в этом году, заявок на конкурс пришло очень много. В этом году по количеству заявок лидирует Москва, затем Санкт-Петербург, Пермь, Волгоград, Екатеринбург; много заявок из Казани, Кемерово, Нижнего Новгорода, Ульяновска и Челябинска.</p> <p>По результатам предварительного отбора мы выделили <strong>81 заявку</strong>, которые пройдут в следующий этап. Некоторые из них будут приглашены на очные финалы, большинство же будут соревноваться онлайн. Лучшие <strong>14 проектов</strong> мы пригласим для участия в <strong>российском финале 16 апреля 2017 г.</strong> в Москве.</p> <h3>Как участвовать в онлайн-отборе</h3> <p>Для участия в онлайн-отборе вам необходимо подготовить <strong>видео-ролик</strong>, рассказывающий о проекте, продолжительностью не более 7 минут. Отправить нам все материалы необходимо <strong>не позднее 26 марта 2017 г.</strong>, заполнив <a href="https://forms.office.com/Pages/ResponsePage.aspx?id=v4j5cvGGr0GRqy180BHbRxat6FtTRm5NsPxpveKgHdNURElHMFo3MkVSVUtQSE1MUDRNSDFERUE2Ui4u">онлайн-форму</a>.</p> <p>Видео-ролик обязательно должен содержать в себе следующую информацию, необходимую для адекватной оценки проекта в соответствии с критериями оценки (подробнее про критерии оценки можно почитать <a href="https://iccms.blob.core.windows.net/library/IC17%20Official%20Rules%20and%20Regulations-83e17fb3bcb9.pdf">в официальных правилах конкурса</a>):</p> <ul> <li><strong>Актуальность проблемы </strong>и постановку задачи <strong>[15%].</strong> Расскажите про целевую аудиторию, с какими проблемами она сталкивается, и как вы решаете эти проблемы</li> <li><strong>Инновационность решения [20%]</strong> – что же нового вы предлагаете. Здесь помогает сравнить ваше решение с другими существующими (конкурентами)</li> <li><strong>Технологии [50%]</strong> – самый главный пункт в этом году! Будут учитываться, используются ли современные и инновационные технологии (когнитивные сервисы, искусственный интеллект, чат-боты и т.д.), насколько интересна архитектура решения, как используется облако Microsoft Azure, а также насколько привлекательно и профессионально выглядит проект в целом.</li> <ul> <li>Для того, чтобы оценить этот пункт, ролик <strong>обязательно должен содержать живую демонстрацию продукта</strong>.</li> <li>Обратите внимание, что использование <strong>Microsoft Azure </strong>в том или ином виде является обязательным требованием конкурса, не забудьте рассказать, как вы используете облако!</li></ul> <li><strong>Бизнес-часть и команда [15%]</strong> – ваша задача показать, что проект будет развиваться дальше. Для этого полезно понимать, как вы сможете привлечь средства для развития проекта, и как предполагается его монетизировать.</li></ul> <p>Не надо беспокоиться о качестве ролика – жюри будет обращать внимание только на содержимое, но не на качество исполнения.</p> <h3>Как записать видео-ролик</h3> <p>Для записи видео-ролика мы рекомендуем использовать плагин к Powerpoint под названием <a href="http://mix.office.com">Office MIX</a>. Он позволяет записывать и монтировать видеоролик прямо из слайдов Powerpoint, умеет осуществлять захват экрана (это нужно для живой демонстрации продукта), а также записывать и внедрять в слайды видео-поток с веб-камеры, чтобы было видно говорящего. При этом у вас будет возможность переписать каждый слайд до достижения требуемого результата, а потом экспортировать всё ваше выступление как единый видеоролик.</p> <p>Альтернативно вы можете собраться в аудитории перед проектором или экраном, и снять всё своё выступление – как если бы вы выступали перед реальным жюри – на видеокамеру. После этого без монтажа отправить нам этот ролик.</p> <h3>На что обратить внимание</h3> <p><strong>Игровые проекты</strong>. После мировых побед российских команд в категории игры, традиционно на Imagine Cup подаётся много игровых проектов. Этот год – не исключение, было очень много красивых игр. Но <strong>ВАЖНО ПОНЯТЬ</strong>, что в этом году игровые проекты оцениваются наряду с остальными проектами по единым критериям, куда не входят такие критерии, как <strong>fun</strong>, <strong>gameplay</strong> и другие. Как и другие проекты, <strong>игры будут в первую очередь оцениваться по технологиям</strong>, поэтому не очень красивая игра, использующая Azure и когнитивные сервисы имеет больше шансов, чем прекрасная и инновационная по геймплею игра, не использующая облака (она скорее всего не пройдет по формальным признакам). Поэтому если ваша игра не победила – не расстраивайтесь, а обязательно участвуйте в других игровых конкурсах, например, <a href="http://gamesjam.org/">GamesJam</a>. Подать туда проект можно <strong>до 17 апреля</strong>. А подавая ролик к нам, не забудьте сделать акцент на технических аспектах проекта.</p> <p><strong>Нужна демонстрация и прототип.</strong> Imagine Cup – это конкурс проектов, поэтому в видео необходимо показать работающих прототип. Бизнес-проектам, в которых приводится только идея, но нет реализации – рекомендуем подаваться на другие конкурсы.</p> <p><strong>Технологии важнее всего</strong>. Поэтому из отведенных 7 минут говорите 50% о технологиях. Нас интересуют подробности, какие алгоритмы машинного обучения и какие архитектуры нейросетей вы использовали, какова архитектура решения, какие облачные технологии и сервисы. Распределите время презентации в соответствии с важностью критериев оценки, не забыв оставить время на демонстрацию.</p> <h3>Что будет дальше</h3> <p>После подачи вашего ролика в течение нескольких дней без перерыва будет работать жюри, которое посмотрит все ваши заявки. По результатам мы выберем 14 команд, которые будут представлять свои работы на финале в Москве. Мы сообщим о решении жюри в районе <strong>5 апреля 2017 г</strong>.</p> <p>Удачи в подготовке ролика, и мы с нетерпением ждем более подробную информацию о ваших проектах!</p>]]></content:encoded>
    </item>
    <item>
      <title>Магия треугольника Серпинского в функциональном стиле</title>
      <link>https://docs.microsoft.com/archive/blogs/sos/792</link>
      <pubDate>Tue, 03 Nov 2015 08:22:20 GMT</pubDate>
      <dc:creator><![CDATA[Dmitry Soshnikov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sos/2015/11/03/792/</guid>
      <description><![CDATA[Сегодня мы поговорим про замечательную геометрическую фигуру: треугольник Серпинского. Это...]]></description>
      <content:encoded><![CDATA[<p>Сегодня мы поговорим про замечательную геометрическую фигуру: <a href="https://ru.wikipedia.org/wiki/%D0%A2%D1%80%D0%B5%D1%83%D0%B3%D0%BE%D0%BB%D1%8C%D0%BD%D0%B8%D0%BA_%D0%A1%D0%B5%D1%80%D0%BF%D0%B8%D0%BD%D1%81%D0%BA%D0%BE%D0%B3%D0%BE">треугольник Серпинского</a>. Это фрактальная самоподобная структура, которая однако очень проста в построении.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/4530.image_38D0AA12.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/4530.image_5F00_38D0AA12.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/2210.image_thumb_7660988C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/2210.image_5F00_thumb_5F00_7660988C.png" width="500" height="406" /></a></p>  <p>Алгоритм построения треугольника таков:</p>  <ul>   <li>Задаем координаты трех вершин – аттракторов (x1,y1), (x2,y2) и (x3,y3) </li>    <li>Выбираем некоторую точку (x,y) внутри треугольника </li>    <li>Повторяем много раз:      <ul>       <li>Ставим точку с координатами (x,y) </li>        <li>Выбираем случайным образом одну из вершин (xi,yi) </li>        <li>Вычисляем координаты новой точки по формуле ((x+xi)/2,(y+yi)/2) </li>     </ul>   </li> </ul>  <p>Такой алгоритм легко реализовать на любом языке программирования, однако его реализация на функциональных языках имеет ряд интересных моментов. В частности, для вычисления координат всех точек треугольника мы используем концепцию <a href="https://ru.wikipedia.org/wiki/%D0%9A%D0%BE%D1%80%D0%B5%D0%BA%D1%83%D1%80%D1%81%D0%B8%D1%8F">корекурсии</a>.</p>  <p>В соответствии с этим, мы реализуем функцию <strong>sierpinski</strong>, которая будет возвращать бесконечную последовательность координат точек треугольника</p>  <pre style="overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; width: 500px; border-bottom: #cecece 1px solid; padding-bottom: 5px; padding-top: 5px; padding-left: 5px; min-height: 40px; border-left: #cecece 1px solid; padding-right: 5px; background-color: #ffffff"><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #fbfbfb">let sierpinski (p1,p2,p3) =
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">   let rec sierpinski' pt =
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #fbfbfb">      seq {
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        let p = pick [p1;p2;p3]
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #fbfbfb">        let pt' = mid pt p
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        yield pt'
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #fbfbfb">        yield! sierpinski' pt'
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">      }
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #fbfbfb">   sierpinski' (mid3 p1 p2 p3)
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff"></pre></pre>

<p>В этом определении мы используем вспомогательную вложенную рекурсивную функцию sierpinski’, которая в качестве аргумента передает координаты текущей точки pt. Координаты вершин p1,p2,p3 в данном случае являются внешними по отношению к этой функции. Далее мы выбираем одну из вершин случайным образом, вычисляем координаты очередной точки, “возвращаем” (с помощью <strong>yield</strong>) эти координаты, и затем рекурсивно возвращаем бесконечный остаток списка, который получается из рекурсивного вызова. Затем чтобы сформировать результат мы вызываем sierpinsky’, передавая ему в качестве начальной точки среднюю точку, вычисленную из координат вершин.</p>

<p>Для вычисления средних точек мы определим следующие вспомогательные функции:</p>

<pre style="overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; width: 500px; border-bottom: #cecece 1px solid; padding-bottom: 5px; padding-top: 5px; padding-left: 5px; min-height: 40px; border-left: #cecece 1px solid; padding-right: 5px; background-color: #ffffff"><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #fbfbfb">let mid (x1,y1) (x2,y2) = (x1+x2)/2,(y1+y2)/2 
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">let mid3 (x1,y1) (x2,y2) (x3,y3) = (x1+x2+x3)/3,(y1+y2+y3)/2</pre></pre>

<p>Для выбора вершины случайным образом мы используем немного нефукнциональный подход, который зато позволяет быстро получить требуемую функциональность:</p>

<pre style="overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; width: 500px; border-bottom: #cecece 1px solid; padding-bottom: 5px; padding-top: 5px; padding-left: 5px; min-height: 40px; border-left: #cecece 1px solid; padding-right: 5px; background-color: #ffffff"><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #fbfbfb">let Rnd = new Random()
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">let pick (L:'t list) = L.[Rnd.Next(0,Seq.length L)]</pre></pre>

<p>В завершение остаётся лишь построить полученный треугольник. Для этого можем использовать стандартную библиотеку <strong>FSharp.Charting</strong>:</p>

<pre style="overflow: auto; border-top: #cecece 1px solid; border-right: #cecece 1px solid; width: 500px; border-bottom: #cecece 1px solid; padding-bottom: 5px; padding-top: 5px; padding-left: 5px; min-height: 40px; border-left: #cecece 1px solid; padding-right: 5px; background-color: #ffffff"><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #fbfbfb">sierpinski ((0,0),(300,600),(600,0)) 
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">   |&gt; Seq.take 5000 
</pre><pre style="font-size: 12px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #fbfbfb">   |&gt; Chart.FastPoint</pre></pre>

<p>Вот какой получился результат:</p>

<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/7446.image_4CEC574C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/7446.image_5F00_4CEC574C.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/0871.image_thumb_27EE96D3.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/0871.image_5F00_thumb_5F00_27EE96D3.png" width="500" height="410" /></a></p>

<p>Весь исходный код можно найти тут: <a title="http://fssnip.net/ta" href="http://fssnip.net/ta">http://fssnip.net/ta</a></p>

<p>В качестве самостоятельного упражнения, попробуйте в качестве эксперимента модифицировать функцию <strong>sieprinski</strong>, чтобы она могла принимать произвольное число вершин. И посмотрите, будет ли сохраняться фрактальное свойство для квадратов, шестиугольников и т.д. Буду рад видеть результаты ваших экспериментов в комментариях!</p>]]></content:encoded>
    </item>
    <item>
      <title>Windows Remote Arduino–управляем настольной лампой прямо из универсального приложения Windows</title>
      <link>https://docs.microsoft.com/archive/blogs/sos/windows-remote-arduino</link>
      <pubDate>Fri, 10 Jul 2015 16:58:00 GMT</pubDate>
      <dc:creator><![CDATA[Dmitry Soshnikov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sos/2015/07/10/windows-remote-arduino/</guid>
      <description><![CDATA[Сейчас в Майкрософт мы проводим “неделю интернета вещей”, поэтому тема моего очередного поста – о...]]></description>
      <content:encoded><![CDATA[<p>Сейчас в Майкрософт мы проводим “<a href="http://habrahabr.ru/company/microsoft/blog/261367/">неделю интернета вещей</a>”, поэтому тема моего очередного поста – о подключении устройств к Windows. Я <a href="http://blog.soshnikov.com/2015/04/28/%d0%be%d1%82-%d0%bf%d0%be%d0%b3%d0%be%d0%b4%d0%bd%d0%be%d0%b9-%d1%81%d1%82%d0%b0%d0%bd%d1%86%d0%b8%d0%b8-%d0%ba-%d0%b2%d0%b7%d1%80%d0%be%d1%81%d0%bb%d0%be%d0%bc%d1%83-%d0%b8%d0%bd%d1%82%d0%b5%d1%80/">уже писал про различные сценарии подключение датчиков к облаку</a>, но бывают сценарии, когда нам хочется просто подключить датчики или устройства к нашему компьютеру и удаленно ими управлять. Например, мы хотим сделать приложение, которое будет включать и выключать настольную лампу. В этих случаях самым простым решение будет использование <a href="http://ms-iot.github.io/content/en-US/win10/SetupWRA.htm">Windows Remote Arduino</a> – проекта, который <a href="http://blog.soshnikov.com/2015/07/10/%d0%ba%d0%b0%d0%ba-microsoft-%d0%b4%d1%80%d1%83%d0%b6%d0%b8%d1%82-%d1%81-arduino-%d0%b8-%d0%b4%d1%80%d1%83%d0%b3%d0%b8%d0%bc%d0%b8-%d0%bd%d0%b0-%d0%bf%d0%be%d1%87%d0%b2%d0%b5-iot/">был недавно объявлен в рамках партнерства с Arduino</a>.</p>  <h4>Что такое Windows Remote Arduino</h4>  <p><a href="http://ms-iot.github.io/content/en-US/win10/SetupWRA.htm">Windows Remote Arduino</a> – это библиотека WinRT, которая позволяет управлять цифровыми и аналоговыми каналами платы Arduino из Windows/Windows Phone приложения, с помощью привычных команд типа digitalRead/Write и analogRead/Write. При этом сама плата может быть подключена к компьютеру по USB или Bluetooth – важно, чтобы обеспечивался последовательный канал передачи данных. Помимо простых команд включения/выключения входов/выходов, в том или ином виде предусмотрена работа с шиной I2C и (в перспективе) сервоприводами.</p>  <p><img title="WinRemoteArduino" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="WinRemoteArduino" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/4857.WinRemoteArduino_40829309.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/4857.WinRemoteArduino_5F00_40829309.png" width="500" height="150" /></p>  <p>Для работы необходимо, чтобы на плате Arduino была запущена специальная программа, поддерживающая выполнение команд по протоколу <a href="http://firmata.org/">Firmata</a>, а библиотека Windows Remote Arduino обменивается командами с платой, предоставляя программисту высокоуровневые функции управления.</p>  <p>Библиотеки Windows Remote Arduino доступны для Windows 8.1/Windows Phone 8.1 и для универсальных приложений Windows 10. Чтобы использовать их в своем проекте, в настоящее время проще всего <a href="https://github.com/ms-iot/remote-wiring/">клонировать Git-репозиторий проекта</a> и добавить необходимые проекты в своё решение (пример я опишу ниже).</p>  <h4>Пример – включатель лампочки</h4>  <p>В моём примере я буду разрабатывать интеллектуальное приложение, включающее настольную лампу. Для этой цели я использую плату Arduino Uno и реле, способное коммутировать 220В. Реле подключается к какому-нибудь цифровому выходу (например, 7). Также для обнаружения присутствия человека будем использовать <a href="http://amperka.ru/product/infrared-motion-sensor">инфракрасный датчик присутствия</a>, который включим в аналоговый вход A0. Для удобства коммутации мы используем <a href="http://amperka.ru/product/arduino-troyka-shield">Troyka Shiled</a> от <a href="http://amperka.ru/">Амперки</a>.</p>  <p>На приведенном ниже видео смотрите подробнее про hardware setup, а также весь проект в действиии.</p>  <p><iframe height="281" src="https://www.youtube.com/embed/bPWFyLyQBrY" frameborder="0" width="500"></iframe></p>  <h4>&#160;</h4>  <h4>Устанавливаем скетч Firmata на плату Arduino</h4>  <p>Первым делом необходимо установить на плату Arduino стандартный скетч для выполнения команд Firmata. Такой скетч уже включен в стандартную поставку Arduino IDE – достаточно выбрать в меню File –&gt; Examples –&gt; Firmata –&gt; Standard Firmata. Открывшийся при этом скетч необходимо загрузить в плату обычным образом.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/6866.image12_4F4F04C5.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/6866.image12_5F00_4F4F04C5.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/3782.image12_thumb_730ABD10.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/3782.image12_5F00_thumb_5F00_730ABD10.png" width="500" height="494" /></a></p>  <p>На этом этапе полезно будет убедиться, что все аппаратное обеспечение настроено правильно. Для этого можно использовать специальную программу <a href="http://www.pjrc.com/teensy/firmata_test/firmata_test.exe">firmata_test.exe</a> (или любую из программ, описанных <a href="http://firmata.org/wiki/Main_Page">здесь</a>) – вы должны быть в состоянии управлять лампочкой, переключая состояние цифрового выхода 7, а также на входе A0 вы должны получать значение 0, если движения вокруг сенсора нет, и большое положительное значение (около 600 и более) в противном случае.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/6378.image_047DAE9A.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/6378.image_5F00_047DAE9A.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/3162.image_thumb_6228A9D1.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/3162.image_5F00_thumb_5F00_6228A9D1.png" width="240" height="395" /></a>&#160;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/8473.image_5F827ED1.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/8473.image_5F00_5F827ED1.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/2553.image_thumb_7A4EA4DD.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/2553.image_5F00_thumb_5F00_7A4EA4DD.png" width="240" height="395" /></a></p>  <h4>Готовим проект с Windows Remote Ardunio</h4>  <p>Чтобы использовать Windows Remote Arduino в своём проекте, необходимо подключить соответствующие библиотеки. На текущий момент технология достаточно молодая, и библиотеки существуют в виде исходных текстов. Мы надеемся, что со временем появятся пакеты NuGet, сейчас же процесс включения библиотек выглядит следующим образом:</p>  <p>1. Создадим новый проект – в нашем случае это будет универсальное приложение Windows 10</p>  <p>2. Клонируйте <a href="https://github.com/ms-iot/remote-wiring/">GitHub-репозиторий проекта Windows Remote Arduino</a> в какую-нибудь локальную папку:</p>  <pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">git clone https://github.com/ms-iot/remote-wiring/</pre>

<p><strong>ВАЖНО</strong>: в репозитории имеются несколько версий библиотеки. На момент написания статьи последняя ветка <strong>master</strong> содержала много ошибок при работе с аналоговыми и цифровыми входами, поэтому я использовать наиболее стабильную ветку revert-18-develop. Очень надеюсь, что в скором времени выйдет еще более стабильная версия, и клонировать можно будет как показано выше, без указания ветки.</p>

<p>3. В получившемся дереве проектов есть папки Microsoft.Maker.Win10 и Microsoft.Maker.Win8_1. Мы будем использовать вариант для Windows 10, но проект поддерживает и универсальные приложения Windows 8.1</p>

<p>4. В папке Microsoft.Maker.Win10 есть три проекта: Microsoft.Maker.Firmata, Microsoft.Maker.Serial и Microsoft.Maker.RemoteWiring. Эти три проекта нужно добавить в созданный нами проект (Add Existing Project)</p>

<p>5. В результате получится следующее дерево проектов:</p>

<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/5633.image_5EACA998.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/5633.image_5F00_5EACA998.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/6165.image_thumb_002BD928.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/6165.image_5F00_thumb_5F00_002BD928.png" width="230" height="268" /></a>&#160;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/8156.image_1277309B.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/8156.image_5F00_1277309B.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/1643.image_thumb_742C79A4.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/1643.image_5F00_thumb_5F00_742C79A4.png" width="264" height="266" /></a></p>

<p>6. Чтобы проекты компилировались в правильном порядке, необходимо установить правильные зависимости проектов. Нажмите правой кнопкой на наш проект, выберите “Зависимости сборки –&gt; Зависимости проектов”, и установите галочки напротив всех трех добавленных проектов. Остальные зависимости должны быть уже установлены, но вы можете их проверить: проект Firmata зависит от проекта Serial, проект RemoteWiring – от проектов Firmata и Serial. Соответственно, на вкладке “Порядок сборки” проекты должны собираться в таком порядке: Serial, Firmata, RemoteWiring и наше приложение.</p>

<p>7. В нашем основном проекте правой кнопкой нажмите на References и добавьте расширение Microsoft Visual C++ AppLocal Runtime Package for Windows UAP. Там же в пункте “Общие проекты” выберите все три добавленных проекта Windows Remote Arduino.</p>

<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/1033.image_6AD34521.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/1033.image_5F00_6AD34521.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/4214.image_thumb_69FD6FE8.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/4214.image_5F00_thumb_5F00_69FD6FE8.png" width="500" height="233" /></a></p>

<p>8. <strong>ВАЖНО</strong>: Чтобы наше приложение могло использовать возможности работы с последовательным портом, необходимо в манифест приложения (файл <strong>Package.appxmanifest</strong>) в раздел &lt;Capabilities&gt; добавить следующий код:</p>

<pre><pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff"><span style="color: #0000ff">&lt;</span><span style="color: #800000">DeviceCapability</span> <span style="color: #ff0000">Name</span>=<span style="color: #0000ff">&quot;serialcommunication&quot;</span><span style="color: #0000ff">&gt;</span>
</pre><pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">   <span style="color: #0000ff">&lt;</span><span style="color: #800000">Device</span> <span style="color: #ff0000">Id</span>=<span style="color: #0000ff">&quot;any&quot;</span><span style="color: #0000ff">&gt;</span>
</pre><pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">      <span style="color: #0000ff">&lt;</span><span style="color: #800000">Function</span> <span style="color: #ff0000">Type</span>=<span style="color: #0000ff">&quot;name:serialPort&quot;</span><span style="color: #0000ff">/&gt;</span>
</pre><pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">   <span style="color: #0000ff">&lt;/</span><span style="color: #800000">Device</span><span style="color: #0000ff">&gt;</span>
</pre><pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff"><span style="color: #0000ff">&lt;/</span><span style="color: #800000">DeviceCapability</span><span style="color: #0000ff">&gt;</span>
</pre></pre>

<p>9. Мы готовы к созданию нашего приложения, которое будет использовать библиотеки Windows Remote Arduino! Чтобы убедиться, что всё работает правильно, попробуйте выполнить сборку получившегося приложения.</p>

<h4>Создаем приложение с Windows Remote Arduino</h4>

<p>Наше приложение будет иметь три кнопки: для включения лампочки, для выключения, и для авто-режима, в котором лампочка будет управляться датчиком присутствия. Для начала добавим на главную страницу приложения <strong>MainPage.xaml</strong> эти три кнопки, установив их в неактивное состояние:</p>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff"><span style="color: #0000ff">&lt;</span><span style="color: #800000">StackPanel</span> <span style="color: #ff0000">Orientation</span>=<span style="color: #0000ff">&quot;Horizontal&quot;</span> <span style="color: #ff0000">HorizontalAlignment</span>=<span style="color: #0000ff">&quot;Center&quot;</span> <span style="color: #ff0000">VerticalAlignment</span>=<span style="color: #0000ff">&quot;Center&quot;</span><span style="color: #0000ff">&gt;</span></pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    <span style="color: #0000ff">&lt;</span><span style="color: #800000">Button</span> <span style="color: #ff0000">x</span>:<span style="color: #ff0000">Name</span>=<span style="color: #0000ff">&quot;on&quot;</span> <span style="color: #ff0000">IsEnabled</span>=<span style="color: #0000ff">&quot;False&quot;</span> <span style="color: #ff0000">Content</span>=<span style="color: #0000ff">&quot;ON&quot;</span> <span style="color: #ff0000">FontSize</span>=<span style="color: #0000ff">&quot;30&quot;</span> <span style="color: #ff0000">Width</span>=<span style="color: #0000ff">&quot;150&quot;</span> <span style="color: #ff0000">Height</span>=<span style="color: #0000ff">&quot;150&quot;</span> <span style="color: #ff0000">Click</span>=<span style="color: #0000ff">&quot;on_Click&quot;</span><span style="color: #0000ff">&gt;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">Button</span><span style="color: #0000ff">&gt;</span></pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    <span style="color: #0000ff">&lt;</span><span style="color: #800000">Button</span> <span style="color: #ff0000">x</span>:<span style="color: #ff0000">Name</span>=<span style="color: #0000ff">&quot;off&quot;</span> <span style="color: #ff0000">IsEnabled</span>=<span style="color: #0000ff">&quot;False&quot;</span> <span style="color: #ff0000">Content</span>=<span style="color: #0000ff">&quot;OFF&quot;</span> <span style="color: #ff0000">FontSize</span>=<span style="color: #0000ff">&quot;30&quot;</span> <span style="color: #ff0000">Width</span>=<span style="color: #0000ff">&quot;150&quot;</span> <span style="color: #ff0000">Height</span>=<span style="color: #0000ff">&quot;150&quot;</span> <span style="color: #ff0000">Click</span>=<span style="color: #0000ff">&quot;off_Click&quot;</span><span style="color: #0000ff">&gt;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">Button</span><span style="color: #0000ff">&gt;</span></pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    <span style="color: #0000ff">&lt;</span><span style="color: #800000">Button</span> <span style="color: #ff0000">x</span>:<span style="color: #ff0000">Name</span>=<span style="color: #0000ff">&quot;auto&quot;</span> <span style="color: #ff0000">IsEnabled</span>=<span style="color: #0000ff">&quot;False&quot;</span> <span style="color: #ff0000">Content</span>=<span style="color: #0000ff">&quot;AUTO&quot;</span> <span style="color: #ff0000">FontSize</span>=<span style="color: #0000ff">&quot;30&quot;</span> <span style="color: #ff0000">Width</span>=<span style="color: #0000ff">&quot;150&quot;</span> <span style="color: #ff0000">Height</span>=<span style="color: #0000ff">&quot;150&quot;</span> <span style="color: #ff0000">Click</span>=<span style="color: #0000ff">&quot;auto_Click&quot;</span> <span style="color: #0000ff">&gt;</span><span style="color: #0000ff">&lt;/</span><span style="color: #800000">Button</span><span style="color: #0000ff">&gt;</span></pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff"><span style="color: #0000ff">&lt;/</span><span style="color: #800000">StackPanel</span><span style="color: #0000ff">&gt;</span></pre>

<p>Этот код добавляется внутрь того контейнера Grid, который уже по умолчанию есть у страницы. Также для каждой кнопки создадим функцию-обработчик на C# (кликнув дважды на каждую кнопку, или через окно свойств Visual Studio).</p>

<p>Для начала инициализируем объект Remote Arduino. Для этого в классе окна в файле <strong>MainPage.xaml.cs</strong> опишем две переменные</p>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">UsbSerial usbcomm;</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">RemoteDevice arduino;</pre>

<p>Для подключение к Arduino опишем функцию connect(), которую затем вызовем из конструктора MainPage():</p>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff"><span style="color: #0000ff">private</span> async <span style="color: #0000ff">void</span> connect()</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">{</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    var dev = await UsbSerial.listAvailableDevicesAsync();</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    usbcomm = <span style="color: #0000ff">new</span> UsbSerial(dev[0]);</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    arduino = <span style="color: #0000ff">new</span> RemoteDevice(usbcomm);</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    usbcomm.ConnectionEstablished += Comm_ConnectionEstablished;</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    usbcomm.begin(57600, SerialConfig.SERIAL_8N1);</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">}</pre>

<p>Здесь мы сначала определяем список доступных портов, куда может быть подключен Arduino, затем создаем последовательное соединение UsbSerial с этим портом, а затем поверх этого – объект RemoteDevice, который будет представлять собой виртуальный Arduino. После этого мы вызываем функцию begin, передавая параметры последовательного соединения.</p>

<p><strong>ВАЖНО:</strong> Скорость последовательного канала, используемая протоколом Firmatа, задается в скетче StandardFirmata. По умолчанию она равна 57600, поэтому мы выбираем именно эту скорость в нашем приложении. Если скорости не будут совпадать, то соединение не будет работать.</p>

<p>При установлении соединения с платой, будет вызвана функция Comm_ConnectionEstablished, которую мы опишем следующим образом:</p>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff"><span style="color: #0000ff">private</span> <span style="color: #0000ff">void</span> Comm_ConnectionEstablished()</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">{</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,<span style="color: #0000ff">new</span> Windows.UI.Core.DispatchedHandler(() =&gt;</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    {</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        arduino.pinMode(14, PinMode.ANALOG);</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        arduino.pinMode(relay_pin, PinMode.OUTPUT);</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        dt = <span style="color: #0000ff">new</span> DispatcherTimer() { Interval = <span style="color: #0000ff">new</span> TimeSpan(500) };</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        dt.Tick += loop;</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        dt.Start();</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        on.IsEnabled = <span style="color: #0000ff">true</span>;</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        off.IsEnabled = <span style="color: #0000ff">true</span>;</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        auto.IsEnabled = <span style="color: #0000ff">true</span>;</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    }));</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">}</pre>

<p>Основной код функции обернут в вызов диспетчера, который запускает его в потоке UI – это важно. Далее мы устанавливаем режимы основных используемых пинов Arduino, создаем таймер, который будет вызывать каждые пол-секунды функцию loop(), а также делаем кнопки видимыми.</p>

<p>Интересный момент – установка порта 14 в аналоговый режим. Дело в том, что аналоговые входы в Arduino нумеруются после цифровых, и входу A0 соответствует номер 14. Режим необходимо установить именно ANALOG, а не INPUT, который означает цифровой ввод.</p>

<p>Теперь для включения/выключения лампочки достаточно описать функции-обработчики событий кнопок следующим образом:</p>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff"><span style="color: #0000ff">private</span> <span style="color: #0000ff">void</span> on_Click(<span style="color: #0000ff">object</span> sender, RoutedEventArgs e)</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">{</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    auto_mode = <span style="color: #0000ff">false</span>;</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    arduino.digitalWrite(relay_pin, PinState.HIGH);</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">}</pre>

<p>Функция off_Click делается по аналогии. Обратите внимание, что для работы с пинами удалённой платы Arduino используются привычные команды типа digitalRead/digitalWrite.</p>

<p>Переменная auto_mode – это переменная типа bool, которую нужно описать в нашем классе. Она нужна для того, чтобы обрабатывать автоматический режим. Кнопка AUTO будет устанавливать эту переменную в true:</p>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff"><span style="color: #0000ff">private</span> <span style="color: #0000ff">void</span> auto_Click(<span style="color: #0000ff">object</span> sender, RoutedEventArgs e)</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">{</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    auto_mode = <span style="color: #0000ff">true</span>;</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">}</pre>

<p>Теперь дело осталось за малым – реализовать функцию loop, которая будет переключать лампу в зависимости от наличия сигнала с датчика присутствия. Делать это надо только в том случае, если auto_mode установлен:</p>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff"><span style="color: #0000ff">private</span> <span style="color: #0000ff">void</span> loop(<span style="color: #0000ff">object</span> sender, <span style="color: #0000ff">object</span> e)</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">{</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    <span style="color: #0000ff">if</span> (auto_mode)</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    {</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        arduino.pinMode(14, PinMode.ANALOG);</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        var on = arduino.analogRead(0) &gt; 512;</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">        arduino.digitalWrite(relay_pin, on ? PinState.HIGH : PinState.LOW);</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    }</pre>

<pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">}</pre>

<p>ВАЖНО</p>

<p>: Для более надежной работы прямо перед чтением порта лучше еще раз установить его режим в аналоговый. Так быть не должно, но на практике это улучшает стабильность. Также обратите внимание, что мы используем analogRead(0), хотя строчкой ранее указывали номер порта 14. В analogRead передается номер порта от 0 до 5, при установке режима порта надо прибавить к этим номерам 14.</p>

<h4>Как подключить Arduino по Bluetooth и управлять со смартфона</h4>

<p>В нашем случае мы написали универсальное приложение, которое можем разместить на Windows Phone 10. Однако это бесполезно, поскольку подключить плату Arduino по USB к смартфону невозможно. Однако если мы будем использовать Bluetooth вместо USB-соединения, подключив к Arduino <a href="https://www.sparkfun.com/products/12577">bluetooth-плату</a>, то мы сможем этим же приложением управлять нашей конструкцией со смартфона. В этом случае в манифест приложения необходимо добавить следующие строчки для разрешения коммуникации по Bluetooth:</p>

<pre><pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff"><span style="color: #0000ff">&lt;</span><span style="color: #800000">DeviceCapability</span> <span style="color: #ff0000">Name</span>=<span style="color: #0000ff">&quot;bluetooth.rfcomm&quot;</span><span style="color: #0000ff">&gt;</span>
</pre><pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">  <span style="color: #0000ff">&lt;</span><span style="color: #800000">Device</span> <span style="color: #ff0000">Id</span>=<span style="color: #0000ff">&quot;any&quot;</span><span style="color: #0000ff">&gt;</span>
</pre><pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">    <span style="color: #0000ff">&lt;</span><span style="color: #800000">Function</span> <span style="color: #ff0000">Type</span>=<span style="color: #0000ff">&quot;name:serialPort&quot;</span><span style="color: #0000ff">/&gt;</span>
</pre><pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff">  <span style="color: #0000ff">&lt;/</span><span style="color: #800000">Device</span><span style="color: #0000ff">&gt;</span>
</pre><pre style="font-size: 10px; font-family: consolas,&#39;Courier New&#39;,courier,monospace; width: 100%; margin: 0em; background-color: #ffffff"><span style="color: #0000ff">&lt;/</span><span style="color: #800000">DeviceCapability</span><span style="color: #0000ff">&gt;</span>
</pre></pre>

<p>Мы не будем сейчас рассматривать использование bluetooth – это тема для отдельной статьи. Однако заметим, что с точки зрения программирования разницы почти нет – надо использовать объект BluetoothSerial вместо UsbSerial, а все остальные конструкции остаются без изменений.</p>

<h4>Где взять исходный код</h4>

<p><a href="https://github.com/evangelism/RemoteArduinoLamp/tree/v1.0-serial"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; float: right; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/2063.image_6E0A4E6B.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/2063.image_5F00_6E0A4E6B.png" width="240" align="right" height="82" /></a></p>

<p align="left">Исходный код моего проекта по традиции доступен на GitHub:</p>

<p align="left"><a title="https://github.com/evangelism/RemoteArduinoLamp/tree/v1.0-serial" href="https://github.com/evangelism/RemoteArduinoLamp/tree/v1.0-serial">https://github.com/evangelism/RemoteArduinoLamp/tree/v1.0-serial</a></p>

<p>Пожалуйста, берите его, экспериментируйте, если найдете ошибки – делайте pull requests.</p>

<h4>Выводы</h4>

<p>Windows Remote Arduino – достаточно молодая и сырая технология, но уже сейчас она может упростить решения ряда задач, в которых необходимо напрямую управлять входами/выходами платы Arduino, подключенной к компьютеру. Конечно, всё тоже самое можно запрограммировать вручную, написав свой скетч и используя последовательный канал для взаимодействия с ним, но почему бы не использовать уже готовые наработки и сложившиеся стандарты (Firmata), чтобы не изобретать велосипеды.</p>

<p>Я надеюсь, вы будете использовать Windows Remote Arduino в своих проектах, и если вы сделаете что-то интересное – мне будет приятно об этом узнать! Пишите в комментариях, <a href="https://twitter.com/shwars">в твиттере</a> или <a href="http://vk.com/shwars">вконтакте</a>!</p>]]></content:encoded>
    </item>
    <item>
      <title>Как Microsoft дружит с Arduino и другими на почве IoT</title>
      <link>https://docs.microsoft.com/archive/blogs/sos/microsoft-arduino</link>
      <pubDate>Thu, 09 Jul 2015 14:13:08 GMT</pubDate>
      <dc:creator><![CDATA[Dmitry Soshnikov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sos/2015/07/09/microsoft-arduino/</guid>
      <description><![CDATA[Пару месяцев назад в рамках конференции Build было объявлено о партнерстве Microsoft с платформой...]]></description>
      <content:encoded><![CDATA[<p>Пару месяцев назад в рамках <a href="http://habrahabr.ru/company/microsoft/blog/257029/">конференции Build</a> было объявлено о партнерстве Microsoft с платформой Arduino, а Windows 10 была названа первой Arduino-сертифицированной операционной системой. Исходные анонсы вы можете прочитать <a href="https://blog.arduino.cc/2015/04/30/microsoft-and-arduino-new-partnership/">на сайте Arduino</a> и <a href="http://blogs.windows.com/buildingapps/2015/04/29/microsoft-brings-windows-10-to-makers/">в официальном блоге Windows</a>. Давайте разберемся, что же стоит за этими объявлениями.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/4807.image_5D7A7D96.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/4807.image_5F00_5D7A7D96.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/6320.image_thumb_53B5161E.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/6320.image_5F00_thumb_5F00_53B5161E.png" width="500" height="115" /></a></p>  <p>Самое главное, что следует из этого объявления – Microsoft понимает важность платформы Arduino и будет предпринимать усилия, чтобы соответствующие устройства хорошо взаимодействовали с устройствами на Windows 10. Представьте себе мир, в котором внешние электронные устройства на базе Arduino являются продолжением аппаратного обеспечения компьютера, работающего под управлением Windows 10, и доступны из стандартных универсальных приложений с помощью соответствующих расширений. Это позволит оптимальным образом сочетать вычислительную мощность традиционных компьютеров и облака с потенциалом микроконтроллеров по управлению устройствами, создавая целостные решения в области интернета вещей.</p>  <p>В рамках текущих анонсов было объявлено о доступности двух технологий:</p>  <ul>   <li><strong><a href="http://ms-iot.github.io/content/win10/SetupWRA.htm">Windows Remote Arduino</a></strong> – это технология, которая позволяет управлять платой Arduino, подключенной по USB или Bluetooth, из универсального приложения, как если бы её порты ввода-вывода были доступны локально. Разработчику универсального приложения становятся доступны основные команды по работе с аналоговыми и цифровыми портами, что позволяет напрямую работать с рядом датчиков. Для взаимодействия Arduino с компьютером используется протокол <a href="http://firmata.org/">Firmata</a>, а на плате запускается специальный Firmata Sketch, выполняющий приходящие от удаленного компьютера команды. Проект доступен для Windows 8.1 и Windows 10 <a href="https://github.com/ms-iot/remote-wiring">в виде исходных текстов на GitHub</a>. </li>    <li><strong><a href="http://ms-iot.github.io/content/en-US/win10/SetupWVSA.htm">Windows Virtual Shield for Arduino</a></strong> позволяет использовать смартфон под управлением Windows Phone 10 в качестве виртуальной платы расширения для Arduino. При этом смартфон может использоваться как текстовый экран, как гироскоп/акселерометр/GPS, в качестве распознавателя речи и т.д. Вы получаете простой доступ ко всем этим возможностям прямо из своего Arduino Sketch. В качестве примера можно посмотреть на проект <a href="http://www.hackster.io/windowsiot/picture-of-the-weather">Picture the Weather</a>. </li> </ul>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/5635.image_23219866.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/5635.image_5F00_23219866.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/8875.image_thumb_46DFE162.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/8875.image_5F00_thumb_5F00_46DFE162.png" width="500" height="333" /></a></p>  <p>Эти проекты в настоящий момент активно дорабатываются, и стоит воспринимать их как первый шаг в правильном направлении. В следующей своей статье я подробнее и на примере познакомлю вас с тем, как всё это работает.</p>  <p align="left">В новом мире многие инновации делаются энтузиастами, которые любят собирать что-то своими руками – в английском языке для этого есть прекрасное слово <strong>maker</strong>. Понимая важность такого сообщества для будущих инноваций в IoT, Microsoft также <a href="http://blogs.windows.com/buildingapps/2015/04/29/microsoft-brings-windows-10-to-makers/">объявила</a> о партнерстве с популярным порталом <a href="http://www.hackster.io/">Hackster.IO</a>, на котором теперь <a href="http://www.hackster.io/microsoft">будут представлены многие официальные примеры разработок для Windows IoT</a>.</p>  <p align="left"><a href="http://hackster.io"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/4530.image_4363E129.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/4530.image_5F00_4363E129.png" width="500" height="123" /></a></p>  <p>Если же говорить про промышленный интернет вещей, то здесь важной задачей является построение инфраструктуры для взаимодействия сети устройств между собой и с клиентами. Одной из таких платформ межмашинного взаимодействия является <a href="http://devicehive.com">DeviceHive</a> от компании <a href="http://dataart.com">DataArt</a>. В результате партнерства компании с Microsoft теперь можно легко разворачивать DeviceHive в облаке прямо из магазина Azure Marketplace, что существенно упрощает создания облачных решений для интернета вещей.</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/8182.image_6DD2A2F7.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/8182.image_5F00_6DD2A2F7.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/6076.image_thumb_64796E74.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/6076.image_5F00_thumb_5F00_64796E74.png" width="500" height="152" /></a></p>  <p>Интернет вещей на текущий момент является достаточно молодым направлением. Чем больше компаний будут работать вместе над развитием этой отрасли, чем больше соглашений и промышленных стандартов типа <a href="http://habrahabr.ru/company/microsoft/blog/261515/">AllJoyn</a> – тем больше устройств будут общаться между собой и работать вместе, обеспечивая нам прекрасное технологическое будущее.</p>]]></content:encoded>
    </item>
    <item>
      <title>Подключение Arduino к Raspberry Pi</title>
      <link>https://docs.microsoft.com/archive/blogs/sos/arduino-raspberry-pi</link>
      <pubDate>Wed, 01 Jul 2015 15:05:00 GMT</pubDate>
      <dc:creator><![CDATA[Dmitry Soshnikov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sos/2015/07/01/arduino-raspberry-pi/</guid>
      <description><![CDATA[Как я уже писал ранее, во многих сценариях интернета вещей бывает полезно иметь один достаточно...]]></description>
      <content:encoded><![CDATA[<p>Как <a href="http://blogs.msdn.com/b/sos/archive/2015/04/28/connect-the-dots.aspx">я уже писал ранее</a>, во многих сценариях интернета вещей бывает полезно иметь один достаточно мощный встраиваемый компьютер (в роли которого может выступать Raspberry Pi), который может связываться с множеством расположенных в разных местах сенсоров, управляемых простыми микроконтроллерами семейства Arduino. При этом можности Raspberry Pi хватает, чтобы передавать данные по защищенным каналам HTTPS в облако, а также чтобы осуществлять взаимодействие с пользователем посредством развитого интерфейса (например, при работе под управлением Windows 10).</p>
<p>Для соединения Arduino с Raspberry Pi существует несколько сценариев подключения:</p>
<ul>
<ul>
<li><a href="http://blog.oscarliang.net/raspberry-pi-and-arduino-connected-serial-gpio/">Использование явного последовательного канала передачи данных</a>. При этом возникают сложности, связанные с разностью уровней питания: Raspberry Pi работает на напряжении 3.3В, в то время как Arduino использует 5 вольт. При этом для обеспечения безопасного соединения рекомендуется использовать специальные преобразователи уровня.</li>
<li><a href="http://blog.oscarliang.net/raspberry-pi-arduino-connected-i2c/">Использование последовательной шины I2C</a>, что позволяет подключать к одной Raspberry Pi до 128 устройств Arduino в режиме slave, при этом такое подключение также не требует преобразователей уровня.</li>
<li><a href="http://blog.oscarliang.net/connect-raspberry-pi-and-arduino-usb-cable/">Подключение по USB</a> является пожалуй самым простым способом, поскольку для этого достаточно всего лишь воткнуть Arduino через стандартный кабель в USB-разъем Raspberry Pi. Именно этот способ мы и рассмотрим.</li>
</ul>
</ul>
<p>В качестве примера рассмотрим простейший датчик температуры и давления BMP-180, <a href="http://adatum.ru/podklyuchenie-tsifrovogo-barometra-bmp180-k-arduino.html">подключенный к Arduino Uno по стандартной схеме</a>. После этого контроллер Arduino включается в USB-разъем Raspberry Pi, а сам Raspberry Pi затем подключается обычным образом к питанию, монитору и т.д.</p>
<p><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-width: 0px;" title="WP_20150427_18_39_55_Pro" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/6204.WP_20150427_18_39_55_Pro_4DE55949.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/6204.WP_5F00_20150427_5F00_18_5F00_39_5F00_55_5F00_Pro_5F00_4DE55949.jpg" alt="WP_20150427_18_39_55_Pro" width="500" height="410" border="0" /></p>
<p>При таком подключении общение Arduino и Raspberry происходит по последовательному порту. Предварительно (до подключения) на Arduino необходимо залить требуемый скетч &ndash; в нашем случае это простая программа, считывающая значение давления и температуры с датчика и печатающая их в виде текста в последовательный канал (температура предваряется символом Т, а давление &ndash; P):</p>
<div id="codeSnippetWrapper" style="font-size: 8pt; overflow: auto; cursor: text; font-family: 'Courier New', courier, monospace; width: 97.5%; direction: ltr; text-align: left; margin: 20px 0px 10px; line-height: 12pt; max-height: 200px; background-color: #f4f4f4; border: silver 1px solid; padding: 4px;">
<pre id="codeSnippet" style="font-size: 8pt; overflow: visible; font-family: 'Courier New', courier, monospace; width: 100%; color: black; direction: ltr; text-align: left; margin: 0em; line-height: 12pt; background-color: #f4f4f4; border-style: none; padding: 0px;"><span style="color: #cc6633;">#include</span> &lt;Wire.h&gt;<br /><span style="color: #cc6633;">#include</span> &lt;BMP180.h&gt;<br /><br />BMP180 barometer;<br /><br /><span style="color: #0000ff;">void</span> setup()<br />{<br />  Serial.begin(9600);<br />  Wire.begin();<br />  barometer = BMP180();<br />  <span style="color: #0000ff;">if</span>(barometer.EnsureConnected())<br />  {<br />    barometer.SoftReset();<br />    barometer.Initialize();<br />  }<br />  <span style="color: #0000ff;">else</span><br />  { <br />    Serial.println(<span style="color: #006080;">"E"</span>);<br />  }<br />}<br /><br /><span style="color: #0000ff;">void</span> loop()<br />{<br />  <span style="color: #0000ff;">if</span>(barometer.IsConnected)<br />  {<br />    <span style="color: #0000ff;">long</span> pres = barometer.GetPressure();<br />    <span style="color: #0000ff;">float</span> temp = barometer.GetTemperature();<br />    Serial.print(<span style="color: #006080;">"P"</span>); Serial.println(pres);<br />    Serial.print(<span style="color: #006080;">"T"</span>); Serial.println(temp);<br />    delay(1000);<br />  }<br />}</pre>
</div>
<p>Проверим работоспособность скетча в Arduino IDE, открыв монитор последовательного порта &ndash; мы должны увидеть появляющиеся значения температуры и давления. Обратите внимание на скорость &ndash; в программе задана скорость в 9600 бод, такие же установки должны быть и у монитора последовательного порта.</p>
<p>Теперь загрузим Raspberry Pi &ndash; в моем примере я использую Pidora в качестве базовой операционной системы и классическую модель Rapsberry Pi, хотя с таким же успехом можно использовать Raspberry Pi 2 и Windows 10.</p>
<p>Первым делом нужно определить, какой последовательный порт будет отвечать за общение с Arduino-контроллером. Проще всего это сделать следующим образом: при выключенной плате Arduino смотрим все доступные последовательные терминалы (ls /dev/tty*), после чего включаем плату в USB-порт, и через некоторое время снова смотрим список терминалов. То устройство, которое появилось в списке, и будет требуемым портом. В моём случае это был /dev/ttyUSB0, но в зависимости от номера порта, используемого дистрибутива системы и других факторов это имя может сильно отличаться.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/2742.2015-04-27-190009_1920x1200_scrot_3A5D1B0A.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/2742.2015_2D00_04_2D00_27_2D00_190009_5F00_1920x1200_5F00_scrot_5F00_3A5D1B0A.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-width: 0px;" title="2015-04-27-190009_1920x1200_scrot" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/7776.2015-04-27-190009_1920x1200_scrot_thumb_1B3F1F8C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/7776.2015_2D00_04_2D00_27_2D00_190009_5F00_1920x1200_5F00_scrot_5F00_thumb_5F00_1B3F1F8C.png" alt="2015-04-27-190009_1920x1200_scrot" width="500" height="308" border="0" /></a></p>
<p>Теперь мы можем использовать любые средства общения с последовательным портом для того, чтобы принять значения от датчика, передаваемые Arduino. Чаще всего удобным оказывается использовать Python и библиотеку serial. Например, следующий простой скрипт, набранный прямо в REPL, будет отображать на экране все данные, приходящие в последовательный порт:</p>
<div id="codeSnippetWrapper" style="font-size: 8pt; overflow: auto; cursor: text; font-family: 'Courier New', courier, monospace; width: 97.5%; direction: ltr; text-align: left; margin: 20px 0px 10px; line-height: 12pt; max-height: 200px; background-color: #f4f4f4; border: silver 1px solid; padding: 4px;">
<pre id="codeSnippet" style="font-size: 8pt; overflow: visible; font-family: 'Courier New', courier, monospace; width: 100%; color: black; direction: ltr; text-align: left; margin: 0em; line-height: 12pt; background-color: #f4f4f4; border-style: none; padding: 0px;">import serial<br />ser = serial.Serial(<span style="color: #006080;">"/dev/ttyUSB0"</span>,9600)<br /><span style="color: #0000ff;">while</span> 1:<br />   ser.readline()</pre>
</div>
<p>Ниже показано, как этот скрипт выполняется в окне терминала на Raspberry Pi:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/4520.2015-04-27-185823_1920x1200_scrot_4307B65A.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/4520.2015_2D00_04_2D00_27_2D00_185823_5F00_1920x1200_5F00_scrot_5F00_4307B65A.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-width: 0px;" title="2015-04-27-185823_1920x1200_scrot" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/1854.2015-04-27-185823_1920x1200_scrot_thumb_56B7309F.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/1854.2015_2D00_04_2D00_27_2D00_185823_5F00_1920x1200_5F00_scrot_5F00_thumb_5F00_56B7309F.png" alt="2015-04-27-185823_1920x1200_scrot" width="500" height="310" border="0" /></a></p>
<p>После этого получения данных в требуемом виде с последующей отправкой их в облако или сохранением в локальной базе данных является делом техники. Об этом я поговорю в следующих выпусках своей <a href="http://blogs.msdn.com/b/sos/archive/tags/IoT/">колонки про интернет вещей</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>Как научить ребенка программировать</title>
      <link>https://docs.microsoft.com/archive/blogs/sos/761</link>
      <pubDate>Mon, 08 Jun 2015 06:09:00 GMT</pubDate>
      <dc:creator><![CDATA[Dmitry Soshnikov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sos/2015/06/08/761/</guid>
      <description><![CDATA[Если у вас, как и у меня, есть дети &ndash; то вас наверняка волнует вопрос, как воспитать в них...]]></description>
      <content:encoded><![CDATA[<p>Если у вас, <a href="http://blog.vickie.soshnikov.com/">как и у меня</a>, есть дети &ndash; то вас наверняка волнует вопрос, как воспитать в них творческое начало, сделать их заинтересованными активными членами общества. Огромная роль в этом принадлежит родителям, которым для достижения результата приходится вкладывать очень много любви, своей креативности, терпения и усилий. Но как всегда на помощь могут прийти информационные технологии &ndash; ведь они способны усиливать любое правильное (и неправильное) начинание, делая процесс намного более эффективным.</p>
<p>Использованию информационных технологиях применительно к развитию детей посвящен мой новый курс на <a href="http://msftva.ru/">Microsoft Virtual Academy</a>, под кодовым названием &ldquo;<a href="http://www.microsoftvirtualacademy.com/training-courses/how-to-teach-your-child-to-program-rus">Как научить ребенка программировать</a>&rdquo;. На самом деле речь пойдет не только о программировании &ndash; в этом курсе я постарался описать известные мне технологии Майкрософт, которые могут вдохновить ребенка на творчество.</p>
<p><a href="http://www.microsoftvirtualacademy.com/training-courses/how-to-teach-your-child-to-program-rus"><img style="margin: 10px 0px;" src="https://habrastorage.org/files/cb1/943/84b/cb194384b99741abbb12e00f5814ecb6.png" alt="" width="717" height="366" border="0" /></a></p>
<p>Этот курс предназначен для родителей, которые ищут различные способы занять своих детей творчеством. Мы видим, что дети с удовольствием берут в руки компьютер, и важно дать им правильные творческие инструменты, чтобы они воспринимали компьютер не как поставщика развлекательного контента (наравне с телевизором), а как инструмент для творчества. На самом деле детям нравится творить (этим в основном объясняется популярность <a href="https://minecraft.net/">Minecraft</a>), и важно направить их в правильное русло.</p>
<p>Среди технологий, о которых рассказывается в курсе &ndash; технологии для исследования мира (<a href="http://maps.bing.com">Bing Maps</a>, <a href="http://worldwidetelescope.org">Worldwide Telescope</a>), для коллективного творчества (<a href="http://photosynth.net">Photosynth</a>, <a href="http://research.microsoft.com/en-us/research/toys/image-composite-editor.aspx">Image Composite Editor</a> и др.). Я также уделяю особое внимание вопросу мотивации детей, и рассказываю о своем опыте работы с детскими коллективами как <a href="http://junio-r.ru/">преподавателя детского лагеря ЮНИО-Р</a>, одного из идеологов <a href="http://coderussia.ru">часа кода</a> в России и <a href="https://www.youtube.com/watch?v=jh7GHuWsJCM">детского трека Microsoft AppDay</a>, лектора и ведущего мастер-классов в <a href="http://computeria.ru">Компьютерии</a>.</p>
<p>Особое внимание конечно уделяется именно обучению программированию. На мой взгляд, важнее всего увлечь ребенка на первых этапах процесса, поэтому предлагается использовать инструменты быстрого создания игровых миров <a href="https://aka.ms/koduintro">Kodu Game Lab</a> и <a href="http://www.projectspark.com/">Project Spark</a>. Ну а потом уже подключаются различные &ldquo;традиционные&rdquo; инструменты &ndash; от <a href="https://aka.ms/learncs">увлекательного введения в C#</a>, до возможного пути <a href="http://smallbasic.com/">Small Basic</a> &ndash;&gt; Visual Basic и функционального программирования на F#.</p>
<p>Конечно, приведенный подход не является единственно правильным, и может кому-то показаться дискуссионным. Если у вас возникают какие-то свои мысли, или вы хотите поделиться своим опытом и пообсуждать вопросы обучения детей &ndash; я с удовольствием это сделаю в комментариях к этому посту, <a href="http://vk.com/shwars">вконтакте</a> или <a href="https://twitter.com/shwars">в твиттере</a>.</p>
<p>Надеюсь, этот курс будет полезен и интересен &ndash; по крайней мере об интересе к этой теме говорят полные залы на моих докладах на конференциях DevCon и ряде других. Приятного просмотра!</p>]]></content:encoded>
    </item>
    <item>
      <title>Команда евангелистов Microsoft готовится к конференции DevCon 2015</title>
      <link>https://docs.microsoft.com/archive/blogs/sos/microsoft-1089</link>
      <pubDate>Tue, 19 May 2015 05:46:13 GMT</pubDate>
      <dc:creator><![CDATA[Dmitry Soshnikov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sos/2015/05/19/microsoft-1089/</guid>
      <description><![CDATA[Завтра в подмосковном курорте Яхонты начнется крупнейшая в России конференция для разработчиков по...]]></description>
      <content:encoded><![CDATA[<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/0804.image_67781308.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/0804.image_5F00_67781308.png"><img title="image" style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; display: inline; padding-right: 0px; border-top-width: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/6253.image_thumb_08D987AF.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/6253.image_5F00_thumb_5F00_08D987AF.png" width="321" height="321" /></a></p>  <p>Завтра в подмосковном курорте Яхонты начнется крупнейшая в России конференция для разработчиков по технологиям Майкрософт – <a href="http://msdevcon.ru">Microsoft DevCon</a>. Но наша команда евангелистов уже сегодня присутствует на мероприятии и активно готовиться поразить всех слушателей завтра самыми искрометными и интересными докладами. А для тех, кто не сможет присутствовать лично – смотрите онлайн-трансляцию <a href="http://msdevcon.ru">на сайте конференции</a>.</p>]]></content:encoded>
    </item>
    <item>
      <title>От погодной станции к взрослому интернету вещей. Проект Connect the Dots</title>
      <link>https://docs.microsoft.com/archive/blogs/sos/1080-3</link>
      <pubDate>Tue, 28 Apr 2015 09:00:14 GMT</pubDate>
      <dc:creator><![CDATA[Dmitry Soshnikov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sos/2015/04/28/1080-3/</guid>
      <description><![CDATA[Я уже раньше писал про создания простейшей погодной станции на NetDuino. У рассмотренного решения...]]></description>
      <content:encoded><![CDATA[<p>Я уже раньше <a href="http://blog.soshnikov.com/2015/01/26/%d0%be%d0%b1%d0%bb%d0%b0%d1%87%d0%bd%d0%b0%d1%8f-%d0%bf%d0%be%d0%b3%d0%be%d0%b4%d0%bd%d0%b0%d1%8f-%d1%81%d1%82%d0%b0%d0%bd%d1%86%d0%b8%d1%8f-%d0%b8-%d0%b8%d0%bd%d1%82%d0%b5%d1%80%d0%bd%d0%b5%d1%82/">писал про создания простейшей погодной станции</a> на NetDuino. У рассмотренного решения есть ряд недостатков:</p>  <ul>   <li>Используется достаточно редкий контроллер NetDuino 2 Plus – было бы здорово перейти с него на более классический и недорогой Arduino </li>    <li>Передача данных в сеть идет по незащищенному каналу связи HTTP – в реальных задачах разумнее использовать HTTPS, чтобы а значит для отправки данных необходим более мощный процессор, чем в Arduino </li>    <li>Предложенный подход с веб-сервисом не всегда способен принять большой поток данных, поэтому если мы хотим поддерживать множество аналогичных погодных станций – необходимо использовать специальные облачные механизмы для интернета вещей, такие как <strong><a href="http://azure.microsoft.com/ru-Ru/services/event-hubs/">концентраторы событий</a> </strong>(<strong>Event Hub</strong>). </li> </ul>  <p>В этой заметке я немного порассуждаю на тему того, как должно быть устроено “взрослое” решение для интернета вещей и приведу много ссылок на англоязычные источники, а в следующих заметках уже опишу некоторые детали процесса подробнее на русском.</p>  <h4>Аппаратная сторона</h4>  <p>Итак, для отправки HTTPS-запросов и для работы с продвинутыми сервисами Azure нам необходим более мощный процессор, чем в Arduino – при этом Arduino по-прежнему остаётся стандартом де-факто для подключения различных датчиков. Поэтому обычно используют в том или ином виде комбинацию Arduino с более мощным микрокомпьютером, обычно работающим под управлением какой-то операционной системы:</p>  <ul>   <li>Связка <a href="https://www.raspberrypi.org/">Raspberry Pi</a> + <a href="http://arduino.cc/">Arduino</a>. Raspberry Pi представляет собой недорогой одноплатный контроллер стоимостью около $35, на котором могут работать как разные версии UNIX, так и <a href="http://blogs.windows.com/buildingapps/2015/02/02/windows-10-coming-to-raspberry-pi-2/">Windows 10</a>. В случае простых датчиков – их можно подключить прямо к Raspberry Pi, но обычно используют Arduino, которое может быть связано с Raspberry Pi по последовательному каналу либо <a href="http://blog.oscarliang.net/connect-raspberry-pi-and-arduino-usb-cable/">через USB</a> (самый простой способ), либо <a href="http://blog.oscarliang.net/raspberry-pi-and-arduino-connected-serial-gpio/">напрямую</a> или <a href="http://blog.oscarliang.net/raspberry-pi-arduino-connected-i2c/">по интерфейсу I2C</a>. Преимуществом Raspberry Pi является то, что можно использовать достаточно мощную операционную систему и производить на плате не только передачу данных, но и осмысленную пред-обработку и принятие решений. </li>    <li><a href="http://www.arduino.cc/en/Main/ArduinoBoardYun">Arduino Yun</a> представляет собой одну плату, на которой по сути совмещена функциональность классического Arduino Uno и микрокомпьютера под управлением специальной версии Linux – OpenWrt. С точки зрения программирования такая связка очень похожа на рассмотренный выше вариант с Raspberry Pi, но при этом мы имеем одну плату (правда, с чуть менее функциональной ОС). </li>    <li><a href="http://www.intel.ru/content/www/ru/ru/do-it-yourself/maker.html">Intel Gallileo или Edison</a> – это компактные платы на основе процессоров x86, которые аппаратно совместимы с Arduino, при этом работают под управлением полноценной ОС типа Unix (или <a href="http://ms-iot.github.io/content/Gen2.htm">даже Windows</a>). Можно работать со всеми аппаратными возможностями этих плат непосредственно из уровня ОС (например, используя Python и библиотеку <a href="https://github.com/emutex/wiring-x86">Wiring x86</a> – пример такого скрипта <a href="https://github.com/evangelism/hello_intel_galileo">смотрите тут</a>, при этом такой же скрипт может отправлять данные в облако), либо с помощью эмулятора Arduino – в этом случае аппаратная часть программируется стандартным способом как Arduino Sketch в Arduino IDE, и затем связывается с базовой ОС через вызов system для отправки данных (пример <a href="https://learn.sparkfun.com/tutorials/galileo-unread-email-counter">смотрите тут</a>). </li>    <li>Конечно, можно еще использовать полноценный персональный компьютер с подключенным Arduino, но такой способ мы не будем рассматривать ввиду его простоты и экономической неэффективности. </li> </ul>  <h4>&#160;</h4>  <h4>Программная часть</h4>  <p>Для отправки потоковых данных с датчиков в облако рекомендуется использовать технологию <a href="http://azure.microsoft.com/ru-ru/services/event-hubs/">концентраторов событий</a>. Если вы используете в качестве базового микрокомпьютера Raspberry Pi, Arduino Yun или Intel Galileo, то удобнее всего будет разрабатывать на Python. Для этого установите свежую версию <a href="https://github.com/Azure/azure-sdk-for-python/">Azure SDK for Python</a>, в котором есть API для работы с Event Hub. Более подробный пример работы (с использованием более старого API) приведен <a href="http://blog.kloud.com.au/2014/10/11/the-internet-of-things-with-arduino-azure-event-hubs-and-the-azure-python-sdk/">тут</a>, документация по функциям работы с Event Hub – <a href="http://azure-sdk-for-python.readthedocs.org/en/latest/servicebus.html#event-hub">тут</a>, а я постараюсь в ближайшее время также описать этот процесс на русском языке. </p>  <h4>Проект Connect-the-Dots</h4>  <p>Проект <a href="https://github.com/MSOpenTech/connectthedots">Connect-the-Dots</a> – это проект <a href="https://msopentech.com/">Microsoft Open Technologies</a> с открытым исходным кодом, который показывает разные сценарии использования сенсоров для сбора данных в облаке с использованием Event Hub. Он по сути дела представляет собой реализацию тех принципов, о которых я написал выше – использование достаточно производительных контроллеров для отправки потоковых данных в Azure Event Hub.</p>  <p><a href="https://github.com/MSOpenTech/connectthedots/blob/master/Wiki/Images/ConnectTheDots%20architecture.png"><img style="border-left-width: 0px; border-right-width: 0px; background-image: none; border-bottom-width: 0px; padding-top: 0px; padding-left: 0px; padding-right: 0px; border-top-width: 0px" border="0" alt="" src="https://github.com/MSOpenTech/connectthedots/raw/master/Wiki/Images/ConnectTheDots%20architecture.png" width="482" height="331" /></a></p>  <p>В качестве сценариев поддерживается прямая отсылка данных в облако (с Intel Galileo или плат на основе .NET MicroFramework), или же использование шлюза на базе Raspberry Pi и конечных устройств на Arduino Uno, а также возможность использовать Windows Phone как прямой источник данных (освещенность, акселерометр и т.д.).</p>  <p>Проект Connect-the-Dots хорошо использовать как отправную точку для своих масштабных проектов, связанных с интернетом вещей. Более скромные проекты, возможно, имеет смысл делать с меньшим размахом – про это я буду писать в своих следующих заметках.</p>]]></content:encoded>
    </item>
    <item>
      <title>Облачная погодная станция и интернет вещей</title>
      <link>https://docs.microsoft.com/archive/blogs/sos/742</link>
      <pubDate>Mon, 26 Jan 2015 15:46:00 GMT</pubDate>
      <dc:creator><![CDATA[Dmitry Soshnikov]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/sos/2015/01/26/742/</guid>
      <description><![CDATA[Наверняка вы слышали термин &ldquo;интернет вещей&rdquo; &ndash; про него сейчас много говорят. Речь...]]></description>
      <content:encoded><![CDATA[<p>Наверняка вы слышали термин &ldquo;<a href="https://ru.wikipedia.org/wiki/%C8%ED%F2%E5%F0%ED%E5%F2_%E2%E5%F9%E5%E9">интернет вещей</a>&rdquo; &ndash; про него сейчас много говорят. Речь идет о том, что количество подключенных к интернет устройств растёт экспоненциально, и скоро все вещи, окружающие нас, будут подключены к интернет и смогут взаимодействовать как с нами, так и между собой. Новогодние каникулы &ndash; отличное время для экспериментов, и мне захотелось немного поэкспериментировать с интернетом вещей и сделать свою погодную станцию. А заодно и сервис, который мог бы сравнивать данные о погоде &ldquo;за окном&rdquo; со значениями прогноза погоды, а в будущем возможно и делать какие-то предсказания на основе шаманского анализа временных рядов. Хочу поделиться с вами результатами.</p>
<p>Сделать свою погодную станцию &ndash; это традиционное развлечение, например оно описано <a href="http://habrahabr.ru/post/214011/">здесь</a>, <a href="http://habrahabr.ru/post/213405/">здесь</a>, <a href="http://habrahabr.ru/company/flprog/blog/241913/">здесь</a>, и еще отчасти <a href="http://habrahabr.ru/post/165747/">тут</a>. Мне же хотелось построить всё на технологиях Майкрософт, при этом сделав упор на облачный сервис, который будет выполнять центральную роль во всей системе &ndash; а собственно сама станция будет просто отправлять данные в интернет. Посмотреть температуру за окном можно будет с мобильного устройства или <a href="http://weathermon.cloudapp.net/">через веб-интерфейс</a>.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/5466.IMG_6268_3B2CF8B6.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/5466.IMG_5F00_6268_5F00_3B2CF8B6.jpg"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border: 0px;" title="IMG_6268" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/6403.IMG_6268_thumb_215B5338.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/6403.IMG_5F00_6268_5F00_thumb_5F00_215B5338.jpg" alt="IMG_6268" width="500" height="333" border="0" /></a>&nbsp; <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/3034.image41_659E4B35.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/3034.image41_5F00_659E4B35.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/7762.image41_thumb_29E14333.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/7762.image41_5F00_thumb_5F00_29E14333.png" alt="image" width="155" height="289" border="0" /></a>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/4174.image_0B968C3D.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/4174.image_5F00_0B968C3D.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/2451.image_thumb_21EC3182.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/2451.image_5F00_thumb_5F00_21EC3182.png" alt="image" width="349" height="266" border="0" /></a></p>
<h2>Две составляющие интернета вещей</h2>
<p>На сегодняшний день, интернет вещей становится возможным благодаря наличию двух важных составляющих:</p>
<ul>
<li>Компактные <strong>клиентские устройства</strong>, подключенные к сети. С этой стороны процесс бурно развивается благодаря наличию недорогих микроконтроллеров, начиная от <a href="http://arduino.cc">Arduino</a> (который можно легко <a href="http://visualmicro.com">программировать на Visual Studio</a>), и заканчивая <a href="http://www.espruino.com/">экзотическими контроллерами, программируемыми на JavaScript</a>.</li>
<li>Развитие <strong>облачных технологий</strong>, которые позволяют накапливать в облаке большие объемы данных и производить их анализ и обработку. С этой точки зрения очень интересно использование <a href="http://habrahabr.ru/company/microsoft/blog/236823/">AzureML</a>.</li>
</ul>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/4035.image41_46804FB7.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/4035.image41_5F00_46804FB7.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-width: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/7633.image41_thumb_5CD5F4FC.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/7633.image41_5F00_thumb_5F00_5CD5F4FC.png" alt="image" width="506" height="223" border="0" /></a></p>
<p>Именно сочетание этих двух составляющих, когда устройства способны воспринимать наш реальный мир и как-то влиять на него, а облако обеспечивает высокопроизводительный мозг, управляющий поведением всего комплекса &ndash; позволяет обеспечивать подлинную магию интернета вещей.</p>
<h2>Архитектура облачной погодной станции</h2>
<p>В соответствии с таким пониманием интернета вещей мы и спроектируем нашу погодную станцию. Роль конечного устройства будет выполнять датчик температуры и освещенности (зачем нужно мерить освещенность &ndash; я расскажу в отдельном посте), подключенный к интернет с помощью замечательного контроллера <a href="http://www.netduino.com/netduinoplus2/specs.htm">NetDuino Plus 2</a>, а облачный сервис будет реализован на <a href="http://azure.microsoft.com">Microsoft Azure</a>. Также мы будем получать сведения о текущей температуре из погодного сервиса <a href="http://openweathermap.org/">OpenWeatherMap</a>, чтобы сравнивать наши измерения с данным прогноза погоды.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/6813.image13_453E4789.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/6813.image13_5F00_453E4789.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/2630.image13_thumb_5B93ECCE.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/2630.image13_5F00_thumb_5F00_5B93ECCE.png" alt="image" width="506" height="263" border="0" /></a></p>
<h2>Архитектура облачного сервиса</h2>
<p>Начнем с создания облачного сервиса, как с центральной компоненты системы. Облачный сервис будет содержать следующие компоненты:</p>
<ul>
<li><strong>Хранилище данных</strong>. Нам важно сразу выбрать правильную архитектуру для хранения данных: полноценная база данных SQL, полуструктурированные Azure Storage или же решение типа NoSQL. В нашем случае проще всего было бы использовать SQL Azure, поскольку объем данных не очень велик &ndash; но мы предпочтем в образовательных целях использовать Azure Table Storage, истинное облачное хранилище, которое может потенциально сильно масштабироваться. Ценой такого решения будет то, что мы не сможем использовать достаточно богатые конструкции запросов к данным, что немного усложнит реализацию.</li>
<li><strong>Веб-интерфейс</strong>, который будет отображать пользователю графики температуры, давления, освещенности и т.д. Я буду использовать ASP.NET Web Forms как самую простую технологию, однако на практике для повышения производительности конечно стоит рассмотреть ASP.NET MVC.</li>
<li><strong>Веб-сервис</strong> для регистрации температуры, а также для передачи данных мобильному приложению. Используем замечательную технологию Web API, которую можно развернуть параллельно с веб-интерфейсом на ASP.NET (ниже расскажем, как).</li>
<li>Постоянно работающий процесс <strong>Worker Role</strong>, которые будет периодически собирать данные из сервиса OpenWeatherMap и сохранять в нашем хранилище.</li>
</ul>
<h2>Создание проекта в Visual Studio</h2>
<p>Для начала создадим проект в Visual Studio (я буду использовать Visual Studio 2015). Если вы не хотите создавать проект сами, а лишь поэкспериментировать, то используйте готовый код проекта, доступный на GitHub: <a href="https://github.com/shwars/WeatherMonitor/tree/v1.1">версию, описанную в статье</a>, или <a href="https://github.com/shwars/WeatherMonitor/">последнюю версию</a>.</p>
<p>Начнем с создания облачного сервиса, которые будет содержать в себе Web Role и Worker Role. Для работы с облачными проектами и инструментами вам потребуется установить <a href="http://azure.microsoft.com/ru-ru/downloads/">Windows Azure SDK</a>.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/0842.image10_66C007C9.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/0842.image10_5F00_66C007C9.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/2538.image10_thumb_2B02FFC7.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/2538.image10_5F00_thumb_5F00_2B02FFC7.png" alt="image" width="500" height="346" border="0" /></a></p>
<p>При создании облачной службы мы сразу добавляем в неё необходимые компоненты:</p>
<p>&nbsp;<a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/0841.image15_61077ED4.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/0841.image15_5F00_61077ED4.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/4174.image15_thumb_775D2419.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/4174.image15_5F00_thumb_5F00_775D2419.png" alt="image" width="500" height="307" border="0" /></a></p>
<p>При создании веб-составляющей WeatherMonitorWeb нас попросят выбрать стек технологий ASP.NET &ndash; в нашем случае я использовал ASP.NET Web Forms. Если кто-то поможет переписать этот проект на ASP.NET MVC &ndash; я буду признателен! Используйте pull request для <a href="https://github.com/shwars/WeatherMonitor">исходного кода проекта на GitHub</a>.</p>
<p>Поскольку работать с данными нам будет нужно из обоих проектов, добавим еще один проект &ndash; библиотеку классов, в которую поместим все функции по доступу к облачному хранилищу. Также в это же решение мы можем добавить проект мобильного приложения.</p>
<h2>Организация доступа к данным</h2>
<p>Данные о погоде будут хранится в <a href="http://azure.microsoft.com/ru-ru/documentation/articles/storage-dotnet-how-to-use-tables/">табличном хранилище Microsoft Azure</a>. Мы можем использовать встроенное хранилище, создаваемое в рамках облачной службы, или настроить отдельное хранилище, доступ к которому будет осуществляться из разных служб. Создадим отдельное хранилище &ndash; для этого в портале Microsoft Azure выберем следующий пункт:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/0652.image_70407852.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/0652.image_5F00_70407852.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/2235.image_thumb_2644F760.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/2235.image_5F00_thumb_5F00_2644F760.png" alt="image" width="500" height="185" border="0" /></a></p>
<p>При создании хранилища стоит использовать разумные опции (в зависимости от требований к надежности &ndash; выбирать реплицированное или локальное хранилище). После создания службы данных (мы назовём её <strong>weathermon</strong>) в меню &ldquo;управление ключами&rdquo; (внизу страницы) будут доступны ключи, которые потребуются нам позднее для доступа к данным.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/0513.image_518C1F18.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/0513.image_5F00_518C1F18.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/1538.image_thumb_612EBADA.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/1538.image_5F00_thumb_5F00_612EBADA.png" alt="image" width="500" height="333" border="0" /></a></p>
<p>Табличное хранилище позволяет хранить множество пар атрибут-значение на множестве распределенных серверов. Для удобства мы будем использовать <a href="https://www.nuget.org/packages/WindowsAzure.Storage/">Azure Storage SDK</a>, который входит в состав Azure SDK. Необходимо добавить в проект ссылку (Reference) на <strong>WindowsAzure.Storage</strong>, либо использовать NuGet (<em>install-package WindowsAzure.Storage</em>).</p>
<p>Storage SDK позволит нам автоматически помещать в хранилище объекты, унаследованные от <strong>TableEntity &ndash; </strong>при этом сам SDK позаботится о том, как они будут преобразованы к атрибутам. Запрашивать данные из хранилища мы сможем с помощью простых и привычных LINQ-запросов, получая в результате коллекции объектов.</p>
<p>Для начала опишем <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorLib/WeatherRecord.cs">класс для хранения одного измерения</a>:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:b0f62377-c0a3-4576-a3be-0e3c2b4401e7" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 550px;" style="width: 697px; height: 550px; overflow: auto;">public enum WeatherInfoSource { WeatherService, Device };
public enum ReadingType { Temperature, Humidity, Pressure, Luminocity };

public class WeatherRecord : TableEntity
{

    public WeatherRecord(DateTime When, double Reading, WeatherInfoSource src, ReadingType type)
    {
        this.Reading = Reading;
        this.WeatherInfoSource = src;
        this.When = When;
        this.ReadingType = type;
        PartitionKey = string.Format("{0}.{1}", When.Year, When.Month);
        RowKey = string.Format("{0}-{1}:{2}.{3}.{4},{5}:{6}", src, type, When.Year, When.Month, When.Day, When.Hour, When.Minute);
    }

    public WeatherRecord() { }

    public DateTime When { get; set; }

    public int iWeatherInfoSource
    {
        get; set;
    }
    public WeatherInfoSource WeatherInfoSource
    {
        get { return (WeatherInfoSource)iWeatherInfoSource; }
        set { iWeatherInfoSource = (int)value; }
    }

    public int iReadingType { get; set; }

    public ReadingType ReadingType
    {
        get { return (ReadingType)iReadingType; }
        set { iReadingType = (int)value; }
    }
    public double Reading { get; set; } 

}
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Основные поля в этом классе:</p>
<ul>
<li>WeatherInfoSource - показывает, взято ли измерение из погодного сервиса или из погодной станции</li>
<li>ReadingType - описывает тип измерения: температура, давление, влажность, освещенность</li>
<li>Reading &ndash; само значение измерения</li>
<li>When &ndash; время проведения измерения</li>
</ul>
<p>В этом классе есть две важные тонкости:</p>
<ul>
<li>Мы не можем хранить в TableEntity данные не-примитивных типов. Поэтому вместо публично доступных свойств WeatherInfoSource и ReadingType перечислимого типа, мы описываем поля iReadingType и iWeatherInfoSource типа int, а WeatherInfoSource/ReadingType делаем свойствами, которые скрывают в себе операции преобразования типов</li>
<li>Поля <strong>PartitionKey </strong>и <strong>RowKey</strong> определяют, как данные будут распределяться по серверам в случае значительных объемов. Все объекты с одним PartitionKey хранятся вместе. В нашем случае мы будем хранить вместе все данные за одни месяц, т.к. нам их в дальнейшем может понадобится агрегировать. Правильный выбор PartitionKey/RowKey определяет архитектуру распределенного хранения данных, и может сильно влиять на производительность.</li>
</ul>
<p>Для доступа к данным создадим <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorLib/WeatherDB.cs">статический класс WeatherDB</a>, в котором будут находится необходимые функции. В конструкторе класса мы будем инициализировать внутренние поля для доступа к таблице и создавать таблицу, если её не существует:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:605169e6-d2aa-4ff4-b02c-1b782bc38131" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 192px;" style="width: 697px; height: 192px; overflow: auto;">static CloudStorageAccount CloudStore;
static CloudTableClient CloudTable;
public static CloudTable Table;

static WeatherDB()
{
    string str = "DefaultEndpointsProtocol=http;AccountName=weathermon;AccountKey=...";
    CloudStore = CloudStorageAccount.Parse(str);
    CloudTable = CloudStore.CreateCloudTableClient();
    Table = CloudTable.GetTableReference("WeatherRecord");
    Table.CreateIfNotExists();
}</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>В строке подключения к хранилищу str необходимо в качестве AccountKey добавить секретный ключ, который мы раньше получили при создании облачного хранилища.</p>
<p>Для помещения объекта в таблицу используем следующий метод:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:b477a0a0-45d0-4543-b4fc-0531479cbc25" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 94px;" style="width: 697px; height: 94px; overflow: auto;">public static async void Record(ReadingType t, WeatherInfoSource src, double x)
{
    var rd = new WeatherRecord(CurrentDateTime, x, src, t);
    await Table.ExecuteAsync(TableOperation.Insert(rd));
}
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Механика очень проста &ndash; мы создаем новый объект, после чего вызываем операцию вставки этого объекта в хранилище. Что интересно &ndash; операция асинхронная, поскольку при этом инициируется подключение к облачному сервису через интернет, что может занять некоторое время. Аналогичным образом асинхронно устроена и операция получения всех данных из хранилища:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:f0a68cfe-7e3f-4087-b7f9-7e62602e40de" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 94px;" style="width: 697px; height: 94px; overflow: auto;">public static IEnumerable&lt;WeatherRecord&gt; GetData()
{
    var q = new TableQuery&lt;WeatherRecord&gt;();
    return WeatherDB.Table.ExecuteQuery(q);
}
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Более интересная операция &ndash; это извлечение последних заданных измерений из таблицы. Это нужно, чтобы в приложении в дальнейшем показывать два значения температуры, полученных с сенсора и из погодного сервиса. Следующая функция возвращает массив последних значений заданного ReadingType, по одному измерению для каждого WeatherInfoSource:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:906063a9-ba22-4437-9fb7-4313ee92e426" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 193px;" style="width: 697px; height: 193px; overflow: auto;">public static WeatherRecord[] GetCurrentReading(ReadingType type)
{
    var data = (from z in WeatherDB.Table.CreateQuery&lt;WeatherRecord&gt;()
               where z.When &gt; DateTime.Now.AddMinutes(-10)
               // where z.ReadingType == type
               select z).ToList();
    var res = from z in data
              where z.ReadingType == type
              group z by z.WeatherInfoSource into g
              select g.OrderByDescending(x =&gt; x.When).First();
    return res.ToArray();
}
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Этот метод устроен достаточно хитро. Проблема в том, что мы не можем использовать функции сортировки данных на сервере, поскольку данные возможно распределены по серверам. В первом LINQ-запросе, который выполняется на сервере, нам доступны только очень простые операции. Поэтому вначале мы выбираем из таблицы в память все данные за последние 10 минут &ndash; чтобы уменьшить размер обрабатываемых данных. При этом мы даже не может отфильтровать их по ReadingType &ndash; соответствующая строчка закомментарена.</p>
<p>Далее мы производим запрос в память &ndash; и здесь нам уже доступна вся мощь LINQ, включая <strong>group into</strong>, с помощью которого мы и выделяем требуемые нам данные.</p>
<h2>Worker Role для доступа к погодному сервису</h2>
<p>Теперь запрограммируем Worker Role, который будет обращаться к погодному сервису и раз в 10 минут считывать значение погодных параметров. Для этого нам нужно будет зарегистрироваться в сервисе <a href="http://openweathermap.com/">OpenWeatherMap</a> и получить оттуда API Key для нашего приложения. Для доступа к OpenWeatherMap из нашего сервиса будем использовать <a href="https://github.com/joancaron/OpenWeatherMap-Api-Net">библиотеку OpenWeatherMapClient</a>.</p>
<p>Worker Role &ndash; это процесс, который запускается в облаке и всё время работает. Основной его функционал описывается <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorWorker/WorkerRole.cs">одним классом</a>, а главная функция &ndash; это асинхронная функция <strong>RunAsync</strong>:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:c60e78ea-6de1-44c0-9442-4de36369baed" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 199px;" style="width: 697px; height: 199px; overflow: auto;">private async Task RunAsync(CancellationToken cancellationToken)
{
    while (!cancellationToken.IsCancellationRequested)
    {
        var Weather = await WeatherClient.CurrentWeather.GetByName(TargetCity,MetricSystem.Metric);
        WeatherDB.Record(ReadingType.Temperature, WeatherInfoSource.WeatherService, Weather.Temperature.Value);
        WeatherDB.Record(ReadingType.Pressure, WeatherInfoSource.WeatherService, Weather.Pressure.Value);
        WeatherDB.Record(ReadingType.Humidity, WeatherInfoSource.WeatherService, Weather.Humidity.Value);
        await Task.Delay(1000*60*NumMinutes);
    }
}
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>В ней всё очень просто: считываем текущую погоду через OpenWeatherMapClient, а потом вызываем описанные ранее функции для записи данных в хранилище. Обратите внимание, что поскольку функция асинхронная &ndash; она может спокойно &ldquo;засыпать&rdquo; на длительное время с помощью Task.Delay, освобождая процессорное время для других задач. Кроме того, в асинхронной функции можно удобно вызывать другие асинхронные методы &ndash; как это мы делаем с вызовом API OpenWeatherMap. Пока производится удалённый вызов, наш сервис также находится в состоянии спокойного ожидания.</p>
<p>Нам осталось в методе <strong>OnStart()</strong> инициализировать все необходимые переменные, в т.ч. параметры: название города и периодичность выполнения в минутах:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:d1f950ee-220a-488d-8c70-05ba69061079" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 68px;" style="width: 697px; height: 68px; overflow: auto;">TargetCity = CloudConfigurationManager.GetSetting("City");
WeatherClient = new OpenWeatherMapClient("...OpenWeatherMapAPIKey...");
NumMinutes = int.Parse(CloudConfigurationManager.GetSetting("NumMinutes"));
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Чтобы мы могли использовать CloudConfigurationManager.GetSetting, нам надо прописать соответствующие параметры в конфигурации нашего облачного сервиса. Идем в основной раздел решения в Visual Studio, отвечающий за облачный сервис (WeatherMonitor), выбираем WeatherMonitorWorker, и в разделе &ldquo;параметры&rdquo; добавляем два параметра NumMinutes и City типа string.</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/5635.image_2571B2D8.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/5635.image_5F00_2571B2D8.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-width: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/6254.image_thumb_68DC44EB.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/6254.image_5F00_thumb_5F00_68DC44EB.png" alt="image" width="600" height="225" border="0" /></a></p>
<p>Всегда когда возможно стоит использовать такой способ конфигурации сервисов, поскольку соответствующие значения можно будет менять как из интерфейса Visual Studio, так и из портала Windows Azure. Это очень удобно!</p>
<p>Теперь уже можно запустить наше приложение и проверить его в работе! Убедится, что данные записываются в хранилище, можно с использованием очень удобного инструмента <a href="https://azurestorageexplorer.codeplex.com/">Azure Storage Explorer</a>.</p>
<h2>Отображение данных на веб-портале</h2>
<p>У нашего приложения также есть веб-составляющая <strong>WeatherMonitorWeb</strong>. Это приложение ASP.NET Web Forms, в которое на <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorWeb/Default.aspx">главную страницу</a> мы добавим элемент управления Chart Control для отображения графика измерений, и пару элементов управления для выбора параметров:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:1303c659-a7d3-4c0e-a0d8-7243691f438a" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 383px;" style="width: 697px; height: 383px; overflow: auto;">    &lt;asp:Chart ID="MainChart" runat="server" Height="600" Width="800"&gt;
        &lt;series&gt;
            &lt;asp:Series Name="MainSeries" ChartArea="MainChartArea" ChartType="Line" Color="Black"/&gt;
            &lt;asp:Series Name="RealSeries" ChartArea="MainChartArea" ChartType="Point" Color="Red" /&gt;
        &lt;/series&gt;
        &lt;chartareas&gt;
            &lt;asp:ChartArea Name="MainChartArea" /&gt;
        &lt;/chartareas&gt;
    &lt;/asp:Chart&gt;
&lt;br /&gt;&lt;br /&gt;
&lt;asp:DropDownList runat="server" AutoPostBack="True" ID="ddDispType"&gt;
    &lt;asp:ListItem Value="Temperature" /&gt;
    &lt;asp:ListItem Value="Pressure" /&gt;
    &lt;asp:ListItem Value="Humidity" /&gt;
    &lt;asp:ListItem Value="Luminocity" /&gt;
&lt;/asp:DropDownList&gt;
&lt;asp:DropDownList runat="server" AutoPostBack="True" ID="ddTimePeriod"&gt;
    &lt;asp:ListItem Value="Day" /&gt;
    &lt;asp:ListItem Value="Week" /&gt;
    &lt;asp:ListItem Value="Month" /&gt;
    &lt;asp:ListItem Value="Year" /&gt;
&lt;/asp:DropDownList&gt;
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Здесь в Chart Control мы накладываем друг на друга два графика: реальные результаты измерений (которые мы научимся измерять чуть позже), и сохраненные нами значения из погодного сервиса.</p>
<p>Наполнять график данными мы будем в методе Page_Load() <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorWeb/Default.aspx.cs">нашей странички</a>. Для начала, определим по положению элементов управления срок измерений, который нам надо отображать, и тип измерений:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:0ab523a6-0780-4072-a119-3bc4c46226ae" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 149px;" style="width: 697px; height: 149px; overflow: auto;">ReadingType t = ReadingType.Temperature;
DateTime fr = DateTime.Now.AddYears(-1);
switch (ddTimePeriod.SelectedValue)
{
    case "Day":
        fr = DateTime.Now.AddDays(-1); break;
    case "Month":
        fr = DateTime.Now.AddMonths(-1); break;
    case "Year":
        fr = DateTime.Now.AddYears(-1); break;
    case "Week":
        fr = DateTime.Now.AddDays(-7); break;
}
switch(ddDispType.SelectedValue)
{
    case "Temperature":
        t = ReadingType.Temperature; break;
    case "Pressure":
        t = ReadingType.Pressure; break;
    case "Humidity":
        t = ReadingType.Humidity; break;
    case "Luminocity":
        t = ReadingType.Luminocity; break;
}
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Далее собственно переходим к получению данных и их отображению с помощью DataBinding:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:39fdd2b9-e565-452e-86bd-f2a2381041dc" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 247px;" style="width: 697px; height: 247px; overflow: auto;">var db = WeatherDB.GetData();
var data = from z in db
            where z.WeatherInfoSource == WeatherInfoSource.WeatherService
            where z.ReadingType == t
            where z.When &gt; fr
            orderby z.When descending
            select new { When = z.When, Data = z.Reading };
MainChart.Series[0].Points.DataBind(data,"When", "Data", "");
var rdata = from z in db
               where z.WeatherInfoSource == WeatherInfoSource.Device
               where z.ReadingType == t
               where z.When &gt; fr
               orderby z.When descending
               select new { When = z.When, Data = z.Reading };
MainChart.Series[1].Points.DataBind(rdata, "When", "Data", "");
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Здесь мы используем не очень хороший подход &ndash; сначала запрашиваем все данные в память с помощью описанного ранее метода <strong>GetData()</strong>, а уже потом осуществляем их выборку и сортировку. Оставляю читателю оптимизацию этого кода в качестве самостоятельного упражнения (<strong>Pull Requests приветствуются</strong>) &ndash; это можно сделать, ограничив объем передаваемых данных нужным периодом, как это сделано в функции <strong>GetCurrentReading </strong>выше.</p>
<p>На этом этапе вы должны быть в состоянии запустить сервис на своём компьютере, дать ему поработать некоторое время, и увидеть график температуры, собранной с OpenWeatherMap. Теперь самое время разместить проект в реальном облаке &ndash; для этого выберем облачный сервис WeatherMonitor правой кнопкой и используем &ldquo;<strong>Опубликовать</strong>&rdquo;.</p>
<p>При этом скорее всего у вас возникнут проблемы, связанные с особенностью реализации Chart Control. Дело в том, что этот элемент использует временные файлы в системной директории, которая при облачном развертывании оказывается защищена от записи. Для борьбы с этой проблемой, надо добавить в Web.Config следующие строчки конфигурации:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:ceda97d2-e299-4270-829b-abfcf0cc776b" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: xml; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 58px;" style="width: 697px; height: 58px; overflow: auto;">&lt;configuration&gt;
  &lt;appSettings&gt;
    &lt;add key="ChartImageHandler" value="storage=session;timeout=20;" /&gt;</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<h2>Создаем сервис WebAPI</h2>
<p>Для того, чтобы мы могли в дальнейшем записывать данные от контроллера NetDuino, нам необходимо предусмотреть простое API, чтобы запрос по некоторому адресу URL автоматически приводил к записи соответствующего значения в таблицу. Для этого проще всего использовать технологию Web API, которую можно развернуть вместе с сайтом на базе ASP.NET. Подробнее этот процесс описан <a href="http://www.asp.net/web-api/overview/getting-started-with-aspnet-web-api/using-web-api-with-aspnet-web-forms">в этой англоязычной статье</a>.</p>
<p>Первым делом, в файл <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorWeb/Global.asax.cs">Global.asax.cs</a> необходимо добавить следующий код настройки маршрутизации, выполняемый при запуске приложения:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:bd27810c-46a8-4b72-93cd-604a20191085" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 153px;" style="width: 697px; height: 153px; overflow: auto;">void Application_Start(object sender, EventArgs e)
{
    RouteTable.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = System.Web.Http.RouteParameter.Optional });
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
}
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Теперь для того чтобы создать API по записи температуры, нам надо создать класс контроллера <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorWeb/TemperatureController.cs">TemperatureController.cs</a> &ndash; проще всего это сделать через добавление в проект класса Web API:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/3566.image_33D2466C.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/3566.image_5F00_33D2466C.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border-width: 0px;" title="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/7711.image_thumb_78153E69.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/7711.image_5F00_thumb_5F00_78153E69.png" alt="image" width="600" height="282" border="0" /></a>&nbsp;</p>
<p>Контроллер позволяет определять методы, которые будут вызываться при получении GET или POST-запросов по адресам типа <strong>/api/temperature </strong>или <strong>/api/temperature/13</strong>. Например, для записи температуры в базу по GET-запросу, переопределим следующий метод:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:0265cd15-14e5-4343-a8aa-3e843f33b8a1" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 132px;" style="width: 697px; height: 132px; overflow: auto;">// GET api/&lt;controller&gt;/5
public async Task&lt;string&gt; Get(int id)
{
    await WeatherDB.RecordSuccess(ReadingType.Temperature, WeatherInfoSource.Device, id / 100.0);
    return DateTime.Now.ToString();
}
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Все преобразование входных и выходных параметров будут проведены автоматически инфраструктурой Web API. В нашем случае мы принимаем на вход температуру (умноженную на 100, чтобы использовать целый тип), а возвращаем текущую дату и время &ndash; это будет полезно, если мы захотим использовать контроллер Arduino без часов реального времени.</p>
<p>Аналогичным образом мы опишем метод, который по обычному запросу по адресу <strong>/api/temperature</strong> без параметров вернет последние измерения температуры. Все измерения возвращаются в формате JSON:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:7c7cd9b0-33b3-4fc5-852d-d0ec8a3e550b" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 76px;" style="width: 697px; height: 76px; overflow: auto;">public WeatherRecord[] Get()
{
    return WeatherDB.GetCurrentReading(ReadingType.Temperature);
}
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Аналогично создаются контроллеры для <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorWeb/PressureController.cs">давления</a>, <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorWeb/HumidityController.cs">влажности</a>, <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorWeb/LuminocityController.cs">освещенности</a>.</p>
<p>Теперь мы можем, сделав запрос по адресу <a href="http://weathermon.cloudapp.net/temperature/-13">http://weathermon.cloudapp.net/temperature/-13</a>, записать в таблицу любое измерение. Это измерение можно будет сразу посмотреть на графике.</p>
<h2>Проектируем аппаратное устройство</h2>
<p>Для реализации устройства, которое будет измерять температуру, давление и освещенность за окном, используем замечательный микроконтроллер NetDuino Plus 2. Его основной плюс состоит в том, что он имеет встроенный контроллер Ethernet, а также является совместимым в Arduino по форм-фактору.</p>
<p>Для измерения давления и температуры будем использовать замечательный <a href="http://www.adafruit.com/product/1603">датчик BMP-180</a>, а для измерения освещенности &ndash; классическую схему с фоторезистором. Принципиальная схема устройства приведена ниже:</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/1212.Schematic_151DED32.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/1212.Schematic_5F00_151DED32.png"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border: 0px;" title="Schematic" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/8524.Schematic_thumb_6013EEB2.png" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/8524.Schematic_5F00_thumb_5F00_6013EEB2.png" alt="Schematic" width="500" height="431" border="0" /></a></p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/8154.Board_766993F7.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/8154.Board_5F00_766993F7.jpg"><img style="background-image: none; padding-top: 0px; padding-left: 0px; margin: 10px 0px; display: inline; padding-right: 0px; border: 0px;" title="Board" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/12/51/metablogapi/2642.Board_thumb_4ABB5AAC.jpg" original-url="http://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-01-12-51-metablogapi/2642.Board_5F00_thumb_5F00_4ABB5AAC.jpg" alt="Board" width="500" height="388" border="0" /></a></p>
<p>Для сборки устройства я использовал обычный тонкий провод и паяльник, собрав схему прямо &ldquo;на проводах&rdquo; без использование макетной платы (конструкция видна на фото вверху статьи). Провод Ethernet втыкался прямо из марштуризатора (важно, чтобы на маршрутизаторе был включен DHCP), питание &ndash; от стандартного блока питания на 9 вольт.</p>
<p>В полученном дизайне значение освещенности мы будем снимать в аналогового входа 0, а сенсором давления/температуры будем управлять через последовательную шину I2C.</p>
<h2>Пишем программу для NetDuino</h2>
<p>Я был искренне и приятно удивлен, но программа для NetDuino пишется на слегка урезанном .NET &ndash; <a href="http://netmf.com">.NET Micro Framework</a> (<a href="http://www.netmf.ru/">русскоязычный сайт</a>). В отличие от Arduino, где работа с Ethernet Shield происходит достаточно сложно, здесь нам сразу становятся доступны все функции .NET по работе с сетью.</p>
<p>Для работы с сенсором я использовал <a href="https://aka.ms/netduino_sensorlib">библиотеку SensorLib</a>. Поскольку программирование в .NET требует Visual Studio 2013 (<a href="http://blogs.msdn.com/b/netmfteam/archive/2014/09/23/net-micro-framework-sdk-beta-released.aspx">вот тут рассказывается про работу .NET MF с VS2013</a>), я создал отдельный проект (<a href="https://github.com/shwars/WeatherCloudStationMF/tree/v0">код на GitHub</a>).</p>
<p>Вся функциональность программируется <a href="https://github.com/shwars/WeatherCloudStationMF/blob/v0/WeatherCloudStation/WeatherCloudStation.cs">в одном главном файле</a> в функции Main. Вначале мы инициализируем порты и создаем объект для работы с сенсором:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:71ecf1de-ccf2-49d7-96ae-95f8ecd146e8" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 141px;" style="width: 697px; height: 141px; overflow: auto;">AnalogInput photo_in = new AnalogInput(Pins.GPIO_PIN_A0);
OutputPort pin = new OutputPort(Pins.GPIO_PIN_SDA, true);
Thread.Sleep(200);
pin.Write(false); pin.Dispose();

Debug.Print("Initializing sensor library...");
SensorLibrary.BMP180 sensor = new SensorLibrary.BMP180();
sensor.Init(SensorLibrary.BMP180.Mode.BMP085_MODE_STANDARD);</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>После этого, основная работа контроллера заключена в бесконечный цикл. Он проводит измерение, отправляет его в интернет и затем засыпает на заданное количество минут:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:beebb9f4-9304-434c-be33-01870e8169f6" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 233px;" style="width: 697px; height: 233px; overflow: auto;">while (true)
{
    double pressure = sensor.GetPressure() / 100.0F;
    double pressuremm = pressure * 0.75006375541921;
    Debug.Print("Pressure:    " + pressuremm.ToString() + " mmHg");
    double temperature = sensor.GetTemperature();
    Debug.Print("Temperature: " + temperature.ToString() + " C");
    int lum = photo_in.Read();
    Debug.Print("Luminocity: " + lum.ToString());
    record("Temperature", (int)(temperature * 100));
    record("Pressure", (int)(pressuremm * 10));
    record("Luminocity", lum);
    Thread.Sleep(1000 * 60 * 10);
}</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Для отправки в интернет служит функция record &ndash; она инициирует запрос в интернет по соответствующей URL и игнорирует результат:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:b751f0ed-83a9-4a1b-93f8-ec5281037d8c" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 279px;" style="width: 697px; height: 279px; overflow: auto;">private static void record(string measurement, int value)
{
    TouchUrl("http://weathermon.cloudapp.net/api/" + 
	  measurement + "/" + value.ToString());
}

private static string TouchUrl(string url)
{
    using (var req = (HttpWebRequest)WebRequest.Create(url))
    {
        req.Method = "GET";
        using (var resp = req.GetResponse())
        {
		  // Если нужно - тут можем прочесть результат
        }
    }
}</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>На всякий случай мы также предусмотрели промежуточную печать с помощью Debug.Print &ndash; это очень удобно, поскольку когда плата подключена к компьютеру по USB, мы можем сразу видеть соответствующие строки прямо в Visual Studio. Также .NET MicroFramework поддерживает удобную отладку прямо из Visual Studio.</p>
<p>Запустив контроллер и подключив его к интернет мы можем наблюдать, как наш график начинает наполнятся данными. Вы можете посмотреть <a href="http://weathermon.cloudapp.net/">реальные данные</a> температуры, давления и освещенности за моим окном!</p>
<p>Если вы посмотрите внимательнее на историю данных &ndash; в некоторых местах реальные измерения отсутствуют. Это происходит потому, что мы не предусмотрели обработку исключений нашим контроллером. Если вдруг доступ в интернет временно теряется, приложение генерирут исключение, и бесконечный цикл останавливается. Для более устойчивой работы контроллера требуется более аккуратно подходить к вопросам исключительных ситуаций (<strong>Pull Request Welcome</strong>)!</p>
<h2>Реализуем приложение для Windows Phone</h2>
<p>Заключительным аккордом нашего упражнения станет реализация приложения для Windows Phone, которое позволит отображать текущую температуру. Будем использовать созданное нами WebAPI, которое возвращает необходимые данные в формате JSON. Для работы с такими данными нам потребуется подключить к вновь созданному проекту Windows Phone библиотеку JSON.NET (через NuGet &ndash; <em>install-package Newtonsoft.Json</em>).</p>
<p>Чтобы десериализовывать данные, нам потребуется класс, аналогичный созданному ранее <strong>WeatherRecord</strong>. Однако подключить <strong>WeatherLib </strong>напрямую к нашему проекту мы не можем, поскольку она не поддерживает платформу Windows Runtime, да и тащить за собой в приложения для смартфона всю облачную функциональность не очень правильно. Поэтому мы можем воспользоваться Cut-And-Paste, либо создать упрощенный вариант класса <strong>WeatherRecord </strong>&ndash; <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorApp/SimpleWeatherRecord.cs">SimpleWeatherRecord</a>.</p>
<p>Для получения результата вызова JSON-метода создадим функцию GetReading, которая будет вызывать нужные метод API и возвращать массив последних измерений типа <strong>SimpleWeatherRecord</strong>:</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:a808f018-8332-43fb-823a-8bda18953576" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 115px;" style="width: 697px; height: 115px; overflow: auto;">public async Task&lt;SimpleWeatherRecord[]&gt; GetReading(string rtype)
{
    var cli = new HttpClient();
    var str = string.Format("http://weathermon.cloudapp.net/api/{0}", rtype);
    var s = await cli.GetStringAsync(new Uri(str));
    return Newtonsoft.Json.JsonConvert.DeserializeObject&lt;SimpleWeatherRecord[]&gt;(s);
}
</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Обратите внимание на естественную асинхронность метода, и на простоту преобразования из формата JSON в строго типизированный массив объектов &ndash; одной строчкой!</p>
<p>Далее, создадим на главной странице набор элементов управления для отображения результатов. Я не буду вдаваться в подробности XAML-дизайна (исходный код можно <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorApp/MainPage.xaml">посмотреть тут</a>), приведу лишь пример получения температуры (остальные значения получаются аналогично, полный текст файла с кодом <a href="https://github.com/shwars/WeatherMonitor/blob/v1.1/WeatherMonitorApp/MainPage.xaml.cs">тут</a>):</p>
<div id="scid:9D7513F9-C04C-4721-824A-2B34F0212519:884d7732-e9cc-4a69-b9a3-e61b79bbb4b6" class="wlWriterEditableSmartContent" style="float: none; margin: 0px; display: inline; padding: 0px;">
<pre class="brush: csharp; gutter: true; first-line: 1; tab-size: 4;  toolbar: false;  width: 697px; height: 263px;" style="width: 697px; height: 263px; overflow: auto;">private async void Page_Loaded(object sender, RoutedEventArgs e)
{
    var ind = StatusBar.GetForCurrentView().ProgressIndicator;
    await ind.ShowAsync();
    ind.Text = "Getting temperature...";
    TempService.Text = "--"; TempOut.Text = "--";
    var temp = await GetReading("temperature");
    foreach(var x in temp)
    {
        if (x.iWeatherInfoSource == 0) // данные из сервиса
            TempService.Text = x.Reading.ToString();
        else // данные с термометра
            TempOut.Text = x.Reading.ToString();
    }
    await ind.HideAsync();
}</pre>
<!-- Code inserted with Steve Dunn's Windows Live Writer Code Formatter Plugin.  http://dunnhq.com --></div>
<p>Полученные с сервиса данные обычно содержат массив из двух измерений &ndash; последнее измерение температуры на улице и из OpenWeatherMap. Пройдясь по массиву циклом, мы помещаем каждое из измерений в соответствующий ему TextBlock. Если вдруг за последние 10 минут не было сделано измерения (это возможно, если например наше устройство NetDuino перестало передавать данные в облако), то соответствующей записи не будет в массиве, и текстовое поле будет отображать значение &ldquo;&mdash;&ldquo;.</p>
<p>Конечно, было бы правильнее сразу сделать это приложение универсальным приложением Windows. Но я оставлю это в качеству упражнения читателям, и буду ждать ваших <strong>Pull Requests</strong>.</p>
<h2>Заключение</h2>
<p>Статья получилась достаточно длинной, но, надеюсь, полезной! Она показывает, как на одном языке программирования C# и в одной среде (Visual Studio) вы можете создавать совершенно разное программное обеспечение &ndash; облачные сервисы, приложения для смартфонов, программы для микроконтроллеров. Программист все время использует (почти) одну и ту же библиотеку базовых функций, удобную среду программирования и отладки. Всё это делает платформу Майкрософт приятной в использовании, настолько, что я решил потратить часть новогодних каникул на проектирование этой погодной станции.</p>
<p>В статье содержится ряд пожеланий по улучшению. Мне будет очень любопытно, если кто-то подключится и в режиме <strong>pull request</strong> улучшит или дополнит мои примеры. Давайте вместе улучшать примеры в лучших традициях Open Source!</p>
<p>Эта статья - по всей видимости не последняя про погодную станцию, я расскажу вам про обработку получаемых данных на F#, а также возможно про создание аналогичной погодной станции на Arduino, как на более распространенном контроллере. Если у вас есть пожелания или идеи, о чем лучше рассказать &ndash; <a href="http://vk.com/shwars">пишите мне</a>! В любом случае, мне будет очень интересно узнать (в комментариях или <a href="http://vk.com/shwars">вконтакте</a>), сколько человек дочитало статью до конца, что показалось полезным, а что &ndash; нет.</p>
<p>Счастливого кодирования!</p>]]></content:encoded>
    </item>
  </channel>
</rss>