Objective-C: Get Default Gateway Information - objective-c

How can I get my default Gateway IP and MAC Address in Objective-C?
Right now, I was able to make a function to get the IP that uses arpa/inet.h it works fine, but I've no idea how to get the default gateway IP and MAC Address.
NSString* GetMyIP(NSString* inf) {
NSString *address = #"error";
struct ifaddrs *interfaces = NULL;
struct ifaddrs *temp_addr = NULL;
int success = 0;
// retrieve the current interfaces - returns 0 on success
success = getifaddrs(&interfaces);
if (success == 0)
{
// Loop through linked list of interfaces
temp_addr = interfaces;
while(temp_addr != NULL)
{
if(temp_addr->ifa_addr->sa_family == AF_INET)
{
// Check if interface!
if([[NSString stringWithUTF8String:temp_addr->ifa_name] isEqualToString:inf])
{
// Get NSString from C String
address = [NSString stringWithUTF8String:inet_ntoa(((struct sockaddr_in *)temp_addr->ifa_addr)->sin_addr)];
}
}
temp_addr = temp_addr->ifa_next;
}
}
freeifaddrs(interfaces);
return address;
}
I used arp and netstat for a while but they don't always work as expected and as you might understand are risky solutions...
Is there another more "Apple-way" to get this kind of info? Without using arpa/inet.h? Examples?
Thank you.

This answer explains how to get the MAC address for an interface on OSX.
The default gateway is registered in the routing table - not directly associated with a network interface. Note that the default gateway (i.e. the gateway for 0.0.0.0) is probably not the only one you need to care about, there might be routers set for more constrained address ranges. (also, there may be multiple gateways, default or otherwise, with different priorities)
The routing table is documented in the route(4) manpage - that should hopefully give you enough information for retrieving the routing table in your app.

Related

Sending Cloud to Device Messages using IoT DevKit and Azure IoT Hub - Device Code

I need to send a message from the IoT Hub to the DevKit Device. Based on https://learn.microsoft.com/en-au/azure/iot-hub/iot-hub-devguide-c2d-guidance I want to send a Direct Method as I need to manage a bank of relays.
I have an IoT DevKit and have successfully configured it and are able to send device to IoT Hub messages but am looking for a sample to do this the other way. I currently can only find samples that set the device twin properties, not send direct methods. On the server-side I believe I would use Microsoft.Azure.Devices.ServiceClient to SendAsync a message to the device (happy to be corrected is incorrect).
On the device I think (???) I need to use SetDeviceMethodCallback but I have no idea how to initialise it and receive messages. Ideally, the sample would also include how to send an acknowledgement that the message was received and actioned.
Any help would be appreciated even if just to let me know I am on the right track here. Thanks in advance.
Here is some sample that I used before with the IoT DevKit (=Mxchip) on the device side:
static int DeviceMethodCallback(const char *methodName, const unsigned char *payload, int size, unsigned char **response, int *response_size)
{
LogInfo("Try to invoke method %s", methodName);
const char *responseMessage = "\"Successfully invoke device method\"";
int result = 200;
if (strcmp(methodName, "start") == 0)
{
DoSomething();
}
else if (strcmp(methodName, "stop") == 0)
{
DoSomethingElse();
}
else
{
LogInfo("No method %s found", methodName);
responseMessage = "\"No method found\"";
result = 404;
}
*response_size = strlen(responseMessage) + 1;
*response = (unsigned char *)strdup(responseMessage);
return result;
}
DevKitMQTTClient_SetDeviceMethodCallback(DeviceMethodCallback);
On the services side (where you do the method invocation) here is some C# example
ServiceClient _iothubServiceClient = ServiceClient.CreateFromConnectionString(config["iothubowner_cs"]);
var result = await _iothubServiceClient.InvokeDeviceMethodAsync(deviceid, "start");
var status = result.Status;

Reading data from an HID device from userspace in OSX [duplicate]

I am attempting to communicate with a rather specific USB device and developing both Windows and Mac code to do so.
The device is a USB device with a HID interface (class 3) with two endpoints, an interrupt input and an interrupt output. The nature of the device is such that data is sent out from the device on the input endpoint only when data is requested from the host: the host sends it data which the device responds to on its input interrupt endpoint. Getting data to the device (a write) is much more simple...
The code for Windows is rather straight-forward: I get a handle to the device and then call either ReadFile or WriteFile. Apparently much of the underlying asynchronous behavior is abstracted out. It appears to work fine.
On Mac, however, it is a bit stickier. I have tried a number of things, none which have been fully successful, but here are the two things which seemed most promising...
1.) Attempt to get access to the device (as USB) via IOUSBInterfaceInterface, iterate through the endpoints to determine the input and output endpoints, and (hopefully) use ReadPipe and WritePipe to communicate. Unfortunately I am unable to open the interface once I have it, with the return value (kIOReturnExclusiveAccess) noting that something already has the device open exclusively. I have tried using IOUSBinterfaceInterface183, so that I could call USBInterfaceOpenSeize, but that results in the same return error value.
--- update 7/30/2010 ---
Apparently, the Apple IOUSBHIDDriver matches early to the device and this is what likely is preventing opening the IOUSBInterfaceInterface. From some digging about it seems that the common way to prevent the IOUSBHIDDriver from matching is to write a code-less kext (kernel extension) with a higher probe score. This would match early, preventing the IOUSBHIDDriver from opening the device, and should, in theory, permit me to open the interface and to write and read to endpoints directly. This is OK, but I would much prefer not having to install something additional on the user machine. If anyone knows of a solid alternative I would be thankful for the information.
2.) Open the device as an IOHIDDeviceInterface122 (or later). To read, I set up an async port, event source and callback method to be called when data is ready - when data is sent from the device on the input interrupt endpoint. However, to write the data — that the device needs — to initialize a response I can't find a way. I'm stumped. setReport typically writes to the control endpoint, plus I need a write that does not expect any direct response, no blocking.
I have looked around online and have tried many things, but none of them is giving me success. Any advice? I can not use much of the Apple HIDManager code since much of that is 10.5+ and my application must work on 10.4 as well.
I have now a working Mac driver to a USB device that requires communication through interrupt endpoints. Here is how I did it:
Ultimately the method that worked well for me was option 1 (noted above). As noted, I was having issues opening the COM-style IOUSBInterfaceInterface to the device. It became clear over time that this was due to the HIDManager capturing the device. I was unable to wrest control of the device from the HIDManager once it was captured (not even the USBInterfaceOpenSeize call or the USBDeviceOpenSeize calls would work).
To take control of the device I needed to grab it before the HIDManager. The solution to this was to write a codeless kext (kernel extension). A kext is essentially a bundle that sits in System/Library/Extensions that contains (usually) a plist (property list) and (occasionally) a kernel-level driver, among other items. In my case I wanted only the plist, which would give the instructions to the kernel on what devices it matches. If the data gives a higher probe score than the HIDManager then I could essentially capture the device and use a user-space driver to communicate with it.
The kext plist written, with some project-specific details modified, is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>OSBundleLibraries</key>
<dict>
<key>com.apple.iokit.IOUSBFamily</key>
<string>1.8</string>
<key>com.apple.kernel.libkern</key>
<string>6.0</string>
</dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleGetInfoString</key>
<string>Demi USB Device</string>
<key>CFBundleIdentifier</key>
<string>com.demiart.mydevice</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>Demi USB Device</string>
<key>CFBundlePackageType</key>
<string>KEXT</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>IOKitPersonalities</key>
<dict>
<key>Device Driver</key>
<dict>
<key>CFBundleIdentifier</key>
<string>com.apple.kernel.iokit</string>
<key>IOClass</key>
<string>IOService</string>
<key>IOProviderClass</key>
<string>IOUSBInterface</string>
<key>idProduct</key>
<integer>12345</integer>
<key>idVendor</key>
<integer>67890</integer>
<key>bConfigurationValue</key>
<integer>1</integer>
<key>bInterfaceNumber</key>
<integer>0</integer>
</dict>
</dict>
<key>OSBundleRequired</key>
<string>Local-Root</string>
</dict>
</plist>
The idVendor and idProduct values give the kext specificity and increase its probe score sufficiently.
In order to use the kext, the following things need to be done (which my installer will do for clients):
Change the owner to root:wheel (sudo chown root:wheel DemiUSBDevice.kext)
Copy the kext to Extensions (sudo cp DemiUSBDevice.kext /System/Library/Extensions)
Call the kextload utility to load the kext for immediate use without restart (sudo kextload -vt /System/Library/Extensions/DemiUSBDevice.kext)
Touch the Extensions folder so that the next restart will force a cache rebuild (sudo touch /System/Library/Extensions)
At this point the system should use the kext to keep the HIDManager from capturing my device. Now, what to do with it? How to write to and read from it?
Following are some simplified snippets of my code, minus any error handling, that illustrate the solution. Before being able to do anything with the device, the application needs to know when the device attaches (and detaches). Note that this is merely for purposes of illustration — some of the variables are class-level, some are global, etc. Here is the initialization code that sets the attach/detach events up:
#include <IOKit/IOKitLib.h>
#include <IOKit/IOCFPlugIn.h>
#include <IOKit/usb/IOUSBLib.h>
#include <mach/mach.h>
#define DEMI_VENDOR_ID 12345
#define DEMI_PRODUCT_ID 67890
void DemiUSBDriver::initialize(void)
{
IOReturn result;
Int32 vendor_id = DEMI_VENDOR_ID;
Int32 product_id = DEMI_PRODUCT_ID;
mach_port_t master_port;
CFMutableDictionaryRef matching_dict;
IONotificationPortRef notify_port;
CFRunLoopSourceRef run_loop_source;
//create a master port
result = IOMasterPort(bootstrap_port, &master_port);
//set up a matching dictionary for the device
matching_dict = IOServiceMatching(kIOUSBDeviceClassName);
//add matching parameters
CFDictionarySetValue(matching_dict, CFSTR(kUSBVendorID),
CFNumberCreate(kCFAllocatorDefault, kCFNumberInt32Type, &vendor_id));
CFDictionarySetValue(matching_dict, CFSTR(kUSBProductID),
CFNumberCreate(kCFAllocatorDefault, kCFNumberInt32Type, &product_id));
//create the notification port and event source
notify_port = IONotificationPortCreate(master_port);
run_loop_source = IONotificationPortGetRunLoopSource(notify_port);
CFRunLoopAddSource(CFRunLoopGetCurrent(), run_loop_source,
kCFRunLoopDefaultMode);
//add an additional reference for a secondary event
// - each consumes a reference...
matching_dict = (CFMutableDictionaryRef)CFRetain(matching_dict);
//add a notification callback for detach event
//NOTE: removed_iter is a io_iterator_t, declared elsewhere
result = IOServiceAddMatchingNotification(notify_port,
kIOTerminatedNotification, matching_dict, device_detach_callback,
NULL, &removed_iter);
//call the callback to 'arm' the notification
device_detach_callback(NULL, removed_iter);
//add a notification callback for attach event
//NOTE: added_iter is a io_iterator_t, declared elsewhere
result = IOServiceAddMatchingNotification(notify_port,
kIOFirstMatchNotification, matching_dict, device_attach_callback,
NULL, &g_added_iter);
if (result)
{
throw Exception("Unable to add attach notification callback.");
}
//call the callback to 'arm' the notification
device_attach_callback(NULL, added_iter);
//'pump' the run loop to handle any previously added devices
service();
}
There are two methods that are used as callbacks in this initialization code: device_detach_callback and device_attach_callback (both declared at static methods). device_detach_callback is straightforward:
//implementation
void DemiUSBDevice::device_detach_callback(void* context, io_iterator_t iterator)
{
IOReturn result;
io_service_t obj;
while ((obj = IOIteratorNext(iterator)))
{
//close all open resources associated with this service/device...
//release the service
result = IOObjectRelease(obj);
}
}
device_attach_callback is where most of the magic happens. In my code I have this broken into multiple methods, but here I'll present it as a big monolithic method...:
void DemiUSBDevice::device_attach_callback(void * context,
io_iterator_t iterator)
{
IOReturn result;
io_service_t usb_service;
IOCFPlugInInterface** plugin;
HRESULT hres;
SInt32 score;
UInt16 vendor;
UInt16 product;
IOUSBFindInterfaceRequest request;
io_iterator_t intf_iterator;
io_service_t usb_interface;
UInt8 interface_endpoint_count = 0;
UInt8 pipe_ref = 0xff;
UInt8 direction;
UInt8 number;
UInt8 transfer_type;
UInt16 max_packet_size;
UInt8 interval;
CFRunLoopSourceRef m_event_source;
CFRunLoopSourceRef compl_event_source;
IOUSBDeviceInterface245** dev = NULL;
IOUSBInterfaceInterface245** intf = NULL;
while ((usb_service = IOIteratorNext(iterator)))
{
//create the intermediate plugin
result = IOCreatePlugInInterfaceForService(usb_service,
kIOUSBDeviceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin,
&score);
//get the device interface
hres = (*plugin)->QueryInterface(plugin,
CFUUIDGetUUIDBytes(kIOUSBDeviceInterfaceID245), (void**)&dev);
//release the plugin - no further need for it
IODestroyPlugInInterface(plugin);
//double check ids for correctness
result = (*dev)->GetDeviceVendor(dev, &vendor);
result = (*dev)->GetDeviceProduct(dev, &product);
if ((vendor != DEMI_VENDOR_ID) || (product != DEMI_PRODUCT_ID))
{
continue;
}
//set up interface find request
request.bInterfaceClass = kIOUSBFindInterfaceDontCare;
request.bInterfaceSubClass = kIOUSBFindInterfaceDontCare;
request.bInterfaceProtocol = kIOUSBFindInterfaceDontCare;
request.bAlternateSetting = kIOUSBFindInterfaceDontCare;
result = (*dev)->CreateInterfaceIterator(dev, &request, &intf_iterator);
while ((usb_interface = IOIteratorNext(intf_iterator)))
{
//create intermediate plugin
result = IOCreatePlugInInterfaceForService(usb_interface,
kIOUSBInterfaceUserClientTypeID, kIOCFPlugInInterfaceID, &plugin,
&score);
//release the usb interface - not needed
result = IOObjectRelease(usb_interface);
//get the general interface interface
hres = (*plugin)->QueryInterface(plugin, CFUUIDGetUUIDBytes(
kIOUSBInterfaceInterfaceID245), (void**)&intf);
//release the plugin interface
IODestroyPlugInInterface(plugin);
//attempt to open the interface
result = (*intf)->USBInterfaceOpen(intf);
//check that the interrupt endpoints are available on this interface
//calling 0xff invalid...
m_input_pipe = 0xff; //UInt8, pipe from device to Mac
m_output_pipe = 0xff; //UInt8, pipe from Mac to device
result = (*intf)->GetNumEndpoints(intf, &interface_endpoint_count);
if (!result)
{
//check endpoints for direction, type, etc.
//note that pipe_ref == 0 is the control endpoint (we don't want it)
for (pipe_ref = 1; pipe_ref <= interface_endpoint_count; pipe_ref++)
{
result = (*intf)->GetPipeProperties(intf, pipe_ref, &direction,
&number, &transfer_type, &max_packet_size, &interval);
if (result)
{
break;
}
if (transfer_type == kUSBInterrupt)
{
if (direction == kUSBIn)
{
m_input_pipe = pipe_ref;
}
else if (direction == kUSBOut)
{
m_output_pipe = pipe_ref;
}
}
}
}
//set up async completion notifications
result = (*m_intf)->CreateInterfaceAsyncEventSource(m_intf,
&compl_event_source);
CFRunLoopAddSource(CFRunLoopGetCurrent(), compl_event_source,
kCFRunLoopDefaultMode);
break;
}
break;
}
}
At this point we should have the numbers of the interrupt endpoints and an open IOUSBInterfaceInterface to the device. An asynchronous writing of data can be done by calling something like:
result = (intf)->WritePipeAsync(intf, m_output_pipe,
data, OUTPUT_DATA_BUF_SZ, device_write_completion,
NULL);
where data is a char buffer of data to write, the final parameter is an optional context object to pass into the callback, and device_write_completion is a static method with the following general form:
void DemiUSBDevice::device_write_completion(void* context,
IOReturn result, void* arg0)
{
//...
}
reading from the interrupt endpoint is similar:
result = (intf)->ReadPipeAsync(intf, m_input_pipe,
data, INPUT_DATA_BUF_SZ, device_read_completion,
NULL);
where device_read_completion is of the following form:
void DemiUSBDevice::device_read_completion(void* context,
IOReturn result, void* arg0)
{
//...
}
Note that to receive these callbacks the run loop must be running (see this link for more information about the CFRunLoop). One way to achieve this is to call CFRunLoopRun() after calling the async read or write methods at which point the main thread blocks while the run loop runs. After handling your callback you can call CFRunLoopStop(CFRunLoopGetCurrent()) to stop the run loop and hand execution back to the main thread.
Another alternative (which I do in my code) is to pass a context object (named 'request' in the following code sample) into the WritePipeAsync/ReadPipeAsync methods - this object contains a boolean completion flag (named 'is_done' in this example). After calling the read/write method, instead of calling CFRunLoopRun(), something like the following can be executed:
while (!(request->is_done))
{
//run for 1/10 second to handle events
Boolean returnAfterSourceHandled = false;
CFTimeInterval seconds = 0.1;
CFStringRef mode = kCFRunLoopDefaultMode;
CFRunLoopRunInMode(mode, seconds, returnAfterSourceHandled);
}
This has the benefit that if you have other threads that use the run loop you won't prematurely exit should another thread stop the run loop...
I hope that this is helpful to people. I had to pull from many incomplete sources to solve this problem and this required considerable work to get running well...
After reading this question a few times and thinking about it for a bit, I thought of another solution for emulating blocking read behavior, but using the HID manager instead of replacing it.
A blocking read function can register an input callback for the device, register the device on the current run loop, and then block by calling CFRunLoopRun(). The input callback can then copy the report into a shared buffer and call CFRunLoopStop(), which causes CFRunLoopRun() to return, thereby unblocking read(). Then, read() can return the report to the caller.
The first issue I can think of is the case where the device is already scheduled on a run loop. Scheduling and then unscheduling the device in the read function may have adverse affects. But that would only be a problem if the application is trying to use both synchronous and asynchronous calls on the same device.
The second thing that comes to mind is the case where the calling code already has a run loop running (Cocoa and Qt apps for example). But, the documentation for CFRunLoopStop() seems to indicate that nested calls to CFRunLoopRun() are handled properly. So, it should be ok.
Here's a bit of simplified code to go with that. I just implemented something similar in my HID Library and it seems to work, although I haven't tested it extensively.
/* An IN report callback that stops its run loop when called.
This is purely for emulating blocking behavior in the read() method */
static void input_oneshot(void* context,
IOReturn result,
void* deviceRef,
IOHIDReportType type,
uint32_t reportID,
uint8_t* report,
CFIndex length)
{
buffer_type *const buffer = static_cast<HID::buffer_type*>(context);
/* If the report is valid, copy it into the caller's buffer
The Report ID is prepended to the buffer so the caller can identify
the report */
if( buffer )
{
buffer->clear(); // Return an empty buffer on error
if( !result && report && deviceRef )
{
buffer->reserve(length+1);
buffer->push_back(reportID);
buffer->insert(buffer->end(), report, report+length);
}
}
CFRunLoopStop(CFRunLoopGetCurrent());
}
// Block while waiting for an IN interrupt report
bool read(buffer_type& buffer)
{
uint8_t _bufferInput[_lengthInputBuffer];
// Register a callback
IOHIDDeviceRegisterInputReportCallback(deviceRef, _bufferInput, _lengthInputBuffer, input_oneshot, &buffer);
// Schedule the device on the current run loop
IOHIDDeviceScheduleWithRunLoop(deviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
// Trap in the run loop until a report is received
CFRunLoopRun();
// The run loop has returned, so unschedule the device
IOHIDDeviceUnscheduleFromRunLoop(deviceRef, CFRunLoopGetCurrent(), kCFRunLoopDefaultMode);
if( buffer.size() )
return true;
return false;
}
I ran into this same kIOReturnExclusiveAccess. Instead of fighting it (building a kext, etc). I found the device and used the POSIX api's.
//My funcation was named differently, but I'm using this for continuity..
void DemiUSBDevice::device_attach_callback(void * context,
io_iterator_t iterator)
{
DeviceManager *deviceManager = (__bridge DADeviceManager *)context;
io_registry_entry_t device;
while ((device = IOIteratorNext(iterator))) {
CFTypeRef prop;
prop = IORegistryEntrySearchCFProperty(device,
kIOServicePlane,
CFSTR(kIODialinDeviceKey),
kCFAllocatorDefault,
kIORegistryIterateRecursively);
if(prop){
deviceManager->devPath = (__bridge NSString *)prop;
[deviceManager performSelector:#selector(openDevice)];
}
}
}
once devPath is set you can call open and read/write..
int dfd;
dfd = open([devPath UTF8String], O_RDWR | O_NOCTTY | O_NDELAY);
if (dfd == -1) {
//Could not open the port.
NSLog(#"open_port: Unable to open %#", devPath);
return;
} else {
fcntl(fd, F_SETFL, 0);
}

respondsToSelector: equivalent for CoreFoundation?

I have a CFArrayRef which mostly has CFDictionaryRef, but sometimes it'll contain other things. I'd like to access a value from the dictionary in the array if I can, and not crash if I can't. Here's the code:
bool result = false;
CFArrayRef devices = CFArrayCreateCopy(kCFAllocatorDefault, SDMMobileDevice->deviceList);
if (devices) {
for (uint32_t i = 0; i < CFArrayGetCount(devices); i++) {
CFDictionaryRef device = CFArrayGetValueAtIndex(devices, i);
if (device) { // *** I need to verify this is actually a dictionary or actually responds to the getObjectForKey selector! ***
CFNumberRef idNumber = CFDictionaryGetValue(device, CFSTR("DeviceID"));
if (idNumber) {
uint32_t fetched_id = 0;
CFNumberGetValue(idNumber, 0x3, &fetched_id);
if (fetched_id == device_id) {
result = true;
break;
}
}
}
}
CFRelease(devices);
}
return result;
Any suggestions for how I can ensure that I only treat device like a CFDictionary if it's right to do so?
(I'm dealing with some open source code that isn't particularly well documented, and it doesn't seem to be particularly reliable either. I'm not sure if it's a bug that the array contains non-dictionary objects or a bug that it doesn't detect when it contains non-dictionary objects, but it seems to me that adding a check here is less likely to break other code then forcing it to only contain dictionaries elsewhere. I don't often work with CoreFoundation, so I'm not sure if I'm using the proper terms.)
In this case, since it looks like you are traversing the I/O Registry, you can use CFGetTypeId():
CFTypeRef device = CFArrayGetValueAtIndex(devices, i); // <-- use CFTypeRef
if(CFGetTypeID(device) == CFDictionaryGetTypeID()) { // <-- ensure it's a dictionary
...
}
If you really need to send messages to NSObject's interface from your C code, you can (see #include <objc/objc.h> and friends, or call to a C helper function in a .m file), but these strategies are not as straight forward as CFGetTypeID(), and much more error-prone.

FTDI Communication with USB device - Objective C

I'm trying to communicate with the Enttec USB DMX Pro. Mainly receiving DMX.
They released a Visual C++ version here, but I'm a little stumped on what to do to convert to Obj-c. Enttec writes, "Talk to the PRO using FTDI library for Mac, and refer to D2XX programming guide to open and talk to the device." Any example apps for Objective-C out there? Is there an easy way to communicate with the Enttec DMX USB Pro?
I've done a significant amount of work with the FTDI chips on the Mac, so I can provide a little insight here. I've used the single-channel and dual-channel variants of their USB-serial converters, and they all behave the same way.
FTDI has both their Virtual COM Port drivers, which create a serial COM port on your system representing the serial connection attached to their chip, and their D2XX direct communication libraries. You're going to want to work with the latter, which can be downloaded from their site for various platforms.
The D2XX libraries for the Mac come in a standalone .dylib (the latest being libftd2xx.1.2.2.dylib) or a new static library they started shipping recently. Included in that package will be the appropriate header files you need (ftd2xx.h and WinTypes.h) as well.
In your Xcode project, add the .dylib as a framework to be linked in, and add the ftd2xx.h, WinTypes.h, and ftd2xx.cfg files to your project. In your Copy Bundled Frameworks build phase, make sure that libftd2xx.1.2.2.dylib and ftd2xx.cfg are present in that phase. You may also need to adjust the relative path that this library expects, in order for it to function within your app bundle, so you may need to run the following command against it at the command line:
install_name_tool -id #executable_path/../Frameworks/libftd2xx.1.2.2.dylib libftd2xx.1.2.2.dylib
Once your project is all properly configured, you'll want to import the FTDI headers:
#import "ftd2xx.h"
and start to connect to your serial devices. The example you link to in your question has a downloadable C++ sample that shows how they communicate to their device. You can bring across almost all of the C code used there and place it within your Objective-C application. They just look to be using the standard FTDI D2XX commands, which are described in detail within the downloadable D2XX Programmer's Guide.
This is some code that I've lifted from one of my applications, used to connect to one of these devices:
DWORD numDevs = 0;
// Grab the number of attached devices
ftdiPortStatus = FT_ListDevices(&numDevs, NULL, FT_LIST_NUMBER_ONLY);
if (ftdiPortStatus != FT_OK)
{
NSLog(#"Electronics error: Unable to list devices");
return;
}
// Find the device number of the electronics
for (int currentDevice = 0; currentDevice < numDevs; currentDevice++)
{
char Buffer[64];
ftdiPortStatus = FT_ListDevices((PVOID)currentDevice,Buffer,FT_LIST_BY_INDEX|FT_OPEN_BY_DESCRIPTION);
NSString *portDescription = [NSString stringWithCString:Buffer encoding:NSASCIIStringEncoding];
if ( ([portDescription isEqualToString:#"FT232R USB UART"]) && (usbRelayPointer != NULL))
{
// Open the communication with the USB device
ftdiPortStatus = FT_OpenEx("FT232R USB UART",FT_OPEN_BY_DESCRIPTION,usbRelayPointer);
if (ftdiPortStatus != FT_OK)
{
NSLog(#"Electronics error: Can't open USB relay device: %d", (int)ftdiPortStatus);
return;
}
//Turn off bit bang mode
ftdiPortStatus = FT_SetBitMode(*usbRelayPointer, 0x00,0);
if (ftdiPortStatus != FT_OK)
{
NSLog(#"Electronics error: Can't set bit bang mode");
return;
}
// Reset the device
ftdiPortStatus = FT_ResetDevice(*usbRelayPointer);
// Purge transmit and receive buffers
ftdiPortStatus = FT_Purge(*usbRelayPointer, FT_PURGE_RX | FT_PURGE_TX);
// Set the baud rate
ftdiPortStatus = FT_SetBaudRate(*usbRelayPointer, 9600);
// 1 s timeouts on read / write
ftdiPortStatus = FT_SetTimeouts(*usbRelayPointer, 1000, 1000);
// Set to communicate at 8N1
ftdiPortStatus = FT_SetDataCharacteristics(*usbRelayPointer, FT_BITS_8, FT_STOP_BITS_1, FT_PARITY_NONE); // 8N1
// Disable hardware / software flow control
ftdiPortStatus = FT_SetFlowControl(*usbRelayPointer, FT_FLOW_NONE, 0, 0);
// Set the latency of the receive buffer way down (2 ms) to facilitate speedy transmission
ftdiPortStatus = FT_SetLatencyTimer(*usbRelayPointer,2);
if (ftdiPortStatus != FT_OK)
{
NSLog(#"Electronics error: Can't set latency timer");
return;
}
}
}
Disconnection is fairly simple:
ftdiPortStatus = FT_Close(*electronicsPointer);
*electronicsPointer = 0;
if (ftdiPortStatus != FT_OK)
{
return;
}
Writing to the serial device is then pretty easy:
__block DWORD bytesWrittenOrRead;
unsigned char * dataBuffer = (unsigned char *)[command bytes];
//[command getBytes:dataBuffer];
runOnMainQueueWithoutDeadlocking(^{
ftdiPortStatus = FT_Write(electronicsCommPort, dataBuffer, (DWORD)[command length], &bytesWrittenOrRead);
});
if((bytesWrittenOrRead < [command length]) || (ftdiPortStatus != FT_OK))
{
NSLog(#"Bytes written: %d, should be:%d, error: %d", bytesWrittenOrRead, (unsigned int)[command length], ftdiPortStatus);
return NO;
}
(command is an NSData instance, and runOnMainQueueWithoutDeadlocking() is merely a convenience function I use to guarantee execution of a block on the main queue).
You can read raw bytes from the serial interface using something like the following:
NSData *response = nil;
DWORD numberOfCharactersToRead = size;
__block DWORD bytesWrittenOrRead;
__block unsigned char *serialCommunicationBuffer = malloc(numberOfCharactersToRead);
runOnMainQueueWithoutDeadlocking(^{
ftdiPortStatus = FT_Read(electronicsCommPort, serialCommunicationBuffer, (DWORD)numberOfCharactersToRead, &bytesWrittenOrRead);
});
if ((bytesWrittenOrRead < numberOfCharactersToRead) || (ftdiPortStatus != FT_OK))
{
free(serialCommunicationBuffer);
return nil;
}
response = [[NSData alloc] initWithBytes:serialCommunicationBuffer length:numberOfCharactersToRead];
free(serialCommunicationBuffer);
At the end of the above, response will be an NSData instance containing the bytes you've read from the port.
Additionally, I'd suggest that you should always access the FTDI device from the main thread. Even though they say they support multithreaded access, I've found that any kind of non-main-thread access (even guaranteed exclusive accesses from a single thread) cause intermittent crashes on the Mac.
Beyond the cases I've described above, you can consult the D2XX programming guide for the other functions FTDI provides in their C library. Again, you should just need to move over the appropriate code from the samples that have been provided to you by your device manufacturer.
I was running into a similar issue (trying to write to the EntTec Open DMX using Objective-C), without any success. After following #Brad's great answer, I realized that you also need to toggle the BREAK state each time you send a DMX packet.
Here's an example of my loop in some testing code that sends packets with a 20 millisecond delay between frames.
while (1) {
FT_SetBreakOn(usbRelayPointer);
FT_SetBreakOff(usbRelayPointer);
ftdiPortStatus = FT_Write(usbRelayPointer, startCode, 1, &bytesWrittenOrRead);
ftdiPortStatus = FT_Write(usbRelayPointer, dataBuffer, (DWORD)[command length], &bytesWrittenOrRead);
usleep(20000);
}
Hope this helps someone else out there!

MidiReadProc - using srcConnRefCon to listen to only one source

I am trying to write a basic app that uses CoreMidi to receive midi events from a specific source. I understand that all midi events that come into a port call the proc that I connected via MidiInputPortCreate(). I also understand that when using MidiPortConnectSource() that you can send an identifier (connRefCon) to help know what the source is. But I'm not sure how to use it.
I figure that within my MidiReadProc that I can use the scrConnRefCon and an if statement to listen to a specific source, but I still dont know what *void I should pass to separate each source. Ideally my ReadProc will look something like this:
void SourceReadProc (const MIDIPacketList *pktlist,
void *readProcRefCon,
void *srcConnRefCon)
{
if (srcConnRefCon == mySourceChoice) {
// pass the pktlist to do something
}
};
Any help will be greatly appreciated.
GW
After a break I've come back to this project with a fresh perspective. When I call MIDIPortConnectSource and pass a unique connRefCon it's not apparently passing for each endpoint. Here's my code:
ItemCount count = MIDIGetNumberOfSources();
for (Itemcount i=0; i<count; i++) {
MIDIEndpointRef endpoint = MIDIGetSource(i);
MIDIObjectGetStringProperty(endpoint,kMIDIPropertyName, &midiEndpointSourceName);
NSLog(#"Source %lu: %#", i, midiEndpointSourceName);
MIDIPortConnectSource(midiSourcePort, endpoint, (void*)&i);
}
Then my read proc:
void SourceReadProc (const MIDIPacketList *pktlist,
void *readProcRefCon,
void *srcConnRefCon)
{
ItemCount *source = (ItemCount*) srcConnRefCon;
NSLog(#"source: %lu", *source);
}
I've hooked up two different midi sources and I can find them both just fine. My first code reports that there are two sources and tells me their names. But my read proc says that the sources is always the first source. I've tried three different data types when passing the connRefCon with no luck. I feel that my issue must be with the MIDIPortConnectSource.
Any help or even troubleshooting ideas would be great. I wish that CoreMIDI had functions to query what's connected to ports so I could check that, but alas, there's not.
The srcConnRefCon is useful if you've made multiple MIDIPortConnectSource() calls. Most commonly, it's a pointer to an object representing the source, but it could be anything. If you just want to disambiguate multiple sources, you could, say, use a string.
MIDIPortConnectSource(port, endpoint1, (void *)"endpoint1");
MIDIPortConnectSource(port, endpoint2, (void *)"endpoint2");
Then, in your SourceReadProc, you'd do something like this:
char *source = (char *)srcConnRefCon;
if (!strcmp(source, "endpoint1")) {
// Process packets from source 1
}
Make sure the allocation lifetime of whatever you pass in extends as long as the port is connected - otherwise you'll get a dangling pointer, which can be hell to debug.