There is a problem I forgot to take care before creating the custom image for my radio node project. Programming SA818 module via Raspberry Pi’s UART interface cannot be done unless the UART interface is released from any interaction in console mode. I was banging my head for hours because of sustained DMOERROR message for each and every single command it was issued on the UART interface.
Understanding UART, miniUART, Bluetooth, and Console Interaction on the Raspberry Pi (BCM2837)
Overview
The Broadcom BCM2837 System-on-Chip (SoC), used in Raspberry Pi 3 and some later models, provides two UART peripherals:
- PL011 UART – A full-featured UART derived from the ARM PrimeCell PL011.
- miniUART – A simpler UART with limited features and dependent clocking.
The interplay between these two UARTs, the Bluetooth module, and the Linux console configuration confused me (and surely had caused confusion among other users) — especially when serial communication appears to fail unless Bluetooth or the serial console is disabled. Programming SA818 module is a very good example.
This document tries to provide a complete, structured explanation.
BCM2837 UART Architecture
Available UART Interfaces
| Interface | Name | Features | Default usage |
|---|---|---|---|
| PL011 UART | /dev/ttyAMA0 |
Full-featured UART with independent clock, FIFOs, and flow control | Used for Bluetooth on Raspberry Pi 3/4 |
| miniUART | /dev/ttyS0 |
Simplified UART, no independent baud rate generator, clock-tied to CPU core frequency | Used for GPIO serial (pins 14/15) by default |
Clock Dependencies
- PL011 UART:
Uses a dedicated clock source, unaffected by CPU frequency scaling.
→ Stable baud rate under all operating conditions. - miniUART:
Derives its clock from the core clock (VPU frequency).
→ If the CPU frequency changes (for power saving, DVFS), the baud rate changes, breaking serial communication stability. Because of this instability, the miniUART is not ideal for reliable serial communication unless the core clock is fixed.
Bluetooth and UART Resource Reassignment
Original UART Assignments (Pre–Raspberry Pi 3)
| Function | Device | UART | GPIO Pins |
|---|---|---|---|
| Primary UART (Serial Console) | /dev/ttyAMA0 |
PL11 | GPIO 14 (TX), GPIO 15 (RX) |
| No Bluetooth present | — | — | — |
New Assignments (Raspberry Pi 3 and later)
| Function | Device | UART | GPIO Pins |
|---|---|---|---|
| Bluetooth | /dev/ttyAMA0 |
PL11 | Internal connection to BT chip |
| Serial GPIO (pins 14/15) | /dev/ttyS0 |
miniUART | GPIO header |
Thus, for newer models, the hardware UART (PL011) is taken over by the Bluetooth controller, and users are left with the miniUART for serial console or communication — unless Bluetooth is disabled.
Why This Causes Problems
- The miniUART is clock-dependent → unstable baud rate.
- The console or serial login service is enabled by default on serial0.
- The default device tree overlay ties
/dev/serial0to the miniUART. - Bluetooth occupies
/dev/ttyAMA0unless explicitly disabled.
Therefore:
- If Bluetooth is active → PL011 is unavailable for GPIO serial.
If console mode is active → miniUART is already reserved for system console use. - Result: No reliable UART available for custom serial communication.
Disabling Bluetooth to Reclaim PL011 for GPIO Serial
Device Tree Overlay Control
The Raspberry Pi OS device tree overlays determine which UART is assigned to which function.
To disable Bluetooth and reclaim the PL011 UART for GPIO edit /boot/config.txt or /boot/firmware/config.txt:
dtoverlay=disable-bt enable_uart=1
dtoverlay=disable-bt→ disables the Bluetooth service and reassigns PL011 to GPIO 14/15.enable_uart=1→ ensures UART pins are active and not power-gated.
After reboot:
/dev/ttyAMA0→ GPIO pins (PL011, stable)/dev/ttyS0→ unused- Bluetooth → disabled
Serial Console Interaction
What Is the Serial Console
The serial console allows kernel messages and shell access over UART. This is configured at boot by passing kernel parameters through /boot/cmdline.txt. Example default entry:
console=serial0,115200 console=tty1
This means:
- Kernel logs are sent to
serial0(UART). - Console is also available on the HDMI display (tty1).
Device Aliases: serial0 and serial1
To provide consistency across models:
| Alias | Description |
|---|---|
serial0 |
The primary UART (can map to PL011 or miniUART depending on configuration) |
serial1 |
The secondary UART |
The serial0 mapping depends on whether Bluetooth is enabled.
| Bluetooth state | serial0 maps to |
Type |
|---|---|---|
| Enabled | /dev/ttyS0 |
miniUART |
Disabled (dtoverlay=disable-bt) |
/dev/ttyAMA0 |
PL011 |
The cmdline.txt Effect
If you have an entry such as:
console=serial0,9600
This line reserves the serial port for kernel and login console use, meaning:
- The system kernel and getty service take control of the UART.
- Your user program cannot open the port because it is locked by the system.
This is why serial communication “does not work” when this line is present — the port is already in use by the kernel and login console. This is the BOTTOM of the SA818 DMOERROR problem !
SOLUTION: How to Free the UART for Application Use
Step-by-Step
Edit /boot/config.txt:
enable_uart=1 dtoverlay=disable-bt
Edit /boot/cmdline.txt:
Remove any console=serial0,... or console=ttyAMA0,... entries.
Example final version:
console=tty1 root=PARTUUID=xxxxxxxx-02 rootfstype=ext4 elevator=deadline fsck.repair=yes rootwait
Disable serial console service:
sudo systemctl stop serial-getty@serial0.service sudo systemctl disable serial-getty@serial0.service
Reboot
After reboot:
/dev/ttyAMA0(PL011) is free for user-level serial communication.- Bluetooth and console no longer interfere.
Verifying UART Functionality
Checking UART Device Mapping
ls -l /dev/serial*
Expected output (after disabling BT):
serial0 -> ttyAMA0 serial1 -> ttyS0
Checking Configuration
vcgencmd get_config enable_uart
Should return:
enable_uart=1
Testing
sudo minicom -b 115200 -o -D /dev/serial0
Or use Python:
import serial
ser = serial.Serial('/dev/serial0', 115200)
ser.write(b'Hello UART!\n')
ser.close()
| Scenario | Bluetooth | Console Enabled | GPIO UART | Device | Stable ? | User Access |
|---|---|---|---|---|---|---|
| Default (Raspberry Pi 3) | ON | YES | MiniUART | /dev/ttyS0 |
NO | NO |
| Bluetooth ON, Console Disabled | ON | NO | miniUART | /dev/ttyS0 |
NO | YES |
| Bluetooth OFF, Console Disabled | OFF | NO | PL011 | /dev/ttyAMA0 |
YES | YES |
| Bluetooth OFF, Console Enabled | OFF | YES | PL011 | /dev/ttyAMA0 |
YES | NO |
Key Takeaways
Finally — and this was a long post ! — the following are the key takeaways:
- BCM2837 has two UARTs: PL011 (stable, clocked) and miniUART (unstable, clock-dependent).
- Bluetooth uses the PL011, forcing the GPIO UART to use miniUART unless disabled.
- The serial console in cmdline.txt prevents user access to UART.
- To use UART for your own program:
- Disable Bluetooth (dtoverlay=disable-bt)
- Enable UART (enable_uart=1)
- Remove console from cmdline.txt
- Disable serial-getty service
Using lsof
lsof command can show which processes are using which resources. This is extremely useful for troubleshooting. Basic syntax is:
lsof [options] [file]
Without arguments, it lists all open files on the system (requires root privileges).
Common Use Cases and Examples:
Find which process is using a file:
tom@rpi-i2s:~ $ sudo lsof /dev/serial0 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME screen 795 root 6u CHR 204,64 0t0 133 /dev/ttyAMA0
Find which process is using a port:
tom@rpi-i2s:~ $ sudo lsof -i :80
The above command Lists processes using TCP/UDP port 80 (e.g., web server).
List all open files by a specific user:
tom@rpi-i2s:~ $ lsof -u pi
The command above shows all files opened by user pi.
List all network connections:
tom@rpi-i2s:~ $ sudo lsof -i
The command above displays all open network sockets and connections.
List all open files for a process:
tom@rpi-i2s:~ $ lsof -p 1234
The command above shows all files opened by the process with PID 1234.
Find which process is using a mount point or device:
tom@rpi-i2s:~ $ sudo lsof /dev/sda1
The command above is useful when you get a “device is busy” error during unmount.
| Option | Description |
|---|---|
-p <pid> |
List files opened by a specific process ID |
-u <user> |
Show files opened by a user |
-i |
Show network connections |
+D <dir> |
Recursively list all files opened under a directory |
-t |
Output only PIDs (useful in scripts) |
-n |
Skip DNS resolution (faster output) |
-P |
Show port numbers instead of names |
Practical Example on Raspberry Pi
If you find that the UART (/dev/ttyAMA0 or /dev/serial0) is busy or inaccessible, run:
sudo lsof /dev/serial0
You might see:
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME agetty 1014 root 3u CHR 204,64 0t0 1234 /dev/ttyAMA0
This means the serial console service (agetty) is occupying the UART — so your program can’t use it until you stop that service.
In Summary
| Task | Command | Purpose |
|---|---|---|
| Check which process uses a port | sudo lsof -i :22 |
Find SSH or HTTP process |
| Check which process uses a file/device | lsof /dev/ttyAMA0 |
Check serial port usage |
| See all network connections | sudo lsof -i |
Network diagnostics |
| Kill a process holding a file | kill $(sudo lsof -t /path/to/file) |
Release a locked file |
kill user:
fuser -k /dev/ttyUSB9
That’s it !
Thanks for reading
73