HC-05 and Arduino
The cheapest way to put an Arduino on Bluetooth, and the one with the most sharp edges.
Install#
flutter pub add flutter_classic_bluetoothimport 'package:flutter_classic_bluetooth/flutter_classic_bluetooth.dart';Connecting#
An HC-05 in its normal mode is a transparent serial bridge. Bytes you write arrive on the Arduino's RX pin and bytes the Arduino writes come back.
final paired = await bluetooth.getPairedDevices();
final hc05 = paired.firstWhere((d) => d.name == 'HC-05');
final connection = await bluetooth.connect(
address: hc05.address,
timeout: const Duration(seconds: 8),
);
connection.input.lines().listen((line) => print('Arduino: $line'));
await connection.output.writeLine('LED ON');Pair it in system settings first. The default PIN is 1234 or 0000. These modules do not always pair cleanly from inside an app, and doing it once in settings removes a whole class of confusing failures.
The sketch#
#include <SoftwareSerial.h>
SoftwareSerial bt(10, 11); // RX, TX
void setup() {
bt.begin(9600);
}
void loop() {
if (bt.available()) {
String cmd = bt.readStringUntil(10);
if (cmd.startsWith("LED ON")) digitalWrite(13, HIGH);
bt.println("ok");
}
}Wire the module's TX to the Arduino's RX and its RX to the Arduino's TX. The HC-05 RX pin is 3.3V, so it needs a divider from a 5V board. Skipping that is the most common reason a module works for a while and then stops.
Baud rate#
The module's Bluetooth side and its serial side are configured separately. bt.begin(9600) has to match the module's UART baud, which is 9600 by default but is often changed to 38400 or 115200 and then forgotten.
A mismatch does not fail cleanly. You get a connection, you can write, and what comes back is garbage bytes. If lines is emitting nonsense, suspect the baud rate before anything in Dart.
AT command mode#
Held in AT mode, an HC-05 answers configuration commands over the same link.
final name = await connection.sendAndReceive('AT+NAME?');
final version = await connection.sendAndReceive('AT+VERSION?');
final ok = await connection.sendAndReceive(
'AT+UART=9600,0,0',
where: (line) => line == 'OK',
);HC-05 firmware expects \r\n, which is what sendAndReceive sends by default. Some clones want a bare \n:
await connection.sendAndReceive('AT', newline: '\n');Entering AT mode is a hardware step: hold the module's KEY or EN pin high while powering it up. There is no way to trigger it from Dart.
HC-06#
An HC-06 is slave only. It cannot initiate a connection, which is fine here since your app is always the one connecting. Its AT dialect is different: no \r\n terminator, and no ? query form. Everything else on this page applies.
Common problems#
| Symptom | Usually |
|---|---|
| Received text is garbage | Baud mismatch between the sketch and the module |
| Connects, then drops after a second | Powered from a pin that cannot supply the peak current |
| Module stops responding after weeks | 5V on the 3.3V RX pin. Use a divider |
| Arduino receives nothing | TX and RX not crossed, or writeString used with no delimiter |
| AT commands return nothing | Not in AT mode. The KEY pin must be high at power up |
| Pairing fails from the app | Pair once in system settings with PIN 1234 or 0000 |