Skip to main content

SmartData

SmartData is a data class for storing and controlling data. While designed specifically with qControl and qCommand in mind, it can be used by itself as well. SmartData is a templated class, meaning it takes a type argument and the class has different properties for different types. To use the data class, add the following line to the top of your program:

#include "smartData.h"

The SmartData class can be broken down into three distinct categories:

  • SmartData for scalars (i.e., single data types like floats and ints)

  • SmartData for an array of options. Each option has a value and, optionally, a name. Data can only be set to values that are in the options array.

  • SmartData for an array of data.

Each category has similar but slightly different behavior.

SmartData for Scalars

SmartData<DataType> Object(DataType init_value);

The above instantiation creates a SmartData object with type DataType where DataType can be bool, uint8_t ,int8_t, uint16_t , int16_t, uint32_t, int32_t, float, double or any type that reduces to one of those types (char, int, uint). The object has the following member functions:

get

DataType get(void);

This function returns the current value of the underlying datatype. Referencing the SmartData object for reading is the same as calling the get function, as shown in this example:

Example

SmartData<int16_t> Data(34); //initialize SmartData scalar with type int16_t and value of 34 
Serial.print(Data.get()); // prints 34
Serial.print(Data); // prints 34, same as calling Data.get()

set

void set(DataType dataValue);

The set(DataType) function sets the value of the SmartData object. Additionally, the sendUpdate function will get called internally when set is run, which tells the Quarto that the data in this object is stale and when qControl queries for new data, the Quarto will send the updated value. This allows qControl to automatically display the latest value of an object whenever the set command is called. Similar to get, writing to the SmartData object is the same as calling set.

Example

SmartData<int16_t> Data(34); //initialize SmartData scalar with type int16_t and value of 34 
Serial.print(Data.get()); // prints 34
Data.set(15); // sets value to 15
Serial.print(Data); // prints 15
Data = 20; //Same as Data.set(20)
Serial.print(Data); // prints 20

setSetter

void setSetter(DataType(*)(DataType newValue, DataType oldValue));

The setSetter command sets a custom control function to be called whenever the object's data is about to be updated. This control function can alter the new data value if necessary, before the object is updated. This can be used to coerce values into a range, or force an object to be read-only under certain conditions.

Example

uint16_t limitRange(uint16_t input, uint16_t oldValue) { 
if (input > 100 ) {
return 100;
} else if (input < 20) {
return 20;
} else {
return input;
}
}
SmartData<uint16_t> Data(34); //initialize SmartData scalar with type uint16_t and value of 34
Data.setSetter(limitRange);
Data = 150; // tries to set data to 150, but is limited by limitRange function to 100
Serial.print(Data); // prints 100
Data.set(56); // set data to 56
Serial.print(Data.get()); // prints 56
Data = 7; // tries to set data to 7, but is limited by limitRange function to 20
Serial.print(Data); // prints 20

sendUpdate

void sendUpdate(void);

The sendUpdate function is rarely called directly as it is is called automatically when set is called. When called, it flags the SmartData object as stale and when qControl queries the Quarto for new data, the Quarto will send the updated value. This allows qControl to display the latest value of an object.

SmartData for an Array of Options

SmartData<Option<DataType>*> Object(Option<DataType>* optionArray);

SmartData for an Array of Options is very similar to SmartData for Scalars, except that only specified values are allowed. This is useful to guarantee that a variable is always set an allowed value. Here's an example:

Option<uint8_t> NumberOfChannels[] {
{1},
{2},
{4}
}; // Defines an Array of Options (NumberOfChannels) that only allows the values 1, 2 and 4

SmartData<Option<uint8_t>*> OscopeChannels(NumberOfChannels); //SmartData object (OscopeChannels)
OscopeChannels = 2; // Sets OscopeChannels to 2
OscopeChannels = 3; // 3 is not an allowed value, command does nothing
OscopeChannels = 4; // Sets OscopeChannels to 4

Additionally, a label can be associated with each Option. This is used in qControl and qCommand to give descriptive names to certain values.

Option<uint8_t> AvailableChannelsNamed[] {
{1, "Single"},
{2, "Dual"},
{4, "Quad"}
}; // Defines an Array of Options (NumberOfChannels) that only allows the values 1, 2 and 4

All the functions available for scalar objects, such as get, set, setSetter and sendUpdate are available with the same syntax. In addition, there is a function, getName, to get the name of the currently selected object.

getName

const char* getName(void);

This function returns a pointer to a character array storing the current name of the active selection. If there is no name, it returns an empty (null-terminated) string.

Example

SmartData<Option<uint8_t>*> OscopeChannelsNamed(AvailableChannelsNamed);
OscopeChannelsNamed = 1;
const char* name = OscopeChannelsNamed.getName();
Serial.println(name); //prints Single
OscopeChannelsNamed = 4; // sets OscopeChannelsNamed to 4 (Quad)
name = OscopeChannelsNamed.getName();
Serial.printf("O-scope mode is %s\n", name); // prints "O-scope mode is Quad"

SmartData for an Array of Data

SmartData<DataType*> Object(DataType* dataObject);

SmartData can also be used to manage an array of data. Unlike with scalar objects, the data array needs to be initialized externally and then SmartData is initialized with a pointer to that data. The DataType can be any of the types allowed for SmartData for Scalars. Here's an example of how to instantiate both the underlying data array and the SmartData object for that data:

float floatData[100]; //initialize an array of 100 floats
SmartData<float*> Data(floatData); //initialize SmartData with type float* and pointing to the floatData

A SmartData object for arrays has the following member functions that can be called:

In addition to these member functions, the SmartData object also has an internal variable that tracks the index for where the next datapoint in the array can be stored. This index starts at zero (the beginning of the array) and can be changed with the set and setNext commands. Additionally, when qControl reads the data in this array, the index is reset back to zero.

get

DataType get(size_t element);

This function takes an argument element which sets the index from the underlying data array to use. The value of the data array at that index is returned.

Example

uint8_t dataArray[4]; //initialize an array of 4 uint8_t
dataArray[0] = 10;
dataArray[1] = 11;
dataArray[2] = 12;
dataArray[3] = 13;
SmartData<uint8_t*> Data(dataArray); //initialize SmartData with type uint8_t* and pointing to the dataArray
Serial.print(Data.get(2)); // prints 12
Serial.print(Data.get(0)); // prints 10

set

void set(DataType dataPoint, size_t element);

This function takes two arguments: the first one, dataPoint, is the value to set and the second one, element, is the index in which to store it. In addition to storing the data, it sets the internal index to the next value in the array.

Example

uint8_t dataArray[4]; //initialize an array of 4 uint8_t
SmartData<uint8_t*> Data(dataArray); //initialize SmartData with type uint8_t* and pointing to the dataArray
Data.set(10,0); // set value of 10 at index 0;
Data.set(11,1); // set value of 11 at index 1;
Data.set(12,2); // set value of 12 at index 2;
Data.set(13,3); // set value of 13 at index 3;
Serial.print(Data.get(2)); // prints 12
Serial.print(Data.get(0)); // prints 10

setNext

bool setNext(DataType dataPoint);

setNext is a simplified version of set that handles the array index internally. Every time it is called, it stores the data at the location set by the internal index (starting at 0) and increments that index by 1. When the array is full, the function calls sendUpdate and ignores subsequent calls to write to data as the array is full. However, because sendUpdate is called, qControl will query for this data, and when the data is received, the index will get reset. So after the data is read, setNext will next update the value at index 0, then index 1 and the process will repeat. setNext returns a boolean that is true if data was written to the array and false if it was not. This can be used to wait until the array has been read before updating the data. For applications that can handle data loss between updates, setNext can simply be run in a loop.

Example

float floatData[4]; // An array of floats with only 4 elements
SmartData<float*> Data(floatData);

Data.setNext(1.23); // set element 0 to 1.23, returns true
Data.setNext(2.34); // set element 1 to 2.34, returns true
Data.setNext(3.45); // set element 2 to 3.45, returns true
Data.setNext(4.56); // set element 3 to 4.56, returns true. Array full, sendUpdate is run
Data.setNext(5.67); // array full, nothing happens, returns false
Data.setNext(6.78); // array full, nothing happens, returns false
// qControl queries Data and display new data: 1.23,2.34,3.45,4.56. Internal index reset to 0.
Data.setNext(7.89); // set element 0 to 7.89, returns true
Data.setNext(8.90); // set element 1 to 8.90, returns true
Data.setNext(9.01); // set element 2 to 9.01, returns true
Data.setNext(10.12); // set element 3 to 10.12, returns true
Data.setNext(11.23); // array full, nothing happens, returns false
Data.setNext(12.34); // array full, nothing happens, returns false
// qControl queries Data and display new data: 7.89, 8.90, 9.01, 10.12

getCurrentElement

size_t getCurrentElement(void);

This function returns the value of the internal index to the array. This value is incremented by setNext and reset to zero when qControl reads the data. It is also set by the set function.

Example

float floatData[4]; // An array of floats with only 4 elements
SmartData<float*> Data(floatData);

Serial.print(Data.getCurrentElement); // returns 0;
Data.setNext(1.23); // set element 0 to 1.23, returns true
Serial.print(Data.getCurrentElement); // returns 1;
Data.setNext(2.34); // set element 1 to 2.34, returns true
Data.setNext(3.45); // set element 2 to 3.45, returns true
Serial.print(Data.getCurrentElement); // returns 3;
Data.setNext(4.56); // set element 3 to 4.56, returns true. Array full, sendUpdate is run
Serial.print(Data.getCurrentElement); // returns 4;
Data.setNext(5.67); // array full, nothing happens, returns false
Serial.print(Data.getCurrentElement); // returns 4;
// qControl queries Data and display new data: 1.23,2.34,3.45,4.56. Internal index reset to 0.
Serial.print(Data.getCurrentElement); // returns 0;
Data.set(8.76,2); // set element 2 to 8.76 and sets index (for next write) to 3
Serial.print(Data.getCurrentElement); // returns 3;

resetCurrentElement

void resetCurrentElement(void);

The current index element will be reset to 0 when resetCurrentElement() is called.

getTotalElements

size_t getTotalElements(void);

This function returns the number of elements in the array.

sendUpdate

void sendUpdate(void);

When called, sendUpdate flags the SmartData object as stale and when qControl queries the Quarto for new data, the Quarto will send the updated value. The function is automatically called when setNext causes the array to become full. However, when using set to control the array, sendUpdate will need to be called when the data is to be read by qControl.

isEmpty

bool isEmpty(void);

Simple helper function that returns if the internal index is set to 0. Useful for checking if qControl has queried the data (and reset the index) before writing new data.

isFull

bool isFull(void);

Simple helper function that returns if the internal index is set to the number of elements in the array. Useful for checking if the array is full and needs to be reset by qControl before writing more data.