Skip to content

Instantly share code, notes, and snippets.

@okalachev
Last active March 13, 2023 10:29
Show Gist options
  • Save okalachev/7704b15c7d0752acad72e50a35a460b1 to your computer and use it in GitHub Desktop.
Save okalachev/7704b15c7d0752acad72e50a35a460b1 to your computer and use it in GitHub Desktop.
Read and parse USB data from KingKong/LDARC Tiny X8 2.4G 8CH/16CH Radio Transmitter
#!/usr/bin/env python3
# Read and parse USB data from KingKong/LDARC Tiny X8 2.4G 8CH Radio Transmitter
# Usage: ./tiny_rc.py <device_path>
# Example: ./tiny_rc.py /dev/tty.usbserial-210
# Author: Oleg Kalachev <okalachev@gmail.com>
# https://github.com/okalachev
import sys
import serial
PACKET_SIZE = 103
HEADER = bytes([0x4C, 0x44, 0x41, 0x54, 0x58, 0x31, 0x30])
DEVICE = sys.argv[1] if len(sys.argv) > 1 else '/dev/tty.usbserial-210'
# Packet structure (https://github.com/kingkong-rc/TINY-TX-X8/blob/0fcbf/FRSKY-D8/IAR-STM8-210/U3.c#L14):
# Start header (7 Byte data fixed): 0x4C, 0x44, 0x41, 0x54, 0x58, 0x31, 0x30
# Remote control type (1 Byte 0x45 / 0x44)
# Remote control software version number (3 Byte year-month-day)
# Wireless protocol version number (1 Byte)
# American hand/Japanese hand mode selection (1 Byte)
# The unique ID number of the remote control (4 Byte)
# 16 channel data (2 Byte per channel = 16*2 Byte = 32 Byte)
# Channel reverse flag (1 Byte)
# All buttons + 8-bit DIP switch GPIO value (2 Byte)
# Battery voltage value (2 Byte battery voltage is magnified by 100 times)
# Reference voltage 431 + high frequency module + neutral calibration is successful flag bit (1 Byte)
# All channels AD sampling original value (2 Byte per channel = 6*2 Byte = 12 Byte)
# LED status (1 Byte) 1 means on, 0 means off
# Alarm status (1 Byte)
# The number of timer crashes (1 Byte)
# Four joystick offset value (4 Byte)
# The maximum AD value of the four rockers (4*2 Byte = 8 Byte)
# The median AD value of the four joysticks (4*2 Byte = 8 Byte)
# The minimum AD value of the four rockers (4*2 Byte = 8 Byte)
# Packet checksum (2 Byte)
device = serial.Serial(DEVICE, 115200, timeout=1)
buf = bytes()
while True:
# wait for the magic header
buf += device.read(1)
if len(buf) < len(HEADER):
if HEADER.startswith(buf):
continue
if not HEADER.startswith(buf):
buf = bytes()
continue
# skip uninteresting data
device.read(10)
# grab channels data
data = device.read(32)
# parse and print channel values
channels = []
for ch in range(16):
channels.append(data[ch * 2] << 8 | (data[ch * 2 + 1]))
print(f'Channel {ch + 1}: {channels[-1]}')
# continue looking for the header...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment