Работа с COM-портом Arduino из Java-приложения

в 11:27, , рубрики: arduino, java, serial port, программирование микроконтроллеров, Разработка для интернета вещей

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

Недавно на просторах интернета наткнулся на весьма простую библиотеку Java-Arduino Communication Library. Не найдя публикаций на эту тему здесь, решил поделиться с вами опытом использования. Для работы нам понадобятся установленные Arduino IDE, IntelliJ IDEA, Java SE Development Kit и, собственно, сам микроконтроллер (я тестировал на китайской Arduino Nano и Strela на базе Leonardo от Амперки, на обоих все все работало отлично).

Задача проста — создадим консольное приложение, которое при запуске устанавливает Serial-соединение с микроконтроллером и в бесконечном цикле ожидает ввода строки от пользователя. В зависимости от введенной строки возможны следующие варианты:

  • «on» — микроконтроллер включает встроенный светодиод;
  • «off» — микроконтроллер выключает встроенный светодиод;
  • «exit» — микроконтроллер выключает встроенный светодиод, и приложение завершает работу.

Скетч для микроконтроллера

Построение системы начнем с написания и загрузки скетча в Arduino Nano. Ничего сверхсложного. В блоке «setup» конфигурируем пин со светодиодом и Serial-порт, а в блоке «loop» слушаем Serial-порт на предмет пришедших байтов. В зависимости от полученного значения выполняем ту или иную операцию.

Исходный код скетча

/*пин №13 связан со встроенным светодиодом на платах Uno,
 * Mega, Nano, Leonardo, Mini и др.
 */
#define LED_PIN = 13

void setup() {
  //открытие Serial-порта со скоростью 9600 бод/c
  Serial.begin(9600);

  //настройка пина со светодиодом в режим выхода
  pinMode(LED_PIN, OUTPUT);
}

void loop() {

  //если в буфере Serial-порта пришли байты (символы) и ожидают считывания
  if (Serial.available() != 0) {  
    
    //то считываем один полученный байт (символ)
    byte b = Serial.read();
    
    //если получен символ '1', то светодиод включается
    if (b == 49) digitalWrite(LED_PIN, HIGH);
    
    //если получен символ '0', то светодиод выключается
    if (b == 48) digitalWrite(LED_PIN, LOW);
}

Небольшого пояснения и внимательности требует лишь проверка условий (b == 49) и (b == 48). Если не понимаете почему так, то добро пожаловать под спойлер:

Ответ на главный вопрос жизни, вселенной и всего такого

Все дело в том, что при отправке на микроконтроллер по Serial-соединению символа (Chr) '1' используется кодировка ASCII, в которой символ '1' кодируется целочисленным десятичным значение (Dec) 49. При считывании символа микроконтроллером, значение символа '1' присваивается целочисленной переменной byte b. То есть фактически значение переменной b равно 49.

Для проверки на этом этапе можно из встроенного в Arduino IDE монитора порта отправить 1 и 0. Если светодиод на плате не включается/выключается, то ищите ошибку у себя в скетче.

Java-приложение

Теперь запустим IntelliJ IDEA и создадимм новый Java-проект. Для работы потребуется подключить две дополнительные библиотеки: jSerialComm-1.3.11.jar и arduino.jar. Как добавить скаченные jar-архивы можно прочитать вот здесь.

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

Исходный Java-код

import arduino.Arduino;
import java.util.Scanner;

public class AppMain {

    public static void main(String[] args) throws InterruptedException {

        Scanner scanner = new Scanner(System.in);
        Arduino arduino = new Arduino("COM52", 9600);

        boolean connected = arduino.openConnection();
        System.out.println("Соединение установлено: " + connected);
        Thread.sleep(2000);

        label_1:
        while (scanner.hasNext()) {

            String s = scanner.nextLine();

            switch (s) {
                case "on":
                    arduino.serialWrite('1');
                    break;
                case "off":
                    arduino.serialWrite('0');
                    break;
                case "exit":
                    arduino.serialWrite('0');
                    arduino.closeConnection();
                    break label_1;             
                default:
                    System.out.println(s + " - не является командой");
                    break;
            }
        }
    }
}

Для работы с COM портом создается объект класcа Arduino. Конструктор принимает два параметра:

  1. String portDescrition — название COM-порта
  2. int baud_rate — скорость передачи

Лучше указать эти параметры сразу в конструкторе, но можно и установить отдельно с помощью сеттеров. Название COM-порта можно посмотреть в Arduino IDE, либо в диспетчере устройств. Скорость передачи должна совпадать с той, что указана в блоке «setup» скетча для микроконтроллера, в данном случае 9600 бод/c:

void setup() {
  //открытие Serial-порта со скоростью 9600 бод/c
  Serial.begin(9600);

  //настройка пина со светодиодом в режим выхода
  pinMode(LED_PIN, OUTPUT);
}

}

Далее необходимо установить соединение с помощью метода openConnection(). Метод возвращает true в случае успешного соединения. Выведем это значение в консоль, чтобы убедиться в правильности выполненных действий.

Важно: после открытия соединения необходимо сделать паузу с помощью метода Thread.sleep(), в данном случае 2000 миллисекунд. Arduino Nano оказался настоящим тугодумом по сравнению со Strela, отправлять данные которой можно было сразу же после установки соединения. Вполне возможно, что вашему контроллеру понадобится даже больше времени. Поэтому если соединение установлено, данные отправляются, но не приходят, то первым делом увеличьте величину паузы.

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

        label_1:
        while (scanner.hasNext()) {

            String s = scanner.nextLine();

            switch (s) {
                case "on":
                    arduino.serialWrite('1');
                    break;
                case "off":
                    arduino.serialWrite('0');
                    break;
                case "exit":
                    arduino.serialWrite('0');
                    arduino.closeConnection();
                    break label_1;             
                default:
                    System.out.println(s + " - не является командой");
                    break;
            }
        }

При введении очередной строки и нажатии «enter» выполняется ее чтение и сохранение в переменную String s. В зависимости от значения этой строки оператор switch отсылает на микроконтроллер символ '1' или '0' с помощью метода serialWrite(char c). Не забывайте, что когда микроконтроллер получит эти символы и сохранит их в целочисленную переменную, то вы получите 49, либо 48).

Вообще для пересылки данных можно использовать следующие перегруженные методы класса Arduino:

  1. public void serialWrite(String s);
  2. public void serialWrite(char c);
  3. public void serialWrite(String s,int noOfChars, int delay);
  4. public void serialWrite(char c, int delay);

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

При завершении программы желательно закрыть COM-port с помощью метода close.connection(), чтобы при повторном запуске программы не получить ошибку, связанную с тем, что COM-порт прежнему занят, а для выхода из бесконечного цикла, ожидающего ввод строки, используйте оператор break c указанием метки label_1, который позволяет выйти из цикла, перед которым стоит соответствующая метка:

case "exit":
     arduino.serialWrite('0');
     arduino.closeConnection();
     break label_1;                    

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

Автор: CodeRunner

Источник


  1. Юрий:

    Прошло почти 2а года , вряд ли кто ответит, но всё же.
    Я использую эту связку Java – Arduino и статью нашёл во время. – СПАСИБО.
    Если можно – вопросик. /я в Jave не очень – так на уровне поделок/
    Подскажите как реализовать задачу.:
    приложение у меня открывает единственную форму в которой пользователь
    вводит данные, запускает вычисления, запускает управление механикой из Ардуино, с помощью выше описанной библиотеки.
    Мне надо независимо от происходящего в приложении считать значение порта Ардуино – arduino.serialRead(), и присвоить значение(текст) метке на форме. Так как чтение ни с чем не синхронизировано то надо вероятно использовать прерывание по приходу данных от ардуино в порт. Подскажите как реализовать в Jave. Спасибо, за ответ.

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js