Skip to content

Writing a driver for a communication device

Georg Lippitsch edited this page May 9, 2019 · 2 revisions

This guide provides high-level step by step instructions on how to support a new communication device (like a cellular modem or WiFi module).

The general approach for writing a communication device driver is creating a state machine to send data to the device and process all possible replies. Because of the non-blocking design of the Cicada library, it's not allowed to busy-wait for a reply or even for data being sent to device. Instead, the driver must only process data which is already available in a buffer, or copy data into a buffer. The driver must be fully aware of the current state of the communication device.

Also, the communication device driver must be agnostic of the underlying micro-controller platform and must not use any libraries except Cicada itself.

Overview

The main steps to write a new communication device driver are:

  1. Subclass IPCommDevice (for general IP based communcation devices) or SimCommDevices for cellular modems
  2. Implement required pure virtual methods from ICommDevice and IIPCommDevice. All must be non-blocking.
  3. Overwrite the Tasks run() function to actually process data.

Subclass IPCommDevice

IPCommDevice provides a base class for Internet Protocol based communication devices, like cellular modems or WiFi modules. If your communication device is similar to the Simcom modem devices (using AT commands), you can instead subclass SimCommDevice, which already has some more utilities for processing data from these modems.

Implement pure virtual methods

The base class should already implement most pure virtual functions from the ICommDevice and IIPCommDevice interfaces. In case the default implementation is missing or not suitable for your case, you must re-implement these functions. Note that the functions must be non-blocking! They basically copy data to the internal read/write buffers or set flags, which are actually processed in the driver's task run() method.

Overwrite run()

Creating the run() function is the largest task when writing a new communication device driver. The driver inherits the Task class, which gets it's run() function executed by the task scheduler. The run() function is usually implemented as a state machine, processing the data in the read/write buffers and acting according to the flags set in the non-blocking API functions. run() itself must be non-blocking as well, it's not allowed to busy-wait for replies.

The exact behavior of the run() function depends on the architecture of the device, so there is no general formula of what to do here. Things it probably needs are:

  • Run general checks on the serial device and open it
  • Check for driver locking, to not interfere with custom user data
  • Error and debug logging
  • Process messages or replies from the device
  • Send commands to the device
  • Send and receive data to/from the device