---
title: "📟 FAQ по доступу к Serial из Crystal"
date: 2023-05-16T01:29:30+03:00
draft: false
tags: [crystal, serial, tips]
---

Перевёл FAQ чувака с SOF, на вопрос работы с Serial в Crystal.

## Как мне получить доступ к Serial в Linux/BSD?

Открыть его как файл.
В Linux/BSD последовательное соединение устанавливается в тот момент,
когда устройство было подключено,
и затем отображается в разделе `/dev/` (обычно как `/dev/ttyUSB0`).
Чтобы получить доступ к этому соединению, просто откройте его как обычный файл.
Иногда этого достаточно, чтобы начать взаимодействовать с устройством,
поскольку современное оборудование работает со всеми скоростями и флагами по умолчанию.

## Как мне настроить устройство Serial/tty в Linux/BSD?

Необходимо установить флаги [termios](https://linux.die.net/man/3/termios) для файла.
Если необходимо для соединения установить такие параметры,
как скорость передачи данных в бодах, IXON/IXOFF и т.д.,
это можно сделать еще до запуска вашей программы с помощью команды
[stty](https://linux.die.net/man/1/stty), если она доступна.
Например чтобы установить скорость передачи данных в бодах,
необходимо выполнить следующую команду: `stty -F /dev/ttyUSB0 9600`.
И после того, как соединение настроено,
можно просто открыть соединение как файл и начать использовать.

Вы можете запустить `stty` из Crystal с помощью
`Process.run`, если нужен простой способ настройки устройства из вашего приложения.
Я бы, вероятно, порекомендовал этот способо в качестве решения.

## Как мне установить флаги termios из Crystal, не используя stty?

Используйте функции `termios` напрямую.
Crystal предоставляет FileDescriptor с несколькими
распространенными настройками `termios`, такими как
[cooked](https://crystal-lang.org/api/1.8.2/IO/FileDescriptor.html#cooked%28%26%3Aself-%3E_%29-instance-method),
что означает, наличие минимальных привязок
[termios](https://github.com/crystal-lang/crystal/blob/master/src/termios.cr).

Мы можем начать с использования существующего кода:

```crystal
require "termios"

# Открываем файл
serial_file = File.open("/dev/ttyACM0")
raise "Oh no, not a TTY" unless serial_file.tty?

# Извлекаем unix FD. Это всего лишь цифра.
fd = serial_file.fd

# Извлекаем существующие флаги TTY файла
raise "Can't access TTY?" unless LibC.tcgetattr(fd, out mode) == 0

# `mode` теперь содержит структуру termios. Давайте включим, ммм.. ISTRIP and IXON
mode.c_iflag |= (Termios::InputMode::ISTRIP | Termios::InputMode::IXON).value
# Давайте также отключим IXOFF.
mode.c_iflag &= ~Termios::InputMode::IXOFF.value

# Обнаружение неисправности: в Termios недоступна скорость cfset[ввода-вывода]
# Давайте добавим их, чтобы изменить скорость передачи данных в бодах было не так сложно.
lib LibC
  fun cfsetispeed(termios_p : Termios*, speed : SpeedT) : Int
  fun cfsetospeed(termios_p : Termios*, speed : SpeedT) : Int
end

# Используйте приведенные выше функции, чтобы установить скорость ввода
# и скорость вывода на заданную вами скорость передачи данных в бодах.
LibC.cfsetispeed(pointerof(mode), Termios::BaudRate::B9600)
LibC.cfsetospeed(pointerof(mode), Termios::BaudRate::B9600)
# Пишите свои изменения в FDA.
LibC.tcsetattr(fd, Termios::LineControl::TCSANOW, pointerof(mode))

# Сделано! Ваш дескриптор serial_file готов к использованию.
```

Чтобы установить другие флаги, обратитесь
к [руководству](https://linux.die.net/man/3/termios) termios
или к этому замечательному
[руководству](https://en.wikibooks.org/wiki/Serial_Programming/termios) по Serial,
которое я только что нашел.

## Есть ли библиотека, которая могла бы сделать все это за меня?

Нет :(. Но было бы здорово, если бы кто-нибудь написал.
Вероятно, что написать библиотеку не составит большого труда,
если у кого-нибудь будет личный интерес :)

## Источник
- [Crystal-lang Accessing Serial port](https://stackoverflow.com/questions/51069578/crystal-lang-accessing-serial-port) — StackOverFlow.
