Embedded Freaks..

November 3, 2008

Simple Hayes AT Command Parser

Filed under: java — Tags: — kunilkuda @ 1:33 pm

After a very long time, I finally have time to implement this idea: AT command parser. My first thought about AT command was how to separate the data from the OK/ERROR, and how to differentiate the URC from AT command.

So, here’s my idea (to be specific, the interface for the AT parser):


/**
* Send AT command to the serial port
*
* @param atCmd The AT command (ended with r or )
*/
void sendAtCmd(String atCmd) throws IOException;

/**
* Get the parser status
*
* @return true if parsing is done, false otherwise
*/
boolean isParsingDone();

/**
* Get the AT command's data
*
* @return Array of string consist of the AT command data
*/
String[] getAtCmdData();

/**
* Get the status of the executed AT command
*
* @return AtCmdStatus
*/
AtCmdStatus getAtCmdStatus();

The usage of the AT command parser will be something like this:


try {

    atParser.sendAtCmd("AT+CMGL=4r");

    // Wait until the parsing is done
    while (!atParser.isParsingDone()) {
        // Sleep for 1-s
        Thread.sleep(10);
    }

    if (atParser.getAtCmdStatus() == AtCmdStatus.OK) {
        for (String atCmdData : atParser.getAtCmdData()) {
            System.out.println("Data :" + atCmdData);
    }
    } else {
        System.out.println("AT Command Error");
    }
} catch (InterruptedException ex) {
} catch (IOException ex) {
    System.err.println(ex.getMessage());
    System.exit(1);
}

So, here’s the source code (along with the tester code):


/*
 * Copyright (c) 2008, Daniel Widyanto
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 *     * Neither the name of the  nor the
 *       names of its contributors may be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Daniel Widyanto ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL Daniel Widyanto BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package com.kunilkuda.sms;

import gnu.io.SerialPortEvent;
import gnu.io.SerialPortEventListener;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.TooManyListenersException;

/**
 * The simplest AT command parser
 *
 * @author kunil
 */
public class SimpleAtParser implements SerialPortEventListener {

    /**
     * Serial I/O
     */
    private OutputStream outStream;
    private LineNumberReader inStreamReader;

    /**
     * AT parser phases: IDLE or parsing AT command's data
     */
    private enum ParserPhase {

        IDLE, DATA
    }
    private ParserPhase currPhase;
    /**
     * The AT command's data
     */
    ArrayList atCmdData;

    /**
     * The AT command's status: OK or ERROR
     */
    public enum AtCmdStatus {

        OK, ERROR
    }
    private AtCmdStatus currStatus;
    /**
     * Flag to indicate whether the parsing is done or not
     */
    private boolean doneFlag;

    public SimpleAtParser(InputStream inStream, OutputStream outStream) {
        inStreamReader =
                new LineNumberReader(new InputStreamReader(inStream));
        this.outStream = outStream;

        // Initialize the parser phase
        currPhase = ParserPhase.IDLE;
    }

    /**
     * Send AT command to the serial port
     *
     * @param atCmd The AT command (ended with r or )
     */
    public void sendAtCmd(String atCmd) throws IOException {
        outStream.write(atCmd.getBytes());
        doneFlag = false;
    }

    /**
     * Get the AT command's data
     *
     * @return Array of string consist of the AT command data
     */
    public String[] getAtCmdData() {
        ArrayList cmdData = this.atCmdData;

        // Clear the data for next instruction
        this.atCmdData = null;

        return (String[]) cmdData.toArray(new String[0]);
    }

    /**
     * Get the status of the executed AT command
     *
     * @return AtCmdStatus
     */
    public AtCmdStatus getAtCmdStatus() {
        return currStatus;
    }

    /**
     * Get the parser status
     *
     * @return true if parsing is done, false otherwise
     */
    public boolean isParsingDone() {
        return doneFlag;
    }

    public void serialEvent(SerialPortEvent event) {
        switch (event.getEventType()) {
            case SerialPortEvent.DATA_AVAILABLE:
                readLine();
                break;
            default:
                System.out.println("New serial event received and ignored");
        }
    }

    private void readLine() {
        try {
            while (inStreamReader.ready()) {
                String newline = inStreamReader.readLine();

                // Skip empty lines
                if (newline.equals("")) {
                    continue;
                }

                if (currPhase == ParserPhase.IDLE) {
                    if (newline.startsWith("AT")) {
                        currPhase = ParserPhase.DATA;
                    } else {
                        handleUrc(newline);
                    }
                } else if (currPhase == ParserPhase.DATA) {
                    if (newline.startsWith("OK")) {
                        currPhase = ParserPhase.IDLE;
                        currStatus = AtCmdStatus.OK;

                        doneFlag = true;
                    } else if ((newline.startsWith("ERROR")) ||
                            (newline.startsWith("+CME ERROR")) ||
                            (newline.startsWith("+CMS ERROR"))) {
                        currPhase = ParserPhase.IDLE;
                        currStatus = AtCmdStatus.ERROR;

                        doneFlag = true;
                    } else {
                        if (atCmdData == null) {
                            atCmdData = new ArrayList();
                        }
                        atCmdData.add(newline);
                    }
                }

            //System.out.println("Recv :" + newline);
            //System.out.println("currPhase :" + currPhase);
            }
        } catch (IOException ex) {
        }
    }

    /**
     * Handle the URC from the modem
     *
     * @param urc
     */
    protected void handleUrc(String urc) {
        System.out.println("Received URC :" + urc);
    }

    /**
     * This is the embedded static class to test this function
     *
     */
    public static class AtParserTester {

        private static final String serialPort = "/dev/ttyACM0";

        public static void main(String[] args) {
            SimpleAtParser atParser = null;
            SerialHelper serialHelper = new SerialHelper();

            try {
                serialHelper.connect(AtParserTester.serialPort);

                InputStream inStream = serialHelper.getSerialInputStream();
                OutputStream outStream = serialHelper.getSerialOutputStream();

                atParser = new SimpleAtParser(inStream, outStream);
                serialHelper.addDataAvailableListener(atParser);
            } catch (IOException ex) {
                System.err.println(ex.getMessage());
                System.exit(1);
            } catch (TooManyListenersException ex) {
                System.err.println(ex.getMessage());
                System.exit(1);
            }

            if (atParser != null) {
                try {
                    atParser.sendAtCmd("AT+CMGL=4r");

                    // Wait until the parsing is done
                    while (!atParser.isParsingDone()) {
                        // Sleep for 1-s
                        Thread.sleep(10);
                    }

                    if (atParser.getAtCmdStatus() == AtCmdStatus.OK) {
                        for (String atCmdData : atParser.getAtCmdData()) {
                            System.out.println("Data :" + atCmdData);
                        }
                    } else {
                        System.out.println("AT Command Error");
                    }

                } catch (InterruptedException ex) {
                } catch (IOException ex) {
                    System.err.println(ex.getMessage());
                    System.exit(1);
                }
            }

            serialHelper.disconnect();
        }
    }
}

Advertisements

3 Comments »

  1. What is SerialHelper in Main method?

    Comment by Raj — February 16, 2010 @ 5:26 pm

  2. Hello,
    the software to us AT command works on serial port arm 9 and it is based on rxtx ? if yes could you please tell me if the txrx lib for arm9 are available and where I can fine it?
    Thank you in advance for any suggestion!!
    Paolo

    Comment by Paolo — February 20, 2011 @ 5:58 pm

  3. Hi Paolo,

    Sorry, but I don’t play around for RXTX lib nowadays. I think you should be able to cross compile it, but you’ll need JVM that works for ARM architecture (which I don’t think it’ll be free).

    Regards,
    -daniel

    Comment by kunilkuda — February 20, 2011 @ 11:02 pm


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: