Iskra JS для тех, кто знаком с Arduino

Вы работали с платами Arduino или xDuino? Разобраться с Iskra JS и экосистемой 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 сделана в форм-факторе Arduino R3 — с ней можно использовать те же платы расширения, модули и корпуса. Отличие в микроконтроллере: ARM Cortex-M4 с 1 МБ flash-памяти способен разместить как JavaScript-ядро, так и саму программу.

(фото платы)

Ядро прошивается в микроконтроллер при производстве, а программы загружаются через USB-порт. По USB можно и обновить ядро, а через SWD-разъём — полностью перепрошить устройство.

Отличия JavaScript

JavaScript и C++ — разные языки, в которых сходства меньше, чем различий. Но после программирования Arduino освоение JavaScript не вызовет проблем.

Код на верхнем уровне

Как отреагирует компилятор на такой код C++?

#include <Servo.h>
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 библиотека подключается встроенной функцией 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. Чтобы управлять временем исполнения кода, существуют функции setTimeout, setInterval, digitalPulse, Pin.writeAtTime.

Например, чтобы заставить светодиод переключаться раз в секунду, можно воспользоваться setInterval:

// setInterval будет вызывать функцию каждые 1000 мс.
setInterval(function() {
  led.toggle();
}, 1000);

Пока микроконтроллер не занят исполнением одной функции, он может выполнять основной код или код других периодических функций. Если задач нет, микроконтроллер перейдёт в режим сна и проснётся, когда появится работа. Это позволяет экономить энергию.

Среда программирования

У Arduino есть среда программирования Arduino IDE. У Iskra JS — Espruino IDE, установка и работа в которой проще.

Espruino IDE — это приложение на платформе Google Chrome, которое устанавливается буквально в один клик.

Главное окно среды разделено на две части: с одной стороны — аналог Serial Monitor, с другой — поле для кода программы. Программы загружаются в плату нажатием одной кнопки.

С чего начать

Понадобится железо: можно взять Iskra JS или набор Йодо из платы и модулей для экспериментов. К ним подойдут всё те же платы расширения и модули Arduino.

Знания о JavaScript, доступных функциях и объектах можно найти на Wiki. Особого внимания заслуживает документация библиотек и встроенных функций. Они станут основой для новых программ.