Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Serial port read returns no data with Arduino Leonardo #164

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

ksmathers
Copy link

I ran into a problem with an Arduino Leonardo board. When first plugged I was able to open the serial port without error, but could not read any data from the device. I tried explicitly setting each of the serial parameters, but the only thing that was working for me was to run PuTTY before running my own code that uses 'serial', in which case the port would work correctly, only to fail again the next time I plugged in the board.

This patch borrows the boilerplate device control block settings from PuTTY to initialize the serial device, which fixes the problem with 'serial'.

…ectly without having to be initialized by another application.
Copy link
Owner

@wjwwood wjwwood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the pull request! I had some comments and requested changes, but after those I think it should be ok to take them.

@@ -70,7 +70,7 @@ Serial::SerialImpl::open ()
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
FILE_ATTRIBUTE_NORMAL, // FILE_FLAG_OVERLAPPED
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change doesn't seem to do anything, can you please remove it?

dcbSerialParams.fNull = FALSE;
dcbSerialParams.fRtsControl = RTS_CONTROL_ENABLE;
dcbSerialParams.fAbortOnError = FALSE;
dcbSerialParams.fOutxCtsFlow = FALSE;
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of these settings are set lower in this function, so it seems this line will not have any effect. Can you only set the ones that are not set elsewhere? These are the ones I see that are redundant, but you should double check me :)

  • fOutX
  • fInX
  • fRtsControl
  • fOutxCtsFlow

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw I was seeing the same thing with an Arduino uno and was able to get it working by adding only the following line:

dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE;

woollybah added a commit to maxmods/bah.mod that referenced this pull request Oct 26, 2018
On the off-chance that some of this is generally useful.
@GordonLElliott
Copy link

GordonLElliott commented Feb 10, 2021

Like @tmpvar, I was able to get Arduino Leonardo (compatible) to work with a simple change to DTR. I also enabled RTS, for standard compatibility with terminals, and I have not tested to be sure that is not needed for Leonardo, but in my opinion not relevant.

The DTR and RTS signals out of an RS232 port have more or less standard meanings. And a standard port those signals are always being output (whether or not the device at other end of the cable receives or implements those signals).

The settings DTR_CONTROL_ENABLE, DTR_CONTROL_DISABLE do not in fact "enable" the interface, rather they set the DTR pin output or function on and off, respectively.

The normal handshake on a device (from the old days of RS232) would be to wait for DTR to be turned "on" before attempting communication. Then the RTS "on" condition would allow transfer of data (but this is part of the flow control handshake). So normally one would turn both signals "on" unless they were specifically in use for flow control as a default, in case the hardware at the far end did depend upon them, even if flow control is not implemented. (By the way, flow control on RTS would then turn RTS on to allow transfer of data, and off to stop transfer--thus the default of "on" when not using flow control at the PC end just in case so it does not stop such data transfer .)

All this said, I made the following code changes in win.cc's Serial::SerialImpl::reconfigurePort function. You will notice that I added another mode for flow control (not a matter of this thread's discussion), but thought it would be informative to show that as well. (I have not tested these modes yet, but since they mimic the apparently working flow control mode I assume the new mode will work--once again not topic of this thread.)

The key point is that after these changes, the Arduino Leonardo started to work. And these changes should make the defaults more compatible for other applications.

  // setup flowcontrol
  if (flowcontrol_ == flowcontrol_none) {
    dcbSerialParams.fOutxCtsFlow = false;
    dcbSerialParams.fOutxDsrFlow = false;    // [GLE]
    dcbSerialParams.fRtsControl = RTS_CONTROL_ENABLE; // RTS_CONTROL_DISABLE; // [GLE] enable
    dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE; //DTR_CONTROL_DISABLE;  // [GLE] add, enable
    dcbSerialParams.fOutX = false;
    dcbSerialParams.fInX = false;
  }
  if (flowcontrol_ == flowcontrol_software) {
    dcbSerialParams.fOutxCtsFlow = false;
    dcbSerialParams.fOutxDsrFlow = false;    // [GLE]
    dcbSerialParams.fRtsControl = RTS_CONTROL_ENABLE; // RTS_CONTROL_DISABLE; // [GLE] enable
    dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE; //DTR_CONTROL_DISABLE;  // [GLE] add, enable
    dcbSerialParams.fOutX = true;
    dcbSerialParams.fInX = true;
  }
  if (flowcontrol_ == flowcontrol_hardware) {
    dcbSerialParams.fOutxCtsFlow = true;
    dcbSerialParams.fOutxDsrFlow = false;    // [GLE]
    dcbSerialParams.fRtsControl = RTS_CONTROL_HANDSHAKE;
    dcbSerialParams.fDtrControl = DTR_CONTROL_ENABLE; //DTR_CONTROL_DISABLE;  // [GLE] add, enable
    dcbSerialParams.fOutX = false;
    dcbSerialParams.fInX = false;
  }
  if (flowcontrol_ == flowcontrol_hardware_DTR_DSR) {
      dcbSerialParams.fOutxCtsFlow = false;
      dcbSerialParams.fOutxDsrFlow = true;    // [GLE]
      dcbSerialParams.fRtsControl = RTS_CONTROL_ENABLE; // RTS_CONTROL_DISABLE; // [GLE] enable
      dcbSerialParams.fDtrControl = DTR_CONTROL_HANDSHAKE;  // [GLE]
      dcbSerialParams.fOutX = false;
      dcbSerialParams.fInX = false;
  }

Also note that @wjwood commented on the line dcbSerialParams.fRtsControl = RTS_CONTROL_ENABLE; in code above, which is also to be found in my code. Yes, there are low level functions, reflected to high level functions, that will enable those.

To answer the question @wjwood asked, these settings continue with their default values (off in the original code) until a top level function invokes the DTR or RTS function. Either an RTS or a DTR on setting is required for the Arduino to communicate via the port. The example code does not do this, thus for an Arduino the RTS and DTR signals are off, apparently stopping it from working. (Another interesting note is that an Arduino Leonardo has no actual hardware port, and these settings are virtual in software port--furthermore the baud rate is ignored and communicates at full rate of the USB bus.)

How this should be handled is an interesting question. If an application is going to control those functions then the defaults shown here are probably wrong, and the "DISABLE" variants should probably be the default, so that the high level program enables those. However the example serial code does not do that, and using those high level functions to enable DTR would most likely allow the Leonardo to work as well. If used, it makes more sense that the Data Terminal Ready (the PC is the "Data Terminal" in its role using the old terminology of RS232) and it should not be made ready until the application is ready.

So the above is not a complete or compelling set of changes, rather just a demonstration of a method that works when the high level application does not enable the pins. Perhaps a better method would be to revert to default 'DISABLE' states for both RTS and DTR, or build a more complex tracking mechanism so the reconfiguration follows a state variable for each. The tracking problem exists because if the high level code set RTS, then changed to flow control on RTS, then back to no flow control, the high level setting of "on" would need to be preserved, or else it would mysteriously turn off or would need to be re-implemented after the flow control change.

That could be the subject of a future change (beyond the topic here -- but at the heart of the problem that causes the Arduino and/or Arduino Leonardo to not work in the example code).

By the way, there are other issues with Windows 10 implementation that must be fixed to work with Arduino, also separate topics. Turning on DTR seems to allow Arduino to work.

dschr pushed a commit to john-wigg/LaserDiodeDriver that referenced this pull request Sep 13, 2021
@dschr
Copy link

dschr commented Sep 13, 2021

I switched from an Arduino Nano (328P) to an Arduino Nano 33 BLE and ran into the issue that commands sent via serial to the Arduino were no longer executed. By using the proposed patch of @GordonLElliott , I could solve my issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants