Blog
Connected, but the Arduino receives nothing — the five causes, in order
Troniction

Your phone says connected. The module's LED has settled. You press the button in the app, and the Arduino does nothing at all.
Connected only means the two radios agreed to hold a link open. It is a statement about the radio and nothing else. A module with both data wires unplugged still pairs, still connects, and still shows exactly the same green state in the app.
So before changing anything, find out whether bytes are arriving at all. That one answer splits the problem into two halves that share no causes.
The sixty-second test
Upload this, open the Serial Monitor at 9600, and send a single character from your phone.
#include <SoftwareSerial.h>
SoftwareSerial bt(10, 11); // RX, TX
void setup() {
Serial.begin(9600);
bt.begin(9600);
Serial.println("listening");
}
void loop() {
if (bt.available()) {
int b = bt.read();
Serial.print("got byte: ");
Serial.print(b);
Serial.print(" as char: ");
Serial.println((char) b);
}
}
Two outcomes, and they send you to opposite ends of this article.
A number appears. The radio, the wiring and the module are all fine. Your bytes are reaching the Arduino and the problem is in how your sketch interprets them — skip to The bytes arrive but nothing happens.
Nothing appears. The bytes are not reaching the Arduino at all. The cause is physical or it is the wrong serial object — read on.
First, check the LED agrees with the app
A module that is blinking fast and continuously is advertising, not connected. That is its "nobody is talking to me" state, and it does not change because an app drew a green dot.
Apps lie about this more often than you would expect. Some report a connection the moment they have finished their own setup, some remember the last device and show it as connected before anything has happened, and a BLE app showing a device in a scan list is showing a device it can see, not one it is attached to. If the LED says advertising, believe the LED, and fix the connection before you spend an evening on your sketch.
And the test that proves both directions
The sixty-second test proves the phone can reach the Arduino. It says nothing about the return path, which is the half you need the moment you want a sensor reading back on the screen. An echo sketch proves both at once — anything arriving goes straight back out, so it has no logic to be wrong:
#include <SoftwareSerial.h>
SoftwareSerial bt(10, 11); // RX, TX
void setup() {
bt.begin(9600);
}
void loop() {
if (bt.available()) {
bt.write(bt.read());
}
}
Type a letter in a terminal app and watch for it to come back. If the letter comes back, you are finished here — the radio works, the wiring is right in both directions, and the baud rates agree. Everything after that is code, and it is the second half of this article.
Nothing arrives: three causes, in order
TX and RX are not crossed
This is the most common cause by a wide margin, and it is the easiest to get wrong because the labels invite it.
TX means "this pin transmits". The module's TX must land on the Arduino's RX, and the Arduino's TX must land on the module's RX. Wired TX to TX, both ends talk and neither listens. The module still pairs, because pairing happens in the radio and never touches these two pins.
Follow the physical wire with a finger rather than trusting the colour. On the sketch above, the module's TX goes to pin 10.
The sketch is reading the wrong serial port
The second most common, and it produces a sketch that looks completely correct.
Serial is the USB connection to your computer. bt is the SoftwareSerial object on pins 10 and 11. If the module is wired to those two pins but the sketch calls Serial.available(), it is asking the USB port whether the phone sent anything — and the answer is always no.
The tell is a sketch that works perfectly when you type into the Serial Monitor and does nothing over Bluetooth. That is not a Bluetooth fault at all; the sketch was only ever reading the cable.
⚠️ The argument order is SoftwareSerial(rxPin, txPin). Reversed, the object transmits on the pin you wired for receiving, which produces exactly the same silence.
If the module is instead on pins 0 and 1, you have a different problem, and it shows up as an upload that hangs forever long before it shows up as missing data.
The module's input is damaged
Less common, and worth checking third because the first two are free.
An HC-05 or HC-06 RX pin is a 3.3V input. Fed 5V directly from an Arduino TX, it may keep working for weeks and then stop receiving — while the radio half carries on pairing and connecting normally, because pairing does not use that pin.
The test is whether it still answers AT commands. If it responds, the module is alive. If it pairs but answers nothing on any route, it is the input that has gone, and the replacement wants the voltage divider on its RX line.
The bytes arrive but nothing happens
If a number appeared in the test above, everything physical is correct and the remaining two causes are both protocol. They are the ones that survive longest, because nothing looks broken.
You are sending words where the sketch expects a letter
Bluetooth does not deliver messages. It delivers bytes.
There is no envelope around FORWARD. What arrives at the Arduino is F, then O, then R — and they may arrive in one burst or spread over several trips through loop(), depending on radio timing you do not control.
A sketch that reads what is available and compares it to "FORWARD" sees FOR on one pass and WARD on the next, matches neither, and does nothing. It works on the bench when you type slowly. It fails on the road.
Send one character per command. One byte is never split, because there is nothing to split.
| Command | Send |
|---|---|
| Forward | F |
| Back | B |
| Left | L |
| Right | R |
| Stop | S |
| Speed | 0–9 |
That is not a simplification for beginners. It is what the ready-made controller apps send, and it is why they are reliable.
If you are driving the Arduino from someone else's app, you do not have to guess which letters it uses. Load the echo sketch above, connect with the app instead of a terminal, and press its buttons: whatever comes back is exactly what that app transmits. Ten seconds of that beats an evening of reading an app's screenshots, and it catches the apps that send a trailing newline or a two-character sequence where you assumed one letter.
readString() is returning fragments
If you searched for how to read from serial you met readString() or readStringUntil(). Both are convenient and both surprise people.
They are timeout-driven, and the default timeout is one second. readStringUntil() returns when it sees the delimiter you asked for or when the timeout expires, whichever comes first.
Read that again, because it is the whole problem: the timeout counts as success. If your message has not fully arrived within a second you get back the fragment that did, with no indication that anything is missing.
Two symptoms follow, and both get blamed on the module.
- Your sketch feels laggy — it is, because
readString()waits out its full second whenever there is nothing more to read. - Long messages arrive in pieces, and each piece looks like a perfectly valid short message.
Shortening the timeout with setTimeout() to make things feel snappier makes fragmentation more likely, not less.
None of this is a bug. It is a function designed for a terminal typing at human speed, used on a radio link that arrives in bursts.
The pattern that works
For single letters — which is what you want:
#include <SoftwareSerial.h>
SoftwareSerial bt(10, 11); // RX, TX
void setup() {
bt.begin(9600);
}
void loop() {
if (bt.available()) {
char c = bt.read();
switch (c) {
case 'F': forward(); break;
case 'B': backward(); break;
case 'S': stop(); break;
}
}
}
// Your motor code goes in these three.
void forward() {}
void backward() {}
void stop() {}
available() says how many bytes are waiting. read() takes exactly one. No timeouts, no accumulation, nothing to fragment. That is the whole of command handling for most projects.
If you genuinely need multi-character messages, read until a terminator and decide for yourself when the message is complete — do not let a timeout decide for you:
#include <SoftwareSerial.h>
SoftwareSerial bt(10, 11); // RX, TX
char buf[32];
byte n = 0;
void setup() {
bt.begin(9600);
}
void loop() {
while (bt.available()) {
char c = bt.read();
if (c == '\n') { // the terminator says "that's the whole message"
buf[n] = '\0';
handle(buf);
n = 0;
} else if (n < sizeof(buf) - 1) {
buf[n++] = c;
}
}
}
void handle(char *msg) {}
The sender must end every message with \n. In exchange you get messages that are complete or absent, never half, and a loop() that never blocks.
⚠️ Note the n < sizeof(buf) - 1. Without it, a sender that never sends a newline walks straight off the end of the array. That is not a Bluetooth bug; it is the oldest bug there is, and a radio link is an easy way to trigger it by accident.
Both sketches above compile for an Arduino Uno — checked with arduino-cli on 2026-09-28, at 2,192 and 2,218 bytes of flash. They are compiled, not run against hardware; the motor and message functions are deliberately empty so you can drop your own in.
What if that was not it
Work down this list only after the sixty-second test, because it tells you which half to look in.
- Check the baud rates match on both ends. A mismatch usually produces garbage rather than silence, so if you are seeing strange characters the answer is in the baud rate, not here.
- Check nothing else is holding the module. A second phone or a laptop that connected earlier keeps the link, and the module will not accept yours.
- Check the module is powered from 5V, not 3.3V. A breakout regulator needs the higher rail, and an under-fed module pairs and then behaves erratically.
- Send from a plain terminal app rather than your own controller. If the terminal works, the fault is in the app, not the link.
Garbage in the Serial Monitor rather than nothing is a different symptom with a single cause, and it is worth ruling in or out first because it is one number.
Every symptom in this subject sorts the same way, by which part of the chain is misbehaving, on the Arduino Bluetooth fixes index.
Common questions
- The app says connected. Does that not mean it is working?
- No. Connected means the two radios agreed to hold a link open. It is a statement about the radio and nothing else — it does not mean your TX and RX wires are the right way round, it does not mean your sketch is reading the pins the module is wired to, and it does not mean the bytes you send are the bytes your sketch is looking for. A module with both data wires unplugged still pairs and still shows as connected.
- How do I tell whether any bytes are arriving at all?
- Upload a sketch that prints every byte it receives to the Serial Monitor with its numeric value, and send one character from your phone. If a number appears, the radio and the wiring are fine and the problem is in how your sketch interprets the data. If nothing appears, the bytes are not reaching the Arduino and the cause is wiring, the wrong serial object, or a damaged module input. That single test splits the problem in half.
- Why does sending FORWARD not work when sending F does?
- Because Bluetooth delivers bytes, not messages. There is no envelope around FORWARD. The Arduino may see FOR on one pass through loop and WARD on the next, and neither matches the word you compared against, so nothing happens. It works on the bench when you type slowly and fails as soon as the radio delivers in bursts. One character per command is never split, because there is nothing to split.
- I am using readString and getting half messages.
- That is readString working as designed. It is timeout-driven, the default timeout is one second, and readStringUntil returns when it sees your delimiter or when the timeout expires, whichever comes first. The timeout counts as success, so you get the fragment that arrived with no indication anything is missing. Shortening the timeout to feel snappier makes fragmentation more likely, not less.
- Could the module itself be damaged?
- It can be, and the test is whether it still answers AT commands. An HC-05 or HC-06 whose RX pin was fed 5V directly may still pair and still show as connected while its input no longer receives anything, because pairing is handled by the radio and does not touch that pin. If AT commands come back normally the module is alive; if the module pairs but answers nothing on any route, replace it and fit the divider on the replacement.
Still not connecting?
Arduino Bluetooth — Make It Connect is 62 pages of every way the link fails, why, and the fix — HC-05, HC-06 and HM-10 BLE, including the clone family almost nothing covers. $9.