Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

C/C++

Object-Oriented Device Networking


Aug01: Object-Oriented Device Networking

Michael is chief technology officer for emWare. He can be contacted at mhoward@ emware.com.


Device networking is the focus of many current development efforts, with initial efforts focusing on remotely manipulating and monitoring newly connected devices. This is appropriate when the intent is to put users directly in control of the device. However, the problem is that billions of these new devices are built every year and many of them need to be part of larger intelligent systems. Clearly, architectures for device networking must provide a framework for creating complex systems from simple, standardized building blocks — and object-oriented design is the key to making this happen. In this article, I'll present an object-oriented approach that is an alternative to the traditional ways of designing networked devices.

The Opportunity

When I started working in the device-networking field four years ago, the task at hand seemed straightforward. We wanted to leverage the massive amount of Internet-related software R&D by making systems that connected web browsers to embedded devices. These devices were often built around simple microcontrollers in extremely cost-sensitive applications. Even though the microcontrollers were low cost, the value in being able to monitor and manipulate the devices was often surprisingly high.

We identified several areas where networking added value to previously isolated embedded systems. These areas included:

  • Inexpensive user interfaces. A communications port can link a device application to a browser-based interface. This interface is significantly cheaper than an LCD screen, with its many buttons and user manuals.

  • Remote access for control and monitoring. With each new design that incorporates a microcontroller, there needs to be access to information stored in the microcontroller.

  • Integration of web-based information sources with device applications. There are many instances in which devices can benefit from having access to information from the outside world. Living in a desert, my favorite example is having a sprinkler controller use weather forecast information to avoid watering before or during rainfalls.

  • Distributed control systems. Many applications of device communications involve making several computer systems act like one. This type of application is often motivated by the desire to reduce wiring costs to a central controller. Another example is when a single system, say a complex commercial air-conditioning system, must be built from a set of unknown components.

Initial Efforts

To address this market, we created a device-resident server that makes a set of functions, variables, and events available for remote manipulation via web-based clients. In effect, each device is encapsulated as a single device object in a web-based proxy server. The device object can be manipulated from remote interfaces using C, Java, or ActiveX APIs. The device object had a complete description of each function, variable, or event, but there was no attempt to create device types.

Initial customer interest came from service applications in which technicians walked up to a piece of equipment and used our software and a Java service applet to diagnose a complex system. Web technology made this type of application really cool because when the local technician had problems with a device, our software made the same diagnostic information available to an expert service technician located at the corporate office. The expert was available online to help the local technician with particularly difficult maintenance and service procedures.

Problems Start Coming Up

Since using web technology to connect people to networked devices seemed reasonable, I patted myself on my back and believed — for a while, anyway — that I understood the problem space.

As we moved forward to extend the Internet to microcontroller-based devices, however, I realized how massively outnumbered we are by the machines around us and couldn't imagine interacting with so many devices through individual UIs. The last thing I want is to have to pay attention to more details, so a web interface for each microcontroller-based device in my house seemed less and less like a good idea.

Other problems that arose with this paradigm included:

  • Diversity of client devices and time to custom code and test UIs.
  • Although there are many kinds of devices, device security and management functions should be similar — but there is no consistent method for encapsulating common functions. In fact, many types of devices fall into general categories; therefore, the notion of object interfaces needs to be supported.

  • Most intelligent devices have static behavior. Imagine coding thousands of specific requests for information into devices, then parsing the responses and acting on them. Now add the restriction that whatever the device does, it will continue to do even if the information sources change.

Although our initial development efforts solved the connectivity problem, the lack of typing and any sort of behavior contract created serious problems. These problems made it difficult to build larger systems.

Currently, I'm focusing on automation. Consequently, what I'd like to have is a device network infrastructure that is automated in terms of configuration, maintenance, and operation. The result should be that any information on a device is accessible by network resident applications through standard database APIs. The database APIs are also used to affect the state of many devices, not just a single one or a single type. The networked devices are also logically connected to each other. When one device has information relevant to the operation of another, this information is transferred according to predefined rules for interaction.

The problem space for this approach is divided into problems of representation and coordination. I want to maintain a network-based representation of a device. This representation is simply a replicated copy. It reflects the state of the device. When I manipulate the copy, the device state changes as well. When I talk about "device coordination," I'm referring to the creation of distributed systems. I call it "coordination" because the systems can be loosely coupled and one device is controlling the state of another device. For example, because a telephone possesses intelligence, it can send messages instructing stereos or TVs to turn down their volumes when the phone is ringing, thereby eliminating missed calls.

The Traditional Approach

Manufacturers have developed thousands of devices with connectivity in mind, and most have incorporated RS232 ports with custom protocols. Although these proprietary protocols may implement commands for diagnostics, data logging, and device control intended for use by other devices, their level of interoperability with disparate networks is limited. Of course, the benefit of this approach is that it is simple and relatively inexpensive. However, the problem with devices that utilize this limited form of connectivity is that they don't incorporate the concept of Design by Contract (for more information on DbC, see http://www.elj.com/eiffel/bm/ot-dbc/). Thus, these devices and systems are not scalable, and cannot integrate higher level systems. Someone has to look at each and every device type and create a translation module. This is not a simple process because of the lack of protocol standardization and abstraction.

The Object-Oriented Approach

With this in mind, we are pushing to create uniform interfaces to object representations of devices and use standard class definitions to describe similar devices. By doing this, the task of creating useful software applications that incorporate device information can be decoupled from the task of getting that information from the device. Additionally, the device descriptions represent a behavioral contract, which is a prerequisite to any real progress in the automation of device network configuration and management.

Our current effort starts with a system that lets you encapsulate a device into one or more objects. Each of these objects is essentially an instantiation of a predefined device-related class. Typical implementations include class implementations for management, general device application, and a subclass for proprietary extensions. The management class implements features used for firmware upgrades, capabilities discovery, device identification, and performance-related tuning. The class that implements general device application is essentially the feature set of the virtual base class for the specific type of device. For example, a networked furnace would have a basic set of properties used for maintenance, diagnostics, and control. This class definition would be one defined by and agreed upon by a standards group such as the American Home Appliance Manufacturers (http://www.aham.org/), whose charter is to promote and define standards that allow interoperability of networked appliances.

Interfaces

At first glance, the encapsulation of an embedded device application into an object appears trivial. The need for industry-wide device specification is apparent when you look at the support that Universal Plug-and-Play (http://www.upnp.org/) is getting. Given a way to let the device describe which classes and interfaces are implemented, and a protocol to allow manipulation of the device objects, the desired automation should be within reach.

But problems start creeping from several different factors:

  • A device manufacturer adds product features to differentiate their product from other manufacturers. If these features are to be available to other devices, then a combinatorial explosion occurs, which removes the usefulness of the objects for software that relies on uniformity. A class definition based on the feature set of the lowest common denominator allows the notion of polymorphism at a system level. An interface can be written to this object. The question remains whether treating all devices the same offers enough value from interoperability to prevent manufacturers from ignoring standardization efforts.
  • Different aspects of a device's operation will be exposed as different objects or interfaces within an object. However, each of these is accessed through a communication link that is often bandwidth limited. This brings up a host of quality of service issues, including questions of timing: Do the class definitions need to specify required responsiveness of the objects being used? Will spurious delays break some applications?

Inheritance

Inheritance in the device-networking realm is an interesting feature. The implementation of inheritance that I need is a distributed inheritance. The base class of a device object is implemented on a larger device gateway program; the subclass is implemented on the product. The gateway device then further subclasses the device. This implementation lets you control the final presentation of an object from a device gateway.

The distributed class hierarchy that implements this type of inheritance allows you to create a device application that is based on services that are available through a supporting system such as a device gateway. Take, for example, a home-heating and air-conditioning (HVAC) system. At its simplest, this is a system composed of a heating unit, chiller, and thermostat. If the devices were connected via different transports to a home gateway, then the gateway would provide services to the application running in the thermostat for network connectivity. The method invocations of the HVAC control to raise the temperature would result in invocations in the gateway-resident base class, which are then forwarded to the other subnetwork. This may be superior to having the thermostat application smart enough to deal with networking issues and encoding the data communications into the application.

Connecting Objects

Given a device network of objects, the next step is to create bindings between the objects. The approach I first thought to take was to create bindings between the properties, methods, and events of various objects. As Figure 1 shows, there are a number of ways you can approach this.

  • Make a set of device objects and interfaces available from each device. Then make that set available to some configuration process. The configuration process then determines what the overall interaction should be and binds the methods and properties of the objects as appropriate. If the underlying communications stack does not allow peer-to-peer communication, then the binding is accomplished through an intermediary node that acts as the bus master and spends a lot of time copying data around.
  • Implement the interactions between devices adding remote object manipulation features to the device, then program the device applications to use remote device objects. For example, a thermostat could look up all available furnaces, bind to the appropriate one (magic happens here), then manipulate the furnace object directly.

  • Implement a notion of object input for the needed objects, but let some external module control the remote object interaction and binding. Using this system, the device is not the only thing typed. The input to the device is from binding with a specific type of object. This is stronger than simply binding to a property of product because the type of the device supplying this input is enforced. The idea here is to keep the application developer from having to deal with the complexity of network configuration and management. Instead, the device firmware is created with a set of predefined interactions with other known devices. This is a scheme that I think is appropriate in a situation where there is a network coprocessor or a centralized device gateway; see Listing One.

In Figure 1(a), the user of the Hot Tub object knows that it is manipulating the Hot Tub, so the interaction is assumed appropriate. The developer of the Hot Tub enforces correct iteration with the equipment through the class definition, but trusts that users of this object will manipulate it appropriately. In Figure 1(b), the connection is between the implementor of an Occupant Status class, which describes the status of home occupants. The object state reflects that the occupant will not be around for the day. The desired actions resulting from this status are not the responsibility of the home controller. Finally, in Figure 1(c), the Hot Tub object is created to use an Occupant Status object if it is connected. If so, the behavior is made to place the equipment in the appropriate mode, given the status information supplied.

In all these scenarios, the binding phase presents a problem. The desire is to have a completely automatic plug-and-play type of system. The reality is that the binding of devices is often difficult or impossible to resolve automatically. Take, for example, a case where a consumer has two furnaces and two thermostats. How do these nodes know which to bind? The process of binding devices into control systems must be accomplished by:

  • Discovery of available devices.
  • Designation of suitable bindings.

  • Binding the devices.

  • Confirmation of correct binding.

One or more of these stages can be automated or skipped, but I have never seen a system where all these steps can always be skipped. One of the hurdles comes when the device subnetwork transport overlaps with subnetworks owned by other consumers. It would be a real problem to bind a smoke detector with RF output from one house into another.

With an object-oriented approach, device networks can be built that are automatically configured and efficiently managed, but there is no magical property that can make what would essentially be a random guess into a wise choice.

An Example

The device network interface software that Listing One is written for lets each variable access to be implemented with an accessor function that controls the movement of data to and from the communications channel. This is why there is no apparent data movement from the outside world into this system. This example assumes that the classes implementing Furnace are built from C. This is typical of control applications.

If you look at the state transitions, the over-temperature fault, which happens when the filter gets too dirty, does not stop the furnace from proceeding to cool-down phase and eventual startup. This is indicative of how these types of devices must continue to operate as good as possible even in the face of serious problems. That is why getting this valuable fault information is so important to the consumer. A remote service tech can easily determine the problem and even be alerted by the thermostat, which will know that its control is slipping.

Any device that wishes to control the furnace must supply the HeatControl output. This is stronger typing than simply linking to any module that can control temperature. The devices or applications interested in fault and status may be a separate maintenance module, not just the thermostat. The meaning of the input/output functions are not ambiguous and leave little room for misunderstanding in terms of behavior resulting from communications.

Extending the Contract

Recall that I suggested devices might be implemented using inputs that are of certain class objects instead of using inputs from properties that are manipulated. This is an important choice because, on one hand, you get devices that cannot possibly be connected to inappropriate input. On the other hand, you risk losing flexibility and the ability to bind inputs to reasonable but unanticipated sources.

Bidirectional contracts are good when the behaviors for given inputs are easy enough to design, but the inputs should be more strongly typed. If you view the inputs as classes, then this is done for you already because the contract is extended to include the appropriateness of the inputs to the device.

Device networking and object-oriented design is a natural fit because object-oriented design is premised on modeling real world systems and relationships. When embedded applications can be treated as instances of standard device classes, the devices can be integrated into larger, more automatic systems. To accomplish this automation, the device application developers must not only create an instance of a device class, they must be able to structure the relationships of their application and other devices. Having this type of object-oriented approach to device networking lets you vastly simplify the end-user's environment by making devices work together without needing an end-user to be tied to the system.

Conclusion

As embedded microcontrollers find their way into more and more applications around us, we are becoming surrounded by systems that implement behaviors appropriate for their limited inputs. Many of these systems will have valuable information available to them that is never made available to the outside world. This is sometimes due to cost, location, or for the sake of simplicity for users. By adding networking capabilities, you can tie these isolated systems together into larger coordinated systems. The components of the larger systems may be created by different vendors, but should easily, if not automatically, connect and work together.

This requirement is familiar to software developers who have dealt with software interoperability issues for many years. The tool developed for this problem of interoperability is object-oriented design. In device networking, an object-oriented approach of encapsulating device capabilities into classes facilitates network configuration and provides strong typing required for ensuring the appropriateness of an interaction. However, the relationship created by merely encapsulating capabilities, then using them elsewhere, does not sufficiently guarantee to the manufacturer of a device that the device will be used appropriately. I have suggested that making input objects for the controlled systems will provide this guarantee and pave the way for automatic configuration.

Applying object-oriented design to embedded devices promises many of the same benefits it delivers to larger software systems. To get these benefits, the mechanics of creating objects out of embedded applications must be addressed.

DDJ

Listing One

/* Heat Control Input.  This input represents the heat on/off 
** control from a thermostat.  
** EmergencyOff 
** The emergency off input requires that any activity should 
** be terminated as soon as possible, but in a way that will
** not damage equipment.  In this system, the cool down cycle
** is shortened when this is present.
** Heat
** The heat input from the thermostat.  The furnace must start
** heating as soon as possible and remain on until this is de-asserted.
*/  
typedef struct {
      BOOL EmergencyOff;
      BOOL Heat;
      BOOL Economy;
      } HeatControlIn;
/* Furnace Status Output.
** This is an output specific to Furnace device types. The
** fault flag is used to indicate if there is a current fault to report.  
** FanOn        - indicates whether the fan is currently running.
** Heating      - Indicates if the furnace is currently active.  This
**                is true during the phases of warm-up, heat, cool-down.
** InternalTemp - The internal temperature.  This value is used to 
**                control the phase transitions as well as the
**                fault shut-down.
*/
typedef struct {
      BOOL Fault;
      BOOL FanOn;
      BOOL Heating;
      TEMP InternalTemp;
      } FurnaceStatus;
/* Fault Output.  This is a generic equipment fault output.
** The Reason field is an ascii text description of the fault.
** The code is a device type specific fault value.  The
** tuple of (TypeId,code) will determine the fault meaning 
** for other devices.
*/
typedef struct {
      char *Reason;
      BYTE Code;
      DEVID Id;
      DevType TypeId;
      } FaultDesc;     

/* Sample State machine to go with these I/O on a furnace. */
/* check internal temperature, maintain status */
status.InternalTemp = InternalTemp = ReadTemp();

/* handle state transitions */
switch (FSTATE)
    {
      case idle:
        if (! EmergencyOff)
            {
               if (Heat)
             StartHeat();
            }
            break;
      case starting:
        if (InternalTemp >= START_TEMP)
           {
           SetFan(ON);
           FSTATE = heating;
           }
        break;
      case cooling:
        if ((EmergencyOff && (InternalTemp <= FAST_STOP_TEMP))
           || (InternalTemp <= STOP_TEMP))
           {
           SetFan(OFF);
           FSTATE = idle;
           }
        break;
      case heating:
        if (! Heat)
          {
          StopHeat();
          FSTATE = cooling;
          }
        if (InternalTemp >= MAX_TEMP)
          {
          StopHeat();
          FSTATE = cooling;
          status.Fault = TRUE;
          fault.Reason = "Internal temp exceeded, check filter";
          fault.Code = FAULT_FURNACE_TEMP;
          fault.Id = MyId;
          fault.TypeId = Mytype;
          } 
        break;
     }

Back to Article


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.