What are the differences in serial communication between PC <---> Arduino UNO (ATMEGA328p) and PC <---> Arduino Leonardo (ATMEGA32u4)?
I am asking because of a problem related to using these two Arduino boards for data logging in the following way:
- PC sends a specific char string to Arduino, via serial port, to initialize datalogging
- Arduino measures data, let's say a fixed number of ADC readings
- Arduino stops measuring, sends data back to the PC over serial port
- Arduino waits until new serial data are received before logging new data
If I send/receive data via the Arduino IDE serial monitor, this procedure works on both boards. However, I want a separate GUI program (Qt) to handle the serial communication, where one clicks a button to send the data in step 1, and plot the data from step 3 on a graph.
I wrote a simple Qt GUI where a button is clicked, and the measured data are printed to the GUI screen. Observations while testing it:
- Arduino UNO as datalogger: Arduino is plugged into USB socket of PC, then I launch the GUI, then click button to start data acquisition. The data are shown. Program appears to work fine.
- Arduino Leonardo as datalogger: Arduino is plugged into USB socket of PC, then I launch the GUI, then click button to start data acquisition. The data are not received, program hangs.
Then
- Arduino Leonardo as datalogger: Arduino is plugged into USB socket of PC, Open Arduino IDE serial monitor, send command to acquire data. Then close Arduino IDE serial monitor, launch the GUI, click button to start data acquisition. The GUI program now works.
Long story, but basically there appears to be a difference when using the two Arduino boards with external programs to do serial communication. Can someone outline the differences and why Leonardo needs the "jump start" I described above using Arduino serial monitor? Ultimately I want to fix the problem and avoid that hack.
Reluctant to post Qt code on Arduino stack exchange, but here is how I start the serial port:
ui->setupUi(this);
microcontroller_is_available = false;
microcontroller_port_name = "false";
microcontroller = new QSerialPort(this);
qDebug() << "Number of available ports: " << QSerialPortInfo::availablePorts().length();
foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()) {
qDebug() << "Has product ID " << serialPortInfo.hasProductIdentifier();
if(serialPortInfo.hasProductIdentifier()){qDebug() << "Product ID: " << serialPortInfo.productIdentifier();}
qDebug() << "Has vendor ID " << serialPortInfo.hasVendorIdentifier();
if(serialPortInfo.hasVendorIdentifier()){qDebug() << "Vendor ID: " << serialPortInfo.vendorIdentifier();}
}
foreach (const QSerialPortInfo &serialPortInfo, QSerialPortInfo::availablePorts()) {
if(serialPortInfo.hasVendorIdentifier() && serialPortInfo.hasProductIdentifier()){
if(serialPortInfo.vendorIdentifier() == micro_vendor_id){
if(serialPortInfo.productIdentifier() == micro_product_id){
microcontroller_port_name = serialPortInfo.portName();
microcontroller_is_available = true;
}
}
}
}
if(microcontroller_is_available){ // open and configure serialport
microcontroller->setPortName(microcontroller_port_name);
microcontroller->open(QSerialPort::ReadWrite);
microcontroller->setBaudRate(QSerialPort::Baud115200);
microcontroller->setDataBits(QSerialPort::Data8);
microcontroller->setParity(QSerialPort::NoParity);
microcontroller->setStopBits(QSerialPort::OneStop);
microcontroller->setFlowControl(QSerialPort::NoFlowControl);
}
else{QMessageBox::warning(this, "Port error", "Cannot connect DAQ.");}
Setup part of my Arduino code:
void setup(){
DDRB |= B11111111; // (654210 outputs, miso input)
PORTB &= ~_BV(RF_AMPL);
DDRD |= B1100010;
PORTD |= (_BV(SRAM_HOLD) | _BV(SRAM_SS1));
ADCSRA=0;
SPCR = B01010000; // SPI init (could get away with 11010000 on 328p but not 32u4)
SPSR = B00000001;
PORTD &= ~_BV(SRAM_SS1); // Slave init
SPDR=WRMR; while(!(SPSR & (1<<SPIF)));
SPDR=SEQUENTIAL_MODE; while(!(SPSR & (1<<SPIF)));
PORTD |= _BV(SRAM_SS1);
__builtin_avr_delay_cycles(160);
PORTD &= ~_BV(SRAM_SS1);
SPDR=RDMR; while(!(SPSR & (1<<SPIF)));
SPDR=0x00; while(!(SPSR & (1<<SPIF)));
PORTD |= (_BV(SRAM_HOLD) | _BV(SRAM_SS1));
state1=0;
count=0;
pulse_count=0;
serial_counter=0;
Serial.begin(115200); while(!Serial){}
}
2 Answers 2
Yes, there are differences.
- The Uno has an ATMega16U2 as a USB to UART bridge. When you open the serial port the ATMega16U2 signals to the ATMega328p to reset and start your sketch from the beginning.
- The Leonardo has an ATMega32U4 as the main MCU which is directly connected to the USB port. When you open the serial port you connect to your program wherever it happens to be at the time.
The key thing there is that when you open the Uno's port the board reboots and your sketch restarts. When you open the Leonardo's port the board doesn't reset and your sketch is wherever your sketch happens to be.
So: you have to ensure that your sketch doesn't rely on things in setup() or early on in its execution for making the serial connection work.
-
I already followed the Arduino guide to use
Serial.begin(115200); while(!Serial){}
. Setup code posted above (minus pin definitions). Can you see anything obviously wrong? Same Arduino sketch is used for UNO and Leonardo.MichaelT– MichaelT2018年07月15日 14:20:02 +00:00Commented Jul 15, 2018 at 14:20 -
Would it be possible on Leonardo device to print a file / data via serial to the PC using the USB port? So basically writing a file to the PC. I would love to know how if that is possible instead of only using the ATMega32U4 with the Keyboard library.Melroy van den Berg– Melroy van den Berg2023年10月01日 18:17:25 +00:00Commented Oct 1, 2023 at 18:17
-
To answer my own question, I think I will give Lufa a try: github.com/Palatis/Arduino-LufaMelroy van den Berg– Melroy van den Berg2023年10月01日 18:43:48 +00:00Commented Oct 1, 2023 at 18:43
I solved this by resetting the Arduino (open serial port at 1200 baud, then close serial port) when launching the Qt program.
Here is the relevant part of the Qt program:
microcontroller->setPortName(microcontroller_port_name);
microcontroller->open(QSerialPort::ReadWrite);
microcontroller->setBaudRate(QSerialPort::Baud1200);
microcontroller->setDataBits(QSerialPort::Data8);
microcontroller->setParity(QSerialPort::NoParity);
microcontroller->setStopBits(QSerialPort::OneStop);
microcontroller->setFlowControl(QSerialPort::HardwareControl);
qDebug() << "open 1200 baud";
Sleep(10);
microcontroller->close();
Sleep(10);
microcontroller->open(QSerialPort::ReadWrite);
microcontroller->setBaudRate(QSerialPort::Baud115200);
microcontroller->setDataBits(QSerialPort::Data8);
microcontroller->setParity(QSerialPort::NoParity);
microcontroller->setStopBits(QSerialPort::OneStop);
microcontroller->setFlowControl(QSerialPort::HardwareControl);
Now whenever I launch the Qt program, the Arduino is ready to accept serial data.
-
It's OK to accept your answer if it is the solution to the problem.VE7JRO– VE7JRO2019年08月09日 19:50:38 +00:00Commented Aug 9, 2019 at 19:50
Explore related questions
See similar questions with these tags.
plug in leonardo -- then reset leonardo -- then start GUI
. . . . . . . . . . . . . . . or,plug in -- start GUI -- reset
?