Electronic – AVR – strange communication between ATmega8A and bluetooth module

atmegaavrmicrocontroller

I'm using ATmega8A-PU AVR microcontroller, and HC-05 bluetooth module on a breadboard.

What I'm doing here, is to connect to BT module from my mobile phone and toggle the LED state by sending commands to the microcontroller.

But the problem is, as I send a character to the microcontroller through BT module, I won't receive the same character and LED won't toggle.

I connected BT module and LED to the microcontroller by the following schematics:

serial

What I did here:

  1. connected VCC & GND pins properly.
  2. connected TX pin of BT module to RX pin of the microcontroller.
  3. dropped the voltage of TX pin of the microcontroller from +5v down to +3.3v , and connected that to RX pin of BT module (voltage division technique V2 = R2 x V1 / (R1+R2)) as this pin operates on 3.3v only.
  4. connected a 5mm LED to pin PB0 of the microcontroller.

So I guess the hardware part is properly set and there's no problem in here as BT module and LED both work perfectly.

I compile my programs on Linux, Ubuntu 16.04 platform by avr-gcc compiler.
I also use an USBasp AVR programmer to upload hex files into the microcontroller.

I set the BT module baud rate equal to the microcontroller's, so that they can talk to each other at the same frequency.
ATmega8A default baud rate is 9600.
BT module baud rate is 38400 by default, but to make it work with the microcontroller, I set it to 9600 by AT commands (AT+UART=9600,0,0) using Arduino platform. (maybe this causes the problem I'm going to explain).

I should note that, I can't set the microcontroller's baud rate to 38400, as the compiler warns that baud rate is higher than allowed. that's why I changed the BT module baud rate instead.

Finally, I compiled following code and uploaded it to the microcontroller:

#define F_CPU 1000000UL
#define FOSC 1843200 // Clock Speed
#define BAUD 9600
#define MYUBRR FOSC/16/BAUD-1

#define bitset(bit) (1 << (bit))
#define is_set(sfr, port) (((sfr) & bitset((port))))
#define is_dead(sfr, port) (!is_set((sfr),(port)))
#define wait_to_set(sfr, port) do {} while (is_dead((sfr),(port)))
#define wait_to_die(sfr, port) do {} while (is_set((sfr),(port)))

#include <avr/io.h>
#include <util/setbaud.h>

void USART_Init (const uint8_t ubrr)
{
    UCSRA |= (1 << U2X);
    UCSRB = (1<<RXEN)|(1<<TXEN);
    UCSRC = (1<<URSEL)|(1<<USBS)|(3<<UCSZ0);
    UBRRH = (ubrr>>8);
    UBRRL = ubrr;
}

void USART_Flush ()
{
    unsigned char dummy;
    while ( UCSRA & (1<<RXC) )
        dummy += UDR;
}

void USART_Transmit (const unsigned char data)
{
    /* Wait for empty transmit buffer */
    while ( !( UCSRA & (1<<UDRE)) );
    /* Put data into buffer, sends the data */
    UDR = data;
}

uint8_t USART_Receive ()
{
    /* Wait for data to be received */
    while ( !(UCSRA & (1<<RXC)) );
    /* Get and return received data from buffer */
    return UDR;
}

inline void LED_poweron () {
    PORTB |= (1<<PB0);
}

inline void LED_poweroff () {
    PORTB &= ~(1<<PB0);
}

int main ()
{
    USART_Init(MYUBRR); // setup serial connection
    DDRB = 0xff; // setup LED DDR
    PORTB = 0; // power off LED at the beginning

    while (1) {
        const uint8_t byte = USART_Receive (); // receive data
        USART_Flush (); // flush UDR to drop useless data

        if (byte == 2)
            LED_poweron ();
        else if (byte == 1)
            LED_poweroff ();

        USART_Transmit (byte); // send data back to sender
    }

    return 0;
}

The code is simple.
it initializes serial connection and receives data from BT module. if data holds number 2, LED would power on, if it holds number 1, LED would power off and any other value would be ignored, and finally the value transmits back into the sender.

I compile and upload this code by the following commands:

avr-gcc -Os -Wall -mmcu=atmega8a -o serial.o serial.c
avr-objcopy -j.text serial.o serial.hex 2> /dev/null
avrdude -p m8 -c usbasp -b 9600 -U flash:w:serial.hex

The code uploads properly and no error will pop up.

avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: AVR device initialized and ready to accept instructions

Reading | ################################################## | 100% 0.01s

avrdude: Device signature = 0x1e9307 (probably m8)
avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed
        To disable this feature, specify the -D option.
avrdude: erasing chip
avrdude: warning: cannot set sck period. please check for usbasp firmware update.
avrdude: reading input file "serial.hex"
avrdude: input file serial.hex auto detected as ELF
avrdude: writing flash (146 bytes):

Writing | ################################################## | 100% 0.12s

avrdude: 146 bytes of flash written
avrdude: verifying flash memory against serial.hex:
avrdude: load data flash data from input file serial.hex:
avrdude: input file serial.hex auto detected as ELF
avrdude: input file serial.hex contains 146 bytes
avrdude: reading on-chip flash data:

Reading | ################################################## | 100% 0.11s

avrdude: verifying ...
avrdude: 146 bytes of flash verified

avrdude: safemode: Fuses OK (E:FF, H:D9, L:E1)

avrdude done.  Thank you.

I use Serial Bluetooth Terminal Android application to connect to the BT module from my phone.

The problem occurs just here, when I send a string to the BT module, it sends back non-ascii characters and LED won't toggle!

The odd part is, I realized that when USART_Receive function happens to receive 1 byte, it iterates 4 times and receives 4 bytes instead!
I found this by setting the LED to blink for each time that function receives a byte.

I explained everything in detail, so any idea why am I not receiving the same character that I'm sending to BT module?
Is it about the timing? baud rates badly set?
I have no idea what's going on!

EDIT: you can find this project here

Best Answer

Several obvious issues jump out, but there may be other problems as well

connected VCC & GND pins properly.

No, you have not. Your MCU is missing two critically required power connections. This is known to lead to interesting failures. You must connect all the power pins, not just some of them.

if data holds number 2, LED would power on, if it holds number 1, LED would power off and any other value would be ignored

This is not implemented in a way that would be expected in the type of context you have. Your code is looking for byte values 1 and 2. But these are non-printable control codes. While it is possible to send these over a serial link, typically they are not values that would be sent, at least not until entering a binary-clean mode.

More typically, in a simple setting you would by exchanging printable string representations of numbers. So for example 1 has the code 0x31 and may also be represented to a C/C++ compiler as a character constant '1'

It's also very possible that whatever you use to send data is going to intersperse other characters such as carriage returns and newlines. If that is the case, your flush routine may become problematic - if it does receive anything, the lack of synchronization means it's quite possible it will consume the next byte you want to evaluate. But the way it assumes the next character indicator would be immediately present, versus a processor speed likely fast enough to easily "see" the time it takes for a byte to serially arrive suggests it is likely to receive nothing.

Chances are you want to evaluate all received bytes. If values are contextual, you should track their context in a state machine, not simply as a matter of time - especially because the packetized nature of bluetooth may group bytes oddly in time, either running together things that were sent with slight delays, or introducing delays between bytes that were immediately sequential in the original input.

It's quite possible your ATmega is not operating at the baud rate you expect. For example, you may still have default clock division enabled by default fuse settings. Or your may be operating off the internal oscillator, not the external crystal you have provided, again because of default fuses. Making a quick alternate program which prints a character over and over, and verifying that your PC can receive that would be a good check; even better you would check the output rate with an oscilloscope.

You could double check your baud rate settings of the module by operating the alternate Arduino system at 9600 baud. You can also work through the division calculations and see what the error in the 9600 baud setting will be for your atypical crystal frequency. Additional you should take time to understand the two possible oversampling settings. It may be of interest to know that you can read back the divider settings on an Arduino after the UART is configured by the usual libraries - though of course that will be in the context of the Arduino's clock frequency. You can also change the clock setting in your bare-metal program to match the Arduino's clock and try it on the Arduino (just be sure to load the resulting program in via the bootloader; if you ISP your Arduino you will erase its bootloader and have to use the ISP to restore that).

Another thing you might want to do for debugging would be to connect a logic level serial input of your PC to the BT module's output in parallel with the ATmega. Then you can use a serial program on your PC (ideally one that can show actual byte values as a hexdump) to see what the BT module is really outputting, if anything. Right now your project has a lot of moving parts, and you have very little visibility into which of them is not operating in the way you expected it to.