flux sdk  v01.02.02-3-g292b3a7
Embedded C++ SDK
Loading...
Searching...
No Matches
Writing a Device Class

A framework device class wraps an underlying device driver, providing the needed functionally to bring the device withing the framework.

The key capabilities provided by the implemented framework device class are:

  • Enable device runtime detection and loading
  • Property and Parameter definitions that support runtime introspection
  • Common interfaces to fit within the defined structure of the framework implementation pattern

Defining the Device Class

The device class should following the naming pattern flxDev[Name], where Name is a unique name of the class.

The implementation requires separate header and implementation files, since several class variables and a global object are defined that required the use of an implementation file.

The new device class should subclass from the frameworks flxDevice class, using the flxDeviceI2CType<DeviceName> template. Additionally, the device class subclasses from the underlying Arduino driver class in most cases. This allows the descriptor class to support the existing driver's interface.

‍Note - In some cases, because of the underlying Arduino Library design, an alternative implementation pattern is required - such as object containment.

Example of a class definition

Implementing a driver for the BME280 Device.

class flxDevBME280 : public flxDeviceI2CType<flxDevBME280>, public BME280
{
};
Definition: flxDevBME280.h:33
Definition: flxDevice.h:53

Automatic Device Discovery

The framework supports runtime discovery of connected devices. This is performed using information from the framework device class, and not creating a device instance until the device is actually detected.

To accomplish this task, class level (static) methods and data are implemented by the device object. Each device class implements the following:

Item Description
bool isConnected(flxBusI2C, address) Returns true if the device is connected
char* getDeviceName() Returns the Device Name
uint8_t *getDefaultAddresses() Return a list of addresses for the device. This list terminates with the value of kSparkDeviceAddressNull
flxDeviceConfidence_t connectedConfidence() Returns the confidence level of the drivers isConnected() algorithm. Values supported range from Exact to Ping

>* Often the device implements the address list as a class level variable >* It is common to define a constant or macro for the device name and return it from getDeviceName()

Device Detection

Device Addresses

The first step for a given driver is the retrieval of default addresses for the device. In this step, the system calls the getDefaultAddresses() method of the driver. The driver should return an array of type uint8_t, with the last value of the array being the sentinel value of kSparkDeviceAddressNull.

The system uses the array of addresses to determine what addresses are currently available, and call the isConnected() with the possible and available addresses until a connection is found, or it hits the end of the possible addresses for the device.

Method Signature

static const uint8_t *getDefaultAddresses();

Is Connected

For each of the addresses returned, the system calls the drivers isConnected() method. The driver should returns true if the device is connected at the given address, otherwise false.

How the driver determines if a device is connected is determined by the implementation

Method Signature

static bool isConnected(flxBusI2C &i2cDriver, uint8_t address);
Definition: flxBusI2C.h:25

Arguments

  • i2cDriver - a I2C Bus driver object the driver can use to query the I2C bus
  • address - The address to check for a device

Device Name

The static interface for the device also includes a method to return the name of the device.

Method Signature

static const char *getDeviceName()

This method returns a constant C string.

Connected Confidence

This method returns the confidence level for the algorithm in the devices isConnected() method in exactly determining if a device is connected at the specific address.

This confidence level is used to resolve detection conflicts between devices that support the same address on the I2C bus. Drivers that have a higher confidence level are evaluated first.

Method Signature

static flxDeviceConfidence_t connectedConfidence(void)
flxDeviceConfidence_t
Definition: flxCoreDevice.h:120

The return value should be one of the following:

flxDevConfidenceExact The algorithm can perform an exact match
flxDevConfidenceFuzzy The algorithm has high-confidence in a match, but it's not exact
flxDevConfidencePing An address "ping" is used - just detecting a device at a location, but not verifying the device type.

‍Only one device with a PING confidence is allowed at an address.

Example Method Definition

This example is taken from the device driver for the BME280 device.

The class definition - flxDevBME280.h

// What is the name used to ID this device?
#define kBME280DeviceName "bme280";
// Define our class - note sub-classing from the Qwiic Library
class flxDevBME280 : public flxDeviceI2CType<flxDevBME280>, public BME280
{
public:
// Device is connected methods
static bool isConnected(flxBusI2C &i2cDriver, uint8_t address);
{
}
static const char *getDeviceName()
{
};
static const uint8_t *getDefaultAddresses()
{
}
// holds list of possible addresses/IDs for this objects
static uint8_t defaultDeviceAddress[];
// Method called to initialize the class
bool onInitialize(TwoWire &);
};
static uint8_t defaultDeviceAddress[]
Definition: flxDevBME280.h:57
static const uint8_t * getDefaultAddresses()
Definition: flxDevBME280.h:52
bool onInitialize(TwoWire &)
Definition: flxDevBME280.cpp:82
static flxDeviceConfidence_t connectedConfidence(void)
Definition: flxDevBME280.h:42
static bool isConnected(flxBusI2C &i2cDriver, uint8_t address)
Definition: flxDevBME280.cpp:63
flxDevBME280()
Definition: flxDevBME280.cpp:45
static const char * getDeviceName()
Definition: flxDevBME280.h:47
uint8_t address(void)
Definition: flxCoreDevice.h:200
@ flxDevConfidenceExact
Definition: flxCoreDevice.h:121
#define kBME280DeviceName
Definition: flxDevBME280.h:29

Notes

  • This device defined its name, bme280, using the macro kBME280DeviceName
  • Default device addresses are contained in a class variable defaultDeviceAddress[];
  • The method onInitialize() is called after the object is instantiated.
  • The class subclasses from flxDeviceI2CType, passing in the class name to the template. The class also subclasses from the Arduino Library class - BME280

Auto Discovery - Class Implementation

To complete the auto-discovery capabilities of the system, besides the implementation of the above methods, the classes implementation file must include a call to register the device with the system.

This is call is placed before the class implementation and has the following signature:

flxRegisterDevice(DeviceClassName);
#define flxRegisterDevice(kDevice)
Definition: flxCoreDevice.h:393

Where DeviceClassName is the class name of the device being registered.

Once a device is registered, it is available for auto-detection and loading by the framework during the startup process of system.

Building off the above BME280 example, the implementation looks like:

#define kBMEAddressDefault 0x77
#define kBMEAddressAlt1 0x76
// Define our class static variables - allocs storage for them
// Register this class with the system,
//----------------------------------------
// Constructor
{
// Setup unique identifiers for this device and basic device object systems
setDescription("The Bosch BME280 Atmospheric Sensor");
}
void setName(const char *new_name)
Set the Name object - storing the pointer to the provided string No allocation is performed....
Definition: flxCoreTypes.h:55
void setDescription(const char *new_desc)
Set the Description object - the input value is constant and not copied. If the previous description ...
Definition: flxCoreTypes.h:134
#define kSparkDeviceAddressNull
Definition: flxCoreDevice.h:36
#define kBMEAddressAlt1
Definition: flxDevBME280.cpp:27
#define kBMEAddressDefault
Definition: flxDevBME280.cpp:26

‍* This example includes the implementation of defaultDeviceAddress[], the class variable holding the addresses for the device.

  • The device is registered before the class constructor
  • In the constructor, the device identity is set, which is based of runtime conditions.

The isConnected() method for this example is:

bool flxDevBME280::isConnected(flxBusI2C &i2cDriver, uint8_t address)
{
uint8_t chipID = i2cDriver.readRegister(address, BME280_CHIP_ID_REG);
// Should return 0x60 or 0x58
return (chipID == 0x58 || chipID == 0x60);
}
uint8_t readRegister(uint8_t i2c_address, uint8_t offset)
Definition: flxBusI2C.cpp:50
#define BME280_CHIP_ID_REG
Definition: flxDevBME280.cpp:24

‍* This is a static (has no this instance) and as such uses the methods on the passed in I2C bus driver to determine in a BME280 is connected to the system

Startup Sequence

The last part of implementing a device descriptor/driver class is the addition of an initialization method, named onInitialize().

Method Signature

bool onInitialize(TwoWire &);

The only argument to this methods is the Arduino I2C TwoWire class, which the class can use to initialize the device. The method returns true on success, false on failure.

The BME280 example implementation:

bool flxDevBME280::onInitialize(TwoWire &wirePort)
{
// set the device address
BME280::setI2CAddress(address());
return BME280::beginI2C(wirePort);
}

‍The address() method returns the device address for this instance of the driver.

Determining if a Device is Initialized

To determine if a device is initialized, the Device Driver implementation should call the method isInitialized(), which returns the value that was returned by onInitialize(). It also returns false before the call to isInitialized()

Device Properties

See the detailed description of Properties

Device Parameters

See the detailed description of Parameters