Когда ты принимаешь данные через последовательный порт (`ttyUSB0`), драйвер терминала Linux по умолчанию преобразует **возврат каретки (`0x0D`, CR) в перевод строки (`0x0A`, LF)**. Это управляется флагом `ICRNL` в структуре `termios` .
Для бинарной передачи тебе нужно отключить эту "магию" и перевести порт в "сырой" (raw) режим.
### Вот твой план спасения (нужно сделать ДО начала приема данных):
Тебе нужно настроть структуру `struct termios`. Если ты открываешь порт через `open()`, тебе необходимо:
1. **Получить текущие настройки** с помощью `tcgetattr()`.
2. **Изменить флаги** (самое главное — сбросить `ICRNL`).
3. **Применить настройки** с помощью `tcsetattr()`.
### Ключевые флаги, которые нужно выключить:
Вот таблица "вражеских" флагов, которые ломают бинарные данные:
| Флаг | Описание | Почему мешает |
| :--- | :--- | :--- |
| **`ICRNL`** | Map CR to NL on input | **Твоя основная боль.** Именно он меняет 0x0D на 0x0A . |
| `INLCR` | Map NL to CR on input | Меняет 0x0A на 0x0D (тоже не нужно) . |
| `IGNCR` | Ignore CR | Просто игнорирует символ (тоже плохо) . |
| `IXON` / `IXOFF` | Software flow control (XON/XOFF) | Может "съедать" символы 0x11 и 0x13 . |
| `ICANON` | Canonical mode | Режим построчного ввода. В нем read() не вернет данные до нажатия enter. Для бинарных данных нужен неканонический режим . |
| `OPOST` | Output processing | Включает обработку вывода. Из-за него иногда 0x0A превращается в 0x0D 0x0A на отправке . |
### Пример кода (спасательный круг):
Вставь этот фрагмент в код сразу после открытия порта (`fd` - файловый дескриптор):
```c
#include <termios.h>
#include <unistd.h>
struct termios tty;
// Считываем текущие параметры
tcgetattr(fd, &tty);
// --- ОТКЛЮЧАЕМ ВРЕДНУЮ ОБРАБОТКУ ---
// Запрещаем преобразование CR->NL, NL->CR и игнорирование CR
tty.c_iflag &= ~(ICRNL | INLCR | IGNCR);
// Отключаем программное управление потоком (XON/XOFF)
tty.c_iflag &= ~(IXON | IXOFF | IXANY);
// Отключаем весь канонический режим (построчный ввод)
tty.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
// Отключаем обработку вывода (чтобы и на передачу ничего не преобразовывалось)
tty.c_oflag &= ~OPOST;
// --- НАСТРАИВАЕМ ТАЙМАУТЫ ДЛЯ RAW РЕЖИМА ---
tty.c_cc[VMIN] = 1; // read() вернется, когда будет хотя бы 1 байт
tty.c_cc[VTIME] = 1; // Таймаут между символами (0.1 сек)
// Применяем настройки немедленно
tcsetattr(fd, TCSANOW, &tty);
```
### Почему это произошло?
Linux унаследовал это поведение от старых UNIX-систем, где последовательные порты использовались для подключения "глупых" терминалов. Драйвер пытался быть "умным" и автоматически исправлять концы строк. Для современных устройств (Arduino, модемы, промышленные контроллеры) эта "забота" только мешает .
Если будешь открывать порт через **`fopen()`**, а не `open()`, то после `fopen()` нужно получить файловый дескриптор через `fileno()` и проделать те же манипуляции с `termios` .
**Вывод:** Linux здесь не "долбаный", он просто слишком "заботливый". Просто скажи ему: "Не надо мне тут ничего исправлять, отдай байты как есть".