Code Monkey home page Code Monkey logo

orsserialport's Introduction

ORSSerialPort

ORSSerialPort is an easy-to-use Objective-C serial port library for macOS. It is useful for programmers writing Objective-C or Swift Mac apps that communicate with external devices through a serial port (most commonly RS-232). You can use ORSSerialPort to write apps that connect to Arduino projects, robots, data acquisition devices, ham radios, and all kinds of other devices. Using ORSSerialPort to open a port and send data can be as simple as this:

let serialPort = ORSSerialPort(path: "/dev/cu.KeySerial1")
serialPort.baudRate = 4800
serialPort.open()
serialPort.send(someData) // someData is an NSData object
serialPort.close() // Later, when you're done with the port

Or, in Objective-C:

ORSSerialPort *serialPort = [ORSSerialPort serialPortWithPath:@"/dev/cu.KeySerial1"];
serialPort.baudRate = @4800;
[serialPort open];
[serialPort sendData:someData]; // someData is an NSData object
[serialPort close]; // Later, when you're done with the port

ORSSerialPort is released under an MIT license, meaning you're free to use it in both closed and open source projects. However, even in a closed source project, you must include a publicly-accessible copy of ORSSerialPort's copyright notice, which you can find in the LICENSE file.

If you have any questions about, suggestions for, or contributions to ORSSerialPort, please contact me. I'd also love to hear about any cool projects you're using it in.

This readme provides an overview of the ORSSerialPort library and is meant to provide enough information to get up and running quickly. You can read complete technical documentation for ORSSerialPort on http://cocoadocs.org/docsets/ORSSerialPort/. The ORSSerialPort wiki also contains detailed documentation.

Most of the example code in this readme is in Swift. However, ORSSerialPort can also easily be used from Objective-C code. The Examples folder contains Swift and Objective-C versions of all four example projects. See the Example Projects section below for more information.

How to Use ORSSerialPort

There are a number of ways to add ORSSerialPort to your project. You can use the included framework project, Carthage, CocoaPods, or the Swift Package Manager. See the Guide to Installing ORSSerialPort for detailed instructions for each of these methods.

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 property (explained below). The other way is to get a new ORSSerialPort instance using the serial port's BSD device path:

let port = ORSSerialPort(path: "/dev/cu.KeySerial1")

Note that you must give ORSSerialPort.init(path:) 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:

port.baudRate = 9600
port.parity = .none
port.numberOfStopBits = 1
port.usesRTSCTSFlowControl = true

For more information, see the Getting Started Guide.

Sending Data

Send raw data by passing a Data object to the send(_:) method:

let dataToSend = "Hello".data(using: .utf8)
port.send(dataToSend)

Receiving Data

To receive data, you can implement the ORSSerialPortDelegate protocol's serialPort(_:, didReceive:) method, and set the ORSSerialPort instance's delegate property. As noted below, this method is always called on the main queue. An example implementation is included below:

func serialPort(_ serialPort: ORSSerialPort, didReceive data: Data) {
    let string = String(data: data, encoding: .utf8)
    print("Got \(string) from the serial port!")
}

ORSSerialPortDelegate

ORSSerialPort includes a delegate property, and a delegate protocol called ORSSerialPortDelegate. A port informs its delegate of events including receipt of data, port open/close events, removal from the system, and errors. For more information, see the Getting Started Guide, or read the documentation in ORSSerialPort.h.

ORSSerialPortManager

ORSSerialPortManager is a singleton class (one instance per application) that can be used to get a list of available serial ports. Use the manager's availablePorts property to get a list of ports:

let ports = ORSSerialPortManager.shared().availablePorts

ORSSerialPortManager's availablePorts can be observed with Key Value Observing to be notified when a USB to serial adapter is plugged in or removed. Additionally, it posts NSNotifications when these events occur. 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 receive data.

For more information about ORSSerialPortManager, see the Getting Started Guide, or read the documentation in ORSSerialPortManager.h.

ORSSerialPacketDescriptor

Incoming serial data is delivered to your application as it is received. A low level library like ORSSerialPort has no way of knowing anything about the structure and format of the data you're sending and receiving. For example, you may be expecting a complete packet of data, but receive callbacks for each byte. Normally, this requires you to maintain a buffer which you fill up with incoming data, only processing it when a complete packet has been received. In order to eliminate the need for manual management and buffering of incoming data, ORSSerialPort includes a packet parsing API. This is implemented by ORSSerialPacketDescriptor and associated methods on ORSSerialPort.

For more information about ORSSerialPort's packet parsing API, see the Packet Parsing API Guide, read the documentation in ORSSerialPacketDescriptor.h, and see the PacketParsingDemo example app.

ORSSerialRequest

Often, applications will want to send a command to a device, then wait to receive a specific response before continuing. To ease implementing this kind of scenario, ORSSerialPort includes a request/response API. This is implemented by ORSSerialRequest and associated methods on ORSSerialPort.

For example, a program that read the temperature from a connected device might do the following:

func readTemperature() {
    let command = "$TEMP?;".data(using: String.Encoding.ascii)!
    let responseDescriptor = ORSSerialPacketDescriptor(prefixString: "!TEMP", suffixString: ";", maximumPacketLength: 10, userInfo: nil)
    let request = ORSSerialRequest(dataToSend: command,
        userInfo: SerialBoardRequestType.readTemperature.rawValue,
        timeoutInterval: 0.5,
        responseDescriptor: responseDescriptor)
    serialPort?.send(request)
}

func serialPort(_ serialPort: ORSSerialPort, didReceiveResponse responseData: Data, to request: ORSSerialRequest) {
    temperature = temperatureFromResponsePacket(responseData)!
}

func serialPort(_ serialPort: ORSSerialPort, requestDidTimeout request: ORSSerialRequest) {
    print("Command timed out!")
}

For more information about ORSSerialPort's request/response API, see the Request/Response API Guide, read the documentation in ORSSerialRequest.h, and see the RequestResponseDemo example app.

Example Projects

Included with ORSSerialPort is a folder called Examples, containing Xcode projects for small programs demonstrating the use of ORSSerialPort. Each example is available in both Objective-C and Swift. The following example apps are included:

You can read more about these examples on the ORSSerialPort wiki.

Contributing

Contributions to ORSSerialPort are very welcome. However, contributors are encouraged to read the contribution guidelines before starting work on any contributions. Please also feel free to open a GitHub issue or email with questions about specific contributions.

GitHub License Badge Build Status Badge CocoaPods Badge

orsserialport's People

Contributors

armadsen avatar azplanlos avatar bryant1410 avatar domhawken avatar kevinvitale avatar kouky avatar nathanntg avatar razor-1 avatar robertmryan avatar shagru avatar stbraun avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

orsserialport's Issues

Add request/response API to ORSSerialPort

Let me preface this issue by saying that I might be wrong. I noticed this last night and I haven't had the chance to debug this properly (I can do more digging tonight).

I'm interacting with a device that might have responses that are relatively large (maybe 5k bytes?). Ideally, my delegate would receive the NSData for the full response but I'm getting only a chunk of it. The read buffer is currently 1024 bytes long but we could potentially read until there are no more bytes left to read, right?

Or is the idea that the delegate would accumulate bytes and put the response back together?

ORSSerialPort sent commands not accepted by device

I have been trying to get the ORSSerialPort to send commands to a MagTek RS232 check scanner using the ORSSerialPortCocoaDemo. So far, I have not gotten the MagTek to receive a single command through the demo.

I have noticed though, that I can run minicom and send and receive commands consistently. When I do this, I can also see some of the data showing up in the ORSSerialPortCocoaDemo window. So this leads me to believe that there is something going on with the sendData command that causes the MagTek to not accept the sent data.

What would be keeping the sent commands from ORSSerialPort from being accepted by the device - knowing that the device is connected and is getting valid commands sent to it?

Request timeout never fires in command line app

If ORSSerialRequest is used in a simple command line app (e.g. one based on the command line example), requests never timeout failing receipt of a valid response. This is because ORSSerialPort's pendingRequestTimeoutTimer never fires. I can't quite track down exactly what is causing this, but have a hunch that it's run loop related.

Verify that ORSSerialPort behaves reasonably when ports are removed from the system without ORSSerialPortManager

Verify that ORSSerialPort behaves reasonably when ports are removed from the system without ORSSerialPortManager. Currently, ORSSerialPortManager is responsible for calling -[ORSSerialPort cleanup] when a port is removed from the system. -cleanup in turn notifies the port's delegate giving them a chance to remove the port. This either needs to work when ORSSerialPortManager isn't present, ORSSerialPortManager needs to be required to use ORSSerialPort.

Add a way to packetize incoming data even in the absence of a pending ORSSerialRequest.

A common scenario for serial communications is a device that automatically sends packets of data periodically or in response to (real world) events, rather than as a response to some request from the computer. ORSSerialPort doesn't currently provide any help with parsing/packetizing this kind of incoming data, like it does for structured request/response scenarios.

It would be valuable to provide an API so that a user of ORSSerialPort could install a "packetizer", then be notified of incoming packets that have been validated. This could look quite similar to the existing response part of ORSSerialRequest support, including the use of a response evaluator block (could be called a "packet evaluator" block).

Some requirements for the API:

  • Should not interfere with the existing raw data receipt mechanism (-serialPort:didReceiveData:).
  • Should work simultaneously with the request response API.
  • Should allow for multiple packetizers to be used at once, for cases where a serial device sends packets in more than one format.
  • Should allow for removal of specific packetizers in order to cease notification for incoming packets of a given type.

I think this may warrant some refactoring of the existing request response API, so I think it should wait until the next major update (ie. 2.0).

Kernel panic when closing an FTDI port using the Apple driver, when another app has an open port

User report:

Seems to occur only when RUMlogNG is also running and has other serial ports open, but not the one I have open with ORSSerialPortDemo. Happens on the close() call. Doesn’t happen every time, maybe once in three or four calls to close().

The call to close() hangs, and the entire Mac crashes after a few seconds, This is on OSX 10.9.5 and I compiled the demo program unchanged with Xcode 6.2 using the 10.10 SDK.

When OSX does not crash, the close() call happens quickly, as you would want it to. But when RUMlogNG 1.2.5 is running with (I assume) the latest version of ORSSerialPort, it will crash OSX fairly often.

The request/response API should easily enable sending a request which does not provoke a response

Quick idea for doing this:

Make a nil responseEvaluator block the 'canonical' way of specifying that the request won't provoke a response. In this case, the data for the request should be sent without then making the request "pending", and the next request should be sent immediately.

The tricky part here is going to be that if the programmer doesn't care about the response to a request, but the request does in fact provoke one, that data will be considered part of the next queued request (if there is one). The only solution that comes to mind is to document that if a request is going to provoke a response, whether the user cares about it or not, the responseEvaluator block should not be nil.

An ORSSerialPort created with serialPortWithPath:tty.device actually opens the callout path.

Hi, please bear with me, I'm new with Arduino and hardware programming.

During the programming of my Arduino, I've always used the /dev/tty.usbmodemXXX for the serial monitor without issue. And it never works with /dev/cu, I never really thought about it because, well it works with tty, so I just use that. And here I am, trying to use ORSSerialPort to send and receive data with my Arduino;

Serial port usbmodem1441 encountered an error: Error Domain=NSPOSIXErrorDomain Code=16 "Resource busy" UserInfo=0x6080000763c0 {NSFilePath=/dev/cu.usbmodem1441, NSLocalizedDescription=Resource busy}

Why is the ORSSerialPort only wanting to work with cu port? The ORSSerialPortManager only listed the cu paths. Even when I hardcoded the connection (using the Cocoa demo);

_serialPort = [ORSSerialPort serialPortWithPath:@"/dev/tty.usbmodem1441"];
_serialPort.baudRate = @115200;
_serialPort.delegate = self;
[_serialPort open];

I'm getting the exact same error message saying /dev/cu.usbmodemXXX resource is busy.

Any idea how to fix this? I know the tty port is there, I can see it from Arduino.app

SerialPortWasRemovedFromSystem not called

The serialPortWasRemovedFromSystem delegate method is not called on my computer. I tested it in both my own application and the cocoa example, and this method is not called when I physically remove my Arduino from my computer. However, the didEncounterError delegate method is called infinitely (I assume because the reference to the ORSSerialPort instance is not released). Any ideas why this might happen?

Break up documentation

At this point, the ORSSerialPort README is starting to get big and unwieldy. I think it seems like a good idea to remove some of the less essential/more detailed information and put it in other files, or perhaps start a GitHub wiki with additional information. Care should be taken so that documentation still works well with CocoaDocs.

Switch to dispatch source for reading data from port

ORSSerialPort has always used a "read poller" block that runs an infinite while loop on a global queue. (see -[ORSSerialPort open]) to watch for and read incoming bytes. This was only done because my original attempts to use a dispatch source on the port's file descriptor didn't work (see e.g. here and here). It's not exactly ideal, and has always bugged me a little.

I've now briefly tested the dispatch source approach on 10.10 and it seems to work fine. The change results in the deletion of several lines of code, including the occasionally problematic select() call.

In order to make this change in the framework, I first need to know in which OS X version the fix was implemented. It can't be made until 10.6 support is dropped, at least not without conditionally choosing an approach at runtime.

Stray line in ORSSerialPort.m

At the top of the method: - (BOOL)sendData:(NSData *)data there's an extra line of text 'B9600'.

Not a big deal, but the code fails to compile when checked out.

-[ORSSErialPort close] calls dispatch_retain(nil) --> crash

1] -[ORSSErialPort close] calls self.pinPollTimer = nil;

2] -[ORSSerialPort setPinPollTimer:] calls ORS_GCD_RETAIN(timer) with timer = nil.

3] ORS_GCD_RETAIN(x) is defined to dispatch_retain(x)

4] The documentation for dispatch_retain(x) says that x cannot be nil.

In fact if while running ORSSerialPortCocoaDemo I unplug my USB-serial adapter, I crash inside ORS_GCD_RETAIN(x)

Compile error when building in a project with OS_OBJECT_USE_OBJC=0

This came up in a project using CocoaPods with a deployment target of 10.9. CocoaPods automatically defines OS_OBJECT_USE_OBJC=0 for source files from a pod whose deployment target is less than 10.8, when used in a project whose deployment target is 10.8 or greater.

ORSSerialPort is checking OS_OBJECT_HAVE_OBJC_SUPPORT to determine if GCD objects are Objective-C objects, which causes the following compiler error if OS_OBJECT_USE_OBJC=0 is manually defined:

ORSSerialPort.m:89:1: Property with 'retain (or strong)' attribute must be of object type

The solution is to check OS_OBJECT_USE_OBJC, which should always be correct, instead.

not compiling on OS X 10.7

not compiling on OS X 10.7 due to NS_DESIGNATED_INITIALIZER in ORSSerialPort.h as discussed in pullrequest #46 comments.

Serial Data in a Swift Playground

This code is great.

ORSSerial works in my OS X app very well. Any chance you'd write a port of ORSSerialPort's functionality into a Swift Playground? For example, a "Hello World playground displaying a realtime serial data-stream". That would be awesome.

I'm trying to visually graph an incoming sensor value from Arduino's Analog Input Pin, in realtime.
I'd be happy to fill-out the rest of the code (from a Hello-world Playground), to "live graph" in-coming serial data in a playground.

Simply having an issue wrapping my head around getting live serial data into a Swift Playground...?

Custom Baudrate

The library works fine communicating with an Arduino on default baud rates (9600 etc.). My problem is, that I want to use a custom baud rate (2.000.000) to get more data across the USB. But as it seems, this does not work at the moment.

Is there a way to get this high baud rate to work?

sleep/wake handling doesn't actually work

ORSSerialPortManager attempts to register for notification of NSWorkspaceWillSleepNotification and NSWorkspaceDidWakeNotification in order to close ports and re-open them when the machine sleeps and wakes. However, these are registered on the default notification center and thus are never fired.

Per https://developer.apple.com/library/mac/qa/qa1340/_index.html they need to be registered on the NSWorkspace notification center. Something like this:

    NSNotificationCenter *nw = [[NSWorkspace sharedWorkspace] notificationCenter];
    [nw addObserver:self selector:@selector(systemWillSleep:) name:NSWorkspaceWillSleepNotification object:NULL];
    [nw addObserver:self selector:@selector(systemDidWake:) name:NSWorkspaceDidWakeNotification object:NULL];

Working in sandbox

I have written a peace of code to send and receive data from a serial device.
It works perfectly and the library is very easy to use.

Unfortunately, when I sendbox my app the device port can't be opened anymore and I get this error message:

Error Domain=NSPOSIXErrorDomain Code=1 "Operation not permitted" UserInfo=0x1018b8070 {NSFilePath=/dev/cu.usbmodem1d11, NSLocalizedDescription=Operation not permitted}

I have enabled the option "Allow USB Access" (com.apple.security.device.usb) but this didn't solve the problem.

Can this library be used in the sandbox ?

Some ORSSerialPortDelegate methods are dispatched asynchronously to main queue; others called synchronously on the current queue

The serialPortWasOpened method and some of the didEncounterError methods resulting from notifyDelegateOfPosixError are dispatched asynchronously on the main queue whereas the other ORSSerialPortDelegate methods are just called synchronously on the current queue. As it is, this can result in somewhat curious sequence of these delegate method being called back to the main app.

I'd suggest you either just call them all directly, or dispatch them all asynchronously to the main queue, but right now it feels a little haphazard.

I'm happy to do a pull request, but let me know if you'd prefer to go asynchronous on the main queue or synchronous on the current queue.

Add a (possibly optionally) asynchronous variant of -sendData:.

My work on #20 led me to the conclusion that it would be helpful to have a -sendData:waitUntilDone: style method. For backwards compatibility, -sendData: will remain synchronous, but this new method could be used to send data asynchronously without waiting for the send to complete.

It quite possibly also makes sense to add a corresponding -sendRequest:waitUntilSendIsDone: method.

STM32 Virtual USB Com Port problem on Maverick OSX

Hi,

We are successfully using ORSSerialPort on OSX to connect and read data from serial over usb devices. It is a very helpful library. However we cannot get it to work with a device which uses the STM32 chipset. It is listed as a modem device and when we connect to it we get no data. Anyone else come across this and know what the trick is to connect to it.

I think I've identified a race condition

I've been having trouble when disconnecting an FTDI based USB device. Occasionally, unplugging the device messes up my USB bus and 'disconnects' Xcode's debugger from my code (I can neither stop or pause my code), making this a particularly nasty problem to debug. Also, I usually have audio from iTunes playing through a USB interface and that stops as well. The only thing that clears this up is restarting the machine, which I sometimes have to do via Terminal.

I've tracked this down to what I believe is a race condition.

When a device is unplugged, eventually -[ORSSerialPort close] is called on the main thread. Inside -[ORSSerialPort close] are calls to tcsetattr().

Meanwhile in another thread, a while loop executing in the read poller calls select() on each iteration.

Using printf() debugging, I have come to believe that if select() is called while a call to tcsetattr() is in flight (or vice-versa), some sort of deadlock occurs.

My fix for this is to wrap the critical parts of -[ORSSerialPort close] in a @synchronized block

- (BOOL)close;
{
    BOOL result;

    if (!self.isOpen)
    {
        result = YES;
    }
    else
    {
        @synchronized(self) {

            [self.writeBuffer replaceBytesInRange:NSMakeRange(0, [self.writeBuffer length]) withBytes:NULL length:0];

            // 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))
            {
                result = NO;
                self.fileDescriptor = localFD;
                LOG_SERIAL_PORT_ERROR(@"Error closing serial port with file descriptor %i:%i", self.fileDescriptor, errno);
                [self notifyDelegateOfPosixError];
            }
            else
            {
                result = YES;
                if ([(id)self.delegate respondsToSelector:@selector(serialPortWasClosed:)])
                {
                    [self.delegate serialPortWasClosed:self];
                }
            }
        }
    }

    return result;
}

Similarly, I wrap the read poller's call to select() in a @synchronized block:

// Start a read poller in the background
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    while (self.isOpen) 
    {
        ...
        @synchronized(self){
            result = select(localPortFD+1, &localReadFDSet, NULL, NULL, &timeout);
        }
        ...
    }
    ...
});

I've not had a USB lock up when disconnecting my device since making this change.

Please add semantic version tags.

I’ve added ORSSerialPort to the CocoaPods package manager repo.

CocoaPods is a tool for managing dependencies for OS X and iOS Xcode projects and provides a central repository for iOS/OS X libraries. This makes adding libraries to a project and updating them extremely easy and it will help users to resolve dependencies of the libraries they use.

However, ORSSerialPort doesn't have any version tags. I’ve added an earlier commit as version 0.0.1, but a version tag will make dependency resolution much easier.

Semantic version tags (instead of plain commit hashes/revisions) allow for resolution of cross-dependencies.

In case you didn’t know this yet; you can tag the current HEAD as, for instance, version 1.0.0, like so:

$ git tag -a 1.0.0 -m "Tag release 1.0.0"
$ git push --tags

sendData should keep sending until all data is sent

Right now, the sendData method appears to write data to the fileDescriptor, but if not all of the data was written, it doesn't bother to try to keep sending. It strikes me that rather than:

- (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;
}

You might want something like:

- (BOOL)sendData:(NSData *)data;
{
    if (!self.isOpen) return NO;

    [self.writeBuffer appendData:data];

    while ([self.writeBuffer length] > 0)
    {
        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;
        }
        else if (numBytesWritten > 0)
        {
            [self.writeBuffer replaceBytesInRange:NSMakeRange(0, numBytesWritten) withBytes:NULL length:0];
        }
    }

    return YES;
}

You are never guaranteed that write will write all of the data requested. So you really should loop through it.

Technically, if you want to be precise about it, you should avoid using the bytes method at all, because if the NSData was not contiguous, this ends up copying the full NSData to a contiguous block, which is inefficient.

You really should be using enumerateByteRangesUsingBlock to enumerate the various blocks of the NSData, and for each of them, use a loop similar to the one above, e.g.:

- (BOOL)sendData:(NSData *)data;
{
    BOOL __block success = YES;

    if (!self.isOpen) return NO;

    [self.writeBuffer appendData:data];

    long __block totalNumBytesWritten = 0l;

    [self.writeBuffer enumerateByteRangesUsingBlock:^(const void *bytes, NSRange byteRange, BOOL *stop) {
        const void *currentBytePointer = bytes;
        long numBytesRemaining = byteRange.length;
        while (numBytesRemaining > 0l)
        {
            long numBytesWritten = write(self.fileDescriptor, currentBytePointer, numBytesRemaining);
            if (numBytesWritten < 0l)
            {
                LOG_SERIAL_PORT_ERROR(@"Error writing to serial port:%d", errno);
                [self notifyDelegateOfPosixError];
                success = NO;
                *stop = YES;
            }
            currentBytePointer += numBytesWritten;
            totalNumBytesWritten += numBytesWritten;
            numBytesRemaining -= numBytesWritten;
        }
    }];

    [self.writeBuffer replaceBytesInRange:NSMakeRange(0, totalNumBytesWritten) withBytes:NULL length:0];

    return success;
}

I haven't tested the above code, but hopefully it illustrates the idea. But the take-home message is (a) don't assume that write will write all of the data in a single call; and (b) don't assume that the NSData was held in a single contiguous block of memory. If you look at Apple's various samples of using the write function, they always do something very similar to the above.

Make unsafe_unretained properties weak if deployment target is 10.8 or higher.

ARC weak references are not available on 10.6, and aren’t really practical in a “public” API like this on 10.7 due to several common classes (e.g. NSViewController) not supporting them. I’m not ready to drop 10.6 or 10.7 support yet, so unsafe_unretained needs to be used in favor of weak. That said, I think it probably makes sense to make the delegate weak if the deployment target is 10.8 or higher.

Request for timeout property

It would be nice to have a timeout public property so that the user can set a timeout on the serialport for read operations, so if there's no read from serial port after a certain user defined amount of time, delegate calls/notifications are triggered.
Does this feature request makes sense to you?
Thanks!

Make ORSSerialPort useable in Foundation-only command line programs

Currently, ORSSerialPort uses NSApplicationWillTerminateNotification and NSWorkspaceWill/DidSleepNotification, which are defined in AppKit. This prevents using ORSSerialPort in a Foundation-only command line tool. Linking with Cocoa.framework fixes this, but the notifications are of course never fired (no NSApplication), and it would be desirable to remove the dependence on Cocoa.framework anyway.

respondsToSelector: causes a bad access error

ORSSerialPort cleanupAfterSystemRemoval uses respondsToSelector: to determine whether or not to send serialPortWasRemovedFromSystem: to the delegate. However, respondsToSelector: needs a reference to AppKit, which ORSSerialPort lacks. One solution is to import AppKit. On the other hand, the respondsToSelector: call is not really necessary since the delegate is specified as confirming to the ORSSerialPortDelegate protocol in which serialPortWasRemovedFromSystem: is a required method.
So, a simple check to see if the delegate has been should be sufficient, as in:

- (void)cleanupAfterSystemRemoval
{
    if (_delegate)
    {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self.delegate serialPortWasRemovedFromSystem:self];
            [self close];
        });
    }
    else
    {
        [self close];
    }
}

Expose IOKit device identifier (io_object_t) in ORSSerialPort's public interface

In certain cases (e.g. as described here), it would be useful for ORSSerialPort to expose an IOKit device descriptor. For one, this would allow users of ORSSerialPort to use IOKit API to discover more information about a port on the system than ORSSerialPort's interface exposes.

I'm not yet sure how widely useful this would be, nor have I thought through possible problems/pitfalls involved with doing this.

Update to use Objective-C generics

This provides some advantages when using ORSSerialPort from Objective-C, but is particularly good for Swift integration. It should wait for release in master until Xcode 7.0 is no longer in beta, unless the feature can be conditionally compiled as is being done with nullability annotations.

AT Commands

Hi
I'm trying to send "AT Commands" to a mobile phone, to work with some basic functions like SMS, Call, etc.
The interface is already configured on my Mac, as a Blackberry modem.

I haven't changed your project at all.
The new "Blackberry" interface is shown in your drop box, and I'm able to open it.
The delegate method "serialPortWasOpened" is called.
I just type a string like "at" in the input textField, and I send it.
The delegate method "didReceiveData" never gets called.

Any idea?

Thanks for your hard work

serialPortWasRemovedFromSystem issue

serialPortWasRemovedFromSystem not working in my app. I can open the serial port, I can send data. When data received the didReceiveData called perfectly. serialPortWasOpened called perfectly. But when I tried to unplug my serial USB adapter the serialPortWasRemovedFromSystem not called and the cpu usage go up to near 100%. For test I tried to close the serial port, and test the serialPortWasClosed and I get an error message with highlited the following line from the ORSSerialPort.m :if ([(id)self.delegate respondsToSelector:@selector(serialPortWasClosed:)]) Thread 1:EXC_BAD_ACCES (code=EXC_I386_GPFLT)

I think the problem is in my code, because everything works fine with the cocoa sample. Do you have any idea what is the problem in my app?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.