Internet Connectivity In a Box

Dr. Dobb's Journal January 2003

Grafting connectivity to inexpensive processors easily and inexpensively

By Al Williams

Al is a writer and consultant. He can be contacted at alw@al-williams.com.

Every computer, no matter how small, is a candidate for connecting to the Internet. But small embedded processors face two major hurdles to Internet connectivity. First, handling a TCP/IP stack requires resources that might be scarce on an embedded microcontroller. Second, although the Internet seems to be everywhere, it isn't always on a plant floor, at a pump station in the middle of a pipeline, or other places embedded systems tend to reside.

One way to handle these problems is to simply farm the TCP/IP stack out to another dedicated embedded system. Many vendors are making products to fill this niche. If you have a network connection, you can find a board that connects to Ethernet. If not, you may be interested in using a phone modem to connect. Of course, transient connections with a telephone line bring a whole other set of problems, but nothing that is insoluble.

I've recently been working with an embedded modem from Cermetek (http://www.cermetek.com/) that lets small controllers send/receive e-mail. At first, it might seem that e-mail is not the ideal way to connect to the Internet, but for small, intermittently connected devices, it has its advantages.

Roll Your Own?

Of course, you could roll your own TCP/IP stack. However, consider all that a processor has to do to process TCP. First, it needs to handle incoming traffic in a timely manner. You'll likely need a buffer (and perhaps a fairly large one) to hold data while you are doing other processing. On top of that, a TCP stack has to be prepared to buffer packets that arrive out of order so that the stack can deliver the packets in order.

True, many modern controllers have interrupts and enough memory to do this. However, the low-end processors (like the Microchip PIC, for example) are always starved for memory. On top of that, this demanding processing eats into your resources and the time you need for the actual job at hand (whatever that is). Even if you restrict the TCP window to a single packet (so you don't have to handle many outstanding packets at once), TCP requires a lot of resources.

UDP isn't quite as stressful, but only a handful of Internet protocols use UDP. If you want to write a custom program to receive data, UDP could be an answer. However, if you want to send data via e-mail, FTP, or HTTP, you had better be able to handle TCP.

iModem

Cermetek is known for its embedded modems and its iModem (http://www .imodem.net/). The iModem looks like a grossly oversized IC—it's about 2-inches long, 1 and 1/2-inches wide, and about a 1/2-inch tall. Inside is a complete modem, either a 56K or 2400-baud modem, depending on the model. You also get a complete data access arrangement (DAA) inside the package, so you can simply connect the device to the power and phone lines, and you are ready to go.

In fact, the iModem works like a normal modem if you want to use it that way. It accepts the de facto standard AT commands and operates like a garden variety modem. The difference is that it also accepts special @T commands to send/receive e-mail. Of course, this assumes you have a dial-up ISP that lets you send mail (via SMTP) and receive mail (via POP3).

By itself, this would be useful. An embedded system could generate reports and send them via e-mail. You could potentially send commands to the device's e-mail address (assuming the device periodically checks for mail). However, Cermetek offers an optional service that makes the modem even more useful. For a nominal fee, you can subscribe to its Press4Service ISP (http://press4service.alanet.net/). This provides your e-mail connection, of course. However, it also provides special e-mail addresses your modem can use to transfer files to web servers (via FTP), store files on an ISP-provided web server, send faxes, or even send voice messages. Even though the device sends an e-mail, it can do other Internet tasks using the special e-mail addresses.

Limitations

The iModem sounds great in theory. However, there are a few limitations that aren't apparent at first glance:

In addition, there are a few unusual requirements that aren't limitations, but do necessitate a bit of planning on your part. For one thing, the modem requires you to use a specific baud rate to use the Internet features. This is unlike a conventional modem, which determines your baud rate and adapts. An additional wrinkle is that you must pause a bit between characters when sending to the modem. The documentation suggests 50s and even more time between actual commands.

If you are using a simple microcontroller, the worst part is definitely the response codes. The strings are difficult to differentiate since they seem to be meant for humans to read, not machines. On the other hand, they are all relatively short, so only the lowest powered processors are unable to use the modem.

Sending E-Mail

To send e-mail, you hardly need a microcontroller at all. The modem has a default e-mail message that reports the status of two input pins that are otherwise unused by the modem. Asserting a special SEND pin on the modem causes it to transmit this default message. Of course, you'd initially use a PC or other host to set the modem's ISP information; but once set, it remembers. It isn't hard to imagine using the modem by itself to e-mail you when your vacation home's basement is full of water, for example.

Of course, a host microprocessor can also trigger the iModem to send this canned message. However, it is more interesting to build a custom message that says anything you want. There are some limits. The custom message can't exceed 100 characters and the subject must be 15 characters or less.

Connecting an iModem to a PC running a terminal program lets you set the iModem's parameters. This is an easy job if you have the evaluation board, which consists of a power supply, some LEDs, phone jack, and RS-232-level converter with a DB9 attached. Nothing fancy, but handy nonetheless. Figure 1 is a typical session.

iModem

Depending on your particular situation, you can probably set most of these items once and forget them, so the embedded controller only has to send a few items. You can also query most of the values using a question mark (that is, @TL1? responds with the current ISP user name).

The e-mail body ends with a line that contains a single period. That isn't surprising because that's exactly what the underlying remote SMTP server will expect.

If everything works, great! However, if there are errors, you may see one or more of the following messages:

Of course, you could look at the first letter of each line (everything that starts with an N is an error). This strategy works, assuming Cermetek doesn't add more error messages in the future. Still, if you have the processing horsepower, it is probably best to go ahead and process the entire line.

If you are using the Press4Service ISP, then sending a fax or other type of document is just as simple as sending an e-mail. You simply use a special e-mail address. A web interface lets you set the actual destination for these e-mail aliases.

Receiving E-Mail

Although the iModem can receive e-mail, it isn't the device's strongest point. The @TDG command can retrieve a single e-mail from the server. You simply supply the message number (1 to 99). In practice, you almost always want to read message 1 because, when you delete a message (using the @TDK command), the server renumbers the messages—so the oldest message is always message 1.

Ideally, an e-mail reception session with the iModem looks like Figure 2(a). If you want to delete the message, that requires another phone call; see Figure 2(b). However, I found e-mail reception to be unreliable. Connecting was difficult. Sometimes the lines containing the date, where the message is from, and its subject were corrupt. Also, after a successful connection, the POP3 server would not release, which would cause the next operation to fail with a "POP3 TERMINATION" message.

In the end, I decided the POP3 feature of the modem wasn't quite ready for prime time. You can read mail, but it is a struggle since you have to repeatedly deal with errors and retry the operations. Since it can take up to 15 minutes for the POP3 server to reset, handling just a few incoming e-mails can take a long time.

A Remote Temperature Sensor

To exercise the iModem, I decided to use the Javelin from Parallax (http://www.parallaxinc.com/), maker of the Basic Stamp processor. The Javelin uses a Java-like language, but (like the Basic Stamp) is a self-contained module comprised of a CPU, memory, and power supply.

The Javelin allows Java-like classes and all the benefits that entails. It doesn't provide some Java features (like true multithreading and garbage collection) so as not to compromise the real-time performance of the processor.

The Javelin's development board has an RS-232-level converter and prototyping area. Parallax provides a class that can read a Dallas Semiconductor DS1620 temperature sensor. Combine this with the iModem development board, and you can quickly assemble an online embedded system to, say, report the temperature from a remote location.

The DS1620 requires 5V and ground (on pins 8 and 4, respectively). The Javelin connects to the device using three pins (follow the directions in the Javelin documentation). Since the Javelin is object oriented, you create a DS1620 object to connect to the device:

sensor=new DS1620(CPU.pin4,CPU.pin5, CPU.pin6);

The pin numbers indicate which Javelin pins you've used to connect to the temperature sensor. Once you have created the sensor object, it is easy to get the temperature:

temp=sensor.getTempF();

So, reading the temperature is a simple operation. Listing One checks the temperature every 30 seconds. If the temperature exceeds a preset limit, the program forms an e-mail alert and uses the iModem to send it.

There are several routines that support the iModem:

Using these routines, it is straightforward to create a new e-mail. The actual sending of the e-mail looks like Figure 3.

Other Alternatives

If you want to connect a simple device to the Internet, the iModem is not your only choice (although it is somewhat unique in its approach). For example, NetMedia (http://www.netmedia.com/) makes SitePlayer, an embedded server with an Ethernet interface. SitePlayer lets you create object definitions, then use these objects in HTML files. The SitePlayer transfers objects using a special "object packet" format that your microcontroller sends (or receives, if it wants to read data from the Web).

Another alternative is the HelloDevice made by Sena Technologies (http://www.hellodevice.com/). HelloDevice forms a bridge between an RS-232 device and Ethernet. You can buy these as complete units, boards, or modules. There are a variety of adapters that can convert RS-232, RS-422, or RS-485 to Ethernet.

As low-end microcontrollers get more powerful, the importance of these products fades. Eventually, even the simplest controllers will have networking abilities. Meanwhile, these products let you graft connectivity to inexpensive processors and even existing projects easily.

DDJ

Listing One

import stamp.core.*;
import stamp.peripheral.sensor.temperature.DS1620;

/** Handler for iModem, @version 1.0 -- @author Al Williams  */
public class iModemDDJ {
  final static int SERIAL_RX_PIN = CPU.pin0;
  final static int SERIAL_TX_PIN = CPU.pin1;

  static Uart rxUart = new Uart( Uart.dirReceive, SERIAL_RX_PIN, 
                          Uart.dontInvert, Uart.speed2400, Uart.stop1 );
  static Uart txUart = new Uart( Uart.dirTransmit, SERIAL_TX_PIN, 
                          Uart.dontInvert, Uart.speed2400, Uart.stop1 );
static StringBuffer t=new StringBuffer(81);
static Timer waittimer = new Timer();

public static void sendString(String s) {
   for (int i=0;i<s.length();i++) {
     txUart.sendByte(s.charAt(i));
     CPU.delay(500);     // modem needs delay between characters
     }
   txUart.sendByte('\r');
   waitStatus(5);   // read echoed line
 }
 public static boolean waitOK() {
   return waitStatus(2)=='O';
   }
  public static char waitStatus(int timeout) {
  Timer to=waittimer;
   char cr='\0',c=' ';
   to.mark();
   do {
     if (rxUart.byteAvailable()) {
       c=(char)rxUart.receiveByte();
       System.out.print(c);
       if (c!='\r' && c!='\n' && cr=='\0') cr=c;  // save first character
       }
     } while ((cr=='\0' || c!='\n') && !to.timeoutSec(timeout));
     if (c!='\n') System.out.println("Timeout!");
  return cr;
  }
public static boolean findModem() {
  sendString("");
  sendString("AT");
  if (!waitOK()) {
    System.out.println("Can't find modem");
    return false;
    }
  return true;
  }
  public static void main() {
  // careful... the iModem takes a few seconds to "wake up"
   Timer clock=new Timer();
   Timer level0timer = new Timer();
   DS1620 sensor=null;
   int cnt=-1;
   int temp;
   int i;
   sensor=new DS1620(CPU.pin4,CPU.pin5,CPU.pin6);
   // let's try to see if the modem is awake and clear
   clock.mark();
   out:
    do {
     txUart.sendByte('\r');
     do {
       if (rxUart.byteAvailable() && rxUart.receiveByte()=='\r') break out;
       }  while (!clock.timeout(4000));
    } while (true);
  mainloop:
    while (true) {
     if (rxUart.byteAvailable()) System.out.print((char)rxUart.receiveByte());
     if (cnt!=-1 && !clock.timeoutSec(30)) continue;  // only check every 30s
     clock.mark();
     cnt++;
     if (cnt%10!=0) continue;  // only every 5 minutes
     cnt=0; // stop future roll over
     temp=sensor.getTempF();
     if (temp<75) continue;  // only report if >= 75F
     t.clear();
     t.append(Integer.toString(temp));
     if (!findModem()) continue; // no modem
     do
       sendString("@TS1=Temp Report");
     while (!waitOK());
     do
       sendString("@TA1=alw@al-williams.com");
     while (!waitOK());
     do {
       sendString("@TM1=Greetings from Javelin!");
       CPU.delay(100);  // pause for message line processing
       sendString("The temperature is ");
       CPU.delay(100);
       sendString(t.toString());
       CPU.delay(100);
       sendString(".");  // end of message
     } while (!waitOK());
     CPU.delay(1000);
     sendString("@TDM1");
     if (waitStatus(90)!='C' ||
         waitStatus(30)!='P' ||
         waitStatus(60)!='H' ||
         waitStatus(30)!='M')
           System.out.println("Can't connect!");
    System.out.println("Done");
    }
  }
}

Back to Article