Search code examples
embeddediotc-stringsusartatmega16

How to receive strings from HC05 Bluetooth module using ATmega16 microcontroller


I am having problem in receiving string from HC05 to ATmega16. I am able receive characters but not able to receive strings.

I want to control DC motor wirelessly using ATmega16 and Bluetooth module (HC05). I am sending the timer OCR1A values from serial monitor app to ATmega16 by HC05 but not succeeded.

#define F_CPU 16000000UL 
#include<string.h>
#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>
#include <stdio.h>

void UART_init()
{
    UCSRB |= (1 << RXEN) | (1 << TXEN);
    UCSRC |= (1 << URSEL) | (1 << UCSZ0) | (1 << UCS Z1);
    UBRRL = 0x67;
}

unsigned char UART_RxChar()
{
    while( (UCSRA & (1 << RXC)) == 0 );
    return(UDR);
}

void UART_TxChar( char ch )
{
    while( !(UCSRA & (1 << UDRE)) );  /* Wait for empty transmit buffer*/
    UDR = ch ;
}

void UART_SendString( char* str )
{
    unsigned char j = 0;

    while( j <= 2 )
    {
        UART_TxChar( str[j] );
        j++;
    }
}

int main( void )
{
    char buff[3];
    char j;
    int i = 0, k = 0;
    DDRD = (1 << PD5);

    UART_init();

    while( 1 )
    {
        buff[0] = UART_RxChar();
        buff[1] = UART_RxChar();
        buff[2] = UART_RxChar();

        j = UART_RxChar();

        if( j == '!' )
        {
            UART_SendString( buff );   // this is to check whether the atmega16 received correct values for timer or not.
            UART_SendString( "\n" );
        }
    }
}

The expected result is when I enter the number in serial monitor app, I should get back the same number on serial monitor app.

In the actual result I am getting different characters sometimes and empty some times.


Solution

  • The string buff is unterminated, so UART_SendString( buff ); will send whatever junk follows the received three characters until a NUL (0) byte is found.

    char buff[4] = {0};
    

    Will have room for the NUL and the initialisation will ensure that buff[3] is a NUL terminator.

    Alternatively, send the three characters individually since without the terminator they do not constitute a valid C (ASCIIZ) string.

    Apart from the lack of nul termination, you code requires input of exactly the form nnn!nnn!nnn!.... If the other end is in fact sending lines with CR or CR+LF terminators - nnn!<newline>nnn!<newline>nnn!<newline>... your receive loop will get out of sync.

    A safer solution is to use the previously received three characters whenever a '!' character is received. This can be done in a number of ways - for long buffers a ring-buffer would be advised, but for just three characters it is probably efficient enough to simply shift characters left when inserting a new character - for example:

    char buff[4] ;
    for(;;)
    {
        memset( buff, '0', sizeof(buff) - 1 ) ;
    
        char ch = 0 ;
        while( (ch != '!' )
        {   
            ch = UART_RxChar() ;
            if( isdigit(ch) )
            {
                // Shift left one digit
                memmove( buff, &buff[1], sizeof(buff) - 2 ) ; 
    
                // Insert new digit at the right
                buff[sizeof(buff) - 2] = ch ;
            }
            else if( ch != '!' )
            {
                // Unexpected character, reset buffer
                memset( buff, '0', sizeof(buff) - 1 ) ;
            }
        }
    
        UART_SendString( buff ) ;
        UART_SendString( "\n" ) ;
    }
    

    This also has the advantage that it will work when the number entered is less than three digits, and will discard any sequence containing non-digit characters.