Espruino: JavaScript в микроконтроллере

    image

    «Зачем?», «Что за бред?», «Извращение!», «Фу-фу-фу» — вот некоторые из многих высказываний, которые мы услышали, когда выпустили плату Iskra JS на ядре Espruino.

    Зачем


    Когда правильный электронщик слышит, что что-то сделано на Arduino, температура его тела поднимается примерно на полградуса: «взяли годный микроконтроллер и вместо того, чтобы фигак-фигак и регистрами выжать из него все соки, опошлили всё на свете… нет слов, одна ненависть».

    Но ведь можно пойти ещё дальше. Взять микроконтроллер Cortex M4, который в десятки раз богаче того, что стоит на той же Arduino Uno, запихнуть туда интерпретатор JavaScript и делать проекты на JavaScript!

    Думаю, что на этом моменте те, кто не готов к такому надругательству над святыми микросхемами, уже лопнули. Я продолжу для остальных.



    В хобби-электронике для подавляющего большинства проектов Arduino Uno хватает за глаза. И даже не важно будет она работать на штатных 16 МГц или на 4 МГц: включится ли подсветка кровати или насос на даче за 1 мс или за 10 мс не имеет значения.

    Что имеет значение, так это то, что C++ — сложный и деревянный. Да, он близок к железу, но именно поэтому требует глубокого понимания этого железа и информатики. Добавим к этому синтаксис с кучей нюансов, который является суммой ошибок 30-летнего развития языка. Затем лишим то, что получилось адекватных массивов, строк, словарей, а ещё лучше — вообще лишим динамической памяти. Вот, наконец мы получили Arduino.

    Наблюдая за вопросами на нашем форуме я вижу, что люди тратят время на долбание с языком, а не на сути своих проектов. Так почему бы не снизить пропасть между железом и людьми? Железо — дешёвое и постоянно развивается, время людей — дорогое и ниоткуда не берётся.

    Похожий процесс я наблюдал много лет пока работал в разработке игр. В 2003-м году представить было нельзя, что игру можно писать на чём-то кроме C/C++. Потом в движках появились встроенные скрипт машины. А затем и создание полноценного продукта стало возможным без единой строки на Си. Только Java, C#, Ruby, Python, JavaScript. Это логично, потому что железо стало тянуть «медленные языки», а засчёт в разы более быстрой разработки и большего количества доступных программистов, стало возможным больше ресурсов тратить на интересность игры, а не на поиск утечек памяти.

    На что похоже программирование микроконтроллеров на JavaScript


    Программирование похоже на… JavaScript. На разработку для браузера или Node.js. Вот пример:

    // SMS
    var sim = require('@amperka/Sim900r').connect();
    sim.powerOn();
    sim.on('sms', function(sms) {
        var rgb = sms.text.split(' ');
        var r = +rgb[0];
        var g = +rgb[1];
        var b = +rgb[2];
        if (isNaN(r + g + b)) return;
        fadeTo(r, g, b);
    });
    
    // Лента
    SPI2.setup({baud:3200000, mosi:B15});
    var LED_COUNT = 150;
    var colors = new Uint8ClampedArray(LED_COUNT * 3);
    
    function fadeTo(r, g, b) {
      var n = 0;
      var ivalID = setInterval(function() {
        colors[n*3 + 0] = b;
        colors[n*3 + 1] = r;
        colors[n*3 + 2] = g;
        SPI2.send4bit(colors, 0b0001, 0b0011);
        if (n++ >= LED_COUNT) {
          clearInterval(ivalID);
        }
      }, 50);
    }
    


    Эта программа занимается тем, что принимает SMS’ки и в зависимости от их текста бегущим огоньком меняет цвет подключённой RGB-ленты. Мы делали такой проект для нашей новогодней ёлки, но на классической Arduino. Кода было в 4 раза больше.

    Ядро Espruino


    Итак, у компьютера есть движки V8 и SpiderMonkey для JavaScript. А у микроконтроллеров есть движок Espruino.

    Если говорить грубо, движок Espruino — это прошивка (скетч, скетчище) для микроконтроллеров, который в свою очередь может исполнять JavaScript-код, который вы передаёте на плату через serial-соединение, например по USB.

    Espruino — это open source проект, фаундером которого является Gordon Williams. Человек, который в одиночку реализовал большую часть всей экосистемы и успешно сходил с ней на KickStarter.

    Эта экосистема состоит из нескольких основных частей:

    • Espruino Web IDE — среда программирования;
    • Espruino Firmware — JavaScript-машина, которая крутится на микроконтроллере;
    • Железо — сами платы, которые совместимы с Espruino;
    • Стандартная библиотека и внешние библиотеки;
    • Документация.


    Обо всём по порядку.

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


    Написание JavaScript-кода, загрузка его в плату и наблюдение за выводом происходит в Espruino Web IDE. Это приложение для Google Chrome, поэтому оно ставится в 1 клик и работает под всеми настольными операционками.

    Для пользователя эта среда заменяет Arduino IDE.

    По функционалу Espruino IDE, конечно, много проще полновесных Eclipse или Visual Studio, но она более продвинута, чем Arduino IDE.

    image


    Если окинуть окно среды одним взглядом, увидите редактор кода (справа), панель консоли (слева) и кнопку для загрузки кода в плату.

    Консоль — это как Serial Monitor в Arduino IDE, но в отличии от него он не только выводит, но и принимает: можно прямо налету, без ресета и загрузки новой программы, вызвать функцию, посмотреть значение переменной, прервать цикл и т.п.

    Поработав плотнее, вы столкнётесь с отладчиком (ага, step-by-step исполнение, breakpoint’ы), подсказками редактора, подсвечиванием ошибок налету и прочими приятными фишечками.

    Кроме того, прямо из IDE можно обновить версию самой Espruino Firmware до последней. Среда сама сама скачает последний доступный релиз и зальёт его через USB.

    Firmware


    Сердце системы — прошивка, которая крутится на микроконтроллере. Она написана на чистом Си, загружается единожды в отдельное место флеш-памяти микроконтроллера и занимается тем, что исполняет пользовательский JS-код, который приходит через последовательное соединение или находится в пользовательской области флеш-памяти.

    Если схематично изобразить расклад флеш-памяти для Iskra JS, получим такую картину:

    image

    Загрузчик


    Сначала идёт загрузчик, которы не обновляется (условно) никогда. Это маленькая программа, с исполнения которой абсолютно всегда начинается жизнь микроконтроллера после включения или сброса.

    Загрузчик делает довольно простую вещь:

    • Если не нажата пользовательская кнопка BTN1, тут же передаёт управление интерпретатору.
    • Если кнопка была нажата при старте, он зацикливается на себе и ждёт либо пока кнопку нажмут ещё раз, чтобы таки выйти в интерпретатор, либо ждёт пока через USB загрузят новую версию интерпретатора. В этом случае bootloader перепишет область флеш-памяти с Espruino Firmware, а мы получим на плате свежую версию интерпретатора.


    Интерпретатор


    Итак за загрузчиком следует интерпретатор, который и делает основную работу.

    Когда он стартует, проверяет с помощью проверки волшебного байта, есть ли в пользовательской флеш-памяти сохранённый код JavaScript. Если есть, принимается его исполнять.

    Если кода нет или же, если мы вручную с помощью BTN1 вышли из загрузчика, ничего слёту исполнено не будет, мы просто получим готовую к работе JS-машину.

    Свободная память


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

    Пользовательский код


    Здесь хранится та JS-программа, которую выполняет интерпретатор. Программа лежит там в обычном текстовом виде, одним брикетом, как есть.

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

    Вернее, всю работу делает не IDE, а её GUI-независимая составная часть EspruinoTools, у которой есть интерфейс коммандной строки и точка входа для использования в качестве Node.js-модуля. Поэтому при желании вы можете использовать Vim, Atom, Eclipse или чего вам удобнее для разработки.

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

    Библиотеки


    Библиотеки можно разделить на 3 вида: стандартная, встроенные и внешние.

    Стандартная библиотека


    Стандартная библиотека — это функции и объекты, которые можно слёту использовать. Среди них, как привычные и прописанные в стандарте языка `Date`, `JSON`, `Math`, `Object`, `setInterval`, `setTimeout` и прочие-прочие, так и специфичные для Espruino функции для работы с пинами, интерфейсами, памятью.

    Среди функций вы найдёте привычные ардуинщику `pinMode`, `digitalRead`, `digitalWrite`, `analogRead`, `analogWrite`, а ещё не столь привычные `setWatch` для наблюдения за входными пинами, `digitalPulse` для точной генерации последовательности цифровых импульсов и ещё много разных штук для работы с периферией.

    Кстати, несмотря на то, что функция `pinMode` на платформе есть, по умолчанию она работаем сама, за кадром, поэтому в большинстве сценариев можете о ней не вспоминать (на этом месте громко лопнул последний электронщик).

    Чего вы не найдёте в стандартной библиотеке, так это `delay`. Mamma Mia, как я благодарен за это! Достаточно немного перестроить мышление и становится ясно, что всё решается через `setInterval`, `setTimeout` без всякого `delay`.

    В благодарность вы получаете асинхронность исполнения: можете мигать двадцатью светодиодами независимо, каждым со своей частотой. А ещё вы получаете энергоэффективность: `delay` — это прожигание процессорного сремени, а без `delay` процессор может мирно спать пока нечего делать. В Espruino пока нет работы процессор засыпает и потребляет в десятки раз меньший ток.

    Встроенные библиотеки


    Встроенные библиотеки — это такая же часть стандартной библиотеки в том смысле, что они жёстко вшиты в Espruino Firmware, но для доступа к их функционалу требуется синтаксически их подключить с помощью знакомой JavaScript-программистам функции `require`.

    Среди таких библиотек вы найдёте, например, `fs` для работы с файловыми системами SD-карт, `http` для создания web-интерфейса к своему устройству, `Waveform` для записи/воспроизведения аудиосигналов и другие.

    Стандартная и встроенные библиотеки реализованы на чистом Си, а пользователю выведен лишь JavaScript-интерфейс в виде набора объектов, функций и модулей. Это позволяет достичь максимальной эффективности для реализации сложных алгоритмов, оставляя при этом возможность обращения к ним из высокоуровнего кода.

    Вы и сами можете реализовать на Си что-то, на что JavaScript сказал бы «нет, спасибо», а наружу вывести только несколько необходимых для запуска хвостов.

    Внешние библиотеки


    Внешние библиотеки — это то, что формирует богатство экосистемы. Их пишут на JavaScript авторы Espruino, мы пишем, пишут пользователи Espruino.

    Храниться они могут на локальном диске, в песочнице текущего проекта, могут быть в интернет-репозитории, могут быть на GitHub’е, могут лежать на NPM’е.

    Подключаются библиотеки, традиционно, с помощью `require`.

    Когда среда видит `require`, она рекурсивно загружает их из удалённого источника и при загрузке скетча приклеивает их содержимое к началу вашей программы. Таким образом для микроконтроллера формируется цельный брикетик с кодом, о котором я говорил выше.

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

    Железо


    Вот мы и подошли к самому осязаемому компоненту экосистемы Espruino. Это платы, которые могут исполнить Espruino Firmware.

    image


    К ним относятся оригинальные Espruino Board, Espruino Pico, а также выпущенная Амперкой Iskra JS. На перечисленных платах всё уже установлено на заводе, они сразу готовы к работе.

    Вы можете несколько раз стукнуть в бубен и самостоятельно поставить Espruino-ядро на STM32 Discovery, Nucleo и, внезапно, на ESP8266.

    Документация


    Нет ничего грустнее, чем что-то очень крутое и мощное, но без документации.

    У Espruino — неплохая документация. На www.espruino.com/Reference вы найдёте много справочных и обучающих статей.

    Мы хотим сделать ещё больше и лучше. Поэтому создали отдельный раздел: js.amperka.ru, снимаем видеоуроки. Работа пока в процессе, но многое уже освещено.

    И традиционно для тех, кто любит коробочные решения, мы создали набор «Йодо». В нём Iskra JS, модули и цветная книга.

    Вот такая петрушка. Загадкой остаётся лишь то, смог ли хоть один правильный электронщик дочитать до этой строки?
    Амперка 34,94
    Производим электронные модули и конструкторы
    Поделиться публикацией
    Похожие публикации
    Никаких подозрительных скриптов, только релевантные баннеры. Не релевантные? Пиши на: adv@tmtm.ru с темой «Полундра»

    Зачем оно вам?
    Реклама
    Комментарии 37
    • 0
      А stm32f4discovery на картинке про поддерживаемые платы просто для смеха размещена, или ее тоже можно прошить и использовать с Espruino? А то я бы достал ее с полки чтоб попрообовать.
      • 0
        Нет, вовсе не для смеха. Под неё можно собрать ядро из исходников и загрузить через штатный ST-Link по USB-кабелю. Мы пробовали — работает.
        • 0
          Оу, отлично. Надо сдуть пыль со своей и попробовать.
      • +2
        Что имеет значение, так это то, что C++ — сложный и деревянный. Да, он близок к железу, но именно поэтому требует глубокого понимания этого железа и информатики.

        Скажу как человек, который любит высокоуровневые языки и которому лень изучать что то другое.
        Когда только начал смотреть в сторону arduino много гуглил, можно ли под него взять какой нибудь питон и не париться. Но когда все же попробовал писать на С++ (знаю знаю, arduino ide добавляет очень много сахара), никаких проблем не возникло. Если не лезть в дебри, то можно обойтись обычным официальным мануалом. Это не сложнее, чем JavaScript.
        • –1
          Сложно написать большую программу, а причин две: 1) отсутствие неявного преобразования типов и как следствие — выходящие из под контроля монструозные функции и 2) погруженность в единый loop(), от которого, как ни крутись, не избавишься.
          • 0
            2) погруженность в единый loop(), от которого, как ни крутись, не избавишься

            Вы не поверите, но с этим никаких проблем!
            Вам никто и ничто не мешает написать типичное "int main(){...}".
            • +1
              Ну тут как посмотреть. Я бы сказал, что тут у медали, как водится, две стороны
              Извините за каламбур, но плюсы плюсов:
              1) отстутствие неявного преобразования типов, и, следовательно, упрощение разработки в IDE и меньшее количество багов на этапе отладки
              и
              2) явная погруженность в единый loop(), что позволяет намного точнее управлять происходящим, нежели некий абстрактный setInterval()

              p.s: код, чтобы гонять туда-сюда массивы на любом языке плюс/минус одинаков
              • 0
                «код, чтобы гонять туда-сюда массивы на любом языке плюс/минус одинаков»
                вот только почему то изредка попадаются вакансии на то чтоб гонять туда сюда массивы байт в сыром виде,
                и начинаются они с 150-200тр.
                совсем недавно мужик искал спеца по передаче данных для мед техники и томографов с «простой» задачей — достигнуть 50% полосы пропускания USB 2.0 HS (480 МГц) на фтди. Сфейлилась вся очередь желающих и соискателей.
              • 0
                Хм, я, например, в js как раз больше всего и не перевариваю это неявное приведение типа.
            • 0
              Не смотря на то, что старенькая STM32VLDISCOVERY в списке поддерживаемых, после прошивки бутлоадера по ману с их сайта — дискавери отвалилась навсегда. Теперь она не определяется в системе и прошить ее штатно невозможно. Ну а поскольку отдельного STLink-а под руками нет — пришлось на все это забить. Вот такая печаль-беда. Теперь есть дискавери-сувенир =(.
              • 0
                Именно версию VL не пробовал, пробовал F4. Если что-то пошло не так, на плате должен быть пин BOOT0. Замыкаете его на 3.3V, ресетите и вы в DFU — сможете заново залить нужную прошивку.
                • 0
                  СТМ32 можно навсегда ушатать, только прошив Level 2 защиту. Из всех остальных состояний они поднимаются через бутлоадер, жытаг и даже СВД с пре-ресетом
                • 0
                  Эти Espruino стоят практически как полноценные мини-пк вроде raspberry, ну и зачем они тогда?
                  • 0
                    Это разные вещи. Каждой — своя задача. RPi стартует дольше минуты, опустошает батарейку моментально и не имеет аппаратных средств для работы с большей частью хоббийной периферии, зато мощи хоть отбавляй: Linux, мультимедиа, все дела. Espruino из другого лагеря: лёгкая, энергоэффективная, коммуникабельная.
                  • +1
                    ----Программирование похоже на… JavaScript.

                    то что написали практически один в один С++
                    • 0
                      Может, не совсем удачный пример. Вся прелесть становится очевидно в проектах сложных с архитектурной точки зрения.
                      Но даже в этом сниппете: сможете на C++ также быстро разобрать SMS и проверить её валидность?
                    • +2
                      Если имплементированы необходимые библиотеки, то ваш код будет работать без изменений на C++.
                      Я понимаю если бы вы писали про голимый С — там с архитектурной точки зрения все плохо, но java-script — это полный кошмар среди языков, хуже наверное тока VB.

                      Современный С++ обладает большинством фишек функциональных языков.
                      А уж с архитектурой приложений на нем всегда было в порядке.
                      • +1
                        Ну а всё же, смску разберёте на C++?
                        • +1
                          Мне кажется, вы лукавите, судя по документации, с вашем примере просто использована встроенная функциональность: http://www.espruino.com/SIM900

                          На C++ при наличии готовой библиотеки было бы что-то типа такого (не ручаюсь за синтаксис, я на С++ не писал давольно давно, а профессионально, в общем, никогда):

                          #include <stdio.h>
                          #include <sim900.h>

                          void SMSHandler(char* text) {
                          int r,g,b;
                          sscanf(text, "%i %i %i", &r, &g, &b);
                          if(r && g && b) fadeTo(r,g,b);
                          }

                          sim900 *sim = new sim900();
                          sim->powerOn();
                          sim->addHandler(sim900::EVENT_SMS, &SMSHandler);

                          • 0
                            А хорошо :) Речь не о библиотеке шла, а о скорее банальных вещах. Вот в вашем примере: кто хозяин строки текста СМС, т.е. кто должен освободить память? Кто-то как-то должен и для этого должен быть дополнительный код или вызов. Аналогично, addHandler мы сделали, но где-то нужно давать шанс этому хендлеру вызываться. Ничего сложного, но мелочей много и что-то обычно уходит из виду.
                            • +1
                              >> кто хозяин строки текста СМС, т.е. кто должен освободить память
                              класс sim900, он нам ее передает, так что это его проблемы.

                              >> нужно давать шанс этому хендлеру вызываться
                              опять же, sim900 его и вызовет

                              Для нас все будет точно так же, на нашем уровне абстракции нам только надо написать обработчик с прототипом, указанным в документации («SMSHandler») и зарегистрировать его («sim->addHandler(sim900::EVENT_SMS, &SMSHandler);»).

                              Я понимаю, к чему вы клоните :) Да, многие вещи на JS делать приятнее и можно чуть дольше оставаться в зоне комфорта, не отвлекаясь на какие-то низкоуровневые моменты. Но если проект хоть немного сложный, думать и разбираться придется и там и там. Причем, на JS борьба, например, с утечкой памяти, едва ли не сложнее, чем в С.

                              Я просто хотел сказать, что C++ позволяет при желании писать точно в таком же стиле. А то, что многие примеры «как сделать XXX на МК» в интернете представляют собой лапшу из бизнес-логики и двоичных операций над регистрами — это не вина языка.
                        • 0
                          >java-script — это полный кошмар

                          Ну ужас. Но не УЖАС УЖАС! (с)

                          На js можно транспилить что то вроде больше сотни языков https://github.com/jashkenas/coffeescript/wiki/list-of-languages-that-compile-to-js
                          Берете любой по своим понятиям и пишите — в чем проблема?

                          PS авторам — может действительно стоило бы разобрать пример с каким нибудь питоном?
                        • 0
                          … а теперь мы попробуем запустить майнкрафт на AtTiny48!
                          • 0
                            Майнкрафт на JavaScript?
                          • 0
                            А кстати, что у Espruino с генерацией точной частоты?
                            Чтобы сделать музыкальный синтезатор на Arduino, я выводил tone() на пин, затем железно соединял его с прерыванием, по которому выполнялся код, который обрабатывал короткий Калман-фильтр с клавиатуры и мог выставить новое значение tone().
                            SetInterval(), как я понимаю, не гарантирует микросекундную точность?
                            С другой стороны, digitalPulse не дает последовательной пульсации.
                            • 0
                              Задача tone — генерация ШИМ с 50% скважностью, но с заданной частотой, которая отличается от «жёстко-установленной» для analogWrite.
                              На Espruino всё проще, analogWrite сама принимает параметр частоты. Вот, например, 3 кГц:
                              analogWrite(P2, 0.5, {freq: 3000});

                              Или вот сигнал для серво:
                              analogWrite(P2, 0.05 /* 1000 us */, {freq: 50});
                              • 0
                                Спасибо, это хороший вариант.
                                Обратно также через прерывания заводить?
                                • 0
                                  Да, через setWatch. Это не совсем настоящее прерывание: при возникновении события ставится лишь флаг на исполнение, а настоящее исполнение происходит в основном event-loop’е, но его будет вполне достаточно, если в event-loop’е нет других задач.
                                  Настоящие прерывания всё равно возможны, только они сложнее: обработчик обязан быть написан на нативном Си.
                                  • 0
                                    Не, это работать не будет. Впрочем, никто не мешает мне поставить рядом ардуино для таких задач…
                            • 0
                              Подробнее про ESP8266 можете рассказать? Интересно использование Espuino с дешевым железом
                            • 0
                              C++ тоже далек от железа. Как, например, на нём сделать циклический сдвиг через флаг переноса? И как вообще работать с флагами? Всякие прикольные инструкции не поиспользуешь. Код, для которого нужны жесткие тайминги, тоже непонятно как писать. Кто его знает во что C++ код будет откомпилирован и сколько тактов это будет выполняться.
                              С другой стороны, для большинства задач это наверное не важно.
                              • 0
                                _asm(...)
                                • 0
                                  #asm(«sei»);

                                  Еспруино это ладно. Меня вчера матерый электронщик, дед с окладистой бородой, спрашивал — вот смотри, раньше в электронике-60 системах была команда Halt, меня интересует, а сейчас на коре2дуо я такой же фокус повторить смогу? Тырить осциллографом уровни при адресации… Ну, я ему обьяснил, как устроена материнка.
                                  Я его огорчил. Сильно. Потому что нижнего уровня почти не осталось. А если еще контроллеры начнут на яваскрипте запускать — то всё, тут даже всей лабораторией с кучей навороченных приборов уже мало что поймёшь. Утрирую, но нынешние SOC — кошмар в плане ремонта и отладки, если ты не подписал NDA.
                              • 0
                                Здесь WebAssembly был бы в тему, чтобы заливать в железку байт-код, а не текст JavaScript.
                                • 0
                                  Видимо они решили, что 256КБ флэша хватит всем, а кому не хватит пусть страдают
                                • +1
                                  Главная проблема любого фрэймворка и высокоуровнего языка — это документация. О многих имеющихся функциях не узнаешь, пока на stackoverflow кто-нибудь не упомянуть. Откуда информацию берут тамошние обитатели — для меня загадка. Пока что единственный пример, который меня в этом плане устроил — это док на unity3d. Каждый метод, каждое свойство идут здоровенным списком и, как минимум, дают знать о том что они существуют. А примеры уже внутри, не снаружи. Обратный пример — Qt: сначала читаете примеры, в которых есть не всё, а потом ищем класс в доке. Наоборот не получится: классификация их по разделам угробищная и не привязана к решаемой задаче, как в U3D. Или stackoverflow. Вашу доку сейчас посмотреть не могу, но вы можете сделать это сами и оценить к какому из примеров она ближе. Если к юнити — молодцы, не считая того, что в принципе сделали ересь.

                                  Ардуинщики в большинстве своём не станут профессионалами в отличие от тех, кто в изучал своё хобби на примере 2313 и ассемблерных уроков с радиокота и им подобных. Зато первые хороши тем, что им можно что нибудь продать, это да.

                                  Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                  Самое читаемое