As my first collaboration to the .NET Gadgeteer project, I am writing the drivers for the Seeed Studio Cellular Radio Module. This board ships with a SIMCOM SIM900 cellular engine. It required a lot of patience and work, so I am going to share some advice if you ever find yourself in a similar project. I will make the driver available for download in a near future.
Understand the driver building template
Microsoft Research really made our lives easier in this step. Their builder template (available here) is really self explanatory. I also recommend reading the builder guide to make sure your code adhere to the Gadgeteer specifications.
Choose wisely the programming paradigm
One the first steps I took when I started to implement the drivers was look online for similar projects. I found the drivers for a SIM900 GSM/GPRS shield for Arduino and started to implement my drivers in a similar fashion, only to discover that this wasn’t the best way to implement the Gadgeteer drivers.
Arduino code runs in a while(true) loop indefinitely and these drivers use this assumption. Gadgeteer code, however, is event-driven. In fact, because of that you aren’t even supposed to use while(true) loops in your application code (see why in this post by Kerry Hammil).
After learning this the hard way, I had to restart from scratch and indeed the code runs much better.
Read the AT Commands Documentation thoroughly
This is a really boring step, but it is crucial for the success of the project. The documentation for the SIM900 commands is almost 200 pages long and every bit of it ended up being relevant. Understand well the capabilities of the module, especially the syntax of the commands. Whilst browsing online forums I found loads of people having trouble with operations like making a voice call just because they were missing a semicolon in the end of the command.
Be prepared to deal with a very loose response format
One of the biggest challenges I faced was parsing the responses from the module. The documentation states that each response is separated by <CR><LF> characters (carriage return and new line), but these are characters that can also be found elsewhere in messages. Turning on the echo helps.
Find a suitable testing routine
Because the drivers are asynchronous, the API works in the following fashion. The user requests an operation from the module and the drivers send the corresponding command. In a separate thread, the drivers are listening for incoming responses from the module. When it detects that the response for an operation has arrived, they raise the corresponding event. By assigning a handler to that event the user processes that response.
To test this, I would follow the same procedure for every command:
- Implement the method to send the AT command in the driver.
- Call the method in the application
- Check the possible responses from the module using Debug.Print()
- Implement the event in the driver
- Subscribe to the event in the application.
- Raise the event in the driver.
- Make sure the application is seeing the event.
- Implement the code to parse the response in the driver.
- Raise the event in the parsing code.
- Print the response in the application to make sure the communication between driver and application is working.
Make sure the rest of the equipment works properly
When developing embedded systems, there are many variables that might be cause of error. In this case, it could be the main board, the module, the computer, the driver, the application, the SIM card, and so on… Therefore, it is important to narrow down those variables to make sure you are looking for errors in the right place. It took me a while to figure out why my voice calls methods weren’t working until I discovered that the SIM card that I was using didn’t make phone calls…
Also, this is a very power consuming module. I read online that it can draw up to 2A of current, so the USB port in your computer might not be enough, especially if you are also using power demanding modules like the LCD display.
Allow time to receive a response from the module
Sometimes, when you send one command after the other without giving the module enough time to process them, the module doesn’t respond, so make sure to add a few Thread.Sleep() lines to your code, especially if you are using a multi-threaded design like myself.