USB to serial chips are often used as a debug interface either directly on the target board, or via a dedicated debug board. But some models have extra pins exposed, and one of those is WCH CH341, which also includes I2C & SPI interfaces and up to 8 GPIOs.

But software support for those extra pins is not currently built-in into the drivers found in Linux mainline, and you’d also have to find a board that breakout the relevant pins. It turns out there are few of things including “CH341A ALL IN 1 USB to SPI/I2C/IIC/UART/TTL/ISP serial adapter” board going for $10 shipped on Aliexpress, and which Zoobab successfully used to control 6 (out of 8) GPIOs over USB.

The board comes with a USB board to connect to your computer, several header for I2C, UART, SPI, some LEDs, and jumper to select I2C/SPI or UART mode and voltage (5V/3.3V).

The board is recognized differently whether you use I2C/SPI or UART mode in Linux:

I2C-SPI mode

$ lsusb Bus 002 Device 003: ID 1a86:5512 QinHeng Electronics CH341 in EPP/MEM/I2C mode, EPP/I2C adapter $ dmesg [ 1739.299811] usb 2-1.2: new full-speed USB device number 3 using ehci-pci [ 1739.385559] usb 2-1.2: New USB device found, idVendor=1a86, idProduct=5512 [ 1739.385565] usb 2-1.2: New USB device strings: Mfr=0, Product=0, SerialNumber=0 1 2 3 4 5 6 $ lsusb Bus 002 Device 003 : ID 1a86 : 5512 QinHeng Electronics CH341 in EPP / MEM / I2C mode , EPP / I2C adapter $ dmesg [ 1739.299811 ] usb 2 - 1.2 : new full - speed USB device number 3 using ehci - pci [ 1739.385559 ] usb 2 - 1.2 : New USB device found , idVendor = 1a86 , idProduct = 5512 [ 1739.385565 ] usb 2 - 1.2 : New USB device strings : Mfr = 0 , Product = 0 , SerialNumber = 0

UART mode

$ lsusb Bus 002 Device 004: ID 1a86:5523 QinHeng Electronics CH341 in serial mode, usb to serial port converter $ dmesg [ 1982.227595] usb 2-1.2: new full-speed USB device number 5 using ehci-pci [ 1982.313544] usb 2-1.2: New USB device found, idVendor=1a86, idProduct=5523 [ 1982.313550] usb 2-1.2: New USB device strings: Mfr=0, Product=0, SerialNumber=0 [ 1982.314088] ch341 2-1.2:1.0: ch341-uart converter detected [ 1982.315989] usb 2-1.2: ch341-uart converter now attached to ttyUSB0 1 2 3 4 5 6 7 8 $ lsusb Bus 002 Device 004 : ID 1a86 : 5523 QinHeng Electronics CH341 in serial mode , usb to serial port converter $ dmesg [ 1982.227595 ] usb 2 - 1.2 : new full - speed USB device number 5 using ehci - pci [ 1982.313544 ] usb 2 - 1.2 : New USB device found , idVendor = 1a86 , idProduct = 5523 [ 1982.313550 ] usb 2 - 1.2 : New USB device strings : Mfr = 0 , Product = 0 , SerialNumber = 0 [ 1982.314088 ] ch341 2 - 1.2 : 1.0 : ch341 - uart converter detected [ 1982.315989 ] usb 2 - 1.2 : ch341 - uart converter now attached to ttyUSB0

You’ll however need the out of tree i2c-ch341-usb driver to expose the 8 GPIOs and control them over USB/I2C. He set the direction to output for 6 of the pin in the source code (6-7 could not be set to output for whatever reason):

struct ch341_pin_config ch341_board_config[CH341_GPIO_NUM_PINS] = { // pin GPIO mode GPIO name hwirq { 15, CH341_PIN_MODE_OUT , "gpio0" , 0 }, // used as output { 16, CH341_PIN_MODE_OUT , "gpio1" , 0 }, // used as output { 17, CH341_PIN_MODE_OUT , "gpio2" , 0 }, // used as output { 18, CH341_PIN_MODE_OUT , "gpio3" , 0 }, // used as output { 19, CH341_PIN_MODE_OUT , "gpio4" , 1 }, // used as output with hardware IRQ { 20, CH341_PIN_MODE_OUT , "gpio5" , 0 }, // used as ouput { 21, CH341_PIN_MODE_IN , "gpio6" , 0 }, // used as input { 22, CH341_PIN_MODE_IN , "gpio7" , 0 } // used as input }; 1 2 3 4 5 6 7 8 9 10 11 12 struct ch341_pin_config ch341_board_config [ CH341_GPIO_NUM_PINS ] = { // pin GPIO mode GPIO name hwirq { 15 , CH341_PIN_MODE_OUT , "gpio0" , 0 } , // used as output { 16 , CH341_PIN_MODE_OUT , "gpio1" , 0 } , // used as output { 17 , CH341_PIN_MODE_OUT , "gpio2" , 0 } , // used as output { 18 , CH341_PIN_MODE_OUT , "gpio3" , 0 } , // used as output { 19 , CH341_PIN_MODE_OUT , "gpio4" , 1 } , // used as output with hardware IRQ { 20 , CH341_PIN_MODE_OUT , "gpio5" , 0 } , // used as ouput { 21 , CH341_PIN_MODE_IN , "gpio6" , 0 } , // used as input { 22 , CH341_PIN_MODE_IN , "gpio7" , 0 } // used as input } ;

After after build the module, and load the module:



make sudo make install sudo modprobe i2c-ch341-usb 1 2 3 make sudo make install sudo modprobe i2c - ch341 - usb



gpio0 to gpio5 would should in /sys/class/gpio, which means you can control them with the usual sysfs commands:



echo 0 > /sys/class/gpio/gpio1/value echo 1 > /sys/class/gpio/gpio1/value 1 2 echo 0 > / sys / class / gpio / gpio1 / value echo 1 > / sys / class / gpio / gpio1 / value



He also benchmarked the pins with a shell script to turn on and off connected LED, and managed to do that at around 2.2KHz. It may be a little faster by changing the I2C bus speed (100 kHZ by default) and/or using C code instead. Alternatively, using spi-ch341-usb driver from the same developer (gschorcht) may speed things up a little bit despite the documented “slow SPI”.

Another shell script with 6 LEDs connected to the board is demonstrated below.

Anyway that good news since that means you can add (extra) GPIOs to any Linux board with USB relatively easily and cheaply. But if you’re not quite willing to spending $10 on that option, there are cheaper options such as a CH341 USB programmer going for $2.30 + shipping on Electrodragon, or CJMCU-341 board sold for around $5 including shipping on Aliexpress or eBay.