Привет, уважаемый читатель!

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

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

  1. Oracle Java ME – проект, скорее мертв, чем жив. Давным-давно, в 2006 году, Oracle прибило его гвоздями к Raspberry Pi Model B/STM32429I-EVAL/32746GDISCOVERY с минимальными требованиями по железу того времени. И так выглядит скорее заброшенным, то похоже не полетел.

  2. Pi4j – неплохой проект от Robert Savage, который развивается до сих пор. Однако это просто враппер на WiringPi, который автор забросил и “прибитый” гвоздями к определенным платам. Сейчас на сколько я вижу, идут попытки отойти от этого.

  3. Diozero – так же неплохой проект от Matthew Lewis, однако как и предыдущие два, “прибит” гвоздями к определенным SoC или платам.

  4. UPD 1: DeviceIO от того же Оракла. Поддержка I2C/SPI для любого linux. GPIO так же, но через depricated sysfs. Работа с памятью для BCM2835 only. (за наводку спасибо qvas)

Если я что-то упустил, пишите в комментариях.

А как же “Write once and run anywhere?”, спросит читатель. А вот никак и точка.

“Моргать светодиодом” хотелось, но Си было вспоминать лень, Питон учить не хотелось еще больше, а вся мощная экосистема java, которая имеется на текущий момент оставалась за бортом и не давала покоя. Плюс академический интерес.

Несмотря на то, что мир за пределами JVM представлял для меня темный лес, то задача выглядела интересно. Да и учетом того, что современные одноплатники уже начали переходить на 64 бит, имеют большие тактовые частоты и несколько ядер. Имеют на борту такие объемы Memory/Storage, за которые не то, что программист, геймер каких то 15 лет назад продал бы душу “кремневому дьяволу”, то условия и возможности современного железа достаточно сильно поменялись...

И с этим хотелось что-то сделать...

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

  • нужна библиотека, которая не несет на борту дополнительного самописного нативного кода

  • библиотека работает на любом Linux, любого одноплатника (при наличии там драйверов)

  • библиотека поддерживает SPI/I2C/Serial/GPIO

  • библиотека работает как в JVM mode, так и Native. В результате чего ее можно будет использовать в экосистемах тех же Spring и Quarkus.

Архитектура

Чтобы добиться требуемого, надо было научиться работать с Linux/POSIX из java, научиться компиляться в native из которого работать с том же Linux/POSIX, что на первый взгляд представлялось проблемой. Однако, через некоторое время, проблема была решена.

Для JVM mode был выбран неплохая библиотека JNA (да, там под капотом нативный libffi), о которой на хабре написано, включая и подводные камни работы с библиотекой. С последующей миграцией на Project Panama, как только проект выйдет из инкубатора.

Для Native, тут без вариантов – GraalVM.

В результате архитектура стала выглядеть так:

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

По крайней мере, в результате проверок, на Raspberry Pi Zero W и Raspberry Pi на Raspberry Pi OS 32/64 bits(который Debian) и Orange Pi Zero на Armbian, все прекрасно работает на тестовых примерах. В теории, т.к. там используется голый kernel 4.8+ будет работать из коробки на любом Linux и потенциально Android(который вроде как тот же линукс).

API и использование

Я не буду описывать, что такое I2C/SPI/Serial и GPIO, материалов в google больше, чем достаточно. В рамках данного поста опишу просто как это использовать.

Данные интерфейсы мапятся в device files вида /dev/i2c*, /dev/spi*, /dev/serial* (тут бывают варианты) доступ к которым был реализован через API:

I2CBus.java

SpiBus.java

SerialPort.java

C GPIO ситуация несколько другая. С kernel 4.8 в Linux появилось возможность нормально взаимодействовать со всеми PIN-ами через device files, а не через sysfs. В результате чего, пины доступны всегда. Правда, стоит заметить, что нашелся и side effect, некоторые пины процессора, определяются линуксом как unused, но могут использоваться системой. Любая попытка обращения к ним приводит к полному зависанию системы. Спасает обычного пользователя тут одно - обычно эти пины на плату не выведены.

Подергать пины, поморгать светодиодом можно с помощью GpioManager.java.

Более полные примеры работы можно найти в тестовых примерах.

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

В результате архитектура библиотеки для java программиста стала выглядеть так:

Сама библиотека доступна тут.

И да, не надо меня пинать за громкое название библиотеки. Ничего лучше на ум не пришло.

Что дальше?

Если вы дочитали до этого момента, то значит вам это действительно интересно.

Если вы java-программист, берите и используете библиотеку, если необходимо.

Свой академический интерес я удовлетворил.

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

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

Но главная цель достигнута – из java работать на одноплатниках легко и просто и был получен неоценимый опыт. Получен опыт Linux/POSIX, работы с “железными протоколами”, работы с регистрами устройств. Выйти из ограничений виртуальной машины вполне возможно и там доступен новый чудный мир который возможно интегрировать в java-приложение.

На сим откланиваюсь. Спасибо всем кто прочитал статью. Если интересны какие то детали, пишите в комментариях.

P.S. В рамках статьи не упомянута native часть, ее так же нет в исходниках на github, если у community есть интерес по поводу GraalVM и как писать native приложения на java с подключением линуксовских библиотек, то раскрою тему в рамках отдельной статьи.

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


  1. Gorthauer87
    28.09.2021 11:36
    +1

    В Java ведь так и нету unsigned типов? Сильно ли это мешало?


    1. sandersru Автор
      28.09.2021 11:40

      Исключительно при написании драйверов под чипы. Например в BMP280 в datasheet вся "математика" на побитовых сдвигах. Сначала свернул не туда, потом поменял на умножение/деление.

      При написании bridge java-posix даже этого не заметил.


      1. quaer
        28.09.2021 21:29

        При использовании сдвигов у явы не должно быть проблем относительно unsigned/signed.
        Для сдвига вправо для signed есть >>, для unsigned >>>. Сдвиг влево в обоих случаях это <<.
        Так что лучше от умножения/деления избавиться, добавит скорости работы.


  1. Paskin
    28.09.2021 11:37

    Вопрос - как производительность и системные требования Java на одноплатниках выглядят в сравнении с, допустим Python.


    1. sandersru Автор
      28.09.2021 11:48
      +2

      По моему это от задачи скорее зависит и сколько вы Xms and Xmx для JVM выделите под эти задачи.

      Но на Raspberry Pi Zero W и Orange Pi Zero особых проблем не испытывал с примерами с гитхаба.

      Обратите внимание, современный одноплатник на борту имеет CPU/Memory больше чем сервера 10-15 лет назад, где java прекрасно работала(и продолжает работать) в enterprise на том же WebLogic с 4-8GB на ноду, по этому я бы с ходу этим вопросом не заморачивался с самого начала.

      Если говорить про GraalVM native-image, то ядро занимает около 10МБ на диске. В памяти на 64mb я свои тестовые примеры в native запускал без проблем.


      1. Shatun
        28.09.2021 15:25
        +1

        Обратите внимание, современный одноплатник на борту имеет CPU/Memory больше чем сервера 10-15 лет назад, где java прекрасно работала(и продолжает работать) в enterprise на том же WebLogic с 4-8GB на ноду,

        8 gb на ноду это прямо даже много было 15 лет назад.

        В памяти на 64mb я свои тестовые примеры в native запускал без проблем.

        Я без грааля смотрел на одноплатнике дешевом лет 5 назад на JAVA 8, у меня получилось примерно так(смотрел по хmx, выделенная память, реально потребеление будет чуть выше):

        • вроде бы 2 mb RAM было минимум(это с небольшими приседаниями)

        • 8мб уже можно было использовать для коносльных приложений, простого-веб-сервиса или веб-морды. Но надо смотреть на используемые бибилотеки.

        • 64 мб вполне успешно крутился томкат с веб-сервисом и мордой на JSP, каких-. По сути уже можно использвоать для всего подряд. Спринг правда тяжеловат, его бы не рекомендовал брать в таком конфиге.

        По логике с новой джавой и новыми GC(zgc, shenandoah) потребление памяти должно было бы упасть. С граалем соотвственно еще меньше.


        1. sandersru Автор
          28.09.2021 16:18

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

          А с учётом будущего выхода из incubator Project Panama все станет ещё интереснее.

          К тому же Graal все же разрабатывается как платформа для многих языков. Что сделает более тесную интеграцию кода.

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


          1. Shatun
            28.09.2021 17:20

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

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


            1. sandersru Автор
              28.09.2021 17:31
              +1

              Так Панама - это так же замена JNI и отсутствие необходимости писать враперы на сях для доступа в натив.

              Надеюсь только ее Оракл с Graal-Native подружит(запросы на гитхабе уже есть) чтобы не пришлось два различных пути делать.

              Говоря про производительность - это возвращаясь к разговору о серверах. Уже лучше одноплатники чем 10 лет назад большое железо. Улучшения же в jave давно ждем.


              1. Shatun
                28.09.2021 18:18

                Ох, спутал панаму с лумом. Да, согласен с вами.


  1. qvas
    28.09.2021 12:51

    Есть еще библиотека DeviceIO (https://wiki.openjdk.java.net/display/dio) от того же оракла, поддерживает все linux платы (теоретически, т.к. не использует ничего вендорспецифичного). GPIO работает поверх sysfs


    1. sandersru Автор
      28.09.2021 12:59

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

      Как я писал в статье, это еще один пример от которого хотелось уйти - масса сишного кода (то есть вся работа в си, java - враппер). Sysfs - depricated с 4.8 и требует дополнительных действий для того чтобы его включить-выключить.

      Но еще раз спасибо за ссылку, посмотрю в исходники на тему может я что-то "продолбал"


      1. qvas
        28.09.2021 15:31

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

        Кстати, сама библиотека довольно широко разошлась, Eclipse Kura ее использует. Было бы неплохо ее переписать чисто на жавке с сохранением АПИ


        1. sandersru Автор
          28.09.2021 16:14

          В DIO нативный код был не от хорошей жизни, библиотека писалась для JavaME c ее ограничениями

          Да, понятное дело. Все же писалась она достаточно давно. Требования тогда были другие.

          Было бы неплохо ее переписать чисто на жавке с сохранением АПИ

          А вот это отличная идея. Завтра посмотрю как сделать.


    1. sandersru Автор
      28.09.2021 13:29

      Update: судя по исходникам, SPI/I2C/GPIO - действительно ничего вендорспецифичного.

      Однако mmio.c прибит гвоздями:

      #define BLOCK_LEN 0x01000000
      #define BASE_ADDR 0x20000000
      //#define VIRT_ADDR 0x7e000000
      

      К BCM2835 - https://www.raspberrypi.org/app/uploads/2012/02/BCM2835-ARM-Peripherals.pdf (то есть RPi A(+)/B(+)/Zero. На другом железе (включая RPi4) это работать не будет.

      и опять же в сишном коде. Я от direct memory access пока отказался в верхоуровневом API (кейса не нашел), но это так же возможно - https://github.com/java-embedded-framework/jef/blob/master/linux-core/src/main/java/ru/iothub/jef/linux/core/Mmap.java

      Пусть и вендорспецифик, но это можно использовать.


  1. inferrna
    28.09.2021 14:11
    +2

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


    1. sandersru Автор
      28.09.2021 14:17
      +1

      Или может время еще не пришло?

      Инжектор был придуман в 1864 году, однако когда он начал появляться в ДВС автомобилей?

      Просто "железо" сначала дозрело до RTOS, потом дозрело до Linux и продолжает дальше зреть. Замечу, 3 моих соседа рулят отделами разработки в Siemens, Qualcomm, Broadcomm и пишут.... на golang. Это не штучные люди, а направление миграции "железячных компаний" от си-с++ в сторону go. Ваш комментарий, 5 лет назад звучал бы для golang точно так же, как сейчас для Java. Однако...

      Java имеет огромную экосистему и придя на embedded, она в том числе сократит стоимость разработки и time to market для будущих систем. А возможно и нет. Время покажет.


      1. inferrna
        28.09.2021 17:28

        Я немного не о том: любительские проекты на Java без поддержки корпораций не выживают, так как сил сообщества оказывается недостаточно (не находится достаточно энтузиастов). Хотя, казалось бы, один из популярнейших языков. Тот же golang вполне имеет своих фанатов, которые пишут на нём "для души", на Python чуть ли не половина пишут только пет-проекты.

        То есть, люди пишут на Java, так как за неё хорошо платят корпорации, но дома, "для души", избегают.
        (да, я не люблю Java, но ещё я не люблю C++, но вот у него в этом плане порядок - кто на нём пишет, тот его любит)


        1. sandersru Автор
          28.09.2021 17:33
          +1

          Apache Foundation и Eclipse Foundation с вами не согласятся. :)


          1. inferrna
            28.09.2021 18:54

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


            1. sandersru Автор
              28.09.2021 20:03

              Вообще то java - как бы из мира виртуальных машин, они с железом слабо пересекаются ;) Отсюда и программисты не сильно пересекаются, пока. :)


        1. quaer
          28.09.2021 21:35

          Java как раз удобна для написания приложений для души, потому что написанное приложение работает на винде, линуксе, маке и если отвязать GUI — то и на Андроид.


          1. inferrna
            29.09.2021 12:08

            В теории - да, на практике же чего-то известного (из изначально любительского) на жаве довольно мало, если считать относительно общего числа разработчиков на ней. Из того, чем я лично пользовался, могу разве только вспомнить Sikuli и Jython (у ней внутре).


      1. Paskin
        28.09.2021 18:13

        Про инжекторы и развитие в целом - я согласен, как и про приход ЯВУ в embedded. Но курьез в том, что многие железки уже поддерживают MicroPython "из коробки" - а Java, изначально придуманная для embedded - потеряла рынок приложений для телефонов (что там с SIM-приложениями - я не знаю), и вряд ли вернется в одноплатные компьютеры.


        1. sandersru Автор
          28.09.2021 18:23

          А можно подробнее, что значит:

          MicroPython "из коробки" 

          Название железа, плат и т.д. То про, что вы говорите, это работа на микроконтроллерах похоже. Ниша которых в ближайшем будущем - работа от батарейки (энергоэффективность) и RT системы. Больше - они нигде будут не нужны скорее всего.

          Современная "плата" 2021го года это Linux 4.8+, с DDR4 и ARM64(в процессе перехода). Вы на ней, спокойно разворачиваете и Java и Golang (да вон на ней докер легко бегает), и работаете ну как минимум не хуже чем на PC 5 лет назад.

          А с учетом, что я написал в комментариях, что Samsung/Broadcom/Qualcomm уже переходят на Golang, то какой микропитон тут уж. Жить ему где то на STM32/ESP32 (вроде там он и живет ныне) и всяких 8ми битных ардуинах.


          1. Paskin
            29.09.2021 08:02

            MicroPython "из коробки" - значит что подключаясь к плате, вы попадаете в консоль Python, а исходник с определенным именем автоматически запускается при подаче питания/рестарте.
            "Современные платы" с Linux - это совсем другой класс (одноплатные компьютеры), да и от батарейки с потреблением в 15 Вт как у "малины" - вы их работать долго не заставите. А реальный "embedded" действительно живет на STM32 и ему подобных.
            В качестве примера платы - могу порекомендовать MAIX Bit. Разумеется, MicroPython там можно не загружать и писать на С/С++ - как и в ESP32. Но в качестве средства для прототипирования и отладки алгоритмов - MicroPython очень удобен.


            1. sandersru Автор
              29.09.2021 08:51

              Приветствую, спасибо.

              все ровно как я написал выше и про батарейку и кто там останется вскоре и про «640 КБ должно хватить всем» (с). К сожалению это вымирающий вид. Нет он не вымрет, но ниша будет сокращаться. Как у Amtel 8 бит, или STM32 убивший своего младшего брата STM8.

              Если у вас есть розетка, то проще взять тот же Allwinner F100S или V3S (а они стоят практически тех же денег, что и STM, а иногда и дешевле) и получить готовый линукс со всеми вкусностями и плюшками, которые возможны на операционной системы.


              1. Paskin
                29.09.2021 10:03

                Посмотрите на ту плату, которую я упомянул - или пресловутый ESP32. Линукс там ни для чего не нужен в 99.99% применений. Можно еще упомянуть эпичную историю с раутерами Linкsys - выкинув оттуда Линукс, удалось сократить вдвое количество памяти на плате при одновременном добавлении новых возможностей.
                "Если у вас есть розетка" - а что, если нет? И радиатор с вентилятором некуда ставить - а без него включится тепловой троттлинг и производительность упадет...