Насколько быстро можно создать устройство для Интернета вещей (IoT), которое управляется через браузер, получает и передает информацию, учитывая, что вы никогда не работали с микроконтроллерами, а только занимались высокоуровневым программированием на JavaScript?



Например, такое устройство как на фото. У него есть ЖК-экран и стрелочный указатель на сервоприводе. Рядом лежит телефон с запущенным браузером, который подключен к вебсерверу на устройстве. При нажатии стрелок в браузере, указатель поворачивается вправо, влево или устанавливается в нейтральное положение. После установки значения, оно передается обратно в браузер и показывается как значение Value. Дополнительно в браузере можно задать текстовую строку, которая после нажатия кнопки «Set text» выводится на ЖК-экране. Всё взаимодействие происходит по Wi-Fi. Как вы думаете, сколько времени уйдёт на разработку?

Если поразмышлять, придется много чего сделать. Написать код на C/С++, который выполняется на устройстве и взаимодействует с датчиками, поднять вебсервер, реализовать бэкэнд, фронтэнд, и всё это соединить. На самом деле, всё намного проще. И, осмелюсь утверждать, что на всё про всё хватит часа. Почему? Да потому, что есть готовые решения, которые можно объединить и, таким образом, быстро сделать прототип. Мы возьмём плату Intel Edison, среду разработки Intel XDK IoT Edition, набор датчиков Grove Kit и всего один язык программирования — JavaScript. Да, никакого С/С++. Всё просто, сразу из коробки.

Я расскажу обо всём по порядку, начиная с самых азов, чтобы даже неподготовленный разработчик не потерялся. Ну, а в завершении мы рассмотрим описанный вначале пример.

Grove Starter Kit Plus — Intel IoT Edition Gen2




Набор Grove Starter Kit — это замечательная штука. Знаю, что некоторые программисты боятся работать с радиоэлектроникой. Всякие там провода, соединения, пайка, радиодетали… Но этот набор позволяет упростить работу. Все датчики подключаются универсально с помощью стандартных разъёмов, которые, при всём желании, нельзя соединить неправильно. Все разъемы на плате расширения подписаны. Достаточно подсоединить нужные провода к датчикам и можно сосредоточиться на программной части устройства.

В комплект входят:

  • Плата расширения, с разъемами для стандартного подключения всех датчиков.
  • Светодиод.
  • Зуммер.
  • Реле.
  • Кнопка обычная.
  • Кнопка сенсорная.
  • Датчик поворота.
  • Термометр.
  • Микрофон.
  • Датчик освещения.
  • Сервопривод.
  • ЖК-экран.
  • Провода с разъёмами.
  • USB разъёмы.
  • Разъем для батарейки типа Крона.

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

Среда разработки Intel XDK IoT Edition




Среда разработки Intel XDK позволяет создавать приложение на JavaScript на Node.js и запускать их на плате Intel Edison и Intel Galileo. Это дает возможность тем разработчикам, которые раньше писали на JavaScript в основном для веба, попробовать себя в чём-то новом. Достоинство в том, что если раньше, чтобы писать для микроконтроллера, надо было знать C/C++, то теперь всё становится намного проще. Вы просто пишете свою программу на вашем любимом JavaScript, и все работает.

Возможно, что приложение, написанное на JavaScript, будет уступать в скорости такому же, но на C++. Но дело в том, что код писать легче, да, и железо сейчас мощное. Преимущество Intel XDK еще и в том, что загрузка и запуск приложения на плату со всеми связанными библиотеками и файлами делается нажатием пары кнопок.

Опять приведу пример с хакатона. Одна из команд рассказывала, что они долго мучились с реализацией своего проекта. Пытались заставить его работать и на Python и на С++. Но только перейдя на JavaScript у них всё сразу получилось.

Трудности, которые были при разработке


Иногда XDK не мог подключиться к Edison. При этом плата работала по серийному порту (эмуляция через USB). Проверка показала, что не работал WiFi на плате. Оказалось, что в моей версии прошивки платы был баг. Когда работаешь с разъемом D7, то начинает плохо работать Wi-Fi. После этого очень проблематично заставить плату работать нормально. Т. к. при перезапуске платы, программа стартует автоматически. Я решил только подключением к USB с эмуляцией серийного порта. Подключившись, исправил программу, чтобы она не обращалась к D7 и перезапустил плату.

Вот описание проблемы
https://github.com/intel-iot-devkit/mraa/blob/master/docs/edison.md — Arduino pin 7 can sometimes negatively impact the WiFi capability, if using WiFi avoid using this pin.
Проблема устранена в последнем релизе:
http://download.intel.com/support/edison/sb/edison_rn_332032009.pdf
Resolved issues:
EDISON-2356 Setting Arduino pin 7 to output causes Wi-Fi connection to become flaky.

Подключение платы


Предполагаю, что вы уже подключили плату Intel Edison к компьютеру через USB или WiFi. Если нет, то есть несколько способов. Можно включить эмуляцию серийного порта по USB, это крайний разъем microUSB. Можно включить эмуляцию сетевого интерфейса по USB, это средний разъем microUSB. Можно использовать WiFi. Питание можно подключить через USB или при помощи блока питания.


Библиотека MRAA


Для упрощения работы с датчиками из JavaScript есть специальные библиотеки. Одна из них это MRAA. Она обеспечивает достаточно низкий уровень работы с платой.

http://iotdk.intel.com/docs/master/mraa/index.html — документация по MRAA

Её основная задача – настроить пин на ввод или вывод, выдать ноль или единицу, считать значение цифрового или аналогово сигнала с контакта. Может настроить пин на работу в ШИМ (PWM) режиме и задать его параметры. Этого достаточно, чтобы помигать светодиодом, считать состояние кнопки, получить значение температуры с датчика, включить сервопривод.
Вот, например, программа включения встроенного на плате светодиода:

var mraa = require('mraa'); 
var myOnboardLed = new mraa.Gpio(13); // работаем с 13-ым пином
myOnboardLed.dir(mraa.DIR_OUT); // настраиваем его на вывод
myOnboardLed.write(1);  // устанавливаем высокий уровень сигнала на пине

Но вот если надо управлять чем-то более сложным, например, ЖК-экраном, то этого недостаточно.

Библиотека UPM


Следующая библиотека это UPM. Она предоставляет более высокий уровень работы.

http://iotdk.intel.com/docs/master/upm/node/ — документация по UPM для Node.JS

В библиотеке реализованы алгоритмы для управления сложными устройствами, например, экранами, сервомоторами и т. д. Иначе, это пришлось бы делать самостоятельно. Мы будем её использовать для управления ЖК-экраном по последовательной шине I2C.

Пример программы выводящий на ЖК-экранчик текст:

var jsUpmI2cLcd = require ('jsupm_i2clcd');
var lcd = new jsUpmI2cLcd.Jhd1313m1(0, 0x3E, 0x62);
lcd.setCursor(1,5);
lcd.write('Hello Edison!');

Работа Node.JS


Когда вы создаете приложение в Intel XDK на компьютере, вы выгружаете его на плату (кнопка Upload). Он будет помещен в папку, но не будет запущен. Проект находится на плате в папке /home/root/.node_app_slot на эту папку настроена ссылка из корня /node_app_slot

Отправленное на плату приложение можно запустить двумя способами. Либо непосредственно из среды разработки кнопкой Run. Либо вручную в консоли на плате, для этого надо перейти в папку /node_app_slot и выполнить команду

node main.js

Если же вы перезапустите плату Edison, приложение стартует автоматически. Чтобы это не происходило, можно либо убрать из автозапуска старт демона, либо убрать или переименовать файл main.js, что проще.

Сейчас мы рассмотрим все датчики, которые идут в комплекте Grove Starter Kit Plus (Intel IoT Edition) группами по типу подключения.

Светодиод, зуммер, реле




Все эти устройства подключаются к цифровому выходу. Можно использовать D2-D8. При подаче единицы они включаются, при нуле выключаются.

Светодиод


В комплекте есть три светодиода — красный, синий и зеленый. Выбирайте любой. Светодиод устройство полярное, значит важно подключить его правильно. Надо вставлять светодиод так, чтобы длинная ножка (анод) была в разъеме со значком «+». Но если вы подключите неправильно, он всего лишь не будет светить.



Подключим его к D2 и помигаем.



Исходный код main.js
/*jslint node:true, vars:true, bitwise:true, unparam:true */
/*jshint unused:true */

// Подключить библиотеку mraa
var mraa = require('mraa'); 

// Инициализируем D2 вывод как цифровой выход
var myOnboardLed = new mraa.Gpio(2); 

// Настроим на вывод
myOnboardLed.dir(mraa.DIR_OUT); 
var ledState = true; //Текущее состояние светодиода

// Будем вызывать функцию через каждые 100 мс
setInterval(periodicActivity,100);

function periodicActivity()
{
    myOnboardLed.write(ledState?1:0); // установим сигнал по состоянию
    ledState = !ledState; // изменим состояние на обратное
}

Зуммер


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

Реле


Реле представляет собой выключатель, который управляется сигналом. При подаче единицы выключатель замыкает контакты, при подаче нуля, размыкает. Его можно использовать для управления устройствами, которые рассчитаны на напряжение, большее чем 5В и ток, больший чем 0.5А и поэтому подключены к внешнему источнику питания. Например, можно включить настольную лампу. Реле, которое идет в комплекте Grove имеет параметры 10A х 250V. Это 2,5 кВт. Но это предельная мощность. Думаю, чайник не стоит включать таким способом. И если будете коммутировать прибор с напряжением питания 220 В, будьте осторожны, во-первых само напряжение опасно для здоровья, а во-вторых, если оно попадет на плату, тогда она скорее всего сгорит.

ШИМ и регулировка яркости светодиода


А что делать, если мы хотим выдавать аналоговый сигнал промежуточного уровня, а не только ноль и один? Или как включить светодиод на половинную яркость, если у него только два состояния? Этого можно достичь при помощи ШИМ – широтно-импульсной модуляции (PWM – Pulse-width modulation). При её использовании сигнал периодически меняется с 1 до 0.

Это происходит каждый фрейм. Когда время обоих уровней одинаковое, то в среднем мы получаем значение 0,5. Допустим, единица была 25% времени, тогда и сигнал получается таким же, 0.25 от максимального. Если мы так управляем светодиодом и фрейм выбран достаточно малым, то из-за инертности нашего зрения мы будем видеть это среднее значение яркости.

Часть цифровых разъемов на плате может работать в режиме PWM, на шилде это D3, D5, D6. (На самой плате Edison они помечены значком «~»).

Для включения PWM надо задать несколько параметров. Первое, это период повторения сигнала, т. е. величина фрейма. Второй параметр это время, в течение которого будет единица. Его можно задавать или как коэффициент 0-1, или как значение времени в мкс.

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

Исходный код main.js
/*jslint node:true, vars:true, bitwise:true, unparam:true */
/*jshint unused:true */

var mraa = require("mraa");
var pwm3 = new mraa.Pwm(5);

var frameSize = 2000;
pwm3.enable(true);
pwm3.period_us(frameSize);

var value = 0;
var step = 0.04;

setInterval(proc, 20);

function proc()
{
    value += step;
    if( value < 0 )
    {
         value = 0;
         step = -step;
    }
    else if( value > 1 )
    {
         value = 1;
         step = -step;
    }

    pwm3.write(value);
}

Кнопка обычная и сенсорная




Кнопки подключаются к цифровым входам D2-D8. Выдают единицу, если на них нажали. Отличие друг от друга в том, что сенсорная кнопка представляет собой емкостный датчик и поэтому срабатывает от легкого прикосновения и работает даже через слой бумаги.

Создадим приложение, которое будет выводить на экран состояние кнопки.
Подключаем кнопку к D8:



Исходный код main.js
/*jslint node:true, vars:true, bitwise:true, unparam:true */
/*jshint unused:true */

var mraa = require('mraa');

// будем использовать вывод D8
var myButton = new mraa.Gpio(8);

// настраиваем на ввод
myButton.dir(mraa.DIR_IN); 

// вызываем опрос кнопки
getButton(); 

// каждые 20 мс опрашиваем кнопку
function getButton()
{
    var b = myButton.read();
    console.log('button='+b);  
    setTimeout(getButton,20); 
}

Если после запуска вы будете нажимать кнопку, то увидите что-то подобное.



Датчик поворота, термометр, микрофон, датчик освещения




Все эти датчики выдают аналоговый сигнал разного уровня в зависимости от внешнего воздействия на них. Подключаются к аналоговым входам A0-A3. Значение сигнала 0-1023.

Датчик поворота


Возвращает угол, на который он повернут. Значения в диапазоне 0-1023.
Подключим его в A0.



Исходный код main.js
/*jslint node:true, vars:true, bitwise:true, unparam:true */
/*jshint unused:true */

var mraa = require('mraa'); //require mraa

// Подключаем к A0
var myRotary = new mraa.Aio(0); 

getRotary();

function getRotary()
{
    var b = myRotary.read();
    console.log('rotary='+b);  
    setTimeout(getRotary,20); 
}

При запуске программы и вращении ручки вы увидите примерно такой вывод.



Датчик температуры


Как можно догадаться, он возвращает значение температуры. Но недостаточно просто считать значение, так как это будет относительное сопротивление терморезистора. Для перевода в градусы лучше использовать библиотеку UPM. Итак, подключим датчик к A0.

Исходный код main.js
/*jslint node:true, vars:true, bitwise:true, unparam:true */
/*jshint unused:true */

var mraa = require('mraa');
var groveSensor = require('jsupm_grove');

// Подключаем к A0
var temp = new groveSensor.GroveTemp(0);

setInterval(getTemp,100);

function getTemp()
{   
    var celsius = temp.value();
    console.log('temp='+celsius);
}

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

Исходный код main.js
/*jslint node:true, vars:true, bitwise:true, unparam:true */
/*jshint unused:true */

var mraa = require('mraa'); //require mraa

var temp = new mraa.Aio(0); 

setInterval(getTemp,100);

function getTemp()
{   
    var sens = temp.read();
    var celsius = convertTemp(sens);
    console.log('temp='+celsius);
}

function convertTemp(sensorValue)
{
    var resistance=(1023-sensorValue)*10000/sensorValue; //получим сопротивление
    var temperature=1/(Math.log(resistance/10000)/3975+1/298.15)-273.15;//convert by datasheet ;  
    return temperature;
}

Микрофон


Микрофон выдает уровень звукового сигнала. Подключим к A1. Значения, как обычно для аналогового входа, лежат в диапазоне от 0 до 1023.

Исходный код main.js
/*jslint node:true, vars:true, bitwise:true, unparam:true */
/*jshint unused:true */

var mraa = require('mraa');
var myMic = new mraa.Aio(1); 

getLevel(); 

function getLevel()
{
    var lev = myMic.read();
    console.log('level='+lev);  
    setTimeout(getLevel,20); 
}

Сервопривод




Более интересная штука — сервопривод, это мотор, который поворачивается на заданный угол (0-180 градусов) и старается сохранить это положение.

Для задания угла, надо подавать импульс, определенной длительности (скважности). Такие импульсы должны приходить постоянно, обычно 50 раз в секунду (20 мс). Но период прихода не так важен, важна длительность. Она может быть от 500 мкс до 2000 мкс. При минимальном значении импульса устанавливается угол в 0 градусов, при максимальном в 180 градусов.

Для разных сервоприводов граничные значения длительности импульса могут отличаться. Их можно подобрать экспериментально. Но надо быть аккуратным. Если значение длительности лежит вне допустимого диапазона, тогда сервомотор начинает стучать и сильно нагревается. Сразу же отключайте его от разъема! При правильной работе он должен повернуться на заданный угол, остановится и замолчать, оставаясь холодным.

Будем использовать цифровой выход, который умеет выводить ШИМ (PWM) сигнал. Это выводы D3, D5, D6.

Надо учесть, что из-за того, что PWM может принимать на вход только 256 значений и нельзя использовать все значения для заполнения, а только небольшую часть, то получается, что фиксированных углов у сервопривода не так и много, у меня получилось около 20.
Исходный код main.js
var mraa = require('mraa');

// размер фрейма в мкс
var frameSize = 20000; 

// сервопривод подключим к D3
var pwm3 = new mraa.Pwm(3);
pwm3.enable(true);
pwm3.period_us(frameSize);

// минимальное значение импульса в мкс
var minServoTime = 420;

// максимальное значение импульса в мкс
var maxServoTime = 2000;    

// положение поворота  0-1
var value = 0.5;// установим в среднее положение, 90 градусов

// вычислим временной интервал
var servoVal = Math.floor(minServoTime + (maxServoTime - minServoTime) * (1-value));

pwm3.pulsewidth_us(servoVal);

ЖК-экран




Экран устройство более сложное, чем светодиод, поэтому и подключается к специальной последовательной шине I2C.

На экран можно выводить текст в 2 строки по 16 символов в каждой. Есть RGB подсветка. В документации сказано, что поддерживает Английский и Японский языки и есть возможность добавлять некоторое количество своих символов.

Так как устройства к шине I2C подключаются параллельно, то все разъемы на плате равнозначны, и экран можно вставить в любой из них.



Мы используем библиотеку UPM.

http://iotdk.intel.com/docs/master/upm/node/classes/jhd1313m1.html — работа с этим экраном.

var jsUpmI2cLcd = require ('jsupm_i2clcd');
var lcd = new jsUpmI2cLcd.Jhd1313m1(0, 0x3E, 0x62);
lcd.setColor(100, 127, 53);
lcd.setCursor(1,1);
lcd.write('Hello Edison!');


Соберём всё вместе


Вот мы и рассмотрели все устройства, которые есть в наборе Grove Starter Kit Plus. Попробуем собрать что-то поинтереснее, что одновременно использует несколько модулей.

У нас будет датчик поворота (A0), при вращении которого сервопривод (D3) будет поворачиваться на соответствующий угол. Найденная длительность импульса ШИМ для поворота на этот угол будет отображаться на ЖК-экране (I2C).



Исходный код main.js
/*jslint node:true, vars:true, bitwise:true, unparam:true */
/*jshint unused:true */

var mraa = require('mraa');

var jsUpmI2cLcd = require ('jsupm_i2clcd');
var lcd = new jsUpmI2cLcd.Jhd1313m1(0, 0x3E, 0x62);
lcd.setColor(100, 127, 53);

var myRotary = new mraa.Aio(0); 

var pwm3 = new mraa.Pwm(3);
pwm3.enable(true);
var frameSize = 20000; 
pwm3.period_us(frameSize);

setInterval(proc, 100);

function proc()
{
    // определим относительное положение поворота
    var maxRotary = 1023;
    var valueRate = value / maxRotary ;
    
    // найдем длительность импульса   
    var minServoTime = 420;
    var maxServoTime = 2000;
    var servoVal = Math.floor(minServoTime + (maxServoTime - minServoTime)*valueRate);
    
    // установим длительность в микросекундах
    pwm3.pulsewidth_us(servoVal);
    
    // покажем длительность на экранчике
    lcd.setCursor(0,0);
    lcd.write('Time=' + servoVal + ' us    ');
}

Создание веб-сервера


Раз мы рассматриваем разработку для IoT (Internet of Things – Интернет вещей) нам надо сделать возможность работы с устройством через интернет. Следующий пример создает вебсервер, который показывает текущую температуру на температурном датчике. Сервер слушает 1337 порт и отвечает на запрос к корню сайта. Датчик температуры подключаем к A0. Если ваша плата имеет адрес 192.168.0.105, тогда в браузере указываем 192.168.0.105:1337.

Исходный код main.js
var mraa = require('mraa');
var groveSensor = require('jsupm_grove');

var temp = new groveSensor.GroveTemp(0);

var lastSetVal = 0;

var app = require('express')();
var http = require('http').Server(app);
var url = require("url");

var numCalls = 0;

app.get('', function (req, res)
{
    'use strict';
    res.writeHead(200, {"Content-Type": "text/html"});
    res.write('Intel Edison Sensor temperature ' + temp.value() + '<sup>o</sup>C');
    res.end();
});

http.listen(1337, function () {
    'use strict';
    console.log('listening on *:1337');
});

Пример работы:



Десерт — управление через браузер




Итак, обещанное в самом начале. Создадим устройство, управляемое через браузер, со стрелкой на сервоприводе и экранчиком. И при каждой команде поворота от клиента, оно будет в формате JSON возвращать реально установленное значение, так как в крайних положениях поворота не происходит. И как маленький бонус, на подключенный ЖК-экран можно отсылать текст.

Программа состоит из двух частей. Одна из них находится на устройстве Edison. Это вебсервер, файл main.js. Он отвечает на запросы от клиента, обслуживает сервопривод и экран. Использует библиотеку express и http.

Вторая часть находится на клиенте в браузере. Она получает команды от пользователя и пересылает их на сервер. Получает данные от вебсервера в формате JSON и отображает их. Использует библиотеку jQuery.

Это приложение будет немного посложнее. В проекте создадим папку public, в неё поместим статический контент. В нашем случае это три изображения для кнопок — rotate_ccw.png, rotate_cw.png, rotate_center.png, html-файл index.html, который показывается в браузере клиента и библиотека jquery-1.11.3.min.js (Её надо будет скачать с официального сайта jquery.com/download)



На клиенте скрипт отслеживает нажатие на изображения стрелок и при возникновении такого события отправляет запрос на сервер по соответствующему адресу /right, /left, /center и ожидает получение JSON данных с информацией о положение стрелки. При нажатии на кнопку «Set text», текст из поля ввода отправляется на сервер.

Содержимое файла index.html
<html><body>

<title>Rotation</title>

<script src="jquery-1.11.3.min.js"></script>

<script>
        
    function showVal(val)
    {
        $("#val").text('Value='+val);
    }
            
    function left()
    {
        $.getJSON( "/left", function( data ) {
          showVal(data['value']);
        } ) ;
    } 

    function right()
    {
        $.getJSON( "/right", function( data ) {
          showVal(data['value']);
        }  );
    } 

    function center()
    {
        $.getJSON( "/center", function( data ) {
          showVal(data['value']);
        }  );
    } 

    function setText()
    {
        $.get("/text/"+$("#text").val());
    } 

</script>

<a onclick="left();">
<img src="rotate_ccw.png"/>
</a>

<a onclick="center();">
<img src="rotate_center.png"/>
</a>

<a onclick="right();">
<img src="rotate_cw.png"/>
</a>   
<br/>
<h1 id="val">Value=0</h1>

<input id="text" type="text" name="data" value="">   
<input type="submit" value="Set text" onclick="setText();" />
    
</body></html>

На плате экран подключен к I2C. Сервопривод подключается к D3. Сервер слушает 1337 порт. Приложение ожидает обращение к /right. При таком запросе, если возможно, выполняется поворот вправо и посылается ответ в формате JSON с информацией о положении стрелки. Аналогично обрабатывается обращение к /left и /center. При запросе вида /text/abcdef строка abcdef будет выведена на ЖК-экран. Работа с сервоприводом и экраном была рассмотрена ранее.

Содержимое main.js
var mraa = require('mraa');

//----- PWM -----
var value = 0.5;
var step = 0.05;
var frameSize = 20000; 

var pwm3 = new mraa.Pwm(3);
pwm3.enable(true);
pwm3.period_us(frameSize);
setTime(value);

//---------- LCD ----------
var jsUpmI2cLcd = require ('jsupm_i2clcd');
var lcd = new jsUpmI2cLcd.Jhd1313m1(0, 0x3E, 0x62);

var express = require('express');
var http = require('http');
var app = express();

// папка со статическим контентом
app.use(express.static(__dirname + '/public'));

// поворот влево
app.get('/left', function (req, res)
{
    value = fitValue(value-step);
    setServoValue(res,value);
});

// поворот вправо
app.get('/right', function (req, res)
{
    value = fitValue(value+step);
    setServoValue(res,value);
});

// установка в центру
app.get('/center', function (req, res)
{
    value = 0.5;   
    setServoValue(res,value);
});

// отображение текста на LCD
app.get('/text/:text', function (req, res)
{
    var text = req.params.text;
    
    var t = text.toString().substring(0, 16);
    var vs = t + '                '.substring(0,16-t.length);
    
    lcd.setCursor(0,0);
    lcd.write(vs);
    console.log('value='+vs+'=');
    res.writeHead(200, {"Content-Type": "text/html"});
    res.end();
});

// создание сервера
http.createServer(app).listen(1337);

// поворот и возврат положения в JSON 
function setServoValue(res,v)
{
    res.writeHead(200, {"Content-Type": "text/json"});
    
    var text = '{"value":"'+value.toFixed(2)+'"}';
    res.write(text);    
    console.log(text); 

    setTime(value);   
    res.end();    
}

// установка значения для сервопривода
function setTime(valueRate)
{
    var minServoTime = 420;
    var maxServoTime = 2000;    
    var servoVal = Math.floor(minServoTime + (maxServoTime - minServoTime)*(1-valueRate));
    pwm3.pulsewidth_us(servoVal);
}

// корректировка значения
function fitValue(v)
{
    if( v > 1 ) { v = 1; }
    if( v < 0 ) { v = 0; }
    return v;
}

Содержимое package.json
{
  "name": "PWM",
  "description": "",
  "version": "0.0.0",
  "main": "main.js",
  "engines": {
    "node": ">=0.10.0"
  },
  "dependencies": {
       "express" : "latest",
       "http" : "latest"
      
  }
}

Изображение rotate_ccw.png


Изображение rotate_cw.png


Изображение rotate_center.png


Вот собственно и всё. Запускаем и проверяем.

Демоны Node.JS


Пара слов о том, как работают демоны на плате Intel Edison.
В системе запущен демон xdk-daemon. Если его остановить командой:

systemctl stop xdk-daemon

тогда работающее Node.JS-приложение остановится. Запустить повторно можно командой:

systemctl start xdk-daemon

или перезапустить:

systemctl restart xdk-daemon

Программу можно редактировать и непосредственно на плате (vi), только не забывайте после изменения её перезапускать. Либо так:

systemctl restart xdk-daemon

Либо, если программа была запущена в консоли через команду:

cd /node_app_slot
node main.js

то остановить её можно через <Ctrl>+<C>

Полезное


» Воспроизведение звука на Intel Edison через Bluetooth с использованием Advanced Audio Distribution Profile (A2DP)
» Установка OpenCV 3.0.0-rc1 (с использованием IPP и TBB) на Intel Edison Yocto. USB-камера в OpenCV
» Использование платы Intel Edison для изменения цвета шара Orbotix Sphero при появлении новых твитов
» Подключаемся к Intel Edison через Android с Bluetooth LE (BLE)

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


  1. shpaker
    16.06.2015 14:15

    Ну прикольно, а каков стоимость этого праздника жизни?


    1. rkhatko
      16.06.2015 14:56
      +2

      Intel® Edison for Arduino (комплект из Edison Compute Module + Edison Arduino Board) — примерно $100
      Например:
      www.seeedstudio.com/depot/Intel-Edison-for-Arduino-p-2149.html?cPath=6_7 — $97.95
      www.sparkfun.com/products/13097 — $99.95
      www.linuxcenter.ru/shop/ops_hard/Edison/EDI2ARDUIN.AL.K — 8673 руб.

      Grove starter kit plus – Intel IoT Edition for Intel Galileo Gen 2 and Edison — $79
      www.seeedstudio.com/depot/Grove-starter-kit-plus-Intel-IoT-Edition-for-Intel-Galileo-Gen-2-and-Edison-p-1978.html

      Итого: примерно $180 без учета доставки.


  1. Aspire89
    16.06.2015 17:28

    В какой последовательности нужно запускать проект на устройстве?
    В IDE есть кнопки: Upload, Install/Build и Stop, Run.
    Если на новом проекте нажать Install/Build то устройство (Galileo gen 2) как я понял начинает компилировать необходимые библиотеки и это продолжается почти сутки!!!
    В проекте используется http, socket и jsupm_bmpx8x.


    1. varerysan Автор
      16.06.2015 19:26
      +3

      Сутки это очень сурово. Всё обычно занимает несколько минут. Там нечему компилироваться, все файлы просто передаются на плату, и при необходимости подгружаются используемые библиотеки. Я бы попробовал с простого проекта, просто помигать встроенным светодиодом. Посмотреть, отсылается ли файл на плату. Долгий процесс может быть и из за проблем со связью платы и компа. И я ещё не протестировал работу с Intel Galileo Gen 2, только Edison. Если руки дойдут, то проверю на ней и напишу.


    1. varerysan Автор
      16.06.2015 19:33
      +3

      При первом запуске надо нажать Install/Build, чтобы всё загрузилось. Потом при каждом изменении проекта достаточно нажимать Upload. Чтобы остановить работающий проект можно нажать Stop. Чтобы запустить Run.


    1. varerysan Автор
      19.06.2015 04:11

      Я проверил XDK на плате Intel Galileo Gen 2. Создал проект с мигинием встроенного светодиода. Все заработало. Надо убедиться, что файл main.js передался, он должен быть на плате в папке /node_app_slot

      var mraa = require('mraa');
      
      var myOnboardLed = new mraa.Gpio(13);
      myOnboardLed.dir(mraa.DIR_OUT);
      var ledState = true;
      
      setInterval(periodicActivity,200);
      
      function periodicActivity()
      {
          myOnboardLed.write(ledState?1:0);
          ledState = !ledState;
      }

      Сначала нажал Install/Build, потом Upload, потом Run. Уже после Upload файл должен быть на плате.

      Может у вас старая версия Yocto? Хотя у меня XDK ругался, что на плате старая версия xdk-daemon, но все работало.


      1. Aspire89
        19.06.2015 06:09

        У меня тоже все работает, но очень долго компилирует при первой загрузке проекта. Как оказалось, так долго компилируется модуль socket.io