Saturday, April 18, 2015

Connect ATmega328 with HC-05 (ZS-040) Bluetooth module

Parts list

  • ATMega328 (I use handy Arduino Nano);
  • HC-05 (ZS-040) Bluetooth module (EGBT-045MS);
  • 1K Ohm and 2K Ohm resistors;


HC-05 or ZS-040 Bluetooth module is based on EGBT-045MS chip and has following pins on board:
  • STATE - HIGH when module is connected;
  • RXD - data input pin;
  • TXD - data output pin;
  • GND - ground pin;
  • VCC - power input pin;
  • EN - Unknown pin. It should be connected to pin 34 of EGBT-045MS Bluetooth module, but it seems it doesn't work in preferred way;
So I connected HC-05 (ZS-040) as schema shows:

HC-05 TXD - ATmega RXD/PD0 (no voltage divider);
HC-05 RXD - ATmega TXD/PD1 (through voltage divider 1K Ohm + 2K Ohm)
HC-05 GND - Ground
HC-05 VCC - +5V
The connection of HC-05 module is very easy. Only RXD pin requires a voltage shifting, the connection of all other pins are straightforward.
Important note: This schema assumes that HC-05 will utilize RXD/TXD pins of ATmega328, which are used for UART and uploading a firmware. It means you have to power off HC-05 module during uploading of firmware to ATmega328. It can distract during development, but it's better to use hardware UART for communication with HC-05 in regular use.

AT mode

Before we start to use HC-05 (ZS-040) in regular communication, we need to change several internal settings of module. It can be done by switching HC-05 module to AT mode. In order to enable AT mode, use following steps:
  1. Power off HC-05 module.
  2. Press and hold small button above EN pin.
  3. Power on and keep pressing small button.
  4. Small LED should start to blink slowly about once every 2 seconds.
When HC-05 is switched to AT mode it's possible to send following AT commands:
  • AT+NAME=BLUES\r\n - change the name of module. It helps to recognize it.
  • AT+PSWD=7515\r\n - change the default PIN for pairing. More security for you the final device.
  • AT+UART=38400,1,0\r\n - change the default UART settings (9600 Baud). More speed as needed (up to 115200 Baud).
By default HC-05 uses 38400, 1 stop bit, no parity bit for AT mode communication and it cannot be changed by user. So in order to send these commands to HC-05 module, you have to initialize ATmega328 UART with above settings. HC-05 responses with OK if command executed successfully or ERROR:(?) with error code inside braces. See link with full description of commands and errors.

Source code

Here is the source code of UART communication for ATmega328.

#include <stdlib.h>
#include <string.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <util/atomic.h>
#include <util/delay.h>

void uart_init(uint32_t baudrate, uint8_t double_speed) {
    uint16_t ubrr = 0;
    if (double_speed) {
        UCSR0A = _BV(U2X0);  //Enable 2x speed
        ubrr = (F_CPU / (8UL * baudrate)) - 1;
    } else {
        ubrr = (F_CPU / (16UL * baudrate)) - 1;
    UBRR0H = ubrr >> 8;
    UBRR0L = ubrr;

    UCSR0C &= ~(_BV(UMSEL01) | _BV(UMSEL00)); // enable asynchronous USART
    UCSR0C &= ~(_BV(UPM01) | _BV(UPM00)); // disable parity mode
    UCSR0C &= ~_BV(USBS0); // set 1-bit stop
    UCSR0C &= ~_BV(UCSZ02); 
    UCSR0C |= _BV(UCSZ01) | _BV(UCSZ00); // set 8-bit data

    UCSR0B = _BV(RXEN0) | _BV(TXEN0);   // Enable RX and TX 

uint8_t uart_getchar() {
    loop_until_bit_is_set(UCSR0A, RXC0);
    return UDR0;

void uart_read_line(uint8_t *value, uint8_t size) {
    for (uint8_t i = 0; i < size; i++) {
        value[i] = uart_getchar();
        if (value[i] == '\r') {
            value[i] = '\0';

void uart_putchar(const uint8_t data) {
    loop_until_bit_is_set(UCSR0A, UDRE0);
    UDR0 = data;

void uart_print(const char *value) {
    while (*value != '\0') {

int main(void) 
    uart_init(38400, 1);




    char data[11];
    for (;;) {
        memset(data, 0, 11);
        uart_read_line(data, 10);
This code initializes HC-05 with described above settings when it switched to AT mode and makes echo output in the main loop. After first initialization, it's better to comment out or delete AT commands in source code.


  1. What are the header files that are to be used?

  2. They are from avr-libc

  3. This comment has been removed by a blog administrator.

  4. I am trying to implement the same using an STM32f$ board. Would those libraries you used work in my case ?
    Would the setup be similar ?

    1. No, you have to use STM32 libraries but approach for enabling AT mode is the same.

  5. If you put a logic "0" in the enable pin you can put the bluetooth module in off mode, this terminal enable the power in the 3v3 regulator.
    For do the AT+NAME command are necesary had a button PRESS

  6. My HC-05 doesn't have this button. It is printed on the upside, but no button. I can't enter in the AT mode( with the 2 sec gap blinking). Can you help me, please?

  7. I bought a HM-10 BLE Bluetooth 4.0 CC2540 CC2541 Serial Wireless Module for Arduino. I am using Arduino UNO and used this code:

    char choice;
    int led = 13;
    void setup() {
    pinMode (led , OUTPUT);


    void loop() {
    if(Serial.available()) {
    choice =;

    if(choice == '1') {
    digitalWrite(led, HIGH);
    else if(choice == '0') {
    digitalWrite(led, LOW);

    this is the link of I bought Bluetooth module:

    when I connect from samsung J7 mobile phone it shows "Pairing Rejected by BT05"

    Please help me...

    1. I have a similar issue with a HC-05 (ZS-040) ... any solutions for "BT05 declined your pairing request"?

  8. i think that when we init the data setting we use UCSR0B &= ~(1<<UCSZ02) ;
    not UCSR0C

  9. How did you go about creating the code for this project? What resources did you use? I've been looking for books or any information on the atmega328p to able to transfer arduino projects to c.