====== 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|встроенных функций]]. Они станут основой для новых программ.