Browse Source

Fully implemented functionality

Thomas Buck 8 years ago
parent
commit
bb132d141a

BIN
SerialGamepad.xcodeproj/project.xcworkspace/xcuserdata/thomas.xcuserdatad/UserInterfaceState.xcuserstate View File


+ 6
- 6
SerialGamepad/Base.lproj/MainMenu.xib View File

@@ -698,27 +698,27 @@
698 698
                     </button>
699 699
                     <levelIndicator verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Bo5-fb-a0u">
700 700
                         <rect key="frame" x="20" y="140" width="290" height="16"/>
701
-                        <levelIndicatorCell key="cell" alignment="left" doubleValue="-511" minValue="-511" maxValue="511" warningValue="2000" criticalValue="2000" levelIndicatorStyle="continuousCapacity" id="xcx-tB-hUl"/>
701
+                        <levelIndicatorCell key="cell" alignment="left" maxValue="1023" warningValue="2000" criticalValue="2000" levelIndicatorStyle="continuousCapacity" id="xcx-tB-hUl"/>
702 702
                     </levelIndicator>
703 703
                     <levelIndicator verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="LlC-lV-IAQ">
704 704
                         <rect key="frame" x="20" y="116" width="290" height="16"/>
705
-                        <levelIndicatorCell key="cell" alignment="left" doubleValue="-511" minValue="-511" maxValue="511" warningValue="2000" criticalValue="2000" levelIndicatorStyle="continuousCapacity" id="sb6-cl-NKs"/>
705
+                        <levelIndicatorCell key="cell" alignment="left" maxValue="1023" warningValue="2000" criticalValue="2000" levelIndicatorStyle="continuousCapacity" id="sb6-cl-NKs"/>
706 706
                     </levelIndicator>
707 707
                     <levelIndicator verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="URz-Id-X9n">
708 708
                         <rect key="frame" x="20" y="92" width="290" height="16"/>
709
-                        <levelIndicatorCell key="cell" alignment="left" doubleValue="-511" minValue="-511" maxValue="511" warningValue="2000" criticalValue="2000" levelIndicatorStyle="continuousCapacity" id="Sov-4g-u59"/>
709
+                        <levelIndicatorCell key="cell" alignment="left" maxValue="1023" warningValue="2000" criticalValue="2000" levelIndicatorStyle="continuousCapacity" id="Sov-4g-u59"/>
710 710
                     </levelIndicator>
711 711
                     <levelIndicator verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="txY-x4-Zyp">
712 712
                         <rect key="frame" x="20" y="68" width="290" height="16"/>
713
-                        <levelIndicatorCell key="cell" alignment="left" doubleValue="-511" minValue="-511" maxValue="511" warningValue="2000" criticalValue="2000" levelIndicatorStyle="continuousCapacity" id="RKp-wa-VLB"/>
713
+                        <levelIndicatorCell key="cell" alignment="left" maxValue="1023" warningValue="2000" criticalValue="2000" levelIndicatorStyle="continuousCapacity" id="RKp-wa-VLB"/>
714 714
                     </levelIndicator>
715 715
                     <levelIndicator verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VyV-eY-7y9">
716 716
                         <rect key="frame" x="20" y="44" width="290" height="16"/>
717
-                        <levelIndicatorCell key="cell" alignment="left" doubleValue="-511" minValue="-511" maxValue="511" warningValue="2000" criticalValue="2000" levelIndicatorStyle="continuousCapacity" id="CyL-Tq-w7d"/>
717
+                        <levelIndicatorCell key="cell" alignment="left" maxValue="1023" warningValue="2000" criticalValue="2000" levelIndicatorStyle="continuousCapacity" id="CyL-Tq-w7d"/>
718 718
                     </levelIndicator>
719 719
                     <levelIndicator verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="vCW-tD-iWD">
720 720
                         <rect key="frame" x="20" y="20" width="290" height="16"/>
721
-                        <levelIndicatorCell key="cell" alignment="left" doubleValue="-511" minValue="-511" maxValue="511" warningValue="2000" criticalValue="2000" levelIndicatorStyle="continuousCapacity" id="nfv-aV-cRB"/>
721
+                        <levelIndicatorCell key="cell" alignment="left" maxValue="1023" warningValue="2000" criticalValue="2000" levelIndicatorStyle="continuousCapacity" id="nfv-aV-cRB"/>
722 722
                     </levelIndicator>
723 723
                     <popUpButton verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="mvV-p0-WO6">
724 724
                         <rect key="frame" x="18" y="214" width="295" height="26"/>

+ 1
- 1
SerialGamepad/Info.plist View File

@@ -21,7 +21,7 @@
21 21
 	<key>CFBundleSignature</key>
22 22
 	<string>????</string>
23 23
 	<key>CFBundleVersion</key>
24
-	<string>61</string>
24
+	<string>87</string>
25 25
 	<key>LSApplicationCategoryType</key>
26 26
 	<string>public.app-category.utilities</string>
27 27
 	<key>LSMinimumSystemVersion</key>

+ 5
- 2
SerialGamepad/MainWindow.h View File

@@ -8,9 +8,9 @@
8 8
 
9 9
 #import <Cocoa/Cocoa.h>
10 10
 
11
-#import "Thread.h"
11
+@class Thread;
12 12
 
13
-@interface MainWindow : NSWindow
13
+@interface MainWindow : NSWindow <NSWindowDelegate>
14 14
 
15 15
 @property (weak) IBOutlet NSPopUpButton *portList;
16 16
 @property (weak) IBOutlet NSButton *connectButton;
@@ -24,6 +24,9 @@
24 24
 
25 25
 @property (strong) Thread *serialThread;
26 26
 
27
+@property (assign) BOOL gamepadCreated;
28
+
27 29
 - (void)populatePortList;
30
+- (void)setChannels:(id)data;
28 31
 
29 32
 @end

+ 66
- 5
SerialGamepad/MainWindow.m View File

@@ -8,17 +8,46 @@
8 8
 
9 9
 #import "MainWindow.h"
10 10
 
11
+#import "fooHID.h"
11 12
 #import "Serial.h"
13
+#import "Thread.h"
12 14
 
13 15
 @implementation MainWindow
14 16
 
15 17
 @synthesize portList, connectButton, createButton;
16 18
 @synthesize level1, level2, level3, level4, level5, level6;
17
-@synthesize serialThread;
19
+@synthesize serialThread, gamepadCreated;
20
+
21
+- (id)init {
22
+    self = [super init];
23
+    if (self != nil) {
24
+        serialThread = nil;
25
+        gamepadCreated = NO;
26
+        [self setDelegate:self];
27
+    }
28
+    return self;
29
+}
30
+
31
+- (BOOL)windowShouldClose:(id)sender {
32
+    if (gamepadCreated) {
33
+        [fooHID close];
34
+        gamepadCreated = NO;
35
+    }
36
+    
37
+    if (serialThread != nil) {
38
+        // Stop thread and wait for it to finish
39
+        [serialThread setRunning:NO];
40
+        while ([serialThread isFinished] == NO) {
41
+            usleep(1000);
42
+        }
43
+    }
44
+    
45
+    return YES;
46
+}
18 47
 
19 48
 - (IBAction)connectButtonPressed:(id)sender {
20 49
     if (serialThread == nil) {
21
-        serialThread = [[Thread alloc] init];
50
+        serialThread = [[Thread alloc] initWithWindow:self];
22 51
         [serialThread setName:@"Serial Communication"];
23 52
         [serialThread setPortName:[portList titleOfSelectedItem]];
24 53
         
@@ -30,15 +59,22 @@
30 59
         }
31 60
     } else {
32 61
         [serialThread setRunning:NO];
33
-        // TODO disable button, thread should call back when closed
34
-        // so we can set serialThread to nil then
35 62
         serialThread = nil;
36 63
         [connectButton setTitle:@"Connect"];
37 64
     }
38 65
 }
39 66
 
40 67
 - (IBAction)createButtonPressed:(id)sender {
41
-    
68
+    if (gamepadCreated) {
69
+        [fooHID close];
70
+        gamepadCreated = NO;
71
+        [createButton setTitle:@"Create"];
72
+    } else {
73
+        if ([fooHID init] == 0) {
74
+            gamepadCreated = YES;
75
+            [createButton setTitle:@"Destroy"];
76
+        }
77
+    }
42 78
 }
43 79
 
44 80
 - (void)populatePortList {
@@ -46,11 +82,36 @@
46 82
     if ([ports count] > 0) {
47 83
         [portList removeAllItems];
48 84
         [portList addItemsWithTitles:ports];
85
+        
86
+        for (int i = 0; i < [ports count]; i++) {
87
+            if ([[ports objectAtIndex:i] isEqualToString:@"/dev/tty.SLAB_USBtoUART"]) {
88
+                [portList selectItemAtIndex:i];
89
+            }
90
+        }
91
+        
49 92
         [portList setEnabled:YES];
50 93
         [connectButton setEnabled:YES];
94
+        [createButton setEnabled:YES];
51 95
     } else {
52 96
         NSLog(@"Couldn't find any serial ports!\n");
53 97
     }
54 98
 }
55 99
 
100
+- (void)setChannels:(id)data {
101
+    if ([data count] < 6) {
102
+        NSLog(@"Not enough channel data (%lu)?!\n", (unsigned long)[data count]);
103
+    } else {
104
+        [level1 setDoubleValue:[[data objectAtIndex:0] doubleValue]];
105
+        [level2 setDoubleValue:[[data objectAtIndex:1] doubleValue]];
106
+        [level3 setDoubleValue:[[data objectAtIndex:2] doubleValue]];
107
+        [level4 setDoubleValue:[[data objectAtIndex:3] doubleValue]];
108
+        [level5 setDoubleValue:[[data objectAtIndex:4] doubleValue]];
109
+        [level6 setDoubleValue:[[data objectAtIndex:5] doubleValue]];
110
+        
111
+        if (gamepadCreated) {
112
+            [fooHID send:data];
113
+        }
114
+    }
115
+}
116
+
56 117
 @end

+ 4
- 0
SerialGamepad/Thread.h View File

@@ -8,12 +8,16 @@
8 8
 
9 9
 #import <Foundation/Foundation.h>
10 10
 
11
+@class MainWindow;
12
+
11 13
 @interface Thread : NSThread
12 14
 
13 15
 @property BOOL running;
14 16
 @property int fd;
15 17
 @property (strong) NSString *portName;
18
+@property (weak) MainWindow *mainWindow;
16 19
 
20
+- (id)initWithWindow:(MainWindow *)window;
17 21
 - (NSInteger)openPort;
18 22
 
19 23
 @end

+ 65
- 23
SerialGamepad/Thread.m View File

@@ -12,24 +12,51 @@
12 12
 
13 13
 #import "Thread.h"
14 14
 #import "fooHID.h"
15
+#import "MainWindow.h"
16
+
17
+#define CHANNELS 6
18
+#define TESTCHANNEL 2
19
+#define PACKETSIZE 18
20
+#define HEADERBYTES 2
21
+#define CHECKSUMBYTES 2
22
+#define PAYLOADBYTES (PACKETSIZE - HEADERBYTES - CHECKSUMBYTES)
23
+#define HEADERBYTE_A 85
24
+#define HEADERBYTE_B 252
25
+
26
+enum ThreadState {
27
+    READ_FIRST_BYTE,
28
+    READ_SECOND_BYTE,
29
+    READ_PAYLOAD,
30
+    READ_CHECKSUM
31
+};
15 32
 
16 33
 @implementation Thread
17 34
 
18
-@synthesize running, fd, portName;
35
+@synthesize running, fd, portName, mainWindow;
36
+
37
+- (id)initWithWindow:(MainWindow *)window {
38
+    self = [super init];
39
+    if (self != nil) {
40
+        mainWindow = window;
41
+    }
42
+    return self;
43
+}
19 44
 
20 45
 - (NSInteger)openPort {
21 46
     if (portName == nil) {
22 47
         return 1;
23 48
     }
24 49
     
50
+    // Open port read-only, without controlling terminal, non-blocking
25 51
     fd = open([portName UTF8String], O_RDONLY | O_NOCTTY | O_NONBLOCK);
26 52
     if (fd == -1) {
27 53
         NSLog(@"Error opening serial port \"%@\"!\n", portName);
28 54
         return 1;
29 55
     }
30 56
     
31
-    fcntl(fd, F_SETFL, 0);
57
+    fcntl(fd, F_SETFL, 0); // Enable blocking I/O
32 58
     
59
+    // Read current settings
33 60
     struct termios options;
34 61
     tcgetattr(fd, &options);
35 62
     
@@ -38,41 +65,29 @@
38 65
     options.c_iflag = 0;
39 66
     options.c_cflag = 0;
40 67
     
41
-    options.c_cflag |= CS8;
42
-    options.c_cflag |= CREAD;
43
-    options.c_cflag |= CLOCAL;
68
+    options.c_cflag |= CS8; // 8 data bits
69
+    options.c_cflag |= CREAD; // Enable receiver
70
+    options.c_cflag |= CLOCAL; // Ignore modem status lines
44 71
     
45 72
     cfsetispeed(&options, B115200);
46 73
     cfsetospeed(&options, B115200);
47 74
     
48
-    options.c_cc[VMIN] = 0;
49
-    options.c_cc[VTIME] = 1;
75
+    options.c_cc[VMIN] = 0; // Return even with zero bytes...
76
+    options.c_cc[VTIME] = 1; // ...but only after .1 seconds
50 77
     
78
+    // Set new settings
51 79
     tcsetattr(fd, TCSANOW, &options);
52 80
     tcflush(fd, TCIOFLUSH);
53 81
     
54 82
     return 0;
55 83
 }
56 84
 
57
-#define PACKETSIZE 18
58
-#define HEADERBYTES 2
59
-#define CHECKSUMBYTES 2
60
-#define PAYLOADBYTES (PACKETSIZE - HEADERBYTES - CHECKSUMBYTES)
61
-#define HEADERBYTE_A 85
62
-#define HEADERBYTE_B 252
63
-
64
-enum ThreadState {
65
-    READ_FIRST_BYTE,
66
-    READ_SECOND_BYTE,
67
-    READ_PAYLOAD,
68
-    READ_CHECKSUM
69
-};
70
-
71 85
 - (void)main {
72 86
     enum ThreadState state = READ_FIRST_BYTE;
73 87
     unsigned char c = 0;
74
-    unsigned char buffer[PACKETSIZE];
88
+    unsigned char buffer[PAYLOADBYTES];
75 89
     unsigned char checksum[CHECKSUMBYTES];
90
+    uint16_t channels[CHANNELS + 1];
76 91
     int received = 0;
77 92
     
78 93
     NSLog(@"Connection running...\n");
@@ -107,7 +122,34 @@ enum ThreadState {
107 122
             if (received >= CHECKSUMBYTES) {
108 123
                 state = READ_FIRST_BYTE;
109 124
                 
110
-                // TODO got something
125
+                uint16_t sum = 0;
126
+                for (int i = 0; i < PAYLOADBYTES; i++) {
127
+                    sum += buffer[i];
128
+                }
129
+                
130
+                if (sum != ((checksum[0] << 8) | checksum[1])) {
131
+                    NSLog(@"Wrong checksum: %d != %d\n", sum, ((checksum[0] << 8) | checksum[1]));
132
+                } else {
133
+                    for (int i = 0; i < (CHANNELS + 1); i++) {
134
+                        channels[i] = buffer[2 * i] << 8;
135
+                        channels[i] |= buffer[(2 * i) + 1];
136
+                    
137
+                        if (i < CHANNELS) {
138
+                            channels[i] -= 1000;
139
+                        }
140
+                    }
141
+                    
142
+                    if (channels[CHANNELS] != channels[TESTCHANNEL]) {
143
+                        NSLog(@"Wrong test channel value: %d != %d\n", channels[CHANNELS], channels[TESTCHANNEL]);
144
+                    }
145
+                    
146
+                    NSMutableArray *arr = [[NSMutableArray alloc] initWithCapacity:CHANNELS];
147
+                    for (int i = 0; i < CHANNELS; i++) {
148
+                        [arr addObject:[[NSNumber alloc] initWithInteger:(NSInteger)channels[i]]];
149
+                    }
150
+                    
151
+                    [mainWindow performSelectorOnMainThread:@selector(setChannels:) withObject:arr waitUntilDone:NO];
152
+                }
111 153
             }
112 154
         } else {
113 155
             NSLog(@"Invalid state?!\n");

+ 15
- 3
SerialGamepad/fooHID.m View File

@@ -59,7 +59,7 @@ struct gamepad_report_t {
59 59
 };
60 60
 
61 61
 static io_connect_t connector;
62
-static uint64_t input[4];
62
+static uint64_t deviceName = 0, deviceNameLength;
63 63
 static struct gamepad_report_t gamepad;
64 64
 
65 65
 /*
@@ -118,9 +118,14 @@ int foohidInit() {
118 118
     
119 119
     NSLog(@"Creating virtual HID device...\n");
120 120
     
121
-    input[0] = (uint64_t)strdup(VIRTUAL_DEVICE_NAME);
122
-    input[1] = strlen((char*)input[0]);
121
+    if (deviceName == 0) {
122
+        deviceName = (uint64_t)strdup(VIRTUAL_DEVICE_NAME);
123
+        deviceNameLength = strlen((char *)deviceName);
124
+    }
123 125
     
126
+    uint64_t input[4];
127
+    input[0] = deviceName;
128
+    input[1] = deviceNameLength;
124 129
     input[2] = (uint64_t)report_descriptor;
125 130
     input[3] = sizeof(report_descriptor);
126 131
     
@@ -138,6 +143,10 @@ int foohidInit() {
138 143
 void foohidClose() {
139 144
     NSLog(@"Destroying virtual HID device\n");
140 145
     
146
+    uint64_t input[2];
147
+    input[0] = deviceName;
148
+    input[1] = deviceNameLength;
149
+    
141 150
     uint32_t output_count = 1;
142 151
     uint64_t output = 0;
143 152
     kern_return_t ret = IOConnectCallScalarMethod(connector, FOOHID_DESTROY, input, 2, &output, &output_count);
@@ -170,6 +179,9 @@ void foohidSend(uint16_t *data) {
170 179
      NSLog(@"Aux 2: %d\n", gamepad.aux2);
171 180
      */
172 181
     
182
+    uint64_t input[4];
183
+    input[0] = deviceName;
184
+    input[1] = deviceNameLength;
173 185
     input[2] = (uint64_t)&gamepad;
174 186
     input[3] = sizeof(struct gamepad_report_t);
175 187
     

Loading…
Cancel
Save