:: Arduino + Cocoa/Objective-C ::

Three methods of interfacing with Objective C are presented here.


Methods

  • Popen() - The least flexible way to interface with an arduino, although if you are only sending data it is also the easiest.
  • Just use POSIX C - Objective C is still just C. If you don't need high baudrates, then this is an excellent option.
  • IOKit/ioctl - The "real" way. The advantages to this method are that you can list available serial-ports easily, and that you can use very high baudrates ( 2000000 baud for a 16Mhz Arduino )

Code Download


Popen()

The easiest and most straight forward way to communicate with your Arduino is to use the C-style command popen() to call a few bash commands (the commands that you use in Terminal or Command Prompt on a PC).

A simple code snippet that would send the words "hello world" to your Arduino is this:

    popen("echo hello world > /dev/tty.usbserial-A6006kDo", "r");


It is important that the /dev/tty.usbserial-A6006kDo part of this be changed to whatever serial port your Arduino is connected to which can be found by looking at which serial port is checked off in the Arduino IDE under Tools>Serial Port.


If you're wondering what exactly this is doing, basically it takes the output from "echo hello world" which is the text "hello world" and writes it into the file "/dev/tty.usbserial-A6006kDo". The > operator overwrites any existing text in the file its writing to which is okay because any text in this file is deleted once it is sent. By the way, the "r" in popen() denotes that you want to run the commands.

If you're a seasoned Objective-C programmer, you might have noticed that there is no @ operator in front of the quotes. This is because the @ operator denotes that the stuff in the quotes is a string object. Because this is a C-Style command, there are no objects and therefore the @ operator is not needed. Unfortunately the @ operator makes it a different kind of string. This means that you cannot pass an NSString to popen. Before you pass any strings to it they must be converted to C-Style strings. This is how you would do that:

    NSString *toSend = 
        [NSString stringWithFormat:@"echo 3 > %@", 
            [SERIALPORTFIELD stringValue]];
    char *toCSend = 
        [toSend UTF8String];  //converting part
    popen(toCSend, "r");


----

Just use C

Objective C is a strict superset of C, so the most basic way to interface is just use POSIX C. The main downside here is that you will be limited to the baud rates that are pre-defined for POSIX serial ports.


IOKit/ioctl

This method is very similar to POSIX C, but allows you to set more options(i.e. any baud rate and read latency) and list available serial ports. Apple's serial example shows the use of ioctl() to set serial port options and IOKit to list serial ports. The explanation and code below are primarily based off Apple's example.

To open the serial port:

    // open the serial like POSIX C
    serialFileDescriptor = open(
        "/dev/tty.usbserial-A6008cD3",
        O_RDWR |
        O_NOCTTY |
        O_NONBLOCK );

    // block non-root users from using this port
    ioctl(serialFileDescriptor, TIOCEXCL);

    // clear the O_NONBLOCK flag, so that read() will
    //   block and wait for data.
    fcntl(serialFileDescriptor, F_SETFL, 0);

    // grab the options for the serial port
    tcgetattr(serialFileDescriptor, &options);

    // setting raw-mode allows the use of tcsetattr() and ioctl()
    cfmakeraw(&options);

    // specify any arbitrary baud rate
    ioctl(serialFileDescriptor, IOSSIOSPEED, &baudRate);

    // start a thread to read the data coming in
    [self
        performSelectorInBackground:
            @selector(incomingTextUpdateThread:) 
        withObject:
            [NSThread currentThread]];


To read from the serial port:

    // This selector will be called as another thread
    - (void)incomingTextUpdateThread: (NSThread *) parentThread {
        char byte_buffer[100]; // buffer for holding incoming data
        int numBytes=1; // number of bytes read during read

        // create a pool so we can use regular Cocoa stuff
        //   child threads can't re-use the parent's autorelease pool
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

        // this will loop until the serial port closes
        while(numBytes>0) {
            // read() blocks until data is read or the port is closed
            numBytes = read(serialFileDescriptor, byte_buffer, 100);

            // you would want to do something useful here
            NSLog([NSString stringWithCString:byte_buffer length:numBytes]);
        }

        // give back the pool
        [pool release];
    }


To write to the serial port:

    uint8_t val = 'A';
    write(serialFileDescriptor, &val, 1);


To list availble serial ports:

    io_object_t serialPort;
    io_iterator_t serialPortIterator;

    // ask for all the serial ports
    IOServiceGetMatchingServices(
        kIOMasterPortDefault, 
        IOServiceMatching(kIOSerialBSDServiceValue), 
        &serialPortIterator);

    // loop through all the serial ports
    while (serialPort = IOIteratorNext(serialPortIterator)) {
        // you want to do something useful here
        NSLog(
            (NSString*)IORegistryEntryCreateCFProperty(
                serialPort, CFSTR(kIOCalloutDeviceKey),     
                kCFAllocatorDefault, 0));
        IOObjectRelease(serialPort);
    }

    IOObjectRelease(serialPortIterator);


Try looking through the Example XCode Project, the code presented on this website is simplified to make it more readable(at least that was the intention). You may also want to look at Apple's serial example.


Code Download

Share