Telit Cinterion IoT Developer Community
Create a better ATC manager
Tutorial, March 25, 2016 - 8:51am, 8647 views
The ATCommand class API is primitive, if one ***** to share the ATCommand instance between MIDlets, and expect to handle specific URC inside respective MIDlet, there are some housekeeping tasks to do. Possible tasks are
1. Use Singleton pattern to maintain one shared instance instance between Java class modules
2. Use Observer pattern to create call-back function so thate respective MIDlet can process the concerning URC locally.
For the two goals, I plan to use an AtcManager manager class to wrap most of existing ATCommand class API, and providing the Singleton and Observer call-back function.
To create an Observer, we can create an UrcListener Interface that hosts the URC handler for event URC processing.
/** * Interface for specific URC Handler * * @author Antony Shen <antony.shen@gemalto.com> * */ public interface UrcListener { public boolean handleUrc(String urc); }
With this interface being ready, we can prepare the housekeeping API, the AtcManager:
/** * An AT command manager class, providing single AT channel and URC handler dispatcher. * * @author Antony Shen <antony.shen@gemalto.com> */ public class AtcManager { private static AtcManager instance = null; private ATCommand atc = null; private ATCommandListener urcListener = null; private Hashtable urcTbl = null; // Constructor private AtcManager() { } // Method return the proper instance of the singleton class public static AtcManager getAtInstance() { if (instance == null) { // if instance doesn't exist - create one instance = new AtcManager(); // initialize the instance instance.init(); } // returns the proper instance return instance; } // Initiates AtcManager object, creates ATCommand instance and // ATCommandLitener private void init() { try { // Collection of user-registable URC Handlers urcTbl = new Hashtable(); // Create an instance of ATCommand atc = new ATCommand(false); // Create a listener for dispatching URC urcListener = new ATCommandListener() { public void RINGChanged(boolean SignalState) { } public void DSRChanged(boolean SignalState) { } public void DCDChanged(boolean SignalState) { } public void CONNChanged(boolean SignalState) { } public void ATEvent(String urc) { int bIdx = urc.indexOf('+'); int eIdx = urc.indexOf(':'); System.out.println(urc); if (bIdx == -1) bIdx = urc.indexOf('\n') + 1; if (bIdx == -1) bIdx = 0; if (eIdx == -1) eIdx = urc.length(); String key = urc.substring(bIdx, eIdx); UrcListener handler = (UrcListener) urcTbl.get(key); if (handler != null) handler.handleUrc(urc); } }; // Adding the listener to the ATCommand atc.addListener(urcListener); } catch (ATCommandFailedException e) { e.printStackTrace(); } } // Allow user to register a handler for specific URC like RING or +CREG public boolean addUrcListener(String urc, UrcListener handler) { boolean rc = false; if ((instance != null) && !urcTbl.containsKey(urc)) { urcTbl.put(urc, handler); rc = true; } return rc; } // Allow user to de-register a handler public boolean removeUrcListener(String urc) { boolean rc = false; if ((instance != null) && urcTbl.containsKey(urc)) { urcTbl.remove(urc); rc = true; } return rc; } // resource clean-up public void destroy() throws IOException, IllegalStateException { if (instance != null) { urcTbl.clear(); atc.removeListener(urcListener); atc.release(); atc = null; urcTbl = null; instance = null; } } // cancel a running ATC /** * @throws ATCommandFailedException * @throws IllegalStateException */ public void cancelCommand() throws ATCommandFailedException, IllegalStateException { if (instance != null) { atc.cancelCommand(); } } // Method sends the command and prints the module's response public String send(String command) { String response = ""; if (instance != null) { try { // Send the command and save it's response response = atc.send(command + "\r"); // print the module's response System.out.println(response); } catch (ATCommandFailedException e) { // This exception must be caught System.out.println(e); } } return response; } public void send(String command, ATCommandResponseListener listener) throws ATCommandFailedException, IllegalStateException, IllegalArgumentException { if (instance != null) atc.send(command, listener); } }
With this AtcManager API, for tasks like monitoring +CREG status or timezone information update, one can handle it locally in relevant Java class. Here I rewrite the AutoRtcUpdate project to use this new feature:
/** * An demo shows how to use AT+CTZR for wall clock time update. * * @author Antony Shen <antony.shen@gemalto.com> */ public class AutoRtcUpdate extends MIDlet implements UrcListener { private AtcManager atc = null; private String pincode = null; public AutoRtcUpdate() { System.out.println("\nConstructor"); atc = AtcManager.getAtInstance(); atc.addUrcListener("+NITZINFO", this); try { pincode = getAppProperty("AutoRtcUpdate-SIMPIN"); } catch (NullPointerException e) { System.out.println("There is no PIN CODE."); } } public void startApp() throws MIDletStateChangeException { System.out.println("\nstartApp\n"); // This command enables the extended error codes atc.send("AT+CMEE=2\r"); atc.send("AT+COPS=2\r"); atc.send("AT+CTZR=1\r"); // Checks if pin is needed then enters it and checks // whether it was acknowledged if (enterSimPin()) { atc.send("AT+COPS=0\r"); } } public void pauseApp() { System.out.println("\npauseApp()"); } public void destroyApp(boolean cond) { System.out.println("\ndestroyApp(" + cond + ")"); // Release ATCommand instance try { atc.destroy(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } notifyDestroyed(); } // Method enters the SIM PIN (if needed) using AT Command and check if it // was entered correctly private boolean enterSimPin() { boolean rc = false; String response = atc.send("AT+CPIN?\r"); if (response.indexOf("SIM PIN") >= 0) { System.out.println("Pin ***** to be entered"); String localResponse = ""; if (pincode != null) { System.out.println("Entering SIM PIN\n"); localResponse = atc.send("AT+CPIN=\"" + pincode + "\"\r"); if (localResponse.indexOf("OK") >= 0) { System.out.println("Pin was accepted"); rc = true; } else { System.out.println("Pin wasn't accepted"); } } else { System.out .println("Please specify the PIN code of this SIM inside the .jad file."); } } else if (response.indexOf("READY") >= 0) { System.out.println("Pin already entered."); rc = true; } else if (response.indexOf("SIM not inserted") >= 0) { System.out.println("Insert SIM card."); } return rc; } public boolean handleUrc(String urc) { boolean rc = false; String timestamp = null; if (urc.indexOf("+NITZINFO:") >= 0) { timestamp = urc.substring(urc.indexOf(',') + 1); atc.send("AT+CCLK=" + timestamp + "\r"); System.out.println("Current time is " + timestamp); } atc.send("AT+CCLK?\r"); destroyApp(true); return rc; } }
Compare to the original version, now I can handle the +NITZINFO URC locally inside AutoRtcUpdate class, and handle the URC interested in only. It's much cleaner than previous one.
Any idea for improvement is welcome. If you have better code to contribute, please don't hesitate to post it here.
Hi Antony
Really nice article!!!!
Regards
ALopez
Somewhere over the rainbow!!! Looking for the Oz Land!!!
Thanks!
Best Regards,
Antony Shen
This example is diffrent aproach, not better then Anthon's code.
I have recently created AT command handler for multithread MIDlets. If you issue AT commands from multiple threads you need to watch out that you don't issue commands at the same time. Also if you issue AT command while you issued AT command to send SMS, you could be adding that AT command to SMS text.
Because of that I created AT handler, which queues AT commands and you are able to put multiple commands in one queue spot, this comes handy when sending SMS. This is not 100% finished and tested so there can still be bugs, if you find any don’t hesitate to tell me.
AT command handler is part of bigger project, that I use as personal library for Gemalto modules. This library prevents to frequent module reststarts, send mails, enables debugging over GPRS, asynchrouns HTTP client any many other things, because of which I had to remove some code that you would need rest of the classes. Because of that I also coulden't post code as a project.
First we have class in which we store at command, it’s called ATCommandData: http://pastebin.com/sxd6xv9c
Mode variable is a little bit obsolete. In the older version was used to block other AT commands, when we were sending SMS.
Then we have singelton ATCommandHandler http://pastebin.com/PD14qDCa. He uses my version of FIFOArray: http://pastebin.com/ZbzxGfFm, which isn’t 100% FIFO because was a little bit modified because of my ***** in some MIDlets. If it detects that there is for some reason more than 20 AT commands waiting in the line it won’t accept any new ones. You could also reset module inside that IF, if that happens.
But its main task is to add new commands in FIFO line, and return current command in line to ATCommandIssuer (couldn’t think a better name) http://pastebin.com/svVu448Y. ATCommandIssuer is a class that contains thread. Each thread has its own ID variable. With this ID it checks with ATCommandHandler when it’s its turn to issues AT command. When finished it will return the result trough callback to its parent thread. For this we also need callback: http://pastebin.com/s3QyD0Wi.
And here is example how you can use it: http://pastebin.com/Xr789Yc3. It adds some additional code, for each AT call, but it could be worth it. If you have any question ask and I will try to improve the post. If you think that it would be better to post code here instead on pastebin also let me know.
Best regards,
Jure
Hi,
I really like both these pieces of work, because AT command concurrency etc is something I struggled with too.
On the subject of SMSes - I created one AT Command singleton for "normal" AT Commands and a second ATCommandSms singleton just for SMSes.
Best regards
Simon