300 baud on Uno

I’ve read many old threads on this topic but haven’t found any solutions that work for me. I’m brand new to Arduino and am trying to set up my Uno to replace the controller for a heat recovery ventilator. The control logic operates at 300 baud so I’m stuck with that bit rate. I set up a simple serial echo loop which works fine for 1200 baud and higher, but spits out garbage at 300 baud. This is identical to reports in 2-7 year old threads. Is there a workaround to this problem or am I stuck?

Here is the test sketch I’m using. Nothing fancy. But I did try one workaround I found in an old thread which was to initialize the serial port with 600 baud and then halve that with the code commented out in the setup. But it didn’t work.

Any suggestions are very much appreciated.

void setup()
{
  Serial.begin(300);
  // This halves the baud rate
  //UCSR0A &= ~(1<<U2X0);
}

void loop(){

  // Wait for input
  while (Serial.available() == 0);

  int val = Serial.read();

  //Echo the input
  Serial.println(val);
  
//  Serial.write(val); // send a byte with the value 'val' 

  delay(1000);
}

initialize the serial port with 600 baud and then halve that with the code commented out in the setup.

That should work… I don’t have any other suggestions. The divisor you’d need to get 300 to work just doesn’t fit in the number of bits that are available.

You could add

   UBRR0H = 0xD;
   UBRR0L = 0x04;

westfw: That should work... I don't have any other suggestions. The divisor you'd need to get 300 to work just doesn't fit in the number of bits that are available.

You could add

UBRR0H = 0xD; UBRR0L = 0x04;

Thanks for the reply. Unfortunately adding the UBRRn value didn't help. It works for 1200 baud and up, but not for 300. I read on this old thread (http://forum.arduino.cc/index.php?topic=124703.0) where someone got it working using digital pins instead of the hardware serial. So I've been playing around with software serial. Unfortunately I don't have a usb to ttl converter so no way to debug using a terminal. I was able to read responses from the device I'm eventually hoping to talk to. Not the responses I was expecting but at least they were readable digits. Maybe a framing issue but I suspect I need to get a converter to properly test whether 300 baud is working on the software serial pins. Hoping I can get this working and didn't waste time and money by choosing the wrong dev board for this project.

bbrock:
Hoping I can get this working and didn’t waste time and money by choosing the wrong dev board for this project.

I’m thinking perhaps you could code your sketch to communicate with the device at 1200 baud just like it is a 1200 baud device. Then switch your Uno to run at 4MHz, which would be 1/4 speed. At the top of your sketch:

#include <avr/power.h>

In the beginning of the setup function of your sketch:

clock_prescale_set(clock_div_4);

You can practice with those 2 lines of code in the blink sketch to see what I’m talking about. It should make the blink sketch run 4 seconds LED on, 4 seconds LED off, etc.

I dunno. This works fine for me at 300bps via the serial monitor.

void setup()
{
  Serial.begin(600);
  // This halves the baud rate
  UCSR0A &= ~(1<<U2X0);
}

void loop(){

  // Wait for input
  while (Serial.available() == 0);

  int val = Serial.read();

  //Echo the input
  Serial.print(val);
  Serial.print(" '");
  Serial.write(val);
  Serial.println("'");
  
//  Serial.write(val); // send a byte with the value 'val' 

  delay(1000);
}

Exactly what code are you running to try to get 300bps, and exactly what data do you see?

westfw:
I dunno. This works fine for me at 300bps via the serial monitor.

This is interesting that it works for you. My sketch is nearly identical to yours except for variations in the formatting of the print statements. But just to make sure I didn’t have some subtle typo I was overlooking, I copied your sketch and uploaded it to my board. I’ve attached the results.

300baud.PNG = Serial.begin(600); and serial monitor set to 300 baud

1200baud.PNG = Serial.begin(2400); and serial monitor set to 1200 baud

dmjlambert:
I’m thinking perhaps you could code your sketch to communicate with the device at 1200 baud just like it is a 1200 baud device. Then switch your Uno to run at 4MHz, which would be 1/4 speed.

I was hopeful this would work but got the same result. Initializing the serial to 1200 bps with the clock divider and monitor to 300 produced the same result as 300 baud.png. Setting serial to 4800 with clock divider and monitor to 1200 is the same as 1200baud.png.

It is strange. All alternatives work at higher speeds but not at 300. There seems to be something going on at that speed the atmega or UART doesn’t like.

Oh!

DUUUUUUH!

It's an official Uno with the 16u2?

The Atmega16u2 is probably using U2X mode, and 300 baud isn't possible in U2X mode. So even though you've set up the chip to give you 300 baud, the 16u2 can't be set to 300 baud.

This is actually not too uncommon - lots of usb serial chips have a minimum baud rate, often 1200 or 2400 (CH340G minimum is 2400 - FT232 can do 300 though)

The Atmega16u2 is probably using U2X mode, and 300 baud isn't possible in U2X mode. So even though you've set up the chip to give you 300 baud, the 16u2 can't be set to 300 baud.

Ah. That makes sense! That means it should still work connecting pins 0/1 to an actual 300bps device...

(My tests were with an older FTDI-based board.)

Wouldn’t it be cool if the 16u2’s default firmware warned the user? Like, it could just spam on the serial input “UNSUPPORTED BAUD RATE” - though if they were smart enough to do that, they could get the baud rate down to 300 just by turning off U2X so…

U2X is turned off for 57600 bps… It would be easy to do the same for 300.

https://github.com/arduino/Arduino/blob/master/hardware/arduino/avr/firmwares/atmegaxxu2/arduino-usbserial/Arduino-usbserial.c#L209

        /* Special case 57600 baud for compatibility with the ATmega328 bootloader. */
        UBRR1  = (CDCInterfaceInfo->State.LineEncoding.BaudRateBPS == 57600)
                         ? SERIAL_UBBRVAL(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS)
                         : SERIAL_2X_UBBRVAL(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS);

        UCSR1C = ConfigMask;
        UCSR1A = (CDCInterfaceInfo->State.LineEncoding.BaudRateBPS == 57600) ? 0 : (1 << U2X1);
        UCSR1B = ((1 << RXCIE1) | (1 << TXEN1) | (1 << RXEN1));

By the way, HardwareSerial turns U2X off when necessary, so Serial.begin(300) works just fine.

oqibidipo: By the way, HardwareSerial turns U2X off when necessary, so Serial.begin(300) works just fine.

This, of course, was the first thing I tried. And when that failed, I started looking for more exotic options. I have to run but you all have provided some other interesting ideas I'll explore when I get back. Thanks so much for the help and ideas.

HardwareSerial turns U2X off when necessary, so Serial.begin(300) works just fine.

So it does. Huh. I could swear that the last time I looked at that code, 57600bps was the only special case, but I can’t actually find any source where that’s true…

The xxxU2 code that does this seems to be broken, however:

        /* Special case 57600 baud for compatibility with the ATmega328 bootloader. */	
	UBRR1 = (CDCInterfaceInfo->State.LineEncoding.BaudRateBPS == 57600)
			 ? SERIAL_UBBRVAL(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS)
			 : SERIAL_2X_UBBRVAL(CDCInterfaceInfo->State.LineEncoding.BaudRateBPS);	

	UCSR1C = ConfigMask;
	UCSR1A = (CDCInterfaceInfo->State.LineEncoding.BaudRateBPS == 57600) ? 0 : (1 << U2X1);
	UCSR1B = ((1 << RXCIE1) | (1 << TXEN1) | (1 << RXEN1));

It unsets U2X, but it never recomputes the divisor for UBRR…

I've submitted https://github.com/arduino/Arduino/issues/4714

Sorry for the delayed reply but thanks again for the help and for submitting the issue. From the issue thread, it looks like there may be hope by updating the bootloader. This is all new to me (I'm a python guy), so it looks like I have some more learning to do re: burning a new bootloader. Thanks again!

bbrock: Sorry for the delayed reply but thanks again for the help and for submitting the issue. From the issue thread, it looks like there may be hope by updating the bootloader. This is all new to me (I'm a python guy), so it looks like I have some more learning to do re: burning a new bootloader. Thanks again!

Not the bootloader. The bootloader runs on the main processor (ATmega328P).

On reply #6 the conversation switched from talking about the main processor that runs your sketch, to the USB-to-serial chip, on your board it is probably an ATmega16U2. You can check with a magnifying glass the small chip next to the USB port. If it is ATmega16U2 or ATmega8U2, it has firmware on it that translates USB signal to serial. You are using that chip when communicating with serial monitor. westfw submitted a request for a fix to the firmware (for future versions).

If you are communicating with a device using pins 0 and 1 (not your computer serial monitor) the 300 baud rate is probably fine. You can connect to the Uno using an external FTDI adapter and probably use the serial monitor just fine. Or wait for a firmware update to be available, and then upload it to the ATmega16U2 chip using DFU or ISP.

burning a new bootloader.

It's actually the 16u2 firmware that needs to change. I think that that's easier than a bootloader, but also done less often. Note that the found bug affects the speed of the USB/Serial link. If you're talking to another device using pins 0/1, it should work OK now.

dmjlambert: Not the bootloader. The bootloader runs on the main processor (ATmega328P).

westfw: It's actually the 16u2 firmware that needs to change. I think that that's easier than a bootloader, but also done less often. Note that the found bug affects the speed of the USB/Serial link. If you're talking to another device using pins 0/1, it should work OK now.

Ah! Thanks for the clarification. I should have read more closely. And yes, I do have an ATmega16U2 chip on the board. But I would still need a programmer to change the firmware, correct?

Edit: Never mind. I found the instructions for updating the 16U2 using DFU here: https://www.arduino.cc/en/Hacking/DFUProgramming8U2

As for being able to communicate directly from pins 0 and 1, I'm waiting on delivery of a cheap USB/TTL converter so I can communicate with those pins without going through the 16U2. I'm assuming that will let me test if I'm communicating at 300 bps.

FIXED! I managed to upload the revised 16U2 firmware from here on github and am now able to communicate at 300 bps with the serial monitor. One amendment for anyone else who needs to do this, the readme in the arduino_usbserial folder provides instructions to download and unpack LUFA to re-compile the firmware. There is no need as LUFA is already included in the git package and if you follow the instruction to download LUFA, the paths in the makefile won't work. So just unpack the git package, uncomment the appropriate PID line as instructed and run make from within the directory.

When I posted my question, I had no idea it would result in someone recoding firmware. Hats off to this community. It is truly impressive.

Not sure if I should feel like a genius for figuring it out or like a dummy because I had no idea what I was doing. Either way, thanks for posting what you did to resolve this!