FTDI FT232RL: Bit Bang Mode (Linux) [Part 2]

FTDI FT232RL: Bit Bang Mode (Linux) [Part 2]

In the previous post, we covered the installation of FTDI drivers on Ubuntu. This section focuses on controlling GPIO using a Python script.

FTDI has 8 digital pins used for serial connections (RXD, TXD, RTS, CTS, DTR, DSR, DCD, RI) and 5 CBUS pins. Initially, I thought to use the CBUS pins on my module since they were exposed and assumed they were especially designed for GPIO control. However, I was mistaken when I first looked into the FT232RL schematic diagram. Here is a typical one:

As we can see, CBUS0 and CBUS1 are reserved for the TX and RX LEDs. CBUS2 is used with RS485 receivers, and CBUS3 and CBUS4 have system functions. This behavior can be modified by changing the EEPROM, but it seems complicated since there is no documentation describing the possible EEPROM values. Additionally, the documentation recommends using only the Windows executable FT_PROG.

We will be using RXD, TXD, RTS, CTS, DTR, DSR, DCD, RI as GPIO pins. To summarize the information from the application notes and datasheet for the FT232RL, I have compiled it into a clear table:

UART Pin Configuration

Signal Pin No D2XX Signal
TXD PIN_1 D0
RXD PIN_5 D1
RTS PIN_3 D2
CTS PIN_11 D3
DTR PIN_2 D4
DSR PIN_9 D5
DCD PIN_10 D6
RI PIN_6 D7

The Python code to make the RXD pin to trigger HIGH for a second and then back to LOW:

import ftd2xx
import time
import sys
from enum import Enum

# Define the PINNames enum with relevant pins
class PINNames(Enum):
    TXD = 0  # PIN_1 | D0
    RXD = 1  # PIN_5 | D1 
    RTS = 2  # PIN_3 | D2 
    CTS = 3  # PIN_11| D3 
    DTR = 4  # PIN_2 | D4
    DSR = 5  # PIN_9 | D5
    DCD = 6  # PIN_10| D6
    RI  = 7  # PIN_6 | D7

class FT232RLDevice:
    def __init__(self, device_index=0):
        try:
            self.dev = ftd2xx.open(device_index)
            print("FT232RL device opened successfully.")
            self.previous_state = 0x00  # Initialize all pins to LOW
            self.dev.setBitMode(0x00, 0x01)  # Set to asynchronous bit-bang mode
            print("Bit mode set to asynchronous bit-bang mode.")
        except ftd2xx.DeviceError as e:
            print(f"Error opening FT232RL device: {e}")
            sys.exit(1)
    
    def write_pin(self, pin: PINNames, value: bool):
        bit_position = pin.value
        if value:
            self.previous_state |= (1 << bit_position)  # Set the bit
        else:
            self.previous_state &= ~(1 << bit_position)  # Clear the bit
        try:
            self.dev.write(bytes([self.previous_state]))
            print(f"Set {pin.name} to {'HIGH' if value else 'LOW'}.")
        except ftd2xx.DeviceError as e:
            print(f"Failed to write to device: {e}")
    
    def set_pins_as_output(self, pins):
        bitmask = 0x00
        for pin in pins:
            bitmask |= (1 << pin.value)
        try:
            self.dev.setBitMode(bitmask, 0x01)  # 0x01 for asynchronous bit-bang mode
            print(f"Pins {[pin.name for pin in pins]} set as OUTPUT.")
        except ftd2xx.DeviceError as e:
            print(f"Failed to set bit mode: {e}")
            sys.exit(1)
    
    def close(self):
        try:
            self.dev.close()
            print("FT232RL device closed.")
        except ftd2xx.DeviceError as e:
            print(f"Failed to close device: {e}")

def toggle_rxd_pin():
    # Instantiate the FT232RL device
    device = FT232RLDevice()
    
    # Set RXD as output
    device.set_pins_as_output([PINNames.RXD])
    
    try:
        # Set RXD high
        device.write_pin(PINNames.RXD, True)
        print("RXD is HIGH for 1 second.")
        
        # Wait for 1 second
        time.sleep(1)
        
        # Set RXD low
        device.write_pin(PINNames.RXD, False)
        print("RXD is LOW.")
    finally:
        # Ensure the device is closed properly
        device.close()

if __name__ == '__main__':
    toggle_rxd_pin()
0:00
/0:02

As a result, we see the LED blink for a second. Great! In the video above, I have an optocoupler module hooked to the FT232RL adapter to opto-isolate the controlled pins. I will use it to reset the Sonoff NSPanel Pro 120 into different DFU modes in the upcoming articles. Stay tuned.