Последние два года я разрабатывал собственную IoT платформу и сегодня готов показать ее альфа версию.


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


Статья будет интересна людям, которым близка тема IoT и они уже что-то делали в этой сфере. Важным замечанием будет то, что платформа должна заинтересовать (внезапно) JavaScript разработчиков, т.к. именно этот язык выбран как основа платформы. Конечно, и С/С++ разработчикам тоже будет что почитать.


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



Проблемы IoT:


— Проблема коротких рук


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


Даже если отдельный специалист обладает нужными навыками, чтобы это все сделать, то ему просто не хватит времени (рук) на реализацию такой идеи. Пока он будет ее пилить, она морально устареет.


— Проблема Вавилонской башни


Разработка полноценной IoT экосистемы требует очень широкий технологический стек. Быть фулстеком в IoT это прям… сложно. Нужен опыт везде. Похвастаться таким широким спектром знаний, да еще и опытом, могут далеко не все. И тут вопрос не в умственных способностях. Это очевидный вывод из проблемы “коротких рук”.


Для создания действительно состоятельной экосистемы требуется труд многих достаточно узких специалистов, но с глубокими знаниями в свой области. Говорят эти специалисты на разных языках, используют разные паттерны, да и часто элементарные термины они понимают по-разному. А учитывая, что IoT базируется на устройствах с ограниченными ресурсами, эффективные коммуникации критически важны для реализации задуманного.


— Проблема Стокгольмского синдрома


Сегодня есть вендоры, которые развивают свои экосистемы. Это Google, Microsoft, Yandex, Мегафон, МТС и т.д. Некоторые из них позволяют интегрировать собственные вещи в их экосистемы на их условиях. Это во многом закрывает выше описанные проблемы. Но создает новую — зависимость. А условия интеграции вендоры очень любят менять. И уж, тем более о самореализации в этой парадигме речи не идет.


Решения проблем:


— Сообщество, зависимости, модно, молодежно


Выше описанные проблемы, фактически, закрывают доступ к разработке IoT для индивидуалов. Разработка платформы была начата с осознанием этих проблем. В основу было заложено развитие платформы через сообщество.


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


Если вы не знаете, что такое зависимости, самое время с этим познакомиться. Но если попытаться очень просто объяснить, то разрабатываемый вами модуль может зависеть от другого, который напишет ваш друг. И вы будете обращаться к его модулю по заранее оговоренному интерфейсу.


Таким образом, одновременно, независимо, множество людей могут разрабатывать собственные компоненты платформы и переиспользовать уже имеющиеся, разработанные кем-то. Это кардинально решает проблему “коротких рук”.



Также, решается проблема “Вавилонской башни”. Зависимости построены так, что различные уровни платформы, разрабатываемые на разных языках, имеют заранее определенный механизм выстраивания зависимостей между собой.


Например, разработчик на С может воспользоваться готовым компонентом фронтэнда, предоставив ему требуемый интерфейс. Или напротив, разработчик фронтэнда может использовать готовый компонент написанный на С. Т.е. каждый будет делать то, что он умеет лучше всего.


— Больше обещаний и абстракций


Протокол общения между устройствами неопределен. Вместо него есть абстракция — шина данных. Устройство может послать в шину событие или прослушать шину. Кто в шину пишет и кто получает, заранее неясно. И когда тоже. Обмен данными асинхронный и доставка не гарантируется. В общем — ад. Без паники. Так задумано.


Все дело в том, что экосистема это группа отдельных, самодостаточных устройств. В любой момент времени часть устройств может быть недоступна. По различным причинам. Останавливать активность прочих устройств, если недоступна часть — не лучший сценарий. Нужно узаконить то, что невозможно предотвратить.


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


Проблема синхронной связи решается передачей через шину событий со ссылками на синхронные каналы. Протокол синхронного канала определяется самим типом события. К примеру, можно отправить событие с типом “do-render-video-stream” и как payload передать IP WEB-камеры. Таким образом, получатель будет знать, что нужно воспроизвести видеопоток с указанного адреса.



Но как физически работает шина? Реализация шины возлагается на сообщество. Шина расширяется тем транспортом, который требует ваш проект. Например, событие получается по http и ретранслируется по UART. Для всех элементов экосистемы внешне ничего не изменится.


— Виртуальные IoT устройства


Для ThingJS вещью является не только физическая вещь, но и специальное приложение — виртуальная вещь. Более того, физическая вещь может содержать в себе несколько виртуальных вещей (приложений), которые используют ресурсы физической вещи.


Такой подход позволяет унифицировать взаимодействие между условным backend (контроллер/сервер/облако и т.п.) и frontend (браузер, приложение и т.п.), а также b2b и даже f2f. Построить матрицу, а не иерархию взаимодействия.



Простым примером может быть WEB-камера, которая в себе имеет виртуальную вещь — интерфейс пользователя. Когда пользователь заходит по адресу http://192.168.4.1 открывается WEB страница, где начинает “жить” виртуальная вещь. Камера (физическая вещь) и страница (виртуальная вещь) автоматически становятся экосистемой, где доступна унифицированная шина данных. Через нее виртуальная вещь общается с физической. В этом случае: физическая вещь сообщает виртуальной вещи через шину адрес видеопотока, свое состояние и т.п., а виртуальная демонстрирует пользователю видео и отдает необходимые команды физической вещи.


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


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


Техническая информация


Структура приложения ThingJS



Стек технологий


Аппаратной платформой выбран контроллер ESP32. Платформа проектировалась аппаратно независимой. Но, к сожалению, партировать на иные устройства не было времени.


Для разработки прошивки используется рекомендованные Espressif средства. Прошивка разработана на языке С. Сборщик cmake. Проект использует концепцию компонентов, также продвигаемую Espressif.


Помимо, esp-idf, используются Mongoose WEB Server, а также доработанный JavaScript интерпритатор Mongoose mJS.


Для разработки приложений используется JavaScript с фреймворком VUE 2. Сборка приложений осуществляется посредством webpack. Менеджер пакетов — npm. Как основа для среды разработки использовался VUE CLI.


Для того чтобы стандартизировать визуализацию приложений и облегчить муки UI творчества, в платформу включен пакет vuetifyjs.


Возможности среды разработки


Для JavaScript разработчиков (виртуальные вещи):


  • Рекомендованная IDE — WEBStorm;
  • Все профиты, которые дает VUE CLI и IDE;
  • Внутрисистемная отладка приложений (дебаггер mJS на контроллере);
  • В mJS реализована команда debugger, позволяющая вызывать отладчик в произвольном месте;
  • Горячая загрузка обновленных файлов на контроллер при разработке (JavaScript девелоперы уже без этой фичи не живут);
  • Runtime разработка в паре с реальным контроллером. Программируешь и, тут же, видишь результат на железе;
  • Настроенный ESLint для понимания объектов платформы.

Для С разработчиков (физические вещи):


  • Рекомендованная IDE — CLion;
  • Все профиты esp-idf и IDE;
  • Платформа разделена на компоненты в рамках концепции esp-idf;
  • Легкая интеграция с платформой собственных компонентов.

Поддерживаемые устройства


В настоящий момент поддерживается только ESP32. Чип популярен, ввиду своей доступности при поразительных технических характеристиках. На его базе создано достаточно много готовых IoT устройств, которые можно использовать под управлением ThingJS.


Сравнение с конкурентами


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


Быстрый старт


Мне только посмотреть



Хочу попробовать


Для того чтобы попробовать платформу на реальном железе, вам понадобится любое устройство на базе ESP32 c flash 4mb и возможностью его прошивать через USB. Но лучше всего подойдет ESP32 core board v2.



Купить такие штуки можно без проблем на Aliexpress или Ebay. Более того, есть даже представительства в России. Я лично покупаю в Питере.


Для того чтобы проверить работу тестового приложения “Blink”, потребуется подключить светодиод. На некоторых версиях плат есть предустановленный светодиод, подключенный к GPIO2. Если у вас такая плата, то можно ничего не делать. Blink должен работать без лишних движений. Если у вас только один диод (питание), то придется подключить индикаторный диод самому. В этом нет ничего сложного.


Вам понадобится любой индикационный светодиод и сопротивление от 1 до 5КОм.



Осталось дело за малым — развернуть пользовательский пакет на устройстве. Взять его можно тут. Инструкция по развертыванию находится там же.




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


Сценарий прост. При установке приложения на физическом устройстве начинает мигать светодиод (заранее к нему подключенный) с частотой 1Гц. Пользователь может включать или выключать мигание диода из интерфейса. Видео можно посмотреть в разделе “Мне только посмотреть”.


Исходники лежат в репозитории src/applications/blink. Для того чтобы собрать blink и с ним “поиграться”, вам понадобится только этот репозиторий. Убедитесь, что git, npm и nodejs у вас уже установлены.


git clone --branch alpha https://github.com/rpiontik/ThingJS-front
cd ThingJS-front
npm install
npm run build

Если все прошло гладко, то в результате вы получите примерно следующее:



Поздравляю! Вы собрали свое первое приложение для ThingJS. Найти его можно в папке dist/apps/blink и сразу же попробовать поставить на устройство, руководствуясь видео из раздела "Мне только посмотреть".



Файл Описание
scripts/blink.js Скрипт, который устанавливается на контроллер
blink.js Точка монтирования компонента приложения
Blink.vue VUE компонент, реализующий интерфейс пользователя
favicon.svg Иконка приложения
langs.js Языковой пакет приложения
manifest.json Манифест приложения

Со всеми деталями приложения вы можете познакомиться самостоятельно. Я заострю внимание на нескольких файлах.



{
 "name": "Blink",
 "vendor" : "rpiontik",
 "version" : 1,
 "subversion" : 0,
 "patch" : 0,
 "description": {
   "ru": "Мигание диодом",
   "en": "Blink Example"
 },
 "components": {...},
 "scripts": {...},
 "requires" : {...}
}

Как следует из названия файла, это манифест приложения. В нем есть метаданные общего характера о назначении которых легко догадаться. Помимо них, есть три важных блока. Давайте рассмотрим их пристально:



"components": {
   "blink-app": {
     "source": "blink.js",
     "intent_filter": [
       {
         "action": "thingjs.intent.action.MAIN",
         "category": "thingjs.intent.category.LAUNCH"
       }
     ]
   }
 }

Блок описывает всю компонентную базу приложения. Поле “source” указывает на точку монтирования компонента (см. blink.js) и является точкой входа сборки для webpack (entry). Таким образом, каждый компонент будет оформлен в отдельный бандл. Загрузка этого бандла будет выполняться по мере необходимости (lazy load).


Важной структурой является “intent_filter”. Если вам случалось программировать для Android, вы найдете нечто знакомое для себя. И не ошибетесь. В системе возникают интерфейсные и сервисные события на которые подписывается компонент. Если возникает событие, удовлетворяющее условиям фильтрации, то компонент будет загружен и управление будет передано точке монтирования.


В данном случае, компонент “blink-app” подписан на событие запуска основного интерфейсного компонента приложения. Когда лаунчер будет запускать приложение, именно этот компонент будет представлен.


Если модифицировать манифест, изменив строчку


thingjs.intent.category.LAUNCH >> thingjs.intent.category.PREFERENCE


, то после его сборки и установки окажется, что приложение перестало открываться на рабочем столе. Но появилась новая “плитка” в разделе “Settings”. При этом функционально ничего не поменялось.


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



"scripts": {
 "entry": "blink",
 "subscriptions" : ["$-script-restart", "blink"],
 "modules": {
   "blink": {
     "hot_reload": true,
     "source": "scripts/blink.js",
     "optimize": false
   }
 }
}

Этот блок аналогичен по функции блоку “components”, но описывает компонентную базу приложения на стороне контроллера.


В нем явно указывается точка входа. В поле “entry”. Отдельно обращу внимание, что при установке приложения скрипт не запускается сразу. Он запускается только тогда, когда наступает одно из событий, на которые скрипт подписан.


За подписки отвечает поле “subscriptions”. Сейчас в нем указаны два события:


  • $-script-restart — возникает в случае запуска или перезапуска системы;
  • blink — кастомное событие, которое релевантно для экосистемы blink.

В блоке “modules” следует описание состава скриптов. Отмечу два поля:


  • hot_reload — если это поле установлено в true, то при изменении файла в режиме разработки он автоматически будет загружаться на контроллер (hot reload);
  • optimize — если true, то при сборке проекта скрипт будет оптимизироваться и аглифицироваться.


"requires" : {
 "interfaces" : {
   "blink" : {
     "type" : "bit_port",
     "required" : true,
     "default" : 2,
     "description" : {
       "ru" : "LED индикатор",
       "en" : "LED indicator"
     }
   }
 }
}

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


В этом блоке указываются зависимости. В данном случае, чтобы приложение могло функционировать, ему требуется предоставить интерфейс с типом “bit_port”. Этот интерфейс является обязательным требованием (required = true) и по умолчанию, указывается GPIO2 (default = 2). В скрипт он будет спроецирован с именем “blink”.


При установке приложения учитывается профиль оборудования, на которое будут разворачиваться скрипты. В этом профиле перечисляются доступные интерфейсы и доступные для них аппаратные ресурсы (в частности, пины и их комбинации). Проверяется совместимость требований и оборудования. Если оборудование может удовлетворить требованиям приложения, пользователю отображается схема распределения ресурсов, где первично ресурсы распределены автоматически с учетом рекомендаций из манифеста. Т.е. из того самого поля “default”.


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



import App from './Blink.vue';
import Langs from './langs';

$includeLang(Langs);
$exportComponent('blink-app', App);

Файл является точкой монтирования компонента, анонсированного в манифесте (см. manifest.js/components) В нем выполняется регистрация VUE компонента ‘blink-app’ через метод абстракции $exportComponent, а также регистрируется языковой пакет.


Возможно, вы спросите — зачем такие сложности? Почему не регистрировать сразу VUE компонент, который указывать в source? Дело в том, что манифест описывает публичные компоненты. Эти компоненты могут запрашиваться сторонними приложениями (runtime зависимости). Точка монтирования, в свою очередь, может зарегистрировать сопутствующие компоненты (для внутреннего пользования), а также сервисы. Т.е., подготовить окружение компонента.



export default {
 name: 'Blink',
 watch: {
   blink_state (state) {
     // Send event to script
     this.$bus.$emit($consts.EVENTS.UBUS_MESSAGE, 'blink', state);
   }
 },
 data () {
   return {
     blink_state: true
   };
 }
};

Код говорит сам за себя. При изменении свойства “blink_state” отправляется сообщение в шину ($bus) с текущим значением. Это все, что нужно сделать, чтобы скрипт на стороне контроллера получил нужную команду.



let active = true;
let state = true;

// Set port direction
$res.blink.direction($res.blink.DIR_MODE_DEF_OUTPUT);

// Run background process
setInterval(function () {
 if (active) {
   // $res - is container with required resources
   $res.blink.set(state);
   // Do invert
   state = !state;
 }
}, 1000);

// Event listener
// $bus - system bus interface
$bus.on(function (event, content, data) {
 if (event === 'blink') {
   active = !!JSON.parse(content);
 }
}, null);

В целом, код очень похож на классическое использование таймера в JavaScript. За исключением того, что в данном диалекте JavaScript его нет. Он реализован в платформе. Знакомьтесь, это mJS. Более подробно о нем можно узнать на официальной странице проекта.


Для нужд платформы диалект доработан. Завезены таймеры, а также такая полезная команда как “debugger”. Ну, и сам отладчик. Подробнее об этом отдельно в разделе “Среда разработки”.


Обратите внимание на глобальные объекты платформы. Они именуются с символа “$”.


  • $res — содержит ресурсы, которые выделены скрипту;
  • $bus — интерфейс шины.

Т.к. приложение запросило интерфейс с типом “bit_port” (см. profile.json/requires) и именем “blink”, ему он был выдан как $res.blink. Интерфейс реализует всего три функции:


  • set(value) — установить уровень GPIO
  • get() — получить текущий уровень GPIO
  • direction(value) — установить режим GPIO

Для функции direction описаны доступные константы через тот же интерфейс $res.blink.: DIR_MODE_DISABLE; DIR_MODE_DEF_INPUT; DIR_MODE_DEF_OUTPUT; DIR_MODE_INPUT_OUTPUT_OD; DIR_MODE_INPUT_OUTPUT.


Подписка на события шины осуществляется через метод $bus.on. При этом в обработчик будут приходить все события, на которые скрипт подписан. Обработчик принимает три параметра:


  • event — идентификатор события. В этом случае возможны всего два: “$-script-restart” и “blink”. Из которых обрабатывается всего одно — blink. Подписка на второе нужна лишь для того, чтобы скрипт запускался сразу при старте системы.
  • content — с событием могут приходить данные. Их размер ограничен 126 байтами с учетом длины идентификатора события.
  • data — данные, которые передаются при подписке на событие как второй параметр. И в данном случае, равны null.

Интерфейсы расширяемы. Ниже вы найдете описание, как создать собственный интерфейс.


Реализация интерфейса


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


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


Рассмотрим реализацию интерфейса “bit_port”, который используется в примере “Blink”. Для того чтобы начать, потребуется развернуть проект ThingJS-template alpha релиз. Документация по развертыванию находится в самом проекте.


git clone --branch alpha https://github.com/rpiontik/ThingJS-template

В проект входят компоненты:


  • ThingJS-boards — содержит в себе конфигурации устройств. Пока только ESP32_CORE_BOARD V2 и совместимые;
  • ThingJS-extern — библиотеки сторонних проектов, которые использует ThingJS;
  • ThingJS-core — ядро платформы;
  • ThingJS-front — среда разработки приложений;
  • ThingJS-stdi — стандартные интерфейсы.

Нас интересует проект ThingJS-stdi. Его структура следующая:


Файл Описание
implementation/tgsi_bit_port.c Реализация интерфейса bit_port
implementation/tgsi_bit_port.h Заголовочный файл интерфейса bit_pro
CMakeLists.txt cmake скрипт сборки
README.md
sdti_utils.h Хелперы
thingjs_stdi.c Точка монтирования интерфейсов
thingjs_stdi.h Заголовочный файл точки монтирования

По сути, нас интересует только один файл — implementation/tgsi_bit_port.c. Именно в нем содержится все, что требует отдельного пояснения.


void thingjsBitPortRegister(void) {
   static int thingjs_bit_port_cases[] = DEF_CASES(
           DEF_CASE(GPIO0),  DEF_CASE(GPIO2), DEF_CASE(GPIO3), DEF_CASE(GPIO4),
           DEF_CASE(GPIO5),  DEF_CASE(GPIO12), DEF_CASE(GPIO13), DEF_CASE(GPIO14),
           DEF_CASE(GPIO15), DEF_CASE(GPIO16), DEF_CASE(GPIO17), DEF_CASE(GPIO18),
           DEF_CASE(GPIO19), DEF_CASE(GPIO21), DEF_CASE(GPIO22), DEF_CASE(GPIO23),
           DEF_CASE(GPIO25), DEF_CASE(GPIO26), DEF_CASE(GPIO27), DEF_CASE(GPIO32),
           DEF_CASE(GPIO33)
   );

   static const struct st_thingjs_interface_manifest interface = {
           .type           = "bit_port",
           .constructor    = thingjsBitPortConstructor,
           .cases          = thingjs_bit_port_cases
   };

   thingjsRegisterInterface(&interface);
}

Функция thingjsBitPortRegister регистрирует компонент в ядре ThingJS. Для этого она вызывает функцию thingjsRegisterInterface, которой передает структуру с описанием интерфейса.


  • type — идентификатор интерфейса. Именно он указывается как тип в файле manifest.json приложения;
  • constructor — ссылка на конструктор интерфейса. Функция вызывается каждый раз, когда нужно создать новый экземпляр интерфейса;
  • cases — массив, описывающий возможные аппаратные ресурсы, которые может использовать интерфейс для своей работы. В данном случае, это одиночные GPIO. Но могут быть отдельно описаны их комбинации или зависимости.

Конструктор интерфейса монтирует интерфейс в машину mJS.


mjs_val_t thingjsBitPortConstructor(struct mjs *mjs, cJSON *params) {
   //Validate preset params
   //The params must have pin number
   if (!cJSON_IsNumber(params))
       return MJS_UNDEFINED;
   //Get pin number
   gpio_num_t gpio = params->valueint;
   //Create mjs object
   mjs_val_t interface = mjs_mk_object(mjs);
   /* Configure the IOMUX register for pad BLINK_GPIO (some pads are
      muxed to GPIO on reset already, but some default to other
      functions and need to be switched to GPIO. Consult the
      Technical Reference for a list of pads and their default
      functions.)
   */
   gpio_pad_select_gpio(gpio);
   //Add protected property to interface
   mjs_set(mjs, interface, "gpio", ~0, mjs_mk_number(mjs, gpio));
   //Set protected flag
   mjs_set_protected(mjs, interface, "gpio", ~0, true);
   //Bind functions
   mjs_set(mjs, interface, "set", ~0,
           mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) thingjsBitPortSet));
   mjs_set(mjs, interface, "get", ~0,
           mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) thingjsBitPortGet));
   mjs_set(mjs, interface, "direction", ~0,
           mjs_mk_foreign_func(mjs, (mjs_func_ptr_t) thingjsBitPortDirection));
   //Consts
   mjs_set(mjs, interface, "DIR_MODE_DISABLE", ~0, mjs_mk_number(mjs, GPIO_MODE_DISABLE));
   mjs_set(mjs, interface, "DIR_MODE_DEF_INPUT", ~0, mjs_mk_number(mjs, GPIO_MODE_DEF_INPUT));
   mjs_set(mjs, interface, "DIR_MODE_DEF_OUTPUT", ~0, mjs_mk_number(mjs, GPIO_MODE_DEF_OUTPUT));
   mjs_set(mjs, interface, "DIR_MODE_INPUT_OUTPUT_OD", ~0, mjs_mk_number(mjs, GPIO_MODE_INPUT_OUTPUT_OD));
   mjs_set(mjs, interface, "DIR_MODE_INPUT_OUTPUT", ~0, mjs_mk_number(mjs, GPIO_MODE_INPUT_OUTPUT));
   //Return mJS interface object
   return interface;
}

Как параметры передаются:


  • mjs — глобальный контекст выполнения;
  • params — параметры инициализации интерфейса. В данном случае это номер GPIO.

Создается объект mJS “interface”, куда монтируются методы и свойства интерфейса:


  • gpio — readonly свойство, в котором хранится номер используемого GPIO;
  • set — метод установки уровня сигнала;
  • get — метод получения текущего уровня сигнала;
  • direction — установка режима GPIO;

Также, монтируются константы, которыми смогут оперировать скрипты (DIR_MODE_DISABLE, DIR_MODE_DEF_INPUT и т.д.).


После создания интерфейса, он монтируется под определенным идентификатором (в примере Blink это “blink”) в глобальный объект $res. Пример использования можно посмотреть в разделе Blink (scripts/blink.js).


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


Среда разработки


Разработка приложений


Среда разработки приложений основывается на VUE CLI, который был доработан для соответствия потребностям платформы ThingJS. Это жесткий форк, т.ч. новых фич из VUE CLI стоит ждать в том случае, если они прям сильно облегчают жизнь.


Для развертывания среды необходимо клонировать проект ThingJS-front alpha-релиз. Убедитесь, что git, npm и nodejs у вас уже установлены.


git clone --branch alpha https://github.com/rpiontik/ThingJS-front
cd ThingJS-front
npm install

При разработке рекомендую использовать IDE WEBStorm.


Состав и структура проекта наследует VUE CLI. Я отражу значимые отличия:


  1. Переработаны скрипты сборки в папке build.
  2. В конфиг dev среды (config/dev.env.js) добавлена переменная окружения “HW_DEVICE_URL”. В ней необходимо указывать ссылку на физическое устройство с которым вы будете работать.
  3. Появилась системная папка src/applications. В ней содержатся приложения, которые будут собираться автоматически. В частности, в ней находятся два приложения: ante (лаунчер) и blink (приложение).
  4. Все, что выше папки src/applications считается платформенными модулями и ресурсами. Вы, конечно, можете вносить изменения в них, но в этом случае, они появятся в контроллере только после его перепрошивки. Т.ч. если специально вы себе цели не ставили, лучше их не трогать.

Для пробы, можно тут же запустить dev-сервер. Хотя полноценно вы не сможете проводить разработку без физической “железки”, разрабатывать интерфейс это не мешает. И так, запускам dev-сервер:


npm run dev

Результат должен быть примерно таким:



Открыв браузер и введя в адресную строку http://0.0.0.0:8080 вы увидите платформу в режиме разработки:



Сам процесс разработки интерфейса мало чем отличается от классической разработки фронта на VUE. За исключением того, что есть глобальные объекты платформы, о которых нужно знать:


  • $const — содержит в себе платформенные константы, а также языковые пакеты;
  • $bus — Шина данных;
  • $store — глобальное хранилище (VUEX).
    Из примеров вы сможете понять как их использовать.

Мультиязычность реализована простейшим образом — через фильтр “lang”. Указываете языковую константу, она интерпретируется в текст в зависимости от языка интерфейса.


v-bind:label="'BLINK_SATE' | lang"

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


После прошивки контроллера и подключения в сеть, нужно убедиться, что контроллер доступен по IP с вашего компьютера. Для этого в браузере наберите http://[IP контроллера]. Должен открыться WEB-интерфейс.


Теперь требуется указать адрес контроллера в файле config/dev.env.js


'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')

module.exports = merge(prodEnv, {
 NODE_ENV: '"development"',
 HW_DEVICE_URL: '"http://[IP контроллера]"'
 //HW_DEVICE_URL: '"http://192.168.8.105"',
 //HW_DEVICE_URL: '"http://192.168.4.1"',
})

Если dev-сервер был запущен, остановите его и вновь запустите. В будущем, после изменения файлов сборки, конфигурации, а также манифестов приложений, всегда перезапускайте dev-сервер.


Хотя при работе в dev-среде отображаются все приложения, которые есть в папке src/application как установленные, полнофункционально будут работать только те, которые действительно установлены на контроллер. Это не фича, а баг альфы. В будущем синхронизация железки и dev-среды будет происходить автоматически. Но пока, нужно поставить в ручном режиме приложение на контроллер, чтобы среда его “зацепила” и синхронизировала с тем, что есть в dev.


Собираем приложение в прод режиме:


npm run prod

Устанавливаем собранные приложения на контроллер напрямую. Не через dev-сервер.


Вот теперь можно приступить к разработке. Любые ваши изменения файлов будут автоматически запускать пересборку приложений и картинка на экране будет меняться (hot reload). Это же правило распространяется на срипты контроллера. К примеру, можно добавить в скрипт приложения blink команду debugger и посмотреть результат.


// Event listener
// $bus - system bus interface
$bus.on(function (event, content, data) {
 if (event === 'blink') {
   debugger;
   active = !!JSON.parse(content);
 }
}, null);

Теперь при изменении состояния чекбокса приложения Blink, среда разработки выкинет такое сообщение:



Кликнув по ссылке “Start debugger”, вы попадете в отладчик. Отобразится строка на которой произошла остановка.



Сам процесс отладки мало чем отличается от прочих отладчиков.



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


Среда отладки находится в процессе интенсивной доработки. Предстоит встроить еще много инструментов мониторинга и отладки. Заранее прошу прощение, за возможные баги.


Разработка прошивки


Разработка прошивки основывается на концепции предложенной Espressif. Переплюнуть родную документацию на этот счет я не смогу.


Для быстрого старта подготовлен репозиторий. В нем содержится информация по развертыванию. Пример использования смотрите в разделе “Реализация интерфейса”.


Сборка очень простая и буквально через 1-2 часа вы будете уже собирать прошивки без проблем.


Что дальше?


Дальше, если платформа заинтересует сообщество, планируется:


  • Развитие среды отладки;
  • Стандартизация именований интерфейсов, событий, компонентов;
  • Подробная документация на платформу;
  • Облачный хостинг для виртуальных вещей;
  • Runtime репозитории;
  • Партирование на различные готовые устройства.

Также, я ищу людей, которые хотели бы развивать платформу со мной. Она уже очень большая по размаху и амбициям. Я предполагаю равноправное сотрудничество, целью которого станет развитие платформы до полноценной на принципах OpenSource.


Для вступления в проект необходимо сделать ценные pull-реквесты в интересующие вас компоненты платформы.


Ссылки


Ресурсы проекта ThingJS:



Репозитории проекта ThingJS:



Используемые проекты:



FAQ


Тут будут появляться ответы на вопросы в комментариях.

Комментарии (24)


  1. smart_alex
    07.11.2019 15:02
    +1

    Может кто-то простым языком объяснить что это, есть ли уже что-то аналогичное и кому и зачем это может быть нужно?

    В чём смысл, соль и новизна?

    (Вопрос без подвоха, просто подробностей много, а общее понимание не сложилось после прочтения статьи.)


    1. rpiontik Автор
      07.11.2019 15:40

      Ответ также без подвоха.

      Я старался различными способами донести простые мысли. Но либо они оказывались слишком простыми (способы) и нужны были подробности, либо слишком сложными. В этой статье я постарался сделать все. И простым и сложным языком описать. И видео снял. Если бы я мог (был способен) описать это еще проще я бы так и сделал.

      Но давайте попробую.

      Что это?:
      1. Это IoT платформа. Она позволяет развернуть экосистему интернет-вещей.
      2. Платформа имеет два слоя. Первый прошивка. Она аппаратно-зависимая. Второй — приложения. Этот слой аппаратно-независимый.
      3. Между первым слоем и вторым существует абстракция — интерфейсы. Они позволяют второму слою управлять первым.

      В чем новизна:
      1. Стратегия платформы — развитие через сообщество. Т.е. не имеет вендора;
      2. Разделена на несколько сегментов, которые могут развиваться параллельно и независимо;
      3. Позволяет внедрять зависимости в проект. В том числе runtime;
      4. Имеет универсальную шину данных для обмена событиями во всей экосистеме.
      5. Уровень входа для разработки приложений аппаратно-независимого слоя минимален (для целевой аудитории — JavaScript разработчиков).

      Есть ли аналогичное:
      1. Нет. Такой парадигмы нет. Точнее я не встречал.
      2. Есть платформы решающие те же задачи (экосистемы) иными путями.

      В чем смысл:
      1. В том, что каждый для себя сможет пилить нужный ему проект и оставаться в контексте общей парадигмы. Сможет переиспользовать кем-то написанный код и публиковать свой для других. Примерно как с пакетными менеджерами. Типа npm.


      1. smart_alex
        07.11.2019 17:18
        +1

        Спасибо, так стало гораздо понятнее.

        Осталось только небольшое сомнение: дело в том, что все, кто занимается IoT — уже работают в какой-то своей среде и своей парадигме — неясной остаётся мотивация зачем разработчик должен «всё бросить» вместе со специфическим опытом и наработками и начать работать в новой системе.

        Но в любом случае желаю успеха вашей системе.


        1. rpiontik Автор
          07.11.2019 17:30

          Сомнение есть всегда. Вы зрите в корень. Чтобы кто-то что-то бросил, нужны веские основания. Но, чтобы они появились (основания) кто-то, что-то должен сделать. Я просто это делаю. И в это верю.

          Я не рассчитывал, что эта статья радикально изменит ситуацию. Хотя глупо было совсем на это не надеяться. Но любое движение ведет к развитию. Как минимум, если я не достигну ключевой цели (найти сподвижников) мне будет уже гораздо легче доносить материал тем, кто задаст вопрос — а что ты делаешь?

          Бэта будет уже направлена на конечных пользователей. Т.е. можно будет «потрогать» результат работы платформы. И с донесением смысла (я надеюсь) станет легче.

          Еще я планирую завезти Blockly Что б уж каждый мог попробовать что-то сделать.

          Я прекрасно понимаю, что людям нужно показать очевидные профиты. Но пока их нет. Точнее они есть, но не очевидны. Но это (статью) в любом случае нужно было сделать, чтобы не жалеть в будущем, что я это не сделал.


          1. smart_alex
            07.11.2019 18:06

            мне будет уже гораздо легче доносить материал тем, кто задаст вопрос — а что ты делаешь?

            Даже не надейтесь :) Я, как человек запиливший что-то подобное, по опыту могу сказать, что ни сайт, ни документация, ни дистрибутивы, ни статьи, ни примеры, ни ролики на ютубе, ни кейсы работающих проектов, ни личные разъяснения ни на йоту не добавляют понимания широкой аудитории, если автор со своими идеями далеко оторвался от неё :)

            Но в запиливании подобных проектов есть и свои положительные моменты — вы получаете огромный опыт и ни с чем не сравнимый фан (что само по себе (без)ценно).


            1. rpiontik Автор
              07.11.2019 18:11

              Возможно. Я пока этот опыт не получил. Изучаю успешные проекты open source. Пока, то, что я нашел, практически все начались совсем иначе… с инхауса в opensource. На определенном этапе развития.


              1. da0c
                07.11.2019 22:57
                +1

                Спасибо за статью! Вопрос, а не встречалась вам опенсорсная платформа с близким функционалом, но на питоне?


                1. rpiontik Автор
                  07.11.2019 23:05

                  За питоном я не слежу. Но платформы различные смотрел. Не находил похожих. Есть в какой-то мере похожая от mongoose платформа с mJS. Но концепция там совершенно иная.

                  Причем я смотрел целенаправленно, чтобы не заниматься лишней работой. Т.е. хотел взять за основу готовый проект и поучаствовать в его развитии. Адаптировать под себя.

                  Слышал, что есть завезенный микропитон на ESP32. Но не более.


                  1. Bluefox
                    09.11.2019 20:58

                    Питон — Home assistant? Не?


      1. Bluefox
        09.11.2019 21:18

        Людям ничего не надо, кроме красивых кнопочек… А тех кто разберётся сам с кодом и командной строкой или написанием скриптов — единицы.
        Я являюсь основателем именно такого Сообщества для интернета вещёй и у меня уже есть статистика и опыт.
        Как уже писали здесь: https://m.habr.com/ru/post/474356/comments/#comment_20860056… Людей заинтересовать ОЧЕНЬ сложно… А программистов почти невозможно.


        Сейчас из 32000 пользователей у нас 23 разработчика, которые иногда что то делают с центральным ядром. Это 0,07%.
        Хотя число людей которые постоянно что то делают с ядром системы, равно четырём.
        Немного больше людей пилят части (драйвера) для себя — около 100.


        Так что заинтересовать людей можно только если вложится в маркетинг, как это делает Apple, Samsung, Google.


        Я не понимаю, почему сказано, что автор ничего не нашёл? А ioBroker, OpenHAB, Home Assistant, FHEM, Domoticz, Majordomo для софтовой стороны? А mysensors, blink, esp+mqtt, sonoff и куча непонятного железа для железной части?


        А мой совет — пишите платформу для себя и для своих нужд. И если концепт правильный, то люди подтянутся, а если нет… То опыт сын ошибок трудных тоже бесценен.


        1. rpiontik Автор
          09.11.2019 22:40

          Сказано было не так. Я сказал, что подобной концепции я не нашел. Если речь о моем высказывании.

          Что касается — вкладываться в маркетинг, то я не хочу тратить свое время на то, что делать не умею. Я прекрасно понимаю, что бизнес стоит на нем. Но я не маркетолог. Всему свое время и люди.

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

          Спасибо за советы!


  1. Delsian
    07.11.2019 19:06
    -1

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

    image


  1. Tyiler
    07.11.2019 22:05

    Слишком сложно. Сложно даже до конца прочитать статью, наворочено много всего.

    Вот, например, OPC — интерфейс между промышленными контроллерами с одной стороны и скада системами (визуализацией) с другой.
    Что он из себя представляет по сути — набор библиотек с С-интерфейсом, чтобы можно было из любого языка вызвать несколько функций. И нет никакой кучи слоев передачи данных с модными названиями.

    Например, появился новый контроллер, пусть тот же ESP32. Пишется для него драйвер Один раз для передачи данных в OPC сервер. Все на этом. Все панели управления или ПК, которые понимают OPC, смогут управлять этим контроллером, либо показывать с него информацию, дальше ее передавать куда то и тд.


    1. Siemargl
      07.11.2019 22:46
      +1

      У вас очень ограниченное понимание предметной области. ОРС например, не может работать нигде кроме Вин, не умеет в принципе в сколь-нибудь сложную топологию — модем, или передача по требованию контроллера, или раз в неделю итд.


      1. Whuthering
        08.11.2019 11:02

        ОРС например, не может работать нигде кроме Вин
        А мужики-то не знали! (с)
        Вылезайте из своего 20-го века, референсные OPC UA библиотеки (который на C и C#) прекрасно работают под Linux, а еще есть много открытых имплементаций клиентов и серверов на кросс-платформенных языках типа Java, Rust, Go, Python и JS.


        1. Siemargl
          08.11.2019 11:11

          Только ОРС и OPC UA разные совершенно вещи =)


          1. Whuthering
            08.11.2019 11:29

            (немного занудства)
            OPC DA и OPC UA — это да, совершенно разные вещи, а OPC и OPC UA «совершенно разными» вещами быть не могут, т.к. UA это один из подвидов (стандартов) OPC.

            Кстати, забавно, но по ссылке, приведенной автором исходного комментария, говорится, что даже OPC DA (тот самый, который через виндовский DCOM) все-таки может работать под Linux, QNX и VxWorks с помощью сторонних костылей решений. Насколько оно жизнеспособно рассуждать не берусь, т.к. на практике с такими конфигурациями не сталкивался.


  1. udvnl
    07.11.2019 23:29

    На мой взгляд, всё как то абстрактно. Сейчас IoT платформой чего только не обзывают, начиная с удаленного управления светом и заканчивая АСУ ТП в промышленных масштабах )).
    Концепция, модули, развёртывание и т.д. — всё это хорошо, но не понятно, а что дает эта платформа пользователю? Хотя бы несколько примеров решения практичных задач. На какой сегмент она ориентирована и как в неё можно интегрировать уже существующие платформы?
    Вообщем лично у меня вопросов больше чем ответов.

    В чем новизна:
    1. Стратегия платформы — развитие через сообщество. Т.е. не имеет вендора;
    2. Разделена на несколько сегментов, которые могут развиваться параллельно и независимо;
    3. Позволяет внедрять зависимости в проект. В том числе runtime;
    4. Имеет универсальную шину данных для обмена событиями во всей экосистеме.
    5. Уровень входа для разработки приложений аппаратно-независимого слоя минимален (для целевой аудитории — JavaScript разработчиков).

    Есть ли аналогичное:
    1. Нет. Такой парадигмы нет. Точнее я не встречал.
    2. Есть платформы решающие те же задачи (экосистемы) иными путями.

    В чем смысл:
    1. В том, что каждый для себя сможет пилить нужный ему проект и оставаться в контексте общей парадигмы. Сможет переиспользовать кем-то написанный код и публиковать свой для других. Примерно как с пакетными менеджерами. Типа npm.

    Скажите, чем отличаются те же сообщества обозванные по старинке, именованием «Умный Дом» или системами «Домашней автоматизации» ( OpenHab, Home Assistant, Domoticz, HomeBridge, MajorDoMo и т.д) от вашего проекта, за исключением п.5. и названием IoT?


    1. rpiontik Автор
      07.11.2019 23:57

      На мой взгляд, всё как то абстрактно.

      Не знаю даже что сказать. Я бы согласился, если бы не было возможности взять и собрать прошивку. Специально постарался выложить то, что реально можно собрать прошить и потрогать. Может как-то не так понял вас?

      Сейчас IoT платформой чего только не обзывают, начиная с удаленного управления светом и заканчивая АСУ ТП в промышленных масштабах )).

      Совершенно согласен.

      всё это хорошо, но не понятно, а что дает эта платформа пользователю? Хотя бы несколько примеров решения практичных задач.

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

      На какой сегмент она ориентирована и как в неё можно интегрировать уже существующие платформы?

      Сегмент — бытовой. Пром я не рассматривал. Я бы вообще не называл нормальный пром IoT. Все пром системы имеют собственные классификации.

      Скажите, чем отличаются те же сообщества обозванные по старинке, именованием «Умный Дом» или системами «Домашней автоматизации» ( OpenHab, Home Assistant, Domoticz, HomeBridge, MajorDoMo и т.д) от вашего проекта, за исключением п.5. и названием IoT?

      И пункта 3. Его тоже нигде нет. Да и пункта 4 тоже. Буду благодарен за обратные примеры.

      Но даже если рассматривать пункт 5, то это ровно 50% от всей платформы. И, что самое примечательное, именно это является «лицом» платформы для пользователя. Т.е. это не просто пункт 5, а ПУНКТ 5!


      1. udvnl
        08.11.2019 08:58

        Специально постарался выложить то, что реально можно собрать прошить и потрогать. Может как-то не так понял вас?

        Да, может мы не правильно понимаем друг друга. В начале, статья мне понравилась, видно вложено много труда как к статье, так и самое главное, к работе по проекту.
        Сам немного интересуюсь домашней автоматизацией и иногда что то делаю. Поэтому задал несколько вопросов. Лично я не совсем понял, как практически использовать ваш проект.
        И пункта 3. Его тоже нигде нет. Да и пункта 4 тоже. Буду благодарен за обратные примеры.

        Я немного использую MajorDoMo, могу сказать, что эти пункты в том или качестве присутствуют. по п.4 даже не один вид шины (протокола), если я вас правильно понимаю, есть возможность использования MQTT, ZigBee, Z-Way и т.д.
        А вот п.5 меня смущает. Порог вхождения получается довольно высокий, явно рассчитан не на простых пользователей, на мой взгляд большинство открытых систем стараются снизить этот показатель, чтоб привлечь как можно больше энтузиастов в свой проект.


        1. rpiontik Автор
          08.11.2019 10:40

          MQTT, ZigBee, Z-Way это протоколы и сети. Шина это унифицированное средство доставки сообщений приложениям и устройствам. Она реализуется (при необходимости) на любом из выше описанных протоколов.

          Реализация шины в проекте скорее ближе к сервисной шине или интеграционной.

          А вот п.5 меня смущает. Порог вхождения получается довольно высокий, явно рассчитан не на простых пользователей...


          Тут вы и правы и нет одновременно. Это не платформа аля ардуино. Я делал ее на профессиональных фреймворках и технологиях. Порог вхождения в 5 высок для тех, кто не работает профессионально с JavaScript. Для тех, кто работает его практически нет. В том то и дело.

          Также как и для разработчиков на С. Используется FreeRTOS, Mongoose решения, cmake. Т.е. профессионал должен себя чувствовать комфортно.

          тоб привлечь как можно больше энтузиастов в свой проект

          Если под энтузиастами вы понимаете любителей, то они не будут обделены. В бэте фокус будет уже на готовые устройства. А также будет завезен язык визуального программирования — blockly. На нем можно будет создавать приложения для ThingJS прям на сайте.


  1. predator86
    08.11.2019 00:44
    +1

    Интересная статья, спасибо.
    Почему вы выбрали именно mJS, а не Espruino?
    В нем вроде и ограничений для JS кода меньше.


    1. rpiontik Автор
      08.11.2019 00:57

      Я старался минимизировать зоопарк технологий и модулей. Mongoose является во всех смыслах уважаймой конторой для встраиваемых систем. Делает много полезного. Я использую его WEB сервер и сразу обратил внимание на mJS.

      С mJS удалось быстро разобраться и кастомизировать. В частности, встроить отладчик. Ограничения не сильно пугают, т.к. не вижу припятствий в развитии диалекта. Ребята с монгуста делают очень читабельный и расширяемый код.


  1. zim32
    08.11.2019 11:24

    Я думал на аппаратном уровне уже есть mqtt и больше там ничего не надо.