====== Iskra JS для тех, кто знаком с Arduino ======
Вы работали с платами Arduino или xDuino? Разобраться с [[Iskra JS|Iskra JS]] и экосистемой [[Espruino|Espruino]] будет просто. Iskra JS сочетает:
* простоту и скорость разработки на JavaScript;
* привычные функции и форм-фактор Arduino R3.
===== Зачем разбираться с JS =====
В 2005 году Arduino произвела фурор в мире DIY. Раньше разработчик гаджета должен был разбираться в программировании, аппаратной архитектуре и схемотехнике — даже чтобы сделать простое устройство. С появлением Arduino мир электроники стал доступен новичкам, а время разработки сократилось в разы.
Iskra JS — следующий шаг в упрощении разработки!
Разработка стала ещё быстрее, код понятнее, освоение проще. Можно сосредоточиться на сути проекта, а не на архитектурных особенностях микроконтроллера, борьбе за память и программировании на C++.
Сейчас не встретить веб-приложений на C++: балом правят PHP, Python, Ruby и JavaScript. Они медленнее C++, но гораздо выразительнее и проще: скорость разработки растёт, а освоить их легче.
Так и Iskra JS: если важны результат и комфортная среда программирования, а оптимизация не является самоцелью — плата Iskra JS станет логичным продолжением разработки устройств на платформе Arduino.
===== Железо =====
[[Iskra JS|Iskra JS]] сделана в форм-факторе Arduino R3 — с ней можно использовать те же платы расширения, модули и корпуса. Отличие в микроконтроллере: ARM Cortex-M4 с 1 МБ flash-памяти способен разместить как JavaScript-ядро, так и саму программу.
{{ :js:iskra_js:iskra-js.1.jpg |(фото платы)}}
Ядро прошивается в микроконтроллер при производстве, а программы загружаются через USB-порт. По USB можно и обновить ядро, а через SWD-разъём — полностью перепрошить устройство.
===== Отличия JavaScript =====
JavaScript и C++ — разные языки, в которых сходства меньше, чем различий. Но после программирования Arduino освоение JavaScript не вызовет проблем.
==== Код на верхнем уровне ====
Как отреагирует компилятор на такой код C++?
#include
Servo myServo;
myServo.attach(3); // Err
myServo.write(120); // Yargh!
Естественно, ошибкой. ''attach'' и ''write'' вызваны вне рамок ''setup'' или ''loop'', а код не может исполняться в глобальной области видимости.
В JavaScript код можно и нужно писать на первом уровне:
var myServo = require('@amperka/servo').connect(P3);
myServo.write(120);
Этот код выставит сервопривод на 3-м пине в положение на 120°.
==== Динамическая типизация ====
В C++ каждая переменная должна обладать явно прописанным типом: ''int'', ''bool'', ''float'', ''LiquidCrystal'' и т. д:
int x = 0;
bool b = true;
float f = 3.1415926;
FantasyShield myShield = FantasyShield(1, 2, 3, 4, 5, 6);
void setup() {
x = myLCD; // ошибка: x и myShield имеют разные типы
}
В JavaScript всё проще: тип переменной устанавливается автоматически, а в ходе выполнения программы переменная даже может изменять свой тип.
var x = 0;
var b = true;
var f = 3.1415926;
var myShield = FantasyShield(1, 2, 3, 4, 5, 6);
x = myShield; // Это нормально. x стала объектом-FantasyShield
==== Функции для работы с пинами ====
Функции ''pinMode'', ''digitalRead'', ''digitalWrite'', ''analogRead'' доступны и на Iskra JS. Не нужно учить ничего нового:
| ^ Arduino ^ Iskra JS ^
^ Считать цифровой пин | ''digitalRead(3)'' | ''digitalRead(P3)'' |
^ Считать аналоговый пин | ''analogRead(A3)'' | ''analogRead(A3)'' |
^ Записать в цифровой пин | ''digitalWrite(3, HIGH)'' | ''digitalWrite(P3, HIGH)'' |
^ Подать ШИМ-сигнал 50% | ''analogWrite(3, 127)'' | ''analogWrite(P3, 0.5)'' |
^ Установить режим пина | ''pinMode(3, OUTPUT)'' | ''pinMode(P3, 'output')'' |
Не нужно устанавливать режим пина (''pinMode''): при обращении к нему ядро выставит режим, основываясь на вызванной функции.
Философия Iskra JS заключается в отказе от использования низкоуровневых функций. Для популярной периферии уже написаны модули, которые позволяют оперировать терминами устройства, а не электрических сигналов.
Так выглядит код простой кнопки:
var myButton = require('@amperka/button').connect(P4);
myButton.on('click', function() {
console.log("Hey! You've clicked on me!");
});
==== Понятные строки ====
В C++ строка представляется в виде массива байт-кодов символов со специальным символом конца строки (''\x00''). Операции над строками всегда выглядят запутанными:
char str[40]; // нужно задумываться о длинах буферов
char val[10];
// перевод числа в строку — отдельная забота
itoa(analogRead(A0), val, 10);
strcpy(str, "Value: "); // копирование строки — особая функция
strcat(str, val); // склеивание тоже
if (strcmp(str, "Value: 0") == 0) {
// сравнение, как и всё, тоже отдельная функция
}
Строки в JavaScript — полноправный тип данных, и оперировать ими можно так же, как и числами.
var str = "Value: " + analogRead(A0);
if (str === "Value: 0") {
// ...
}
==== Резиновые массивы ====
Массивы в C++ — просто блоки памяти заданного на этапе компиляции размера. Элемент нельзя добавить или убрать «на лету». Для C++ есть библиотека STL, но она не доступна разработчикам Arduino: работа с массивами выглядит громоздко и неуклюже.
Массив JavaScript — полноправный тип данных (''[[Array]]'') со своими методами и свойствами. Элементы массива можно добавлять по мере необходимости:
var values = [];
while (digitalRead(P4)) {
// Метод push добавляет новый элемент
// в конец массива
values.push(analogRead(A0));
}
var sum = 0;
// Свойство length содержит текущую длину массива
for (var i = 0; i < values.length; ++i) {
// Обращение к элементам по индексу происходит
// привычным образом
sum += values[i];
}
var average = sum / values.length;
==== События ====
В JavaScript есть механизм событий, который помогает упростить некоторые задачи. Например, чтобы при нажатии кнопки светодиод переключался в противоположенное состояние, используем код:
// Объект-кнопка умеет генерировать событие "click".
// С помощью стандартного метода "on" мы можем подписаться
// на интересующее событие, чтобы сделать что-то полезное
// при его возникновении
myButton.on('click', function() {
myLed.toggle();
});
==== Библиотеки ====
В библиотеках — сила.
Как подключить библиотеку на Arduino? Нужно скопировать исходные файлы в правильную директорию и подключить ее в скетче директивой ''#include''. После этого становятся доступны классы и функции библиотеки. Однако если в двух библиотеках объявлены символы с одинаковыми именами — возникнет проблема; если обе используют один и тот же таймер — проблема. Если библиотека использует возможности другой библиотеки, забота о зависимостях — отдельная проблема.
Чтобы сделать собственную библиотеку, придется разделить код на файлы ''*.h'' и ''*.cpp''. Это разделение — историческое следствие недостатков языка C.
В JavaScript и Espruino библиотека подключается встроенной функцией ''[[builtins#require|require]]'' с именем библиотеки в качестве параметра.
// Переменная Servo будет содержать объект-библиотеку
var Servo = require('@amperka/servo');
Servo.defaultOptions.valueMin = -90;
Servo.defaultOptions.valueMax = +90;
var servo1 = Servo.connect(P13);
var servo2 = Servo.connect(P12);
var servo3 = Servo.connect(P11);
Механизм ''require'' ищет код библиотеки на локальном диске, и если он не найден, ищет его в интернете: в репозитории, на GitHub или в каталоге npmjs.org.
Можно забыть об установке библиотек вручную.
==== Именованные параметры ====
В скетчах на Arduino встречаются конструкции вида:
LiquidCrystal lcd(12, 11, 10, 5, 4, 3, 2);
Что означают эти параметры? Это номера управляющих пинов, но без документации не разобраться, на каком месте какой.
В JavaScript аналогичная запись выглядит так:
var lcd = LiquidCrystal.connect({
rsPin: P12,
rwPin: P11,
enablePin: P10,
d4Pin: P5,
d5Pin: P4,
d6Pin: P3,
d7Pin: P2
});
Код длиннее, но и читабельность на порядок выше. Помните: код пишется один раз, а читается сто.
==== Отсутствие delay ====
В Arduino часто используются функция ''delay'' — она определяет период исполнения кода или позволяет дождаться определённого события. У такого подхода есть существенный недостаток: во время задержки микроконтроллер крутит пустой цикл и не даёт выполняться другому коду. Для микроконтроллера ''delay'' — тяжёлая работа, от которой он не может оторваться.
На Iskra JS нет аналогов ''delay''. Чтобы управлять временем исполнения кода, существуют функции ''[[builtins#setTimeout|setTimeout]]'', ''[[builtins#setInterval|setInterval]]'', ''[[builtins#digitalPulse|digitalPulse]]'', ''[[pin#writeAtTime|Pin.writeAtTime]]''.
Например, чтобы заставить светодиод переключаться раз в секунду, можно воспользоваться ''setInterval'':
// setInterval будет вызывать функцию каждые 1000 мс.
setInterval(function() {
led.toggle();
}, 1000);
Пока микроконтроллер не занят исполнением одной функции, он может выполнять основной код или код других периодических функций. Если задач нет, микроконтроллер перейдёт в режим сна и проснётся, когда появится работа. Это позволяет экономить энергию.
===== Среда программирования =====
У Arduino есть среда программирования Arduino IDE. У Iskra JS — [[IDE|Espruino IDE]], установка и работа в которой проще.
Espruino IDE — это приложение на платформе Google Chrome, которое устанавливается буквально в один клик.
{{ :js:для_тех_кто_переходит_с_arduino:espruino_web_ide_005.png?nolink |}}
Главное окно среды разделено на две части: с одной стороны — аналог Serial Monitor, с другой — поле для кода программы. Программы загружаются в плату нажатием одной кнопки.
===== С чего начать =====
Понадобится железо: можно взять [[Iskra JS|Iskra JS]] или набор [[amp>product/yodo|Йодо]] из платы и модулей для экспериментов. К ним подойдут всё те же платы расширения и модули Arduino.
Знания о JavaScript, доступных функциях и объектах можно найти на [[js:|Wiki]]. Особого внимания заслуживает документация библиотек и [[builtins|встроенных функций]]. Они станут основой для новых программ.