Pages: [1] 2   Go Down
Author Topic: Connect to Arduino Mega serial interface by C code  (Read 8079 times)
0 Members and 1 Guest are viewing this topic.
0
Offline Offline
Newbie
*
Karma: 0
Posts: 13
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Hi all

I'm having trouble talking to an Arduino Mega using serial port commands on Windows. Identical code works fine to connect to an Arduino Duemilanove but doesn't allow connection to the serial port with the Mega. Also, I can access both cards from the Arduino IDE software, on both Linux and Windows.

I have serial port code that works fine on Linux for both Duemilanove and Mega. But I can't write code that works for the Mega on Windows, even though it works fine with the Duemilanove at this stage.

My only thought is that perhaps something is different in the way that the Mega's USB-to-serial hardware is set up, and perhaps, at least on Windows, I need to add some additional command (maybe on the Arduino, or maybe in my C code) to enable the interface to be correctly visible/accessible.

Any thoughts anyone? Anyone else had this problem?

Cheers
JP
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 626
Posts: 34132
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Just to say I haven't had this problem. Is windows recognising the mega? It could be a problem with the drivers. Once that is established it just looks like a serial port. Of course the COM port number will be different for the different boards, have you changes that in your PC setup?
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 13
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I would have thought that too, but the Arduino IDE is able to connect to the software in both cases... does the Arduino IDE use its own built-in drivers, or something like that? How can the serial connection work from within the IDE but fail in home-made C code?
« Last Edit: October 05, 2010, 06:21:27 am by jdpipe » Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 626
Posts: 34132
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
How can the serial connection work from within the IDE but fail in home-made C code?

May I suggest a fault in the home-made C code.  :smiley
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 13
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Of course, but what would cause success with my code with the Duemilanove but failure with the same code when attempting to connect with the Mega ? Don't they both connect using the same serial protocol, communication settings, etc? I am finding that I don't even get past the first stop of opening the COM port!
« Last Edit: October 05, 2010, 07:55:00 am by jdpipe » Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 626
Posts: 34132
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I am finding that I don't even get past the first stop of opening the COM port!

That suggest to me that you are using the same COM port number for both boards and they will be different. As you say everything else is the same.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 13
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

No, I'm changing the port numbers. I use the port names as indicated in the Arduino IDE. My error messages clearly indicate that the port names are being correctly conveyed through the code.
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 626
Posts: 34132
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
I use the port names as indicated in the Arduino IDE.

Have you got the monitor open in the Arduino IDE? That will stop anything else connecting to it.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 13
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Yes, I've closed the monitor. That's not it. Only thing I noticed so far was that the Device ID is slightly different, as shown in the Windows Device Manager.
« Last Edit: October 05, 2010, 05:27:42 pm by jdpipe » Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 207
Posts: 12904
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


The Mega is on which serial port?
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 13
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

On a Windows 7 machine, it was coming up as COM14. On a Windows XP machine, it was coming up as COM11. The port numbers are different from the ports that an  Duemilanove comes up on. The port names come up the same every time, for a given board type.
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 207
Posts: 12904
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


Post the CreateFile call.
Logged

0
Offline Offline
Newbie
*
Karma: 0
Posts: 13
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Complete serial port access code is following. Code is released as FOSS under the GPL3 license. The CreateFile call is

CreateFile(serialport, GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);

Following is the full coding listing. Firstly, serialport.c, written by me (with contributions from Ming Chin Pua) and based on SerialClass.cpp from Arduino.cc site.

Code:
#include <stdio.h>    /* Standard input/output definitions */
#include <stdlib.h>
#include <string.h>   /* String function definitions */
#include <fcntl.h>    /* File control definitions */
#include <errno.h>    /* Error number definitions */

#ifdef _WIN32
# include <windows.h>
#else
# include <termios.h>  /* POSIX terminal control definitions */
# include <sys/ioctl.h>
#endif

#include "serialport.h"

int serialport_writebyte(SERIALPORT_HANDLE fd, uint8_t b){
#ifdef _WIN32
    DWORD errors;
    COMSTAT status;
    DWORD n;
    if(!WriteFile((HANDLE)fd, (void *)(&b), 1, &n, NULL)){
        ClearCommError((HANDLE)fd, &errors, &status);
        perror("serialport_write: Error writing to COM port");
        return -1;
    }
#else /* Linux, Mac */
    int n = write(fd,&b,1);
    if( n!=1)
        return -1;
#endif
    return 0;
}

int serialport_write(SERIALPORT_HANDLE fd, const char* str){
    int len = strlen(str);
#ifdef _WIN32
    DWORD n;
    DWORD errors;
    COMSTAT status;
    if(!WriteFile((HANDLE)fd, (void *)str, len, &n, 0)){
        ClearCommError((HANDLE)fd, &errors, &status);
        perror("serialport_write: Error writing to COM port");
        return -1;
    }
#else /* Linux, Mac */
    int n = write(fd, str, len);
    if( n!=len )
        return -1;
#endif
    return 0;
}

int serialport_read_until(SERIALPORT_HANDLE fd, char* buf, char until){
    char b[1] = " ";
    int i=0;
    do {
#ifdef _WIN32
        DWORD n;
        if(!ReadFile(fd, b, 1, &n, NULL)){ /* READFILE returns TRUE on success */
            return -1;
        }
#else /* Linux, Mac */
        int n = read(fd, b, 1);  // read a char at a time
#endif
        if( n==-1){
#if _WIN32
            fprintf(stderr,"COULDN'T READ\n");
            return -1;    // couldn't read
        }else if( n==0 ) {
            //fprintf(stderr,"READ 0\n");
            usleep( 10 ); // wait 10 msec try again
            continue;
#else
            continue;
#endif
        }else{
            //fprintf(stderr,"%d=[%c]\n",i,b[0]);
            buf[i] = b[0];
            i = i + 1;
        }
        /* TODO add some code to check the length of the buffer is not exceeded */
    } while( b[0] != until );

    buf[i] = '\0';  // null terminate the string
    return 0;
}

int serialport_readbyte(SERIALPORT_HANDLE fd, char *b){
#ifdef _WIN32
    DWORD n;
    if(ReadFile(fd, b, 1, &n, NULL)){
        return -1;
    }
#else /* Linux, Mac */
    int n = read(fd, b, 1);  // read a char at a time
    if(n==1)return 0;
    return -1;
#endif
}


// takes the string name of the serial port (e.g. "/dev/tty.usbserial","COM1")
// and a baud rate (bps) and connects to that port at that speed and 8N1.
// opens the port in fully raw mode so you can send binary data.
// returns valid fd, or -1 on error


SERIALPORT_HANDLE serialport_init(const char *serialport, int baud, SERIALPORT_TERMIOS *oldsettings){
    SERIALPORT_HANDLE fd;
    
#ifdef _WIN32
   {fd = CreateFile(serialport, GENERIC_READ|GENERIC_WRITE, 0, NULL
            , OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL
    );
    if(fd==INVALID_HANDLE_VALUE){
        DWORD error = GetLastError();
        switch(error){  
            case ERROR_ACCESS_DENIED:
                fprintf(stderr,"serislport_init: Unable to open port: Access denied (port in use?)\n");
                return SERIALPORT_FILE_ERROR;
            case ERROR_FILE_NOT_FOUND:
                fprintf(stderr,"serialport_init: Unable to open port '%s'. File not found.\n",serialport);
                return SERIALPORT_FILE_ERROR;
            default:
                perror("serialport_init: Unknown error in CreateFile call.");
                return SERIALPORT_FILE_ERROR;
        }
    }

    DCB toptions;
    if(!GetCommState(fd, &toptions)){
        perror("serialport_init: Couldn't get COM state");
        return SERIALPORT_FILE_ERROR;
    }

    // save old settings if requested
    if(oldsettings){
           *oldsettings = toptions;
    }

    DWORD brate;
    switch(baud) {
        case 4800:   brate=CBR_4800;   break;
        case 9600:   brate=CBR_9600;   break;
        case 14400:  brate=CBR_14400;  break;
        case 19200:  brate=CBR_19200;  break;
        case 38400:  brate=CBR_38400;  break;
        case 57600:  brate=CBR_57600;  break;
        case 115200: brate=CBR_115200; break;  
        // some other speeds are possible, see DCB structure details (this
        // 115200 is the max speed of arduino.
    }

    toptions.BaudRate = brate;
    toptions.ByteSize = 8;
    toptions.StopBits = ONESTOPBIT;
    toptions.Parity = NOPARITY;
    toptions.fOutxCtsFlow = FALSE;
    toptions.fOutxDsrFlow = FALSE;
    toptions.fOutX = FALSE;
    toptions.fInX = FALSE;
    toptions.fRtsControl = RTS_CONTROL_DISABLE;
    toptions.fTXContinueOnXoff = TRUE;
    toptions.fNull = FALSE;
    
    
    if(!SetCommState(fd, &toptions)) {
        perror("serialport_init: Couldn't set COM state");
        return SERIALPORT_FILE_ERROR;
    }
    }

#else /* Linux, Mac */
   {struct termios toptions;
    
    //fprintf(stderr,"init_serialport: opening port %s @ %d bps\n",
    //        serialport,baud);

    fd = open(serialport, O_RDWR | O_NOCTTY | O_NDELAY);
    if (fd == -1)  {
        perror("serialport_init: Unable to open port ");
        return SERIALPORT_FILE_ERROR;
    }
    
    if (tcgetattr(fd, &toptions) < 0) {
        perror("serialport_init: Couldn't get term attributes");
        return SERIALPORT_FILE_ERROR;
    }

    // save old settings if requested
    if(oldsettings){
           *oldsettings = toptions;
    }

    speed_t brate = baud; // let you override switch below if needed
    switch(baud) {
    case 4800:   brate=B4800;   break;
    case 9600:   brate=B9600;   break;
#ifdef B14400
    case 14400:  brate=B14400;  break;
#endif
    case 19200:  brate=B19200;  break;
#ifdef B28800
    case 28800:  brate=B28800;  break;
#endif
    case 38400:  brate=B38400;  break;
    case 57600:  brate=B57600;  break;
    case 115200: brate=B115200; break;
    }
    cfsetispeed(&toptions, brate);
    cfsetospeed(&toptions, brate);

    // 8N1
    toptions.c_cflag &= ~PARENB;
    toptions.c_cflag &= ~CSTOPB;
    toptions.c_cflag &= ~CSIZE;
    toptions.c_cflag |= CS8;
    // no flow control
    toptions.c_cflag &= ~CRTSCTS;

    toptions.c_cflag |= CREAD | CLOCAL;  // turn on READ & ignore ctrl lines
    toptions.c_iflag &= ~(IXON | IXOFF | IXANY); // turn off s/w flow ctrl

    toptions.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG); // make raw
    toptions.c_oflag &= ~OPOST; // make raw

    // see: http://unixwiz.net/techtips/termios-vmin-vtime.html
    toptions.c_cc[VMIN]  = 0;
    toptions.c_cc[VTIME] = 20;
    
    if( tcsetattr(fd, TCSANOW, &toptions) < 0) {
        perror("serialport_init: Couldn't set term attributes");
        return SERIALPORT_FILE_ERROR;
    }
    }
#endif

    return fd;
}

int serialport_restoresettings(SERIALPORT_HANDLE fd, SERIALPORT_TERMIOS *oldsettings){

#ifdef _WIN32
    if(!SetCommState(fd, oldsettings)) {
        perror("serialport_restoresettings: Couldn't set COM state");
        return -1;
    }
#else
    if(tcsetattr(fd, TCSANOW, oldsettings) < 0) {
        perror("serialport_restoresettings: Couldn't set term attributes");
        return -1;
    }
#endif
    return 0;
}


int serialport_close(SERIALPORT_HANDLE fd){
    if(fd<0){
        perror("serialport_close: COM was not open");
        return -1;
    }
#ifdef _WIN32
    CloseHandle(fd);
#else /* Linux, Mac */
    close(fd);
#endif
    return 0;
}


serialport.h

Code:
#ifndef SERIALPORT_H
#define SERIALPORT_H

#include <stdint.h>   /* Standard types */
#include <unistd.h>   /* UNIX standard function definitions */

#ifdef _WIN32
# include <windows.h>
# define SERIALPORT_TERMIOS DCB
# define SERIALPORT_HANDLE HANDLE
# define SERIALPORT_FILE_ERROR INVALID_HANDLE_VALUE
# define SERIALPORT_SLEEP(N) Sleep(N)
#else
# include <termios.h>
# define SERIALPORT_TERMIOS struct termios
# define SERIALPORT_HANDLE int
# define SERIALPORT_FILE_ERROR -1
# define SERIALPORT_SLEEP(N) usleep(N*1e3)
#endif

/**
    Open serial port for communication.
    @param serialport name of the port to be opened (eg /dev/tty.usbserial)
    @param baud baud rate (integer)
    @param oldsettings pointer to a struct in which old comm settings should be
    stored, if desired (else NULL if not desired)

    @return valid file descriptor, or -1 on error.
*/
SERIALPORT_HANDLE serialport_init(const char* serialport, int baud, SERIALPORT_TERMIOS *oldsettings);

int serialport_writebyte(SERIALPORT_HANDLE fd, uint8_t b);

/*
    Read a byte from the serial connection.
    @return 0 on success
*/
int serialport_readbyte(SERIALPORT_HANDLE fd, char *byte);

int serialport_write(SERIALPORT_HANDLE fd, const char* str);

int serialport_read_until(SERIALPORT_HANDLE fd, char* buf, char until);

int serialport_restoresettings(SERIALPORT_HANDLE fd, SERIALPORT_TERMIOS *oldsettings);

/**
    Close a success-opened connection, return 0 on success.
*/
int serialport_close(SERIALPORT_HANDLE fd);

#endif
Logged

Manchester (England England)
Offline Offline
Brattain Member
*****
Karma: 626
Posts: 34132
Solder is electric glue
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
The port names come up the same every time, for a given board type.
Yes they will do but the names you use in your code must be the names that come up in the PC driver because that is what you code is trying to use.
Logged

Global Moderator
Dallas
Offline Offline
Shannon Member
*****
Karma: 207
Posts: 12904
View Profile
WWW
 Bigger Bigger  Smaller Smaller  Reset Reset


Post the call to serialport_init.
Logged

Pages: [1] 2   Go Up
Jump to: