Espruino:Примеры/Сбор данных при помощи Espruino: различия между версиями

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску
Нет описания правки
Нет описания правки
Строка 5: Строка 5:
=Сбор данных при помощи Espruino<ref>[https://www.espruino.com/Data+Collection www.espruino.com - Data Collection with Espruino]</ref>=
=Сбор данных при помощи Espruino<ref>[https://www.espruino.com/Data+Collection www.espruino.com - Data Collection with Espruino]</ref>=


Зачастую требуется собрать устройство, которое будет просто «сидеть» в одном месте и собирать (регистрировать) данные. Espruino-устройства особенно хорошо подходят для этой задачи, т.к. все они оснащены часами реального времени (RTC-часами) и в состоянии простоя расходуют очень мало энергии.
Зачастую требуется собрать устройство, которое будет просто ''«сидеть»'' в одном месте и собирать (регистрировать) данные. [[Espruino-устройства]] особенно хорошо подходят для этой задачи, т.к. все они оснащены часами реального времени ([[RTC]]-часами) и в состоянии простоя расходуют очень мало энергии.


Примечание: Пример сбора данных с помощью Bangle.js можно найти [https://www.espruino.com/Bangle.js+Storage тут].
'''Примечание:''' Пример сбора данных с помощью [[Bangle.js]] можно найти [https://www.espruino.com/Bangle.js+Storage тут].


На простейшем уровне ваш код должен выглядеть как-то так:
На простейшем уровне ваш код должен выглядеть как-то так:
Строка 22: Строка 22:
==Считывание данных==
==Считывание данных==


Во-первых, считывать данные с Espruino-устройств можно при помощи функции E.getTemperature() – она считывает температуру с помощью температурного датчика, который встроен во все Espruino-устройства.
Во-первых, считывать данные с [[Espruino]]-устройств можно при помощи функции E.getTemperature() – она считывает температуру с помощью температурного датчика, который встроен во все [[Espruino-устройства]].


Кроме того, [https://www.espruino.com/Puck.js#on-board-peripherals множеством различных датчиков] оснащено устройство Puck.js – вы тоже можете считывать с них данные.
Кроме того, [https://www.espruino.com/Puck.js#on-board-peripherals множеством различных датчиков] оснащено устройство [[Puck.js]] – вы тоже можете считывать с них данные.


Но вы также часто будете использовать analogRead(pin) для считывания аналоговых данных с контактов. Кроме того, вам могут потребоваться [https://www.espruino.com/Modules модули] Espruino для взаимодействия с внешними датчиками (например, с [https://www.espruino.com/DS18B20 температурным датчиком DS18B20]).
Но вы также часто будете использовать analogRead(pin) для считывания аналоговых данных с контактов. Кроме того, вам могут потребоваться [https://www.espruino.com/Modules модули] Espruino для взаимодействия с внешними датчиками (например, с [https://www.espruino.com/DS18B20 температурным датчиком DS18B20]).
Строка 34: Строка 34:
Но иногда сохранение временной метки всё же нужно. Например, вы хотите знать моменты времени закрытия и открытия двери – в этой ситуации никаких данных, кроме момента времени, когда произошло событие, сохранять не надо.
Но иногда сохранение временной метки всё же нужно. Например, вы хотите знать моменты времени закрытия и открытия двери – в этой ситуации никаких данных, кроме момента времени, когда произошло событие, сохранять не надо.


Прочесть время в удобочитаемом формате можно при помощи функции (new Date()).toString(), а  помощью функции Date.now() можно прочесть количество миллисекунд, прошедших с  1970 года.
Прочесть время в удобочитаемом формате можно при помощи функции (new Date()).toString(), а  помощью функции Date.now() можно прочесть количество миллисекунд, прошедших с  [[1970 год]]а.


Считываемое время соответствует времени RTC-часов, которые встроены в Espruino. Настроить их можно при помощи setTime(secondsSince1970). Вы также можете поставить галочку в пункте Set Current Time в онлайн-IDE (чтобы найти его, кликните на кнопку с шестерёнкой справа вверху, а затем на Communications – пункт Set Current Time будет в самом низу) – это автоматически задаст текущее время при следующей загрузке кода. Если вы используете библиотеку Web Bluetooth для [https://www.espruino.com/Web+Bluetooth#extra-features Puck.js], вы также можете воспользоваться функцией Puck.setTime() на вебсайте с Web Bluetooth.  
Считываемое время соответствует времени [[RTC]]-часов, которые встроены в [[Espruino]]. Настроить их можно при помощи setTime(secondsSince1970). Вы также можете поставить галочку в пункте '''Set Current Time''' в онлайн-IDE (чтобы найти его, кликните на кнопку с шестерёнкой справа вверху, а затем на '''Communications''' – пункт '''Set Current Time''' будет в самом низу) – это автоматически задаст текущее время при следующей загрузке кода. Если вы используете библиотеку Web Bluetooth для [https://www.espruino.com/Web+Bluetooth#extra-features Puck.js], вы также можете воспользоваться функцией Puck.setTime() на вебсайте с Web Bluetooth.  


==Хранение данных==
==Хранение данных==


Далее надо определиться, как вы будете хранить свои данные. У вас есть несколько вариантов. Если вы хотите пропустить этот этап и просто получить какой-то рабочий вариант, ищите абзац с require("Storage").open в разделе «Flash-память» ниже.
Далее надо определиться, как вы будете хранить свои данные. У вас есть несколько вариантов. Если вы хотите пропустить этот этап и просто получить какой-то рабочий вариант, ищите абзац с require("Storage").open в разделе [http://wikihandbk.com/wiki/Espruino:%D0%9F%D1%80%D0%B8%D0%BC%D0%B5%D1%80%D1%8B/%D0%A1%D0%B1%D0%BE%D1%80_%D0%B4%D0%B0%D0%BD%D0%BD%D1%8B%D1%85_%D0%BF%D1%80%D0%B8_%D0%BF%D0%BE%D0%BC%D0%BE%D1%89%D0%B8_Espruino#Flash-.D0.BF.D0.B0.D0.BC.D1.8F.D1.82.D1.8C «Flash-память»] ниже.


=== RAM-память – JavaScript-переменные ===
=== RAM-память – JavaScript-переменные ===


Простейший (и наименее эффективный) вариант – это просто сохранить данные в JavaScript-массиве. Например:
Простейший (и наименее эффективный) вариант – это просто сохранить данные в [[JavaScript-массив]]е. Например:


<syntaxhighlight lang="javascript" enclose="div">
<syntaxhighlight lang="javascript" enclose="div">
Строка 54: Строка 54:
</syntaxhighlight>
</syntaxhighlight>


Но в таком случае память расходуется неэффективно. С каждой записью данных будет использоваться всё больше памяти – до тех пор, пока не будет забита вся память Espruino.
Но в таком случае память расходуется неэффективно. С каждой записью данных будет использоваться всё больше памяти – до тех пор, пока не будет забита вся память [[Espruino]].


Обойти это можно при помощи ограничения размера массива. Например:
Обойти это можно при помощи ограничения размера массива. Например:
Строка 72: Строка 72:
=== RAM-память – типизированный массив ===
=== RAM-память – типизированный массив ===


Хранение данных в JavaScript-переменных – это простой, но в то же очень требовательный к памяти метод (примерно 32 байта на один элемент). Но, имея определённые знания о том, что из себя представляют входные данные, мы можем хранить их в гораздо более компактной форме.
Хранение данных в [[JavaScript-переменных]] – это простой, но в то же очень требовательный к памяти метод (примерно ''32 байта на один элемент''). Но, имея определённые знания о том, что из себя представляют входные данные, мы можем хранить их в гораздо более компактной форме.


К примеру, если мы знаем, что каждый элемент считанных данных – это число, то можем использовать для их хранения фиксированный 32-битный буфер для чисел с плавающей точкой.
К примеру, если мы знаем, что каждый элемент считанных данных – это число, то можем использовать для их хранения фиксированный ''32-битный буфер'' для чисел с плавающей точкой.


<syntaxhighlight lang="javascript" enclose="div">
<syntaxhighlight lang="javascript" enclose="div">
Строка 87: Строка 87:
</syntaxhighlight>
</syntaxhighlight>


В этом случае для одного элемента будет использоваться только 4 байта, а не 32, как раньше.
В этом случае для одного элемента будет использоваться только ''4 байта'', а ''не 32'', как раньше.


Кроме того, для хранения очень точных чисел можно воспользоваться объектом Float64Array (8 байт), а для хранения целых чисел – объектами Uint8Array/Int8Array (1 байт), а также Uint16Array/Int16Array/Uint32Array/Int32Array.
Кроме того, для хранения очень точных чисел можно воспользоваться объектом Float64Array (8 байт), а для хранения целых чисел – объектами Uint8Array/Int8Array (1 байт), а также Uint16Array/Int16Array/Uint32Array/Int32Array.


В коде выше используется принцип кольцевого буфера, а не сдвиг элементов внутри самого массива (поскольку методов push() и shift() в типизированных массивах вроде Float32Array нет). Это значит, что при выводе данных с переменной logIndex нужно будет работать задом наперёд, чтобы данные выводились в правильном порядке.
В коде выше используется принцип кольцевого буфера, а не сдвиг элементов внутри самого массива (поскольку методов push() и shift() в типизированных массивах вроде Float32Array нет). Это значит, что при выводе данных с переменной '''logIndex''' нужно будет работать задом наперёд, чтобы данные выводились в правильном порядке.


Но если вам нужен сдвиг данных внутри массива, то сделать это можно, применив к массиву метод set():
Но если вам нужен сдвиг данных внутри массива, то сделать это можно, применив к массиву метод set():
Строка 106: Строка 106:
</syntaxhighlight>
</syntaxhighlight>


Это медленнее, чем при использовании logIndex выше, но так проще выводить данные и делать графики на их основе.
Это медленнее, чем при использовании '''logIndex''' выше, но так проще выводить данные и делать графики на их основе.


=== RAM-память – объект DataView ===
=== RAM-память – объект DataView ===
Строка 112: Строка 112:
Метод с использованием объекта DataView очень похож на метод с типизированным массивом выше, но он также позволяет получить доступ к исходным неформатированным данным во множестве разных форм.
Метод с использованием объекта DataView очень похож на метод с типизированным массивом выше, но он также позволяет получить доступ к исходным неформатированным данным во множестве разных форм.


К примеру, ниже мы сохраняем дату в 4 байтах, а температуру – в одном байте со знаком, упаковывая всё как можно компактнее:
К примеру, ниже мы сохраняем дату в ''4 байтах'', а температуру – в одном байте со знаком, упаковывая всё как можно компактнее:


<syntaxhighlight lang="javascript" enclose="div">
<syntaxhighlight lang="javascript" enclose="div">
Строка 138: Строка 138:
=== Flash-память ===
=== Flash-память ===


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


Кроме того, вы можете воспользоваться require("Flash") для записи байтов прямиком на flash-память – но это очень продвинутый метод, требующий работы со страницами (включая стирание страниц).
Кроме того, вы можете воспользоваться '''require("Flash")''' для записи байтов прямиком на [[flash-память]] – но это очень продвинутый метод, требующий работы со страницами (включая стирание страниц).


На Espruino 1v97 и новее есть [https://www.espruino.com/Reference#Storage модуль Storage], который реализует в flash-памяти простую файловую систему и, помимо прочего, используется для сохранения вашего программного кода.
На [[Espruino]] 1v97 и новее есть [https://www.espruino.com/Reference#Storage модуль Storage], который реализует в [[flash-памяти]] простую файловую систему и, помимо прочего, используется для сохранения вашего программного кода.


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


В коде ниже мы сохраняем данные в журнальный файл (и будем поочерёдно менять log1 от log2):
В коде ниже мы сохраняем данные в журнальный файл (и будем поочерёдно менять log1 от log2):
Строка 204: Строка 204:
=== Внешняя flash- или EEPROM-память ===
=== Внешняя flash- или EEPROM-память ===


Вы также можете подключить внешнюю память (flash или EEPROM) – как правило, через SPI или I2C.
Вы также можете подключить внешнюю память ([[flash]] или [[EEPROM]]) – как правило, через [[SPI]] или [[I2C]].


=== SD-карта ===
=== SD-карта ===
Строка 210: Строка 210:
Чтобы максимально увеличить место для хранения данных, можно воспользоваться SD-картой.
Чтобы максимально увеличить место для хранения данных, можно воспользоваться SD-картой.


У платы Espruino Original есть предустановленный слот для карты Micro SD. У других Espruino-плат такого слота нет, но подключить SD-карту к ним другими способами совсем не сложно.
У платы [[Espruino Original]] есть предустановленный слот для карты [[Micro SD]]. У других [[Espruino-плат]] такого слота нет, но подключить [[SD-карту]] к ним другими способами совсем не сложно.


Если вам нужна простая регистрация данных, то данные можно записывать в виде обычного текста (вроде CSV-файлов), который в дальнейшем можно будет прочесть на ПК:
Если вам нужна простая регистрация данных, то данные можно записывать в виде обычного текста (вроде [[CSV-файл]]ов), который в дальнейшем можно будет прочесть на [[ПК]]:


<syntaxhighlight lang="javascript" enclose="div">
<syntaxhighlight lang="javascript" enclose="div">
Строка 221: Строка 221:
</syntaxhighlight>
</syntaxhighlight>


Примечание: Как и на ПК, перед физическим извлечением SD-карты из Espruino её лучше сначала извлечь программно. Это делается при помощи функции E.unmountSD().
'''Примечание:''' Как и на [[ПК]], перед физическим извлечением [[SD-карты]] из [[Espruino]] её лучше сначала извлечь программно. Это делается при помощи функции E.unmountSD().


== Извлечение данных ==
== Извлечение данных ==
Строка 227: Строка 227:
Теперь, когда данные сохранены, их можно прочесть.
Теперь, когда данные сохранены, их можно прочесть.


Если вы используете SD-карту, то ничего сложного – просто выньте её из Espruino и вставьте в ПК.
Если вы используете [[SD-карту]], то ничего сложного – просто выньте её из [[Espruino]] и вставьте в [[ПК]].


При использовании других методов вывод данных лучше делать по USB-соединению (или через Bluetooth, если вы подключены к Puck.js). Вы не сможете загрузить всё в RAM-память за раз, поэтому вам нужно будет разбить этот процесс на несколько итераций.
При использовании других методов вывод данных лучше делать по USB-соединению (или через [[Bluetooth]], если вы подключены к [[Puck.js]]). Вы не сможете загрузить всё в [[RAM-память]] за раз, поэтому вам нужно будет разбить этот процесс на несколько итераций.


Должно сработать что-то вроде этого:
Должно сработать что-то вроде этого:
Строка 240: Строка 240:
</syntaxhighlight>
</syntaxhighlight>


Примечание: При использовании способа с типизированным массивом итерации лучше делать по направлению вперёд при помощи logIndex+1 – чтобы сохранить правильный порядок.
'''Примечание:''' При использовании способа с типизированным массивом итерации лучше делать по направлению вперёд при помощи '''logIndex+1''' – чтобы сохранить правильный порядок.


== Простой пример ==
== Простой пример ==
Строка 246: Строка 246:
Для этого примера мы воспользуемся функциями, которыми, во-первых, легко пользоваться, и которые, во-вторых, есть на всех платах – температурным датчиком и типизированными массивами.
Для этого примера мы воспользуемся функциями, которыми, во-первых, легко пользоваться, и которые, во-вторых, есть на всех платах – температурным датчиком и типизированными массивами.


Просто скопируйте и вставьте код ниже в правую часть IDE Espruino, включите в IDE настройку Set Current Time (чтобы найти её, кликните на кнопку с шестерёнкой справа вверху, а потом на Communications – пункт Set Current Time будет в самом низу) и кликните на кнопку загрузки.
Просто скопируйте и вставьте код ниже в правую часть [[IDE Espruino]], включите в IDE настройку '''Set Current Time''' (чтобы найти её, кликните на кнопку с шестерёнкой справа вверху, а потом на '''Communications''' – пункт '''Set Current Time''' будет в самом низу) и кликните на кнопку загрузки.


<syntaxhighlight lang="javascript" enclose="div">
<syntaxhighlight lang="javascript" enclose="div">
Строка 282: Строка 282:
</syntaxhighlight>
</syntaxhighlight>


Espruino сохранит первые данные спустя минуту после загрузки кода и продолжит сохранять их каждую последующую минуту. Поскольку длина объекта Float32Array составляет 100 элементов, в нём будет храниться только 100 последних записей. Вы можете без труда увеличить размер массива – большинство Espruino-плат способны обрабатывать не менее 5000 элементов, а многие и ещё больше.
Espruino сохранит первые данные спустя минуту после загрузки кода и продолжит сохранять их каждую последующую минуту. Поскольку длина объекта Float32Array составляет ''100 элементов'', в нём будет храниться только ''100 последних записей''. Вы можете без труда увеличить размер массива – большинство [[Espruino-плат]] способны обрабатывать ''не менее 5000 элементов'', а многие и ещё больше.


Чтобы прочесть данные, просто впишите getData() в левую часть IDE и нажмите на Enter, а затем скопируйте данные из терминала. Поскольку мы в качестве разделителя используем символ табуляции (\t), обычно вы можете вставлять скопированные данные напрямую в электронную таблицу вроде «Google Таблиц».  
Чтобы прочесть данные, просто впишите getData() в левую часть [[IDE]] и нажмите на {{клавиша|Enter}}, а затем скопируйте данные из терминала. Поскольку мы в качестве разделителя используем символ табуляции ('''\t'''), обычно вы можете вставлять скопированные данные напрямую в электронную таблицу вроде '''«Google Таблиц»'''.  


Вы также можете загружать данные прямо в файл – кликните на кнопку «Попробуй!» под кодом в разделе «Автоматическое восстановление данных».
Вы также можете загружать данные прямо в файл – кликните на кнопку '''«Попробуй!»''' под кодом в разделе '''«Автоматическое восстановление данных»'''.


== Как сделать ещё лучше ==
== Как сделать ещё лучше ==


Возможно, вам нужно сохранять больше одного элемента данных – в этом случае вы можете либо сохранять по несколько элементов в массиве log, либо сделать по отдельному массиву log для каждого типа данных (например, один для температуры, второй – для световых значений и т.д.).
Возможно, вам нужно сохранять больше одного элемента данных – в этом случае вы можете либо сохранять по несколько элементов в массиве '''log''', либо сделать по отдельному массиву '''log''' для каждого типа данных (например, один для температуры, второй – для световых значений и т.д.).


Кроме того, данные можно хранить более эффективно. Если вам нужны температурные данные с точностью только до градуса, Float32Array можно заменить на Int8Array – это позволит хранить в 4 раза больше данных (при условии, что диапазон температуры будет между -128 и 127 градусами Цельсия – именно такой диапазон вмещается в Int8Array).
Кроме того, данные можно хранить более эффективно. Если вам нужны температурные данные с точностью только до градуса, Float32Array можно заменить на Int8Array – это позволит хранить ''в 4 раза больше данных'' (при условии, что диапазон температуры будет ''между -128 и 127 градусами Цельсия'' – именно такой диапазон вмещается в Int8Array).


== Автоматическое восстановление данных ==
== Автоматическое восстановление данных ==


Если вы пытаетесь наладить передачу этих данных на ПК-приложение, вам нужно лишь открыть последовательное или Bluetooth-соединение, отправить строку "\x10getData()\n", а затем просто считывать присылаемые в ответ данные.
Если вы пытаетесь наладить передачу этих данных на ПК-приложение, вам нужно лишь открыть последовательное или Bluetooth-соединение, отправить строку '''"\x10getData()\n"''', а затем просто считывать присылаемые в ответ данные.


Для прямой коммуникации с веб-страницей можно воспользоваться библиотекой [https://www.espruino.com/Web+Bluetooth Puck.js] (Web Bluetooth) или [https://www.espruino.com/UART.js UART.js] (Web Serial). Например:
Для прямой коммуникации с веб-страницей можно воспользоваться библиотекой [https://www.espruino.com/Web+Bluetooth Puck.js] (Web Bluetooth) или [https://www.espruino.com/UART.js UART.js] (Web Serial). Например:
Кликните на кнопку «Попробуй!» под кодом ниже, чтобы опробовать его в действии и прочесть данные со своего Espruino-устройства.
Кликните на кнопку '''«Попробуй!»''' под кодом ниже, чтобы опробовать его в действии и прочесть данные со своего [[Espruino-устройства]].


Если вам нужен только Web Bluetooth, вы также можете также воспользоваться [https://www.espruino.com/Web+Bluetooth библиотекой Puck.js].
Если вам нужен только [[Web Bluetooth]], вы также можете также воспользоваться [https://www.espruino.com/Web+Bluetooth библиотекой Puck.js].


<syntaxhighlight lang="html5" enclose="div">
<syntaxhighlight lang="html5" enclose="div">
Строка 344: Строка 344:
</syntaxhighlight>
</syntaxhighlight>


В коде выше показан самый простейший случай – но в целях надёжности кода спустя 30 секунд загрузки у библиотек «uart.js» и «puck.js» сработает таймаут. Если у вас загрузка занимает больше времени, вам нужно будет вручную обработать каждую строчку данных по её приходу. Например:
В коде выше показан самый простейший случай – но в целях надёжности кода спустя 30 секунд загрузки у библиотек '''«uart.js»''' и '''«puck.js»''' сработает таймаут. Если у вас загрузка занимает больше времени, вам нужно будет вручную обработать каждую строчку данных по её приходу. Например:


<syntaxhighlight lang="javascript" enclose="div">
<syntaxhighlight lang="javascript" enclose="div">

Версия от 18:47, 4 марта 2021

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


Сбор данных при помощи Espruino[1]

Зачастую требуется собрать устройство, которое будет просто «сидеть» в одном месте и собирать (регистрировать) данные. Espruino-устройства особенно хорошо подходят для этой задачи, т.к. все они оснащены часами реального времени (RTC-часами) и в состоянии простоя расходуют очень мало энергии.

Примечание: Пример сбора данных с помощью Bangle.js можно найти тут.

На простейшем уровне ваш код должен выглядеть как-то так:

function getData() {
  var data = readMyData();
  storeMyData(data);
}

setInterval(getData, 60*1000); // каждую минуту

Считывание данных

Во-первых, считывать данные с Espruino-устройств можно при помощи функции E.getTemperature() – она считывает температуру с помощью температурного датчика, который встроен во все Espruino-устройства.

Кроме того, множеством различных датчиков оснащено устройство Puck.js – вы тоже можете считывать с них данные.

Но вы также часто будете использовать analogRead(pin) для считывания аналоговых данных с контактов. Кроме того, вам могут потребоваться модули Espruino для взаимодействия с внешними датчиками (например, с температурным датчиком DS18B20).

Время

При записи данных через фиксированные интервалы времени вам может не понадобиться сохранять время каждого измерения (за исключением, возможно, стартового времени). Это позволит сэкономить память и соответственно – сохранить в неё больше данных.

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

Прочесть время в удобочитаемом формате можно при помощи функции (new Date()).toString(), а помощью функции Date.now() можно прочесть количество миллисекунд, прошедших с 1970 года.

Считываемое время соответствует времени RTC-часов, которые встроены в Espruino. Настроить их можно при помощи setTime(secondsSince1970). Вы также можете поставить галочку в пункте Set Current Time в онлайн-IDE (чтобы найти его, кликните на кнопку с шестерёнкой справа вверху, а затем на Communications – пункт Set Current Time будет в самом низу) – это автоматически задаст текущее время при следующей загрузке кода. Если вы используете библиотеку Web Bluetooth для Puck.js, вы также можете воспользоваться функцией Puck.setTime() на вебсайте с Web Bluetooth.

Хранение данных

Далее надо определиться, как вы будете хранить свои данные. У вас есть несколько вариантов. Если вы хотите пропустить этот этап и просто получить какой-то рабочий вариант, ищите абзац с require("Storage").open в разделе «Flash-память» ниже.

RAM-память – JavaScript-переменные

Простейший (и наименее эффективный) вариант – это просто сохранить данные в JavaScript-массиве. Например:

var log = [];

function storeMyData(data) {
  log.push(data); // добавляем новый элемент в массив
}

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

Обойти это можно при помощи ограничения размера массива. Например:

function storeMyData(data) {
  // Задаем, чтобы в массиве 
  // гарантированно было не более 500 элементов:
  while (log.length >= 500) log.shift();
  // Добавляем новый элемент в массив:
  log.push(data);
}

Обычно каждое число, добавляемое в массив, занимает два слота переменных – один для самого числа, а второй для индекса массива (более подробно читайте в статье о производительности Espruino). При помощи process.memory().free можно узнать количество доступных слотов переменных, а следовательно – понять, насколько вам нужно ограничить свой массив (но вам также нужно будет оставить несколько слотов переменных для выполнения кода!).

RAM-память – типизированный массив

Хранение данных в JavaScript-переменных – это простой, но в то же очень требовательный к памяти метод (примерно 32 байта на один элемент). Но, имея определённые знания о том, что из себя представляют входные данные, мы можем хранить их в гораздо более компактной форме.

К примеру, если мы знаем, что каждый элемент считанных данных – это число, то можем использовать для их хранения фиксированный 32-битный буфер для чисел с плавающей точкой.

var log = new Float32Array(1000);
var logIndex = 0;

function storeMyData(data) {
  logIndex++;
  if (logIndex>=log.length) logIndex=0;
  log[logIndex] = data;
}

В этом случае для одного элемента будет использоваться только 4 байта, а не 32, как раньше.

Кроме того, для хранения очень точных чисел можно воспользоваться объектом Float64Array (8 байт), а для хранения целых чисел – объектами Uint8Array/Int8Array (1 байт), а также Uint16Array/Int16Array/Uint32Array/Int32Array.

В коде выше используется принцип кольцевого буфера, а не сдвиг элементов внутри самого массива (поскольку методов push() и shift() в типизированных массивах вроде Float32Array нет). Это значит, что при выводе данных с переменной logIndex нужно будет работать задом наперёд, чтобы данные выводились в правильном порядке.

Но если вам нужен сдвиг данных внутри массива, то сделать это можно, применив к массиву метод set():

function storeMyData(data) {
  // Сдвигаем элементы в обратном порядке.
  // Обратите внимание на цифру «4»:
  // мы используем её, потому что Float32 – это 4 байта.
  log.set(new Float32Array(log.buffer, 4 /*bytes*/));
  // Добавляем финальный элемент.
  log[log.length-1] = data;
}

Это медленнее, чем при использовании logIndex выше, но так проще выводить данные и делать графики на их основе.

RAM-память – объект DataView

Метод с использованием объекта DataView очень похож на метод с типизированным массивом выше, но он также позволяет получить доступ к исходным неформатированным данным во множестве разных форм.

К примеру, ниже мы сохраняем дату в 4 байтах, а температуру – в одном байте со знаком, упаковывая всё как можно компактнее:

const EVENT_SIZE = 5;
/* Каждое событие будет состоять из:
 Байты 0-3: секунды, прошедшие с 1970 года (хватит до 2106 года)
 Байт 4: температура в градусах Цельсия
*/
var log = new DataView(new ArrayBuffer(EVENT_SIZE*1000));
...

// Для записи:
var o = indexToWrite*EVENT_SIZE;
log.setUint32(o+0,Date.now()/1000);
log.setInt8(o+4,E.getTemperature());

// Для чтения:
var o = indexToRead*EVENT_SIZE;
var event = {
  time : new Date(log.getUint32(o+0)*1000),
  temp : log.getInt8(o+4)
};

Flash-память

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

Кроме того, вы можете воспользоваться require("Flash") для записи байтов прямиком на flash-память – но это очень продвинутый метод, требующий работы со страницами (включая стирание страниц).

На Espruino 1v97 и новее есть модуль Storage, который реализует в flash-памяти простую файловую систему и, помимо прочего, используется для сохранения вашего программного кода.

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

В коде ниже мы сохраняем данные в журнальный файл (и будем поочерёдно менять log1 от log2):

var storage = require("Storage");
var FILESIZE = 2048;
var file = {
  name : "",
  offset : FILESIZE, // сначала принудительно
                     // генерируемый новый файл
};

// Добавляем новые данные в журнальный файл
// или в переключаемые журнальные файлы:
function saveData(txt) {
  var l = txt.length;
  if (file.offset+l>FILESIZE) {
    // Нужен новый файл...
    file.name = file.name=="log2"?"log1":"log2";
    // Записываем данные в файл – 
    // это перезапишет данные, которые были записаны туда ранее:
    storage.write(file.name,txt,0,FILESIZE);
    file.offset = l;
  } else {
    // Просто добавляем данные:
    storage.write(file.name,txt,file.offset);
    file.offset += l;
  }
}

// Для записи данных:
setInterval(function() {
  saveData(getTime()+","+E.getTemperature()+"\n");
}, 1000);


// Для чтения данных:
// storage.read("log1");
На Espruino 2v05 и новее также можно использовать функцию require("Storage").open, которая позволяет открывать Storage-файл для добавления в него данных и значительно упрощает этот процесс. Рекомендуем использовать именно его:
var f = require("Storage").open("log","a");

// Записываем данные:
setInterval(function() {
  f.write(getTime()+","+E.getTemperature()+"\n");
}, 1000);

function getData(callback) {
  var f = require("Storage").open("log","r")  
  var l = f.readLine();
  while (l!==undefined) {
    callback(l);
    l = f.readLine();
  }
}
// Считываем данные при помощи: getData(print);

Внешняя flash- или EEPROM-память

Вы также можете подключить внешнюю память (flash или EEPROM) – как правило, через SPI или I2C.

SD-карта

Чтобы максимально увеличить место для хранения данных, можно воспользоваться SD-картой.

У платы Espruino Original есть предустановленный слот для карты Micro SD. У других Espruino-плат такого слота нет, но подключить SD-карту к ним другими способами совсем не сложно.

Если вам нужна простая регистрация данных, то данные можно записывать в виде обычного текста (вроде CSV-файлов), который в дальнейшем можно будет прочесть на ПК:

function storeMyData(data) {
  var csvline = (new Date()).toString() + "," + data + "\n";
  require("fs").appendFileSync("mydata.csv", csvline);
}

Примечание: Как и на ПК, перед физическим извлечением SD-карты из Espruino её лучше сначала извлечь программно. Это делается при помощи функции E.unmountSD().

Извлечение данных

Теперь, когда данные сохранены, их можно прочесть.

Если вы используете SD-карту, то ничего сложного – просто выньте её из Espruino и вставьте в ПК.

При использовании других методов вывод данных лучше делать по USB-соединению (или через Bluetooth, если вы подключены к Puck.js). Вы не сможете загрузить всё в RAM-память за раз, поэтому вам нужно будет разбить этот процесс на несколько итераций.

Должно сработать что-то вроде этого:

function getData() {
  for (var i=0;i<log.length;i++)
    console.log(i+","+log[i]);
}

Примечание: При использовании способа с типизированным массивом итерации лучше делать по направлению вперёд при помощи logIndex+1 – чтобы сохранить правильный порядок.

Простой пример

Для этого примера мы воспользуемся функциями, которыми, во-первых, легко пользоваться, и которые, во-вторых, есть на всех платах – температурным датчиком и типизированными массивами.

Просто скопируйте и вставьте код ниже в правую часть IDE Espruino, включите в IDE настройку Set Current Time (чтобы найти её, кликните на кнопку с шестерёнкой справа вверху, а потом на Communications – пункт Set Current Time будет в самом низу) и кликните на кнопку загрузки.

var log = new Float32Array(100); // наши сохранённые данные
var logIndex = 0; // индекс последнего сохранённого
                  // элемента данных
var timePeriod = 60*1000; // каждую минуту
var lastReadingTime; // время последнего считывания

// Сохраняем данные в RAM-память:
function storeMyData(data) {
  logIndex++;
  if (logIndex>=log.length) logIndex=0;
  log[logIndex] = data;
}

// Считываем данные и сохраняем их в RAM-память:
function getData() {
  var data = E.getTemperature();
  storeMyData(data);
  lastReadingTime = Date.now();
}

// Выгружаем наши данные в удобочитаемом формате:
function getData() {
  for (var i=1;i<=log.length;i++) {
    var time = new Date(lastReadingTime - (log.length-i)*timePeriod);
    var data = log[(i+logIndex)%log.length];
    console.log(time.toString()+"\t"+data);
  }
}

// Начинаем запись:
setInterval(getData, timePeriod);

Espruino сохранит первые данные спустя минуту после загрузки кода и продолжит сохранять их каждую последующую минуту. Поскольку длина объекта Float32Array составляет 100 элементов, в нём будет храниться только 100 последних записей. Вы можете без труда увеличить размер массива – большинство Espruino-плат способны обрабатывать не менее 5000 элементов, а многие и ещё больше.

Чтобы прочесть данные, просто впишите getData() в левую часть IDE и нажмите на  ↵ Enter , а затем скопируйте данные из терминала. Поскольку мы в качестве разделителя используем символ табуляции (\t), обычно вы можете вставлять скопированные данные напрямую в электронную таблицу вроде «Google Таблиц».

Вы также можете загружать данные прямо в файл – кликните на кнопку «Попробуй!» под кодом в разделе «Автоматическое восстановление данных».

Как сделать ещё лучше

Возможно, вам нужно сохранять больше одного элемента данных – в этом случае вы можете либо сохранять по несколько элементов в массиве log, либо сделать по отдельному массиву log для каждого типа данных (например, один для температуры, второй – для световых значений и т.д.).

Кроме того, данные можно хранить более эффективно. Если вам нужны температурные данные с точностью только до градуса, Float32Array можно заменить на Int8Array – это позволит хранить в 4 раза больше данных (при условии, что диапазон температуры будет между -128 и 127 градусами Цельсия – именно такой диапазон вмещается в Int8Array).

Автоматическое восстановление данных

Если вы пытаетесь наладить передачу этих данных на ПК-приложение, вам нужно лишь открыть последовательное или Bluetooth-соединение, отправить строку "\x10getData()\n", а затем просто считывать присылаемые в ответ данные.

Для прямой коммуникации с веб-страницей можно воспользоваться библиотекой Puck.js (Web Bluetooth) или UART.js (Web Serial). Например: Кликните на кнопку «Попробуй!» под кодом ниже, чтобы опробовать его в действии и прочесть данные со своего Espruino-устройства.

Если вам нужен только Web Bluetooth, вы также можете также воспользоваться библиотекой Puck.js.

<html>
 <head>
 </head>
 <body>
  <script src="https://www.espruino.com/js/uart.js"></script>
  <script>
// Выводим в консоль отладочную информацию о присланных данных:
UART.debug=3;

// Сохраняем CSV-файл на диск:
function saveFile(csvText, fileName) {
  var saver = document.createElement("a");
  var blob = new Blob([csvText], {type : 'text/csv'});
  var blobURL = saver.href = URL.createObjectURL(blob),
      body = document.body;
  saver.download = fileName;
  body.appendChild(saver);
  saver.dispatchEvent(new MouseEvent("click"));
  body.removeChild(saver);
  URL.revokeObjectURL(blobURL);
}

// Считываем данные с Espruino:
function getData() {
  UART.write('\x03\x10getData()\n', function(data) {
    console.log("Received",JSON.stringify(data));
    // Если getData() использует console.log, а не Bluetooth.println,
    // символ приглашения ввода текста будет напечатан
    // в конце выводимых данных.
    // Здесь мы находим его и удаляем:
    if (data.endsWith(">")) data = data.slice(0,-1);
    saveFile(data,"info.csv");
  });
}
  </script>
  <button onclick="getData()">Download Data</button>
 </body>
</html>

В коде выше показан самый простейший случай – но в целях надёжности кода спустя 30 секунд загрузки у библиотек «uart.js» и «puck.js» сработает таймаут. Если у вас загрузка занимает больше времени, вам нужно будет вручную обработать каждую строчку данных по её приходу. Например:

function onLine(data) {
  // Тут выполняется получение CSV-данных
}

var connection;
button.addEventListener("click", function() {
  if (connection) {
    connection.close();
    connection = undefined;
  }
  Puck.connect(function(c) {
    if (!c) {
      alert("Не удалось подключиться!");
      return;
    }
    connection = c;
    // Обрабатываем полученные в ответ данные
    // и вызываем 'onLine' при каждом получении новой строчки:
    var buf = "";
    connection.on("data", function(d) {
      buf += d;
      var i = buf.indexOf("\n");
      while (i>=0) {
        onLine(buf.substr(0,i));
        buf = buf.substr(i+1);
        i = buf.indexOf("\n");
      }
    });
    // Запрашиваем данные у Puck.js:
    connection.write("\x10getData()\n");
  });
});

См.также

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