Browse Source

Initial commit

Thomas Buck 8 years ago
commit
1be54acfc8
6 changed files with 568 additions and 0 deletions
  1. 3
    0
      .gitignore
  2. 24
    0
      README.md
  3. 10
    0
      protocol/Makefile
  4. 142
    0
      protocol/main.c
  5. 245
    0
      protocol/serial.c
  6. 144
    0
      protocol/serial.h

+ 3
- 0
.gitignore View File

@@ -0,0 +1,3 @@
1
+.DS_Store
2
+*.o
3
+protocol/proto

+ 24
- 0
README.md View File

@@ -0,0 +1,24 @@
1
+# Mac OS X Gamepad Driver for Flysky compatible transmitters
2
+
3
+
4
+
5
+## Protocol Test Utility
6
+
7
+In the `protocol` subdirectory you can find a simple command-line app to test the proper decoding of the transmitters serial protocol.
8
+
9
+## Other Resources
10
+
11
+ * [Serial protocol analysis](http://www.rcgroups.com/forums/showpost.php?p=11384029&postcount=79)
12
+ * [T6config program informations](http://www.mycoolheli.com/t6config.html)
13
+ * [T6config alternatives](http://www.mycoolheli.com/t6Alternate.html)
14
+ * [Mac OS X version](http://www.zenoshrdlu.com/turborix/)
15
+
16
+## Licensing
17
+
18
+    ----------------------------------------------------------------------------
19
+    "THE BEER-WARE LICENSE" (Revision 42):
20
+    <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
21
+    you can do whatever you want with this stuff. If we meet some day, and you
22
+    think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
23
+    ----------------------------------------------------------------------------
24
+

+ 10
- 0
protocol/Makefile View File

@@ -0,0 +1,10 @@
1
+# Copyright 2015 Thomas Buck <xythobuz@xythobuz.de>
2
+
3
+all: proto
4
+
5
+proto: serial.o main.o
6
+	$(CC) -o proto serial.o main.o
7
+
8
+clean:
9
+	rm -rf proto main.o serial.o
10
+

+ 142
- 0
protocol/main.c View File

@@ -0,0 +1,142 @@
1
+/*
2
+ * ----------------------------------------------------------------------------
3
+ * "THE BEER-WARE LICENSE" (Revision 42):
4
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
5
+ * you can do whatever you want with this stuff. If we meet some day, and you
6
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
7
+ * ----------------------------------------------------------------------------
8
+ */
9
+
10
+#include <stdint.h>
11
+#include <stdio.h>
12
+#include <signal.h>
13
+
14
+#include "serial.h"
15
+
16
+#define BAUDRATE 115200
17
+#define PACKETSIZE 18
18
+#define HEADERBYTES 2
19
+#define HEADERBYTE_A 85
20
+#define HEADERBYTE_B 252
21
+#define CHECKSUMBYTES 2
22
+#define PAYLOADBYTES (PACKETSIZE - HEADERBYTES - CHECKSUMBYTES)
23
+#define CHANNELS 6
24
+
25
+/*
26
+#define TESTCHANNEL 2
27
+#define TESTCHANNELVALUE 2044
28
+*/
29
+
30
+static int running = 1;
31
+static int firstPrint = 0;
32
+static int extraLine = 0;
33
+
34
+static void signalHandler(int signo) {
35
+    running = 0;
36
+}
37
+
38
+int main(int argc, char* argv[]) {
39
+    if (argc != 2) {
40
+        printf("Usage:\n\t%s /dev/serial_port\n", argv[0]);
41
+        return 1;
42
+    }
43
+
44
+    if (signal(SIGINT, signalHandler) == SIG_ERR) {
45
+        perror("Couldn't register signal handler");
46
+        return 1;
47
+    }
48
+
49
+    int fd = serialOpen(argv[1], BAUDRATE);
50
+    if (fd == -1) {
51
+        return 1;
52
+    }
53
+
54
+    while (running != 0) {
55
+        if (serialHasChar(fd)) {
56
+            unsigned char c1;
57
+            serialReadChar(fd, (char*)&c1);
58
+            if (c1 == HEADERBYTE_A) {
59
+                // Found first byte of protocol start
60
+                while (!serialHasChar(fd)) {
61
+                    if (running == 0) {
62
+                        serialClose(fd);
63
+                        return 0;
64
+                    }
65
+                }
66
+
67
+                unsigned char c2;
68
+                serialReadChar(fd, (char*)&c2);
69
+                if (c2 == HEADERBYTE_B) {
70
+                    // Protocol start has been found, read payload
71
+                    unsigned char data[PAYLOADBYTES];
72
+                    int read = 0;
73
+                    while ((read < PAYLOADBYTES) && (running != 0)) {
74
+                        read += serialReadRaw(fd, (char*)data + read, PAYLOADBYTES - read);
75
+                    }
76
+
77
+                    // Read 16bit checksum
78
+                    unsigned char checksumData[CHECKSUMBYTES];
79
+                    read = 0;
80
+                    while ((read < CHECKSUMBYTES) && (running != 0)) {
81
+                        read += serialReadRaw(fd, (char*)checksumData + read,
82
+                                              CHECKSUMBYTES - read);
83
+                    }
84
+
85
+                    // Check if checksum matches
86
+                    uint16_t checksum = 0;
87
+                    for (int i = 0; i < PAYLOADBYTES; i++) {
88
+                        checksum += data[i];
89
+                    }
90
+
91
+                    if (checksum != ((checksumData[0] << 8) | checksumData[1])) {
92
+                        printf("Wrong checksum: %d != %d                    \n",
93
+                               checksum, ((checksumData[0] << 8) | checksumData[1]));
94
+                        extraLine++;
95
+                        if (extraLine > 1) {
96
+                            extraLine--;
97
+                            printf("\r\033[1A");
98
+                        }
99
+                    } else {
100
+                        // Decode channel values
101
+                        uint16_t buff[CHANNELS + 1];
102
+                        for (int i = 0; i < (CHANNELS + 1); i++) {
103
+                            buff[i] = data[2 * i] << 8;
104
+                            buff[i] |= data[(2 * i) + 1];
105
+                        }
106
+
107
+                        /*
108
+                        // Check Test Channel Value (?)
109
+                        if (buff[CHANNELS] != (TESTCHANNELVALUE - buff[TESTCHANNEL])) {
110
+                            printf("Wrong test channel value: %d != %d (%d - %d)\n",
111
+                                   buff[CHANNELS], TESTCHANNELVALUE - buff[TESTCHANNEL],
112
+                                   TESTCHANNELVALUE, buff[TESTCHANNEL]);
113
+
114
+                            printf("Correct would be: %d\n",
115
+                                   buff[TESTCHANNEL] + buff[CHANNELS]);
116
+                        }
117
+                        */
118
+
119
+                        if (firstPrint == 0) {
120
+                            firstPrint = 1;
121
+                        } else {
122
+                            int num = CHANNELS + extraLine;
123
+                            extraLine = 0;
124
+                            for (int i = 0; i < num; i++) {
125
+                                printf("\r\033[1A");
126
+                            }
127
+                        }
128
+
129
+                        for (int i = 0; i < CHANNELS; i++) {
130
+                            printf("CH%d: %d                    \n", i + 1, buff[i] - 1000);
131
+                        }
132
+                    }
133
+                }
134
+            }
135
+        }
136
+    }
137
+
138
+    serialClose(fd);
139
+
140
+    return 0;
141
+}
142
+

+ 245
- 0
protocol/serial.c View File

@@ -0,0 +1,245 @@
1
+/*
2
+ * ----------------------------------------------------------------------------
3
+ * "THE BEER-WARE LICENSE" (Revision 42):
4
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
5
+ * you can do whatever you want with this stuff. If we meet some day, and you
6
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
7
+ * ----------------------------------------------------------------------------
8
+ */
9
+
10
+#include <stdio.h>
11
+#include <stdlib.h>
12
+#include <string.h>
13
+#include <unistd.h>
14
+#include <fcntl.h>
15
+#include <termios.h>
16
+#include <dirent.h>
17
+#include <errno.h>
18
+#include <time.h>
19
+#include <poll.h>
20
+
21
+#include "serial.h"
22
+
23
+#ifndef XON
24
+#define XON 0x11
25
+#endif
26
+
27
+#ifndef XOFF
28
+#define XOFF 0x13
29
+#endif
30
+
31
+#ifndef TIMEOUT
32
+#define TIMEOUT 2
33
+#endif
34
+
35
+int serialOpen(const char *port, unsigned int baud) {
36
+    struct termios options;
37
+
38
+    int fd = open(port, O_RDWR | O_NOCTTY | O_NDELAY);
39
+    if (fd == -1) {
40
+        fprintf(stderr, "Couldn't open port \"%s\": %s\n", port, strerror(errno));
41
+        return -1;
42
+    }
43
+
44
+    tcgetattr(fd, &options);
45
+
46
+    options.c_lflag = 0;
47
+    options.c_oflag = 0;
48
+    options.c_iflag = 0;
49
+
50
+    // Set Baudrate
51
+    switch (baud) {
52
+        case 9600:
53
+            cfsetispeed(&options, B9600);
54
+            cfsetospeed(&options, B9600);
55
+            break;
56
+        case 19200:
57
+            cfsetispeed(&options, B19200);
58
+            cfsetospeed(&options, B19200);
59
+            break;
60
+        case 38400:
61
+            cfsetispeed(&options, B38400);
62
+            cfsetospeed(&options, B38400);
63
+            break;
64
+        case 76800:
65
+            cfsetispeed(&options, B76800);
66
+            cfsetospeed(&options, B76800);
67
+            break;
68
+        case 115200:
69
+            cfsetispeed(&options, B115200);
70
+            cfsetospeed(&options, B115200);
71
+            break;
72
+        default:
73
+            fprintf(stderr, "Warning: Baudrate not supported!\n");
74
+            serialClose(fd);
75
+            return -1;
76
+    }
77
+
78
+    // Input Modes
79
+    options.c_iflag |= IGNCR; // Ignore CR
80
+#ifdef XONXOFF
81
+    options.c_iflag |= IXON; // XON-XOFF Flow Control
82
+#endif
83
+
84
+    // Output Modes
85
+    options.c_oflag |= OPOST; // Post-process output
86
+
87
+    // Control Modes
88
+    options.c_cflag |= CS8; // 8 data bits
89
+    options.c_cflag |= CREAD; // Enable Receiver
90
+    options.c_cflag |= CLOCAL; // Ignore modem status lines
91
+
92
+    // Local Modes
93
+    options.c_lflag |= IEXTEN; // Extended input character processing
94
+
95
+    // Special characters
96
+    options.c_cc[VMIN] = 0; // Always return...
97
+    options.c_cc[VTIME] = 0; // ..immediately from read()
98
+#ifdef XONXOFF
99
+    options.c_cc[VSTOP] = XOFF;
100
+    options.c_cc[VSTART] = XON;
101
+#endif
102
+
103
+    tcsetattr(fd, TCSANOW, &options);
104
+
105
+    tcflush(fd, TCIOFLUSH);
106
+
107
+    return fd;
108
+}
109
+
110
+void serialClose(int fd) {
111
+    tcflush(fd, TCIOFLUSH);
112
+    close(fd);
113
+}
114
+
115
+int serialHasChar(int fd) {
116
+    struct pollfd fds;
117
+    fds.fd = fd;
118
+    fds.events = (POLLIN | POLLPRI); // Data may be read
119
+    if (poll(&fds, 1, 0) > 0) {
120
+        return 1;
121
+    } else {
122
+        return 0;
123
+    }
124
+}
125
+
126
+void serialWaitUntilSent(int fd) {
127
+    while (tcdrain(fd) == -1) {
128
+        fprintf(stderr, "Could not drain data: %s\n", strerror(errno));
129
+    }
130
+}
131
+
132
+unsigned int serialWriteRaw(int fd, const char *d, int len) {
133
+    unsigned int processed = 0;
134
+    time_t start = time(NULL);
135
+
136
+    while ((processed < len) && (difftime(time(NULL), start) < TIMEOUT)) {
137
+        int t = write(fd, (d + processed), (len - processed));
138
+        if (t == -1) {
139
+            fprintf(stderr, "Error while writing: %s\n", strerror(errno));
140
+            return processed;
141
+        } else {
142
+            processed += t;
143
+        }
144
+    }
145
+
146
+    return processed;
147
+}
148
+
149
+unsigned int serialReadRaw(int fd, char *d, int len) {
150
+    unsigned int processed = 0;
151
+    time_t start = time(NULL);
152
+
153
+    while ((processed < len) && (difftime(time(NULL), start) < TIMEOUT)) {
154
+        int t = read(fd, (d + processed), (len - processed));
155
+        if (t == -1) {
156
+            fprintf(stderr, "Error while reading: %s\n", strerror(errno));
157
+            return processed;
158
+        } else {
159
+            processed += t;
160
+        }
161
+    }
162
+
163
+    return processed;
164
+}
165
+
166
+void serialWriteChar(int fd, char c) {
167
+    while (serialWriteRaw(fd, &c, 1) != 1);
168
+}
169
+
170
+void serialReadChar(int fd, char *c) {
171
+    while (serialReadRaw(fd, c, 1) != 1);
172
+#ifdef XONXOFF
173
+    if (*c == XON) {
174
+        if (tcflow(fd, TCOON) == -1) {
175
+            fprintf(stderr, "Could not restart flow: %s\n", strerror(errno));
176
+        }
177
+        serialReadChar(fd, c);
178
+    } else if (*c == XOFF) {
179
+        if (tcflow(fd, TCOOFF) == -1) {
180
+            fprintf(stderr, "Could not stop flow: %s\n", strerror(errno));
181
+        }
182
+        serialReadChar(fd, c);
183
+    }
184
+#endif
185
+}
186
+
187
+void serialWriteString(int fd, const char *s) {
188
+    while (*s)
189
+        serialWriteChar(fd, *(s++));
190
+}
191
+
192
+char** getSerialPorts(void) {
193
+    DIR *dir;
194
+    struct dirent *ent;
195
+
196
+    int size = 0;
197
+    dir = opendir("/dev/");
198
+    while ((ent = readdir(dir)) != NULL) {
199
+#ifdef SEARCH
200
+        if (strstr(ent->d_name, SEARCH) != NULL)
201
+#endif
202
+        size++;
203
+    }
204
+    closedir(dir);
205
+
206
+    char **files = (char **)malloc((size + 1) * sizeof(char *));
207
+
208
+    int i = 0;
209
+    dir = opendir("/dev/");
210
+    while (((ent = readdir(dir)) != NULL) && (i < size)) {
211
+
212
+#ifdef SEARCH
213
+        if (strstr(ent->d_name, SEARCH) != NULL) {
214
+#endif
215
+
216
+            int tmp = strlen(ent->d_name) + 6;
217
+            files[i] = (char *)malloc(tmp * sizeof(char));
218
+            strcpy(files[i], "/dev/");
219
+            strcpy(files[i] + 5, ent->d_name);
220
+            files[i][tmp - 1] = '\0';
221
+
222
+#ifdef TRY_TO_OPEN_PORTS
223
+            int fdtmp = serialOpen(files[i], 9600);
224
+            if (fdtmp != -1) {
225
+                serialClose(fdtmp);
226
+#endif
227
+
228
+                i++;
229
+
230
+#ifdef TRY_TO_OPEN_PORTS
231
+            } else {
232
+                free(files[i]);
233
+            }
234
+#endif
235
+
236
+#ifdef SEARCH
237
+        }
238
+#endif
239
+
240
+    }
241
+    closedir(dir);
242
+    files[i] = NULL;
243
+    return files;
244
+}
245
+

+ 144
- 0
protocol/serial.h View File

@@ -0,0 +1,144 @@
1
+/*
2
+ * ----------------------------------------------------------------------------
3
+ * "THE BEER-WARE LICENSE" (Revision 42):
4
+ * <xythobuz@xythobuz.de> wrote this file.  As long as you retain this notice
5
+ * you can do whatever you want with this stuff. If we meet some day, and you
6
+ * think this stuff is worth it, you can buy me a beer in return.   Thomas Buck
7
+ * ----------------------------------------------------------------------------
8
+ */
9
+
10
+#ifndef _SERIAL_H_
11
+#define _SERIAL_H_
12
+
13
+/*
14
+ * Configuration
15
+ */
16
+
17
+/*!
18
+ * \brief Enable XON/XOFF flow control.
19
+ *
20
+ * If you uncomment this definition, the serial port code will
21
+ * stop sending when a XOFF was received, and start again upon
22
+ * receiving XON. However, you need to use the blocking
23
+ * read/write functions!
24
+ */
25
+//#define XONXOFF
26
+#define XON 0x11  //!< XON flow control character
27
+#define XOFF 0x13 //!< XOFF flow control character
28
+
29
+/*!
30
+ * \brief Search term to filter the list of available ports.
31
+ *
32
+ * If you define SEARCH, instead of simply returning a list of
33
+ * files in /dev/, getSerialPorts() will only return items that
34
+ * contain the string defined to SEARCH.
35
+ */
36
+#define SEARCH "tty"
37
+
38
+/*!
39
+ * \brief Only list real serial ports.
40
+ *
41
+ * If you uncomment this definition, getSerialPorts() will try to
42
+ * open every port, only returning the name if it is a real serial
43
+ * port. This could cause a big delay, if eg. your system tries to
44
+ * open non-existing bluetooth devices, waiting for their timeout.
45
+ * Also, if your console tty is probed, it might change it's settings.
46
+ */
47
+//#define TRY_TO_OPEN_PORTS
48
+
49
+/*!
50
+ * \brief The timeout in seconds for raw reading/writing.
51
+ *
52
+ * If this amount of time passes without being able to write/read
53
+ * a character, the raw I/O functions will return 0.
54
+ */
55
+#define TIMEOUT 2
56
+
57
+/*
58
+ * Setup
59
+ */
60
+
61
+/*!
62
+ * \brief open a serial port
63
+ * \param port name of port
64
+ * \param baud baudrate
65
+ * \returns file handle or -1 on error
66
+ */
67
+int serialOpen(const char *port, unsigned int baud);
68
+
69
+/*!
70
+ * \brief close an open serial port
71
+ * \param fd file handle of port to close
72
+ */
73
+void serialClose(int fd);
74
+
75
+/*!
76
+ * \brief query available serial ports
77
+ * \returns string array with serial port names.
78
+ * Last element is NULL. Don't forget to free()
79
+ * after using it!
80
+ */
81
+char **getSerialPorts(void);
82
+
83
+/*
84
+ * Raw, non-blocking I/O
85
+ */
86
+
87
+/*!
88
+ * \brief read from an open serial port
89
+ * \param fd file handle of port to read from
90
+ * \param data buffer big enough to fit all read data
91
+ * \param length maximum number of bytes to read
92
+ * \returns number of bytes really read
93
+ */
94
+unsigned int serialReadRaw(int fd, char *data, int length);
95
+
96
+/*!
97
+ * \brief write to an open serial port
98
+ * \param fd file handle of port to write to
99
+ * \param data buffer containing data to write
100
+ * \param length number of bytes to write
101
+ * \returns number of bytes really written
102
+ */
103
+unsigned int serialWriteRaw(int fd, const char *data, int length);
104
+
105
+/*!
106
+ * \brief wait until data is sent
107
+ * \param fd file handle of port to wait for
108
+ */
109
+void serialWaitUntilSent(int fd);
110
+
111
+/*
112
+ * Blocking I/O
113
+ */
114
+
115
+/*!
116
+ * \brief check if a character has arrived and can be read
117
+ * \param fd file handle of port to check
118
+ * \returns 1 if a character is available, 0 if not
119
+ */
120
+int serialHasChar(int fd);
121
+
122
+/*!
123
+ * \brief read a single character
124
+ * \param fd file handle of port to read from
125
+ * \param c where read character will be stored
126
+ */
127
+void serialReadChar(int fd, char *c);
128
+
129
+/*!
130
+ * \brief write a single character
131
+ * \param fd file handle to write to
132
+ * \param c character to write
133
+ */
134
+void serialWriteChar(int fd, char c);
135
+
136
+/*!
137
+ * \brief write a string
138
+ * \param fd file handle to write to
139
+ * \param s C string to be written
140
+ */
141
+void serialWriteString(int fd, const char *s);
142
+
143
+#endif
144
+

Loading…
Cancel
Save