RS485: Inappropriate ioctl for device
I think it might add some value for others facing similar issues in the future to elaborate a bit more on Rodney's answer from a hardware side.
Although I came here looking for the answer to the thread question (why was I getting an ioctl error trying to activate the RS485 mode on pyserial (which I was actually calling from pyModbus) I found the answer to that question above. Plain and simple, and to provide a shorter answer than Rodney's: you can't. You won't be able to activate the RS485 mode on your FTDI because there is no function to use the RTS signal as a drive enable for your RS485 chip.
Some devices, like those using the atmel_serial driver do implement this functionality, but you won't find that many. Digging into this issue you'll find people saying you can write your own driver based on one of those that support the RS485 mode (see, for instance: automatically changing RTS for RS-485 communication, but in my view they are wrong)
Now that you have the bad news, you can ask yourself why would you need to use the RS485 mode? I imagine that, like me, you were trying to establish a half-duplex link between two devices using a two wire RS485 setup (which is surprisingly frequent in many industrial devices where you have a master-slave configuration, where you have one side sending a request for data and the other side duly responding, as you do with Modbus).
If you are looking for a software solution you can still take the RTS signal high and low manually before starting and after finishing transmitting from each side. This solution will work for testing and just for fun in most environments (at least those I tried, it even works with Python on a Raspberry Pi using a GPIO line to control the Drive Enable/~Read Enable signals) but I would not use it if you want to get good reliability. With any non-real time OS there is no warranty you will get the correct timing and some devices are quite picky and will report a timeout error. If you are free to choose both sides (or all of them if you have more than two stations) of the bus you can always increase your tiemout everywhere and hope for the best.
Good news is, as Rodney said, on the FTDI chip you have the TXDEN signal available on pin 13 so you can use that right away. Unfortunately, most cheap USB to serial adaptor PCBs don't have that signal easily accessible on the board. That's what happened to my Sparkfun board. If you are not up for the challenge of attaching a cable directly to the SMD IC you can do what I did: download mprog 3.5 from FTDI, connect to the EPROM, under I/O Controls select TXDEN instead of TXLED and click save to write the updated firmware to the chip. Then you can easily attach a cable to the LED pad and use it as your Drive Enable/~Read Enable signal (see pictures, the scope snapshot shows it works nicely).
Modified FTDI board with TXDEN signal on TXLED
Scope capture of the modified circuit: blue is TX, yellow TXDEN
Lastly, if you are unlucky enough to run into other chips that don't have this option (TXDEN), like the PL2303 you can always go old school and build a hardware TXDEN with a 555 timer (see here: http://www.embeddedsys.com/subpages/resources/images/documents/microsys_art_RS485.pdf). I tested this circuit and it works fine, at least at 9600 bps (note that I had to use a 39K resistor instead of a 3.9K, I think that's a typo but I might be wrong, I did not go into fine details, I just needed something quick and dirty to pair with my FTDI.
As mentioned in the comment by @Richard Chambers you are using the structure uninitialised. You should do
struct serial_rs485 rs485conf = {0};
However, as discussed that didn't solve the problem.
Quoting from the Kernel document you referenced
Some CPUs/UARTs (e.g., Atmel AT91 or 16C950 UART) contain a built-in half-duplex mode capable of automatically controlling line direction by toggling RTS or DTR signals. That can be used to control external half-duplex hardware like an RS485 transceiver...
However this device has a dedicated pin called TXDEN for that. Quoting from the Data sheet section 4.3.3
With RS485, the transmitter is only enabled when a character is being transmitted from the UART. The TXDEN pins on the FT4232H are provided for exactly that purpose, and so the transmitter enables are wired to the TXDEN
and you can see that the RS232 outputs RTS / DTR not connected in RS485, this is in contrast to devices where RTS becomes TXDEN which would require an intervention by the driver.
If we pick apart struct serial_rs485
we can see that it is mostly about controlling RTS for this purpose. Since this device has a dedicated TXDEN pin, those control fields are irrelevant.
Another flag that is used in this structure:
/* Set this flag if you want to receive data even whilst sending data */
rs485conf.flags |= SER_RS485_RX_DURING_TX;
And yes looking at the circuit, what you put out on the line you will also receive. Looks like you cannot turn that off. RS485 is multidrop, so you should be filtering out messages that were not addressed to you anyway. The fact that some of those messages may have originated from you doesn't matter.
Finally (and firstly) we have this
/* Enable RS485 mode: */
rs485conf.flags |= SER_RS485_ENABLED;
That's really saying "enable all the RS485-specific stuff that's controlled by this structure". Except that we've just made all that stuff irrelevant, so enabling it has no effect.
This is why the ioctl is not implemented for that UART.
You have a number of options, these are just suggestions so pick what suits you
- Delete this section of the initializtion code as it's not needed
- Compile it conditionally
#if RS485_IOCTLS
- Run it conditionally
- Treat that
errno
valueENOTTY
as indicating that, in this context theioctl
was not required and you can in fact proceed as if there was no error