Search in this blog

Saturday, July 4, 2015

Adafruitt FONA MiniGSM C Library for Raspberry Pi and similars

Adafruit FONA MiniGSM is a great product, you can learn more here, they wrote a library for Arduino which is found here.

If you buy this product and use it on Arduino, you will have not problems but what about if you need to do some complex needing a Raspberry Pi (RPi) or similar computer? Adafruit provide you a post for connecting FONA to a RPi here but has nothing for programming something.

When I bought FONA (like a year ago) I was in this situation, I wrote a small library in C which supports the most basic operations like send command and check command reply, I wrote a function to send SMS which basically use the previous functions, if you need more functions is not hard to extend the library.

The library is written in C and tested in Linux, it should work with most Linux distributions and with most SIMXXX chips working with AT commands.

I put a Main function as an example for using this library.

Note: This assume you have connected FONA to the port "/dev/ttyUSB0", be sure to change it if is not the case.

/**
 * source: http://stackoverflow.com/a/6947758/3211175
 *
 * Author: Alexis Hernandez
 *
 * gcc adafruit_fona_rpi.c -o fona -std=c99
**/

#include <errno.h>
#include <termios.h>
#include <unistd.h>
#include <stdio.h>

#include <signal.h>
#include <fcntl.h>
#include <features.h>
#include <string.h>

#ifndef CRTSCTS
#  ifdef CNEW_RTSCTS
#    define CRTSCTS CNEW_RTSCTS
#  else
#    define CRTSCTS 0x80000000
#  endif /* CNEW_RTSCTS */
#endif /* !CRTSCTS */

int set_interface_attribs(int fd, int speed, int parity)    {
        struct termios tty;
        memset(&tty, 0, sizeof tty);
        if ( tcgetattr(fd, &tty) !=  0 )    {
                printf("error %d from tcgetattr\n", errno);
                return -1;
        }

        cfsetospeed (&tty, speed);
        cfsetispeed (&tty, speed);

        tty.c_cflag = (tty.c_cflag & ~CSIZE) | CS8;     // 8-bit chars
        // disable IGNBRK for mismatched speed tests; otherwise receive break
        // as \000 chars
        tty.c_iflag &= ~IGNBRK;         // disable break processing
        tty.c_lflag = 0;                // no signaling chars, no echo,
                                        // no canonical processing
        tty.c_oflag = 0;                // no remapping, no delays
        tty.c_cc[VMIN]  = 0;            // read doesn't block
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        tty.c_iflag &= ~(IXON | IXOFF | IXANY); // shut off xon/xoff ctrl

        tty.c_cflag |= (CLOCAL | CREAD);// ignore modem controls,
                                        // enable reading
        tty.c_cflag &= ~(PARENB | PARODD);      // shut off parity
        tty.c_cflag |= parity;
        tty.c_cflag &= ~CSTOPB;
        tty.c_cflag &= ~CRTSCTS;

        if ( tcsetattr(fd, TCSANOW, &tty) != 0 )    {
                printf("error %d from tcsetattr", errno);
                return -1;
        }
        return 0;
}

void set_blocking(int fd, int should_block)    {
        struct termios tty;
        memset (&tty, 0, sizeof tty);
        if ( tcgetattr(fd, &tty) != 0 )    {
                printf("error %d from tggetattr", errno);
                return;
        }

        tty.c_cc[VMIN]  = should_block ? 1 : 0;
        tty.c_cc[VTIME] = 5;            // 0.5 seconds read timeout

        if ( tcsetattr(fd, TCSANOW, &tty) != 0 )    {
                printf("error %d setting term attributes", errno);
        }
}


int fd;    // file descriptor for connection
char buf [100];    // buffer for the reply

// init connection
int startConnection(char *portname)    {

    fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
    if (fd < 0)    {
        printf("error %d opening %s: %s", errno, portname, strerror (errno));
        return 0;
    }

    set_interface_attribs(fd, B115200, 0);  // set speed to 115,200 bps, 8n1 (no parity)
    set_blocking(fd, 0);                // set no blocking
   
    //
    send("AT");
    usleep(100000);
   
    // turn off Echo!j
    send("ATE0");
    usleep(100000);
   
    return    sendCheckReply("ATE0", "OK");
}

void endConnection()    {
    close(fd);
}


// send a command to the serial port, read the answer but do nothing
int send(char *cmd)    {
//    printf("send: %s\n", cmd);
   
    int cmd_len = strlen(cmd);
    write( fd, cmd, cmd_len );                // send cmd
    write( fd, "\n", 1 );

    usleep ( (cmd_len + 10 + 25) * 300 );        // sleep enough to transmit the cmd plus
                                        // receive 25:  approx 100 uS per char
                                       
    int read_len = read(fd, buf, sizeof buf);  // read up to 100 characters if ready to read
   
    /*
    // print result
    printf(" read: %d bytes\n", read_len);
    for (int i = 0; i < read_len; i++)    printf( " %c", buf[i] );
    printf("\n");
    for (int i = 0; i < read_len; i++)    printf( " %d", buf[i] );
    printf("\n");
    */
   
    return 1;
}


/**
 * send cmd and checks device's reply match exact message
 * cmd and reply should ends with "cr+lf"
**/
int sendCheckReply(char *cmd, char *reply)    {

//    printf("sendCheckReply: %s\n", cmd);
    int cmd_len = strlen(cmd);
    write( fd, cmd, cmd_len );                // send cmd
    write( fd, "\n", 1 );

    usleep ( (cmd_len + 10 + 25) * 300 );        // sleep enough to transmit the cmd plus
                                        // receive 25:  approx 100 uS per char
                                       
    int read_len = read(fd, buf, sizeof buf);  // read up to 100 characters if ready to read
    // should return some like "\r\nxx\r\n", then avoid first 2 and last 2 bytes
   
    // print result
    /*
    printf(" read: %d bytes\n", read_len);
    for (int i = 0; i < read_len; i++)    printf( " %c", buf[i] );
    printf("\n");
    for (int i = 0; i < read_len; i++)    printf( " %d", buf[i] );
    printf("\n");
    */
   
    // strcmp
    int reply_idx = 0;
    int reply_len = strlen(reply);
    int read_idx = 2;
    read_len -= 2;
   
    for (; reply_idx < reply_len && read_idx < read_len && reply[reply_idx] == buf[read_idx]; reply_idx++, read_idx++);
   
    return    reply_idx == reply_len && read_idx == read_len ? 1 : 0;
}



/**
 * send cmd and checks device's reply is a prefix of response
**/
int sendCheckReplyPrefix(char *cmd, char *reply, int extraSleep)    {

    printf("sendCheckReplyPrefix: %s\n", cmd);
    int cmd_len = strlen(cmd);
    write( fd, cmd, cmd_len );                // send cmd
    write( fd, "\n", 1 );

    usleep ( (cmd_len + 10 + 25) * 300 );        // sleep enough to transmit the cmd plus
                                        // receive 25:  approx 100 uS per char
              
    if    ( extraSleep )
        usleep(extraSleep);
                       
    int read_len = read(fd, buf, sizeof buf);  // read up to 100 characters if ready to read
    // should return some like "\r\nxx\r\n", then avoid first 2 and last 2 bytes
   
    // print result
    printf(" read: %d bytes\n", read_len);
    for (int i = 0; i < read_len; i++)    printf( " %c", buf[i] );
    printf("\n");
    for (int i = 0; i < read_len; i++)    printf( " %d", buf[i] );
    printf("\n");
   
    // strcmp
    int reply_idx = 0;
    int reply_len = strlen(reply);
    int read_idx = 2;
   
    for (; reply_idx < reply_len && read_idx < read_len && reply[reply_idx] == buf[read_idx]; reply_idx++, read_idx++);
   
    return    reply_idx == reply_len ? 1 : 0;
}


// send smmmsg to smsaddr
int sendSMS(char *smsaddr, char *smsmsg) {
    if ( !sendCheckReply("AT+CMGF=1", "OK") )    {
        printf("fail: AT+CMGF=1\n");
        return 0;
    }
   
    // build send command like = AT+CMGS="nnnn"
    char sendcmd[30] = "AT+CMGS=\"";
    strncpy( sendcmd+9, smsaddr, 30-9-2); // 9 bytes beginning, 2 bytes for close quote + null
    sendcmd[ strlen(sendcmd) ] = '\"';
   
    printf("trying to send: %s\n", sendcmd);
   
    if ( !sendCheckReplyPrefix( &sendcmd[0], "> ", 0 ) )    {
        printf("fail: AT+CMGS=num\n");
        return 0;
    }
   
    // build msg command, append new line + ctrl+z
    char msgcmd [200];
    int msglen = strlen(smsmsg);
    strncpy(msgcmd, smsmsg, msglen);
    msgcmd[msglen] = '\n';
    msgcmd[msglen + 1] = 0x1A;
   
    return    sendCheckReplyPrefix( msgcmd, "+CMGS", 1000000 * 3 );
}








// main
int main()    {

    char *portname = "/dev/ttyUSB0";
   
    int attemps = 10;
    while    ( attemps-- &&    !startConnection(portname) )    usleep(1000);
   
    if    ( attemps <= 0 )    {
        printf( "ERROR: Can not start connection\n" );
        return    0;
    }
   
    printf("Connection started!\n");
   
    char *num = "0123456789";
    char *msg = "message here";
   
    if    ( sendSMS(num, msg) )    {
        printf("SMS sent\n");
    }    else    {
        printf("SMS can not be send\n");
    }
   
   
    printf("end\n");
   
    endConnection();
    return    0;
}






I hope this can be useful for you, be sure to ask any question.

Thanks for reading and see you in next post.

No comments:

Post a Comment

Leave me a comment