The baudrates are derived from the chip's internal clock (usually 12, 48 or 96 MHz) by setting up the divider registers.
Some chips support only a defined set of baud rates and won't operate on anything else.
Others (eg. CP2102) have rewritable table of supported baudrates and associated dividers.
Yet others (eg. FT232R) support arbitrary baudrates out of the box.
Pinout:
USB-serial Ardu ESP male female female RTS RESET pin added; DTR RESET GPIO0 programming selector for ESP32/ESP8266, pulled H for normal boot, L for prog; to RESET via capacitor on arduinos Rx Tx Tx Tx Rx Rx +5V +5V +5V(to-3.3v) some boards need 3.3v stabilizer; may be disconnected for internally powered devices, or via diode GND GND GND +3.3V pin removed, sometimes used separately with pigtail
WARN: on linux, DTR is ALWAYS pulsed on port open (kernel driver issue). One would think after such time they'd put in an IOCTL to disable this if needed. On port open, standard arduino will reset.
Moving reset pin from DTR to better controllable RTS may be helpful here. Modifying the reset tool to pulse both DTR and RTS may help. Customizing the reset circuitry with bigger series cap and smaller cap to the ground on the arduino side may tune the reset circuit to require long enough DTR pulses to make it insensitive to situations when port is open and DTR is then immediately commanded to unset.
adapter between Tasmota ESP8266/ESP32 devices and Arduinos
Pinout:
Arduino ESPxxxx n/c RESET reset for ESP, not connected RESET --------------- GPIO0 GPIO0 used as ArduSlave reset Tx ---R1k----\/--- Tx ESP Tx connected via 1k resistor to Ardu Rx (can be direct, resistor to protect ESP in case slave rebels and puts 5V high on its Rx pin; must be H at ESP boot or ESP won't start) Rx ---R1k----/\*-- Rx ESP Rx connected via resistor divider to Ardu Tx (to convert 5v to 3.3v; can use a zener instead) +5V ------------|-- +5V 5V power bus; may be powered from either side or from a side connector GND ----*-------|-- GND GND, what else to say \--R2k2-
flavors:
package xtal eeprom temperature CH340B SOP-16 internal built-in -20..+70 reset output, reset input, Tx-in-progress output (pin 15) CH340C SOP-16 internal -20..+70 second most common CH340E MSOP-10 internal -20..+70 no DCD,RI,DSR,DTR; Tx-in-progress output (pin 6) CH340G SOP-16 external n/a -40..+85 most common "in the wild" CH340R SSOP-20 external -40..+85 USB-config-complete output, forbid-device-suspend input;for infrared/IrDA, reverse polarity Tx and modem signals; selectable InfraredSIR/commonUART by pin CH340N SOIC-8 internal only Rx,Tx,RTS; relabeled CH330N? datasheet CH340T SSOP-20 external -40..+85 USB-config-complete output, forbid-device-suspend input, Tx-in-progress output (pin 17)
NOT CONFIGURABLE PRODUCT STRING! Cannot differentiate between devices by /dev/serial/by-id!!! (except variant B which is very uncommon)
Can differentiate between devices by /dev/serial/by-id, can use product-ID in automatic udev rules, can define arbitrary baudrates albeit with some leg work.
cp210x-program
example output:
[usb device] product_string = CP2102 Arduino USB-serial interface serial_number = 0001 vendor_id = 10C4 product_id = EA60 version = 1.00 bus_powered = no max_power = 100 locked = no part_number = 2 vendor_string = Silicon Labs [baudrate table] 1500000 = FFF0, FFFA, 1 # 1500000 Baud, 12 us 1500000 = FFF0, FFFA, 1 # 1500000 Baud, 12 us 1200000 = FFEC, FFF8, 1 # 1200000 Baud, 16 us 921600 = FFE6, FFF6, 1 # 923077 Baud, 20 us 576000 = FFD6, FFF0, 1 # 571429 Baud, 32 us 500000 = FFD0, FFEE, 1 # 500000 Baud, 36 us 460800 = FFCC, FFEC, 1 # 461538 Baud, 40 us 256000 = FFA2, FFDC, 1 # 255319 Baud, 72 us 250000 = FFA0, FFDC, 1 # 250000 Baud, 72 us 230400 = FF98, FFD9, 1 # 230769 Baud, 78 us 153600 = FF64, FFC5, 1 # 153846 Baud, 118 us 128000 = FF44, FFB9, 1 # 127660 Baud, 142 us 115200 = FF30, FFB2, 1 # 115385 Baud, 156 us 76800 = FEC8, FF8B, 1 # 76923 Baud, 234 us 64000 = FE89, FF73, 1 # 64000 Baud, 282 us 57600 = FE5F, FF63, 1 # 57554 Baud, 314 us 56000 = FE53, FF5F, 1 # 55944 Baud, 322 us 51200 = FE2B, FF50, 1 # 51173 Baud, 352 us 38400 = FD8F, FF15, 1 # 38400 Baud, 470 us 28800 = FCBF, FEC7, 1 # 28812 Baud, 626 us 19200 = FB1E, FE2B, 1 # 19200 Baud, 938 us 16000 = FA24, FE0C, 1 # 16000 Baud, 1.000 ms 14400 = F97D, FE0C, 1 # 14397 Baud, 1.000 ms 9600 = F63C, FE0C, 1 # 9600 Baud, 1.000 ms 7200 = F2FB, FE0C, 1 # 7201 Baud, 1.000 ms 4800 = EC78, FE0C, 1 # 4800 Baud, 1.000 ms 4000 = E890, FE0C, 1 # 4000 Baud, 1.000 ms 2400 = D8F0, FE0C, 1 # 2400 Baud, 1.000 ms 1800 = CBEB, FE0C, 1 # 1800 Baud, 1.000 ms 1200 = B1E0, FE0C, 1 # 1200 Baud, 1.000 ms 600 = 63C0, FE0C, 1 # 600 Baud, 1.000 ms 300 = B1E0, FE0C, 4 # 300 Baud, 1.000 ms
Can differentiate between devices by /dev/serial/by-id, can use product-ID in automatic udev rules, can use arbitrary baudrates.
ftdi-eeprom
FT has a whole family of chips. FT232/245B and R for standard speeds, FT2232D for standard speed two-channel, FT232H for high speed, 2232H for high speed dual and 4232H for quad. The newer FT-X subfamily has many options differing in size, pin number, purpose... from full and basic UART to 8bit and 4bit SPI, I2C, and 8bit FIFO.
No TX-in-progress output. Configurable product string.
discriminate between devices by their path, derived from the physical connector they are plugged into, as of /dev/serial/by-path/
examples:
physically labeling the ports with numbers is helpful for such configurations
should be possible to write udev rule that creates /dev/ttyUSB_x.y.z port from physical port, may need a RUN or PROGRAM command in the rule
---------- --------- --------- | | | USB 2 | | USB 4 | | ETHERNET | | ------- | | ------- | | | | USB 3 | | USB 5 | ======================================== port 1 == internal ethernet card, SMSC95xx
in /etc/udev/rules/99-usb.rules (or whatever):
ACTION=="add", SUBSYSTEM=="tty", SUBSYSTEMS=="usb" PROGRAM="/usr/bin/usbserial-getdevpath.sh %k" SYMLINK+="%c"
in /usr/bin/usbserial-getdevpath.sh:
#!/bin/bash x=`echo "$ID_PATH"|cut -d ':' -f 2` if [ "${x:0:2}" == "1." ]; then x=${x:2}; else exit 1; fi echo "ttyUSB.$x"
On raspberry pi the USB devices always start with platform-3f980000.usb-usb-0:1. so we can afford to just throw out the part before first colon and then throw out the first two characters that are always 1..
The part after the second colon is specific to the device itself. Multiport devices like FT2232 will fail here and will require the script to be amended. (TODO.)
KERNEL[2246689.154895] add /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3 (usb) ACTION=add BUSNUM=001 DEVNAME=/dev/bus/usb/001/043 DEVNUM=043 DEVPATH=/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3 DEVTYPE=usb_device MAJOR=189 MINOR=42 PRODUCT=10c4/ea60/100 SEQNUM=1690 SUBSYSTEM=usb TYPE=0/0/0 KERNEL[2246689.170447] add /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0 (usb) ACTION=add DEVPATH=/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0 DEVTYPE=usb_interface INTERFACE=255/0/0 MODALIAS=usb:v10C4pEA60d0100dc00dsc00dp00icFFisc00ip00in00 PRODUCT=10c4/ea60/100 SEQNUM=1691 SUBSYSTEM=usb TYPE=0/0/0 KERNEL[2246689.173837] add /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB1 (usb-serial) ACTION=add DEVPATH=/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB1 SEQNUM=1692 SUBSYSTEM=usb-serial KERNEL[2246689.191081] add /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB1/tty/ttyUSB1 (tty) ACTION=add DEVNAME=/dev/ttyUSB1 DEVPATH=/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB1/tty/ttyUSB1 MAJOR=188 MINOR=1 SEQNUM=1693 SUBSYSTEM=tty UDEV [2246689.346077] add /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3 (usb) ACTION=add BUSNUM=001 DEVNAME=/dev/bus/usb/001/043 DEVNUM=043 DEVPATH=/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3 DEVTYPE=usb_device ID_BUS=usb ID_MODEL=CP2102_Arduino_USB-serial_interface ID_MODEL_ENC=CP2102\x20Arduino\x20USB-serial\x20interface ID_MODEL_FROM_DATABASE=CP210x UART Bridge / myAVR mySmartUSB light ID_MODEL_ID=ea60 ID_REVISION=0100 ID_SERIAL=Silicon_Labs_CP2102_Arduino_USB-serial_interface_0001 ID_SERIAL_SHORT=0001 ID_USB_INTERFACES=:ff0000: ID_VENDOR=Silicon_Labs ID_VENDOR_ENC=Silicon\x20Labs ID_VENDOR_FROM_DATABASE=Cygnal Integrated Products, Inc. ID_VENDOR_ID=10c4 MAJOR=189 MINOR=42 PRODUCT=10c4/ea60/100 SEQNUM=1690 SUBSYSTEM=usb TYPE=0/0/0 USEC_INITIALIZED=2246689345478 UDEV [2246689.454330] add /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0 (usb) ACTION=add DEVPATH=/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0 DEVTYPE=usb_interface ID_MODEL_FROM_DATABASE=CP210x UART Bridge / myAVR mySmartUSB light ID_VENDOR_FROM_DATABASE=Cygnal Integrated Products, Inc. INTERFACE=255/0/0 MODALIAS=usb:v10C4pEA60d0100dc00dsc00dp00icFFisc00ip00in00 PRODUCT=10c4/ea60/100 SEQNUM=1691 SUBSYSTEM=usb TYPE=0/0/0 USEC_INITIALIZED=2246689453644 UDEV [2246689.457449] add /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB1 (usb-serial) ACTION=add DEVPATH=/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB1 SEQNUM=1692 SUBSYSTEM=usb-serial USEC_INITIALIZED=2246689457261 UDEV [2246689.470734] add /devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB1/tty/ttyUSB1 (tty) ID_PORT=0
ACTION=add DEVLINKS=/dev/serial/by-id/usb-Silicon_Labs_CP2102_Arduino_USB-serial_interface_0001-if00-port0 /dev/serial/by-path/platform-3f980000.usb-usb-0:1.3:1.0-port0 /dev/ttyArdu DEVNAME=/dev/ttyUSB1 DEVPATH=/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB1/tty/ttyUSB1 ID_BUS=usb ID_MODEL=CP2102_Arduino_USB-serial_interface ID_MODEL_ENC=CP2102\x20Arduino\x20USB-serial\x20interface ID_MODEL_FROM_DATABASE=CP210x UART Bridge / myAVR mySmartUSB light ID_MODEL_ID=ea60 ID_PATH=platform-3f980000.usb-usb-0:1.3:1.0 ID_PATH_TAG=platform-3f980000_usb-usb-0_1_3_1_0 ID_REVISION=0100 ID_SERIAL=Silicon_Labs_CP2102_Arduino_USB-serial_interface_0001 ID_SERIAL_SHORT=0001 ID_TYPE=generic ID_USB_DRIVER=cp210x ID_USB_INTERFACES=:ff0000: ID_USB_INTERFACE_NUM=00 ID_VENDOR=Silicon_Labs ID_VENDOR_ENC=Silicon\x20Labs ID_VENDOR_FROM_DATABASE=Cygnal Integrated Products, Inc. ID_VENDOR_ID=10c4 MAJOR=188 MINOR=1 SEQNUM=1693 SUBSYSTEM=tty TAGS=:systemd: USEC_INITIALIZED=2246689470110
get sys path from dev name:
udevadm info -a -p `udevadm info -q path -n /dev/ttyUSB1`
Udevadm info starts with the device specified by the devpath and then walks up the chain of parent devices. It prints for every device found, all possible attributes in the udev rules key format. A rule to match, can be composed by the attributes of the device and the attributes from one single parent device. looking at device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB1/tty/ttyUSB1': KERNEL=="ttyUSB1" SUBSYSTEM=="tty" DRIVER=="" looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0/ttyUSB1': KERNELS=="ttyUSB1" SUBSYSTEMS=="usb-serial" DRIVERS=="cp210x" ATTRS{port_number}=="0" looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3/1-1.3:1.0': KERNELS=="1-1.3:1.0" SUBSYSTEMS=="usb" DRIVERS=="cp210x" ATTRS{authorized}=="1" ATTRS{bAlternateSetting}==" 0" ATTRS{bInterfaceClass}=="ff" ATTRS{bInterfaceNumber}=="00" ATTRS{bInterfaceProtocol}=="00" ATTRS{bInterfaceSubClass}=="00" ATTRS{bNumEndpoints}=="02" ATTRS{interface}=="CP2102 Arduino USB-serial interface" ATTRS{supports_autosuspend}=="1" looking at parent device '/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.3': KERNELS=="1-1.3" SUBSYSTEMS=="usb" DRIVERS=="usb" ATTRS{authorized}=="1" ATTRS{avoid_reset_quirk}=="0" ATTRS{bConfigurationValue}=="1" ATTRS{bDeviceClass}=="00" ATTRS{bDeviceProtocol}=="00" ATTRS{bDeviceSubClass}=="00" ATTRS{bMaxPacketSize0}=="64" ATTRS{bMaxPower}=="100mA" ATTRS{bNumConfigurations}=="1" ATTRS{bNumInterfaces}==" 1" ATTRS{bcdDevice}=="0100" ATTRS{bmAttributes}=="80" ATTRS{busnum}=="1" ATTRS{configuration}=="" ATTRS{devnum}=="43" ATTRS{devpath}=="1.3" ATTRS{devspec}==" (null)" ATTRS{idProduct}=="ea60" ATTRS{idVendor}=="10c4" ATTRS{ltm_capable}=="no" ATTRS{manufacturer}=="Silicon Labs" ATTRS{maxchild}=="0" ATTRS{product}=="CP2102 Arduino USB-serial interface" ATTRS{quirks}=="0x0" ATTRS{removable}=="removable" ATTRS{serial}=="0001" ATTRS{speed}=="12" ATTRS{urbnum}=="16" ATTRS{version}==" 1.10"...here it stops being so interesting, this is the hub and the controller above, no more the device we play with...
ACTION='add' DEVLINKS='/dev/serial/by-path/platform-3f980000.usb-usb-0:1.5.6.1:1.0-port0 /dev/ttyArdu /dev/serial/by-id/usb-Silicon_Labs_CP2102_Arduino_USB-serial_interface_0001-if00-port0' DEVNAME='/dev/ttyUSB1' DEVPATH='/devices/platform/soc/3f980000.usb/usb1/1-1/1-1.5/1-1.5.6/1-1.5.6.1/1-1.5.6.1:1.0/ttyUSB1/tty/ttyUSB1' ID_BUS='usb' ID_MODEL='CP2102_Arduino_USB-serial_interface' ID_MODEL_ENC='CP2102\x20Arduino\x20USB-serial\x20interface' ID_MODEL_FROM_DATABASE='CP210x UART Bridge / myAVR mySmartUSB light' ID_MODEL_ID='ea60' ID_PATH='platform-3f980000.usb-usb-0:1.5.6.1:1.0' ID_PATH_TAG='platform-3f980000_usb-usb-0_1_5_6_1_1_0' ID_REVISION='0100' ID_SERIAL='Silicon_Labs_CP2102_Arduino_USB-serial_interface_0001' ID_SERIAL_SHORT='0001' ID_TYPE='generic' ID_USB_DRIVER='cp210x' ID_USB_INTERFACES=':ff0000:' ID_USB_INTERFACE_NUM='00' ID_VENDOR='Silicon_Labs' ID_VENDOR_ENC='Silicon\x20Labs' ID_VENDOR_FROM_DATABASE='Cygnal Integrated Products, Inc.' ID_VENDOR_ID='10c4' IFS=' ' MAJOR='188' MINOR='1' OPTIND='1' PATH='/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' PPID='31637' PS1='# ' PS2='> ' PS4='+ ' PWD='/' SEQNUM='2063' SUBSYSTEM='tty' USEC_INITIALIZED='2306412822524'
invoked via /bin/sh; /bin/bash adds a lot of other variables of its own, irrelevant to udev system
[ref]
here, first the subsystem "tty" and product ID from device upstream sysfs attributes (ATTRS{key}, crawl up until first key match) are matched, then the SYMLINK command is executed
# arduino interface, /dev/ttyArdu # udevadm properties dump above SUBSYSTEM=="tty" ATTRS{product}=="CP2102 Arduino USB-serial interface" SYMLINK+="ttyArdu" # NFC toy, /dev/ttyNFC SUBSYSTEM=="tty" ATTRS{product}=="CP2102 PN532 NFC" SYMLINK+="ttyNFC" # Marlin, /dev/ttyMarlin # arduino board for 3d printer (name contains attached hardware speed/setting, mode set to allow access to all) SUBSYSTEM=="tty" ATTRS{product}=="CP2102 Marlin 250000n8" SYMLINK+="ttyMarlin" MODE:="0666" # webcam auto-stream rules ACTION=="add", KERNEL=="video[0-9]*" SYMLINK+="webcam%n" ENV{DEVNUM}="%n" TAG+="systemd" ENV{SYSTEMD_WANTS}="webcam@%n.service" ACTION=="remove", KERNEL=="video[0-9]*" RUN+="/home/pi/scripts/webcam_server_kill.sh %n" # do not forget to reload! # udevadm control --reload-rules
webcam systemd start script, %i is the device number passed above via %n, taken by the script as $1; example to add such services to rule-matching serial ports
# belongs to /etc/systemd/system/webcam@.service # called from /etc/udev/rules/webcams.rules # # do not forget to run systemctl daemon-reload [Unit] Description=webcam server on /dev/video%i [Service] User=pi Type=forking RemainAfterExit=yes ExecStart=/home/pi/scripts/webcam_server.sh %i ExecStop=/home/pi/scripts/webcam_server_kill.sh %i #ExecReload=/home/pi/scripts/webcam_server.sh %i [Install] WantedBy=multi-user.target