Page 1 of 3 123 LastLast
Results 1 to 10 of 30

Thread: CAN-Bus Interface to Mac Mini

  1. #1
    Newbie
    Join Date
    Jan 2006
    Posts
    8

    CAN-Bus Interface to Mac Mini

    Hi guys,

    I'm taking delivery of an Infiniti M45 Sport in February and am sold on using a Mac Mini w/ Front Row as my CarPC of choice (thanks to fellow member FikseGTS' post here).

    My main hurdle to overcome is that I'd like to control the Mac via the existing in-car controls. To do this, I'll need to find a way to intercept CAN-Bus messages on the Mac. Has anybody on here tried this before?

    It looks like a CAN-Bus to USB interface will be the way to go. Here are links to a few of the options, but I have no idea if they have Mac drivers available.

    http://www.apoxcontrols.com/usbcan.htm?
    http://www.qprotos.com/quickcan.htm
    http://www.easysync-ltd.com/index.ht...arget=d20.html

    Any tips would be appreciated and I'd love to hear from anybody that has tried this in the past. Thanks!

    -Dave

  2. #2
    CarFrontEnd Creator iamgnat's Avatar
    Join Date
    Jul 2004
    Location
    NoVA
    Posts
    867
    Quote Originally Posted by dknightx
    My main hurdle to overcome is that I'd like to control the Mac via the existing in-car controls. To do this, I'll need to find a way to intercept CAN-Bus messages on the Mac. Has anybody on here tried this before?
    I've looked at this but so far had no luck (I actually want to do it to go the other way ) finding anything for the Mac. I think you did though.

    I haven't looked at the other links yet, but there is a note about Linux there that means it will work great for a Mac. They mention that Linux needs the FTDI drivers from FTDI's website. FTDI is a great supplier for rs232 -> usb chips (I have a few proto boards using them) and the drivers they refer to are for using it as a Serial Port. Given this, all you have to do is install the Mac FTDI drivers and right some code to use the interface. That's a really sweet solution

    Of course it sounds like the catch is that you need to learn to talk CAN, but my (limited) understanding is that you could use a device like this to log all the CAN traffic on the network so you can decipher the commands you want to send/receive (assuming you can't get your MFGs command set).

    -dave

  3. #3
    CarFrontEnd Creator iamgnat's Avatar
    Join Date
    Jul 2004
    Location
    NoVA
    Posts
    867
    This one uses FTDI as well. The second doesn't appear to at a quick glance, so I have no idea about that one.

    -dave

  4. #4
    Constant Bitrate
    Join Date
    Dec 2005
    Location
    London, UK
    Posts
    182
    Hi Dave,
    Let me know if you get one. I'm after the same thing as well. Thanks!!!

  5. #5
    Newbie
    Join Date
    Jan 2006
    Posts
    8
    Quick Update:

    I sent an email to Apox Controls asking specifically about Mac support and it sounds like it should work (as iamgnat pointed out above). Here is the response I received:

    --------------

    Hi Dave,

    We do not currently support the Mac, HOWEVER, Technically, if you are a savvy programmer, it appears That you should be able to make it work.

    Here are the steps that are involved.

    1.) We use the FT245BM chip from www.ftdichip.com To handle all USB traffic to and from our device. At the above web pages they have a drivers section. We use the D2XX driver, the VCP driver could be used But at a reduced speed.

    2.) You would install the above drivers meant for OS-X onto your mac.

    3.) Plug in the USB-CAN board, and the device should be recognized By your mac. (Although I am not quite sure how a Mac handles plug and play. So you would have to do research on this from the FTDI site.)

    4.) The D2XX drivers contain a linkable DLL to which your program would attach. Using my Windows example code, you could convert my code to the Mac, And start talking to the device.

    ---------------

    I don't have my car or a Mac Mini yet (I'm eagerly awaiting Jobs' keynote today before I purchase one) , but I think I'll go ahead and order the Apox device to start playing with. I'm not a Mac developer, either, but have extensive Windows and Unix experience so I should be able to come up to speed pretty quickly.

    -Dave

  6. #6
    CarFrontEnd Creator iamgnat's Avatar
    Join Date
    Jul 2004
    Location
    NoVA
    Posts
    867
    Quote Originally Posted by dknightx
    We do not currently support the Mac, HOWEVER, Technically, if you are a savvy programmer, it appears That you should be able to make it work.
    Savvy nothing, this is easy stuff

    1.) We use the FT245BM chip from www.ftdichip.com To handle all USB traffic to and from our device. At the above web pages they have a drivers section. We use the D2XX driver, the VCP driver could be used But at a reduced speed.
    Wonderful chip. Have 2 myself . It's been ~6 months since i've fiddled with any of that stuff but I believe the D2XX driver is Windows only (but maybe it's the VCP, the FTDI website will tell you for sure). As far as speed, FTDI says it will do 2 mega-bit with the drivers that are supported for the Mac when using USB and assuming the software on both the chip (the microprocessor between the FTDI and CAN chips) and the computer are programmed to do that. More likely 1mb is what you will get as there are serious drawbacks to using it in the 2mb mode.

    3.) Plug in the USB-CAN board, and the device should be recognized By your mac. (Although I am not quite sure how a Mac handles plug and play. So you would have to do research on this from the FTDI site.)
    This is where knowing a little Unix will help you out. When you plug it in, you'll get a device created in your /dev directory that looks something like /dev/usb-ftdiaa (though I imagine they changed the firmware on the FTDI chip which will change the /dev name, but it should start with usb-).

    4.) The D2XX drivers contain a linkable DLL to which your program would attach. Using my Windows example code, you could convert my code to the Mac, And start talking to the device.
    Forget the Windows example code, it bears little resemblance to what is needed for the Mac. There are two ways you can do it. The Unix (C) way of opening the file, setting it up, and using select(). Or you can use the Objective-C method which I forget off the top of my head, but is really simple (though you do have to drop down to some low level C code to set your port speed (same as in the C method). Really the main difference between the two is how you manage the select loop. In the all C method, you have to do it and it is NOT compatible with embedding it in an Objective-C GUI app with out a lot of nasty code to support it. The alternative method uses the built-in Run Loop that is in every Aqua app.

    I would suggest grabbing a copy of ZTerm (it's a free termial package that is pretty simple and perfect for playing with devices like this while you test and debug them).

    All my example code is on my laptop at home. I'd be glad either post it (if it's as small as I remember) or email it if people are interested.

    -dave

  7. #7
    Newbie
    Join Date
    Jan 2006
    Posts
    8
    Quote Originally Posted by iamgnat
    All my example code is on my laptop at home. I'd be glad either post it (if it's as small as I remember) or email it if people are interested.
    I'm DEFINITELY interested....

    Thanks!

    -Dave

  8. #8
    Constant Bitrate
    Join Date
    Dec 2005
    Location
    London, UK
    Posts
    182
    Quote Originally Posted by iamgnat
    All my example code is on my laptop at home. I'd be glad either post it (if it's as small as I remember) or email it if people are interested.
    Me too, yes, very much interested in deed. It' so kind of you. Thanks!!!

  9. #9
    CarFrontEnd Creator iamgnat's Avatar
    Join Date
    Jul 2004
    Location
    NoVA
    Posts
    867
    Ok. Here is some sample C code. The compiler line is at the top.

    If you are using it in two port mode, it will send string back and forth between them (this was for a device I had where I needed to test 2 ports.

    The deviceA and deviceB options are expecting the path to your serial devices (i.e. /dev/usb-somethingoranother).

    There is also almost no error checking in this code, so it won't be much help if you give it the wrong thing.

    Code:
    // cc -o usbserial -framework CoreFoundation -framework IOKit usbserial.c
    
    #include	<stdio.h>
    #include	<fcntl.h>
    #include	<sys/errno.h>
    #include	<sys/termios.h>
    #include	<sys/types.h>
    #include	<sys/time.h>
    #include	<unistd.h>
    
    #include	<sys/filio.h>
    #include	<sys/ioctl.h>
    #include	<CoreFoundation/CoreFoundation.h>
    
    #include	<IOKit/IOKitLib.h>
    #include	<IOKit/serial/IOSerialKeys.h>
    #include	<IOKit/IOBSD.h>
    
    int main (int argc, char *argv[]) {
        struct termios  termiosA;
        struct termios  termiosB;
        struct timeval  timeout;
        fd_set          rds;
        fd_set          wds;
        fd_set          exs;
        char            *devA;
        char            *devB;
        char            strA[1024];
        char            strB[1024];
        int             modemA;
        int             modemB;
        int             baudA;
        int             baudB;
        int             fdA;
        int             fdB;
        int             ret;
        int             iA = 0;
        int             iB = 0;
        
        if (argc != 3 && argc != 5) {
            printf("Usage: %s deviceA baudA ?deviceB baudB?\n", argv[0]);
            exit(1);
        }
        
        devA = argv[1];
        baudA = atoi(argv[2]);
        printf("Interface A:\n\tDevice = %s\n\tBaud   = %i\n", devA, baudA);
        
        if (argc == 5) {
           devB = argv[3];
            baudB = atoi(argv[4]);
            printf("Interface B:\n\tDevice = %s\n\tBaud   = %i\n", devB, baudB);
        }
        
        printf("\nOpening Interface A...\n");
        fdA = open(devA, O_RDWR | O_NONBLOCK);
        memset(&termiosA, 0, sizeof(struct termios));
        cfmakeraw(&termiosA);
        cfsetspeed(&termiosA, baudA);   // Set Baud
        termiosA.c_cflag |= CS8;        // 8 Data Bits
        ret = tcsetattr(fdA, TCSANOW, &termiosA);
        if (ret) {
            printf("ioctl (A) - returned %i errno %i\n", ret, errno);
            close(fdA);
            exit(1);
        }
        ioctl(fdA, TIOCMGET, &modemA);
        modemA |= TIOCM_DTR;
        ioctl(fdA, TIOCMSET, &modemA);
        
        if (argc == 5) {
            printf("\nOpening Interface B...\n");
            fdB = open(devB, O_RDWR | O_NONBLOCK);
            memset(&termiosB, 0, sizeof(struct termios));
            cfmakeraw(&termiosB);
            cfsetspeed(&termiosB, baudB);   // Set Baud
            termiosB.c_cflag |= CS8;        // 8 Data Bits
            ret = tcsetattr(fdB, TCSANOW, &termiosB);
            if (ret) {
                printf("ioctl (B) - returned %i errno %i\n", ret, errno);
                close(fdA);
                close(fdB);
                exit(1);
            }
            ioctl(fdB, TIOCMGET, &modemB);
            modemB |= TIOCM_DTR;
            ioctl(fdB, TIOCMSET, &modemB);
        }
        
        printf("Setting up the select loop...\n");
        memset(&timeout, 0, sizeof(struct timeval));
        timeout.tv_sec = 15;
        
        printf("Starting the select loop for %i & %i.\n", fdA, fdB);
        while (1) {
            char    tmpA[1024];
            char    tmpB[1024];
            
            memset(tmpA, '\0', 1024);
            memset(tmpB, '\0', 1024);
            
            FD_ZERO(&rds);
            FD_ZERO(&wds);
            FD_ZERO(&exs);
            FD_SET(fdA, &rds);
            FD_SET(fdA, &wds);
            FD_SET(fdA, &exs);
            if (argc == 5) {
                FD_SET(fdB, &rds);
                FD_SET(fdB, &wds);
                FD_SET(fdB, &exs);
            }
            
            ret = select(5, &rds, &wds, &exs, &timeout);
            
            if (FD_ISSET(fdA, &rds)) {
                if (read(fdA, tmpA, 1) < 0) {
                    printf("errno = %i\n", errno);
                    break;
                }
                if (tmpA[0] == '\r' || tmpA[0] == '\n') {
                    if (iA > 0) {
                        strA[iA] = '\0';
                        printf("A(%i) -> %s\n", iA, strA);
                        memset(strA, '\0', 1024);
                        iA = 0;
                    }
                    continue;
                } else {
                    strA[iA++] = tmpA[0];
                    tmpA[0] = '\0';
                }
            }
            if (argc == 5 && FD_ISSET(fdB, &rds)) {
                if (read(fdB, tmpB, 1) < 0) {
                    printf("errno = %i\n", errno);
                    break;
                }
                if (tmpB[0] == '\r' || tmpB[0] == '\n') {
                    if (iB > 0) {
                        strB[iB] = '\0';
                        printf("B(%i) -> %s\n", iB, strB);
                        memset(strB, '\0', 1024);
                        iB = 0;
                    }
                    continue;
                } else {
                    strB[iB++] = tmpB[0];
                    tmpB[0] = '\0';
                }
            }
            if (FD_ISSET(fdA, &wds)) {
                printf("Port A ready for writing.\n");
                write(fdA, "Test from port A.\r\n", 19);
            }
            if (argc == 5 && FD_ISSET(fdB, &wds)) {
                printf("Port B ready for writing.\n");
                write(fdB, "Test from port B.\r\n", 19);
            }
            if (FD_ISSET(fdA, &exs)) {
                printf("Port A had an exception.\n");
            }
            if (argc == 5 && FD_ISSET(fdB, &exs)) {
                printf("Port B had an exception.\n");
            }
        }
    }
    -dave

  10. #10
    CarFrontEnd Creator iamgnat's Avatar
    Join Date
    Jul 2004
    Location
    NoVA
    Posts
    867
    Here is the Objective-C version. It's more convoluted if you aren't familiar with how Objective-C and Aqua work, but it's actually really much better.

    This is in 3 files. The first two make up my SerialObject and do most of the dirty work. The rest of the dirty work is done by your RunLoop and the Notification system in the background (no coding required).

    The final bit is just a clip of what you would put in your initialization code some where when your App starts up.

    Code:
    //
    //  SerialObject.h
    //  mac_escape
    //
    //  Created by gnat on 07/24/05.
    //  Copyright 2005 __MyCompanyName__. All rights reserved.
    //
    
    #import <Cocoa/Cocoa.h>
    #import	<fcntl.h>
    #import <unistd.h>
    #import	<sys/types.h>
    #import <sys/errno.h>
    #import	<sys/filio.h>
    #import <sys/ioctl.h>
    #import	<sys/termios.h>
    #import <IOKit/IOBSD.h>
    #import <IOKit/IOKitLib.h>
    #import <IOKit/serial/IOSerialKeys.h>
    
    @interface SerialObject : NSObject {
    	NSFileHandle	*serialPort;
    }
    
    - (id)				initWithDeviceAndBaud:(NSString *) device baud:(int) baud;
    - (NSString *)		description;
    - (void)			setupReader:(id) obj selector:(SEL) selector;
    - (void)			writeSerial:(NSData *) data;
    - (void)			close;
    @end
    Code:
    //
    //  EscapeSerial.m
    //  mac_escape
    //
    //  Created by gnat on 07/24/05.
    //  Copyright 2005 __MyCompanyName__. All rights reserved.
    //
    
    #import "SerialObject.h"
    
    
    @implementation SerialObject
    - (id) initWithDeviceAndBaud:(NSString *) device baud:(int) baud {
    	struct termios	term;
    	char			*dev = [device cString];
    	int				ctl;
    	int				fd = 0;
    	
    	fd = open(dev, O_RDWR | O_NONBLOCK);
    	
    	// Couldn't open for some reason...
    	if (fd == nil) {
    		NSLog(@"Unable to open %s.", dev);
    		return nil;
    	}
    	
    	memset(&term, 0, sizeof(term));
    	cfmakeraw(&term);
    	cfsetspeed(&term, baud);
    	term.c_cflag |= CS8;	// 8 Data Bits
    	ctl = tcsetattr(fd, TCSANOW, &term);
    	if (ctl) {
    		NSLog(@"SerialObject: unable to set spead: %i.", ctl);
    		[serialPort closeFile];
    		return nil;
    	}
    	
    	ioctl(fd, TIOCMGET, &ctl);
    	ctl |= TIOCM_DTR;
    	ioctl(fd, TIOCMSET, &ctl);
    	
    	serialPort = [[NSFileHandle alloc] initWithFileDescriptor:fd];
    	if (serialPort == nil) {
    		close(fd);
    		NSLog(@"Unable to create NSFileHandle from fd: %i", fd);
    		return nil;
    	}
    	[serialPort retain];
    	
    	return self;
    }
    
    - (NSString *) description {
    	return [serialPort description];
    }
    
    - (void) setupReader:(id) obj selector:(SEL) selector {
    	NSNotificationCenter	*nc = [NSNotificationCenter defaultCenter];
    	
    	[nc addObserver:obj selector:selector
    			name:NSFileHandleDataAvailableNotification
    			object:serialPort];
    	[serialPort waitForDataInBackgroundAndNotify];
    	
    }
    
    - (void) writeSerial:(NSData *) data {
    	[serialPort writeData:data];
    }
    
    - (void) close {
    	int fd = [serialPort fileDescriptor];
    	
    	close(fd);
    	[serialPort release];
    }
    @end
    Code:
    	// Do something to setup wheelRemoteDev and baud from a prefs dictionary.
    	wheelRemotePort = [[SerialObject alloc] initWithDeviceAndBaud:wheelRemoteDev baud:baud];
    	if (wheelRemotePort == nil) {
    		NSLog(@"Unable to open %@ for the wheel remote.",
    			  wheelRemoteDev);
    	} else {
    		[wheelRemotePort retain];
    		[wheelRemotePort setupReader:self selector:@selector(readWheelRemote:)];
    	}
    EDIT="The Notification handler I forgot like a bonehead."
    Code:
    - (void) readWheelRemote:(NSNotification *) context {
    	NSFileHandle	*fh = [context object];
    	NSData			*data = [fh availableData];
    	char			switchId = 0;
    	char			switchCode = 0;
    	char			buf = 0;
    	
    	[data getBytes:&buf length:sizeof(buf)];
    	switchId = buf / 10;
    	switchCode = buf % 10;
    	
    	switch (switchId) {
    		case 0:
    			// Stereo controller remote.
    			switch (switchCode) {
    				case 1:
    					[audioControl upVolume];
    					break;
    				case 2:
    					[audioControl downVolume];
    					break;
    				case 3:
    					break;
    				case 4:
    					break;
    				case 5:
    					break;
    				default:
    					NSLog(@"Unknown Stereo remote command: %i", switchCode);
    			}
    			break;
    		
    		default:
    			NSLog(@"Unknown switch id: %i", switchId);
    	}
    	
    	[fh waitForDataInBackgroundAndNotify];
    }
    /EDIT
    NB: This is actual code from my frontend. The "wheelRemotePort" in question is a custom bluetooth remote i've been "building" since June. That's about the same state as my frontend too

    But I bought my Lilly tonight, so now I guess I have to get off my lazy *** and do something rather than let $500 (mounting kit too) gather dust.

    -dave

Page 1 of 3 123 LastLast

Similar Threads

  1. FAQ: Installing a Mac Mini in Your Car
    By Bugbyte in forum The FAQ Emporium
    Replies: 20
    Last Post: 06-11-2011, 08:12 PM
  2. Mini Mac 4 Porsche 944
    By 4thChild in forum MacCar
    Replies: 14
    Last Post: 03-13-2009, 10:16 PM
  3. Mac mini in dash questions.
    By dfluxnet in forum MacCar
    Replies: 6
    Last Post: 10-03-2006, 03:02 AM
  4. FS: Mac Mini CarPC Package/Parts LOADED!
    By markcc01 in forum Classified Archive
    Replies: 1
    Last Post: 01-03-2006, 10:47 PM
  5. Hot Rod Company to start incorporating Mac Minis
    By stacheldraht in forum MacCar
    Replies: 4
    Last Post: 02-01-2005, 12:25 PM

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •