Основы · 10 мин чтения

Что такое UART? Подключение TX/RX, скорость в бодах и Serial на Arduino

Что такое UART и как он работает? Перекрёстное подключение TX/RX, скорость в бодах, стартовые и стоповые биты, связь двух Arduino, SoftwareSerial, уровни 5V/3.3V, Serial2 на ESP32 и UART на Pico.

Краткий ответ на вопрос «Что такое UART?»: UART (Universal Asynchronous Receiver-Transmitter, универсальный асинхронный приёмопередатчик) — аппаратный узел последовательной связи, который позволяет двум устройствам обмениваться данными побайтно всего по двум сигнальным проводам (TX и RX) с общей землёй GND. Каждая строка, которую ты видишь в мониторе порта, когда Arduino подключена к компьютеру, поступает через UART. Десятки модулей, например GPS-приёмники, Bluetooth-модуль HC-05, Wi-Fi-модуль ESP-01, GSM-модуль SIM800L, сканеры отпечатков пальцев и MP3-плееры, тоже общаются с Arduino по UART.

В этой статье мы пошагово разберём, почему UART не нужна тактовая линия, что такое скорость в бодах, как байт оформляется стартовым битом, битами данных и стоповым битом, зачем перекрёстно подключают TX и RX, как наладить сначала одностороннюю, а затем двустороннюю связь двух Arduino, каковы ограничения SoftwareSerial, как безопасно соединять платы на 5 V и 3,3 V, а также три UART у ESP32 и пины UART у Raspberry Pi Pico. В последнем разделе ты узнаешь, как формировать пакеты с начальным и конечным символами, чтобы данные не смешивались, и разберёшь самые частые ошибки.

Преобразователь USB-TTL CP2102
Преобразователь USB-TTL CP2102: превращает USB-порт компьютера в UART с пинами TX, RX и GND; USB-микросхема на Arduino Uno делает то же самое.

Что такое UART и как он работает?

Ключевое слово в названии UART — «асинхронный»: в I2C и SPI вместе с данными передаётся тактовый сигнал, а у UART тактовой линии нет. Вместо этого обе стороны заранее договариваются о скорости — скорости в бодах. В состоянии покоя линия находится в HIGH (1). Чтобы отправить байт, передатчик переводит линию в LOW на время одного бита; это стартовый бит, который сообщает приёмнику: «Начинай отсчёт». Затем идут 8 бит данных, начиная с младшего значащего бита (LSB), за ними может следовать необязательный бит чётности, и наконец линия удерживается в HIGH не менее одного битового интервала; это стоповый бит. Настройка Arduino по умолчанию — 8N1: 8 бит данных, без чётности, 1 стоповый бит.

БитСтартD0D1D2D3D4D5D6D7Стоп
Буква «A» (0x41 = 0100 0001)0100000101
Уровень линииLOWHIGHLOWLOWLOWLOWLOWHIGHLOWHIGH

Каждый бит длится 1 / baud секунды. При 9600 бод на бит приходится 104,2 микросекунды; поскольку кадр 8N1 содержит 10 бит на байт (старт + 8 бит данных + стоп), можно передать максимум 960 байт в секунду. Обнаружив спадающий фронт стартового бита, приёмник считывает уровень линии в середине каждого бита. Даже если частоты двух генераторов немного различаются, данные читаются правильно, пока накопленная за 10 бит ошибка меньше половины бита; поэтому суммарная ошибка скорости должна оставаться примерно ниже 2–3%. Arduino Uno с кварцевым резонатором на 16 MHz не может точно обеспечить 115200 бод, и реальная скорость отличается примерно на 2,1%; связь всё равно работает, поскольку это в допустимых пределах.

Скорость, бодДлительность битаМаксимум байт в секунду (8N1)Где используется
9600104,2 µs960NEO-6M GPS, режим передачи данных HC-05, DFPlayer Mini, классическая настройка монитора порта
1920052,1 µs1920Некоторые датчики и старые модемы
3840026,0 µs3840Режим AT-команд HC-05
5760017,4 µs5760Старые загрузчики Arduino, верхний предел для SoftwareSerial
1152008,68 µs11 520ESP32 и ESP-01, быстрый отладочный вывод
1000000 и выше1 µs или меньше100 000+Короткие соединения между платами, быстрая регистрация данных

Сравнение UART, I2C и SPI

Три самых распространённых способа последовательной связи на Arduino — UART, I2C и SPI. UART работает только между двумя устройствами, один к одному, и обе стороны могут передавать данные в любой момент (полный дуплекс). I2C объединяет десятки устройств на двух общих проводах с помощью адресов; SPI гораздо быстрее и использует четыре провода, но требует отдельного пина выбора для каждого устройства. Подробности — в статьях «Что такое I2C» и «Что такое SPI».

ХарактеристикаUARTI2CSPI
Провода2 (TX, RX) + GND2 (SDA, SCL) + GND4 (SCK, MOSI, MISO, CS) + GND
Тактовая линияНет (асинхронный)ДаДа
Число устройств2 («точка-точка»)До 127, по адресамПо числу пинов CS
Типичная скорость9600–115200 бод100–400 kHz1–80 MHz
Оба направления одновременноДа (полный дуплекс)НетДа
Типичные модулиGPS, Bluetooth, GSM, ESP-01, сканер отпечатков пальцевOLED, LCD, RTC, BME280SD-карта, RFID, TFT, LoRa

Перекрёстное подключение TX и RX и общая земля GND

Золотое правило подключения UART: пин TX (передача) одного устройства подключается к пину RX (приём) другого, то есть провода перекрещиваются. Представь связь рта и уха: когда один говорит, другой должен слушать. Если соединить TX с TX, два выхода окажутся соединены, и данные не пройдут. Третий провод, GND, задаёт обеим платам общую точку отсчёта напряжения; без него «HIGH» и «LOW» теряют смысл, а входящие данные превращаются в мусорные символы. Даже при питании плат от разных источников их земли нужно соединить.

Arduino AПроводArduino BПримечание
TX (D11, SoftwareSerial)→RX (D10, SoftwareSerial)Передаваемые A данные поступают к B
RX (D10, SoftwareSerial)←TX (D11, SoftwareSerial)Ответ B поступает к A
GND—GNDОбщая земля, обязательна
5VНеобязательно5VТолько если по USB питается лишь одна плата
На Arduino Uno и Nano пины аппаратного UART D0 (RX) и D1 (TX) также подключены к USB-микросхеме. Если загружать код с модулем, подключённым к этим пинам, загрузка завершится ошибкой «not in sync», а в мониторе порта данные модуля будут смешиваться с данными компьютера. Либо отключай провода D0/D1 на время загрузки, либо используй другие пины с SoftwareSerial, как в примерах этой статьи.

Связь двух Arduino: в одну сторону

Arduino Uno
Arduino Uno: поскольку её единственный аппаратный UART (D0/D1) занят USB, проще всего соединить две Uno через SoftwareSerial на D10/D11.

В первом примере плата A десять раз в секунду передаёт плате B значение потенциометра на A0, а плата B соответственно регулирует яркость светодиода на D9. Обе платы используют SoftwareSerial на D10 (RX) и D11 (TX), оставляя USB-соединение и монитор порта свободными. Поскольку значения отправляются через println(), каждое число завершается символами «\r\n» (конец строки); по ним приёмник понимает, что число передано полностью. Подключение: D11 платы A к D10 платы B, D10 платы A к D11 платы B и земли вместе.

C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11 (diğer karta / to the other board)
4
5void setup() {
6  Serial.begin(9600);           // USB: bilgisayar / computer
7  link.begin(9600);             // iki tarafta aynı baud / same baud on both sides
8}
9
10void loop() {
11  int deger = analogRead(A0);   // potansiyometre 0-1023 / potentiometer 0-1023
12  link.println(deger);          // ör. "512\r\n" / e.g. "512\r\n"
13  Serial.println(deger);
14  delay(100);                   // saniyede 10 ölçüm / 10 readings per second
15}
16
C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11
4const int LED_PIN = 9;          // PWM pini / PWM pin
5
6char tampon[8];                 // en fazla 7 karakter + '\0' / up to 7 chars + '\0'
7byte konum = 0;
8
9void setup() {
10  Serial.begin(9600);
11  link.begin(9600);
12  pinMode(LED_PIN, OUTPUT);
13}
14
15void loop() {
16  while (link.available()) {               // gelen her baytı oku / read every incoming byte
17    char c = link.read();
18    if (c == '\n') {                       // satır bitti / line complete
19      tampon[konum] = '\0';
20      int deger = atoi(tampon);
21      analogWrite(LED_PIN, map(deger, 0, 1023, 0, 255));
22      Serial.println(deger);
23      konum = 0;
24    } else if (c != '\r' && konum < sizeof(tampon) - 1) {
25      tampon[konum++] = c;                 // satıra ekle / append to the line
26    }
27  }
28}
29

В коде приёмника обрати внимание на две вещи. Во-первых, данные никогда не приходят «целиком»: «512» поступает как три отдельных байта с промежутками примерно в 1 миллисекунду. Поэтому мы собираем байты в буфере и преобразуем их в число, когда приходит конец строки. Во-вторых, код никогда не ждёт: функции вроде readString() или parseInt() при отсутствии данных по умолчанию ждут 1 секунду и замедляют программу. Читать байты по мере поступления — самый надёжный и быстрый подход. Мы также ограничиваем размер буфера, чтобы повреждённые данные не записывались за пределы массива.

Двусторонняя связь: команда и ответ

В реальных проектах обычно нужен двусторонний обмен: одна плата отправляет команду, другая выполняет её и отвечает. В примере ниже плата A работает как мост: пересылает B каждый символ, введённый в мониторе порта, и выводит каждый символ, поступающий от B. Плата B понимает команды «LED ON», «LED OFF» и «DURUM?» (состояние), отвечает на каждую и также самостоятельно сообщает о нажатии кнопки на D2. Не забудь выбрать конец строки Newline («Новая строка») в мониторе порта; по этому символу B определяет завершение команды.

C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11
4
5void setup() {
6  Serial.begin(9600);
7  link.begin(9600);
8  Serial.println("Komut yaz / type a command: LED ON, LED OFF, DURUM?");
9}
10
11void loop() {
12  if (Serial.available()) link.write(Serial.read());   // bilgisayar -> B kartı / computer -> board B
13  if (link.available()) Serial.write(link.read());     // B kartı -> bilgisayar / board B -> computer
14}
15
C++ (Arduino IDE)
1#include <SoftwareSerial.h>
2
3SoftwareSerial link(10, 11);    // RX = 10, TX = 11
4const int LED_PIN = 13;         // kart üstündeki LED / on-board LED
5const int BUTON_PIN = 2;        // buton GND'ye / button to GND
6
7String satir = "";
8int oncekiButon = HIGH;
9
10void komutCalistir(String k) {
11  k.trim();                                  // \r ve boşlukları at / strip \r and spaces
12  if (k == "LED ON") {
13    digitalWrite(LED_PIN, HIGH);
14    link.println("OK LED=1");
15  } else if (k == "LED OFF") {
16    digitalWrite(LED_PIN, LOW);
17    link.println("OK LED=0");
18  } else if (k == "DURUM?") {                // durum sorgusu / status query
19    link.print("LED=");
20    link.print(digitalRead(LED_PIN));
21    link.print(" BUTON=");
22    link.println(digitalRead(BUTON_PIN) == LOW ? 1 : 0);
23  } else {
24    link.print("HATA / unknown: ");
25    link.println(k);
26  }
27}
28
29void setup() {
30  link.begin(9600);
31  pinMode(LED_PIN, OUTPUT);
32  pinMode(BUTON_PIN, INPUT_PULLUP);
33  satir.reserve(32);
34}
35
36void loop() {
37  while (link.available()) {
38    char c = link.read();
39    if (c == '\n') {
40      komutCalistir(satir);
41      satir = "";
42    } else if (satir.length() < 31) {
43      satir += c;
44    }
45  }
46
47  int buton = digitalRead(BUTON_PIN);        // değişince kendiliğinden bildir / report changes unprompted
48  if (buton != oncekiButon) {
49    oncekiButon = buton;
50    link.println(buton == LOW ? "BUTON BASILDI / pressed" : "BUTON BIRAKILDI / released");
51    delay(20);                               // basit debounce / simple debounce
52  }
53}
54

Вызов trim() на плате B удаляет символ \r, который приходит при настройке монитора порта «Both NL & CR», и лишние пробелы, поэтому команды распознаются при обеих настройках. Вызов reserve(32) для объекта String предотвращает повторное выделение памяти при каждом добавлении символа и уменьшает фрагментацию 2 КБ ОЗУ Uno. Эта структура также лежит в основе обмена с Arduino из программы на Python на компьютере; подробности — в статье «Последовательная связь Arduino с Python».

Ограничения SoftwareSerial

SoftwareSerial имитирует UART программно вместо аппаратной реализации: процессор самостоятельно отсчитывает время каждого бита. Это очень удобно, но имеет свою цену. При передаче прерывания отключаются на всё время каждого байта; при 9600 бод это около 1 миллисекунды на байт, в течение которой millis() может отставать, импульсы сервопривода — дрожать, а данные другого последовательного порта — теряться. Одновременно принимать данные может только один порт SoftwareSerial; если открыть второй, нужно выбрать принимающий порт через listen(). На платах вроде Mega и Leonardo для RX подходят только определённые пины с поддержкой прерываний.

ХарактеристикаАппаратный UART (Serial)SoftwareSerialAltSoftSerial
Пины на UnoD0 (RX), D1 (TX), общие с USBПочти любой пин (PCINT для RX)Фиксированные: D8 (RX), D9 (TX)
Максимальная надёжная скорость1–2 Мбод57600 бод (115200 — на пределе)Стабилен на умеренных скоростях (9600–38400)
Нагрузка на процессорОчень низкая (аппаратный буфер)Высокая, при передаче прерывания отключеныСредняя, использует Timer1
Одновременная передача и приёмДаНетДа
Несколько портов4 на MegaОдновременно принимает только одинОдин порт
КонфликтыЗагрузка и монитор портаЧувствительные к таймингам библиотеки, например Servo и NeoPixelШИМ на D10 и Servo (Timer1)
Если планируешь использовать несколько модулей UART (например, GPS + Bluetooth), выбери Arduino Mega с четырьмя аппаратными UART (Serial1–Serial3 помимо Serial) или ESP32 с тремя. На Leonardo и Pro Micro USB отделён, поэтому Serial1 на D0/D1 полностью свободен.

Согласование уровней 5V и 3.3V

В UART уровень «HIGH» равен рабочему напряжению платы: 5 V у Arduino Uno, 3,3 V у ESP32, Raspberry Pi Pico, ESP-01 и большинства современных модулей. Если напрямую подключить выход TX на 5 V ко входу RX на 3,3 V, вход получит перенапряжение, которое повредит пин сразу или со временем; ESP32 и Pico на RP2040 не выдерживают 5 V. Обратное направление обычно работает: Uno воспринимает 3,0 V и выше как HIGH, поэтому читает TX с уровнем 3,3 V, но это близко к порогу, и на длинных проводах преобразователь уровней надёжнее.

СоединениеЧто нужноПример
Uno TX (5 V) → ESP32 / Pico RX (3,3 V)Делитель напряжения или преобразователь уровней1 kΩ последовательно + 2 kΩ к GND: 5 × 2/3 ≈ 3,3 V
ESP32 / Pico TX (3,3 V) → Uno RX (5 V)Обычно напрямуюUno воспринимает напряжение выше 3,0 V как HIGH
Uno TX → HC-05 RXРекомендуется делитель напряженияВход RX у HC-05 рассчитан на уровень 3,3 V
Uno TX → SIM800L RXДелитель напряженияУровень сигнала SIM800L — около 2,8 V
Обе платы на 5 V или обе на 3,3 VНапрямуюUno ↔ Nano, ESP32 ↔ Pico
Делитель напряжения подходит только для однонаправленных сигналов низкой и средней скорости (например, до 115200 бод). Для двунаправленных или очень быстрых линий используй двунаправленный преобразователь логических уровней на BSS138. Номиналы резисторов можно рассчитать в утилите делителя напряжения; преобразователи уровней подробно разобраны в статье «Преобразователь логических уровней».

UART на ESP32: Serial, Serial1 и Serial2

У ESP32 есть три аппаратных UART, и самое удобное — благодаря матрице GPIO их линии можно переназначить почти на любые пины. UART0 (Serial) подключён к USB-UART-преобразователю и используется монитором порта. UART2 (Serial2) по умолчанию выведен на GPIO 16 (RX) и 17 (TX). Старые пины UART1 по умолчанию (GPIO 9 и 10) подключены к флеш-памяти платы и не подходят для прямого использования, поэтому всегда запускай Serial1 с указанием пинов: begin(baud, SERIAL_8N1, RX, TX). Код ниже создаёт двусторонний мост между монитором порта и устройством на UART2 (GPS, другой Arduino, датчиком); он идеально подходит для просмотра предложений NMEA от GPS-модуля.

UARTИмя в ArduinoRX / TX по умолчаниюПримечание
UART0SerialGPIO 3 / GPIO 1Подключён к USB-UART-преобразователю, используется для загрузки и монитора порта
UART1Serial1GPIO 9 / GPIO 10 (флеш-память, не использовать)Укажи другие пины в begin(), например 25 / 26
UART2Serial2GPIO 16 / GPIO 17На платах WROVER пины 16/17 заняты PSRAM, выбери другие
C++ (Arduino IDE)
1// ESP32: UART0 = Serial (USB), UART2 = Serial2 (GPIO 16/17)
2const int RXD2 = 16;            // karşı cihazın TX'i buraya / the other device's TX goes here
3const int TXD2 = 17;            // karşı cihazın RX'ine / to the other device's RX
4
5void setup() {
6  Serial.begin(115200);                          // USB Seri Monitör / USB Serial Monitor
7  Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2);   // baud, çerçeve, RX, TX / baud, frame, RX, TX
8  // UART1'i de istediğin pinlere taşıyabilirsin / UART1 can be moved to any free pins:
9  // Serial1.begin(115200, SERIAL_8N1, 25, 26);
10  Serial.println("UART2 hazir / ready");
11}
12
13void loop() {
14  while (Serial2.available()) Serial.write(Serial2.read());   // UART2 -> USB
15  while (Serial.available()) Serial2.write(Serial.read());    // USB -> UART2
16}
17

UART у ESP32 работают на скорости до 5 Мбод, и у каждого есть аппаратный буфер; приёмный буфер по умолчанию составляет 256 байт, его можно увеличить до begin() вызовом вроде Serial2.setRxBufferSize(1024). Это важно для GPS-модулей, передающих десятки предложений в секунду, или GSM-модулей с длинными ответами. Поскольку пины ESP32 работают на 3,3 V, при подключении Arduino на 5 V не забудь делитель из предыдущего раздела. Распиновку ESP32 можно посмотреть в утилите распиновки.

Пины UART Raspberry Pi Pico и MicroPython

У Raspberry Pi Pico два аппаратных UART (uart0 и uart1), каждый можно вывести на несколько разных пар пинов. В MicroPython REPL и вывод print() работают через отдельный виртуальный последовательный порт по USB, поэтому UART0 полностью свободен; отключать провода при загрузке кода, как на Arduino, не нужно. Пины Pico работают на 3,3 V, а у Pico на RP2040 не выдерживают 5 V. Код ниже выполняет на Pico те же задачи, что плата B на Arduino: читает строки из UART0, включает и выключает встроенный светодиод командами «LED ON» и «LED OFF» и отвечает. Если хочешь связать его со скетчем моста на Uno, не забудь делитель напряжения на линии TX Uno.

UARTПины TXПины RXПример MicroPython
uart0GP0, GP12, GP16GP1, GP13, GP17UART(0, 9600, tx=Pin(0), rx=Pin(1))
uart1GP4, GP8GP5, GP9UART(1, 9600, tx=Pin(4), rx=Pin(5))
MicroPython
1from machine import UART, Pin
2import time
3
4uart = UART(0, baudrate=9600, tx=Pin(0), rx=Pin(1))   # UART0: GP0 = TX, GP1 = RX
5led = Pin("LED", Pin.OUT)                             # Pico ve Pico W dahili LED / on-board LED
6
7tampon = b""
8while True:
9    if uart.any():
10        tampon += uart.read()                         # gelen baytlar / incoming bytes
11        while b"\n" in tampon:                        # her tam satırı işle / handle each full line
12            satir, tampon = tampon.split(b"\n", 1)
13            try:
14                komut = satir.decode().strip()
15            except UnicodeError:
16                continue                              # bozuk bayt: atla / corrupt byte: skip
17            if komut == "LED ON":
18                led.on()
19                uart.write("OK LED=1\n")
20            elif komut == "LED OFF":
21                led.off()
22                uart.write("OK LED=0\n")
23            else:
24                uart.write("HATA / unknown: " + komut + "\n")
25    if len(tampon) > 64:                              # satır sonu hiç gelmezse taşmayı önle / avoid overflow
26        tampon = b""
27    time.sleep_ms(5)
28

Формирование пакетов: начальный и конечный символы

UART — поток байтов; он не знает, где что начинается и заканчивается. Когда передатчик отправляет «23.5,48,812», приёмник может начать слушать с середины, байт может потеряться из-за помех, или два измерения могут слиться. Решение — оформить данные в пакет: если каждый пакет начинается с «<» и заканчивается «>», приёмник игнорирует всё до первого «<», обрабатывает пакет при поступлении «>» и отбрасывает неполные пакеты между ними. Передатчику нужно лишь вывести значения между Serial.print('<'), запятыми и Serial.print('>'). Код приёмника ниже можно сразу проверить, введя <23.5,48,812> в монитор порта.

C++ (Arduino IDE)
1// Paket biçimi / packet format: <sicaklik,nem,isik>  ör. / e.g. <23.5,48,812>
2const byte MAKS = 32;
3char tampon[MAKS];
4byte konum = 0;
5bool paketIcinde = false;                    // '<' görüldü mü? / have we seen '<'?
6
7void paketiIsle(char* p) {
8  char* parca = strtok(p, ",");              // ilk alan / first field
9  if (!parca) return;
10  float sicaklik = atof(parca);
11  parca = strtok(NULL, ",");
12  if (!parca) return;                        // eksik alan: paketi at / missing field: drop
13  int nem = atoi(parca);
14  parca = strtok(NULL, ",");
15  if (!parca) return;
16  int isik = atoi(parca);
17
18  Serial.print("Sicaklik / temp: ");
19  Serial.print(sicaklik, 1);
20  Serial.print("  Nem / humidity: ");
21  Serial.print(nem);
22  Serial.print("  Isik / light: ");
23  Serial.println(isik);
24}
25
26void setup() {
27  Serial.begin(9600);                        // Seri Monitör'den <23.5,48,812> yazarak dene / test from the Serial Monitor
28}
29
30void loop() {
31  while (Serial.available()) {
32    char c = Serial.read();
33    if (c == '<') {                          // yeni paket başladı / a new packet starts
34      paketIcinde = true;
35      konum = 0;
36    } else if (c == '>' && paketIcinde) {    // paket bitti / packet complete
37      tampon[konum] = '\0';
38      paketIcinde = false;
39      paketiIsle(tampon);
40    } else if (paketIcinde) {
41      if (konum < MAKS - 1) tampon[konum++] = c;
42      else paketIcinde = false;              // çok uzun, bozuk: at / too long, corrupt: drop
43    }                                        // paket dışındaki baytlar yok sayılır / bytes outside packets are ignored
44  }
45}
46
МетодПримерПлюсыМинусы
Текст с концом строки512\nСамый простой, читается в мониторе портаПо неполной строке нельзя определить её начало
Начальный и конечный символы<23.5,48,812>Самостоятельно восстанавливается после потери синхронизацииНельзя использовать < и > внутри данных
Текст с контрольной суммой (как NMEA)$GPGGA,...*47Повреждённые пакеты обнаруживаютсяЧуть больше вычислений и кода
Двоичный пакет0xAA, длина, данные, CRCСамый короткий и быстрыйТрудно читать и отлаживать

При помехах или длинных проводах можно пойти дальше и добавить к пакету контрольную сумму. Хороший пример — формат NMEA, используемый GPS-модулями: все символы между «$» и «*» объединяются операцией XOR, а результат записывается в конце как двузначное шестнадцатеричное число. Приёмник выполняет такой же расчёт и отбрасывает пакет, если результат не совпадает. Это защищает от незаметных ошибок, например когда повреждённое значение «812» обрабатывается как «872».

Передатчик не должен отправлять данные быстрее, чем приёмник успевает их обрабатывать. Аппаратный приёмный буфер Arduino Uno вмещает всего 64 байта; если использовать длительные вызовы delay() в loop(), поступающие байты потеряются после заполнения буфера. На приёмнике используй millis() вместо delay() и проверяй Serial.available() на каждом проходе цикла.

Идеи проектов с UART

  • Управление с телефона: Bluetooth-модуль HC-05 по сути является беспроводным UART; управляй светодиодом, реле или робомашинкой с телефона через HC-05.
  • Запись GPS-координат: разбирай предложения NMEA, которые модуль NEO-6M GPS отправляет по UART, и записывай координаты на SD-карту.
  • Система из двух плат: соедини по UART Uno, считывающую датчики, с ESP32, которая отправляет данные в интернет через Wi-Fi.
  • SMS-сигнализация: отправляй AT-команды модулю SIM800L по UART и получай SMS при открытии двери.
  • Панель управления на компьютере: отправляй команды Arduino из настольного приложения на Python и строй графики данных датчиков.
  • MP3-плеер: выбирай треки, регулируй громкость и воспроизводи голосовые оповещения по UART с помощью DFPlayer Mini.

Частые ошибки и их решение

СимптомВероятная причинаРешение
Мусорные символы в мониторе портаСкорости в бодах не совпадаютВыбери одинаковую скорость в коде и мониторе порта
Данные вообще не поступаютTX подключён к TX, RX — к RXПодключи провода перекрёстно: TX → RX, RX → TX
Случайные искажённые символыGND не подключёнСоедини земли обеих плат
Ошибка загрузки (not in sync)К D0/D1 подключён модульОтключи провода на время загрузки или используй SoftwareSerial
Команды не распознаютсяКонец строки — \r\n или отсутствуетВыбери Newline в мониторе порта и очищай текст через trim()
Конец длинных сообщений теряетсяПереполнен буфер на 64 байтаИзбегай delay(), читай данные по мере поступления, снизь скорость
Пин RX ESP32 не работает или повреждёнTX с уровнем 5 V подключён напрямуюДобавь делитель напряжения или преобразователь уровней
SoftwareSerial даёт ошибки на 115200Программный отсчёт времени не успеваетИспользуй 57600 или ниже либо перейди на аппаратный UART
Если модуль UART не отвечает, сначала подключи его напрямую к компьютеру через преобразователь USB-TTL и отправь команды из последовательного терминала. Если модуль там работает, проблема в коде Arduino или подключении; если не работает и там, проверь скорость в бодах, питание и сам модуль.

UART — первый способ связи, который стоит освоить в электронике: достаточно двух проводов, общей земли GND и одинаковой скорости в бодах; при правильной настройке он работает годами без проблем. Освоив перекрёстное подключение, согласование уровней и формирование пакетов, ты сможешь легко использовать десятки модулей — от GPS до GSM. В приложении BlueGrays есть пошаговые проекты со схемами и кодом: односторонняя и двусторонняя связь между двумя Arduino, включение светодиода с телефона по Bluetooth и машинка с управлением по Bluetooth.

Частые вопросы

Как расшифровывается UART и для чего он нужен?

UART расшифровывается как Universal Asynchronous Receiver-Transmitter — универсальный асинхронный приёмопередатчик. Он позволяет двум устройствам обмениваться данными побайтно без тактовой линии, используя только провода TX и RX и заранее согласованную скорость в бодах. Монитор порта Arduino, модули GPS, Bluetooth и GSM используют UART.

Как подключить TX и RX?

Пин TX одного устройства подключается к RX другого, а RX — к TX другого, то есть соединение перекрёстное. Пины GND обоих устройств тоже обязательно нужно соединить. Подключение TX к TX полностью блокирует передачу данных.

Что такое скорость в бодах и какую выбрать?

Скорость в бодах — число передаваемых бит в секунду, и она должна совпадать с обеих сторон. Для Arduino и модулей наиболее распространены значения 9600 и 115200; 9600 — скорость по умолчанию у большинства GPS- и Bluetooth-модулей, а 115200 предпочитают для ESP32 и быстрого вывода. Начни со скорости по умолчанию, указанной в документации модуля.

UART и последовательный порт — одно и то же?

В повседневной речи они часто означают одно и то же. UART — аппаратный узел, преобразующий данные в последовательную форму; последовательный порт — соединение COM или /dev/tty на компьютере. RS-232 и RS-485 — стандарты, использующие другие уровни напряжения для передачи сигналов UART на большие расстояния.

Как использовать несколько UART на Arduino?

У Uno один аппаратный UART, общий с USB; для дополнительных портов используют SoftwareSerial, но одновременно принимать данные может только один из них. У Arduino Mega три дополнительных аппаратных UART (Serial1, Serial2 и Serial3), у ESP32 всего три, а у Raspberry Pi Pico — два.

Какой длины может быть кабель UART?

UART с уровнями TTL 3,3 V или 5 V надёжно работает на расстоянии до нескольких метров при низких скоростях в бодах; с ростом скорости расстояние сокращается. Для более длинных линий приёмопередатчики RS-485 (например, MAX485) обеспечивают сотни метров, а RS-232 — около 15 метров.

Все проекты
Односторонняя связь между двумя Arduino
#57Средний
Premium

Односторонняя связь между двумя Arduino

Ты заставляешь две Arduino разговаривать по одному проводу: нажми кнопку на одной, и на другой загорится светодиод, нажми снова — погаснет. Плата-передатчик сообщает о нажатии кнопки коротким сообщени

4.4(37)
50
Двусторонняя связь между двумя Arduino
#58Средний
Premium

Двусторонняя связь между двумя Arduino

Две Arduino соединены проводами и разговаривают друг с другом: нажми кнопку на одной, и на другой загорится свет, нажми снова — погаснет. На обеих платах одна и та же программа, поэтому каждая и отпра

4.5(20)
26
Управление светодиодом с телефона по Bluetooth
#34Новичок
Premium

Управление светодиодом с телефона по Bluetooth

Небольшая репетиция того, как включать и выключать свет в комнате с телефона. Bluetooth-модуль передаёт в Arduino буквы, отправленные с телефона; одна буква включает красный светодиод, другая — зелёны

4.8(68)
89
Машинка на L298N с управлением по Bluetooth
#80Продвинутый
Premium

Машинка на L298N с управлением по Bluetooth

Преврати телефон в пульт и управляй своей машинкой-роботом. Arduino получает команды из приложения-пульта на телефоне через Bluetooth-модуль и вращает колёса через драйвер моторов; машинка едет вперёд

5.0(38)
65
Клавиатура разработчика на Python
#66Средний
Premium

Клавиатура разработчика на Python

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

4.6(35)
45
Измерение расстояния HC-SR04 (вывод в порт)
#27Новичок

Измерение расстояния HC-SR04 (вывод в порт)

Датчик HC-SR04 работает совсем как летучая мышь, которая находит дорогу в темноте: он посылает звук, которого ты не слышишь, слушает эхо и измеряет, насколько далеко предмет перед ним. Arduino каждые

4.6(46)
71
Температура и влажность с DHT11
#55Средний

Температура и влажность с DHT11

Насколько тепло в твоей комнате и влажный ли воздух или сухой? Датчик DHT11 измеряет температуру и влажность, а Arduino выводит значения на двухстрочный LCD-экран и обновляет их каждые пару секунд. Ес

4.7(47)
73
Измерение освещённости модулем LDR на ESP32
#113Новичок

Измерение освещённости модулем LDR на ESP32

Наблюдение в мониторе порта в реальном времени за сырым значением ADC модуля LDR и относительным процентом освещённости, вычисленным по опорным значениям темноты и света. Этот процент — не измерение в люксах. Учит логике калибровки модуля. Идеально выполнить его перед проектом 10, потому что он нужен для определения порога.

4.6(36)
55