Mac OS X gamepad emulator for serial RC transmitters
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

Thread.m 5.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. //
  2. // Thread.m
  3. // SerialGamepad
  4. //
  5. // Created by Thomas Buck on 15.12.15.
  6. // Copyright © 2015 xythobuz. All rights reserved.
  7. //
  8. #import <termios.h>
  9. #import <fcntl.h>
  10. #import <unistd.h>
  11. #import "Thread.h"
  12. #import "fooHID.h"
  13. #import "MainWindow.h"
  14. #define CHANNELS 6
  15. #define TESTCHANNEL 2
  16. #define PACKETSIZE 18
  17. #define HEADERBYTES 2
  18. #define CHECKSUMBYTES 2
  19. #define PAYLOADBYTES (PACKETSIZE - HEADERBYTES - CHECKSUMBYTES)
  20. #define HEADERBYTE_A 85
  21. #define HEADERBYTE_B 252
  22. enum ThreadState {
  23. READ_FIRST_BYTE,
  24. READ_SECOND_BYTE,
  25. READ_PAYLOAD,
  26. READ_CHECKSUM
  27. };
  28. @implementation Thread
  29. @synthesize running, fd, portName, mainWindow;
  30. - (id)initWithWindow:(MainWindow *)window {
  31. self = [super init];
  32. if (self != nil) {
  33. mainWindow = window;
  34. }
  35. return self;
  36. }
  37. - (NSInteger)openPort {
  38. if (portName == nil) {
  39. return 1;
  40. }
  41. // Open port read-only, without controlling terminal, non-blocking
  42. fd = open([portName UTF8String], O_RDONLY | O_NOCTTY | O_NONBLOCK);
  43. if (fd == -1) {
  44. NSLog(@"Error opening serial port \"%@\"!\n", portName);
  45. return 1;
  46. }
  47. fcntl(fd, F_SETFL, 0); // Enable blocking I/O
  48. // Read current settings
  49. struct termios options;
  50. tcgetattr(fd, &options);
  51. options.c_lflag = 0;
  52. options.c_oflag = 0;
  53. options.c_iflag = 0;
  54. options.c_cflag = 0;
  55. options.c_cflag |= CS8; // 8 data bits
  56. options.c_cflag |= CREAD; // Enable receiver
  57. options.c_cflag |= CLOCAL; // Ignore modem status lines
  58. cfsetispeed(&options, B115200);
  59. cfsetospeed(&options, B115200);
  60. options.c_cc[VMIN] = 0; // Return even with zero bytes...
  61. options.c_cc[VTIME] = 1; // ...but only after .1 seconds
  62. // Set new settings
  63. tcsetattr(fd, TCSANOW, &options);
  64. tcflush(fd, TCIOFLUSH);
  65. return 0;
  66. }
  67. - (void)main {
  68. enum ThreadState state = READ_FIRST_BYTE;
  69. unsigned char c = 0;
  70. unsigned char buffer[PAYLOADBYTES];
  71. unsigned char checksum[CHECKSUMBYTES];
  72. uint16_t channels[CHANNELS + 1];
  73. int received = 0;
  74. NSLog(@"Connection running...\n");
  75. running = YES;
  76. while (running) {
  77. if (state == READ_FIRST_BYTE) {
  78. if (read(fd, &c, 1) == 1) {
  79. if (c == HEADERBYTE_A) {
  80. state = READ_SECOND_BYTE;
  81. }
  82. }
  83. } else if (state == READ_SECOND_BYTE) {
  84. if (read(fd, &c, 1) == 1) {
  85. if (c == HEADERBYTE_B) {
  86. state = READ_PAYLOAD;
  87. received = 0;
  88. } else {
  89. state = READ_FIRST_BYTE;
  90. }
  91. }
  92. } else if (state == READ_PAYLOAD) {
  93. ssize_t ret = read(fd, buffer + received, PAYLOADBYTES - received);
  94. if (ret >= 0) received += ret;
  95. if (received >= PAYLOADBYTES) {
  96. state = READ_CHECKSUM;
  97. received = 0;
  98. }
  99. } else if (state == READ_CHECKSUM) {
  100. ssize_t ret = read(fd, checksum + received, CHECKSUMBYTES - received);
  101. if (ret >= 0) received += ret;
  102. if (received >= CHECKSUMBYTES) {
  103. state = READ_FIRST_BYTE;
  104. uint16_t sum = 0;
  105. for (int i = 0; i < PAYLOADBYTES; i++) {
  106. sum += buffer[i];
  107. }
  108. if (sum != ((checksum[0] << 8) | checksum[1])) {
  109. NSLog(@"Wrong checksum: %d != %d\n", sum, ((checksum[0] << 8) | checksum[1]));
  110. } else {
  111. for (int i = 0; i < (CHANNELS + 1); i++) {
  112. channels[i] = buffer[2 * i] << 8;
  113. channels[i] |= buffer[(2 * i) + 1];
  114. if (i < CHANNELS) {
  115. channels[i] -= 1000;
  116. }
  117. }
  118. if (channels[CHANNELS] != channels[TESTCHANNEL]) {
  119. // This channel contains the throttle value even if it has been disabled using the switches
  120. // on the transmitter. Therefore, there's not really a warning required here.
  121. //NSLog(@"Wrong test channel value: %d != %d\n", channels[CHANNELS], channels[TESTCHANNEL]);
  122. }
  123. NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:CHANNELS];
  124. for (int i = 0; i < CHANNELS; i++) {
  125. [arr addObject:[[NSNumber alloc] initWithInteger:(NSInteger)channels[i]]];
  126. }
  127. [mainWindow performSelectorOnMainThread:@selector(setChannels:) withObject:arr waitUntilDone:NO];
  128. }
  129. }
  130. } else {
  131. NSLog(@"Invalid state?!\n");
  132. state = READ_FIRST_BYTE;
  133. }
  134. }
  135. // Ensure the GUI is always reset after clicking Disconnect
  136. NSArray *zero = [NSArray arrayWithObjects:[NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], [NSNumber numberWithInt:0], nil];
  137. [mainWindow performSelectorOnMainThread:@selector(setChannels:) withObject:zero waitUntilDone:NO];
  138. close(fd);
  139. NSLog(@"Connection closed...\n");
  140. fd = -1;
  141. }
  142. @end