An apology: The software is not yet in a state where I feel comfortable about publishing it as open source. So for now, I’m only going to give some explanations about its structure, but not the source code itself.
Alarmino’s software is divided into blocks (implemented as C++ classes) that take care of the following aspects:
- Communicating with the alarm system – emulating a phone line and a central-station (handling the Contact-ID protocol)
- Communicating with the GSM modem
- User interface – There are two user interfaces:
- A terminal-based UI – A shell-like UI. The user gets to this UI by connecting a PC to Alarmino with a USB cable, and using a terminal-emulator application (I’m using PuTTY). This UI gives complete control over Alarmino, and most commands are only available through it
- An SMS-based UI – Some commands can be issued by sending an SMS with the commands to Alarmino, and receiving a response via a reply SMS. This UI is password-protected (every SMS must start with a password). It is (at least in the current version) very limited
- Event logging – Events are stored in a 16-entries cyclic log in EEPROM. When a new event is logged, it replaces the oldest event in the log. To make this simple, and since EEPROM space is scarce, events are not stored as text messages, but rather as structured blocks of data. Each event is stored with its time-stamp (date and time).
Cooperative Multitasking and Response Time
One of the most dominant aspects of Alarmino’s software, and certainly the one thing that had the greatest influence on its architecture was the need to be able to do real-time multitasking. First I want to explain how multitasking can be achieved in a small system like Arduino.
Note to experienced developers – This section is sort of a general introduction to cooperative multitasking and how it affects response times of microcontroller-based systems. If you’re already familiar with these concepts, simply skip this section.
So, you’re still with me, ehh? I appreciate it.
The problem with blocking I/O
Let’s start with a simple example of a microcontroller-based system. Our system is not totally unlike Alarmino. It is connected to two devices: an alarm system and a GSM modem. (I know; Now you think this is exactly like Alarmino. But it’s by pure coincidence. Honestly!).
Anyway, the alarm system has some strict timing requirements: It can “pick-up” the phone at any time, and must “hear” a dial-tone immediately, or it will deem the phone line defective. Then it communicates using the Contact-ID protocol that has strict timing windows of its own. For example, a kiss-off tone must be received by the alarm within a certain time window after a message transmission. Otherwise, the alarm thinks that the message was lost and retransmits it.
The GSM modem, on the other hand, has operations that can take a long time. Communicating with the modem is done over a serial port with AT Commands (http://en.wikipedia.org/wiki/AT_Commands). For every command we send the modem, we receive a response that indicates whether the command was executed successfully or not. Sometimes we send a command and the modem fail to receive it. We detect this by realizing that too much time (e.g. 1 second) has passed since we sent the command to the modem, and we still didn’t receive a response. In this case we usually retransmit the command. The retransmission can also fail, and so on. After a few retransmissions we probably realize that something is totally messed up with our modem, reset it, and retry all over again.
A straightforward implementation of this would be a function that takes the AT command, sends it to the modem and waits for a response. Waiting is done in a tight-loop that simply checks if a response has arrived from the modem. If it waits more than a second without getting a response, it re-transmits the command. This can be repeated up to 3 times. If three retransmissions occur, it resets the modem and start all over again. All this can take a very long time, and within all that time, the function never returns to its caller.
I think that by now you start to see the problem: If the alarm suddenly needs our attention, but we’re busy talking to the modem, and ignore its signals for too long, the alarm is not going to be happy… (in reality it will probably declare a phone-line malfunctioning, because it doesn’t get the expected responses to its signals). We need to find a way to attend both to the modem and the alarm at the same time. The solution is called cooperative multitasking.
The concept of cooperative multitasking is really simple. The system is build of one controller and multiple workers (in an Arduino system, typically the controller and each of the workers would be implemented in a separate C++ class). Each of the worker classes is responsible for a certain aspect of the system. In our example, there would be one worker responsible for the communication with the alarm, and another for the communication with the modem. Each worker class has a main entry-point. This is a method that is called whenever we want the class to “do its stuff”. Let’s call this method exec(). All the worker classes guarantee that their exec() method never blocks for a long period of time. They always do something simple and return.
The controller simply calls the exec() methods of all the workers one after the other in a loop. This arrangement guarantees that if an external event occurs and requires prompt handling, the responsible worker always gets a chance to handle it on time.
By now you’re probably saying “this is too good to be true. Where’s the catch?”. Well, the catch is in the complexity added to the workers. Let’s go back to our example. In this example, the code responsible for the modem sent an AT-command to the modem, and then waited for a response. The wait might be long, because there’s no guarantee that the modem will answer within a certain amount of time. With our shiny new technique, this long wait for the modem response is a big no-no. So how do we handle this? Well, the trick is that each worker employs a state-machine, where it stores its state. In our example, when the modem handler sends an AT-command to the modem, it stores the current time and the state I’m-waiting-for-a-response-for-an-AT-command-I-just-sent-to-the-modem in its state, and returns. Then, when it is called again, it looks at the state and knows that it should look for a response from the modem. If a response is received, it handles it. If not it can look whether too much time has passed since the command was sent. If indeed too much time has passed it can retransmit the command. If not, it does nothing. In any event it returns pretty quickly to the controller.
Handling all the possible states in the workers becomes a large part of developing an application like Alarmino. Still, it’s the simplest way to guarantee quick response to external events in a system that handles multiple devices.
Alarmino’s Implementation of Cooperative Multitasking
All the software of Alarmino is build around the cooperative-multitasking model. The code never blocks or calls Arduino’s delay() function.
All the workers in Alarmino are singletons (simply in the sense that there’s one instance of each one of them). Each worker implements a setup() and a loop() methods, and maintain its own state information. Each worker’s setup() method is called once after boot from the main setup() function. The controller is simply the main loop() method, which calls the loop() methods of all the workers one after the other in an infinite loop.
Software for Handling the GSM Modem
Probably the most complicated part of Alarmino’s code is the GSM modem handler. Iteadstudio, the maker of the IComSat GSM modem, provide on their website some software to go with their board. On Google Code there’s a newer version of the same library, and I suggest that if you’re going to use this library, you’ll download the latest version from there.
At first I thought I could utilize this library, which would save me quite a lot of coding. However when the time came to actually use it, I realized it doesn’t fit my needs, and I ended up coding everything from scratch. The main reason for this was that this library occasionally blocks for long periods of time, and I needed this code to play nicely in my multitasking scheme.
Decoupling Event Producers and Consumers in Multitasking Systems
Many times a complex system can be simplified by decoupling some of its components. For example, in Alarmino, an alarm event can generate a message that has to be sent via an SMS message. A straightforward solution is to have the code that handles the alarm event call a function that is responsible to sending SMS messages. However since we’re dealing with a multitasking system, it’s very possible that exactly at this time, the system is busy sending a previous SMS message. Handling these situations in the modem’s code can be such a huge mess, that even the bravest programmer will get so stressed, that it’ll negatively affect his marital life in ways I won’t discuss here.
To prevent this (marital life mess and stuff) from happening, we simply decouple the alarm code (event producer) from the modem code (event consumer). This is done by means of a job-queue. Whenever an alarm event is generated, the alarm handler code simply places it in a job-queue and forgets about it. The modem code, on its part, checks the queue for new jobs whenever it is idle. If a new job is found, the modem handler fetch the job from the queue and handles it. During the time it handles the job (which might be long) more jobs can be added to the queue, but this doesn’t bother anyone, since the queue will only be checked once the current task is finished.
Experience developers might think that building a queue is a tad complicated, because a typical queue requires, in addition to the queue itself, some locking mechanisms. However this is not the case here since Arduino has no multithreading and no context-switching. Thus, implementing a queue in Arduino is actually trivial.
When I started using Arduino, I used its IDE which is part of the “Arduino Software”. However I quickly realized that it’s a big step backwards for anyone that has done professional software development before. Things you would take for granted like a powerful editor, code refactoring, code verification as you type, code structure analysis tools (e.g. call hierarchy, etc), and many many more, were simply missing. After a quick search for an alternative I found the Arduino Eclipse plugin (http://www.baeyens.it/eclipse/). I moved to that environment and never looked back. I strongly recommend to anyone developing a larger-than-trivial project with Arduino to use this Eclipse-based environment. This is especially true if you are familiar with Eclipse (because otherwise, I admit, there’s quite a steep learning curve to Eclipse itself).