There is no radio between them: the phone is a pane in Mokxi, not a real radio (Bluetooth, simulated). What is modeled is the half of the module a circuit can see, the UART, and that turns out to be nearly all of what a sketch does with one.
The little blue board with the button on the end is sold as "Bluetooth for
Arduino", and what it actually is could not be simpler: a wireless extension
cable for a serial port. Bytes into RXD come out of the phone at the other
end; bytes the phone sends come out of TXD. That is the whole of the device
from the board's side, and a sketch cannot tell the difference between a paired
HC-05 and a wire to a terminal.
Pins and properties
| Pin | What it does |
|---|---|
VCC, GND |
Supply, 3.6 to 6 V, and ground. The logic is 3.3 V. |
TXD |
The module's output: wire it to the board's RX pin. |
RXD |
The module's input: wire it to the board's TX pin. |
EN |
HC-05 only. High puts it in AT mode. |
STATE |
HC-05 only. High while something is connected. |
The HC-06 has the first four only. Both have baud (9600 by default), name (what
the phone sees) and connected, whether a phone is linked when the run starts: on
for the HC-05, off for the HC-06.
Where the far end is
The phone in the Bluetooth pane. Open the Bluetooth tab while the
circuit runs: the module is listed under its name. Pair with its PIN (1234
until AT+PSWD or AT+PIN changes it; a wrong one fails), connect, and the
pane is a terminal like the "Serial Bluetooth Terminal" app. What you type goes
out of TXD; what the sketch sends into RXD appears under it.
The serial pane has the same far end, under the module's part id with its own input box beside the boards, so typing there is the phone sending too.
Click the module on the canvas to hang up and redial, the same as the pane's
Connect and Disconnect. On the HC-05 STATE follows: high while there is
somebody on the other end, low while there is not, which is how a sketch knows
whether it is talking to itself.
What you cannot learn here is everything on the other side of the antenna: radio range, interference, and the master and slave roles. Those are real, and they are not in this model.
What is real, and it is the half that goes wrong
The bytes are not teleported. TXD and RXD carry 8N1 at the module's baud,
bit by bit: a start bit low, eight data bits least significant first, a stop bit
high.
So the failure everybody has actually had is reproducible here on purpose. Open
the module's properties, set baud to 4800 while the sketch still samples at
9600, and the stop bit lands in the middle of a byte: the frame is thrown away
and the log fills with framing errors. It is the commonest HC-05 problem in the
world and it is worth causing once, deliberately, with the wires in front of
you.
The module ships at 9600 baud in data mode, which is why every tutorial starts
with Serial.begin(9600).
Three volts, not five
The board takes 3.6 to 6 V on VCC through its own regulator, and its logic is
3.3 V. TXD swings to 3.3 V, which an AVR reads as a high with room to spare.
RXD is the pin that matters on a real bench: it is not 5 V tolerant, which
is why every tutorial puts a divider on the board's transmit line. Nothing in
Mokxi can be damaged, so the gallery circuit does not carry one, but TXD
really does only reach 3.3 V here, so a circuit that compares it against the
5 V rail behaves the way the real one does. Add the divider on hardware.
Two pins, and one hardware UART
An Uno has one hardware UART and it is the serial monitor's, so the module gets
a software one, which is what SoftwareSerial is, and why it exists. The
tutorials' sketch compiles and runs as written:
#include <SoftwareSerial.h>
SoftwareSerial BTSerial(2, 3); // RX, TX: the module's TXD into 2
void setup() { Serial.begin(9600); BTSerial.begin(9600); }
void loop() {
if (BTSerial.available()) Serial.write(BTSerial.read());
if (Serial.available()) BTSerial.write(Serial.read());
}
SoftwareSerial.h is the runtime's own, written for Mokxi, with the Arduino
library's calls and its mechanism: a pin-change interrupt catches the start
bit, and counted loops time the other bits, with interrupts off for the byte.
The ATmega328P model runs every instruction at its data sheet cycle count, so
the timing is the chip's. It is tested against this module at 9600, 38400 and
57600 baud on a 16 MHz board, at 9600 and 38400 on the 8 MHz Pro Mini, and with
the module 4% fast or slow. Three behaviors of
the real library carry over, and all matter with this module:
- Half duplex. While the sketch sends a byte, interrupts are off, so a byte arriving at the same moment is lost. Answer the phone after its line has ended, not in the middle of it.
- One port listens at a time. With two
SoftwareSerialports, only the one that last calledlisten()receives. The receive buffer is 64 bytes and holds 63, andoverflow()says when it filled. - 38400 baud is tight at 8 MHz. On the 8 MHz Pro Mini each byte at 38400
keeps interrupts off for about 95% of its time, so when the
millis()timer interrupt lands on a start bit, that byte can come in wrong. In a test of 24 lines of 31 bytes, 23 arrived whole. Use 9600 or 19200 on an 8 MHz board when every byte matters.
The same header works on the Nano, the Pro Mini, the Mega (receive on 10 to 15, 50 to 53 and A8 to A15), the Leonardo and Pro Micro (receive on 8 to 11 and 14 to 16), the ESP8266, the ESP32 and the ESP32-S3. On the ESP boards it is the EspSoftwareSerial way: every edge is timed on an interrupt, so several ports receive at once.
The btlamp sketch counts a byte out by hand at 1200 baud instead, so the
whole of it is visible in thirty lines.
AT mode
EN (marked KEY on some boards) high puts the module into command mode: bytes
on RXD are commands for the module itself rather than traffic for the far end,
and the answers come back on TXD. The subset answered here is the one a lesson
uses:
| command | answer |
|---|---|
AT |
OK |
AT+VERSION? |
+VERSION:3.0-20170601 then OK |
AT+NAME? |
+NAME:<name> then OK |
AT+NAME=<text> |
OK, and the name changes |
AT+UART? |
+UART:<baud>,0,0 then OK |
AT+UART=<baud>,0,0 |
OK, and the rate changes |
AT+ADDR? |
+ADDR:98d3:31:fb1234 then OK |
AT+ROLE?, AT+ROLE=<0..2> |
+ROLE:<n> then OK; OK |
AT+CMODE?, AT+CMODE=<0..1> |
+CMODE:<n> then OK; OK |
AT+PSWD?, AT+PSWD=<pin> |
+PSWD:<pin> then OK; OK |
| anything else | ERROR:(0) |
AT mode talks at 38400 baud, whatever the data rate is set to, as the real
module does in command mode. So the tutorials' AT sketch, with
BTSerial.begin(38400) and EN held high from a pin, works as written. A sketch
that stays at 9600 gets nothing sensible back, which is the commonest AT-mode
mistake. AT+UART= changes the data rate, which takes effect when EN goes low
again. The role and connect mode are stored and read back, and change nothing
else, because there is no radio for them to change. The PIN is the one the
phone in the Bluetooth pane has to type to pair.
The real module wants each command to end with CR LF ("Both NL & CR" in the Arduino IDE's serial monitor). This one also takes a bare LF, which is what Mokxi's serial monitor sends.
The HC-06
The HC-05's slave-only sibling, on the four-pin board: VCC, GND, TXD and
RXD, with no EN and no STATE. It comes out of the bag with nothing
connected, and while nothing is connected everything on RXD is an AT
command, at the data rate, in the old "linvor" dialect: no line ending, and
the module answers once the line has been quiet for a tenth of a second, with
no line ending either. Pick "No line ending" for it.
| command | answer |
|---|---|
AT |
OK |
AT+VERSION |
OKlinvorV1.8 |
AT+NAME<name> |
OKsetname, and the pane shows the new name |
AT+PIN<four digits> |
OKsetPIN, and pairing wants the new PIN |
AT+BAUD<1 to C> |
OK<rate> at the old rate, then the new one: 4 is 9600, 8 is 115200 |
| anything else | nothing at all |
Once the phone connects, the same bytes are traffic, as on the real one.
What is not modeled
The radio, and everything that hangs off it: what the role would do, AT+BIND, AT+INQ and the discovery they belong to. The difference
between entering AT mode at power-up and after it: here EN high is AT mode at
38400 either way. The button on the end of
the board, which on a real one is the other way into AT mode. Flow control,
which the header does not bring out. Any framing but 8N1. And the delay between
a byte reaching the module and leaving the far end. Here it is instant, where a
real link is tens of milliseconds and jitters.
The circuits to open
Bluetooth chat: the tutorial sketch as written, SoftwareSerial on pins 2
and 3 at 9600. Type as the phone in the module's box; send 1 or 0 for the
LED.
Bluetooth lamp: type on, off, blink or status at the module in the
serial pane and the lamp answers, with STATE wired so the sketch can tell you
when nobody is listening.
See it in action
The tutorial Arduino HC-05 Bluetooth walks through the wiring, the chat sketch and the AT commands.
Two projects take commands from the module's box the way a phone app sends them: the Bluetooth RC car drives a 2WD car on an L298N from F, B, L, R and S, and Bluetooth home automation toggles four relays from 1 to 4.