Espruino:Примеры/Сохранение кода на Espruino

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.


Сохранение кода на Espruino[1]

Когда вы загружаете код на Espruino обычным способом, он сохраняется в RAM-память Espruino. В таком случае, если вы выполните сброс платы или если у платы отключится питание, этот код будет потерян.

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

Итого

Просто напишите save() в левой стороне IDE и текущее состояние Espruino, включая весь сохранённый код, будет записано на flash-память, а потом снова загружено в момент запуска.

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

Выбрать нужный метод можно двумя способами:

  • Нажмите на кнопку с треугольником под кнопкой загрузки кода в IDE Espruino и выберите нужный вариант.
  • Кликните на иконку с шестерёнкой справа вверху (Settings), затем на Communications, найдите пункт Save on Send и выберите нужный вариант.

Более подробно читайте ниже.

Процесс загрузки

Чтобы понять, как лучше сохранять данные, нужно знать, как Espruino загружает сохранённый код.

Во время запуска Espruino делает следующее:

  • При нажатии на кнопку BTN1 или при выполнении сброса с помощью функции reset() задаёт в настройке hasBeenReset значение true.
  • Если в hasBeenReset не было задано значение true, ищет сжатый образ (.varimg в хранилище Storage) состояния интерпретатора, сохранённый при помощи save(). Если он есть, Espruino распаковывает его в RAM-память.
  • (для версии 2.0 и новее) Ищет в Storage файлы с названиями .boot0, .boot1, .boot2 и .boot3 и по очереди их выполняет. Если зажать кнопку BTN1 на Bangle.js, эти файлы выполнены не будут, но все прочие устройства всегда их выполняют.
  • Ищет в Storage файл под названием .bootrst, и если он существует, выполняет его (см. раздел «Save on Send» ниже).
  • Если в hasBeenReset не было задано значение true, а в шаге выше не был найден файл .bootrst, ищет в Storage файл под названием .bootcde и выполняет его (см. раздел «Save on Send» ниже).
  • Инициализирует все заранее инициализированные периферийные устройства.
  • Запускает все обработчики, зарегистрированные с помощью E.on('init', function() { ... });.
  • Запускает функцию onInit(), если она существует.

Если сброс Espruino был выполнен с помощью load(), этапы загрузки будут точно такими же, как описано выше, но в настройке hasBeenReset будет задано значение false. В Espruino v2.05 и новее за этими этапами также последует вызов функции load(имя_файла), которая загрузит заданный файл вместо .bootcde/.bootrst.

Есть два главных способа сохранения кода на Espruino:

С помощью функции save()

Если после загрузки кода написать save() в левой части IDE Espruino, текущее содержимое RAM-памяти Espruino будет сжато и записано на flash-память.

Это содержимое включает в себя:

  • Все переменные и функции.
  • Все прерывания, созданные с помощью setWatch().
  • Таймауты и интервалы, созданные с помощью setTimeout() и setInterval().
  • Состояния всех контактов.

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

Это стандартный способ сохранения кода на обычных Espruino-устройствах (для Bangle.js он не подходит), и это значит, что вы можете взаимодействовать со своим кодом в левой части IDE, меняя переменные и функции и даже что угодно сохраняя – даже свои изменения.

Например, если вы загрузите код var t = E.getTemperature() и напишите save(), в переменной t будет температура устройства в момент загрузки кода или даже в момент написания save(), а не в момент запуска устройства.

Однако это значит, что весь код, который был выполнен во время загрузки, повторно выполнен не будет. Например, к вашей Espruino может быть подключено внешнее устройство вроде LCD-дисплея, и после подачи питания этот LCD-дисплей нужно будет инициализировать (т.к. он не умеет запоминать своё состояние). В этом случае мы можем создать функцию onInit() (или добавить прослушиватель E.on('init', function() { ... })), которая будет автоматически вызываться интерпретатором при его инициализации.

Когда код будет сохранён, вы можете вернуть интерпретатор к «нулевому» состоянию при помощи функции reset(). Это не удалит все данные, сохранённые в flash-памяти, поэтому если вы перезагрузите устройство (или вызовите load()), это снова загрузит сохранённое ранее состояние. Чтобы полностью удалить сохранённый код, выполните reset(), а следом save(), чтобы сохранить в flash-память это очищенное «нулевое» состояние.

Плюсы

  • Когда код обретёт рабочее состояние, вы можете просто вызвать save(), и код продолжит работать.
  • Вы можете изменять код, сохранённый с помощью save(), а затем снова вызвать save(), чтобы сохранить свои изменения.
  • JS-код не сохраняется в flash-память в виде обычного текста, так что злоумышленнику будет труднее его извлечь.
  • Чтобы JavaScript-код в RAM-памяти быстрее выполнялся и был очень компактным, можно воспользоваться E.setFlags({pretokenise:1}).

Минусы

  • Не эффективен с точки зрения расходования памяти, так как всё хранится в RAM-памяти.
  • Может сбиться форматирование кода внутри функций. Кроме того, не сохранятся комментарии, находящиеся за пределами функций.
  • Любой код, выполняемый при запуске Espruino, нужно поместить в функцию onInit() или в обработчик событий E.on('init', function() { ... }).

Подводные камни

  • Если не воспользоваться функцией onInit() или E.on('init', ...), код во время загрузки выполняться не будет.
  • Поскольку данные функций setWatch() и setInterval() запоминаются, если вызвать их в onInit(), а затем несколько раз вызвать save(), в результате у вас получится много копий этой информации. Чтобы избежать этого, в onInit() можно вызвать clearInterval() и clearWatch().
  • Если вы загрузили какой-то код с помощью функции onInit() или E.on('init', ...), то помните, что в момент загрузки этот код выполнен не будет. Поэтому, чтобы протестировать его, вам надо будет либо вызвать save(), либо вручную выполнить onInit().

Save on Send (to Flash)

Save on Send – это настройка в IDE Espruino. Внутри неё неявно используется функция E.setBootCode() для сохранения JS-кода напрямую в flash-память Espruino. Этот сохранённый JavaScript-код будет выполнен при запуске Espruino.

Этот метод похож на программирование «обычного» микроконтроллера.

Например, если при включенной настройке Save on Send загрузить код var t = E.getTemperature(), в результате при каждом включении устройства в переменную t будет записываться новая температура, тогда как при использовании save() температура записывается в момент загрузки кода.

В настройке Save on Send (чтобы найти её, кликните на иконку с шестерёнкой справа вверху, затем на Communication, и далее ищите внизу пункт Save on Send) можно задать три режима:

  • No – код будет загружен в RAM-память, но может быть сохранён с помощью save() (см. выше). Под иконкой загрузки будет надпись «RAM».
  • Yes – JavaScript-код будет сохранён в flash-память и выполнен во время загрузки Espruino. Под иконкой загрузки будет надпись «Flash». Если вызвать функцию reset(), Espruino удалит из RAM-памяти весь код и не выполнит сохранённый JS-код. Это сохранит ваш JS-код в файл .bootcde, находящийся в Storage.
  • (устарел) Yes, execute even after reset() – JavaScript-код будет сохранён в flash-память и выполнен даже после загрузки Espruino. Под иконкой загрузки будет надпись «Flash» с треугольником «Внимание!». Если вызвать reset(), Espruino удалит из RAM-памяти весь код, сохранённый с помощью save(), но всё равно выполнит сохранённый JS-код. См. раздел «Оба способа» ниже. Это сохранит ваш код в Storage в файл .bootrst.

Сейчас эта опция удалена из онлайн-IDE Espruino, так как опасна и в 99% случаев просто не нужна. Впрочем, если вам надо воспользоваться этим вариантом, вы можете сохранить код в Storage в файл .bootrst.

Чтобы удалить любой код, сохранённый с помощью Save on Send, просто вызовите E.setBootCode() без аргументов.

Плюсы

  • Запускает весь код во время загрузки, избавляя от необходимости вызывать onInit().
  • Код внутри каждой функции хранится в flash-памяти, так что RAM-память расходуется в экономном режиме. То же самое касается и модулей, если в IDE поставлена галочка в настройке Modules uploaded as functions.

Минусы

  • Ваш JavaScript-код сохраняется в flash-память как обычный текст, из-за чего его можно легко прочесть.
  • Если вы внесёте какие-нибудь изменения в левую часть IDE, сохранить их не получится.
  • Использование E.setFlags({pretokenise:1}) не даст никакого эффекта, поскольку код функции будет сохранён в flash-память. Впрочем, вы можете задать "ram" в первом аргументе функции, чтобы принудительно загрузить её в RAM-память и токенизировать (это процесс, при котором сохраняемые слова разбиваются на байты, а пробельные символы удаляются).
  • В момент загрузки кода выполнить его нельзя – это можно сделать только при включении устройства.

Подводные камни

  • Если включить настройку Save on Send, загрузить код, а затем выключить её, на Espruino одновременно могут остаться оба фрагмента кода (см. раздел «Оба способа» ниже).
  • Если вызвать save() после сохранения кода в flash-память при помощи метода ниже, можно получить ошибку Got EOF expected ... или [ERASED]. Причина в том, что в RAM-память были сохранены функции, чьи элементы ссылаются на код, которого в flash-памяти больше нет.

Сохранение в Storage

Этот вариант аналогичен Save on Send (to Flash), но на Espruino 2v05 и новее вы можете вызвать load(имя_файла) – это выполнит сброс Espruino и загрузит JavaScript-код из именованного файла, хранящегося в памяти Espruino.

Оба варианта

Способы Save on Send и save() можно использовать вместе – более подробно читайте выше, в разделе «Процесс загрузки».

Этот метод позволяет при помощи Save on Send сохранять код, который будет гарантированно выполнять некоторые действия независимо от кода, сохранённого с помощью save().

Вы также можете воспользоваться файлами .boot0, .boot1, .boot2 и .boot3 – с их помощью можно сохранить дополнительный код, который никак не будет пересекаться с кодом, сохранённым другими способами. Например, можно добавить вот такой код:

require("Storage").write(".boot0", `
WIFI_NAME = "MyWiFi";
WIFI_PASS = "HelloWorld123";
`);

Благодаря этому коду у вас всегда будут заданы переменные WIFI_NAME и WIFI_PASS – независимо от другого загруженного кода.

Например, если вы создаёте устройство вроде домашнего Espruino-компьютера, то для сохранения кода инициализации дисплея и клавиатуры можно воспользоваться Save on Send или .boot0. Это позволит вам безопасно программировать сам компьютер, сохраняя его состояние при помощи save() – то есть, независимо от того, что вы сохранили на устройство, вы всегда сможете положиться на то, что дисплей и клавиатура настроены правильно.

Примечания

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

См.также

Внешние ссылки