USART on ATMEGA8 with ESP-01 not returning anything

Go To Last Post
27 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So I have the ESP-01 wireless to serial interface wired up through a level converter @ 3.3v to my atmega8. I get the light on the esp-01 showing it is powered on, but I am unable to read any data. I'm debugging with a 16x2 LCD trying to print out the results. I'm still rather new and just going by what I read in the manuals and online. I would really appricate it if anyone can tell me if I'm doing something obviously wrong?

 

#include <avr/io.h>

#ifndef F_CPU
#define F_CPU 1000000UL //so our code knows the chip clock is 1mhz for delay()
#endif
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 8UL))) - 1)
//table 52 page 132 of the atmega8 shows math for calculating prescale
//double speed mode(u2x=1) is multiply by 8
//asynchronous normal mode(u2x=0) is multiply by 16

#include <util/delay.h>

#include "lcd/lib/lcd.c"
#include "lcd/lib/lcd.h"

/*
 * This function gets a single 8 bit character from the USART RX pin.
 */
char uart_getchar(void)

{
	//to receive data we check the RXC flag to see if receive is complete, it is located inside the UCSRA register
    while ((UCSRA & (1 << RXC)) == 0) {};//do nothing until data has been received and is ready to be read from UDR

    return UDR; //fetch the received byte value in the var

}

void uart_puts(char *str)
{
 while(*str)
   {
		UDR = *str++; //send out the byte value
		while ((UCSRA & (1 << TXC)) == 0) {}; //do nothing until the transmission complete flag set
   }
}

/*
 * This function uses uart_getchar to combine all the characters into a string
 */

char *uart_gets(char *buffer, uint8_t max)

{

    uint8_t i = 0, c;

    while (i < max - 1) {

        c = uart_getchar();

        if (c == '\r' || c == '\n') break;

        buffer[i++] = c;//add the character to the buffer

    }

    buffer[i] = 0;

    return buffer;

}

void main()
{
InitLCD(LS_BLINK|LS_ULINE);//initialize lcd module
LCDClear();//clear the screen
LCDWriteString("Welcome");//sample out

_delay_ms(1000);

UCSRA = (1 << U2X); //turn on asynchronous double speed mode, this is needed due to table on page 153

//usart initialization page 134 atmega8 datasheet
UCSRB = (1 << RXEN) | (1 << TXEN); //turn on the transmission and reception circuitry tx/rx
UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); //use 8 bit character sizes. USREL bit set to select the ucrsc register
UBRRH = (BAUD_PRESCALE >> 8); //load upper 8 bits of baud rate into the high byte of the ubrr register
UBRRL = BAUD_PRESCALE; //load the lower 8 bits of the baud rate value in thte low bte of the ubrr register

//now we can send and receive data by the special register udr short for usart io data register.
//when we assign a byte to the UDR register that byte is sent out on the tx line
//we can tell when the transmission is complete by looking at the transmission completion flag inside usart control registers.

 char mysending[] = "AT\r\n\0";
 uart_puts(mysending);
_delay_ms(1000);

LCDClear();
char *uartString;
uartString = uart_gets(uartString, 50);
LCDWriteString(uartString);

}

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
char *uartString;
uartString = uart_gets(uartString, 50);

Classic error. Sure this creates a 2 byte variable in memory that will hold the address of a block of chars but it does NOT reserve any storage for the characters you are going to receive. Your function parameter suggest you expect up to 50 of them. If so you need 50 bytes of storage reserved to take them. You have two options (though one is almost never used on AVR). The obvious one is:

char uartString[50];
uart_gets(uartString, 50);

That simply creates an array of 50 bytes in memory and passes the address of it to the function. You cannot assign a value to uartString itself (this is an array not a pointer) so the only use of the return value from the function might be if you were planning to do something else like:

make_some_use_of_received_chars(uart_gets(uartString, 50));

in which case the return value of the first function (the address of those 50 characters in memory) is simply fed as the input to the next function. Otherwise there's probably not much point in uart_gets() having a return value unless you want to use it to signal error conditions like "there were more than 50, they wouldn't all fit" or something like that?

 

The other way to allocate the 50 bytes you need is:

char *uartString;
uartString= (char *) malloc(50);
uartString = uart_gets(uartString, 50);

but just don't do this - malloc() has no place on micros with only a few K or even just a few hundred bytes of RAM.

 

BTW just as you have a uart_getchar() I would implement a uart_putchar() that can be used to send just 1 character then your uart_puts() just becomes a case of repeatedly calling that uart_putchar(). You don't need the wait on TXC (which should be UDRE in fact) in the middle of uart_puts() any more as that per character wait will be done each time in uart_putchar().

 

Oh and as you have discovered, 9600 baud is only possible on a 1MHz clock by using U2X mode. If this is an application where UART usage is at the core then consider splashing the entire 10 cents to buy a crystal. Preferably one of the "baud rate friendly" ones.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So since I'm using usart in a way that sends me 8 bits at a time (one character), how can I reliably loop through and store every byte in a single variable that is being sent to my avr? You said to not use (malloc) but wouldn't that only use 50 bytes?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

As I say the easy way in C to reserve 50 bytes is:

some_byte_type data[50];

Sure, if you want to you can:

some_byte_type * ptr;

ptr = (some_byte_type *)malloc(50);

if you really want but apart from the fact that uses a few more than 50 bytes and the fact that if you malloc()/free() you may fragment the heap and run out of memory what do you see as the advantage of allocating your 50 bytes that way? Is it that you can free() them back when you have finished? Are you really going to do that? If the allocation is "forever" then why is:

some_byte_type data[50];

not the obvious choice?

 

Either way you DO have to allocate your 50 bytes to hold the received data somehow. Just declaring:

some_byte_type * ptr;

does not magically deliver 50 bytes out of the ether!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I changed my code following your suggestion to reserve the 50 bytes and I'm still not seeing anything printed to the display after 'Welcome'.

(I also just ordered 10 7.3728Mhz Crystals and some 22pF capacitors, nothing is only a few cents when they sell you 50 of an item that you only need one of.)

#include <avr/io.h>

#ifndef F_CPU
#define F_CPU 1000000UL //so our code knows the chip clock is 1mhz for delay()
#endif
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 8UL))) - 1)
//table 52 page 132 of the atmega8 shows math for calculating prescale
//double speed mode(u2x=1) is multiply by 8
//asynchronous normal mode(u2x=0) is multiply by 16
#include <util/delay.h>

#include "lcd/lib/lcd.c"
#include "lcd/lib/lcd.h"

/*
 * This function gets a single 8 bit character from the USART RX pin.
 */
char uart_getchar(void)

{
	//to receive data we check the RXC flag to see if receive is complete, it is located inside the UCSRA register
    while ((UCSRA & (1 << RXC)) == 0) {};//do nothing until data has been received and is ready to be read from UDR

    return UDR; //fetch the received byte value in the var

}

void uart_puts(char *str)
{
 while(*str)
   {
		UDR = *str++; //send out the byte value
		while ((UCSRA & (1 << TXC)) == 0) {}; //do nothing until the transmission complete flag set
   }
}

/*
 * This function uses uart_getchar to combine all the characters into a string
 */

char *uart_gets(char *buffer, uint8_t max)

{

    uint8_t i = 0, c;

    while (i < max - 1) {

        c = uart_getchar();

        if (c == '\r' || c == '\n') break;

        buffer[i++] = c;//add the character to the buffer

    }

    buffer[i] = 0;

    return buffer;

}

void main()
{
InitLCD(LS_BLINK|LS_ULINE);//initialize lcd module
LCDClear();//clear the screen
LCDWriteString("Welcome");//sample out

_delay_ms(1000);

UCSRA = (1 << U2X); //turn on asynchronous double speed mode, this is needed due to table on page 153

//usart initialization page 134 atmega8 datasheet
UCSRB = (1 << RXEN) | (1 << TXEN); //turn on the transmission and reception circuitry tx/rx
UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); //use 8 bit character sizes. USREL bit set to select the ucrsc register
UBRRH = (BAUD_PRESCALE >> 8); //load upper 8 bits of baud rate into the high byte of the ubrr register
UBRRL = BAUD_PRESCALE; //load the lower 8 bits of the baud rate value in the low byte of the ubrr register

//now we can send and receive data by the special register udr short for usart io data register.
//when we assign a byte to the UDR register that byte is sent out on the tx line
//we can tell when the transmission is complete by looking at the transmission completion flag inside usart control registers.

 char mysending[] = "AT\r\n\0";
 uart_puts(mysending);
_delay_ms(1000);

LCDClear();
char uartString[50];
LCDWriteString(uart_gets(uartString, 50));

}

 

Last Edited: Wed. Oct 19, 2016 - 05:58 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That code returns from main() ?!? (shouldn't matter but if the LCD code was triggering some kind of timed interrupt it would).

 

Anyway how sure are you that the basic UART functionality even works? It could just be stuck waiting to receive anything it can interpret. The code assumes F_CPU=1000000, how certain are you that this is even true?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Well I haven't changed any fuses and atmega8 runs at 1Mhz. How can I be sure basic UART functionality works without connecting the chip up to something?

I see what you mean by the code is probably stuck waiting to receive since I'm doing an LCDClear(); and then nothing populates.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

coreymanshack wrote:
Well I haven't changed any fuses and atmega8 runs at 1Mhz.
That's not entirely true. If you take a chip out of the packet it will be running at somewhere between 0.9MHz and 1.1MHz. The internal RC can be +/-10%.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

coreymanshack wrote:

How can I be sure basic UART functionality works

 

You can vary the OSCCAL value and see if the USART transmits correctly.

Here's a little program from clawson (Link) that does just that.

 

Once you find a value for OSCCAL that works, you just set that value at the beginning of your program.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you guys very much! I've ordered an external crystal as I read that those are more accurate than the internal clock. I'll hook that up when it gets here and test again. I also ordered another esp-01 module in case this one is faulty. I'll update this post again when I get it all sorted.

I did actually change the debugging printout up a little bit by just 'appending' the new value that's supposed to be coming in on USART instead of running 'LCD_Clear()' and it appears the code is stuck waiting on a response so I'm never receiving the response.

Last Edited: Fri. Oct 21, 2016 - 06:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ok so I have the 7.3728Mhz crystal hooked up, fuses set, even ordered a brand new esp-01 to test that vector and I'm still not getting anything returned 3 lines from the bottom near uart_gets() and the code never prints 'some should have appeared'.

I've tried almost every combination of stable baud rates with u2x on and off. The only time I can get output from the esp-01 is when I touch reset to gnd on the esp and let go when the AVR is in the middle of the uart_gets(), bunch of garbage displays and I get 'some should have appeared'.

 

I'm stumped :(

 

#include <avr/io.h>

#ifndef F_CPU
#define F_CPU 7372800UL //so our code knows the chip clock is 7.3728mhz
#endif
#define USART_BAUDRATE 9600
#define BAUD_PRESCALE (((F_CPU / (USART_BAUDRATE * 16UL))) - 1)
//table 52 page 132 of the atmega8 shows math for calculating prescale
//double speed mode(u2x=1) is multiply by 8
//asynchronous normal mode(u2x=0) is multiply by 16
#include <util/delay.h>

#include "lcd/lib/lcd.c"
#include "lcd/lib/lcd.h"
#include "bigStringLCDPrint.c"

/*
 * This function gets a single 8 bit character from the USART RX pin.
 */
char uart_getchar(void)

{
	//to receive data we check the RXC flag to see if receive is complete, it is located inside the UCSRA register
    while ((UCSRA & (1 << RXC)) == 0) {};//do nothing until data has been received and is ready to be read from UDR

    return UDR; //fetch the received byte value in the var

}

void uart_puts(char *str)
{
 while(*str)
   {
		UDR = *str++; //send out the byte value
		while ((UCSRA & (1 << TXC)) == 0) {}; //do nothing until the transmission complete flag set
   }
}

/*
 * This function uses uart_getchar to combine all the characters into a string
 */

char *uart_gets(char *buffer, uint8_t max)

{

    uint8_t i = 0, c;

    while (i < max - 1) {

        c = uart_getchar();

        if (c == '\r' || c == '\n') break;

        buffer[i++] = c;//add the character to the buffer

    }

    buffer[i] = 0;

    return buffer;

}

void main()
{
InitLCD(LS_BLINK|LS_ULINE);//initialize lcd module
LCDClear();//clear the screen
LCDWriteString("Welcome");//sample out

_delay_ms(2000);

//UCSRA = (1 << U2X); //turn on asynchronous double speed mode, this is needed due to table on page 153

//usart initialization page 134 atmega8 datasheet

UBRRH = (BAUD_PRESCALE >> 8); //load upper 8 bits of baud rate into the high byte of the ubrr register
UBRRL = BAUD_PRESCALE; //load the lower 8 bits of the baud rate value in the low byte of the ubrr register
UCSRB = (1 << RXEN) | (1 << TXEN); //turn on the transmission and reception circuitry tx/rx
UCSRC = (1 << URSEL) | (1 << UCSZ0) | (1 << UCSZ1); //use 8 bit character sizes. USREL bit set to select the ucrsc register

//now we can send and receive data by the special register udr short for usart io data register.
//when we assign a byte to the UDR register that byte is sent out on the tx line
//we can tell when the transmission is complete by looking at the transmission completion flag inside usart control registers.

_delay_ms(2000);
 //char mysending[] = ;
uart_puts("AT+GMR\r\n\0");
_delay_ms(2000);
LCDWriteStringXY(15,1, "1");//tell us we sent data

char uartString[50];
bigStringLCDPrint(uart_gets(uartString, 50), 50);//custom function to display the output if its over 16 characters.
LCDWriteStringXY(0,1,"some should have appeared");//first char, second line
}

 

Last Edited: Thu. Oct 27, 2016 - 03:49 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Corey,

 

Could you use Bray's Terminal software (or equivalent) to talk directly to the ESP-01 first (careful of the voltage levels). I have read in a number of places that the default baud rate can vary with the firmware version and that 115200 appears to be the majority "default". Others experienced include (your) 9600 and 57600.

 

Once you can send "AT" to the ESP-01 and receive the "OK" in return, then you can step forward.

 

Cheers,

 

Ross

 

Ross McKenzie ValuSoft Melbourne Australia

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can also use the PC soundcard as an oscilloscope. Works well for lower frequencies like 9600 baud.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Not the problem here but...

#ifndef F_CPU
#define F_CPU 7372800UL //so our code knows the chip clock is 7.3728mhz
#endif

Don't do that. If you know it is 7.3278MHz then don't be afraid to say it whatever.

 

If you really think F_CPU may have been incorrectly set to something else and you want to redefine it then use:

#undef F_CPU
#define F_CPU 7372800UL //so our code knows the chip clock is 7.3728mhz

coreymanshack wrote:
I've tried almost every combination of stable baud rates with u2x on and off.

Part of the reason for using a crystal like 7.3728 is so you never need to contemplate using U2X. It has no place here. (and the *16UL in your baud rate calculating formula is for when you are NOT using U2X - if you felt tempted that would need to be *8UL)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nice catch :). Simple newb mistake here. I think I had some of this in other libraries I included that was overwriting this value somehow, because after changing it my code actually received some garbage when it was supposed to on the uart_gets(). I'm getting intermittent results though and I don't know why.

#ifndef F_CPU
#define F_CPU 1000000UL //so our code knows the chip clock is 7.3728mhz
#endif

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Do I need to be waiting any amount of time to read data after putting data or clearing UDR after putting data?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Your uart_getchar routine is waiting for a character to be received before returning with the contents of UDR, so you do not have to do any other waiting for data reception.

As far as waiting after writing to UDR, it totally depends on what is receiving the data and how much processing the receiving device is going to be doing with the data.

(If the receiver is a PC, there (probably) is no need to wait after writing UDR)

David (aka frog_jr)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I'm thinking using an interrupt routine is more reliable for receiving. I have a feeling I may be missing some data by manually checking if we have received. Any thoughts?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Interrupt won't help your current issue.
From what i see, your code outputs the string, displays on lcd, waits two seconds then expects to read the response. My guess is that it has come and gone by the time you try to read it.

Now, a common method to handle serial comms is to have a circular receive buffer for the usart data. This is what the Arduino implements. It uses interrupts. There's also a tutorial in the tutorial section.

Last Edited: Fri. Oct 28, 2016 - 07:16 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So have you successfully sent "AT" to the ESP-01 and received an "OK" in response yet?

 

Ross McKenzie ValuSoft Melbourne Australia

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I do not have a max232 or similar to convert the esp01 logic to rs232 to connect to my computer and visa versa. I have tried every possible baud rate on this atmega8 listed in the datasheet for stable 7.3728mhz. I have multiple esp-01 modules and multiple atmega8 I've tried. At 76800 baud I can see some 'boot up messages' such as room 12, len 2323 but the rest is garbage, pulled this sample from someone else on the web's boot up to figure out why it was telling me 'len room'

ets Jan 8 2013,rst cause:4, boot mode:(3,5)

wdt reset
load 0x40100000, len 1396, room 16
tail 4
chksum 0x89
load 0x3ffe8000, len 776, room 4
tail 4
chksum 0xe8
load 0x3ffe8308, len 540, room 4
tail 8
chksum 0xc0
csum 0xc0

Here are some images of my circuit, maybe I'm doing something insanely stupid since I'm a newb that someone can point out. Using LM1117-T 3.3v, 2x220uF Caps on the source and output there. USB 5V power straight from my computer for the rest of the circuit. Logic level converter there close to the LM1117-T

Last Edited: Fri. Oct 28, 2016 - 09:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've suggested you use your soundcard as an oscilloscope. I've also outlined problems in your code.
How long do you want to keep guessing?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

So you think I'm getting garbage characters because I don't have a circular buffer and I can solve this with an oscilloscope how? I have never done this before.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Interesting interpretation of what i wrote. Google soundcard oscilloscope
This will allow you to see what is happening rather than blindly guessing.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

My friend is going to let me borrow his oscilloscope, how should I hook it up and what should I be seeing?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Did you not try and use your PC soundcard as an oscilloscope? You've had two months to try....

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Program the AVR to repeatedly send 'U' and the TXD pin should show a constant 0101010101... bit stream. Now use the scope to measure the time between two edges. 

 

If it's really 9600 baud the bit width should be 104.2us