mac下串口通讯工具的编写
2014-09-12 11:10
375 查看
ORSSerialPort.h
ORSSerialPort.m
ORSSerialPortManager.h
ORSSerialPortManager.m
#import <Foundation/Foundation.h> #import <IOKit/IOTypes.h> #import <termios.h> //#define LOG_SERIAL_PORT_ERRORS enum { ORSSerialPortParityNone = 0, ORSSerialPortParityOdd, ORSSerialPortParityEven }; typedef NSUInteger ORSSerialPortParity; @protocol ORSSerialPortDelegate; /** * The ORSSerialPort class represents a serial port, and includes methods to * configure, open and close a port, and send and receive data to and from * a port. * * There is a 1:1 correspondence between port devices on the * system and instances of `ORSSerialPort`. That means that repeated requests * for a port object for a given device or device path will return the same * instance of `ORSSerialPort`. * * Opening a Port and Setting It Up * -------------------------------- * * You can get an `ORSSerialPort` instance either of two ways. The easiest * is to use `ORSSerialPortManager`'s `availablePorts` array. The other way * is to get a new `ORSSerialPort` instance using the serial port's BSD device path: * * ORSSerialPort *port = [ORSSerialPort serialPortWithPath:@"/dev/cu.KeySerial1"]; * * Note that you must give `+serialPortWithPath:` the full path to the * device, as shown in the example above. * * * After you've got a port instance, you can open it with the `-open` * method. When you're done using the port, close it using the `-close` * method. * * Port settings such as baud rate, number of stop bits, parity, and flow * control settings can be set using the various properties `ORSSerialPort` * provides. Note that all of these properties are Key Value Observing * (KVO) compliant. This KVO compliance also applies to read-only * properties for reading the state of the CTS, DSR and DCD pins. Among * other things, this means it's easy to be notified when the state of one * of these pins changes, without having to continually poll them, as well * as making them easy to connect to a UI with Cocoa bindings. * * Sending Data * ------------ * * Send data by passing an `NSData` object to the `-sendData:` method: * * NSData *dataToSend = [self.sendTextField.stringValue dataUsingEncoding:NSUTF8StringEncoding]; * [self.serialPort sendData:dataToSend]; * * Receiving Data * -------------- * * To receive data, you must implement the `ORSSerialPortDelegate` * protocol's `-serialPort:didReceiveData:` method, and set the * `ORSSerialPort` instance's delegate property. As noted in the documentation * for ORSSerialPortDelegate, this method is always called on the main queue. * An example implementation is included below: * * - (void)serialPort:(ORSSerialPort *)serialPort didReceiveData:(NSData *)data * { * NSString *string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; * [self.receivedDataTextView.textStorage.mutableString appendString:string]; * [self.receivedDataTextView setNeedsDisplay:YES]; * } */ @interface ORSSerialPort : NSObject /** --------------------------------------------------------------------------------------- * @name Getting a Serial Port * --------------------------------------------------------------------------------------- */ /** * Returns an `ORSSerialPort` instance representing the serial port at `devicePath`. * * `devicePath` must be the full, callout (cu.) or tty (tty.) path to an available * serial port device on the system. * * @param devicePath The full path (e.g. /dev/cu.usbserial) to the device. * * @return An initalized `ORSSerialPort` instance, or nil if there was an error. * * @see -[ORSSerialPortManager availablePorts] * @see -initWithPath: */ + (ORSSerialPort *)serialPortWithPath:(NSString *)devicePath; /** * Returns an `ORSSerialPort` instance for the serial port represented by `device`. * * Generally, `+serialPortWithPath:` is the method to use to get port instances * programatically. This method may be useful if you're doing your own * device discovery with IOKit functions, or otherwise have an IOKit port object * you want to "turn into" an ORSSerialPort. Most people will not use this method * directly. * * @param device An IOKit port object representing the serial port device. * * @return An initalized `ORSSerialPort` instance, or nil if there was an error. * * @see -[ORSSerialPortManager availablePorts] * @see +serialPortWithPath: */ + (ORSSerialPort *)serialPortWithDevice:(io_object_t)device; /** * Returns an `ORSSerialPort` instance representing the serial port at `devicePath`. * * `devicePath` must be the full, callout (cu.) or tty (tty.) path to an available * serial port device on the system. * * @param devicePath The full path (e.g. /dev/cu.usbserial) to the device. * * @return An initalized `ORSSerialPort` instance, or nil if there was an error. * * @see -[ORSSerialPortManager availablePorts] * @see +serialPortWithPath: */ - (id)initWithPath:(NSString *)devicePath; /** * Returns an `ORSSerialPort` instance for the serial port represented by `device`. * * Generally, `-initWithPath:` is the method to use to get port instances * programatically. This method may be useful if you're doing your own * device discovery with IOKit functions, or otherwise have an IOKit port object * you want to "turn into" an ORSSerialPort. Most people will not use this method * directly. * * @param device An IOKit port object representing the serial port device. * * @return An initalized `ORSSerialPort` instance, or nil if there was an error. * * @see -[ORSSerialPortManager availablePorts] * @see -initWithPath: */ - (id)initWithDevice:(io_object_t)device; /** --------------------------------------------------------------------------------------- * @name Opening and Closing * --------------------------------------------------------------------------------------- */ /** * Opens the port represented by the receiver. * * If this method succeeds, the ORSSerialPortDelegate method `-serialPortWasOpened:` will * be called. * * If opening the port fails, the ORSSerialPortDelegate method `-serialPort:didEncounterError:` will * be called. */ - (BOOL)open; /** * Closes the port represented by the receiver. * * If the port is closed successfully, the ORSSerialPortDelegate method `-serialPortWasClosed:` will * be called before this method returns. * * @return YES if closing the port was closed successfully, NO if closing the port failed. */ - (BOOL)close; - (void)cleanup DEPRECATED_ATTRIBUTE; // Should never have been called in client code, anyway. /** * Closes the port and cleans up. * * This method should never be called directly. Call `-close` to close a port instead. */ - (void)cleanupAfterSystemRemoval; /** --------------------------------------------------------------------------------------- * @name Sending Data * --------------------------------------------------------------------------------------- */ /** * Sends data out through the serial port represented by the receiver. * * This method attempts to send all data synchronously. If the serial port * is unable to accept all the passed in data in a single write operation, * The remaining data is buffered and sent later asynchronously. * * If an error occurs, the ORSSerialPortDelegate method `-serialPort:didEncounterError:` will * be called. The exception to this is if sending data fails because the port * is closed. In that case, this method returns NO, but `-serialPort:didEncounterError:` * is *not* called. You can ensure that the port is open by calling `-isOpen` before * calling this method. * * @param data An `NSData` object containing the data to be sent. * * @return YES if sending data failed, NO if an error occurred. */ - (BOOL)sendData:(NSData *)data; /** --------------------------------------------------------------------------------------- * @name Delegate * --------------------------------------------------------------------------------------- */ /** * The delegate for the serial port object. Must implement the `ORSSerialPortDelegate` protocol. * */ @property (nonatomic, unsafe_unretained) id<ORSSerialPortDelegate> delegate; /** --------------------------------------------------------------------------------------- * @name Port Object Properties * --------------------------------------------------------------------------------------- */ /** * A Boolean value that indicates whether the port is open. (read-only) */ @property (readonly, getter = isOpen) BOOL open; /** * An string representation of the device path for the serial port represented by the receiver. (read-only) */ @property (copy, readonly) NSString *path; /** * The IOKit port object for the serial port device represented by the receiver. (read-only) */ @property (readonly) io_object_t IOKitDevice; /** * The name of the serial port. * * Can be presented to the user, e.g. in a serial port selection pop up menu. */ @property (copy, readonly) NSString *name; /** --------------------------------------------------------------------------------------- * @name Configuring the Serial Port * --------------------------------------------------------------------------------------- */ /** * The baud rate for the port. * * This value should be one of the values defined in termios.h: * * - 0 * - 50 * - 75 * - 110 * - 134 * - 150 * - 200 * - 300 * - 600 * - 1200 * - 1800 * - 2400 * - 4800 * - 9600 * - 19200 * - 38400 * - 7200 * - 14400 * - 28800 * - 57600 * - 76800 * - 115200 * - 230400 * - 19200 * - 38400 */ @property (nonatomic, copy) NSNumber *baudRate; /** * The number of stop bits. Values other than 1 or 2 are invalid. */ @property (nonatomic) NSUInteger numberOfStopBits; /** * */ @property (nonatomic) BOOL shouldEchoReceivedData; /** * The parity setting for the port. Possible values are: * * - ORSSerialPortParityNone * - ORSSerialPortParityOdd * - ORSSerialPortParityEven */ @property (nonatomic) ORSSerialPortParity parity; /** * A Boolean value indicating whether the serial port uses RTS/CTS Flow Control. */ @property (nonatomic) BOOL usesRTSCTSFlowControl; /** * A Boolean value indicating whether the serial port uses DTR/DSR Flow Control. */ @property (nonatomic) BOOL usesDTRDSRFlowControl; /** * A Boolean value indicating whether the serial port uses DCD Flow Control. */ @property (nonatomic) BOOL usesDCDOutputFlowControl; /** --------------------------------------------------------------------------------------- * @name Other Port Pins * --------------------------------------------------------------------------------------- */ /** * The state of the serial port's RTS pin. * * - YES means 1 or high state. * - NO means 0 or low state. * * This property is observable using Key Value Observing. */ @property (nonatomic) BOOL RTS; /** * The state of the serial port's DTR pin. * * - YES means 1 or high state. * - NO means 0 or low state. * * This property is observable using Key Value Observing. */ @property (nonatomic) BOOL DTR; /** * The state of the serial port's CTS pin. * * - YES means 1 or high state. * - NO means 0 or low state. * * This property is observable using Key Value Observing. */ @property (nonatomic, readonly) BOOL CTS; /** * The state of the serial port's DSR pin. (read-only) * * - YES means 1 or high state. * - NO means 0 or low state. * * This property is observable using Key Value Observing. */ @property (nonatomic, readonly) BOOL DSR; /** * The state of the serial port's DCD pin. (read-only) * * - YES means 1 or high state. * - NO means 0 or low state. * * This property is observable using Key Value Observing. */ @property (nonatomic, readonly) BOOL DCD; @end /** * The ORSSerialPortDelegate protocol defines methods to be implemented * by the delegate of an `ORSSerialPort` object. * * *Note*: All `ORSSerialPortDelegate` methods are always called on the main queue. * If you need to handle them on a background queue, you must dispatch your handling * to a background queue in your implementation of the delegate method. */ @protocol ORSSerialPortDelegate @required /** * Called when new data is received by the serial port from an external source. * * @param serialPort The `ORSSerialPort` instance representing the port that received `data`. * @param data An `NSData` instance containing the data received. */ - (void)serialPort:(ORSSerialPort *)serialPort didReceiveData:(NSData *)data; /** * Called when a serial port is removed from the system, e.g. the user unplugs * the USB to serial adapter for the port. * * In this method, you should discard any strong references you have maintained for the * passed in `serialPort` object. The behavior of `ORSSerialPort` instances whose underlying * serial port has been removed from the system is undefined. * * @param serialPort The `ORSSerialPort` instance representing the port that was removed. */ - (void)serialPortWasRemovedFromSystem:(ORSSerialPort *)serialPort; @optional /** * Called when an error occurs during an operation involving a serial port. * * This method is always used to report errors. No `ORSSerialPort` methods * take a passed in `NSError **` reference because errors may occur asynchonously, * after a method has returned. * * Currently, errors reported using this method are always in the `NSPOSIXErrorDomain`, * and a list of error codes can be found in the system header errno.h. * * The error object's userInfo dictionary contains the following keys: * * - NSLocalizedDescriptionKey - An error message string. * - NSFilePathErrorKey - The device path to the serial port. Same as `[serialPort path]`. * * @param serialPort The `ORSSerialPort` instance for the port * @param error An `NSError` object containing information about the error. */ - (void)serialPort:(ORSSerialPort *)serialPort didEncounterError:(NSError *)error; /** * Called when a serial port is successfully opened. * * @param serialPort The `ORSSerialPort` instance representing the port that was opened. */ - (void)serialPortWasOpened:(ORSSerialPort *)serialPort; /** * Called when a serial port was closed (e.g. because `-close`) was called. * * @param serialPort The `ORSSerialPort` instance representing the port that was closed. */ - (void)serialPortWasClosed:(ORSSerialPort *)serialPort; @end
ORSSerialPort.m
#if !__has_feature(objc_arc) #error ORSSerialPort.m must be compiled with ARC. Either turn on ARC for the project or set the -fobjc-arc flag for ORSSerialPort.m in the Build Phases for this target #endif #if OS_OBJECT_USE_OBJC && __has_feature(objc_arc) #define ORS_GCD_RELEASE(x) #define ORS_GCD_RETAIN(x) #else #define ORS_GCD_RELEASE(x) dispatch_release(x) #define ORS_GCD_RETAIN(x) dispatch_retain(x) #endif #import "ORSSerialPort.h" #import <IOKit/serial/IOSerialKeys.h> #import <IOKit/serial/ioss.h> #import <sys/param.h> #import <sys/filio.h> #import <sys/ioctl.h> #ifdef LOG_SERIAL_PORT_ERRORS #define LOG_SERIAL_PORT_ERROR(fmt, ...) NSLog(fmt, ## __VA_ARGS__) #else #define LOG_SERIAL_PORT_ERROR(fmt, ...) #endif static __strong NSMutableArray *allSerialPorts; @interface ORSSerialPort () { struct termios originalPortAttributes; } @property (copy, readwrite) NSString *path; @property (readwrite) io_object_t IOKitDevice; @property (copy, readwrite) NSString *name; @property (strong) NSMutableData *writeBuffer; @property int fileDescriptor; @property (nonatomic, readwrite) BOOL CTS; @property (nonatomic, readwrite) BOOL DSR; @property (nonatomic, readwrite) BOOL DCD; #if OS_OBJECT_USE_OBJC @property (nonatomic, strong) dispatch_source_t pinPollTimer; #else @property (nonatomic) dispatch_source_t pinPollTimer; #endif @end @implementation ORSSerialPort + (void)initialize { static dispatch_once_t once; dispatch_once(&once, ^{ allSerialPorts = [[NSMutableArray alloc] init]; }); } + (void)addSerialPort:(ORSSerialPort *)port; { [allSerialPorts addObject:[NSValue valueWithNonretainedObject:port]]; } + (void)removeSerialPort:(ORSSerialPort *)port; { NSValue *valueToRemove = nil; for (NSValue *value in allSerialPorts) { if ([value nonretainedObjectValue] == port) { valueToRemove = value; break; } } if (valueToRemove) [allSerialPorts removeObject:valueToRemove]; } + (ORSSerialPort *)existingPortWithPath:(NSString *)path; { ORSSerialPort *existingPort = nil; for (NSValue *value in allSerialPorts) { ORSSerialPort *port = [value nonretainedObjectValue]; if ([port.path isEqualToString:path]) { existingPort = port; break; } } return existingPort; } + (ORSSerialPort *)serialPortWithPath:(NSString *)devicePath { return [[self alloc] initWithPath:devicePath]; } + (ORSSerialPort *)serialPortWithDevice:(io_object_t)device; { return [[self alloc] initWithDevice:device]; } - (id)initWithPath:(NSString *)devicePath { io_object_t device = [[self class] deviceFromBSDPath:devicePath]; if (device == 0) { self = nil; return self; } return [self initWithDevice:device]; } - (id)initWithDevice:(io_object_t)device; { NSAssert(device != 0, @"%s requires non-zero device argument.", __PRETTY_FUNCTION__); NSString *bsdPath = [[self class] bsdCalloutPathFromDevice:device]; ORSSerialPort *existingPort = [[self class] existingPortWithPath:bsdPath]; if (existingPort != nil) { self = nil; return existingPort; } self = [super init]; if (self != nil) { self.ioKitDevice = device; self.path = bsdPath; self.name = [[self class] modemNameFromDevice:device]; self.writeBuffer = [NSMutableData data]; self.baudRate = @B9600;//@B19200; self.numberOfStopBits = 1; self.parity = ORSSerialPortParityNone; self.shouldEchoReceivedData = NO; self.usesRTSCTSFlowControl = NO; self.usesDTRDSRFlowControl = NO; self.usesDCDOutputFlowControl = NO; self.RTS = NO; self.DTR = NO; } [[self class] addSerialPort:self]; return self; } - (id)init { NSAssert(0, @"ORSSerialPort must be init'd using -initWithPath:"); return nil; } - (void)dealloc { [[self class] removeSerialPort:self]; self.IOKitDevice = 0; if (_pinPollTimer) { dispatch_source_cancel(_pinPollTimer); ORS_GCD_RELEASE(_pinPollTimer); } } - (NSString *)description { return self.name; // io_object_t device = [[self class] deviceFromBSDPath:self.path]; // return [NSString stringWithFormat:@"BSD Path:%@, base name:%@, modem name:%@, suffix:%@, service type:%@", [[self class] bsdCalloutPathFromDevice:device], [[self class] baseNameFromDevice:device], [[self class] modemNameFromDevice:device], [[self class] suffixFromDevice:device], [[self class] serviceTypeFromDevice:device]]; } - (NSUInteger)hash { return [self.path hash]; } - (BOOL)isEqual:(id)object { if (![object isKindOfClass:[self class]]) return NO; if (![object respondsToSelector:@selector(path)]) return NO; return [self.path isEqual:[object path]]; } #pragma mark - Public Methods - (BOOL)open; { if (self.isOpen) return NO; dispatch_queue_t mainQueue = dispatch_get_main_queue(); int descriptor=0; descriptor = open([self.path cStringUsingEncoding:NSASCIIStringEncoding], O_RDWR | O_NOCTTY | O_EXLOCK | O_NONBLOCK); if (descriptor < 1) { // Error [self notifyDelegateOfPosixError]; return NO; } // Now that the device is open, clear the O_NONBLOCK flag so subsequent I/O will block. // See fcntl(2) ("man 2 fcntl") for details. if (fcntl(descriptor, F_SETFL, 0) == -1) { LOG_SERIAL_PORT_ERROR(@"Error clearing O_NONBLOCK %@ - %s(%d).\n", self.path, strerror(errno), errno); //return NO; } self.fileDescriptor = descriptor; // Port opened successfully, set options tcgetattr(descriptor, &originalPortAttributes); // Get original options so they can be reset later [self setPortOptions]; // Get status of RTS and DTR lines int modemLines=0; if (ioctl(self.fileDescriptor, TIOCMGET, &modemLines) < 0) { LOG_SERIAL_PORT_ERROR(@"Error reading modem lines status"); [self notifyDelegateOfPosixError]; } BOOL desiredRTS = self.RTS; BOOL desiredDTR = self.DTR; self.RTS = modemLines & TIOCM_RTS; self.DTR = modemLines & TIOCM_DTR; self.RTS = desiredRTS; self.DTR = desiredDTR; if ([(id)self.delegate respondsToSelector:@selector(serialPortWasOpened:)]) { dispatch_async(mainQueue, ^{ [self.delegate serialPortWasOpened:self]; }); } // Start a read poller in the background dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ int localPortFD = self.fileDescriptor; struct timeval timeout; int result=0; while (self.isOpen) { fd_set localReadFDSet; FD_ZERO(&localReadFDSet); FD_SET(localPortFD, &localReadFDSet); timeout.tv_sec = 0; timeout.tv_usec = 100000; // Check to see if port closed every 100ms result = select(localPortFD+1, &localReadFDSet, NULL, NULL, &timeout); if (!self.isOpen) break; // Port closed while select call was waiting if (result < 0) { [self notifyDelegateOfPosixError]; continue; } if (result == 0 || !FD_ISSET(localPortFD, &localReadFDSet)) continue; // Data is available char buf[1024]; long lengthRead = read(localPortFD, buf, sizeof(buf)); if (lengthRead>0) { NSData *readData = [NSData dataWithBytes:buf length:lengthRead]; if (readData != nil) [self receiveData:readData]; } } }); // Start another poller to check status of CTS and DSR dispatch_queue_t pollQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, pollQueue); dispatch_source_set_timer(timer, dispatch_time(DISPATCH_TIME_NOW, 0), 10*NSEC_PER_MSEC, 5*NSEC_PER_MSEC); dispatch_source_set_event_handler(timer, ^{ if (!self.isOpen) { dispatch_async(pollQueue, ^{ dispatch_source_cancel(timer); }); return; } int32_t modemLines=0; int result = ioctl(self.fileDescriptor, TIOCMGET, &modemLines); if (result < 0) { [self notifyDelegateOfPosixErrorWaitingUntilDone:(errno == ENXIO)]; if (errno == ENXIO) { [self cleanupAfterSystemRemoval]; } return; } BOOL CTSPin = (modemLines & TIOCM_CTS) != 0; BOOL DSRPin = (modemLines & TIOCM_DSR) != 0; BOOL DCDPin = (modemLines & TIOCM_CAR) != 0; if (CTSPin != self.CTS) dispatch_sync(mainQueue, ^{self.CTS = CTSPin;}); if (DSRPin != self.DSR) dispatch_sync(mainQueue, ^{self.DSR = DSRPin;}); if (DCDPin != self.DCD) dispatch_sync(mainQueue, ^{self.DCD = DCDPin;}); }); self.pinPollTimer = timer; dispatch_resume(self.pinPollTimer); ORS_GCD_RELEASE(timer); return YES; } - (BOOL)close; { if (!self.isOpen) return YES; [self.writeBuffer replaceBytesInRange:NSMakeRange(0, [self.writeBuffer length]) withBytes:NULL length:0]; self.pinPollTimer = nil; // Stop polling CTS/DSR/DCD pins // The next tcsetattr() call can fail if the port is waiting to send data. This is likely to happen // e.g. if flow control is on and the CTS line is low. So, turn off flow control before proceeding struct termios options; tcgetattr(self.fileDescriptor, &options); options.c_cflag &= ~CRTSCTS; // RTS/CTS Flow Control options.c_cflag &= ~(CDTR_IFLOW | CDSR_OFLOW); // DTR/DSR Flow Control options.c_cflag &= ~CCAR_OFLOW; // DCD Flow Control tcsetattr(self.fileDescriptor, TCSANOW, &options); // Set port back the way it was before we used it tcsetattr(self.fileDescriptor, TCSADRAIN, &originalPortAttributes); int localFD = self.fileDescriptor; self.fileDescriptor = 0; // So other threads know that the port should be closed and can stop I/O operations if (close(localFD)) { self.fileDescriptor = localFD; LOG_SERIAL_PORT_ERROR(@"Error closing serial port with file descriptor %i:%i", self.fileDescriptor, errno); [self notifyDelegateOfPosixError]; return NO; } if ([(id)self.delegate respondsToSelector:@selector(serialPortWasClosed:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate serialPortWasClosed:self]; }); } return YES; } - (void)cleanup; { NSLog(@"Cleanup is deprecated and was never intended to be called publicly. You should update your code to avoid calling this method."); [self cleanupAfterSystemRemoval]; } - (void)cleanupAfterSystemRemoval { if ([(id)self.delegate respondsToSelector:@selector(serialPortWasRemovedFromSystem:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate serialPortWasRemovedFromSystem:self]; [self close]; }); } else { [self close]; } } - (BOOL)sendData:(NSData *)data; { if (!self.isOpen) return NO; [self.writeBuffer appendData:data]; if ([self.writeBuffer length] < 1) return YES; long numBytesWritten = write(self.fileDescriptor, [self.writeBuffer bytes], [self.writeBuffer length]); if (numBytesWritten < 0) { LOG_SERIAL_PORT_ERROR(@"Error writing to serial port:%d", errno); [self notifyDelegateOfPosixError]; return NO; } if (numBytesWritten > 0) [self.writeBuffer replaceBytesInRange:NSMakeRange(0, numBytesWritten) withBytes:NULL length:0]; return YES; } #pragma mark - Private Methods #pragma mark Port Read/Write - (void)receiveData:(NSData *)data; { if ([(id)self.delegate respondsToSelector:@selector(serialPort:didReceiveData:)]) { dispatch_async(dispatch_get_main_queue(), ^{ [self.delegate serialPort:self didReceiveData:data]; }); } } #pragma mark Port Propeties Methods - (void)setPortOptions; { if ([self fileDescriptor] < 1) return; struct termios options; tcgetattr(self.fileDescriptor, &options); cfmakeraw(&options); options.c_cc[VMIN] = 1; // Wait for at least 1 character before returning options.c_cc[VTIME] = 2; // Wait 200 milliseconds between bytes before returning from read // Set 8 data bits options.c_cflag &= ~CSIZE; options.c_cflag |= CS8; // Set parity switch (self.parity) { case ORSSerialPortParityNone: options.c_cflag &= ~PARENB; break; case ORSSerialPortParityEven: options.c_cflag |= PARENB; options.c_cflag &= ~PARODD; break; case ORSSerialPortParityOdd: options.c_cflag |= PARENB; options.c_cflag |= PARODD; break; default: break; } options.c_cflag = [self numberOfStopBits] > 1 ? options.c_cflag | CSTOPB : options.c_cflag & ~CSTOPB; // number of stop bits options.c_lflag = [self shouldEchoReceivedData] ? options.c_lflag | ECHO : options.c_lflag & ~ECHO; // echo options.c_cflag = [self usesRTSCTSFlowControl] ? options.c_cflag | CRTSCTS : options.c_cflag & ~CRTSCTS; // RTS/CTS Flow Control options.c_cflag = [self usesDTRDSRFlowControl] ? options.c_cflag | (CDTR_IFLOW | CDSR_OFLOW) : options.c_cflag & ~(CDTR_IFLOW | CDSR_OFLOW); // DTR/DSR Flow Control options.c_cflag = [self usesDCDOutputFlowControl] ? options.c_cflag | CCAR_OFLOW : options.c_cflag & ~CCAR_OFLOW; // DCD Flow Control options.c_cflag |= HUPCL; // Turn on hangup on close options.c_cflag |= CLOCAL; // Set local mode on options.c_cflag |= CREAD; // Enable receiver options.c_lflag &= ~(ICANON /*| ECHO*/ | ISIG); // Turn off canonical mode and signals // Set baud rate cfsetspeed(&options, [[self baudRate] unsignedLongValue]); // TODO: Call delegate error handling method if this fails int result = tcsetattr(self.fileDescriptor, TCSANOW, &options); if (result != 0) NSLog(@"Unable to set options on %@: %i", self, result); } + (io_object_t)deviceFromBSDPath:(NSString *)bsdPath; { if ([bsdPath length] < 1) return 0; CFMutableDictionaryRef matchingDict = NULL; matchingDict = IOServiceMatching(kIOSerialBSDServiceValue); CFRetain(matchingDict); // Need to use it twice CFDictionaryAddValue(matchingDict, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes)); io_iterator_t portIterator = 0; kern_return_t err = IOServiceGetMatchingServices(kIOMasterPortDefault, matchingDict, &portIterator); CFRelease(matchingDict); if (err) return 0; io_object_t eachPort = 0; io_object_t result = 0; while ((eachPort = IOIteratorNext(portIterator))) { NSString *calloutPath = [self bsdCalloutPathFromDevice:eachPort]; NSString *dialinPath = [self bsdDialinPathFromDevice:eachPort]; if ([bsdPath isEqualToString:calloutPath] || [bsdPath isEqualToString:dialinPath]) { result = eachPort; break; } IOObjectRelease(eachPort); } IOObjectRelease(portIterator); return result; } + (NSString *)stringPropertyOf:(io_object_t)aDevice forIOSerialKey:(NSString *)key; { CFStringRef string = (CFStringRef)IORegistryEntryCreateCFProperty(aDevice,(__bridge CFStringRef)key,kCFAllocatorDefault,0); return (__bridge_transfer NSString *)string; } + (NSString *)bsdCalloutPathFromDevice:(io_object_t)aDevice; { return [self stringPropertyOf:aDevice forIOSerialKey:(NSString*)CFSTR(kIOCalloutDeviceKey)]; } + (NSString *)bsdDialinPathFromDevice:(io_object_t)aDevice; { return [self stringPropertyOf:aDevice forIOSerialKey:(NSString*)CFSTR(kIODialinDeviceKey)]; } + (NSString *)baseNameFromDevice:(io_object_t)aDevice; { return [self stringPropertyOf:aDevice forIOSerialKey:(NSString*)CFSTR(kIOTTYBaseNameKey)]; } + (NSString *)serviceTypeFromDevice:(io_object_t)aDevice; { return [self stringPropertyOf:aDevice forIOSerialKey:(NSString*)CFSTR(kIOSerialBSDTypeKey)]; } + (NSString *)modemNameFromDevice:(io_object_t)aDevice; { return [self stringPropertyOf:aDevice forIOSerialKey:(NSString*)CFSTR(kIOTTYDeviceKey)]; } + (NSString *)suffixFromDevice:(io_object_t)aDevice; { return [self stringPropertyOf:aDevice forIOSerialKey:(NSString*)CFSTR(kIOTTYSuffixKey)]; } #pragma mark Helper Methods - (void)notifyDelegateOfPosixError { [self notifyDelegateOfPosixErrorWaitingUntilDone:NO]; } - (void)notifyDelegateOfPosixErrorWaitingUntilDone:(BOOL)shouldWait; { if (![(id)self.delegate respondsToSelector:@selector(serialPort:didEncounterError:)]) return; NSDictionary *errDict = @{NSLocalizedDescriptionKey: @(strerror(errno)), NSFilePathErrorKey: self.path}; NSError *error = [NSError errorWithDomain:NSPOSIXErrorDomain code:errno userInfo:errDict]; void (^notifyBlock)(void) = ^{ [self.delegate serialPort:self didEncounterError:error]; }; if ([NSThread isMainThread]) { notifyBlock(); } else if (shouldWait) { dispatch_sync(dispatch_get_main_queue(), notifyBlock); } else { dispatch_async(dispatch_get_main_queue(), notifyBlock); } } #pragma mark - Properties + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; if ([key isEqualToString:@"isOpen"]) { keyPaths = [keyPaths setByAddingObject:@"fileDescriptor"]; } return keyPaths; } @synthesize delegate = _delegate; #pragma mark Port Properties - (BOOL)isOpen { return self.fileDescriptor != 0; } @synthesize path = _path; @synthesize IOKitDevice = _IOKitDevice; - (void)setIoKitDevice:(io_object_t)device { if (device != _IOKitDevice) { if (_IOKitDevice) IOObjectRelease(_IOKitDevice); _IOKitDevice = device; if (_IOKitDevice) IOObjectRetain(_IOKitDevice); } } @synthesize name = _name; @synthesize baudRate = _baudRate; - (void)setBaudRate:(NSNumber *)rate { if (rate != _baudRate) { _baudRate = [rate copy]; [self setPortOptions]; } } @synthesize numberOfStopBits = _numberOfStopBits; - (void)setNumberOfStopBits:(NSUInteger)num { if (num != _numberOfStopBits) { _numberOfStopBits = num; [self setPortOptions]; } } @synthesize shouldEchoReceivedData = _shouldEchoReceivedData; - (void)setShouldEchoReceivedData:(BOOL)flag { if (flag != _shouldEchoReceivedData) { _shouldEchoReceivedData = flag; [self setPortOptions]; } } @synthesize parity = _parity; - (void)setParity:(ORSSerialPortParity)aParity { if (aParity != _parity) { if (aParity != ORSSerialPortParityNone && aParity != ORSSerialPortParityOdd && aParity != ORSSerialPortParityEven) { aParity = ORSSerialPortParityNone; } _parity = aParity; [self setPortOptions]; } } @synthesize usesRTSCTSFlowControl = _usesRTSCTSFlowControl; - (void)setUsesRTSCTSFlowControl:(BOOL)flag { if (flag != _usesRTSCTSFlowControl) { // Turning flow control one while the port is open doesn't seem to work right, // at least with some drivers, so close it then reopen it if needed BOOL shouldReopen = self.isOpen; [self close]; _usesRTSCTSFlowControl = flag; [self setPortOptions]; if (shouldReopen) [self open]; } } @synthesize usesDTRDSRFlowControl = _usesDTRDSRFlowControl; - (void)setUsesDTRDSRFlowControl:(BOOL)flag { if (flag != _usesDTRDSRFlowControl) { // Turning flow control one while the port is open doesn't seem to work right, // at least with some drivers, so close it then reopen it if needed BOOL shouldReopen = self.isOpen; [self close]; _usesDTRDSRFlowControl = flag; [self setPortOptions]; if (shouldReopen) [self open]; } } @synthesize usesDCDOutputFlowControl = _usesDCDOutputFlowControl; - (void)setUsesDCDOutputFlowControl:(BOOL)flag { if (flag != _usesDCDOutputFlowControl) { // Turning flow control one while the port is open doesn't seem to work right, // at least with some drivers, so close it then reopen it if needed BOOL shouldReopen = self.isOpen; [self close]; _usesDCDOutputFlowControl = flag; [self setPortOptions]; if (shouldReopen) [self open]; } } @synthesize RTS = _RTS; - (void)setRTS:(BOOL)flag { if (flag != _RTS) { _RTS = flag; if (![self isOpen]) return; int bits; ioctl( self.fileDescriptor, TIOCMGET, &bits ) ; bits = _RTS ? bits | TIOCM_RTS : bits & ~TIOCM_RTS; if (ioctl( self.fileDescriptor, TIOCMSET, &bits ) < 0) { LOG_SERIAL_PORT_ERROR(@"Error in %s", __PRETTY_FUNCTION__); [self notifyDelegateOfPosixError]; } } } @synthesize DTR = _DTR; - (void)setDTR:(BOOL)flag { if (flag != _DTR) { _DTR = flag; if (![self isOpen]) return; int bits; ioctl( self.fileDescriptor, TIOCMGET, &bits ) ; bits = _DTR ? bits | TIOCM_DTR : bits & ~TIOCM_DTR; if (ioctl( self.fileDescriptor, TIOCMSET, &bits ) < 0) { LOG_SERIAL_PORT_ERROR(@"Error in %s", __PRETTY_FUNCTION__); [self notifyDelegateOfPosixError]; } } } @synthesize CTS = _CTS; @synthesize DSR = _DSR; @synthesize DCD = _DCD; #pragma mark Private Properties @synthesize writeBuffer = _writeBuffer; @synthesize fileDescriptor = _fileDescriptor; @synthesize pinPollTimer = _pinPollTimer; - (void)setPinPollTimer:(dispatch_source_t)timer { if (timer != _pinPollTimer) { if (_pinPollTimer) { dispatch_source_cancel(_pinPollTimer); ORS_GCD_RELEASE(_pinPollTimer); } if (timer) { ORS_GCD_RETAIN(timer); } _pinPollTimer = timer; } } @end
ORSSerialPortManager.h
#import <Foundation/Foundation.h> /// Posted when a serial port is connected to the system extern NSString * const ORSSerialPortsWereConnectedNotification; /// Posted when a serial port is disconnected from the system extern NSString * const ORSSerialPortsWereDisconnectedNotification; /// Key for connected port in ORSSerialPortWasConnectedNotification userInfo dictionary extern NSString * const ORSConnectedSerialPortsKey; /// Key for disconnected port in ORSSerialPortWasDisconnectedNotification userInfo dictionary extern NSString * const ORSDisconnectedSerialPortsKey; /** * `ORSSerialPortManager` is a singleton class (one instance per * application) that can be used to get a list of available serial ports. * It will also handle closing open serial ports when the Mac goes to * sleep, and reopening them automatically on wake. This prevents problems * I've seen with serial port drivers that can hang if the port is left * open when putting the machine to sleep. Note that using * `ORSSerialPortManager` is optional. It provides some nice functionality, * but only `ORSSerialPort` is necessary to simply send and received data. * * Using ORSSerialPortManager * -------------------------- * * To get the shared serial port * manager: * * ORSSerialPortManager *portManager = [ORSSerialPortManager sharedSerialPortManager]; * * To get a list of available ports: * * NSArray *availablePorts = portManager.availablePorts; * * Notifications * ------------- * * `ORSSerialPort` posts notifications when a port is added to or removed from the system. * `ORSSerialPortsWereConnectedNotification` is posted when one or more ports * are added to the system. `ORSSerialPortsWereDisconnectedNotification` is posted when * one ore more ports are removed from the system. The user info dictionary for each * notification contains the list of ports added or removed. The keys to access these array * are `ORSConnectedSerialPortsKey`, and `ORSDisconnectedSerialPortsKey` respectively. * * KVO Compliance * -------------- * * `ORSSerialPortManager` is Key-Value Observing (KVO) compliant for its * `availablePorts` property. This means that you can observe * `availablePorts` to be notified when ports are added to or removed from * the system. This also means that you can easily bind UI elements to the * serial port manager's `availablePorts` property using Cocoa-bindings. * This makes it easy to create a popup menu that displays available serial * ports and updates automatically, for example. * * Close-On-Sleep * -------------- * * `ORSSerialPortManager`'s close-on-sleep, reopen-on-wake functionality is * automatic. The only thing necessary to enable it is to make sure that * the singleton instance of `ORSSerialPortManager` has been created by * calling `+sharedSerialPortManager` at least once. Note that this * behavior is only available in Cocoa apps, and is disabled when * ORSSerialPort is used in a command-line only app. */ @interface ORSSerialPortManager : NSObject /** * Returns the shared (singleton) serial port manager object. * * @return The shared serial port manager. */ + (ORSSerialPortManager *)sharedSerialPortManager; /** * An array containing ORSSerialPort instances representing the * serial ports available on the system. (read-only) * * As explained above, this property is Key Value Observing * compliant, and can be bound to for example an NSPopUpMenu * to easily give the user a way to select an available port * on the system. */ @property (nonatomic, copy, readonly) NSArray *availablePorts; @end
ORSSerialPortManager.m
#if !__has_feature(objc_arc) #error ORSSerialPortManager.m must be compiled with ARC. Either turn on ARC for the project or set the -fobjc-arc flag for ORSSerialPortManager.m in the Build Phases for this target #endif #import "ORSSerialPortManager.h" #import "ORSSerialPort.h" #import <IOKit/IOKitLib.h> #import <IOKit/serial/IOSerialKeys.h> #ifdef LOG_SERIAL_PORT_ERRORS #define LOG_SERIAL_PORT_ERROR(fmt, ...) NSLog(fmt, ##__VA_ARGS__) #else #define LOG_SERIAL_PORT_ERROR(fmt, ...) #endif NSString * const ORSSerialPortsWereConnectedNotification = @"ORSSerialPortWasConnectedNotification"; NSString * const ORSSerialPortsWereDisconnectedNotification = @"ORSSerialPortWasDisconnectedNotification"; NSString * const ORSConnectedSerialPortsKey = @"ORSConnectedSerialPortsKey"; NSString * const ORSDisconnectedSerialPortsKey = @"ORSDisconnectedSerialPortsKey"; void ORSSerialPortManagerPortsPublishedNotificationCallback(void *refCon, io_iterator_t iterator); void ORSSerialPortManagerPortsTerminatedNotificationCallback(void *refCon, io_iterator_t iterator); @interface ORSSerialPortManager () @property (nonatomic, copy, readwrite) NSArray *availablePorts; @property (nonatomic, strong) NSMutableArray *portsToReopenAfterSleep; @property (nonatomic) io_iterator_t portPublishedNotificationIterator; @property (nonatomic) io_iterator_t portTerminatedNotificationIterator; @end static ORSSerialPortManager *sharedInstance = nil; @implementation ORSSerialPortManager { NSMutableArray *_availablePorts; } #pragma mark - Singleton Methods - (id)init { if (self == sharedInstance) return sharedInstance; // Already initialized self = [super init]; if (self != nil) { self.portsToReopenAfterSleep = [NSMutableArray array]; [self retrieveAvailablePortsAndRegisterForChangeNotifications]; [self registerForNotifications]; } return self; } + (ORSSerialPortManager *)sharedSerialPortManager; { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (sharedInstance == nil) sharedInstance = [(ORSSerialPortManager *)[super allocWithZone:NULL] init]; }); return sharedInstance; } + (id)allocWithZone:(NSZone *)zone { return [self sharedSerialPortManager]; } - (id)copyWithZone:(NSZone *)zone { return self; } - (void)dealloc { NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc removeObserver:self]; // Stop IOKit notifications for ports being added/removed IOObjectRelease(_portPublishedNotificationIterator); _portPublishedNotificationIterator = 0; IOObjectRelease(_portTerminatedNotificationIterator); _portTerminatedNotificationIterator = 0; } - (void)registerForNotifications { // register for notifications (only if AppKit is available) void (^terminationBlock)(void) = ^{ for (ORSSerialPort *eachPort in self.availablePorts) [eachPort cleanupAfterSystemRemoval]; self.availablePorts = nil; }; #ifdef NSAppKitVersionNumber10_0 NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserverForName:NSApplicationWillTerminateNotification object:nil queue:nil usingBlock:^(NSNotification *notification){ // For some unknown reason, this notification fires twice, // doesn't cause a problem right now, but be aware terminationBlock(); }]; [nc addObserver:self selector:@selector(systemWillSleep:) name:NSWorkspaceWillSleepNotification object:NULL]; [nc addObserver:self selector:@selector(systemDidWake:) name:NSWorkspaceDidWakeNotification object:NULL]; #else // If AppKit isn't available, as in a Foundation command-line tool, cleanup upon exit. Sleep/wake // notifications don't seem to be available without NSWorkspace. int result = atexit_b(terminationBlock); if (result) NSLog(@"ORSSerialPort was unable to register its termination handler for serial port cleanup: %i", errno); #endif } #pragma mark - Public Methods #pragma mark - #pragma Sleep/Wake Management - (void)systemWillSleep:(NSNotification *)notification; { NSArray *ports = self.availablePorts; for (ORSSerialPort *port in ports) { if (port.isOpen) { if ([port close]) [self.portsToReopenAfterSleep addObject:port]; } } } - (void)systemDidWake:(NSNotification *)notification; { NSArray *portsToReopen = [self.portsToReopenAfterSleep copy]; for (ORSSerialPort *port in portsToReopen) { [port open]; [self.portsToReopenAfterSleep removeObject:port]; } } #pragma mark - Private Methods - (void)serialPortsWerePublished:(io_iterator_t)iterator; { NSMutableArray *newlyConnectedPorts = [[NSMutableArray alloc] init]; io_object_t device; while ((device = IOIteratorNext(iterator))) { ORSSerialPort *port = [[ORSSerialPort alloc] initWithDevice:device]; if (![self.availablePorts containsObject:port]) [newlyConnectedPorts addObject:port]; IOObjectRelease(device); } [[self mutableArrayValueForKey:@"availablePorts"] addObjectsFromArray:newlyConnectedPorts]; NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; NSDictionary *userInfo = @{ORSConnectedSerialPortsKey : newlyConnectedPorts}; [nc postNotificationName:ORSSerialPortsWereConnectedNotification object:self userInfo:userInfo]; } - (void)serialPortsWereTerminated:(io_iterator_t)iterator; { NSMutableArray *newlyDisconnectedPorts = [[NSMutableArray alloc] init]; io_object_t device; while ((device = IOIteratorNext(iterator))) { ORSSerialPort *port = [[ORSSerialPort alloc] initWithDevice:device]; [newlyDisconnectedPorts addObject:port]; IOObjectRelease(device); } [newlyDisconnectedPorts makeObjectsPerformSelector:@selector(cleanupAfterSystemRemoval)]; [[self mutableArrayValueForKey:@"availablePorts"] removeObjectsInArray:newlyDisconnectedPorts]; NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; NSDictionary *userInfo = @{ORSDisconnectedSerialPortsKey : newlyDisconnectedPorts}; [nc postNotificationName:ORSSerialPortsWereDisconnectedNotification object:self userInfo:userInfo]; } - (void)retrieveAvailablePortsAndRegisterForChangeNotifications; { IONotificationPortRef notificationPort = IONotificationPortCreate(kIOMasterPortDefault); CFRunLoopAddSource(CFRunLoopGetCurrent(),IONotificationPortGetRunLoopSource(notificationPort),kCFRunLoopDefaultMode); CFMutableDictionaryRef matchingDict = NULL; matchingDict = IOServiceMatching(kIOSerialBSDServiceValue); CFRetain(matchingDict); // Need to use it twice CFDictionaryAddValue(matchingDict, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes)); io_iterator_t portIterator = 0; kern_return_t result = IOServiceAddMatchingNotification(notificationPort,kIOPublishNotification,matchingDict,ORSSerialPortManagerPortsPublishedNotificationCallback,(__bridge void *)(self),// refCon/contextInfo &portIterator); if (result){ LOG_SERIAL_PORT_ERROR(@"Error getting serialPort list:%i", result); if (portIterator) IOObjectRelease(portIterator); CFRelease(matchingDict); // Above call to IOServiceAddMatchingNotification consumes one reference, but we added a retain for the below call return; } self.portPublishedNotificationIterator = portIterator; IOObjectRelease(portIterator); NSMutableArray *ports = [NSMutableArray array]; io_object_t eachPort; while ((eachPort = IOIteratorNext(self.portPublishedNotificationIterator))) { ORSSerialPort *port = [ORSSerialPort serialPortWithDevice:eachPort]; if (port) { [ports addObject:port]; } IOObjectRelease(eachPort); } self.availablePorts = ports; // Also register for removal IONotificationPortRef terminationNotificationPort = IONotificationPortCreate(kIOMasterPortDefault); CFRunLoopAddSource(CFRunLoopGetCurrent(),IONotificationPortGetRunLoopSource(terminationNotificationPort),kCFRunLoopDefaultMode); result = IOServiceAddMatchingNotification(terminationNotificationPort,kIOTerminatedNotification,matchingDict,ORSSerialPortManagerPortsTerminatedNotificationCallback,(__bridge void *)(self),// refCon/contextInfo &portIterator); if (result) { LOG_SERIAL_PORT_ERROR(@"Error registering for serial port termination notification:%i.", result); if (portIterator) IOObjectRelease(portIterator); return; } self.portTerminatedNotificationIterator = portIterator; IOObjectRelease(portIterator); while (IOIteratorNext(self.portTerminatedNotificationIterator)) { }; // Run out the iterator or notifications won't start } #pragma mark - Properties @synthesize availablePorts = _availablePorts; - (void)setAvailablePorts:(NSArray *)ports { if (ports != _availablePorts) { _availablePorts = [ports mutableCopy]; } } //可用的串口数量 - (NSUInteger)countOfAvailablePorts { return [_availablePorts count]; } //返回某个串口 - (id)objectInAvailablePortsAtIndex:(NSUInteger)index { return [_availablePorts objectAtIndex:index]; } //插入多个串口 - (void)insertAvailablePorts:(NSArray *)array atIndexes:(NSIndexSet *)indexes { [_availablePorts insertObjects:array atIndexes:indexes]; } //插入单个串口 - (void)insertObject:(ORSSerialPort *)object inAvailablePortsAtIndex:(NSUInteger)index { [_availablePorts insertObject:object atIndex:index]; } //移除多个串口 - (void)removeAvailablePortsAtIndexes:(NSIndexSet *)indexes { [_availablePorts removeObjectsAtIndexes:indexes]; } //移除单个串口 - (void)removeObjectFromAvailablePortsAtIndex:(NSUInteger)index { [_availablePorts removeObjectAtIndex:index]; } @synthesize portsToReopenAfterSleep = _portsToReopenAfterSleep; @synthesize portPublishedNotificationIterator = _portPublishedNotificationIterator; - (void)setPortPublishedNotificationIterator:(io_iterator_t)iterator { if (iterator != _portPublishedNotificationIterator) { if (_portPublishedNotificationIterator) IOObjectRelease(_portPublishedNotificationIterator); _portPublishedNotificationIterator = iterator; IOObjectRetain(_portPublishedNotificationIterator); } } @synthesize portTerminatedNotificationIterator = _portTerminatedNotificationIterator; - (void)setPortTerminatedNotificationIterator:(io_iterator_t)iterator { if (iterator != _portTerminatedNotificationIterator) { if (_portTerminatedNotificationIterator) IOObjectRelease(_portTerminatedNotificationIterator); _portTerminatedNotificationIterator = iterator; IOObjectRetain(_portTerminatedNotificationIterator); } } @end void ORSSerialPortManagerPortsPublishedNotificationCallback(void *refCon, io_iterator_t iterator) { ORSSerialPortManager *manager = (__bridge ORSSerialPortManager *)refCon; if (![manager isKindOfClass:[ORSSerialPortManager class]]) { NSLog(@"Unexpected context object %@ in %s. Context object should be an instance of ORSSerialPortManager", manager, __PRETTY_FUNCTION__); return; } [manager serialPortsWerePublished:iterator]; } void ORSSerialPortManagerPortsTerminatedNotificationCallback(void *refCon, io_iterator_t iterator) { ORSSerialPortManager *manager = (__bridge ORSSerialPortManager *)refCon; if (![manager isKindOfClass:[ORSSerialPortManager class]]) { NSLog(@"Unexpected context object %@ in %s. Context object should be an instance of ORSSerialPortManager", manager, __PRETTY_FUNCTION__); return; } [manager serialPortsWereTerminated:iterator]; }
相关文章推荐
- 功能强大的串口工具:GhostyComm 4.0(万能通讯精灵)
- 用Windows API 编写串口通讯程序
- Ubuntu安装配置串口通讯工具minicom&&cutecom
- 基于Java编写串口通信工具
- linux环境下C语言实现非阻塞方式读取字符串数据的串口测试程序,即串口工具的编写
- 要编写一个程序,控制串口通讯,java里面有...
- Ubuntu安装配置串口通讯工具minicom&&cutecom
- QT 串口通讯软件编写
- 工控自动化通讯测试工具总结之--串口
- Mac下的串口工具
- 转:使用C#编写串口通讯的资料
- VC2005使用MSCOMM编写串口通讯程序
- 简单而强大的多线程串口编程工具CserialPort类(附VC基于MFC单文档协议通讯源程序及详细编程步骤)
- 用Qt编写一个串口通讯程序
- C# 新手开始编写串口测试工具
- VC2005使用MSCOMM编写串口通讯程序
- 用Windows API 编写串口通讯程序(二)
- 简单而强大的多线程串口编程工具CserialPort类(附VC基于MFC单文档协议通讯源程序及详细编程步骤)
- 不使用comm.jar的java串口通讯工具类,使用自己编写的dll类
- 8.S5PV210串口通讯实战 Makefile编写(二)