Share This
Связаться со мной
Крути в низ
Categories
//Начало работы с JavaFX на Raspberry Pi

Начало работы с JavaFX на Raspberry Pi

Raspberry Pi идеально подходит для любителей поэкспериментировать с электронными компонентами. Если объединить его с инструментами Java-разработчика, вы откроете для себя новый мир. Обсудить

nachalo raboty s javafx na raspberry pi 103c119 - Начало работы с JavaFX на Raspberry Pi

Недорогой одноплатный компьютер Raspberry Pi отлично работает с Java, отчего hardware-разработка становится не сложнее классической разработки ПО.

***

Из этой статьи вы узнаете, как разработать приложение на JavaFX в стиле дашборд, используя библиотеку TilesFX.

На рис. 1 изображен пользовательский интерфейс (далее UI).

nachalo raboty s javafx na raspberry pi 9b49fbf - Начало работы с JavaFX на Raspberry Pi

Рис. 1 UI приложения

Также вы можете ознакомиться с видео, на котором показана работа приложения на Raspberry Pi 3B+ и функциональность интерфейса с тачскрином.

Приведенный код и рассмотренные методы применимы только к микрокомпьютерам Raspberry Pi на чипах ARM v7 и ARM v8. В разделе спецификаций плат Raspberry Pi на Википедии вы найдете типы поставляющихся с этими процессорами плат:

● Модель A+, версия 3;

● Модель B, версии 2, 3 и 4;

● Вычислительный модуль (Compute Module), версия 3.

Прочие используемые в проекте электронные компоненты вы найдете в большинстве стартовых наборов Arduino/Pi. Если вы хотите использовать другие элементы, можете начать с компонентов из проекта, и подстроить их под свои нужны. Мой комплект выглядит следующим образом:

● Raspberry Pi 3 Модель B+;

● SD карта 32 GB (или более) на ОС Raspberry Pi (бывш. Raspbian);

● Монитор, клавиатура и мышь;

● Светодиод и резистор (для большинства подойдет 330 Ом);

● Кнопочный переключатель;

● Датчик расстояния HC-SR04;

● Беспаечная макетная плата и провода.

Подготовка платы Raspberry Pi

Если вы работаете Raspberry Pi впервые, подготовьте карту SD с операционной системой. В проекте используется Full ОС Raspbian (полная версия). Загрузите инструмент Imager. Для своей работы я взял версию Raspberry Pi Imager 1.2 от марта 2020 г. (рис. 2 и рис. 3). Убедитесь, что устанавливаете полную версию.

nachalo raboty s javafx na raspberry pi 7280146 - Начало работы с JavaFX на Raspberry Pi

Рис. 2 Страница скачивания инструмента Imager

nachalo raboty s javafx na raspberry pi 7f73c2d - Начало работы с JavaFX на Raspberry Pi

Рис.3 Выберите полную версию (Raspbian Full)

Как только SD карта будет готова, вставьте ее в плату Raspberry Pi, запустите операционную систему и выполните действия по настройке и подключению к вашей сети Wi-Fi.

Установка JDK (Java Development Kit) с помощью JavaFX

В примечаниях к релизу Raspbian указано, что используемая мной версия ОС включает OpenJDK 11.

         2019-06-20: * Based on Debian Buster * Oracle Java 7 and 8 replaced with OpenJDK 11      

Java-версия:

         $ java -version openjdk version "11.0.3" 2019-04-16  OpenJDK Runtime Environment (build 11.0.3+7-post-Raspbian-5)  OpenJDK Server VM (build 11.0.3+7-post-Raspbian-5, mixed mode)      

Теперь плата готова к запуску любых приложений на основе Java 11. Однако JavaFX больше не является частью JDK (начиная с Java 11), и запустить JavaFX-программу на Raspberry Pi «из коробки» не получится.

К счастью, BellSoft разработала Liberica JDK. Предназначенная для Raspberry Pi версия включает JavaFX, поэтому вы сможете запускать упакованное приложение JavaFX с помощью команды java -jar yourapp.jar. Используйте ссылку для загрузки от BellSoft, чтобы установить альтернативную JDK:

         $ cd /home/pi  $ wget https://download.bell-sw.com/java/13/bellsoft-jdk13-linux-arm32-vfp-hflt.deb  $ sudo apt-get install ./bellsoft-jdk13-linux-arm32-vfp-hflt.deb  $ sudo update-alternatives --config javac  $ sudo update-alternatives --config java      

Когда инсталляция будет завершена, проверьте версию снова. Она должна выглядеть следующим образом:

         $ java --version  openjdk version "13-BellSoft" 2019-09-17  OpenJDK Runtime Environment (build 13-BellSoft+33)  OpenJDK Server VM (build 13-BellSoft+33, mixed mode)      

На моей тестовой плате хранятся различные версии Liberica JDK. Переключаться между ними несложно с помощью команды update-alternatives. (см. Рис.4)

nachalo raboty s javafx na raspberry pi 4f5c2e0 - Начало работы с JavaFX на Raspberry Pi

Рис. 4 Переключение между версиями Liberica JDK

В папке исходного кода Chapter_04_Java/scripts на GitHub расположены установочные скрипты различных версий Liberica JDK. Они содержат ссылки для скачивания. (см. Рис. 5)

nachalo raboty s javafx na raspberry pi 6e61b15 - Начало работы с JavaFX на Raspberry Pi

Рис. 5 Установочные скрипты различных версий Liberica JDK

Различные схемы нумерации Raspberry Pi

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

Нумерация Header Pin. Это логическая нумерация заголовка платы. Один ряд содержит четные пины, другой – нечетные.(см. Рис. 6)

nachalo raboty s javafx na raspberry pi 5231e52 - Начало работы с JavaFX на Raspberry Pi

Рис. 6 Нумерация пинов заголовка платы

Нумерация BCM. Относится к номеру канала Broadcom – нумерации внутри микросхемы, используемой на Raspberry Pi.

Нумерация WiringPi. Wiring Pi – основной фреймворк, используемый Pi4J (в проекте он применяется в качестве библиотеки в Java) для управления GPIO. Причина другой схемы нумерации кроется в истории платы. Когда еще велась разработка первых моделей Raspberry Pi, предполагалось всего восемь контактов. В результате дальнейшего развития микрокомпьютера и добавления дополнительных контактов, нумерацию в WiringPi расширили, чтобы можно было на них ссылаться.

Чтобы Java разработчикам было проще понять разницу между различными типами заголовков, пинами и функциями, я разработал небольшую библиотеку, расположенную в репозитории Maven по адресу be.webtechie.pi-headers. Используя ее и небольшое приложение JavaFX, я сделал вспомогательную таблицу (см. Рис. 7), которая упрощает поиск и сопоставление номеров с соответствующими им пинами на плате. Более подробную информацию вы найдете по ссылке “Raspberry Pi history, versions, pins and headers as a Java Maven library.”

nachalo raboty s javafx na raspberry pi e21cf2a - Начало работы с JavaFX на Raspberry Pi

Рис. 7 Сопоставление номеров с пинами на плате

Подключение оборудования

Давайте подключим оборудование, чтобы использовать часть мощностей платы Pi: светодиод, кнопку и датчик расстояния. См. Табл. 1, Рис. 8, Рис. 9.

nachalo raboty s javafx na raspberry pi 9a57d4e - Начало работы с JavaFX на Raspberry Pi

Табл. 1 Сопоставление пинов с соответствующими устройствами

nachalo raboty s javafx na raspberry pi 95a0b8f - Начало работы с JavaFX на Raspberry Pi

Рис. 8 Подключение проводов

nachalo raboty s javafx na raspberry pi 11fad82 - Начало работы с JavaFX на Raspberry Pi

Рис. 9 Схема подключения

На рис. 10 изображена система с использованием моста на макетной плате RasPiO, что упрощает поиск правильного пина. Разъем моста помещает номера BCM в логическом порядке, но я все еще использую отдельную макетную плату, чтобы было немного больше места. Portsplus предлагает аналогичное удобное решение.

nachalo raboty s javafx na raspberry pi 35c24d6 - Начало работы с JavaFX на Raspberry Pi

Рис. 10 Фото системы с использованием моста макетной платы RasPiO

Чтобы проверить, подключен ли светодиод в правильном положении с учетом его полярности, отсоедините кабель между светодиодом и пином GPIO (оранжевый кабель на рис. 10) и подключите его непосредственно к контакту 3,3 В (или к плюсу на плате). Если светодиод не загорится, вам нужно поменять его положение.

Проверьте светодиод и кнопку через терминал

Чтобы проверить соединение, запустите команду gpio через терминал.

Примечание: Если вы работаете с платой Raspberry Pi 4, обязательно используйте версию 2.52 утилиты gpio. Поскольку внутренняя проводка процессора на плате Pi 4 отличается от предыдущих моделей, при необходимости доступно обновление для утилиты. Проверьте свою версию с помощью команды gpio -v через терминал и, если нужно, установите новую с помощью следующих команд:

         $ gpio -v gpio version: 2.50 $ cd /tmp $ wget https://project-downloads.drogon.net/wiringpi-latest.deb $ sudo dpkg -i wiringpi-latest.deb $ gpio -v gpio version: 2.52      

Включить (1) и выключить (0) светодиод:

         $ gpio mode 29 out $ gpio write 29 1 $ gpio write 29 0      

Состояние кнопки (1=нажата, 2=не нажата) 27 пина WiringPi

         $ gpio mode 27 in  $ gpio read 27 1      

Переключение светодиода с помощью Java

Начинается самое интересное! Следующий код настраивает 29 пин WiringPi и 10 раз переключает его между включением и выключением с интервалом в 500 мс, используя ту же команду, которую я применял в терминале. Создайте файл под именем HelloGpio.java со следующим содержанием:

         public class HelloGpio {     public static void main (String[] args) {         System.out.println("Hello Gpio");          try {             Runtime.getRuntime().exec("gpio mode 29 out");              var loopCounter = 0;             var on = true;              while (loopCounter < 10) {                 System.out.println("Changing LED to " + (on ? "on" : "off"));                 Runtime.getRuntime().exec("gpio write 29 " + (on ? "1" : "0"));                  on = !on;                  Thread.sleep(500);                  loopCounter++;             }         } catch (Exception ex) {             System.err.println("Exception from Runtime: " + ex.getMessage());         }     } }      

Поскольку код будет использовать Java 11 (или выше), файл Java может быть выполнен без компиляции:

         $ java HelloGpio.java Hello Gpio Changing LED to on Changing LED to off Changing LED to on …      

Использование датчика движения

Написанное для этого блока приложение использует обычный ультразвуковой датчик расстояния, который можно найти во многих стартовых наборах Arduino и Pi. Это модуль под названием HC-SR04; дополнительную информацию и примеры вы можете найти в интернете. Модулю требуются входные и выходные GPIO-соединения. Датчик работает по той же схеме, что и система, используемая летучей мышью для полета в темноте: он использует отражение ультразвука для расчета расстояния до объекта.

Для измерения расстояния требуется, чтобы приложение и модуль выполнили следующие действия:

  1. Модуль нужно запитать на 5 V;
  2. Приложению нужно установить вывод trig в положение high как минимум на 10 мкс;
  3. Модуль посылает несколько (обычно восемь) сигналов 40 кГц и определяет, когда сигнал принимается обратно;
  4. Вывод echo устанавливается в положение high на ту же продолжительность, что и время, необходимое для возврата ультразвука к датчику;
  5. Приложение измеряет длительность положения high echo-вывода, чтобы рассчитать расстояние на основе скорости звука.

Готовое приложение

Запуск одного файла Java хорошо для тестирования системы, но это только первый шаг. В приложении используется библиотека Pi4J для объединения Java и портов GPIO на плате Raspberry Pi. Pi4J должен быть установлен на Raspberry Pi и может быть интегрирован в приложение Java с Maven. Полный исходный код примера вы можете найти на GitHub.

Следующие зависимости Maven указаны в файле POM:

● Для JavaFX, расширенная зависимость javafx-web, которая включает:

○ javafx-controls – основу для приложения JavaFX;

○ Веб-компоненты, необходимые для TilesFX.

● Логирование;

● TilesFX для панели инструментов Tiles;

● Pi4J для использования портов GPIO.

         <dependency>     <groupId>org.openjfx</groupId>     <artifactId>javafx-web</artifactId>     <version>11.0.2</version> </dependency>  <dependency>     <groupId>org.apache.logging.log4j</groupId>     <artifactId>log4j-core</artifactId>     <version>2.13.1</version> </dependency>  <dependency>     <groupId>eu.hansolo</groupId>     <artifactId>tilesfx</artifactId>     <version>11.13</version> </dependency>  <dependency>     <groupId>com.pi4j</groupId>     <artifactId>pi4j-core</artifactId>     <version>1.2</version> </dependency>      

Вот классы для взаимодействия с оборудованием.

Класс GpioHelper. В нем сгруппированы функции, связанные с портами GPIO. Код начинается с определения контактов, к которым подключены компоненты оборудования, и инициализации Pi4J GpioController. Расширенная функциональность обрабатывается в отдельных классах: ButtonChangeEventListener и DistanceSensorMeasurement (описаны вкратце). Дополнительные методы и геттеры будут использоваться в UI позже.

         public class GpioHelper {      private static final Logger logger = LogManager.getLogger(GpioHelper.class);      /**      * The pins being used in the example.      */     private static final Pin PIN_LED = RaspiPin.GPIO_29;        // BCM 21, Header pin 40     private static final Pin PIN_BUTTON = RaspiPin.GPIO_27;     // BCM 16, Header pin 36     private static final Pin PIN_ECHO = RaspiPin.GPIO_05;       // BCM 24, Header pin 18     private static final Pin PIN_TRIGGER = RaspiPin.GPIO_01;    // BCM 18, Header pin 12      /**      * The connected hardware components.      */     private GpioController gpioController;      /**      * The Pi4J GPIO input and outputs.      */     private GpioPinDigitalOutput led = null;      /**      * The GPIO handlers.      */     private ButtonChangeEventListener buttonChangeEventListener = null;     private DistanceSensorMeasurement distanceSensorMeasurement = null;      /**      * Constructor.      */     public GpioHelper() {         try {             // Initialize the GPIO controller             this.gpioController = GpioFactory.getInstance();              // Initialize the LED pin as a digital output pin with an initial low state             this.led = gpioController.provisionDigitalOutputPin(PIN_LED, "RED", PinState.LOW);             this.led.setShutdownOptions(true, PinState.LOW);              // Initialize the input pin with pulldown resistor             GpioPinDigitalInput button = gpioController                     .provisionDigitalInputPin(PIN_BUTTON, "Button", PinPullResistance.PULL_DOWN);              // Initialize the pins for the distance sensor and start thread             GpioPinDigitalOutput trigger = gpioController.provisionDigitalOutputPin(PIN_TRIGGER, "Trigger", PinState.LOW);             GpioPinDigitalInput echo = gpioController.provisionDigitalInputPin(PIN_ECHO, "Echo", PinPullResistance.PULL_UP);             this.distanceSensorMeasurement = new DistanceSensorMeasurement(trigger, echo);             ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();             executorService.scheduleAtFixedRate(this.distanceSensorMeasurement, 1, 1, TimeUnit.SECONDS);              // Attach an event listener             this.buttonChangeEventListener = new ButtonChangeEventListener();             button.addListener(this.buttonChangeEventListener);         } catch (UnsatisfiedLinkError | IllegalArgumentException ex) {             logger.error("Problem with Pi4J! Probably running on non-Pi-device or Pi4J not installed. Error: {}",                     ex.getMessage());         }     }      public GpioController getGpioController() {         return this.gpioController;     }      /**      * Set the state of the LED.      *      * @param on Flag true if the LED must be switched on      */     public void setLed(boolean on) {         if (this.led != null) {             if (on) {                 this.led.high();             } else {                 this.led.low();             }         }     }      /**      * Get the data from the button.      *      * @return {@link XYChart.Series}      */     public XYChart.Series<String, Number> getButtonEvents() {         if (this.buttonChangeEventListener != null) {             return this.buttonChangeEventListener.getData();         } else {             return new Series<>();         }     }      /**      * Get the data from the distance measurement.      *      * @return {@link XYChart.Series}      */     public XYChart.Series<String, Number> getDistanceMeasurements() {         if (this.distanceSensorMeasurement != null) {             return this.distanceSensorMeasurement.getData();         } else {             return new Series<>();         }     } }      

Класс ButtonChangeEventListener. Поскольку этот класс реализует Pi4J GpioPinListenerDigital, он может обрабатывать изменение кнопки и сохранять изменение в XYChart.Series с отметкой времени.

         @Override public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) {     var timeStamp = LocalTime.now().format(DateTimeFormatter.ofPattern("HH.mm.ss"));     this.data.getData().add(new XYChart.Data<>(timeStamp, event.getState().isHigh() ? 1 : 0));      logger.info("Button state changed to {}", event.getState().isHigh() ? "high" : "low"); }      

Класс DistanceSensorMeasurement. Этот класс является Runnable. Он добавляет отмеренное расстояние к аналогичной серии данных с отметкой времени для каждого пробега.

         @Override public void run() {     // Set trigger high for 0.01 ms     this.trigger.pulse(10, PinState.HIGH, true, TimeUnit.NANOSECONDS);      // Start the measurement     while (this.echo.isLow()) {         // Wait until the echo pin is high, indicating the ultrasound was sent     }     long start = System.nanoTime();      // Wait until measurement is finished     while (this.echo.isHigh()) {         // Wait until the echo pin is low,  indicating the ultrasound was received back     }     long end = System.nanoTime();      // Output the distance     float measuredSeconds = getSecondsDifference(start, end);     int distance = getDistance(measuredSeconds);     logger.info("Distance is: {}cm for {}s ", distance, measuredSeconds);      var timeStamp = new SimpleDateFormat("HH.mm.ss").format(new Date());     this.data.getData().add(new XYChart.Data<>(timeStamp, distance)); }      

UI. Благодаря TilesFX приложение в стиле дашборда может быть создано быстро. Полное построение интерфейса выполняется в классе DashboardScreen.java. В следующем фрагменте кода показана кнопка переключателя для включения и выключения светодиода:

         var ledSwitchTile = TileBuilder.create()         .skinType(SkinType.SWITCH)         .prefSize(200, 200)         .title("LED")         .roundedCorners(false)         .build();  ledSwitchTile.setOnSwitchReleased(e -> gpioHelper.setLed(ledSwitchTile.isActive()));      

Чтобы показать измерение расстояния, я использую tile типа SMOOTHED_CHART, который в свою очередь использует XYChart.Series из доступного через GpioHelper DistanceSensorMeasurement.

         var distanceChart = TileBuilder.create()         .skinType(SkinType.SMOOTHED_CHART)         .prefSize(500, 280)         .title("Distance measurement")         //.animated(true)         .smoothing(false)         .series(gpioHelper.getDistanceMeasurements())         .build();      

Класс приложения. Теперь все элементы готовы к объединению в класс приложения JavaFX. Ниже описано, как инициализировать GpioHelper и использовать его для инициализации DashboardScreen, а также есть дополнительный код, позволяющий красиво закрыть приложение.

         public class DashboardApp extends Application {      private GpioHelper gpioHelper;      @Override     public void start(Stage stage) {         Platform.setImplicitExit(true);          this.gpioHelper = new GpioHelper();          var scene = new Scene(new DashboardScreen(this.gpioHelper), 640, 480);         stage.setScene(scene);         stage.setTitle("JavaFX demo application on Raspberry Pi");         stage.show();          // Make sure the application quits completely on close         stage.setOnCloseRequest(t -> CleanExit.doExit(this.gpioHelper.getGpioController()));     }      public static void main(String[] args) {         launch();     }  }      

Запуск приложения на одноплатном компьютере Raspberry Pi

Вы можете запустить приложение на своем ПК через среду IDE, и UI отобразится, но большинство возможностей будут недоступны, потому что необходимы Pi4J и аппаратные компоненты. Перейдем к Raspberry Pi! Сначала вам нужно установить Pi4J. Это можно сделать с помощью однострочной команды:

$ curl -sSL https://pi4j.com/install | sudo bash

Финальный шаг – загрузить скомпилированный файл JAR с вашего ПК на плату Pi, что можно сделать с помощью SSH, USB-накопителя, загрузки или SD-карты. Поместите файл в /home/pi и запустите его с помощью java -jar:

         $ cd /home/pi $ ls *.jar javamagazine-javafx-example-0.0.1-jar-with-dependencies.jar $ java -jar javamagazine-javafx-example-0.0.1-jar-with-dependencies.jar INFO  be.webtechie.gpio.DistanceSensorMeasurement - Distance is: 103cm for 0.006021977s  INFO  be.webtechie.gpio.DistanceSensorMeasurement - Distance is: 265cm for 0.01544218s  INFO  be.webtechie.gpio.DistanceSensorMeasurement - Distance is: 198cm for 0.011520567s  INFO  be.webtechie.gpio.ButtonChangeEventListener - Button state changed to high INFO  be.webtechie.gpio.ButtonChangeEventListener - Button state changed to low      

Сначала на экране отобразятся логи, а чуть позже откроется экран JavaFX. Диаграмма расстояний получает новое значение каждую секунду, а диаграмма кнопки обновляется при каждом ее нажатии. Светодиод можно переключать с помощью кнопки переключения на экране в одной из плиток. (см. Рис. 11)

nachalo raboty s javafx na raspberry pi 1b7e8f0 - Начало работы с JavaFX на Raspberry Pi

Рис. 11 Фото запущенного приложения вместе с Raspberry Pi и другими компонентами

Заключение

Как только вы узнаете, какую версию Java нужно использовать на Pi, вы можете очень быстро начать работу с простыми тестовыми программами и расширить их с помощью пользовательского интерфейса JavaFX. Создание сложного приложения требует дополнительных усилий, чтобы настроить все в вашей среде IDE и иметь возможность тестового запуска. Это позволяет вам легко разрабатывать программы на ПК и выполнять их на Pi.

Текст опубликован в переводе. Автор оригинальной статьи Frank Delporte.

***

Изучение Java – непростая задача. Можно освоить необходимые знания самостоятельно, но если вы только начинаете путь в профессии, стоит обратить внимание на курс Факультета Java-разработки онлайн-университета GeekBrains. Занятия ведут опытные преподаватели, а студенты во время обучения создают проекты, которые будет не стыдно показать потенциальным работодателям. Кроме того онлайн-университет помогает выпускникам с трудоустройством.

Интересно, хочу попробовать

  • 11 views
  • 0 Comment

Leave a Reply

Ваш адрес email не будет опубликован. Обязательные поля помечены *

Этот сайт использует Akismet для борьбы со спамом. Узнайте, как обрабатываются ваши данные комментариев.

Связаться со мной
Close