Canandcolor Digital Ports

The canandcolor’s digital ports are very flexible outputs that can be used in multiple modes. Primarily, they are grouped into two categories: PWM output mode and digital logic mode.

In PWM mode, selected values (such as proximity, red channel, HSV hue, etc) are transmitted via a PWM signal. In digital logic mode, a custom digital logic system can be used to trigger the port on and off.

PWM Mode

The digital output ports can be configured to output the Red, Blue, Green, White, Hue, Saturation, Value, or Proximity values over PWM. Disabling a value’s CAN packet rate will not disable its PWM output, so this can be used without the CAN output.

// A Canandcolor object with CAN ID 0
Canandcolor canandcolor = new Canandcolor(0);

// Instantiate an empty settings object
Canandcolor.Settings settings = canandcolor.getSettings();

// Sets DIG1 to output proximity over PWM
settings.setDigoutOutputConfig(Canandcolor.Digout.kDigout1, DigoutMode.DutyCycle(Canandcolor.DataSource.kProximity));

// Sets DIG2 to output hue over PWM
settings.setDigoutOutputConfig(Canandcolor.Digout.kDigout2, DigoutMode.DutyCycle(Canandcolor.DataSource.kHue));

// Apply the configuration to the device.
if (canandcolor.setSettings(settings)) {
    System.out.println("digout configuration success");
} else {
    System.out.println("digout configuration failure");
}
using namespace redux::sensors::canandcolor;
using namespace redux::sensors::canandcolor::digout;

// Get a canandcolor object with CAN ID 0
Canandcolor canandcolor{0};

// Instantiate an empty settings object.
CanandcolorSettings settings{};

// Sets DIG1 to output proximity over PWM
settings.SetDigoutOutputConfig(CanandcolorDigout::kDigout1, DigoutMode::DutyCycle{CanandcolorDataSource::kProximity});

// Sets DIG2 to output hue over PWM
settings.SetDigoutOutputConfig(CanandcolorDigout::kDigout2, DigoutMode::DutyCycle{CanandcolorDataSource::kHue});

if (canandcolor.SetSettings(settings).IsEmpty()) {
    fmt::println("Settings set correctly!");
} else {
    fmt::println("Settings did not set correctly");
}

Digital Logic Mode

In digital logic mode, a custom digital logic statement is used to evaluate if the port should output HIGH or LOW.

Statements are made up of two parts: Slots and Clauses. Slots are individual statements that resolve to true or false (such as checking if a value is equal to a constant, or whether a value has been true for a set amount of time), whereas clauses are groups of slots. All clauses must evaluate to true for the port to turn on (meaning all clauses are ANDed together).

An example of how slots and clauses can work is presented below:

Slot

Clause action

Clause

Proximity > 0.3

OR

Clause 1

White > 0.7

Terminate

Red - 0.2 > blue

AND

Clause 2

Prev slot true for >= 1.5 sec

Terminate

This is broadly equivalent to the Java pseudocode:

Canandcolor color;
double colorDiffTrueSeconds; // number of seconds that color.getRed() - 0.2 > color.getBlue() has been true for
boolean output = (color.getProximity() || color.getWhite() > 0.7) && (color.getRed() - 0.2 > color.getBlue() && (colorDiffTrueSeconds >= 1.5));

Slots

There are 6 different slot statements:

  • Compare a data source to fixed value

  • Compare a data source to another data source

  • Compare a data source to another data source, performing mathematical operations on the first data source

  • Previous slot true for a given period of time

  • Previous clause true for a given period of time

  • Disable

In addition, comparisons are done using 5 different operators (equal, greater than, less than, greater than or equals, less than or equals).

Comparing to a fixed value

Data sources are compared using the following syntax: [data source] [comparison] [fixed value]

For example, comparing data source kGreen, with comparison less than or equal to, and fixed value 0.6, results in the device evaluating:

getGreen() <= 0.6

// Creates a slot which evaluates to true when the proximity is less then or equal to 0.5
// Similiar to canandcolor.getProximity() <= 0.5
DigoutSlot slot = DigoutSlot.CompareImmediate(Canandcolor.DataSource.kProximity, SlotComparison.kLessThanOrEquals, 0.5);

// Creates a slot which evaluates to true when the red channel is greater then 0.3
// Similiar to canandcolor.GetRed() > 0.3
DigoutSlot slot2 = DigoutSlot.CompareImmediate(Canandcolor.DataSource.kRed, SlotComparison.kGreaterThan, 0.3);
using namespace redux::sensors::canandcolor::digout;

// Creates a slot which evaluates to true when the proximity is less then or equal to 0.5
// Similiar to canandcolor.GetProximity() <= 0.5
DigoutSlot slot = DigoutSlot::CompareImmediate(CanandcolorDataSource::kProximity, SlotComparison::kLessThanOrEquals, 0.5);

// Creates a slot which evaluates to true when the red channel is greater then 0.3
// Similiar to canandcolor.GetRed() > 0.3
DigoutSlot slot2 = DigoutSlot::CompareImmediate(CanandcolorDataSource::kRed, SlotComparison::kGreaterThan, 0.3);

Comparing to another data source

Given two data sources, the sources are compared with the following syntax: [data source 1] [comparison] [data source 2]

For example, comparing kGreen with comparison greater than to data source kRed, results in the device evaluating:

getGreiwn() > getRed()

// Creates a slot which evaluates to true when the blue channel is less then the red channel
// Similar to canandcolor.getBlue() < canandcolor.getRed()
DigoutSlot slot = DigoutSlot.CompareAffine(Canandcolor.DataSource.kBlue, SlotComparison.kLessThan, Canandcolor.DataSource.kRed);

// Creates a slot which evaluates to true when the red channel is greater then the proximity channel
// Similar to canandcolor.getRed() > canandcolor.getProximity()
DigoutSlot slot2 = DigoutSlot.CompareAffine(Canandcolor.DataSource.kRed, SlotComparison.kGreaterThan, Canandcolor.DataSource.kProximity);
// Creates a slot which evaluates to true when the blue channel is less then the red channel
// Similar to canandcolor.GetBlue() < canandcolor.GetRed()
DigoutSlot slot = DigoutSlot::CompareAffine(CanandcolorDataSource::kBlue, SlotComparison::kLessThan, CanandcolorDataSource::kRed);

// Creates a slot which evaluates to true when the red channel is greater then the proximity channel
// Similar to canandcolor.GetRed() > canandcolor.GetProximity()
DigoutSlot slot2 = DigoutSlot::CompareAffine(CanandcolorDataSource::kRed, SlotComparison::kGreaterThan, CanandcolorDataSource::kProximity);

Comparing to another data source with transformation

Given two data sources, scale value s, and offset value o, the sources are compared with the following syntax: ([data source 1] * s) + o [comparison] [data source 2]

For example, comparing data source kGreen with scale value 0.5, and offset value 0.2, using comparison greater than, to data source kRed, results in the device evaluating:

(kGreen * 0.5) + 0.2 > kRed

//Creates a slot which evaluates to true when the blue channel plus 0.2 is less then the red channel
DigoutSlot slot = DigoutSlot.CompareAffine(Canandcolor.DataSource.kBlue, SlotComparison.kLessThan, Canandcolor.DataSource.kRed 1, 0.2);

//Creates a slot which evaluates to true when the proximity times 0.5 is greater then the red channel
DigoutSlot slot2 = DigoutSlot.CompareAffine(Canandcolor.DataSource.kProximity, SlotComparison.kGreaterThan, Canandcolor.DataSource.kRed, 0.5, 0);
//Creates a slot which evaluates to true when the blue channel plus 0.2 is less then the red channel
DigoutSlot slot = DigoutSlot::CompareAffine(CanandcolorDataSource::kBlue, SlotComparison::kLessThan, CanandcolorDataSource::kRed, 1, 0.2);

//Creates a slot which evaluates to true when the proximity times 0.5 is greater then the red channel
DigoutSlot slot2 = DigoutSlot::CompareAffine(CanandcolorDataSource::kProximity, SlotComparison::kGreaterThan, CanandcolorDataSource::kRed, 0.5, 0);

Time based comparator

Time based comparators indicate true when the previous slot or clause has been true for at minimum the provided number of seconds.

// Creates a slot which evaluates to true when the previous slot has been true for 2.5 seconds
DigoutSlot slot = DigoutSlot.PrevSlotTrueFor(2.5);

// Creates a slot which evaluates to true when the previous clause has been true for 0.8 seconds
DigoutSlot slot2 = DigoutSlot.PrevClauseTrueFor(0.8);
// Creates a slot which evaluates to true when the previous slot has been true for 2.5 seconds
DigoutSlot slot = DigoutSlot.PrevSlotTrueFor(2.5_s);

// Creates a slot which evaluates to true when the previous clause has been true for 0.8 seconds
DigoutSlot slot2 = DigoutSlot.PrevClauseTrueFor(0.8_s);

Clauses

A clause is a group of slots together, chained using either AND or OR operations. All clauses are ANDed together, meaning they must all evaluate to true if the slot is to evaluate to true.

Clauses are created by chaining slots with the ClauseAction field, which indicates if the given slot should join with the next slot to form a clause, or terminate the clause at the end. For example, if there are 4 slots in use (indexed 0, 1, 2, and 3), and you want a clause of slots 0, 1, and 2 by ORing them all together, you would need to set slots 0 and 1 to kOrWithNextSlot, and finally slot 2 should be set to kTerminateClause.

Note

The following code sample sets up a clause that will evaluate to true if an object is less then or equal to 0.5 proximity, or if the red channel is greater then or equal to 0.2

// Creates a slot which evaluates to true when the proximity is less then or equal to 0.5
DigoutSlot slot1 = DigoutSlot.CompareImmediate(Canandcolor.DataSource.kProximity, SlotComparison.kLessThanOrEquals, 0.5, ClauseJoin.kOrWithNextSlot, false);

// Creates a slot which evaluates to true when the red channel is greater then 0.3.
// Slots terminate a clause by default.
DigoutSlot slot2 = DigoutSlot.CompareImmediate(Canandcolor.DataSource.kProximity, SlotComparison.kGreaterThan, 0.3);

// Write the clause to the device.
// Note that the digital output must be configured to output using Canandcolor.Settings.setDigoutOutputConfig/Canandcolor.SetSettings first.
canandcolor.setDigoutSlot(Canandcolor.Digout.kDigout1, 0, slot1);
canandcolor.setDigoutSlot(Canandcolor.Digout.kDigout1, 1, slot2);
// Creates a slot which evaluates to true when the proximity is less then or equal to 0.5
DigoutSlot slot1 = DigoutSlot::CompareImmediate(CanandcolorDataSource::kProximity, SlotComparison::kLessThanOrEquals, 0.5, ClauseJoin::kOrWithNextSlot, false);

// Creates a slot which evaluates to true when the red channel is greater then 0.3.
// Slots terminate a clause by default.
DigoutSlot slot2 = DigoutSlot::CompareImmediate(CanandcolorDataSource::kProximity, SlotComparison::kGreaterThan, 0.3);

// Write the clause to the device.
// Note that the digital output must be configured to output using CanandcolorSettings.SetDigoutOutputConfig/Canandcolor.SetSettings first.
canandcolor.SetDigoutSlot(CanandcolorDigout::kDigout1, 0, slot1);
canandcolor.SetDigoutSlot(CanandcolorDigout::kDigout1, 1, slot2);

Enabling Digital Logic Mode

Once you have created your slots, you need to write them to the device then enable the digital output port. Digital output ports can be enabled as Normally Open or Normally Closed via a boolean passed to Canandcolor.DigoutMode.digoutSlot(boolean normallyClosed).

// a Canandcolor object with CAN ID 0
Canandcolor canandcolor = new Canandcolor(0);

//Fetch this canandcolor's settings
//This runs with a default timeout of 0.35 seconds
Canandcolor.Settings settings = canandcolor.getSettings();

//Sets digital output port 1 to use the digital logic system, setting it to normally closed
settings.setDigoutOutputConfig(Canandcolor.Digout.kDigout1, DigoutMode.DigoutSlot(true));

//Sets digital output port 2 to use the digital logic system, setting it to normally open
settings.setDigoutOutputConfig(Canandcolor.Digout.kDigout2, DigoutMode.DigoutSlot(false));

//Create a generic slot configuration
DigoutSlot slot = DigoutSlot.CompareImmediate(Canandcolor.DataSource.kProximity, SlotComparison.kLessThanOrEquals, 0.5);

//Set port 1 to use the previously created slot configuration in slot 0
canandcolor.setDigoutSlot(Canandcolor.Digout.kDigout1, 0, slot);

//Save settings to device
canandcolor.setSettings(settings, 0.050);
// a Canandcolor object with CAN ID 0
Canandcolor canandcolor{0};

// A new blank CanandcolorSettings object
CanandcolorSettings settings{};

// Sets digital output port 1 to use the digital logic system, setting it to normally closed (3.3v on false, 0v on true)
settings.SetDigoutOutputConfig(CanandcolorDigout::kDigout1, DigoutMode::DigoutSlot(true));

// Sets digital output port 2 to use the digital logic system, setting it to normally open (0v on false, 3.3v on true)
settings.SetDigoutOutputConfig(CanandcolorDigout::kDigout2, DigoutMode::DigoutSlot(false));

// Create a generic slot configuration
DigoutSlot slot = DigoutSlot::CompareImmediate(CanandcolorDataSource::kProximity, SlotComparison::kLessThanOrEquals, 0.5);

//Set port 1 to use the previously created slot configuration in slot 0
canandcolor.SetDigoutSlot(Canandcolor.Digout.kDigout1, 0, slot);

//Save settings to device
canandcolor.SetSettings(settings, 0.050);

Advanced information

The technical specification on how digout slots are encoded in CAN packets can be found in the Canandcolor message spec.