Skip to content

Instantly share code, notes, and snippets.

@max-verem
Created November 27, 2025 07:32
Show Gist options
  • Select an option

  • Save max-verem/2c0b0812be321863796d1599c0f0dc59 to your computer and use it in GitHub Desktop.

Select an option

Save max-verem/2c0b0812be321863796d1599c0f0dc59 to your computer and use it in GitHub Desktop.
CRSF [EE 18 16] => [C8 18 16]
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <asm/termbits.h>
#include <fcntl.h>
#include <sys/ioctl.h>
int serial_arbitrary_baudrate(int fd, int baudrate)
{
#if !defined BOTHER
fprintf(stderr, "%s: not supported\n", __FUNCTION__);
return -EINVAL;
#else
int r;
struct termios2 tio;
r = ioctl(fd, TCGETS2, &tio);
if(r < 0)
{
r = errno;
fprintf(stderr, "%s: ioctl(TCGETS2) failed: r=%d (%s)\n", __FUNCTION__, r, strerror(r));
return -r;
};
/* Clear the current output baud rate and fill a new value */
tio.c_cflag &= ~CBAUD;
tio.c_cflag |= BOTHER;
tio.c_ospeed = baudrate;
/* Clear the current input baud rate and fill a new value */
tio.c_cflag &= ~(CBAUD << IBSHIFT);
tio.c_cflag |= BOTHER << IBSHIFT;
tio.c_ispeed = baudrate;
r = ioctl(fd, TCSETS2, &tio);
if(r < 0)
{
r = errno;
fprintf(stderr, "%s: ioctl(TCSETS2) failed: r=%d (%s)\n", __FUNCTION__, r, strerror(r));
return -r;
};
r = ioctl(fd, TCGETS2, &tio);
if(r < 0)
{
r = errno;
fprintf(stderr, "%s: ioctl(TCGETS2) failed: r=%d (%s)\n", __FUNCTION__, r, strerror(r));
return -r;
};
fprintf(stderr, "%s: c_ospeed=%u, c_ispeed=%u\n", __FUNCTION__, tio.c_ospeed, tio.c_ispeed);
return 0;
#endif
};
#ifndef serial_arbitrary_baudrate_h
#define serial_arbitrary_baudrate_h
int serial_arbitrary_baudrate(int fd, int baudrate);
#endif
/*
gcc -Wall -g -ggdb -O0 serial_arbitrary_baudrate.c test2.c -o test2
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <inttypes.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <time.h>
#include <termios.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <linux/serial.h>
#include <fcntl.h>
#include <netdb.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pty.h>
#include "serial_arbitrary_baudrate.h"
#define CRSF_DEV "/home/demo/ttyCRSF"
#define SERIAL_DEV "/dev/serial/by-id/usb-1a86_USB2.0-Ser_-if00-port0"
#define SERIAL_SPEED 400000
int main(int argc, char** argv)
{
int r, fd_serial = -1, fd_pty = -1, q;
uint64_t cnt_total = 0, cnt_crsf = 0, cnt_display = 0;
struct termios newtio;
char pty_name[1024];
/* remove existing link to pty */
unlink(CRSF_DEV);
/* open pty */
r = openpty(&fd_pty, &q, pty_name, NULL, NULL);
if(r < 0)
{
r = errno;
fprintf(stderr, "%s: openpty() failed: r=%d (%s)\n",
__FUNCTION__, r, strerror(r));
goto ex;
};
{
struct termios t;
tcgetattr(q, &t);
cfmakeraw(&t);
tcsetattr(q, TCSANOW, &t);
close(q);
};
// r = fcntl(fd_pty, F_GETFL, 0);
// if(!r)
fcntl(fd_pty, F_SETFL, /* r | O_NONBLOCK | */ FNDELAY);
fprintf(stderr, "%s:%d [%s]\n", __FUNCTION__, __LINE__, pty_name);
/* create symlink */
r = symlink(pty_name, CRSF_DEV);
if(r < 0)
{
r = errno;
fprintf(stderr, "%s: symlink([%s], [%s]) failed: r=%d (%s)\n",
__FUNCTION__, pty_name, CRSF_DEV, r, strerror(r));
goto ex;
};
/* open serial port */
fd_serial = open(SERIAL_DEV, O_RDWR | O_NOCTTY);
if(fd_serial < 0)
{
r = errno;
fprintf(stderr, "%s: open(%s) failed: r=%d (%s)\n",
__FUNCTION__, SERIAL_DEV, r, strerror(r));
return -r;
};
/* configure serial device */
memset(&newtio, 0, sizeof(struct termios)); /* clear struct for new port settings */
newtio.c_iflag = IGNPAR; /* ignore bytes with parity errors */
newtio.c_oflag = 0; /* Raw output */
newtio.c_lflag = 0; /* enable canonical input */
newtio.c_cc[VTIME] = 1; /* inter-character timer unused */
newtio.c_cc[VMIN] = 1; /* blocking read until 5 chars received */
newtio.c_cflag =
CS8 |
CREAD |
CLOCAL;
r = cfsetspeed(&newtio, B115200);
r = tcsetattr(fd_serial, TCSANOW, &newtio);
if(r < 0)
{
r = errno;
fprintf(stderr, "%s: tcsetattr failed: r=%d (%s)\n",
__FUNCTION__, r, strerror(r));
close(fd_serial);
return -r;
};
serial_arbitrary_baudrate(fd_serial, SERIAL_SPEED);
tcflush(fd_serial, TCIFLUSH);
tcflush(fd_serial, TCIOFLUSH);
while(1)
{
int l;
static const char anim[4] = "|/-\\";
unsigned char msg[512];
r = read(fd_serial, msg, sizeof(msg));
cnt_total++;
#if 0
fprintf(stdout, "%5d 0x%.02X 0x%.02X 0x%.02X 0x%.02X ... \r",
r, msg[0], msg[1], msg[2], msg[3]);
#endif
/*
[ 26] EE 18 16 EB 9B DE C4 B9 D7 0A F0 B5 A2 15 E0 03 1F F8 C0 07 3E F0 81 0F 7C 54
[ 26] EE 18 16 ED A3 DE C4 B9 D7 0A F0 B5 A2 15 E0 03 1F F8 C0 07 3E F0 81 0F 7C FF
[ 26] EE 18 16 EC A3 DE C4 B9 D7 0A F0 B5 A2 15 E0 03 1F F8 C0 07 3E F0 81 0F 7C C4
*/
#if 0
/* we accept only CRSF_FRAMETYPE_RC_CHANNELS_PACKED */
if(r != 26 || msg[0] != 0xEE || msg[1] != 0x18 || msg[2] != 0x16)
{
fprintf(stderr, ".");
continue;
};
msg[0] = 0xc8; // Flight Controller
#else
for(l = 0; l < (r - 3); l++)
{
if(msg[l + 0] == 0xEE && msg[l + 1] == 0x18 && msg[l + 2] == 0x16)
msg[l + 0] = 0xC8;
};
#endif
cnt_crsf++;
r = write(fd_pty, msg, l = r);
if(r < 0)
{
r = errno;
if(r != EAGAIN && r != EWOULDBLOCK)
{
fprintf(stderr, "%s:%d: write failed, r=%d (%s)\n",
__FUNCTION__, __LINE__, r, strerror(r));
break;
};
};
if(cnt_crsf % 20)
continue;
cnt_display++;
fprintf(stderr, "[%c] ---\r", anim[cnt_display % sizeof(anim)]);
};
ex:
if(fd_serial > 0)
close(fd_serial);
if(fd_pty > 0)
close(fd_pty);
return r;
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment